]> Pileus Git - ~andy/gtk/commitdiff
add editable_cells.c, list_store.c and tree_store.c
authorKristian Rietveld <kristian@planet.nl>
Tue, 2 Oct 2001 18:54:05 +0000 (18:54 +0000)
committerKristian Rietveld <kristian@src.gnome.org>
Tue, 2 Oct 2001 18:54:05 +0000 (18:54 +0000)
Tue Oct  2 20:18:32 2001  Kristian Rietveld  <kristian@planet.nl>

        * demos/gtk-demo/Makefile.am: add editable_cells.c,
        list_store.c and tree_store.c

        * demos/gtk-demo/appwindow.c: remove tearoff item from
        File menu, put menubar and toolbar in handle boxes.

        * demos/gtk-demo/genincude.pl: various changes to support trees

        * demos/gtk-demo/main.c: various changes to support trees

        * demos/gtk-demo/stock_browser.c: changed name of demo
        to Stock Item and Icon Browser, so geninclude.pl doesn't see
        it as child

        * demos/gtk-demo/editable_cells.c: new file/demo
        * demos/gtk-demo/list_store.c: new file/demo
        * demos/gtk-demo/tree_store.c: new file/demo

16 files changed:
ChangeLog
ChangeLog.pre-2-0
ChangeLog.pre-2-10
ChangeLog.pre-2-2
ChangeLog.pre-2-4
ChangeLog.pre-2-6
ChangeLog.pre-2-8
demos/gtk-demo/Makefile.am
demos/gtk-demo/appwindow.c
demos/gtk-demo/editable_cells.c [new file with mode: 0644]
demos/gtk-demo/geninclude.pl
demos/gtk-demo/geninclude.pl.in
demos/gtk-demo/list_store.c [new file with mode: 0644]
demos/gtk-demo/main.c
demos/gtk-demo/stock_browser.c
demos/gtk-demo/tree_store.c [new file with mode: 0644]

index 03ea65ba5e4199a60ef228e93ac44cef12349d3c..ba12c7757c1a971375a3c8cdd5451b8276a889b0 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,23 @@
+Tue Oct  2 20:18:32 2001  Kristian Rietveld  <kristian@planet.nl>
+
+       * demos/gtk-demo/Makefile.am: add editable_cells.c,
+       list_store.c and tree_store.c
+
+       * demos/gtk-demo/appwindow.c: remove tearoff item from
+       File menu, put menubar and toolbar in handle boxes.
+
+       * demos/gtk-demo/genincude.pl: various changes to support trees
+
+       * demos/gtk-demo/main.c: various changes to support trees
+
+       * demos/gtk-demo/stock_browser.c: changed name of demo
+       to Stock Item and Icon Browser, so geninclude.pl doesn't see
+       it as child
+
+       * demos/gtk-demo/editable_cells.c: new file/demo
+       * demos/gtk-demo/list_store.c: new file/demo
+       * demos/gtk-demo/tree_store.c: new file/demo
+
 Mon Oct  1 16:05:44 2001  Owen Taylor  <otaylor@redhat.com>
 
        * gtk/gtkrc.c gtk/gtksettings.c: 
index 03ea65ba5e4199a60ef228e93ac44cef12349d3c..ba12c7757c1a971375a3c8cdd5451b8276a889b0 100644 (file)
@@ -1,3 +1,23 @@
+Tue Oct  2 20:18:32 2001  Kristian Rietveld  <kristian@planet.nl>
+
+       * demos/gtk-demo/Makefile.am: add editable_cells.c,
+       list_store.c and tree_store.c
+
+       * demos/gtk-demo/appwindow.c: remove tearoff item from
+       File menu, put menubar and toolbar in handle boxes.
+
+       * demos/gtk-demo/genincude.pl: various changes to support trees
+
+       * demos/gtk-demo/main.c: various changes to support trees
+
+       * demos/gtk-demo/stock_browser.c: changed name of demo
+       to Stock Item and Icon Browser, so geninclude.pl doesn't see
+       it as child
+
+       * demos/gtk-demo/editable_cells.c: new file/demo
+       * demos/gtk-demo/list_store.c: new file/demo
+       * demos/gtk-demo/tree_store.c: new file/demo
+
 Mon Oct  1 16:05:44 2001  Owen Taylor  <otaylor@redhat.com>
 
        * gtk/gtkrc.c gtk/gtksettings.c: 
index 03ea65ba5e4199a60ef228e93ac44cef12349d3c..ba12c7757c1a971375a3c8cdd5451b8276a889b0 100644 (file)
@@ -1,3 +1,23 @@
+Tue Oct  2 20:18:32 2001  Kristian Rietveld  <kristian@planet.nl>
+
+       * demos/gtk-demo/Makefile.am: add editable_cells.c,
+       list_store.c and tree_store.c
+
+       * demos/gtk-demo/appwindow.c: remove tearoff item from
+       File menu, put menubar and toolbar in handle boxes.
+
+       * demos/gtk-demo/genincude.pl: various changes to support trees
+
+       * demos/gtk-demo/main.c: various changes to support trees
+
+       * demos/gtk-demo/stock_browser.c: changed name of demo
+       to Stock Item and Icon Browser, so geninclude.pl doesn't see
+       it as child
+
+       * demos/gtk-demo/editable_cells.c: new file/demo
+       * demos/gtk-demo/list_store.c: new file/demo
+       * demos/gtk-demo/tree_store.c: new file/demo
+
 Mon Oct  1 16:05:44 2001  Owen Taylor  <otaylor@redhat.com>
 
        * gtk/gtkrc.c gtk/gtksettings.c: 
index 03ea65ba5e4199a60ef228e93ac44cef12349d3c..ba12c7757c1a971375a3c8cdd5451b8276a889b0 100644 (file)
@@ -1,3 +1,23 @@
+Tue Oct  2 20:18:32 2001  Kristian Rietveld  <kristian@planet.nl>
+
+       * demos/gtk-demo/Makefile.am: add editable_cells.c,
+       list_store.c and tree_store.c
+
+       * demos/gtk-demo/appwindow.c: remove tearoff item from
+       File menu, put menubar and toolbar in handle boxes.
+
+       * demos/gtk-demo/genincude.pl: various changes to support trees
+
+       * demos/gtk-demo/main.c: various changes to support trees
+
+       * demos/gtk-demo/stock_browser.c: changed name of demo
+       to Stock Item and Icon Browser, so geninclude.pl doesn't see
+       it as child
+
+       * demos/gtk-demo/editable_cells.c: new file/demo
+       * demos/gtk-demo/list_store.c: new file/demo
+       * demos/gtk-demo/tree_store.c: new file/demo
+
 Mon Oct  1 16:05:44 2001  Owen Taylor  <otaylor@redhat.com>
 
        * gtk/gtkrc.c gtk/gtksettings.c: 
index 03ea65ba5e4199a60ef228e93ac44cef12349d3c..ba12c7757c1a971375a3c8cdd5451b8276a889b0 100644 (file)
@@ -1,3 +1,23 @@
+Tue Oct  2 20:18:32 2001  Kristian Rietveld  <kristian@planet.nl>
+
+       * demos/gtk-demo/Makefile.am: add editable_cells.c,
+       list_store.c and tree_store.c
+
+       * demos/gtk-demo/appwindow.c: remove tearoff item from
+       File menu, put menubar and toolbar in handle boxes.
+
+       * demos/gtk-demo/genincude.pl: various changes to support trees
+
+       * demos/gtk-demo/main.c: various changes to support trees
+
+       * demos/gtk-demo/stock_browser.c: changed name of demo
+       to Stock Item and Icon Browser, so geninclude.pl doesn't see
+       it as child
+
+       * demos/gtk-demo/editable_cells.c: new file/demo
+       * demos/gtk-demo/list_store.c: new file/demo
+       * demos/gtk-demo/tree_store.c: new file/demo
+
 Mon Oct  1 16:05:44 2001  Owen Taylor  <otaylor@redhat.com>
 
        * gtk/gtkrc.c gtk/gtksettings.c: 
