gnome-utils r7954 - in trunk: . baobab baobab/data baobab/src



Author: acastro
Date: Sat Jun 28 17:52:55 2008
New Revision: 7954
URL: http://svn.gnome.org/viewvc/gnome-utils?rev=7954&view=rev

Log:
	Patch contributed by Eduardo Lima <elima igalia com>, bug #530512

	Implemented a new treemap object and moved into the main window,
	now we can select with a combobox in the main window which graph
	we want: ringschart or treemap. We have used the ringschart code
	in order to create a new abstract class and the new treemap just
	inherits from it and changes the rendering, in the near future we
	will change the ringschart in order to use this new abstract
	class.

	* data/baobab.glade:
	* src/Makefile.am:
	* src/baobab-graphwin.c:
	* src/baobab-graphwin.h:
	* src/baobab-tree-map.c:
	* src/baobab-tree-map.h:
	* src/baobab-treeview.c
	(on_tv_cur_changed),
	(on_tv_button_press):
	* src/baobab-utils.c
	(popupmenu_list):
	* src/callbacks.h:
	* src/callbacks.c
	(on_tv_selection_changed),
	(on_move_upwards_cb),
	(on_zoom_in_cb),
	(on_zoom_out_cb),
	(on_chart_type_change):
	Removed files, code and options in the menu that were used to show
	the old treemap. Notice we have removed GnomeCanvas dependency
	which was removed in the configure.ac file

        * src/baobab-chart.c:
        * src/baobab-chart.h:
        * src/baobab-treemap.c:
        * src/baobab-treemap.h:
	New objects, the chart is the new abstract class, the treemap is
	the new graphical representation, treemap inherits from chart. As
	we said before the code from chart is copied from ringschart, in
	the near future we must change ringschart in order to inherit also
	from chart.

	* src/baobab.h:
	* src/baobab.c
	(baobab_set_busy),
	(check_menu_sens),
	(baobab_scan_location),
	(initialize_ringschart),
	(main):
	Added the code to manage the new treemap in the program.



Added:
   trunk/baobab/src/baobab-chart.c
   trunk/baobab/src/baobab-chart.h
   trunk/baobab/src/baobab-treemap.c
   trunk/baobab/src/baobab-treemap.h
Removed:
   trunk/baobab/src/baobab-graphwin.c
   trunk/baobab/src/baobab-graphwin.h
   trunk/baobab/src/baobab-tree-map.c
   trunk/baobab/src/baobab-tree-map.h
Modified:
   trunk/ChangeLog
   trunk/baobab/ChangeLog
   trunk/baobab/data/baobab.glade
   trunk/baobab/src/Makefile.am
   trunk/baobab/src/baobab-treeview.c
   trunk/baobab/src/baobab-utils.c
   trunk/baobab/src/baobab.c
   trunk/baobab/src/baobab.h
   trunk/baobab/src/callbacks.c
   trunk/baobab/src/callbacks.h
   trunk/configure.ac

Modified: trunk/baobab/data/baobab.glade
==============================================================================
--- trunk/baobab/data/baobab.glade	(original)
+++ trunk/baobab/data/baobab.glade	Sat Jun 28 17:52:55 2008
@@ -264,24 +264,10 @@
 		    </widget>
 		  </child>
 
-		  <child>
-		    <widget class="GtkSeparatorMenuItem" id="separator4">
-		      <property name="visible">True</property>
 		    </widget>
 		  </child>
-
-		  <child>
-		    <widget class="GtkMenuItem" id="menu_treemap">
-		      <property name="visible">True</property>
-		      <property name="label" translatable="yes">_Graphical Usage Map</property>
-		      <property name="use_underline">True</property>
-		      <signal name="activate" handler="on_menu_treemap_activate" last_modification_time="Fri, 21 Apr 2006 16:44:40 GMT"/>
 		    </widget>
 		  </child>
-		</widget>
-	      </child>
-	    </widget>
-	  </child>
 
 	  <child>
 	    <widget class="GtkMenuItem" id="help1">
@@ -774,357 +760,4 @@
   </child>
 </widget>
 
