diff options
Diffstat (limited to 'baobab/src/baobab.c')
-rw-r--r-- | baobab/src/baobab.c | 1317 |
1 files changed, 1317 insertions, 0 deletions
diff --git a/baobab/src/baobab.c b/baobab/src/baobab.c new file mode 100644 index 00000000..063731d2 --- /dev/null +++ b/baobab/src/baobab.c @@ -0,0 +1,1317 @@ +/* + * baobab.c + * This file is part of baobab + * + * Copyright (C) 2005-2006 Fabio Marzocca <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include <gtk/gtk.h> +#include <glib/gi18n.h> +#include <gio/gio.h> +#include <mateconf/mateconf-client.h> +#include <glibtop.h> + +#include "baobab.h" +#include "baobab-scan.h" +#include "baobab-treeview.h" +#include "baobab-utils.h" +#include "callbacks.h" +#include "baobab-prefs.h" + +#include "baobab-treemap.h" +#include "baobab-ringschart.h" + +static void push_iter_in_stack (GtkTreeIter *); +static GtkTreeIter pop_iter_from_stack (void); + +static gint currentdepth = 0; +static GtkTreeIter currentiter; +static GtkTreeIter firstiter; +static GQueue *iterstack = NULL; + +enum { + DND_TARGET_URI_LIST +}; + +static GtkTargetEntry dnd_target_list[] = { + { "text/uri-list", 0, DND_TARGET_URI_LIST }, +}; + +static gboolean +scan_is_local (GFile *file) +{ + gchar *uri_scheme; + gboolean ret = FALSE; + + uri_scheme = g_file_get_uri_scheme (file); + if (g_ascii_strcasecmp(uri_scheme,"file") == 0) + ret = TRUE; + + g_free (uri_scheme); + + return ret; +} + +void +baobab_set_busy (gboolean busy) +{ + static GdkCursor *busy_cursor = NULL; + GdkCursor *cursor = NULL; + GdkWindow *window; + + if (busy == TRUE) { + if (!busy_cursor) { + busy_cursor = gdk_cursor_new (GDK_WATCH); + } + cursor = busy_cursor; + + gtk_widget_show (baobab.spinner); + gtk_spinner_start (GTK_SPINNER (baobab.spinner)); + + baobab_chart_freeze_updates (baobab.rings_chart); + + baobab_chart_freeze_updates (baobab.treemap_chart); + + gtk_widget_set_sensitive (baobab.chart_type_combo, FALSE); + } + else { + gtk_widget_hide (baobab.spinner); + gtk_spinner_stop (GTK_SPINNER (baobab.spinner)); + + baobab_chart_thaw_updates (baobab.rings_chart); + baobab_chart_thaw_updates (baobab.treemap_chart); + + gtk_widget_set_sensitive (baobab.chart_type_combo, TRUE); + } + + /* change the cursor */ + window = gtk_widget_get_window (baobab.window); + if (window) { + gdk_window_set_cursor (window, cursor); + } +} + +static void +set_drop_target (GtkWidget *target, gboolean active) { + if (active) { + gtk_drag_dest_set (GTK_WIDGET (target), + GTK_DEST_DEFAULT_DROP | GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT, + dnd_target_list, + G_N_ELEMENTS (dnd_target_list), + GDK_ACTION_COPY); + } else { + gtk_drag_dest_unset (target); + } +} + +/* menu & toolbar sensitivity */ +static void +check_menu_sens (gboolean scanning) +{ + gboolean has_current_location; + + if (scanning) { + while (gtk_events_pending ()) + gtk_main_iteration (); + + baobab_set_statusbar (_("Scanning...")); + set_ui_action_sens ("expand_all", TRUE); + set_ui_action_sens ("collapse_all", TRUE); + } + + has_current_location = baobab.current_location != NULL; + + set_ui_action_sens ("menuscanhome", !scanning); + set_ui_action_sens ("menuallfs", !scanning); + set_ui_action_sens ("menuscandir", !scanning); + set_ui_action_sens ("menustop", scanning); + set_ui_action_sens ("menurescan", !scanning && has_current_location); + set_ui_action_sens ("preferenze1", !scanning); + set_ui_action_sens ("menu_scan_rem", !scanning); + set_ui_action_sens ("ck_allocated", !scanning && baobab.is_local); + + set_ui_widget_sens ("tbscanhome", !scanning); + set_ui_widget_sens ("tbscanall", !scanning); + set_ui_widget_sens ("tbscandir", !scanning); + set_ui_widget_sens ("tbstop", scanning); + set_ui_widget_sens ("tbrescan", !scanning && has_current_location); + set_ui_widget_sens ("tb_scan_remote", !scanning); +} + +static void +check_drop_targets (gboolean scanning) +{ + set_drop_target (baobab.rings_chart, !scanning); + set_drop_target (baobab.treemap_chart, !scanning); +} + +static void +update_scan_label (void) +{ + gchar *markup; + gchar *total; + gchar *used; + gchar *available; + GtkWidget *label; + + total = g_format_size_for_display (baobab.fs.total); + used = g_format_size_for_display (baobab.fs.used); + available = g_format_size_for_display (baobab.fs.avail); + + /* Translators: these are labels for disk space */ + markup = g_markup_printf_escaped ("<small>%s <b>%s</b> (%s %s %s %s )</small>", + _("Total filesystem capacity:"), total, + _("used:"), used, + _("available:"), available); + + g_free (total); + g_free (used); + g_free (available); + + label = GTK_WIDGET (gtk_builder_get_object (baobab.main_ui, "label1")); + + gtk_label_set_markup (GTK_LABEL (label), markup); +} + +void +baobab_update_filesystem (void) +{ + baobab_get_filesystem (&baobab.fs); + update_scan_label (); +} + +void +baobab_scan_location (GFile *file) +{ + GtkToggleAction *ck_allocated; + + if (!baobab_check_dir (file)) + return; + + if (iterstack !=NULL) + return; + + if (baobab.current_location) + g_object_unref (baobab.current_location); + baobab.current_location = g_object_ref (file); + + baobab.STOP_SCANNING = FALSE; + baobab_set_busy (TRUE); + check_menu_sens (TRUE); + check_drop_targets (TRUE); + gtk_tree_store_clear (baobab.model); + currentdepth = -1; /* flag */ + iterstack = g_queue_new (); + + /* check if the file system is local or remote */ + baobab.is_local = scan_is_local (file); + ck_allocated = GTK_TOGGLE_ACTION (gtk_builder_get_object (baobab.main_ui, "ck_allocated")); + if (!baobab.is_local) { + gtk_toggle_action_set_active (ck_allocated, FALSE); + gtk_action_set_sensitive (GTK_ACTION (ck_allocated), FALSE); + baobab.show_allocated = FALSE; + } + else { + gtk_action_set_sensitive (GTK_ACTION (ck_allocated), TRUE); + } + + baobab_scan_execute (file); + + /* set statusbar, percentage and allocated/normal size */ + baobab_set_statusbar (_("Calculating percentage bars...")); + gtk_tree_model_foreach (GTK_TREE_MODEL (baobab.model), + show_bars, + NULL); + + baobab_chart_set_max_depth (baobab.rings_chart, baobab.model_max_depth); + baobab_chart_set_max_depth (baobab.treemap_chart, baobab.model_max_depth); + + baobab_set_busy (FALSE); + check_menu_sens (FALSE); + check_drop_targets (FALSE); + baobab_set_statusbar (_("Ready")); + + gtk_tree_view_columns_autosize (GTK_TREE_VIEW (baobab.tree_view)); + baobab.STOP_SCANNING = TRUE; + g_queue_free (iterstack); + iterstack = NULL; + baobab.CONTENTS_CHANGED_DELAYED = FALSE; +} + +void +baobab_scan_home (void) +{ + GFile *file; + + file = g_file_new_for_path (g_get_home_dir ()); + baobab_scan_location (file); + g_object_unref (file); +} + +void +baobab_scan_root (void) +{ + GFile *file; + + file = g_file_new_for_uri ("file:///"); + baobab_scan_location (file); + g_object_unref (file); +} + +void +baobab_rescan_current_dir (void) +{ + g_return_if_fail (baobab.current_location != NULL); + + baobab_update_filesystem (); + + g_object_ref (baobab.current_location); + baobab_scan_location (baobab.current_location); + g_object_unref (baobab.current_location); +} + +void +baobab_stop_scan (void) +{ + baobab.STOP_SCANNING = TRUE; + + baobab_set_statusbar (_("Calculating percentage bars...")); + gtk_tree_model_foreach (GTK_TREE_MODEL (baobab.model), + show_bars, NULL); + gtk_tree_view_columns_autosize (GTK_TREE_VIEW (baobab.tree_view)); +} + +/* + * pre-fills model during scanning + */ +static void +prefill_model (struct chan_data *data) +{ + GtkTreeIter iter, iterparent; + char *name; + char *str; + + if (currentdepth == -1) { + gtk_tree_store_append (baobab.model, &iter, NULL); + firstiter = iter; + } + else if (data->depth == 1) { + GtkTreePath *path; + + gtk_tree_store_append (baobab.model, &iter, &firstiter); + path = gtk_tree_model_get_path (GTK_TREE_MODEL (baobab.model), + &firstiter); + gtk_tree_view_expand_row (GTK_TREE_VIEW (baobab.tree_view), + path, FALSE); + gtk_tree_path_free (path); + } + else if (data->depth > currentdepth) { + gtk_tree_store_append (baobab.model, &iter, ¤titer); + } + else if (data->depth == currentdepth) { + gtk_tree_model_iter_parent ((GtkTreeModel *) baobab.model, + &iterparent, ¤titer); + gtk_tree_store_append (baobab.model, &iter, &iterparent); + } + else if (data->depth < currentdepth) { + GtkTreeIter tempiter; + gint i; + iter = currentiter; + for (i = 0; i <= (currentdepth - data->depth); i++) { + gtk_tree_model_iter_parent ((GtkTreeModel *) + baobab.model, + &tempiter, &iter); + iter = tempiter; + } + gtk_tree_store_append (baobab.model, &iter, &tempiter); + } + + currentdepth = data->depth; + push_iter_in_stack (&iter); + currentiter = iter; + + /* in case filenames contains gmarkup */ + name = g_markup_escape_text (data->display_name, -1); + + str = g_strdup_printf ("<small><i>%s</i></small>", _("Scanning...")); + + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (baobab.tree_view), TRUE); + gtk_tree_store_set (baobab.model, &iter, + COL_DIR_NAME, name, + COL_H_PARSENAME, "", + COL_H_ELEMENTS, -1, + COL_H_PERC, -1.0, + COL_DIR_SIZE, str, + COL_ELEMENTS, str, -1); + + g_free (name); + g_free (str); + + while (gtk_events_pending ()) { + gtk_main_iteration (); + } +} + +static void +first_row (void) +{ + char *size; + gdouble perc; + char *label; + + GtkTreeIter root_iter; + + gchar *capacity_label, *capacity_size; + + gtk_tree_store_append (baobab.model, &root_iter, NULL); + capacity_size = g_format_size_for_display (baobab.fs.total); + + capacity_label = g_strdup (_("Total filesystem capacity")); + gtk_tree_store_set (baobab.model, &root_iter, + COL_DIR_NAME, capacity_label, + COL_H_PARSENAME, "", + COL_H_PERC, 100.0, + COL_DIR_SIZE, capacity_size, + COL_H_SIZE, baobab.fs.total, + COL_H_ALLOCSIZE, baobab.fs.total, + COL_H_ELEMENTS, -1, -1); + g_free (capacity_label); + g_free (capacity_size); + + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (baobab.tree_view), FALSE); + gtk_tree_store_append (baobab.model, &firstiter, &root_iter); + size = g_format_size_for_display (baobab.fs.used); + if (baobab.fs.total == 0 && baobab.fs.used == 0) { + perc = 100.0; + } else { + g_assert (baobab.fs.total != 0); + perc = ((gdouble) baobab.fs.used * 100) / (gdouble) baobab.fs.total; + } + + label = g_strdup (_("Total filesystem usage")); + gtk_tree_store_set (baobab.model, &firstiter, + COL_DIR_NAME, label, + COL_H_PARSENAME, "", + COL_H_PERC, perc, + COL_DIR_SIZE, size, + COL_H_SIZE, baobab.fs.used, + COL_H_ALLOCSIZE, baobab.fs.used, + COL_H_ELEMENTS, -1, -1); + + g_free (size); + g_free (label); + + gtk_tree_view_expand_all (GTK_TREE_VIEW (baobab.tree_view)); +} + +/* fills model during scanning */ +void +baobab_fill_model (struct chan_data *data) +{ + GtkTreeIter iter; + GString *hardlinks; + GString *elements; + char *name; + char *size; + char *alloc_size; + + if (data->elements == -1) { + prefill_model (data); + return; + } + + iter = pop_iter_from_stack (); + + /* in case filenames contains gmarkup */ + name = g_markup_escape_text (data->display_name, -1); + + hardlinks = g_string_new (""); + if (data->tempHLsize > 0) { + size = g_format_size_for_display (data->tempHLsize); + g_string_assign (hardlinks, "<i>("); + g_string_append (hardlinks, _("contains hardlinks for:")); + g_string_append (hardlinks, " "); + g_string_append (hardlinks, size); + g_string_append (hardlinks, ")</i>"); + g_free (size); + } + + elements = g_string_new (""); + g_string_printf (elements, + ngettext ("%5d item", "%5d items", + data->elements), data->elements); + + size = g_format_size_for_display (data->size); + alloc_size = g_format_size_for_display (data->alloc_size); + + gtk_tree_store_set (baobab.model, &iter, + COL_DIR_NAME, name, + COL_H_PARSENAME, data->parse_name, + COL_H_PERC, -1.0, + COL_DIR_SIZE, + baobab.show_allocated ? alloc_size : size, + COL_H_SIZE, data->size, + COL_ELEMENTS, elements->str, + COL_H_ELEMENTS, data->elements, + COL_HARDLINK, hardlinks->str, + COL_H_HARDLINK, data->tempHLsize, + COL_H_ALLOCSIZE, data->alloc_size, -1); + + while (gtk_events_pending ()) { + gtk_main_iteration (); + } + + g_string_free (hardlinks, TRUE); + g_string_free (elements, TRUE); + g_free (name); + g_free (size); + g_free (alloc_size); +} + +void +push_iter_in_stack (GtkTreeIter *iter) +{ + g_queue_push_head (iterstack, iter->user_data3); + g_queue_push_head (iterstack, iter->user_data2); + g_queue_push_head (iterstack, iter->user_data); + g_queue_push_head (iterstack, GINT_TO_POINTER (iter->stamp)); +} + +GtkTreeIter +pop_iter_from_stack (void) +{ + GtkTreeIter iter; + + iter.stamp = GPOINTER_TO_INT (g_queue_pop_head (iterstack)); + iter.user_data = g_queue_pop_head (iterstack); + iter.user_data2 = g_queue_pop_head (iterstack); + iter.user_data3 = g_queue_pop_head (iterstack); + + return iter; +} + +gboolean +baobab_is_excluded_location (GFile *file) +{ + gboolean ret = FALSE; + GSList *l; + + g_return_val_if_fail (file != NULL, FALSE); + + for (l = baobab.excluded_locations; l != NULL; l = l->next) { + if (g_file_equal (l->data, file)) { + ret = TRUE; + break; + } + } + + return ret; +} + +static void +volume_changed (GVolumeMonitor *volume_monitor, + GVolume *volume, + gpointer user_data) +{ + /* filesystem has changed (mounted or unmounted device) */ + baobab_update_filesystem (); +} + +static void +home_contents_changed (GFileMonitor *file_monitor, + GFile *child, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data) + +{ + gchar *excluding; + + if (baobab.CONTENTS_CHANGED_DELAYED) + return; + + excluding = g_file_get_basename (child); + if (strcmp (excluding, ".recently-used") == 0 || + strcmp (excluding, ".mate2_private") == 0 || + strcmp (excluding, ".xsession-errors") == 0 || + strcmp (excluding, ".bash_history") == 0 || + strcmp (excluding, ".mateconfd") == 0) { + g_free (excluding); + return; + } + g_free (excluding); + + baobab.CONTENTS_CHANGED_DELAYED = TRUE; +} + +static void +monitor_volume (void) +{ + baobab.monitor_vol = g_volume_monitor_get (); + + g_signal_connect (baobab.monitor_vol, "volume_changed", + G_CALLBACK (volume_changed), NULL); +} + +static void +monitor_home (gboolean enable) +{ + if (enable && baobab.monitor_home == NULL) { + GFile *file; + GError *error = NULL; + + file = g_file_new_for_path (g_get_home_dir ()); + baobab.monitor_home = g_file_monitor_directory (file, 0, NULL, &error); + g_object_unref (file); + + if (!baobab.monitor_home) { + message (_("Could not initialize monitoring"), + _("Changes to your home folder will not be monitored."), + GTK_MESSAGE_WARNING, NULL); + g_print ("homedir:%s\n", error->message); + g_error_free (error); + } + else { + g_signal_connect (baobab.monitor_home, + "changed", + G_CALLBACK (home_contents_changed), + NULL); + } + } + else if (!enable && baobab.monitor_home != NULL) { + g_file_monitor_cancel (baobab.monitor_home); + g_object_unref (baobab.monitor_home); + baobab.monitor_home = NULL; + } +} + +void +baobab_set_toolbar_visible (gboolean visible) +{ + GtkToggleAction *action; + + if (visible) + gtk_widget_show (baobab.toolbar); + else + gtk_widget_hide (baobab.toolbar); + + /* make sure the check menu item is consistent */ + action = GTK_TOGGLE_ACTION (gtk_builder_get_object (baobab.main_ui, "view_tb")); + gtk_toggle_action_set_active (action, visible); +} + +void +baobab_set_statusbar_visible (gboolean visible) +{ + GtkToggleAction *action; + + if (visible) + gtk_widget_show (baobab.statusbar); + else + gtk_widget_hide (baobab.statusbar); + + /* make sure the check menu item is consistent */ + action = GTK_TOGGLE_ACTION (gtk_builder_get_object (baobab.main_ui, "view_sb")); + gtk_toggle_action_set_active (action, visible); +} + +void +baobab_set_statusbar (const gchar *text) +{ + gtk_statusbar_pop (GTK_STATUSBAR (baobab.statusbar), 1); + gtk_statusbar_push (GTK_STATUSBAR (baobab.statusbar), 1, text); + + while (gtk_events_pending ()) + gtk_main_iteration (); +} + +static void +toolbar_reconfigured_cb (GtkToolItem *item, + GtkWidget *spinner) +{ + GtkToolbarStyle style; + GtkIconSize size; + + style = gtk_tool_item_get_toolbar_style (item); + + if (style == GTK_TOOLBAR_BOTH) + { + size = GTK_ICON_SIZE_DIALOG; + } + else + { + size = GTK_ICON_SIZE_LARGE_TOOLBAR; + } + + gtk_widget_set_size_request (spinner, size, size); +} + +static void +baobab_toolbar_style (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + gchar *toolbar_setting; + + toolbar_setting = baobab_mateconf_get_string_with_default (baobab.mateconf_client, + SYSTEM_TOOLBAR_STYLE_KEY, + "both"); + + if (!strcmp(toolbar_setting, "icons")) { + gtk_toolbar_set_style (GTK_TOOLBAR(baobab.toolbar), + GTK_TOOLBAR_ICONS); + } + else if (!strcmp(toolbar_setting, "both")) { + gtk_toolbar_set_style (GTK_TOOLBAR(baobab.toolbar), + GTK_TOOLBAR_BOTH); + } + else if (!strcmp(toolbar_setting, "both-horiz")) { + gtk_toolbar_set_style (GTK_TOOLBAR(baobab.toolbar), + GTK_TOOLBAR_BOTH_HORIZ); + } + else if (!strcmp(toolbar_setting, "text")) { + gtk_toolbar_set_style (GTK_TOOLBAR(baobab.toolbar), + GTK_TOOLBAR_TEXT); + } + + g_free (toolbar_setting); +} + +static void +baobab_create_toolbar (void) +{ + GtkWidget *toolbar; + GtkToolItem *item; + GtkToolItem *separator; + gboolean visible; + + toolbar = GTK_WIDGET (gtk_builder_get_object (baobab.main_ui, "toolbar1")); + if (toolbar == NULL) { + g_printerr ("Could not build toolbar\n"); + return; + } + + baobab.toolbar = toolbar; + + separator = gtk_separator_tool_item_new (); + gtk_separator_tool_item_set_draw (GTK_SEPARATOR_TOOL_ITEM (separator), FALSE); + gtk_tool_item_set_expand (GTK_TOOL_ITEM (separator), TRUE); + gtk_container_add (GTK_CONTAINER (toolbar), GTK_WIDGET (separator)); + gtk_widget_show (GTK_WIDGET (separator)); + + baobab.spinner = gtk_spinner_new (); + item = gtk_tool_item_new (); + gtk_container_add (GTK_CONTAINER (item), baobab.spinner); + gtk_container_add (GTK_CONTAINER (toolbar), GTK_WIDGET (item)); + gtk_widget_show (GTK_WIDGET (item)); + + g_signal_connect (item, "toolbar-reconfigured", + G_CALLBACK (toolbar_reconfigured_cb), baobab.spinner); + toolbar_reconfigured_cb (item, baobab.spinner); + + baobab_toolbar_style (NULL, 0, NULL, NULL); + + visible = mateconf_client_get_bool (baobab.mateconf_client, + BAOBAB_TOOLBAR_VISIBLE_KEY, + NULL); + + baobab_set_toolbar_visible (visible); +} + +static void +baobab_create_statusbar (void) +{ + gboolean visible; + + baobab.statusbar = GTK_WIDGET (gtk_builder_get_object (baobab.main_ui, + "statusbar1")); + if (baobab.statusbar == NULL) { + g_printerr ("Could not build statusbar\n"); + return; + } + + visible = mateconf_client_get_bool (baobab.mateconf_client, + BAOBAB_STATUSBAR_VISIBLE_KEY, + NULL); + + baobab_set_statusbar_visible (visible); +} + +static void +baobab_subfolderstips_toggled (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + baobab_ringschart_set_subfoldertips_enabled (baobab.rings_chart, + mateconf_client_get_bool (baobab.mateconf_client, + BAOBAB_SUBFLSTIPS_VISIBLE_KEY, + NULL)); +} + +static void +baobab_set_excluded_locations (GSList *excluded_uris) +{ + GSList *l; + + g_slist_foreach (baobab.excluded_locations, (GFunc) g_object_unref, NULL); + g_slist_free (baobab.excluded_locations); + baobab.excluded_locations = NULL; + for (l = excluded_uris; l != NULL; l = l->next) { + baobab.excluded_locations = g_slist_prepend (baobab.excluded_locations, + g_file_new_for_uri (l->data)); + } +} + +static void +store_excluded_locations (void) +{ + GSList *l; + GSList *uri_list = NULL; + + for (l = baobab.excluded_locations; l != NULL; l = l->next) { + GSList *uri_list = NULL; + + uri_list = g_slist_prepend (uri_list, g_file_get_uri(l->data)); + } + + mateconf_client_set_list (baobab.mateconf_client, + BAOBAB_EXCLUDED_DIRS_KEY, + MATECONF_VALUE_STRING, + uri_list, + NULL); + + g_slist_foreach (uri_list, (GFunc) g_free, NULL); + g_slist_free (uri_list); +} + +static void +sanity_check_excluded_locations (void) +{ + GFile *root; + GSList *l; + + /* Verify if mateconf wrongly contains root dir exclusion, and remove it from mateconf. */ + root = g_file_new_for_uri ("file:///"); + + for (l = baobab.excluded_locations; l != NULL; l = l->next) { + if (g_file_equal (l->data, root)) { + baobab.excluded_locations = g_slist_delete_link (baobab.excluded_locations, l); + store_excluded_locations (); + break; + } + } + + g_object_unref (root); +} + +static void +excluded_locations_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + GSList *uris; + + uris = mateconf_client_get_list (client, + BAOBAB_EXCLUDED_DIRS_KEY, + MATECONF_VALUE_STRING, + NULL); + baobab_set_excluded_locations (uris); + g_slist_foreach (uris, (GFunc) g_free, NULL); + g_slist_free (uris); + + baobab_update_filesystem (); + + gtk_tree_store_clear (baobab.model); + first_row (); +} + +static void +baobab_monitor_home_toggled (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + gboolean enable; + + enable = mateconf_client_get_bool (baobab.mateconf_client, + BAOBAB_ENABLE_HOME_MONITOR_KEY, + NULL); + + monitor_home (enable); +} + +static void +baobab_init (void) +{ + GSList *uri_list; + GError *error = NULL; + gboolean enable; + + /* FileSystem usage */ + baobab_get_filesystem (&baobab.fs); + + /* Load the UI */ + baobab.main_ui = gtk_builder_new (); + gtk_builder_add_from_file (baobab.main_ui, BAOBAB_UI_FILE, &error); + + if (error) { + g_object_unref (baobab.main_ui); + g_critical ("Unable to load the user interface file: %s", error->message); + g_error_free (error); + exit (1); + } + + gtk_builder_connect_signals (baobab.main_ui, NULL); + + /* Misc */ + baobab.CONTENTS_CHANGED_DELAYED = FALSE; + baobab.STOP_SCANNING = TRUE; + baobab.show_allocated = TRUE; + baobab.is_local = TRUE; + + /* MateConf */ + baobab.mateconf_client = mateconf_client_get_default (); + mateconf_client_add_dir (baobab.mateconf_client, BAOBAB_KEY_DIR, + MATECONF_CLIENT_PRELOAD_NONE, NULL); + mateconf_client_notify_add (baobab.mateconf_client, BAOBAB_EXCLUDED_DIRS_KEY, excluded_locations_changed, + NULL, NULL, NULL); + mateconf_client_notify_add (baobab.mateconf_client, SYSTEM_TOOLBAR_STYLE_KEY, baobab_toolbar_style, + NULL, NULL, NULL); + mateconf_client_notify_add (baobab.mateconf_client, BAOBAB_SUBFLSTIPS_VISIBLE_KEY, baobab_subfolderstips_toggled, + NULL, NULL, NULL); + mateconf_client_notify_add (baobab.mateconf_client, BAOBAB_ENABLE_HOME_MONITOR_KEY, baobab_monitor_home_toggled, + NULL, NULL, NULL); + + uri_list = mateconf_client_get_list (baobab.mateconf_client, + BAOBAB_EXCLUDED_DIRS_KEY, + MATECONF_VALUE_STRING, + NULL); + + baobab_set_excluded_locations (uri_list); + + g_slist_foreach (uri_list, (GFunc) g_free, NULL); + g_slist_free (uri_list); + + sanity_check_excluded_locations (); + + baobab_create_toolbar (); + + baobab_create_statusbar (); + + monitor_volume (); + + enable = mateconf_client_get_bool (baobab.mateconf_client, + BAOBAB_ENABLE_HOME_MONITOR_KEY, + NULL); + + monitor_home (enable); +} + +static void +baobab_shutdown (void) +{ + if (baobab.current_location) + g_object_unref (baobab.current_location); + + if (baobab.monitor_vol) { + g_object_unref (baobab.monitor_vol); + } + + if (baobab.monitor_home) { + g_file_monitor_cancel (baobab.monitor_home); + g_object_unref (baobab.monitor_home); + } + + g_free (baobab.selected_path); + + g_slist_foreach (baobab.excluded_locations, (GFunc) g_object_unref, NULL); + g_slist_free (baobab.excluded_locations); + + if (baobab.mateconf_client) { + g_object_unref (baobab.mateconf_client); + } +} + +static BaobabChartMenu * +create_context_menu (void) +{ + BaobabChartMenu *menu = NULL; + + baobab.chart_menu = g_new0 (BaobabChartMenu, 1); + menu = baobab.chart_menu; + + menu->widget = gtk_menu_new (); + + menu->up_item = gtk_image_menu_item_new_with_label (_("Move to parent folder")); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu->up_item), + gtk_image_new_from_stock(GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU)); + + menu->zoom_in_item = gtk_image_menu_item_new_with_label (_("Zoom in")) ; + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu->zoom_in_item), + gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_MENU)); + + menu->zoom_out_item = gtk_image_menu_item_new_with_label (_("Zoom out")); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu->zoom_out_item), + gtk_image_new_from_stock(GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU)); + + menu->snapshot_item = gtk_image_menu_item_new_with_label (_("Save screenshot")); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu->snapshot_item), + gtk_image_new_from_file (BAOBAB_PIX_DIR "shot.png")); + + gtk_menu_shell_append (GTK_MENU_SHELL (menu->widget), + menu->up_item); + gtk_menu_shell_append (GTK_MENU_SHELL (menu->widget), + gtk_separator_menu_item_new ()); + gtk_menu_shell_append (GTK_MENU_SHELL (menu->widget), + menu->zoom_in_item); + gtk_menu_shell_append (GTK_MENU_SHELL (menu->widget), + menu->zoom_out_item); + gtk_menu_shell_append (GTK_MENU_SHELL (menu->widget), + gtk_separator_menu_item_new ()); + gtk_menu_shell_append (GTK_MENU_SHELL (menu->widget), + menu->snapshot_item); + + /* connect signals */ + g_signal_connect (menu->up_item, "activate", + G_CALLBACK (on_move_upwards_cb), NULL); + g_signal_connect (menu->zoom_in_item, "activate", + G_CALLBACK (on_zoom_in_cb), NULL); + g_signal_connect (menu->zoom_out_item, "activate", + G_CALLBACK (on_zoom_out_cb), NULL); + g_signal_connect (menu->snapshot_item, "activate", + G_CALLBACK (on_chart_snapshot_cb), NULL); + + gtk_widget_show_all (menu->widget); + + return menu; +} + +static void +on_chart_item_activated (BaobabChart *chart, GtkTreeIter *iter) +{ + GtkTreePath *path; + + path = gtk_tree_model_get_path (GTK_TREE_MODEL (baobab.model), iter); + + if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (baobab.tree_view), path)) + gtk_tree_view_expand_to_path (GTK_TREE_VIEW (baobab.tree_view), path); + + gtk_tree_view_set_cursor (GTK_TREE_VIEW (baobab.tree_view), + path, NULL, FALSE); + gtk_tree_path_free (path); +} + +static gboolean +on_chart_button_release (BaobabChart *chart, + GdkEventButton *event, + gpointer data) +{ + if (baobab_chart_is_frozen (baobab.current_chart)) + return FALSE; + + if (event->button== 3) /* right button */ + { + GtkTreePath *root_path; + BaobabChartMenu *menu; + + root_path = baobab_chart_get_root (baobab.current_chart); + + menu = baobab.chart_menu; + gtk_widget_set_sensitive (menu->up_item, + ((root_path != NULL) && + (gtk_tree_path_get_depth (root_path) > 1))); + gtk_widget_set_sensitive (menu->zoom_in_item, + baobab_chart_can_zoom_in (baobab.current_chart)); + gtk_widget_set_sensitive (menu->zoom_out_item, + baobab_chart_can_zoom_out (baobab.current_chart)); + + /* show the menu */ + gtk_menu_popup (GTK_MENU (menu->widget), + NULL, NULL, NULL, NULL, + event->button, event->time); + + gtk_tree_path_free (root_path); + } + + return FALSE; +} + +static void +drag_data_received_handl (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *selection_data, + guint target_type, + guint time, + gpointer data) +{ + GFile *gf = NULL; + + /* set "gf" if we got some valid data */ + if ((selection_data != NULL) && + (gtk_selection_data_get_length (selection_data) >= 0) && + (target_type == DND_TARGET_URI_LIST)) { + gchar **uri_list; + uri_list = g_uri_list_extract_uris ((const gchar *) gtk_selection_data_get_data (selection_data)); + /* check list is 1 item long */ + if (uri_list != NULL && uri_list[0] != NULL && uri_list[1] == NULL) { + gf = g_file_new_for_uri (uri_list[0]); + } + g_strfreev (uri_list); + } + + /* success if "gf" has been set */ + if (gf != NULL) { + /* finish drop before beginning scan, as the drag-drop can + probably time out */ + gtk_drag_finish (context, TRUE, FALSE, time); + baobab_scan_location (gf); + g_object_unref (gf); + } else { + gtk_drag_finish (context, FALSE, FALSE, time); + } +} + +static void +initialize_charts (void) +{ + GtkWidget *hpaned_main; + GtkWidget *chart_frame; + GtkWidget *hbox1; + + chart_frame = gtk_frame_new (NULL); + gtk_frame_set_label_align (GTK_FRAME (chart_frame), 0.0, 0.0); + gtk_frame_set_shadow_type (GTK_FRAME (chart_frame), GTK_SHADOW_IN); + + hpaned_main = GTK_WIDGET (gtk_builder_get_object (baobab.main_ui, "hpaned_main")); + gtk_paned_pack2 (GTK_PANED (hpaned_main), + chart_frame, TRUE, TRUE); + gtk_paned_set_position (GTK_PANED (hpaned_main), 480); + + baobab.chart_type_combo = gtk_combo_box_new_text (); + gtk_combo_box_append_text (GTK_COMBO_BOX (baobab.chart_type_combo), + _("View as Rings Chart")); + gtk_combo_box_append_text (GTK_COMBO_BOX (baobab.chart_type_combo), + _("View as Treemap Chart")); + gtk_combo_box_set_active (GTK_COMBO_BOX (baobab.chart_type_combo), 0); + gtk_widget_show (baobab.chart_type_combo); + g_signal_connect (baobab.chart_type_combo, + "changed", + G_CALLBACK (on_chart_type_change), NULL); + + hbox1 = GTK_WIDGET (gtk_builder_get_object (baobab.main_ui, "hbox1")); + gtk_container_add (GTK_CONTAINER (hbox1), baobab.chart_type_combo); + gtk_box_set_spacing (GTK_BOX (hbox1), 50); + gtk_box_set_child_packing (GTK_BOX (hbox1), + baobab.chart_type_combo, + FALSE, + TRUE, + 0, GTK_PACK_END); + + baobab.chart_menu = create_context_menu (); + + /* Baobab's Treemap Chart */ + baobab.treemap_chart = baobab_treemap_new (); + baobab_chart_set_model_with_columns (baobab.treemap_chart, + GTK_TREE_MODEL (baobab.model), + COL_DIR_NAME, + COL_DIR_SIZE, + COL_H_PARSENAME, + COL_H_PERC, + COL_H_ELEMENTS, + NULL); + baobab_chart_set_max_depth (baobab.treemap_chart, 1); + g_signal_connect (baobab.treemap_chart, "item_activated", + G_CALLBACK (on_chart_item_activated), NULL); + g_signal_connect (baobab.treemap_chart, "button-release-event", + G_CALLBACK (on_chart_button_release), NULL); + g_signal_connect (baobab.treemap_chart, "drag-data-received", + G_CALLBACK (drag_data_received_handl), NULL); + gtk_widget_show (baobab.treemap_chart); + /* Ends Baobab's Treemap Chart */ + + /* Baobab's Rings Chart */ + baobab.rings_chart = (GtkWidget *) baobab_ringschart_new (); + baobab_chart_set_model_with_columns (baobab.rings_chart, + GTK_TREE_MODEL (baobab.model), + COL_DIR_NAME, + COL_DIR_SIZE, + COL_H_PARSENAME, + COL_H_PERC, + COL_H_ELEMENTS, + NULL); + baobab_ringschart_set_subfoldertips_enabled (baobab.rings_chart, + mateconf_client_get_bool (baobab.mateconf_client, + BAOBAB_SUBFLSTIPS_VISIBLE_KEY, + NULL)); + baobab_chart_set_max_depth (baobab.rings_chart, 1); + g_signal_connect (baobab.rings_chart, "item_activated", + G_CALLBACK (on_chart_item_activated), NULL); + g_signal_connect (baobab.rings_chart, "button-release-event", + G_CALLBACK (on_chart_button_release), NULL); + g_signal_connect (baobab.rings_chart, "drag-data-received", + G_CALLBACK (drag_data_received_handl), NULL); + gtk_widget_show (baobab.rings_chart); + /* Ends Baobab's Treemap Chart */ + + baobab.current_chart = baobab.rings_chart; + + g_object_ref_sink (baobab.treemap_chart); + baobab_chart_freeze_updates (baobab.treemap_chart); + + gtk_container_add (GTK_CONTAINER (chart_frame), + baobab.current_chart); + gtk_widget_show_all (chart_frame); + + check_drop_targets (FALSE); +} + +static gboolean +start_proc_on_command_line (GFile *file) +{ + baobab_scan_location (file); + + return FALSE; +} + +static gboolean +show_version (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + g_print("%s %s\n", g_get_application_name (), VERSION); + exit (0); + return TRUE; /* It's just good form */ +} + +int +main (int argc, char *argv[]) +{ + gchar **directories = NULL; + const GOptionEntry options[] = { + {"version", 'V', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, show_version, N_("Show version"), NULL}, + {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &directories, NULL, N_("[DIRECTORY]")}, + {NULL} + }; + GOptionContext *context; + GError *error = NULL; + + bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + g_set_application_name ("Baobab"); + + context = g_option_context_new (NULL); + g_option_context_set_ignore_unknown_options (context, FALSE); + g_option_context_set_help_enabled (context, TRUE); + g_option_context_add_main_entries(context, options, GETTEXT_PACKAGE); + g_option_context_add_group (context, gtk_get_option_group (TRUE)); + + g_option_context_parse (context, &argc, &argv, &error); + + if (error) { + g_critical ("Unable to parse option: %s", error->message); + g_error_free (error); + g_option_context_free (context); + + exit (1); + } + g_option_context_free (context); + + if (directories && directories[0] && directories[1]) { + g_critical (_("Too many arguments. Only one directory can be specified.")); + exit (1); + } + + glibtop_init (); + + gtk_window_set_default_icon_name ("baobab"); + + baobab_init (); + + if (baobab.fs.total == 0) { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (NULL, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + _("Could not detect any mount point.")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG(dialog), + _("Without mount points disk usage cannot be analyzed.")); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + goto closing; + } + + check_menu_sens (FALSE); + update_scan_label (); + + baobab.window = GTK_WIDGET (gtk_builder_get_object (baobab.main_ui, "baobab_window")); + gtk_window_set_position (GTK_WINDOW (baobab.window), + GTK_WIN_POS_CENTER); + + baobab.tree_view = create_directory_treeview (); + + set_ui_action_sens ("menurescan", FALSE); + + /* set allocated space checkbox */ + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (gtk_builder_get_object (baobab.main_ui, + "ck_allocated")), + baobab.show_allocated); + + gtk_widget_show (baobab.window); + + first_row (); + baobab_set_statusbar (_("Ready")); + + /* The ringschart */ + initialize_charts (); + + /* commandline */ + if (directories && directories[0]) { + GFile *file; + + file = g_file_new_for_commandline_arg (directories[0]); + + /* start processing the dir specified on the + * command line as soon as we enter the main loop */ + g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, + (GSourceFunc) start_proc_on_command_line, + file, + (GDestroyNotify) g_object_unref); + } + g_strfreev (directories); + + gtk_main (); + + closing: + baobab_shutdown (); + + glibtop_close (); + + return 0; +} |