index 03ea65ba5e4199a60ef228e93ac44cef12349d3c..ba12c7757c1a971375a3c8cdd5451b8276a889b0 100644 (file)
@@ -1,3 +1,23 @@
+Tue Oct  2 20:18:32 2001  Kristian Rietveld  <kristian@planet.nl>
+
+       * demos/gtk-demo/Makefile.am: add editable_cells.c,
+       list_store.c and tree_store.c
+
+       * demos/gtk-demo/appwindow.c: remove tearoff item from
+       File menu, put menubar and toolbar in handle boxes.
+
+       * demos/gtk-demo/genincude.pl: various changes to support trees
+
+       * demos/gtk-demo/main.c: various changes to support trees
+
+       * demos/gtk-demo/stock_browser.c: changed name of demo
+       to Stock Item and Icon Browser, so geninclude.pl doesn't see
+       it as child
+
+       * demos/gtk-demo/editable_cells.c: new file/demo
+       * demos/gtk-demo/list_store.c: new file/demo
+       * demos/gtk-demo/tree_store.c: new file/demo
+
 Mon Oct  1 16:05:44 2001  Owen Taylor  <otaylor@redhat.com>
 
        * gtk/gtkrc.c gtk/gtksettings.c: 
index 03ea65ba5e4199a60ef228e93ac44cef12349d3c..ba12c7757c1a971375a3c8cdd5451b8276a889b0 100644 (file)
@@ -1,3 +1,23 @@
+Tue Oct  2 20:18:32 2001  Kristian Rietveld  <kristian@planet.nl>
+
+       * demos/gtk-demo/Makefile.am: add editable_cells.c,
+       list_store.c and tree_store.c
+
+       * demos/gtk-demo/appwindow.c: remove tearoff item from
+       File menu, put menubar and toolbar in handle boxes.
+
+       * demos/gtk-demo/genincude.pl: various changes to support trees
+
+       * demos/gtk-demo/main.c: various changes to support trees
+
+       * demos/gtk-demo/stock_browser.c: changed name of demo
+       to Stock Item and Icon Browser, so geninclude.pl doesn't see
+       it as child
+
+       * demos/gtk-demo/editable_cells.c: new file/demo
+       * demos/gtk-demo/list_store.c: new file/demo
+       * demos/gtk-demo/tree_store.c: new file/demo
+
 Mon Oct  1 16:05:44 2001  Owen Taylor  <otaylor@redhat.com>
 
        * gtk/gtkrc.c gtk/gtksettings.c: 
index b1dc0205980484e6d0559373b13ab0a857dcccba..cd92264c287a9bebd646c483580101c3ac742a4a 100644 (file)
@@ -10,14 +10,17 @@ demos = @STRIP_BEGIN@       \
        colorsel.c      \
        dialog.c        \
        drawingarea.c   \
+       editable_cells.c \
        images.c        \
        item_factory.c  \
+       list_store.c    \
        menus.c         \
        panes.c         \
        pixbufs.c       \
        sizegroup.c     \
        stock_browser.c \
        textview.c      \
+       tree_store.c    \
 @STRIP_END@
 
 INCLUDES = @STRIP_BEGIN@ \