-<widget class="GtkWindow" id="graph_win">
-  <property name="visible">True</property>
-  <property name="title" translatable="yes"></property>
-  <property name="type">GTK_WINDOW_TOPLEVEL</property>
-  <property name="window_position">GTK_WIN_POS_NONE</property>
-  <property name="modal">False</property>
-  <property name="default_width">600</property>
-  <property name="default_height">400</property>
-  <property name="resizable">True</property>
-  <property name="destroy_with_parent">False</property>
-  <property name="decorated">True</property>
-  <property name="skip_taskbar_hint">False</property>
-  <property name="skip_pager_hint">False</property>
-  <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
-  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
-  <property name="focus_on_map">True</property>
-  <property name="urgency_hint">False</property>
-
-  <child>
-    <widget class="GtkVBox" id="vbox6">
-      <property name="visible">True</property>
-      <property name="homogeneous">False</property>
-      <property name="spacing">0</property>
-
-      <child>
-	<widget class="GtkToolbar" id="toolbar2">
-	  <property name="visible">True</property>
-	  <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
-	  <property name="toolbar_style">GTK_TOOLBAR_ICONS</property>
-	  <property name="tooltips">True</property>
-	  <property name="show_arrow">True</property>
-
-	  <child>
-	    <widget class="GtkToolButton" id="graph_zoom_in">
-	      <property name="visible">True</property>
-	      <property name="tooltip" translatable="yes">Zoom in</property>
-	      <property name="stock_id">gtk-zoom-in</property>
-	      <property name="visible_horizontal">True</property>
-	      <property name="visible_vertical">True</property>
-	      <property name="is_important">False</property>
-	    </widget>
-	    <packing>
-	      <property name="expand">False</property>
-	      <property name="homogeneous">False</property>
-	    </packing>
-	  </child>
-
-	  <child>
-	    <widget class="GtkToolButton" id="graph_zoom_100">
-	      <property name="visible">True</property>
-	      <property name="tooltip" translatable="yes">Zoom 100%</property>
-	      <property name="stock_id">gtk-zoom-100</property>
-	      <property name="visible_horizontal">True</property>
-	      <property name="visible_vertical">True</property>
-	      <property name="is_important">False</property>
-	    </widget>
-	    <packing>
-	      <property name="expand">False</property>
-	      <property name="homogeneous">False</property>
-	    </packing>
-	  </child>
-
-	  <child>
-	    <widget class="GtkToolButton" id="graph_zoom_out">
-	      <property name="visible">True</property>
-	      <property name="tooltip" translatable="yes">Zoom out</property>
-	      <property name="stock_id">gtk-zoom-out</property>
-	      <property name="visible_horizontal">True</property>
-	      <property name="visible_vertical">True</property>
-	      <property name="is_important">False</property>
-	    </widget>
-	    <packing>
-	      <property name="expand">False</property>
-	      <property name="homogeneous">False</property>
-	    </packing>
-	  </child>
-
-	  <child>
-	    <widget class="GtkSeparatorToolItem" id="separatortoolitem6">
-	      <property name="visible">True</property>
-	      <property name="draw">True</property>
-	      <property name="visible_horizontal">True</property>
-	      <property name="visible_vertical">True</property>
-	    </widget>
-	    <packing>
-	      <property name="expand">False</property>
-	      <property name="homogeneous">False</property>
-	    </packing>
-	  </child>
-
-	  <child>
-	    <widget class="GtkToolButton" id="graph_save">
-	      <property name="visible">True</property>
-	      <property name="tooltip" translatable="yes">Take a snapshot</property>
-	      <property name="label" translatable="yes"></property>
-	      <property name="use_underline">True</property>
-	      <property name="icon">shot.png</property>
-	      <property name="visible_horizontal">True</property>
-	      <property name="visible_vertical">True</property>
-	      <property name="is_important">False</property>
-	    </widget>
-	    <packing>
-	      <property name="expand">False</property>
-	      <property name="homogeneous">True</property>
-	    </packing>
-	  </child>
-
-	  <child>
-	    <widget class="GtkSeparatorToolItem" id="separatortoolitem7">
-	      <property name="visible">True</property>
-	      <property name="draw">True</property>
-	      <property name="visible_horizontal">True</property>
-	      <property name="visible_vertical">True</property>
-	    </widget>
-	    <packing>
-	      <property name="expand">False</property>
-	      <property name="homogeneous">False</property>
-	    </packing>
-	  </child>
-
-	  <child>
-	    <widget class="GtkToolItem" id="toolitem4">
-	      <property name="visible">True</property>
-	      <property name="visible_horizontal">True</property>
-	      <property name="visible_vertical">True</property>
-	      <property name="is_important">False</property>
-
-	      <child>
-		<widget class="GtkLabel" id="label101">
-		  <property name="visible">True</property>
-		  <property name="label" translatable="yes">_Depth level:</property>
-		  <property name="use_underline">True</property>
-		  <property name="use_markup">False</property>
-		  <property name="justify">GTK_JUSTIFY_LEFT</property>
-		  <property name="wrap">False</property>
-		  <property name="selectable">False</property>
-		  <property name="xalign">0.5</property>
-		  <property name="yalign">0.5</property>
-		  <property name="xpad">11</property>
-		  <property name="ypad">0</property>
-		  <property name="mnemonic_widget">graph_spin</property>
-		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
-		  <property name="width_chars">-1</property>
-		  <property name="single_line_mode">False</property>
-		  <property name="angle">0</property>
-		</widget>
-	      </child>
-	    </widget>
-	    <packing>
-	      <property name="expand">False</property>
-	      <property name="homogeneous">False</property>
-	    </packing>
-	  </child>
-
-	  <child>
-	    <widget class="GtkToolItem" id="toolitem3">
-	      <property name="visible">True</property>
-	      <property name="visible_horizontal">True</property>
-	      <property name="visible_vertical">True</property>
-	      <property name="is_important">False</property>
-
-	      <child>
-		<widget class="GtkSpinButton" id="graph_spin">
-		  <property name="visible">True</property>
-		  <property name="can_focus">True</property>
-		  <property name="climb_rate">1</property>
-		  <property name="digits">0</property>
-		  <property name="numeric">False</property>
-		  <property name="update_policy">GTK_UPDATE_ALWAYS</property>
-		  <property name="snap_to_ticks">False</property>
-		  <property name="wrap">False</property>
-		  <property name="adjustment">-1 -1 50 1 5 5</property>
-		</widget>
-	      </child>
-	    </widget>
-	    <packing>
-	      <property name="expand">False</property>
-	      <property name="homogeneous">False</property>
-	    </packing>
-	  </child>
-
-	  <child>
-	    <widget class="GtkToolButton" id="graph_refresh">
-	      <property name="visible">True</property>
-	      <property name="tooltip" translatable="yes">Refresh graph</property>
-	      <property name="stock_id">gtk-refresh</property>
-	      <property name="visible_horizontal">True</property>
-	      <property name="visible_vertical">True</property>
-	      <property name="is_important">False</property>
-	    </widget>
-	    <packing>
-	      <property name="expand">False</property>
-	      <property name="homogeneous">True</property>
-	    </packing>
-	  </child>
-
-	  <child>
-	    <widget class="GtkSeparatorToolItem" id="separatortoolitem8">
-	      <property name="visible">True</property>
-	      <property name="draw">True</property>
-	      <property name="visible_horizontal">True</property>
-	      <property name="visible_vertical">True</property>
-	    </widget>
-	    <packing>
-	      <property name="expand">False</property>
-	      <property name="homogeneous">False</property>
-	    </packing>
-	  </child>
-
-	  <child>
-	    <widget class="GtkToolItem" id="toolitem5">
-	      <property name="visible">True</property>
-	      <property name="visible_horizontal">True</property>
-	      <property name="visible_vertical">True</property>
-	      <property name="is_important">False</property>
-
-	      <child>
-		<widget class="GtkLabel" id="label102">
-		  <property name="visible">True</property>
-		  <property name="label" translatable="yes">_Total folders:</property>
-		  <property name="use_underline">True</property>
-		  <property name="use_markup">False</property>
-		  <property name="justify">GTK_JUSTIFY_LEFT</property>
-		  <property name="wrap">False</property>
-		  <property name="selectable">False</property>
-		  <property name="xalign">0.5</property>
-		  <property name="yalign">0.5</property>
-		  <property name="xpad">8</property>
-		  <property name="ypad">0</property>
-		  <property name="mnemonic_widget">graph_total</property>
-		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
-		  <property name="width_chars">-1</property>
-		  <property name="single_line_mode">False</property>
-		  <property name="angle">0</property>
-		</widget>
-	      </child>
-	    </widget>
-	    <packing>
-	      <property name="expand">False</property>
-	      <property name="homogeneous">False</property>
-	    </packing>
-	  </child>
-
-	  <child>
-	    <widget class="GtkToolItem" id="toolitem6">
-	      <property name="visible">True</property>
-	      <property name="visible_horizontal">True</property>
-	      <property name="visible_vertical">True</property>
-	      <property name="is_important">False</property>
-
-	      <child>
-		<widget class="GtkEntry" id="graph_total">
-		  <property name="visible">True</property>
-		  <property name="can_focus">True</property>
-		  <property name="editable">False</property>
-		  <property name="visibility">True</property>
-		  <property name="max_length">0</property>
-		  <property name="text" translatable="yes"></property>
-		  <property name="has_frame">True</property>
-		  <property name="invisible_char">*</property>
-		  <property name="activates_default">False</property>
-		  <property name="width_chars">8</property>
-		</widget>
-	      </child>
-	    </widget>
-	    <packing>
-	      <property name="expand">False</property>
-	      <property name="homogeneous">False</property>
-	    </packing>
-	  </child>
-	</widget>
-	<packing>
-	  <property name="padding">0</property>
-	  <property name="expand">False</property>
-	  <property name="fill">False</property>
-	</packing>
-      </child>
-
-      <child>
-	<widget class="GtkVBox" id="vbox14">
-	  <property name="border_width">6</property>
-	  <property name="visible">True</property>
-	  <property name="homogeneous">False</property>
-	  <property name="spacing">6</property>
-
-	  <child>
-	    <widget class="GtkScrolledWindow" id="graph_scroll">
-	      <property name="visible">True</property>
-	      <property name="can_focus">True</property>
-	      <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-	      <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-	      <property name="shadow_type">GTK_SHADOW_IN</property>
-	      <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
-
-	      <child>
-		<placeholder/>
-	      </child>
-	    </widget>
-	    <packing>
-	      <property name="padding">0</property>
-	      <property name="expand">True</property>
-	      <property name="fill">True</property>
-	    </packing>
-	  </child>
-
-	  <child>
-	    <widget class="GtkHBox" id="hbox5">
-	      <property name="visible">True</property>
-	      <property name="homogeneous">False</property>
-	      <property name="spacing">0</property>
-
-	      <child>
-		<placeholder/>
-	      </child>
-
-	      <child>
-		<placeholder/>
-	      </child>
-
-	      <child>
-		<widget class="GtkButton" id="graph_close_btn">
-		  <property name="visible">True</property>
-		  <property name="can_focus">True</property>
-		  <property name="label">gtk-close</property>
-		  <property name="use_stock">True</property>
-		  <property name="relief">GTK_RELIEF_NORMAL</property>
-		  <property name="focus_on_click">True</property>
-		</widget>
-		<packing>
-		  <property name="padding">0</property>
-		  <property name="expand">False</property>
-		  <property name="fill">False</property>
-		  <property name="pack_type">GTK_PACK_END</property>
-		</packing>
-	      </child>
-	    </widget>
-	    <packing>
-	      <property name="padding">0</property>
-	      <property name="expand">False</property>
-	      <property name="fill">True</property>
-	    </packing>
-	  </child>
-	</widget>
-	<packing>
-	  <property name="padding">0</property>
-	  <property name="expand">True</property>
-	  <property name="fill">True</property>
-	</packing>
-      </child>
-    </widget>
-  </child>
-</widget>
-
 </glade-interface>

