[gnome-disk-utility] add some experimental code for grid-based layout



commit ba32ed65991f520258aa5bb56f7bbb04763e4346
Author: David Zeuthen <davidz redhat com>
Date:   Mon Mar 30 10:32:34 2009 -0400

    add some experimental code for grid-based layout
---
 configure.ac                           |    2 +
 src/Makefile.am                        |    2 +-
 src/playground/Makefile.am             |    4 +
 src/playground/grid/Makefile.am        |   56 +++
 src/playground/grid/gdu-grid-element.c |  811 ++++++++++++++++++++++++++++++++
 src/playground/grid/gdu-grid-element.h |   58 +++
 src/playground/grid/gdu-grid-hbox.c    |  169 +++++++
 src/playground/grid/gdu-grid-hbox.h    |   38 ++
 src/playground/grid/gdu-grid-types.h   |   17 +
 src/playground/grid/gdu-grid-view.c    |  455 ++++++++++++++++++
 src/playground/grid/gdu-grid-view.h    |   47 ++
 src/playground/grid/grid.c             |   58 +++
 12 files changed, 1716 insertions(+), 1 deletions(-)

diff --git a/configure.ac b/configure.ac
index b4a4255..2b5c27a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -177,6 +177,8 @@ src/gdu/gdu.pc
 src/gdu-gtk/Makefile
 src/gdu-gtk/gdu-gtk.pc
 src/palimpsest/Makefile
+src/playground/Makefile
+src/playground/grid/Makefile
 po/Makefile.in
 data/Makefile
 data/icons/Makefile
diff --git a/src/Makefile.am b/src/Makefile.am
index 2ea64de..181303f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = gdu gdu-gtk palimpsest
+SUBDIRS = gdu gdu-gtk palimpsest playground
 
 clean-local :
 	rm -f *~