index cb88dc7fe878b7c922a6fd63d9de00788e41fcc7..b1befcbe69f4760848fab23391d1f6e644746e54 100644 (file)
@@ -35,7 +35,6 @@ menuitem_cb (gpointer             callback_data,
 static GtkItemFactoryEntry menu_items[] =
 {
   { "/_File",            NULL,         0,                     0, "<Branch>" },
-  { "/File/tearoff1",    NULL,         menuitem_cb,       0, "<Tearoff>" },
   { "/File/_New",        "<control>N", menuitem_cb,       0, "<StockItem>", GTK_STOCK_NEW },
   { "/File/_Open",       "<control>O", menuitem_cb,       0, "<StockItem>", GTK_STOCK_OPEN },
   { "/File/_Save",       "<control>S", menuitem_cb,       0, "<StockItem>", GTK_STOCK_SAVE },
@@ -182,6 +181,8 @@ do_appwindow (void)
   if (!window)
     {
       GtkWidget *table;
+      GtkWidget *menubar_handle;
+      GtkWidget *toolbar_handle;
       GtkWidget *toolbar;
       GtkWidget *statusbar;
       GtkWidget *contents;
@@ -214,6 +215,8 @@ do_appwindow (void)
       gtk_accel_group_attach (accel_group, G_OBJECT (window));
       gtk_accel_group_unref (accel_group);
       
+      menubar_handle = gtk_handle_box_new ();
+
       item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>", accel_group);
 
       /* Set up item factory to go away with the window */
@@ -228,8 +231,11 @@ do_appwindow (void)
       gtk_item_factory_create_items (item_factory, G_N_ELEMENTS (menu_items),
                                      menu_items, window);
 
+      gtk_container_add (GTK_CONTAINER (menubar_handle),
+                        gtk_item_factory_get_widget (item_factory, "<main>"));
+      
       gtk_table_attach (GTK_TABLE (table),
-                        gtk_item_factory_get_widget (item_factory, "<main>"),
+                       menubar_handle,
                         /* X direction */          /* Y direction */
                         0, 1,                      0, 1,
                         GTK_EXPAND | GTK_FILL,     0,
@@ -237,6 +243,8 @@ do_appwindow (void)
 
       /* Create the toolbar
        */
+      toolbar_handle = gtk_handle_box_new ();
+
       toolbar = gtk_toolbar_new ();
 
       gtk_toolbar_insert_stock (GTK_TOOLBAR (toolbar),
@@ -265,8 +273,10 @@ do_appwindow (void)
                                 window, /* user data for callback */
                                 -1);  /* -1 means "append" */
 
+      gtk_container_add (GTK_CONTAINER (toolbar_handle), toolbar);
+
       gtk_table_attach (GTK_TABLE (table),
-                        toolbar,
+                        toolbar_handle,
                         /* X direction */       /* Y direction */
                         0, 1,                   1, 2,
                         GTK_EXPAND | GTK_FILL,  0,
diff --git a/demos/gtk-demo/editable_cells.c b/demos/gtk-demo/editable_cells.c
new file mode 100644 (file)
index 0000000..7157f60
--- /dev/null
@@ -0,0 +1,298 @@
+/* Tree View/Editable Cells
+ * 
+ * This demo demostrates the use of editable cells in a GtkTreeView. If
+ * you're new to the GtkTreeView widgets and associates, look into
+ * the GtkListStore example first.
+ *
+ */
+
+#include <gtk/gtk.h>
+#include <string.h>
+#include <stdlib.h>
+
+static GtkWidget *window = NULL;
+
+typedef struct
+{
+  gint     number;
+  gchar   *product;
+  gboolean editable;
+}
+Item;
+
+enum
+{
+  COLUMN_NUMBER,
+  COLUMN_PRODUCT,
+  COLUMN_EDITABLE,
+  NUM_COLUMNS
+};
+
+static GArray *articles = NULL;
+
+static void
+add_items (void)
+{
+  Item foo;
+
+  g_return_if_fail (articles != NULL);
+
+  foo.number = 3;
+  foo.product = g_strdup ("bottles of coke");
+  foo.editable = TRUE;
+  g_array_append_vals (articles, &foo, 1);
+
+  foo.number = 5;
+  foo.product = g_strdup ("packages of noodles");
+  foo.editable = TRUE;
+  g_array_append_vals (articles, &foo, 1);
+
+  foo.number = 2;
+  foo.product = g_strdup ("packages of chocolate chip cookies");
+  foo.editable = TRUE;
+  g_array_append_vals (articles, &foo, 1);
+
+  foo.number = 1;
+  foo.product = g_strdup ("can vanilla ice cream");
+  foo.editable = TRUE;
+  g_array_append_vals (articles, &foo, 1);
+
+  foo.number = 6;
+  foo.product = g_strdup ("eggs");
+  foo.editable = TRUE;
+  g_array_append_vals (articles, &foo, 1);
+}
+
+static GtkTreeModel *
+create_model (void)
+{
+  gint i = 0;
+  GtkListStore *model;
+  GtkTreeIter iter;
+
+  /* create array */
+  articles = g_array_sized_new (FALSE, FALSE, sizeof (Item), 1);
+  
+  add_items ();
+
+  /* create list store */
+  model = gtk_list_store_new (NUM_COLUMNS, G_TYPE_INT, G_TYPE_STRING,
+                             G_TYPE_BOOLEAN);
+
+  /* add items */
+  for (i = 0; i < articles->len; i++)
+    {
+      gtk_list_store_append (model, &iter);
+
+      gtk_list_store_set (model, &iter,
+                         COLUMN_NUMBER,
+                         g_array_index (articles, Item, i).number,
+                         COLUMN_PRODUCT,
+                         g_array_index (articles, Item, i).product,
+                         COLUMN_EDITABLE,
+                         g_array_index (articles, Item, i).editable,
+                         -1);
+    }
+
+  return GTK_TREE_MODEL (model);
+}
+
+static void
+add_item (GtkWidget *button, gpointer data)
+{
+  Item foo;
+  GtkTreeIter iter;
+  GtkTreeModel *model = (GtkTreeModel *)data;
+
+  g_return_if_fail (articles != NULL);
+
+  foo.number = 0;
+  foo.product = g_strdup ("Description here");
+  foo.editable = TRUE;
+  g_array_append_vals (articles, &foo, 1);
+
+  gtk_list_store_append (GTK_LIST_STORE (model), &iter);
+  gtk_list_store_set (GTK_LIST_STORE (model), &iter,
+                     COLUMN_NUMBER, foo.number,
+                     COLUMN_PRODUCT, foo.product,
+                     COLUMN_EDITABLE, foo.editable,
+                     -1);
+}
+
+static void
+remove_item (GtkWidget *widget, gpointer data)
+{
+  GtkTreeIter iter;
+  GtkTreeView *treeview = (GtkTreeView *)data;
+  GtkTreeModel *model = gtk_tree_view_get_model (treeview);
+  GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
+
+  if (gtk_tree_selection_get_selected (selection, NULL, &iter))
+    {
+      gint i;
+      GtkTreePath *path;
+      
+      path = gtk_tree_model_get_path (model, &iter);
+      i = gtk_tree_path_get_indices (path)[0];
+      gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
+      
+      g_array_remove_index (articles, i);
+
+      gtk_tree_path_free (path);
+    }
+}
+
+static void
+cell_edited (GtkCellRendererText *cell,
+            gchar               *path_string,
+            gchar               *new_text,
+            gpointer             data)
+{
+  GtkTreeModel *model = (GtkTreeModel *)data;
+  GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
+  GtkTreeIter iter;
+  
+  gchar old_text[256]; /* ugly? */
+  gint *column;
+  
+  column = g_object_get_data (G_OBJECT (cell), "column");
+
+  gtk_tree_model_get_iter (model, &iter, path);
+  gtk_tree_model_get (model, &iter, column, old_text, -1);
+
+  switch ((gint) column)
+    {
+    case COLUMN_NUMBER:
+      {
+       gint i;
+       
+       i = gtk_tree_path_get_indices (path)[0];
+       g_array_index (articles, Item, i).number = atoi (new_text);
+       
+       gtk_list_store_set (GTK_LIST_STORE (model), &iter, column,
+                           atoi (new_text), -1);
+      }
+      break;
+      
+    case COLUMN_PRODUCT:
+      {
+       gint i;
+
+       i = gtk_tree_path_get_indices (path)[0];
+       g_free (g_array_index (articles, Item, i).product);
+       g_array_index (articles, Item, i).product = g_strdup (new_text);
+
+       gtk_list_store_set (GTK_LIST_STORE (model), &iter, column,
+                           new_text, -1);
+      }
+      break;
+    }
+
+  gtk_tree_path_free (path);
+}
+
+static void
+add_columns (GtkTreeView *treeview)
+{
+  GtkCellRenderer *renderer;
+  GtkTreeModel *model = gtk_tree_view_get_model (treeview);
+
+  /* number column */
+  renderer = gtk_cell_renderer_text_new ();
+  g_signal_connect (G_OBJECT (renderer), "edited",
+                   G_CALLBACK (cell_edited), model);
+  g_object_set_data (G_OBJECT (renderer), "column", (gint *)COLUMN_NUMBER);
+
+  gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
+                                              -1, "Number", renderer,
+                                              "text", COLUMN_NUMBER,
+                                              "editable", COLUMN_EDITABLE,
+                                              NULL);
+
+  /* product column */
+  renderer = gtk_cell_renderer_text_new ();
+  g_signal_connect (G_OBJECT (renderer), "edited",
+                   G_CALLBACK (cell_edited), model);
+  g_object_set_data (G_OBJECT (renderer), "column", (gint *)COLUMN_PRODUCT);
+  
+  gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
+                                              -1, "Product", renderer,
+                                              "text", COLUMN_PRODUCT,
+                                              "editable", COLUMN_EDITABLE,
+                                              NULL);
+}
+
+GtkWidget *
+do_editable_cells (void)
+{
+  if (!window)
+    {
+      GtkWidget *vbox;
+      GtkWidget *hbox;
+      GtkWidget *sw;
+      GtkWidget *treeview;
+      GtkWidget *button;
+      GtkTreeModel *model;
+      
+      /* create window, etc */
+      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+      gtk_window_set_title (GTK_WINDOW (window), "Shopping list");
+      gtk_container_set_border_width (GTK_CONTAINER (window), 5);
+      g_signal_connect (G_OBJECT (window), "destroy",
+                       G_CALLBACK (gtk_widget_destroyed), &window);
+
+      vbox = gtk_vbox_new (FALSE, 5);
+      gtk_container_add (GTK_CONTAINER (window), vbox);
+
+      gtk_box_pack_start (GTK_BOX (vbox),
+                         gtk_label_new ("Shopping list (you can edit the cells!)"),
+                         FALSE, FALSE, 0);
+
+      sw = gtk_scrolled_window_new (NULL, NULL);
+      gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
+                                          GTK_SHADOW_ETCHED_IN);
+      gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+                                     GTK_POLICY_AUTOMATIC,
+                                     GTK_POLICY_AUTOMATIC);
+      gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
+
+      /* create model */
+      model = create_model ();
+
+      /* create tree view */
+      treeview = gtk_tree_view_new_with_model (model);
+      gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (treeview), TRUE);
+      gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)),
+                                  GTK_SELECTION_SINGLE);
+      
+      add_columns (GTK_TREE_VIEW (treeview));
+      
+      gtk_container_add (GTK_CONTAINER (sw), treeview);
+      
+      /* some buttons */
+      hbox = gtk_hbox_new (TRUE, 4);
+      gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+
+      button = gtk_button_new_with_label ("Add item");
+      g_signal_connect (G_OBJECT (button), "clicked",
+                       G_CALLBACK (add_item), model);
+      gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+
+      button = gtk_button_new_with_label ("Remove item");
+      g_signal_connect (G_OBJECT (button), "clicked",
+                       G_CALLBACK (remove_item), treeview);
+      gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+
+      gtk_window_set_default_size (GTK_WINDOW (window), 320, 200);
+    }
+  
+  if (!GTK_WIDGET_VISIBLE (window))
+    gtk_widget_show_all (window);
+  else
+    {
+      gtk_widget_destroy (window);
+      window = NULL;
+    }
+
+  return window;
+}
index df534bc46d5a9a6534b34a15187580c7de0502df..f5fcd3ff48d2ab87a6d57e55626ad33075d56406 100755 (executable)
@@ -10,22 +10,16 @@ struct _Demo
   gchar *title;
   gchar *filename;
   GDoDemoFunc func;