Modified: trunk/baobab/src/Makefile.am
==============================================================================
--- trunk/baobab/src/Makefile.am	(original)
+++ trunk/baobab/src/Makefile.am	Sat Jun 28 17:52:55 2008
@@ -21,8 +21,6 @@
 	baobab.h \
 	baobab-cell-renderer-progress.c \
 	baobab-cell-renderer-progress.h \
-	baobab-graphwin.c \
-	baobab-graphwin.h \
 	baobab-ringschart.c \
 	baobab-ringschart.h \
 	baobab-scan.c \
@@ -35,19 +33,20 @@
 	callbacks.h \
 	baobab-prefs.c \
 	baobab-prefs.h \
-	baobab-tree-map.c \
-	baobab-tree-map.h \
 	baobab-remote-connect-dialog.c \
 	baobab-remote-connect-dialog.h \
 	baobab-mount-operation.c \
 	baobab-mount-operation.h \
 	gedit-spinner.c \
-	gedit-spinner.h
+	gedit-spinner.h \
+	baobab-chart.c \
+	baobab-chart.h \
+	baobab-treemap.c \
+	baobab-treemap.h
 
 baobab_CFLAGS = 			\
 	$(GNOME_UTILS_CFLAGS)		\
 	$(LIBGLADE_CFLAGS)		\
-	$(GNOMECANVAS_CFLAGS)		\
 	$(LIBGTOP_CFLAGS)		\
 	$(NULL)
 
@@ -55,6 +54,5 @@
 baobab_LDADD = 				\
 	$(GNOME_UTILS_LIBS)		\
 	$(LIBGLADE_LIBS)		\
-	$(GNOMECANVAS_LIBS)		\
 	$(LIBGTOP_LIBS)			\
 	$(NULL)