diff --git a/src/playground/Makefile.am b/src/playground/Makefile.am
new file mode 100644
index 0000000..9f742eb
--- /dev/null
+++ b/src/playground/Makefile.am
@@ -0,0 +1,4 @@
+SUBDIRS = grid
+
+clean-local :
+	rm -f *~
diff --git a/src/playground/grid/Makefile.am b/src/playground/grid/Makefile.am
new file mode 100644
index 0000000..a07e341
--- /dev/null
+++ b/src/playground/grid/Makefile.am
@@ -0,0 +1,56 @@
+
+NULL =
+
+noinst_PROGRAMS = grid
+
+grid_SOURCES = 										\
+	grid.c										\
+	gdu-grid-types.h								\
+	gdu-grid-element.h			gdu-grid-element.c			\
+	gdu-grid-hbox.h				gdu-grid-hbox.c				\
+	gdu-grid-view.h				gdu-grid-view.c				\
+	$(NULL)
+
+grid_CPPFLAGS = 					\
+	-I$(top_srcdir)/src/				\
+	-I$(top_builddir)/src/				\
+	-DG_LOG_DOMAIN=\"TestGrid\"			\
+	-DGNOMELOCALEDIR=\""$(datadir)/locale"\"	\
+	$(DISABLE_DEPRECATED)				\
+	-DGDU_API_IS_SUBJECT_TO_CHANGE			\
+	-DGDU_GTK_API_IS_SUBJECT_TO_CHANGE		\
+	$(AM_CPPFLAGS)
+
+grid_CFLAGS = 						\
+	$(GLIB2_CFLAGS)					\
+	$(GOBJECT2_CFLAGS)				\
+	$(GIO2_CFLAGS)					\
+	$(GIO_UNIX2_CFLAGS)				\
+	$(DBUS_GLIB_CFLAGS)				\
+	$(POLKIT_DBUS_CFLAGS)				\
+	$(POLKIT_GNOME_CFLAGS)				\
+	$(GNOME_KEYRING_CFLAGS)				\
+	$(GTK2_CFLAGS)					\
+	$(LIBSEXY_CFLAGS)				\
+	$(WARN_CFLAGS)					\
+	$(AM_CFLAGS)
+
+grid_LDFLAGS = 						\
+	$(AM_LDFLAGS)
+
+grid_LDADD = 						\
+	$(GLIB2_LIBS)					\
+	$(GIO2_LIBS)					\
+	$(GIO_UNIX2_LIBS)				\
+	$(DBUS_GLIB_LIBS)				\
+	$(POLKIT_DBUS_LIBS)				\
+	$(POLKIT_GNOME_LIBS)				\
+	$(GNOME_KEYRING_LIBS)				\
+	$(GTK2_LIBS)					\
+	$(LIBSEXY_LIBS)					\
+	$(INTLLIBS)					\
+	$(top_builddir)/src/gdu/libgdu.la		\
+	$(top_builddir)/src/gdu-gtk/libgdu-gtk.la
+
+clean-local :
+	rm -f *~
diff --git a/src/playground/grid/gdu-grid-element.c b/src/playground/grid/gdu-grid-element.c
new file mode 100644
index 0000000..5f8b506
--- /dev/null
+++ b/src/playground/grid/gdu-grid-element.c
@@ -0,0 +1,811 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+
+#include <math.h>
+#include <glib/gi18n.h>
+#include <gdk/gdkkeysyms.h>
+#include <gdk/gdkx.h>
+#include <X11/XKBlib.h>
+
+#define GDU_GTK_API_IS_SUBJECT_TO_CHANGE
+#include <gdu-gtk/gdu-gtk.h>
+
+#include "gdu-grid-view.h"
+#include "gdu-grid-element.h"
+
+struct GduGridElementPrivate
+{
+        GduGridView *view;
+        GduPresentable *presentable;
+        guint minimum_size;
+        gdouble percent_size;
+        GduGridElementFlags flags;
+};
+
+enum
+{
+        PROP_0,
+        PROP_VIEW,
+        PROP_PRESENTABLE,
+        PROP_MINIMUM_SIZE,
+        PROP_PERCENT_SIZE,
+        PROP_FLAGS,
+};
+
+G_DEFINE_TYPE (GduGridElement, gdu_grid_element, GTK_TYPE_DRAWING_AREA)
+
+static void
+gdu_grid_element_finalize (GObject *object)
+{
+        GduGridElement *element = GDU_GRID_ELEMENT (object);
+
+        if (element->priv->presentable != NULL)
+                g_object_unref (element->priv->presentable);
+
+        if (G_OBJECT_CLASS (gdu_grid_element_parent_class)->finalize != NULL)
+                G_OBJECT_CLASS (gdu_grid_element_parent_class)->finalize (object);
+}
+
+static void
+gdu_grid_element_get_property (GObject    *object,
+                            guint       property_id,
+                            GValue     *value,
+                            GParamSpec *pspec)
+{
+        GduGridElement *element = GDU_GRID_ELEMENT (object);
+
+        switch (property_id) {
+        case PROP_VIEW:
+                g_value_set_object (value, element->priv->view);
+                break;
+
+        case PROP_PRESENTABLE:
+                g_value_set_object (value, element->priv->presentable);
+                break;
+
+        case PROP_MINIMUM_SIZE:
+                g_value_set_uint (value, element->priv->minimum_size);
+                break;
+
+        case PROP_PERCENT_SIZE:
+                g_value_set_double (value, element->priv->percent_size);
+                break;
+
+        case PROP_FLAGS:
+                g_value_set_uint (value, element->priv->flags);
+                break;
+
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+        }
+}
+
+static void
+gdu_grid_element_set_property (GObject      *object,
+                            guint         property_id,
+                            const GValue *value,
+                            GParamSpec   *pspec)
+{
+        GduGridElement *element = GDU_GRID_ELEMENT (object);
+
+        switch (property_id) {
+        case PROP_VIEW:
+                /* don't increase reference count */
+                element->priv->view = g_value_get_object (value);
+                break;
+
+        case PROP_PRESENTABLE:
+                element->priv->presentable = g_value_dup_object (value);
+                break;
+
+        case PROP_MINIMUM_SIZE:
+                element->priv->minimum_size = g_value_get_uint (value);
+                break;
+
+        case PROP_PERCENT_SIZE:
+                element->priv->percent_size = g_value_get_double (value);
+                break;
+
+        case PROP_FLAGS:
+                element->priv->flags = g_value_get_uint (value);
+                break;
+
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+        }
+}
+
+static void
+round_rect (cairo_t *cr,
+            gdouble x, gdouble y,
+            gdouble w, gdouble h,
+            gdouble r,
+            gboolean top_left_round,
+            gboolean top_right_round,
+            gboolean bottom_right_round,
+            gboolean bottom_left_round)
+{
+        if (top_left_round) {
+                cairo_move_to  (cr,
+                                x + r, y);
+        } else {
+                cairo_move_to  (cr,
+                                x, y);
+        }
+
+        if (top_right_round) {
+                cairo_line_to  (cr,
+                                x + w - r, y);
+                cairo_curve_to (cr,
+                                x + w, y,
+                                x + w, y,
+                                x + w, y + r);
+        } else {
+                cairo_line_to  (cr,
+                                x + w, y);
+        }
+
+        if (bottom_right_round) {
+                cairo_line_to  (cr,
+                                x + w, y + h - r);
+                cairo_curve_to (cr,
+                                x + w, y + h,
+                                x + w, y + h,
+                                x + w - r, y + h);
+        } else {
+                cairo_line_to  (cr,
+                                x + w, y + h);
+        }
+
+        if (bottom_left_round) {
+                cairo_line_to  (cr,
+                                x + r, y + h);
+                cairo_curve_to (cr,
+                                x, y + h,
+                                x, y + h,
+                                x, y + h - r);
+        } else {
+                cairo_line_to  (cr,
+                                x, y + h);
+        }
+
+        if (top_left_round) {
+                cairo_line_to  (cr,
+                                x, y + r);
+                cairo_curve_to (cr,
+                                x, y,
+                                x, y,
+                                x + r, y);
+        } else {
+                cairo_line_to  (cr,
+                                x, y);
+        }
+}
+
+static void
+render_pixbuf (cairo_t   *cr,
+               gdouble    x,
+               gdouble    y,
+               GdkPixbuf *pixbuf)
+{
+        gdk_cairo_set_source_pixbuf (cr, pixbuf, x, y);
+        cairo_rectangle (cr,
+                         x,
+                         y,
+                         gdk_pixbuf_get_width (pixbuf),
+                         gdk_pixbuf_get_height (pixbuf));
+        cairo_fill (cr);
+}
+
+static gboolean
+gdu_grid_element_expose_event (GtkWidget           *widget,
+                               GdkEventExpose      *event)
+{
+        GduGridElement *element = GDU_GRID_ELEMENT (widget);
+        cairo_t *cr;
+        gdouble width, height;
+        gdouble rect_x, rect_y, rect_width, rect_height;
+        gboolean is_selected;
+        gboolean has_focus;
+        GduGridElementFlags f;
+        gdouble fill_red;
+        gdouble fill_green;
+        gdouble fill_blue;
+        gdouble fill_selected_red;
+        gdouble fill_selected_green;
+        gdouble fill_selected_blue;
+        gdouble focus_rect_red;
+        gdouble focus_rect_green;
+        gdouble focus_rect_blue;
+        gdouble focus_rect_selected_red;
+        gdouble focus_rect_selected_green;
+        gdouble focus_rect_selected_blue;
+        gdouble stroke_red;
+        gdouble stroke_green;
+        gdouble stroke_blue;
+        gdouble stroke_selected_red;
+        gdouble stroke_selected_green;
+        gdouble stroke_selected_blue;
+        gdouble text_red;
+        gdouble text_green;
+        gdouble text_blue;
+        gdouble text_selected_red;
+        gdouble text_selected_green;
+        gdouble text_selected_blue;
+        gdouble border_width;
+
+        f = element->priv->flags;
+
+        width = widget->allocation.width;
+        height = widget->allocation.height;
+
+        border_width = 4;
+
+        rect_x = 0.5;
+        rect_y = 0.5;
+        rect_width = width;
+        rect_height = height;
+        if (f & GDU_GRID_ELEMENT_FLAGS_EDGE_LEFT) {
+                rect_x += border_width;
+                rect_width -= border_width;
+        }
+        if (f & GDU_GRID_ELEMENT_FLAGS_EDGE_TOP) {
+                rect_y += border_width;
+                rect_height -= border_width;
+        }
+        if (f & GDU_GRID_ELEMENT_FLAGS_EDGE_RIGHT) {
+                rect_width -= border_width;
+        }
+        if (f & GDU_GRID_ELEMENT_FLAGS_EDGE_BOTTOM) {
+                rect_height -= border_width;
+        }
+
+        cr = gdk_cairo_create (widget->window);
+        cairo_rectangle (cr,
+                         event->area.x, event->area.y,
+                         event->area.width, event->area.height);
+        cairo_clip (cr);
+
+        has_focus = GTK_WIDGET_HAS_FOCUS (widget);
+        if (element->priv->presentable != NULL)
+                is_selected = gdu_grid_view_is_selected (element->priv->view, element->priv->presentable);
+        else
+                is_selected = FALSE;
+
+        fill_red     = 1;
+        fill_green   = 1;
+        fill_blue    = 1;
+        fill_selected_red     = 0.40;
+        fill_selected_green   = 0.60;
+        fill_selected_blue    = 0.80;
+        focus_rect_red     = 0.75;
+        focus_rect_green   = 0.75;
+        focus_rect_blue    = 0.75;
+        focus_rect_selected_red     = 0.70;
+        focus_rect_selected_green   = 0.70;
+        focus_rect_selected_blue    = 0.80;
+        stroke_red   = 0.75;
+        stroke_green = 0.75;
+        stroke_blue  = 0.75;
+        stroke_selected_red   = 0.3;
+        stroke_selected_green = 0.45;
+        stroke_selected_blue  = 0.6;
+        text_red     = 0;
+        text_green   = 0;
+        text_blue    = 0;
+        text_selected_red     = 1;
+        text_selected_green   = 1;
+        text_selected_blue    = 1;
+
+        /* draw element */
+        if (is_selected) {
+                cairo_pattern_t *gradient;
+                gradient = cairo_pattern_create_radial (rect_x + rect_width / 2,
+                                                        rect_y + rect_height / 2,
+                                                        0.0,
+                                                        rect_x + rect_width / 2,
+                                                        rect_y + rect_height / 2,
+                                                        rect_width/2.0);
+                cairo_pattern_add_color_stop_rgb (gradient,
+                                                  0.0,
+                                                  1.0 * fill_selected_red,
+                                                  1.0 * fill_selected_green,
+                                                  1.0 * fill_selected_blue);
+                cairo_pattern_add_color_stop_rgb (gradient,
+                                                  1.0,
+                                                  0.8 * fill_selected_red,
+                                                  0.8 * fill_selected_green,
+                                                  0.8 * fill_selected_blue);
+                cairo_set_source (cr, gradient);
+                cairo_pattern_destroy (gradient);
+        } else {
+                cairo_set_source_rgb (cr, fill_red, fill_green, fill_blue);
+        }
+        f = element->priv->flags;
+        round_rect (cr,
+                    rect_x, rect_y,
+                    rect_width, rect_height,
+                    20,
+                    (f & GDU_GRID_ELEMENT_FLAGS_EDGE_LEFT)  && (f & GDU_GRID_ELEMENT_FLAGS_EDGE_TOP),
+                    (f & GDU_GRID_ELEMENT_FLAGS_EDGE_RIGHT) && (f & GDU_GRID_ELEMENT_FLAGS_EDGE_TOP),
+                    (f & GDU_GRID_ELEMENT_FLAGS_EDGE_RIGHT) && (f & GDU_GRID_ELEMENT_FLAGS_EDGE_BOTTOM),
+                    (f & GDU_GRID_ELEMENT_FLAGS_EDGE_LEFT)  && (f & GDU_GRID_ELEMENT_FLAGS_EDGE_BOTTOM));
+        cairo_fill_preserve (cr);
+        if (is_selected)
+                cairo_set_source_rgb (cr, stroke_selected_red, stroke_selected_green, stroke_selected_blue);
+        else
+                cairo_set_source_rgb (cr, stroke_red, stroke_green, stroke_blue);
+        cairo_set_line_width (cr, 1);
+        cairo_stroke (cr);
+
+        /* draw focus indicator */
+        if (has_focus) {
+                gdouble dashes[] = {2.0};
+                round_rect (cr,
+                            rect_x + 3, rect_y + 3,
+                            rect_width - 3 * 2, rect_height - 3 * 2,
+                            20,
+                            (f & GDU_GRID_ELEMENT_FLAGS_EDGE_LEFT)  && (f & GDU_GRID_ELEMENT_FLAGS_EDGE_TOP),
+                            (f & GDU_GRID_ELEMENT_FLAGS_EDGE_RIGHT) && (f & GDU_GRID_ELEMENT_FLAGS_EDGE_TOP),
+                            (f & GDU_GRID_ELEMENT_FLAGS_EDGE_RIGHT) && (f & GDU_GRID_ELEMENT_FLAGS_EDGE_BOTTOM),
+                            (f & GDU_GRID_ELEMENT_FLAGS_EDGE_LEFT)  && (f & GDU_GRID_ELEMENT_FLAGS_EDGE_BOTTOM));
+                if (is_selected)
+                        cairo_set_source_rgb (cr, focus_rect_selected_red, focus_rect_selected_green, focus_rect_selected_blue);
+                else
+                        cairo_set_source_rgb (cr, focus_rect_red, focus_rect_green, focus_rect_blue);
+                cairo_set_dash (cr, dashes, 1, 0.0);
+                cairo_set_line_width (cr, 1.0);
+                cairo_stroke (cr);
+        }
+
+        /* adjust clip rect */
+        cairo_rectangle (cr,
+                         rect_x + 3, rect_y + 3,
+                         rect_width - 3 * 2, rect_height - 3 * 2);
+        cairo_clip (cr);
+
+        /* draw icons/text */
+        if (GDU_IS_DRIVE (element->priv->presentable)) {
+                GdkPixbuf *pixbuf;
+                gint icon_width;
+                cairo_text_extents_t te;
+                gchar *s;
+                gdouble y;
+                gint line_height;
+
+                y = 0;
+
+                pixbuf = gdu_util_get_pixbuf_for_presentable (element->priv->presentable, GTK_ICON_SIZE_SMALL_TOOLBAR);
+                icon_width = 0;
+                if (pixbuf != NULL) {
+                        icon_width = gdk_pixbuf_get_width (pixbuf);
+                        render_pixbuf (cr,
+                                       ceil (rect_x) + 4,
+                                       ceil (rect_y) + 4,
+                                       pixbuf);
+                        g_object_unref (pixbuf);
+                }
+
+                if (is_selected)
+                        cairo_set_source_rgb (cr, text_selected_red, text_selected_green, text_selected_blue);
+                else
+                        cairo_set_source_rgb (cr, text_red, text_green, text_blue);
+
+                /* drive name */
+                s = gdu_presentable_get_name (element->priv->presentable);
+                cairo_select_font_face (cr,
+                                        "sans",
+                                        CAIRO_FONT_SLANT_NORMAL,
+                                        CAIRO_FONT_WEIGHT_BOLD);
+                cairo_set_font_size (cr, 8.0);
+                cairo_text_extents (cr, s, &te);
+                cairo_move_to (cr,
+                               ceil (ceil (rect_x) + 4 + icon_width + 4 - te.x_bearing),
+                               ceil (ceil (rect_y) + te.height - te.y_bearing));
+                cairo_show_text (cr, s);
+                g_free (s);
+                line_height = te.height + 4;
+                y += line_height;
+
+
+                GduDevice *d;
+                d = gdu_presentable_get_device (element->priv->presentable);
+                if (d != NULL) {
+                        s = g_strdup (gdu_device_get_device_file (d));
+                } else {
+                        s = g_strdup (" ");
+                }
+                cairo_select_font_face (cr, "sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
+                cairo_set_font_size (cr, 8.0);
+                cairo_text_extents (cr, s, &te);
+                cairo_move_to (cr, ceil (ceil (rect_x) + 4 + icon_width + 4 - te.x_bearing),
+                               ceil (ceil (rect_y) + te.height - te.y_bearing + y));
+                cairo_show_text (cr, s);
+                g_free (s);
+                y += line_height;
+
+                //s = g_strdup ("foobar");
+                //cairo_select_font_face (cr, "sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
+                //cairo_set_font_size (cr, 8.0);
+                //cairo_text_extents (cr, s, &te);
+                //cairo_move_to (cr, ceil (ceil (rect_x) + 4 - te.x_bearing),
+                //               ceil (ceil (rect_y) + te.height - te.y_bearing + y));
+                //cairo_show_text (cr, s);
+                //g_free (s);
+                //y += line_height;
+
+                if (d != NULL)
+                        g_object_unref (d);
+
+        } else {
+                gchar *s;
+                gchar *s1;
+                cairo_text_extents_t te;
+                cairo_text_extents_t te1;
+                GduDevice *d;
+                gdouble text_height;
+
+                d = gdu_presentable_get_device (element->priv->presentable);
+
+                s = NULL;
+                s1 = NULL;
+                if (d != NULL && g_strcmp0 (gdu_device_id_get_usage (d), "filesystem") == 0) {
+                        gchar *fstype_str;
+                        gchar *size_str;
+                        s = g_strdup (gdu_device_id_get_label (d));
+                        fstype_str = gdu_util_get_fstype_for_display (gdu_device_id_get_type (d),
+                                                                      gdu_device_id_get_version (d),
+                                                                      FALSE);
+                        size_str = gdu_util_get_size_for_display (gdu_device_get_size (d), FALSE);
+                        s1 = g_strdup_printf ("%s %s", size_str, fstype_str);
+                        g_free (fstype_str);
+                        g_free (size_str);
+                } else if (d != NULL && gdu_device_is_partition (d) &&
+                           (g_strcmp0 (gdu_device_partition_get_type (d), "0x05") == 0 ||
+                            g_strcmp0 (gdu_device_partition_get_type (d), "0x0f") == 0 ||
+                            g_strcmp0 (gdu_device_partition_get_type (d), "0x85") == 0)) {
+                        s = g_strdup (_("Extended"));
+                        s1 = gdu_util_get_size_for_display (gdu_presentable_get_size (element->priv->presentable), FALSE);
+                } else if (d != NULL && g_strcmp0 (gdu_device_id_get_usage (d), "crypto") == 0) {
+                        s = g_strdup (_("Encrypted"));
+                        s1 = gdu_util_get_size_for_display (gdu_presentable_get_size (element->priv->presentable), FALSE);
+                } else if (!gdu_presentable_is_allocated (element->priv->presentable)) {
+                        s = g_strdup (_("Free"));
+                        s1 = gdu_util_get_size_for_display (gdu_presentable_get_size (element->priv->presentable), FALSE);
+                } else if (!gdu_presentable_is_recognized (element->priv->presentable)) {
+                        s = g_strdup (_("Unknown"));
+                        s1 = gdu_util_get_size_for_display (gdu_presentable_get_size (element->priv->presentable), FALSE);
+                }
+
+                if (s == NULL)
+                        s = gdu_presentable_get_name (element->priv->presentable);
+                if (s1 == NULL)
+                        s1 = g_strdup ("");
+
+                if (is_selected)
+                        cairo_set_source_rgb (cr, text_selected_red, text_selected_green, text_selected_blue);
+                else
+                        cairo_set_source_rgb (cr, text_red, text_green, text_blue);
+                cairo_select_font_face (cr, "sans",
+                                        CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
+                cairo_set_font_size (cr, 8.0);
+
+                cairo_text_extents (cr, s, &te);
+                cairo_text_extents (cr, s1, &te1);
+
+                text_height = te.height + te1.height;
+
+                cairo_move_to (cr,
+                               ceil (rect_x + rect_width / 2 - te.width/2  - te.x_bearing),
+                               ceil (rect_y + rect_height / 2 - 2 - text_height/2 - te.y_bearing));
+                cairo_show_text (cr, s);
+                cairo_move_to (cr,
+                               ceil (rect_x + rect_width / 2 - te1.width/2  - te1.x_bearing),
+                               ceil (rect_y + rect_height / 2 + 2 - te1.y_bearing));
+                cairo_show_text (cr, s1);
+                g_free (s);
+                g_free (s1);
+
+                if (d != NULL)
+                        g_object_unref (d);
+        }
+
+
+        cairo_destroy (cr);
+
+        return FALSE;
+}
+
+static gboolean
+is_ctrl_pressed (void)
+{
+        gboolean ret;
+        XkbStateRec state;
+        Bool status;
+
+        ret = FALSE;
+
+        gdk_error_trap_push ();
+        status = XkbGetState (GDK_DISPLAY (), XkbUseCoreKbd, &state);
+        gdk_error_trap_pop ();
+
+        if (status == Success) {
+                ret = ((state.mods & ControlMask) != 0);
+        }
+
+        return ret;
+}
+
+static gboolean
+gdu_grid_element_key_press_event (GtkWidget      *widget,
+                                  GdkEventKey    *event)
+{
+        GduGridElement *element = GDU_GRID_ELEMENT (widget);
+        gboolean handled;
+
+        handled = FALSE;
+
+        if (event->type != GDK_KEY_PRESS)
+                goto out;
+
+        if (event->keyval == GDK_space) {
+                //g_debug ("Space pressed on %p - setting as selected", widget);
+                if (!is_ctrl_pressed ()) {
+                        gdu_grid_view_selection_clear (element->priv->view);
+                        gdu_grid_view_selection_add (element->priv->view, element->priv->presentable);
+                } else {
+                        if (!gdu_grid_view_is_selected (element->priv->view, element->priv->presentable))
+                                gdu_grid_view_selection_add (element->priv->view, element->priv->presentable);
+                        else
+                                gdu_grid_view_selection_remove (element->priv->view, element->priv->presentable);
+                }
+                gtk_widget_grab_focus (widget);
+                handled = TRUE;
+        }
+
+ out:
+        return handled;
+}
+
+static gboolean
+gdu_grid_element_button_press_event (GtkWidget      *widget,
+                                     GdkEventButton *event)
+{
+        GduGridElement *element = GDU_GRID_ELEMENT (widget);
+        gboolean handled;
+
+        handled = FALSE;
+
+        if (event->type != GDK_BUTTON_PRESS)
+                goto out;
+
+        if (event->button == 1) {
+                //g_debug ("Left button pressed on %p - setting as selected", widget);
+                if (!is_ctrl_pressed ()) {
+                        gdu_grid_view_selection_clear (element->priv->view);
+                        gdu_grid_view_selection_add (element->priv->view, element->priv->presentable);
+                } else {
+                        if (!gdu_grid_view_is_selected (element->priv->view, element->priv->presentable))
+                                gdu_grid_view_selection_add (element->priv->view, element->priv->presentable);
+                        else
+                                gdu_grid_view_selection_remove (element->priv->view, element->priv->presentable);
+                }
+                gtk_widget_grab_focus (widget);
+                handled = TRUE;
+        }
+
+ out:
+        return handled;
+}
+
+static gboolean
+gdu_grid_element_focus (GtkWidget        *widget,
+                        GtkDirectionType  direction)
+{
+        GduGridElement *element = GDU_GRID_ELEMENT (widget);
+        gboolean handled;
+
+        handled = GTK_WIDGET_CLASS (gdu_grid_element_parent_class)->focus (widget, direction);
+
+        switch (direction) {
+        case GTK_DIR_UP:
+        case GTK_DIR_DOWN:
+        case GTK_DIR_LEFT:
+        case GTK_DIR_RIGHT:
+                if (!is_ctrl_pressed ()) {
+                        gdu_grid_view_selection_clear (element->priv->view);
+                        gdu_grid_view_selection_add (element->priv->view, element->priv->presentable);
+                }
+                break;
+
+        default:
+                break;
+        }
+
+        return handled;
+}
+
+static void
+gdu_grid_element_realize (GtkWidget *widget)
+{
+        GduGridElement *element = GDU_GRID_ELEMENT (widget);
+        GdkWindowAttr attributes;
+        gint attributes_mask;
+
+        GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+
+        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.window_type = GDK_WINDOW_CHILD;
+        attributes.event_mask = gtk_widget_get_events (widget) |
+                GDK_KEY_PRESS_MASK |
+                GDK_EXPOSURE_MASK |
+                GDK_BUTTON_PRESS_MASK |
+                GDK_BUTTON_RELEASE_MASK |
+                GDK_ENTER_NOTIFY_MASK |
+                GDK_LEAVE_NOTIFY_MASK;
+        attributes.visual = gtk_widget_get_visual (widget);
+        attributes.colormap = gtk_widget_get_colormap (widget);
+
+        attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+
+        widget->window = gtk_widget_get_parent_window (widget);
+        g_object_ref (widget->window);
+
+        widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
+                                         &attributes, attributes_mask);
+        gdk_window_set_user_data (widget->window, element);
+
+        widget->style = gtk_style_attach (widget->style, widget->window);
+
+        gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
+}
+
+#if 0
+static void
+gdu_grid_element_unrealize (GtkWidget *widget)
+{
+        GduGridElement *element = GDU_GRID_ELEMENT (widget);
+
+        if (element->priv->event_window != NULL) {
+                gdk_window_set_user_data (element->priv->event_window, NULL);
+                gdk_window_destroy (element->priv->event_window);
+                element->priv->event_window = NULL;
+        }
+
+        GTK_WIDGET_CLASS (gdu_grid_element_parent_class)->unrealize (widget);
+}
+#endif
+
+static void
+gdu_grid_element_class_init (GduGridElementClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+        GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+        g_type_class_add_private (klass, sizeof (GduGridElementPrivate));
+
+        object_class->get_property = gdu_grid_element_get_property;
+        object_class->set_property = gdu_grid_element_set_property;
+        object_class->finalize     = gdu_grid_element_finalize;
+
+        widget_class->realize            = gdu_grid_element_realize;
+        //widget_class->unrealize          = gdu_grid_element_unrealize;
+        widget_class->expose_event       = gdu_grid_element_expose_event;
+        widget_class->key_press_event    = gdu_grid_element_key_press_event;
+        widget_class->button_press_event = gdu_grid_element_button_press_event;
+        widget_class->focus              = gdu_grid_element_focus;
+
+        g_object_class_install_property (object_class,
+                                         PROP_VIEW,
+                                         g_param_spec_object ("view",
+                                                              _("View"),
+                                                              _("The GduGridView object that the element is associated with"),
+                                                              GDU_TYPE_GRID_VIEW,
+                                                              G_PARAM_READABLE |
+                                                              G_PARAM_WRITABLE |
+                                                              G_PARAM_CONSTRUCT_ONLY));
+
+        g_object_class_install_property (object_class,
+                                         PROP_PRESENTABLE,
+                                         g_param_spec_object ("presentable",
+                                                              _("Presentable"),
+                                                              _("The presentable shown or NULL if this is a element representing lack of media"),
+                                                              GDU_TYPE_PRESENTABLE,
+                                                              G_PARAM_READABLE |
+                                                              G_PARAM_WRITABLE |
+                                                              G_PARAM_CONSTRUCT_ONLY));
+
+        g_object_class_install_property (object_class,
+                                         PROP_MINIMUM_SIZE,
+                                         g_param_spec_uint ("minimum-size",
+                                                            _("Minimum Size"),
+                                                            _("The mininum size of the element"),
+                                                            0,
+                                                            G_MAXUINT,
+                                                            40,
+                                                            G_PARAM_READABLE |
+                                                            G_PARAM_WRITABLE |
+                                                            G_PARAM_CONSTRUCT_ONLY));
+
+        g_object_class_install_property (object_class,
+                                         PROP_PERCENT_SIZE,
+                                         g_param_spec_double ("percent-size",
+                                                              _("Percent Size"),
+                                                              _("The size in percent this element should claim or 0 to always claim the specified minimum size"),
+                                                              0.0,
+                                                              100.0,
+                                                              0.0,
+                                                              G_PARAM_READABLE |
+                                                              G_PARAM_WRITABLE |
+                                                              G_PARAM_CONSTRUCT_ONLY));
+
+        g_object_class_install_property (object_class,
+                                         PROP_FLAGS, /* TODO: proper type */
+                                         g_param_spec_uint ("flags",
+                                                            _("Flags"),
+                                                            _("Flags for the element"),
+                                                            0,
+                                                            G_MAXUINT,
+                                                            0,
+                                                            G_PARAM_READABLE |
+                                                            G_PARAM_WRITABLE |
+                                                            G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+gdu_grid_element_init (GduGridElement *element)
+{
+        element->priv = G_TYPE_INSTANCE_GET_PRIVATE (element, GDU_TYPE_GRID_ELEMENT, GduGridElementPrivate);
+
+        GTK_WIDGET_SET_FLAGS (element, GTK_CAN_FOCUS);
+}
+
+GtkWidget *
+gdu_grid_element_new (GduGridView        *view,
+                      GduPresentable     *presentable,
+                      guint               minimum_size,
+                      gdouble             percent_size,
+                      GduGridElementFlags flags)
+{
+        return GTK_WIDGET (g_object_new (GDU_TYPE_GRID_ELEMENT,
+                                         "view", view,
+                                         "presentable", presentable,
+                                         "minimum-size", minimum_size,
+                                         "percent-size", percent_size,
+                                         "flags", flags,
+                                         NULL));
+}
+
+GduGridView *
+gdu_grid_element_get_view (GduGridElement *element)
+{
+        return g_object_ref (element->priv->view);
+}
+
+GduPresentable *
+gdu_grid_element_get_presentable (GduGridElement *element)
+{
+        return g_object_ref (element->priv->presentable);
+}
+
+guint
+gdu_grid_element_get_minimum_size (GduGridElement *element)
+{
+        return element->priv->minimum_size;
+}
+
+gdouble
+gdu_grid_element_get_percent_size (GduGridElement *element)
+{
+        return element->priv->percent_size;
+}
+
+GduGridElementFlags
+gdu_grid_element_get_flags (GduGridElement *element)
+{
+        return element->priv->flags;
+}
+
diff --git a/src/playground/grid/gdu-grid-element.h b/src/playground/grid/gdu-grid-element.h
new file mode 100644
index 0000000..cd64b0f
--- /dev/null
+++ b/src/playground/grid/gdu-grid-element.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+
+#ifndef __GDU_GRID_ELEMENT_H
+#define __GDU_GRID_ELEMENT_H
+
+#include "gdu-grid-types.h"
+
+G_BEGIN_DECLS
+
+#define GDU_TYPE_GRID_ELEMENT         gdu_grid_element_get_type()
+#define GDU_GRID_ELEMENT(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDU_TYPE_GRID_ELEMENT, GduGridElement))
+#define GDU_GRID_ELEMENT_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), GDU_TYPE_GRID_ELEMENT, GduGridElementClass))
+#define GDU_IS_GRID_ELEMENT(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDU_TYPE_GRID_ELEMENT))
+#define GDU_IS_GRID_ELEMENT_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GDU_TYPE_GRID_ELEMENT))
+#define GDU_GRID_ELEMENT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDU_TYPE_GRID_ELEMENT, GduGridElementClass))
+
+typedef struct GduGridElementClass   GduGridElementClass;
+typedef struct GduGridElementPrivate GduGridElementPrivate;
+
+struct GduGridElement
+{
+        GtkDrawingArea parent;
+
+        /*< private >*/
+        GduGridElementPrivate *priv;
+};
+
+struct GduGridElementClass
+{
+        GtkDrawingAreaClass parent_class;
+};
+
+typedef enum
+{
+        GDU_GRID_ELEMENT_FLAGS_NONE        = 0,
+        GDU_GRID_ELEMENT_FLAGS_EDGE_TOP    = (1<<0),
+        GDU_GRID_ELEMENT_FLAGS_EDGE_BOTTOM = (1<<1),
+        GDU_GRID_ELEMENT_FLAGS_EDGE_LEFT   = (1<<2),
+        GDU_GRID_ELEMENT_FLAGS_EDGE_RIGHT  = (1<<3)
+} GduGridElementFlags;
+
+GType           gdu_grid_element_get_type         (void) G_GNUC_CONST;
+GtkWidget*      gdu_grid_element_new              (GduGridView        *view,
+                                                   GduPresentable     *presentable,
+                                                   guint               minimum_size,
+                                                   gdouble             percent_size,
+                                                   GduGridElementFlags flags);
+GduGridView        *gdu_grid_element_get_view         (GduGridElement *element);
+GduPresentable     *gdu_grid_element_get_presentable  (GduGridElement *element);
+guint               gdu_grid_element_get_minimum_size (GduGridElement *element);
+gdouble             gdu_grid_element_get_percent_size (GduGridElement *element);
+GduGridElementFlags gdu_grid_element_get_flags        (GduGridElement *element);
+
+G_END_DECLS
+
+
+
+#endif /* __GDU_GRID_ELEMENT_H */
diff --git a/src/playground/grid/gdu-grid-hbox.c b/src/playground/grid/gdu-grid-hbox.c
new file mode 100644
index 0000000..38890c4
--- /dev/null
+++ b/src/playground/grid/gdu-grid-hbox.c
@@ -0,0 +1,169 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+
+#include <glib/gi18n.h>
+
+#include "gdu-grid-hbox.h"
+#include "gdu-grid-element.h"
+
+struct GduGridHBoxPrivate
+{
+        guint dummy;
+};
+
+G_DEFINE_TYPE (GduGridHBox, gdu_grid_hbox, GTK_TYPE_HBOX)
+
+static void
+gdu_grid_hbox_finalize (GObject *object)
+{
+        //GduGridHBox *hbox = GDU_GRID_HBOX (object);
+
+        if (G_OBJECT_CLASS (gdu_grid_hbox_parent_class)->finalize != NULL)
+                G_OBJECT_CLASS (gdu_grid_hbox_parent_class)->finalize (object);
+}
+
+static guint
+get_desired_width (GduGridHBox *hbox)
+{
+        guint width;
+        GList *children;
+        GList *l;
+
+        width = 0;
+
+        children = GTK_BOX (hbox)->children;
+        if (children == NULL)
+                goto out;
+
+        for (l = children; l != NULL; l = l->next) {
+                GtkBoxChild *child = l->data;
+                GduGridElement *e;
+
+                if (GTK_IS_VBOX (child->widget)) {
+                        e = GDU_GRID_ELEMENT (((GtkBoxChild *) ((GTK_BOX (child->widget)->children)->data))->widget);
+                } else {
+                        e = GDU_GRID_ELEMENT (child->widget);
+                }
+
+                width += gdu_grid_element_get_minimum_size (e);
+        }
+
+ out:
+        return width;
+}
+
+static void
+gdu_grid_hbox_size_request (GtkWidget      *widget,
+                            GtkRequisition *requisition)
+{
+        requisition->width = get_desired_width (GDU_GRID_HBOX (widget));
+        requisition->height = 80;
+}
+
+static void
+gdu_grid_hbox_size_allocate (GtkWidget      *widget,
+                             GtkAllocation  *allocation)
+{
+        GList *children;
+        GList *l;
+        guint n;
+        guint num_children;
+        gint x;
+        guint *children_sizes;
+        guint used_size;
+        guint extra_space;
+        guint desired_width;
+
+        children = GTK_BOX (widget)->children;
+        if (children == NULL)
+                goto out;
+
+        num_children = g_list_length (children);
+
+        children_sizes = g_new0 (guint, num_children);
+
+        /* distribute size.. give at least minimum_width (since that is guaranteed to work) and
+         * then assign extra space based on the percentage
+         */
+        desired_width = get_desired_width (GDU_GRID_HBOX (widget));
+        if (desired_width < allocation->width)
+                extra_space = allocation->width - desired_width;
+        else
+                extra_space = 0;
+
+        used_size = 0;
+        for (l = children, n = 0; l != NULL; l = l->next, n++) {
+                GtkBoxChild *child = l->data;
+                GduGridElement *e;
+                guint width;
+                guint e_minimum;
+                gdouble e_percent;
+
+                if (GTK_IS_VBOX (child->widget)) {
+                        e = GDU_GRID_ELEMENT (((GtkBoxChild *) ((GTK_BOX (child->widget)->children)->data))->widget);
+                } else {
+                        e = GDU_GRID_ELEMENT (child->widget);
+                }
+
+                e_minimum = gdu_grid_element_get_minimum_size (e);
+                e_percent = gdu_grid_element_get_percent_size (e);
+
+                width = e_minimum + e_percent * extra_space;
+
+                /* fix up last child so it's aligned with the right border */
+                if (l->next == NULL) {
+                        if (e_percent != 0.0) {
+                                width = allocation->width - used_size;
+                        }
+                }
+
+                children_sizes[n] = width;
+                used_size += width;
+        }
+
+        x = 0;
+        for (l = children, n = 0; l != NULL; l = l->next, n++) {
+                GtkBoxChild *child = l->data;
+                GtkAllocation child_allocation;
+
+                child_allocation.x = allocation->x + x;
+                child_allocation.y = allocation->y;
+                child_allocation.width = children_sizes[n];
+                child_allocation.height = allocation->height;
+                x += children_sizes[n];
+
+                gtk_widget_size_allocate (child->widget, &child_allocation);
+        }
+
+        g_free (children_sizes);
+
+ out:
+        ;
+}
+
+static void
+gdu_grid_hbox_class_init (GduGridHBoxClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+        GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+        g_type_class_add_private (klass, sizeof (GduGridHBoxPrivate));
+
+        object_class->finalize     = gdu_grid_hbox_finalize;
+
+        widget_class->size_request = gdu_grid_hbox_size_request;
+        widget_class->size_allocate = gdu_grid_hbox_size_allocate;
+}
+
+static void
+gdu_grid_hbox_init (GduGridHBox *box)
+{
+}
+
+GtkWidget *
+gdu_grid_hbox_new (void)
+{
+        return GTK_WIDGET (g_object_new (GDU_TYPE_GRID_HBOX,
+                                         NULL));
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
diff --git a/src/playground/grid/gdu-grid-hbox.h b/src/playground/grid/gdu-grid-hbox.h
new file mode 100644
index 0000000..b3dc5d5
--- /dev/null
+++ b/src/playground/grid/gdu-grid-hbox.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+
+#ifndef __GDU_GRID_HBOX_H
+#define __GDU_GRID_HBOX_H
+
+#include "gdu-grid-types.h"
+
+G_BEGIN_DECLS
+
+#define GDU_TYPE_GRID_HBOX         gdu_grid_hbox_get_type()
+#define GDU_GRID_HBOX(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDU_TYPE_GRID_HBOX, GduGridHBox))
+#define GDU_GRID_HBOX_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), GDU_TYPE_GRID_HBOX, GduGridHBoxClass))
+#define GDU_IS_GRID_HBOX(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDU_TYPE_GRID_HBOX))
+#define GDU_IS_GRID_HBOX_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GDU_TYPE_GRID_HBOX))
+#define GDU_GRID_HBOX_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDU_TYPE_GRID_HBOX, GduGridHBoxClass))
+
+typedef struct GduGridHBoxClass   GduGridHBoxClass;
+typedef struct GduGridHBoxPrivate GduGridHBoxPrivate;
+
+struct GduGridHBox
+{
+        GtkHBox parent;
+
+        /*< private >*/
+        GduGridHBoxPrivate *priv;
+};
+
+struct GduGridHBoxClass
+{
+        GtkHBoxClass parent_class;
+};
+
+GType       gdu_grid_hbox_get_type (void) G_GNUC_CONST;
+GtkWidget*  gdu_grid_hbox_new      (void);
+
+G_END_DECLS
+
+#endif /* __GDU_GRID_HBOX_H */
diff --git a/src/playground/grid/gdu-grid-types.h b/src/playground/grid/gdu-grid-types.h
new file mode 100644
index 0000000..14d969d
--- /dev/null
+++ b/src/playground/grid/gdu-grid-types.h
@@ -0,0 +1,17 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+
+#ifndef __GDU_GRID_TYPES_H
+#define __GDU_GRID_TYPES_H
+
+#include <gtk/gtk.h>
+#include <gdu/gdu.h>
+
+G_BEGIN_DECLS
+
+typedef struct GduGridView        GduGridView;
+typedef struct GduGridHBox        GduGridHBox;
+typedef struct GduGridElement     GduGridElement;
+
+G_END_DECLS
+
+#endif /* __GDU_GRID_TYPES_H */
diff --git a/src/playground/grid/gdu-grid-view.c b/src/playground/grid/gdu-grid-view.c
new file mode 100644
index 0000000..936e2ad
--- /dev/null
+++ b/src/playground/grid/gdu-grid-view.c
@@ -0,0 +1,455 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+
+#include <glib/gi18n.h>
+
+#include "gdu-grid-view.h"
+#include "gdu-grid-element.h"
+#include "gdu-grid-hbox.h"
+
+struct GduGridViewPrivate
+{
+        GduPool *pool;
+        GList *elements;
+
+        /* GList of GduPresentable of the currently selected elements */
+        GList *selected;
+};
+
+enum
+{
+        PROP_0,
+        PROP_POOL,
+};
+
+static void on_presentable_added   (GduPool        *pool,
+                                    GduPresentable *presentable,
+                                    GduGridView    *view);
+static void on_presentable_removed (GduPool        *pool,
+                                    GduPresentable *presentable,
+                                    GduGridView    *view);
+static void on_presentable_changed (GduPool        *pool,
+                                    GduPresentable *presentable,
+                                    GduGridView    *view);
+
+G_DEFINE_TYPE (GduGridView, gdu_grid_view, GTK_TYPE_VBOX)
+
+static void
+gdu_grid_view_finalize (GObject *object)
+{
+        GduGridView *view = GDU_GRID_VIEW (object);
+
+        g_list_foreach (view->priv->selected, (GFunc) g_object_unref, NULL);
+        g_list_free (view->priv->selected);
+
+        g_list_free (view->priv->elements);
+
+        g_signal_handlers_disconnect_by_func (view->priv->pool, on_presentable_added, view);
+        g_signal_handlers_disconnect_by_func (view->priv->pool, on_presentable_removed, view);
+        g_signal_handlers_disconnect_by_func (view->priv->pool, on_presentable_changed, view);
+        g_object_unref (view->priv->pool);
+
+        if (G_OBJECT_CLASS (gdu_grid_view_parent_class)->finalize != NULL)
+                G_OBJECT_CLASS (gdu_grid_view_parent_class)->finalize (object);
+}
+
+static void
+gdu_grid_view_get_property (GObject    *object,
+                            guint       property_id,
+                            GValue     *value,
+                            GParamSpec *pspec)
+{
+        GduGridView *view = GDU_GRID_VIEW (object);
+
+        switch (property_id) {
+        case PROP_POOL:
+                g_value_set_object (value, view->priv->pool);
+                break;
+
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+        }
+}
+
+static void
+gdu_grid_view_set_property (GObject      *object,
+                            guint         property_id,
+                            const GValue *value,
+                            GParamSpec   *pspec)
+{
+        GduGridView *view = GDU_GRID_VIEW (object);
+
+        switch (property_id) {
+        case PROP_POOL:
+                view->priv->pool = g_value_dup_object (value);
+                break;
+
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+        }
+}
+
+static gint
+presentable_sort_offset (GduPresentable *a, GduPresentable *b)
+{
+        guint64 oa, ob;
+
+        oa = gdu_presentable_get_offset (a);
+        ob = gdu_presentable_get_offset (b);
+
+        if (oa < ob)
+                return -1;
+        else if (oa > ob)
+                return 1;
+        else
+                return 0;
+}
+
+static void
+recompute_grid (GduGridView *view)
+{
+        GList *presentables;
+        GList *l;
+        GList *children;
+
+        children = gtk_container_get_children (GTK_CONTAINER (view));
+        for (l = children; l != NULL; l = l->next) {
+                gtk_container_remove (GTK_CONTAINER (view), GTK_WIDGET (l->data));
+        }
+        g_list_free (children);
+
+        g_list_free (view->priv->elements);
+        view->priv->elements = NULL;
+
+        presentables = gdu_pool_get_presentables (view->priv->pool);
+        presentables = g_list_sort (presentables, (GCompareFunc) gdu_presentable_compare);
+        for (l = presentables; l != NULL; l = l->next) {
+                GduPresentable *p = GDU_PRESENTABLE (l->data);
+                GtkWidget *element;
+                GtkWidget *hbox;
+                guint64 size;
+                GList *enclosed_partitions;
+                GList *ll;
+
+                if (!GDU_IS_DRIVE (p))
+                        continue;
+
+                size = gdu_presentable_get_size (p);
+
+                hbox = gdu_grid_hbox_new ();
+                gtk_box_pack_start (GTK_BOX (view),
+                                    hbox,
+                                    FALSE,
+                                    FALSE,
+                                    0);
+
+                enclosed_partitions = gdu_pool_get_enclosed_presentables (view->priv->pool, p);
+                enclosed_partitions = g_list_sort (enclosed_partitions, (GCompareFunc) presentable_sort_offset);
+
+                element = gdu_grid_element_new (view,
+                                                p,
+                                                180,
+                                                0.0,
+                                                GDU_GRID_ELEMENT_FLAGS_EDGE_TOP |
+                                                GDU_GRID_ELEMENT_FLAGS_EDGE_BOTTOM |
+                                                GDU_GRID_ELEMENT_FLAGS_EDGE_LEFT |
+                                                (enclosed_partitions == NULL ? GDU_GRID_ELEMENT_FLAGS_EDGE_RIGHT : 0));
+                view->priv->elements = g_list_prepend (view->priv->elements, element);
+                gtk_box_pack_start (GTK_BOX (hbox),
+                                    element,
+                                    FALSE,
+                                    FALSE,
+                                    0);
+
+                for (ll = enclosed_partitions; ll != NULL; ll = ll->next) {
+                        GduPresentable *ep = GDU_PRESENTABLE (ll->data);
+                        GList *enclosed_logical_partitions;
+                        gboolean is_last;
+
+                        is_last = (ll->next == NULL);
+
+                        /* handle extended partitions */
+                        enclosed_logical_partitions = gdu_pool_get_enclosed_presentables (view->priv->pool, ep);
+                        if (enclosed_logical_partitions != NULL) {
+                                GList *lll;
+                                guint64 esize;
+                                GtkWidget *vbox;
+                                GtkWidget *hbox2;
+                                guint num_logical_partitions;
+
+                                num_logical_partitions = g_list_length (enclosed_logical_partitions);
+
+                                vbox = gtk_vbox_new (TRUE, 0);
+                                gtk_box_pack_start (GTK_BOX (hbox),
+                                                    vbox,
+                                                    FALSE,
+                                                    FALSE,
+                                                    0);
+
+                                /* the extended partition */
+                                esize = gdu_presentable_get_size (ep);
+                                element = gdu_grid_element_new (view,
+                                                                ep,
+                                                                60 * num_logical_partitions,
+                                                                ((gdouble) esize) / size,
+                                                                GDU_GRID_ELEMENT_FLAGS_EDGE_TOP |
+                                                                (is_last ? GDU_GRID_ELEMENT_FLAGS_EDGE_RIGHT : 0));
+                                view->priv->elements = g_list_prepend (view->priv->elements, element);
+                                gtk_box_pack_start (GTK_BOX (vbox),
+                                                    element,
+                                                    TRUE,
+                                                    TRUE,
+                                                    0);
+
+                                /* hbox for logical partitions */
+                                hbox2 = gdu_grid_hbox_new ();
+                                gtk_box_pack_start (GTK_BOX (vbox),
+                                                    hbox2,
+                                                    TRUE,
+                                                    TRUE,
+                                                    0);
+
+                                /* handle logical partitions in extended partition */
+                                enclosed_logical_partitions = g_list_sort (enclosed_logical_partitions,
+                                                                           (GCompareFunc) presentable_sort_offset);
+                                for (lll = enclosed_logical_partitions; lll != NULL; lll = lll->next) {
+                                        GduPresentable *lp = GDU_PRESENTABLE (lll->data);
+                                        guint64 lsize;
+                                        gboolean is_last_logical;
+
+                                        is_last_logical = (is_last && (lll->next == NULL));
+
+                                        lsize = gdu_presentable_get_size (lp);
+                                        element = gdu_grid_element_new (view,
+                                                                        lp,
+                                                                        60,
+                                                                        ((gdouble) lsize) / esize,
+                                                                        GDU_GRID_ELEMENT_FLAGS_EDGE_BOTTOM |
+                                                                        (is_last_logical ? GDU_GRID_ELEMENT_FLAGS_EDGE_RIGHT : 0));
+                                        view->priv->elements = g_list_prepend (view->priv->elements, element);
+                                        gtk_box_pack_start (GTK_BOX (hbox2),
+                                                            element,
+                                                            FALSE,
+                                                            FALSE,
+                                                            0);
+                                }
+                                g_list_foreach (enclosed_logical_partitions, (GFunc) g_object_unref, NULL);
+                                g_list_free (enclosed_logical_partitions);
+                        } else {
+                                guint64 psize;
+
+                                /* primary partition */
+                                psize = gdu_presentable_get_size (ep);
+                                element = gdu_grid_element_new (view,
+                                                                ep,
+                                                                60,
+                                                                ((gdouble) psize) / size,
+                                                                GDU_GRID_ELEMENT_FLAGS_EDGE_TOP |
+                                                                GDU_GRID_ELEMENT_FLAGS_EDGE_BOTTOM |
+                                                                (is_last ? GDU_GRID_ELEMENT_FLAGS_EDGE_RIGHT : 0));
+                                view->priv->elements = g_list_prepend (view->priv->elements, element);
+                                gtk_box_pack_start (GTK_BOX (hbox),
+                                                    element,
+                                                    FALSE,
+                                                    FALSE,
+                                                    0);
+                        }
+
+                }
+                g_list_foreach (enclosed_partitions, (GFunc) g_object_unref, NULL);
+                g_list_free (enclosed_partitions);
+
+                gtk_widget_show_all (GTK_WIDGET (hbox));
+        }
+        g_list_foreach (presentables, (GFunc) g_object_unref, NULL);
+        g_list_free (presentables);
+}
+
+static void
+gdu_grid_view_constructed (GObject *object)
+{
+        GduGridView *view = GDU_GRID_VIEW (object);
+
+        recompute_grid (view);
+
+        g_signal_connect (view->priv->pool, "presentable-added", G_CALLBACK (on_presentable_added), view);
+        g_signal_connect (view->priv->pool, "presentable-removed", G_CALLBACK (on_presentable_removed), view);
+        g_signal_connect (view->priv->pool, "presentable-changed", G_CALLBACK (on_presentable_changed), view);
+
+        if (G_OBJECT_CLASS (gdu_grid_view_parent_class)->constructed != NULL)
+                G_OBJECT_CLASS (gdu_grid_view_parent_class)->constructed (object);
+}
+
+static void
+gdu_grid_view_class_init (GduGridViewClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+        g_type_class_add_private (klass, sizeof (GduGridViewPrivate));
+
+        object_class->get_property = gdu_grid_view_get_property;
+        object_class->set_property = gdu_grid_view_set_property;
+        object_class->constructed  = gdu_grid_view_constructed;
+        object_class->finalize     = gdu_grid_view_finalize;
+
+        g_object_class_install_property (object_class,
+                                         PROP_POOL,
+                                         g_param_spec_object ("pool",
+                                                              _("Pool"),
+                                                              _("The pool of devices to show"),
+                                                              GDU_TYPE_POOL,
+                                                              G_PARAM_READABLE |
+                                                              G_PARAM_WRITABLE |
+                                                              G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+gdu_grid_view_init (GduGridView *view)
+{
+        view->priv = G_TYPE_INSTANCE_GET_PRIVATE (view, GDU_TYPE_GRID_VIEW, GduGridViewPrivate);
+}
+
+GtkWidget *
+gdu_grid_view_new (GduPool *pool)
+{
+        return GTK_WIDGET (g_object_new (GDU_TYPE_GRID_VIEW,
+                                         "pool", pool,
+                                         NULL));
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/* TODO: It would be a lot more efficient to just add/remove elements as appropriate.. also,
+ *       if we did that we wouldn't be screwing a11y users over
+ */
+
+static void
+on_presentable_added (GduPool        *pool,
+                      GduPresentable *presentable,
+                      GduGridView    *view)
+{
+        recompute_grid (view);
+}
+
+
+static void
+on_presentable_removed (GduPool        *pool,
+                        GduPresentable *presentable,
+                        GduGridView    *view)
+{
+        gdu_grid_view_selection_remove (view, presentable);
+        recompute_grid (view);
+}
+
+static void
+on_presentable_changed (GduPool        *pool,
+                        GduPresentable *presentable,
+                        GduGridView    *view)
+{
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static GduGridElement *
+get_element_for_presentable (GduGridView *view,
+                             GduPresentable *presentable)
+{
+        GduGridElement *ret;
+        GList *l;
+
+        ret = NULL;
+
+        for (l = view->priv->elements; l != NULL && ret == NULL; l = l->next) {
+                GduGridElement *e = GDU_GRID_ELEMENT (l->data);
+                GduPresentable *p;
+                p = gdu_grid_element_get_presentable (e);
+                if (p != NULL) {
+                        if (p == presentable)
+                                ret = e;
+                        g_object_unref (p);
+                }
+        }
+
+        return ret;
+}
+
+GList *
+gdu_grid_view_selection_get (GduGridView *view)
+{
+        GList *ret;
+        ret = g_list_copy (view->priv->selected);
+        g_list_foreach (ret, (GFunc) g_object_ref, NULL);
+        return ret;
+}
+
+void
+gdu_grid_view_selection_add (GduGridView    *view,
+                             GduPresentable *presentable)
+{
+        GduGridElement *e;
+
+        g_return_if_fail (presentable != NULL);
+
+        view->priv->selected = g_list_prepend (view->priv->selected, g_object_ref (presentable));
+
+        e = get_element_for_presentable (view, presentable);
+        if (e != NULL) {
+                gtk_widget_queue_draw (GTK_WIDGET (e));
+        }
+}
+
+void
+gdu_grid_view_selection_remove (GduGridView    *view,
+                                GduPresentable *presentable)
+{
+        GList *l;
+        for (l = view->priv->selected; l != NULL; l = l->next) {
+                if (l->data == presentable) {
+                        GduGridElement *e;
+                        e = get_element_for_presentable (view, presentable);
+                        if (e != NULL) {
+                                gtk_widget_queue_draw (GTK_WIDGET (e));
+                        }
+                        view->priv->selected = g_list_remove (view->priv->selected, presentable);
+                        g_object_unref (presentable);
+                        break;
+                }
+        }
+}
+
+gboolean
+gdu_grid_view_is_selected (GduGridView    *view,
+                           GduPresentable *presentable)
+{
+        GList *l;
+        gboolean ret;
+
+        ret = FALSE;
+
+        for (l = view->priv->selected; l != NULL; l = l->next) {
+                if (l->data == presentable) {
+                        ret = TRUE;
+                        break;
+                }
+        }
+
+        return ret;
+}
+
+void
+gdu_grid_view_selection_clear (GduGridView *view)
+{
+        GList *l;
+        for (l = view->priv->selected; l != NULL; l = l->next) {
+                GduGridElement *e;
+                e = get_element_for_presentable (view, l->data);
+                if (e != NULL) {
+                        gtk_widget_queue_draw (GTK_WIDGET (e));
+                }
+        }
+
+        g_list_foreach (view->priv->selected, (GFunc) g_object_unref, NULL);
+        g_list_free (view->priv->selected);
+        view->priv->selected = NULL;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
diff --git a/src/playground/grid/gdu-grid-view.h b/src/playground/grid/gdu-grid-view.h
new file mode 100644
index 0000000..9e00e54
--- /dev/null
+++ b/src/playground/grid/gdu-grid-view.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+
+#ifndef __GDU_GRID_VIEW_H
+#define __GDU_GRID_VIEW_H
+
+#include "gdu-grid-types.h"
+
+G_BEGIN_DECLS
+
+#define GDU_TYPE_GRID_VIEW         gdu_grid_view_get_type()
+#define GDU_GRID_VIEW(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDU_TYPE_GRID_VIEW, GduGridView))
+#define GDU_GRID_VIEW_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), GDU_TYPE_GRID_VIEW, GduGridViewClass))
+#define GDU_IS_GRID_VIEW(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDU_TYPE_GRID_VIEW))
+#define GDU_IS_GRID_VIEW_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GDU_TYPE_GRID_VIEW))
+#define GDU_GRID_VIEW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDU_TYPE_GRID_VIEW, GduGridViewClass))
+
+typedef struct GduGridViewClass   GduGridViewClass;
+typedef struct GduGridViewPrivate GduGridViewPrivate;
+
+struct GduGridView
+{
+        GtkVBox parent;
+
+        /*< private >*/
+        GduGridViewPrivate *priv;
+};
+
+struct GduGridViewClass
+{
+        GtkVBoxClass parent_class;
+};
+
+GType           gdu_grid_view_get_type         (void) G_GNUC_CONST;
+GtkWidget      *gdu_grid_view_new              (GduPool        *pool);
+
+gboolean        gdu_grid_view_is_selected      (GduGridView    *view,
+                                                GduPresentable *presentable);
+GList          *gdu_grid_view_selection_get    (GduGridView    *view);
+void            gdu_grid_view_selection_add    (GduGridView    *view,
+                                                GduPresentable *presentable);
+void            gdu_grid_view_selection_remove (GduGridView    *view,
+                                                GduPresentable *presentable);
+void            gdu_grid_view_selection_clear  (GduGridView    *view);
+
+G_END_DECLS
+
+#endif /* __GDU_GRID_VIEW_H */
diff --git a/src/playground/grid/grid.c b/src/playground/grid/grid.c
new file mode 100644
index 0000000..7a86120
--- /dev/null
+++ b/src/playground/grid/grid.c
@@ -0,0 +1,58 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+
+#include <gtk/gtk.h>
+#include <gdu/gdu.h>
+
+#include "gdu-grid-view.h"
+
+int
+main (int argc, char *argv[])
+{
+        GduPool *pool;
+        GtkWidget *window;
+        GtkWidget *vbox;
+        GtkWidget *scrolled_window;
+        GtkWidget *grid_view;
+
+        gtk_init (&argc, &argv);
+
+        pool = gdu_pool_new ();
+
+        window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+        vbox = gtk_vbox_new (FALSE, 0);
+        gtk_container_add (GTK_CONTAINER (window), vbox);
+
+        grid_view = gdu_grid_view_new (pool);
+
+        scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+        gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+                                        GTK_POLICY_NEVER,
+                                        GTK_POLICY_AUTOMATIC);
+        gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled_window), grid_view);
+        gtk_box_pack_start (GTK_BOX (vbox),
+                            scrolled_window,
+                            TRUE,
+                            TRUE,
+                            0);
+
+        /* add a dummy button box for now.. just to test focus */
+        GtkWidget *button_box;
+        button_box = gtk_hbutton_box_new ();
+        gtk_container_add (GTK_CONTAINER (button_box), gtk_button_new_from_stock (GTK_STOCK_OK));
+        gtk_container_add (GTK_CONTAINER (button_box), gtk_button_new_from_stock (GTK_STOCK_APPLY));
+        gtk_container_add (GTK_CONTAINER (button_box), gtk_button_new_from_stock (GTK_STOCK_CANCEL));
+        gtk_box_pack_start (GTK_BOX (vbox),
+                            button_box,
+                            FALSE,
+                            FALSE,
+                            0);
+
+        gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);
+        gtk_widget_show_all (window);
+        gtk_main ();
+
+        g_object_unref (pool);
+
+        return 0;
+}



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