+  Demo *children;
 };
 
 EOT
 
-$array = "";
-$first = 1;
 for $file (@ARGV) {
+    my %demo;
     
     ($basename = $file) =~ s/\.c$//;
 
-    if ($first) {
-       $first = 0;
-    } else {
-       $array .= ",\n";
-    }
-
     open INFO_FILE, $file or die "Cannot open '$file'\n";
     $title = <INFO_FILE>;
     $title =~ s@^\s*/\*\s*@@;
@@ -34,9 +28,136 @@ for $file (@ARGV) {
     close INFO_FILE;
 
     print "GtkWidget *do_$basename (void);\n";
-    $array .= qq(  { "$title", "$file", do_$basename });
+
+    push @demos, {"title" => $title, "file" => $file,
+                 "func"  => "do_$basename"};
+}
+
+# generate a list of 'parent names'
+foreach $href (@demos) {
+    if ($href->{"title"} =~ m|^([\w\s]+)/[\w\s]+$|) {
+       my $parent_name = $1;
+       my $do_next = 0;
+
+       # parent detected
+       if (defined @parents) {
+           foreach $foo (@parents) {
+               if ($foo eq $parent_name) {
+                   $do_next = 1;
+               }
+           }
+           
+           if ($do_next) {
+               next;
+           }
+       }
+
+       push @parents, $parent_name;
+
+       $tmp = (defined @child_arrays)?($#child_arrays + 1):0;
+       push @child_arrays, "child$tmp";
+
+       push @demos, {"title" => $parent_name, "file" => "NULL",
+                     "func" => "NULL"};
+    }
+}
+
+if (defined @parents) {
+    $i = 0;
+    for ($i = 0; $i <= $#parents; $i++) {
+       $first = 1;
+       
+       print "\nDemo ", $child_arrays[$i], "[] = {\n";
+       
+       $j = 0;
+       for ($j = 0; $j <= $#demos; $j++) {
+           $href = $demos[$j];
+           
+           if (!defined $demos[$j]) {
+               next;
+           }
+           
+           if ($demos[$j]{"title"} =~ m|^$parents[$i]/([\w\s]+)$|) {
+               if ($first) {
+                   $first = 0;
+               } else {
+                   print ",\n";
+               }
+               
+               print qq (  { "$1", "$demos[$j]{file}", $demos[$j]{func}, NULL });
+
+               # hack ... ugly
+               $demos[$j]{"title"} = "foo";
+           }
+       }
+
+       print ",\n";
+       print qq (  { NULL } );
+       print "\n};\n";
+    }   
+}
+
+# sort @demos
+@demos_old = @demos;
+
+@demos = sort {
+    $a->{"title"} cmp $b->{"title"};
+} @demos_old;
+
+# sort the child arrays
+if (defined @child_arrays) {
+    for ($i = 0; $i <= $#child_arrays; $i++) {
+       @foo_old = @{$child_arrays[$i]};
+
+       @{$child_arrays[$i]} = sort {
+           $a->{"title"} cmp $b->{"title"};
+       } @foo_old;
+    }
 }
 
-print "\nDemo testgtk_demos[] = {";
-print $array;
+# toplevel
+print "\nDemo testgtk_demos[] = {\n";
+
+$first = 1;
+foreach $href (@demos) {
+    $handled = 0;
+
+    # ugly evil hack
+    if ($href->{title} eq "foo") {
+       next;
+    }
+
+    if ($first) {
+       $first = 0;
+    } else {
+       print ", \n";
+    }
+
+    if (defined @parents) {
+       for ($i = 0; $i <= $#parents; $i++) {
+           if ($parents[$i] eq $href->{title}) {
+
+               if ($href->{file} eq 'NULL') {
+                   print qq (  { "$href->{title}", NULL, $href->{func}, $child_arrays[$i] });
+               } else {
+                   print qq (  { "$href->{title}", "$href->{file}", $href->{func}, $child_arrays[$i] });
+               }
+               
+               $handled = 1;
+               last;
+           }
+       }
+    }
+    
+    if ($handled) {
+       next;
+    }
+    
+    print qq (  { "$href->{title}", "$href->{file}", $href->{func}, NULL });
+}
+
+print ",\n";
+print qq (  { NULL } );
 print "\n};\n";
+
+exit 0;
index df534bc46d5a9a6534b34a15187580c7de0502df..f5fcd3ff48d2ab87a6d57e55626ad33075d56406 100755 (executable)
@@ -10,22 +10,16 @@ struct _Demo
   gchar *title;
   gchar *filename;
   GDoDemoFunc func;
+  Demo *children;
 };
 
 EOT
 
-$array = "";
-$first = 1;
 for $file (@ARGV) {
+    my %demo;
     
     ($basename = $file) =~ s/\.c$//;
 
-    if ($first) {
-       $first = 0;
-    } else {
-       $array .= ",\n";
-    }
-
     open INFO_FILE, $file or die "Cannot open '$file'\n";
     $title = <INFO_FILE>;
     $title =~ s@^\s*/\*\s*@@;
@@ -34,9 +28,136 @@ for $file (@ARGV) {
     close INFO_FILE;
 
     print "GtkWidget *do_$basename (void);\n";
-    $array .= qq(  { "$title", "$file", do_$basename });
+
+    push @demos, {"title" => $title, "file" => $file,
+                 "func"  => "do_$basename"};
+}
+
+# generate a list of 'parent names'
+foreach $href (@demos) {
+    if ($href->{"title"} =~ m|^([\w\s]+)/[\w\s]+$|) {
+       my $parent_name = $1;
+       my $do_next = 0;
+
+       # parent detected
+       if (defined @parents) {
+           foreach $foo (@parents) {
+               if ($foo eq $parent_name) {
+                   $do_next = 1;
+               }
+           }
+           
+           if ($do_next) {
+               next;
+           }
+       }
+
+       push @parents, $parent_name;
+
+       $tmp = (defined @child_arrays)?($#child_arrays + 1):0;
+       push @child_arrays, "child$tmp";
+
+       push @demos, {"title" => $parent_name, "file" => "NULL",
+                     "func" => "NULL"};
+    }
+}
+
+if (defined @parents) {
+    $i = 0;
+    for ($i = 0; $i <= $#parents; $i++) {
+       $first = 1;
+       
+       print "\nDemo ", $child_arrays[$i], "[] = {\n";
+       
+       $j = 0;
+       for ($j = 0; $j <= $#demos; $j++) {
+           $href = $demos[$j];
+           
+           if (!defined $demos[$j]) {
+               next;
+           }
+           
+           if ($demos[$j]{"title"} =~ m|^$parents[$i]/([\w\s]+)$|) {
+               if ($first) {
+                   $first = 0;
+               } else {
+                   print ",\n";
+               }
+               
+               print qq (  { "$1", "$demos[$j]{file}", $demos[$j]{func}, NULL });
+
+               # hack ... ugly
+               $demos[$j]{"title"} = "foo";
+           }
+       }
+
+       print ",\n";
+       print qq (  { NULL } );
+       print "\n};\n";
+    }   
+}
+
+# sort @demos
+@demos_old = @demos;
+
+@demos = sort {
+    $a->{"title"} cmp $b->{"title"};
+} @demos_old;
+
+# sort the child arrays
+if (defined @child_arrays) {
+    for ($i = 0; $i <= $#child_arrays; $i++) {
+       @foo_old = @{$child_arrays[$i]};
+
+       @{$child_arrays[$i]} = sort {
+           $a->{"title"} cmp $b->{"title"};
+       } @foo_old;
+    }
 }
 
-print "\nDemo testgtk_demos[] = {";
-print $array;
+# toplevel
+print "\nDemo testgtk_demos[] = {\n";
+
+$first = 1;
+foreach $href (@demos) {
+    $handled = 0;
+
+    # ugly evil hack
+    if ($href->{title} eq "foo") {
+       next;
+    }
+
+    if ($first) {
+       $first = 0;
+    } else {
+       print ", \n";
+    }
+
+    if (defined @parents) {
+       for ($i = 0; $i <= $#parents; $i++) {
+           if ($parents[$i] eq $href->{title}) {
+
+               if ($href->{file} eq 'NULL') {
+                   print qq (  { "$href->{title}", NULL, $href->{func}, $child_arrays[$i] });
+               } else {
+                   print qq (  { "$href->{title}", "$href->{file}", $href->{func}, $child_arrays[$i] });
+               }
+               
+               $handled = 1;
+               last;
+           }
+       }
+    }
+    
+    if ($handled) {
+       next;
+    }
+    
+    print qq (  { "$href->{title}", "$href->{file}", $href->{func}, NULL });
+}
+
+print ",\n";
+print qq (  { NULL } );
 print "\n};\n";
+
+exit 0;
diff --git a/demos/gtk-demo/list_store.c b/demos/gtk-demo/list_store.c
new file mode 100644 (file)
index 0000000..5902895
--- /dev/null
@@ -0,0 +1,220 @@
+/* Tree View/List Store
+ *
+ * The GtkListStore is used to store data in list form, to be used 
+ * later on by a GtkTreeView to display it. This demo builds a
+ * simple GtkListStore and displays it. See the Stock Browser
+ * demo for a more advanced example.
+ *
+ */
+
+#include <gtk/gtk.h>
+
+static GtkWidget *window = NULL;
+
+typedef struct
+{
+  const gboolean  fixed;
+  const guint     number;
+  const gchar    *severity;
+  const gchar    *description;
+}
+Bug;
+
+enum
+{
+  COLUMN_FIXED,
+  COLUMN_NUMBER,
+  COLUMN_SEVERITY,
+  COLUMN_DESCRIPTION,
+  NUM_COLUMNS
+};
+
+static Bug data[] =
+{
+  { FALSE, 60482, "Normal",     "scrollable notebooks and hidden tabs" },
+  { FALSE, 60620, "Critical",   "gdk_window_clear_area (gdkwindow-win32.c) is not thread-safe" },
+  { FALSE, 50214, "Major",      "Xft support does not clean up correctly" },
+  { TRUE,  52877, "Major",      "GtkFileSelection needs a refresh method. " },
+  { FALSE, 56070, "Normal",     "Can't click button after setting in sensitive" },
+  { TRUE,  56355, "Normal",     "GtkLabel - Not all changes propagate correctly" },
+  { FALSE, 50055, "Normal",     "Rework width/height computations for TreeView" },
+  { FALSE, 58278, "Normal",     "gtk_dialog_set_response_sensitive () doesn't work" },
+  { FALSE, 55767, "Normal",     "Getters for all setters" },
+  { FALSE, 56925, "Normal",     "Gtkcalender size" },
+  { FALSE, 56221, "Normal",     "Selectable label needs right-click copy menu" },
+  { TRUE,  50939, "Normal",     "Add shift clicking to GtkTextView" },
+  { FALSE, 6112,  "Enhancement","netscape-like collapsable toolbars" },
+  { FALSE, 1,     "Normal",     "First bug :=)" },
+  { 0, 0, NULL, NULL }
+};
+
+static GtkTreeModel *
+create_model (void)
+{
+  gint i = 0;
+  GtkListStore *store;
+  GtkTreeIter iter;
+
+  /* create list store */
+  store = gtk_list_store_new (NUM_COLUMNS,
+                             G_TYPE_BOOLEAN,
+                             G_TYPE_UINT,
+                             G_TYPE_STRING,
+                             G_TYPE_STRING);
+
+  /* add data to the list store */
+  while (data[i].number)
+    {
+      gtk_list_store_append (store, &iter);
+      gtk_list_store_set (store, &iter,
+                         COLUMN_FIXED, data[i].fixed,
+                         COLUMN_NUMBER, data[i].number,
+                         COLUMN_SEVERITY, data[i].severity,
+                         COLUMN_DESCRIPTION, data[i].description,
+                         -1);
+      i++;
+    }
+
+  return GTK_TREE_MODEL (store);
+}
+
+static void
+fixed_toggled (GtkCellRendererToggle *cell,
+              gchar                 *path_str,
+              gpointer               data)
+{
+  GtkTreeModel *model = (GtkTreeModel *)data;
+  GtkTreeIter  iter;
+  GtkTreePath *path = gtk_tree_path_new_from_string (path_str);
+  gboolean fixed;
+
+  /* get toggled iter */
+  gtk_tree_model_get_iter (model, &iter, path);
+  gtk_tree_model_get (model, &iter, COLUMN_FIXED, &fixed, -1);
+
+  /* do something with the value */
+  fixed ^= 1;
+  
+  /* set new value */
+  gtk_list_store_set (GTK_LIST_STORE (model), &iter, COLUMN_FIXED, fixed, -1);
+  
+  /* clean up */
+  gtk_tree_path_free (path);
+}
+
+static void
+add_columns (GtkTreeView *treeview)
+{
+  GtkCellRenderer *renderer;
+  GtkTreeViewColumn *column;
+  GtkTreeModel *model = gtk_tree_view_get_model (treeview);
+
+  /* column for fixed toggles */
+  renderer = gtk_cell_renderer_toggle_new ();
+  g_signal_connect (G_OBJECT (renderer), "toggled",
+                   G_CALLBACK (fixed_toggled), model);
+
+  column = gtk_tree_view_column_new_with_attributes ("Fixed?",
+                                                    renderer,
+                                                    "active", COLUMN_FIXED,
+                                                    NULL);
+
+  /* set this column to a fixed sizing (of 50 pixels) */
+  gtk_tree_view_column_set_sizing (GTK_TREE_VIEW_COLUMN (column),
+                                  GTK_TREE_VIEW_COLUMN_FIXED);
+  gtk_tree_view_column_set_fixed_width (GTK_TREE_VIEW_COLUMN (column), 50);
+  gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE);
+
+  gtk_tree_view_append_column (treeview, column);
+
+  /* column for bug numbers */
+  renderer = gtk_cell_renderer_text_new ();
+  column = gtk_tree_view_column_new_with_attributes ("Bug number",
+                                                    renderer,
+                                                    "text",
+                                                    COLUMN_NUMBER,
+                                                    NULL);
+  gtk_tree_view_append_column (treeview, column);
+                               
+  /* column for severities */
+  renderer = gtk_cell_renderer_text_new ();
+  column = gtk_tree_view_column_new_with_attributes ("Severity",
+                                                    renderer,
+                                                    "text",
+                                                    COLUMN_SEVERITY,
+                                                    NULL);
+  gtk_tree_view_append_column (treeview, column);
+                                                    
+  /* column for description */
+  renderer = gtk_cell_renderer_text_new ();
+  column = gtk_tree_view_column_new_with_attributes ("Description",
+                                                    renderer,
+                                                    "text",
+                                                    COLUMN_DESCRIPTION,
+                                                    NULL);
+  gtk_tree_view_append_column (treeview, column);
+}
+
+GtkWidget *
+do_list_store (void)
+{
+  if (!window)
+    {
+      GtkWidget *vbox;
+      GtkWidget *label;
+      GtkWidget *sw;
+      GtkTreeModel *model;
+      GtkWidget *treeview;
+
+      /* create window, etc */
+      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+      gtk_window_set_title (GTK_WINDOW (window), "GtkListStore demo");
+
+      g_signal_connect (G_OBJECT (window), "destroy",
+                       G_CALLBACK (gtk_widget_destroyed), &window);
+      gtk_container_set_border_width (GTK_CONTAINER (window), 8);
+
+      vbox = gtk_vbox_new (FALSE, 8);
+      gtk_container_add (GTK_CONTAINER (window), vbox);
+
+      label = gtk_label_new ("This is the bug list (note: not based on real data, it would be nice to have a nice ODBC interface to bugzilla or so, though).");
+      gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+      sw = gtk_scrolled_window_new (NULL, NULL);
+      gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
+                                          GTK_SHADOW_ETCHED_IN);
+      gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+                                     GTK_POLICY_NEVER,
+                                     GTK_POLICY_AUTOMATIC);
+      gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
+      
+      /* create tree model */
+      model = create_model ();
+
+      /* create tree view */
+      treeview = gtk_tree_view_new_with_model (model);
+      gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (treeview), TRUE);
+      gtk_tree_view_set_search_column (GTK_TREE_VIEW (treeview),
+                                      COLUMN_DESCRIPTION);
+
+      g_object_unref (G_OBJECT (model));
+      
+      gtk_container_add (GTK_CONTAINER (sw), treeview);
+      
+      /* add columns to the tree view */
+      add_columns (GTK_TREE_VIEW (treeview));
+
+      /* finish & show */
+      gtk_window_set_default_size (GTK_WINDOW (window), 280, 250);
+    }
+
+  if (!GTK_WIDGET_VISIBLE (window))
+    gtk_widget_show_all (window);
+  else
+    {
+      gtk_widget_destroy (window);
+      window = NULL;
+    }
+
+  return window;
+}
index 00fabe74aff3169db0596face2d0cb9a7b3de861..b40f6ec1ea379a6333bff691839c4664859997d0 100644 (file)
@@ -408,6 +408,7 @@ load_file (const gchar *filename)
     {
       gchar *p = buffer->str;
       gchar *q;
+      gchar *r;
       
       switch (state)
        {
@@ -415,6 +416,11 @@ load_file (const gchar *filename)
          /* Reading title */
          while (*p == '/' || *p == '*' || isspace (*p))
            p++;
+         r = p;
+         while (*r != '/' && strlen (r))
+           r++;
+         if (strlen (r) > 0)
+           p = r + 1;
          q = p + strlen (p);
          while (q > p && isspace (*(q - 1)))
            q--;
@@ -532,28 +538,34 @@ button_press_event_cb (GtkTreeView    *tree_view,
                              FUNC_COLUMN, &func,
                              ITALIC_COLUMN, &italic,
                              -1);
-         gtk_tree_store_set (GTK_TREE_STORE (model),
-                             &iter,
-                             ITALIC_COLUMN, !italic,
-                             -1);
-         window = (func) ();
-         if (window != NULL)
-           {
-             CallbackData *cbdata;
-
-             cbdata = g_new (CallbackData, 1);
-             cbdata->model = model;
-             cbdata->path = path;
 
-             gtk_signal_connect (GTK_OBJECT (window),
-                                  "destroy",
-                                  GTK_SIGNAL_FUNC (window_closed_cb),
-                                  cbdata);
-           }
-         else
+         if (func)
            {
-             gtk_tree_path_free (path);
+             gtk_tree_store_set (GTK_TREE_STORE (model),
+                                 &iter,
+                                 ITALIC_COLUMN, !italic,
+                                 -1);
+             window = (func) ();
+             if (window != NULL)
+               {
+                 CallbackData *cbdata;
+                 
+                 cbdata = g_new (CallbackData, 1);
+                 cbdata->model = model;
+                 cbdata->path = path;
+                 
+                 gtk_signal_connect (GTK_OBJECT (window),
+                                     "destroy",
+                                     GTK_SIGNAL_FUNC (window_closed_cb),
+                                     cbdata);
+               }
+             else
+               {
+                 gtk_tree_path_free (path);
+               }
            }
+         else
+           gtk_tree_path_free (path);
        }
 
       gtk_signal_emit_stop_by_name (GTK_OBJECT (tree_view),
@@ -583,24 +595,28 @@ row_activated_cb (GtkTreeView       *tree_view,
                      FUNC_COLUMN, &func,
                      ITALIC_COLUMN, &italic,
                      -1);
-  gtk_tree_store_set (GTK_TREE_STORE (model),
-                     &iter,
-                     ITALIC_COLUMN, !italic,
-                     -1);
-  window = (func) ();
 
-  if (window != NULL)
+  if (func)
     {
-      CallbackData *cbdata;
-      
-      cbdata = g_new (CallbackData, 1);
-      cbdata->model = model;
-      cbdata->path = gtk_tree_path_copy (path);
+      gtk_tree_store_set (GTK_TREE_STORE (model),
+                         &iter,
+                         ITALIC_COLUMN, !italic,
+                         -1);
+      window = (func) ();
       
-      gtk_signal_connect (GTK_OBJECT (window),
-                         "destroy",
-                         GTK_SIGNAL_FUNC (window_closed_cb),
-                         cbdata);
+      if (window != NULL)
+       {
+         CallbackData *cbdata;
+         
+         cbdata = g_new (CallbackData, 1);
+         cbdata->model = model;
+         cbdata->path = gtk_tree_path_copy (path);
+         
+         gtk_signal_connect (GTK_OBJECT (window),
+                             "destroy",
+                             GTK_SIGNAL_FUNC (window_closed_cb),
+                             cbdata);
+       }
     }
 }
 
@@ -617,7 +633,8 @@ selection_cb (GtkTreeSelection *selection,
   gtk_tree_model_get_value (model, &iter,
                            FILENAME_COLUMN,
                            &value);
-  load_file (g_value_get_string (&value));
+  if (g_value_get_string (&value))
+    load_file (g_value_get_string (&value));
   g_value_unset (&value);
 }
 
@@ -668,7 +685,6 @@ create_text (GtkTextBuffer **buffer,
   return scrolled_window;
 }
 
-/* Technically a list, but if we do go to 80 demos, we may want to move to a tree */
 static GtkWidget *
 create_tree (void)
 {
@@ -678,7 +694,8 @@ create_tree (void)
   GtkTreeViewColumn *column;
   GtkTreeStore *model;
   GtkTreeIter iter;
-  gint i;
+
+  Demo *d = testgtk_demos;
 
   model = gtk_tree_store_new (NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_BOOLEAN);
   tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
@@ -688,17 +705,44 @@ create_tree (void)
                               GTK_SELECTION_BROWSE);
   gtk_widget_set_size_request (tree_view, 200, -1);
 
-  for (i=0; i < G_N_ELEMENTS (testgtk_demos); i++)
+  /* this code only supports 1 level of children. If we
+   * want more we probably have to use a recursing function.
+   */
+  while (d->title)
     {
+      Demo *children = d->children;
+
       gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL);
 
       gtk_tree_store_set (GTK_TREE_STORE (model),
                          &iter,
-                         TITLE_COLUMN, testgtk_demos[i].title,
-                         FILENAME_COLUMN, testgtk_demos[i].filename,
-                         FUNC_COLUMN, testgtk_demos[i].func,
+                         TITLE_COLUMN, d->title,
+                         FILENAME_COLUMN, d->filename,
+                         FUNC_COLUMN, d->func,
                          ITALIC_COLUMN, FALSE,
                          -1);
+
+      d++;
+
+      if (!children)
+       continue;
+      
+      while (children->title)
+       {
+         GtkTreeIter child_iter;
+
+         gtk_tree_store_append (GTK_TREE_STORE (model), &child_iter, &iter);
+         
+         gtk_tree_store_set (GTK_TREE_STORE (model),
+                             &child_iter,
+                             TITLE_COLUMN, children->title,
+                             FILENAME_COLUMN, children->filename,
+                             FUNC_COLUMN, children->func,
+                             ITALIC_COLUMN, FALSE,
+                             -1);
+         
+         children++;
+       }
     }
 
   cell = gtk_cell_renderer_text_new ();
