summaryrefslogtreecommitdiff
path: root/baobab/src
diff options
context:
space:
mode:
authorStefano Karapetsas <[email protected]>2011-12-14 10:13:54 +0100
committerStefano Karapetsas <[email protected]>2011-12-14 10:13:54 +0100
commitef0467789bfc8406b57ba553e4d59f4d6c3f9be8 (patch)
tree09d541636a16cb38448fe6183289ebdc3080c1bf /baobab/src
downloadmate-utils-ef0467789bfc8406b57ba553e4d59f4d6c3f9be8.tar.bz2
mate-utils-ef0467789bfc8406b57ba553e4d59f4d6c3f9be8.tar.xz
Moved from Mate-Extra repository
Diffstat (limited to 'baobab/src')
-rw-r--r--baobab/src/Makefile.am49
-rw-r--r--baobab/src/baobab-cell-renderer-progress.c265
-rw-r--r--baobab/src/baobab-cell-renderer-progress.h57
-rw-r--r--baobab/src/baobab-chart.c1876
-rw-r--r--baobab/src/baobab-chart.h159
-rw-r--r--baobab/src/baobab-prefs.c338
-rw-r--r--baobab/src/baobab-prefs.h28
-rw-r--r--baobab/src/baobab-remote-connect-dialog.c803
-rw-r--r--baobab/src/baobab-remote-connect-dialog.h59
-rw-r--r--baobab/src/baobab-ringschart.c675
-rw-r--r--baobab/src/baobab-ringschart.h72
-rw-r--r--baobab/src/baobab-scan.c381
-rw-r--r--baobab/src/baobab-scan.h30
-rw-r--r--baobab/src/baobab-treemap.c342
-rw-r--r--baobab/src/baobab-treemap.h64
-rw-r--r--baobab/src/baobab-treeview.c336
-rw-r--r--baobab/src/baobab-treeview.h47
-rw-r--r--baobab/src/baobab-utils.c567
-rw-r--r--baobab/src/baobab-utils.h49
-rw-r--r--baobab/src/baobab.c1317
-rw-r--r--baobab/src/baobab.h128
-rw-r--r--baobab/src/callbacks.c394
-rw-r--r--baobab/src/callbacks.h62
23 files changed, 8098 insertions, 0 deletions
diff --git a/baobab/src/Makefile.am b/baobab/src/Makefile.am
new file mode 100644
index 00000000..224ca126
--- /dev/null
+++ b/baobab/src/Makefile.am
@@ -0,0 +1,49 @@
+NULL =
+
+INCLUDES = \
+ -DPREFIX=\""$(prefix)"\" \
+ -DSYSCONFDIR=\""$(sysconfdir)"\" \
+ -DLIBDIR=\""$(libdir)"\" \
+ -DDATADIR=\""$(datadir)"\" \
+ -DPKGDATADIR=\""$(datadir)/mate-disk-usage-analyzer"\" \
+ -DBAOBAB_PIX_DIR=\""$(datadir)/mate-disk-usage-analyzer/pixmaps/"\" \
+ -DMATELOCALEDIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \
+ $(NULL)
+
+bin_PROGRAMS = mate-disk-usage-analyzer
+
+mate_disk_usage_analyzer_SOURCES = \
+ baobab.c \
+ baobab.h \
+ baobab-cell-renderer-progress.c \
+ baobab-cell-renderer-progress.h \
+ baobab-ringschart.c \
+ baobab-ringschart.h \
+ baobab-scan.c \
+ baobab-scan.h \
+ baobab-treeview.c \
+ baobab-treeview.h \
+ baobab-utils.c \
+ baobab-utils.h \
+ callbacks.c \
+ callbacks.h \
+ baobab-prefs.c \
+ baobab-prefs.h \
+ baobab-remote-connect-dialog.c \
+ baobab-remote-connect-dialog.h \
+ baobab-chart.c \
+ baobab-chart.h \
+ baobab-treemap.c \
+ baobab-treemap.h
+
+mate_disk_usage_analyzer_CFLAGS = \
+ $(MATE_UTILS_CFLAGS) \
+ $(LIBGTOP_CFLAGS) \
+ $(NULL)
+
+mate_disk_usage_analyzer_LDFLAGS = -export-dynamic
+mate_disk_usage_analyzer_LDADD = \
+ -lm \
+ $(MATE_UTILS_LIBS) \
+ $(LIBGTOP_LIBS) \
+ $(NULL)
diff --git a/baobab/src/baobab-cell-renderer-progress.c b/baobab/src/baobab-cell-renderer-progress.c
new file mode 100644
index 00000000..753d8b0a
--- /dev/null
+++ b/baobab/src/baobab-cell-renderer-progress.c
@@ -0,0 +1,265 @@
+/* baobab-cell-renderer-progress.c
+ *
+ * Copyright (C) 2006 Paolo Borelli
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include <stdlib.h>
+
+#include "baobab-cell-renderer-progress.h"
+
+#define BAOBAB_CELL_RENDERER_PROGRESS_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), \
+ BAOBAB_TYPE_CELL_RENDERER_PROGRESS, \
+ BaobabCellRendererProgressPrivate))
+
+enum
+{
+ PROP_0,
+ PROP_PERC
+};
+
+struct _BaobabCellRendererProgressPrivate
+{
+ double perc;
+};
+
+G_DEFINE_TYPE (BaobabCellRendererProgress, baobab_cell_renderer_progress, GTK_TYPE_CELL_RENDERER)
+
+static void
+baobab_cell_renderer_progress_init (BaobabCellRendererProgress *cellprogress)
+{
+ cellprogress->priv = BAOBAB_CELL_RENDERER_PROGRESS_GET_PRIVATE (cellprogress);
+ cellprogress->priv->perc = 0;
+
+ gtk_cell_renderer_set_padding (GTK_CELL_RENDERER (cellprogress), 4, 4);
+}
+
+GtkCellRenderer*
+baobab_cell_renderer_progress_new (void)
+{
+ return g_object_new (BAOBAB_TYPE_CELL_RENDERER_PROGRESS, NULL);
+}
+
+static void
+baobab_cell_renderer_progress_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ BaobabCellRendererProgress *cellprogress = BAOBAB_CELL_RENDERER_PROGRESS (object);
+
+ switch (param_id)
+ {
+ case PROP_PERC:
+ g_value_set_double (value, cellprogress->priv->perc);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ }
+}
+
+static void
+baobab_cell_renderer_progress_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ BaobabCellRendererProgress *cellprogress = BAOBAB_CELL_RENDERER_PROGRESS (object);
+
+ switch (param_id)
+ {
+ case PROP_PERC:
+ cellprogress->priv->perc = g_value_get_double (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ }
+}
+
+/* we simply have a fixed size */
+
+#define FIXED_WIDTH 70
+#define FIXED_HEIGHT 8
+
+static void
+baobab_cell_renderer_progress_get_size (GtkCellRenderer *cell,
+ GtkWidget *widget,
+ GdkRectangle *cell_area,
+ gint *x_offset,
+ gint *y_offset,
+ gint *width,
+ gint *height)
+{
+ gint calc_width;
+ gint calc_height;
+ gint xpad;
+ gint ypad;
+ gfloat xalign;
+ gfloat yalign;
+
+ gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
+ calc_width = (gint) xpad * 2 + FIXED_WIDTH;
+ calc_height = (gint) ypad * 2 + FIXED_HEIGHT;
+
+ if (width)
+ *width = calc_width;
+
+ if (height)
+ *height = calc_height;
+
+ if (cell_area)
+ {
+ gtk_cell_renderer_get_alignment (cell, &xalign, &yalign);
+ if (x_offset)
+ {
+ *x_offset = xalign * (cell_area->width - calc_width);
+ *x_offset = MAX (*x_offset, 0);
+ }
+
+ if (y_offset)
+ {
+ *y_offset = yalign * (cell_area->height - calc_height);
+ *y_offset = MAX (*y_offset, 0);
+ }
+ }
+}
+
+static void
+set_color_according_to_perc (cairo_t *cr, double value)
+{
+ static GdkColor red;
+ static GdkColor yellow;
+ static GdkColor green;
+ static gboolean colors_initialized = FALSE;
+
+ if (!colors_initialized)
+ {
+ /* hardcoded tango colors */
+ gdk_color_parse ("#cc0000", &red);
+ gdk_color_parse ("#edd400", &yellow);
+ gdk_color_parse ("#73d216", &green);
+
+ colors_initialized = TRUE;
+ }
+
+ if (value <= 0)
+ {
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ return;
+ }
+ else if (value <= 33.33)
+ {
+ gdk_cairo_set_source_color (cr, &green);
+ return;
+ }
+ else if (value <= 66.66)
+ {
+ gdk_cairo_set_source_color (cr, &yellow);
+ return;
+ }
+ else if (value <= 100.0)
+ {
+ gdk_cairo_set_source_color (cr, &red);
+ return;
+ }
+ else
+ g_assert_not_reached ();
+}
+
+static void
+baobab_cell_renderer_progress_render (GtkCellRenderer *cell,
+ GdkWindow *window,
+ GtkWidget *widget,
+ GdkRectangle *background_area,
+ GdkRectangle *cell_area,
+ GdkRectangle *expose_area,
+ guint flags)
+{
+ BaobabCellRendererProgress *cellprogress = BAOBAB_CELL_RENDERER_PROGRESS (cell);
+ gint x, y, w, h, perc_w;
+ gboolean is_rtl;
+ cairo_t *cr;
+ gint xpad;
+ gint ypad;
+ GtkStyle *style;
+
+ is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
+
+ cr = gdk_cairo_create (window);
+
+ gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
+
+ x = cell_area->x + xpad;
+ y = cell_area->y + ypad;
+
+ w = cell_area->width - xpad * 2;
+ h = cell_area->height - ypad * 2;
+
+ /*
+ * we always use a white bar with black
+ * border and green/yellow/red progress...
+ * I know it's not theme friendly, but we don't
+ * want a plain progress bar
+ */
+
+ cairo_rectangle (cr, x, y, w, h);
+ cairo_set_source_rgb (cr, 0, 0, 0);
+ cairo_fill (cr);
+
+ style = gtk_widget_get_style (widget);
+ x += style->xthickness;
+ y += style->ythickness;
+ w -= style->xthickness * 2;
+ h -= style->ythickness * 2;
+
+ cairo_rectangle (cr, x, y, w, h);
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_fill (cr);
+
+ perc_w = w * MAX (0, cellprogress->priv->perc) / 100;
+
+ cairo_rectangle (cr, is_rtl ? (x + w - perc_w) : x, y, perc_w, h);
+ set_color_according_to_perc (cr, cellprogress->priv->perc);
+ cairo_fill (cr);
+
+ cairo_destroy (cr);
+}
+
+static void
+baobab_cell_renderer_progress_class_init (BaobabCellRendererProgressClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
+
+ object_class->get_property = baobab_cell_renderer_progress_get_property;
+ object_class->set_property = baobab_cell_renderer_progress_set_property;
+
+ cell_class->get_size = baobab_cell_renderer_progress_get_size;
+ cell_class->render = baobab_cell_renderer_progress_render;
+
+ g_object_class_install_property (object_class,
+ PROP_PERC,
+ g_param_spec_double ("perc",
+ "percentage",
+ "precentage",
+ -1, 100, 0,
+ G_PARAM_READWRITE));
+
+ g_type_class_add_private (object_class,
+ sizeof (BaobabCellRendererProgressPrivate));
+}
diff --git a/baobab/src/baobab-cell-renderer-progress.h b/baobab/src/baobab-cell-renderer-progress.h
new file mode 100644
index 00000000..563e693a
--- /dev/null
+++ b/baobab/src/baobab-cell-renderer-progress.h
@@ -0,0 +1,57 @@
+/* baobab-cell-renderer-progress.h
+ *
+ * Copyright (C) 2006 Paolo Borelli
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __BAOBAB_CELL_RENDERER_PROGRESS_H__
+#define __BAOBAB_CELL_RENDERER_PROGRESS_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define BAOBAB_TYPE_CELL_RENDERER_PROGRESS (baobab_cell_renderer_progress_get_type ())
+#define BAOBAB_CELL_RENDERER_PROGRESS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BAOBAB_TYPE_CELL_RENDERER_PROGRESS, BaobabCellRendererProgress))
+#define BAOBAB_CELL_RENDERER_PROGRESS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), BAOBAB_TYPE_CELL_RENDERER_PROGRESS, BaobabCellRendererProgressClass))
+#define BAOBAB_IS_CELL_RENDERER_PROGRESS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BAOBAB_TYPE_CELL_RENDERER_PROGRESS))
+#define BAOBAB_IS_CELL_RENDERER_PROGRESS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), BAOBAB_TYPE_CELL_RENDERER_PROGRESS))
+#define BAOBAB_CELL_RENDERER_PROGRESS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), BAOBAB_TYPE_CELL_RENDERER_PROGRESS, BaobabCellRendererProgressClass))
+
+typedef struct _BaobabCellRendererProgress BaobabCellRendererProgress;
+typedef struct _BaobabCellRendererProgressClass BaobabCellRendererProgressClass;
+typedef struct _BaobabCellRendererProgressPrivate BaobabCellRendererProgressPrivate;
+
+struct _BaobabCellRendererProgress
+{
+ GtkCellRenderer parent_instance;
+
+ /*< private >*/
+ BaobabCellRendererProgressPrivate *priv;
+};
+
+struct _BaobabCellRendererProgressClass
+{
+ GtkCellRendererClass parent_class;
+};
+
+GType baobab_cell_renderer_progress_get_type (void) G_GNUC_CONST;
+GtkCellRenderer* baobab_cell_renderer_progress_new (void);
+
+G_END_DECLS
+
+#endif /* __BAOBAB_CELL_RENDERER_PROGRESS_H__ */
diff --git a/baobab/src/baobab-chart.c b/baobab/src/baobab-chart.c
new file mode 100644
index 00000000..dce8bfb6
--- /dev/null
+++ b/baobab/src/baobab-chart.c
@@ -0,0 +1,1876 @@
+/*
+ * baobab-chart.c
+ *
+ * Copyright (C) 2006, 2007, 2008 Igalia
+ *
+ * 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
+ *
+ * Authors:
+ * Felipe Erias <[email protected]>
+ * Pablo Santamaria <[email protected]>
+ * Jacobo Aragunde <[email protected]>
+ * Eduardo Lima <[email protected]>
+ * Mario Sanchez <[email protected]>
+ * Miguel Gomez <[email protected]>
+ * Henrique Ferreiro <[email protected]>
+ * Alejandro Pinheiro <[email protected]>
+ * Carlos Sanmartin <[email protected]>
+ * Alejandro Garcia <[email protected]>
+ */
+
+#include <config.h>
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+#include "baobab-chart.h"
+
+#define BAOBAB_CHART_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
+ BAOBAB_CHART_TYPE, \
+ BaobabChartPrivate))
+
+#define SNAPSHOT_DEF_FILENAME_FORMAT "%s-disk-usage"
+
+G_DEFINE_ABSTRACT_TYPE (BaobabChart, baobab_chart, GTK_TYPE_WIDGET);
+
+#define BAOBAB_CHART_MAX_DEPTH 8
+#define BAOBAB_CHART_MIN_DEPTH 1
+
+enum
+{
+ LEFT_BUTTON = 1,
+ MIDDLE_BUTTON = 2,
+ RIGHT_BUTTON = 3
+};
+
+struct _BaobabChartPrivate
+{
+ guint name_column;
+ guint size_column;
+ guint info_column;
+ guint percentage_column;
+ guint valid_column;
+ gboolean button_pressed;
+ gboolean is_frozen;
+ cairo_surface_t *memento;
+
+ guint max_depth;
+ gboolean model_changed;
+
+ GtkTreeModel *model;
+ GtkTreeRowReference *root;
+
+ GList *first_item;
+ GList *last_item;
+ GList *highlighted_item;
+};
+
+/* Signals */
+enum
+{
+ ITEM_ACTIVATED,
+ LAST_SIGNAL
+};
+
+static guint baobab_chart_signals [LAST_SIGNAL] = { 0 };
+
+/* Properties */
+enum
+{
+ PROP_0,
+ PROP_MAX_DEPTH,
+ PROP_MODEL,
+ PROP_ROOT,
+};
+
+/* Colors */
+const BaobabChartColor baobab_chart_tango_colors[] = {{0.94, 0.16, 0.16}, /* tango: ef2929 */
+ {0.68, 0.49, 0.66}, /* tango: ad7fa8 */
+ {0.45, 0.62, 0.82}, /* tango: 729fcf */
+ {0.54, 0.89, 0.20}, /* tango: 8ae234 */
+ {0.91, 0.73, 0.43}, /* tango: e9b96e */
+ {0.99, 0.68, 0.25}}; /* tango: fcaf3e */
+
+static void baobab_chart_class_init (BaobabChartClass *class);
+static void baobab_chart_init (BaobabChart *object);
+static void baobab_chart_realize (GtkWidget *widget);
+static void baobab_chart_dispose (GObject *object);
+static void baobab_chart_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+static void baobab_chart_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void baobab_chart_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void baobab_chart_free_items (GtkWidget *chart);
+static void baobab_chart_draw (GtkWidget *chart,
+ cairo_t *cr,
+ GdkRectangle area);
+static void baobab_chart_update_draw (BaobabChart *chart,
+ GtkTreePath *path);
+static void baobab_chart_row_changed (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data);
+static void baobab_chart_row_inserted (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data);
+static void baobab_chart_row_has_child_toggled (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data);
+static void baobab_chart_row_deleted (GtkTreeModel *model,
+ GtkTreePath *path,
+ gpointer data);
+static void baobab_chart_rows_reordered (GtkTreeModel *model,
+ GtkTreePath *parent,
+ GtkTreeIter *iter,
+ gint *new_order,
+ gpointer data);
+static gboolean baobab_chart_expose (GtkWidget *chart,
+ GdkEventExpose *event);
+static void baobab_chart_interpolate_colors (BaobabChartColor *color,
+ BaobabChartColor colora,
+ BaobabChartColor colorb,
+ gdouble percentage);
+static gint baobab_chart_button_release (GtkWidget *widget,
+ GdkEventButton *event);
+static gint baobab_chart_scroll (GtkWidget *widget,
+ GdkEventScroll *event);
+static gint baobab_chart_motion_notify (GtkWidget *widget,
+ GdkEventMotion *event);
+static gint baobab_chart_leave_notify (GtkWidget *widget,
+ GdkEventCrossing *event);
+static inline void baobab_chart_disconnect_signals (GtkWidget *chart,
+ GtkTreeModel *model);
+static inline void baobab_chart_connect_signals (GtkWidget *chart,
+ GtkTreeModel *model);
+static void baobab_chart_get_items (GtkWidget *chart, GtkTreePath *root);
+static gboolean baobab_chart_query_tooltip (GtkWidget *widget,
+ gint x,
+ gint y,
+ gboolean keyboard_mode,
+ GtkTooltip *tooltip,
+ gpointer user_data);
+
+
+static void
+baobab_chart_class_init (BaobabChartClass *class)
+{
+ GObjectClass *obj_class;
+ GtkWidgetClass *widget_class;
+
+ obj_class = G_OBJECT_CLASS (class);
+ widget_class = GTK_WIDGET_CLASS (class);
+
+ /* GtkObject signals */
+ obj_class->set_property = baobab_chart_set_property;
+ obj_class->get_property = baobab_chart_get_property;
+ obj_class->dispose = baobab_chart_dispose;
+
+ /* GtkWidget signals */
+ widget_class->realize = baobab_chart_realize;
+ widget_class->expose_event = baobab_chart_expose;
+ widget_class->size_allocate = baobab_chart_size_allocate;
+ widget_class->scroll_event = baobab_chart_scroll;
+
+ /* Baobab Chart abstract methods */
+ class->draw_item = NULL;
+ class->pre_draw = NULL;
+ class->post_draw = NULL;
+ class->calculate_item_geometry = NULL;
+ class->is_point_over_item = NULL;
+ class->get_item_rectangle = NULL;
+ class->can_zoom_in = NULL;
+ class->can_zoom_out = NULL;
+
+ g_object_class_install_property (obj_class,
+ PROP_MAX_DEPTH,
+ g_param_spec_int ("max-depth",
+ _("Maximum depth"),
+ _("The maximum depth drawn in the chart from the root"),
+ 1,
+ BAOBAB_CHART_MAX_DEPTH,
+ BAOBAB_CHART_MAX_DEPTH,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (obj_class,
+ PROP_MODEL,
+ g_param_spec_object ("model",
+ _("Chart model"),
+ _("Set the model of the chart"),
+ GTK_TYPE_TREE_MODEL,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (obj_class,
+ PROP_ROOT,
+ g_param_spec_boxed ("root",
+ _("Chart root node"),
+ _("Set the root node from the model"),
+ GTK_TYPE_TREE_ITER,
+ G_PARAM_READWRITE));
+
+ baobab_chart_signals[ITEM_ACTIVATED] =
+ g_signal_new ("item_activated",
+ G_TYPE_FROM_CLASS (obj_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (BaobabChartClass, item_activated),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE, 1,
+ GTK_TYPE_TREE_ITER);
+
+ g_type_class_add_private (obj_class, sizeof (BaobabChartPrivate));
+}
+
+static void
+baobab_chart_init (BaobabChart *chart)
+{
+ BaobabChartPrivate *priv;
+
+ priv = BAOBAB_CHART_GET_PRIVATE (chart);
+ chart->priv = priv;
+
+ priv->model = NULL;
+ priv->max_depth = BAOBAB_CHART_MAX_DEPTH;
+ priv->name_column = 0;
+ priv->size_column = 0;
+ priv->info_column = 0;
+ priv->percentage_column = 0;
+ priv->valid_column = 0;
+ priv->button_pressed = FALSE;
+ priv->is_frozen = FALSE;
+ priv->memento = NULL;
+ priv->root = NULL;
+
+ priv->first_item = NULL;
+ priv->last_item = NULL;
+ priv->highlighted_item = NULL;
+}
+
+static void
+baobab_chart_dispose (GObject *object)
+{
+ BaobabChartPrivate *priv;
+
+ baobab_chart_free_items (GTK_WIDGET (object));
+
+ priv = BAOBAB_CHART (object)->priv;
+
+ if (priv->model)
+ {
+ baobab_chart_disconnect_signals (GTK_WIDGET (object),
+ priv->model);
+
+ g_object_unref (priv->model);
+
+ priv->model = NULL;
+ }
+
+ if (priv->root)
+ {
+ gtk_tree_row_reference_free (priv->root);
+
+ priv->root = NULL;
+ }
+
+ G_OBJECT_CLASS (baobab_chart_parent_class)->dispose (object);
+}
+
+static void
+baobab_chart_realize (GtkWidget *widget)
+{
+ BaobabChart *chart;
+ GdkWindowAttr attributes;
+ gint attributes_mask;
+ GtkAllocation allocation;
+ GdkWindow *window;
+
+ g_return_if_fail (BAOBAB_IS_CHART (widget));
+
+ chart = BAOBAB_CHART (widget);
+ gtk_widget_set_realized (widget, TRUE);
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.x = allocation.x;
+ attributes.y = allocation.y;
+ attributes.width = allocation.width;
+ attributes.height = allocation.height;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+ attributes.visual = gtk_widget_get_visual (widget);
+ attributes.colormap = gtk_widget_get_colormap (widget);
+ attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
+
+ attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+
+ window = gdk_window_new (gtk_widget_get_parent_window (widget),
+ &attributes,
+ attributes_mask);
+ gtk_widget_set_window (widget, window);
+ gdk_window_set_user_data (window, chart);
+
+ gtk_widget_style_attach (widget);
+ gtk_style_set_background (gtk_widget_get_style (widget),
+ window,
+ GTK_STATE_NORMAL);
+
+ gtk_widget_add_events (widget,
+ GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK |
+ GDK_POINTER_MOTION_HINT_MASK | GDK_LEAVE_NOTIFY_MASK);
+}
+
+static void
+baobab_chart_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ BaobabChartPrivate *priv;
+ BaobabChartClass *class;
+ BaobabChartItem *item;
+ GList *node;
+
+ g_return_if_fail (BAOBAB_IS_CHART (widget));
+ g_return_if_fail (allocation != NULL);
+
+ priv = BAOBAB_CHART (widget)->priv;
+ class = BAOBAB_CHART_GET_CLASS (widget);
+
+ gtk_widget_set_allocation (widget, allocation);
+
+ if (gtk_widget_get_realized (widget))
+ {
+ gdk_window_move_resize (gtk_widget_get_window (widget),
+ allocation->x, allocation->y,
+ allocation->width, allocation->height);
+
+ node = priv->first_item;
+ while (node != NULL)
+ {
+ item = (BaobabChartItem *) node->data;
+ item->has_visible_children = FALSE;
+ item->visible = FALSE;
+ class->calculate_item_geometry (widget, item);
+
+ node = node->next;
+ }
+ }
+}
+
+static void
+baobab_chart_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ BaobabChart *chart;
+
+ chart = BAOBAB_CHART (object);
+
+ switch (prop_id)
+ {
+ case PROP_MAX_DEPTH:
+ baobab_chart_set_max_depth (GTK_WIDGET (chart), g_value_get_int (value));
+ break;
+ case PROP_MODEL:
+ baobab_chart_set_model (GTK_WIDGET (chart), g_value_get_object (value));
+ break;
+ case PROP_ROOT:
+ baobab_chart_set_root (GTK_WIDGET (chart), g_value_get_object (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+baobab_chart_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ BaobabChartPrivate *priv;
+
+ priv = BAOBAB_CHART (object)->priv;
+
+ switch (prop_id)
+ {
+ case PROP_MAX_DEPTH:
+ g_value_set_int (value, priv->max_depth);
+ break;
+ case PROP_MODEL:
+ g_value_set_object (value, priv->model);
+ break;
+ case PROP_ROOT:
+ g_value_set_object (value, priv->root);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GList
+*baobab_chart_add_item (GtkWidget *chart,
+ guint depth,
+ gdouble rel_start,
+ gdouble rel_size,
+ GtkTreeIter iter)
+{
+ BaobabChartPrivate *priv;
+ BaobabChartItem *item;
+
+ gchar *name;
+ gchar *size;
+
+ priv = BAOBAB_CHART_GET_PRIVATE (chart);
+
+ gtk_tree_model_get (priv->model, &iter,
+ priv->name_column, &name, -1);
+ gtk_tree_model_get (priv->model, &iter,
+ priv->size_column, &size, -1);
+
+ item = g_new (BaobabChartItem, 1);
+ item->name = name;
+ item->size = size;
+ item->depth = depth;
+ item->rel_start = rel_start;
+ item->rel_size = rel_size;
+ item->has_any_child = FALSE;
+ item->visible = FALSE;
+ item->has_visible_children = FALSE;
+
+ item->iter = iter;
+
+ item->parent = NULL;
+ item->data = NULL;
+
+ priv->last_item = g_list_prepend (priv->last_item, item);
+
+ return priv->last_item;
+}
+
+static void
+baobab_chart_free_items (GtkWidget *chart)
+{
+ BaobabChartPrivate *priv;
+ BaobabChartItem *item;
+ GList *node;
+ GList *next;
+
+ priv = BAOBAB_CHART_GET_PRIVATE (chart);
+
+ node = priv->first_item;
+ while (node != NULL)
+ {
+ next = node->next;
+
+ item = (BaobabChartItem *) node->data;
+
+ g_free (item->name);
+ g_free (item->size);
+
+ g_free (item->data);
+ item->data = NULL;
+
+ g_free (item);
+ g_list_free_1 (node);
+
+ node = next;
+ }
+
+ priv->first_item = NULL;
+ priv->last_item = NULL;
+ priv->highlighted_item = NULL;
+}
+
+static void
+baobab_chart_get_items (GtkWidget *chart, GtkTreePath *root)
+{
+ BaobabChartPrivate *priv;
+ BaobabChartItem *item;
+
+ GList *node;
+ GtkTreeIter initial_iter = {0};
+ gdouble size;
+ GtkTreePath *model_root_path;
+ GtkTreeIter model_root_iter;
+
+ BaobabChartClass *class;
+ GtkTreeIter child_iter = {0};
+ GList *child_node;
+ BaobabChartItem *child;
+ gdouble rel_start;
+
+ priv = BAOBAB_CHART_GET_PRIVATE (chart);
+
+ /* First we free current item list */
+ baobab_chart_free_items (chart);
+
+ /* Get the tree iteration corresponding to root */
+ if (!gtk_tree_model_get_iter (priv->model, &initial_iter, root))
+ {
+ priv->model_changed = FALSE;
+ return;
+ }
+
+
+
+ model_root_path = gtk_tree_path_new_first ();
+ gtk_tree_model_get_iter (priv->model, &model_root_iter, model_root_path);
+ gtk_tree_path_free (model_root_path);
+
+ gtk_tree_model_get (priv->model, &model_root_iter,
+ priv->percentage_column, &size, -1);
+
+ /* Create first item */
+ node = baobab_chart_add_item (chart, 0, 0, 100, initial_iter);
+
+ /* Iterate through childs building the list */
+ class = BAOBAB_CHART_GET_CLASS (chart);
+
+ do
+ {
+ item = (BaobabChartItem *) node->data;
+ item->has_any_child = gtk_tree_model_iter_children (priv->model,
+ &child_iter,
+ &(item->iter));
+
+ /* Calculate item geometry */
+ class->calculate_item_geometry (chart, item);
+
+ if (! item->visible)
+ {
+ node = node->prev;
+ continue;
+ }
+
+ /* Get item's children and add them to the list */
+ if ((item->has_any_child) && (item->depth < priv->max_depth + 1))
+ {
+ rel_start = 0;
+
+ do
+ {
+ gtk_tree_model_get (priv->model, &child_iter,
+ priv->percentage_column, &size, -1);
+
+ child_node = baobab_chart_add_item (chart,
+ item->depth + 1,
+ rel_start,
+ size,
+ child_iter);
+ child = (BaobabChartItem *) child_node->data;
+ child->parent = node;
+ rel_start += size;
+ }
+ while (gtk_tree_model_iter_next (priv->model, &child_iter));
+ }
+
+ node = node->prev;
+ }
+ while (node != NULL);
+
+ /* Reverse the list, 'cause we created it from the tail, for efficiency reasons */
+ priv->first_item = g_list_reverse (priv->last_item);
+
+ priv->model_changed = FALSE;
+}
+
+static void
+baobab_chart_draw (GtkWidget *chart,
+ cairo_t *cr,
+ GdkRectangle area)
+{
+ BaobabChartPrivate *priv;
+ BaobabChartClass *class;
+
+ GList *node;
+ BaobabChartItem *item;
+ gboolean highlighted;
+
+ priv = BAOBAB_CHART_GET_PRIVATE (chart);
+ class = BAOBAB_CHART_GET_CLASS (chart);
+
+ /* call pre-draw abstract method */
+ if (class->pre_draw)
+ class->pre_draw (chart, cr);
+
+ cairo_save (cr);
+
+ node = priv->first_item;
+ while (node != NULL)
+ {
+ item = (BaobabChartItem *) node->data;
+
+ if ((item->visible) && (gdk_rectangle_intersect (&area, &item->rect, NULL))
+ && (item->depth <= priv->max_depth))
+ {
+ highlighted = (node == priv->highlighted_item);
+
+ class->draw_item (chart, cr, item, highlighted);
+ }
+
+ node = node->next;
+ }
+
+ cairo_restore (cr);
+
+ /* call post-draw abstract method */
+ if (class->post_draw)
+ class->post_draw (chart, cr);
+}
+
+static void
+baobab_chart_update_draw (BaobabChart* chart,
+ GtkTreePath *path)
+{
+ BaobabChartPrivate *priv;
+ GtkTreePath *root_path = NULL;
+ gint root_depth, node_depth;
+
+ if (!gtk_widget_get_realized ( GTK_WIDGET (chart)))
+ return;
+
+ priv = BAOBAB_CHART (chart)->priv;
+
+ if (priv->root != NULL)
+ {
+ root_path = gtk_tree_row_reference_get_path (priv->root);
+
+ if (root_path == NULL)
+ {
+ gtk_tree_row_reference_free (priv->root);
+ priv->root = NULL;
+ }
+ }
+
+ if (priv->root == NULL)
+ root_path = gtk_tree_path_new_first ();
+
+
+ root_depth = gtk_tree_path_get_depth (root_path);
+ node_depth = gtk_tree_path_get_depth (path);
+
+ if (((node_depth-root_depth)<=priv->max_depth)&&
+ ((gtk_tree_path_is_ancestor (root_path, path))||
+ (gtk_tree_path_compare (root_path, path) == 0)))
+ {
+ gtk_widget_queue_draw (GTK_WIDGET (chart));
+ }
+
+ gtk_tree_path_free (root_path);
+}
+
+static void
+baobab_chart_row_changed (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ g_return_if_fail (BAOBAB_IS_CHART (data));
+ g_return_if_fail (path != NULL || iter != NULL);
+
+ BAOBAB_CHART_GET_PRIVATE (data)->model_changed = TRUE;
+
+ baobab_chart_update_draw (BAOBAB_CHART (data), path);
+}
+
+static void
+baobab_chart_row_inserted (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ g_return_if_fail (BAOBAB_IS_CHART (data));
+ g_return_if_fail (path != NULL || iter != NULL);
+
+ BAOBAB_CHART_GET_PRIVATE (data)->model_changed = TRUE;
+
+ baobab_chart_update_draw (BAOBAB_CHART (data), path);
+}
+
+static void
+baobab_chart_row_has_child_toggled (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ g_return_if_fail (BAOBAB_IS_CHART (data));
+ g_return_if_fail (path != NULL || iter != NULL);
+
+ BAOBAB_CHART_GET_PRIVATE (data)->model_changed = TRUE;
+
+ baobab_chart_update_draw (BAOBAB_CHART (data), path);
+}
+
+static void
+baobab_chart_row_deleted (GtkTreeModel *model,
+ GtkTreePath *path,
+ gpointer data)
+{
+ g_return_if_fail (BAOBAB_IS_CHART (data));
+ g_return_if_fail (path != NULL);
+
+ BAOBAB_CHART_GET_PRIVATE (data)->model_changed = TRUE;
+
+ baobab_chart_update_draw (BAOBAB_CHART (data), path);
+
+}
+
+static void
+baobab_chart_rows_reordered (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gint *new_order,
+ gpointer data)
+{
+ g_return_if_fail (BAOBAB_IS_CHART (data));
+ g_return_if_fail (path != NULL || iter != NULL);
+
+ BAOBAB_CHART_GET_PRIVATE (data)->model_changed = TRUE;
+
+ baobab_chart_update_draw (BAOBAB_CHART (data), path);
+
+}
+
+static gboolean
+baobab_chart_expose (GtkWidget *chart, GdkEventExpose *event)
+{
+ cairo_t *cr;
+ BaobabChartPrivate *priv;
+ gint w, h;
+ gdouble p, sx, sy;
+ GtkTreePath *root_path = NULL;
+ GtkTreePath *current_path = NULL;
+ GtkAllocation allocation;
+
+ priv = BAOBAB_CHART (chart)->priv;
+
+ /* the columns are not set we paint nothing */
+ if (priv->name_column == priv->percentage_column)
+ return FALSE;
+
+ /* get a cairo_t */
+ cr = gdk_cairo_create (gtk_widget_get_window (chart));
+
+ cairo_rectangle (cr,
+ event->area.x, event->area.y,
+ event->area.width, event->area.height);
+
+ /* there is no model we can not paint */
+ if ((priv->is_frozen) || (priv->model == NULL))
+ {
+ if (priv->memento != NULL)
+ {
+ w = cairo_image_surface_get_width (priv->memento);
+ h = cairo_image_surface_get_height (priv->memento);
+
+ cairo_clip (cr);
+
+ gtk_widget_get_allocation (GTK_WIDGET (chart), &allocation);
+ if (w > 0 && h > 0 &&
+ !(allocation.width == w && allocation.height == h))
+ {
+ /* minimal available proportion */
+ p = MIN (allocation.width / (1.0 * w),
+ allocation.height / (1.0 * h));
+
+ sx = (gdouble) (allocation.width - w * p) / 2.0;
+ sy = (gdouble) (allocation.height - h * p) / 2.0;
+
+ cairo_translate (cr, sx, sy);
+ cairo_scale (cr, p, p);
+ }
+
+ cairo_set_source_surface (cr,
+ priv->memento,
+ 0, 0);
+ cairo_paint (cr);
+ }
+ }
+ else
+ {
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_fill_preserve (cr);
+
+ cairo_clip (cr);
+
+ if (priv->root != NULL)
+ root_path = gtk_tree_row_reference_get_path (priv->root);
+
+ if (root_path == NULL) {
+ root_path = gtk_tree_path_new_first ();
+ priv->root = NULL;
+ }
+
+ /* Check if tree model was modified in any way */
+ if ((priv->model_changed) ||
+ (priv->first_item == NULL))
+ baobab_chart_get_items (chart, root_path);
+ else
+ {
+ /* Check if root was changed */
+ current_path = gtk_tree_model_get_path (priv->model,
+ &((BaobabChartItem*) priv->first_item->data)->iter);
+
+ if (gtk_tree_path_compare (root_path, current_path) != 0)
+ baobab_chart_get_items (chart, root_path);
+
+ gtk_tree_path_free (current_path);
+ }
+
+ gtk_tree_path_free (root_path);
+
+ baobab_chart_draw (chart, cr, event->area);
+ }
+
+ cairo_destroy (cr);
+
+ return FALSE;
+}
+
+static void
+baobab_chart_interpolate_colors (BaobabChartColor *color,
+ BaobabChartColor colora,
+ BaobabChartColor colorb,
+ gdouble percentage)
+{
+ gdouble diff;
+
+ diff = colora.red - colorb.red;
+ color->red = colora.red-diff*percentage;
+
+ diff = colora.green - colorb.green;
+ color->green = colora.green-diff*percentage;
+
+ diff = colora.blue - colorb.blue;
+ color->blue = colora.blue-diff*percentage;
+}
+
+void
+baobab_chart_get_item_color (BaobabChartColor *color,
+ gdouble rel_position,
+ gint depth,
+ gboolean highlighted)
+{
+ gdouble intensity;
+ gint color_number;
+ gint next_color_number;
+ gdouble maximum;
+ static const BaobabChartColor level_color = {0.83, 0.84, 0.82};
+ static const BaobabChartColor level_color_hl = {0.88, 0.89, 0.87};
+
+ intensity = 1 - (((depth-1)*0.3) / BAOBAB_CHART_MAX_DEPTH);
+
+ if (depth == 0)
+ *color = level_color;
+ else
+ {
+ color_number = rel_position / (100/3);
+ next_color_number = (color_number + 1) % 6;
+
+ baobab_chart_interpolate_colors (color,
+ baobab_chart_tango_colors[color_number],
+ baobab_chart_tango_colors[next_color_number],
+ (rel_position - color_number * 100/3) / (100/3));
+ color->red = color->red * intensity;
+ color->green = color->green * intensity;
+ color->blue = color->blue * intensity;
+ }
+
+ if (highlighted)
+ {
+ if (depth == 0)
+ *color = level_color_hl;
+ else
+ {
+ maximum = MAX (color->red,
+ MAX (color->green,
+ color->blue));
+ color->red /= maximum;
+ color->green /= maximum;
+ color->blue /= maximum;
+ }
+ }
+}
+
+static gint
+baobab_chart_button_release (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ BaobabChartPrivate *priv;
+
+ priv = BAOBAB_CHART (widget)->priv;
+
+ if (priv->is_frozen)
+ return TRUE;
+
+ switch (event->button)
+ {
+ case LEFT_BUTTON:
+ /* Enter into a subdir */
+ if (priv->highlighted_item != NULL)
+ g_signal_emit (BAOBAB_CHART (widget),
+ baobab_chart_signals[ITEM_ACTIVATED],
+ 0, &((BaobabChartItem*) priv->highlighted_item->data)->iter);
+
+ break;
+
+ case MIDDLE_BUTTON:
+ /* Go back to the parent dir */
+ baobab_chart_move_up_root (widget);
+ break;
+ }
+
+ return FALSE;
+}
+
+static gint
+baobab_chart_scroll (GtkWidget *widget,
+ GdkEventScroll *event)
+{
+ switch (event->direction)
+ {
+ case GDK_SCROLL_LEFT :
+ case GDK_SCROLL_UP :
+ if (baobab_chart_can_zoom_out (widget))
+ baobab_chart_zoom_out (widget);
+ /* change the selected item when zooming */
+ baobab_chart_motion_notify (widget, (GdkEventMotion *)event);
+ break;
+
+ case GDK_SCROLL_RIGHT :
+ case GDK_SCROLL_DOWN :
+ if (baobab_chart_can_zoom_in (widget))
+ baobab_chart_zoom_in (widget);
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+baobab_chart_set_item_highlight (GtkWidget *chart,
+ GList *node,
+ gboolean highlighted)
+{
+ BaobabChartItem *item;
+ BaobabChartPrivate *priv;
+ BaobabChartClass *class;
+
+ if (node == NULL)
+ return;
+
+ item = (BaobabChartItem *) node->data;
+ priv = BAOBAB_CHART_GET_PRIVATE (chart);
+ class = BAOBAB_CHART_GET_CLASS (chart);
+
+ if (highlighted)
+ priv->highlighted_item = node;
+ else
+ priv->highlighted_item = NULL;
+
+ gdk_window_invalidate_rect (gtk_widget_get_window ( GTK_WIDGET (chart)),
+ &item->rect, TRUE);
+}
+
+static gint
+baobab_chart_motion_notify (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ BaobabChartPrivate *priv;
+ BaobabChartClass *class;
+ GList *node;
+ BaobabChartItem *item;
+ gboolean found = FALSE;
+
+ priv = BAOBAB_CHART_GET_PRIVATE (widget);
+ class = BAOBAB_CHART_GET_CLASS (widget);
+
+ /* Check if the pointer is over an item */
+ node = priv->last_item;
+ while (node != NULL)
+ {
+ item = (BaobabChartItem *) node->data;
+
+ if ((item->visible) && (class->is_point_over_item (widget, item, event->x, event->y)))
+ {
+ if (priv->highlighted_item != node)
+ {
+ baobab_chart_set_item_highlight (widget, priv->highlighted_item, FALSE);
+
+ gtk_widget_set_has_tooltip (widget, TRUE);
+ baobab_chart_set_item_highlight (widget, node, TRUE);
+ }
+
+ found = TRUE;
+ break;
+ }
+ node = node->prev;
+ }
+
+ /* If we never found a highlighted item, but there is an old highlighted item,
+ redraw it to turn it off */
+ if (! found)
+ {
+ baobab_chart_set_item_highlight (widget, priv->highlighted_item, FALSE);
+ gtk_widget_set_has_tooltip (widget, FALSE);
+ }
+
+ /* Continue receiving motion notifies */
+ gdk_event_request_motions (event);
+
+ return FALSE;
+}
+
+static gint
+baobab_chart_leave_notify (GtkWidget *widget,
+ GdkEventCrossing *event)
+{
+ BaobabChartPrivate *priv;
+
+ priv = BAOBAB_CHART_GET_PRIVATE (widget);
+ baobab_chart_set_item_highlight (widget, priv->highlighted_item, FALSE);
+
+ return FALSE;
+}
+
+static inline void
+baobab_chart_connect_signals (GtkWidget *chart,
+ GtkTreeModel *model)
+{
+ g_signal_connect (model,
+ "row_changed",
+ G_CALLBACK (baobab_chart_row_changed),
+ chart);
+ g_signal_connect (model,
+ "row_inserted",
+ G_CALLBACK (baobab_chart_row_inserted),
+ chart);
+ g_signal_connect (model,
+ "row_has_child_toggled",
+ G_CALLBACK (baobab_chart_row_has_child_toggled),
+ chart);
+ g_signal_connect (model,
+ "row_deleted",
+ G_CALLBACK (baobab_chart_row_deleted),
+ chart);
+ g_signal_connect (model,
+ "rows_reordered",
+ G_CALLBACK (baobab_chart_rows_reordered),
+ chart);
+ g_signal_connect (chart,
+ "query-tooltip",
+ G_CALLBACK (baobab_chart_query_tooltip),
+ chart);
+ g_signal_connect (chart,
+ "motion-notify-event",
+ G_CALLBACK (baobab_chart_motion_notify),
+ chart);
+ g_signal_connect (chart,
+ "leave-notify-event",
+ G_CALLBACK (baobab_chart_leave_notify),
+ chart);
+ g_signal_connect (chart,
+ "button-release-event",
+ G_CALLBACK (baobab_chart_button_release),
+ chart);
+}
+
+static inline void
+baobab_chart_disconnect_signals (GtkWidget *chart,
+ GtkTreeModel *model)
+{
+ g_signal_handlers_disconnect_by_func (model,
+ baobab_chart_row_changed,
+ chart);
+ g_signal_handlers_disconnect_by_func (model,
+ baobab_chart_row_inserted,
+ chart);
+ g_signal_handlers_disconnect_by_func (model,
+ baobab_chart_row_has_child_toggled,
+ chart);
+ g_signal_handlers_disconnect_by_func (model,
+ baobab_chart_row_deleted,
+ chart);
+ g_signal_handlers_disconnect_by_func (model,
+ baobab_chart_rows_reordered,
+ chart);
+ g_signal_handlers_disconnect_by_func (chart,
+ baobab_chart_query_tooltip,
+ chart);
+ g_signal_handlers_disconnect_by_func (chart,
+ baobab_chart_motion_notify,
+ chart);
+ g_signal_handlers_disconnect_by_func (chart,
+ baobab_chart_leave_notify,
+ chart);
+ g_signal_handlers_disconnect_by_func (chart,
+ baobab_chart_button_release,
+ chart);
+}
+
+static gboolean
+baobab_chart_query_tooltip (GtkWidget *widget,
+ gint x,
+ gint y,
+ gboolean keyboard_mode,
+ GtkTooltip *tooltip,
+ gpointer user_data)
+{
+ BaobabChartPrivate *priv;
+ BaobabChartItem *item;
+ char *markup;
+
+ priv = BAOBAB_CHART_GET_PRIVATE (widget);
+
+ if (priv->highlighted_item == NULL)
+ return FALSE;
+
+ item = (BaobabChartItem *) priv->highlighted_item->data;
+
+ if ( (item->name == NULL) || (item->size == NULL) )
+ return FALSE;
+
+ gtk_tooltip_set_tip_area (tooltip, &item->rect);
+
+ markup = g_strconcat (item->name,
+ "\n",
+ item->size,
+ NULL);
+ gtk_tooltip_set_markup (tooltip, markup);
+ g_free (markup);
+
+ return TRUE;
+}
+
+GdkPixbuf*
+baobab_chart_get_pixbuf (GtkWidget *widget)
+{
+ gint w, h;
+ GdkPixbuf *pixbuf;
+
+ g_return_val_if_fail (BAOBAB_IS_CHART (widget), NULL);
+
+ #if GTK_CHECK_VERSION(3, 0, 0)
+ w = gdk_window_get_width(gtk_widget_get_window(widget));
+ h = gdk_window_get_height(gtk_widget_get_window(widget));
+ #else
+ gdk_drawable_get_size(gtk_widget_get_window(widget), &w, &h);
+ #endif
+
+ pixbuf = gdk_pixbuf_get_from_drawable (NULL,
+ gtk_widget_get_window (widget),
+ gdk_colormap_get_system (),
+ 0, 0,
+ 0, 0,
+ w, h);
+
+ return pixbuf;
+}
+
+/* Public functions start here */
+
+/**
+ * baobab_chart_new:
+ *
+ * Constructor for the baobab_chart class
+ *
+ * Returns: a new #BaobabChart object
+ *
+ **/
+GtkWidget *
+baobab_chart_new ()
+{
+ return g_object_new (BAOBAB_CHART_TYPE, NULL);
+}
+
+/**
+ * baobab_chart_set_model_with_columns:
+ * @chart: the #BaobabChart whose model is going to be set
+ * @model: the #GtkTreeModel which is going to set as the model of
+ * @chart
+ * @name_column: number of column inside @model where the file name is
+ * stored
+ * @size_column: number of column inside @model where the file size is
+ * stored
+ * @info_column: number of column inside @model where the percentage
+ * of disk usage is stored
+ * @percentage_column: number of column inside @model where the disk
+ * usage percentage is stored
+ * @valid_column: number of column inside @model where the flag indicating
+ * if the row data is right or not.
+ * @root: a #GtkTreePath indicating the node of @model which will be
+ * used as root.
+ *
+ * Sets @model as the #GtkTreeModel used by @chart. Indicates the
+ * columns inside @model where the values file name, file
+ * size, file information, disk usage percentage and data correction are stored, and
+ * the node which will be used as the root of @chart. Once
+ * the model has been successfully set, a redraw of the window is
+ * forced.
+ * This function is intended to be used the first time a #GtkTreeModel
+ * is assigned to @chart, or when the columns containing the needed data
+ * are going to change. In other cases, #baobab_chart_set_model should
+ * be used.
+ * This function does not change the state of the signals from the model, which
+ * is controlled by he #baobab_chart_freeze_updates and the
+ * #baobab_chart_thaw_updates functions.
+ *
+ * Fails if @chart is not a #BaobabChart or if @model is not a
+ * #GtkTreeModel.
+ **/
+void
+baobab_chart_set_model_with_columns (GtkWidget *chart,
+ GtkTreeModel *model,
+ guint name_column,
+ guint size_column,
+ guint info_column,
+ guint percentage_column,
+ guint valid_column,
+ GtkTreePath *root)
+{
+ BaobabChartPrivate *priv;
+
+ g_return_if_fail (BAOBAB_IS_CHART (chart));
+ g_return_if_fail (GTK_IS_TREE_MODEL (model));
+
+ priv = BAOBAB_CHART (chart)->priv;
+
+ baobab_chart_set_model (chart, model);
+
+ if (root != NULL)
+ {
+ priv->root = gtk_tree_row_reference_new (model, root);
+ g_object_notify (G_OBJECT (chart), "root");
+ }
+
+ priv->name_column = name_column;
+ priv->size_column = size_column;
+ priv->info_column = info_column;
+ priv->percentage_column = percentage_column;
+ priv->valid_column = valid_column;
+}
+
+/**
+ * baobab_chart_set_model:
+ * @chart: the #BaobabChart whose model is going to be set
+ * @model: the #GtkTreeModel which is going to set as the model of
+ * @chart
+ *
+ * Sets @model as the #GtkTreeModel used by @chart, and takes the needed
+ * data from the columns especified in the last call to
+ * #baobab_chart_set_model_with_colums.
+ * This function does not change the state of the signals from the model, which
+ * is controlled by he #baobab_chart_freeze_updates and the
+ * #baobab_chart_thaw_updates functions.
+ *
+ * Fails if @chart is not a #BaobabChart or if @model is not a
+ * #GtkTreeModel.
+ **/
+void
+baobab_chart_set_model (GtkWidget *chart,
+ GtkTreeModel *model)
+{
+ BaobabChartPrivate *priv;
+
+ g_return_if_fail (BAOBAB_IS_CHART (chart));
+ g_return_if_fail (GTK_IS_TREE_MODEL (model));
+
+ priv = BAOBAB_CHART (chart)->priv;
+
+ if (model == priv->model)
+ return;
+
+ if (priv->model)
+ {
+ if (! priv->is_frozen)
+ baobab_chart_disconnect_signals (chart,
+ priv->model);
+ g_object_unref (priv->model);
+ }
+
+ priv->model = model;
+ g_object_ref (priv->model);
+
+ if (! priv->is_frozen)
+ baobab_chart_connect_signals (chart,
+ priv->model);
+
+ if (priv->root)
+ gtk_tree_row_reference_free (priv->root);
+
+ priv->root = NULL;
+
+ g_object_notify (G_OBJECT (chart), "model");
+
+ gtk_widget_queue_draw (chart);
+}
+
+/**
+ * baobab_chart_get_model:
+ * @chart: a #BaobabChart whose model will be returned.
+ *
+ * Returns the #GtkTreeModel which is the model used by @chart.
+ *
+ * Returns: %NULL if @chart is not a #BaobabChart.
+ **/
+GtkTreeModel *
+baobab_chart_get_model (GtkWidget *chart)
+{
+ g_return_val_if_fail (BAOBAB_IS_CHART (chart), NULL);
+
+ return BAOBAB_CHART (chart)->priv->model;
+}
+
+/**
+ * baobab_chart_set_max_depth:
+ * @chart: a #BaobabChart
+ * @max_depth: the new maximum depth to show in the widget.
+ *
+ * Sets the maximum number of nested levels that are going to be show in the
+ * wigdet, and causes a redraw of the widget to show the new maximum
+ * depth. If max_depth is < 1 MAX_DRAWABLE_DEPTH is used.
+ *
+ * Fails if @chart is not a #BaobabChart.
+ **/
+void
+baobab_chart_set_max_depth (GtkWidget *chart,
+ guint max_depth)
+{
+ BaobabChartPrivate *priv;
+
+ g_return_if_fail (BAOBAB_IS_CHART (chart));
+
+ priv = BAOBAB_CHART_GET_PRIVATE (chart);
+
+ max_depth = MIN (max_depth, BAOBAB_CHART_MAX_DEPTH);
+ max_depth = MAX (max_depth, BAOBAB_CHART_MIN_DEPTH);
+
+ if (max_depth == priv->max_depth)
+ return;
+
+ priv->max_depth = max_depth;
+ g_object_notify (G_OBJECT (chart), "max-depth");
+
+ priv->model_changed = TRUE;
+
+ gtk_widget_queue_draw (chart);
+}
+
+/**
+ * baobab_chart_get_max_depth:
+ * @chart: a #BaobabChart.
+ *
+ * Returns the maximum number of levels that will be show in the
+ * widget.
+ *
+ * Fails if @chart is not a #BaobabChart.
+ **/
+guint
+baobab_chart_get_max_depth (GtkWidget *chart)
+{
+ g_return_val_if_fail (BAOBAB_IS_CHART (chart), 0);
+
+ return BAOBAB_CHART (chart)->priv->max_depth;
+}
+
+/**
+ * baobab_chart_set_root:
+ * @chart: a #BaobabChart
+ * @root: a #GtkTreePath indicating the node which will be used as
+ * the widget root.
+ *
+ * Sets the node pointed by @root as the new root of the widget
+ * @chart.
+ *
+ * Fails if @chart is not a #BaobabChart or if @chart has not
+ * a #GtkTreeModel set.
+ **/
+void
+baobab_chart_set_root (GtkWidget *chart,
+ GtkTreePath *root)
+{
+ BaobabChartPrivate *priv;
+ GtkTreePath *current_root;
+
+ g_return_if_fail (BAOBAB_IS_CHART (chart));
+
+ priv = BAOBAB_CHART (chart)->priv;
+
+ g_return_if_fail (priv->model != NULL);
+
+ if (priv->root) {
+ /* Check that given root is different from current */
+ current_root = gtk_tree_row_reference_get_path (priv->root);
+ if ( (current_root) && (gtk_tree_path_compare (current_root, root) == 0) )
+ return;
+
+ /* Free current root */
+ gtk_tree_row_reference_free (priv->root);
+ }
+
+ priv->root = gtk_tree_row_reference_new (priv->model, root);
+
+ g_object_notify (G_OBJECT (chart), "root");
+
+ gtk_widget_queue_draw (chart);
+}
+
+/**
+ * baobab_chart_get_root:
+ * @chart: a #BaobabChart.
+ *
+ * Returns a #GtkTreePath pointing to the root of the widget. The
+ * programmer has the responsability to free the used memory once
+ * finished with the returned value. It returns NULL if there is no
+ * root node defined
+ *
+ * Fails if @chart is not a #BaobabChart.
+ **/
+GtkTreePath*
+baobab_chart_get_root (GtkWidget *chart)
+{
+ g_return_val_if_fail (BAOBAB_IS_CHART (chart), NULL);
+
+ if (BAOBAB_CHART (chart)->priv->root)
+ return gtk_tree_row_reference_get_path (BAOBAB_CHART (chart)->priv->root);
+ else
+ return NULL;
+}
+
+/**
+ * baobab_chart_freeze_updates:
+ * @chart: the #BaobabChart whose model signals are going to be frozen.
+ *
+ * Disconnects @chart from the signals emitted by its model, and sets
+ * the window of @chart to a "processing" state, so that the window
+ * ignores changes in the chart's model and mouse events.
+ * In order to connect again the window to the model, a call to
+ * #baobab_chart_thaw_updates must be done.
+ *
+ * Fails if @chart is not a #BaobabChart.
+ **/
+void
+baobab_chart_freeze_updates (GtkWidget *chart)
+{
+ BaobabChartPrivate *priv;
+ cairo_surface_t *surface = NULL;
+ cairo_t *cr = NULL;
+ GdkRectangle area;
+ GtkAllocation allocation;
+
+ g_return_if_fail (BAOBAB_IS_CHART (chart));
+
+ priv = BAOBAB_CHART_GET_PRIVATE (chart);
+
+ if (priv->is_frozen)
+ return;
+
+ if (priv->model)
+ baobab_chart_disconnect_signals (chart,
+ priv->model);
+
+ gtk_widget_get_allocation (GTK_WIDGET (chart), &allocation);
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ allocation.width,
+ allocation.height);
+
+ if (cairo_surface_status (surface) == CAIRO_STATUS_SUCCESS)
+ {
+ cr = cairo_create (surface);
+
+ area.x = 0;
+ area.y = 0;
+ area.width = allocation.width;
+ area.height = allocation.height;
+ baobab_chart_draw (chart, cr, area);
+
+ cairo_rectangle (cr,
+ 0, 0,
+ allocation.width,
+ allocation.height);
+
+ cairo_set_source_rgba (cr, 0.93, 0.93, 0.93, 0.5); /* tango: eeeeec */
+ cairo_fill_preserve (cr);
+
+ cairo_clip (cr);
+
+ priv->memento = surface;
+
+ cairo_destroy (cr);
+ }
+
+ priv->is_frozen = TRUE;
+
+ gtk_widget_queue_draw (chart);
+}
+
+/**
+ * baobab_chart_thaw_updates:
+ * @chart: the #BaobabChart whose model signals are frozen.
+ *
+ * Reconnects @chart to the signals emitted by its model, which
+ * were disconnected through a call to #baobab_chart_freeze_updates.
+ * Takes the window out of its "processing" state and forces a redraw
+ * of the widget.
+ *
+ * Fails if @chart is not a #BaobabChart.
+ **/
+void
+baobab_chart_thaw_updates (GtkWidget *chart)
+{
+ BaobabChartPrivate *priv;
+
+ g_return_if_fail (BAOBAB_IS_CHART (chart));
+
+ priv = BAOBAB_CHART_GET_PRIVATE (chart);
+
+ if (priv->is_frozen)
+ {
+ if (priv->model)
+ baobab_chart_connect_signals (chart,
+ priv->model);
+
+ if (priv->memento)
+ {
+ cairo_surface_destroy (priv->memento);
+ priv->memento = NULL;
+ }
+
+ priv->is_frozen = FALSE;
+
+ priv->model_changed = TRUE;
+ gtk_widget_queue_draw (chart);
+ }
+}
+
+/**
+ * baobab_chart_zoom_in:
+ * @chart: the #BaobabChart requested to zoom in.
+ *
+ * Zooms in the chart by decreasing its maximun depth.
+ *
+ * Fails if @chart is not a #BaobabChart.
+ **/
+void
+baobab_chart_zoom_in (GtkWidget *chart)
+{
+ BaobabChartPrivate *priv;
+ BaobabChartClass *class;
+ guint new_max_depth;
+
+ g_return_if_fail (BAOBAB_IS_CHART (chart));
+
+ priv = BAOBAB_CHART (chart)->priv;
+ class = BAOBAB_CHART_GET_CLASS (chart);
+
+ if (class->can_zoom_in != NULL)
+ new_max_depth = class->can_zoom_in (chart);
+ else
+ new_max_depth = priv->max_depth - 1;
+
+ baobab_chart_set_max_depth (chart, new_max_depth);
+}
+
+/**
+ * baobab_chart_zoom_out:
+ * @chart: the #BaobabChart requested to zoom out.
+ *
+ * Zooms out the chart by increasing its maximun depth.
+ *
+ * Fails if @chart is not a #BaobabChart.
+ **/
+void
+baobab_chart_zoom_out (GtkWidget *chart)
+{
+ g_return_if_fail (BAOBAB_IS_CHART (chart));
+
+ baobab_chart_set_max_depth (chart,
+ baobab_chart_get_max_depth (chart) + 1);
+}
+
+/**
+ * baobab_chart_move_up_root:
+ * @chart: the #BaobabChart whose root is requested to move up one level.
+ *
+ * Move root to the inmediate parent of the current root item of @chart.
+ *
+ * Fails if @chart is not a #BaobabChart.
+ **/
+void
+baobab_chart_move_up_root (GtkWidget *chart)
+{
+ BaobabChartPrivate *priv;
+
+ GtkTreeIter parent_iter;
+ GtkTreePath *path;
+ GtkTreeIter root_iter;
+
+ gint valid;
+ GtkTreePath *parent_path;
+
+ g_return_if_fail (BAOBAB_IS_CHART (chart));
+
+ priv = BAOBAB_CHART_GET_PRIVATE (chart);
+
+ if (priv->root == NULL)
+ return;
+
+ path = gtk_tree_row_reference_get_path (priv->root);
+
+ if (path != NULL)
+ gtk_tree_model_get_iter (priv->model, &root_iter, path);
+ else
+ return;
+
+ if (gtk_tree_model_iter_parent (priv->model, &parent_iter, &root_iter))
+ {
+ gtk_tree_model_get (priv->model, &parent_iter, priv->valid_column,
+ &valid, -1);
+
+ if (valid == -1)
+ return;
+
+ gtk_tree_row_reference_free (priv->root);
+ parent_path = gtk_tree_model_get_path (priv->model, &parent_iter);
+ priv->root = gtk_tree_row_reference_new (priv->model, parent_path);
+ gtk_tree_path_free (parent_path);
+
+ g_signal_emit (BAOBAB_CHART (chart),
+ baobab_chart_signals[ITEM_ACTIVATED],
+ 0, &parent_iter);
+
+ gtk_widget_queue_draw (chart);
+ }
+
+ gtk_tree_path_free (path);
+}
+
+/**
+ * baobab_chart_save_snapshot:
+ * @chart: the #BaobabChart requested to be exported to image.
+ *
+ * Opens a dialog to allow saving the current chart's image as a PNG, JPEG or
+ * BMP image.
+ *
+ * Fails if @chart is not a #BaobabChart.
+ **/
+void
+baobab_chart_save_snapshot (GtkWidget *chart)
+{
+ BaobabChartPrivate *priv;
+
+ GdkPixbuf *pixbuf;
+
+ GtkWidget *fs_dlg;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *label;
+ GtkWidget *opt_menu;
+ gchar *sel_type;
+ gchar *filename;
+ gchar *def_filename;
+
+ BaobabChartItem *item;
+
+ g_return_if_fail (BAOBAB_IS_CHART (chart));
+
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+
+ /* Get the chart's pixbuf */
+ pixbuf = baobab_chart_get_pixbuf (chart);
+ if (pixbuf == NULL)
+ {
+ GtkWidget *dialog;
+ dialog = gtk_message_dialog_new (NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ _("Cannot create pixbuf image!"));
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+
+ return;
+ }
+
+ priv = BAOBAB_CHART (chart)->priv;
+
+ /* Popup the File chooser dialog */
+ fs_dlg = gtk_file_chooser_dialog_new (_("Save Snapshot"),
+ NULL,
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE,
+ GTK_RESPONSE_ACCEPT, NULL);
+
+ item = (BaobabChartItem *) priv->first_item->data;
+ def_filename = g_strdup_printf (SNAPSHOT_DEF_FILENAME_FORMAT, item->name);
+
+ gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (fs_dlg), def_filename);
+ g_free (def_filename);
+
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (fs_dlg),
+ g_get_home_dir ());
+
+#if GTK_CHECK_VERSION(2,8,0)
+ gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (fs_dlg), TRUE);
+#endif
+
+ /* extra widget */
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 0);
+ gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (fs_dlg), vbox);
+
+ hbox = gtk_hbox_new (FALSE, 12);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 6);
+
+ label = gtk_label_new_with_mnemonic (_("_Image type:"));
+ gtk_box_pack_start (GTK_BOX (hbox),
+ label,
+ FALSE, FALSE, 0);
+
+ opt_menu = gtk_combo_box_new_text ();
+ gtk_combo_box_append_text (GTK_COMBO_BOX (opt_menu), "png");
+ gtk_combo_box_append_text (GTK_COMBO_BOX (opt_menu), "jpeg");
+ gtk_combo_box_append_text (GTK_COMBO_BOX (opt_menu), "bmp");
+ gtk_combo_box_set_active (GTK_COMBO_BOX (opt_menu), 0);
+ gtk_box_pack_start (GTK_BOX (hbox), opt_menu, TRUE, TRUE, 0);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), opt_menu);
+ gtk_widget_show_all (vbox);
+
+ if (gtk_dialog_run (GTK_DIALOG (fs_dlg)) == GTK_RESPONSE_ACCEPT)
+ {
+ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs_dlg));
+ sel_type = gtk_combo_box_get_active_text (GTK_COMBO_BOX (opt_menu));
+ if (! g_str_has_suffix (filename, sel_type))
+ {
+ gchar *tmp;
+ tmp = filename;
+ filename = g_strjoin (".", filename, sel_type, NULL);
+ g_free (tmp);
+ }
+ gdk_pixbuf_save (pixbuf, filename, sel_type, NULL, NULL);
+
+ g_free (filename);
+ g_free (sel_type);
+ }
+
+ gtk_widget_destroy (fs_dlg);
+ g_object_unref (pixbuf);
+}
+
+/**
+ * baobab_chart_is_frozen:
+ * @chart: the #BaobabChart to ask if frozen.
+ *
+ * Returns a boolean telling whether the chart is in a frozen state, meanning
+ * that no actions should be taken uppon it.
+ *
+ * Fails if @chart is not a #BaobabChart.
+ **/
+gboolean
+baobab_chart_is_frozen (GtkWidget *chart)
+{
+ BaobabChartPrivate *priv;
+
+ g_return_val_if_fail (BAOBAB_IS_CHART (chart), FALSE);
+
+ priv = BAOBAB_CHART_GET_PRIVATE (chart);
+ return priv->is_frozen;
+}
+
+/**
+ * baobab_chart_is_frozen:
+ * @chart: the #BaobabChart to obtain the highlighted it from.
+ *
+ * Returns a BaobabChartItem corresponding to the item that currently has mouse
+ * pointer over, or NULL if no item is highlighted.
+ *
+ * Fails if @chart is not a #BaobabChart.
+ **/
+BaobabChartItem *
+baobab_chart_get_highlighted_item (GtkWidget *chart)
+{
+ BaobabChartPrivate *priv;
+
+ g_return_val_if_fail (BAOBAB_IS_CHART (chart), NULL);
+
+ priv = BAOBAB_CHART_GET_PRIVATE (chart);
+ return (priv->highlighted_item ?
+ (BaobabChartItem *) priv->highlighted_item->data : NULL);
+}
+
+/**
+ * baobab_chart_can_zoom_in:
+ * @chart: the #BaobabChart to ask if can be zoomed in.
+ *
+ * Returns a boolean telling whether the chart can be zoomed in, given its current
+ * visualization conditions.
+ *
+ * Fails if @chart is not a #BaobabChart.
+ **/
+gboolean
+baobab_chart_can_zoom_in (GtkWidget *chart)
+{
+ BaobabChartPrivate *priv;
+ BaobabChartClass *class;
+
+ g_return_val_if_fail (BAOBAB_IS_CHART (chart), FALSE);
+
+ priv = BAOBAB_CHART (chart)->priv;
+ class = BAOBAB_CHART_GET_CLASS (chart);
+
+ if (class->can_zoom_in != NULL)
+ return class->can_zoom_in (chart) > 0;
+ else
+ return priv->max_depth > 1;
+}
+
+/**
+ * baobab_chart_can_zoom_out:
+ * @chart: the #BaobabChart to ask if can be zoomed out.
+ *
+ * Returns a boolean telling whether the chart can be zoomed out, given its current
+ * visualization conditions.
+ *
+ * Fails if @chart is not a #BaobabChart.
+ **/
+gboolean
+baobab_chart_can_zoom_out (GtkWidget *chart)
+{
+ BaobabChartPrivate *priv;
+ BaobabChartClass *class;
+
+ g_return_val_if_fail (BAOBAB_IS_CHART (chart), FALSE);
+
+ priv = BAOBAB_CHART (chart)->priv;
+ class = BAOBAB_CHART_GET_CLASS (chart);
+
+ if (class->can_zoom_out != NULL)
+ return class->can_zoom_out (chart) > 0;
+ else
+ return (priv->max_depth < BAOBAB_CHART_MAX_DEPTH);
+}
diff --git a/baobab/src/baobab-chart.h b/baobab/src/baobab-chart.h
new file mode 100644
index 00000000..19773024
--- /dev/null
+++ b/baobab/src/baobab-chart.h
@@ -0,0 +1,159 @@
+/*
+ * baobab-chart.h
+ *
+ * Copyright (C) 2006, 2007, 2008 Igalia
+ *
+ * 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
+ *
+ * Authors:
+ * Felipe Erias <[email protected]>
+ * Pablo Santamaria <[email protected]>
+ * Jacobo Aragunde <[email protected]>
+ * Eduardo Lima <[email protected]>
+ * Mario Sanchez <[email protected]>
+ * Miguel Gomez <[email protected]>
+ * Henrique Ferreiro <[email protected]>
+ * Alejandro Pinheiro <[email protected]>
+ * Carlos Sanmartin <[email protected]>
+ * Alejandro Garcia <[email protected]>
+ */
+
+#ifndef __BAOBAB_CHART_H__
+#define __BAOBAB_CHART_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define BAOBAB_CHART_TYPE (baobab_chart_get_type ())
+#define BAOBAB_CHART(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BAOBAB_CHART_TYPE, BaobabChart))
+#define BAOBAB_CHART_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), BAOBAB_CHART_TYPE, BaobabChartClass))
+#define BAOBAB_IS_CHART(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BAOBAB_CHART_TYPE))
+#define BAOBAB_IS_CHART_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), BAOBAB_CHART_TYPE))
+#define BAOBAB_CHART_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), BAOBAB_CHART_TYPE, BaobabChartClass))
+
+typedef struct _BaobabChart BaobabChart;
+typedef struct _BaobabChartClass BaobabChartClass;
+typedef struct _BaobabChartPrivate BaobabChartPrivate;
+typedef struct _BaobabChartColor BaobabChartColor;
+typedef struct _BaobabChartItem BaobabChartItem;
+
+struct _BaobabChart
+{
+ GtkWidget parent;
+
+ /* < private > */
+ BaobabChartPrivate *priv;
+};
+
+struct _BaobabChartColor
+{
+ gdouble red;
+ gdouble green;
+ gdouble blue;
+};
+
+struct _BaobabChartItem
+{
+ gchar *name;
+ gchar *size;
+ guint depth;
+ gdouble rel_start;
+ gdouble rel_size;
+ GtkTreeIter iter;
+ gboolean visible;
+ gboolean has_any_child;
+ gboolean has_visible_children;
+ GdkRectangle rect;
+
+ GList *parent;
+
+ gpointer data;
+};
+
+struct _BaobabChartClass
+{
+ GtkWidgetClass parent_class;
+
+ /* Signal prototypes */
+ void (* item_activated) (BaobabChart *chart,
+ GtkTreeIter *iter);
+
+ /* Abstract methods */
+ void (* draw_item) (GtkWidget *chart,
+ cairo_t *cr,
+ BaobabChartItem *item,
+ gboolean highlighted);
+
+ void (* pre_draw) (GtkWidget *chart,
+ cairo_t *cr);
+
+ void (* post_draw) (GtkWidget *chart,
+ cairo_t *cr);
+
+ void (* calculate_item_geometry) (GtkWidget *chart,
+ BaobabChartItem *item);
+
+ gboolean (* is_point_over_item) (GtkWidget *chart,
+ BaobabChartItem *item,
+ gdouble x,
+ gdouble y);
+
+ void (* get_item_rectangle) (GtkWidget *chart,
+ BaobabChartItem *item);
+
+ guint (* can_zoom_in) (GtkWidget *chart);
+ guint (* can_zoom_out) (GtkWidget *chart);
+};
+
+GType baobab_chart_get_type (void) G_GNUC_CONST;
+GtkWidget* baobab_chart_new (void);
+void baobab_chart_set_model_with_columns (GtkWidget *chart,
+ GtkTreeModel *model,
+ guint name_column,
+ guint size_column,
+ guint info_column,
+ guint percentage_column,
+ guint valid_column,
+ GtkTreePath *root);
+void baobab_chart_set_model (GtkWidget *chart,
+ GtkTreeModel *model);
+GtkTreeModel* baobab_chart_get_model (GtkWidget *chart);
+void baobab_chart_set_max_depth (GtkWidget *chart,
+ guint max_depth);
+guint baobab_chart_get_max_depth (GtkWidget *chart);
+void baobab_chart_set_root (GtkWidget *chart,
+ GtkTreePath *root);
+GtkTreePath *baobab_chart_get_root (GtkWidget *chart);
+void baobab_chart_freeze_updates (GtkWidget *chart);
+void baobab_chart_thaw_updates (GtkWidget *chart);
+void baobab_chart_get_item_color (BaobabChartColor *color,
+ gdouble position,
+ gint depth,
+ gboolean highlighted);
+void baobab_chart_move_up_root (GtkWidget *chart);
+void baobab_chart_zoom_in (GtkWidget *chart);
+void baobab_chart_zoom_out (GtkWidget *chart);
+void baobab_chart_save_snapshot (GtkWidget *chart);
+gboolean baobab_chart_is_frozen (GtkWidget *chart);
+BaobabChartItem *baobab_chart_get_highlighted_item (GtkWidget *chart);
+
+gboolean baobab_chart_can_zoom_in (GtkWidget *chart);
+gboolean baobab_chart_can_zoom_out (GtkWidget *chart);
+
+G_END_DECLS
+
+#endif
diff --git a/baobab/src/baobab-prefs.c b/baobab/src/baobab-prefs.c
new file mode 100644
index 00000000..c440aa7f
--- /dev/null
+++ b/baobab/src/baobab-prefs.c
@@ -0,0 +1,338 @@
+/*
+ * baobab-prefs.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 <string.h>
+#include <sys/stat.h>
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <mateconf/mateconf-client.h>
+#include <glibtop/mountlist.h>
+#include <glibtop/fsusage.h>
+#include "baobab.h"
+#include "baobab-utils.h"
+#include "baobab-prefs.h"
+
+enum
+{
+ COL_CHECK,
+ COL_DEVICE,
+ COL_MOUNT_D,
+ COL_MOUNT,
+ COL_TYPE,
+ COL_FS_SIZE,
+ COL_FS_AVAIL,
+ TOT_COLUMNS
+};
+
+static gboolean
+add_excluded_item (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ GSList **list)
+{
+ GSList *l;
+ gchar *mount;
+ gboolean check;
+
+ l = *list;
+
+ gtk_tree_model_get (model,
+ iter,
+ COL_MOUNT, &mount,
+ COL_CHECK, &check,
+ -1);
+
+ if (!check) {
+ l = g_slist_prepend (l, mount);
+ }
+
+ *list = l;
+
+ return FALSE;
+}
+
+static GSList *
+get_excluded_locations (GtkTreeModel *model)
+{
+ GSList *l = NULL;
+
+ gtk_tree_model_foreach (model,
+ (GtkTreeModelForeachFunc) add_excluded_item,
+ &l);
+
+ return g_slist_reverse (l);
+}
+
+static void
+save_mateconf (GtkTreeModel *model)
+{
+ GSList *l;
+
+ l = get_excluded_locations (model);
+
+ mateconf_client_set_list (baobab.mateconf_client,
+ BAOBAB_EXCLUDED_DIRS_KEY, MATECONF_VALUE_STRING,
+ l, NULL);
+
+ g_slist_foreach (l, (GFunc) g_free, NULL);
+ g_slist_free (l);
+}
+
+static void
+enable_home_cb (GtkToggleButton *togglebutton, gpointer user_data)
+{
+ gboolean enable;
+
+ enable = gtk_toggle_button_get_active (togglebutton);
+
+ mateconf_client_set_bool (baobab.mateconf_client,
+ BAOBAB_ENABLE_HOME_MONITOR_KEY,
+ enable,
+ NULL);
+
+}
+
+static void
+filechooser_response_cb (GtkDialog *dialog,
+ gint response_id,
+ GtkTreeModel *model)
+{
+ switch (response_id) {
+ case GTK_RESPONSE_HELP:
+ baobab_help_display (GTK_WINDOW (baobab.window),
+ "baobab.xml", "baobab-preferences");
+ break;
+ case GTK_RESPONSE_DELETE_EVENT:
+ case GTK_RESPONSE_CLOSE:
+ save_mateconf (model);
+ default:
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ break;
+ }
+}
+
+static void
+check_toggled (GtkCellRendererToggle *cell,
+ gchar *path_str,
+ GtkTreeModel *model)
+{
+ GtkTreeIter iter;
+ GtkTreePath *path = gtk_tree_path_new_from_string (path_str);
+ gboolean toggle;
+ gchar *mountpoint;
+
+ /* get toggled iter */
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_model_get (model,
+ &iter,
+ COL_CHECK, &toggle,
+ COL_MOUNT_D, &mountpoint,
+ -1);
+
+ /* check if root dir */
+ if (strcmp ("/", mountpoint) == 0)
+ goto clean_up;
+
+ /* set new value */
+ gtk_list_store_set (GTK_LIST_STORE (model),
+ &iter,
+ COL_CHECK, !toggle,
+ -1);
+
+ clean_up:
+ g_free (mountpoint);
+ gtk_tree_path_free (path);
+}
+
+static void
+create_tree_props (GtkBuilder *builder, GtkTreeModel *model)
+{
+ GtkCellRenderer *cell;
+ GtkTreeViewColumn *col;
+ GtkWidget *tvw;
+
+ tvw = GTK_WIDGET (gtk_builder_get_object (builder , "tree_view_props"));
+
+ /* checkbox column */
+ cell = gtk_cell_renderer_toggle_new ();
+ g_signal_connect (cell, "toggled",
+ G_CALLBACK (check_toggled), model);
+
+ col = gtk_tree_view_column_new_with_attributes (_("Scan"), cell,
+ "active", COL_CHECK,
+ NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (tvw), col);
+
+ /* First text column */
+ cell = gtk_cell_renderer_text_new ();
+ col = gtk_tree_view_column_new_with_attributes (_("Device"), cell,
+ "markup", COL_DEVICE,
+ "text", COL_DEVICE,
+ NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (tvw), col);
+
+ /* second text column */
+ cell = gtk_cell_renderer_text_new ();
+ col = gtk_tree_view_column_new_with_attributes (_("Mount Point"),
+ cell, "markup",
+ COL_MOUNT_D, "text",
+ COL_MOUNT_D, NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (tvw), col);
+
+ /* third text column */
+ cell = gtk_cell_renderer_text_new ();
+ col = gtk_tree_view_column_new_with_attributes (_("Filesystem Type"),
+ cell, "markup",
+ COL_TYPE, "text",
+ COL_TYPE, NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (tvw), col);
+
+ /* fourth text column */
+ cell = gtk_cell_renderer_text_new ();
+ col = gtk_tree_view_column_new_with_attributes (_("Total Size"),
+ cell, "markup",
+ COL_FS_SIZE, "text",
+ COL_FS_SIZE, NULL);
+ g_object_set (G_OBJECT (cell), "xalign", (gfloat) 1.0, NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (tvw), col);
+
+ /* fifth text column */
+ cell = gtk_cell_renderer_text_new ();
+ col = gtk_tree_view_column_new_with_attributes (_("Available"),
+ cell, "markup",
+ COL_FS_AVAIL, "text",
+ COL_FS_AVAIL, NULL);
+ g_object_set (G_OBJECT (cell), "xalign", (gfloat) 1.0, NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (tvw), col);
+
+ gtk_tree_view_set_model (GTK_TREE_VIEW (tvw), model);
+ g_object_unref (model);
+}
+
+static void
+fill_props_model (GtkListStore *store)
+{
+ GtkTreeIter iter;
+ guint lo;
+ glibtop_mountlist mountlist;
+ glibtop_mountentry *mountentry, *mountentry_tofree;
+ guint64 fstotal, fsavail;
+
+ mountentry_tofree = glibtop_get_mountlist (&mountlist, 0);
+
+ for (lo = 0, mountentry = mountentry_tofree;
+ lo < mountlist.number;
+ lo++, mountentry++) {
+ glibtop_fsusage fsusage;
+ gchar * total, *avail;
+ GFile *file;
+ gchar *uri;
+ gboolean excluded;
+
+ struct stat buf;
+ if (g_stat (mountentry->devname,&buf) == -1)
+ continue;
+
+ glibtop_get_fsusage (&fsusage, mountentry->mountdir);
+ fstotal = fsusage.blocks * fsusage.block_size;
+ fsavail = fsusage.bfree * fsusage.block_size;
+ total = g_format_size_for_display(fstotal);
+ avail = g_format_size_for_display(fsavail);
+ file = g_file_new_for_path (mountentry->mountdir);
+ uri = g_file_get_uri (file);
+ excluded = baobab_is_excluded_location (file);
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ COL_CHECK, !excluded,
+ COL_DEVICE, mountentry->devname,
+ COL_MOUNT_D, mountentry->mountdir,
+ COL_MOUNT, uri,
+ COL_TYPE, mountentry->type,
+ COL_FS_SIZE, total,
+ COL_FS_AVAIL, avail,
+ -1);
+ g_free(total);
+ g_free(avail);
+ g_free(uri);
+ g_object_unref(file);
+ }
+
+ g_free (mountentry_tofree);
+}
+
+void
+baobab_prefs_dialog (void)
+{
+ GtkBuilder *builder;
+ GtkWidget *dlg;
+ GtkWidget *check_enablehome;
+ GtkListStore *model;
+ GError *error = NULL;
+
+ builder = gtk_builder_new ();
+ gtk_builder_add_from_file (builder, BAOBAB_DIALOG_SCAN_UI_FILE, &error);
+
+ if (error) {
+ g_critical ("Can't load user interface file for the scan properties dialog: %s",
+ error->message);
+ g_object_unref (builder);
+ g_error_free (error);
+
+ return;
+ }
+
+ dlg = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_scan_props"));
+
+ gtk_window_set_transient_for (GTK_WINDOW (dlg),
+ GTK_WINDOW (baobab.window));
+
+ model = gtk_list_store_new (TOT_COLUMNS,
+ G_TYPE_BOOLEAN, /* checkbox */
+ G_TYPE_STRING, /* device */
+ G_TYPE_STRING, /*mount point display */
+ G_TYPE_STRING, /* mount point uri */
+ G_TYPE_STRING, /* fs type */
+ G_TYPE_STRING, /* fs size */
+ G_TYPE_STRING /* fs avail */
+ );
+
+ create_tree_props (builder, GTK_TREE_MODEL (model));
+ fill_props_model (model);
+
+ check_enablehome = GTK_WIDGET (gtk_builder_get_object (builder, "check_enable_home"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_enablehome),
+ baobab.monitor_home != NULL);
+
+ g_signal_connect (check_enablehome, "toggled",
+ G_CALLBACK (enable_home_cb), NULL);
+
+ g_signal_connect (dlg, "response",
+ G_CALLBACK (filechooser_response_cb), model);
+
+ gtk_widget_show_all (dlg);
+}
+
diff --git a/baobab/src/baobab-prefs.h b/baobab/src/baobab-prefs.h
new file mode 100644
index 00000000..57cbb972
--- /dev/null
+++ b/baobab/src/baobab-prefs.h
@@ -0,0 +1,28 @@
+/*
+ * baobab-prefs.h
+ * 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
+ */
+
+#ifndef __BAOBAB_PROPS_H__
+#define __BAOBAB_PROPS_H__
+
+void baobab_prefs_dialog (void);
+
+#endif /* __BAOBAB_PROPS_H__ */
diff --git a/baobab/src/baobab-remote-connect-dialog.c b/baobab/src/baobab-remote-connect-dialog.c
new file mode 100644
index 00000000..5e53f27e
--- /dev/null
+++ b/baobab/src/baobab-remote-connect-dialog.c
@@ -0,0 +1,803 @@
+/* Baobab - (C) 2005 Fabio Marzocca
+
+ baobab-remote-connect-dialog.c
+
+ Modified module from caja-connect-server-dialog.c
+ Released under same licence
+ */
+/*
+ * Caja
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * Caja 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.
+ *
+ * Caja 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; see the file COPYING. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include "baobab-remote-connect-dialog.h"
+
+#include <string.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+#include "baobab.h"
+
+
+/* Translators: all the strings in this file are meant to map the
+ similar strings inside caja-connect-server and should be
+ translated the same way
+*/
+
+struct _BaobabRemoteConnectDialogDetails {
+
+ GtkWidget *table;
+
+ GtkWidget *type_combo;
+ GtkWidget *uri_entry;
+ GtkWidget *server_entry;
+ GtkWidget *share_entry;
+ GtkWidget *port_entry;
+ GtkWidget *folder_entry;
+ GtkWidget *domain_entry;
+ GtkWidget *user_entry;
+};
+
+static void baobab_remote_connect_dialog_class_init (BaobabRemoteConnectDialogClass *class);
+static void baobab_remote_connect_dialog_init (BaobabRemoteConnectDialog *dialog);
+
+G_DEFINE_TYPE(BaobabRemoteConnectDialog, baobab_remote_connect_dialog, GTK_TYPE_DIALOG)
+
+#define RESPONSE_CONNECT GTK_RESPONSE_OK
+
+
+static void
+display_error_dialog (GError *error,
+ GFile *location,
+ GtkWindow *parent)
+{
+ GtkWidget *dlg;
+ char *parse_name;
+ char *error_message;
+
+ parse_name = g_file_get_parse_name (location);
+ error_message = g_strdup_printf (_("Cannot scan location \"%s\""),
+ parse_name);
+ g_free (parse_name);
+
+ dlg = gtk_message_dialog_new (parent,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ error_message, NULL);
+
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dlg),
+ "%s", error->message);
+
+ g_free (error_message);
+
+ gtk_dialog_run (GTK_DIALOG (dlg));
+ gtk_widget_destroy (dlg);
+}
+
+static void
+mount_enclosing_ready_cb (GFile *location,
+ GAsyncResult *res,
+ GtkWindow *app)
+{
+ gboolean success;
+ GError *error = NULL;
+
+ success = g_file_mount_enclosing_volume_finish (location,
+ res, &error);
+
+ if (success ||
+ g_error_matches (error, G_IO_ERROR, G_IO_ERROR_ALREADY_MOUNTED)) {
+ baobab_scan_location (location);
+ }
+ else {
+ display_error_dialog (error, location, app);
+ }
+
+ if (error)
+ g_error_free (error);
+
+ if (location)
+ g_object_unref (location);
+}
+
+static void
+connect_server_dialog_present_uri (GtkWindow *app,
+ GFile *location,
+ GtkWidget *widget)
+{
+ GMountOperation *op;
+
+ op = gtk_mount_operation_new (GTK_WINDOW (widget));
+
+ g_file_mount_enclosing_volume (location,
+ 0, op,
+ NULL,
+ (GAsyncReadyCallback) mount_enclosing_ready_cb,
+ app);
+ g_object_unref (op);
+}
+
+struct MethodInfo {
+ const char *scheme;
+ guint flags;
+};
+
+/* A collection of flags for MethodInfo.flags */
+enum {
+ DEFAULT_METHOD = 0x00000001,
+
+ /* Widgets to display in setup_for_type */
+ SHOW_SHARE = 0x00000010,
+ SHOW_PORT = 0x00000020,
+ SHOW_USER = 0x00000040,
+ SHOW_DOMAIN = 0x00000080,
+
+ IS_ANONYMOUS = 0x00001000
+};
+
+/* Remember to fill in descriptions below */
+static struct MethodInfo methods[] = {
+ /* FIXME: we need to alias ssh to sftp */
+ { "sftp", SHOW_PORT | SHOW_USER },
+ { "ftp", SHOW_PORT | SHOW_USER },
+ { "ftp", DEFAULT_METHOD | IS_ANONYMOUS | SHOW_PORT},
+ { "smb", SHOW_SHARE | SHOW_USER | SHOW_DOMAIN },
+ { "dav", SHOW_PORT | SHOW_USER },
+ /* FIXME: hrm, shouldn't it work? */
+ { "davs", SHOW_PORT | SHOW_USER },
+ { NULL, 0 }, /* Custom URI method */
+};
+
+/* To get around non constant gettext strings */
+static const char*
+get_method_description (struct MethodInfo *meth)
+{
+ if (!meth->scheme) {
+ return _("Custom Location");
+ } else if (strcmp (meth->scheme, "sftp") == 0) {
+ return _("SSH");
+ } else if (strcmp (meth->scheme, "ftp") == 0) {
+ if (meth->flags & IS_ANONYMOUS) {
+ return _("Public FTP");
+ } else {
+ return _("FTP (with login)");
+ }
+ } else if (strcmp (meth->scheme, "smb") == 0) {
+ return _("Windows share");
+ } else if (strcmp (meth->scheme, "dav") == 0) {
+ return _("WebDAV (HTTP)");
+ } else if (strcmp (meth->scheme, "davs") == 0) {
+ return _("Secure WebDAV (HTTPS)");
+
+ /* No descriptive text */
+ } else {
+ return meth->scheme;
+ }
+}
+
+static void
+baobab_remote_connect_dialog_finalize (GObject *object)
+{
+ BaobabRemoteConnectDialog *dialog;
+
+ dialog = BAOBAB_REMOTE_CONNECT_DIALOG(object);
+
+ g_object_unref (dialog->details->uri_entry);
+ g_object_unref (dialog->details->server_entry);
+ g_object_unref (dialog->details->share_entry);
+ g_object_unref (dialog->details->port_entry);
+ g_object_unref (dialog->details->folder_entry);
+ g_object_unref (dialog->details->domain_entry);
+ g_object_unref (dialog->details->user_entry);
+
+ g_free (dialog->details);
+
+ G_OBJECT_CLASS (baobab_remote_connect_dialog_parent_class)->finalize (object);
+}
+
+static void
+connect_to_server (BaobabRemoteConnectDialog *dialog, GtkWindow *app)
+{
+ struct MethodInfo *meth;
+ char *uri;
+ GFile *location;
+ int index;
+ GtkTreeIter iter;
+
+ /* Get our method info */
+ gtk_combo_box_get_active_iter (GTK_COMBO_BOX (dialog->details->type_combo), &iter);
+ gtk_tree_model_get (gtk_combo_box_get_model (GTK_COMBO_BOX (dialog->details->type_combo)),
+ &iter, 0, &index, -1);
+ g_assert (index < G_N_ELEMENTS (methods) && index >= 0);
+ meth = &(methods[index]);
+
+ if (meth->scheme == NULL) {
+ uri = gtk_editable_get_chars (GTK_EDITABLE (dialog->details->uri_entry), 0, -1);
+ /* FIXME: we should validate it in some way? */
+ } else {
+ char *user, *port, *initial_path, *server, *folder ,*domain ;
+ char *t, *join;
+ gboolean free_initial_path, free_user, free_domain, free_port;
+
+ server = gtk_editable_get_chars (GTK_EDITABLE (dialog->details->server_entry), 0, -1);
+ if (strlen (server) == 0) {
+ GtkWidget *dlg;
+
+ dlg = gtk_message_dialog_new (GTK_WINDOW (dialog),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ _("Cannot Connect to Server. You must enter a name for the server."));
+
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dlg),
+ _("Please enter a name and try again."));
+
+ gtk_dialog_run (GTK_DIALOG (dlg));
+ gtk_widget_destroy (dlg);
+
+ g_free (server);
+ return;
+ }
+
+ user = "";
+ port = "";
+ initial_path = "";
+ domain = "";
+ free_initial_path = FALSE;
+ free_user = FALSE;
+ free_domain = FALSE;
+ free_port = FALSE;
+
+ /* FTP special case */
+ if (meth->flags & IS_ANONYMOUS) {
+ user = "anonymous";
+
+ /* SMB special case */
+ } else if (strcmp (meth->scheme, "smb") == 0) {
+ t = gtk_editable_get_chars (GTK_EDITABLE (dialog->details->share_entry), 0, -1);
+ initial_path = g_strconcat ("/", t, NULL);
+ free_initial_path = TRUE;
+ g_free (t);
+ }
+
+ if (gtk_widget_get_parent (dialog->details->port_entry) != NULL) {
+ free_port = TRUE;
+ port = gtk_editable_get_chars (GTK_EDITABLE (dialog->details->port_entry), 0, -1);
+ }
+ folder = gtk_editable_get_chars (GTK_EDITABLE (dialog->details->folder_entry), 0, -1);
+ if (gtk_widget_get_parent (dialog->details->user_entry) != NULL) {
+ free_user = TRUE;
+
+ t = gtk_editable_get_chars (GTK_EDITABLE (dialog->details->user_entry), 0, -1);
+
+ user = g_uri_escape_string (t, G_URI_RESERVED_CHARS_ALLOWED_IN_USERINFO, FALSE);
+
+ g_free (t);
+ }
+ if (gtk_widget_get_parent (dialog->details->domain_entry) != NULL) {
+ free_domain = TRUE;
+
+ domain = gtk_editable_get_chars (GTK_EDITABLE (dialog->details->domain_entry), 0, -1);
+
+ if (strlen (domain) != 0) {
+ t = user;
+
+ user = g_strconcat (domain , ";" , t, NULL);
+
+ if (free_user) {
+ g_free (t);
+ }
+
+ free_user = TRUE;
+ }
+ }
+
+ if (folder[0] != 0 &&
+ folder[0] != '/') {
+ join = "/";
+ } else {
+ join = "";
+ }
+
+ t = folder;
+ folder = g_strconcat (initial_path, join, t, NULL);
+ g_free (t);
+
+ t = folder;
+ folder = g_uri_escape_string (t, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, FALSE);
+ g_free (t);
+
+ uri = g_strdup_printf ("%s://%s%s%s%s%s%s",
+ meth->scheme,
+ user, (user[0] != 0) ? "@" : "",
+ server,
+ (port[0] != 0) ? ":" : "", port,
+ folder);
+
+ if (free_initial_path) {
+ g_free (initial_path);
+ }
+ g_free (server);
+ if (free_port) {
+ g_free (port);
+ }
+ g_free (folder);
+ if (free_user) {
+ g_free (user);
+ }
+ if (free_domain) {
+ g_free (domain);
+ }
+ }
+
+ gtk_widget_hide (GTK_WIDGET (dialog));
+
+ location = g_file_new_for_uri (uri);
+ g_free (uri);
+
+ connect_server_dialog_present_uri (app,
+ location,
+ GTK_WIDGET (dialog));
+}
+
+static void
+response_callback (BaobabRemoteConnectDialog *dialog,
+ int response_id,
+ GtkWindow *app)
+{
+ switch (response_id) {
+ case RESPONSE_CONNECT:
+ connect_to_server (dialog, app);
+ break;
+ case GTK_RESPONSE_NONE:
+ case GTK_RESPONSE_DELETE_EVENT:
+ case GTK_RESPONSE_CANCEL:
+ break;
+ default :
+ g_assert_not_reached ();
+ }
+}
+
+static void
+baobab_remote_connect_dialog_class_init (BaobabRemoteConnectDialogClass *class)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS (class);
+ gobject_class->finalize = baobab_remote_connect_dialog_finalize;
+}
+
+static void
+setup_for_type (BaobabRemoteConnectDialog *dialog)
+{
+ struct MethodInfo *meth;
+ int index, i;
+ GtkWidget *label, *table;
+ GtkTreeIter iter;
+
+ /* Get our method info */
+ gtk_combo_box_get_active_iter (GTK_COMBO_BOX (dialog->details->type_combo), &iter);
+ gtk_tree_model_get (gtk_combo_box_get_model (GTK_COMBO_BOX (dialog->details->type_combo)),
+ &iter, 0, &index, -1);
+ g_assert (index < G_N_ELEMENTS (methods) && index >= 0);
+ meth = &(methods[index]);
+
+ if (gtk_widget_get_parent (dialog->details->uri_entry) != NULL) {
+ gtk_container_remove (GTK_CONTAINER (dialog->details->table),
+ dialog->details->uri_entry);
+ }
+ if (gtk_widget_get_parent (dialog->details->server_entry) != NULL) {
+ gtk_container_remove (GTK_CONTAINER (dialog->details->table),
+ dialog->details->server_entry);
+ }
+ if (gtk_widget_get_parent (dialog->details->share_entry) != NULL) {
+ gtk_container_remove (GTK_CONTAINER (dialog->details->table),
+ dialog->details->share_entry);
+ }
+ if (gtk_widget_get_parent (dialog->details->port_entry) != NULL) {
+ gtk_container_remove (GTK_CONTAINER (dialog->details->table),
+ dialog->details->port_entry);
+ }
+ if (gtk_widget_get_parent (dialog->details->folder_entry) != NULL) {
+ gtk_container_remove (GTK_CONTAINER (dialog->details->table),
+ dialog->details->folder_entry);
+ }
+ if (gtk_widget_get_parent (dialog->details->user_entry) != NULL) {
+ gtk_container_remove (GTK_CONTAINER (dialog->details->table),
+ dialog->details->user_entry);
+ }
+ if (gtk_widget_get_parent (dialog->details->domain_entry) != NULL) {
+ gtk_container_remove (GTK_CONTAINER (dialog->details->table),
+ dialog->details->domain_entry);
+ }
+ /* Destroy all labels */
+ gtk_container_foreach (GTK_CONTAINER (dialog->details->table),
+ (GtkCallback) gtk_widget_destroy, NULL);
+
+
+ i = 1;
+ table = dialog->details->table;
+
+ if (meth->scheme == NULL) {
+ label = gtk_label_new_with_mnemonic (_("_Location (URI):"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table), label,
+ 0, 1,
+ i, i+1,
+ GTK_FILL, GTK_FILL,
+ 0, 0);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), dialog->details->uri_entry);
+ gtk_widget_show (dialog->details->uri_entry);
+ gtk_table_attach (GTK_TABLE (table), dialog->details->uri_entry,
+ 1, 2,
+ i, i+1,
+ GTK_FILL | GTK_EXPAND, GTK_FILL,
+ 0, 0);
+
+ i++;
+
+ return;
+ }
+
+ label = gtk_label_new_with_mnemonic (_("_Server:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table), label,
+ 0, 1,
+ i, i+1,
+ GTK_FILL, GTK_FILL,
+ 0, 0);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), dialog->details->server_entry);
+ gtk_widget_show (dialog->details->server_entry);
+ gtk_table_attach (GTK_TABLE (table), dialog->details->server_entry,
+ 1, 2,
+ i, i+1,
+ GTK_FILL | GTK_EXPAND, GTK_FILL,
+ 0, 0);
+
+ i++;
+
+ label = gtk_label_new (_("Optional information:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table), label,
+ 0, 2,
+ i, i+1,
+ GTK_FILL, GTK_FILL,
+ 0, 0);
+
+ i++;
+
+ if (meth->flags & SHOW_SHARE) {
+ label = gtk_label_new_with_mnemonic (_("_Share:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table), label,
+ 0, 1,
+ i, i+1,
+ GTK_FILL, GTK_FILL,
+ 0, 0);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), dialog->details->share_entry);
+ gtk_widget_show (dialog->details->share_entry);
+ gtk_table_attach (GTK_TABLE (table), dialog->details->share_entry,
+ 1, 2,
+ i, i+1,
+ GTK_FILL | GTK_EXPAND, GTK_FILL,
+ 0, 0);
+
+ i++;
+ }
+
+ if (meth->flags & SHOW_PORT) {
+ label = gtk_label_new_with_mnemonic (_("_Port:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table), label,
+ 0, 1,
+ i, i+1,
+ GTK_FILL, GTK_FILL,
+ 0, 0);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), dialog->details->port_entry);
+ gtk_widget_show (dialog->details->port_entry);
+ gtk_table_attach (GTK_TABLE (table), dialog->details->port_entry,
+ 1, 2,
+ i, i+1,
+ GTK_FILL | GTK_EXPAND, GTK_FILL,
+ 0, 0);
+
+ i++;
+ }
+
+ label = gtk_label_new_with_mnemonic (_("_Folder:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table), label,
+ 0, 1,
+ i, i+1,
+ GTK_FILL, GTK_FILL,
+ 0, 0);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), dialog->details->folder_entry);
+ gtk_widget_show (dialog->details->folder_entry);
+ gtk_table_attach (GTK_TABLE (table), dialog->details->folder_entry,
+ 1, 2,
+ i, i+1,
+ GTK_FILL | GTK_EXPAND, GTK_FILL,
+ 0, 0);
+
+ i++;
+
+ if (meth->flags & SHOW_USER) {
+ label = gtk_label_new_with_mnemonic (_("_User Name:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table), label,
+ 0, 1,
+ i, i+1,
+ GTK_FILL, GTK_FILL,
+ 0, 0);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), dialog->details->user_entry);
+ gtk_widget_show (dialog->details->user_entry);
+ gtk_table_attach (GTK_TABLE (table), dialog->details->user_entry,
+ 1, 2,
+ i, i+1,
+ GTK_FILL | GTK_EXPAND, GTK_FILL,
+ 0, 0);
+
+ i++;
+ }
+
+ if (meth->flags & SHOW_DOMAIN) {
+ label = gtk_label_new_with_mnemonic (_("_Domain Name:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table), label,
+ 0, 1,
+ i, i+1,
+ GTK_FILL, GTK_FILL,
+ 0, 0);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), dialog->details->domain_entry);
+ gtk_widget_show (dialog->details->domain_entry);
+ gtk_table_attach (GTK_TABLE (table), dialog->details->domain_entry,
+ 1, 2,
+ i, i+1,
+ GTK_FILL | GTK_EXPAND, GTK_FILL,
+ 0, 0);
+
+ i++;
+ }
+}
+
+static void
+combo_changed_callback (GtkComboBox *combo_box,
+ BaobabRemoteConnectDialog *dialog)
+{
+ setup_for_type (dialog);
+}
+
+static void
+port_insert_text (GtkEditable *editable,
+ const gchar *new_text,
+ gint new_text_length,
+ gint *position)
+{
+ int pos;
+
+ if (new_text_length < 0) {
+ new_text_length = strlen (new_text);
+ }
+
+ /* Only allow digits to be inserted as port number */
+ for (pos = 0; pos < new_text_length; pos++) {
+ if (!g_ascii_isdigit (new_text[pos])) {
+ GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (editable));
+ if (toplevel != NULL) {
+ gdk_window_beep (gtk_widget_get_window (toplevel));
+ }
+ g_signal_stop_emission_by_name (editable, "insert_text");
+ return;
+ }
+ }
+}
+
+static void
+baobab_remote_connect_dialog_init (BaobabRemoteConnectDialog *dialog)
+{
+ GtkWidget *label;
+ GtkWidget *table;
+ GtkWidget *combo;
+ GtkWidget *hbox;
+ GtkWidget *vbox;
+ GtkListStore *store;
+ GtkCellRenderer *renderer;
+ int i;
+
+ dialog->details = g_new0 (BaobabRemoteConnectDialogDetails, 1);
+
+ gtk_window_set_title (GTK_WINDOW (dialog), _("Connect to Server"));
+ gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+ gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+ gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 2);
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+
+ vbox = gtk_vbox_new (FALSE, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ vbox, FALSE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ hbox = gtk_hbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (vbox),
+ hbox, FALSE, TRUE, 0);
+ gtk_widget_show (hbox);
+
+ label = gtk_label_new_with_mnemonic (_("Service _type:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_widget_show (label);
+ gtk_box_pack_start (GTK_BOX (hbox),
+ label, FALSE, FALSE, 0);
+
+ dialog->details->type_combo = combo = gtk_combo_box_new ();
+
+ /* each row contains: method index, textual description */
+ store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
+ gtk_combo_box_set_model (GTK_COMBO_BOX (combo), GTK_TREE_MODEL (store));
+ g_object_unref (G_OBJECT (store));
+
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, TRUE);
+ gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo), renderer, "text", 1);
+
+ for (i = 0; i < G_N_ELEMENTS (methods); i++) {
+ GtkTreeIter iter;
+ const gchar * const *supported;
+ int j;
+
+ /* skip methods that don't have corresponding MateVFSMethods */
+ supported = g_vfs_get_supported_uri_schemes (g_vfs_get_default ());
+
+ if (methods[i].scheme != NULL) {
+ gboolean found;
+
+ found = FALSE;
+ for (j = 0; supported[j] != NULL; j++) {
+ if (strcmp (methods[i].scheme, supported[j]) == 0) {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found) {
+ continue;
+ }
+ }
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ 0, i,
+ 1, get_method_description (&(methods[i])),
+ -1);
+
+
+ if (methods[i].flags & DEFAULT_METHOD) {
+ gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &iter);
+ }
+ }
+
+ if (gtk_combo_box_get_active (GTK_COMBO_BOX (combo)) < 0) {
+ /* default method not available, use any other */
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0);
+ }
+
+ gtk_widget_show (combo);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo);
+ gtk_box_pack_start (GTK_BOX (hbox),
+ combo, TRUE, TRUE, 0);
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (combo_changed_callback),
+ dialog);
+
+
+ hbox = gtk_hbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (vbox),
+ hbox, FALSE, TRUE, 0);
+ gtk_widget_show (hbox);
+
+ label = gtk_label_new_with_mnemonic (" ");
+ gtk_widget_show (label);
+ gtk_box_pack_start (GTK_BOX (hbox),
+ label, FALSE, FALSE, 0);
+
+
+ dialog->details->table = table = gtk_table_new (5, 2, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 12);
+ gtk_widget_show (table);
+ gtk_box_pack_start (GTK_BOX (hbox),
+ table, TRUE, TRUE, 0);
+
+ //dialog->details->uri_entry = caja_location_entry_new ();
+ dialog->details->uri_entry = gtk_entry_new();
+ dialog->details->server_entry = gtk_entry_new ();
+ dialog->details->share_entry = gtk_entry_new ();
+ dialog->details->port_entry = gtk_entry_new ();
+ g_signal_connect (dialog->details->port_entry, "insert_text", G_CALLBACK (port_insert_text),
+ NULL);
+ dialog->details->folder_entry = gtk_entry_new ();
+ dialog->details->domain_entry = gtk_entry_new ();
+ dialog->details->user_entry = gtk_entry_new ();
+
+ gtk_entry_set_activates_default (GTK_ENTRY (dialog->details->uri_entry), TRUE);
+ gtk_entry_set_activates_default (GTK_ENTRY (dialog->details->server_entry), TRUE);
+ gtk_entry_set_activates_default (GTK_ENTRY (dialog->details->share_entry), TRUE);
+ gtk_entry_set_activates_default (GTK_ENTRY (dialog->details->port_entry), TRUE);
+ gtk_entry_set_activates_default (GTK_ENTRY (dialog->details->folder_entry), TRUE);
+ gtk_entry_set_activates_default (GTK_ENTRY (dialog->details->domain_entry), TRUE);
+ gtk_entry_set_activates_default (GTK_ENTRY (dialog->details->user_entry), TRUE);
+
+ /* We need an extra ref so we can remove them from the table */
+ g_object_ref (dialog->details->uri_entry);
+ g_object_ref (dialog->details->server_entry);
+ g_object_ref (dialog->details->share_entry);
+ g_object_ref (dialog->details->port_entry);
+ g_object_ref (dialog->details->folder_entry);
+ g_object_ref (dialog->details->domain_entry);
+ g_object_ref (dialog->details->user_entry);
+
+ setup_for_type (dialog);
+
+ gtk_dialog_add_button (GTK_DIALOG (dialog),
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL);
+ gtk_dialog_add_button (GTK_DIALOG (dialog),
+ _("_Scan"),
+ RESPONSE_CONNECT);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog),
+ RESPONSE_CONNECT);
+}
+
+GtkWidget *
+baobab_remote_connect_dialog_new (GtkWindow *window, GFile *location)
+{
+ GtkWidget *dialog;
+
+ dialog = gtk_widget_new (BAOBAB_TYPE_REMOTE_CONNECT_DIALOG, NULL);
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (response_callback),
+ window);
+
+ if (window) {
+ gtk_window_set_screen (GTK_WINDOW (dialog),
+ gtk_window_get_screen (GTK_WINDOW (window)));
+ }
+
+ return dialog;
+}
+
diff --git a/baobab/src/baobab-remote-connect-dialog.h b/baobab/src/baobab-remote-connect-dialog.h
new file mode 100644
index 00000000..17095e5a
--- /dev/null
+++ b/baobab/src/baobab-remote-connect-dialog.h
@@ -0,0 +1,59 @@
+/* Baobab - (C) 2005 Fabio Marzocca
+
+ baobab-remote-connect-dialog.h
+
+ Modified module from BaobabRemoteConnectDialog.h
+ Released under same licence
+ */
+
+/*
+ * Caja
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * Caja 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.
+ *
+ * Caja 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; see the file COPYING. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef BAOBAB_REMOTE_CONNECT_DIALOG_H
+#define BAOBAB_REMOTE_CONNECT_DIALOG_H
+
+#include <gtk/gtk.h>
+#include <gio/gio.h>
+
+
+#define BAOBAB_TYPE_REMOTE_CONNECT_DIALOG (baobab_remote_connect_dialog_get_type())
+#define BAOBAB_REMOTE_CONNECT_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BAOBAB_TYPE_REMOTE_CONNECT_DIALOG, BaobabRemoteConnectDialog))
+#define BAOBAB_REMOTE_CONNECT_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), BAOBAB_TYPE_REMOTE_CONNECT_DIALOG, BaobabRemoteConnectDialogClass))
+#define BAOBAB_IS_REMOTE_CONNECT_DIALOG(obj) (G_TYPE_INSTANCE_CHECK_TYPE ((obj), BAOBAB_TYPE_REMOTE_CONNECT_DIALOG)
+
+typedef struct _BaobabRemoteConnectDialog BaobabRemoteConnectDialog;
+typedef struct _BaobabRemoteConnectDialogClass BaobabRemoteConnectDialogClass;
+typedef struct _BaobabRemoteConnectDialogDetails BaobabRemoteConnectDialogDetails;
+
+struct _BaobabRemoteConnectDialog {
+ GtkDialog parent;
+ BaobabRemoteConnectDialogDetails *details;
+};
+
+struct _BaobabRemoteConnectDialogClass {
+ GtkDialogClass parent_class;
+};
+
+GType baobab_remote_connect_dialog_get_type (void);
+GtkWidget* baobab_remote_connect_dialog_new (GtkWindow *window,
+ GFile *location);
+
+#endif /* BAOBAB_REMOTE_CONNECT_DIALOG_H */
diff --git a/baobab/src/baobab-ringschart.c b/baobab/src/baobab-ringschart.c
new file mode 100644
index 00000000..4e51c01e
--- /dev/null
+++ b/baobab/src/baobab-ringschart.c
@@ -0,0 +1,675 @@
+/*
+ * baobab-ringschart.c
+ *
+ * Copyright (C) 2008 Igalia
+ *
+ * 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
+ *
+ * Authors:
+ * Felipe Erias <[email protected]>
+ * Pablo Santamaria <[email protected]>
+ * Jacobo Aragunde <[email protected]>
+ * Eduardo Lima <[email protected]>
+ * Mario Sanchez <[email protected]>
+ * Miguel Gomez <[email protected]>
+ * Henrique Ferreiro <[email protected]>
+ * Alejandro Pinheiro <[email protected]>
+ * Carlos Sanmartin <[email protected]>
+ * Alejandro Garcia <[email protected]>
+ */
+
+#include <math.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include <pango/pangocairo.h>
+
+#include "baobab-chart.h"
+#include "baobab-ringschart.h"
+
+#define BAOBAB_RINGSCHART_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
+ BAOBAB_RINGSCHART_TYPE, \
+ BaobabRingschartPrivate))
+
+#define ITEM_BORDER_WIDTH 1
+#define CHART_PADDING 13
+#define ITEM_MIN_ANGLE 0.03
+#define EDGE_ANGLE 0.004
+
+#define SUBFOLDER_TIP_PADDING 3
+
+G_DEFINE_TYPE (BaobabRingschart, baobab_ringschart, BAOBAB_CHART_TYPE);
+
+typedef struct _BaobabRingschartItem BaobabRingschartItem;
+
+struct _BaobabRingschartItem
+{
+ gdouble min_radius;
+ gdouble max_radius;
+ gdouble start_angle;
+ gdouble angle;
+ gboolean continued;
+};
+
+struct _BaobabRingschartPrivate
+{
+ gboolean subfoldertips_enabled;
+ BaobabChartItem *highlighted_item;
+ guint tips_timeout_event;
+ GList *subtip_items;
+ gboolean drawing_subtips;
+ gint subtip_timeout;
+};
+
+static void baobab_ringschart_class_init (BaobabRingschartClass *class);
+static void baobab_ringschart_init (BaobabRingschart *object);
+static void baobab_ringschart_draw_sector (cairo_t *cr,
+ gdouble center_x, gdouble center_y,
+ gdouble radius, gdouble thickness,
+ gdouble init_angle, gdouble final_angle,
+ BaobabChartColor fill_color,
+ gboolean continued, guint border);
+static void baobab_ringschart_draw_item (GtkWidget *chart,
+ cairo_t *cr,
+ BaobabChartItem *item,
+ gboolean highlighted);
+static void baobab_ringschart_calculate_item_geometry (GtkWidget *chart,
+ BaobabChartItem *item);
+static gboolean baobab_ringschart_is_point_over_item (GtkWidget *chart,
+ BaobabChartItem *item,
+ gdouble x,
+ gdouble y);
+static void baobab_ringschart_get_point_min_rect (gdouble cx, gdouble cy,
+ gdouble radius, gdouble angle,
+ GdkRectangle *rect);
+static void baobab_ringschart_get_item_rectangle (GtkWidget *chart,
+ BaobabChartItem *item);
+static void baobab_ringschart_pre_draw (GtkWidget *chart, cairo_t *cr);
+static void baobab_ringschart_post_draw (GtkWidget *chart, cairo_t *cr);
+
+
+static void
+baobab_ringschart_class_init (BaobabRingschartClass *class)
+{
+ GObjectClass *obj_class;
+ BaobabChartClass *chart_class;
+
+ obj_class = G_OBJECT_CLASS (class);
+ chart_class = BAOBAB_CHART_CLASS (class);
+
+ /* BaobabChart abstract methods */
+ chart_class->draw_item = baobab_ringschart_draw_item;
+ chart_class->calculate_item_geometry = baobab_ringschart_calculate_item_geometry;
+ chart_class->is_point_over_item = baobab_ringschart_is_point_over_item;
+ chart_class->get_item_rectangle = baobab_ringschart_get_item_rectangle;
+ chart_class->pre_draw = baobab_ringschart_pre_draw;
+ chart_class->post_draw = baobab_ringschart_post_draw;
+
+ g_type_class_add_private (obj_class, sizeof (BaobabRingschartPrivate));
+}
+
+static void
+baobab_ringschart_init (BaobabRingschart *chart)
+{
+ BaobabRingschartPrivate *priv;
+ GtkSettings* settings;
+ gint timeout;
+
+ priv = BAOBAB_RINGSCHART_GET_PRIVATE (chart);
+
+ priv->subfoldertips_enabled = FALSE;
+ priv->highlighted_item = NULL;
+ priv->tips_timeout_event = 0;
+ priv->subtip_items = NULL;
+ priv->drawing_subtips = FALSE;
+
+ settings = gtk_settings_get_default ();
+ g_object_get (G_OBJECT (settings), "gtk-tooltip-timeout", &timeout, NULL);
+ priv->subtip_timeout = 2 * timeout;
+}
+
+static void
+baobab_ringschart_draw_sector (cairo_t *cr,
+ gdouble center_x,
+ gdouble center_y,
+ gdouble radius,
+ gdouble thickness,
+ gdouble init_angle,
+ gdouble final_angle,
+ BaobabChartColor fill_color,
+ gboolean continued,
+ guint border)
+{
+ cairo_set_line_width (cr, border);
+ if (radius > 0)
+ cairo_arc (cr, center_x, center_y, radius, init_angle, final_angle);
+ cairo_arc_negative (cr, center_x, center_y, radius+thickness,
+ final_angle, init_angle);
+ cairo_close_path(cr);
+
+ cairo_set_source_rgb (cr, fill_color.red, fill_color.green, fill_color.blue);
+ cairo_fill_preserve (cr);
+ cairo_set_source_rgb (cr, 0, 0, 0);
+ cairo_stroke (cr);
+
+ if (continued)
+ {
+ cairo_set_line_width (cr, 3);
+ cairo_arc (cr, center_x, center_y, radius+thickness + 4,
+ init_angle+EDGE_ANGLE, final_angle-EDGE_ANGLE);
+ cairo_stroke (cr);
+ }
+}
+
+static void
+baobab_ringschart_draw_item (GtkWidget *chart,
+ cairo_t *cr,
+ BaobabChartItem *item,
+ gboolean highlighted)
+{
+ BaobabRingschartPrivate *priv;
+ BaobabRingschartItem *data;
+ BaobabChartColor fill_color;
+ GtkAllocation allocation;
+
+ priv = BAOBAB_RINGSCHART_GET_PRIVATE (chart);
+
+ data = (BaobabRingschartItem *) item->data;
+
+ if (priv->drawing_subtips)
+ if ( (priv->highlighted_item) && (item->parent) &&
+ (((BaobabChartItem *) item->parent->data) == priv->highlighted_item) )
+ {
+ GList *node;
+ node = g_new0 (GList, 1);
+ node->data = (gpointer) item;
+
+ node->next = priv->subtip_items;
+ if (priv->subtip_items)
+ priv->subtip_items->prev = node;
+ priv->subtip_items = node;
+ }
+
+ baobab_chart_get_item_color (&fill_color,
+ data->start_angle / M_PI * 99,
+ item->depth,
+ highlighted);
+
+ gtk_widget_get_allocation (chart, &allocation);
+ baobab_ringschart_draw_sector (cr,
+ allocation.width / 2,
+ allocation.height / 2,
+ data->min_radius,
+ data->max_radius - data->min_radius,
+ data->start_angle,
+ data->start_angle + data->angle,
+ fill_color,
+ data->continued,
+ ITEM_BORDER_WIDTH);
+}
+
+static void
+baobab_ringschart_calculate_item_geometry (GtkWidget *chart,
+ BaobabChartItem *item)
+{
+ BaobabRingschartItem *data;
+ BaobabRingschartItem p_data;
+ BaobabChartItem *parent = NULL;
+ GtkAllocation allocation;
+
+ gdouble max_radius;
+ gdouble thickness;
+ guint max_depth;
+
+ max_depth = baobab_chart_get_max_depth (chart);
+
+ if (item->data == NULL)
+ item->data = g_new (BaobabRingschartItem, 1);
+
+ data = (BaobabRingschartItem *) item->data;
+
+ data->continued = FALSE;
+ item->visible = FALSE;
+
+ gtk_widget_get_allocation (chart, &allocation);
+ max_radius = MIN (allocation.width / 2, allocation.height / 2) - CHART_PADDING;
+ thickness = max_radius / (max_depth + 1);
+
+ if (item->parent == NULL)
+ {
+ data->min_radius = 0;
+ data->max_radius = thickness;
+ data->start_angle = 0;
+ data->angle = 2 * M_PI;
+ }
+ else
+ {
+ parent = (BaobabChartItem *) item->parent->data;
+ g_memmove (&p_data, parent->data, sizeof (BaobabRingschartItem));
+
+ data->min_radius = (item->depth) * thickness;
+
+ if (data->min_radius + thickness > max_radius)
+ return;
+ else
+ data->max_radius = data->min_radius + thickness;
+
+ data->angle = p_data.angle * item->rel_size / 100;
+ if (data->angle < ITEM_MIN_ANGLE)
+ return;
+
+ data->start_angle = p_data.start_angle + p_data.angle * item->rel_start / 100;
+
+ data->continued = (item->has_any_child) && (item->depth == max_depth);
+
+ parent->has_visible_children = TRUE;
+ }
+
+ item->visible = TRUE;
+ baobab_ringschart_get_item_rectangle (chart, item);
+}
+
+static gboolean
+baobab_ringschart_is_point_over_item (GtkWidget *chart,
+ BaobabChartItem *item,
+ gdouble x,
+ gdouble y)
+{
+ BaobabRingschartItem *data;
+ gdouble radius, angle;
+ GtkAllocation allocation;
+
+ data = (BaobabRingschartItem *) item->data;
+ gtk_widget_get_allocation (chart, &allocation);
+ x = x - allocation.width / 2;
+ y = y - allocation.height / 2;
+
+ radius = sqrt (x*x + y*y);
+ angle = atan2 (y, x);
+ angle = (angle > 0) ? angle : angle + 2*G_PI;
+
+ return (radius >= data->min_radius) &&
+ (radius <= data->max_radius) &&
+ (angle >= data->start_angle) &&
+ (angle <= data->start_angle + data->angle);
+}
+
+static void
+baobab_ringschart_get_point_min_rect (gdouble cx,
+ gdouble cy,
+ gdouble radius,
+ gdouble angle,
+ GdkRectangle *rect)
+{
+ gdouble x, y;
+
+ x = cx + cos (angle) * radius;
+ y = cy + sin (angle) * radius;
+
+ rect->x = MIN (rect->x, x);
+ rect->y = MIN (rect->y, y);
+ rect->width = MAX (rect->width, x);
+ rect->height = MAX (rect->height, y);
+}
+
+static void
+baobab_ringschart_get_item_rectangle (GtkWidget *chart,
+ BaobabChartItem *item)
+{
+ BaobabRingschartItem *data;
+ GdkRectangle rect;
+ gdouble cx, cy, r1, r2, a1, a2;
+ GtkAllocation allocation;
+
+ data = (BaobabRingschartItem *) item->data;
+
+ gtk_widget_get_allocation (chart, &allocation);
+ cx = allocation.width / 2;
+ cy = allocation.height / 2;
+ r1 = data->min_radius;
+ r2 = data->max_radius;
+ a1 = data->start_angle;
+ a2 = data->start_angle + data->angle;
+
+ rect.x = allocation.width;
+ rect.y = allocation.height;
+ rect.width = 0;
+ rect.height = 0;
+
+ baobab_ringschart_get_point_min_rect (cx, cy, r1, a1, &rect);
+ baobab_ringschart_get_point_min_rect (cx, cy, r2, a1, &rect);
+ baobab_ringschart_get_point_min_rect (cx, cy, r1, a2, &rect);
+ baobab_ringschart_get_point_min_rect (cx, cy, r2, a2, &rect);
+
+ if ( (a1 <= M_PI/2) && (a2 >= M_PI/2) )
+ rect.height = MAX (rect.height, cy + sin (M_PI/2) * r2);
+
+ if ( (a1 <= M_PI) && (a2 >= M_PI) )
+ rect.x = MIN (rect.x, cx + cos (M_PI) * r2);
+
+ if ( (a1 <= M_PI*1.5) && (a2 >= M_PI*1.5) )
+ rect.y = MIN (rect.y, cy + sin (M_PI*1.5) * r2);
+
+ if ( (a1 <= M_PI*2) && (a2 >= M_PI*2) )
+ rect.width = MAX (rect.width, cx + cos (M_PI*2) * r2);
+
+ rect.width -= rect.x;
+ rect.height -= rect.y;
+
+ item->rect = rect;
+}
+
+gboolean
+baobab_ringschart_subfolder_tips_timeout (gpointer data)
+{
+ BaobabRingschartPrivate *priv;
+
+ priv = BAOBAB_RINGSCHART_GET_PRIVATE (data);
+
+ priv->drawing_subtips = TRUE;
+
+ gtk_widget_queue_draw (GTK_WIDGET (data));
+
+ return FALSE;
+}
+
+void
+baobab_ringschart_clean_subforlder_tips_state (GtkWidget *chart)
+{
+ BaobabRingschartPrivate *priv;
+ GList *node;
+
+ priv = BAOBAB_RINGSCHART_GET_PRIVATE (chart);
+
+ if (priv->drawing_subtips)
+ gtk_widget_queue_draw (chart);
+
+ priv->drawing_subtips = FALSE;
+
+ if (priv->highlighted_item == NULL)
+ return;
+
+ if (priv->tips_timeout_event)
+ {
+ g_source_remove (priv->tips_timeout_event);
+ priv->tips_timeout_event = 0;
+ }
+
+ priv->highlighted_item = NULL;
+
+ /* Free subtip_items GList */
+ node = priv->subtip_items;
+ while (node != NULL)
+ {
+ priv->subtip_items = node->next;
+ g_free (node);
+
+ node = priv->subtip_items;
+ }
+ priv->subtip_items = NULL;
+}
+
+static void
+baobab_ringschart_draw_subfolder_tips (GtkWidget *chart, cairo_t *cr)
+{
+ BaobabRingschartPrivate *priv;
+ GList *node;
+ BaobabChartItem *item;
+ BaobabRingschartItem *data;
+
+ gdouble q_angle, q_width, q_height;
+
+ gdouble tip_x, tip_y;
+ gdouble middle_angle, middle_angle_n, middle_radius;
+ gdouble sector_center_x, sector_center_y;
+ gdouble a;
+ guint i;
+ GtkAllocation allocation;
+
+ PangoLayout *layout;
+ PangoRectangle layout_rect;
+ gchar *markup = NULL;
+
+ cairo_rectangle_t tooltip_rect;
+ GdkRectangle _rect, last_rect;
+
+ priv = BAOBAB_RINGSCHART_GET_PRIVATE (chart);
+
+ gtk_widget_get_allocation (chart, &allocation);
+ q_width = allocation.width / 2;
+ q_height = allocation.height / 2;
+ q_angle = atan2 (q_height, q_width);
+
+ memset (&last_rect, 0, sizeof (GdkRectangle));
+
+ cairo_save (cr);
+
+ node = priv->subtip_items;
+ while (node != NULL)
+ {
+ item = (BaobabChartItem *) node->data;
+ data = (BaobabRingschartItem *) item->data;
+
+ /* get the middle angle */
+ middle_angle = data->start_angle + data->angle / 2;
+
+ /* normalize the middle angle (take it to the first quadrant) */
+ middle_angle_n = middle_angle;
+ while (middle_angle_n > M_PI/2)
+ middle_angle_n -= M_PI;
+ middle_angle_n = ABS (middle_angle_n);
+
+ /* get the pango layout and its enclosing rectangle */
+ layout = gtk_widget_create_pango_layout (chart, NULL);
+ markup = g_strconcat ("<span size=\"small\">", item->name, "</span>", NULL);
+ pango_layout_set_markup (layout, markup, -1);
+ g_free (markup);
+ pango_layout_set_indent (layout, 0);
+ pango_layout_set_spacing (layout, 0);
+ pango_layout_get_pixel_extents (layout, NULL, &layout_rect);
+
+ /* get the center point of the tooltip rectangle */
+ if (middle_angle_n < q_angle)
+ {
+ tip_x = q_width - layout_rect.width/2 - SUBFOLDER_TIP_PADDING * 2;
+ tip_y = tan (middle_angle_n) * tip_x;
+ }
+ else
+ {
+ tip_y = q_height - layout_rect.height/2 - SUBFOLDER_TIP_PADDING * 2;
+ tip_x = tip_y / tan (middle_angle_n);
+ }
+
+ /* get the tooltip rectangle */
+ tooltip_rect.x = q_width + tip_x - layout_rect.width/2 - SUBFOLDER_TIP_PADDING;
+ tooltip_rect.y = q_height + tip_y - layout_rect.height/2 - SUBFOLDER_TIP_PADDING;
+ tooltip_rect.width = layout_rect.width + SUBFOLDER_TIP_PADDING * 2;
+ tooltip_rect.height = layout_rect.height + SUBFOLDER_TIP_PADDING * 2;
+
+ /* Check tooltip's width is not greater than half of the widget */
+ if (tooltip_rect.width > q_width)
+ {
+ g_object_unref (layout);
+ node = node->next;
+ continue;
+ }
+
+ /* translate tooltip rectangle and edge angles to the original quadrant */
+ a = middle_angle;
+ i = 0;
+ while (a > M_PI/2)
+ {
+ if (i % 2 == 0)
+ tooltip_rect.x = allocation.width - tooltip_rect.x
+ - tooltip_rect.width;
+ else
+ tooltip_rect.y = allocation.height - tooltip_rect.y
+ - tooltip_rect.height;
+
+ i++;
+ a -= M_PI/2;
+ }
+
+ /* get the GdkRectangle of the tooltip (with a little padding) */
+ _rect.x = tooltip_rect.x - 1;
+ _rect.y = tooltip_rect.y - 1;
+ _rect.width = tooltip_rect.width + 2;
+ _rect.height = tooltip_rect.height + 2;
+
+ /* Check if tooltip overlaps */
+ if (! gdk_rectangle_intersect (&_rect, &last_rect, NULL))
+ {
+ g_memmove (&last_rect, &_rect, sizeof (GdkRectangle));
+
+ /* Finally draw the tooltip to cairo! */
+
+ /* TODO: Do not hardcode colors */
+
+ /* avoid blurred lines */
+ tooltip_rect.x = floor (tooltip_rect.x) + 0.5;
+ tooltip_rect.y = floor (tooltip_rect.y) + 0.5;
+
+ middle_radius = data->min_radius + (data->max_radius - data->min_radius) / 2;
+ sector_center_x = q_width + middle_radius * cos (middle_angle);
+ sector_center_y = q_height + middle_radius * sin (middle_angle);
+
+ /* draw line from sector center to tooltip center */
+ cairo_set_line_width (cr, 1);
+ cairo_move_to (cr, sector_center_x, sector_center_y);
+ cairo_set_source_rgb (cr, 0.8275, 0.8431, 0.8118); /* tango: #d3d7cf */
+ cairo_line_to (cr, tooltip_rect.x + tooltip_rect.width / 2,
+ tooltip_rect.y + tooltip_rect.height / 2);
+ cairo_stroke (cr);
+
+ /* draw a tiny circle in sector center */
+ cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.6666 );
+ cairo_arc (cr, sector_center_x, sector_center_y, 1.0, 0, 2 * G_PI );
+ cairo_stroke (cr);
+
+ /* draw tooltip box */
+ cairo_set_line_width (cr, 0.5);
+ cairo_rectangle (cr, tooltip_rect.x, tooltip_rect.y,
+ tooltip_rect.width, tooltip_rect.height);
+ cairo_set_source_rgb (cr, 0.8275, 0.8431, 0.8118); /* tango: #d3d7cf */
+ cairo_fill_preserve(cr);
+ cairo_set_source_rgb (cr, 0.5333, 0.5412, 0.5216); /* tango: #888a85 */
+ cairo_stroke (cr);
+
+ /* draw the text inside the box */
+ cairo_set_source_rgb (cr, 0, 0, 0);
+ cairo_move_to (cr, tooltip_rect.x + SUBFOLDER_TIP_PADDING,
+ tooltip_rect.y + SUBFOLDER_TIP_PADDING);
+ pango_cairo_show_layout (cr, layout);
+ cairo_set_line_width (cr, 1);
+ cairo_stroke (cr);
+ }
+
+ /* Free stuff */
+ g_object_unref (layout);
+
+ node = node->next;
+ }
+
+ cairo_restore (cr);
+}
+
+static void
+baobab_ringschart_pre_draw (GtkWidget *chart, cairo_t *cr)
+{
+ BaobabRingschartPrivate *priv;
+ BaobabChartItem *hl_item;
+
+ priv = BAOBAB_RINGSCHART_GET_PRIVATE (chart);
+
+ hl_item = baobab_chart_get_highlighted_item (chart);
+
+ if ( (hl_item == NULL) || (! hl_item->has_visible_children) )
+ {
+ baobab_ringschart_clean_subforlder_tips_state (chart);
+
+ return;
+ }
+
+ if (hl_item != priv->highlighted_item)
+ {
+ baobab_ringschart_clean_subforlder_tips_state (chart);
+
+ priv->highlighted_item = hl_item;
+
+ /* Launch timer to show subfolder tooltips */
+ priv->tips_timeout_event = g_timeout_add (priv->subtip_timeout,
+ (GSourceFunc) baobab_ringschart_subfolder_tips_timeout,
+ (gpointer) chart);
+ }
+}
+
+static void
+baobab_ringschart_post_draw (GtkWidget *chart, cairo_t *cr)
+{
+ BaobabRingschartPrivate *priv;
+
+ priv = BAOBAB_RINGSCHART_GET_PRIVATE (chart);
+
+ if (priv->drawing_subtips)
+ {
+ /* Reverse the glist, which was created from the tail */
+ priv->subtip_items = g_list_reverse (priv->subtip_items);
+
+ baobab_ringschart_draw_subfolder_tips (chart, cr);
+ }
+}
+
+/* Public functions start here */
+
+/**
+ * baobab_ringschart_new:
+ *
+ * Constructor for the baobab_ringschart class
+ *
+ * Returns: a new #BaobabRingschart object
+ *
+ **/
+GtkWidget *
+baobab_ringschart_new (void)
+{
+ return g_object_new (BAOBAB_RINGSCHART_TYPE, NULL);
+}
+
+/**
+ * baobab_ringschart_set_subfoldertips_enabled:
+ * @chart: the #BaobabRingschart to set the
+ * @enabled: boolean to determine whether to show sub-folder tips.
+ *
+ * Enables/disables drawing tooltips of sub-forlders of the highlighted sector.
+ *
+ * Fails if @chart is not a #BaobabRingschart.
+ *
+ **/
+void
+baobab_ringschart_set_subfoldertips_enabled (GtkWidget *chart, gboolean enabled)
+{
+ BaobabRingschartPrivate *priv;
+
+ priv = BAOBAB_RINGSCHART_GET_PRIVATE (chart);
+
+ priv->subfoldertips_enabled = enabled;
+
+ if ( (! enabled) && (priv->drawing_subtips) )
+ {
+ /* Turn off currently drawn tips */
+ baobab_ringschart_clean_subforlder_tips_state (chart);
+ }
+}
diff --git a/baobab/src/baobab-ringschart.h b/baobab/src/baobab-ringschart.h
new file mode 100644
index 00000000..276bd7fe
--- /dev/null
+++ b/baobab/src/baobab-ringschart.h
@@ -0,0 +1,72 @@
+/*
+ * baobab-ringschart.h
+ *
+ * Copyright (C) 2008 igalia
+ *
+ * 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
+ *
+ * Authors:
+ * Felipe Erias <[email protected]>
+ * Pablo Santamaria <[email protected]>
+ * Jacobo Aragunde <[email protected]>
+ * Eduardo Lima <[email protected]>
+ * Mario Sanchez <[email protected]>
+ * Miguel Gomez <[email protected]>
+ * Henrique Ferreiro <[email protected]>
+ * Alejandro Pinheiro <[email protected]>
+ * Carlos Sanmartin <[email protected]>
+ * Alejandro Garcia <[email protected]>
+ */
+
+#ifndef __BAOBAB_RINGSCHART_H__
+#define __BAOBAB_RINGSCHART_H__
+
+#include <gtk/gtk.h>
+#include "baobab-chart.h"
+
+G_BEGIN_DECLS
+
+#define BAOBAB_RINGSCHART_TYPE (baobab_ringschart_get_type ())
+#define BAOBAB_RINGSCHART(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BAOBAB_RINGSCHART_TYPE, BaobabRingschart))
+#define BAOBAB_RINGSCHART_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), BAOBAB_RINGSCHART, BaobabRingschartClass))
+#define BAOBAB_IS_RINGSCHART(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BAOBAB_RINGSCHART_TYPE))
+#define BAOBAB_IS_RINGSCHART_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), BAOBAB_RINGSCHART_TYPE))
+#define BAOBAB_RINGSCHART_GET_CLASS (G_TYPE_INSTANCE_GET_CLASS ((obj), BAOBAB_RINGSCHART_TYPE, BaobabRingschartClass))
+
+typedef struct _BaobabRingschart BaobabRingschart;
+typedef struct _BaobabRingschartClass BaobabRingschartClass;
+typedef struct _BaobabRingschartPrivate BaobabRingschartPrivate;
+
+struct _BaobabRingschart
+{
+ BaobabChart parent;
+
+ BaobabRingschartPrivate *priv;
+};
+
+struct _BaobabRingschartClass
+{
+ BaobabChartClass parent_class;
+};
+
+GType baobab_ringschart_get_type (void) G_GNUC_CONST;
+GtkWidget *baobab_ringschart_new (void);
+void baobab_ringschart_set_subfoldertips_enabled (GtkWidget *chart,
+ gboolean enabled);
+
+G_END_DECLS
+
+#endif
diff --git a/baobab/src/baobab-scan.c b/baobab/src/baobab-scan.c
new file mode 100644
index 00000000..8c85d859
--- /dev/null
+++ b/baobab/src/baobab-scan.c
@@ -0,0 +1,381 @@
+/*
+ * baobab-scan.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 <string.h>
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <gio/gio.h>
+
+#include "baobab.h"
+#include "baobab-utils.h"
+
+
+/*
+ Hardlinks handling.
+
+ As long as we're optimistic about hardlinks count
+ over the whole system (250 files with st_nlink > 1 here),
+ we keep linear search. If it turns out to be an bottleneck
+ we can switch to an hash table or tree.
+
+ TODO: get real timings about this code. find out the average
+ number of files with st_nlink > 1 on average computer.
+
+ To save memory, we store only { inode, dev } instead of full
+ GFileInfo.
+
+ EDIT: /me stupid. I realize that this code was not called that often
+ 1 call per file with st_nlink > 1. BUT, i'm using pdumpfs to backup
+ my /etc. pdumpfs massively uses hard links. So there are more than
+ 5000 files with st_nlink > 1. I believe this is the worst case.
+*/
+
+typedef struct {
+ guint64 inode;
+ dev_t device;
+} BaobabHardLink;
+
+typedef GArray BaobabHardLinkArray;
+
+static BaobabHardLinkArray *
+baobab_hardlinks_array_create (void)
+{
+ return g_array_new (FALSE, FALSE, sizeof(BaobabHardLink));
+}
+
+static gboolean
+baobab_hardlinks_array_has (BaobabHardLinkArray *a,
+ BaobabHardLink *s)
+{
+ guint i;
+
+ for (i = 0; i < a->len; ++i) {
+ BaobabHardLink *cur = &g_array_index (a, BaobabHardLink, i);
+
+ /*
+ * cur->st_dev == s->st_dev is the common case and may be more
+ * expansive than cur->st_ino == s->st_ino
+ * so keep this order */
+ if (cur->inode == s->inode && cur->device == s->device)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* return FALSE if the element was already in the array */
+static gboolean
+baobab_hardlinks_array_add (BaobabHardLinkArray *a,
+ GFileInfo *s)
+{
+
+ if (g_file_info_has_attribute (s, G_FILE_ATTRIBUTE_UNIX_INODE) &&
+ g_file_info_has_attribute (s, G_FILE_ATTRIBUTE_UNIX_DEVICE))
+ {
+ BaobabHardLink hl;
+
+ hl.inode = g_file_info_get_attribute_uint64 (s,
+ G_FILE_ATTRIBUTE_UNIX_INODE);
+ hl.device = g_file_info_get_attribute_uint32 (s,
+ G_FILE_ATTRIBUTE_UNIX_DEVICE);
+
+ if (baobab_hardlinks_array_has (a, &hl))
+ return FALSE;
+
+ g_array_append_val (a, hl);
+
+ return TRUE;
+ }
+ else
+ {
+ g_warning ("Could not obtain inode and device for hardlink");
+ }
+
+ return FALSE;
+}
+
+static void
+baobab_hardlinks_array_free (BaobabHardLinkArray *a)
+{
+/* g_print ("HL len was %d\n", a->len); */
+
+ g_array_free (a, TRUE);
+}
+
+#define BLOCK_SIZE 512
+
+struct allsizes {
+ goffset size;
+ goffset alloc_size;
+ gint depth;
+};
+
+static const char *dir_attributes = \
+ G_FILE_ATTRIBUTE_STANDARD_NAME "," \
+ G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME "," \
+ G_FILE_ATTRIBUTE_STANDARD_TYPE "," \
+ G_FILE_ATTRIBUTE_STANDARD_SIZE "," \
+ G_FILE_ATTRIBUTE_UNIX_BLOCKS "," \
+ G_FILE_ATTRIBUTE_UNIX_NLINK "," \
+ G_FILE_ATTRIBUTE_UNIX_INODE "," \
+ G_FILE_ATTRIBUTE_UNIX_DEVICE "," \
+ G_FILE_ATTRIBUTE_ACCESS_CAN_READ;
+
+
+static gboolean
+is_in_dot_gvfs (GFile *file)
+{
+ static GFile *dot_gvfs_dir = NULL;
+ GFile *parent;
+ gboolean res = FALSE;
+
+ if (dot_gvfs_dir == NULL)
+ {
+ gchar *dot_gvfs;
+
+ dot_gvfs = g_build_filename (g_get_home_dir (), ".gvfs", NULL);
+
+ dot_gvfs_dir = g_file_new_for_path (dot_gvfs);
+
+ g_free (dot_gvfs);
+ }
+
+ parent = g_file_get_parent (file);
+
+ if (parent != NULL)
+ {
+ res = g_file_equal (parent, dot_gvfs_dir);
+ g_object_unref (parent);
+ }
+
+ return res;
+}
+
+static struct allsizes
+loopdir (GFile *file,
+ GFileInfo *info,
+ guint count,
+ BaobabHardLinkArray *hla,
+ gint current_depth)
+{
+ guint64 tempHLsize = 0;
+ gint elements = 0;
+ struct chan_data data;
+ struct allsizes retloop, temp;
+ GFileInfo *temp_info;
+ GFileEnumerator *file_enum;
+ gchar *dir_uri = NULL;
+ gchar *display_name = NULL;
+ gchar *parse_name = NULL;
+ GError *err = NULL;
+
+ count++;
+ retloop.size = 0;
+ retloop.alloc_size = 0;
+ retloop.depth = 0;
+
+ /* Skip the user excluded folders */
+ if (baobab_is_excluded_location (file))
+ goto exit;
+
+ /* Skip the virtual file systems */
+ if (is_virtual_filesystem (file))
+ goto exit;
+
+ /* FIXME: skip dirs in ~/.gvfs. It would be better to have a way
+ * to check if a file is a FUSE mountpoint instead of just
+ * hardcoding .gvfs */
+ if (is_in_dot_gvfs (file))
+ goto exit;
+
+ parse_name = g_file_get_parse_name (file);
+
+ if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SIZE))
+ retloop.size = g_file_info_get_size (info);
+
+ if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_UNIX_BLOCKS))
+ retloop.alloc_size = BLOCK_SIZE *
+ g_file_info_get_attribute_uint64 (info,
+ G_FILE_ATTRIBUTE_UNIX_BLOCKS);
+
+ if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME))
+ display_name = g_strdup (g_file_info_get_display_name (info));
+ else
+ /* paranoid fallback */
+ display_name = g_filename_display_basename (g_file_info_get_name (info));
+
+ /* load up the file enumerator */
+ file_enum = g_file_enumerate_children (file,
+ dir_attributes,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ NULL,
+ &err);
+
+ if (file_enum == NULL) {
+ if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED)) {
+ g_warning ("couldn't get dir enum for dir %s: %s\n",
+ parse_name, err->message);
+ }
+ goto exit;
+ }
+
+ /* All skipped folders (i.e. bad type, excluded, /proc) must be
+ skept *before* this point. Everything passes the prefill-model
+ will be part of the GUI. */
+
+ /* prefill the model */
+ data.size = 1;
+ data.alloc_size = 1;
+ data.depth = count - 1;
+ data.elements = -1;
+ data.display_name = display_name;
+ data.parse_name = parse_name;
+ data.tempHLsize = tempHLsize;
+ baobab_fill_model (&data);
+
+ g_clear_error (&err);
+ while ((temp_info = g_file_enumerator_next_file (file_enum,
+ NULL,
+ &err)) != NULL) {
+ GFileType temp_type = g_file_info_get_file_type (temp_info);
+ if (baobab.STOP_SCANNING) {
+ g_object_unref (temp_info);
+ g_object_unref (file_enum);
+ goto exit;
+ }
+
+ /* is a directory? */
+ if (temp_type == G_FILE_TYPE_DIRECTORY) {
+ GFile *child_dir = g_file_get_child (file,
+ g_file_info_get_name (temp_info));
+ temp = loopdir (child_dir, temp_info, count, hla, current_depth+1);
+ retloop.size += temp.size;
+ retloop.alloc_size += temp.alloc_size;
+ retloop.depth = ((temp.depth + 1) > retloop.depth) ? temp.depth + 1 : retloop.depth;
+ elements++;
+ g_object_unref (child_dir);
+ }
+
+ /* is it a regular file? */
+ else if (temp_type == G_FILE_TYPE_REGULAR) {
+
+ /* check for hard links only on local files */
+ if (g_file_info_has_attribute (temp_info,
+ G_FILE_ATTRIBUTE_UNIX_NLINK) &&
+ g_file_info_get_attribute_uint32 (temp_info,
+ G_FILE_ATTRIBUTE_UNIX_NLINK) > 1) {
+
+ if (!baobab_hardlinks_array_add (hla, temp_info)) {
+
+ /* we already acconted for it */
+ tempHLsize += g_file_info_get_size (temp_info);
+ g_object_unref (temp_info);
+ continue;
+ }
+ }
+
+ if (g_file_info_has_attribute (temp_info, G_FILE_ATTRIBUTE_UNIX_BLOCKS)) {
+ retloop.alloc_size += BLOCK_SIZE *
+ g_file_info_get_attribute_uint64 (temp_info,
+ G_FILE_ATTRIBUTE_UNIX_BLOCKS);
+ }
+ retloop.size += g_file_info_get_size (temp_info);
+ elements++;
+ }
+
+ /* ignore other types (symlinks, sockets, devices, etc) */
+
+ g_object_unref (temp_info);
+ }
+
+ /* won't be an error if we've finished normally */
+ if (err != NULL) {
+ g_warning ("error in dir %s: %s\n",
+ parse_name, err->message);
+ }
+
+ data.display_name = display_name;
+ data.parse_name = parse_name;
+ data.size = retloop.size;
+ data.alloc_size = retloop.alloc_size;
+ data.depth = count - 1;
+ data.elements = elements;
+ data.tempHLsize = tempHLsize;
+ baobab_fill_model (&data);
+ g_object_unref (file_enum);
+
+ exit:
+ g_free (dir_uri);
+ g_free (display_name);
+ g_free (parse_name);
+ if (err)
+ g_error_free (err);
+
+ return retloop;
+}
+
+void
+baobab_scan_execute (GFile *location)
+{
+ BaobabHardLinkArray *hla;
+ GFileInfo *info;
+ GError *err = NULL;
+ GFileType ftype;
+ struct allsizes sizes;
+
+ g_return_if_fail (location != NULL);
+
+ /* NOTE: for the root of the scan we follow symlinks */
+ info = g_file_query_info (location,
+ dir_attributes,
+ G_FILE_QUERY_INFO_NONE,
+ NULL,
+ &err);
+
+ if (info == NULL) {
+ char *parse_name = g_file_get_parse_name (location);
+ g_warning ("couldn't get info for dir %s: %s\n",
+ parse_name, err->message);
+ g_free (parse_name);
+ g_error_free (err);
+
+ return;
+ }
+
+ ftype = g_file_info_get_file_type (info);
+
+ if (ftype == G_FILE_TYPE_DIRECTORY) {
+ hla = baobab_hardlinks_array_create ();
+
+ sizes = loopdir (location, info, 0, hla, 0);
+ baobab.model_max_depth = sizes.depth;
+
+ baobab_hardlinks_array_free (hla);
+ }
+
+ g_object_unref (info);
+}
+
diff --git a/baobab/src/baobab-scan.h b/baobab/src/baobab-scan.h
new file mode 100644
index 00000000..c6400d56
--- /dev/null
+++ b/baobab/src/baobab-scan.h
@@ -0,0 +1,30 @@
+/*
+ * baobab-scan.h
+ * 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
+ */
+
+#ifndef __BAOBAB_SCAN_H__
+#define __BAOBAB_SCAN_H__
+
+#include <gio/gio.h>
+
+void baobab_scan_execute (GFile *location);
+
+#endif /* __BAOBAB_SCAN_H__ */
diff --git a/baobab/src/baobab-treemap.c b/baobab/src/baobab-treemap.c
new file mode 100644
index 00000000..7262b819
--- /dev/null
+++ b/baobab/src/baobab-treemap.c
@@ -0,0 +1,342 @@
+/*
+ * baobab-treemap.c
+ *
+ * Copyright (C) 2008 Igalia
+ *
+ * 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
+ *
+ * Authors:
+ * Fabio Marzocca <[email protected]>
+ * Paolo Borelli <[email protected]>
+ * Miguel Gomez <[email protected]>
+ * Eduardo Lima Mitev <[email protected]>
+ */
+
+#include <math.h>
+#include <string.h>
+#include <gtk/gtk.h>
+
+#include "baobab-chart.h"
+#include "baobab-treemap.h"
+
+#define BAOBAB_TREEMAP_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
+ BAOBAB_TREEMAP_TYPE, \
+ BaobabTreemapPrivate))
+
+#define ITEM_TEXT_PADDING 3
+#define ITEM_BORDER_WIDTH 1
+#define ITEM_PADDING 6
+
+#define ITEM_MIN_WIDTH 3
+#define ITEM_MIN_HEIGHT 3
+
+#define ITEM_SHOW_LABEL TRUE
+
+G_DEFINE_TYPE (BaobabTreemap, baobab_treemap, BAOBAB_CHART_TYPE);
+
+struct _BaobabTreemapPrivate
+{
+ guint max_visible_depth;
+ gboolean more_visible_childs;
+};
+
+static void baobab_treemap_class_init (BaobabTreemapClass *class);
+static void baobab_treemap_init (BaobabTreemap *object);
+static void baobab_treemap_draw_rectangle (GtkWidget *chart,
+ cairo_t *cr,
+ gdouble x, gdouble y, gdouble width, gdouble height,
+ BaobabChartColor fill_color,
+ const char *text,
+ gboolean show_text);
+static void baobab_treemap_draw_item (GtkWidget *chart,
+ cairo_t *cr,
+ BaobabChartItem *item,
+ gboolean highlighted);
+static void baobab_treemap_calculate_item_geometry (GtkWidget *chart,
+ BaobabChartItem *item);
+static gboolean baobab_treemap_is_point_over_item (GtkWidget *chart,
+ BaobabChartItem *item,
+ gdouble x,
+ gdouble y);
+static void baobab_treemap_get_item_rectangle (GtkWidget *chart,
+ BaobabChartItem *item);
+guint baobab_treemap_can_zoom_in (GtkWidget *chart);
+guint baobab_treemap_can_zoom_out (GtkWidget *chart);
+
+static void
+baobab_treemap_class_init (BaobabTreemapClass *class)
+{
+ GObjectClass *obj_class;
+ BaobabChartClass *chart_class;
+
+ obj_class = G_OBJECT_CLASS (class);
+ chart_class = BAOBAB_CHART_CLASS (class);
+
+ /* BaobabChart abstract methods */
+ chart_class->draw_item = baobab_treemap_draw_item;
+ chart_class->calculate_item_geometry = baobab_treemap_calculate_item_geometry;
+ chart_class->is_point_over_item = baobab_treemap_is_point_over_item;
+ chart_class->get_item_rectangle = baobab_treemap_get_item_rectangle;
+ chart_class->can_zoom_in = baobab_treemap_can_zoom_in;
+ chart_class->can_zoom_out = baobab_treemap_can_zoom_out;
+
+ g_type_class_add_private (obj_class, sizeof (BaobabTreemapPrivate));
+}
+
+static void
+baobab_treemap_init (BaobabTreemap *chart)
+{
+ BaobabTreemapPrivate *priv;
+
+ priv = BAOBAB_TREEMAP_GET_PRIVATE (chart);
+
+ chart->priv = priv;
+}
+
+static void
+baobab_treemap_draw_rectangle (GtkWidget *chart,
+ cairo_t *cr,
+ gdouble x, gdouble y, gdouble width, gdouble height,
+ BaobabChartColor fill_color,
+ const char *text,
+ gboolean show_text)
+{
+ guint border = ITEM_BORDER_WIDTH;
+ PangoRectangle rect;
+ PangoLayout *layout;
+
+ cairo_stroke (cr);
+
+ cairo_set_line_width (cr, border);
+ cairo_rectangle (cr, x + border, y + border,
+ width - border*2,
+ height - border*2);
+ cairo_set_source_rgb (cr, fill_color.red, fill_color.green, fill_color.blue);
+ cairo_fill_preserve (cr);
+ cairo_set_source_rgb (cr, 0, 0, 0);
+ cairo_stroke (cr);
+
+ if ((show_text) && (ITEM_SHOW_LABEL))
+ {
+ layout = gtk_widget_create_pango_layout (chart, NULL);
+ pango_layout_set_markup (layout, text, -1);
+ pango_layout_get_pixel_extents (layout, NULL, &rect);
+
+ if ((rect.width + ITEM_TEXT_PADDING*2 <= width) &&
+ (rect.height + ITEM_TEXT_PADDING*2 <= height))
+ {
+ cairo_move_to (cr, x + width/2 - rect.width/2,
+ y + height/2 - rect.height/2);
+ pango_cairo_show_layout (cr, layout);
+ }
+
+ g_object_unref (layout);
+ }
+}
+
+static void
+baobab_treemap_draw_item (GtkWidget *chart,
+ cairo_t *cr,
+ BaobabChartItem *item,
+ gboolean highlighted)
+{
+ cairo_rectangle_t * rect;
+ BaobabChartColor fill_color;
+ GtkAllocation allocation;
+ gdouble width, height;
+
+ rect = (cairo_rectangle_t *) item->data;
+
+ gtk_widget_get_allocation (chart, &allocation);
+
+ if (item->depth % 2 != 0)
+ {
+ baobab_chart_get_item_color (&fill_color, rect->x/allocation.width*200,
+ item->depth, highlighted);
+ width = rect->width - ITEM_PADDING;
+ height = rect->height;
+ }
+ else
+ {
+ baobab_chart_get_item_color (&fill_color, rect->y/allocation.height*200,
+ item->depth, highlighted);
+ width = rect->width;
+ height = rect->height - ITEM_PADDING;
+ }
+
+ baobab_treemap_draw_rectangle (chart,
+ cr,
+ rect->x,
+ rect->y,
+ width,
+ height,
+ fill_color,
+ item->name,
+ (! item->has_visible_children) );
+}
+
+static void
+baobab_treemap_calculate_item_geometry (GtkWidget *chart,
+ BaobabChartItem *item)
+{
+ BaobabTreemapPrivate *priv;
+ cairo_rectangle_t p_area;
+ static cairo_rectangle_t *rect;
+ gdouble width, height;
+ BaobabChartItem *parent = NULL;
+ GtkAllocation allocation;
+
+ priv = BAOBAB_TREEMAP (chart)->priv;
+
+ if (item->depth == 0)
+ {
+ priv->max_visible_depth = 0;
+ priv->more_visible_childs = FALSE;
+ }
+
+ item->visible = FALSE;
+
+ if (item->parent == NULL)
+ {
+ gtk_widget_get_allocation (chart, &allocation);
+ p_area.x = 0 - ITEM_PADDING/2;
+ p_area.y = 0 - ITEM_PADDING/2;
+ p_area.width = allocation.width + ITEM_PADDING * 2;
+ p_area.height = allocation.height + ITEM_PADDING;
+ }
+ else
+ {
+ parent = (BaobabChartItem *) item->parent->data;
+ g_memmove (&p_area, parent->data, sizeof (cairo_rectangle_t));
+ }
+
+ if (item->data == NULL)
+ item->data = g_new (cairo_rectangle_t, 1);
+
+ rect = (cairo_rectangle_t *) item->data;
+
+ if (item->depth % 2 != 0)
+ {
+ width = p_area.width - ITEM_PADDING;
+
+ rect->x = p_area.x + (item->rel_start * width / 100) + ITEM_PADDING;
+ rect->y = p_area.y + ITEM_PADDING;
+ rect->width = width * item->rel_size / 100;
+ rect->height = p_area.height - ITEM_PADDING * 3;
+ }
+ else
+ {
+ height = p_area.height - ITEM_PADDING;
+
+ rect->x = p_area.x + ITEM_PADDING;
+ rect->y = p_area.y + (item->rel_start * height / 100) + ITEM_PADDING;
+ rect->width = p_area.width - ITEM_PADDING * 3;
+ rect->height = height * item->rel_size / 100;
+ }
+
+ if ((rect->width - ITEM_PADDING < ITEM_MIN_WIDTH) ||
+ (rect->height - ITEM_PADDING < ITEM_MIN_HEIGHT))
+ return;
+
+ rect->x = floor (rect->x) + 0.5;
+ rect->y = floor (rect->y) + 0.5;
+ rect->width = floor (rect->width);
+ rect->height = floor (rect->height);
+
+ item->visible = TRUE;
+
+ if (parent != NULL)
+ parent->has_visible_children = TRUE;
+
+ baobab_treemap_get_item_rectangle (chart, item);
+
+ if (item->depth == baobab_chart_get_max_depth (chart) + 1)
+ priv->more_visible_childs = TRUE;
+ else
+ priv->max_visible_depth = MAX (priv->max_visible_depth, item->depth);
+}
+
+static gboolean
+baobab_treemap_is_point_over_item (GtkWidget *chart,
+ BaobabChartItem *item,
+ gdouble x,
+ gdouble y)
+{
+ GdkRectangle *rect;
+
+ rect = &item->rect;
+ return ((x >= rect->x) && (x <= rect->x + rect->width) &&
+ (y >= rect->y) && (y <= rect->y + rect->height));
+}
+
+static void
+baobab_treemap_get_item_rectangle (GtkWidget *chart,
+ BaobabChartItem *item)
+{
+ cairo_rectangle_t *_rect;
+
+ _rect = (cairo_rectangle_t *) item->data;
+
+ item->rect.x = _rect->x;
+ item->rect.y = _rect->y;
+ if (item->depth % 2 != 0)
+ {
+ item->rect.width = _rect->width - ITEM_PADDING;
+ item->rect.height = _rect->height;
+ }
+ else
+ {
+ item->rect.width = _rect->width;
+ item->rect.height = _rect->height - ITEM_PADDING;
+ }
+
+}
+
+guint
+baobab_treemap_can_zoom_in (GtkWidget *chart)
+{
+ BaobabTreemapPrivate *priv;
+
+ priv = BAOBAB_TREEMAP (chart)->priv;
+
+ return MAX (0, (gint) (priv->max_visible_depth - 1));
+}
+
+guint
+baobab_treemap_can_zoom_out (GtkWidget *chart)
+{
+ BaobabTreemapPrivate *priv;
+
+ priv = BAOBAB_TREEMAP (chart)->priv;
+
+ return priv->more_visible_childs ? 1 : 0;
+}
+
+/* Public functions start here */
+
+/**
+ * baobab_treemap_new:
+ *
+ * Constructor for the baobab_treemap class
+ *
+ * Returns: a new #BaobabTreemap object
+ *
+ **/
+GtkWidget*
+baobab_treemap_new (void)
+{
+ return g_object_new (BAOBAB_TREEMAP_TYPE, NULL);
+}
diff --git a/baobab/src/baobab-treemap.h b/baobab/src/baobab-treemap.h
new file mode 100644
index 00000000..734bc272
--- /dev/null
+++ b/baobab/src/baobab-treemap.h
@@ -0,0 +1,64 @@
+/*
+ * baobab-treemap.h
+ *
+ * Copyright (C) 2008 igalia
+ *
+ * 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
+ *
+ * Authors:
+ * Fabio Marzocca <[email protected]>
+ * Paolo Borelli <[email protected]>
+ * Miguel Gomez <[email protected]>
+ * Eduardo Lima Mitev <[email protected]>
+ */
+
+#ifndef __BAOBAB_TREEMAP_H__
+#define __BAOBAB_TREEMAP_H__
+
+#include <gtk/gtk.h>
+#include "baobab-chart.h"
+
+G_BEGIN_DECLS
+
+#define BAOBAB_TREEMAP_TYPE (baobab_treemap_get_type ())
+#define BAOBAB_TREEMAP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BAOBAB_TREEMAP_TYPE, BaobabTreemap))
+#define BAOBAB_TREEMAP_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), BAOBAB_TREEMAP, BaobabTreemapClass))
+#define BAOBAB_IS_TREEMAP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BAOBAB_TREEMAP_TYPE))
+#define BAOBAB_IS_TREEMAP_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), BAOBAB_TREEMAP_TYPE))
+#define BAOBAB_TREEMAP_GET_CLASS (G_TYPE_INSTANCE_GET_CLASS ((obj), BAOBAB_TREEMAP_TYPE, BaobabTreemapClass))
+
+typedef struct _BaobabTreemap BaobabTreemap;
+typedef struct _BaobabTreemapClass BaobabTreemapClass;
+typedef struct _BaobabTreemapPrivate BaobabTreemapPrivate;
+
+struct _BaobabTreemap
+{
+ BaobabChart parent;
+
+ BaobabTreemapPrivate *priv;
+};
+
+struct _BaobabTreemapClass
+{
+ BaobabChartClass parent_class;
+};
+
+GType baobab_treemap_get_type (void) G_GNUC_CONST;
+GtkWidget* baobab_treemap_new (void);
+
+G_END_DECLS
+
+#endif
diff --git a/baobab/src/baobab-treeview.c b/baobab/src/baobab-treeview.c
new file mode 100644
index 00000000..2a3aad3f
--- /dev/null
+++ b/baobab/src/baobab-treeview.c
@@ -0,0 +1,336 @@
+/*
+ * baobab-treeview.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.h>
+#include <glib/gprintf.h>
+#include <glib/gi18n.h>
+#include <string.h>
+
+#include "baobab.h"
+#include "baobab-treeview.h"
+#include "baobab-cell-renderer-progress.h"
+#include "baobab-utils.h"
+#include "callbacks.h"
+
+static GtkTreeStore *
+create_model (void)
+{
+ GtkTreeStore *mdl = gtk_tree_store_new (NUM_TREE_COLUMNS,
+ G_TYPE_STRING, /* COL_DIR_NAME */
+ G_TYPE_STRING, /* COL_H_PARSENAME */
+ G_TYPE_DOUBLE, /* COL_H_PERC */
+ G_TYPE_STRING, /* COL_DIR_SIZE */
+ G_TYPE_UINT64, /* COL_H_SIZE */
+ G_TYPE_UINT64, /* COL_H_ALLOCSIZE */
+ G_TYPE_STRING, /* COL_ELEMENTS */
+ G_TYPE_INT, /* COL_H_ELEMENTS */
+ G_TYPE_STRING, /* COL_HARDLINK */
+ G_TYPE_UINT64 /* COL_H_HARDLINK */
+ );
+
+ return mdl;
+}
+
+static void
+on_tv_row_expanded (GtkTreeView *treeview,
+ GtkTreeIter *arg1,
+ GtkTreePath *arg2,
+ gpointer data)
+{
+ gtk_tree_view_columns_autosize (treeview);
+}
+
+static void
+on_tv_cur_changed (GtkTreeView *treeview, gpointer data)
+{
+ GtkTreeIter iter;
+ gchar *parsename = NULL;
+
+ gtk_tree_selection_get_selected (gtk_tree_view_get_selection (treeview), NULL, &iter);
+
+ if (gtk_tree_store_iter_is_valid (baobab.model, &iter)) {
+ gtk_tree_model_get (GTK_TREE_MODEL (baobab.model), &iter,
+ COL_H_PARSENAME, &parsename, -1);
+ }
+}
+
+static void
+contents_changed (void)
+{
+ if (messageyesno (_("Rescan your home folder?"),
+ _("The content of your home folder has changed. Select rescan to update the disk usage details."),
+ GTK_MESSAGE_QUESTION, _("_Rescan"), baobab.window) == GTK_RESPONSE_OK) {
+ baobab_rescan_current_dir ();
+ }
+ else {
+ /* Just update the total */
+ baobab_update_filesystem ();
+ }
+}
+
+static gboolean
+on_tv_button_press (GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer data)
+{
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ GFile *file;
+
+ gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
+ event->x, event->y,
+ &path, NULL, NULL, NULL);
+ if (!path)
+ return TRUE;
+
+ /* get the selected path */
+ g_free (baobab.selected_path);
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (baobab.model), &iter,
+ path);
+ gtk_tree_model_get (GTK_TREE_MODEL (baobab.model), &iter,
+ COL_H_PARSENAME, &baobab.selected_path, -1);
+
+ file = g_file_parse_name (baobab.selected_path);
+
+ if (baobab.CONTENTS_CHANGED_DELAYED) {
+ GFile *home_file;
+
+ home_file = g_file_new_for_path (g_get_home_dir ());
+ if (g_file_has_prefix (file, home_file)) {
+ baobab.CONTENTS_CHANGED_DELAYED = FALSE;
+ if (baobab.STOP_SCANNING) {
+ contents_changed ();
+ }
+ }
+ g_object_unref (home_file);
+ }
+
+ /* right-click */
+ if (event->button == 3) {
+
+ if (g_file_query_exists (file, NULL)) {
+ popupmenu_list (path, event, can_trash_file (file));
+ }
+ }
+
+ gtk_tree_path_free (path);
+ g_object_unref (file);
+
+ return FALSE;
+}
+
+static gboolean
+baobab_treeview_equal_func (GtkTreeModel *model,
+ gint column,
+ const gchar *key,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ gboolean results = TRUE;
+ gchar *name;
+
+ gtk_tree_model_get (model, iter, 1, &name, -1);
+
+ if (name != NULL) {
+ gchar * casefold_key;
+ gchar * casefold_name;
+
+ casefold_key = g_utf8_casefold (key, -1);
+ casefold_name = g_utf8_casefold (name, -1);
+
+ if ((casefold_key != NULL) &&
+ (casefold_name != NULL) &&
+ (strstr (casefold_name, casefold_key) != NULL)) {
+ results = FALSE;
+ }
+ g_free (casefold_key);
+ g_free (casefold_name);
+ g_free (name);
+ }
+ return results;
+}
+
+static void
+perc_cell_data_func (GtkTreeViewColumn *col,
+ GtkCellRenderer *renderer,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ gdouble perc;
+ gchar textperc[10];
+
+ gtk_tree_model_get (model, iter, COL_H_PERC, &perc, -1);
+
+ if (perc < 0)
+ strcpy (textperc, "-.- %");
+ else if (perc == 100.0)
+ strcpy (textperc, "100 %");
+ else
+ g_sprintf (textperc, " %.1f %%", perc);
+
+ g_object_set (renderer, "text", textperc, NULL);
+}
+
+GtkWidget *
+create_directory_treeview (void)
+{
+ GtkCellRenderer *cell;
+ GtkTreeViewColumn *col;
+ GtkWidget *scrolled;
+
+ GtkWidget *tvw = GTK_WIDGET (gtk_builder_get_object (baobab.main_ui, "treeview1"));
+
+ g_signal_connect (tvw, "row-expanded",
+ G_CALLBACK (on_tv_row_expanded), NULL);
+ g_signal_connect (tvw, "cursor-changed",
+ G_CALLBACK (on_tv_cur_changed), NULL);
+ g_signal_connect (tvw, "button-press-event",
+ G_CALLBACK (on_tv_button_press), NULL);
+
+ /* dir name column */
+ g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (tvw)), "changed",
+ G_CALLBACK (on_tv_selection_changed), NULL);
+ cell = gtk_cell_renderer_text_new ();
+ col = gtk_tree_view_column_new_with_attributes (NULL, cell, "markup",
+ COL_DIR_NAME, "text",
+ COL_DIR_NAME, NULL);
+ gtk_tree_view_column_set_sort_column_id (col, COL_DIR_NAME);
+ gtk_tree_view_column_set_reorderable (col, TRUE);
+ gtk_tree_view_column_set_title (col, _("Folder"));
+ gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_column_set_resizable (col, TRUE);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (tvw), col);
+
+ /* percentage bar & text column */
+ col = gtk_tree_view_column_new ();
+
+ cell = baobab_cell_renderer_progress_new ();
+ gtk_tree_view_column_pack_start (col, cell, FALSE);
+ gtk_tree_view_column_set_attributes (col, cell, "perc",
+ COL_H_PERC, NULL);
+
+ cell = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_pack_start (col, cell, TRUE);
+ gtk_tree_view_column_set_cell_data_func (col, cell,
+ perc_cell_data_func,
+ NULL, NULL);
+
+ g_object_set (G_OBJECT (cell), "xalign", (gfloat) 1.0, NULL);
+ gtk_tree_view_column_set_sort_column_id (col, COL_H_PERC);
+ gtk_tree_view_column_set_reorderable (col, TRUE);
+ gtk_tree_view_column_set_title (col, _("Usage"));
+ gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_column_set_resizable (col, FALSE);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (tvw), col);
+
+ /* directory size column */
+ cell = gtk_cell_renderer_text_new ();
+ col = gtk_tree_view_column_new_with_attributes (NULL, cell, "markup",
+ COL_DIR_SIZE, "text",
+ COL_DIR_SIZE, NULL);
+ g_object_set (G_OBJECT (cell), "xalign", (gfloat) 1.0, NULL);
+ gtk_tree_view_column_set_sort_column_id (col,
+ baobab.show_allocated ? COL_H_ALLOCSIZE : COL_H_SIZE);
+ gtk_tree_view_column_set_reorderable (col, TRUE);
+ gtk_tree_view_column_set_title (col, _("Size"));
+ gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_column_set_resizable (col, TRUE);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (tvw), col);
+
+ /* store this column, we need it when toggling 'allocated' */
+ g_object_set_data (G_OBJECT (tvw), "baobab_size_col", col);
+
+ /* objects column */
+ cell = gtk_cell_renderer_text_new ();
+ col = gtk_tree_view_column_new_with_attributes (NULL, cell, "markup",
+ COL_ELEMENTS, "text",
+ COL_ELEMENTS, NULL);
+ g_object_set (G_OBJECT (cell), "xalign", (gfloat) 1.0, NULL);
+ gtk_tree_view_column_set_sort_column_id (col, COL_H_ELEMENTS);
+ gtk_tree_view_column_set_reorderable (col, TRUE);
+ gtk_tree_view_column_set_title (col, _("Contents"));
+ gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_column_set_resizable (col, TRUE);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (tvw), col);
+
+ /* hardlink column */
+ cell = gtk_cell_renderer_text_new ();
+ col = gtk_tree_view_column_new_with_attributes (NULL, cell, "markup",
+ COL_HARDLINK, "text",
+ COL_HARDLINK, NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (tvw), col);
+
+ gtk_tree_view_collapse_all (GTK_TREE_VIEW (tvw));
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tvw), FALSE);
+ scrolled = GTK_WIDGET (gtk_builder_get_object (baobab.main_ui, "scrolledwindow1"));
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+
+ gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (tvw),
+ baobab_treeview_equal_func,
+ NULL, NULL);
+
+ baobab.model = create_model ();
+
+ /* By default, sort by size */
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (baobab.model),
+ baobab.show_allocated ? COL_H_ALLOCSIZE : COL_H_SIZE,
+ GTK_SORT_DESCENDING);
+
+ gtk_tree_view_set_model (GTK_TREE_VIEW (tvw),
+ GTK_TREE_MODEL (baobab.model));
+ g_object_unref (baobab.model);
+
+ return tvw;
+}
+
+void
+baobab_treeview_show_allocated_size (GtkWidget *tv,
+ gboolean show_allocated)
+{
+ gint sort_id;
+ gint new_sort_id;
+ GtkSortType order;
+ GtkTreeViewColumn *size_col;
+
+ g_return_if_fail (GTK_IS_TREE_VIEW (tv));
+
+ gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (baobab.model),
+ &sort_id, &order);
+
+ /* set the sort id for the size column */
+ size_col = g_object_get_data (G_OBJECT (tv), "baobab_size_col");
+ new_sort_id = show_allocated ? COL_H_ALLOCSIZE : COL_H_SIZE;
+ gtk_tree_view_column_set_sort_column_id (size_col, new_sort_id);
+
+ /* if we are currently sorted on size or allocated size,
+ * then trigger a resort (with the same order) */
+ if (sort_id == COL_H_SIZE || sort_id == COL_H_ALLOCSIZE) {
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (baobab.model),
+ new_sort_id, order);
+ }
+}
diff --git a/baobab/src/baobab-treeview.h b/baobab/src/baobab-treeview.h
new file mode 100644
index 00000000..38aca412
--- /dev/null
+++ b/baobab/src/baobab-treeview.h
@@ -0,0 +1,47 @@
+/*
+ * baobab-treeview.h
+ * 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
+ */
+
+#ifndef __BAOBAB_TV_H__
+#define __BAOBAB_TV_H__
+
+#include <gtk/gtk.h>
+
+/* tree model columns (_H_ are hidden) */
+enum
+{
+ COL_DIR_NAME,
+ COL_H_PARSENAME,
+ COL_H_PERC,
+ COL_DIR_SIZE,
+ COL_H_SIZE,
+ COL_H_ALLOCSIZE,
+ COL_ELEMENTS,
+ COL_H_ELEMENTS,
+ COL_HARDLINK,
+ COL_H_HARDLINK,
+ NUM_TREE_COLUMNS
+};
+
+GtkWidget *create_directory_treeview (void);
+void baobab_treeview_show_allocated_size (GtkWidget *tv, gboolean show_allocated);
+
+#endif /* __BAOBAB_TV_H__ */
diff --git a/baobab/src/baobab-utils.c b/baobab/src/baobab-utils.c
new file mode 100644
index 00000000..12fbcbfb
--- /dev/null
+++ b/baobab/src/baobab-utils.c
@@ -0,0 +1,567 @@
+/*
+ * baobab-utils.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 <glib.h>
+#include <glib/gstdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <glib/gi18n.h>
+#include <glib/gprintf.h>
+#include <gtk/gtk.h>
+#include <gio/gio.h>
+#include <glibtop/mountlist.h>
+#include <glibtop/fsusage.h>
+
+#include "baobab.h"
+#include "baobab-treeview.h"
+#include "baobab-utils.h"
+#include "callbacks.h"
+
+void
+baobab_get_filesystem (BaobabFS *fs)
+{
+ size_t i;
+ glibtop_mountlist mountlist;
+ glibtop_mountentry *mountentries;
+
+ memset (fs, 0, sizeof *fs);
+
+ mountentries = glibtop_get_mountlist (&mountlist, FALSE);
+
+ for (i = 0; i < mountlist.number; ++i) {
+ GFile *file;
+ glibtop_fsusage fsusage;
+
+ file = g_file_new_for_path (mountentries[i].mountdir);
+
+ if (!baobab_is_excluded_location (file)) {
+
+ glibtop_get_fsusage (&fsusage, mountentries[i].mountdir);
+
+ fs->total += fsusage.blocks * fsusage.block_size;
+ fs->avail += fsusage.bfree * fsusage.block_size;
+ fs->used += (fsusage.blocks - fsusage.bfree) * fsusage.block_size;
+ }
+
+ g_object_unref (file);
+ }
+
+ g_free (mountentries);
+}
+
+void
+filechooser_cb (GtkWidget *chooser,
+ gint response,
+ gpointer data)
+{
+ if (response == GTK_RESPONSE_OK) {
+ gchar *filename;
+ GFile *file;
+
+ filename = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (chooser));
+ gtk_widget_hide (chooser);
+
+ file = g_file_new_for_uri (filename);
+ baobab_scan_location (file);
+ g_free (filename);
+ g_object_unref (file);
+ }
+ else {
+ gtk_widget_hide (chooser);
+ }
+}
+
+/*
+ * GtkFileChooser to select a directory to scan
+ */
+gchar *
+dir_select (gboolean SEARCH, GtkWidget *parent)
+{
+ static GtkWidget *file_chooser = NULL;
+ GtkWidget *toggle;
+
+ if (file_chooser == NULL) {
+ file_chooser = gtk_file_chooser_dialog_new (_("Select Folder"),
+ GTK_WINDOW (parent),
+ GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OPEN,
+ GTK_RESPONSE_OK, NULL);
+
+ gtk_file_chooser_set_show_hidden (GTK_FILE_CHOOSER (file_chooser), FALSE);
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (file_chooser),
+ g_get_home_dir ());
+ /* add extra widget */
+ toggle = gtk_check_button_new_with_mnemonic (_("_Show hidden folders"));
+ gtk_widget_show (toggle);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), FALSE);
+ g_signal_connect ((gpointer) toggle, "toggled",
+ G_CALLBACK (on_toggled), file_chooser);
+ gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (file_chooser),
+ toggle);
+
+ g_signal_connect (file_chooser, "response",
+ G_CALLBACK (filechooser_cb), NULL);
+ g_signal_connect (file_chooser, "destroy",
+ G_CALLBACK (gtk_widget_destroyed), &file_chooser);
+
+ gtk_window_set_modal (GTK_WINDOW (file_chooser), TRUE);
+ gtk_window_set_position (GTK_WINDOW (file_chooser), GTK_WIN_POS_CENTER_ON_PARENT);
+ }
+
+ gtk_widget_show (GTK_WIDGET (file_chooser));
+
+ return NULL;
+}
+
+void
+on_toggled (GtkToggleButton *togglebutton, gpointer dialog)
+{
+ gtk_file_chooser_set_show_hidden (GTK_FILE_CHOOSER (dialog),
+ !gtk_file_chooser_get_show_hidden
+ (GTK_FILE_CHOOSER (dialog)));
+}
+
+void
+set_ui_action_sens (const gchar *name, gboolean sens)
+{
+ GtkAction *a;
+
+ a = GTK_ACTION (gtk_builder_get_object (baobab.main_ui, name));
+ gtk_action_set_sensitive (a, sens);
+}
+
+void
+set_ui_widget_sens (const gchar *name, gboolean sens)
+{
+ GtkWidget *w;
+
+ w = GTK_WIDGET (gtk_builder_get_object (baobab.main_ui, name));
+ gtk_widget_set_sensitive (w, sens);
+}
+
+gboolean
+show_bars (GtkTreeModel *mdl,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ GtkTreeIter parent;
+ gdouble perc;
+ gint readelements, size_col;
+ guint64 refsize, size;
+ char *sizecstr = NULL;
+
+ if (baobab.show_allocated)
+ size_col = (gint) COL_H_ALLOCSIZE;
+ else
+ size_col = (gint) COL_H_SIZE;
+
+ if (gtk_tree_model_iter_parent (mdl, &parent, iter)) {
+ gtk_tree_model_get (mdl, iter, COL_H_ELEMENTS,
+ &readelements, -1);
+
+ if (readelements == -1) {
+ gtk_tree_store_set (GTK_TREE_STORE (mdl), iter,
+ COL_DIR_SIZE, "--",
+ COL_ELEMENTS, "--", -1);
+ return FALSE;
+ }
+
+ gtk_tree_model_get (mdl, &parent, COL_H_ELEMENTS,
+ &readelements, -1);
+
+ gtk_tree_model_get (mdl, iter, size_col, &size, -1);
+ sizecstr = g_format_size_for_display (size);
+
+ if (readelements == -1) {
+ gtk_tree_store_set (GTK_TREE_STORE (mdl), iter,
+ COL_DIR_SIZE, sizecstr, -1);
+
+ g_free (sizecstr);
+ return FALSE;
+ }
+
+ gtk_tree_model_get (mdl, &parent, size_col, &refsize, -1);
+ perc = (refsize != 0) ? ((gdouble) size * 100) / (gdouble) refsize : 0.0;
+
+ gtk_tree_store_set (GTK_TREE_STORE (mdl), iter,
+ COL_DIR_SIZE, sizecstr,
+ COL_H_PERC, perc, -1);
+
+ g_free (sizecstr);
+ } else {
+ gtk_tree_model_get (mdl, iter, COL_H_ELEMENTS,
+ &readelements, -1);
+
+ if (readelements != -1) {
+ gtk_tree_model_get (mdl, iter, size_col, &size,
+ -1);
+ sizecstr = g_format_size_for_display (size);
+
+ gtk_tree_store_set (GTK_TREE_STORE (mdl), iter,
+ COL_H_PERC, 100.0,
+ COL_DIR_SIZE, sizecstr, -1);
+
+ g_free (sizecstr);
+ }
+ else {
+ gtk_tree_store_set (GTK_TREE_STORE (mdl), iter,
+ COL_DIR_SIZE, "--",
+ COL_ELEMENTS, "--", -1);
+ }
+ }
+
+ return FALSE;
+}
+
+void
+message (const gchar *primary_msg,
+ const gchar *secondary_msg,
+ GtkMessageType type,
+ GtkWidget *parent)
+{
+ GtkWidget *dialog;
+ dialog = gtk_message_dialog_new (GTK_WINDOW (parent),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ type,
+ GTK_BUTTONS_OK, "%s", primary_msg);
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ "%s", secondary_msg);
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+}
+
+gint
+messageyesno (const gchar *primary_msg,
+ const gchar *secondary_msg,
+ GtkMessageType type,
+ gchar *ok_button,
+ GtkWidget *parent)
+{
+ GtkWidget *dialog;
+ GtkWidget *button;
+ gint response;
+
+ dialog = gtk_message_dialog_new (GTK_WINDOW (parent),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ type,
+ GTK_BUTTONS_CANCEL,
+ "%s", primary_msg);
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ "%s", secondary_msg);
+
+ button = gtk_button_new_with_mnemonic (ok_button);
+ gtk_widget_set_can_default (button, TRUE);
+ gtk_widget_show (button);
+
+ gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, GTK_RESPONSE_OK);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+
+ return response;
+}
+
+gboolean
+baobab_check_dir (GFile *file)
+{
+ GFileInfo *info;
+ GError *error = NULL;
+ gboolean ret = TRUE;
+
+ if (baobab_is_excluded_location (file)) {
+ message("", _("Cannot check an excluded folder!"), GTK_MESSAGE_INFO, baobab.window);
+ return FALSE;
+ }
+
+ info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_TYPE,
+ G_FILE_QUERY_INFO_NONE,
+ NULL,
+ &error);
+
+ if (!info) {
+ message("", error->message, GTK_MESSAGE_INFO, baobab.window);
+ g_error_free (error);
+
+ return FALSE;
+ }
+
+ if ((g_file_info_get_file_type (info) != G_FILE_TYPE_DIRECTORY) ||
+ is_virtual_filesystem(file)) {
+
+ char *error_msg = NULL;
+ gchar *name = NULL;
+
+ name = g_file_get_parse_name (file);
+ error_msg = g_strdup_printf (_("\"%s\" is not a valid folder"),
+ name);
+
+ message (error_msg, _("Could not analyze disk usage."),
+ GTK_MESSAGE_ERROR, baobab.window);
+
+ g_free (error_msg);
+ g_free (name);
+ ret = FALSE;
+ }
+
+ g_object_unref(info);
+
+ return ret;
+}
+
+static void
+add_popupmenu_item (GtkMenu *pmenu, const gchar *label, const gchar *stock, GCallback item_cb)
+{
+ GtkWidget *item;
+ GtkWidget *image;
+
+ item = gtk_image_menu_item_new_with_mnemonic (label);
+ image = gtk_image_new_from_stock (stock, GTK_ICON_SIZE_MENU);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
+
+ g_signal_connect (item, "activate", item_cb, NULL);
+
+ gtk_container_add (GTK_CONTAINER (pmenu), item);
+}
+
+void
+popupmenu_list (GtkTreePath *path, GdkEventButton *event, gboolean can_trash)
+{
+ GtkWidget *pmenu;
+ gchar *path_to_string;
+
+ /* path_to_string is freed in callback function */
+ path_to_string = gtk_tree_path_to_string (path);
+
+ pmenu = gtk_menu_new ();
+
+ add_popupmenu_item (GTK_MENU (pmenu),
+ _("_Open Folder"),
+ "gtk-open",
+ G_CALLBACK (open_file_cb));
+
+ if (baobab.is_local && can_trash) {
+ add_popupmenu_item (GTK_MENU (pmenu),
+ _("Mo_ve to Trash"),
+ "gtk-delete",
+ G_CALLBACK (trash_dir_cb));
+ }
+
+ gtk_widget_show_all (pmenu);
+ gtk_menu_popup (GTK_MENU (pmenu), NULL, NULL, NULL, NULL,
+ event->button, event->time);
+}
+
+void
+open_file_with_application (GFile *file)
+{
+ GAppInfo *application;
+ gchar *primary;
+ GFileInfo *info;
+ gchar *uri_scheme;
+ const char *content;
+ gboolean local = FALSE;
+
+ info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+ G_FILE_QUERY_INFO_NONE,
+ NULL,
+ NULL);
+ if (!info) return;
+
+ uri_scheme = g_file_get_uri_scheme (file);
+ if (g_ascii_strcasecmp(uri_scheme,"file") == 0) local = TRUE;
+
+ content = g_file_info_get_content_type (info);
+ application = g_app_info_get_default_for_type (content, TRUE);
+
+ if (!application) {
+ primary = g_strdup_printf (_("Could not open folder \"%s\""),
+ g_file_get_basename (file));
+ message (primary,
+ _("There is no installed viewer capable "
+ "of displaying the folder."),
+ GTK_MESSAGE_ERROR,
+ baobab.window);
+ g_free (primary);
+ }
+ else {
+ GList *uris = NULL;
+ gchar *uri;
+
+ uri = g_file_get_uri (file);
+ uris = g_list_append (uris, uri);
+ g_app_info_launch_uris (application, uris, NULL, NULL);
+
+ g_list_free (uris);
+ g_free (uri);
+ }
+
+ g_free (uri_scheme);
+
+ if (application)
+ g_object_unref (application);
+
+ g_object_unref (info);
+}
+
+gboolean
+can_trash_file (GFile *file)
+{
+ GFileInfo *info;
+ gboolean can_trash = FALSE;
+
+ info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH,
+ G_FILE_QUERY_INFO_NONE,
+ NULL,
+ NULL);
+
+ if (info) {
+ if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH)) {
+ can_trash = g_file_info_get_attribute_boolean (info,
+ G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH);
+ }
+
+ g_object_unref (info);
+ }
+
+ return can_trash;
+}
+
+gboolean
+trash_file (GFile *file)
+{
+ GError *error = NULL;
+
+ if (!g_file_trash (file, NULL, &error)) {
+ GFileInfo *info;
+ char *str = NULL;
+ char *mess;
+
+ info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
+ G_FILE_QUERY_INFO_NONE,
+ NULL,
+ NULL);
+
+ if (info) {
+ const char *displayname = g_file_info_get_display_name (info);
+ if (displayname)
+ str = g_strdup_printf (_("Could not move \"%s\" to the Trash"),
+ displayname);
+
+ g_object_unref (info);
+ }
+
+ /* fallback */
+ if (str == NULL)
+ str = g_strdup (_("Could not move file to the Trash"));
+
+ mess = g_strdup_printf (_("Details: %s"), error->message);
+ message (str, mess, GTK_MESSAGE_ERROR, baobab.window);
+ g_free (str);
+ g_free (mess);
+ g_error_free (error);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gchar *
+baobab_mateconf_get_string_with_default (MateConfClient *client,
+ const gchar *key,
+ const gchar *def)
+{
+ gchar *val;
+
+ val = mateconf_client_get_string (client, key, NULL);
+ return val ? val : g_strdup (def);
+}
+
+gboolean
+baobab_help_display (GtkWindow *parent,
+ const gchar *file_name,
+ const gchar *link_id)
+{
+ GError *error = NULL;
+ char *uri;
+ gboolean ret;
+
+ uri = (link_id) ?
+ g_strdup_printf ("ghelp:%s#%s", file_name, link_id) :
+ g_strdup_printf ("ghelp:%s", file_name);
+
+ ret = gtk_show_uri (gtk_window_get_screen (parent),
+ uri, gtk_get_current_event_time (), &error);
+ g_free (uri);
+
+ if (error != NULL) {
+ GtkWidget *dialog;
+
+ dialog = gtk_message_dialog_new (parent,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE,
+ _("There was an error displaying help."));
+
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ "%s", error->message);
+
+ g_signal_connect (G_OBJECT (dialog), "response",
+ G_CALLBACK (gtk_widget_destroy), NULL);
+
+ gtk_widget_show (dialog);
+
+ g_error_free (error);
+ }
+
+ return ret;
+}
+
+gboolean
+is_virtual_filesystem (GFile *file)
+{
+ gboolean ret = FALSE;
+ char *path;
+
+ path = g_file_get_path (file);
+
+ /* FIXME: we need a better way to check virtual FS */
+ if (path != NULL) {
+ if ((strcmp (path, "/proc") == 0) ||
+ (strcmp (path, "/sys") == 0))
+ ret = TRUE;
+ }
+
+ g_free (path);
+
+ return ret;
+}
diff --git a/baobab/src/baobab-utils.h b/baobab/src/baobab-utils.h
new file mode 100644
index 00000000..37330240
--- /dev/null
+++ b/baobab/src/baobab-utils.h
@@ -0,0 +1,49 @@
+/*
+ * baobab-utils.h
+ * 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
+ */
+
+#ifndef __BAOBAB_UTILS_H__
+#define __BAOBAB_UTILS_H__
+
+#include "baobab.h"
+
+void baobab_get_filesystem (BaobabFS *fs);
+gchar* dir_select (gboolean, GtkWidget *);
+void on_toggled (GtkToggleButton *, gpointer);
+void stop_scan (void);
+gboolean show_bars (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data);
+void message (const gchar *primary_msg, const gchar *secondary_msg, GtkMessageType type, GtkWidget *parent);
+gint messageyesno (const gchar *primary_msg, const gchar *secondary_msg, GtkMessageType type, gchar * ok_button, GtkWidget *parent);
+gboolean baobab_check_dir (GFile *);
+void popupmenu_list (GtkTreePath *path, GdkEventButton *event, gboolean can_trash);
+void open_file_with_application (GFile *file);
+gboolean can_trash_file (GFile *file);
+gboolean trash_file (GFile *file);
+void set_ui_action_sens (const gchar *name, gboolean sens);
+void set_ui_widget_sens (const gchar *name, gboolean sens);
+gchar *baobab_mateconf_get_string_with_default (MateConfClient *client, const gchar *key, const gchar *def);
+gboolean baobab_help_display (GtkWindow *parent, const gchar *file_name, const gchar *link_id);
+gboolean is_virtual_filesystem (GFile *file);
+
+#endif /* __BAOBAB_UTILS_H__ */
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, &currentiter);
+ }
+ else if (data->depth == currentdepth) {
+ gtk_tree_model_iter_parent ((GtkTreeModel *) baobab.model,
+ &iterparent, &currentiter);
+ 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;
+}
diff --git a/baobab/src/baobab.h b/baobab/src/baobab.h
new file mode 100644
index 00000000..bab9b8bb
--- /dev/null
+++ b/baobab/src/baobab.h
@@ -0,0 +1,128 @@
+/*
+ * baobab.h
+ * 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
+ */
+
+#ifndef __BAOBAB_H__
+#define __BAOBAB_H__
+
+#include <time.h>
+#include <sys/types.h>
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <gio/gio.h>
+#include <mateconf/mateconf-client.h>
+
+typedef struct _baobab_fs baobab_fs;
+struct BaobabSearchOpt;
+
+#define BAOBAB_UI_FILE PKGDATADIR "/baobab-main-window.ui"
+#define BAOBAB_DIALOG_SCAN_UI_FILE PKGDATADIR "/baobab-dialog-scan-props.ui"
+
+/* Settings */
+#define BAOBAB_KEY_DIR "/apps/baobab"
+#define BAOBAB_TOOLBAR_VISIBLE_KEY BAOBAB_KEY_DIR "/ui/toolbar_visible"
+#define BAOBAB_STATUSBAR_VISIBLE_KEY BAOBAB_KEY_DIR "/ui/statusbar_visible"
+#define BAOBAB_SUBFLSTIPS_VISIBLE_KEY BAOBAB_KEY_DIR "/ui/baobab_subfoldertips_visible"
+#define BAOBAB_EXCLUDED_DIRS_KEY BAOBAB_KEY_DIR "/properties/skip_scan_uri_list"
+#define BAOBAB_ENABLE_HOME_MONITOR_KEY BAOBAB_KEY_DIR "/properties/enable_home_monitor"
+#define SYSTEM_TOOLBAR_STYLE_KEY "/desktop/mate/interface/toolbar_style"
+
+typedef struct _BaobabChartMenu BaobabChartMenu;
+
+struct _BaobabChartMenu {
+ GtkWidget *widget;
+ GtkWidget *up_item;
+ GtkWidget *zoom_in_item;
+ GtkWidget *zoom_out_item;
+ GtkWidget *subfolders_item;
+ GtkWidget *snapshot_item;
+ GtkWidget *set_root_item;
+};
+
+typedef struct _BaobabFS BaobabFS;
+
+struct _BaobabFS {
+ guint64 total;
+ guint64 used;
+ guint64 avail;
+};
+
+typedef struct _BaobabApplication BaobabApplication;
+
+struct _BaobabApplication {
+ BaobabFS fs;
+
+ GtkBuilder *main_ui;
+ GtkWidget *window;
+ GtkWidget *tree_view;
+ GtkWidget *rings_chart;
+ GtkWidget *treemap_chart;
+ GtkWidget *current_chart;
+ GtkWidget *chart_type_combo;
+ BaobabChartMenu *chart_menu;
+ GtkWidget *toolbar;
+ GtkWidget *spinner;
+ GtkWidget *statusbar;
+ GtkTreeStore *model;
+ gboolean STOP_SCANNING;
+ gboolean CONTENTS_CHANGED_DELAYED;
+ GSList *excluded_locations;
+ gboolean show_allocated;
+ gboolean is_local;
+
+ char *selected_path;
+
+ GFile *current_location;
+
+ GVolumeMonitor *monitor_vol;
+ GFileMonitor *monitor_home;
+
+ MateConfClient *mateconf_client;
+ gint model_max_depth;
+};
+
+/* Application singleton */
+BaobabApplication baobab;
+
+struct chan_data {
+ guint64 size;
+ guint64 alloc_size;
+ guint64 tempHLsize;
+ guint depth;
+ gint elements;
+ gchar *display_name;
+ gchar *parse_name;
+};
+
+void baobab_set_busy (gboolean busy);
+void baobab_update_filesystem (void);
+void baobab_scan_location (GFile *);
+void baobab_scan_home (void);
+void baobab_scan_root (void);
+void baobab_rescan_current_dir (void);
+void baobab_stop_scan (void);
+void baobab_fill_model (struct chan_data *);
+gboolean baobab_is_excluded_location (GFile *);
+void baobab_set_toolbar_visible (gboolean visible);
+void baobab_set_statusbar_visible (gboolean visible);
+void baobab_set_statusbar (const gchar *);
+
+#endif /* __BAOBAB_H_ */
diff --git a/baobab/src/callbacks.c b/baobab/src/callbacks.c
new file mode 100644
index 00000000..3dc8ede4
--- /dev/null
+++ b/baobab/src/callbacks.c
@@ -0,0 +1,394 @@
+/*
+ * callbacks.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
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <string.h>
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <mateconf/mateconf-client.h>
+#include <gio/gio.h>
+
+#include "baobab.h"
+#include "baobab-treeview.h"
+#include "baobab-utils.h"
+#include "callbacks.h"
+#include "baobab-prefs.h"
+#include "baobab-remote-connect-dialog.h"
+#include "baobab-chart.h"
+
+void
+on_menuscanhome_activate (GtkMenuItem *menuitem, gpointer user_data)
+{
+ baobab_scan_home ();
+}
+
+void
+on_menuallfs_activate (GtkMenuItem *menuitem, gpointer user_data)
+{
+ baobab_scan_root ();
+}
+
+void
+on_menuscandir_activate (GtkMenuItem *menuitem, gpointer user_data)
+{
+ dir_select (FALSE, baobab.window);
+}
+
+void
+on_esci1_activate (GtkObject *menuitem, gpointer user_data)
+{
+ baobab_stop_scan ();
+ gtk_main_quit ();
+}
+
+void on_about_activate(GtkMenuItem* menuitem, gpointer user_data)
+{
+ const gchar* const authors[] = {
+ "Fabio Marzocca <[email protected]>",
+ "Paolo Borelli <[email protected]>",
+ "BenoĆ®t Dejean <[email protected]>",
+ "Igalia (rings-chart and treemap widget) <www.igalia.com>",
+ NULL
+ };
+
+ const gchar* license[] = {
+ N_("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."),
+
+ N_("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."),
+
+ N_("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 Street, Fifth Floor, Boston, MA 02110-1301, USA")
+ };
+
+
+ gchar* license_trans = g_strjoin("\n\n", _(license[0]), _(license[1]), _(license[2]), NULL);
+
+ static const gchar copyright[] = "Copyright \xc2\xa9 2005-2010 Fabio Marzocca";
+
+ gtk_show_about_dialog (NULL,
+ "name", _("Baobab"),
+ "comments", _("A graphical tool to analyze disk usage."),
+ "version", VERSION,
+ "copyright", copyright,
+ "logo-icon-name", "baobab",
+ "license", license_trans,
+ "authors", authors,
+ "translator-credits", _("translator-credits"),
+ "wrap-license", TRUE,
+ NULL);
+
+ g_free(license_trans);
+}
+
+void
+on_menu_expand_activate (GtkMenuItem *menuitem, gpointer user_data)
+{
+ gtk_tree_view_expand_all (GTK_TREE_VIEW (baobab.tree_view));
+}
+
+void
+on_menu_collapse_activate (GtkMenuItem *menuitem, gpointer user_data)
+{
+ gtk_tree_view_collapse_all (GTK_TREE_VIEW (baobab.tree_view));
+}
+
+void
+on_menu_stop_activate (GtkMenuItem *menuitem, gpointer user_data)
+{
+ baobab_stop_scan ();
+}
+
+void
+on_menu_rescan_activate (GtkMenuItem *menuitem, gpointer user_data)
+{
+ baobab_rescan_current_dir ();
+}
+
+void
+on_tbscandir_clicked (GtkToolButton *toolbutton, gpointer user_data)
+{
+ dir_select (FALSE, baobab.window);
+}
+
+void
+on_tbscanhome_clicked (GtkToolButton *toolbutton, gpointer user_data)
+{
+ baobab_scan_home ();
+}
+
+void
+on_tbscanall_clicked (GtkToolButton *toolbutton, gpointer user_data)
+{
+ baobab_scan_root ();
+}
+
+void
+on_tb_scan_remote_clicked (GtkToolButton *toolbutton, gpointer user_data)
+{
+ GtkWidget *dlg;
+
+ dlg = baobab_remote_connect_dialog_new (GTK_WINDOW (baobab.window),
+ NULL);
+ gtk_dialog_run (GTK_DIALOG (dlg));
+
+ gtk_widget_destroy (dlg);
+}
+
+void
+on_menu_scan_rem_activate (GtkMenuItem *menuitem, gpointer user_data)
+{
+ on_tb_scan_remote_clicked (NULL, NULL);
+}
+
+void
+on_tbstop_clicked (GtkToolButton *toolbutton, gpointer user_data)
+{
+ baobab_stop_scan ();
+}
+
+void
+on_tbrescan_clicked (GtkToolButton *toolbutton, gpointer user_data)
+{
+ baobab_rescan_current_dir ();
+}
+
+gboolean
+on_delete_activate (GtkWidget *widget,
+ GdkEvent *event, gpointer user_data)
+{
+ on_esci1_activate (NULL, NULL);
+ return TRUE;
+}
+
+void
+open_file_cb (GtkMenuItem *pmenu, gpointer dummy)
+{
+ GFile *file;
+
+ g_assert (!dummy);
+ g_assert (baobab.selected_path);
+
+ file = g_file_parse_name (baobab.selected_path);
+
+ if (!g_file_query_exists (file, NULL)) {
+ message (_("The document does not exist."), "",
+ GTK_MESSAGE_INFO, baobab.window);
+ g_object_unref (file);
+ return;
+ }
+
+ open_file_with_application (file);
+ g_object_unref (file);
+}
+
+void
+trash_dir_cb (GtkMenuItem *pmenu, gpointer dummy)
+{
+ GFile *file;
+
+ g_assert (!dummy);
+ g_assert (baobab.selected_path);
+
+ file = g_file_parse_name (baobab.selected_path);
+
+ if (trash_file (file)) {
+ GtkTreeIter iter;
+ guint64 filesize;
+ GtkTreeSelection *selection;
+
+ selection =
+ gtk_tree_view_get_selection ((GtkTreeView *) baobab.
+ tree_view);
+ gtk_tree_selection_get_selected (selection, NULL, &iter);
+ gtk_tree_model_get ((GtkTreeModel *) baobab.model, &iter,
+ 5, &filesize, -1);
+ gtk_tree_store_remove (GTK_TREE_STORE (baobab.model),
+ &iter);
+ }
+
+ g_object_unref (file);
+}
+
+void
+on_pref_menu (GtkAction *a, gpointer user_data)
+{
+ baobab_prefs_dialog ();
+}
+
+void
+on_ck_allocated_activate (GtkToggleAction *action,
+ gpointer user_data)
+{
+ if (!baobab.is_local)
+ return;
+
+ baobab.show_allocated = gtk_toggle_action_get_active (action);
+
+ baobab_treeview_show_allocated_size (baobab.tree_view,
+ baobab.show_allocated);
+
+ baobab_set_busy (TRUE);
+ baobab_set_statusbar (_("Calculating percentage bars..."));
+ gtk_tree_model_foreach (GTK_TREE_MODEL (baobab.model),
+ show_bars, NULL);
+ baobab_set_busy (FALSE);
+ baobab_set_statusbar (_("Ready"));
+}
+
+void
+on_view_tb_activate (GtkToggleAction *action,
+ gpointer user_data)
+{
+ gboolean visible;
+
+ visible = gtk_toggle_action_get_active (action);
+ baobab_set_toolbar_visible (visible);
+
+ mateconf_client_set_bool (baobab.mateconf_client,
+ BAOBAB_TOOLBAR_VISIBLE_KEY,
+ visible,
+ NULL);
+}
+
+void
+on_view_sb_activate (GtkToggleAction *action,
+ gpointer user_data)
+{
+ gboolean visible;
+
+ visible = gtk_toggle_action_get_active (action);
+ baobab_set_statusbar_visible (visible);
+
+ mateconf_client_set_bool (baobab.mateconf_client,
+ BAOBAB_STATUSBAR_VISIBLE_KEY,
+ visible,
+ NULL);
+}
+
+void
+on_helpcontents_activate (GtkAction *a, gpointer user_data)
+{
+ baobab_help_display (GTK_WINDOW (baobab.window), "baobab", NULL);
+}
+
+void
+scan_folder_cb (GtkMenuItem *pmenu, gpointer dummy)
+{
+ GFile *file;
+
+ g_assert (!dummy);
+ g_assert (baobab.selected_path);
+
+ file = g_file_parse_name (baobab.selected_path);
+
+ if (!g_file_query_exists (file, NULL)) {
+ message (_("The folder does not exist."), "", GTK_MESSAGE_INFO, baobab.window);
+ }
+
+ baobab_scan_location (file);
+ g_object_unref (file);
+}
+
+void
+on_tv_selection_changed (GtkTreeSelection *selection, gpointer user_data)
+{
+ GtkTreeIter iter;
+
+ if (gtk_tree_selection_get_selected (selection, NULL, &iter)) {
+ GtkTreePath *path;
+
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (baobab.model), &iter);
+
+ baobab_chart_set_root (baobab.rings_chart, path);
+ baobab_chart_set_root (baobab.treemap_chart, path);
+
+ gtk_tree_path_free (path);
+ }
+}
+
+void
+on_move_upwards_cb (GtkCheckMenuItem *checkmenuitem, gpointer user_data)
+{
+ baobab_chart_move_up_root (baobab.current_chart);
+}
+
+void
+on_zoom_in_cb (GtkCheckMenuItem *checkmenuitem, gpointer user_data)
+{
+ baobab_chart_zoom_in (baobab.current_chart);
+}
+
+void
+on_zoom_out_cb (GtkCheckMenuItem *checkmenuitem, gpointer user_data)
+{
+ baobab_chart_zoom_out (baobab.current_chart);
+}
+
+void
+on_chart_snapshot_cb (GtkCheckMenuItem *checkmenuitem, gpointer user_data)
+{
+ baobab_chart_save_snapshot (baobab.current_chart);
+}
+
+void
+on_chart_type_change (GtkWidget *combo, gpointer user_data)
+{
+ GtkWidget *chart;
+ GtkWidget *frame;
+
+ guint active;
+
+ active = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
+
+ switch (active) {
+ case 0:
+ chart = baobab.rings_chart;
+ break;
+ case 1:
+ chart = baobab.treemap_chart;
+ break;
+ default:
+ g_return_if_reached ();
+ }
+
+ frame = gtk_widget_get_parent (baobab.current_chart);
+
+ baobab_chart_freeze_updates (baobab.current_chart);
+ baobab_chart_thaw_updates (chart);
+
+ g_object_ref_sink (baobab.current_chart);
+ gtk_container_remove (GTK_CONTAINER (frame), baobab.current_chart);
+ gtk_container_add (GTK_CONTAINER (frame), chart);
+
+ baobab.current_chart = chart;
+}
+
diff --git a/baobab/src/callbacks.h b/baobab/src/callbacks.h
new file mode 100644
index 00000000..93e70fbc
--- /dev/null
+++ b/baobab/src/callbacks.h
@@ -0,0 +1,62 @@
+/*
+ * callbacks.h
+ * 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
+ */
+
+#ifndef __BAOBAB_CALLBACKS_H__
+#define __BAOBAB_CALLBACKS_H__
+
+#include <gtk/gtk.h>
+#include "baobab-chart.h"
+
+void on_about_activate (GtkMenuItem *menuitem, gpointer user_data);
+void on_menuscanhome_activate (GtkMenuItem *menuitem, gpointer user_data);
+void on_menuallfs_activate (GtkMenuItem *menuitem, gpointer user_data);
+void on_menuscandir_activate (GtkMenuItem *menuitem, gpointer user_data);
+void on_menu_stop_activate (GtkMenuItem *menuitem, gpointer user_data);
+void on_menu_rescan_activate (GtkMenuItem *menuitem, gpointer user_data);
+void on_tbscandir_clicked (GtkToolButton *toolbutton, gpointer user_data);
+void on_tbscanhome_clicked (GtkToolButton *toolbutton, gpointer user_data);
+void on_tbscanall_clicked (GtkToolButton *toolbutton, gpointer user_data);
+void on_tbstop_clicked (GtkToolButton *toolbutton, gpointer user_data);
+void on_tbrescan_clicked (GtkToolButton *toolbutton, gpointer user_data);
+void on_radio_allfs_clicked (GtkButton *button, gpointer user_data);
+void on_radio_dir_clicked (GtkButton *button, gpointer user_data);
+void on_esci1_activate (GtkObject *object, gpointer user_data);
+gboolean on_delete_activate (GtkWidget *widget, GdkEvent *event, gpointer user_data);
+void open_file_cb (GtkMenuItem *pmenu, gpointer dummy);
+void scan_folder_cb (GtkMenuItem *pmenu, gpointer dummy);
+void trash_dir_cb (GtkMenuItem *pmenu, gpointer dummy);
+void list_all_cb (GtkMenuItem *pmenu, gpointer dummy);
+void on_pref_menu (GtkAction *a, gpointer user_data);
+void on_tb_scan_remote_clicked (GtkToolButton *toolbutton, gpointer user_data);
+void on_menu_scan_rem_activate (GtkMenuItem *menuitem, gpointer user_data);
+void on_view_tb_activate (GtkToggleAction *action, gpointer user_data);
+void on_view_sb_activate (GtkToggleAction *action, gpointer user_data);
+void on_ck_allocated_activate (GtkToggleAction *action, gpointer user_data);
+void on_helpcontents_activate (GtkAction *a, gpointer user_data);
+void on_tv_selection_changed (GtkTreeSelection *selection, gpointer user_data);
+void on_move_upwards_cb (GtkCheckMenuItem *checkmenuitem, gpointer user_data);
+void on_zoom_in_cb (GtkCheckMenuItem *checkmenuitem, gpointer user_data);
+void on_zoom_out_cb (GtkCheckMenuItem *checkmenuitem, gpointer user_data);
+void on_chart_snapshot_cb (GtkCheckMenuItem *checkmenuitem, gpointer user_data);
+void on_chart_type_change (GtkWidget *combo, gpointer user_data);
+
+#endif /* __BAOBAB_CALLBACKS_H__ */