Added: trunk/baobab/src/baobab-chart.c
==============================================================================
--- (empty file)
+++ trunk/baobab/src/baobab-chart.c	Sat Jun 28 17:52:55 2008
@@ -0,0 +1,1983 @@
+/*
+ * 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 <femorandeira igalia com>
+ *   Pablo Santamaria <psantamaria igalia com>
+ *   Jacobo Aragunde <jaragunde igalia com>
+ *   Eduardo Lima <elima igalia com>
+ *   Mario Sanchez <msanchez igalia com>
+ *   Miguel Gomez <magomez igalia com>
+ *   Henrique Ferreiro <hferreiro igalia com>
+ *   Alejandro Pinheiro <apinheiro igalia com>
+ *   Carlos Sanmartin <csanmartin igalia com>
+ *   Alejandro Garcia <alex igalia com>
+ */
+
+#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))
+
+G_DEFINE_ABSTRACT_TYPE (BaobabChart, baobab_chart, GTK_TYPE_WIDGET);
+
+enum
+{
+  LEFT_BUTTON   = 1,
+  MIDDLE_BUTTON = 2,
+  RIGHT_BUTTON  = 3
+};
+
+
+typedef struct _BaobabChartContextMenu BaobabChartContextMenu;
+
+struct _BaobabChartContextMenu
+{
+  GtkWidget *menu;
+  GtkWidget *set_root_item;
+  GtkWidget *up_item;
+  GtkWidget *zoom_in_item;
+  GtkWidget *zoom_out_item;
+  GtkWidget *snapshot_item;
+};
+
+
+struct _BaobabChartPrivate
+{
+  gchar *total_fs_size;
+  gboolean summary_mode;
+
+  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;
+
+  BaobabChartContextMenu context_menu;
+};
+
+/* Signals */
+enum
+{
+  SECTOR_ACTIVATED,
+  POPULATE_MENU,
+  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 gboolean baobab_chart_on_set_root_item (GtkCheckMenuItem *menuitem,
+                                               gpointer 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->calculate_item_geometry = NULL;
+  class->is_point_over_item      = NULL;
+  class->get_item_rectangle      = 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[SECTOR_ACTIVATED] =
+    g_signal_new ("sector_activated",
+          G_TYPE_FROM_CLASS (obj_class),
+          G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+          G_STRUCT_OFFSET (BaobabChartClass, sector_activated),
+          NULL, NULL,
+          g_cclosure_marshal_VOID__BOXED,
+          G_TYPE_NONE, 1,
+          GTK_TYPE_TREE_ITER);
+
+  baobab_chart_signals[POPULATE_MENU] =
+    g_signal_new ("populate_menu",
+          G_TYPE_FROM_CLASS (obj_class),
+          G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+          G_STRUCT_OFFSET (BaobabChartClass, sector_activated),
+          NULL, NULL,
+          g_cclosure_marshal_VOID__BOXED,
+          G_TYPE_NONE, 1,
+          GTK_TYPE_WIDGET);
+
+  g_type_class_add_private (obj_class, sizeof (BaobabChartPrivate));
+}
+
+static void
+baobab_chart_init (BaobabChart *chart)
+{
+  BaobabChartPrivate *priv;
+  BaobabChartContextMenu *menu;
+
+  priv = BAOBAB_CHART_GET_PRIVATE (chart);
+  chart->priv = priv;
+
+  priv->total_fs_size = NULL;
+  priv->summary_mode = TRUE;
+  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;
+
+  /* Init Context Menu */
+  menu = &priv->context_menu;
+  menu->set_root_item = NULL;
+
+  menu->menu = 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 snapshot"));
+  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->menu),
+                         menu->up_item);
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu->menu),
+                         gtk_separator_menu_item_new ());
+
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu->menu),
+                         menu->zoom_in_item);
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu->menu),
+                         menu->zoom_out_item);
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu->menu),
+                         gtk_separator_menu_item_new ());
+
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu->menu),
+                         menu->snapshot_item);
+
+  gtk_widget_show_all (menu->menu);
+}
+
+static void
+baobab_chart_dispose (GObject *object)
+{
+  BaobabChartPrivate *priv;
+
+  baobab_chart_free_items (GTK_WIDGET (object));
+
+  priv = BAOBAB_CHART (object)->priv;
+
+  /* Free context menu */
+  g_object_unref (priv->context_menu.menu);
+
+  /* Free total filesystem capacity label */
+  if (priv->total_fs_size != NULL)
+    g_free (priv->total_fs_size);
+
+  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;
+
+  g_return_if_fail (BAOBAB_IS_CHART (widget));
+
+  chart = BAOBAB_CHART (widget);
+  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+
+  attributes.window_type = GDK_WINDOW_CHILD;
+  attributes.x = widget->allocation.x;
+  attributes.y = widget->allocation.y;
+  attributes.width = widget->allocation.width;
+  attributes.height = widget->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;
+
+  widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
+                                   &attributes,
+                                   attributes_mask);
+  gdk_window_set_user_data (widget->window, chart);
+
+  widget->style = gtk_style_attach (widget->style, widget->window);
+  gtk_style_set_background (widget->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);
+
+  widget->allocation = *allocation;
+
+  if (GTK_WIDGET_REALIZED (widget))
+    {
+      gdk_window_move_resize (widget->window,
+                  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);
+      if (item->data != NULL)
+        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;
+  gdouble rel_size;
+
+  priv = BAOBAB_CHART_GET_PRIVATE (chart);
+
+  /* First we free current item list */
+  baobab_chart_free_items (chart);
+
+  /* Get the tree iteration corresponding to root */
+  gtk_tree_model_get_iter (priv->model, &initial_iter, root);
+
+
+  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);
+
+  if (priv->summary_mode)
+    {
+      node = baobab_chart_add_item (chart, 0, 0, 100, initial_iter);
+      item = (BaobabChartItem *) node->data;
+      g_free (item->name);
+      g_free (item->size);
+      item->name = g_strdup (_("Total filesystem capacity"));
+      item->size = g_strdup (priv->total_fs_size);
+
+      child_node = baobab_chart_add_item (chart, 1, 0, size, initial_iter);
+      item = (BaobabChartItem *) child_node->data;
+      item->parent = node;
+      g_free (item->name);
+      item->name = g_strdup (_("Total filesystem usage"));
+    }
+  else
+    /* 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))
+        {
+          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);
+
+  cairo_save (cr);
+
+  node = priv->first_item;
+  while (node != NULL)
+    {
+      item = (BaobabChartItem *) node->data;
+
+      if ((item->visible) && (gdk_rectangle_intersect (&area, &item->rect, NULL)))
+        {
+          highlighted = (node == priv->highlighted_item);
+
+          class->draw_item (chart, cr, item, highlighted);
+        }
+
+      node = node->next;
+    }
+
+  cairo_restore (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_REALIZED (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;
+
+  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 (chart->window);
+
+  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);
+
+          if (w > 0 && h > 0 &&
+          !(chart->allocation.width == w &&
+                chart->allocation.height == h))
+            {
+              /* minimal available proportion */
+              p = MIN (chart->allocation.width / (1.0 * w),
+                       chart->allocation.height / (1.0 * h));
+
+              sx = (gdouble) (chart->allocation.width - w * p) / 2.0;
+              sy = (gdouble) (chart->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 ();
+
+      /* 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;
+  GtkTreePath *root_path;
+  BaobabChartContextMenu *menu;
+  BaobabChartItem *item;
+  char *text;
+
+  priv = BAOBAB_CHART (widget)->priv;
+
+  switch (event->button)
+    {
+    case LEFT_BUTTON:
+      /* Enter into a subdir */
+      if (priv->highlighted_item != NULL)
+        g_signal_emit (BAOBAB_CHART (widget),
+                       baobab_chart_signals[SECTOR_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;
+
+    case RIGHT_BUTTON:
+      {
+        /* Popup context menu */
+        root_path = baobab_chart_get_root (widget);
+
+        menu = &priv->context_menu;
+
+        if (menu->set_root_item != NULL)
+          {
+            g_signal_handlers_disconnect_by_func (menu->set_root_item,
+                                                  baobab_chart_on_set_root_item,
+                                                  widget);
+            gtk_container_remove (GTK_CONTAINER (menu->menu), menu->set_root_item);
+            menu->set_root_item = NULL;
+          }
+
+        if ((! priv->summary_mode) &&
+            (priv->highlighted_item != NULL) &&
+            (priv->highlighted_item != priv->first_item))
+          {
+            item = (BaobabChartItem *) priv->highlighted_item->data;
+            text = g_strconcat ("Move to folder '", item->name, "'", NULL);
+
+            menu->set_root_item = gtk_image_menu_item_new_with_label (_(text));
+            gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu->set_root_item),
+                                           gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD,
+                                                                     GTK_ICON_SIZE_MENU));
+            gtk_menu_shell_prepend (GTK_MENU_SHELL (menu->menu),
+                                    menu->set_root_item);
+
+            g_signal_connect (menu->set_root_item, "activate",
+                              G_CALLBACK (baobab_chart_on_set_root_item), widget);
+
+            gtk_widget_show (menu->set_root_item);
+
+            g_free (text);
+          }
+
+        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_get_max_depth (widget) > BAOBAB_CHART_MIN_DEPTH));
+        gtk_widget_set_sensitive (menu->zoom_out_item,
+                                  (baobab_chart_get_max_depth (widget) < BAOBAB_CHART_MAX_DEPTH));
+
+        g_signal_emit (BAOBAB_CHART (widget),
+                       baobab_chart_signals[POPULATE_MENU],
+                       0, menu->menu);
+
+        gtk_menu_popup (GTK_MENU (menu->menu), NULL, NULL, NULL, NULL,
+                        event->button, event->time);
+      }
+      break;
+    }
+
+  return TRUE;
+}
+
+static gint
+baobab_chart_scroll (GtkWidget *widget,
+                     GdkEventScroll *event)
+{
+  BaobabChartPrivate * priv = BAOBAB_CHART_GET_PRIVATE (widget);
+
+  switch (event->direction)
+    {
+    case GDK_SCROLL_LEFT :
+    case GDK_SCROLL_UP :
+      baobab_chart_zoom_out (widget);
+      /* change the selected sector when zooming */
+      baobab_chart_motion_notify (widget, (GdkEventMotion *)event);
+      break;
+
+    case GDK_SCROLL_RIGHT :
+    case GDK_SCROLL_DOWN :
+      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 (chart->window, &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 gboolean
+baobab_chart_on_set_root_item (GtkCheckMenuItem *menuitem, gpointer data)
+{
+  BaobabChartPrivate *priv;
+
+  priv = BAOBAB_CHART_GET_PRIVATE (data);
+
+  g_signal_emit (BAOBAB_CHART (data),
+                 baobab_chart_signals[SECTOR_ACTIVATED],
+                 0, &((BaobabChartItem*) priv->highlighted_item->data)->iter);
+
+  return TRUE;
+}
+
+static gboolean
+baobab_chart_on_up_menu_item (GtkCheckMenuItem *menuitem, gpointer data)
+{
+  baobab_chart_move_up_root (GTK_WIDGET (data));
+
+  return TRUE;
+}
+
+static gboolean
+baobab_chart_on_zoom_in_menu_item (GtkCheckMenuItem *menuitem, gpointer data)
+{
+  baobab_chart_zoom_in (GTK_WIDGET (data));
+
+  return TRUE;
+}
+
+static gboolean
+baobab_chart_on_zoom_out_menu_item (GtkCheckMenuItem *menuitem, gpointer data)
+{
+  baobab_chart_zoom_out (GTK_WIDGET (data));
+
+  return TRUE;
+}
+
+static gboolean
+baobab_chart_on_snapshot_menu_item (GtkCheckMenuItem *menuitem, gpointer data)
+{
+  baobab_chart_save_snapshot (GTK_WIDGET (data));
+
+  return TRUE;
+}
+
+static inline void
+baobab_chart_connect_signals (GtkWidget *chart,
+                              GtkTreeModel *model)
+{
+  BaobabChartContextMenu *menu;
+
+  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);
+
+  /* Activate context menu callbacks */
+  menu = &BAOBAB_CHART_GET_PRIVATE (chart)->context_menu;
+
+  g_signal_connect (menu->up_item, "activate",
+                    G_CALLBACK (baobab_chart_on_up_menu_item), chart);
+  g_signal_connect (menu->zoom_in_item, "activate",
+                    G_CALLBACK (baobab_chart_on_zoom_in_menu_item), chart);
+  g_signal_connect (menu->zoom_out_item, "activate",
+                    G_CALLBACK (baobab_chart_on_zoom_out_menu_item), chart);
+  g_signal_connect (menu->snapshot_item, "activate",
+                    G_CALLBACK (baobab_chart_on_snapshot_menu_item), chart);
+}
+
+static inline void
+baobab_chart_disconnect_signals (GtkWidget *chart,
+                                 GtkTreeModel *model)
+{
+  BaobabChartContextMenu *menu;
+
+  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);
+
+  /* Deactivate context menu callbacks */
+  menu = &BAOBAB_CHART_GET_PRIVATE (chart)->context_menu;
+
+  g_signal_handlers_disconnect_by_func (menu->up_item,
+                                        baobab_chart_on_up_menu_item,
+                                        chart);
+  g_signal_handlers_disconnect_by_func (menu->zoom_in_item,
+                                        baobab_chart_on_zoom_in_menu_item,
+                                        chart);
+  g_signal_handlers_disconnect_by_func (menu->zoom_out_item,
+                                        baobab_chart_on_zoom_out_menu_item,
+                                        chart);
+  g_signal_handlers_disconnect_by_func (menu->snapshot_item,
+                                        baobab_chart_on_snapshot_menu_item,
+                                        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;
+
+  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_if_fail (BAOBAB_IS_CHART (widget));
+
+  gdk_drawable_get_size (widget->window, &w, &h);
+  pixbuf = gdk_pixbuf_get_from_drawable (NULL,
+                                         widget->window,
+                                         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;
+
+  g_return_if_fail (BAOBAB_IS_CHART (chart));
+
+  priv = BAOBAB_CHART (chart)->priv;
+
+  g_return_if_fail (priv->model != NULL);
+
+  if (priv->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;
+
+  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);
+
+  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                        chart->allocation.width,
+                                        chart->allocation.height);
+
+  if (cairo_surface_status (surface) == CAIRO_STATUS_SUCCESS)
+    {
+      cr = cairo_create (surface);
+
+      area.x = 0;
+      area.y = 0;
+      area.width = chart->allocation.width;
+      area.height = chart->allocation.height;
+      baobab_chart_draw (chart, cr, area);
+
+      cairo_rectangle (cr,
+                       0, 0,
+                       chart->allocation.width,
+                       chart->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)
+{
+  g_return_if_fail (BAOBAB_IS_CHART (chart));
+
+  baobab_chart_set_max_depth (chart,
+                              baobab_chart_get_max_depth (chart) - 1);
+}
+
+/**
+ * baobab_chart_zoom_in:
+ * @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[SECTOR_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;
+
+  g_return_if_fail (BAOBAB_IS_CHART (chart));
+
+  /* Popdown context menu */
+  priv = BAOBAB_CHART_GET_PRIVATE (chart);
+  gtk_menu_popdown (GTK_MENU (priv->context_menu.menu));
+  gtk_widget_queue_draw (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;
+    }
+
+  /* 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);
+
+  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));
+      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_set_total_fs_size:
+ * @chart: the #BaobabChart to set the filesystem size to.
+ * @total_fs_size: The total filesystem size.
+ *
+ * Sets the total filesystem size, to be shown as tooltip when summary mode is on.
+ *
+ * Fails if @chart is not a #BaobabChart.
+ **/
+void
+baobab_chart_set_total_fs_size (GtkWidget *chart, guint64 total_fs_size)
+{
+  BaobabChartPrivate *priv;
+
+  g_return_if_fail (BAOBAB_IS_CHART (chart));
+
+  priv = BAOBAB_CHART_GET_PRIVATE (chart);
+  priv->total_fs_size = g_format_size_for_display (total_fs_size);
+}
+
+/**
+ * baobab_chart_set_summary_mode:
+ * @chart: the #BaobabChart to set summary mode to.
+ * @summary_mode: boolean specifying TRUE is summary mode is on.
+ *
+ * Toggles on/off the summary mode (the initial mode that shows filesystem
+ * usage and capacity.
+ *
+ * Fails if @chart is not a #BaobabChart.
+ **/
+void
+baobab_chart_set_summary_mode (GtkWidget *chart,
+                               gboolean summary_mode)
+{
+  BaobabChartPrivate *priv;
+
+  g_return_if_fail (BAOBAB_IS_CHART (chart));
+
+  priv = BAOBAB_CHART_GET_PRIVATE (chart);
+  priv->summary_mode = summary_mode;
+}

Added: trunk/baobab/src/baobab-chart.h
==============================================================================
--- (empty file)
+++ trunk/baobab/src/baobab-chart.h	Sat Jun 28 17:52:55 2008
@@ -0,0 +1,153 @@
+/*
+ * 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 <femorandeira igalia com>
+ *   Pablo Santamaria <psantamaria igalia com>
+ *   Jacobo Aragunde <jaragunde igalia com>
+ *   Eduardo Lima <elima igalia com>
+ *   Mario Sanchez <msanchez igalia com>
+ *   Miguel Gomez <magomez igalia com>
+ *   Henrique Ferreiro <hferreiro igalia com>
+ *   Alejandro Pinheiro <apinheiro igalia com>
+ *   Carlos Sanmartin <csanmartin igalia com>
+ *   Alejandro Garcia <alex igalia com>
+ */
+
+#ifndef __BAOBAB_CHART_H__
+#define __BAOBAB_CHART_H__
+
+#include <gtk/gtk.h>
+#include <gtk/gtktreemodel.h>
+
+G_BEGIN_DECLS
+
+#define BAOBAB_CHART_MAX_DEPTH 8
+#define BAOBAB_CHART_MIN_DEPTH 1
+
+#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 (* sector_activated) (BaobabChart *chart,
+                             GtkTreeIter *iter);
+
+  /* Abstract methods */
+  void (* draw_item) (GtkWidget *chart,
+                      cairo_t *cr,
+                      BaobabChartItem *item,
+                      gboolean highlighted);
+
+  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);
+};
+
+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);
+void baobab_chart_set_total_fs_size (GtkWidget *chart,
+                                     guint64 total_fs_size);
+void baobab_chart_set_summary_mode (GtkWidget *chart,
+                                    gboolean summary_mode);
+
+G_END_DECLS
+
+#endif