@@ -719,6 +763,8 @@ create_tree (void)
   g_signal_connect (G_OBJECT (selection), "changed", GTK_SIGNAL_FUNC (selection_cb), model);
   g_signal_connect (G_OBJECT (tree_view), "row_activated", GTK_SIGNAL_FUNC (row_activated_cb), model);
 
+  gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view));
+
   return tree_view;
 }
 
index 0622d40ec8beed83409098b52185563c1fd2dcf9..5594a423d65067877f57a3268ce01b4f6a527640 100644 (file)
@@ -1,4 +1,4 @@
-/* Stock Item/Icon Browser
+/* Stock Item and Icon Browser
  *
  * This source code for this demo doesn't demonstrate anything
  * particularly useful in applications. The purpose of the "demo" is
diff --git a/demos/gtk-demo/tree_store.c b/demos/gtk-demo/tree_store.c
new file mode 100644 (file)
index 0000000..92e68b6
--- /dev/null
@@ -0,0 +1,446 @@
+/* Tree View/Tree Store
+ *
+ * The GtkTreeStore is used to store data in tree form, to be
+ * used later on by a GtkTreeView to display it. This demo builds
+ * a simple GtkTreeStore and displays it. If you're new to the 
+ * GtkTreeView widgets and associates, look into the GtkListStore
+ * example first.
+ *
+ */
+
+#include <gtk/gtk.h>
+
+static GtkWidget *window = NULL;
+
+/* TreeItem structure */
+typedef struct _TreeItem TreeItem;
+struct _TreeItem
+{
+  const gchar    *label;
+  gboolean        alex;
+  gboolean        havoc;
+  gboolean        tim;
+  gboolean        owen;
+  gboolean        dave;
+  gboolean        world_holiday; /* shared by the European hackers */
+  TreeItem       *children;
+};
+
+/* columns */
+enum
+{
+  HOLIDAY_NAME_COLUMN = 0,
+  ALEX_COLUMN,
+  HAVOC_COLUMN,
+  TIM_COLUMN,
+  OWEN_COLUMN,
+  DAVE_COLUMN,
+
+  VISIBLE_COLUMN,
+  WORLD_COLUMN,
+  NUM_COLUMNS
+};
+
+/* tree data */
+static TreeItem january[] =
+{
+  {"New Years Day", TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, NULL },
+  {"Presidential Inauguration", FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, NULL },
+  {"Martin Luther King Jr. day", FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, NULL }, 
+  { NULL }
+};
+
+static TreeItem february[] =
+{
+  { "Presidents' Day", FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, NULL },
+  { "Groundhog Day", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL },
+  { "Valentine's Day", FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, NULL },
+  { NULL }
+};
+
+static TreeItem march[] =
+{
+  { "National Tree Planting Day", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL },
+  { "St Patrick's Day", FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, NULL },
+  { NULL }
+};
+static TreeItem april[] =
+{
+  { "April Fools' Day", FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, NULL },
+  { "Army Day", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL },
+  { "Earth Day", FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, NULL },
+  { "Administrative Professionals' Day", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL },
+  { NULL }
+};
+
+static TreeItem may[] =
+{
+  { "Nurses' Day", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL },
+  { "National Day of Prayer", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL },
+  { "Mothers' Day", FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, NULL },
+  { "Armed Forces Day", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL },
+  { "Memorial Day", TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, NULL },
+  { NULL }
+};
+
+static TreeItem june[] =
+{
+  { "June Fathers' Day", FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, NULL },
+  { "Juneteenth (Liberation of Slaves)", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL },
+  { "Flag Day", FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, NULL },
+  { NULL }
+};
+
+static TreeItem july[] =
+{
+  { "Parents' Day", FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, NULL },
+  { "Independence Day", FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, NULL },
+  { NULL }
+};
+
+static TreeItem august[] =
+{
+  { "Air Force Day", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL },
+  { "Coast Guard Day", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL },
+  { "Friendship Day", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL },
+  { NULL }
+};
+
+static TreeItem september[] =
+{
+  { "Grandparents' Day", FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, NULL },
+  { "Citizenship Day or Constitution Day", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL },
+  { "Labor Day", TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, NULL },
+  { NULL }
+};
+
+static TreeItem october[] =
+{
+  { "National Children's Day", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, 
+  { "Bosses' Day", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL },
+  { "Sweetest Day", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL },
+  { "Mother-in-Law's Day", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL },
+  { "Navy Day", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL },
+  { "Columbus Day", FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, NULL },
+  { "Halloween", FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, NULL },
+  { NULL }
+};
+
+static TreeItem november[] =
+{
+  { "Marine Corps Day", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL },
+  { "Veterans' Day", TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, NULL },
+  { "Thanksgiving", FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, NULL },
+  { NULL }
+};
+
+static TreeItem december[] =
+{
+  { "Pearl Harbor Remembrance Day", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL },
+  { "Christmas", TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, NULL },
+  { "Kwanzaa", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL },
+  { NULL }
+};
+
+
+static TreeItem toplevel[] =
+{
+  {"January", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, january},
+  {"February", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, february},
+  {"March", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, march},
+  {"April", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, april},
+  {"May", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, may},
+  {"June", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, june},
+  {"July", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, july},
+  {"August", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, august},
+  {"September", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, september},
+  {"October", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, october},
+  {"November", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, november},
+  {"December", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, december},
+  {NULL}
+};
+
+
+static GtkTreeModel *
+create_model (void)
+{
+  GtkTreeStore *model;
+  GtkTreeIter iter;
+  TreeItem *month = toplevel;
+
+  /* create tree store */
+  model = gtk_tree_store_new (NUM_COLUMNS,
+                             G_TYPE_STRING,
+                             G_TYPE_BOOLEAN,
+                             G_TYPE_BOOLEAN,
+                             G_TYPE_BOOLEAN,
+                             G_TYPE_BOOLEAN,
+                             G_TYPE_BOOLEAN,
+                             G_TYPE_BOOLEAN,
+                             G_TYPE_BOOLEAN);
+
+  /* add data to the tree store */
+  while (month->label)
+    {
+      TreeItem *holiday = month->children;
+
+      gtk_tree_store_append (model, &iter, NULL);
+      gtk_tree_store_set (model, &iter,
+                         HOLIDAY_NAME_COLUMN, month->label,
+                         ALEX_COLUMN, FALSE,
+                         HAVOC_COLUMN, FALSE,
+                         TIM_COLUMN, FALSE,
+                         OWEN_COLUMN, FALSE,
+                         DAVE_COLUMN, FALSE,
+                         VISIBLE_COLUMN, FALSE,
+                         WORLD_COLUMN, FALSE,
+                         -1);
+
+      /* add children */
+      while (holiday->label)
+       {
+         GtkTreeIter child_iter;
+         
+         gtk_tree_store_append (model, &child_iter, &iter);
+         gtk_tree_store_set (model, &child_iter,
+                             HOLIDAY_NAME_COLUMN, holiday->label,
+                             ALEX_COLUMN, holiday->alex,
+                             HAVOC_COLUMN, holiday->havoc,
+                             TIM_COLUMN, holiday->tim,
+                             OWEN_COLUMN, holiday->owen,
+                             DAVE_COLUMN, holiday->dave,
+                             VISIBLE_COLUMN, TRUE,
+                             WORLD_COLUMN, holiday->world_holiday,
+                             -1);
+                             
+         holiday++;
+       }
+
+      month++;
+    }
+
+  return GTK_TREE_MODEL (model);
+}
+
+static void
+item_toggled (GtkCellRendererToggle *cell,
+             gchar                 *path_str,
+             gpointer               data)
+{
+  GtkTreeModel *model = (GtkTreeModel *)data;
+  GtkTreePath *path = gtk_tree_path_new_from_string (path_str);
+  GtkTreeIter iter;
+  gboolean toggle_item;
+
+  gint *column;
+
+  column = g_object_get_data (G_OBJECT (cell), "column");
+  
+  /* get toggled iter */
+  gtk_tree_model_get_iter (model, &iter, path);
+  gtk_tree_model_get (model, &iter, column, &toggle_item, -1);
+
+  /* do something with the value */
+  toggle_item ^= 1;
+
+  /* set new value */
+  gtk_tree_store_set (GTK_TREE_STORE (model), &iter, column,
+                     toggle_item, -1);
+
+  /* clean up */
+  gtk_tree_path_free (path);
+}
+
+static void
+add_columns (GtkTreeView *treeview)
+{
+  gint col_offset;
+  GtkCellRenderer *renderer;
+  GtkTreeViewColumn *column;
+  GtkTreeModel *model = gtk_tree_view_get_model (treeview);
+
+  /* column for holiday names */
+  renderer = gtk_cell_renderer_text_new ();
+  g_object_set (G_OBJECT (renderer), "xalign", 0.0, NULL);
+
+  col_offset = gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
+                                                           -1, "Holiday",
+                                                           renderer, "text",
+                                                           HOLIDAY_NAME_COLUMN,
+                                                           NULL);
+  column = gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), col_offset - 1);
+  gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE);
+
+  /* alex column */
+  renderer = gtk_cell_renderer_toggle_new ();
+  g_object_set (G_OBJECT (renderer), "xalign", 0.0, NULL);
+  g_object_set_data (G_OBJECT (renderer), "column", (gint *)ALEX_COLUMN);
+
+  g_signal_connect (G_OBJECT (renderer), "toggled", G_CALLBACK (item_toggled),
+                   model);
+  col_offset = gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
+                                                           -1, "Alex",
+                                                           renderer,
+                                                           "active",
+                                                           ALEX_COLUMN,
+                                                           "visible",
+                                                           VISIBLE_COLUMN,
+                                                           "activatable",
+                                                           WORLD_COLUMN, NULL);
+  
+  column = gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), col_offset - 1);
+  gtk_tree_view_column_set_sizing (GTK_TREE_VIEW_COLUMN (column),
+                                  GTK_TREE_VIEW_COLUMN_FIXED);
+  gtk_tree_view_column_set_fixed_width (GTK_TREE_VIEW_COLUMN (column), 50);
+  gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE);
+                                     
+  /* havoc column */
+  renderer = gtk_cell_renderer_toggle_new ();
+  g_object_set (G_OBJECT (renderer), "xalign", 0.0, NULL);
+  g_object_set_data (G_OBJECT (renderer), "column", (gint *)HAVOC_COLUMN);
+
+  g_signal_connect (G_OBJECT (renderer), "toggled", G_CALLBACK (item_toggled),
+                   model);
+  col_offset = gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
+                                                           -1, "Havoc",
+                                                           renderer,
+                                                           "active",
+                                                           HAVOC_COLUMN,
+                                                           "visible",
+                                                           VISIBLE_COLUMN,
+                                                           NULL);
+  
+  column = gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), col_offset - 1);
+  gtk_tree_view_column_set_sizing (GTK_TREE_VIEW_COLUMN (column),
+                                  GTK_TREE_VIEW_COLUMN_FIXED);
+  gtk_tree_view_column_set_fixed_width (GTK_TREE_VIEW_COLUMN (column), 50);
+  gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE);
+                                     
+  /* tim column */
+  renderer = gtk_cell_renderer_toggle_new ();
+  g_object_set (G_OBJECT (renderer), "xalign", 0.0, NULL);
+  g_object_set_data (G_OBJECT (renderer), "column", (gint *)TIM_COLUMN);
+  
+  g_signal_connect (G_OBJECT (renderer), "toggled", G_CALLBACK (item_toggled),
+                   model);
+  col_offset = gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
+                                                           -1, "Tim",
+                                                           renderer,
+                                                           "active",
+                                                           TIM_COLUMN,
+                                                           "visible",
+                                                           VISIBLE_COLUMN,
+                                                           "activatable",
+                                                           WORLD_COLUMN, NULL);
+  
+  column = gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), col_offset - 1);
+  gtk_tree_view_column_set_sizing (GTK_TREE_VIEW_COLUMN (column),
+                                  GTK_TREE_VIEW_COLUMN_FIXED);
+  gtk_tree_view_column_set_fixed_width (GTK_TREE_VIEW_COLUMN (column), 50);
+  gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE);
+                                     
+  /* owen column */
+  renderer = gtk_cell_renderer_toggle_new ();
+  g_object_set (G_OBJECT (renderer), "xalign", 0.0, NULL);
+  g_object_set_data (G_OBJECT (renderer), "column", (gint *)OWEN_COLUMN);
+  
+  g_signal_connect (G_OBJECT (renderer), "toggled", G_CALLBACK (item_toggled),
+                   model);
+  col_offset = gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
+                                                           -1, "Owen",
+                                                           renderer,
+                                                           "active",
+                                                           OWEN_COLUMN,
+                                                           "visible",
+                                                           VISIBLE_COLUMN,
+                                                           NULL);
+  
+  column = gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), col_offset - 1);
+  gtk_tree_view_column_set_sizing (GTK_TREE_VIEW_COLUMN (column),
+                                  GTK_TREE_VIEW_COLUMN_FIXED);
+  gtk_tree_view_column_set_fixed_width (GTK_TREE_VIEW_COLUMN (column), 50);
+  gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE);
+  
+  /* dave column */
+  renderer = gtk_cell_renderer_toggle_new ();
+  g_object_set (G_OBJECT (renderer), "xalign", 0.0, NULL);
+  g_object_set_data (G_OBJECT (renderer), "column", (gint *)DAVE_COLUMN);
+
+  g_signal_connect (G_OBJECT (renderer), "toggled", G_CALLBACK (item_toggled),
+                   model);
+  col_offset = gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
+                                                           -1, "Dave",
+                                                           renderer,
+                                                           "active",
+                                                           DAVE_COLUMN,
+                                                           "visible",
+                                                           VISIBLE_COLUMN,
+                                                           NULL);
+  
+  column = gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), col_offset - 1);
+  gtk_tree_view_column_set_sizing (GTK_TREE_VIEW_COLUMN (column),
+                                  GTK_TREE_VIEW_COLUMN_FIXED);
+  gtk_tree_view_column_set_fixed_width (GTK_TREE_VIEW_COLUMN (column), 50);
+  gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE);
+}
+
+GtkWidget *
+do_tree_store (void)
+{
+  if (!window)
+    {
+      GtkWidget *vbox;
+      GtkWidget *sw;
+      GtkWidget *treeview;
+      GtkTreeModel *model;
+
+      /* create window, etc */
+      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+      gtk_window_set_title (GTK_WINDOW (window), "Card planning sheet");
+      g_signal_connect (G_OBJECT (window), "destroy", 
+                       G_CALLBACK (gtk_widget_destroyed), &window);
+
+      vbox = gtk_vbox_new (FALSE, 8);
+      gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
+      gtk_container_add (GTK_CONTAINER (window), vbox);
+      
+      gtk_box_pack_start (GTK_BOX (vbox),
+                         gtk_label_new ("Jonathan's Holiday Card Planning Sheet"),
+                         FALSE, FALSE, 0);
+      
+      sw = gtk_scrolled_window_new (NULL, NULL);
+      gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
+                                          GTK_SHADOW_ETCHED_IN);
+      gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+                                     GTK_POLICY_AUTOMATIC,
+                                     GTK_POLICY_AUTOMATIC);
+      gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
+
+      /* create model */
+      model = create_model ();
+      
+      /* create tree view */
+      treeview = gtk_tree_view_new_with_model (model);
+      gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (treeview), TRUE);
+      gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)),
+                                  GTK_SELECTION_MULTIPLE);
+      
+      add_columns (GTK_TREE_VIEW (treeview));
+      
+      gtk_container_add (GTK_CONTAINER (sw), treeview);
+      
+      g_signal_connect (G_OBJECT (treeview), "realize",
+                       G_CALLBACK (gtk_tree_view_expand_all), NULL);
+      gtk_window_set_default_size (GTK_WINDOW (window), 650, 400);
+    }
+
+  if (!GTK_WIDGET_VISIBLE (window))
+    gtk_widget_show_all (window);
+  else
+    {
+      gtk_widget_destroy (window);
+      window = NULL;
+    }
+
+  return window;
+}