From 3a45434834999e1ac7c7a99cdaa644d939b859b0 Mon Sep 17 00:00:00 2001
From: Matthias Vogelgesang <matthias.vogelgesang@gmail.com>
Date: Tue, 25 Sep 2012 18:18:23 +0200
Subject: Move tools from test/ to tools/ directory

---
 tools/gui/control.c                    | 350 +++++++++++++++++++
 tools/gui/control.glade                | 302 +++++++++++++++++
 tools/gui/egg-property-cell-renderer.c | 594 +++++++++++++++++++++++++++++++++
 tools/gui/egg-property-cell-renderer.h |  55 +++
 tools/gui/egg-property-tree-view.c     | 113 +++++++
 tools/gui/egg-property-tree-view.h     |  54 +++
 6 files changed, 1468 insertions(+)
 create mode 100644 tools/gui/control.c
 create mode 100644 tools/gui/control.glade
 create mode 100644 tools/gui/egg-property-cell-renderer.c
 create mode 100644 tools/gui/egg-property-cell-renderer.h
 create mode 100644 tools/gui/egg-property-tree-view.c
 create mode 100644 tools/gui/egg-property-tree-view.h

(limited to 'tools/gui')

diff --git a/tools/gui/control.c b/tools/gui/control.c
new file mode 100644
index 0000000..75b3cde
--- /dev/null
+++ b/tools/gui/control.c
@@ -0,0 +1,350 @@
+/* Copyright (C) 2011, 2012 Matthias Vogelgesang <matthias.vogelgesang@kit.edu>
+   (Karlsruhe Institute of Technology)
+
+   This library is free software; you can redistribute it and/or modify it
+   under the terms of the GNU Lesser General Public License as published by the
+   Free Software Foundation; either version 2.1 of the License, or (at your
+   option) any later version.
+
+   This library is distributed in the hope that it will be useful, but WITHOUT
+   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+   FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+   details.
+
+   You should have received a copy of the GNU Lesser General Public License along
+   with this library; if not, write to the Free Software Foundation, Inc., 51
+   Franklin St, Fifth Floor, Boston, MA 02110, USA */
+
+#include <glib/gprintf.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkkeysyms.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "config.h"
+#include "uca-camera.h"
+#include "uca-plugin-manager.h"
+#include "egg-property-tree-view.h"
+
+
+typedef struct {
+    gboolean running;
+    gboolean store;
+
+    guchar *buffer, *pixels;
+    GdkPixbuf *pixbuf;
+    GtkWidget *image;
+    GtkTreeModel *property_model;
+    UcaCamera *camera;
+
+    GtkStatusbar *statusbar;
+    guint statusbar_context_id;
+
+    int timestamp;
+    int width;
+    int height;
+    int pixel_size;
+} ThreadData;
+
+enum {
+    COLUMN_NAME = 0,
+    COLUMN_VALUE,
+    COLUMN_EDITABLE,
+    NUM_COLUMNS
+};
+
+static UcaPluginManager *plugin_manager;
+
+static void
+convert_8bit_to_rgb (guchar *output, guchar *input, int width, int height)
+{
+    for (int i = 0, j = 0; i < width*height; i++) {
+        output[j++] = input[i];
+        output[j++] = input[i];
+        output[j++] = input[i];
+    }
+}
+
+static void
+convert_16bit_to_rgb (guchar *output, guchar *input, int width, int height)
+{
+    guint16 *in = (guint16 *) input;
+    guint16 min = G_MAXUINT16, max = 0;
+    gfloat spread = 0.0f;
+
+    for (int i = 0; i < width * height; i++) {
+        guint16 v = in[i];
+        if (v < min)
+            min = v;
+        if (v > max)
+            max = v;
+    }
+
+    spread = (gfloat) max - min;
+
+    if (spread > 0.0f) {
+        for (int i = 0, j = 0; i < width*height; i++) {
+            guchar val = (guint8) (((in[i] - min) / spread) * 255.0f);
+            output[j++] = val;
+            output[j++] = val;
+            output[j++] = val;
+        }
+    }
+}
+
+static void *
+grab_thread (void *args)
+{
+    ThreadData *data = (ThreadData *) args;
+    gchar filename[FILENAME_MAX] = {0,};
+    gint counter = 0;
+
+    while (data->running) {
+        uca_camera_grab (data->camera, (gpointer) &data->buffer, NULL);
+
+        if (data->store) {
+            snprintf (filename, FILENAME_MAX, "frame-%i-%08i.raw", data->timestamp, counter++);
+            FILE *fp = fopen (filename, "wb");
+            fwrite (data->buffer, data->width*data->height, data->pixel_size, fp);
+            fclose (fp);
+        }
+
+        /* FIXME: We should actually check if this is really a new frame and
+         * just do nothing if it is an already displayed one. */
+        if (data->pixel_size == 1)
+            convert_8bit_to_rgb (data->pixels, data->buffer, data->width, data->height);
+        else if (data->pixel_size == 2) {
+            convert_16bit_to_rgb (data->pixels, data->buffer, data->width, data->height);
+        }
+
+        gdk_threads_enter ();
+        gdk_flush ();
+        gtk_image_clear (GTK_IMAGE (data->image));
+        gtk_image_set_from_pixbuf (GTK_IMAGE (data->image), data->pixbuf);
+        gtk_widget_queue_draw_area (data->image, 0, 0, data->width, data->height);
+        gdk_threads_leave ();
+    }
+    return NULL;
+}
+
+gboolean
+on_delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+    return FALSE;
+}
+
+void
+on_destroy (GtkWidget *widget, gpointer data)
+{
+    ThreadData *td = (ThreadData *) data;
+    td->running = FALSE;
+    g_object_unref (td->camera);
+    gtk_main_quit ();
+}
+
+static void
+on_toolbutton_run_clicked (GtkWidget *widget, gpointer args)
+{
+    ThreadData *data = (ThreadData *) args;
+
+    if (data->running)
+        return;
+
+    GError *error = NULL;
+    data->running = TRUE;
+
+    uca_camera_start_recording (data->camera, &error);
+
+    if (error != NULL) {
+        g_printerr ("Failed to start recording: %s\n", error->message);
+        return;
+    }
+
+    if (!g_thread_create (grab_thread, data, FALSE, &error)) {
+        g_printerr ("Failed to create thread: %s\n", error->message);
+        return;
+    }
+}
+
+static void
+on_toolbutton_stop_clicked (GtkWidget *widget, gpointer args)
+{
+    ThreadData *data = (ThreadData *) args;
+    data->running = FALSE;
+    data->store = FALSE;
+    GError *error = NULL;
+    uca_camera_stop_recording (data->camera, &error);
+
+    if (error != NULL)
+        g_printerr ("Failed to stop: %s\n", error->message);
+}
+
+static void
+on_toolbutton_record_clicked (GtkWidget *widget, gpointer args)
+{
+    ThreadData *data = (ThreadData *) args;
+    data->timestamp = (int) time (0);
+    data->store = TRUE;
+    GError *error = NULL;
+
+    gtk_statusbar_push (data->statusbar, data->statusbar_context_id, "Recording...");
+
+    if (data->running != TRUE) {
+        data->running = TRUE;
+        uca_camera_start_recording (data->camera, &error);
+
+        if (!g_thread_create (grab_thread, data, FALSE, &error))
+            g_printerr ("Failed to create thread: %s\n", error->message);
+    }
+}
+
+static void
+create_main_window (GtkBuilder *builder, const gchar* camera_name)
+{
+    static ThreadData td;
+
+    GError *error = NULL;
+    UcaCamera *camera = uca_plugin_manager_new_camera (plugin_manager, camera_name, &error);
+
+    if ((camera == NULL) || (error != NULL)) {
+        g_error ("%s\n", error->message);
+        gtk_main_quit ();
+    }
+
+    guint bits_per_sample;
+    g_object_get (camera,
+            "roi-width", &td.width,
+            "roi-height", &td.height,
+            "sensor-bitdepth", &bits_per_sample,
+            NULL);
+
+    GtkWidget *window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
+    GtkWidget *image = GTK_WIDGET (gtk_builder_get_object (builder, "image"));
+    GtkWidget *property_tree_view = egg_property_tree_view_new (G_OBJECT (camera));
+    GtkContainer *scrolled_property_window = GTK_CONTAINER (gtk_builder_get_object (builder, "scrolledwindow2"));
+
+    gtk_container_add (scrolled_property_window, property_tree_view);
+    gtk_widget_show_all (GTK_WIDGET (scrolled_property_window));
+
+    GdkPixbuf *pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, td.width, td.height);
+    gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf);
+
+    td.pixel_size = bits_per_sample > 8 ? 2 : 1;
+    td.image  = image;
+    td.pixbuf = pixbuf;
+    td.buffer = (guchar *) g_malloc (td.pixel_size * td.width * td.height);
+    td.pixels = gdk_pixbuf_get_pixels (pixbuf);
+    td.running = FALSE;
+    td.statusbar = GTK_STATUSBAR (gtk_builder_get_object (builder, "statusbar"));
+    td.statusbar_context_id = gtk_statusbar_get_context_id (td.statusbar, "Recording Information");
+    td.store = FALSE;
+    td.camera = camera;
+    td.property_model = GTK_TREE_MODEL (gtk_builder_get_object (builder, "camera-properties"));
+
+    g_signal_connect (window, "destroy", G_CALLBACK (on_destroy), &td);
+    g_signal_connect (gtk_builder_get_object (builder, "toolbutton_run"),
+            "clicked", G_CALLBACK (on_toolbutton_run_clicked), &td);
+    g_signal_connect (gtk_builder_get_object (builder, "toolbutton_stop"),
+            "clicked", G_CALLBACK (on_toolbutton_stop_clicked), &td);
+    g_signal_connect (gtk_builder_get_object (builder, "toolbutton_record"),
+            "clicked", G_CALLBACK (on_toolbutton_record_clicked), &td);
+
+    gtk_widget_show (image);
+    gtk_widget_show (window);
+}
+
+static void
+on_button_proceed_clicked (GtkWidget *widget, gpointer data)
+{
+    GtkBuilder *builder = GTK_BUILDER (data);
+    GtkWidget *choice_window = GTK_WIDGET (gtk_builder_get_object (builder, "choice-window"));
+    GtkTreeView *treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, "treeview-cameras"));
+    GtkListStore *list_store = GTK_LIST_STORE (gtk_builder_get_object (builder, "camera-types"));
+
+    GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
+    GList *selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
+    GtkTreeIter iter;
+
+    gtk_widget_destroy (choice_window);
+    gboolean valid = gtk_tree_model_get_iter (GTK_TREE_MODEL (list_store), &iter, selected_rows->data);
+
+    if (valid) {
+        gchar *data;
+        gtk_tree_model_get (GTK_TREE_MODEL (list_store), &iter, 0, &data, -1);
+        create_main_window (builder, data);
+        g_free (data);
+    }
+
+    g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
+    g_list_free (selected_rows);
+}
+
+static void
+on_treeview_keypress (GtkWidget *widget, GdkEventKey *event, gpointer data)
+{
+    if (event->keyval == GDK_KEY_Return)
+        gtk_widget_grab_focus (GTK_WIDGET (data));
+}
+
+static void
+create_choice_window (GtkBuilder *builder)
+{
+    GList *camera_types = uca_plugin_manager_get_available_cameras (plugin_manager);
+
+    GtkWidget *choice_window = GTK_WIDGET (gtk_builder_get_object (builder, "choice-window"));
+    GtkTreeView *treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, "treeview-cameras"));
+    GtkListStore *list_store = GTK_LIST_STORE (gtk_builder_get_object (builder, "camera-types"));
+    GtkButton *proceed_button = GTK_BUTTON (gtk_builder_get_object (builder, "button-proceed"));
+    GtkTreeIter iter;
+
+    for (GList *it = g_list_first (camera_types); it != NULL; it = g_list_next (it)) {
+        gtk_list_store_append (list_store, &iter);
+        gtk_list_store_set (list_store, &iter, 0, g_strdup ((gchar *) it->data), -1);
+    }
+
+    gboolean valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter);
+
+    if (valid) {
+        GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
+        gtk_tree_selection_unselect_all (selection);
+        gtk_tree_selection_select_path (selection, gtk_tree_model_get_path (GTK_TREE_MODEL (list_store), &iter));
+    }
+
+    g_signal_connect (proceed_button, "clicked", G_CALLBACK (on_button_proceed_clicked), builder);
+    g_signal_connect (treeview, "key-press-event", G_CALLBACK (on_treeview_keypress), proceed_button);
+    gtk_widget_show_all (GTK_WIDGET (choice_window));
+
+    g_list_foreach (camera_types, (GFunc) g_free, NULL);
+    g_list_free (camera_types);
+}
+
+int
+main (int argc, char *argv[])
+{
+    GError *error = NULL;
+
+    g_thread_init (NULL);
+    gdk_threads_init ();
+    gtk_init (&argc, &argv);
+
+    GtkBuilder *builder = gtk_builder_new ();
+
+    if (!gtk_builder_add_from_file (builder, CONTROL_GLADE_PATH, &error)) {
+        g_print ("Error: %s\n", error->message);
+        return 1;
+    }
+
+    plugin_manager = uca_plugin_manager_new ();
+    create_choice_window (builder);
+    gtk_builder_connect_signals (builder, NULL);
+
+    gdk_threads_enter ();
+    gtk_main ();
+    gdk_threads_leave ();
+
+    g_object_unref (plugin_manager);
+    return 0;
+}
diff --git a/tools/gui/control.glade b/tools/gui/control.glade
new file mode 100644
index 0000000..d7ba2fc
--- /dev/null
+++ b/tools/gui/control.glade
@@ -0,0 +1,302 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="2.20"/>
+  <!-- interface-naming-policy project-wide -->
+  <object class="GtkListStore" id="camera-types">
+    <columns>
+      <!-- column-name name -->
+      <column type="gchararray"/>
+    </columns>
+  </object>
+  <object class="GtkListStore" id="camera-properties">
+    <columns>
+      <!-- column-name PropertyName -->
+      <column type="gchararray"/>
+      <!-- column-name PropertyValue -->
+      <column type="gchararray"/>
+      <!-- column-name writeable -->
+      <column type="gboolean"/>
+    </columns>
+  </object>
+  <object class="GtkWindow" id="window">
+    <property name="title" translatable="yes">Camera Control</property>
+    <property name="default_width">1024</property>
+    <property name="default_height">768</property>
+    <signal name="delete_event" handler="on_delete_event"/>
+    <child>
+      <object class="GtkVBox" id="vbox1">
+        <property name="visible">True</property>
+        <child>
+          <object class="GtkMenuBar" id="menubar1">
+            <property name="visible">True</property>
+            <child>
+              <object class="GtkMenuItem" id="menuitem1">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_File</property>
+                <property name="use_underline">True</property>
+                <child type="submenu">
+                  <object class="GtkMenu" id="menu_file">
+                    <property name="visible">True</property>
+                    <child>
+                      <object class="GtkImageMenuItem" id="imagemenuitem1">
+                        <property name="label">gtk-new</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkImageMenuItem" id="imagemenuitem2">
+                        <property name="label">gtk-open</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkImageMenuItem" id="imagemenuitem3">
+                        <property name="label">gtk-save</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkImageMenuItem" id="imagemenuitem4">
+                        <property name="label">gtk-save-as</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkSeparatorMenuItem" id="separatormenuitem1">
+                        <property name="visible">True</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkImageMenuItem" id="imagemenuitem_quit">
+                        <property name="label">gtk-quit</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                        <signal name="activate" handler="gtk_main_quit"/>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkMenuItem" id="menuitem4">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_Help</property>
+                <property name="use_underline">True</property>
+                <child type="submenu">
+                  <object class="GtkMenu" id="menu_help">
+                    <property name="visible">True</property>
+                    <child>
+                      <object class="GtkImageMenuItem" id="imagemenuitem_about">
+                        <property name="label">gtk-about</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkToolbar" id="toolbar">
+            <property name="visible">True</property>
+            <child>
+              <object class="GtkToolButton" id="toolbutton_run">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Run</property>
+                <property name="use_underline">True</property>
+                <property name="stock_id">gtk-media-play</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="homogeneous">True</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkToolButton" id="toolbutton_record">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Record</property>
+                <property name="use_underline">True</property>
+                <property name="stock_id">gtk-media-record</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="homogeneous">True</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkToolButton" id="toolbutton_stop">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Stop</property>
+                <property name="use_underline">True</property>
+                <property name="stock_id">gtk-media-stop</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="homogeneous">True</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkHPaned" id="hpaned1">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="border_width">6</property>
+            <child>
+              <object class="GtkScrolledWindow" id="scrolledwindow1">
+                <property name="width_request">300</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="hscrollbar_policy">automatic</property>
+                <property name="vscrollbar_policy">automatic</property>
+                <child>
+                  <object class="GtkViewport" id="viewport1">
+                    <property name="visible">True</property>
+                    <property name="resize_mode">queue</property>
+                    <child>
+                      <object class="GtkImage" id="image">
+                        <property name="visible">True</property>
+                        <property name="stock">gtk-missing-image</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="resize">True</property>
+                <property name="shrink">False</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkScrolledWindow" id="scrolledwindow2">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="hscrollbar_policy">automatic</property>
+                <property name="vscrollbar_policy">automatic</property>
+                <child>
+                  <placeholder/>
+                </child>
+              </object>
+              <packing>
+                <property name="resize">True</property>
+                <property name="shrink">True</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkStatusbar" id="statusbar">
+            <property name="visible">True</property>
+            <property name="spacing">2</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="position">3</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="GtkAdjustment" id="adjustment_scale">
+    <property name="value">65535</property>
+    <property name="lower">1</property>
+    <property name="upper">65535</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkWindow" id="choice-window">
+    <property name="border_width">6</property>
+    <child>
+      <object class="GtkVBox" id="vbox3">
+        <property name="visible">True</property>
+        <property name="spacing">2</property>
+        <child>
+          <object class="GtkTreeView" id="treeview-cameras">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="model">camera-types</property>
+            <child>
+              <object class="GtkTreeViewColumn" id="treeviewcolumn1">
+                <property name="title">Choose camera</property>
+                <child>
+                  <object class="GtkCellRendererText" id="cellrenderertext1"/>
+                  <attributes>
+                    <attribute name="text">0</attribute>
+                  </attributes>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkHButtonBox" id="hbuttonbox1">
+            <property name="visible">True</property>
+            <property name="spacing">6</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="button-cancel">
+                <property name="label">gtk-quit</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+                <signal name="clicked" handler="gtk_main_quit"/>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="button-proceed">
+                <property name="label">gtk-ok</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="padding">6</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/tools/gui/egg-property-cell-renderer.c b/tools/gui/egg-property-cell-renderer.c
new file mode 100644
index 0000000..9df5cc3
--- /dev/null
+++ b/tools/gui/egg-property-cell-renderer.c
@@ -0,0 +1,594 @@
+/* Copyright (C) 2011, 2012 Matthias Vogelgesang <matthias.vogelgesang@kit.edu>
+   (Karlsruhe Institute of Technology)
+
+   This library is free software; you can redistribute it and/or modify it
+   under the terms of the GNU Lesser General Public License as published by the
+   Free Software Foundation; either version 2.1 of the License, or (at your
+   option) any later version.
+
+   This library is distributed in the hope that it will be useful, but WITHOUT
+   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+   FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+   details.
+
+   You should have received a copy of the GNU Lesser General Public License along
+   with this library; if not, write to the Free Software Foundation, Inc., 51
+   Franklin St, Fifth Floor, Boston, MA 02110, USA */
+
+#include <stdlib.h>
+#include "egg-property-cell-renderer.h"
+
+G_DEFINE_TYPE (EggPropertyCellRenderer, egg_property_cell_renderer, GTK_TYPE_CELL_RENDERER)
+
+#define EGG_PROPERTY_CELL_RENDERER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), EGG_TYPE_PROPERTY_CELL_RENDERER, EggPropertyCellRendererPrivate))
+
+
+struct _EggPropertyCellRendererPrivate
+{
+    GObject         *object;
+    GtkListStore    *list_store;
+    GtkCellRenderer *renderer;
+    GtkCellRenderer *text_renderer;
+    GtkCellRenderer *spin_renderer;
+    GtkCellRenderer *toggle_renderer;
+    GtkCellRenderer *combo_renderer;
+    GHashTable      *combo_models;
+};
+
+enum
+{
+    PROP_0,
+    PROP_PROP_NAME,
+    N_PROPERTIES
+};
+
+enum
+{
+    COMBO_COLUMN_VALUE_NAME,
+    COMBO_COLUMN_VALUE,
+    N_COMBO_COLUMNS
+};
+
+static GParamSpec *egg_property_cell_renderer_properties[N_PROPERTIES] = { NULL, };
+
+GtkCellRenderer *
+egg_property_cell_renderer_new (GObject         *object,
+                                GtkListStore    *list_store)
+{
+    EggPropertyCellRenderer *renderer;
+
+    renderer = EGG_PROPERTY_CELL_RENDERER (g_object_new (EGG_TYPE_PROPERTY_CELL_RENDERER, NULL));
+    renderer->priv->object = object;
+    renderer->priv->list_store = list_store;
+    return GTK_CELL_RENDERER (renderer);
+}
+
+static GParamSpec *
+get_pspec_from_object (GObject *object, const gchar *prop_name)
+{
+    GObjectClass *oclass = G_OBJECT_GET_CLASS (object);
+    return g_object_class_find_property (oclass, prop_name);
+}
+
+static void
+get_string_double_repr (GObject *object, const gchar *prop_name, gchar **text, gdouble *number)
+{
+    GParamSpec *pspec;
+    GValue from = { 0 };
+    GValue to_string = { 0 };
+    GValue to_double = { 0 };
+
+    pspec = get_pspec_from_object (object, prop_name);
+    g_value_init (&from, pspec->value_type);
+    g_value_init (&to_string, G_TYPE_STRING);
+    g_value_init (&to_double, G_TYPE_DOUBLE);
+    g_object_get_property (object, prop_name, &from);
+
+    if (g_value_transform (&from, &to_string))
+        *text = g_strdup (g_value_get_string (&to_string));
+    else
+        g_warning ("Could not convert from %s gchar*\n", g_type_name (pspec->value_type));
+
+    if (g_value_transform (&from, &to_double))
+        *number = g_value_get_double (&to_double);
+    else
+        g_warning ("Could not convert from %s to gdouble\n", g_type_name (pspec->value_type));
+}
+
+static void
+clear_adjustment (GObject *object)
+{
+    GtkAdjustment *adjustment;
+
+    g_object_get (object,
+            "adjustment", &adjustment,
+            NULL);
+
+    if (adjustment)
+        g_object_unref (adjustment);
+
+    g_object_set (object,
+            "adjustment", NULL,
+            NULL);
+}
+
+static void
+egg_property_cell_renderer_set_renderer (EggPropertyCellRenderer    *renderer,
+                                         const gchar                *prop_name)
+{
+    EggPropertyCellRendererPrivate *priv;
+    GParamSpec *pspec;
+    gchar *text = NULL;
+    gdouble number;
+
+    priv = EGG_PROPERTY_CELL_RENDERER_GET_PRIVATE (renderer);
+    pspec = get_pspec_from_object (priv->object, prop_name);
+
+    /*
+     * Set this renderers mode, so that any actions can be forwarded to our
+     * child renderers.
+     */
+    switch (pspec->value_type) {
+        /* toggle renderers */
+        case G_TYPE_BOOLEAN:
+            priv->renderer = priv->toggle_renderer;
+            g_object_set (renderer, "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
+            break;
+
+        /* spin renderers */
+        case G_TYPE_FLOAT:
+        case G_TYPE_DOUBLE:
+            priv->renderer = priv->spin_renderer;
+            g_object_set (renderer, "mode", GTK_CELL_RENDERER_MODE_EDITABLE, NULL);
+            g_object_set (priv->renderer, "digits", 5, NULL);
+            break;
+
+        case G_TYPE_INT:
+        case G_TYPE_UINT:
+        case G_TYPE_LONG:
+        case G_TYPE_ULONG:
+        case G_TYPE_INT64:
+        case G_TYPE_UINT64:
+            priv->renderer = priv->spin_renderer;
+            g_object_set (renderer, "mode", GTK_CELL_RENDERER_MODE_EDITABLE, NULL);
+            g_object_set (priv->renderer, "digits", 0, NULL);
+            break;
+
+        /* text renderers */
+        case G_TYPE_POINTER:
+        case G_TYPE_STRING:
+            priv->renderer = priv->text_renderer;
+            g_object_set (renderer, "mode", GTK_CELL_RENDERER_MODE_EDITABLE, NULL);
+            break;
+
+        /* combo renderers */
+        default:
+            if (G_TYPE_IS_ENUM (pspec->value_type)) {
+                priv->renderer = priv->combo_renderer;
+                g_object_set (renderer, "mode", GTK_CELL_RENDERER_MODE_EDITABLE, NULL);
+            }
+            break;
+    }
+
+    /*
+     * Set the content from the objects property.
+     */
+    switch (pspec->value_type) {
+        case G_TYPE_BOOLEAN:
+            {
+                gboolean val;
+
+                g_object_get (priv->object, prop_name, &val, NULL);
+                g_object_set (priv->renderer,
+                        "active", val,
+                        "activatable", pspec->flags & G_PARAM_WRITABLE ? TRUE : FALSE,
+                        NULL);
+                break;
+            }
+
+        case G_TYPE_INT:
+        case G_TYPE_UINT:
+        case G_TYPE_LONG:
+        case G_TYPE_ULONG:
+        case G_TYPE_INT64:
+        case G_TYPE_UINT64:
+        case G_TYPE_FLOAT:
+        case G_TYPE_DOUBLE:
+            get_string_double_repr (priv->object, prop_name, &text, &number);
+            break;
+
+        case G_TYPE_STRING:
+            g_object_get (priv->object, prop_name, &text, NULL);
+            break;
+
+        case G_TYPE_POINTER:
+            {
+                gpointer val;
+
+                g_object_get (priv->object, prop_name, &val, NULL);
+                text = g_strdup_printf ("0x%x", GPOINTER_TO_INT (val));
+            }
+            break;
+
+        default:
+            if (G_TYPE_IS_ENUM (pspec->value_type)) {
+                GParamSpecEnum *pspec_enum;
+                GEnumClass *enum_class;
+                GtkTreeModel *combo_model;
+                GtkTreeIter iter;
+                gint value;
+
+                g_object_get (priv->object, prop_name, &value, NULL);
+
+                pspec_enum = G_PARAM_SPEC_ENUM (pspec);
+                enum_class = pspec_enum->enum_class;
+                combo_model = g_hash_table_lookup (priv->combo_models, prop_name);
+
+                if (combo_model == NULL) {
+                    combo_model = GTK_TREE_MODEL (gtk_list_store_new (N_COMBO_COLUMNS, G_TYPE_STRING, G_TYPE_INT));
+                    g_hash_table_insert (priv->combo_models, g_strdup (prop_name), combo_model);
+
+                    for (guint i = 0; i < enum_class->n_values; i++) {
+                        gtk_list_store_append (GTK_LIST_STORE (combo_model), &iter);
+                        gtk_list_store_set (GTK_LIST_STORE (combo_model), &iter,
+                                COMBO_COLUMN_VALUE_NAME, enum_class->values[i].value_name,
+                                COMBO_COLUMN_VALUE, enum_class->values[i].value,
+                                -1);
+                    }
+                }
+
+
+                for (guint i = 0; i < enum_class->n_values; i++) {
+                    if (enum_class->values[i].value == value)
+                        text = g_strdup (enum_class->values[i].value_name);
+                }
+
+                g_object_set (priv->renderer,
+                        "model", combo_model,
+                        "text-column", 0,
+                        NULL);
+            }
+            break;
+    }
+
+    if (pspec->flags & G_PARAM_WRITABLE) {
+        if (GTK_IS_CELL_RENDERER_TOGGLE (priv->renderer))
+            g_object_set (priv->renderer, "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
+        else
+            g_object_set (priv->renderer, "foreground", "#000000", NULL);
+
+        if (GTK_IS_CELL_RENDERER_TEXT (priv->renderer)) {
+            g_object_set (priv->renderer,
+                    "editable", TRUE,
+                    "mode", GTK_CELL_RENDERER_MODE_EDITABLE,
+                    NULL);
+        }
+
+        if (GTK_IS_CELL_RENDERER_SPIN (priv->renderer)) {
+            GtkObject *adjustment = NULL;
+
+#define gtk_typed_adjustment_new(type, pspec, val, step_inc, page_inc) \
+    gtk_adjustment_new (val, ((type *) pspec)->minimum, ((type *) pspec)->maximum, step_inc, page_inc, 0)
+
+            switch (pspec->value_type) {
+                case G_TYPE_INT:
+                    adjustment = gtk_typed_adjustment_new (GParamSpecInt, pspec, number, 1, 10);
+                    break;
+                case G_TYPE_UINT:
+                    adjustment = gtk_typed_adjustment_new (GParamSpecUInt, pspec, number, 1, 10);
+                    break;
+                case G_TYPE_LONG:
+                    adjustment = gtk_typed_adjustment_new (GParamSpecLong, pspec, number, 1, 10);
+                    break;
+                case G_TYPE_ULONG:
+                    adjustment = gtk_typed_adjustment_new (GParamSpecULong, pspec, number, 1, 10);
+                    break;
+                case G_TYPE_INT64:
+                    adjustment = gtk_typed_adjustment_new (GParamSpecInt64, pspec, number, 1, 10);
+                    break;
+                case G_TYPE_UINT64:
+                    adjustment = gtk_typed_adjustment_new (GParamSpecUInt64, pspec, number, 1, 10);
+                    break;
+                case G_TYPE_FLOAT:
+                    adjustment = gtk_typed_adjustment_new (GParamSpecFloat, pspec, number, 0.05, 10);
+                    break;
+                case G_TYPE_DOUBLE:
+                    adjustment = gtk_typed_adjustment_new (GParamSpecDouble, pspec, number, 0.05, 10);
+                    break;
+            }
+
+            clear_adjustment (G_OBJECT (priv->renderer));
+            g_object_set (priv->renderer, "adjustment", adjustment, NULL);
+        }
+    }
+    else {
+        g_object_set (priv->renderer, "mode", GTK_CELL_RENDERER_MODE_INERT, NULL);
+
+        if (!GTK_IS_CELL_RENDERER_TOGGLE (priv->renderer))
+            g_object_set (priv->renderer, "foreground", "#aaaaaa", NULL);
+    }
+
+    if (text != NULL) {
+        g_object_set (priv->renderer, "text", text, NULL);
+        g_free (text);
+    }
+}
+
+static gchar *
+get_prop_name_from_tree_model (GtkTreeModel *model, const gchar *path)
+{
+    GtkTreeIter iter;
+    gchar *prop_name = NULL;
+
+    /* TODO: don't assume column 0 to contain the prop name */
+    if (gtk_tree_model_get_iter_from_string (model, &iter, path))
+        gtk_tree_model_get (model, &iter, 0, &prop_name, -1);
+
+    return prop_name;
+}
+
+static void
+egg_property_cell_renderer_toggle_cb (GtkCellRendererToggle *renderer,
+                                      gchar                 *path,
+                                      gpointer               user_data)
+{
+    EggPropertyCellRendererPrivate *priv;
+    gchar *prop_name;
+
+    priv = (EggPropertyCellRendererPrivate *) user_data;
+    prop_name = get_prop_name_from_tree_model (GTK_TREE_MODEL (priv->list_store), path);
+
+    if (prop_name != NULL) {
+        gboolean activated;
+
+        g_object_get (priv->object, prop_name, &activated, NULL);
+        g_object_set (priv->object, prop_name, !activated, NULL);
+        g_free (prop_name);
+    }
+}
+
+static void
+egg_property_cell_renderer_text_edited_cb (GtkCellRendererText  *renderer,
+                                           gchar                *path,
+                                           gchar                *new_text,
+                                           gpointer              user_data)
+{
+    EggPropertyCellRendererPrivate *priv;
+    gchar *prop_name;
+
+    priv = (EggPropertyCellRendererPrivate *) user_data;
+    prop_name = get_prop_name_from_tree_model (GTK_TREE_MODEL (priv->list_store), path);
+
+    if (prop_name != NULL) {
+        g_object_set (priv->object, prop_name, new_text, NULL);
+        g_free (prop_name);
+    }
+}
+
+static void
+egg_property_cell_renderer_spin_edited_cb (GtkCellRendererText  *renderer,
+                                           gchar                *path,
+                                           gchar                *new_text,
+                                           gpointer              user_data)
+{
+    EggPropertyCellRendererPrivate *priv;
+    gchar *prop_name;
+
+    priv = (EggPropertyCellRendererPrivate *) user_data;
+    prop_name = get_prop_name_from_tree_model (GTK_TREE_MODEL (priv->list_store), path);
+
+    if (prop_name != NULL) {
+        GParamSpec *pspec;
+        GValue from = { 0 };
+        GValue to = { 0 };
+
+        pspec = get_pspec_from_object (priv->object, prop_name);
+
+        g_value_init (&from, G_TYPE_DOUBLE);
+        g_value_init (&to, pspec->value_type);
+        g_value_set_double (&from, strtod (new_text, NULL));
+
+        if (g_value_transform (&from, &to))
+            g_object_set_property (priv->object, prop_name, &to);
+        else
+            g_warning ("Could not transform %s to %s\n",
+                    g_value_get_string (&from), g_type_name (pspec->value_type));
+
+        g_free (prop_name);
+    }
+}
+
+static void
+egg_property_cell_renderer_changed_cb (GtkCellRendererCombo *combo,
+                                       gchar                *path,
+                                       GtkTreeIter          *new_iter,
+                                       gpointer              user_data)
+{
+    EggPropertyCellRendererPrivate *priv;
+    gchar *prop_name;
+
+    priv = (EggPropertyCellRendererPrivate *) user_data;
+    prop_name = get_prop_name_from_tree_model (GTK_TREE_MODEL (priv->list_store), path);
+
+    if (prop_name != NULL) {
+        GtkTreeModel *combo_model;
+        gchar *value_name;
+        gint value;
+
+        combo_model = g_hash_table_lookup (priv->combo_models, prop_name);
+
+        gtk_tree_model_get (combo_model, new_iter,
+                COMBO_COLUMN_VALUE_NAME, &value_name,
+                COMBO_COLUMN_VALUE, &value,
+                -1);
+
+        g_object_set (priv->object, prop_name, value, NULL);
+        g_free (value_name);
+        g_free (prop_name);
+    }
+}
+
+static void
+egg_property_cell_renderer_get_size (GtkCellRenderer    *cell,
+                                     GtkWidget          *widget,
+                                     GdkRectangle       *cell_area,
+                                     gint               *x_offset,
+                                     gint               *y_offset,
+                                     gint               *width,
+                                     gint               *height)
+{
+
+    EggPropertyCellRendererPrivate *priv = EGG_PROPERTY_CELL_RENDERER_GET_PRIVATE (cell);
+    gtk_cell_renderer_get_size (priv->renderer, widget, cell_area, x_offset, y_offset, width, height);
+}
+
+static void
+egg_property_cell_renderer_render (GtkCellRenderer      *cell,
+                                   GdkDrawable          *window,
+                                   GtkWidget            *widget,
+                                   GdkRectangle         *background_area,
+                                   GdkRectangle         *cell_area,
+                                   GdkRectangle         *expose_area,
+                                   GtkCellRendererState  flags)
+{
+    EggPropertyCellRendererPrivate *priv = EGG_PROPERTY_CELL_RENDERER_GET_PRIVATE (cell);
+    gtk_cell_renderer_render (priv->renderer, window, widget, background_area, cell_area, expose_area, flags);
+}
+
+static gboolean
+egg_property_cell_renderer_activate (GtkCellRenderer        *cell,
+                                     GdkEvent               *event,
+                                     GtkWidget              *widget,
+                                     const gchar            *path,
+                                     GdkRectangle           *background_area,
+                                     GdkRectangle           *cell_area,
+                                     GtkCellRendererState    flags)
+{
+    EggPropertyCellRendererPrivate *priv = EGG_PROPERTY_CELL_RENDERER_GET_PRIVATE (cell);
+    return gtk_cell_renderer_activate (priv->renderer, event, widget, path, background_area, cell_area, flags);
+}
+
+static GtkCellEditable *
+egg_property_cell_renderer_start_editing (GtkCellRenderer        *cell,
+                                          GdkEvent               *event,
+                                          GtkWidget              *widget,
+                                          const gchar            *path,
+                                          GdkRectangle           *background_area,
+                                          GdkRectangle           *cell_area,
+                                          GtkCellRendererState    flags)
+{
+    EggPropertyCellRendererPrivate *priv = EGG_PROPERTY_CELL_RENDERER_GET_PRIVATE (cell);
+    return gtk_cell_renderer_start_editing (priv->renderer, event, widget, path, background_area, cell_area, flags);
+}
+
+static void
+egg_property_cell_renderer_dispose (GObject *object)
+{
+    EggPropertyCellRenderer *renderer = EGG_PROPERTY_CELL_RENDERER (object);
+    g_hash_table_destroy (renderer->priv->combo_models);
+}
+
+static void
+egg_property_cell_renderer_set_property (GObject        *object,
+                                         guint           property_id,
+                                         const GValue   *value,
+                                         GParamSpec     *pspec)
+{
+    g_return_if_fail (EGG_IS_PROPERTY_CELL_RENDERER (object));
+    EggPropertyCellRenderer *renderer = EGG_PROPERTY_CELL_RENDERER (object);
+
+    switch (property_id) {
+        case PROP_PROP_NAME:
+            egg_property_cell_renderer_set_renderer (renderer, g_value_get_string (value));
+            break;
+
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+            return;
+    }
+}
+
+static void
+egg_property_cell_renderer_get_property (GObject    *object,
+                                         guint       property_id,
+                                         GValue     *value,
+                                         GParamSpec *pspec)
+{
+    g_return_if_fail (EGG_IS_PROPERTY_CELL_RENDERER (object));
+
+    switch (property_id) {
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+            return;
+    }
+}
+
+static void
+egg_property_cell_renderer_class_init (EggPropertyCellRendererClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+    GtkCellRendererClass *cellrenderer_class = GTK_CELL_RENDERER_CLASS (klass);
+
+    gobject_class->set_property = egg_property_cell_renderer_set_property;
+    gobject_class->get_property = egg_property_cell_renderer_get_property;
+    gobject_class->dispose = egg_property_cell_renderer_dispose;
+
+    cellrenderer_class->render = egg_property_cell_renderer_render;
+    cellrenderer_class->get_size = egg_property_cell_renderer_get_size;
+    cellrenderer_class->activate = egg_property_cell_renderer_activate;
+    cellrenderer_class->start_editing = egg_property_cell_renderer_start_editing;
+
+    egg_property_cell_renderer_properties[PROP_PROP_NAME] =
+            g_param_spec_string("prop-name",
+                                "Property name", "Property name", "",
+                                G_PARAM_READWRITE);
+
+    g_object_class_install_property(gobject_class, PROP_PROP_NAME, egg_property_cell_renderer_properties[PROP_PROP_NAME]);
+
+    g_type_class_add_private (klass, sizeof (EggPropertyCellRendererPrivate));
+}
+
+static void
+egg_property_cell_renderer_init (EggPropertyCellRenderer *renderer)
+{
+    EggPropertyCellRendererPrivate *priv;
+
+    renderer->priv = priv = EGG_PROPERTY_CELL_RENDERER_GET_PRIVATE (renderer);
+
+    priv->text_renderer = gtk_cell_renderer_text_new ();
+    priv->spin_renderer = gtk_cell_renderer_spin_new ();
+    priv->toggle_renderer = gtk_cell_renderer_toggle_new ();
+    priv->combo_renderer = gtk_cell_renderer_combo_new ();
+    priv->renderer = priv->text_renderer;
+    priv->combo_models = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+
+    g_object_set (priv->text_renderer,
+            "editable", TRUE,
+            NULL);
+
+    g_object_set (priv->spin_renderer,
+            "editable", TRUE,
+            NULL);
+
+    g_object_set (priv->toggle_renderer,
+            "xalign", 0.0f,
+            "activatable", TRUE,
+            "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE,
+            NULL);
+
+    g_object_set (priv->combo_renderer,
+            "has-entry", FALSE,
+            NULL);
+
+    g_signal_connect (priv->spin_renderer, "edited",
+            G_CALLBACK (egg_property_cell_renderer_spin_edited_cb), priv);
+
+    g_signal_connect (priv->text_renderer, "edited",
+            G_CALLBACK (egg_property_cell_renderer_text_edited_cb), NULL);
+
+    g_signal_connect (priv->toggle_renderer, "toggled",
+            G_CALLBACK (egg_property_cell_renderer_toggle_cb), priv);
+
+    g_signal_connect (priv->combo_renderer, "changed",
+            G_CALLBACK (egg_property_cell_renderer_changed_cb), priv);
+}
diff --git a/tools/gui/egg-property-cell-renderer.h b/tools/gui/egg-property-cell-renderer.h
new file mode 100644
index 0000000..d4dbe02
--- /dev/null
+++ b/tools/gui/egg-property-cell-renderer.h
@@ -0,0 +1,55 @@
+/* Copyright (C) 2011, 2012 Matthias Vogelgesang <matthias.vogelgesang@kit.edu>
+   (Karlsruhe Institute of Technology)
+
+   This library is free software; you can redistribute it and/or modify it
+   under the terms of the GNU Lesser General Public License as published by the
+   Free Software Foundation; either version 2.1 of the License, or (at your
+   option) any later version.
+
+   This library is distributed in the hope that it will be useful, but WITHOUT
+   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+   FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+   details.
+
+   You should have received a copy of the GNU Lesser General Public License along
+   with this library; if not, write to the Free Software Foundation, Inc., 51
+   Franklin St, Fifth Floor, Boston, MA 02110, USA */
+
+#ifndef EGG_PROPERTY_CELL_RENDERER_H
+#define EGG_PROPERTY_CELL_RENDERER_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define EGG_TYPE_PROPERTY_CELL_RENDERER             (egg_property_cell_renderer_get_type())
+#define EGG_PROPERTY_CELL_RENDERER(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), EGG_TYPE_PROPERTY_CELL_RENDERER, EggPropertyCellRenderer))
+#define EGG_IS_PROPERTY_CELL_RENDERER(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), EGG_TYPE_PROPERTY_CELL_RENDERER))
+#define EGG_PROPERTY_CELL_RENDERER_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), EGG_TYPE_PROPERTY_CELL_RENDERER, EggPropertyCellRendererClass))
+#define EGG_IS_PROPERTY_CELL_RENDERER_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), EGG_TYPE_PROPERTY_CELL_RENDERER))
+#define EGG_PROPERTY_CELL_RENDERER_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), EGG_TYPE_PROPERTY_CELL_RENDERER, EggPropertyCellRendererClass))
+
+typedef struct _EggPropertyCellRenderer           EggPropertyCellRenderer;
+typedef struct _EggPropertyCellRendererClass      EggPropertyCellRendererClass;
+typedef struct _EggPropertyCellRendererPrivate    EggPropertyCellRendererPrivate;
+
+struct _EggPropertyCellRenderer
+{
+    GtkCellRenderer parent_instance;
+
+    /*< private >*/
+    EggPropertyCellRendererPrivate *priv;
+};
+
+struct _EggPropertyCellRendererClass
+{
+    GtkCellRendererClass parent_class;
+};
+
+GType               egg_property_cell_renderer_get_type (void);
+GtkCellRenderer*    egg_property_cell_renderer_new      (GObject         *object,
+                                                         GtkListStore    *list_store);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/gui/egg-property-tree-view.c b/tools/gui/egg-property-tree-view.c
new file mode 100644
index 0000000..52d1e10
--- /dev/null
+++ b/tools/gui/egg-property-tree-view.c
@@ -0,0 +1,113 @@
+/* Copyright (C) 2011, 2012 Matthias Vogelgesang <matthias.vogelgesang@kit.edu>
+   (Karlsruhe Institute of Technology)
+
+   This library is free software; you can redistribute it and/or modify it
+   under the terms of the GNU Lesser General Public License as published by the
+   Free Software Foundation; either version 2.1 of the License, or (at your
+   option) any later version.
+
+   This library is distributed in the hope that it will be useful, but WITHOUT
+   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+   FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+   details.
+
+   You should have received a copy of the GNU Lesser General Public License along
+   with this library; if not, write to the Free Software Foundation, Inc., 51
+   Franklin St, Fifth Floor, Boston, MA 02110, USA */
+
+
+#include "egg-property-tree-view.h"
+#include "egg-property-cell-renderer.h"
+
+G_DEFINE_TYPE (EggPropertyTreeView, egg_property_tree_view, GTK_TYPE_TREE_VIEW)
+
+#define EGG_PROPERTY_TREE_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), EGG_TYPE_PROPERTY_TREE_VIEW, EggPropertyTreeViewPrivate))
+
+struct _EggPropertyTreeViewPrivate
+{
+    GtkListStore *list_store;
+};
+
+enum
+{
+    COLUMN_PROP_NAME,
+    COLUMN_PROP_ROW,
+    COLUMN_PROP_ADJUSTMENT,
+    N_COLUMNS
+};
+
+static void
+egg_property_tree_view_populate_model_with_properties (GtkListStore *model, GObject *object)
+{
+    GParamSpec **pspecs;
+    GObjectClass *oclass;
+    guint n_properties;
+    GtkTreeIter iter;
+
+    oclass = G_OBJECT_GET_CLASS (object);
+    pspecs = g_object_class_list_properties (oclass, &n_properties);
+
+    for (guint i = 0; i < n_properties; i++) {
+        if (pspecs[i]->flags & G_PARAM_READABLE) {
+            GtkObject *adjustment;
+
+            adjustment = gtk_adjustment_new (5, 0, 1000, 1, 10, 0);
+
+            gtk_list_store_append (model, &iter);
+            gtk_list_store_set (model, &iter,
+                    COLUMN_PROP_NAME, pspecs[i]->name,
+                    COLUMN_PROP_ROW, FALSE,
+                    COLUMN_PROP_ADJUSTMENT, adjustment,
+                    -1);
+        }
+    }
+
+    g_free (pspecs);
+}
+
+GtkWidget *
+egg_property_tree_view_new (GObject *object)
+{
+    EggPropertyTreeView *property_tree_view;
+    GtkTreeView *tree_view;
+    GtkTreeViewColumn *prop_column, *value_column;
+    GtkCellRenderer *prop_renderer, *value_renderer;
+    GtkListStore *list_store;
+
+    property_tree_view = EGG_PROPERTY_TREE_VIEW (g_object_new (EGG_TYPE_PROPERTY_TREE_VIEW, NULL));
+    list_store = property_tree_view->priv->list_store;
+    tree_view = GTK_TREE_VIEW (property_tree_view);
+
+    egg_property_tree_view_populate_model_with_properties (list_store, object);
+    gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (list_store));
+
+    prop_renderer = gtk_cell_renderer_text_new ();
+    prop_column = gtk_tree_view_column_new_with_attributes ("Property", prop_renderer,
+            "text", COLUMN_PROP_NAME,
+            NULL);
+
+    value_renderer = egg_property_cell_renderer_new (object, list_store);
+    value_column = gtk_tree_view_column_new_with_attributes ("Value", value_renderer,
+            "prop-name", COLUMN_PROP_NAME,
+            NULL);
+
+    gtk_tree_view_append_column (tree_view, prop_column);
+    gtk_tree_view_append_column (tree_view, value_column);
+
+    return GTK_WIDGET (tree_view);
+}
+
+static void
+egg_property_tree_view_class_init (EggPropertyTreeViewClass *klass)
+{
+    g_type_class_add_private (klass, sizeof (EggPropertyTreeViewPrivate));
+}
+
+static void
+egg_property_tree_view_init (EggPropertyTreeView *tree_view)
+{
+    EggPropertyTreeViewPrivate *priv = EGG_PROPERTY_TREE_VIEW_GET_PRIVATE (tree_view);
+
+    tree_view->priv = priv = EGG_PROPERTY_TREE_VIEW_GET_PRIVATE (tree_view);
+    priv->list_store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_BOOLEAN, GTK_TYPE_ADJUSTMENT);
+}
diff --git a/tools/gui/egg-property-tree-view.h b/tools/gui/egg-property-tree-view.h
new file mode 100644
index 0000000..e8fd0fe
--- /dev/null
+++ b/tools/gui/egg-property-tree-view.h
@@ -0,0 +1,54 @@
+/* Copyright (C) 2011, 2012 Matthias Vogelgesang <matthias.vogelgesang@kit.edu>
+   (Karlsruhe Institute of Technology)
+
+   This library is free software; you can redistribute it and/or modify it
+   under the terms of the GNU Lesser General Public License as published by the
+   Free Software Foundation; either version 2.1 of the License, or (at your
+   option) any later version.
+
+   This library is distributed in the hope that it will be useful, but WITHOUT
+   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+   FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+   details.
+
+   You should have received a copy of the GNU Lesser General Public License along
+   with this library; if not, write to the Free Software Foundation, Inc., 51
+   Franklin St, Fifth Floor, Boston, MA 02110, USA */
+
+#ifndef EGG_PROPERTY_TREE_VIEW_H
+#define EGG_PROPERTY_TREE_VIEW_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define EGG_TYPE_PROPERTY_TREE_VIEW             (egg_property_tree_view_get_type())
+#define EGG_PROPERTY_TREE_VIEW(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), EGG_TYPE_PROPERTY_TREE_VIEW, EggPropertyTreeView))
+#define EGG_IS_PROPERTY_TREE_VIEW(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), EGG_TYPE_PROPERTY_TREE_VIEW))
+#define EGG_PROPERTY_TREE_VIEW_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), EGG_TYPE_PROPERTY_TREE_VIEW, EggPropertyTreeViewClass))
+#define EGG_IS_PROPERTY_TREE_VIEW_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), EGG_TYPE_PROPERTY_TREE_VIEW))
+#define EGG_PROPERTY_TREE_VIEW_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), EGG_TYPE_PROPERTY_TREE_VIEW, EggPropertyTreeViewClass))
+
+typedef struct _EggPropertyTreeView           EggPropertyTreeView;
+typedef struct _EggPropertyTreeViewClass      EggPropertyTreeViewClass;
+typedef struct _EggPropertyTreeViewPrivate    EggPropertyTreeViewPrivate;
+
+struct _EggPropertyTreeView
+{
+    GtkTreeView parent_instance;
+
+    /*< private >*/
+    EggPropertyTreeViewPrivate *priv;
+};
+
+struct _EggPropertyTreeViewClass
+{
+    GtkTreeViewClass parent_class;
+};
+
+GType           egg_property_tree_view_get_type (void)              G_GNUC_CONST;
+GtkWidget*      egg_property_tree_view_new      (GObject *object);
+
+G_END_DECLS
+
+#endif
-- 
cgit v1.2.3