Added: trunk/baobab/src/baobab-treemap.c
==============================================================================
--- (empty file)
+++ trunk/baobab/src/baobab-treemap.c	Sat Jun 28 17:52:55 2008
@@ -0,0 +1,279 @@
+/*
+ * 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  <thesaltydog gmail com>
+ *   Paolo Borelli <pborelli katamail com>
+ *   Miguel Gomez <magomez igalia com>
+ *   Eduardo Lima Mitev <elima igalia com>
+ */
+
+#include <gtk/gtk.h>
+
+#include "baobab-chart.h"
+#include "baobab-treemap.h"
+
+#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);
+
+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);
+
+static void
+baobab_treemap_class_init (BaobabTreemapClass *class)
+{
+  BaobabChartClass *chart_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;
+}
+
+static void
+baobab_treemap_init (BaobabTreemap *chart)
+{
+}
+
+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;
+  gdouble width, height;
+
+  rect = (cairo_rectangle_t *) item->data;
+
+  if (item->depth % 2 != 0)
+    {
+      baobab_chart_get_item_color (&fill_color, rect->x/chart->allocation.width*200,
+                                   item->depth, highlighted);
+      width = rect->width - ITEM_PADDING;
+      height = rect->height;
+    }
+  else
+    {
+      baobab_chart_get_item_color (&fill_color, rect->y/chart->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)
+{
+  cairo_rectangle_t p_area;
+  static cairo_rectangle_t *rect;
+  gdouble width, height;
+  BaobabChartItem *parent = NULL;
+
+  item->visible = FALSE;
+
+  if (item->parent == NULL)
+    {
+      p_area.x = 0 - ITEM_PADDING/2;
+      p_area.y = 0 - ITEM_PADDING/2;
+      p_area.width = chart->allocation.width + ITEM_PADDING * 2;
+      p_area.height = chart->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);
+}
+
+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)
+{
+  GdkRectangle rect;
+  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;
+    }
+
+}
+
+/* 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);
+}

Added: trunk/baobab/src/baobab-treemap.h
==============================================================================
--- (empty file)
+++ trunk/baobab/src/baobab-treemap.h	Sat Jun 28 17:52:55 2008
@@ -0,0 +1,124 @@
+/*
+ * 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  <thesaltydog gmail com>
+ *   Paolo Borelli <pborelli katamail com>
+ *   Miguel Gomez <magomez igalia com>
+ *   Eduardo Lima Mitev <elima igalia com>
+ */
+
+#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;
+
+struct _BaobabTreemap
+{
+  BaobabChart parent;
+};
+
+struct _BaobabTreemapClass
+{
+  BaobabChartClass parent_class;
+};
+
+GType baobab_treemap_get_type (void) G_GNUC_CONST;
+
+GtkWidget* baobab_treemap_new (void);
+
+G_END_DECLS
+
+#endif
+/*
+ * 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  <thesaltydog gmail com>
+ *   Paolo Borelli <pborelli katamail com>
+ *   Miguel Gomez <magomez igalia com>
+ *   Eduardo Lima Mitev <elima igalia com>
+ */
+
+#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;
+
+struct _BaobabTreemap
+{
+  BaobabChart parent;
+};
+
+struct _BaobabTreemapClass
+{
+  BaobabChartClass parent_class;
+};
+
+GType baobab_treemap_get_type (void) G_GNUC_CONST;
+GtkWidget* baobab_treemap_new (void);
+
+G_END_DECLS
+
+#endif

