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, ¤titer);
332 }
333 else if (data->depth == currentdepth) {
334 gtk_tree_model_iter_parent ((GtkTreeModel *) baobab.model,
335 &iterparent, ¤titer);
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