1 /* Copyright (C) 2005-2006 Fabio Marzocca <thesaltydog@gmail.com>
2  * Copyright (C) 2012-2021 MATE Developers
3  *
4  * This file is part of MATE Utils.
5  *
6  * MATE Utils is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * MATE Utils is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with MATE Utils.  If not, see <https://www.gnu.org/licenses/>.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 
24 #include <gtk/gtk.h>
25 #include <glib/gi18n.h>
26 #include <gio/gio.h>
27 #include <glibtop.h>
28 
29 #include "baobab.h"
30 #include "baobab-scan.h"
31 #include "baobab-treeview.h"
32 #include "baobab-utils.h"
33 #include "callbacks.h"
34 #include "baobab-prefs.h"
35 
36 #include "baobab-treemap.h"
37 #include "baobab-ringschart.h"
38 
39 #define BAOBAB_UI_RESOURCE "/org/mate/disk-usage-analyzer/baobab-main-window.ui"
40 #define GET_WIDGET(x) (GTK_WIDGET (gtk_builder_get_object (baobab.main_ui, (x))))
41 #define GET_ACTION(x) (GTK_ACTION (gtk_builder_get_object (baobab.main_ui, (x))))
42 #define GET_TOGGLE_ACTION(x) (GTK_TOGGLE_ACTION (gtk_builder_get_object (baobab.main_ui, (x))))
43 
44 static void push_iter_in_stack (GtkTreeIter *);
45 static GtkTreeIter pop_iter_from_stack (void);
46 
47 BaobabApplication baobab;
48 
49 static gint currentdepth = 0;
50 static GtkTreeIter currentiter;
51 static GtkTreeIter firstiter;
52 static GQueue *iterstack = NULL;
53 
54 enum {
55 	DND_TARGET_URI_LIST
56 };
57 
58 static GtkTargetEntry dnd_target_list[] = {
59 	{ "text/uri-list", 0, DND_TARGET_URI_LIST },
60 };
61 
62 static gboolean
scan_is_local(GFile * file)63 scan_is_local (GFile	*file)
64 {
65 	gchar *uri_scheme;
66 	gboolean ret = FALSE;
67 
68 	uri_scheme = g_file_get_uri_scheme (file);
69 	if (g_ascii_strcasecmp(uri_scheme,"file") == 0)
70 		ret = TRUE;
71 
72 	g_free (uri_scheme);
73 
74 	return ret;
75 }
76 
77 void
baobab_set_busy(gboolean busy)78 baobab_set_busy (gboolean busy)
79 {
80 	static GdkCursor *busy_cursor = NULL;
81 	GdkCursor *cursor = NULL;
82 	GdkWindow *window;
83 	GdkDisplay *display;
84 
85 	window = gtk_widget_get_window (baobab.window);
86 
87 	if (busy == TRUE) {
88 		if (!busy_cursor && window) {
89 			display = gtk_widget_get_display (baobab.window);
90 			busy_cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
91 		}
92 		cursor = busy_cursor;
93 
94 		gtk_widget_show (baobab.spinner);
95 		gtk_spinner_start (GTK_SPINNER (baobab.spinner));
96 
97 		baobab_chart_freeze_updates (baobab.rings_chart);
98 		baobab_chart_freeze_updates (baobab.treemap_chart);
99 
100 		gtk_widget_set_sensitive (baobab.chart_type_combo, FALSE);
101 	}
102 	else {
103 		gtk_widget_hide (baobab.spinner);
104 		gtk_spinner_stop (GTK_SPINNER (baobab.spinner));
105 
106 		baobab_chart_thaw_updates (baobab.rings_chart);
107 		baobab_chart_thaw_updates (baobab.treemap_chart);
108 
109 		gtk_widget_set_sensitive (baobab.chart_type_combo, TRUE);
110 	}
111 
112 	/* change the cursor */
113 	if (window) {
114 		gdk_window_set_cursor (window, cursor);
115 	}
116 }
117 
118 static void
set_drop_target(GtkWidget * target,gboolean active)119 set_drop_target (GtkWidget *target, gboolean active) {
120 	if (active) {
121 		gtk_drag_dest_set (GTK_WIDGET (target),
122 				   GTK_DEST_DEFAULT_DROP | GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT,
123 				   dnd_target_list,
124 				   G_N_ELEMENTS (dnd_target_list),
125 				   GDK_ACTION_COPY);
126 	} else {
127 		gtk_drag_dest_unset (target);
128 	}
129 }
130 
131 /* menu & toolbar sensitivity */
132 static void
check_menu_sens(gboolean scanning)133 check_menu_sens (gboolean scanning)
134 {
135 	gboolean has_current_location;
136 
137 	if (scanning) {
138 		while (gtk_events_pending ())
139 			gtk_main_iteration ();
140 
141 		baobab_set_statusbar (_("Scanning..."));
142 		gtk_action_set_sensitive (GET_ACTION ("expand_all"), TRUE);
143 		gtk_action_set_sensitive (GET_ACTION ("collapse_all"), TRUE);
144 	}
145 
146 	has_current_location = baobab.current_location != NULL;
147 
148 	gtk_action_set_sensitive (GET_ACTION ("menuscanhome"), !scanning);
149 	gtk_action_set_sensitive (GET_ACTION ("menuallfs"), !scanning);
150 	gtk_action_set_sensitive (GET_ACTION ("menuscandir"), !scanning);
151 	gtk_action_set_sensitive (GET_ACTION ("menustop"), scanning);
152 	gtk_action_set_sensitive (GET_ACTION ("menurescan"), !scanning && has_current_location);
153 	gtk_action_set_sensitive (GET_ACTION ("preferenze1"), !scanning);
154 	gtk_action_set_sensitive (GET_ACTION ("menu_scan_rem"), !scanning);
155 	gtk_action_set_sensitive (GET_ACTION ("ck_allocated"), !scanning && baobab.is_local);
156 
157 	gtk_widget_set_sensitive (GET_WIDGET ("tbscanhome"), !scanning);
158 	gtk_widget_set_sensitive (GET_WIDGET ("tbscanall"), !scanning);
159 	gtk_widget_set_sensitive (GET_WIDGET ("tbscandir"), !scanning);
160 	gtk_widget_set_sensitive (GET_WIDGET ("tbstop"), scanning);
161 	gtk_widget_set_sensitive (GET_WIDGET ("tbrescan"), !scanning && has_current_location);
162 	gtk_widget_set_sensitive (GET_WIDGET ("tb_scan_remote"), !scanning);
163 }
164 
165 static void
check_drop_targets(gboolean scanning)166 check_drop_targets (gboolean scanning)
167 {
168 	set_drop_target (baobab.rings_chart, !scanning);
169 	set_drop_target (baobab.treemap_chart, !scanning);
170 }
171 
172 static void
update_scan_label(void)173 update_scan_label (void)
174 {
175 	gchar *markup;
176 	gchar *total;
177 	gchar *used;
178 	gchar *available;
179 
180 		total = g_format_size (baobab.fs.total);
181 		used = g_format_size (baobab.fs.used);
182 		available = g_format_size (baobab.fs.avail);
183 
184 	/* Translators: these are labels for disk space */
185 	markup = g_markup_printf_escaped  ("<small>%s <b>%s</b> (%s %s %s %s)</small>",
186 					   _("Total filesystem capacity:"), total,
187 					   _("used:"), used,
188 					   _("available:"), available);
189 
190 	g_free (total);
191 	g_free (used);
192 	g_free (available);
193 
194 	gtk_label_set_markup (GTK_LABEL (GET_WIDGET ("label1")), markup);
195 	g_free (markup);
196 }
197 
198 void
baobab_update_filesystem(void)199 baobab_update_filesystem (void)
200 {
201 	baobab_get_filesystem (&baobab.fs);
202 	update_scan_label ();
203 }
204 
205 void
baobab_scan_location(GFile * file)206 baobab_scan_location (GFile *file)
207 {
208 	GtkToggleAction *ck_allocated;
209 
210 	if (!baobab_check_dir (file))
211 		return;
212 
213 	if (iterstack !=NULL)
214 		return;
215 
216 	if (baobab.current_location)
217 		g_object_unref (baobab.current_location);
218 	baobab.current_location = g_object_ref (file);
219 
220 	baobab.STOP_SCANNING = FALSE;
221 	baobab_set_busy (TRUE);
222 	check_menu_sens (TRUE);
223 	check_drop_targets (TRUE);
224 	gtk_tree_store_clear (baobab.model);
225 	currentdepth = -1;	/* flag */
226 	iterstack = g_queue_new ();
227 
228 	/* check if the file system is local or remote */
229 	baobab.is_local = scan_is_local (file);
230 	ck_allocated = GET_TOGGLE_ACTION ("ck_allocated");
231 	if (!baobab.is_local) {
232 		gtk_toggle_action_set_active (ck_allocated, FALSE);
233 		gtk_action_set_sensitive (GTK_ACTION (ck_allocated), FALSE);
234 		baobab.show_allocated = FALSE;
235 	}
236 	else {
237 		gtk_action_set_sensitive (GTK_ACTION (ck_allocated), TRUE);
238 	}
239 
240 	baobab_scan_execute (file);
241 
242 	/* set statusbar, percentage and allocated/normal size */
243 	baobab_set_statusbar (_("Calculating percentage bars..."));
244 	gtk_tree_model_foreach (GTK_TREE_MODEL (baobab.model),
245 				show_bars,
246 				NULL);
247 
248 	baobab_chart_set_max_depth (baobab.rings_chart, baobab.model_max_depth);
249 	baobab_chart_set_max_depth (baobab.treemap_chart, baobab.model_max_depth);
250 
251 	baobab_set_busy (FALSE);
252 	check_menu_sens (FALSE);
253 	check_drop_targets (FALSE);
254 	baobab_set_statusbar (_("Ready"));
255 
256 	gtk_tree_view_columns_autosize (GTK_TREE_VIEW (baobab.tree_view));
257 	baobab.STOP_SCANNING = TRUE;
258 	g_queue_free (iterstack);
259 	iterstack = NULL;
260 	baobab.CONTENTS_CHANGED_DELAYED = FALSE;
261 }
262 
263 void
baobab_scan_home(void)264 baobab_scan_home (void)
265 {
266 	GFile *file;
267 
268 	file = g_file_new_for_path (g_get_home_dir ());
269 	baobab_scan_location (file);
270 	g_object_unref (file);
271 }
272 
273 void
baobab_scan_root(void)274 baobab_scan_root (void)
275 {
276 	GFile *file;
277 
278 	file = g_file_new_for_uri ("file:///");
279 	baobab_scan_location (file);
280 	g_object_unref (file);
281 }
282 
283 void
baobab_rescan_current_dir(void)284 baobab_rescan_current_dir (void)
285 {
286 	g_return_if_fail (baobab.current_location != NULL);
287 
288 	baobab_update_filesystem ();
289 
290 	g_object_ref (baobab.current_location);
291 	baobab_scan_location (baobab.current_location);
292 	g_object_unref (baobab.current_location);
293 }
294 
295 void
baobab_stop_scan(void)296 baobab_stop_scan (void)
297 {
298 	baobab.STOP_SCANNING = TRUE;
299 
300 	baobab_set_statusbar (_("Calculating percentage bars..."));
301 	gtk_tree_model_foreach (GTK_TREE_MODEL (baobab.model),
302 				show_bars, NULL);
303 	gtk_tree_view_columns_autosize (GTK_TREE_VIEW (baobab.tree_view));
304 }
305 
306 /*
307  * pre-fills model during scanning
308  */
309 static void
prefill_model(struct chan_data * data)310 prefill_model (struct chan_data *data)
311 {
312 	GtkTreeIter iter, iterparent;
313 	char *name;
314 	char *str;
315 
316 	if (currentdepth == -1) {
317 		gtk_tree_store_append (baobab.model, &iter, NULL);
318 		firstiter = iter;
319 	}
320 	else if (data->depth == 1) {
321 		GtkTreePath *path;
322 
323 		gtk_tree_store_append (baobab.model, &iter, &firstiter);
324 		path = gtk_tree_model_get_path (GTK_TREE_MODEL (baobab.model),
325 						&firstiter);
326 		gtk_tree_view_expand_row (GTK_TREE_VIEW (baobab.tree_view),
327 					  path, FALSE);
328 		gtk_tree_path_free (path);
329 	}
330 	else if (data->depth > currentdepth) {
331 		gtk_tree_store_append (baobab.model, &iter, &currentiter);
332 	}
333 	else if (data->depth == currentdepth) {
334 		gtk_tree_model_iter_parent ((GtkTreeModel *) baobab.model,
335 					    &iterparent, &currentiter);
336 		gtk_tree_store_append (baobab.model, &iter, &iterparent);
337 	}
338 	else if (data->depth < currentdepth) {
339 		GtkTreeIter tempiter;
340 		gint i;
341 		iter = currentiter;
342 		for (i = 0; i <= (currentdepth - data->depth); i++) {
343 			gtk_tree_model_iter_parent ((GtkTreeModel *)
344 						    baobab.model,
345 						    &tempiter, &iter);
346 			iter = tempiter;
347 		}
348 		gtk_tree_store_append (baobab.model, &iter, &tempiter);
349 	}
350 
351 	currentdepth = data->depth;
352 	push_iter_in_stack (&iter);
353 	currentiter = iter;
354 
355 	/* in case filenames contains gmarkup */
356 	name = g_markup_escape_text (data->display_name, -1);
357 
358 	str = g_strdup_printf ("<small><i>%s</i></small>", _("Scanning..."));
359 
360 	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (baobab.tree_view), TRUE);
361 	gtk_tree_store_set (baobab.model, &iter,
362 			    COL_DIR_NAME, name,
363 			    COL_H_PARSENAME, "",
364 			    COL_H_ELEMENTS, -1,
365 			    COL_H_PERC, -1.0,
366 			    COL_DIR_SIZE, str,
367 			    COL_ELEMENTS, str, -1);
368 
369 	g_free (name);
370 	g_free (str);
371 
372 	while (gtk_events_pending ()) {
373 		gtk_main_iteration ();
374 	}
375 }
376 
377 static void
first_row(void)378 first_row (void)
379 {
380 	char *size;
381 	gdouble perc;
382 	char *label;
383 
384 	GtkTreeIter root_iter;
385 
386 	gchar *capacity_label, *capacity_size;
387 
388 	gtk_tree_store_append (baobab.model, &root_iter, NULL);
389 
390 	capacity_size = g_format_size (baobab.fs.total);
391 
392 	capacity_label = g_strdup (_("Total filesystem capacity"));
393 	gtk_tree_store_set (baobab.model, &root_iter,
394 			    COL_DIR_NAME, capacity_label,
395 			    COL_H_PARSENAME, "",
396 			    COL_H_PERC, 100.0,
397 			    COL_DIR_SIZE, capacity_size,
398 			    COL_H_SIZE, baobab.fs.total,
399 			    COL_H_ALLOCSIZE, baobab.fs.total,
400 			    COL_H_ELEMENTS, -1, -1);
401 	g_free (capacity_label);
402 	g_free (capacity_size);
403 
404 	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (baobab.tree_view), FALSE);
405 	gtk_tree_store_append (baobab.model, &firstiter, &root_iter);
406 
407 		size = g_format_size (baobab.fs.used);
408 
409 	if (baobab.fs.total == 0 && baobab.fs.used == 0) {
410 		perc = 100.0;
411 	} else {
412 		g_assert (baobab.fs.total != 0);
413 		perc = ((gdouble) baobab.fs.used * 100) / (gdouble) baobab.fs.total;
414 	}
415 
416 	label = g_strdup (_("Total filesystem usage"));
417 	gtk_tree_store_set (baobab.model, &firstiter,
418 			    COL_DIR_NAME, label,
419 			    COL_H_PARSENAME, "",
420 			    COL_H_PERC, perc,
421 			    COL_DIR_SIZE, size,
422 			    COL_H_SIZE, baobab.fs.used,
423 			    COL_H_ALLOCSIZE, baobab.fs.used,
424 			    COL_H_ELEMENTS, -1, -1);
425 
426 	g_free (size);
427 	g_free (label);
428 
429 	gtk_tree_view_expand_all (GTK_TREE_VIEW (baobab.tree_view));
430 }
431 
432 /* fills model during scanning */
433 void
baobab_fill_model(struct chan_data * data)434 baobab_fill_model (struct chan_data *data)
435 {
436 	GtkTreeIter iter;
437 	GString *hardlinks;
438 	GString *elements;
439 	char *name;
440 	char *size;
441 	char *alloc_size;
442 
443 	if (data->elements == -1) {
444 		prefill_model (data);
445 		return;
446 	}
447 
448 	iter = pop_iter_from_stack ();
449 
450 	/* in case filenames contains gmarkup */
451 	name = g_markup_escape_text (data->display_name, -1);
452 
453 	hardlinks = g_string_new ("");
454 	if (data->tempHLsize > 0) {
455 		size = g_format_size (data->tempHLsize);
456 
457 		g_string_assign (hardlinks, "<i>(");
458 		g_string_append (hardlinks, _("contains hardlinks for:"));
459 		g_string_append (hardlinks, " ");
460 		g_string_append (hardlinks, size);
461 		g_string_append (hardlinks, ")</i>");
462 		g_free (size);
463 	}
464 
465 	elements = g_string_new ("");
466 	g_string_printf (elements,
467 			 ngettext ("%5d item", "%5d items",
468 				   data->elements), data->elements);
469 
470 	size = g_format_size (data->size);
471 	alloc_size = g_format_size (data->alloc_size);
472 
473 	gtk_tree_store_set (baobab.model, &iter,
474 			    COL_DIR_NAME, name,
475 			    COL_H_PARSENAME, data->parse_name,
476 			    COL_H_PERC, -1.0,
477 			    COL_DIR_SIZE,
478 			    baobab.show_allocated ? alloc_size : size,
479 			    COL_H_SIZE, data->size,
480 			    COL_ELEMENTS, elements->str,
481 			    COL_H_ELEMENTS, data->elements,
482 			    COL_HARDLINK, hardlinks->str,
483 			    COL_H_HARDLINK, data->tempHLsize,
484 			    COL_H_ALLOCSIZE, data->alloc_size, -1);
485 
486 	while (gtk_events_pending ()) {
487 		gtk_main_iteration ();
488 	}
489 
490 	g_string_free (hardlinks, TRUE);
491 	g_string_free (elements, TRUE);
492 	g_free (name);
493 	g_free (size);
494 	g_free (alloc_size);
495 }
496 
497 void
push_iter_in_stack(GtkTreeIter * iter)498 push_iter_in_stack (GtkTreeIter *iter)
499 {
500 	g_queue_push_head (iterstack, iter->user_data3);
501 	g_queue_push_head (iterstack, iter->user_data2);
502 	g_queue_push_head (iterstack, iter->user_data);
503 	g_queue_push_head (iterstack, GINT_TO_POINTER (iter->stamp));
504 }
505 
506 GtkTreeIter
pop_iter_from_stack(void)507 pop_iter_from_stack (void)
508 {
509 	GtkTreeIter iter;
510 
511 	iter.stamp = GPOINTER_TO_INT (g_queue_pop_head (iterstack));
512 	iter.user_data = g_queue_pop_head (iterstack);
513 	iter.user_data2 = g_queue_pop_head (iterstack);
514 	iter.user_data3 = g_queue_pop_head (iterstack);
515 
516 	return iter;
517 }
518 
519 gboolean
baobab_is_excluded_location(GFile * file)520 baobab_is_excluded_location (GFile *file)
521 {
522 	gboolean ret = FALSE;
523 	GSList *l;
524 
525 	g_return_val_if_fail (file != NULL, FALSE);
526 
527 	for (l = baobab.excluded_locations; l != NULL; l = l->next) {
528 		if (g_file_equal (l->data, file)) {
529 			ret = TRUE;
530 			break;
531 		}
532 	}
533 
534 	return ret;
535 }
536 
537 static void
volume_changed(GVolumeMonitor * volume_monitor,GVolume * volume,gpointer user_data)538 volume_changed (GVolumeMonitor *volume_monitor,
539                 GVolume        *volume,
540                 gpointer        user_data)
541 {
542 	/* filesystem has changed (mounted or unmounted device) */
543 	baobab_update_filesystem ();
544 }
545 
546 static void
home_contents_changed(GFileMonitor * file_monitor,GFile * child,GFile * other_file,GFileMonitorEvent event_type,gpointer user_data)547 home_contents_changed (GFileMonitor      *file_monitor,
548 		       GFile             *child,
549 		       GFile             *other_file,
550 		       GFileMonitorEvent  event_type,
551 		       gpointer           user_data)
552 
553 {
554 	gchar *excluding;
555 
556 	if (baobab.CONTENTS_CHANGED_DELAYED)
557 		return;
558 
559 	excluding = g_file_get_basename (child);
560 	if (strcmp (excluding, ".recently-used") == 0   ||
561 	    strcmp (excluding, ".mate2_private") == 0  ||
562 	    strcmp (excluding, ".xsession-errors") == 0 ||
563 	    strcmp (excluding, ".bash_history") == 0) {
564 		g_free (excluding);
565 		return;
566 	}
567 	g_free (excluding);
568 
569 	baobab.CONTENTS_CHANGED_DELAYED = TRUE;
570 }
571 
572 static void
monitor_volume(void)573 monitor_volume (void)
574 {
575 	baobab.monitor_vol = g_volume_monitor_get ();
576 
577 	g_signal_connect (baobab.monitor_vol, "volume_changed",
578 			  G_CALLBACK (volume_changed), NULL);
579 }
580 
581 static void
monitor_home(gboolean enable)582 monitor_home (gboolean enable)
583 {
584 	if (enable && baobab.monitor_home == NULL) {
585 		GFile *file;
586 		GError *error = NULL;
587 
588 		file = g_file_new_for_path (g_get_home_dir ());
589 		baobab.monitor_home = g_file_monitor_directory (file, 0, NULL, &error);
590 		g_object_unref (file);
591 
592 		if (!baobab.monitor_home) {
593 			message (_("Could not initialize monitoring"),
594 				 _("Changes to your home folder will not be monitored."),
595 				 GTK_MESSAGE_WARNING, NULL);
596 			g_print ("homedir:%s\n", error->message);
597 			g_error_free (error);
598 		}
599 		else {
600 			g_signal_connect (baobab.monitor_home,
601 					  "changed",
602 					  G_CALLBACK (home_contents_changed),
603 					  NULL);
604 		}
605 	}
606 	else if (!enable && baobab.monitor_home != NULL) {
607 		g_file_monitor_cancel (baobab.monitor_home);
608 		g_object_unref (baobab.monitor_home);
609 		baobab.monitor_home = NULL;
610 	}
611 }
612 
613 void
baobab_set_statusbar(const gchar * text)614 baobab_set_statusbar (const gchar *text)
615 {
616 	gtk_statusbar_pop (GTK_STATUSBAR (baobab.statusbar), 1);
617 	gtk_statusbar_push (GTK_STATUSBAR (baobab.statusbar), 1, text);
618 
619 	while (gtk_events_pending ())
620 		gtk_main_iteration ();
621 }
622 
623 static void
toolbar_reconfigured_cb(GtkToolItem * item,GtkWidget * spinner)624 toolbar_reconfigured_cb (GtkToolItem *item,
625 			 GtkWidget   *spinner)
626 {
627 	GtkToolbarStyle style;
628 	GtkIconSize size;
629 
630 	style = gtk_tool_item_get_toolbar_style (item);
631 
632 	if (style == GTK_TOOLBAR_BOTH)
633 	{
634 		size = GTK_ICON_SIZE_DIALOG;
635 	}
636 	else
637 	{
638 		size = GTK_ICON_SIZE_LARGE_TOOLBAR;
639 	}
640 
641 	gtk_widget_set_size_request (spinner, size, size);
642 }
643 
644 static void
baobab_create_toolbar(void)645 baobab_create_toolbar (void)
646 {
647 	GtkWidget *toolbar;
648 	GtkToolItem *item;
649 	GtkToolItem *separator;
650 
651 	toolbar = GET_WIDGET ("toolbar1");
652 	if (toolbar == NULL) {
653 		g_printerr ("Could not build toolbar\n");
654 		return;
655 	}
656 
657 	baobab.toolbar = toolbar;
658 
659 	separator = gtk_separator_tool_item_new ();
660 	gtk_separator_tool_item_set_draw (GTK_SEPARATOR_TOOL_ITEM (separator), FALSE);
661 	gtk_tool_item_set_expand (GTK_TOOL_ITEM (separator), TRUE);
662 	gtk_container_add (GTK_CONTAINER (toolbar), GTK_WIDGET (separator));
663 	gtk_widget_show (GTK_WIDGET (separator));
664 
665 	baobab.spinner = gtk_spinner_new ();
666 	item = gtk_tool_item_new ();
667 	gtk_container_add (GTK_CONTAINER (item), baobab.spinner);
668 	gtk_container_add (GTK_CONTAINER (toolbar), GTK_WIDGET (item));
669 	gtk_widget_show (GTK_WIDGET (item));
670 
671 	g_signal_connect (item, "toolbar-reconfigured",
672 			  G_CALLBACK (toolbar_reconfigured_cb), baobab.spinner);
673 	toolbar_reconfigured_cb (item, baobab.spinner);
674 
675 	g_settings_bind (baobab.ui_settings,
676 			 BAOBAB_SETTINGS_TOOLBAR_VISIBLE,
677 			 baobab.toolbar, "visible",
678 			 G_SETTINGS_BIND_DEFAULT);
679 	g_settings_bind (baobab.ui_settings,
680 			 BAOBAB_SETTINGS_TOOLBAR_VISIBLE,
681 			 GET_TOGGLE_ACTION ("view_tb"), "active",
682 			 G_SETTINGS_BIND_DEFAULT);
683 
684 }
685 
686 static void
baobab_create_statusbar(void)687 baobab_create_statusbar (void)
688 {
689 	baobab.statusbar = GET_WIDGET ("statusbar1");
690 	if (baobab.statusbar == NULL) {
691 		g_printerr ("Could not build statusbar\n");
692 		return;
693 	}
694 
695 	g_settings_bind (baobab.ui_settings,
696 			 BAOBAB_SETTINGS_STATUSBAR_VISIBLE,
697 			 baobab.statusbar, "visible",
698 			 G_SETTINGS_BIND_DEFAULT);
699 	g_settings_bind (baobab.ui_settings,
700 			 BAOBAB_SETTINGS_STATUSBAR_VISIBLE,
701 			 GET_TOGGLE_ACTION ("view_sb"), "active",
702 			 G_SETTINGS_BIND_DEFAULT);
703 }
704 
705 static void
baobab_settings_subfoldertips_changed(GSettings * settings,const gchar * key,gpointer user_data)706 baobab_settings_subfoldertips_changed (GSettings *settings,
707 									   const gchar *key,
708 									   gpointer user_data)
709 {
710 	gboolean visible;
711 
712 	visible = g_settings_get_boolean (settings, key);
713 	baobab_ringschart_set_subfoldertips_enabled (baobab.rings_chart, visible);
714 }
715 
716 static void
baobab_set_excluded_locations(gchar ** uris)717 baobab_set_excluded_locations (gchar **uris)
718 {
719 	gint i;
720 
721 	g_slist_free_full (baobab.excluded_locations, g_object_unref);
722 	baobab.excluded_locations = NULL;
723 	for (i = 0; uris[i] != NULL; ++i) {
724 		baobab.excluded_locations = g_slist_prepend (baobab.excluded_locations,
725 						g_file_new_for_uri (uris[i]));
726 	}
727 }
728 
729 static void
store_excluded_locations(void)730 store_excluded_locations (void)
731 {
732 	GSList *l;
733 	GPtrArray *uris;
734 
735 	uris = g_ptr_array_new ();
736 
737 	for (l = baobab.excluded_locations; l != NULL; l = l->next) {
738 		g_ptr_array_add (uris, g_file_get_uri (l->data));
739 	}
740 
741 	g_ptr_array_add (uris, NULL);
742 
743 	g_settings_set_strv (baobab.prefs_settings,
744 			     BAOBAB_SETTINGS_EXCLUDED_URIS,
745 			     (const gchar *const *) uris->pdata);
746 
747 	g_ptr_array_free (uris, TRUE);
748 }
749 
750 static void
sanity_check_excluded_locations(void)751 sanity_check_excluded_locations (void)
752 {
753 	GFile *root;
754 	GSList *l;
755 
756 	/* Make sure the root dir is not excluded */
757 	root = g_file_new_for_uri ("file:///");
758 
759 	for (l = baobab.excluded_locations; l != NULL; l = l->next) {
760 		if (g_file_equal (l->data, root)) {
761 			baobab.excluded_locations = g_slist_delete_link (baobab.excluded_locations, l);
762 			store_excluded_locations ();
763 			break;
764 		}
765 	}
766 
767 	g_object_unref (root);
768 }
769 
770 static void
excluded_uris_changed(GSettings * settings,const gchar * key,gpointer user_data)771 excluded_uris_changed (GSettings   *settings,
772 		       const gchar *key,
773 		       gpointer     user_data)
774 {
775 	gchar **uris;
776 
777 	uris = g_settings_get_strv (settings, key);
778 	baobab_set_excluded_locations (uris);
779 	g_strfreev (uris);
780 
781 	baobab_update_filesystem ();
782 
783 	gtk_tree_store_clear (baobab.model);
784 	first_row ();
785 }
786 
787 static void
baobab_setup_excluded_locations(void)788 baobab_setup_excluded_locations (void)
789 {
790 	gchar **uris;
791 
792 	g_signal_connect (baobab.prefs_settings,
793 			 "changed::" BAOBAB_SETTINGS_EXCLUDED_URIS,
794 			  G_CALLBACK (excluded_uris_changed),
795 			  NULL);
796 
797 	uris = g_settings_get_strv (baobab.prefs_settings,
798 				    BAOBAB_SETTINGS_EXCLUDED_URIS);
799 	baobab_set_excluded_locations (uris);
800 	g_strfreev (uris);
801 
802 	sanity_check_excluded_locations ();
803 }
804 
805 static void
baobab_settings_monitor_home_changed(GSettings * settings,const gchar * key,gpointer user_data)806 baobab_settings_monitor_home_changed (GSettings *settings,
807 				      const gchar *key,
808 				      gpointer user_data)
809 {
810 	gboolean enable;
811 
812 	enable = g_settings_get_boolean (settings, key);
813 
814 	monitor_home (enable);
815 }
816 
817 static void
baobab_setup_monitors(void)818 baobab_setup_monitors (void)
819 {
820 	gboolean enable;
821 
822 	monitor_volume ();
823 
824 	g_signal_connect (baobab.prefs_settings,
825 			  "changed::" BAOBAB_SETTINGS_MONITOR_HOME,
826 			  G_CALLBACK (baobab_settings_monitor_home_changed),
827 			  NULL);
828 
829 	enable = g_settings_get_boolean (baobab.prefs_settings,
830 					 BAOBAB_SETTINGS_MONITOR_HOME);
831 
832 	monitor_home (enable);
833 }
834 
835 static void
baobab_init(void)836 baobab_init (void)
837 {
838 	GError *error = NULL;
839 
840 	/* FileSystem usage */
841 	baobab_get_filesystem (&baobab.fs);
842 
843 	/* Load the UI */
844 	baobab.main_ui = gtk_builder_new ();
845 	if (gtk_builder_add_from_resource (baobab.main_ui, BAOBAB_UI_RESOURCE, &error) == 0) {
846 		g_object_unref (baobab.main_ui);
847 		g_critical ("Unable to load the user interface file: %s", error->message);
848 		g_error_free (error);
849 		exit (1);
850 	}
851 
852 	gtk_builder_connect_signals (baobab.main_ui, NULL);
853 
854 	/* Settings */
855 	baobab.ui_settings = g_settings_new (BAOBAB_UI_SETTINGS_SCHEMA);
856 	baobab.prefs_settings = g_settings_new (BAOBAB_PREFS_SETTINGS_SCHEMA);
857 
858 	/* Misc */
859 	baobab.CONTENTS_CHANGED_DELAYED = FALSE;
860 	baobab.STOP_SCANNING = TRUE;
861 	baobab.show_allocated = TRUE;
862 	baobab.is_local = TRUE;
863 
864 	baobab_setup_excluded_locations ();
865 
866 	baobab_create_toolbar ();
867 	baobab_create_statusbar ();
868 	baobab_setup_monitors ();
869 
870 	g_signal_connect (baobab.ui_settings, "changed::" BAOBAB_SETTINGS_SUBFLSTIPS_VISIBLE,
871 					  (GCallback) baobab_settings_subfoldertips_changed, NULL);
872 }
873 
874 static void
baobab_shutdown(void)875 baobab_shutdown (void)
876 {
877 	if (baobab.current_location) {
878 		g_object_unref (baobab.current_location);
879 	}
880 
881 	if (baobab.monitor_vol) {
882 		g_object_unref (baobab.monitor_vol);
883 	}
884 
885 	if (baobab.monitor_home) {
886 		g_file_monitor_cancel (baobab.monitor_home);
887 		g_object_unref (baobab.monitor_home);
888 	}
889 
890 	g_free (baobab.selected_path);
891 
892 	g_slist_free_full (baobab.excluded_locations, g_object_unref);
893 
894 	if (baobab.ui_settings) {
895 		g_object_unref (baobab.ui_settings);
896 	}
897 
898 	if (baobab.prefs_settings) {
899 		g_object_unref (baobab.prefs_settings);
900 	}
901 }
902 
903 static BaobabChartMenu *
create_context_menu(void)904 create_context_menu (void)
905 {
906 	BaobabChartMenu *menu = NULL;
907 
908 	baobab.chart_menu = g_new0 (BaobabChartMenu, 1);
909 	menu = baobab.chart_menu;
910 
911 	menu->widget = gtk_menu_new ();
912 
913 	menu->up_item = gtk_image_menu_item_new_with_label (_("Move to parent folder"));
914 	gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu->up_item),
915 				       gtk_image_new_from_icon_name("go-up", GTK_ICON_SIZE_MENU));
916 
917 	menu->zoom_in_item = gtk_image_menu_item_new_with_label (_("Zoom in")) ;
918 	gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu->zoom_in_item),
919 				       gtk_image_new_from_icon_name("list-add", GTK_ICON_SIZE_MENU));
920 
921 	menu->zoom_out_item = gtk_image_menu_item_new_with_label (_("Zoom out"));
922 	gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu->zoom_out_item),
923 				       gtk_image_new_from_icon_name("list-remove", GTK_ICON_SIZE_MENU));
924 
925 	menu->snapshot_item = gtk_image_menu_item_new_with_label (_("Save screenshot"));
926 	gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu->snapshot_item),
927 				       gtk_image_new_from_icon_name ("applets-screenshooter", GTK_ICON_SIZE_MENU));
928 
929 	gtk_menu_shell_append (GTK_MENU_SHELL (menu->widget),
930 			       menu->up_item);
931 	gtk_menu_shell_append (GTK_MENU_SHELL (menu->widget),
932 			       gtk_separator_menu_item_new ());
933 	gtk_menu_shell_append (GTK_MENU_SHELL (menu->widget),
934 			       menu->zoom_in_item);
935 	gtk_menu_shell_append (GTK_MENU_SHELL (menu->widget),
936 			       menu->zoom_out_item);
937 	gtk_menu_shell_append (GTK_MENU_SHELL (menu->widget),
938 			       gtk_separator_menu_item_new ());
939 	gtk_menu_shell_append (GTK_MENU_SHELL (menu->widget),
940 			       menu->snapshot_item);
941 
942 	/* connect signals */
943 	g_signal_connect (menu->up_item, "activate",
944 			  G_CALLBACK (on_move_upwards_cb), NULL);
945 	g_signal_connect (menu->zoom_in_item, "activate",
946 			  G_CALLBACK (on_zoom_in_cb), NULL);
947 	g_signal_connect (menu->zoom_out_item, "activate",
948 			  G_CALLBACK (on_zoom_out_cb), NULL);
949 	g_signal_connect (menu->snapshot_item, "activate",
950 					  G_CALLBACK (on_chart_snapshot_cb), NULL);
951 
952 	gtk_widget_show_all (menu->widget);
953 
954 	return menu;
955 }
956 
957 static void
on_chart_item_activated(BaobabChart * chart,GtkTreeIter * iter)958 on_chart_item_activated (BaobabChart *chart, GtkTreeIter *iter)
959 {
960 	GtkTreePath *path;
961 
962 	path = gtk_tree_model_get_path (GTK_TREE_MODEL (baobab.model), iter);
963 
964 	if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (baobab.tree_view), path))
965 		gtk_tree_view_expand_to_path (GTK_TREE_VIEW (baobab.tree_view), path);
966 
967 	gtk_tree_view_set_cursor (GTK_TREE_VIEW (baobab.tree_view),
968 				  path, NULL, FALSE);
969 	gtk_tree_path_free (path);
970 }
971 
972 static gboolean
on_chart_button_release(BaobabChart * chart,GdkEventButton * event,gpointer data)973 on_chart_button_release (BaobabChart *chart,
974 			 GdkEventButton *event,
975 			 gpointer data)
976 {
977 	if (baobab_chart_is_frozen (baobab.current_chart))
978 		return FALSE;
979 
980 	if (event->button== 3) /* right button */
981 	{
982 		GtkTreePath *root_path;
983 		BaobabChartMenu *menu;
984 
985 		root_path = baobab_chart_get_root (baobab.current_chart);
986 
987 		menu = baobab.chart_menu;
988 		gtk_widget_set_sensitive (menu->up_item,
989 					  ((root_path != NULL) &&
990 					  (gtk_tree_path_get_depth (root_path) > 1)));
991 		gtk_widget_set_sensitive (menu->zoom_in_item,
992 					  baobab_chart_can_zoom_in (baobab.current_chart));
993 		gtk_widget_set_sensitive (menu->zoom_out_item,
994 					  baobab_chart_can_zoom_out (baobab.current_chart));
995 
996 		/* show the menu */
997 		gtk_menu_popup_at_pointer (GTK_MENU (menu->widget),
998 		                           (const GdkEvent*) event);
999 
1000 		gtk_tree_path_free (root_path);
1001 	}
1002 
1003 	return FALSE;
1004 }
1005 
1006 static void
drag_data_received_handl(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection_data,guint target_type,guint time,gpointer data)1007 drag_data_received_handl (GtkWidget *widget,
1008 			 GdkDragContext *context,
1009 			 gint x,
1010 			 gint y,
1011 			 GtkSelectionData *selection_data,
1012 			 guint target_type,
1013 			 guint time,
1014 			 gpointer data)
1015 {
1016 	GFile *gf = NULL;
1017 
1018 	/* set "gf" if we got some valid data */
1019 	if ((selection_data != NULL) &&
1020 	    (gtk_selection_data_get_length (selection_data) >= 0) &&
1021 	    (target_type == DND_TARGET_URI_LIST)) {
1022 		gchar **uri_list;
1023 		uri_list = g_uri_list_extract_uris ((const gchar *) gtk_selection_data_get_data (selection_data));
1024 		/* check list is 1 item long */
1025 		if (uri_list != NULL && uri_list[0] != NULL && uri_list[1] == NULL) {
1026 			gf = g_file_new_for_uri (uri_list[0]);
1027 		}
1028 		g_strfreev (uri_list);
1029 	}
1030 
1031 	/* success if "gf" has been set */
1032 	if (gf != NULL) {
1033 		/* finish drop before beginning scan, as the drag-drop can
1034 		   probably time out */
1035 		gtk_drag_finish (context, TRUE, FALSE, time);
1036 		baobab_scan_location (gf);
1037 		g_object_unref (gf);
1038 	} else {
1039 		gtk_drag_finish (context, FALSE, FALSE, time);
1040 	}
1041 }
1042 
1043 static void
set_active_chart(GtkWidget * chart)1044 set_active_chart (GtkWidget *chart)
1045 {
1046 	if (baobab.current_chart != chart) {
1047 		if (baobab.current_chart) {
1048 			baobab_chart_freeze_updates (baobab.current_chart);
1049 
1050 			g_object_ref (baobab.current_chart);
1051 			gtk_container_remove (GTK_CONTAINER (baobab.chart_frame),
1052 					      baobab.current_chart);
1053 		}
1054 
1055 		gtk_container_add (GTK_CONTAINER (baobab.chart_frame), chart);
1056 		g_object_unref (chart);
1057 
1058 		baobab_chart_thaw_updates (chart);
1059 
1060 		baobab.current_chart = chart;
1061 
1062 		gtk_widget_show_all (baobab.chart_frame);
1063 
1064 		g_settings_set_string (baobab.ui_settings,
1065 				       BAOBAB_SETTINGS_ACTIVE_CHART,
1066 				       baobab.current_chart == baobab.rings_chart ? "rings" : "treemap");
1067 	}
1068 }
1069 
1070 static void
on_chart_type_change(GtkWidget * combo,gpointer user_data)1071 on_chart_type_change (GtkWidget *combo, gpointer user_data)
1072 {
1073 	GtkWidget *chart;
1074 	gint active;
1075 
1076 	active = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
1077 
1078 	switch (active) {
1079 	case 0:
1080 		chart = baobab.rings_chart;
1081 		break;
1082 	case 1:
1083 		chart = baobab.treemap_chart;
1084 		break;
1085 	default:
1086 		g_return_if_reached ();
1087 	}
1088 
1089 	set_active_chart (chart);
1090 }
1091 
1092 static void
initialize_charts(void)1093 initialize_charts (void)
1094 {
1095 	GtkWidget *hpaned_main;
1096 	GtkWidget *hbox1;
1097 	char *saved_chart;
1098 	gboolean visible;
1099 
1100 	baobab.chart_frame = gtk_frame_new (NULL);
1101 	gtk_frame_set_label_align (GTK_FRAME (baobab.chart_frame), 0.0, 0.0);
1102 	gtk_frame_set_shadow_type (GTK_FRAME (baobab.chart_frame), GTK_SHADOW_IN);
1103 
1104 	hpaned_main = GET_WIDGET ("hpaned_main");
1105 	gtk_paned_pack2 (GTK_PANED (hpaned_main),
1106 			 baobab.chart_frame, TRUE, TRUE);
1107 	gtk_paned_set_position (GTK_PANED (hpaned_main), 480);
1108 
1109 	baobab.chart_type_combo = gtk_combo_box_text_new ();
1110 	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (baobab.chart_type_combo),
1111 				   _("View as Rings Chart"));
1112 	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (baobab.chart_type_combo),
1113 				   _("View as Treemap Chart"));
1114 	gtk_widget_show (baobab.chart_type_combo);
1115 	g_signal_connect (baobab.chart_type_combo,
1116 			  "changed",
1117 			  G_CALLBACK (on_chart_type_change), NULL);
1118 
1119 	hbox1 = GET_WIDGET ("hbox1");
1120 	gtk_container_add (GTK_CONTAINER (hbox1), baobab.chart_type_combo);
1121 	gtk_box_set_spacing (GTK_BOX (hbox1), 50);
1122 	gtk_box_set_child_packing (GTK_BOX (hbox1),
1123 				   baobab.chart_type_combo,
1124 				   FALSE,
1125 				   TRUE,
1126 				   0, GTK_PACK_END);
1127 
1128 	baobab.chart_menu = create_context_menu ();
1129 
1130 	/* Baobab's Treemap Chart */
1131 	baobab.treemap_chart = baobab_treemap_new ();
1132 	baobab_chart_set_model_with_columns (baobab.treemap_chart,
1133 					     GTK_TREE_MODEL (baobab.model),
1134 					     COL_DIR_NAME,
1135 					     COL_DIR_SIZE,
1136 					     COL_H_PARSENAME,
1137 					     COL_H_PERC,
1138 					     COL_H_ELEMENTS,
1139 					     NULL);
1140 	baobab_chart_set_max_depth (baobab.treemap_chart, 1);
1141 	g_signal_connect (baobab.treemap_chart, "item_activated",
1142 			  G_CALLBACK (on_chart_item_activated), NULL);
1143 	g_signal_connect (baobab.treemap_chart, "button-release-event",
1144 			  G_CALLBACK (on_chart_button_release), NULL);
1145 	g_signal_connect (baobab.treemap_chart, "drag-data-received",
1146 			  G_CALLBACK (drag_data_received_handl), NULL);
1147 	gtk_widget_show (baobab.treemap_chart);
1148 	g_object_ref_sink (baobab.treemap_chart);
1149 	baobab_chart_freeze_updates (baobab.treemap_chart);
1150 
1151 	/* Baobab's Rings Chart */
1152 	baobab.rings_chart = (GtkWidget *) baobab_ringschart_new ();
1153 	baobab_chart_set_model_with_columns (baobab.rings_chart,
1154 					     GTK_TREE_MODEL (baobab.model),
1155 					     COL_DIR_NAME,
1156 					     COL_DIR_SIZE,
1157 					     COL_H_PARSENAME,
1158 					     COL_H_PERC,
1159 					     COL_H_ELEMENTS,
1160 					     NULL);
1161 
1162 	visible = g_settings_get_boolean (baobab.ui_settings,
1163 									  BAOBAB_SETTINGS_SUBFLSTIPS_VISIBLE);
1164 	baobab_ringschart_set_subfoldertips_enabled (baobab.rings_chart, visible);
1165 
1166 	baobab_chart_set_max_depth (baobab.rings_chart, 1);
1167 	g_signal_connect (baobab.rings_chart, "item_activated",
1168 			  G_CALLBACK (on_chart_item_activated), NULL);
1169 	g_signal_connect (baobab.rings_chart, "button-release-event",
1170 			  G_CALLBACK (on_chart_button_release), NULL);
1171 	g_signal_connect (baobab.rings_chart, "drag-data-received",
1172 			  G_CALLBACK (drag_data_received_handl), NULL);
1173 	gtk_widget_show (baobab.rings_chart);
1174 	g_object_ref_sink (baobab.rings_chart);
1175 	baobab_chart_freeze_updates (baobab.rings_chart);
1176 
1177 	saved_chart = g_settings_get_string (baobab.ui_settings,
1178 					     BAOBAB_SETTINGS_ACTIVE_CHART);
1179 
1180 	if (0 == g_ascii_strcasecmp (saved_chart, "treemap")) {
1181 		set_active_chart (baobab.treemap_chart);
1182 		gtk_combo_box_set_active (GTK_COMBO_BOX (baobab.chart_type_combo), 1);
1183 	} else {
1184 		set_active_chart (baobab.rings_chart);
1185 		gtk_combo_box_set_active (GTK_COMBO_BOX (baobab.chart_type_combo), 0);
1186 	}
1187 
1188 	g_free (saved_chart);
1189 
1190 	check_drop_targets (FALSE);
1191 }
1192 
1193 void
baobab_quit()1194 baobab_quit ()
1195 {
1196 	baobab_stop_scan ();
1197 	gtk_main_quit ();
1198 }
1199 
1200 static gboolean
start_proc_on_command_line(GFile * file)1201 start_proc_on_command_line (GFile *file)
1202 {
1203 	baobab_scan_location (file);
1204 
1205 	return FALSE;
1206 }
1207 
1208 static gboolean
show_version(const gchar * option_name,const gchar * value,gpointer data,GError ** error)1209 show_version (const gchar *option_name,
1210               const gchar *value,
1211               gpointer data,
1212               GError **error)
1213 {
1214 	g_print("%s %s\n", g_get_application_name (), VERSION);
1215 	exit (0);
1216 	return TRUE; /* It's just good form */
1217 }
1218 
1219 int
main(int argc,char * argv[])1220 main (int argc, char *argv[])
1221 {
1222 	gchar **directories = NULL;
1223 	const GOptionEntry options[] = {
1224 		{"version", 'V', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, show_version, N_("Show version"), NULL},
1225 		{G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &directories, NULL, N_("[DIRECTORY]")},
1226 		{NULL}
1227 	};
1228 	GOptionContext *context;
1229 	GError *error = NULL;
1230 
1231 #ifdef ENABLE_NLS
1232 	bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR);
1233 	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1234 	textdomain (GETTEXT_PACKAGE);
1235 #endif /* ENABLE_NLS */
1236 
1237 	g_set_application_name (_("Disk Usage Analyzer"));
1238 
1239 	context = g_option_context_new (NULL);
1240 	g_option_context_set_ignore_unknown_options (context, FALSE);
1241 	g_option_context_set_help_enabled (context, TRUE);
1242 	g_option_context_add_main_entries(context, options, GETTEXT_PACKAGE);
1243 	g_option_context_add_group (context, gtk_get_option_group (TRUE));
1244 
1245 	g_option_context_parse (context, &argc, &argv, &error);
1246 
1247 	if (error) {
1248 		g_critical ("Unable to parse option: %s", error->message);
1249 		g_error_free (error);
1250 		g_option_context_free (context);
1251 
1252 		exit (1);
1253 	}
1254 	g_option_context_free (context);
1255 
1256 	if (directories && directories[0] && directories[1]) {
1257 		g_critical (_("Too many arguments. Only one directory can be specified."));
1258 		exit (1);
1259 	}
1260 
1261 	glibtop_init ();
1262 
1263 	gtk_window_set_default_icon_name ("mate-disk-usage-analyzer");
1264 
1265 	baobab_init ();
1266 
1267 	if (baobab.fs.total == 0) {
1268 		GtkWidget *dialog;
1269 
1270 		dialog = gtk_message_dialog_new (NULL,
1271 				GTK_DIALOG_DESTROY_WITH_PARENT,
1272 				GTK_MESSAGE_ERROR,
1273 				GTK_BUTTONS_CLOSE,
1274 				_("Could not detect any mount point."));
1275 		gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG(dialog),
1276 				_("Without mount points disk usage cannot be analyzed."));
1277 		gtk_dialog_run (GTK_DIALOG (dialog));
1278 		gtk_widget_destroy (dialog);
1279 		goto closing;
1280 	}
1281 
1282 	check_menu_sens (FALSE);
1283 	update_scan_label ();
1284 
1285 	baobab.window = GET_WIDGET ("baobab_window");
1286 	gtk_window_set_position (GTK_WINDOW (baobab.window),
1287 				 GTK_WIN_POS_CENTER);
1288 
1289 	baobab.tree_view = create_directory_treeview ();
1290 
1291 	gtk_action_set_sensitive (GET_ACTION ("menurescan"), FALSE);
1292 
1293 	/* set allocated space checkbox */
1294 	gtk_toggle_action_set_active (GET_TOGGLE_ACTION ("ck_allocated"),
1295 				      baobab.show_allocated);
1296 
1297 	gtk_widget_show (baobab.window);
1298 
1299 	first_row ();
1300 	baobab_set_statusbar (_("Ready"));
1301 
1302 	initialize_charts ();
1303 
1304 	/* commandline */
1305 	if (directories && directories[0]) {
1306 		GFile *file;
1307 
1308 		file = g_file_new_for_commandline_arg (directories[0]);
1309 
1310 		/* start processing the dir specified on the
1311 		 * command line as soon as we enter the main loop */
1312 		g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
1313 		                 (GSourceFunc) start_proc_on_command_line,
1314 		                 file,
1315 		                 g_object_unref);
1316 	}
1317 	g_strfreev (directories);
1318 
1319 	gtk_main ();
1320 
1321  closing:
1322 	baobab_shutdown ();
1323 
1324 	glibtop_close ();
1325 
1326 	return 0;
1327 }
1328