Modified: trunk/baobab/src/baobab-treeview.c
==============================================================================
--- trunk/baobab/src/baobab-treeview.c	(original)
+++ trunk/baobab/src/baobab-treeview.c	Sat Jun 28 17:52:55 2008
@@ -74,16 +74,7 @@
 		gtk_tree_model_get (GTK_TREE_MODEL (baobab.model), &iter,
 				    COL_H_PARSENAME, &parsename, -1);
 	}
-
-	set_glade_widget_sens("menu_treemap",FALSE);
-	if (parsename) {
-		set_statusbar (parsename);
-		if (strcmp (parsename, "") != 0)
-			set_glade_widget_sens("menu_treemap", TRUE);
-
-		g_free (parsename);
 	}
-}
 
 static gboolean
 on_tv_button_press (GtkWidget *widget,
@@ -118,12 +109,6 @@
 	gtk_tree_model_get (GTK_TREE_MODEL (baobab.model), &iter,
 			    COL_H_PARSENAME, &baobab.selected_path, -1);
 	
-	if (strcmp (baobab.selected_path, "") == 0) {
-		set_glade_widget_sens ("menu_treemap",FALSE);
-		gtk_tree_path_free (path);
-		return FALSE;
-	}
-
 	/* right-click */
 	if (event->button == 3) {
 		GFile *file;

Modified: trunk/baobab/src/baobab-utils.c
==============================================================================
--- trunk/baobab/src/baobab-utils.c	(original)
+++ trunk/baobab/src/baobab-utils.c	Sat Jun 28 17:52:55 2008
@@ -318,7 +318,7 @@
 void
 popupmenu_list (GtkTreePath *path, GdkEventButton *event, gboolean can_trash)
 {
-	GtkWidget *pmenu, *open, *trash, *sep, *graph_map, *remove;
+	GtkWidget *pmenu, *open, *trash, *remove;
 	gchar *path_to_string;
 	GtkWidget *image;
 
@@ -331,19 +331,10 @@
 	open = gtk_image_menu_item_new_with_mnemonic(_("_Open Folder"));
 	gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (open), image);
 
-	graph_map = gtk_image_menu_item_new_with_mnemonic (_("_Graphical Usage Map"));
-	image = gtk_image_new_from_stock ("gtk-select-color", GTK_ICON_SIZE_MENU);
-	gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (graph_map), image);
-
-	sep = gtk_separator_menu_item_new ();
-
 	g_signal_connect (open, "activate",
 			  G_CALLBACK (open_file_cb), NULL);
-	g_signal_connect (graph_map, "activate",
-			  G_CALLBACK (graph_map_cb), path_to_string);
 
 	gtk_container_add (GTK_CONTAINER (pmenu), open);
-	gtk_container_add (GTK_CONTAINER (pmenu), graph_map);
 	
 	if (baobab.is_local && can_trash) {
 			trash = gtk_image_menu_item_new_with_mnemonic(_("Mo_ve to Trash"));

Modified: trunk/baobab/src/baobab.c
==============================================================================
--- trunk/baobab/src/baobab.c	(original)
+++ trunk/baobab/src/baobab.c	Sat Jun 28 17:52:55 2008
@@ -39,6 +39,8 @@
 
 #include "gedit-spinner.h"
 
+#include "baobab-treemap.h"
+
 static GVolumeMonitor 	 *monitor_vol;
 static GFileMonitor	 *monitor_home;
 
@@ -84,10 +86,17 @@
 		gedit_spinner_start (GEDIT_SPINNER (baobab.spinner));
  		baobab_ringschart_freeze_updates (baobab.ringschart);
 		baobab_ringschart_set_init_depth (baobab.ringschart, 0);
+
+		baobab_chart_freeze_updates (baobab.treemap_chart);
+		baobab_chart_set_summary_mode (baobab.treemap_chart, FALSE);
+		gtk_widget_set_sensitive (baobab.chart_type_combo, FALSE);
 	}
 	else {
 		gedit_spinner_stop (GEDIT_SPINNER (baobab.spinner));
  		baobab_ringschart_thaw_updates (baobab.ringschart);
+
+		baobab_chart_thaw_updates (baobab.treemap_chart);
+		gtk_widget_set_sensitive (baobab.chart_type_combo, TRUE);
 	}
 
 	/* change the cursor */
@@ -106,7 +115,6 @@
 			gtk_main_iteration ();
 
 		set_statusbar (_("Scanning..."));
-		set_glade_widget_sens ("menu_treemap", FALSE);
 		set_glade_widget_sens ("expand_all", TRUE);
 		set_glade_widget_sens ("collapse_all", TRUE);		
 	}
@@ -174,6 +182,8 @@
 				NULL);
 
 	baobab_ringschart_set_max_depth (baobab.ringschart, baobab.model_max_depth);
+	baobab_chart_set_max_depth (baobab.treemap_chart, baobab.model_max_depth);
+
 	baobab_set_busy (FALSE);
 	check_menu_sens (FALSE);
 	set_statusbar (_("Ready"));
@@ -785,6 +795,7 @@
         GtkWidget *hpaned_main;
         GtkWidget *ringschart_frame;
         ContextMenu *menu = NULL;
+	GtkWidget *hbox1;
 
         baobab.ringschart = GTK_WIDGET (baobab_ringschart_new ());
         baobab_ringschart_set_model_with_columns (baobab.ringschart,
@@ -827,6 +838,46 @@
 			  G_CALLBACK (on_rchart_button_release), NULL);
 
         gtk_widget_show_all (ringschart_frame);
+
+	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 = glade_xml_get_widget (baobab.main_xml, "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's Treemap Chart */
+	baobab.treemap_chart = baobab_treemap_new ();
+	baobab_chart_set_total_fs_size (baobab.treemap_chart, g_fs.total);
+	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, "sector_activated",
+					G_CALLBACK (on_rchart_sector_activated), NULL);
+	gtk_widget_show (baobab.treemap_chart);
+	/* Ends Baobab's Treemap Chart */
+
+	baobab.current_chart = baobab.ringschart;
+	g_object_ref_sink (baobab.treemap_chart);
 }
 
 static gboolean
@@ -900,7 +951,6 @@
 							    "ck_allocated"),
 				      baobab.show_allocated);
 
-	set_glade_widget_sens ("menu_treemap",FALSE);
 	gtk_widget_show (baobab.window);
 
 	first_row ();

Modified: trunk/baobab/src/baobab.h
==============================================================================
--- trunk/baobab/src/baobab.h	(original)
+++ trunk/baobab/src/baobab.h	Sat Jun 28 17:52:55 2008
@@ -62,6 +62,9 @@
   	GtkWidget *window;
   	GtkWidget *tree_view;
   	GtkWidget *ringschart;
+	GtkWidget *treemap_chart;
+	GtkWidget *current_chart;
+	GtkWidget *chart_type_combo;
 	ContextMenu *rchart_menu;
 	GtkWidget *toolbar;
 	GtkWidget *spinner;

Modified: trunk/baobab/src/callbacks.c
==============================================================================
--- trunk/baobab/src/callbacks.c	(original)
+++ trunk/baobab/src/callbacks.c	Sat Jun 28 17:52:55 2008
@@ -31,12 +31,12 @@
 #include <gio/gio.h>
 
 #include "baobab.h"
-#include "baobab-graphwin.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)
@@ -210,18 +210,6 @@
 }
 
 void
-graph_map_cb (GtkMenuItem *pmenu, gchar *path_to_string)
-{
-	baobab_graphwin_create (GTK_TREE_MODEL (baobab.model),
-				path_to_string,
-				BAOBAB_GLADE_FILE,
-				COL_H_PARSENAME,
-				baobab.is_local ? COL_H_ALLOCSIZE : COL_H_SIZE,
-				-1);
-	g_free (path_to_string);
-}
-
-void
 trash_dir_cb (GtkMenuItem *pmenu, gpointer dummy)
 {
 	GFile *file;
@@ -349,28 +337,6 @@
 }
 
 void
-on_menu_treemap_activate (GtkMenuItem *menuitem, gpointer user_data)
-{
-	GtkTreeIter iter;
-	GtkTreePath *path;
-	gchar *path_to_string;
-	
-	if (!gtk_tree_selection_get_selected (gtk_tree_view_get_selection 
-							(GTK_TREE_VIEW(baobab.tree_view)), 
-					      NULL, 
-					      &iter))		 
-		return;
-
-	path = gtk_tree_model_get_path(GTK_TREE_MODEL(baobab.model), &iter);
-	
-	/* path_to_string is freed in graph_map_cb function */
-	path_to_string = gtk_tree_path_to_string (path);
-
-	gtk_tree_path_free(path);
-	graph_map_cb(NULL, path_to_string);
-}
-
-void
 on_helpcontents_activate (GtkMenuItem *menuitem, gpointer user_data)
 {
 	baobab_help_display (GTK_WINDOW (baobab.window), "baobab.xml", NULL);
@@ -404,6 +370,7 @@
                 
 		path = gtk_tree_model_get_path (GTK_TREE_MODEL (baobab.model), &iter);
                 
+		baobab_chart_set_root (baobab.treemap_chart, path);
 		baobab_ringschart_set_root (baobab.ringschart, path);
 		
 		gtk_tree_path_free (path);
@@ -484,6 +451,7 @@
                   parent_path = gtk_tree_model_get_path (GTK_TREE_MODEL (baobab.model), &parent_iter);
 
                   baobab_ringschart_set_root (baobab.ringschart, parent_path);
+                  baobab_chart_set_root (baobab.treemap_chart, parent_path);
 
                   gtk_tree_path_free (parent_path);
                 }
@@ -498,6 +466,8 @@
 {
   baobab_ringschart_set_max_depth (baobab.ringschart,
                                    baobab_ringschart_get_max_depth (baobab.ringschart) - 1);
+
+  baobab_chart_zoom_in (baobab.treemap_chart);
 }
 
 void
@@ -505,4 +475,40 @@
 {
   baobab_ringschart_set_max_depth (baobab.ringschart,
                                    baobab_ringschart_get_max_depth (baobab.ringschart) + 1);
+
+  baobab_chart_zoom_out (baobab.treemap_chart);
+}
+
+void
+on_chart_type_change (GtkWidget *combo, gpointer user_data)
+{
+  GtkWidget *chart;
+  GtkWidget *frame;
+
+  guint active = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
+
+  if (active != 0)
+    chart = baobab.treemap_chart;
+  else
+    chart = baobab.ringschart;
+
+  frame = gtk_widget_get_parent (baobab.current_chart);
+
+  if (BAOBAB_IS_CHART (baobab.current_chart))
+    {
+      baobab_chart_freeze_updates (baobab.current_chart);
+      baobab_ringschart_thaw_updates (chart);
+    }
+  else
+    {
+      baobab_chart_thaw_updates (chart);
+      baobab_ringschart_freeze_updates (baobab.current_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;
 }
+

Modified: trunk/baobab/src/callbacks.h
==============================================================================
--- trunk/baobab/src/callbacks.h	(original)
+++ trunk/baobab/src/callbacks.h	Sat Jun 28 17:52:55 2008
@@ -43,7 +43,6 @@
 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 graph_map_cb (GtkMenuItem *pmenu, gchar * path_to_string);
 void trash_dir_cb (GtkMenuItem *pmenu, gpointer dummy);
 void list_all_cb (GtkMenuItem *pmenu, gpointer dummy);
 void contents_changed_cb (GFileMonitor      *file_monitor,
@@ -55,16 +54,11 @@
 void volume_changed (GVolumeMonitor *volume_monitor,
                      GVolume        *volume,
                      gpointer        user_data);
-void on_graph_close_btn_clicked (GtkButton * button, gpointer user_data);
-void on_graph_zoom_in_clicked (GtkToolButton *toolbutton, gpointer user_data);
-void on_graph_zoom_out_clicked (GtkToolButton *toolbutton, gpointer user_data);
-void on_graph_zoom_100_clicked (GtkToolButton *toolbutton, 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 (GtkCheckMenuItem *checkmenuitem, gpointer user_data); 
 void on_view_sb_activate (GtkCheckMenuItem *checkmenuitem, gpointer user_data); 
 void on_ck_allocated_activate (GtkCheckMenuItem *checkmenuitem, gpointer user_data);
-void on_menu_treemap_activate (GtkMenuItem *menuitem, gpointer user_data);
 void on_helpcontents_activate (GtkMenuItem *menuitem, gpointer user_data);
 void on_tv_selection_changed (GtkTreeSelection *selection, gpointer user_data);
 void on_rchart_sector_activated (BaobabRingschart *rchart, GtkTreeIter *iter);
@@ -72,5 +66,6 @@
 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_type_change (GtkWidget *combo, gpointer user_data);
 
 #endif /* __BAOBAB_CALLBACKS_H__ */

Modified: trunk/configure.ac
==============================================================================
--- trunk/configure.ac	(original)
+++ trunk/configure.ac	Sat Jun 28 17:52:55 2008
@@ -219,10 +219,6 @@
 AC_SUBST(LIBGTOP_CFLAGS)
 AC_SUBST(LIBGTOP_LIBS)
 
-PKG_CHECK_MODULES(GNOMECANVAS, libgnomecanvas-2.0 >= $GNOMECANVAS_REQUIRED)
-AC_SUBST(GNOMECANVAS_CFLAGS)
-AC_SUBST(GNOMECANVAS_LIBS)
-
 dnl ***************************************************************
 dnl Other miscellaneous checks 
 dnl ***************************************************************



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]