summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author4 <jrb@redhat.com>2000-10-05 01:04:57 +0000
committerJonathan Blandford <jrb@src.gnome.org>2000-10-05 01:04:57 +0000
commit3ff4a2c081e5225f010ecaed91b162eabbb9caf5 (patch)
treef1b889ac680e66a61e54d559baa911e524d10f2a
parent76806e9a1e2c56f31d7a96c02ea194d3cc5bbc0e (diff)
downloadgtk+-3ff4a2c081e5225f010ecaed91b162eabbb9caf5.tar.gz
Checked in initial draft of the new tree widget.
2000-10-04 <jrb@redhat.com> * gtk/gtk{tree,cell}?*.[ch]: Checked in initial draft of the new tree widget.
-rw-r--r--ChangeLog5
-rw-r--r--ChangeLog.pre-2-05
-rw-r--r--ChangeLog.pre-2-105
-rw-r--r--ChangeLog.pre-2-25
-rw-r--r--ChangeLog.pre-2-45
-rw-r--r--ChangeLog.pre-2-65
-rw-r--r--ChangeLog.pre-2-85
-rw-r--r--gtk/gtkcellrenderer.c268
-rw-r--r--gtk/gtkcellrenderer.h111
-rw-r--r--gtk/gtkcellrendererpixbuf.c240
-rw-r--r--gtk/gtkcellrendererpixbuf.h58
-rw-r--r--gtk/gtkcellrenderertext.c449
-rw-r--r--gtk/gtkcellrenderertext.h65
-rw-r--r--gtk/gtkcellrenderertextpixbuf.c399
-rw-r--r--gtk/gtkcellrenderertextpixbuf.h61
-rw-r--r--gtk/gtkcellrenderertoggle.c321
-rw-r--r--gtk/gtkcellrenderertoggle.h68
-rw-r--r--gtk/gtkliststore.c458
-rw-r--r--gtk/gtkliststore.h87
-rw-r--r--gtk/gtkmodelsimple.c430
-rw-r--r--gtk/gtkmodelsimple.h73
-rw-r--r--gtk/gtkrbtree.c994
-rw-r--r--gtk/gtkrbtree.h133
-rw-r--r--gtk/gtktreedatalist.c159
-rw-r--r--gtk/gtktreedatalist.h53
-rw-r--r--gtk/gtktreemodel.c421
-rw-r--r--gtk/gtktreemodel.h144
-rw-r--r--gtk/gtktreeprivate.h191
-rw-r--r--gtk/gtktreeselection.c702
-rw-r--r--gtk/gtktreeselection.h120
-rw-r--r--gtk/gtktreestore.c655
-rw-r--r--gtk/gtktreestore.h99
-rw-r--r--gtk/gtktreeview.c3384
-rw-r--r--gtk/gtktreeview.h105
-rw-r--r--gtk/gtktreeviewcolumn.c633
-rw-r--r--gtk/gtktreeviewcolumn.h137
-rw-r--r--gtk/treestoretest.c206
-rw-r--r--tests/treestoretest.c206
38 files changed, 11465 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog
index 3bb78c444f..780d42713b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2000-10-04 <jrb@redhat.com>
+
+ * gtk/gtk{tree,cell}?*.[ch]: Checked in initial draft of the new
+ tree widget.
+
2000-10-04 Jonathan Blandford <jrb@redhat.com>
* gdk/gdkwindow.c (gdk_window_draw_arc): Fix obvious bug in circle
diff --git a/ChangeLog.pre-2-0 b/ChangeLog.pre-2-0
index 3bb78c444f..780d42713b 100644
--- a/ChangeLog.pre-2-0
+++ b/ChangeLog.pre-2-0
@@ -1,3 +1,8 @@
+2000-10-04 <jrb@redhat.com>
+
+ * gtk/gtk{tree,cell}?*.[ch]: Checked in initial draft of the new
+ tree widget.
+
2000-10-04 Jonathan Blandford <jrb@redhat.com>
* gdk/gdkwindow.c (gdk_window_draw_arc): Fix obvious bug in circle
diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10
index 3bb78c444f..780d42713b 100644
--- a/ChangeLog.pre-2-10
+++ b/ChangeLog.pre-2-10
@@ -1,3 +1,8 @@
+2000-10-04 <jrb@redhat.com>
+
+ * gtk/gtk{tree,cell}?*.[ch]: Checked in initial draft of the new
+ tree widget.
+
2000-10-04 Jonathan Blandford <jrb@redhat.com>
* gdk/gdkwindow.c (gdk_window_draw_arc): Fix obvious bug in circle
diff --git a/ChangeLog.pre-2-2 b/ChangeLog.pre-2-2
index 3bb78c444f..780d42713b 100644
--- a/ChangeLog.pre-2-2
+++ b/ChangeLog.pre-2-2
@@ -1,3 +1,8 @@
+2000-10-04 <jrb@redhat.com>
+
+ * gtk/gtk{tree,cell}?*.[ch]: Checked in initial draft of the new
+ tree widget.
+
2000-10-04 Jonathan Blandford <jrb@redhat.com>
* gdk/gdkwindow.c (gdk_window_draw_arc): Fix obvious bug in circle
diff --git a/ChangeLog.pre-2-4 b/ChangeLog.pre-2-4
index 3bb78c444f..780d42713b 100644
--- a/ChangeLog.pre-2-4
+++ b/ChangeLog.pre-2-4
@@ -1,3 +1,8 @@
+2000-10-04 <jrb@redhat.com>
+
+ * gtk/gtk{tree,cell}?*.[ch]: Checked in initial draft of the new
+ tree widget.
+
2000-10-04 Jonathan Blandford <jrb@redhat.com>
* gdk/gdkwindow.c (gdk_window_draw_arc): Fix obvious bug in circle
diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6
index 3bb78c444f..780d42713b 100644
--- a/ChangeLog.pre-2-6
+++ b/ChangeLog.pre-2-6
@@ -1,3 +1,8 @@
+2000-10-04 <jrb@redhat.com>
+
+ * gtk/gtk{tree,cell}?*.[ch]: Checked in initial draft of the new
+ tree widget.
+
2000-10-04 Jonathan Blandford <jrb@redhat.com>
* gdk/gdkwindow.c (gdk_window_draw_arc): Fix obvious bug in circle
diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8
index 3bb78c444f..780d42713b 100644
--- a/ChangeLog.pre-2-8
+++ b/ChangeLog.pre-2-8
@@ -1,3 +1,8 @@
+2000-10-04 <jrb@redhat.com>
+
+ * gtk/gtk{tree,cell}?*.[ch]: Checked in initial draft of the new
+ tree widget.
+
2000-10-04 Jonathan Blandford <jrb@redhat.com>
* gdk/gdkwindow.c (gdk_window_draw_arc): Fix obvious bug in circle
diff --git a/gtk/gtkcellrenderer.c b/gtk/gtkcellrenderer.c
new file mode 100644
index 0000000000..5a12dc21eb
--- /dev/null
+++ b/gtk/gtkcellrenderer.c
@@ -0,0 +1,268 @@
+/* gtkcellrenderer.c
+ * Copyright (C) 2000 Red Hat, Inc. Jonathan Blandford
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gtkcellrenderer.h"
+
+#ifndef _
+#define _(x) x
+#endif
+
+static void gtk_cell_renderer_init (GtkCellRenderer *cell);
+static void gtk_cell_renderer_class_init (GtkCellRendererClass *class);
+static void gtk_cell_renderer_get_param (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec,
+ const gchar *trailer);
+static void gtk_cell_renderer_set_param (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec,
+ const gchar *trailer);
+
+
+enum {
+ PROP_ZERO,
+ PROP_XALIGN,
+ PROP_YALIGN,
+ PROP_XPAD,
+ PROP_YPAD
+};
+
+
+GtkType
+gtk_cell_renderer_get_type (void)
+{
+ static GtkType cell_type = 0;
+
+ if (!cell_type)
+ {
+ static const GTypeInfo cell_info =
+ {
+ sizeof (GtkCellRendererClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) gtk_cell_renderer_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GtkCellRenderer),
+ 0,
+ (GInstanceInitFunc) gtk_cell_renderer_init,
+ };
+
+ cell_type = g_type_register_static (GTK_TYPE_OBJECT, "GtkCellRenderer", &cell_info);
+ }
+
+ return cell_type;
+}
+
+static void
+gtk_cell_renderer_init (GtkCellRenderer *cell)
+{
+ cell->xpad = 0;
+ cell->ypad = 0;
+ cell->xalign = 0.5;
+ cell->yalign = 0.5;
+}
+
+static void
+gtk_cell_renderer_class_init (GtkCellRendererClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ object_class->get_param = gtk_cell_renderer_get_param;
+ object_class->set_param = gtk_cell_renderer_set_param;
+
+ class->render = NULL;
+ class->get_size = NULL;
+
+
+ g_object_class_install_param (object_class,
+ PROP_XALIGN,
+ g_param_spec_float ("xalign",
+ _("xalign"),
+ _("The x-align."),
+ 0.0,
+ 1.0,
+ 0.0,
+ G_PARAM_READABLE |
+ G_PARAM_WRITABLE));
+
+ g_object_class_install_param (object_class,
+ PROP_YALIGN,
+ g_param_spec_float ("yalign",
+ _("yalign"),
+ _("The y-align."),
+ 0.0,
+ 1.0,
+ 0.5,
+ G_PARAM_READABLE |
+ G_PARAM_WRITABLE));
+
+ g_object_class_install_param (object_class,
+ PROP_XPAD,
+ g_param_spec_uint ("xpad",
+ _("xpad"),
+ _("The xpad."),
+ 0,
+ 100,
+ 2,
+ G_PARAM_READABLE |
+ G_PARAM_WRITABLE));
+
+ g_object_class_install_param (object_class,
+ PROP_YPAD,
+ g_param_spec_uint ("ypad",
+ _("ypad"),
+ _("The ypad."),
+ 0,
+ 100,
+ 2,
+ G_PARAM_READABLE |
+ G_PARAM_WRITABLE));
+}
+
+static void
+gtk_cell_renderer_get_param (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec,
+ const gchar *trailer)
+{
+ GtkCellRenderer *cell = GTK_CELL_RENDERER (object);
+
+ switch (param_id)
+ {
+ case PROP_XALIGN:
+ g_value_init (value, G_TYPE_FLOAT);
+ g_value_set_float (value, cell->xalign);
+ break;
+ case PROP_YALIGN:
+ g_value_init (value, G_TYPE_FLOAT);
+ g_value_set_float (value, cell->yalign);
+ break;
+ case PROP_XPAD:
+ g_value_init (value, G_TYPE_INT);
+ g_value_set_float (value, cell->xpad);
+ break;
+ case PROP_YPAD:
+ g_value_init (value, G_TYPE_INT);
+ g_value_set_float (value, cell->ypad);
+ break;
+ default:
+ break;
+ }
+
+}
+
+static void
+gtk_cell_renderer_set_param (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec,
+ const gchar *trailer)
+{
+ GtkCellRenderer *cell = GTK_CELL_RENDERER (object);
+
+ switch (param_id)
+ {
+ case PROP_XALIGN:
+ cell->xalign = g_value_get_float (value);
+ break;
+ case PROP_YALIGN:
+ cell->yalign = g_value_get_float (value);
+ break;
+ case PROP_XPAD:
+ cell->xpad = g_value_get_int (value);
+ break;
+ case PROP_YPAD:
+ cell->ypad = g_value_get_int (value);
+ break;
+ default:
+ break;
+ }
+}
+
+void
+gtk_cell_renderer_get_size (GtkCellRenderer *cell,
+ GtkWidget *widget,
+ gint *width,
+ gint *height)
+{
+ /* It's actually okay to pass in a NULL cell, as we run into that
+ * a lot */
+ if (cell == NULL)
+ return;
+ g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
+ g_return_if_fail (GTK_CELL_RENDERER_GET_CLASS (cell)->get_size != NULL);
+
+ GTK_CELL_RENDERER_GET_CLASS (cell)->get_size (cell, widget, width, height);
+}
+
+void
+gtk_cell_renderer_render (GtkCellRenderer *cell,
+ GdkWindow *window,
+ GtkWidget *widget,
+ GdkRectangle *background_area,
+ GdkRectangle *cell_area,
+ GdkRectangle *expose_area,
+ guint flags)
+{
+ /* It's actually okay to pass in a NULL cell, as we run into that
+ * a lot */
+ if (cell == NULL)
+ return;
+ g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
+ g_return_if_fail (GTK_CELL_RENDERER_GET_CLASS (cell)->render != NULL);
+
+ GTK_CELL_RENDERER_GET_CLASS (cell)->render (cell,
+ window,
+ widget,
+ background_area,
+ cell_area,
+ expose_area,
+ flags);
+}
+
+gint
+gtk_cell_renderer_event (GtkCellRenderer *cell,
+ GdkEvent *event,
+ GtkWidget *widget,
+ gchar *path,
+ GdkRectangle *background_area,
+ GdkRectangle *cell_area,
+ guint flags)
+{
+ /* It's actually okay to pass in a NULL cell, as we run into that
+ * a lot */
+ if (cell == NULL)
+ return FALSE;
+ g_return_val_if_fail (GTK_IS_CELL_RENDERER (cell), FALSE);
+ if (GTK_CELL_RENDERER_GET_CLASS (cell)->event == NULL)
+ return FALSE;
+
+ return GTK_CELL_RENDERER_GET_CLASS (cell)->event (cell,
+ event,
+ widget,
+ path,
+ background_area,
+ cell_area,
+ flags);
+}
+
diff --git a/gtk/gtkcellrenderer.h b/gtk/gtkcellrenderer.h
new file mode 100644
index 0000000000..2b16126920
--- /dev/null
+++ b/gtk/gtkcellrenderer.h
@@ -0,0 +1,111 @@
+/* gtkcellrenderer.h
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_CELL_RENDERER_H__
+#define __GTK_CELL_RENDERER_H__
+
+#include <gtk/gtkobject.h>
+#include <gtk/gtkwidget.h>
+
+#ifdef __cplusplus
+extern "C" {
+
+#endif /* __cplusplus */
+
+typedef enum
+{
+ GTK_CELL_RENDERER_SELECTED = 1 << 0,
+ GTK_CELL_RENDERER_PRELIT = 1 << 1,
+ GTK_CELL_RENDERER_INSENSITIVE = 1 << 2
+} GtkCellRendererType;
+
+#define GTK_TYPE_CELL_RENDERER (gtk_cell_renderer_get_type ())
+#define GTK_CELL_RENDERER(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_CELL_RENDERER, GtkCellRenderer))
+#define GTK_CELL_RENDERER_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_RENDERER, GtkCellRendererClass))
+#define GTK_IS_CELL_RENDERER(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_CELL_RENDERER))
+#define GTK_IS_CELL_RENDERER_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), GTK_TYPE_CELL_RENDERER))
+#define GTK_CELL_RENDERER_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_CELL_RENDERER, GtkCellRendererClass))
+
+typedef struct _GtkCellRenderer GtkCellRenderer;
+typedef struct _GtkCellRendererClass GtkCellRendererClass;
+
+struct _GtkCellRenderer
+{
+ GtkObject parent;
+
+ gfloat xalign;
+ gfloat yalign;
+
+ guint16 xpad;
+ guint16 ypad;
+};
+
+struct _GtkCellRendererClass
+{
+ GtkObjectClass parent_class;
+
+ /* vtable - not signals */
+ void (* get_size) (GtkCellRenderer *cell,
+ GtkWidget *widget,
+ gint *width,
+ gint *height);
+ void (* render) (GtkCellRenderer *cell,
+ GdkWindow *window,
+ GtkWidget *widget,
+ GdkRectangle *background_area,
+ GdkRectangle *cell_area,
+ GdkRectangle *expose_area,
+ guint flags);
+
+ gint (* event) (GtkCellRenderer *cell,
+ GdkEvent *event,
+ GtkWidget *widget,
+ gchar *path,
+ GdkRectangle *background_area,
+ GdkRectangle *cell_area,
+ guint flags);
+};
+
+
+GtkType gtk_cell_renderer_get_type (void);
+void gtk_cell_renderer_get_size (GtkCellRenderer *cell,
+ GtkWidget *widget,
+ gint *width,
+ gint *height);
+void gtk_cell_renderer_render (GtkCellRenderer *cell,
+ GdkWindow *window,
+ GtkWidget *widget,
+ GdkRectangle *background_area,
+ GdkRectangle *cell_area,
+ GdkRectangle *expose_area,
+ guint flags);
+gint gtk_cell_renderer_event (GtkCellRenderer *cell,
+ GdkEvent *event,
+ GtkWidget *widget,
+ gchar *path,
+ GdkRectangle *background_area,
+ GdkRectangle *cell_area,
+ guint flags);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_CELL_RENDERER_H__ */
diff --git a/gtk/gtkcellrendererpixbuf.c b/gtk/gtkcellrendererpixbuf.c
new file mode 100644
index 0000000000..c37cb5afb6
--- /dev/null
+++ b/gtk/gtkcellrendererpixbuf.c
@@ -0,0 +1,240 @@
+/* gtkcellrendererpixbuf.c
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include "gtkcellrendererpixbuf.h"
+
+#ifndef _
+#define _(x) x
+#endif
+
+
+static void gtk_cell_renderer_pixbuf_get_param (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec,
+ const gchar *trailer);
+static void gtk_cell_renderer_pixbuf_set_param (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec,
+ const gchar *trailer);
+static void gtk_cell_renderer_pixbuf_init (GtkCellRendererPixbuf *celltext);
+static void gtk_cell_renderer_pixbuf_class_init (GtkCellRendererPixbufClass *class);
+static void gtk_cell_renderer_pixbuf_get_size (GtkCellRenderer *cell,
+ GtkWidget *widget,
+ gint *width,
+ gint *height);
+static void gtk_cell_renderer_pixbuf_render (GtkCellRenderer *cell,
+ GdkWindow *window,
+ GtkWidget *widget,
+ GdkRectangle *background_area,
+ GdkRectangle *cell_area,
+ GdkRectangle *expose_area,
+ guint flags);
+
+
+enum {
+ PROP_ZERO,
+ PROP_PIXBUF
+};
+
+
+GtkType
+gtk_cell_renderer_pixbuf_get_type (void)
+{
+ static GtkType cell_pixbuf_type = 0;
+
+ if (!cell_pixbuf_type)
+ {
+ static const GTypeInfo cell_pixbuf_info =
+ {
+ sizeof (GtkCellRendererPixbufClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) gtk_cell_renderer_pixbuf_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GtkCellRendererPixbuf),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gtk_cell_renderer_pixbuf_init,
+ };
+
+ cell_pixbuf_type = g_type_register_static (GTK_TYPE_CELL_RENDERER, "GtkCellRendererPixbuf", &cell_pixbuf_info);
+ }
+
+ return cell_pixbuf_type;
+}
+
+static void
+gtk_cell_renderer_pixbuf_init (GtkCellRendererPixbuf *cellpixbuf)
+{
+}
+
+static void
+gtk_cell_renderer_pixbuf_class_init (GtkCellRendererPixbufClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class);
+
+ object_class->get_param = gtk_cell_renderer_pixbuf_get_param;
+ object_class->set_param = gtk_cell_renderer_pixbuf_set_param;
+
+ cell_class->get_size = gtk_cell_renderer_pixbuf_get_size;
+ cell_class->render = gtk_cell_renderer_pixbuf_render;
+
+ g_object_class_install_param (object_class,
+ PROP_PIXBUF,
+ g_param_spec_object ("pixbuf",
+ _("Pixbuf Object"),
+ _("The pixbuf to render."),
+ GDK_TYPE_PIXBUF,
+ G_PARAM_READABLE |
+ G_PARAM_WRITABLE));
+}
+
+static void
+gtk_cell_renderer_pixbuf_get_param (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec,
+ const gchar *trailer)
+{
+ GtkCellRendererPixbuf *cellpixbuf = GTK_CELL_RENDERER_PIXBUF (object);
+
+ switch (param_id)
+ {
+ case PROP_PIXBUF:
+ g_value_init (value, G_TYPE_OBJECT);
+ g_value_set_object (value, G_OBJECT (cellpixbuf->pixbuf));
+ break;
+ default:
+ break;
+ }
+}
+
+
+static void
+gtk_cell_renderer_pixbuf_set_param (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec,
+ const gchar *trailer)
+{
+ GdkPixbuf *pixbuf;
+ GtkCellRendererPixbuf *cellpixbuf = GTK_CELL_RENDERER_PIXBUF (object);
+
+ switch (param_id)
+ {
+ case PROP_PIXBUF:
+ pixbuf = GDK_PIXBUF (g_value_get_object (value));
+ g_object_ref (G_OBJECT (pixbuf));
+ if (cellpixbuf->pixbuf)
+ g_object_unref (G_OBJECT (cellpixbuf->pixbuf));
+ cellpixbuf->pixbuf = pixbuf;
+ break;
+ default:
+ break;
+ }
+}
+
+GtkCellRenderer *
+gtk_cell_renderer_pixbuf_new (void)
+{
+ return GTK_CELL_RENDERER (gtk_type_new (gtk_cell_renderer_pixbuf_get_type ()));
+}
+
+static void
+gtk_cell_renderer_pixbuf_get_size (GtkCellRenderer *cell,
+ GtkWidget *widget,
+ gint *width,
+ gint *height)
+{
+ GtkCellRendererPixbuf *cellpixbuf = (GtkCellRendererPixbuf *) cell;
+
+ if (width)
+ *width = (gint) GTK_CELL_RENDERER (cellpixbuf)->xpad * 2 +
+ (cellpixbuf->pixbuf ? gdk_pixbuf_get_width (cellpixbuf->pixbuf) : 0);
+
+ if (height)
+ *height = (gint) GTK_CELL_RENDERER (cellpixbuf)->ypad * 2 +
+ (cellpixbuf->pixbuf ? gdk_pixbuf_get_height (cellpixbuf->pixbuf) : 0);
+}
+
+static void
+gtk_cell_renderer_pixbuf_render (GtkCellRenderer *cell,
+ GdkWindow *window,
+ GtkWidget *widget,
+ GdkRectangle *background_area,
+ GdkRectangle *cell_area,
+ GdkRectangle *expose_area,
+ guint flags)
+
+{
+ GtkCellRendererPixbuf *cellpixbuf = (GtkCellRendererPixbuf *) cell;
+ GdkPixbuf *pixbuf;
+ guchar *pixels;
+ gint rowstride;
+ gint real_xoffset;
+ gint real_yoffset;
+ GdkGC *bg_gc = NULL;
+
+ pixbuf = cellpixbuf->pixbuf;
+
+ if (!pixbuf)
+ return;
+
+ if ((flags & GTK_CELL_RENDERER_SELECTED) == GTK_CELL_RENDERER_SELECTED)
+ bg_gc = widget->style->bg_gc [GTK_STATE_SELECTED];
+ else
+ bg_gc = widget->style->base_gc [GTK_STATE_NORMAL];
+
+ gdk_gc_set_clip_rectangle (bg_gc, cell_area);
+
+ rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+ pixels = gdk_pixbuf_get_pixels (pixbuf);
+
+ real_xoffset = GTK_CELL_RENDERER (cellpixbuf)->xalign * (cell_area->width - gdk_pixbuf_get_width (pixbuf) - (2 * GTK_CELL_RENDERER (cellpixbuf)->xpad));
+ real_xoffset = MAX (real_xoffset, 0) + GTK_CELL_RENDERER (cellpixbuf)->xpad;
+ real_yoffset = GTK_CELL_RENDERER (cellpixbuf)->yalign * (cell_area->height - gdk_pixbuf_get_height (pixbuf) - (2 * GTK_CELL_RENDERER (cellpixbuf)->ypad));
+ real_yoffset = MAX (real_yoffset, 0) + GTK_CELL_RENDERER (cellpixbuf)->ypad;
+
+ if (gdk_pixbuf_get_has_alpha (pixbuf))
+ gdk_draw_rgb_32_image (window,
+ bg_gc,
+ cell_area->x + real_xoffset,
+ cell_area->y + real_yoffset,
+ gdk_pixbuf_get_width (pixbuf),
+ gdk_pixbuf_get_height (pixbuf),
+ GDK_RGB_DITHER_NORMAL,
+ pixels,
+ rowstride);
+ else
+ gdk_draw_rgb_image (window,
+ bg_gc,
+ cell_area->x + real_xoffset,
+ cell_area->y + real_yoffset,
+ gdk_pixbuf_get_width (pixbuf),
+ gdk_pixbuf_get_height (pixbuf),
+ GDK_RGB_DITHER_NORMAL,
+ pixels,
+ rowstride);
+
+ gdk_gc_set_clip_rectangle (bg_gc, NULL);
+}
diff --git a/gtk/gtkcellrendererpixbuf.h b/gtk/gtkcellrendererpixbuf.h
new file mode 100644
index 0000000000..14fd9b5799
--- /dev/null
+++ b/gtk/gtkcellrendererpixbuf.h
@@ -0,0 +1,58 @@
+/* gtkcellrendererpixbuf.h
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_CELL_RENDERER_PIXBUF_H__
+#define __GTK_CELL_RENDERER_PIXBUF_H__
+
+#include <gtk/gtkcellrenderer.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+#define GTK_TYPE_CELL_RENDERER_PIXBUF (gtk_cell_renderer_pixbuf_get_type ())
+#define GTK_CELL_RENDERER_PIXBUF(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_CELL_RENDERER_PIXBUF, GtkCellRendererPixbuf))
+#define GTK_CELL_RENDERER_PIXBUF_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_RENDERER_PIXBUF, GtkCellRendererPixbufClass))
+#define GTK_IS_CELL_RENDERER_PIXBUF(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_CELL_RENDERER_PIXBUF))
+#define GTK_IS_CELL_RENDERER_PIXBUF_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), GTK_TYPE_CELL_RENDERER_PIXBUF))
+
+typedef struct _GtkCellRendererPixbuf GtkCellRendererPixbuf;
+typedef struct _GtkCellRendererPixbufClass GtkCellRendererPixbufClass;
+
+struct _GtkCellRendererPixbuf
+{
+ GtkCellRenderer parent;
+
+ GdkPixbuf *pixbuf;
+};
+
+struct _GtkCellRendererPixbufClass
+{
+ GtkCellRendererClass parent_class;
+};
+
+GtkType gtk_cell_renderer_pixbuf_get_type (void);
+GtkCellRenderer *gtk_cell_renderer_pixbuf_new (void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __GTK_CELL_RENDERER_PIXBUF_H__ */
diff --git a/gtk/gtkcellrenderertext.c b/gtk/gtkcellrenderertext.c
new file mode 100644
index 0000000000..9925f03ee9
--- /dev/null
+++ b/gtk/gtkcellrenderertext.c
@@ -0,0 +1,449 @@
+/* gtkcellrenderertext.c
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include "gtkcellrenderertext.h"
+
+#ifndef _
+#define _(x) x
+#endif
+
+static void gtk_cell_renderer_text_init (GtkCellRendererText *celltext);
+static void gtk_cell_renderer_text_class_init (GtkCellRendererTextClass *class);
+
+static void gtk_cell_renderer_text_get_param (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec,
+ const gchar *trailer);
+static void gtk_cell_renderer_text_set_param (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec,
+ const gchar *trailer);
+static void gtk_cell_renderer_text_get_size (GtkCellRenderer *cell,
+ GtkWidget *widget,
+ gint *width,
+ gint *height);
+static void gtk_cell_renderer_text_render (GtkCellRenderer *cell,
+ GdkWindow *window,
+ GtkWidget *widget,
+ GdkRectangle *background_area,
+ GdkRectangle *cell_area,
+ GdkRectangle *expose_area,
+ guint flags);
+
+
+enum {
+ PROP_ZERO,
+ PROP_TEXT,
+ PROP_FONT,
+ PROP_BACKGROUND,
+ PROP_BACKGROUND_GDK,
+ PROP_FOREGROUND,
+ PROP_FOREGROUND_GDK,
+ PROP_STRIKETHROUGH,
+ PROP_UNDERLINE,
+ PROP_EDITABLE,
+ PROP_ITALIC,
+ PROP_BOLD
+};
+
+GtkType
+gtk_cell_renderer_text_get_type (void)
+{
+ static GtkType cell_text_type = 0;
+
+ if (!cell_text_type)
+ {
+ static const GTypeInfo cell_text_info =
+ {
+ sizeof (GtkCellRendererTextClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) gtk_cell_renderer_text_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GtkCellRendererText),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gtk_cell_renderer_text_init,
+ };
+
+ cell_text_type = g_type_register_static (GTK_TYPE_CELL_RENDERER, "GtkCellRendererText", &cell_text_info);
+ }
+
+ return cell_text_type;
+}
+
+static void
+gtk_cell_renderer_text_init (GtkCellRendererText *celltext)
+{
+ celltext->attr_list = pango_attr_list_new ();
+ GTK_CELL_RENDERER (celltext)->xalign = 0.0;
+ GTK_CELL_RENDERER (celltext)->yalign = 0.5;
+ GTK_CELL_RENDERER (celltext)->xpad = 2;
+ GTK_CELL_RENDERER (celltext)->ypad = 2;
+ celltext->underline = FALSE;
+}
+
+static void
+gtk_cell_renderer_text_class_init (GtkCellRendererTextClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class);
+
+ object_class->get_param = gtk_cell_renderer_text_get_param;
+ object_class->set_param = gtk_cell_renderer_text_set_param;
+
+ cell_class->get_size = gtk_cell_renderer_text_get_size;
+ cell_class->render = gtk_cell_renderer_text_render;
+
+ g_object_class_install_param (object_class,
+ PROP_TEXT,
+ g_param_spec_string ("text",
+ _("Text String"),
+ _("The text of the renderer."),
+ "",
+ G_PARAM_READABLE |
+ G_PARAM_WRITABLE));
+
+ g_object_class_install_param (object_class,
+ PROP_FONT,
+ g_param_spec_string ("font",
+ _("Font String"),
+ _("The string of the font."),
+ "",
+ G_PARAM_WRITABLE));
+
+ g_object_class_install_param (object_class,
+ PROP_BACKGROUND,
+ g_param_spec_string ("background",
+ _("Background Color string"),
+ _("The color for the background of the text."),
+ "white",
+ G_PARAM_WRITABLE));
+
+ g_object_class_install_param (object_class,
+ PROP_FOREGROUND,
+ g_param_spec_string ("foreground",
+ _("Foreground Color string"),
+ _("The color for the background of the text."),
+ "black",
+ G_PARAM_WRITABLE));
+
+ g_object_class_install_param (object_class,
+ PROP_STRIKETHROUGH,
+ g_param_spec_boolean ("strikethrough",
+ _("Strikethrough"),
+ _("Draw a line through the text."),
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_WRITABLE));
+
+ g_object_class_install_param (object_class,
+ PROP_UNDERLINE,
+ g_param_spec_boolean ("underline",
+ _("Underline"),
+ _("Underline the text."),
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_WRITABLE));
+
+ g_object_class_install_param (object_class,
+ PROP_EDITABLE,
+ g_param_spec_boolean ("editable",
+ _("Editable"),
+ _("Make the text editable."),
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_WRITABLE));
+
+ g_object_class_install_param (object_class,
+ PROP_ITALIC,
+ g_param_spec_boolean ("italic",
+ _("Italic"),
+ _("Make the text italic."),
+ FALSE,
+ G_PARAM_WRITABLE));
+
+ g_object_class_install_param (object_class,
+ PROP_BOLD,
+ g_param_spec_boolean ("bold",
+ _("Bold"),
+ _("Make the text bold."),
+ FALSE,
+ G_PARAM_WRITABLE));
+}
+
+static void
+gtk_cell_renderer_text_get_param (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec,
+ const gchar *trailer)
+{
+ GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (object);
+ PangoAttrIterator *attr_iter;
+ PangoAttribute *attr;
+
+ switch (param_id)
+ {
+ case PROP_TEXT:
+ g_value_init (value, G_TYPE_STRING);
+ g_value_set_string (value, celltext->text);
+ break;
+ case PROP_STRIKETHROUGH:
+ g_value_init (value, G_TYPE_BOOLEAN);
+ attr_iter = pango_attr_list_get_iterator (celltext->attr_list);
+ attr = pango_attr_iterator_get (attr_iter,
+ PANGO_ATTR_STRIKETHROUGH);
+ g_value_set_boolean (value, ((PangoAttrInt*) attr)->value);
+ break;
+ case PROP_UNDERLINE:
+ g_value_init (value, G_TYPE_BOOLEAN);
+ g_value_set_boolean (value, celltext->underline);
+ break;
+ case PROP_EDITABLE:
+ g_value_init (value, G_TYPE_BOOLEAN);
+ g_value_set_boolean (value, celltext->editable);
+ break;
+ default:
+ break;
+ }
+}
+
+
+static void
+gtk_cell_renderer_text_set_param (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec,
+ const gchar *trailer)
+{
+ GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (object);
+
+ GdkColor color;
+ PangoFontDescription *font_desc;
+ gchar *string;
+ PangoAttribute *attribute;
+ gchar *font;
+
+ switch (param_id)
+ {
+ case PROP_TEXT:
+ g_free (celltext->text);
+ celltext->text = g_value_dup_string (value);
+ break;
+ case PROP_FONT:
+ font = g_value_get_string (value);
+
+ if (font)
+ {
+ font_desc = pango_font_description_from_string (font);
+ attribute = pango_attr_font_desc_new (font_desc);
+ attribute->start_index = 0;
+ attribute->end_index = G_MAXINT;
+ pango_font_description_free (font_desc);
+ pango_attr_list_change (celltext->attr_list,
+ attribute);
+ }
+ break;
+ case PROP_BACKGROUND:
+ string = g_value_get_string (value);
+ if (string && gdk_color_parse (string, &color))
+ {
+ attribute = pango_attr_background_new (color.red,
+ color.green,
+ color.blue);
+ attribute->start_index = 0;
+ attribute->end_index = G_MAXINT;
+ pango_attr_list_change (celltext->attr_list,
+ attribute);
+ }
+ break;
+ case PROP_BACKGROUND_GDK:
+ break;
+ case PROP_FOREGROUND:
+ string = g_value_get_string (value);
+ if (string && gdk_color_parse (string, &color))
+ {
+ attribute = pango_attr_foreground_new (color.red,
+ color.green,
+ color.blue);
+ attribute->start_index = 0;
+ attribute->end_index = G_MAXINT;
+ pango_attr_list_change (celltext->attr_list,
+ attribute);
+ }
+ break;
+ case PROP_FOREGROUND_GDK:
+ break;
+ case PROP_STRIKETHROUGH:
+ attribute = pango_attr_strikethrough_new (g_value_get_boolean (value));
+ attribute->start_index = 0;
+ attribute->end_index = G_MAXINT;
+ pango_attr_list_change (celltext->attr_list,
+ attribute);
+ break;
+ case PROP_UNDERLINE:
+ celltext->underline = g_value_get_boolean (value);
+ attribute = pango_attr_underline_new (celltext->underline ?
+ PANGO_UNDERLINE_SINGLE :
+ PANGO_UNDERLINE_NONE);
+ attribute->start_index = 0;
+ attribute->end_index = G_MAXINT;
+ pango_attr_list_change (celltext->attr_list,
+ attribute);
+ break;
+ case PROP_EDITABLE:
+ break;
+ case PROP_ITALIC:
+ attribute = pango_attr_style_new (g_value_get_boolean (value) ?
+ PANGO_STYLE_ITALIC :
+ PANGO_STYLE_NORMAL);
+ attribute->start_index = 0;
+ attribute->end_index = G_MAXINT;
+ pango_attr_list_change (celltext->attr_list,
+ attribute);
+ break;
+ case PROP_BOLD:
+ attribute = pango_attr_weight_new (g_value_get_boolean (value) ?
+ PANGO_WEIGHT_BOLD :
+ PANGO_WEIGHT_NORMAL);
+ attribute->start_index = 0;
+ attribute->end_index = G_MAXINT;
+ pango_attr_list_change (celltext->attr_list,
+ attribute);
+ break;
+ default:
+ break;
+ }
+}
+
+GtkCellRenderer *
+gtk_cell_renderer_text_new (void)
+{
+ return GTK_CELL_RENDERER (gtk_type_new (gtk_cell_renderer_text_get_type ()));
+}
+
+static void
+gtk_cell_renderer_text_get_size (GtkCellRenderer *cell,
+ GtkWidget *widget,
+ gint *width,
+ gint *height)
+{
+ GtkCellRendererText *celltext = (GtkCellRendererText *)cell;
+ PangoRectangle rect;
+ PangoLayout *layout;
+ PangoAttribute *attr;
+ PangoUnderline underline;
+
+ layout = gtk_widget_create_pango_layout (widget, celltext->text);
+ underline = celltext->underline ?
+ PANGO_UNDERLINE_DOUBLE :
+ PANGO_UNDERLINE_NONE;
+
+ attr = pango_attr_underline_new (underline);
+ attr->start_index = 0;
+ attr->end_index = G_MAXINT;
+
+ pango_attr_list_change (celltext->attr_list, attr);
+ pango_layout_set_attributes (layout, celltext->attr_list);
+ pango_layout_set_width (layout, -1);
+ pango_layout_get_pixel_extents (layout, NULL, &rect);
+
+ if (width)
+ *width = GTK_CELL_RENDERER (celltext)->xpad * 2 + rect.width;
+
+ if (height)
+ *height = GTK_CELL_RENDERER (celltext)->ypad * 2 + rect.height;
+
+ g_object_unref (G_OBJECT (layout));
+}
+
+static void
+gtk_cell_renderer_text_render (GtkCellRenderer *cell,
+ GdkWindow *window,
+ GtkWidget *widget,
+ GdkRectangle *background_area,
+ GdkRectangle *cell_area,
+ GdkRectangle *expose_area,
+ guint flags)
+
+{
+ GtkCellRendererText *celltext = (GtkCellRendererText *) cell;
+ PangoRectangle rect;
+ PangoLayout *layout;
+ PangoAttribute *attr;
+ PangoUnderline underline;
+
+ gint real_xoffset;
+ gint real_yoffset;
+
+ GdkGC *font_gc = NULL;
+
+ if ((flags & GTK_CELL_RENDERER_SELECTED) == GTK_CELL_RENDERER_SELECTED)
+ font_gc = widget->style->fg_gc [GTK_STATE_SELECTED];
+ else
+ font_gc = widget->style->fg_gc [GTK_STATE_NORMAL];
+
+ layout = gtk_widget_create_pango_layout (widget, celltext->text);
+
+ if (celltext->underline)
+ underline = PANGO_UNDERLINE_SINGLE;
+ else
+ underline = PANGO_UNDERLINE_NONE;
+
+ attr = pango_attr_underline_new (underline);
+ attr->start_index = 0;
+ attr->end_index = G_MAXINT;
+ pango_attr_list_change (celltext->attr_list, attr);
+ pango_layout_set_attributes (layout, celltext->attr_list);
+
+ pango_layout_set_width (layout, -1);
+ pango_layout_get_pixel_extents (layout, NULL, &rect);
+
+ if ((flags & GTK_CELL_RENDERER_PRELIT) == GTK_CELL_RENDERER_PRELIT)
+ underline = (celltext->underline) ? PANGO_UNDERLINE_DOUBLE:PANGO_UNDERLINE_SINGLE;
+ else
+ underline = (celltext->underline) ? PANGO_UNDERLINE_SINGLE:PANGO_UNDERLINE_NONE;
+
+ attr = pango_attr_underline_new (underline);
+ attr->start_index = 0;
+ attr->end_index = G_MAXINT;
+ pango_attr_list_change (celltext->attr_list, attr);
+ pango_layout_set_attributes (layout, celltext->attr_list);
+
+ gdk_gc_set_clip_rectangle (font_gc, cell_area);
+
+ real_xoffset = cell->xalign * (cell_area->width - rect.width - (2 * cell->xpad));
+ real_xoffset = MAX (real_xoffset, 0) + cell->xpad;
+ real_yoffset = cell->yalign * (cell_area->height - rect.height - (2 * cell->ypad));
+ real_yoffset = MAX (real_yoffset, 0) + cell->ypad;
+
+ gdk_draw_layout (window,
+ font_gc,
+ cell_area->x + real_xoffset,
+ cell_area->y + real_yoffset,
+ layout);
+
+ g_object_unref (G_OBJECT (layout));
+
+ gdk_gc_set_clip_rectangle (font_gc, NULL);
+}
diff --git a/gtk/gtkcellrenderertext.h b/gtk/gtkcellrenderertext.h
new file mode 100644
index 0000000000..762705413c
--- /dev/null
+++ b/gtk/gtkcellrenderertext.h
@@ -0,0 +1,65 @@
+/* gtkcellrenderertext.h
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_CELL_RENDERER_TEXT_H__
+#define __GTK_CELL_RENDERER_TEXT_H__
+
+#include <pango/pango.h>
+#include <gtk/gtkcellrenderer.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+#define GTK_TYPE_CELL_RENDERER_TEXT (gtk_cell_renderer_text_get_type ())
+#define GTK_CELL_RENDERER_TEXT(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_CELL_RENDERER_TEXT, GtkCellRendererText))
+#define GTK_CELL_RENDERER_TEXT_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_RENDERER_TEXT, GtkCellRendererTextClass))
+#define GTK_IS_CELL_RENDERER_TEXT(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_CELL_RENDERER_TEXT))
+#define GTK_IS_CELL_RENDERER_TEXT_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), GTK_TYPE_CELL_RENDERER_TEXT))
+
+typedef struct _GtkCellRendererText GtkCellRendererText;
+typedef struct _GtkCellRendererTextClass GtkCellRendererTextClass;
+
+struct _GtkCellRendererText
+{
+ GtkCellRenderer parent;
+
+ /*< private >*/
+ gchar *text;
+ PangoAttrList *attr_list;
+
+ guint editable : 1;
+ guint underline : 1;
+};
+
+struct _GtkCellRendererTextClass
+{
+ GtkCellRendererClass parent_class;
+};
+
+GtkType gtk_cell_renderer_text_get_type (void);
+GtkCellRenderer *gtk_cell_renderer_text_new (void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_CELL_RENDERER_TEXT_H__ */
diff --git a/gtk/gtkcellrenderertextpixbuf.c b/gtk/gtkcellrenderertextpixbuf.c
new file mode 100644
index 0000000000..3929a13335
--- /dev/null
+++ b/gtk/gtkcellrenderertextpixbuf.c
@@ -0,0 +1,399 @@
+/* gtkcellrenderertextpixbuf.c
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include "gtkcellrenderertextpixbuf.h"
+
+#ifndef _
+#define _(x) x
+#endif
+
+enum {
+ PROP_ZERO,
+ PROP_PIXBUF_POS,
+ PROP_PIXBUF,
+ PROP_PIXBUF_XALIGN,
+ PROP_PIXBUF_YALIGN,
+ PROP_PIXBUF_XPAD,
+ PROP_PIXBUF_YPAD
+};
+
+
+static void gtk_cell_renderer_text_pixbuf_get_param (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec,
+ const gchar *trailer);
+static void gtk_cell_renderer_text_pixbuf_set_param (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec,
+ const gchar *trailer);
+static void gtk_cell_renderer_text_pixbuf_init (GtkCellRendererTextPixbuf *celltextpixbuf);
+static void gtk_cell_renderer_text_pixbuf_class_init (GtkCellRendererTextPixbufClass *class);
+static void gtk_cell_renderer_text_pixbuf_get_size (GtkCellRenderer *cell,
+ GtkWidget *view,
+ gint *width,
+ gint *height);
+static void gtk_cell_renderer_text_pixbuf_render (GtkCellRenderer *cell,
+ GdkWindow *window,
+ GtkWidget *view,
+ GdkRectangle *background_area,
+ GdkRectangle *cell_area,
+ GdkRectangle *expose_area,
+ guint flags);
+
+
+GtkCellRendererTextClass *parent_class = NULL;
+
+
+GtkType
+gtk_cell_renderer_text_pixbuf_get_type (void)
+{
+ static GtkType cell_text_pixbuf_type = 0;
+
+ if (!cell_text_pixbuf_type)
+ {
+ static const GTypeInfo cell_text_pixbuf_info =
+ {
+ sizeof (GtkCellRendererTextPixbufClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) gtk_cell_renderer_text_pixbuf_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GtkCellRendererTextPixbuf),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gtk_cell_renderer_text_pixbuf_init,
+ };
+
+ cell_text_pixbuf_type = g_type_register_static (GTK_TYPE_CELL_RENDERER_TEXT, "GtkCellRendererTextPixbuf", &cell_text_pixbuf_info);
+ }
+
+ return cell_text_pixbuf_type;
+}
+
+static void
+gtk_cell_renderer_text_pixbuf_init (GtkCellRendererTextPixbuf *celltextpixbuf)
+{
+ celltextpixbuf->pixbuf = GTK_CELL_RENDERER_PIXBUF (gtk_cell_renderer_pixbuf_new ());
+ celltextpixbuf->pixbuf_pos = GTK_POS_LEFT;
+}
+
+static void
+gtk_cell_renderer_text_pixbuf_class_init (GtkCellRendererTextPixbufClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class);
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class->get_param = gtk_cell_renderer_text_pixbuf_get_param;
+ object_class->set_param = gtk_cell_renderer_text_pixbuf_set_param;
+
+ cell_class->get_size = gtk_cell_renderer_text_pixbuf_get_size;
+ cell_class->render = gtk_cell_renderer_text_pixbuf_render;
+
+ g_object_class_install_param (object_class,
+ PROP_PIXBUF_POS,
+ g_param_spec_int ("pixbufpos",
+ _("Pixbuf location"),
+ _("The relative location of the pixbuf to the text."),
+ GTK_POS_LEFT,
+ GTK_POS_BOTTOM,
+ GTK_POS_LEFT,
+ G_PARAM_READABLE |
+ G_PARAM_WRITABLE));
+
+ g_object_class_install_param (object_class,
+ PROP_PIXBUF,
+ g_param_spec_object ("pixbuf",
+ _("Pixbuf Object"),
+ _("The pixbuf to render."),
+ GDK_TYPE_PIXBUF,
+ G_PARAM_READABLE |
+ G_PARAM_WRITABLE));
+
+ g_object_class_install_param (object_class,
+ PROP_PIXBUF_XALIGN,
+ g_param_spec_float ("pixbuf xalign",
+ _("pixbuf xalign"),
+ _("The x-align of the pixbuf."),
+ 0.0,
+ 1.0,
+ 0.0,
+ G_PARAM_READABLE |
+ G_PARAM_WRITABLE));
+
+ g_object_class_install_param (object_class,
+ PROP_PIXBUF_YALIGN,
+ g_param_spec_float ("pixbuf yalign",
+ _("pixbuf yalign"),
+ _("The y-align of the pixbuf."),
+ 0.0,
+ 1.0,
+ 0.5,
+ G_PARAM_READABLE |
+ G_PARAM_WRITABLE));
+
+ g_object_class_install_param (object_class,
+ PROP_PIXBUF_XPAD,
+ g_param_spec_uint ("pixbuf xpad",
+ _("pixbuf xpad"),
+ _("The xpad of the pixbuf."),
+ 0,
+ 100,
+ 2,
+ G_PARAM_READABLE |
+ G_PARAM_WRITABLE));
+
+ g_object_class_install_param (object_class,
+ PROP_PIXBUF_YPAD,
+ g_param_spec_uint ("pixbuf ypad",
+ _("pixbuf ypad"),
+ _("The ypad of the pixbuf."),
+ 0,
+ 100,
+ 2,
+ G_PARAM_READABLE |
+ G_PARAM_WRITABLE));
+}
+
+static void
+gtk_cell_renderer_text_pixbuf_get_param (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec,
+ const gchar *trailer)
+{
+ GtkCellRendererTextPixbuf *celltextpixbuf = GTK_CELL_RENDERER_TEXT_PIXBUF (object);
+
+ switch (param_id)
+ {
+ case PROP_PIXBUF_POS:
+ g_value_set_int (value, celltextpixbuf->pixbuf_pos);
+ break;
+ case PROP_PIXBUF:
+ g_object_get_param (G_OBJECT (celltextpixbuf->pixbuf),
+ "pixbuf",
+ value);
+ break;
+ case PROP_PIXBUF_XALIGN:
+ g_object_get_param (G_OBJECT (celltextpixbuf->pixbuf),
+ "xalign",
+ value);
+ break;
+ case PROP_PIXBUF_YALIGN:
+ g_object_get_param (G_OBJECT (celltextpixbuf->pixbuf),
+ "yalign",
+ value);
+ break;
+ case PROP_PIXBUF_XPAD:
+ g_object_get_param (G_OBJECT (celltextpixbuf->pixbuf),
+ "xpad",
+ value);
+ break;
+ case PROP_PIXBUF_YPAD:
+ g_object_get_param (G_OBJECT (celltextpixbuf->pixbuf),
+ "ypad",
+ value);
+ break;
+ default:
+ break;
+ }
+}
+
+
+static void
+gtk_cell_renderer_text_pixbuf_set_param (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec,
+ const gchar *trailer)
+{
+ GtkCellRendererTextPixbuf *celltextpixbuf = GTK_CELL_RENDERER_TEXT_PIXBUF (object);
+
+ switch (param_id)
+ {
+ case PROP_PIXBUF:
+ g_object_set_param (G_OBJECT (celltextpixbuf->pixbuf),
+ "pixbuf",
+ value);
+ break;
+ case PROP_PIXBUF_POS:
+ celltextpixbuf->pixbuf_pos = g_value_get_int (value);
+ break;
+ case PROP_PIXBUF_XALIGN:
+ g_object_set_param (G_OBJECT (celltextpixbuf->pixbuf),
+ "xalign",
+ value);
+ break;
+ case PROP_PIXBUF_YALIGN:
+ g_object_set_param (G_OBJECT (celltextpixbuf->pixbuf),
+ "yalign",
+ value);
+ break;
+ case PROP_PIXBUF_XPAD:
+ g_object_set_param (G_OBJECT (celltextpixbuf->pixbuf),
+ "xpad",
+ value);
+ break;
+ case PROP_PIXBUF_YPAD:
+ g_object_set_param (G_OBJECT (celltextpixbuf->pixbuf),
+ "ypad",
+ value);
+ break;
+ default:
+ break;
+ }
+}
+
+GtkCellRenderer *
+gtk_cell_renderer_text_pixbuf_new (void)
+{
+ return GTK_CELL_RENDERER (gtk_type_new (gtk_cell_renderer_text_pixbuf_get_type ()));
+}
+
+typedef void (* CellSizeFunc) (GtkCellRenderer *cell,
+ GtkWidget *widget,
+ gint *width,
+ gint *height);
+typedef void (* CellRenderFunc) (GtkCellRenderer *cell,
+ GdkWindow *window,
+ GtkWidget *widget,
+ GdkRectangle *background_area,
+ GdkRectangle *cell_area,
+ GdkRectangle *expose_area,
+ guint flags);
+
+static void
+gtk_cell_renderer_text_pixbuf_get_size (GtkCellRenderer *cell,
+ GtkWidget *widget,
+ gint *width,
+ gint *height)
+{
+ GtkCellRendererTextPixbuf *celltextpixbuf = (GtkCellRendererTextPixbuf *)cell;
+ gint pixbuf_width;
+ gint pixbuf_height;
+ gint text_width;
+ gint text_height;
+
+ (* GTK_CELL_RENDERER_CLASS (parent_class)->get_size) (cell, widget, &text_width, &text_height);
+ (* GTK_CELL_RENDERER_CLASS (G_OBJECT_GET_CLASS (celltextpixbuf->pixbuf))->get_size) (GTK_CELL_RENDERER (celltextpixbuf->pixbuf),
+ widget,
+ &pixbuf_width,
+ &pixbuf_height);
+ if (celltextpixbuf->pixbuf_pos == GTK_POS_LEFT ||
+ celltextpixbuf->pixbuf_pos == GTK_POS_RIGHT)
+ {
+ *width = pixbuf_width + text_width;
+ *height = MAX (pixbuf_height, text_height);
+ }
+ else
+ {
+ *width = MAX (pixbuf_width, text_width);
+ *height = pixbuf_height + text_height;
+ }
+}
+
+static void
+gtk_cell_renderer_text_pixbuf_render (GtkCellRenderer *cell,
+ GdkWindow *window,
+ GtkWidget *widget,
+ GdkRectangle *background_area,
+ GdkRectangle *cell_area,
+ GdkRectangle *expose_area,
+ guint flags)
+
+{
+ GtkCellRendererTextPixbuf *celltextpixbuf = (GtkCellRendererTextPixbuf *) cell;
+ CellSizeFunc size_func1, size_func2;
+ CellRenderFunc render_func1, render_func2;
+ GtkCellRenderer *cell1, *cell2;
+ gint tmp_width;
+ gint tmp_height;
+ GdkRectangle real_cell_area;
+
+ if (celltextpixbuf->pixbuf_pos == GTK_POS_LEFT ||
+ celltextpixbuf->pixbuf_pos == GTK_POS_TOP)
+ {
+ size_func1 = GTK_CELL_RENDERER_CLASS (G_OBJECT_GET_CLASS (celltextpixbuf->pixbuf))->get_size;
+ render_func1 = GTK_CELL_RENDERER_CLASS (G_OBJECT_GET_CLASS (celltextpixbuf->pixbuf))->render;
+ cell1 = GTK_CELL_RENDERER (celltextpixbuf->pixbuf);
+
+ size_func2 = GTK_CELL_RENDERER_CLASS (parent_class)->get_size;
+ render_func2 = GTK_CELL_RENDERER_CLASS (parent_class)->render;
+ cell2 = cell;
+ }
+ else
+ {
+ size_func1 = GTK_CELL_RENDERER_CLASS (parent_class)->get_size;
+ render_func1 = GTK_CELL_RENDERER_CLASS (parent_class)->render;
+ cell1 = cell;
+
+ size_func2 = GTK_CELL_RENDERER_CLASS (G_OBJECT_GET_CLASS (celltextpixbuf->pixbuf))->get_size;
+ render_func2 = GTK_CELL_RENDERER_CLASS (G_OBJECT_GET_CLASS (celltextpixbuf->pixbuf))->render;
+ cell2 = GTK_CELL_RENDERER (celltextpixbuf->pixbuf);
+ }
+
+ (size_func1) (cell1, widget, &tmp_width, &tmp_height);
+
+ real_cell_area.x = cell_area->x;
+ real_cell_area.y = cell_area->y;
+
+ if (celltextpixbuf->pixbuf_pos == GTK_POS_LEFT ||
+ celltextpixbuf->pixbuf_pos == GTK_POS_RIGHT)
+ {
+ real_cell_area.width = MIN (tmp_width, cell_area->width);
+ real_cell_area.height = cell_area->height;
+ }
+ else
+ {
+ real_cell_area.height = MIN (tmp_height, cell_area->height);
+ real_cell_area.width = cell_area->width;
+ }
+
+ (render_func1) (cell1,
+ window,
+ widget,
+ background_area,
+ &real_cell_area,
+ expose_area,
+ flags);
+
+ if (celltextpixbuf->pixbuf_pos == GTK_POS_LEFT ||
+ celltextpixbuf->pixbuf_pos == GTK_POS_RIGHT)
+ {
+ real_cell_area.x = real_cell_area.x + real_cell_area.width;
+ real_cell_area.width = cell_area->width - real_cell_area.width;
+ }
+ else
+ {
+ real_cell_area.y = real_cell_area.y + real_cell_area.height;
+ real_cell_area.height = cell_area->height - real_cell_area.height;
+ }
+
+ (render_func2 ) (cell2,
+ window,
+ widget,
+ background_area,
+ &real_cell_area,
+ expose_area,
+ flags);
+}
diff --git a/gtk/gtkcellrenderertextpixbuf.h b/gtk/gtkcellrenderertextpixbuf.h
new file mode 100644
index 0000000000..fa372b4475
--- /dev/null
+++ b/gtk/gtkcellrenderertextpixbuf.h
@@ -0,0 +1,61 @@
+/* gtkcellrenderertextpixbuf.h
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_CELL_RENDERER_TEXT_PIXBUF_H__
+#define __GTK_CELL_RENDERER_TEXT_PIXBUF_H__
+
+#include <gtk/gtkcellrenderertext.h>
+#include <gtk/gtkcellrendererpixbuf.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define GTK_TYPE_CELL_RENDERER_TEXT_PIXBUF (gtk_cell_renderer_text_pixbuf_get_type ())
+#define GTK_CELL_RENDERER_TEXT_PIXBUF(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_CELL_RENDERER_TEXT_PIXBUF, GtkCellRendererTextPixbuf))
+#define GTK_CELL_RENDERER_TEXT_PIXBUF_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_RENDERER_TEXT_PIXBUF, GtkCellRendererTextPixbufClass))
+#define GTK_IS_CELL_RENDERER_TEXT_PIXBUF(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_CELL_RENDERER_TEXT_PIXBUF))
+#define GTK_IS_CELL_RENDERER_TEXT_PIXBUF_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), GTK_TYPE_CELL_RENDERER_TEXT_PIXBUF))
+
+typedef struct _GtkCellRendererTextPixbuf GtkCellRendererTextPixbuf;
+typedef struct _GtkCellRendererTextPixbufClass GtkCellRendererTextPixbufClass;
+
+struct _GtkCellRendererTextPixbuf
+{
+ GtkCellRendererText parent;
+
+ /*< private >*/
+ GtkCellRendererPixbuf *pixbuf;
+ GtkPositionType pixbuf_pos;
+};
+
+struct _GtkCellRendererTextPixbufClass
+{
+ GtkCellRendererTextClass parent_class;
+};
+
+GtkType gtk_cell_renderer_text_pixbuf_get_type (void);
+GtkCellRenderer *gtk_cell_renderer_text_pixbuf_new (void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_CELL_RENDERER_TEXT_PIXBUF_H__ */
diff --git a/gtk/gtkcellrenderertoggle.c b/gtk/gtkcellrenderertoggle.c
new file mode 100644
index 0000000000..6830604b4e
--- /dev/null
+++ b/gtk/gtkcellrenderertoggle.c
@@ -0,0 +1,321 @@
+/* gtkcellrenderertoggle.c
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <gtk/gtkcellrenderertoggle.h>
+#include <gtk/gtksignal.h>
+
+#ifndef _
+#define _(x) x
+#endif
+
+
+static void gtk_cell_renderer_toggle_get_param (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec,
+ const gchar *trailer);
+static void gtk_cell_renderer_toggle_set_param (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec,
+ const gchar *trailer);
+static void gtk_cell_renderer_toggle_init (GtkCellRendererToggle *celltext);
+static void gtk_cell_renderer_toggle_class_init (GtkCellRendererToggleClass *class);
+static void gtk_cell_renderer_toggle_get_size (GtkCellRenderer *cell,
+ GtkWidget *widget,
+ gint *width,
+ gint *height);
+static void gtk_cell_renderer_toggle_render (GtkCellRenderer *cell,
+ GdkWindow *window,
+ GtkWidget *widget,
+ GdkRectangle *background_area,
+ GdkRectangle *cell_area,
+ GdkRectangle *expose_area,
+ guint flags);
+static gint gtk_cell_renderer_toggle_event (GtkCellRenderer *cell,
+ GdkEvent *event,
+ GtkWidget *widget,
+ gchar *path,
+ GdkRectangle *background_area,
+ GdkRectangle *cell_area,
+ guint flags);
+
+
+enum {
+ TOGGLED,
+ LAST_SIGNAL
+};
+
+enum {
+ PROP_ZERO,
+ PROP_STATE,
+ PROP_RADIO
+};
+
+
+#define TOGGLE_WIDTH 12
+
+static guint toggle_cell_signals[LAST_SIGNAL] = { 0 };
+
+
+GtkType
+gtk_cell_renderer_toggle_get_type (void)
+{
+ static GtkType cell_toggle_type = 0;
+
+ if (!cell_toggle_type)
+ {
+ static const GTypeInfo cell_toggle_info =
+ {
+ sizeof (GtkCellRendererToggleClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) gtk_cell_renderer_toggle_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GtkCellRendererToggle),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gtk_cell_renderer_toggle_init,
+ };
+
+ cell_toggle_type = g_type_register_static (GTK_TYPE_CELL_RENDERER, "GtkCellRendererToggle", &cell_toggle_info);
+ }
+
+ return cell_toggle_type;
+}
+
+static void
+gtk_cell_renderer_toggle_init (GtkCellRendererToggle *celltoggle)
+{
+ celltoggle->state = FALSE;
+ celltoggle->radio = FALSE;
+ GTK_CELL_RENDERER (celltoggle)->xpad = 2;
+ GTK_CELL_RENDERER (celltoggle)->ypad = 2;
+}
+
+static void
+gtk_cell_renderer_toggle_class_init (GtkCellRendererToggleClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class);
+
+ object_class->get_param = gtk_cell_renderer_toggle_get_param;
+ object_class->set_param = gtk_cell_renderer_toggle_set_param;
+
+ cell_class->get_size = gtk_cell_renderer_toggle_get_size;
+ cell_class->render = gtk_cell_renderer_toggle_render;
+ cell_class->event = gtk_cell_renderer_toggle_event;
+
+ g_object_class_install_param (object_class,
+ PROP_STATE,
+ g_param_spec_boolean ("state",
+ _("Toggle state"),
+ _("The toggle-state of the button."),
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_WRITABLE));
+
+ g_object_class_install_param (object_class,
+ PROP_RADIO,
+ g_param_spec_boolean ("radio",
+ _("Radio state"),
+ _("Draw the toggle button as a radio button."),
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_WRITABLE));
+
+
+ toggle_cell_signals[TOGGLED] =
+ gtk_signal_new ("toggled",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkCellRendererToggleClass, toggled),
+ gtk_marshal_NONE__POINTER,
+ GTK_TYPE_NONE, 1,
+ GTK_TYPE_POINTER);
+
+ gtk_object_class_add_signals (GTK_OBJECT_CLASS (object_class), toggle_cell_signals, LAST_SIGNAL);
+}
+
+static void
+gtk_cell_renderer_toggle_get_param (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec,
+ const gchar *trailer)
+{
+ GtkCellRendererToggle *celltoggle = GTK_CELL_RENDERER_TOGGLE (object);
+
+ switch (param_id)
+ {
+ case PROP_STATE:
+ g_value_init (value, G_TYPE_BOOLEAN);
+ g_value_set_boolean (value, celltoggle->state);
+ break;
+ case PROP_RADIO:
+ g_value_init (value, G_TYPE_BOOLEAN);
+ g_value_set_boolean (value, celltoggle->radio);
+ break;
+ default:
+ break;
+ }
+}
+
+
+static void
+gtk_cell_renderer_toggle_set_param (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec,
+ const gchar *trailer)
+{
+ GtkCellRendererToggle *celltoggle = GTK_CELL_RENDERER_TOGGLE (object);
+
+ switch (param_id)
+ {
+ case PROP_STATE:
+ celltoggle->state = g_value_get_boolean (value);
+ break;
+ case PROP_RADIO:
+ celltoggle->radio = g_value_get_boolean (value);
+ break;
+ default:
+ break;
+ }
+}
+
+GtkCellRenderer *
+gtk_cell_renderer_toggle_new (void)
+{
+ return GTK_CELL_RENDERER (gtk_type_new (gtk_cell_renderer_toggle_get_type ()));
+}
+
+static void
+gtk_cell_renderer_toggle_get_size (GtkCellRenderer *cell,
+ GtkWidget *widget,
+ gint *width,
+ gint *height)
+{
+ if (width)
+ *width = (gint) cell->xpad * 2 + TOGGLE_WIDTH;
+
+ if (height)
+ *height = (gint) cell->ypad * 2 + TOGGLE_WIDTH;
+}
+
+static void
+gtk_cell_renderer_toggle_render (GtkCellRenderer *cell,
+ GdkWindow *window,
+ GtkWidget *widget,
+ GdkRectangle *background_area,
+ GdkRectangle *cell_area,
+ GdkRectangle *expose_area,
+ guint flags)
+{
+ GtkCellRendererToggle *celltoggle = (GtkCellRendererToggle *) cell;
+ gint width, height;
+ gint real_xoffset, real_yoffset;
+
+ width = MIN (TOGGLE_WIDTH, cell_area->width - cell->xpad * 2);
+ height = MIN (TOGGLE_WIDTH, cell_area->height - cell->ypad * 2);
+
+ if (width <= 0 || height <= 0)
+ return;
+
+ real_xoffset = cell->xalign * (cell_area->width - width - (2 * cell->xpad));
+ real_xoffset = MAX (real_xoffset, 0) + cell->xpad;
+ real_yoffset = cell->yalign * (cell_area->height - height - (2 * cell->ypad));
+ real_yoffset = MAX (real_yoffset, 0) + cell->ypad;
+
+ gdk_gc_set_clip_rectangle (widget->style->black_gc, cell_area);
+
+ if (!celltoggle->radio)
+ {
+ gdk_draw_rectangle (window,
+ widget->style->black_gc,
+ FALSE,
+ cell_area->x + real_xoffset,
+ cell_area->y + real_yoffset,
+ width, height);
+ if (celltoggle->state)
+ {
+ gdk_draw_line (window,
+ widget->style->black_gc,
+ cell_area->x + real_xoffset,
+ cell_area->y + real_yoffset,
+ cell_area->x + real_xoffset + width,
+ cell_area->y + real_yoffset + height);
+ gdk_draw_line (window,
+ widget->style->black_gc,
+ cell_area->x + real_xoffset + width,
+ cell_area->y + real_yoffset,
+ cell_area->x + real_xoffset,
+ cell_area->y + real_yoffset + height);
+ }
+ }
+ else
+ {
+ gdk_draw_arc (window,
+ widget->style->black_gc,
+ FALSE,
+ cell_area->x + real_xoffset,
+ cell_area->y + real_yoffset,
+ width,
+ height,
+ 0, 360*64);
+ if (celltoggle->state)
+ {
+ gdk_draw_arc (window,
+ widget->style->black_gc,
+ TRUE,
+ cell_area->x + real_xoffset + 2,
+ cell_area->y + real_yoffset + 2,
+ width - 4,
+ height - 4,
+ 0, 360*64);
+ }
+ }
+
+
+ gdk_gc_set_clip_rectangle (widget->style->black_gc, NULL);
+}
+
+static gint
+gtk_cell_renderer_toggle_event (GtkCellRenderer *cell,
+ GdkEvent *event,
+ GtkWidget *widget,
+ gchar *path,
+ GdkRectangle *background_area,
+ GdkRectangle *cell_area,
+ guint flags)
+{
+ gtk_signal_emit (GTK_OBJECT (cell), toggle_cell_signals[TOGGLED], path);
+ return TRUE;
+}
+
+void
+gtk_cell_renderer_toggle_set_radio (GtkCellRendererToggle *toggle,
+ gboolean radio)
+{
+ g_return_if_fail (toggle != NULL);
+ g_return_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (toggle));
+
+ toggle->radio = radio;
+}
diff --git a/gtk/gtkcellrenderertoggle.h b/gtk/gtkcellrenderertoggle.h
new file mode 100644
index 0000000000..0df6696468
--- /dev/null
+++ b/gtk/gtkcellrenderertoggle.h
@@ -0,0 +1,68 @@
+/* gtkcellrenderertoggle.h
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_CELL_RENDERER_TOGGLE_H__
+#define __GTK_CELL_RENDERER_TOGGLE_H__
+
+#include <gtk/gtkcellrenderer.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+#define GTK_TYPE_CELL_RENDERER_TOGGLE (gtk_cell_renderer_toggle_get_type ())
+#define GTK_CELL_RENDERER_TOGGLE(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_CELL_RENDERER_TOGGLE, GtkCellRendererToggle))
+#define GTK_CELL_RENDERER_TOGGLE_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_RENDERER_TOGGLE, GtkCellRendererToggleClass))
+#define GTK_IS_CELL_RENDERER_TOGGLE(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_CELL_RENDERER_TOGGLE))
+#define GTK_IS_CELL_RENDERER_TOGGLE_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), GTK_TYPE_CELL_RENDERER_TOGGLE))
+
+typedef struct _GtkCellRendererToggle GtkCellRendererToggle;
+typedef struct _GtkCellRendererToggleClass GtkCellRendererToggleClass;
+
+struct _GtkCellRendererToggle
+{
+ GtkCellRenderer parent;
+
+ /*< private >*/
+ guint state : 1;
+ guint radio : 1;
+};
+
+struct _GtkCellRendererToggleClass
+{
+ GtkCellRendererClass parent_class;
+
+ void (* toggled) (GtkCellRendererToggle *celltoggle,
+ gchar *path);
+};
+
+GtkType gtk_cell_renderer_toggle_get_type (void);
+GtkCellRenderer *gtk_cell_renderer_toggle_new (void);
+
+void gtk_cell_renderer_toggle_set_radio (GtkCellRendererToggle *toggle,
+ gboolean radio);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_CELL_RENDERER_TOGGLE_H__ */
diff --git a/gtk/gtkliststore.c b/gtk/gtkliststore.c
new file mode 100644
index 0000000000..5fb475570f
--- /dev/null
+++ b/gtk/gtkliststore.c
@@ -0,0 +1,458 @@
+/* gtkliststore.c
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include "gtktreemodel.h"
+#include "gtkliststore.h"
+#include "gtktreedatalist.h"
+
+#define G_SLIST(x) ((GSList *) x)
+
+static void gtk_list_store_init (GtkListStore *list_store);
+static void gtk_list_store_class_init (GtkListStoreClass *class);
+static gint gtk_list_store_get_n_columns (GtkTreeModel *tree_model);
+static GtkTreeNode gtk_list_store_get_node (GtkTreeModel *tree_model,
+ GtkTreePath *path);
+static GtkTreePath *gtk_list_store_get_path (GtkTreeModel *tree_model,
+ GtkTreeNode node);
+static void gtk_list_store_node_get_value (GtkTreeModel *tree_model,
+ GtkTreeNode node,
+ gint column,
+ GValue *value);
+static gboolean gtk_list_store_node_next (GtkTreeModel *tree_model,
+ GtkTreeNode *node);
+static GtkTreeNode gtk_list_store_node_children (GtkTreeModel *tree_model,
+ GtkTreeNode node);
+static gboolean gtk_list_store_node_has_child (GtkTreeModel *tree_model,
+ GtkTreeNode node);
+static gint gtk_list_store_node_n_children (GtkTreeModel *tree_model,
+ GtkTreeNode node);
+static GtkTreeNode gtk_list_store_node_nth_child (GtkTreeModel *tree_model,
+ GtkTreeNode node,
+ gint n);
+static GtkTreeNode gtk_list_store_node_parent (GtkTreeModel *tree_model,
+ GtkTreeNode node);
+
+
+static GtkTreeModelClass *parent_class = NULL;
+
+
+GtkType
+gtk_list_store_get_type (void)
+{
+ static GtkType list_store_type = 0;
+
+ if (!list_store_type)
+ {
+ static const GTypeInfo list_store_info =
+ {
+ sizeof (GtkListStoreClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) gtk_list_store_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GtkListStore),
+ 0,
+ (GInstanceInitFunc) gtk_list_store_init,
+ };
+
+ list_store_type = g_type_register_static (GTK_TYPE_TREE_MODEL, "GtkListStore", &list_store_info);
+ }
+
+ return list_store_type;
+}
+
+static void
+gtk_list_store_class_init (GtkListStoreClass *class)
+{
+ GtkObjectClass *object_class;
+ GtkTreeModelClass *tree_model_class;
+
+ object_class = (GtkObjectClass*) class;
+ tree_model_class = (GtkTreeModelClass *) class;
+
+ parent_class = gtk_type_class (gtk_tree_model_get_type ());
+
+ tree_model_class->get_n_columns = gtk_list_store_get_n_columns;
+ tree_model_class->get_node = gtk_list_store_get_node;
+ tree_model_class->get_path = gtk_list_store_get_path;
+ tree_model_class->node_get_value = gtk_list_store_node_get_value;
+ tree_model_class->node_next = gtk_list_store_node_next;
+ tree_model_class->node_children = gtk_list_store_node_children;
+ tree_model_class->node_has_child = gtk_list_store_node_has_child;
+ tree_model_class->node_n_children = gtk_list_store_node_n_children;
+ tree_model_class->node_nth_child = gtk_list_store_node_nth_child;
+ tree_model_class->node_parent = gtk_list_store_node_parent;
+}
+
+static void
+gtk_list_store_init (GtkListStore *list_store)
+{
+ list_store->root = NULL;
+}
+
+GtkObject *
+gtk_list_store_new (void)
+{
+ return GTK_OBJECT (gtk_type_new (gtk_list_store_get_type ()));
+}
+
+GtkObject *
+gtk_list_store_new_with_types (gint n_columns,
+ ...)
+{
+ GtkObject *retval;
+ va_list args;
+ gint i;
+
+ g_return_val_if_fail (n_columns > 0, NULL);
+
+ retval = gtk_list_store_new ();
+ gtk_list_store_set_n_columns (GTK_LIST_STORE (retval),
+ n_columns);
+
+ va_start (args, n_columns);
+ for (i = 0; i < n_columns; i++)
+ gtk_list_store_set_column_type (GTK_LIST_STORE (retval),
+ i, va_arg (args, GType));
+
+ va_end (args);
+
+ return retval;
+}
+
+void
+gtk_list_store_set_n_columns (GtkListStore *list_store,
+ gint n_columns)
+{
+ GType *new_columns;
+
+ g_return_if_fail (list_store != NULL);
+ g_return_if_fail (GTK_IS_LIST_STORE (list_store));
+
+ if (list_store->n_columns == n_columns)
+ return;
+
+ new_columns = g_new0 (GType, n_columns);
+ if (list_store->column_headers)
+ {
+ /* copy the old header orders over */
+ if (n_columns >= list_store->n_columns)
+ memcpy (new_columns, list_store->column_headers, list_store->n_columns * sizeof (gchar *));
+ else
+ memcpy (new_columns, list_store->column_headers, n_columns * sizeof (GType));
+
+ g_free (list_store->column_headers);
+ }
+
+ list_store->column_headers = new_columns;
+ list_store->n_columns = n_columns;
+}
+
+void
+gtk_list_store_set_column_type (GtkListStore *list_store,
+ gint column,
+ GType type)
+{
+ g_return_if_fail (list_store != NULL);
+ g_return_if_fail (GTK_IS_LIST_STORE (list_store));
+ g_return_if_fail (column >=0 && column < list_store->n_columns);
+
+ list_store->column_headers[column] = type;
+}
+
+/* Fulfill the GtkTreeModel requirements */
+static gint
+gtk_list_store_get_n_columns (GtkTreeModel *tree_model)
+{
+ g_return_val_if_fail (tree_model != NULL, 0);
+ g_return_val_if_fail (GTK_IS_LIST_STORE (tree_model), 0);
+
+ return GTK_LIST_STORE (tree_model)->n_columns;
+}
+
+static GtkTreeNode
+gtk_list_store_get_node (GtkTreeModel *tree_model,
+ GtkTreePath *path)
+{
+ g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, NULL);
+
+ return (GtkTreeNode) g_slist_nth (G_SLIST (GTK_LIST_STORE (tree_model)->root),
+ gtk_tree_path_get_indices (path)[0]);
+}
+
+static GtkTreePath *
+gtk_list_store_get_path (GtkTreeModel *tree_model,
+ GtkTreeNode node)
+{
+ GtkTreePath *retval;
+ GSList *list;
+ gint i = 0;
+
+ g_return_val_if_fail (GTK_IS_LIST_STORE (tree_model), NULL);
+
+ for (list = G_SLIST (GTK_LIST_STORE (tree_model)->root); list; list = list->next)
+ {
+ i++;
+ if (list == G_SLIST (node))
+ break;
+ }
+ if (list == NULL)
+ return NULL;
+
+ retval = gtk_tree_path_new ();
+ gtk_tree_path_append_index (retval, i);
+
+ return retval;
+}
+
+static void
+gtk_list_store_node_get_value (GtkTreeModel *tree_model,
+ GtkTreeNode node,
+ gint column,
+ GValue *value)
+{
+ GtkTreeDataList *list;
+ gint tmp_column = column;
+
+ g_return_if_fail (tree_model != NULL);
+ g_return_if_fail (GTK_IS_LIST_STORE (tree_model));
+ g_return_if_fail (node != NULL);
+ g_return_if_fail (column < GTK_LIST_STORE (tree_model)->n_columns);
+
+ list = G_SLIST (node)->data;
+
+ while (tmp_column-- > 0 && list)
+ list = list->next;
+
+ g_return_if_fail (list != NULL);
+
+ gtk_tree_data_list_node_to_value (list,
+ GTK_LIST_STORE (tree_model)->column_headers[column],
+ value);
+}
+
+static gboolean
+gtk_list_store_node_next (GtkTreeModel *tree_model,
+ GtkTreeNode *node)
+{
+ if (node == NULL || *node == NULL)
+ return FALSE;
+
+ *node = (GtkTreeNode *) G_SLIST (*node)->next;
+
+ return (*node != NULL);
+}
+
+static GtkTreeNode
+gtk_list_store_node_children (GtkTreeModel *tree_model,
+ GtkTreeNode node)
+{
+ return NULL;
+}
+
+static gboolean
+gtk_list_store_node_has_child (GtkTreeModel *tree_model,
+ GtkTreeNode node)
+{
+ return FALSE;
+}
+
+static gint
+gtk_list_store_node_n_children (GtkTreeModel *tree_model,
+ GtkTreeNode node)
+{
+ return 0;
+}
+
+static GtkTreeNode
+gtk_list_store_node_nth_child (GtkTreeModel *tree_model,
+ GtkTreeNode node,
+ gint n)
+{
+ return NULL;
+}
+
+static GtkTreeNode
+gtk_list_store_node_parent (GtkTreeModel *tree_model,
+ GtkTreeNode node)
+{
+ return NULL;
+}
+
+/* Public accessors */
+GtkTreeNode *
+gtk_list_store_node_new (void)
+{
+ GtkTreeNode *retval = (GtkTreeNode *) g_slist_alloc ();
+
+ return retval;
+}
+
+/* This is a somewhat inelegant function that does a lot of list
+ * manipulations on it's own.
+ */
+void
+gtk_list_store_node_set_cell (GtkListStore *list_store,
+ GtkTreeNode *node,
+ gint column,
+ GValue *value)
+{
+ GtkTreeDataList *list;
+ GtkTreeDataList *prev;
+
+ g_return_if_fail (list_store != NULL);
+ g_return_if_fail (GTK_IS_LIST_STORE (list_store));
+ g_return_if_fail (node != NULL);
+ g_return_if_fail (column >= 0 && column < list_store->n_columns);
+
+ prev = list = G_SLIST (node)->data;
+
+ while (list != NULL)
+ {
+ if (column == 0)
+ {
+ gtk_tree_data_list_value_to_node (list, value);
+ return;
+ }
+
+ column--;
+ prev = list;
+ list = list->next;
+ }
+
+ if (G_SLIST (node)->data == NULL)
+ {
+ G_SLIST (node)->data = list = gtk_tree_data_list_alloc ();
+ list->next = NULL;
+ }
+ else
+ {
+ list = prev->next = gtk_tree_data_list_alloc ();
+ list->next = NULL;
+ }
+
+ while (column != 0)
+ {
+ list->next = gtk_tree_data_list_alloc ();
+ list = list->next;
+ list->next = NULL;
+ column --;
+ }
+ gtk_tree_data_list_value_to_node (list, value);
+}
+
+void
+gtk_list_store_node_remove (GtkListStore *list_store,
+ GtkTreeNode *node)
+{
+
+}
+
+GtkTreeNode *
+gtk_list_store_node_insert (GtkListStore *list_store,
+ gint position,
+ GtkTreeNode *node)
+{
+ GSList *list;
+
+ g_return_val_if_fail (list_store != NULL, node);
+ g_return_val_if_fail (GTK_IS_LIST_STORE (list_store), node);
+ g_return_val_if_fail (node != NULL, node);
+ g_return_val_if_fail (position < 0, node);
+ g_return_val_if_fail (G_SLIST (node)->next == NULL, node);
+
+ if (position == 0)
+ {
+ gtk_list_store_node_prepend (list_store, node);
+ return node;
+ }
+
+ list = g_slist_nth (G_SLIST (list_store->root), position);
+ if (list)
+ {
+ G_SLIST (node)->next = list->next;
+ list->next = G_SLIST (node)->next;
+ }
+
+ return node;
+}
+
+
+GtkTreeNode *
+gtk_list_store_node_insert_before (GtkListStore *list_store,
+ GtkTreeNode *sibling,
+ GtkTreeNode *node)
+{
+ g_return_val_if_fail (list_store != NULL, node);
+ g_return_val_if_fail (GTK_IS_LIST_STORE (list_store), node);
+ g_return_val_if_fail (node != NULL, node);
+
+ /* FIXME: This is all wrong. This is actually insert_after */
+ if (sibling == NULL)
+ return gtk_list_store_node_prepend (list_store, node);
+
+ G_SLIST (node)->next = G_SLIST (sibling)->next;
+ G_SLIST (sibling)->next = G_SLIST (node);
+ return node;
+}
+
+GtkTreeNode *
+gtk_list_store_node_prepend (GtkListStore *list_store,
+ GtkTreeNode *node)
+{
+ g_return_val_if_fail (list_store != NULL, node);
+ g_return_val_if_fail (GTK_IS_LIST_STORE (list_store), node);
+ g_return_val_if_fail (node != NULL, node);
+
+ G_SLIST (node)->next = G_SLIST (list_store->root);
+ list_store->root = node;
+
+ return node;
+}
+
+GtkTreeNode *
+gtk_list_store_node_append (GtkListStore *list_store,
+ GtkTreeNode *node)
+{
+ GSList *list;
+
+ g_return_val_if_fail (list_store != NULL, node);
+ g_return_val_if_fail (GTK_IS_LIST_STORE (list_store), node);
+ g_return_val_if_fail (node != NULL, node);
+ g_return_val_if_fail (G_SLIST (node)->next == NULL, node);
+
+ list = g_slist_last (G_SLIST (list_store->root));
+ if (list == NULL)
+ list_store->root = node;
+ else
+ list->next = G_SLIST (node);
+
+ return node;
+}
+
+GtkTreeNode *
+gtk_list_store_node_get_root (GtkListStore *list_store)
+{
+ g_return_val_if_fail (list_store != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_LIST_STORE (list_store), NULL);
+
+ return (GtkTreeNode *) list_store->root;
+}
diff --git a/gtk/gtkliststore.h b/gtk/gtkliststore.h
new file mode 100644
index 0000000000..da643888d7
--- /dev/null
+++ b/gtk/gtkliststore.h
@@ -0,0 +1,87 @@
+/* gtkliststore.h
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_LIST_STORE_H__
+#define __GTK_LIST_STORE_H__
+
+#include <gtk/gtktreemodel.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define GTK_TYPE_LIST_STORE (gtk_list_store_get_type ())
+#define GTK_LIST_STORE(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_LIST_STORE, GtkListStore))
+#define GTK_LIST_STORE_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_LISTSTORE, GtkListStoreClass))
+#define GTK_IS_LIST_STORE(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_LIST_STORE))
+#define GTK_IS_LIST_STORE_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), GTK_TYPE_LIST_STORE))
+
+typedef struct _GtkListStore GtkListStore;
+typedef struct _GtkListStoreClass GtkListStoreClass;
+
+struct _GtkListStore
+{
+ GtkTreeModel parent;
+
+ /*< private >*/
+ GtkTreeNode *root;
+ gint n_columns;
+ GType *column_headers;
+};
+
+struct _GtkListStoreClass
+{
+ GtkTreeModelClass parent_class;
+};
+
+GtkType gtk_list_store_get_type (void);
+GtkObject *gtk_list_store_new (void);
+GtkObject *gtk_list_store_new_with_types (gint n_columns,
+ ...);
+void gtk_list_store_set_n_columns (GtkListStore *store,
+ gint n_columns);
+void gtk_list_store_set_column_type (GtkListStore *store,
+ gint column,
+ GType type);
+
+GtkTreeNode *gtk_list_store_node_new (void);
+void gtk_list_store_node_set_cell (GtkListStore *store,
+ GtkTreeNode *node,
+ gint column,
+ GValue *value);
+void gtk_list_store_node_remove (GtkListStore *store,
+ GtkTreeNode *node);
+GtkTreeNode *gtk_list_store_node_insert (GtkListStore *store,
+ gint position,
+ GtkTreeNode *node);
+GtkTreeNode *gtk_list_store_node_insert_before (GtkListStore *store,
+ GtkTreeNode *sibling,
+ GtkTreeNode *node);
+GtkTreeNode *gtk_list_store_node_prepend (GtkListStore *store,
+ GtkTreeNode *node);
+GtkTreeNode *gtk_list_store_node_append (GtkListStore *store,
+ GtkTreeNode *node);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_LIST_STORE_H__ */
diff --git a/gtk/gtkmodelsimple.c b/gtk/gtkmodelsimple.c
new file mode 100644
index 0000000000..4e6d81d885
--- /dev/null
+++ b/gtk/gtkmodelsimple.c
@@ -0,0 +1,430 @@
+/* gtkmodelsimple.c
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gtkmodelsimple.h"
+#include "gtkmarshal.h"
+#include "gtksignal.h"
+enum {
+ GET_N_COLUMNS,
+ GET_NODE,
+ GET_PATH,
+ NODE_GET_VALUE,
+ NODE_COPY,
+ NODE_NEXT,
+ NODE_CHILDREN,
+ NODE_HAS_CHILD,
+ NODE_N_CHILDREN,
+ NODE_NTH_CHILD,
+ NODE_PARENT,
+ LAST_SIGNAL
+};
+
+static void gtk_model_simple_init (GtkModelSimple *model_simple);
+static void gtk_model_simple_class_init (GtkModelSimpleClass *class);
+static gint gtk_real_model_simple_get_n_columns (GtkTreeModel *tree_model);
+static GtkTreeNode gtk_real_model_simple_get_node (GtkTreeModel *tree_model,
+ GtkTreePath *path);
+static GtkTreePath *gtk_real_model_simple_get_path (GtkTreeModel *tree_model,
+ GtkTreeNode node);
+static void gtk_real_model_simple_node_get_value (GtkTreeModel *tree_model,
+ GtkTreeNode node,
+ gint column,
+ GValue *value);
+static gboolean gtk_real_model_simple_node_next (GtkTreeModel *tree_model,
+ GtkTreeNode *node);
+static GtkTreeNode gtk_real_model_simple_node_children (GtkTreeModel *tree_model,
+ GtkTreeNode node);
+static gboolean gtk_real_model_simple_node_has_child (GtkTreeModel *tree_model,
+ GtkTreeNode node);
+static gint gtk_real_model_simple_node_n_children (GtkTreeModel *tree_model,
+ GtkTreeNode node);
+static GtkTreeNode gtk_real_model_simple_node_nth_child (GtkTreeModel *tree_model,
+ GtkTreeNode node,
+ gint n);
+static GtkTreeNode gtk_real_model_simple_node_parent (GtkTreeModel *tree_model,
+ GtkTreeNode node);
+
+
+
+static GtkTreeModelClass *parent_class = NULL;
+static guint model_simple_signals[LAST_SIGNAL] = { 0 };
+
+
+GtkType
+gtk_model_simple_get_type (void)
+{
+ static GtkType model_simple_type = 0;
+
+ if (!model_simple_type)
+ {
+ static const GTypeInfo model_simple_info =
+ {
+ sizeof (GtkModelSimpleClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) gtk_model_simple_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GtkModelSimple),
+ 0,
+ (GInstanceInitFunc) gtk_model_simple_init
+ };
+
+ model_simple_type = g_type_register_static (GTK_TYPE_TREE_MODEL, "GtkModelSimple", &model_simple_info);
+ }
+
+ return model_simple_type;
+}
+
+GtkObject *
+gtk_model_simple_new (void)
+{
+ return GTK_OBJECT (gtk_type_new (GTK_TYPE_MODEL_SIMPLE));
+}
+
+
+typedef gint (*GtkSignal_INT__NONE) (GtkObject * object,
+ gpointer user_data);
+void
+gtk_marshal_INT__NONE (GtkObject * object,
+ GtkSignalFunc func, gpointer func_data, GtkArg * args)
+{
+ GtkSignal_INT__NONE rfunc;
+ gint *return_val;
+ return_val = GTK_RETLOC_INT (args[0]);
+ rfunc = (GtkSignal_INT__NONE) func;
+ *return_val = (*rfunc) (object, func_data);
+}
+
+typedef gpointer (*GtkSignal_POINTER__NONE) (GtkObject * object,
+ gpointer user_data);
+void
+gtk_marshal_POINTER__NONE (GtkObject * object,
+ GtkSignalFunc func, gpointer func_data, GtkArg * args)
+{
+ GtkSignal_POINTER__NONE rfunc;
+ gpointer *return_val;
+ return_val = GTK_RETLOC_POINTER (args[0]);
+ rfunc = (GtkSignal_POINTER__NONE) func;
+ *return_val = (*rfunc) (object, func_data);
+}
+
+typedef gpointer (*GtkSignal_POINTER__POINTER) (GtkObject * object,
+ gpointer arg1,
+ gpointer user_data);
+void
+gtk_marshal_POINTER__POINTER (GtkObject * object,
+ GtkSignalFunc func,
+ gpointer func_data, GtkArg * args)
+{
+ GtkSignal_POINTER__POINTER rfunc;
+ gpointer *return_val;
+ return_val = GTK_RETLOC_POINTER (args[1]);
+ rfunc = (GtkSignal_POINTER__POINTER) func;
+ *return_val = (*rfunc) (object, GTK_VALUE_POINTER (args[0]), func_data);
+}
+
+typedef gpointer (*GtkSignal_POINTER__POINTER_INT) (GtkObject * object,
+ gpointer arg1,
+ gint arg2,
+ gpointer user_data);
+void
+gtk_marshal_POINTER__POINTER_INT (GtkObject * object,
+ GtkSignalFunc func,
+ gpointer func_data, GtkArg * args)
+{
+ GtkSignal_POINTER__POINTER_INT rfunc;
+ gpointer *return_val;
+ return_val = GTK_RETLOC_POINTER (args[2]);
+ rfunc = (GtkSignal_POINTER__POINTER_INT) func;
+ *return_val = (*rfunc) (object, GTK_VALUE_POINTER (args[0]), GTK_VALUE_INT (args[1]), func_data);
+}
+
+static void
+gtk_model_simple_class_init (GtkModelSimpleClass *class)
+{
+ GtkObjectClass *object_class;
+
+ GtkTreeModelClass *tree_model_class;
+
+ object_class = (GtkObjectClass*) class;
+ tree_model_class = (GtkTreeModelClass*) class;
+ parent_class = g_type_class_peek_parent (class);
+
+
+ model_simple_signals[GET_N_COLUMNS] =
+ gtk_signal_new ("get_n_columns",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ 0,
+ gtk_marshal_INT__NONE,
+ GTK_TYPE_INT, 0);
+ model_simple_signals[GET_NODE] =
+ gtk_signal_new ("get_node",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ 0,
+ gtk_marshal_POINTER__POINTER,
+ GTK_TYPE_POINTER, 1,
+ GTK_TYPE_POINTER);
+ model_simple_signals[GET_PATH] =
+ gtk_signal_new ("get_path",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ 0,
+ gtk_marshal_POINTER__POINTER,
+ GTK_TYPE_POINTER, 1,
+ GTK_TYPE_POINTER);
+ model_simple_signals[NODE_GET_VALUE] =
+ gtk_signal_new ("node_get_value",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ 0,
+ gtk_marshal_NONE__POINTER_INT_POINTER,
+ GTK_TYPE_NONE, 3,
+ GTK_TYPE_POINTER,
+ GTK_TYPE_INT,
+ GTK_TYPE_POINTER);
+ model_simple_signals[NODE_NEXT] =
+ gtk_signal_new ("node_next",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ 0,
+ gtk_marshal_BOOL__POINTER,
+ GTK_TYPE_BOOL, 1,
+ GTK_TYPE_POINTER);
+ model_simple_signals[NODE_CHILDREN] =
+ gtk_signal_new ("node_children",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ 0,
+ gtk_marshal_POINTER__POINTER,
+ GTK_TYPE_POINTER, 1,
+ GTK_TYPE_POINTER);
+ model_simple_signals[NODE_HAS_CHILD] =
+ gtk_signal_new ("node_has_child",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ 0,
+ gtk_marshal_POINTER__POINTER,
+ GTK_TYPE_POINTER, 1,
+ GTK_TYPE_POINTER);
+ model_simple_signals[NODE_N_CHILDREN] =
+ gtk_signal_new ("node_n_children",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ 0,
+ gtk_marshal_POINTER__POINTER,
+ GTK_TYPE_POINTER, 1,
+ GTK_TYPE_POINTER);
+ model_simple_signals[NODE_NTH_CHILD] =
+ gtk_signal_new ("node_nth_child",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ 0,
+ gtk_marshal_POINTER__POINTER_INT,
+ GTK_TYPE_POINTER, 2,
+ GTK_TYPE_POINTER,
+ GTK_TYPE_POINTER);
+ model_simple_signals[NODE_PARENT] =
+ gtk_signal_new ("node_parent",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ 0,
+ gtk_marshal_POINTER__POINTER,
+ GTK_TYPE_POINTER, 1,
+ GTK_TYPE_POINTER);
+
+
+ tree_model_class->get_n_columns = gtk_real_model_simple_get_n_columns;
+ tree_model_class->get_node = gtk_real_model_simple_get_node;
+ tree_model_class->get_path = gtk_real_model_simple_get_path;
+ tree_model_class->node_get_value = gtk_real_model_simple_node_get_value;
+ tree_model_class->node_next = gtk_real_model_simple_node_next;
+ tree_model_class->node_children = gtk_real_model_simple_node_children;
+ tree_model_class->node_has_child = gtk_real_model_simple_node_has_child;
+ tree_model_class->node_n_children = gtk_real_model_simple_node_n_children;
+ tree_model_class->node_nth_child = gtk_real_model_simple_node_nth_child;
+ tree_model_class->node_parent = gtk_real_model_simple_node_parent;
+
+ gtk_object_class_add_signals (object_class, model_simple_signals, LAST_SIGNAL);
+}
+
+
+static void
+gtk_model_simple_init (GtkModelSimple *model_simple)
+{
+}
+
+static gint
+gtk_real_model_simple_get_n_columns (GtkTreeModel *tree_model)
+{
+ gint retval = 0;
+
+ gtk_signal_emit (GTK_OBJECT (tree_model), model_simple_signals[GET_N_COLUMNS], &retval);
+
+ return retval;
+}
+
+static GtkTreeNode
+gtk_real_model_simple_get_node (GtkTreeModel *tree_model,
+ GtkTreePath *path)
+{
+ GtkTreeNode retval;
+
+ gtk_signal_emit (GTK_OBJECT (tree_model), model_simple_signals[GET_NODE], path, &retval);
+
+ return retval;
+}
+
+static GtkTreePath *
+gtk_real_model_simple_get_path (GtkTreeModel *tree_model,
+ GtkTreeNode node)
+{
+ GtkTreePath *retval;
+
+ gtk_signal_emit (GTK_OBJECT (tree_model), model_simple_signals[GET_PATH], node, &retval);
+
+ return retval;
+}
+
+static void
+gtk_real_model_simple_node_get_value (GtkTreeModel *tree_model,
+ GtkTreeNode node,
+ gint column,
+ GValue *value)
+{
+ gtk_signal_emit (GTK_OBJECT (tree_model), model_simple_signals[NODE_GET_VALUE], node, column, value);
+}
+
+static gboolean
+gtk_real_model_simple_node_next (GtkTreeModel *tree_model,
+ GtkTreeNode *node)
+{
+ gboolean retval = FALSE;
+
+ gtk_signal_emit (GTK_OBJECT (tree_model), model_simple_signals[NODE_NEXT], node, &retval);
+
+ return retval;
+}
+
+static GtkTreeNode
+gtk_real_model_simple_node_children (GtkTreeModel *tree_model,
+ GtkTreeNode node)
+{
+ GtkTreeNode retval = NULL;
+
+ gtk_signal_emit (GTK_OBJECT (tree_model), model_simple_signals[NODE_CHILDREN], node, &retval);
+
+ return retval;
+}
+
+static gboolean
+gtk_real_model_simple_node_has_child (GtkTreeModel *tree_model,
+ GtkTreeNode node)
+{
+ gboolean retval = FALSE;
+
+ gtk_signal_emit (GTK_OBJECT (tree_model), model_simple_signals[NODE_HAS_CHILD], node, &retval);
+
+ return retval;
+}
+
+static gint
+gtk_real_model_simple_node_n_children (GtkTreeModel *tree_model,
+ GtkTreeNode node)
+{
+ gint retval = 0;
+
+ gtk_signal_emit (GTK_OBJECT (tree_model), model_simple_signals[NODE_N_CHILDREN], node, &retval);
+
+ return retval;
+}
+
+static GtkTreeNode
+gtk_real_model_simple_node_nth_child (GtkTreeModel *tree_model,
+ GtkTreeNode node,
+ gint n)
+{
+ GtkTreeNode retval = NULL;
+
+ gtk_signal_emit (GTK_OBJECT (tree_model), model_simple_signals[NODE_NTH_CHILD], node, n, &retval);
+
+ return retval;
+}
+
+static GtkTreeNode
+gtk_real_model_simple_node_parent (GtkTreeModel *tree_model,
+ GtkTreeNode node)
+{
+ GtkTreeNode retval = NULL;
+
+ gtk_signal_emit (GTK_OBJECT (tree_model), model_simple_signals[NODE_PARENT], node, &retval);
+
+ return retval;
+}
+
+/* Public functions */
+void
+gtk_model_simple_node_changed (GtkModelSimple *simple,
+ GtkTreePath *path,
+ GtkTreeNode *tree_node)
+{
+ g_return_if_fail (simple != NULL);
+ g_return_if_fail (GTK_IS_MODEL_SIMPLE (simple));
+ g_return_if_fail (path != NULL);
+
+ gtk_signal_emit_by_name (GTK_OBJECT (simple), "node_changed", path, tree_node);
+}
+
+void
+gtk_model_simple_node_inserted (GtkModelSimple *simple,
+ GtkTreePath *path,
+ GtkTreeNode *tree_node)
+{
+ g_return_if_fail (simple != NULL);
+ g_return_if_fail (GTK_IS_MODEL_SIMPLE (simple));
+ g_return_if_fail (path != NULL);
+
+ gtk_signal_emit_by_name (GTK_OBJECT (simple), "node_inserted", path, tree_node);
+}
+
+void
+gtk_model_simple_node_child_toggled (GtkModelSimple *simple,
+ GtkTreePath *path,
+ GtkTreeNode *tree_node)
+{
+ g_return_if_fail (simple != NULL);
+ g_return_if_fail (GTK_IS_MODEL_SIMPLE (simple));
+ g_return_if_fail (path != NULL);
+
+ gtk_signal_emit_by_name (GTK_OBJECT (simple), "node_child_toggled", path, tree_node);
+}
+
+void
+gtk_model_simple_node_deleted (GtkModelSimple *simple,
+ GtkTreePath *path,
+ GtkTreeNode *tree_node)
+{
+ g_return_if_fail (simple != NULL);
+ g_return_if_fail (GTK_IS_MODEL_SIMPLE (simple));
+ g_return_if_fail (path != NULL);
+
+ gtk_signal_emit_by_name (GTK_OBJECT (simple), "node_deleted", path, tree_node);
+}
diff --git a/gtk/gtkmodelsimple.h b/gtk/gtkmodelsimple.h
new file mode 100644
index 0000000000..bcce4fa471
--- /dev/null
+++ b/gtk/gtkmodelsimple.h
@@ -0,0 +1,73 @@
+/* gtkmodelsimple.h
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_MODEL_SIMPLE_H__
+#define __GTK_MODEL_SIMPLE_H__
+
+#include <gtk/gtktreemodel.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define GTK_TYPE_MODEL_SIMPLE (gtk_model_simple_get_type ())
+#define GTK_MODEL_SIMPLE(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_MODEL_SIMPLE, GtkModelSimple))
+#define GTK_MODEL_SIMPLE_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_MODEL_SIMPLE, GtkModelSimpleClass))
+#define GTK_IS_MODEL_SIMPLE(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_MODEL_SIMPLE))
+#define GTK_IS_MODEL_SIMPLE_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), GTK_TYPE_MODEL_SIMPLE))
+
+
+typedef struct _GtkModelSimple GtkModelSimple;
+typedef struct _GtkModelSimpleClass GtkModelSimpleClass;
+
+struct _GtkModelSimple
+{
+ GtkTreeModel parent;
+};
+
+struct _GtkModelSimpleClass
+{
+ GtkTreeModelClass parent_class;
+};
+
+
+GtkType gtk_model_simple_get_type (void);
+GtkObject *gtk_model_simple_new (void);
+
+void gtk_model_simple_node_changed (GtkModelSimple *simple,
+ GtkTreePath *path,
+ GtkTreeNode *tree_node);
+void gtk_model_simple_node_inserted (GtkModelSimple *simple,
+ GtkTreePath *path,
+ GtkTreeNode *tree_node);
+void gtk_model_simple_node_child_toggled (GtkModelSimple *simple,
+ GtkTreePath *path,
+ GtkTreeNode *tree_node);
+void gtk_model_simple_node_deleted (GtkModelSimple *simple,
+ GtkTreePath *path,
+ GtkTreeNode *tree_node);
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_MODEL_SIMPLE_H__ */
diff --git a/gtk/gtkrbtree.c b/gtk/gtkrbtree.c
new file mode 100644
index 0000000000..92b170d1ff
--- /dev/null
+++ b/gtk/gtkrbtree.c
@@ -0,0 +1,994 @@
+/* gtkrbtree.c
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gtkrbtree.h"
+
+static void _gtk_rbnode_validate_allocator (GAllocator *allocator);
+static GtkRBNode *_gtk_rbnode_new (GtkRBTree *tree,
+ gint height);
+static void _gtk_rbnode_free (GtkRBNode *node);
+static void _gtk_rbnode_rotate_left (GtkRBTree *tree,
+ GtkRBNode *node);
+static void _gtk_rbnode_rotate_left (GtkRBTree *tree,
+ GtkRBNode *node);
+static void _gtk_rbtree_insert_fixup (GtkRBTree *tree,
+ GtkRBNode *node);
+static void _gtk_rbtree_remove_node_fixup (GtkRBTree *tree,
+ GtkRBNode *node);
+static gint _count_nodes (GtkRBTree *tree,
+ GtkRBNode *node);
+
+
+/* node allocation
+ */
+struct _GAllocator /* from gmem.c */
+{
+ gchar *name;
+ guint16 n_preallocs;
+ guint is_unused : 1;
+ guint type : 4;
+ GAllocator *last;
+ GMemChunk *mem_chunk;
+ GtkRBNode *free_nodes; /* implementation specific */
+};
+
+
+G_LOCK_DEFINE_STATIC (current_allocator);
+static GAllocator *current_allocator = NULL;
+
+/* HOLDS: current_allocator_lock */
+static void
+_gtk_rbnode_validate_allocator (GAllocator *allocator)
+{
+ g_return_if_fail (allocator != NULL);
+ g_return_if_fail (allocator->is_unused == TRUE);
+
+ if (allocator->type != G_ALLOCATOR_NODE)
+ {
+ allocator->type = G_ALLOCATOR_NODE;
+ if (allocator->mem_chunk)
+ {
+ g_mem_chunk_destroy (allocator->mem_chunk);
+ allocator->mem_chunk = NULL;
+ }
+ }
+
+ if (!allocator->mem_chunk)
+ {
+ allocator->mem_chunk = g_mem_chunk_new (allocator->name,
+ sizeof (GtkRBNode),
+ sizeof (GtkRBNode) * allocator->n_preallocs,
+ G_ALLOC_ONLY);
+ allocator->free_nodes = NULL;
+ }
+
+ allocator->is_unused = FALSE;
+}
+
+static GtkRBNode *
+_gtk_rbnode_new (GtkRBTree *tree,
+ gint height)
+{
+ GtkRBNode *node;
+
+ G_LOCK (current_allocator);
+ if (!current_allocator)
+ {
+ GAllocator *allocator = g_allocator_new ("GTK+ default GtkRBNode allocator",
+ 128);
+ _gtk_rbnode_validate_allocator (allocator);
+ allocator->last = NULL;
+ current_allocator = allocator;
+ }
+ if (!current_allocator->free_nodes)
+ node = g_chunk_new (GtkRBNode, current_allocator->mem_chunk);
+ else
+ {
+ node = current_allocator->free_nodes;
+ current_allocator->free_nodes = node->left;
+ }
+ G_UNLOCK (current_allocator);
+
+ node->left = tree->nil;
+ node->right = tree->nil;
+ node->parent = tree->nil;
+ node->flags = GTK_RBNODE_RED;
+ node->count = 1;
+ node->children = NULL;
+ node->offset = height;
+ return node;
+}
+
+static void
+_gtk_rbnode_free (GtkRBNode *node)
+{
+ G_LOCK (current_allocator);
+ node->left = current_allocator->free_nodes;
+ current_allocator->free_nodes = node;
+ G_UNLOCK (current_allocator);
+}
+
+static void
+_gtk_rbnode_rotate_left (GtkRBTree *tree,
+ GtkRBNode *node)
+{
+ gint node_height, right_height;
+ GtkRBNode *right = node->right;
+
+ g_return_if_fail (node != tree->nil);
+
+ node_height = node->offset -
+ (node->left?node->left->offset:0) -
+ (node->right?node->right->offset:0) -
+ (node->children?node->children->root->offset:0);
+ right_height = right->offset -
+ (right->left?right->left->offset:0) -
+ (right->right?right->right->offset:0) -
+ (right->children?right->children->root->offset:0);
+
+ node->right = right->left;
+ if (right->left != tree->nil)
+ right->left->parent = node;
+
+ if (right != tree->nil)
+ right->parent = node->parent;
+ if (node->parent != tree->nil)
+ {
+ if (node == node->parent->left)
+ node->parent->left = right;
+ else
+ node->parent->right = right;
+ } else {
+ tree->root = right;
+ }
+
+ right->left = node;
+ if (node != tree->nil)
+ node->parent = right;
+
+ node->count = 1 + (node->left?node->left->count:0) +
+ (node->right?node->right->count:0);
+ right->count = 1 + (right->left?right->left->count:0) +
+ (right->right?right->right->count:0);
+ node->offset = node_height +
+ (node->left?node->left->offset:0) +
+ (node->right?node->right->offset:0) +
+ (node->children?node->children->root->offset:0);
+ right->offset = right_height +
+ (right->left?right->left->offset:0) +
+ (right->right?right->right->offset:0) +
+ (right->children?right->children->root->offset:0);
+}
+
+static void
+_gtk_rbnode_rotate_right (GtkRBTree *tree,
+ GtkRBNode *node)
+{
+ gint node_height, left_height;
+ GtkRBNode *left = node->left;
+
+ g_return_if_fail (node != tree->nil);
+
+ node_height = node->offset -
+ (node->left?node->left->offset:0) -
+ (node->right?node->right->offset:0) -
+ (node->children?node->children->root->offset:0);
+ left_height = left->offset -
+ (left->left?left->left->offset:0) -
+ (left->right?left->right->offset:0) -
+ (left->children?left->children->root->offset:0);
+
+ node->left = left->right;
+ if (left->right != tree->nil)
+ left->right->parent = node;
+
+ if (left != tree->nil)
+ left->parent = node->parent;
+ if (node->parent != tree->nil)
+ {
+ if (node == node->parent->right)
+ node->parent->right = left;
+ else
+ node->parent->left = left;
+ }
+ else
+ {
+ tree->root = left;
+ }
+
+ /* link node and left */
+ left->right = node;
+ if (node != tree->nil)
+ node->parent = left;
+
+ node->count = 1 + (node->left?node->left->count:0) +
+ (node->right?node->right->count:0);
+ left->count = 1 + (left->left?left->left->count:0) +
+ (left->right?left->right->count:0);
+ node->offset = node_height +
+ (node->left?node->left->offset:0) +
+ (node->right?node->right->offset:0) +
+ (node->children?node->children->root->offset:0);
+ left->offset = left_height +
+ (left->left?left->left->offset:0) +
+ (left->right?left->right->offset:0) +
+ (left->children?left->children->root->offset:0);
+}
+
+static void
+_gtk_rbtree_insert_fixup (GtkRBTree *tree,
+ GtkRBNode *node)
+{
+
+ /* check Red-Black properties */
+ while (node != tree->root && GTK_RBNODE_GET_COLOR (node->parent) == GTK_RBNODE_RED)
+ {
+ /* we have a violation */
+ if (node->parent == node->parent->parent->left)
+ {
+ GtkRBNode *y = node->parent->parent->right;
+ if (GTK_RBNODE_GET_COLOR (y) == GTK_RBNODE_RED)
+ {
+ /* uncle is GTK_RBNODE_RED */
+ GTK_RBNODE_SET_COLOR (node->parent, GTK_RBNODE_BLACK);
+ GTK_RBNODE_SET_COLOR (y, GTK_RBNODE_BLACK);
+ GTK_RBNODE_SET_COLOR (node->parent->parent, GTK_RBNODE_RED);
+ node = node->parent->parent;
+ }
+ else
+ {
+ /* uncle is GTK_RBNODE_BLACK */
+ if (node == node->parent->right)
+ {
+ /* make node a left child */
+ node = node->parent;
+ _gtk_rbnode_rotate_left (tree, node);
+ }
+
+ /* recolor and rotate */
+ GTK_RBNODE_SET_COLOR (node->parent, GTK_RBNODE_BLACK);
+ GTK_RBNODE_SET_COLOR (node->parent->parent, GTK_RBNODE_RED);
+ _gtk_rbnode_rotate_right(tree, node->parent->parent);
+ }
+ }
+ else
+ {
+ /* mirror image of above code */
+ GtkRBNode *y = node->parent->parent->left;
+ if (GTK_RBNODE_GET_COLOR (y) == GTK_RBNODE_RED)
+ {
+ /* uncle is GTK_RBNODE_RED */
+ GTK_RBNODE_SET_COLOR (node->parent, GTK_RBNODE_BLACK);
+ GTK_RBNODE_SET_COLOR (y, GTK_RBNODE_BLACK);
+ GTK_RBNODE_SET_COLOR (node->parent->parent, GTK_RBNODE_RED);
+ node = node->parent->parent;
+ }
+ else
+ {
+ /* uncle is GTK_RBNODE_BLACK */
+ if (node == node->parent->left)
+ {
+ node = node->parent;
+ _gtk_rbnode_rotate_right (tree, node);
+ }
+ GTK_RBNODE_SET_COLOR (node->parent, GTK_RBNODE_BLACK);
+ GTK_RBNODE_SET_COLOR (node->parent->parent, GTK_RBNODE_RED);
+ _gtk_rbnode_rotate_left (tree, node->parent->parent);
+ }
+ }
+ }
+ GTK_RBNODE_SET_COLOR (tree->root, GTK_RBNODE_BLACK);
+}
+
+static void
+_gtk_rbtree_remove_node_fixup (GtkRBTree *tree,
+ GtkRBNode *node)
+{
+ while (node != tree->root && GTK_RBNODE_GET_COLOR (node) == GTK_RBNODE_BLACK)
+ {
+ if (node == node->parent->left)
+ {
+ GtkRBNode *w = node->parent->right;
+ if (GTK_RBNODE_GET_COLOR (w) == GTK_RBNODE_RED)
+ {
+ GTK_RBNODE_SET_COLOR (w, GTK_RBNODE_BLACK);
+ GTK_RBNODE_SET_COLOR (node->parent, GTK_RBNODE_RED);
+ _gtk_rbnode_rotate_left (tree, node->parent);
+ w = node->parent->right;
+ }
+ if (GTK_RBNODE_GET_COLOR (w->left) == GTK_RBNODE_BLACK && GTK_RBNODE_GET_COLOR (w->right) == GTK_RBNODE_BLACK)
+ {
+ GTK_RBNODE_SET_COLOR (w, GTK_RBNODE_RED);
+ node = node->parent;
+ }
+ else
+ {
+ if (GTK_RBNODE_GET_COLOR (w->right) == GTK_RBNODE_BLACK)
+ {
+ GTK_RBNODE_SET_COLOR (w->left, GTK_RBNODE_BLACK);
+ GTK_RBNODE_SET_COLOR (w, GTK_RBNODE_RED);
+ _gtk_rbnode_rotate_right (tree, w);
+ w = node->parent->right;
+ }
+ GTK_RBNODE_SET_COLOR (w, GTK_RBNODE_GET_COLOR (node->parent));
+ GTK_RBNODE_SET_COLOR (node->parent, GTK_RBNODE_BLACK);
+ GTK_RBNODE_SET_COLOR (w->right, GTK_RBNODE_BLACK);
+ _gtk_rbnode_rotate_left (tree, node->parent);
+ node = tree->root;
+ }
+ }
+ else
+ {
+ GtkRBNode *w = node->parent->left;
+ if (GTK_RBNODE_GET_COLOR (w) == GTK_RBNODE_RED)
+ {
+ GTK_RBNODE_SET_COLOR (w, GTK_RBNODE_BLACK);
+ GTK_RBNODE_SET_COLOR (node->parent, GTK_RBNODE_RED);
+ _gtk_rbnode_rotate_right (tree, node->parent);
+ w = node->parent->left;
+ }
+ if (GTK_RBNODE_GET_COLOR (w->right) == GTK_RBNODE_BLACK && GTK_RBNODE_GET_COLOR (w->left) == GTK_RBNODE_BLACK)
+ {
+ GTK_RBNODE_SET_COLOR (w, GTK_RBNODE_RED);
+ node = node->parent;
+ }
+ else
+ {
+ if (GTK_RBNODE_GET_COLOR (w->left) == GTK_RBNODE_BLACK)
+ {
+ GTK_RBNODE_SET_COLOR (w->right, GTK_RBNODE_BLACK);
+ GTK_RBNODE_SET_COLOR (w, GTK_RBNODE_RED);
+ _gtk_rbnode_rotate_left (tree, w);
+ w = node->parent->left;
+ }
+ GTK_RBNODE_SET_COLOR (w, GTK_RBNODE_GET_COLOR (node->parent));
+ GTK_RBNODE_SET_COLOR (node->parent, GTK_RBNODE_BLACK);
+ GTK_RBNODE_SET_COLOR (w->left, GTK_RBNODE_BLACK);
+ _gtk_rbnode_rotate_right (tree, node->parent);
+ node = tree->root;
+ }
+ }
+ }
+ GTK_RBNODE_SET_COLOR (node, GTK_RBNODE_BLACK);
+}
+
+/* Public functions */
+void
+_gtk_rbnode_push_allocator (GAllocator *allocator)
+{
+ G_LOCK (current_allocator);
+ _gtk_rbnode_validate_allocator ( allocator );
+ allocator->last = current_allocator;
+ current_allocator = allocator;
+ G_UNLOCK (current_allocator);
+}
+
+void
+_gtk_rbnode_pop_allocator (void)
+{
+ G_LOCK (current_allocator);
+ if (current_allocator)
+ {
+ GAllocator *allocator;
+
+ allocator = current_allocator;
+ current_allocator = allocator->last;
+ allocator->last = NULL;
+ allocator->is_unused = TRUE;
+ }
+ G_UNLOCK (current_allocator);
+}
+
+GtkRBTree *
+_gtk_rbtree_new (void)
+{
+ GtkRBTree *retval;
+
+ retval = (GtkRBTree *) g_new (GtkRBTree, 1);
+ retval->parent_tree = NULL;
+ retval->parent_node = NULL;
+
+ retval->nil = g_new0 (GtkRBNode, 1);
+ retval->nil->left = NULL;
+ retval->nil->right = NULL;
+ retval->nil->parent = NULL;
+ retval->nil->flags = GTK_RBNODE_BLACK;
+ retval->nil->count = 0;
+ retval->nil->offset = 0;
+
+ retval->root = retval->nil;
+ return retval;
+}
+
+static void
+_gtk_rbtree_free_helper (GtkRBTree *tree,
+ GtkRBNode *node,
+ gpointer data)
+{
+ if (node->children)
+ _gtk_rbtree_free (node->children);
+
+ _gtk_rbnode_free (node);
+}
+
+void
+_gtk_rbtree_free (GtkRBTree *tree)
+{
+ _gtk_rbtree_traverse (tree,
+ tree->root,
+ G_POST_ORDER,
+ _gtk_rbtree_free_helper,
+ NULL);
+
+ if (tree->parent_node &&
+ tree->parent_node->children == tree)
+ tree->parent_node->children = NULL;
+ _gtk_rbnode_free (tree->nil);
+ g_free (tree);
+}
+
+void
+_gtk_rbtree_remove (GtkRBTree *tree)
+{
+ GtkRBTree *tmp_tree;
+ GtkRBNode *tmp_node;
+
+ gint height = tree->root->offset;
+ tmp_tree = tree->parent_tree;
+ tmp_node = tree->parent_node;
+
+ while (tmp_tree && tmp_node && tmp_node != tmp_tree->nil)
+ {
+ tmp_node->offset -= height;
+ tmp_node = tmp_node->parent;
+ if (tmp_node == tmp_tree->nil)
+ {
+ tmp_node = tmp_tree->parent_node;
+ tmp_tree = tmp_tree->parent_tree;
+ }
+ }
+ _gtk_rbtree_free (tree);
+}
+
+
+GtkRBNode *
+_gtk_rbtree_insert_after (GtkRBTree *tree,
+ GtkRBNode *current,
+ gint height)
+{
+ GtkRBNode *node;
+ gboolean right = TRUE;
+ GtkRBNode *tmp_node;
+ GtkRBTree *tmp_tree;
+
+ if (current != NULL && current->right != tree->nil)
+ {
+ current = current->right;
+ while (current->left != tree->nil)
+ current = current->left;
+ right = FALSE;
+ }
+
+ /* setup new node */
+ node = _gtk_rbnode_new (tree, height);
+ node->parent = (current?current:tree->nil);
+
+ /* insert node in tree */
+ if (current)
+ {
+ if (right)
+ current->right = node;
+ else
+ current->left = node;
+ tmp_node = node->parent;
+ tmp_tree = tree;
+ }
+ else
+ {
+ tree->root = node;
+ tmp_node = tree->parent_node;
+ tmp_tree = tree->parent_tree;
+ }
+
+ while (tmp_tree && tmp_node && tmp_node != tmp_tree->nil)
+ {
+ /* We only want to propagate the count if we are in the tree we
+ * started in. */
+ if (tmp_tree == tree)
+ tmp_node->count++;
+ tmp_node->offset += height;
+ tmp_node = tmp_node->parent;
+ if (tmp_node == tmp_tree->nil)
+ {
+ tmp_node = tmp_tree->parent_node;
+ tmp_tree = tmp_tree->parent_tree;
+ }
+ }
+ _gtk_rbtree_insert_fixup (tree, node);
+
+ return node;
+}
+
+GtkRBNode *
+_gtk_rbtree_insert_before (GtkRBTree *tree,
+ GtkRBNode *current,
+ gint height)
+{
+ GtkRBNode *node;
+ gboolean left = TRUE;
+ GtkRBNode *tmp_node;
+ GtkRBTree *tmp_tree;
+
+ if (current != NULL && current->left != tree->nil)
+ {
+ current = current->left;
+ while (current->right != tree->nil)
+ current = current->right;
+ left = FALSE;
+ }
+
+ /* setup new node */
+ node = _gtk_rbnode_new (tree, height);
+ node->parent = (current?current:tree->nil);
+
+ /* insert node in tree */
+ if (current)
+ {
+ if (left)
+ current->left = node;
+ else
+ current->right = node;
+ tmp_node = node->parent;
+ tmp_tree = tree;
+ }
+ else
+ {
+ tree->root = node;
+ tmp_node = tree->parent_node;
+ tmp_tree = tree->parent_tree;
+ }
+
+ while (tmp_tree && tmp_node && tmp_node != tmp_tree->nil)
+ {
+ /* We only want to propagate the count if we are in the tree we
+ * started in. */
+ if (tmp_tree == tree)
+ tmp_node->count++;
+ tmp_node->offset += height;
+ tmp_node = tmp_node->parent;
+ if (tmp_node == tmp_tree->nil)
+ {
+ tmp_node = tmp_tree->parent_node;
+ tmp_tree = tmp_tree->parent_tree;
+ }
+ }
+ _gtk_rbtree_insert_fixup (tree, node);
+
+ return node;
+}
+
+GtkRBNode *
+_gtk_rbtree_find_count (GtkRBTree *tree,
+ gint count)
+{
+ GtkRBNode *node;
+
+ node = tree->root;
+ while (node != tree->nil && (node->left->count + 1 != count))
+ {
+ if (node->left->count >= count)
+ node = node->left;
+ else
+ {
+ count -= (node->left->count + 1);
+ node = node->right;
+ }
+ }
+ if (node == tree->nil)
+ return NULL;
+ return node;
+}
+
+void
+_gtk_rbtree_node_set_height (GtkRBTree *tree,
+ GtkRBNode *node,
+ gint height)
+{
+ gint diff = height - GTK_RBNODE_GET_HEIGHT (node);
+ GtkRBNode *tmp_node = node;
+ GtkRBTree *tmp_tree = tree;
+
+ if (diff == 0)
+ return;
+
+ while (tmp_tree && tmp_node && tmp_node != tmp_tree->nil)
+ {
+ tmp_node->offset += diff;
+ tmp_node = tmp_node->parent;
+ if (tmp_node == tmp_tree->nil)
+ {
+ tmp_node = tmp_tree->parent_node;
+ tmp_tree = tmp_tree->parent_tree;
+ }
+ }
+}
+
+gint
+_gtk_rbtree_node_find_offset (GtkRBTree *tree,
+ GtkRBNode *node)
+{
+ GtkRBNode *last;
+ gint retval = node->left->offset;
+
+ while (tree && node && node != tree->nil)
+ {
+ last = node;
+ node = node->parent;
+ if (node->right == last)
+ retval += node->left->offset + GTK_RBNODE_GET_HEIGHT (node);
+ if (node == tree->nil)
+ {
+ node = tree->parent_node;
+ tree = tree->parent_tree;
+ if (node)
+ retval += node->left->offset;
+ }
+ }
+ return retval;
+}
+
+gint
+_gtk_rbtree_find_offset (GtkRBTree *tree,
+ gint height,
+ GtkRBTree **new_tree,
+ GtkRBNode **new_node)
+{
+ GtkRBNode *tmp_node;
+
+ tmp_node = tree->root;
+ while (tmp_node != tree->nil &&
+ (tmp_node->left->offset > height ||
+ (tmp_node->offset - tmp_node->right->offset) < height))
+ {
+ if (tmp_node->left->offset > height)
+ tmp_node = tmp_node->left;
+ else
+ {
+ height -= (tmp_node->offset - tmp_node->right->offset);
+ tmp_node = tmp_node->right;
+ }
+ }
+ if (tmp_node == tree->nil)
+ {
+ *new_tree = NULL;
+ *new_node = NULL;
+ return 0;
+ }
+ if (tmp_node->children)
+ {
+ if ((tmp_node->offset -
+ tmp_node->right->offset -
+ tmp_node->children->root->offset) > height)
+ {
+ *new_tree = tree;
+ *new_node = tmp_node;
+ return (height - tmp_node->left->offset);
+ }
+ return _gtk_rbtree_find_offset (tmp_node->children,
+ height - tmp_node->left->offset -
+ (tmp_node->offset -
+ tmp_node->left->offset -
+ tmp_node->right->offset -
+ tmp_node->children->root->offset),
+ new_tree,
+ new_node);
+ }
+ *new_tree = tree;
+ *new_node = tmp_node;
+ return (height - tmp_node->left->offset);
+}
+
+
+void
+_gtk_rbtree_remove_node (GtkRBTree *tree,
+ GtkRBNode *node)
+{
+ GtkRBNode *x, *y;
+
+ g_return_if_fail (tree != NULL);
+ g_return_if_fail (node != NULL);
+ /* make sure we're deleting a node that's actually in the tree */
+ for (x = node; x->parent != tree->nil; x = x->parent)
+ ;
+ g_return_if_fail (x == tree->root);
+
+ if (node->left == tree->nil || node->right == tree->nil)
+ {
+ y = node;
+ }
+ else
+ {
+ y = node->right;
+
+ while (y->left != tree->nil)
+ y = y->left;
+ }
+ for (x = y; x != tree->nil; x = x->parent)
+ x->count--;
+ y->count = node->count;
+ /* x is y's only child */
+ if (y->left != tree->nil)
+ x = y->left;
+ else
+ x = y->right;
+
+ /* remove y from the parent chain */
+ x->parent = y->parent;
+ if (y->parent != tree->nil)
+ if (y == y->parent->left)
+ y->parent->left = x;
+ else
+ y->parent->right = x;
+ else
+ tree->root = x;
+
+ if (y != node)
+ node->children = y->children;
+
+ if (GTK_RBNODE_GET_COLOR (y) == GTK_RBNODE_BLACK)
+ _gtk_rbtree_remove_node_fixup (tree, x);
+
+ G_LOCK (current_allocator);
+ y->left = current_allocator->free_nodes;
+ current_allocator->free_nodes = y;
+ G_UNLOCK (current_allocator);
+}
+
+GtkRBNode *
+_gtk_rbtree_next (GtkRBTree *tree,
+ GtkRBNode *node)
+{
+ g_return_val_if_fail (tree != NULL, NULL);
+ g_return_val_if_fail (node != NULL, NULL);
+
+ /* Case 1: the node's below us. */
+ if (node->right != tree->nil)
+ {
+ node = node->right;
+ while (node->left != tree->nil)
+ node = node->left;
+ return node;
+ }
+
+ /* Case 2: it's an ancestor */
+ while (node->parent != tree->nil)
+ {
+ if (node->parent->right == node)
+ node = node->parent;
+ else
+ return (node->parent);
+ }
+
+ /* Case 3: There is no next node */
+ return NULL;
+}
+
+GtkRBNode *
+_gtk_rbtree_prev (GtkRBTree *tree,
+ GtkRBNode *node)
+{
+ g_return_val_if_fail (tree != NULL, NULL);
+ g_return_val_if_fail (node != NULL, NULL);
+
+ /* Case 1: the node's below us. */
+ if (node->left != tree->nil)
+ {
+ node = node->left;
+ while (node->right != tree->nil)
+ node = node->right;
+ return node;
+ }
+
+ /* Case 2: it's an ancestor */
+ while (node->parent != tree->nil)
+ {
+ if (node->parent->left == node)
+ node = node->parent;
+ else
+ return (node->parent);
+ }
+
+ /* Case 3: There is no next node */
+ return NULL;
+}
+
+void
+_gtk_rbtree_next_full (GtkRBTree *tree,
+ GtkRBNode *node,
+ GtkRBTree **new_tree,
+ GtkRBNode **new_node)
+{
+ g_return_if_fail (tree != NULL);
+ g_return_if_fail (node != NULL);
+ g_return_if_fail (new_tree != NULL);
+ g_return_if_fail (new_node != NULL);
+
+ if (node->children)
+ {
+ *new_tree = node->children;
+ *new_node = (*new_tree)->root;
+ while ((*new_node)->left != (*new_tree)->nil)
+ *new_node = (*new_node)->left;
+ return;
+ }
+
+ *new_tree = tree;
+ *new_node = _gtk_rbtree_next (tree, node);
+
+ while ((*new_node == NULL) &&
+ (*new_tree != NULL))
+ {
+ *new_node = (*new_tree)->parent_node;
+ *new_tree = (*new_tree)->parent_tree;
+ if (*new_tree)
+ *new_node = _gtk_rbtree_next (*new_tree, *new_node);
+ }
+}
+
+void
+_gtk_rbtree_prev_full (GtkRBTree *tree,
+ GtkRBNode *node,
+ GtkRBTree **new_tree,
+ GtkRBNode **new_node)
+{
+ g_return_if_fail (tree != NULL);
+ g_return_if_fail (node != NULL);
+ g_return_if_fail (new_tree != NULL);
+ g_return_if_fail (new_node != NULL);
+
+ *new_tree = tree;
+ *new_node = _gtk_rbtree_prev (tree, node);
+
+ if (*new_node == NULL)
+ {
+ *new_node = (*new_tree)->parent_node;
+ *new_tree = (*new_tree)->parent_tree;
+ }
+ else
+ {
+ while ((*new_node)->children)
+ {
+ *new_tree = (*new_node)->children;
+ *new_node = (*new_tree)->root;
+ while ((*new_node)->right != (*new_tree)->nil)
+ *new_node = (*new_node)->right;
+ }
+ }
+}
+
+static void
+_gtk_rbtree_traverse_pre_order (GtkRBTree *tree,
+ GtkRBNode *node,
+ GtkRBTreeTraverseFunc func,
+ gpointer data)
+{
+ if (node == tree->nil)
+ return;
+
+ (* func) (tree, node, data);
+ _gtk_rbtree_traverse_pre_order (tree, node->left, func, data);
+ _gtk_rbtree_traverse_pre_order (tree, node->right, func, data);
+}
+
+static void
+_gtk_rbtree_traverse_post_order (GtkRBTree *tree,
+ GtkRBNode *node,
+ GtkRBTreeTraverseFunc func,
+ gpointer data)
+{
+ if (node == tree->nil)
+ return;
+
+ _gtk_rbtree_traverse_post_order (tree, node->left, func, data);
+ _gtk_rbtree_traverse_post_order (tree, node->right, func, data);
+ (* func) (tree, node, data);
+}
+
+void
+_gtk_rbtree_traverse (GtkRBTree *tree,
+ GtkRBNode *node,
+ GTraverseType order,
+ GtkRBTreeTraverseFunc func,
+ gpointer data)
+{
+ g_return_if_fail (tree != NULL);
+ g_return_if_fail (node != NULL);
+ g_return_if_fail (func != NULL);
+ g_return_if_fail (order <= G_LEVEL_ORDER);
+
+ switch (order)
+ {
+ case G_PRE_ORDER:
+ _gtk_rbtree_traverse_pre_order (tree, node, func, data);
+ break;
+ case G_POST_ORDER:
+ _gtk_rbtree_traverse_post_order (tree, node, func, data);
+ break;
+ case G_IN_ORDER:
+ case G_LEVEL_ORDER:
+ default:
+ g_warning ("unsupported traversal order.");
+ break;
+ }
+}
+
+static gint
+_count_nodes (GtkRBTree *tree,
+ GtkRBNode *node)
+{
+ gint res;
+ if (node == tree->nil)
+ return 0;
+
+ res = (_count_nodes (tree, node->left) +
+ _count_nodes (tree, node->right) + 1);
+
+ if (res != node->count)
+ g_print ("Tree failed\n");
+ return res;
+}
+
+void
+_gtk_rbtree_test (GtkRBTree *tree)
+{
+ if ((_count_nodes (tree, tree->root->left) +
+ _count_nodes (tree, tree->root->right) + 1) == tree->root->count)
+ g_print ("Tree passed\n");
+ else
+ g_print ("Tree failed\n");
+
+}
+
+static void
+_gtk_rbtree_test_height_helper (GtkRBTree *tree,
+ GtkRBNode *node,
+ gint height)
+{
+ if (node == tree->nil)
+ return;
+
+ if (node->offset -
+ (node->left?node->left->offset:0) -
+ (node->right?node->right->offset:0) -
+ (node->children?node->children->root->offset:0) != height)
+ g_error ("tree failed\n");
+
+ _gtk_rbtree_test_height_helper (tree, node->left, height);
+ _gtk_rbtree_test_height_helper (tree, node->right, height);
+ if (node->children)
+ _gtk_rbtree_test_height_helper (node->children, node->children->root, height);
+
+}
+
+void
+_gtk_rbtree_test_height (GtkRBTree *tree,
+ gint height)
+{
+ _gtk_rbtree_test_height_helper (tree, tree->root, height);
+}
diff --git a/gtk/gtkrbtree.h b/gtk/gtkrbtree.h
new file mode 100644
index 0000000000..9446510d56
--- /dev/null
+++ b/gtk/gtkrbtree.h
@@ -0,0 +1,133 @@
+/* gtkrbtree.h
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_RBTREE_H__
+#define __GTK_RBTREE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include <glib.h>
+typedef enum
+{
+ GTK_RBNODE_BLACK = 1 << 0,
+ GTK_RBNODE_RED = 1 << 1,
+ GTK_RBNODE_IS_PARENT = 1 << 2,
+ GTK_RBNODE_IS_SELECTED = 1 << 3,
+ GTK_RBNODE_IS_PRELIT = 1 << 4,
+ GTK_RBNODE_IS_VIEW = 1 << 5
+} GtkRBNodeColor;
+
+typedef struct _GtkRBTree GtkRBTree;
+typedef struct _GtkRBNode GtkRBNode;
+typedef struct _GtkRBTreeView GtkRBTreeView;
+
+typedef void (*GtkRBTreeTraverseFunc) (GtkRBTree *tree,
+ GtkRBNode *node,
+ gpointer data);
+
+struct _GtkRBTree
+{
+ GtkRBNode *root;
+ GtkRBNode *nil;
+ GtkRBTree *parent_tree;
+ GtkRBNode *parent_node;
+};
+
+struct _GtkRBNode
+{
+ guint flags;
+ GtkRBNode *left;
+ GtkRBNode *right;
+ GtkRBNode *parent;
+ gint count; /* aggregate number of children we have */
+ gint offset; /* aggregate of the heights of all our children */
+ GtkRBTree *children;
+};
+
+struct _GtkRBNodeView
+{
+ GtkRBNode parent;
+ gint offset;
+ GtkRBTree *children;
+};
+
+#define GTK_RBNODE_GET_COLOR(node) (node?(((node->flags&GTK_RBNODE_RED)==GTK_RBNODE_RED)?GTK_RBNODE_RED:GTK_RBNODE_BLACK):GTK_RBNODE_BLACK)
+#define GTK_RBNODE_SET_COLOR(node,color) if((node->flags&color)!=color)node->flags=node->flags^(GTK_RBNODE_RED|GTK_RBNODE_BLACK)
+#define GTK_RBNODE_GET_HEIGHT(node) (node->offset-(node->left->offset+node->right->offset+(node->children?node->children->root->offset:0)))
+#define GTK_RBNODE_SET_FLAG(node, flag) G_STMT_START{ (node->flags|=flag); }G_STMT_END
+#define GTK_RBNODE_UNSET_FLAG(node, flag) G_STMT_START{ (node->flags&=~(flag)); }G_STMT_END
+#define GTK_RBNODE_FLAG_SET(node, flag) (node?(((node->flags&flag)==flag)?TRUE:FALSE):FALSE)
+
+
+void _gtk_rbtree_push_allocator (GAllocator *allocator);
+void _gtk_rbtree_pop_allocator (void);
+GtkRBTree *_gtk_rbtree_new (void);
+void _gtk_rbtree_free (GtkRBTree *tree);
+void _gtk_rbtree_remove (GtkRBTree *tree);
+void _gtk_rbtree_destroy (GtkRBTree *tree);
+GtkRBNode *_gtk_rbtree_insert_before (GtkRBTree *tree,
+ GtkRBNode *node,
+ gint height);
+GtkRBNode *_gtk_rbtree_insert_after (GtkRBTree *tree,
+ GtkRBNode *node,
+ gint height);
+void _gtk_rbtree_remove_node (GtkRBTree *tree,
+ GtkRBNode *node);
+GtkRBNode *_gtk_rbtree_find_count (GtkRBTree *tree,
+ gint count);
+void _gtk_rbtree_node_set_height (GtkRBTree *tree,
+ GtkRBNode *node,
+ gint height);
+gint _gtk_rbtree_node_find_offset (GtkRBTree *tree,
+ GtkRBNode *node);
+gint _gtk_rbtree_find_offset (GtkRBTree *tree,
+ gint offset,
+ GtkRBTree **new_tree,
+ GtkRBNode **new_node);
+void _gtk_rbtree_traverse (GtkRBTree *tree,
+ GtkRBNode *node,
+ GTraverseType order,
+ GtkRBTreeTraverseFunc func,
+ gpointer data);
+GtkRBNode *_gtk_rbtree_next (GtkRBTree *tree,
+ GtkRBNode *node);
+GtkRBNode *_gtk_rbtree_prev (GtkRBTree *tree,
+ GtkRBNode *node);
+void _gtk_rbtree_next_full (GtkRBTree *tree,
+ GtkRBNode *node,
+ GtkRBTree **new_tree,
+ GtkRBNode **new_node);
+void _gtk_rbtree_prev_full (GtkRBTree *tree,
+ GtkRBNode *node,
+ GtkRBTree **new_tree,
+ GtkRBNode **new_node);
+
+
+/* This func just checks the integrity of the tree */
+/* It will go away later. */
+void _gtk_rbtree_test (GtkRBTree *tree);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __GTK_RBTREE_H__ */
diff --git a/gtk/gtktreedatalist.c b/gtk/gtktreedatalist.c
new file mode 100644
index 0000000000..47f022fc1c
--- /dev/null
+++ b/gtk/gtktreedatalist.c
@@ -0,0 +1,159 @@
+/* gtktreedatalist.h
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gtktreedatalist.h"
+#include "gobject/gvalue.h"
+
+/* node allocation
+ */
+struct _GAllocator /* from gmem.c */
+{
+ gchar *name;
+ guint16 n_preallocs;
+ guint is_unused : 1;
+ guint type : 4;
+ GAllocator *last;
+ GMemChunk *mem_chunk;
+ GtkTreeDataList *free_nodes;
+};
+
+
+G_LOCK_DEFINE_STATIC (current_allocator);
+static GAllocator *current_allocator = NULL;
+
+/* HOLDS: current_allocator_lock */
+static void
+gtk_tree_data_list_validate_allocator (GAllocator *allocator)
+{
+ g_return_if_fail (allocator != NULL);
+ g_return_if_fail (allocator->is_unused == TRUE);
+
+ if (allocator->type != G_ALLOCATOR_NODE)
+ {
+ allocator->type = G_ALLOCATOR_NODE;
+ if (allocator->mem_chunk)
+ {
+ g_mem_chunk_destroy (allocator->mem_chunk);
+ allocator->mem_chunk = NULL;
+ }
+ }
+
+ if (!allocator->mem_chunk)
+ {
+ allocator->mem_chunk = g_mem_chunk_new (allocator->name,
+ sizeof (GtkTreeDataList),
+ sizeof (GtkTreeDataList) * allocator->n_preallocs,
+ G_ALLOC_ONLY);
+ allocator->free_nodes = NULL;
+ }
+
+ allocator->is_unused = FALSE;
+}
+
+void
+gtk_tree_data_list_push_allocator (GAllocator *allocator)
+{
+ G_LOCK (current_allocator);
+ gtk_tree_data_list_validate_allocator ( allocator );
+ allocator->last = current_allocator;
+ current_allocator = allocator;
+ G_UNLOCK (current_allocator);
+}
+
+void
+gtk_tree_data_list_pop_allocator (void)
+{
+ G_LOCK (current_allocator);
+ if (current_allocator)
+ {
+ GAllocator *allocator;
+
+ allocator = current_allocator;
+ current_allocator = allocator->last;
+ allocator->last = NULL;
+ allocator->is_unused = TRUE;
+ }
+ G_UNLOCK (current_allocator);
+}
+
+GtkTreeDataList *
+gtk_tree_data_list_alloc (void)
+{
+ GtkTreeDataList *list;
+
+ G_LOCK (current_allocator);
+ if (!current_allocator)
+ {
+ GAllocator *allocator = g_allocator_new ("GTK+ default GtkTreeDataList allocator",
+ 128);
+ gtk_tree_data_list_validate_allocator (allocator);
+ allocator->last = NULL;
+ current_allocator = allocator;
+ }
+ if (!current_allocator->free_nodes)
+ list = g_chunk_new (GtkTreeDataList, current_allocator->mem_chunk);
+ else
+ {
+ list = current_allocator->free_nodes;
+ current_allocator->free_nodes = list->next;
+ }
+ G_UNLOCK (current_allocator);
+
+ return list;
+}
+
+void
+gtk_tree_data_list_free (GtkTreeDataList *list)
+{
+ G_LOCK (current_allocator);
+ list->next = current_allocator->free_nodes;
+ current_allocator->free_nodes = list;
+ G_UNLOCK (current_allocator);
+}
+
+void
+gtk_tree_data_list_node_to_value (GtkTreeDataList *list,
+ GType type,
+ GValue *value)
+{
+ g_value_init (value, type);
+
+ switch (type)
+ {
+ case G_TYPE_STRING:
+ g_value_set_string (value, (gchar *) list->data.v_pointer);
+ break;
+ }
+}
+
+void
+gtk_tree_data_list_value_to_node (GtkTreeDataList *list,
+ GValue *value)
+{
+ switch (value->g_type)
+ {
+ case G_TYPE_STRING:
+ list->data.v_pointer = g_value_dup_string (value);
+ break;
+ default:
+ g_warning ("Unsupported type (%s) stored.", g_type_name (value->g_type));
+ return;
+ }
+}
+
diff --git a/gtk/gtktreedatalist.h b/gtk/gtktreedatalist.h
new file mode 100644
index 0000000000..823349c48f
--- /dev/null
+++ b/gtk/gtktreedatalist.h
@@ -0,0 +1,53 @@
+/* gtktreedatalist.h
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __GTK_TREE_DATA_LIST_H__
+#define __GTK_TREE_DATA_LIST_H__
+
+#include <glib.h>
+#include <gobject/gobject.h>
+
+typedef struct _GtkTreeDataList GtkTreeDataList;
+struct _GtkTreeDataList
+{
+ GtkTreeDataList *next;
+
+ union {
+ gint v_int;
+ guint v_uint;
+ gfloat v_float;
+ gpointer v_pointer;
+ } data;
+};
+
+/* FIXME: s/gtk/_gtk/g to make internal */
+void gtk_tree_data_list_push_allocator (GAllocator *allocator);
+void gtk_tree_data_list_pop_allocator (void);
+GtkTreeDataList *gtk_tree_data_list_alloc (void);
+void gtk_tree_data_list_free (GtkTreeDataList *list);
+
+void gtk_tree_data_list_node_to_value (GtkTreeDataList *list,
+ GType type,
+ GValue *value);
+void gtk_tree_data_list_value_to_node (GtkTreeDataList *list,
+ GValue *value);
+
+
+#endif /* __GTK_TREE_DATA_LIST_H__ */
diff --git a/gtk/gtktreemodel.c b/gtk/gtktreemodel.c
new file mode 100644
index 0000000000..2177346583
--- /dev/null
+++ b/gtk/gtktreemodel.c
@@ -0,0 +1,421 @@
+/* gtktreemodel.c
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "gtktreemodel.h"
+#include "gtksignal.h"
+
+enum {
+ NODE_CHANGED,
+ NODE_INSERTED,
+ NODE_CHILD_TOGGLED,
+ NODE_DELETED,
+ LAST_SIGNAL
+};
+
+struct _GtkTreePath
+{
+ gint depth;
+ gint *indices;
+};
+
+static void gtk_tree_model_init (GtkTreeModel *tree_model);
+static void gtk_tree_model_class_init (GtkTreeModelClass *klass);
+
+static GtkObjectClass *parent_class = NULL;
+static guint tree_model_signals[LAST_SIGNAL] = { 0 };
+
+
+GtkType
+gtk_tree_model_get_type (void)
+{
+ static GtkType tree_model_type = 0;
+
+ if (!tree_model_type)
+ {
+ static const GTypeInfo tree_model_info =
+ {
+ sizeof (GtkTreeModelClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) gtk_tree_model_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GtkTreeModel),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gtk_tree_model_init
+ };
+
+ tree_model_type = g_type_register_static (GTK_TYPE_OBJECT, "GtkTreeModel", &tree_model_info);
+ }
+
+ return tree_model_type;
+}
+
+static void
+gtk_tree_model_class_init (GtkTreeModelClass *class)
+{
+ GtkObjectClass *object_class;
+
+ object_class = (GtkObjectClass*) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ tree_model_signals[NODE_CHANGED] =
+ gtk_signal_new ("node_changed",
+ GTK_RUN_FIRST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkTreeModelClass, node_changed),
+ gtk_marshal_NONE__POINTER_POINTER,
+ GTK_TYPE_NONE, 2,
+ GTK_TYPE_POINTER,
+ GTK_TYPE_POINTER);
+ tree_model_signals[NODE_INSERTED] =
+ gtk_signal_new ("node_inserted",
+ GTK_RUN_FIRST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkTreeModelClass, node_inserted),
+ gtk_marshal_NONE__POINTER_POINTER,
+ GTK_TYPE_NONE, 2,
+ GTK_TYPE_POINTER,
+ GTK_TYPE_POINTER);
+ tree_model_signals[NODE_CHILD_TOGGLED] =
+ gtk_signal_new ("node_child_toggled",
+ GTK_RUN_FIRST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkTreeModelClass, node_child_toggled),
+ gtk_marshal_NONE__POINTER_POINTER,
+ GTK_TYPE_NONE, 2,
+ GTK_TYPE_POINTER,
+ GTK_TYPE_POINTER);
+ tree_model_signals[NODE_DELETED] =
+ gtk_signal_new ("node_deleted",
+ GTK_RUN_FIRST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkTreeModelClass, node_deleted),
+ gtk_marshal_NONE__POINTER,
+ GTK_TYPE_NONE, 1,
+ GTK_TYPE_POINTER);
+
+
+ gtk_object_class_add_signals (object_class, tree_model_signals, LAST_SIGNAL);
+
+
+ class->get_node = NULL;
+ class->node_next = NULL;
+ class->node_children = NULL;
+ class->node_n_children = NULL;
+ class->node_nth_child = NULL;
+ class->node_parent = NULL;
+}
+
+
+static void
+gtk_tree_model_init (GtkTreeModel *tree_model)
+{
+
+}
+
+/* GtkTreePath Operations */
+GtkTreePath *
+gtk_tree_path_new (void)
+{
+ GtkTreePath *retval;
+ retval = (GtkTreePath *) g_new (GtkTreePath, 1);
+ retval->depth = 0;
+ retval->indices = NULL;
+
+ return retval;
+}
+
+GtkTreePath *
+gtk_tree_path_new_from_string (gchar *path)
+{
+ GtkTreePath *retval;
+ gchar *ptr;
+ gint i;
+
+ g_return_val_if_fail (path != NULL, gtk_tree_path_new ());
+
+ retval = gtk_tree_path_new ();
+
+ while (1)
+ {
+ i = strtol (path, &ptr, 10);
+ gtk_tree_path_append_index (retval, i);
+
+ if (*ptr == '\000')
+ break;
+ path = ptr + 1;
+ }
+
+ return retval;
+}
+
+gchar *
+gtk_tree_path_to_string (GtkTreePath *path)
+{
+ gchar *retval, *ptr;
+ gint i;
+
+ if (path->depth == 0)
+ return NULL;
+
+ ptr = retval = (gchar *) g_new0 (char *, path->depth*8);
+ sprintf (retval, "%d", path->indices[0]);
+ while (*ptr != '\000')
+ ptr++;
+
+ for (i = 1; i < path->depth; i++)
+ {
+ sprintf (ptr, ":%d", path->indices[i]);
+ while (*ptr != '\000')
+ ptr++;
+ }
+
+ return retval;
+}
+
+GtkTreePath *
+gtk_tree_path_new_root (void)
+{
+ GtkTreePath *retval;
+
+ retval = gtk_tree_path_new ();
+ gtk_tree_path_append_index (retval, 0);
+
+ return retval;
+}
+
+void
+gtk_tree_path_append_index (GtkTreePath *path,
+ gint index)
+{
+ gint *new_indices = g_new (gint, ++path->depth);
+ if (path->indices == NULL)
+ {
+ path->indices = new_indices;
+ path->indices[0] = index;
+ return;
+ }
+
+ memcpy (new_indices, path->indices, (path->depth - 1)*sizeof (gint));
+ g_free (path->indices);
+ path->indices = new_indices;
+ path->indices[path->depth - 1] = index;
+}
+
+void
+gtk_tree_path_prepend_index (GtkTreePath *path,
+ gint index)
+{
+ gint *new_indices = g_new (gint, ++path->depth);
+ if (path->indices == NULL)
+ {
+ path->indices = new_indices;
+ path->indices[0] = index;
+ return;
+ }
+ memcpy (new_indices + 1, path->indices, (path->depth - 1)*sizeof (gint));
+ g_free (path->indices);
+ path->indices = new_indices;
+ path->indices[0] = index;
+}
+
+gint
+gtk_tree_path_get_depth (GtkTreePath *path)
+{
+ return path->depth;
+}
+
+gint *
+gtk_tree_path_get_indices (GtkTreePath *path)
+{
+ return path->indices;
+}
+
+void
+gtk_tree_path_free (GtkTreePath *path)
+{
+ g_free (path->indices);
+ g_free (path);
+}
+
+GtkTreePath *
+gtk_tree_path_copy (GtkTreePath *path)
+{
+ GtkTreePath *retval;
+
+ retval = g_new (GtkTreePath, 1);
+ retval->depth = path->depth;
+ retval->indices = g_new (gint, path->depth);
+ memcpy (retval->indices, path->indices, path->depth * sizeof (gint));
+ return retval;
+}
+
+gint
+gtk_tree_path_compare (GtkTreePath *a,
+ GtkTreePath *b)
+{
+ gint p = 0, q = 0;
+
+ g_return_val_if_fail (a != NULL, 0);
+ g_return_val_if_fail (b != NULL, 0);
+ g_return_val_if_fail (a->depth > 0, 0);
+ g_return_val_if_fail (b->depth > 0, 0);
+
+ do
+ {
+ if (a->indices[p] == b->indices[q])
+ continue;
+ return (a->indices[p] < b->indices[q]?1:-1);
+ }
+ while (++p < a->depth && ++q < b->depth);
+ if (a->depth == b->depth)
+ return 0;
+ return (a->depth < b->depth?1:-1);
+}
+
+void
+gtk_tree_path_next (GtkTreePath *path)
+{
+ g_return_if_fail (path != NULL);
+
+ path->indices[path->depth - 1] ++;
+}
+
+gint
+gtk_tree_path_prev (GtkTreePath *path)
+{
+ g_return_val_if_fail (path != NULL, FALSE);
+
+ if (path->indices[path->depth] == 0)
+ return FALSE;
+
+ path->indices[path->depth - 1] --;
+
+ return TRUE;
+}
+
+gint
+gtk_tree_path_up (GtkTreePath *path)
+{
+ g_return_val_if_fail (path != NULL, FALSE);
+
+ if (path->depth == 1)
+ return FALSE;
+
+ path->depth--;
+
+ return TRUE;
+}
+
+void
+gtk_tree_path_down (GtkTreePath *path)
+{
+ g_return_if_fail (path != NULL);
+
+ gtk_tree_path_append_index (path, 0);
+}
+
+gint
+gtk_tree_model_get_n_columns (GtkTreeModel *tree_model)
+{
+ g_return_val_if_fail (GTK_TREE_MODEL_GET_CLASS (tree_model)->get_n_columns != NULL, 0);
+ return (* GTK_TREE_MODEL_GET_CLASS (tree_model)->get_n_columns) (tree_model);
+}
+
+/* Node options */
+GtkTreeNode
+gtk_tree_model_get_node (GtkTreeModel *tree_model,
+ GtkTreePath *path)
+{
+ g_return_val_if_fail (GTK_TREE_MODEL_GET_CLASS (tree_model)->get_node != NULL, NULL);
+ return (* GTK_TREE_MODEL_GET_CLASS (tree_model)->get_node) (tree_model, path);
+}
+
+GtkTreePath *
+gtk_tree_model_get_path (GtkTreeModel *tree_model,
+ GtkTreeNode node)
+{
+ g_return_val_if_fail (GTK_TREE_MODEL_GET_CLASS (tree_model)->get_path != NULL, NULL);
+ return (* GTK_TREE_MODEL_GET_CLASS (tree_model)->get_path) (tree_model, node);
+}
+
+void
+gtk_tree_model_node_get_value (GtkTreeModel *tree_model,
+ GtkTreeNode node,
+ gint column,
+ GValue *value)
+{
+ g_return_if_fail (GTK_TREE_MODEL_GET_CLASS (tree_model)->node_get_value != NULL);
+ (* GTK_TREE_MODEL_GET_CLASS (tree_model)->node_get_value) (tree_model, node, column, value);
+}
+
+gboolean
+gtk_tree_model_node_next (GtkTreeModel *tree_model,
+ GtkTreeNode *node)
+{
+ g_return_val_if_fail (GTK_TREE_MODEL_GET_CLASS (tree_model)->node_next != NULL, FALSE);
+ return (* GTK_TREE_MODEL_GET_CLASS (tree_model)->node_next) (tree_model, node);
+}
+
+GtkTreeNode
+gtk_tree_model_node_children (GtkTreeModel *tree_model,
+ GtkTreeNode node)
+{
+ g_return_val_if_fail (GTK_TREE_MODEL_GET_CLASS (tree_model)->node_children != NULL, NULL);
+ return (* GTK_TREE_MODEL_GET_CLASS (tree_model)->node_children) (tree_model, node);
+}
+
+gboolean
+gtk_tree_model_node_has_child (GtkTreeModel *tree_model,
+ GtkTreeNode node)
+{
+ g_return_val_if_fail (GTK_TREE_MODEL_GET_CLASS (tree_model)->node_has_child != NULL, FALSE);
+ return (* GTK_TREE_MODEL_GET_CLASS (tree_model)->node_has_child) (tree_model, node);
+}
+
+gint
+gtk_tree_model_node_n_children (GtkTreeModel *tree_model,
+ GtkTreeNode node)
+{
+ g_return_val_if_fail (GTK_TREE_MODEL_GET_CLASS (tree_model)->node_n_children != NULL, -1);
+ return (* GTK_TREE_MODEL_GET_CLASS (tree_model)->node_n_children) (tree_model, node);
+}
+
+GtkTreeNode
+gtk_tree_model_node_nth_child (GtkTreeModel *tree_model,
+ GtkTreeNode node,
+ gint n)
+{
+ g_return_val_if_fail (GTK_TREE_MODEL_GET_CLASS (tree_model)->node_nth_child != NULL, NULL);
+ return (* GTK_TREE_MODEL_GET_CLASS (tree_model)->node_nth_child) (tree_model, node, n);
+}
+
+GtkTreeNode
+gtk_tree_model_node_parent (GtkTreeModel *tree_model,
+ GtkTreeNode node)
+{
+ g_return_val_if_fail (GTK_TREE_MODEL_GET_CLASS (tree_model)->node_parent != NULL, NULL);
+ return (* GTK_TREE_MODEL_GET_CLASS (tree_model)->node_parent) (tree_model, node);
+}
+
diff --git a/gtk/gtktreemodel.h b/gtk/gtktreemodel.h
new file mode 100644
index 0000000000..e70034943a
--- /dev/null
+++ b/gtk/gtktreemodel.h
@@ -0,0 +1,144 @@
+/* gtktreemodel.h
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_TREE_MODEL_H__
+#define __GTK_TREE_MODEL_H__
+
+#include <gtk/gtkobject.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define GTK_TYPE_TREE_MODEL (gtk_tree_model_get_type ())
+#define GTK_TREE_MODEL(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_TREE_MODEL, GtkTreeModel))
+#define GTK_TREE_MODEL_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_TREE_MODEL, GtkTreeModelClass))
+#define GTK_IS_TREE_MODEL(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_TREE_MODEL))
+#define GTK_IS_TREE_MODEL_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), GTK_TYPE_TREE_MODEL))
+#define GTK_TREE_MODEL_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_TREE_MODEL, GtkTreeModelClass))
+
+typedef gpointer GtkTreeNode;
+typedef struct _GtkTreePath GtkTreePath;
+typedef struct _GtkTreeModel GtkTreeModel;
+typedef struct _GtkTreeModelClass GtkTreeModelClass;
+
+struct _GtkTreeModel
+{
+ GtkObject parent;
+};
+
+struct _GtkTreeModelClass
+{
+ GtkObjectClass parent_class;
+
+ /* signals */
+ void (* node_changed) (GtkTreeModel *tree_model,
+ GtkTreePath *path,
+ GtkTreeNode *node);
+ void (* node_inserted) (GtkTreeModel *tree_model,
+ GtkTreePath *path,
+ GtkTreeNode *node);
+ void (* node_child_toggled) (GtkTreeModel *tree_model,
+ GtkTreePath *path,
+ GtkTreeNode *node);
+ void (* node_deleted) (GtkTreeModel *tree_model,
+ GtkTreePath *path);
+
+ /* VTable - not signals */
+ gint (* get_n_columns) (GtkTreeModel *tree_model);
+ GtkTreeNode (* get_node) (GtkTreeModel *tree_model,
+ GtkTreePath *path);
+ GtkTreePath *(* get_path) (GtkTreeModel *tree_model,
+ GtkTreeNode node);
+ void (* node_get_value) (GtkTreeModel *tree_model,
+ GtkTreeNode node,
+ gint column,
+ GValue *value);
+ gboolean (* node_next) (GtkTreeModel *tree_model,
+ GtkTreeNode *node);
+ GtkTreeNode (* node_children) (GtkTreeModel *tree_model,
+ GtkTreeNode node);
+ gboolean (* node_has_child) (GtkTreeModel *tree_model,
+ GtkTreeNode node);
+ gint (* node_n_children) (GtkTreeModel *tree_model,
+ GtkTreeNode node);
+ GtkTreeNode (* node_nth_child) (GtkTreeModel *tree_model,
+ GtkTreeNode node,
+ gint n);
+ GtkTreeNode (* node_parent) (GtkTreeModel *tree_model,
+ GtkTreeNode node);
+};
+
+
+/* Basic tree_model operations */
+GtkType gtk_tree_model_get_type (void);
+
+/* GtkTreePath Operations */
+GtkTreePath *gtk_tree_path_new (void);
+GtkTreePath *gtk_tree_path_new_from_string (gchar *path);
+gchar *gtk_tree_path_to_string (GtkTreePath *path);
+GtkTreePath *gtk_tree_path_new_root (void);
+void gtk_tree_path_append_index (GtkTreePath *path,
+ gint index);
+void gtk_tree_path_prepend_index (GtkTreePath *path,
+ gint index);
+gint gtk_tree_path_get_depth (GtkTreePath *path);
+gint *gtk_tree_path_get_indices (GtkTreePath *path);
+void gtk_tree_path_free (GtkTreePath *path);
+GtkTreePath *gtk_tree_path_copy (GtkTreePath *path);
+gint gtk_tree_path_compare (GtkTreePath *a,
+ GtkTreePath *b);
+void gtk_tree_path_next (GtkTreePath *path);
+gint gtk_tree_path_prev (GtkTreePath *path);
+gint gtk_tree_path_up (GtkTreePath *path);
+void gtk_tree_path_down (GtkTreePath *path);
+
+/* Header operations */
+gint gtk_tree_model_get_n_columns (GtkTreeModel *tree_model);
+
+/* Node operations */
+GtkTreeNode gtk_tree_model_get_node (GtkTreeModel *tree_model,
+ GtkTreePath *path);
+GtkTreePath *gtk_tree_model_get_path (GtkTreeModel *tree_model,
+ GtkTreeNode node);
+void gtk_tree_model_node_get_value (GtkTreeModel *tree_model,
+ GtkTreeNode node,
+ gint column,
+ GValue *value);
+gboolean gtk_tree_model_node_next (GtkTreeModel *tree_model,
+ GtkTreeNode *node);
+GtkTreeNode gtk_tree_model_node_children (GtkTreeModel *tree_model,
+ GtkTreeNode node);
+gboolean gtk_tree_model_node_has_child (GtkTreeModel *tree_model,
+ GtkTreeNode node);
+gint gtk_tree_model_node_n_children (GtkTreeModel *tree_model,
+ GtkTreeNode node);
+GtkTreeNode gtk_tree_model_node_nth_child (GtkTreeModel *tree_model,
+ GtkTreeNode node,
+ gint n);
+GtkTreeNode gtk_tree_model_node_parent (GtkTreeModel *tree_model,
+ GtkTreeNode node);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_TREE_MODEL_H__ */
diff --git a/gtk/gtktreeprivate.h b/gtk/gtktreeprivate.h
new file mode 100644
index 0000000000..0274f32893
--- /dev/null
+++ b/gtk/gtktreeprivate.h
@@ -0,0 +1,191 @@
+/* gtktreeprivate.h
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_TREE_PRIVATE_H__
+#define __GTK_TREE_PRIVATE_H__
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include <gtk/gtktreeview.h>
+#include <gtk/gtktreeselection.h>
+#include <gtk/gtkrbtree.h>
+
+typedef enum {
+ GTK_TREE_VIEW_IS_LIST = 1 << 0,
+ GTK_TREE_VIEW_SHOW_EXPANDERS = 1 << 1,
+ GTK_TREE_VIEW_IN_COLUMN_RESIZE = 1 << 2,
+ GTK_TREE_VIEW_ARROW_PRELIT = 1 << 3,
+ GTK_TREE_VIEW_HEADERS_VISIBLE = 1 << 4,
+ GTK_TREE_VIEW_DRAW_KEYFOCUS = 1 << 5,
+ GTK_TREE_VIEW_MODEL_SETUP = 1 << 6
+} GtkTreeViewFlags;
+
+#define GTK_TREE_VIEW_SET_FLAG(tree_view, flag) G_STMT_START{ (tree_view->priv->flags|=flag); }G_STMT_END
+#define GTK_TREE_VIEW_UNSET_FLAG(tree_view, flag) G_STMT_START{ (tree_view->priv->flags&=~(flag)); }G_STMT_END
+#define GTK_TREE_VIEW_FLAG_SET(tree_view, flag) ((tree_view->priv->flags&flag)==flag)
+#define TREE_VIEW_HEADER_HEIGHT(tree_view) (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE)?tree_view->priv->header_height:0)
+#define TREE_VIEW_COLUMN_SIZE(column) (CLAMP (column->size, (column->min_width!=-1)?column->min_width:column->size, (column->max_width!=-1)?column->max_width:column->size))
+#define TREE_VIEW_DRAW_EXPANDERS(tree_view) (!GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IS_LIST)&&GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_SHOW_EXPANDERS))
+
+struct _GtkTreeViewPrivate
+{
+ GtkTreeModel *model;
+
+ guint flags;
+ /* tree information */
+ GtkRBTree *tree;
+
+ gint tab_offset;
+ GtkRBNode *button_pressed_node;
+ GtkRBTree *button_pressed_tree;
+
+ GList *children;
+ gint width;
+ gint height;
+
+ GtkAdjustment *hadjustment;
+ GtkAdjustment *vadjustment;
+
+ GdkWindow *bin_window;
+ GdkWindow *header_window;
+
+ /* Selection stuff */
+ GtkTreePath *anchor;
+ GtkTreePath *cursor;
+
+ /* Column Resizing */
+ GdkCursor *cursor_drag;
+ GdkGC *xor_gc;
+ gint drag_pos;
+ gint x_drag;
+
+ /* Prelight information */
+ GtkRBNode *prelight_node;
+ GtkRBTree *prelight_tree;
+ gint prelight_offset;
+
+ /* Selection information */
+ GtkTreeSelection *selection;
+
+ /* Header information */
+ gint columns;
+ GList *column;
+ gint header_height;
+};
+
+#ifdef __GNUC__
+
+#define TREE_VIEW_INTERNAL_ASSERT(expr, ret) G_STMT_START{ \
+ if (!(expr)) \
+ { \
+ g_log (G_LOG_DOMAIN, \
+ G_LOG_LEVEL_CRITICAL, \
+ "file %s: line %d (%s): assertion `%s' failed.\n" \
+ "There is a disparity between the internal view of the GtkTreeView,\n" \
+ "and the GtkTreeModel. This generally means that the model has changed\n"\
+ "without letting the view know. Any display from now on is likely to\n" \
+ "be incorrect.\n", \
+ __FILE__, \
+ __LINE__, \
+ __PRETTY_FUNCTION__, \
+ #expr); \
+ return ret; \
+ }; }G_STMT_END
+
+#define TREE_VIEW_INTERNAL_ASSERT_VOID(expr) G_STMT_START{ \
+ if (!(expr)) \
+ { \
+ g_log (G_LOG_DOMAIN, \
+ G_LOG_LEVEL_CRITICAL, \
+ "file %s: line %d (%s): assertion `%s' failed.\n" \
+ "There is a disparity between the internal view of the GtkTreeView,\n" \
+ "and the GtkTreeModel. This generally means that the model has changed\n"\
+ "without letting the view know. Any display from now on is likely to\n" \
+ "be incorrect.\n", \
+ __FILE__, \
+ __LINE__, \
+ __PRETTY_FUNCTION__, \
+ #expr); \
+ return; \
+ }; }G_STMT_END
+
+#else
+
+#define TREE_VIEW_INTERNAL_ASSERT(expr, ret) G_STMT_START{ \
+ if (!(expr)) \
+ { \
+ g_log (G_LOG_DOMAIN, \
+ G_LOG_LEVEL_CRITICAL, \
+ "file %s: line %d: assertion `%s' failed.\n" \
+ "There is a disparity between the internal view of the GtkTreeView,\n" \
+ "and the GtkTreeModel. This generally means that the model has changed\n"\
+ "without letting the view know. Any display from now on is likely to\n" \
+ "be incorrect.\n", \
+ __FILE__, \
+ __LINE__, \
+ #expr); \
+ return ret; \
+ }; }G_STMT_END
+
+#define TREE_VIEW_INTERNAL_ASSERT_VOID(expr) G_STMT_START{ \
+ if (!(expr)) \
+ { \
+ g_log (G_LOG_DOMAIN, \
+ G_LOG_LEVEL_CRITICAL, \
+ "file %s: line %d: assertion '%s' failed.\n" \
+ "There is a disparity between the internal view of the GtkTreeView,\n" \
+ "and the GtkTreeModel. This generally means that the model has changed\n"\
+ "without letting the view know. Any display from now on is likely to\n" \
+ "be incorrect.\n", \
+ __FILE__, \
+ __LINE__, \
+ #expr); \
+ return; \
+ }; }G_STMT_END
+#endif
+
+/* functions that shouldn't be exported */
+void _gtk_tree_selection_internal_select_node (GtkTreeSelection *selection,
+ GtkRBNode *node,
+ GtkRBTree *tree,
+ GtkTreePath *path,
+ GdkModifierType state);
+gboolean _gtk_tree_view_find_node (GtkTreeView *tree_view,
+ GtkTreePath *path,
+ GtkRBTree **tree,
+ GtkRBNode **node);
+GtkTreePath *_gtk_tree_view_find_path (GtkTreeView *tree_view,
+ GtkRBTree *tree,
+ GtkRBNode *node);
+void _gtk_tree_view_set_size (GtkTreeView *tree_view,
+ gint width,
+ gint height);
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_TREE_PRIVATE_H__ */
+
diff --git a/gtk/gtktreeselection.c b/gtk/gtktreeselection.c
new file mode 100644
index 0000000000..73d84f076c
--- /dev/null
+++ b/gtk/gtktreeselection.c
@@ -0,0 +1,702 @@
+/* gtktreeselection.h
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gtktreeselection.h"
+#include "gtktreeprivate.h"
+#include "gtkrbtree.h"
+#include "gtksignal.h"
+
+static void gtk_tree_selection_init (GtkTreeSelection *selection);
+static void gtk_tree_selection_class_init (GtkTreeSelectionClass *class);
+
+enum {
+ ROW_SELECTED,
+ ROW_UNSELECTED,
+ LAST_SIGNAL
+};
+
+static GtkObjectClass *parent_class = NULL;
+static guint tree_selection_signals[LAST_SIGNAL] = { 0 };
+
+static void
+gtk_tree_selection_real_select_node (GtkTreeSelection *selection, GtkRBTree *tree, GtkRBNode *node, gboolean select)
+{
+ gboolean selected = FALSE;
+ GtkTreePath *path = NULL;
+
+ if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) != select)
+ {
+ path = _gtk_tree_view_find_path (selection->tree_view, tree, node);
+ if (selection->user_func)
+ {
+ if ((*selection->user_func) (selection, selection->tree_view->priv->model, path, selection->user_data))
+ selected = TRUE;
+ }
+ else
+ selected = TRUE;
+ }
+ if (selected == TRUE)
+ {
+ GtkTreeNode tree_node;
+ tree_node = gtk_tree_model_get_node (selection->tree_view->priv->model, path);
+
+ node->flags ^= GTK_RBNODE_IS_SELECTED;
+ if (select)
+ gtk_signal_emit (GTK_OBJECT (selection), tree_selection_signals[ROW_SELECTED], selection->tree_view->priv->model, tree_node);
+ else
+ gtk_signal_emit (GTK_OBJECT (selection), tree_selection_signals[ROW_UNSELECTED], selection->tree_view->priv->model, tree_node);
+ gtk_widget_queue_draw (GTK_WIDGET (selection->tree_view));
+ }
+}
+
+GtkType
+gtk_tree_selection_get_type (void)
+{
+ static GtkType selection_type = 0;
+
+ if (!selection_type)
+ {
+ static const GTypeInfo selection_info =
+ {
+ sizeof (GtkTreeSelectionClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) gtk_tree_selection_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GtkTreeSelection),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gtk_tree_selection_init
+ };
+
+ selection_type = g_type_register_static (GTK_TYPE_OBJECT, "GtkTreeSelection", &selection_info);
+ }
+
+ return selection_type;
+}
+
+static void
+gtk_tree_selection_class_init (GtkTreeSelectionClass *class)
+{
+ GtkObjectClass *object_class;
+
+ object_class = (GtkObjectClass*) class;
+ parent_class = g_type_class_peek_parent (class);
+
+ tree_selection_signals[ROW_SELECTED] =
+ gtk_signal_new ("row_selected",
+ GTK_RUN_FIRST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkTreeSelectionClass, row_selected),
+ gtk_marshal_NONE__POINTER_POINTER,
+ GTK_TYPE_NONE, 2,
+ GTK_TYPE_POINTER,
+ GTK_TYPE_POINTER);
+
+ tree_selection_signals[ROW_UNSELECTED] =
+ gtk_signal_new ("row_unselected",
+ GTK_RUN_FIRST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkTreeSelectionClass, row_unselected),
+ gtk_marshal_NONE__POINTER_POINTER,
+ GTK_TYPE_NONE, 2,
+ GTK_TYPE_POINTER,
+ GTK_TYPE_POINTER);
+
+ gtk_object_class_add_signals (object_class, tree_selection_signals, LAST_SIGNAL);
+
+ class->row_selected = NULL;
+ class->row_unselected = NULL;
+}
+
+static void
+gtk_tree_selection_init (GtkTreeSelection *selection)
+{
+ selection->type = GTK_TREE_SELECTION_MULTI;
+ selection->user_func = NULL;
+ selection->user_data = NULL;
+ selection->user_func = NULL;
+ selection->tree_view = NULL;
+}
+
+GtkObject *
+gtk_tree_selection_new (void)
+{
+ GtkObject *selection;
+
+ selection = GTK_OBJECT (gtk_type_new (GTK_TYPE_TREE_SELECTION));
+
+ return selection;
+}
+
+GtkObject *
+gtk_tree_selection_new_with_tree_view (GtkTreeView *tree_view)
+{
+ GtkObject *selection;
+
+ g_return_val_if_fail (tree_view != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
+
+ selection = gtk_tree_selection_new ();
+ gtk_tree_selection_set_tree_view (GTK_TREE_SELECTION (selection), tree_view);
+
+ return selection;
+}
+
+void
+gtk_tree_selection_set_tree_view (GtkTreeSelection *selection,
+ GtkTreeView *tree_view)
+{
+ g_return_if_fail (selection != NULL);
+ g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
+ if (tree_view != NULL)
+ g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+
+ selection->tree_view = tree_view;
+ tree_view->priv->selection = selection;
+}
+
+void
+gtk_tree_selection_set_type (GtkTreeSelection *selection,
+ GtkTreeSelectionType type)
+{
+ g_return_if_fail (selection != NULL);
+ g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
+
+ if (selection->type == type)
+ return;
+
+ if (type == GTK_TREE_SELECTION_SINGLE)
+ {
+ GtkRBTree *tree = NULL;
+ GtkRBNode *node = NULL;
+ gint selected = FALSE;
+
+ if (selection->tree_view->priv->anchor)
+ {
+ _gtk_tree_view_find_node (selection->tree_view,
+ selection->tree_view->priv->anchor,
+ &tree,
+ &node);
+
+ if (node && GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
+ selected = TRUE;
+ }
+ gtk_tree_selection_unselect_all (selection);
+ if (node && selected)
+ GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_SELECTED);
+ }
+ selection->type = type;
+}
+
+void
+gtk_tree_selection_set_select_function (GtkTreeSelection *selection,
+ GtkTreeSelectionFunc func,
+ gpointer data)
+{
+ g_return_if_fail (selection != NULL);
+ g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
+ g_return_if_fail (func != NULL);
+
+ selection->user_func = func;
+ selection->user_data = data;
+}
+
+gpointer
+gtk_tree_selection_get_user_data (GtkTreeSelection *selection)
+{
+ g_return_val_if_fail (selection != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL);
+
+ return selection->user_data;
+}
+
+GtkTreeNode *
+gtk_tree_selection_get_selected (GtkTreeSelection *selection)
+{
+ GtkTreeNode *retval;
+ GtkRBTree *tree;
+ GtkRBNode *node;
+
+ g_return_val_if_fail (selection != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL);
+
+ if (selection->tree_view->priv->anchor == NULL)
+ return NULL;
+
+ g_return_val_if_fail (selection->tree_view != NULL, NULL);
+ g_return_val_if_fail (selection->tree_view->priv->model != NULL, NULL);
+
+ if (!_gtk_tree_view_find_node (selection->tree_view,
+ selection->tree_view->priv->anchor,
+ &tree,
+ &node) &&
+ ! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
+ /* We don't want to return the anchor if it isn't actually selected.
+ */
+
+ return NULL;
+
+ retval = gtk_tree_model_get_node (selection->tree_view->priv->model,
+ selection->tree_view->priv->anchor);
+ return retval;
+}
+
+void
+gtk_tree_selection_selected_foreach (GtkTreeSelection *selection,
+ GtkTreeSelectionForeachFunc func,
+ gpointer data)
+{
+ GtkTreePath *path;
+ GtkRBTree *tree;
+ GtkRBNode *node;
+ GtkTreeNode tree_node;
+
+ g_return_if_fail (selection != NULL);
+ g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
+ g_return_if_fail (selection->tree_view != NULL);
+ g_return_if_fail (selection->tree_view->priv->model != NULL);
+
+ if (func == NULL ||
+ selection->tree_view->priv->tree == NULL ||
+ selection->tree_view->priv->tree->root == NULL)
+ return;
+
+ tree = selection->tree_view->priv->tree;
+ node = selection->tree_view->priv->tree->root;
+
+ while (node->left != tree->nil)
+ node = node->left;
+
+ /* find the node internally */
+ path = gtk_tree_path_new_root ();
+ tree_node = gtk_tree_model_get_node (selection->tree_view->priv->model, path);
+ gtk_tree_path_free (path);
+
+ do
+ {
+ if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
+ (* func) (selection->tree_view->priv->model, tree_node, data);
+ if (node->children)
+ {
+ tree = node->children;
+ node = tree->root;
+ while (node->left != tree->nil)
+ node = node->left;
+ tree_node = gtk_tree_model_node_children (selection->tree_view->priv->model, tree_node);
+
+ /* Sanity Check! */
+ TREE_VIEW_INTERNAL_ASSERT_VOID (tree_node != NULL);
+ }
+ else
+ {
+ gboolean done = FALSE;
+ do
+ {
+ node = _gtk_rbtree_next (tree, node);
+ if (node != NULL)
+ {
+ gtk_tree_model_node_next (selection->tree_view->priv->model, &tree_node);
+ done = TRUE;
+
+ /* Sanity Check! */
+ TREE_VIEW_INTERNAL_ASSERT_VOID (tree_node != NULL);
+ }
+ else
+ {
+ node = tree->parent_node;
+ tree = tree->parent_tree;
+ if (tree == NULL)
+ /* we've run out of tree */
+ /* We're done with this function */
+ return;
+ tree_node = gtk_tree_model_node_parent (selection->tree_view->priv->model, tree_node);
+
+ /* Sanity check */
+ TREE_VIEW_INTERNAL_ASSERT_VOID (tree_node != NULL);
+ }
+ }
+ while (!done);
+ }
+ }
+ while (TRUE);
+}
+
+void
+gtk_tree_selection_select_path (GtkTreeSelection *selection,
+ GtkTreePath *path)
+{
+ GtkRBNode *node;
+ GtkRBTree *tree;
+ GdkModifierType state = 0;
+
+ g_return_if_fail (selection != NULL);
+ g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
+ g_return_if_fail (selection->tree_view != NULL);
+ g_return_if_fail (path != NULL);
+
+ _gtk_tree_view_find_node (selection->tree_view,
+ path,
+ &tree,
+ &node);
+
+ if (node == NULL || GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
+ return;
+
+ if (selection->type == GTK_TREE_SELECTION_MULTI)
+ state = GDK_CONTROL_MASK;
+
+ _gtk_tree_selection_internal_select_node (selection,
+ node,
+ tree,
+ path,
+ state);
+}
+
+void
+gtk_tree_selection_unselect_path (GtkTreeSelection *selection,
+ GtkTreePath *path)
+{
+ GtkRBNode *node;
+ GtkRBTree *tree;
+
+ g_return_if_fail (selection != NULL);
+ g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
+ g_return_if_fail (selection->tree_view != NULL);
+ g_return_if_fail (path != NULL);
+
+ _gtk_tree_view_find_node (selection->tree_view,
+ path,
+ &tree,
+ &node);
+
+ if (node == NULL || !GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
+ return;
+
+ _gtk_tree_selection_internal_select_node (selection,
+ node,
+ tree,
+ path,
+ GDK_CONTROL_MASK);
+}
+
+void
+gtk_tree_selection_select_node (GtkTreeSelection *selection,
+ GtkTreeNode *tree_node)
+{
+ GtkTreePath *path;
+
+ g_return_if_fail (selection != NULL);
+ g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
+ g_return_if_fail (selection->tree_view != NULL);
+ g_return_if_fail (selection->tree_view->priv->model != NULL);
+
+ path = gtk_tree_model_get_path (selection->tree_view->priv->model,
+ tree_node);
+
+ if (path == NULL)
+ return;
+
+ gtk_tree_selection_select_path (selection, path);
+ gtk_tree_path_free (path);
+}
+
+
+void
+gtk_tree_selection_unselect_node (GtkTreeSelection *selection,
+ GtkTreeNode *tree_node)
+{
+ GtkTreePath *path;
+
+ g_return_if_fail (selection != NULL);
+ g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
+ g_return_if_fail (selection->tree_view != NULL);
+
+ path = gtk_tree_model_get_path (selection->tree_view->priv->model,
+ tree_node);
+
+ if (path == NULL)
+ return;
+
+ gtk_tree_selection_select_path (selection, path);
+ gtk_tree_path_free (path);
+}
+
+/* Wish I was in python, right now... */
+struct _TempTuple {
+ GtkTreeSelection *selection;
+ gint dirty;
+};
+
+static void
+select_all_helper (GtkRBTree *tree,
+ GtkRBNode *node,
+ gpointer data)
+{
+ struct _TempTuple *tuple = data;
+
+ if (node->children)
+ _gtk_rbtree_traverse (node->children,
+ node->children->root,
+ G_PRE_ORDER,
+ select_all_helper,
+ data);
+ if (!GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
+ {
+ gtk_tree_selection_real_select_node (tuple->selection, tree, node, TRUE);
+ tuple->dirty = TRUE;
+ }
+}
+
+void
+gtk_tree_selection_select_all (GtkTreeSelection *selection)
+{
+ struct _TempTuple *tuple;
+
+ g_return_if_fail (selection != NULL);
+ g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
+ g_return_if_fail (selection->tree_view != NULL);
+ g_return_if_fail (selection->tree_view->priv->tree != NULL);
+
+ if (selection->type == GTK_TREE_SELECTION_SINGLE)
+ {
+ GtkRBNode *node;
+ node = selection->tree_view->priv->tree->root;
+
+ while (node->right != selection->tree_view->priv->tree->nil)
+ node = node->right;
+ return;
+ }
+
+ tuple = g_new (struct _TempTuple, 1);
+ tuple->selection = selection;
+ tuple->dirty = FALSE;
+
+ _gtk_rbtree_traverse (selection->tree_view->priv->tree,
+ selection->tree_view->priv->tree->root,
+ G_PRE_ORDER,
+ select_all_helper,
+ tuple);
+ if (tuple->dirty)
+ gtk_widget_queue_draw (GTK_WIDGET (selection->tree_view));
+ g_free (tuple);
+}
+
+static void
+unselect_all_helper (GtkRBTree *tree,
+ GtkRBNode *node,
+ gpointer data)
+{
+ struct _TempTuple *tuple = data;
+
+ if (node->children)
+ _gtk_rbtree_traverse (node->children,
+ node->children->root,
+ G_PRE_ORDER,
+ unselect_all_helper,
+ data);
+ if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
+ {
+ gtk_tree_selection_real_select_node (tuple->selection, tree, node, FALSE);
+ tuple->dirty = TRUE;
+ }
+}
+
+void
+gtk_tree_selection_unselect_all (GtkTreeSelection *selection)
+{
+ struct _TempTuple *tuple;
+
+ g_return_if_fail (selection != NULL);
+ g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
+ g_return_if_fail (selection->tree_view != NULL);
+ if (selection->tree_view->priv->tree == NULL)
+ return;
+
+ if (selection->type == GTK_TREE_SELECTION_SINGLE)
+ {
+ GtkRBTree *tree = NULL;
+ GtkRBNode *node = NULL;
+ if (selection->tree_view->priv->anchor == NULL)
+ return;
+
+ _gtk_tree_view_find_node (selection->tree_view,
+ selection->tree_view->priv->anchor,
+ &tree,
+ &node);
+ if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
+ gtk_tree_selection_real_select_node (selection, tree, node, FALSE);
+ return;
+ }
+
+ tuple = g_new (struct _TempTuple, 1);
+ tuple->selection = selection;
+ tuple->dirty = FALSE;
+
+ _gtk_rbtree_traverse (selection->tree_view->priv->tree,
+ selection->tree_view->priv->tree->root,
+ G_PRE_ORDER,
+ unselect_all_helper,
+ tuple);
+ if (tuple->dirty)
+ gtk_widget_queue_draw (GTK_WIDGET (selection->tree_view));
+ g_free (tuple);
+}
+
+void
+gtk_tree_selection_select_range (GtkTreeSelection *selection,
+ GtkTreePath *start_path,
+ GtkTreePath *end_path)
+{
+ GtkRBNode *start_node, *end_node;
+ GtkRBTree *start_tree, *end_tree;
+
+ g_return_if_fail (selection != NULL);
+ g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
+ g_return_if_fail (selection->tree_view != NULL);
+
+ switch (gtk_tree_path_compare (start_path, end_path))
+ {
+ case -1:
+ _gtk_tree_view_find_node (selection->tree_view,
+ end_path,
+ &start_tree,
+ &start_node);
+ _gtk_tree_view_find_node (selection->tree_view,
+ start_path,
+ &end_tree,
+ &end_node);
+ break;
+ case 0:
+ _gtk_tree_view_find_node (selection->tree_view,
+ start_path,
+ &start_tree,
+ &start_node);
+ end_tree = start_tree;
+ end_node = start_node;
+ break;
+ case 1:
+ _gtk_tree_view_find_node (selection->tree_view,
+ start_path,
+ &start_tree,
+ &start_node);
+ _gtk_tree_view_find_node (selection->tree_view,
+ end_path,
+ &end_tree,
+ &end_node);
+ break;
+ }
+
+ g_return_if_fail (start_node != NULL);
+ g_return_if_fail (end_node != NULL);
+
+ do
+ {
+ gtk_tree_selection_real_select_node (selection, start_tree, start_node, TRUE);
+
+ if (start_node == end_node)
+ return;
+
+ if (start_node->children)
+ {
+ start_tree = start_node->children;
+ start_node = start_tree->root;
+ while (start_node->left != start_tree->nil)
+ start_node = start_node->left;
+ }
+ else
+ {
+ gboolean done = FALSE;
+ do
+ {
+ start_node = _gtk_rbtree_next (start_tree, start_node);
+ if (start_node != NULL)
+ {
+ done = TRUE;
+ }
+ else
+ {
+ start_node = start_tree->parent_node;
+ start_tree = start_tree->parent_tree;
+ if (start_tree == NULL)
+ /* we've run out of tree */
+ /* This means we never found end node!! */
+ return;
+ }
+ }
+ while (!done);
+ }
+ }
+ while (TRUE);
+}
+
+
+/* Called internally by gtktree_view. It handles actually selecting
+ * the tree. This should almost certainly ever be called by
+ * anywhere else */
+void
+_gtk_tree_selection_internal_select_node (GtkTreeSelection *selection,
+ GtkRBNode *node,
+ GtkRBTree *tree,
+ GtkTreePath *path,
+ GdkModifierType state)
+{
+ gint flags;
+
+ if (((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) && (selection->tree_view->priv->anchor == NULL))
+ {
+ selection->tree_view->priv->anchor = gtk_tree_path_copy (path);
+ gtk_tree_selection_real_select_node (selection, tree, node, TRUE);
+ }
+ else if ((state & (GDK_CONTROL_MASK|GDK_SHIFT_MASK)) == (GDK_SHIFT_MASK|GDK_CONTROL_MASK))
+ {
+ gtk_tree_selection_select_range (selection,
+ selection->tree_view->priv->anchor,
+ path);
+ }
+ else if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
+ {
+ flags = node->flags;
+ if (selection->type == GTK_TREE_SELECTION_SINGLE)
+ gtk_tree_selection_unselect_all (selection);
+ if (selection->tree_view->priv->anchor)
+ gtk_tree_path_free (selection->tree_view->priv->anchor);
+ selection->tree_view->priv->anchor = gtk_tree_path_copy (path);
+ if ((flags & GTK_RBNODE_IS_SELECTED) == GTK_RBNODE_IS_SELECTED)
+ gtk_tree_selection_real_select_node (selection, tree, node, FALSE);
+ else
+ gtk_tree_selection_real_select_node (selection, tree, node, TRUE);
+ }
+ else if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
+ {
+ gtk_tree_selection_unselect_all (selection);
+ gtk_tree_selection_select_range (selection,
+ selection->tree_view->priv->anchor,
+ path);
+ }
+ else
+ {
+ gtk_tree_selection_unselect_all (selection);
+ if (selection->tree_view->priv->anchor)
+ gtk_tree_path_free (selection->tree_view->priv->anchor);
+ selection->tree_view->priv->anchor = gtk_tree_path_copy (path);
+ gtk_tree_selection_real_select_node (selection, tree, node, TRUE);
+ }
+}
+
diff --git a/gtk/gtktreeselection.h b/gtk/gtktreeselection.h
new file mode 100644
index 0000000000..325cad8a81
--- /dev/null
+++ b/gtk/gtktreeselection.h
@@ -0,0 +1,120 @@
+/* gtktreeselection.h
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_TREE_SELECTION_H__
+#define __GTK_TREE_SELECTION_H__
+
+#include <gobject/gobject.h>
+#include <gtk/gtkobject.h>
+#include <gtk/gtktreeview.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#warning "Die GTK_TREE_SELECTION, DIE"
+#undef GTK_TREE_SELECTION
+
+#define GTK_TYPE_TREE_SELECTION (gtk_tree_selection_get_type ())
+#define GTK_TREE_SELECTION(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_TREE_SELECTION, GtkTreeSelection))
+#define GTK_TREE_SELECTION_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_TREE_SELECTION, GtkTreeSelectionClass))
+#define GTK_IS_TREE_SELECTION(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_TREE_SELECTION))
+#define GTK_IS_TREE_SELECTION_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), GTK_TYPE_TREE_SELECTION))
+
+typedef enum
+{
+ GTK_TREE_SELECTION_SINGLE,
+ GTK_TREE_SELECTION_MULTI
+} GtkTreeSelectionType;
+
+typedef gboolean (* GtkTreeSelectionFunc) (GtkTreeSelection *selection,
+ GtkTreeModel *model,
+ GtkTreePath *path,
+ gpointer data);
+typedef void (* GtkTreeSelectionForeachFunc) (GtkTreeModel *model,
+ GtkTreeNode *node,
+ gpointer data);
+
+struct _GtkTreeSelection
+{
+ GtkObject parent;
+
+ GtkTreeView *tree_view;
+ GtkTreeSelectionType type;
+ GtkTreeSelectionFunc user_func;
+ gpointer user_data;
+};
+
+struct _GtkTreeSelectionClass
+{
+ GtkObjectClass parent_class;
+
+ void (* row_selected) (GtkTreeView *tree_view,
+ GtkTreeModel *tree_model,
+ GtkTreeNode *node);
+ void (* row_unselected) (GtkTreeView *tree_view,
+ GtkTreeModel *tree_model,
+ GtkTreeNode *node);
+};
+
+
+GtkType gtk_tree_selection_get_type (void);
+GtkObject *gtk_tree_selection_new (void);
+void gtk_tree_selection_set_type (GtkTreeSelection *selection,
+ GtkTreeSelectionType type);
+void gtk_tree_selection_set_select_function (GtkTreeSelection *selection,
+ GtkTreeSelectionFunc func,
+ gpointer data);
+gpointer gtk_tree_selection_get_user_data (GtkTreeSelection *selection);
+
+
+/* Only meaningful if GTK_TREE_SELECTION_SINGLE is set */
+/* Use selected_foreach for GTK_TREE_SELECTION_MULTI */
+GtkTreeNode *gtk_tree_selection_get_selected (GtkTreeSelection *selection);
+/* FIXME: Get a more convenient get_selection function???? one returning GSList?? */
+void gtk_tree_selection_selected_foreach (GtkTreeSelection *selection,
+ GtkTreeSelectionForeachFunc func,
+ gpointer data);
+void gtk_tree_selection_select_path (GtkTreeSelection *selection,
+ GtkTreePath *path);
+void gtk_tree_selection_unselect_path (GtkTreeSelection *selection,
+ GtkTreePath *path);
+void gtk_tree_selection_select_node (GtkTreeSelection *selection,
+ GtkTreeNode *tree_node);
+void gtk_tree_selection_unselect_node (GtkTreeSelection *selection,
+ GtkTreeNode *tree_node);
+void gtk_tree_selection_select_all (GtkTreeSelection *selection);
+void gtk_tree_selection_unselect_all (GtkTreeSelection *selection);
+void gtk_tree_selection_select_range (GtkTreeSelection *selection,
+ GtkTreePath *start_path,
+ GtkTreePath *end_path);
+
+
+/*< private >*/
+GtkObject *gtk_tree_selection_new_with_tree_view (GtkTreeView *tree_view);
+void gtk_tree_selection_set_tree_view (GtkTreeSelection *selection,
+ GtkTreeView *tree_view);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_TREE_SELECTION_H__ */
+
diff --git a/gtk/gtktreestore.c b/gtk/gtktreestore.c
new file mode 100644
index 0000000000..b944e4917d
--- /dev/null
+++ b/gtk/gtktreestore.c
@@ -0,0 +1,655 @@
+/* gtktreestore.c
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gtktreemodel.h"
+#include "gtktreestore.h"
+#include "gtktreedatalist.h"
+#include "gtksignal.h"
+#include <string.h>
+
+#define G_NODE(node) ((GNode *)node)
+
+static void gtk_tree_store_init (GtkTreeStore *TreeStore);
+static void gtk_tree_store_class_init (GtkTreeStoreClass *klass);
+static gint gtk_tree_store_get_n_columns (GtkTreeModel *tree_model);
+static GtkTreeNode gtk_tree_store_get_node (GtkTreeModel *tree_model,
+ GtkTreePath *path);
+static GtkTreePath *gtk_tree_store_get_path (GtkTreeModel *tree_model,
+ GtkTreeNode node);
+static void gtk_tree_store_node_get_value (GtkTreeModel *tree_model,
+ GtkTreeNode node,
+ gint column,
+ GValue *value);
+static gboolean gtk_tree_store_node_next (GtkTreeModel *tree_model,
+ GtkTreeNode *node);
+static GtkTreeNode gtk_tree_store_node_children (GtkTreeModel *tree_model,
+ GtkTreeNode node);
+static gboolean gtk_tree_store_node_has_child (GtkTreeModel *tree_model,
+ GtkTreeNode node);
+static gint gtk_tree_store_node_n_children (GtkTreeModel *tree_model,
+ GtkTreeNode node);
+static GtkTreeNode gtk_tree_store_node_nth_child (GtkTreeModel *tree_model,
+ GtkTreeNode node,
+ gint n);
+static GtkTreeNode gtk_tree_store_node_parent (GtkTreeModel *tree_model,
+ GtkTreeNode node);
+
+
+static GtkTreeModelClass *parent_class = NULL;
+
+
+GtkType
+gtk_tree_store_get_type (void)
+{
+ static GtkType tree_store_type = 0;
+
+ if (!tree_store_type)
+ {
+ static const GTypeInfo tree_store_info =
+ {
+ sizeof (GtkTreeStoreClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) gtk_tree_store_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GtkTreeStore),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gtk_tree_store_init
+ };
+
+ tree_store_type = g_type_register_static (GTK_TYPE_TREE_MODEL, "GtkTreeStore", &tree_store_info);
+ }
+
+ return tree_store_type;
+}
+
+static void
+gtk_tree_store_class_init (GtkTreeStoreClass *klass)
+{
+ GtkObjectClass *object_class;
+ GtkTreeModelClass *tree_model_class;
+
+ object_class = (GtkObjectClass *) klass;
+ tree_model_class = (GtkTreeModelClass *) klass;
+
+ parent_class = gtk_type_class (gtk_tree_model_get_type ());
+
+ tree_model_class->get_n_columns = gtk_tree_store_get_n_columns;
+ tree_model_class->get_node = gtk_tree_store_get_node;
+ tree_model_class->get_path = gtk_tree_store_get_path;
+ tree_model_class->node_get_value = gtk_tree_store_node_get_value;
+ tree_model_class->node_next = gtk_tree_store_node_next;
+ tree_model_class->node_children = gtk_tree_store_node_children;
+ tree_model_class->node_has_child = gtk_tree_store_node_has_child;
+ tree_model_class->node_n_children = gtk_tree_store_node_n_children;
+ tree_model_class->node_nth_child = gtk_tree_store_node_nth_child;
+ tree_model_class->node_parent = gtk_tree_store_node_parent;
+}
+
+static void
+gtk_tree_store_init (GtkTreeStore *tree_store)
+{
+ tree_store->root = gtk_tree_store_node_new ();
+}
+
+GtkObject *
+gtk_tree_store_new (void)
+{
+ return GTK_OBJECT (gtk_type_new (gtk_tree_store_get_type ()));
+}
+
+GtkObject *
+gtk_tree_store_new_with_values (gint n_columns,
+ ...)
+{
+ GtkObject *retval;
+ va_list args;
+ gint i;
+
+ g_return_val_if_fail (n_columns > 0, NULL);
+
+ retval = gtk_tree_store_new ();
+ gtk_tree_store_set_n_columns (GTK_TREE_STORE (retval),
+ n_columns);
+
+ va_start (args, n_columns);
+ for (i = 0; i < n_columns; i++)
+ gtk_tree_store_set_column_type (GTK_TREE_STORE (retval),
+ i, va_arg (args, GType));
+
+ va_end (args);
+
+ return retval;
+}
+
+void
+gtk_tree_store_set_n_columns (GtkTreeStore *tree_store,
+ gint n_columns)
+{
+ GType *new_columns;
+
+ g_return_if_fail (tree_store != NULL);
+ g_return_if_fail (GTK_IS_TREE_STORE (tree_store));
+
+ if (tree_store->n_columns == n_columns)
+ return;
+
+ new_columns = g_new0 (GType, n_columns);
+ if (tree_store->column_headers)
+ {
+ /* copy the old header orders over */
+ if (n_columns >= tree_store->n_columns)
+ memcpy (new_columns, tree_store->column_headers, tree_store->n_columns * sizeof (gchar *));
+ else
+ memcpy (new_columns, tree_store->column_headers, n_columns * sizeof (GType));
+
+ g_free (tree_store->column_headers);
+ }
+
+ tree_store->column_headers = new_columns;
+ tree_store->n_columns = n_columns;
+}
+
+void
+gtk_tree_store_set_column_type (GtkTreeStore *tree_store,
+ gint column,
+ GType type)
+{
+ g_return_if_fail (tree_store != NULL);
+ g_return_if_fail (GTK_IS_TREE_STORE (tree_store));
+ g_return_if_fail (column >=0 && column < tree_store->n_columns);
+
+ tree_store->column_headers[column] = type;
+}
+
+/* fulfill the GtkTreeModel requirements */
+/* NOTE: GtkTreeStore::root is a GNode, that acts as the parent node. However,
+ * it is not visible to the tree or to the user., and the path "1" refers to the
+ * first child of GtkTreeStore::root.
+ */
+static gint
+gtk_tree_store_get_n_columns (GtkTreeModel *tree_model)
+{
+ g_return_val_if_fail (tree_model != NULL, 0);
+ g_return_val_if_fail (GTK_IS_TREE_STORE (tree_model), 0);
+
+ return GTK_TREE_STORE (tree_model)->n_columns;
+}
+
+static GtkTreeNode
+gtk_tree_store_get_node (GtkTreeModel *tree_model,
+ GtkTreePath *path)
+{
+ gint i;
+ GtkTreeNode *node;
+ gint *indices = gtk_tree_path_get_indices (path);
+
+ node = GTK_TREE_STORE (tree_model)->root;
+
+ for (i = 0; i < gtk_tree_path_get_depth (path); i ++)
+ {
+ node = (GtkTreeNode *) gtk_tree_store_node_nth_child (tree_model,
+ (GtkTreeNode *) node,
+ indices[i]);
+ if (node == NULL)
+ return NULL;
+ };
+ return (GtkTreeNode) node;
+}
+
+static GtkTreePath *
+gtk_tree_store_get_path (GtkTreeModel *tree_model,
+ GtkTreeNode node)
+{
+ GtkTreePath *retval;
+ GNode *tmp_node;
+ gint i = 0;
+
+ g_return_val_if_fail (tree_model != NULL, NULL);
+
+ if (node == NULL)
+ return NULL;
+ if (node == G_NODE (GTK_TREE_STORE (tree_model)->root))
+ return NULL;
+
+ if (G_NODE (node)->parent == G_NODE (GTK_TREE_STORE (tree_model)->root))
+ {
+ retval = gtk_tree_path_new ();
+ tmp_node = G_NODE (GTK_TREE_STORE (tree_model)->root)->children;
+ }
+ else
+ {
+ retval = gtk_tree_store_get_path (tree_model,
+ G_NODE (node)->parent);
+ tmp_node = G_NODE (node)->parent->children;
+ }
+
+ if (retval == NULL)
+ return NULL;
+ if (tmp_node == NULL)
+ {
+ gtk_tree_path_free (retval);
+ return NULL;
+ }
+
+ for (; tmp_node; tmp_node = tmp_node->next)
+ {
+ if (tmp_node == G_NODE (node))
+ break;
+ i++;
+ }
+ if (tmp_node == NULL)
+ {
+ /* We couldn't find node, meaning it's prolly not ours */
+ gtk_tree_path_free (retval);
+ return NULL;
+ }
+
+ gtk_tree_path_append_index (retval, i);
+
+ return retval;
+}
+
+
+static void
+gtk_tree_store_node_get_value (GtkTreeModel *tree_model,
+ GtkTreeNode node,
+ gint column,
+ GValue *value)
+{
+ GtkTreeDataList *list;
+ gint tmp_column = column;
+
+ g_return_if_fail (tree_model != NULL);
+ g_return_if_fail (GTK_IS_TREE_STORE (tree_model));
+ g_return_if_fail (node != NULL);
+ g_return_if_fail (column < GTK_TREE_STORE (tree_model)->n_columns);
+
+ list = G_NODE (node)->data;
+
+ while (tmp_column-- > 0 && list)
+ list = list->next;
+
+ g_return_if_fail (list != NULL);
+
+ gtk_tree_data_list_node_to_value (list,
+ GTK_TREE_STORE (tree_model)->column_headers[column],
+ value);
+}
+
+static gboolean
+gtk_tree_store_node_next (GtkTreeModel *tree_model,
+ GtkTreeNode *node)
+{
+ if (node == NULL || *node == NULL)
+ return FALSE;
+
+ *node = (GtkTreeNode *) G_NODE (*node)->next;
+ return (*node != NULL);
+}
+
+static GtkTreeNode
+gtk_tree_store_node_children (GtkTreeModel *tree_model,
+ GtkTreeNode node)
+{
+ return (GtkTreeNode) G_NODE (node)->children;
+}
+
+static gboolean
+gtk_tree_store_node_has_child (GtkTreeModel *tree_model,
+ GtkTreeNode node)
+{
+ return G_NODE (node)->children != NULL;
+}
+
+static gint
+gtk_tree_store_node_n_children (GtkTreeModel *tree_model,
+ GtkTreeNode node)
+{
+ gint i = 0;
+
+ node = (GtkTreeNode *) G_NODE (node)->children;
+ while (node != NULL)
+ {
+ i++;
+ node = (GtkTreeNode *) G_NODE (node)->next;
+ }
+
+ return i;
+}
+
+static GtkTreeNode
+gtk_tree_store_node_nth_child (GtkTreeModel *tree_model,
+ GtkTreeNode node,
+ gint n)
+{
+ g_return_val_if_fail (node != NULL, NULL);
+
+ return (GtkTreeNode *) g_node_nth_child (G_NODE (node), n);
+}
+
+static GtkTreeNode
+gtk_tree_store_node_parent (GtkTreeModel *tree_model,
+ GtkTreeNode node)
+{
+ return (GtkTreeNode) G_NODE (node)->parent;
+}
+
+/* Public accessors */
+GtkTreeNode *
+gtk_tree_store_node_new (void)
+{
+ GtkTreeNode *retval;
+
+ retval = (GtkTreeNode *) g_node_new (NULL);
+ return retval;
+}
+
+/*
+ * This is a somewhat inelegant function that does a lot of list
+ * manipulations on it's own.
+ */
+void
+gtk_tree_store_node_set_cell (GtkTreeStore *tree_store,
+ GtkTreeNode *node,
+ gint column,
+ GValue *value)
+{
+ GtkTreeDataList *list;
+ GtkTreeDataList *prev;
+
+ g_return_if_fail (tree_store != NULL);
+ g_return_if_fail (GTK_IS_TREE_STORE (tree_store));
+ g_return_if_fail (node != NULL);
+ g_return_if_fail (column >= 0 && column < tree_store->n_columns);
+
+ prev = list = G_NODE (node)->data;
+
+ while (list != NULL)
+ {
+ if (column == 0)
+ {
+ gtk_tree_data_list_value_to_node (list, value);
+ return;
+ }
+
+ column--;
+ prev = list;
+ list = list->next;
+ }
+
+ if (G_NODE (node)->data == NULL)
+ {
+ G_NODE (node)->data = list = gtk_tree_data_list_alloc ();
+ list->next = NULL;
+ }
+ else
+ {
+ list = prev->next = gtk_tree_data_list_alloc ();
+ list->next = NULL;
+ }
+
+ while (column != 0)
+ {
+ list->next = gtk_tree_data_list_alloc ();
+ list = list->next;
+ list->next = NULL;
+ column --;
+ }
+ gtk_tree_data_list_value_to_node (list, value);
+}
+
+void
+gtk_tree_store_node_remove (GtkTreeStore *model,
+ GtkTreeNode *node)
+{
+ GtkTreePath *path;
+ GNode *parent;
+
+ g_return_if_fail (model != NULL);
+ g_return_if_fail (GTK_IS_TREE_STORE (model));
+ g_return_if_fail (node != NULL);
+ /* FIXME: if node is NULL, do I want to free the tree? */
+
+ parent = G_NODE (node)->parent;
+
+ path = gtk_tree_store_get_path (GTK_TREE_MODEL (model), node);
+ g_node_destroy (G_NODE (node));
+ gtk_signal_emit_by_name (GTK_OBJECT (model),
+ "node_deleted",
+ path);
+ if (parent != G_NODE (model->root) && parent->children == NULL)
+ {
+ gtk_tree_path_up (path);
+ gtk_signal_emit_by_name (GTK_OBJECT (model),
+ "node_child_toggled",
+ path,
+ parent);
+ }
+ gtk_tree_path_free (path);
+}
+
+GtkTreeNode *
+gtk_tree_store_node_insert (GtkTreeStore *model,
+ GtkTreeNode *parent,
+ gint position,
+ GtkTreeNode *node)
+{
+ GtkTreePath *path;
+
+ g_return_val_if_fail (model != NULL, node);
+ g_return_val_if_fail (GTK_IS_TREE_STORE (model), node);
+ g_return_val_if_fail (node != NULL, node);
+
+ if (parent == NULL)
+ parent = model->root;
+
+ g_node_insert (G_NODE (parent), position, G_NODE (node));
+
+ path = gtk_tree_store_get_path (GTK_TREE_MODEL (model), node);
+ gtk_signal_emit_by_name (GTK_OBJECT (model),
+ "node_inserted",
+ path, node);
+ gtk_tree_path_free (path);
+
+ return node;
+}
+
+GtkTreeNode *
+gtk_tree_store_node_insert_before (GtkTreeStore *model,
+ GtkTreeNode *parent,
+ GtkTreeNode *sibling,
+ GtkTreeNode *node)
+{
+ GtkTreePath *path;
+
+ g_return_val_if_fail (model != NULL, node);
+ g_return_val_if_fail (GTK_IS_TREE_STORE (model), node);
+ g_return_val_if_fail (node != NULL, node);
+
+ if (parent == NULL && sibling == NULL)
+ parent = model->root;
+
+ if (parent == NULL)
+ parent = (GtkTreeNode *) G_NODE (sibling)->parent;
+
+ g_node_insert_before (G_NODE (parent), G_NODE (sibling), G_NODE (node));
+
+ path = gtk_tree_store_get_path (GTK_TREE_MODEL (model), node);
+ gtk_signal_emit_by_name (GTK_OBJECT (model),
+ "node_inserted",
+ path, node);
+ gtk_tree_path_free (path);
+
+ return node;
+}
+
+GtkTreeNode *
+gtk_tree_store_node_insert_after (GtkTreeStore *model,
+ GtkTreeNode *parent,
+ GtkTreeNode *sibling,
+ GtkTreeNode *node)
+{
+ GtkTreePath *path;
+
+ g_return_val_if_fail (model != NULL, node);
+ g_return_val_if_fail (GTK_IS_TREE_STORE (model), node);
+ g_return_val_if_fail (node != NULL, node);
+
+ if (parent == NULL && sibling == NULL)
+ parent = model->root;
+
+ if (parent == NULL)
+ parent = (GtkTreeNode *) G_NODE (sibling)->parent;
+
+ g_node_insert_after (G_NODE (parent), G_NODE (sibling), G_NODE (node));
+
+ path = gtk_tree_store_get_path (GTK_TREE_MODEL (model), node);
+ gtk_signal_emit_by_name (GTK_OBJECT (model),
+ "node_inserted",
+ path, node);
+ gtk_tree_path_free (path);
+ return node;
+}
+
+GtkTreeNode *
+gtk_tree_store_node_prepend (GtkTreeStore *model,
+ GtkTreeNode *parent,
+ GtkTreeNode *node)
+{
+ g_return_val_if_fail (model != NULL, node);
+ g_return_val_if_fail (GTK_IS_TREE_STORE (model), node);
+ g_return_val_if_fail (node != NULL, node);
+
+ if (parent == NULL)
+ parent = model->root;
+
+ if (G_NODE (parent)->children == NULL)
+ {
+ GtkTreePath *path;
+ g_node_prepend (G_NODE (parent), G_NODE (node));
+ if (parent != model->root)
+ {
+ path = gtk_tree_store_get_path (GTK_TREE_MODEL (model), parent);
+ gtk_signal_emit_by_name (GTK_OBJECT (model),
+ "node_child_toggled",
+ path,
+ parent);
+ gtk_tree_path_append_index (path, 1);
+ }
+ else
+ {
+ path = gtk_tree_store_get_path (GTK_TREE_MODEL (model), G_NODE (parent)->children);
+ }
+ gtk_signal_emit_by_name (GTK_OBJECT (model),
+ "node_inserted",
+ path,
+ G_NODE (parent)->children);
+ gtk_tree_path_free (path);
+ }
+ else
+ {
+ gtk_tree_store_node_insert_after (model,
+ parent == model->root?NULL:parent,
+ NULL,
+ node);
+ }
+ return node;
+}
+
+GtkTreeNode *
+gtk_tree_store_node_append (GtkTreeStore *model,
+ GtkTreeNode *parent,
+ GtkTreeNode *node)
+{
+ g_return_val_if_fail (model != NULL, node);
+ g_return_val_if_fail (GTK_IS_TREE_STORE (model), node);
+ g_return_val_if_fail (node != NULL, node);
+
+ if (parent == NULL)
+ parent = model->root;
+
+ if (G_NODE (parent)->children == NULL)
+ {
+ GtkTreePath *path;
+ g_node_append (G_NODE (parent), G_NODE (node));
+ if (parent != model->root)
+ {
+ path = gtk_tree_store_get_path (GTK_TREE_MODEL (model), parent);
+ gtk_signal_emit_by_name (GTK_OBJECT (model),
+ "node_child_toggled",
+ path,
+ parent);
+ gtk_tree_path_append_index (path, 1);
+ }
+ else
+ {
+ path = gtk_tree_store_get_path (GTK_TREE_MODEL (model), G_NODE (parent)->children);
+ }
+ gtk_signal_emit_by_name (GTK_OBJECT (model),
+ "node_inserted",
+ path,
+ G_NODE (parent)->children);
+ gtk_tree_path_free (path);
+ }
+ else
+ {
+ gtk_tree_store_node_insert_before (model,
+ parent == model->root?NULL:parent,
+ NULL,
+ node);
+ }
+ return node;
+}
+
+GtkTreeNode *
+gtk_tree_store_node_get_root (GtkTreeStore *model)
+{
+ g_return_val_if_fail (model != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_TREE_STORE (model), NULL);
+
+ return (GtkTreeNode *) model->root;
+}
+
+
+gboolean
+gtk_tree_store_node_is_ancestor (GtkTreeStore *model,
+ GtkTreeNode *node,
+ GtkTreeNode *descendant)
+{
+ g_return_val_if_fail (model != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_TREE_STORE (model), FALSE);
+ g_return_val_if_fail (node != NULL, FALSE);
+ g_return_val_if_fail (descendant != NULL, FALSE);
+
+ return g_node_is_ancestor (G_NODE (node), G_NODE (descendant));
+}
+
+
+gint
+gtk_tree_store_node_depth (GtkTreeStore *model,
+ GtkTreeNode *node)
+{
+ g_return_val_if_fail (model != NULL, 0);
+ g_return_val_if_fail (GTK_IS_TREE_STORE (model), 0);
+ g_return_val_if_fail (node != NULL, 0);
+
+ return g_node_depth (G_NODE (node));
+}
diff --git a/gtk/gtktreestore.h b/gtk/gtktreestore.h
new file mode 100644
index 0000000000..dce0bafedf
--- /dev/null
+++ b/gtk/gtktreestore.h
@@ -0,0 +1,99 @@
+/* gtktreestore.h
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_TREE_STORE_H__
+#define __GTK_TREE_STORE_H__
+
+#include <gtk/gtktreemodel.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define GTK_TYPE_TREE_STORE (gtk_tree_store_get_type ())
+#define GTK_TREE_STORE(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_TREE_STORE, GtkTreeStore))
+#define GTK_TREE_STORE_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_TREE_STORE, GtkTreeStoreClass))
+#define GTK_IS_TREE_STORE(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_TREE_STORE))
+#define GTK_IS_TREE_STORE_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), GTK_TYPE_TREE_STORE))
+
+typedef struct _GtkTreeStore GtkTreeStore;
+typedef struct _GtkTreeStoreClass GtkTreeStoreClass;
+
+struct _GtkTreeStore
+{
+ GtkTreeModel parent;
+ GtkTreeNode *root;
+ gint n_columns;
+ GType *column_headers;
+};
+
+struct _GtkTreeStoreClass
+{
+ GtkTreeModelClass parent_class;
+};
+
+GtkType gtk_tree_store_get_type (void);
+GtkObject *gtk_tree_store_new (void);
+GtkObject *gtk_tree_store_new_with_values (gint n_columns,
+ ...);
+void gtk_tree_store_set_n_columns (GtkTreeStore *tree_store,
+ gint n_columns);
+void gtk_tree_store_set_column_type (GtkTreeStore *store,
+ gint column,
+ GType type);
+
+GtkTreeNode *gtk_tree_store_node_new (void);
+void gtk_tree_store_node_set_cell (GtkTreeStore *tree_store,
+ GtkTreeNode *node,
+ gint column,
+ GValue *value);
+void gtk_tree_store_node_remove (GtkTreeStore *tree_store,
+ GtkTreeNode *node);
+GtkTreeNode *gtk_tree_store_node_insert (GtkTreeStore *tree_store,
+ GtkTreeNode *parent,
+ gint position,
+ GtkTreeNode *node);
+GtkTreeNode *gtk_tree_store_node_insert_before (GtkTreeStore *tree_store,
+ GtkTreeNode *parent,
+ GtkTreeNode *sibling,
+ GtkTreeNode *node);
+GtkTreeNode *gtk_tree_store_node_insert_after (GtkTreeStore *tree_store,
+ GtkTreeNode *parent,
+ GtkTreeNode *sibling,
+ GtkTreeNode *node);
+GtkTreeNode *gtk_tree_store_node_prepend (GtkTreeStore *tree_store,
+ GtkTreeNode *parent,
+ GtkTreeNode *node);
+GtkTreeNode *gtk_tree_store_node_append (GtkTreeStore *tree_store,
+ GtkTreeNode *parent,
+ GtkTreeNode *node);
+GtkTreeNode *gtk_tree_store_node_get_root (GtkTreeStore *tree_store);
+gboolean gtk_tree_store_node_is_ancestor (GtkTreeStore *tree_store,
+ GtkTreeNode *node,
+ GtkTreeNode *descendant);
+gint gtk_tree_store_node_depth (GtkTreeStore *tree_store,
+ GtkTreeNode *node);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_TREE_STORE_H__ */
diff --git a/gtk/gtktreeview.c b/gtk/gtktreeview.c
new file mode 100644
index 0000000000..f2bf4ad74b
--- /dev/null
+++ b/gtk/gtktreeview.c
@@ -0,0 +1,3384 @@
+/* gtktreeview.c
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#include "gtktreeview.h"
+#include "gtkrbtree.h"
+#include "gtktreeprivate.h"
+#include "gtkcellrenderer.h"
+#include "gtksignal.h"
+#include "gtkmain.h"
+#include "gtkbutton.h"
+#include "gtkalignment.h"
+#include "gtklabel.h"
+
+#include <gdk/gdkkeysyms.h>
+
+
+/* the width of the column resize windows */
+#define TREE_VIEW_DRAG_WIDTH 6
+#define TREE_VIEW_EXPANDER_WIDTH 14
+#define TREE_VIEW_EXPANDER_HEIGHT 14
+#define TREE_VIEW_VERTICAL_SEPERATOR 2
+#define TREE_VIEW_HORIZONTAL_SEPERATOR 0
+
+
+typedef struct _GtkTreeViewChild GtkTreeViewChild;
+
+struct _GtkTreeViewChild
+{
+ GtkWidget *widget;
+ gint x;
+ gint y;
+};
+
+
+static void gtk_tree_view_init (GtkTreeView *tree_view);
+static void gtk_tree_view_class_init (GtkTreeViewClass *klass);
+
+/* widget signals */
+static void gtk_tree_view_set_model_realized (GtkTreeView *tree_view);
+static void gtk_tree_view_realize (GtkWidget *widget);
+static void gtk_tree_view_unrealize (GtkWidget *widget);
+static void gtk_tree_view_map (GtkWidget *widget);
+static void gtk_tree_view_size_request (GtkWidget *widget,
+ GtkRequisition *requisition);
+static void gtk_tree_view_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+static void gtk_tree_view_draw (GtkWidget *widget,
+ GdkRectangle *area);
+static gboolean gtk_tree_view_expose (GtkWidget *widget,
+ GdkEventExpose *event);
+static gboolean gtk_tree_view_motion (GtkWidget *widget,
+ GdkEventMotion *event);
+static gboolean gtk_tree_view_enter_notify (GtkWidget *widget,
+ GdkEventCrossing *event);
+static gboolean gtk_tree_view_leave_notify (GtkWidget *widget,
+ GdkEventCrossing *event);
+static gboolean gtk_tree_view_button_press (GtkWidget *widget,
+ GdkEventButton *event);
+static gboolean gtk_tree_view_button_release (GtkWidget *widget,
+ GdkEventButton *event);
+static void gtk_tree_view_draw_focus (GtkWidget *widget);
+static gint gtk_tree_view_focus_in (GtkWidget *widget,
+ GdkEventFocus *event);
+static gint gtk_tree_view_focus_out (GtkWidget *widget,
+ GdkEventFocus *event);
+static gint gtk_tree_view_focus (GtkContainer *container,
+ GtkDirectionType direction);
+
+/* container signals */
+static void gtk_tree_view_remove (GtkContainer *container,
+ GtkWidget *widget);
+static void gtk_tree_view_forall (GtkContainer *container,
+ gboolean include_internals,
+ GtkCallback callback,
+ gpointer callback_data);
+
+/* tree_model signals */
+static void gtk_tree_view_set_adjustments (GtkTreeView *tree_view,
+ GtkAdjustment *hadj,
+ GtkAdjustment *vadj);
+static void gtk_tree_view_node_changed (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeNode *tree_node,
+ gpointer data);
+static void gtk_tree_view_node_inserted (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeNode *tree_node,
+ gpointer data);
+static void gtk_tree_view_node_child_toggled (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeNode *tree_node,
+ gpointer data);
+static void gtk_tree_view_node_deleted (GtkTreeModel *model,
+ GtkTreePath *path,
+ gpointer data);
+
+/* Internal functions */
+static void gtk_tree_view_draw_arrow (GtkTreeView *tree_view,
+ GtkRBNode *node,
+ gint offset,
+ gint x,
+ gint y);
+static gint gtk_tree_view_new_column_width (GtkTreeView *tree_view,
+ gint i,
+ gint *x);
+static void gtk_tree_view_adjustment_changed (GtkAdjustment *adjustment,
+ GtkTreeView *tree_view);
+static gint gtk_tree_view_insert_node_height (GtkTreeView *tree_view,
+ GtkRBTree *tree,
+ GtkTreeNode node,
+ gint depth);
+static void gtk_tree_view_build_tree (GtkTreeView *tree_view,
+ GtkRBTree *tree,
+ GtkTreeNode node,
+ gint depth,
+ gboolean recurse,
+ gboolean calc_bounds);
+static void gtk_tree_view_calc_size (GtkTreeView *priv,
+ GtkRBTree *tree,
+ GtkTreeNode node,
+ gint depth);
+static gboolean gtk_tree_view_discover_dirty_node (GtkTreeView *tree_view,
+ GtkTreeNode node,
+ gint depth,
+ gint *height);
+static void gtk_tree_view_discover_dirty (GtkTreeView *tree_view,
+ GtkRBTree *tree,
+ GtkTreeNode node,
+ gint depth);
+static void gtk_tree_view_check_dirty (GtkTreeView *tree_view);
+static void gtk_tree_view_create_button (GtkTreeView *tree_view,
+ gint i);
+static void gtk_tree_view_create_buttons (GtkTreeView *tree_view);
+static void gtk_tree_view_button_clicked (GtkWidget *widget,
+ gpointer data);
+static void gtk_tree_view_clamp_node_visible (GtkTreeView *tree_view,
+ GtkRBTree *tree,
+ GtkRBNode *node);
+
+
+
+static GtkContainerClass *parent_class = NULL;
+
+
+/* Class Functions */
+GtkType
+gtk_tree_view_get_type (void)
+{
+ static GtkType tree_view_type = 0;
+
+ if (!tree_view_type)
+ {
+ static const GTypeInfo tree_view_info =
+ {
+ sizeof (GtkTreeViewClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) gtk_tree_view_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GtkTreeView),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gtk_tree_view_init
+ };
+
+ tree_view_type = g_type_register_static (GTK_TYPE_CONTAINER, "GtkTreeView", &tree_view_info);
+ }
+
+ return tree_view_type;
+}
+
+static void
+gtk_tree_view_class_init (GtkTreeViewClass *class)
+{
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+ GtkContainerClass *container_class;
+
+ object_class = (GtkObjectClass*) class;
+ widget_class = (GtkWidgetClass*) class;
+ container_class = (GtkContainerClass*) class;
+ parent_class = g_type_class_peek_parent (class);
+
+ widget_class->realize = gtk_tree_view_realize;
+ widget_class->unrealize = gtk_tree_view_unrealize;
+ widget_class->map = gtk_tree_view_map;
+ widget_class->size_request = gtk_tree_view_size_request;
+ widget_class->size_allocate = gtk_tree_view_size_allocate;
+ widget_class->draw = gtk_tree_view_draw;
+ widget_class->expose_event = gtk_tree_view_expose;
+ // widget_class->key_press_event = gtk_tree_view_key_press;
+ widget_class->motion_notify_event = gtk_tree_view_motion;
+ widget_class->enter_notify_event = gtk_tree_view_enter_notify;
+ widget_class->leave_notify_event = gtk_tree_view_leave_notify;
+ widget_class->button_press_event = gtk_tree_view_button_press;
+ widget_class->button_release_event = gtk_tree_view_button_release;
+ widget_class->draw_focus = gtk_tree_view_draw_focus;
+ widget_class->focus_in_event = gtk_tree_view_focus_in;
+ widget_class->focus_out_event = gtk_tree_view_focus_out;
+
+ container_class->forall = gtk_tree_view_forall;
+ container_class->remove = gtk_tree_view_remove;
+ container_class->focus = gtk_tree_view_focus;
+
+ class->set_scroll_adjustments = gtk_tree_view_set_adjustments;
+
+ widget_class->set_scroll_adjustments_signal =
+ gtk_signal_new ("set_scroll_adjustments",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkTreeViewClass, set_scroll_adjustments),
+ gtk_marshal_NONE__POINTER_POINTER,
+ GTK_TYPE_NONE, 2,
+ GTK_TYPE_POINTER, GTK_TYPE_POINTER);
+}
+
+static void
+gtk_tree_view_init (GtkTreeView *tree_view)
+{
+ tree_view->priv = g_new0 (GtkTreeViewPrivate, 1);
+
+ GTK_WIDGET_UNSET_FLAGS (tree_view, GTK_NO_WINDOW);
+ GTK_WIDGET_SET_FLAGS (tree_view, GTK_CAN_FOCUS);
+
+ tree_view->priv->flags = GTK_TREE_VIEW_IS_LIST | GTK_TREE_VIEW_SHOW_EXPANDERS | GTK_TREE_VIEW_DRAW_KEYFOCUS | GTK_TREE_VIEW_HEADERS_VISIBLE;
+ tree_view->priv->tab_offset = TREE_VIEW_EXPANDER_WIDTH;
+ tree_view->priv->columns = 0;
+ tree_view->priv->column = NULL;
+ tree_view->priv->button_pressed_node = NULL;
+ tree_view->priv->button_pressed_tree = NULL;
+ tree_view->priv->prelight_node = NULL;
+ tree_view->priv->prelight_offset = 0;
+ tree_view->priv->header_height = 1;
+ tree_view->priv->x_drag = 0;
+ tree_view->priv->drag_pos = -1;
+ tree_view->priv->selection = NULL;
+ tree_view->priv->anchor = NULL;
+ tree_view->priv->cursor = NULL;
+ gtk_tree_view_set_adjustments (tree_view, NULL, NULL);
+ _gtk_tree_view_set_size (tree_view, 0, 0);
+}
+
+/* Widget methods
+ */
+
+static void
+gtk_tree_view_realize_buttons (GtkTreeView *tree_view)
+{
+ GList *list;
+ GtkTreeViewColumn *column;
+ GdkWindowAttr attr;
+ guint attributes_mask;
+
+ if (!GTK_WIDGET_REALIZED (tree_view) || tree_view->priv->header_window == NULL)
+ return;
+
+ attr.window_type = GDK_WINDOW_CHILD;
+ attr.wclass = GDK_INPUT_ONLY;
+ attr.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
+ attr.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
+ attr.event_mask = gtk_widget_get_events (GTK_WIDGET (tree_view));
+ attr.event_mask = (GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_POINTER_MOTION_MASK |
+ GDK_POINTER_MOTION_HINT_MASK |
+ GDK_KEY_PRESS_MASK);
+ attributes_mask = GDK_WA_CURSOR | GDK_WA_X | GDK_WA_Y;
+ attr.cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
+ tree_view->priv->cursor_drag = attr.cursor;
+
+ attr.y = 0;
+ attr.width = TREE_VIEW_DRAG_WIDTH;
+ attr.height = tree_view->priv->header_height;
+
+ for (list = tree_view->priv->column; list; list = list->next)
+ {
+ column = list->data;
+ if (column->button)
+ {
+ if (column->visible == FALSE)
+ continue;
+ gtk_widget_set_parent_window (column->button,
+ tree_view->priv->header_window);
+ gtk_widget_show (column->button);
+
+ attr.x = (column->button->allocation.x + column->button->allocation.width) - 3;
+
+ column->window = gdk_window_new (tree_view->priv->header_window,
+ &attr, attributes_mask);
+ gdk_window_set_user_data (column->window, tree_view);
+ }
+ }
+}
+
+static void
+gtk_tree_view_realize (GtkWidget *widget)
+{
+ GList *tmp_list;
+ GtkTreeView *tree_view;
+ GdkGCValues values;
+ GdkWindowAttr attributes;
+ gint attributes_mask;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW (widget));
+
+ tree_view = GTK_TREE_VIEW (widget);
+
+ if (!GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_MODEL_SETUP) &&
+ tree_view->priv->model)
+ gtk_tree_view_set_model_realized (tree_view);
+
+ gtk_tree_view_check_dirty (GTK_TREE_VIEW (widget));
+ GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+
+ /* Make the main, clipping window */
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.x = widget->allocation.x;
+ attributes.y = widget->allocation.y;
+ attributes.width = widget->allocation.width;
+ attributes.height = widget->allocation.height;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+ attributes.visual = gtk_widget_get_visual (widget);
+ attributes.colormap = gtk_widget_get_colormap (widget);
+ attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
+
+ attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+
+ widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
+ &attributes, attributes_mask);
+ gdk_window_set_user_data (widget->window, widget);
+
+ /* Make the window for the tree */
+ attributes.x = 0;
+ attributes.y = 0;
+ attributes.width = tree_view->priv->width;
+ attributes.height = tree_view->priv->height + TREE_VIEW_HEADER_HEIGHT (tree_view);
+ attributes.event_mask = GDK_EXPOSURE_MASK |
+ GDK_SCROLL_MASK |
+ GDK_POINTER_MOTION_MASK |
+ GDK_ENTER_NOTIFY_MASK |
+ GDK_LEAVE_NOTIFY_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ gtk_widget_get_events (widget);
+
+ tree_view->priv->bin_window = gdk_window_new (widget->window,
+ &attributes, attributes_mask);
+ gdk_window_set_user_data (tree_view->priv->bin_window, widget);
+
+ /* Make the column header window */
+ attributes.x = 0;
+ attributes.y = 0;
+ attributes.width = MAX (tree_view->priv->width, widget->allocation.width);
+ attributes.height = tree_view->priv->header_height;
+ attributes.event_mask = (GDK_EXPOSURE_MASK |
+ GDK_SCROLL_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_KEY_PRESS_MASK |
+ GDK_KEY_RELEASE_MASK) |
+ gtk_widget_get_events (widget);
+
+ tree_view->priv->header_window = gdk_window_new (widget->window,
+ &attributes, attributes_mask);
+ gdk_window_set_user_data (tree_view->priv->header_window, widget);
+
+
+ values.foreground = (widget->style->white.pixel==0 ?
+ widget->style->black:widget->style->white);
+ values.function = GDK_XOR;
+ values.subwindow_mode = GDK_INCLUDE_INFERIORS;
+ tree_view->priv->xor_gc = gdk_gc_new_with_values (widget->window,
+ &values,
+ GDK_GC_FOREGROUND |
+ GDK_GC_FUNCTION |
+ GDK_GC_SUBWINDOW);
+ /* Add them all up. */
+ widget->style = gtk_style_attach (widget->style, widget->window);
+ gdk_window_set_background (widget->window, &widget->style->base[widget->state]);
+ gdk_window_set_background (tree_view->priv->bin_window, &widget->style->base[widget->state]);
+ gtk_style_set_background (widget->style, tree_view->priv->header_window, GTK_STATE_NORMAL);
+
+ tmp_list = tree_view->priv->children;
+ while (tmp_list)
+ {
+ GtkTreeViewChild *child = tmp_list->data;
+ tmp_list = tmp_list->next;
+
+ gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
+ }
+ gtk_tree_view_realize_buttons (GTK_TREE_VIEW (widget));
+ _gtk_tree_view_set_size (GTK_TREE_VIEW (widget), -1, -1);
+}
+
+static void
+gtk_tree_view_unrealize (GtkWidget *widget)
+{
+ GtkTreeView *tree_view;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW (widget));
+
+ tree_view = GTK_TREE_VIEW (widget);
+
+ gdk_window_set_user_data (tree_view->priv->bin_window, NULL);
+ gdk_window_destroy (tree_view->priv->bin_window);
+ tree_view->priv->bin_window = NULL;
+
+ gdk_window_set_user_data (tree_view->priv->header_window, NULL);
+ gdk_window_destroy (tree_view->priv->header_window);
+ tree_view->priv->header_window = NULL;
+
+ gdk_gc_destroy (tree_view->priv->xor_gc);
+ if (GTK_WIDGET_CLASS (parent_class)->unrealize)
+ (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
+}
+
+static void
+gtk_tree_view_map (GtkWidget *widget)
+{
+ GList *tmp_list;
+ GtkTreeView *tree_view;
+ GList *list;
+ GtkTreeViewColumn *column;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW (widget));
+
+ tree_view = GTK_TREE_VIEW (widget);
+
+ GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
+
+ tmp_list = tree_view->priv->children;
+ while (tmp_list)
+ {
+ GtkTreeViewChild *child = tmp_list->data;
+ tmp_list = tmp_list->next;
+
+ if (GTK_WIDGET_VISIBLE (child->widget))
+ {
+ if (!GTK_WIDGET_MAPPED (child->widget))
+ gtk_widget_map (child->widget);
+ }
+ }
+ gdk_window_show (tree_view->priv->bin_window);
+ if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE))
+ {
+ for (list = tree_view->priv->column; list; list = list->next)
+ {
+ column = list->data;
+ gtk_widget_map (column->button);
+ }
+ for (list = tree_view->priv->column; list; list = list->next)
+ {
+ column = list->data;
+ if (column->visible == FALSE)
+ continue;
+ if (column->column_type == GTK_TREE_VIEW_COLUMN_RESIZEABLE)
+ {
+ gdk_window_raise (column->window);
+ gdk_window_show (column->window);
+ }
+ else
+ gdk_window_hide (column->window);
+ }
+ gdk_window_show (tree_view->priv->header_window);
+ }
+ gdk_window_show (widget->window);
+}
+
+static void
+gtk_tree_view_size_request (GtkWidget *widget,
+ GtkRequisition *requisition)
+{
+ GtkTreeView *tree_view;
+ GList *tmp_list;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW (widget));
+
+ tree_view = GTK_TREE_VIEW (widget);
+
+ requisition->width = 200;
+ requisition->height = 200;
+
+ tmp_list = tree_view->priv->children;
+
+ while (tmp_list)
+ {
+ GtkTreeViewChild *child = tmp_list->data;
+ GtkRequisition child_requisition;
+
+ tmp_list = tmp_list->next;
+
+ gtk_widget_size_request (child->widget, &child_requisition);
+ }
+}
+
+static void
+gtk_tree_view_size_allocate_buttons (GtkWidget *widget)
+{
+ GtkTreeView *tree_view;
+ GList *list;
+ GList *last_column;
+ GtkTreeViewColumn *column;
+ GtkAllocation allocation;
+ gint width = 0;
+
+ tree_view = GTK_TREE_VIEW (widget);
+
+ allocation.y = 0;
+ allocation.height = tree_view->priv->header_height;
+
+ for (last_column = g_list_last (tree_view->priv->column);
+ last_column && !(GTK_TREE_VIEW_COLUMN (last_column->data)->visible);
+ last_column = last_column->prev)
+ ;
+
+ if (last_column == NULL)
+ return;
+
+ for (list = tree_view->priv->column; list != last_column; list = list->next)
+ {
+ column = list->data;
+
+ if (!column->visible)
+ continue;
+
+ allocation.x = width;
+ allocation.width = column->size;
+ width += column->size;
+ gtk_widget_size_allocate (column->button, &allocation);
+
+ if (column->window)
+ gdk_window_move (column->window, width - TREE_VIEW_DRAG_WIDTH/2, 0);
+ }
+ column = list->data;
+ allocation.x = width;
+ allocation.width = MAX (widget->allocation.width, tree_view->priv->width) - width;
+ gtk_widget_size_allocate (column->button, &allocation);
+ if (column->window)
+ gdk_window_move (column->window,
+ allocation.x +allocation.width - TREE_VIEW_DRAG_WIDTH/2,
+ 0);
+}
+
+static void
+gtk_tree_view_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ GList *tmp_list;
+ GtkTreeView *tree_view;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW (widget));
+
+ widget->allocation = *allocation;
+
+ tree_view = GTK_TREE_VIEW (widget);
+
+ tmp_list = tree_view->priv->children;
+
+ while (tmp_list)
+ {
+ GtkAllocation allocation;
+ GtkRequisition requisition;
+
+ GtkTreeViewChild *child = tmp_list->data;
+ tmp_list = tmp_list->next;
+
+ allocation.x = child->x;
+ allocation.y = child->y;
+ gtk_widget_get_child_requisition (child->widget, &requisition);
+ allocation.width = requisition.width;
+ allocation.height = requisition.height;
+
+ gtk_widget_size_allocate (child->widget, &allocation);
+ }
+
+ if (GTK_WIDGET_REALIZED (widget))
+ {
+ gdk_window_move_resize (widget->window,
+ allocation->x, allocation->y,
+ allocation->width, allocation->height);
+ gdk_window_move_resize (tree_view->priv->header_window,
+ 0, 0,
+ MAX (tree_view->priv->width, allocation->width),
+ tree_view->priv->header_height);
+ }
+
+ tree_view->priv->hadjustment->page_size = allocation->width;
+ tree_view->priv->hadjustment->page_increment = allocation->width / 2;
+ tree_view->priv->hadjustment->lower = 0;
+ tree_view->priv->hadjustment->upper = tree_view->priv->width;
+ if (tree_view->priv->hadjustment->value + allocation->width > tree_view->priv->width)
+ tree_view->priv->hadjustment->value = MAX (tree_view->priv->width - allocation->width, 0);
+ gtk_signal_emit_by_name (GTK_OBJECT (tree_view->priv->hadjustment), "changed");
+
+ tree_view->priv->vadjustment->page_size = allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view);
+ tree_view->priv->vadjustment->page_increment = (allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view)) / 2;
+ tree_view->priv->vadjustment->lower = 0;
+ tree_view->priv->vadjustment->upper = tree_view->priv->height;
+ if (tree_view->priv->vadjustment->value + allocation->height > tree_view->priv->height)
+ gtk_adjustment_set_value (tree_view->priv->vadjustment,
+ (gfloat) MAX (tree_view->priv->height - allocation->height, 0));
+ gtk_signal_emit_by_name (GTK_OBJECT (tree_view->priv->vadjustment), "changed");
+
+ if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_MODEL_SETUP))
+ gtk_tree_view_size_allocate_buttons (widget);
+}
+
+static void
+gtk_tree_view_draw (GtkWidget *widget,
+ GdkRectangle *area)
+{
+ GList *tmp_list;
+ GtkTreeView *tree_view;
+ GtkTreeViewColumn *column;
+ GdkRectangle child_area;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW (widget));
+
+ tree_view = GTK_TREE_VIEW (widget);
+
+ /* We don't have any way of telling themes about this properly,
+ * so we just assume a background pixmap
+ */
+ if (!GTK_WIDGET_APP_PAINTABLE (widget))
+ {
+ gdk_window_clear_area (tree_view->priv->bin_window,
+ area->x, area->y, area->width, area->height);
+ gdk_window_clear_area (tree_view->priv->header_window,
+ area->x, area->y, area->width, area->height);
+ }
+
+ tmp_list = tree_view->priv->children;
+ while (tmp_list)
+ {
+ GtkTreeViewChild *child = tmp_list->data;
+ tmp_list = tmp_list->next;
+
+ if (gtk_widget_intersect (child->widget, area, &child_area))
+ gtk_widget_draw (child->widget, &child_area);
+ }
+ for (tmp_list = tree_view->priv->column; tmp_list; tmp_list = tmp_list->next)
+ {
+ column = tmp_list->data;
+ if (!column->visible)
+ continue;
+ if (column->button &&
+ gtk_widget_intersect(column->button, area, &child_area))
+ gtk_widget_draw (column->button, &child_area);
+ }
+}
+
+/* Warning: Very scary function.
+ * Modify at your own risk
+ */
+static gboolean
+gtk_tree_view_bin_expose (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ GtkTreeView *tree_view;
+ GtkTreePath *path;
+ GtkRBTree *tree;
+ GList *list;
+ GtkRBNode *node, *last_node = NULL;
+ GtkRBNode *cursor = NULL;
+ GtkRBTree *cursor_tree = NULL, *last_tree = NULL;
+ GtkTreeNode tree_node;
+ GtkCellRenderer *cell;
+ gint new_y;
+ gint y_offset, x_offset, cell_offset;
+ gint i, max_height;
+ gint depth;
+ GdkRectangle background_area;
+ GdkRectangle cell_area;
+ guint flags;
+ gboolean last_selected;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
+
+ tree_view = GTK_TREE_VIEW (widget);
+
+ if (tree_view->priv->tree == NULL)
+ return TRUE;
+
+ gtk_tree_view_check_dirty (GTK_TREE_VIEW (widget));
+ /* we want to account for a potential HEADER offset.
+ * That is, if the header exists, we want to offset our event by its
+ * height to find the right node.
+ */
+ new_y = (event->area.y<TREE_VIEW_HEADER_HEIGHT (tree_view))?TREE_VIEW_HEADER_HEIGHT (tree_view):event->area.y;
+ y_offset = -_gtk_rbtree_find_offset (tree_view->priv->tree,
+ new_y - TREE_VIEW_HEADER_HEIGHT (tree_view),
+ &tree,
+ &node) + new_y - event->area.y;
+ if (node == NULL)
+ return TRUE;
+
+ /* See if the last node was selected */
+ _gtk_rbtree_prev_full (tree, node, &last_tree, &last_node);
+ last_selected = (last_node && GTK_RBNODE_FLAG_SET (last_node, GTK_RBNODE_IS_SELECTED));
+
+ /* find the path for the node */
+ path = _gtk_tree_view_find_path ((GtkTreeView *)widget,
+ tree,
+ node);
+ tree_node = gtk_tree_model_get_node (tree_view->priv->model, path);
+ depth = gtk_tree_path_get_depth (path);
+ gtk_tree_path_free (path);
+
+ if (tree_view->priv->cursor)
+ _gtk_tree_view_find_node (tree_view, tree_view->priv->cursor, &cursor_tree, &cursor);
+
+ /* Actually process the expose event. To do this, we want to
+ * start at the first node of the event, and walk the tree in
+ * order, drawing each successive node.
+ */
+
+ do
+ {
+ /* Need to think about this more.
+ if (tree_view->priv->show_expanders)
+ max_height = MAX (TREE_VIEW_EXPANDER_MIN_HEIGHT, GTK_RBNODE_GET_HEIGHT (node));
+ else
+ */
+ max_height = GTK_RBNODE_GET_HEIGHT (node);
+
+ x_offset = -event->area.x;
+ cell_offset = 0;
+
+ background_area.y = y_offset + event->area.y + TREE_VIEW_VERTICAL_SEPERATOR;
+ background_area.height = max_height - TREE_VIEW_VERTICAL_SEPERATOR;
+ flags = 0;
+
+ if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PRELIT))
+ flags |= GTK_CELL_RENDERER_PRELIT;
+
+ if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
+ {
+ flags |= GTK_CELL_RENDERER_SELECTED;
+
+ /* Draw the selection */
+ gdk_draw_rectangle (event->window,
+ GTK_WIDGET (tree_view)->style->bg_gc [GTK_STATE_SELECTED],
+ TRUE,
+ event->area.x,
+ background_area.y - (last_selected?TREE_VIEW_VERTICAL_SEPERATOR:0),
+ event->area.width,
+ background_area.height + (last_selected?TREE_VIEW_VERTICAL_SEPERATOR:0));
+ last_selected = TRUE;
+ }
+ else
+ {
+ last_selected = FALSE;
+ }
+
+ for (i = 0, list = tree_view->priv->column; i < tree_view->priv->columns; i++, list = list->next)
+ {
+ GtkTreeViewColumn *column = list->data;
+
+ if (!column->visible)
+ continue;
+
+ cell = column->cell;
+ gtk_tree_view_column_set_cell_data (column,
+ tree_view->priv->model,
+ tree_node);
+
+ background_area.x = cell_offset;
+ background_area.width = TREE_VIEW_COLUMN_SIZE (column);
+ if (i == 0 && TREE_VIEW_DRAW_EXPANDERS(tree_view))
+ {
+ cell_area = background_area;
+ cell_area.x += depth*tree_view->priv->tab_offset;
+ cell_area.width -= depth*tree_view->priv->tab_offset;
+ gtk_cell_renderer_render (cell,
+ event->window,
+ widget,
+ &background_area,
+ &cell_area,
+ &event->area,
+ flags);
+ if ((node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT)
+ {
+ gint x, y;
+ gdk_window_get_pointer (tree_view->priv->bin_window, &x, &y, 0);
+ gtk_tree_view_draw_arrow (GTK_TREE_VIEW (widget),
+ node,
+ event->area.y + y_offset,
+ x, y);
+ }
+ }
+ else
+ {
+ cell_area = background_area;
+ gtk_cell_renderer_render (cell,
+ event->window,
+ widget,
+ &background_area,
+ &cell_area,
+ &event->area,
+ flags);
+ }
+ cell_offset += TREE_VIEW_COLUMN_SIZE (column);
+ }
+
+ if (node == cursor &&
+ GTK_WIDGET_HAS_FOCUS (widget))
+ gtk_tree_view_draw_focus (widget);
+
+ y_offset += max_height;
+ if (node->children)
+ {
+ tree = node->children;
+ node = tree->root;
+ while (node->left != tree->nil)
+ node = node->left;
+ tree_node = gtk_tree_model_node_children (tree_view->priv->model, tree_node);
+ cell = gtk_tree_view_get_column (tree_view, 0)->cell;
+ depth++;
+
+ /* Sanity Check! */
+ TREE_VIEW_INTERNAL_ASSERT (tree_node != NULL, FALSE);
+ }
+ else
+ {
+ gboolean done = FALSE;
+ do
+ {
+ node = _gtk_rbtree_next (tree, node);
+ if (node != NULL)
+ {
+ gtk_tree_model_node_next (tree_view->priv->model, &tree_node);
+ cell = gtk_tree_view_get_column (tree_view, 0)->cell;
+ done = TRUE;
+
+ /* Sanity Check! */
+ TREE_VIEW_INTERNAL_ASSERT (tree_node != NULL, FALSE);
+ }
+ else
+ {
+ node = tree->parent_node;
+ tree = tree->parent_tree;
+ if (tree == NULL)
+ /* we've run out of tree. It's okay though, as we'd only break
+ * out of the while loop below. */
+ return TRUE;
+ tree_node = gtk_tree_model_node_parent (tree_view->priv->model, tree_node);
+ depth--;
+
+ /* Sanity check */
+ TREE_VIEW_INTERNAL_ASSERT (tree_node != NULL, FALSE);
+ }
+ }
+ while (!done);
+ }
+ }
+ while (y_offset < event->area.height);
+
+ return TRUE;
+}
+
+static gboolean
+gtk_tree_view_expose (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ GtkTreeView *tree_view;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
+
+ tree_view = GTK_TREE_VIEW (widget);
+
+ if (event->window == tree_view->priv->bin_window)
+ return gtk_tree_view_bin_expose (widget, event);
+
+ return TRUE;
+}
+
+static gboolean
+gtk_tree_view_motion (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ GtkTreeView *tree_view;
+ GtkRBTree *tree;
+ GtkRBNode *node;
+ gint new_y;
+ gint y_offset;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
+
+ tree_view = GTK_TREE_VIEW (widget);
+
+ if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE))
+ {
+ gint x;
+ gint new_width;
+
+ if (event->is_hint || event->window != widget->window)
+ gtk_widget_get_pointer (widget, &x, NULL);
+ else
+ x = event->x;
+
+ new_width = gtk_tree_view_new_column_width (GTK_TREE_VIEW (widget), tree_view->priv->drag_pos, &x);
+ if (x != tree_view->priv->x_drag)
+ {
+ gtk_tree_view_column_set_size (gtk_tree_view_get_column (GTK_TREE_VIEW (widget), tree_view->priv->drag_pos), new_width);
+ }
+
+ /* FIXME: We need to scroll */
+ _gtk_tree_view_set_size (GTK_TREE_VIEW (widget), -1, tree_view->priv->height);
+ return FALSE;
+ }
+
+ /* Sanity check it */
+ if (event->window != tree_view->priv->bin_window)
+ return FALSE;
+ if (tree_view->priv->tree == NULL)
+ return FALSE;
+
+ if (tree_view->priv->prelight_node != NULL)
+ {
+ if ((((gint) event->y - TREE_VIEW_HEADER_HEIGHT (tree_view) < tree_view->priv->prelight_offset) ||
+ ((gint) event->y - TREE_VIEW_HEADER_HEIGHT (tree_view) >=
+ (tree_view->priv->prelight_offset + GTK_RBNODE_GET_HEIGHT (tree_view->priv->prelight_node))) ||
+ ((gint) event->x > tree_view->priv->tab_offset)))
+ /* We need to unprelight the old one. */
+ {
+ if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT))
+ {
+ GTK_RBNODE_UNSET_FLAG (tree_view->priv->prelight_node, GTK_RBNODE_IS_PRELIT);
+ gtk_tree_view_draw_arrow (GTK_TREE_VIEW (widget),
+ tree_view->priv->prelight_node,
+ tree_view->priv->prelight_offset,
+ event->x,
+ event->y);
+ GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_ARROW_PRELIT);
+ }
+
+ GTK_RBNODE_UNSET_FLAG (tree_view->priv->prelight_node, GTK_RBNODE_IS_PRELIT);
+ tree_view->priv->prelight_node = NULL;
+ tree_view->priv->prelight_tree = NULL;
+ tree_view->priv->prelight_offset = 0;
+ }
+ }
+
+ new_y = ((gint)event->y<TREE_VIEW_HEADER_HEIGHT (tree_view))?TREE_VIEW_HEADER_HEIGHT (tree_view):(gint)event->y;
+ y_offset = -_gtk_rbtree_find_offset (tree_view->priv->tree, new_y - TREE_VIEW_HEADER_HEIGHT (tree_view),
+ &tree,
+ &node) + new_y - (gint)event->y;
+
+ if (node == NULL)
+ return TRUE;
+
+ /* If we are currently pressing down a button, we don't want to prelight anything else. */
+ if ((tree_view->priv->button_pressed_node != NULL) &&
+ (tree_view->priv->button_pressed_node != node))
+ return TRUE;
+
+ /* Do we want to prelight a tab? */
+ GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_ARROW_PRELIT);
+ if (event->x <= tree_view->priv->tab_offset &&
+ event->x >= 0 &&
+ ((node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT))
+ {
+ tree_view->priv->prelight_offset = event->y+y_offset;
+ GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_ARROW_PRELIT);
+ }
+
+ tree_view->priv->prelight_node = node;
+ tree_view->priv->prelight_tree = tree;
+ tree_view->priv->prelight_offset = event->y+y_offset;
+
+ GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_PRELIT);
+ gtk_widget_queue_draw (widget);
+
+ return TRUE;
+}
+
+/* Is this function necessary? Can I get an enter_notify event w/o either
+ * an expose event or a mouse motion event?
+ */
+static gboolean
+gtk_tree_view_enter_notify (GtkWidget *widget,
+ GdkEventCrossing *event)
+{
+ GtkTreeView *tree_view;
+ GtkRBTree *tree;
+ GtkRBNode *node;
+ gint new_y;
+ gint y_offset;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
+
+ tree_view = GTK_TREE_VIEW (widget);
+
+ /* Sanity check it */
+ if (event->window != tree_view->priv->bin_window)
+ return FALSE;
+ if (tree_view->priv->tree == NULL)
+ return FALSE;
+
+ if ((tree_view->priv->button_pressed_node != NULL) &&
+ (tree_view->priv->button_pressed_node != node))
+ return TRUE;
+
+ /* find the node internally */
+ new_y = ((gint)event->y<TREE_VIEW_HEADER_HEIGHT (tree_view))?TREE_VIEW_HEADER_HEIGHT (tree_view):(gint)event->y;
+ y_offset = -_gtk_rbtree_find_offset (tree_view->priv->tree,
+ new_y - TREE_VIEW_HEADER_HEIGHT (tree_view),
+ &tree,
+ &node) + new_y - (gint)event->y;
+
+ if (node == NULL)
+ return FALSE;
+
+ /* Do we want to prelight a tab? */
+ GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_ARROW_PRELIT);
+ if (event->x <= tree_view->priv->tab_offset &&
+ event->x >= 0 &&
+ ((node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT))
+ {
+ tree_view->priv->prelight_offset = event->y+y_offset;
+ GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_ARROW_PRELIT);
+ }
+
+ tree_view->priv->prelight_node = node;
+ tree_view->priv->prelight_tree = tree;
+ tree_view->priv->prelight_offset = event->y+y_offset;
+
+ GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_PRELIT);
+ gtk_widget_queue_draw (widget);
+
+ return TRUE;
+}
+
+static gboolean
+gtk_tree_view_leave_notify (GtkWidget *widget,
+ GdkEventCrossing *event)
+{
+ GtkTreeView *tree_view;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
+
+ tree_view = GTK_TREE_VIEW (widget);
+
+ if (tree_view->priv->prelight_node != NULL)
+ {
+ GTK_RBNODE_UNSET_FLAG (tree_view->priv->prelight_node, GTK_RBNODE_IS_PRELIT);
+ tree_view->priv->prelight_node = NULL;
+ tree_view->priv->prelight_tree = NULL;
+ tree_view->priv->prelight_offset = 0;
+ GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_ARROW_PRELIT);
+ gtk_widget_queue_draw (widget);
+ }
+ return TRUE;
+}
+
+static gboolean
+gtk_tree_view_button_press (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GtkTreeView *tree_view;
+ GList *list;
+ GtkTreeViewColumn *column;
+ gint i;
+ GdkRectangle background_area;
+ GdkRectangle cell_area;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ tree_view = GTK_TREE_VIEW (widget);
+
+ if (event->window == tree_view->priv->bin_window)
+ {
+ GtkRBNode *node;
+ GtkRBTree *tree;
+ GtkTreePath *path;
+ gchar *path_string;
+ gint depth;
+ gint new_y;
+ gint y_offset;
+
+ if (!GTK_WIDGET_HAS_FOCUS (widget))
+ gtk_widget_grab_focus (widget);
+ GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS);
+ /* are we in an arrow? */
+ if (tree_view->priv->prelight_node != FALSE && GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT))
+ {
+ if (event->button == 1)
+ {
+ gtk_grab_add (widget);
+ tree_view->priv->button_pressed_node = tree_view->priv->prelight_node;
+ tree_view->priv->button_pressed_tree = tree_view->priv->prelight_tree;
+ gtk_tree_view_draw_arrow (GTK_TREE_VIEW (widget),
+ tree_view->priv->prelight_node,
+ tree_view->priv->prelight_offset,
+ event->x,
+ event->y);
+ }
+ return TRUE;
+ }
+
+ /* find the node that was clicked */
+ new_y = ((gint)event->y<TREE_VIEW_HEADER_HEIGHT (tree_view))?TREE_VIEW_HEADER_HEIGHT (tree_view):(gint)event->y;
+ y_offset = -_gtk_rbtree_find_offset (tree_view->priv->tree,
+ new_y - TREE_VIEW_HEADER_HEIGHT (tree_view),
+ &tree,
+ &node) + new_y - (gint)event->y;
+
+ if (node == NULL)
+ /* We clicked in dead space */
+ return TRUE;
+
+ /* Get the path and the node */
+ path = _gtk_tree_view_find_path (tree_view, tree, node);
+ depth = gtk_tree_path_get_depth (path);
+ background_area.y = y_offset + event->y + TREE_VIEW_VERTICAL_SEPERATOR;
+ background_area.height = GTK_RBNODE_GET_HEIGHT (node) - TREE_VIEW_VERTICAL_SEPERATOR;
+ background_area.x = 0;
+ /* Let the cell have a chance at selecting it. */
+
+ for (i = 0, list = tree_view->priv->column; i < tree_view->priv->columns; i++, list = list->next)
+ {
+ GtkTreeViewColumn *column = list->data;
+ GtkCellRenderer *cell;
+ GtkTreeNode *tree_node;
+
+ if (!column->visible)
+ continue;
+
+ background_area.width = TREE_VIEW_COLUMN_SIZE (column);
+ if (i == 0 && TREE_VIEW_DRAW_EXPANDERS(tree_view))
+ {
+ cell_area = background_area;
+ cell_area.x += depth*tree_view->priv->tab_offset;
+ cell_area.width -= depth*tree_view->priv->tab_offset;
+ }
+ else
+ {
+ cell_area = background_area;
+ }
+
+ cell = column->cell;
+
+ if ((background_area.x > (gint) event->x) ||
+ (background_area.y > (gint) event->y) ||
+ (background_area.x + background_area.width <= (gint) event->x) ||
+ (background_area.y + background_area.height <= (gint) event->y))
+ {
+ background_area.x += background_area.width;
+ continue;
+ }
+
+ tree_node = gtk_tree_model_get_node (tree_view->priv->model,
+ path);
+ gtk_tree_view_column_set_cell_data (column,
+ tree_view->priv->model,
+ tree_node);
+
+ path_string = gtk_tree_path_to_string (path);
+ if (gtk_cell_renderer_event (cell,
+ (GdkEvent *)event,
+ widget,
+ path_string,
+ &background_area,
+ &cell_area,
+ 0))
+
+ {
+ g_free (path_string);
+ gtk_tree_path_free (path);
+ return TRUE;
+ }
+ else
+ {
+ g_free (path_string);
+ break;
+ }
+ }
+ /* Handle the selection */
+ if (tree_view->priv->selection == NULL)
+ gtk_tree_selection_new_with_tree_view (tree_view);
+
+ _gtk_tree_selection_internal_select_node (tree_view->priv->selection,
+ node,
+ tree,
+ path,
+ event->state);
+ gtk_tree_path_free (path);
+ return TRUE;
+ }
+
+ for (i = 0, list = tree_view->priv->column; list; list = list->next, i++)
+ {
+ column = list->data;
+ if (event->window == column->window &&
+ column->column_type == GTK_TREE_VIEW_COLUMN_RESIZEABLE &&
+ column->window)
+ {
+ gpointer drag_data;
+
+ if (gdk_pointer_grab (column->window, FALSE,
+ GDK_POINTER_MOTION_HINT_MASK |
+ GDK_BUTTON1_MOTION_MASK |
+ GDK_BUTTON_RELEASE_MASK,
+ NULL, NULL, event->time))
+ return FALSE;
+
+ gtk_grab_add (widget);
+ GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE);
+
+ /* block attached dnd signal handler */
+ drag_data = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
+ if (drag_data)
+ gtk_signal_handler_block_by_data (GTK_OBJECT (widget), drag_data);
+
+ if (!GTK_WIDGET_HAS_FOCUS (widget))
+ gtk_widget_grab_focus (widget);
+
+ tree_view->priv->drag_pos = i;
+ tree_view->priv->x_drag = (column->button->allocation.x + column->button->allocation.width);
+ }
+ }
+ return TRUE;
+}
+
+static gboolean
+gtk_tree_view_button_release (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GtkTreeView *tree_view;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ tree_view = GTK_TREE_VIEW (widget);
+
+ if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE))
+ {
+ gpointer drag_data;
+ gint width;
+ gint x;
+ gint i;
+
+ i = tree_view->priv->drag_pos;
+ tree_view->priv->drag_pos = -1;
+
+ /* unblock attached dnd signal handler */
+ drag_data = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
+ if (drag_data)
+ gtk_signal_handler_unblock_by_data (GTK_OBJECT (widget), drag_data);
+
+ GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE);
+ gtk_widget_get_pointer (widget, &x, NULL);
+ gtk_grab_remove (widget);
+ gdk_pointer_ungrab (event->time);
+
+ width = gtk_tree_view_new_column_width (GTK_TREE_VIEW (widget), i, &x);
+ gtk_tree_view_column_set_size (gtk_tree_view_get_column (GTK_TREE_VIEW (widget), i), width);
+ return FALSE;
+ }
+
+ if (tree_view->priv->button_pressed_node == NULL)
+ return FALSE;
+
+ if (event->button == 1)
+ {
+ gtk_grab_remove (widget);
+ if (tree_view->priv->button_pressed_node == tree_view->priv->prelight_node && GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT))
+ {
+ GtkTreePath *path;
+ GtkTreeNode *tree_node;
+
+ /* Actually activate the node */
+ if (tree_view->priv->button_pressed_node->children == NULL)
+ {
+ path = _gtk_tree_view_find_path (GTK_TREE_VIEW (widget),
+ tree_view->priv->button_pressed_tree,
+ tree_view->priv->button_pressed_node);
+ tree_view->priv->button_pressed_node->children = _gtk_rbtree_new ();
+ tree_view->priv->button_pressed_node->children->parent_tree = tree_view->priv->button_pressed_tree;
+ tree_view->priv->button_pressed_node->children->parent_node = tree_view->priv->button_pressed_node;
+ tree_node = gtk_tree_model_get_node (tree_view->priv->model, path);
+ tree_node = gtk_tree_model_node_children (tree_view->priv->model, tree_node);
+
+ gtk_tree_view_build_tree (tree_view,
+ tree_view->priv->button_pressed_node->children,
+ tree_node,
+ gtk_tree_path_get_depth (path) + 1,
+ FALSE,
+ GTK_WIDGET_REALIZED (widget));
+ }
+ else
+ {
+ path = _gtk_tree_view_find_path (GTK_TREE_VIEW (widget),
+ tree_view->priv->button_pressed_node->children,
+ tree_view->priv->button_pressed_node->children->root);
+ tree_node = gtk_tree_model_get_node (tree_view->priv->model, path);
+
+ gtk_tree_view_discover_dirty (GTK_TREE_VIEW (widget),
+ tree_view->priv->button_pressed_node->children,
+ tree_node,
+ gtk_tree_path_get_depth (path));
+ _gtk_rbtree_remove (tree_view->priv->button_pressed_node->children);
+ }
+ gtk_tree_path_free (path);
+
+ _gtk_tree_view_set_size (GTK_TREE_VIEW (widget), -1, -1);
+ gtk_widget_queue_resize (widget);
+ }
+
+ tree_view->priv->button_pressed_node = NULL;
+ }
+
+ return TRUE;
+}
+
+
+static void
+gtk_tree_view_draw_focus (GtkWidget *widget)
+{
+ GtkTreeView *tree_view;
+ GtkRBTree *cursor_tree = NULL;
+ GtkRBNode *cursor = NULL;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW (widget));
+
+ tree_view = GTK_TREE_VIEW (widget);
+
+ if (! GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS))
+ return;
+ if (tree_view->priv->cursor == NULL)
+ return;
+
+ _gtk_tree_view_find_node (tree_view, tree_view->priv->cursor, &cursor_tree, &cursor);
+ if (cursor == NULL)
+ return;
+
+ gdk_draw_rectangle (tree_view->priv->bin_window,
+ widget->style->fg_gc[GTK_STATE_NORMAL],
+ FALSE,
+ 0,
+ _gtk_rbtree_node_find_offset (cursor_tree, cursor) + TREE_VIEW_HEADER_HEIGHT (tree_view),
+ (gint) MAX (tree_view->priv->width, tree_view->priv->hadjustment->upper),
+ GTK_RBNODE_GET_HEIGHT (cursor));
+}
+
+
+static gint
+gtk_tree_view_focus_in (GtkWidget *widget,
+ GdkEventFocus *event)
+{
+ GtkTreeView *tree_view;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ tree_view = GTK_TREE_VIEW (widget);
+
+ GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
+
+ gtk_widget_draw_focus (widget);
+
+ return FALSE;
+}
+
+
+static gint
+gtk_tree_view_focus_out (GtkWidget *widget,
+ GdkEventFocus *event)
+{
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
+
+ gtk_widget_queue_draw (widget);
+
+ return FALSE;
+}
+
+/* FIXME: It would be neat to someday make the headers a seperate widget that
+ * can be shared between various apps
+ */
+/* Returns TRUE if the focus is within the headers, after the focus operation is
+ * done
+ */
+static gboolean
+gtk_tree_view_header_focus (GtkTreeView *tree_view,
+ GtkDirectionType dir)
+{
+ GtkWidget *focus_child;
+ GtkContainer *container;
+
+ GList *last_column, *first_column;
+ GList *tmp_list;
+
+ if (! GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE))
+ return FALSE;
+
+ focus_child = GTK_CONTAINER (tree_view)->focus_child;
+ container = GTK_CONTAINER (tree_view);
+
+ for (last_column = g_list_last (tree_view->priv->column);
+ last_column &&
+ !(GTK_TREE_VIEW_COLUMN (last_column->data)->visible) &&
+ GTK_WIDGET_CAN_FOCUS (GTK_TREE_VIEW_COLUMN (last_column->data)->button);
+ last_column = last_column->prev)
+ ;
+
+ for (first_column = tree_view->priv->column;
+ first_column &&
+ !(GTK_TREE_VIEW_COLUMN (first_column->data)->visible) &&
+ GTK_WIDGET_CAN_FOCUS (GTK_TREE_VIEW_COLUMN (first_column->data)->button);
+ first_column = first_column->next)
+ ;
+
+ /* no headers are visible, or are focussable. We can't focus in or out.
+ * I wonder if focussable is a real word...
+ */
+ if (last_column == NULL)
+ {
+ gtk_container_set_focus_child (container, NULL);
+ return FALSE;
+ }
+
+ /* First thing we want to handle is entering and leaving the headers.
+ */
+ switch (dir)
+ {
+ case GTK_DIR_TAB_BACKWARD:
+ if (!focus_child)
+ {
+ focus_child = GTK_TREE_VIEW_COLUMN (last_column->data)->button;
+ gtk_container_set_focus_child (container, focus_child);
+ gtk_widget_grab_focus (focus_child);
+ goto cleanup;
+ }
+ if (focus_child == GTK_TREE_VIEW_COLUMN (first_column->data)->button)
+ {
+ focus_child = NULL;
+ goto cleanup;
+ }
+ break;
+
+ case GTK_DIR_TAB_FORWARD:
+ if (!focus_child)
+ {
+ focus_child = GTK_TREE_VIEW_COLUMN (first_column->data)->button;
+ gtk_container_set_focus_child (container, focus_child);
+ gtk_widget_grab_focus (focus_child);
+ goto cleanup;
+ }
+ if (focus_child == GTK_TREE_VIEW_COLUMN (last_column->data)->button)
+ {
+ focus_child = NULL;
+ goto cleanup;
+ }
+ break;
+
+ case GTK_DIR_LEFT:
+ if (!focus_child)
+ {
+ focus_child = GTK_TREE_VIEW_COLUMN (last_column->data)->button;
+ gtk_container_set_focus_child (container, focus_child);
+ gtk_widget_grab_focus (focus_child);
+ goto cleanup;
+ }
+ if (focus_child == GTK_TREE_VIEW_COLUMN (first_column->data)->button)
+ {
+ focus_child = NULL;
+ goto cleanup;
+ }
+ break;
+
+ case GTK_DIR_RIGHT:
+ if (!focus_child)
+ {
+ focus_child = GTK_TREE_VIEW_COLUMN (first_column->data)->button;
+ gtk_container_set_focus_child (container, focus_child);
+ gtk_widget_grab_focus (focus_child);
+ goto cleanup;
+ }
+ if (focus_child == GTK_TREE_VIEW_COLUMN (last_column->data)->button)
+ {
+ focus_child = NULL;
+ goto cleanup;
+ }
+ break;
+
+ case GTK_DIR_UP:
+ if (!focus_child)
+ {
+ focus_child = GTK_TREE_VIEW_COLUMN (first_column->data)->button;
+ gtk_container_set_focus_child (container, focus_child);
+ gtk_widget_grab_focus (focus_child);
+ }
+ else
+ {
+ focus_child = NULL;
+ }
+ goto cleanup;
+
+ case GTK_DIR_DOWN:
+ if (!focus_child)
+ {
+ focus_child = GTK_TREE_VIEW_COLUMN (first_column->data)->button;
+ gtk_container_set_focus_child (container, focus_child);
+ gtk_widget_grab_focus (focus_child);
+ }
+ else
+ {
+ focus_child = NULL;
+ }
+ goto cleanup;
+ }
+
+ /* We need to move the focus to the next button. */
+ if (focus_child)
+ {
+ for (tmp_list = tree_view->priv->column; tmp_list; tmp_list = tmp_list->next)
+ if (GTK_TREE_VIEW_COLUMN (tmp_list->data)->button == focus_child)
+ {
+ if (gtk_container_focus (GTK_CONTAINER (GTK_TREE_VIEW_COLUMN (tmp_list->data)->button), dir))
+ {
+ /* The focus moves inside the button. */
+ /* This is probably a great example of bad UI */
+ goto cleanup;
+ }
+ break;
+ }
+
+ /* We need to move the focus among the row of buttons. */
+ while (tmp_list)
+ {
+ GtkTreeViewColumn *column;
+
+ if (dir == GTK_DIR_RIGHT || dir == GTK_DIR_TAB_FORWARD)
+ tmp_list = tmp_list->next;
+ else
+ tmp_list = tmp_list->prev;
+
+ if (tmp_list == NULL)
+ {
+ g_warning ("Internal button not found");
+ goto cleanup;
+ }
+ column = tmp_list->data;
+ if (column->button &&
+ column->visible &&
+ GTK_WIDGET_CAN_FOCUS (column->button))
+ {
+ focus_child = column->button;
+ gtk_container_set_focus_child (container, column->button);
+ gtk_widget_grab_focus (column->button);
+ break;
+ }
+ }
+ }
+
+ cleanup:
+ /* if focus child is non-null, we assume it's been set to the current focus child
+ */
+ if (focus_child)
+ {
+ /* If the following isn't true, then the view is smaller then the scrollpane.
+ */
+ if ((focus_child->allocation.x + focus_child->allocation.width) <=
+ (tree_view->priv->hadjustment->upper))
+ {
+ /* Scroll to the button, if needed */
+ if ((tree_view->priv->hadjustment->value + tree_view->priv->hadjustment->page_size) <
+ (focus_child->allocation.x + focus_child->allocation.width))
+ gtk_adjustment_set_value (tree_view->priv->hadjustment,
+ focus_child->allocation.x + focus_child->allocation.width -
+ tree_view->priv->hadjustment->page_size);
+ else if (tree_view->priv->hadjustment->value > focus_child->allocation.x)
+ gtk_adjustment_set_value (tree_view->priv->hadjustment,
+ focus_child->allocation.x);
+ }
+ }
+ else
+ {
+ gtk_container_set_focus_child (container, NULL);
+ }
+
+ return (focus_child != NULL);
+}
+
+/* WARNING: Scary function */
+static gint
+gtk_tree_view_focus (GtkContainer *container,
+ GtkDirectionType direction)
+{
+ GtkTreeView *tree_view;
+ GtkWidget *focus_child;
+ GdkEvent *event;
+ GtkRBTree *cursor_tree;
+ GtkRBNode *cursor_node;
+
+ g_return_val_if_fail (container != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_TREE_VIEW (container), FALSE);
+ g_return_val_if_fail (GTK_WIDGET_VISIBLE (container), FALSE);
+
+ tree_view = GTK_TREE_VIEW (container);
+
+ if (!GTK_WIDGET_IS_SENSITIVE (container))
+ return FALSE;
+ if (tree_view->priv->tree == NULL)
+ return FALSE;
+
+ focus_child = container->focus_child;
+
+ /* Case 1. Headers have focus. */
+ if (focus_child)
+ {
+ switch (direction)
+ {
+ case GTK_DIR_LEFT:
+ case GTK_DIR_TAB_BACKWARD:
+ return (gtk_tree_view_header_focus (tree_view, direction));
+ case GTK_DIR_UP:
+ gtk_container_set_focus_child (container, NULL);
+ return FALSE;
+ case GTK_DIR_TAB_FORWARD:
+ case GTK_DIR_RIGHT:
+ case GTK_DIR_DOWN:
+ if (direction == GTK_DIR_DOWN)
+ {
+ gtk_container_set_focus_child (container, NULL);
+ }
+ else
+ {
+ if (gtk_tree_view_header_focus (tree_view, direction))
+ return TRUE;
+ }
+ GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS);
+ gtk_widget_grab_focus (GTK_WIDGET (container));
+
+ if (tree_view->priv->selection == NULL)
+ gtk_tree_selection_new_with_tree_view (tree_view);
+
+ /* if there is no keyboard focus yet, we select the first node
+ */
+ if (tree_view->priv->cursor == NULL)
+ tree_view->priv->cursor = gtk_tree_path_new_root ();
+ if (tree_view->priv->cursor)
+ gtk_tree_selection_select_path (tree_view->priv->selection,
+ tree_view->priv->cursor);
+ gtk_widget_queue_draw (GTK_WIDGET (tree_view));
+ return TRUE;
+ }
+ }
+
+ /* Case 2. We don't have focus at all. */
+ if (!GTK_WIDGET_HAS_FOCUS (container))
+ {
+ if ((direction == GTK_DIR_TAB_FORWARD) ||
+ (direction == GTK_DIR_RIGHT) ||
+ (direction == GTK_DIR_DOWN))
+ {
+ if (gtk_tree_view_header_focus (tree_view, direction))
+ return TRUE;
+ }
+
+ /* The headers didn't want the focus, so we take it. */
+ GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS);
+ gtk_widget_grab_focus (GTK_WIDGET (container));
+
+ if (tree_view->priv->selection == NULL)
+ gtk_tree_selection_new_with_tree_view (tree_view);
+
+ if (tree_view->priv->cursor == NULL)
+ tree_view->priv->cursor = gtk_tree_path_new_root ();
+
+ if (tree_view->priv->cursor)
+ gtk_tree_selection_select_path (tree_view->priv->selection,
+ tree_view->priv->cursor);
+ gtk_widget_queue_draw (GTK_WIDGET (tree_view));
+ return TRUE;
+ }
+
+ /* Case 3. We have focus already, but no cursor. We pick the first one
+ * and run with it. */
+ if (tree_view->priv->cursor == NULL)
+ {
+ /* We lost our cursor somehow. Arbitrarily select the first node, and
+ * return
+ */
+ tree_view->priv->cursor = gtk_tree_path_new_root ();
+
+ if (tree_view->priv->cursor)
+ gtk_tree_selection_select_path (tree_view->priv->selection,
+ tree_view->priv->cursor);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment),
+ 0.0);
+ gtk_widget_queue_draw (GTK_WIDGET (tree_view));
+ return TRUE;
+ }
+
+
+ /* Case 3. We have focus already. Move the cursor. */
+ if (direction == GTK_DIR_LEFT)
+ {
+ gfloat val;
+ val = tree_view->priv->hadjustment->value - tree_view->priv->hadjustment->page_size/2;
+ val = MAX (val, 0.0);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->hadjustment), val);
+ gtk_widget_grab_focus (GTK_WIDGET (tree_view));
+ return TRUE;
+ }
+ if (direction == GTK_DIR_RIGHT)
+ {
+ gfloat val;
+ val = tree_view->priv->hadjustment->value + tree_view->priv->hadjustment->page_size/2;
+ val = MIN (tree_view->priv->hadjustment->upper - tree_view->priv->hadjustment->page_size, val);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->hadjustment), val);
+ gtk_widget_grab_focus (GTK_WIDGET (tree_view));
+ return TRUE;
+ }
+ cursor_tree = NULL;
+ cursor_node = NULL;
+
+ _gtk_tree_view_find_node (tree_view, tree_view->priv->cursor,
+ &cursor_tree,
+ &cursor_node);
+ switch (direction)
+ {
+ case GTK_DIR_TAB_BACKWARD:
+ case GTK_DIR_UP:
+ _gtk_rbtree_prev_full (cursor_tree,
+ cursor_node,
+ &cursor_tree,
+ &cursor_node);
+ break;
+ case GTK_DIR_TAB_FORWARD:
+ case GTK_DIR_DOWN:
+ _gtk_rbtree_next_full (cursor_tree,
+ cursor_node,
+ &cursor_tree,
+ &cursor_node);
+ break;
+ default:
+ break;
+ }
+
+ if (cursor_node)
+ {
+ GdkModifierType state = 0;
+
+ event = gdk_event_peek ();
+ if (event && event->type == GDK_KEY_PRESS)
+ /* FIXME: This doesn't seem to work. )-:
+ * I fear the event may already have been gotten */
+ state = ((GdkEventKey *)event)->state;
+
+ if (event)
+ gdk_event_free (event);
+ gtk_tree_path_free (tree_view->priv->cursor);
+
+ tree_view->priv->cursor = _gtk_tree_view_find_path (tree_view,
+ cursor_tree,
+ cursor_node);
+ if (tree_view->priv->cursor)
+ _gtk_tree_selection_internal_select_node (tree_view->priv->selection,
+ cursor_node,
+ cursor_tree,
+ tree_view->priv->cursor,
+ state);
+ gtk_tree_view_clamp_node_visible (tree_view, cursor_tree, cursor_node);
+ gtk_widget_grab_focus (GTK_WIDGET (tree_view));
+ gtk_widget_queue_draw (GTK_WIDGET (tree_view));
+ return TRUE;
+ }
+
+ /* At this point, we've progressed beyond the edge of the rows. */
+
+ if ((direction == GTK_DIR_LEFT) ||
+ (direction == GTK_DIR_TAB_BACKWARD) ||
+ (direction == GTK_DIR_UP))
+ /* We can't go back anymore. Try the headers */
+ return (gtk_tree_view_header_focus (tree_view, direction));
+
+ /* we've reached the end of the tree. Go on. */
+ return FALSE;
+}
+
+/* Container method
+ */
+static void
+gtk_tree_view_remove (GtkContainer *container,
+ GtkWidget *widget)
+{
+ GtkTreeView *tree_view;
+ GtkTreeViewChild *child = NULL;
+ GList *tmp_list;
+
+ g_return_if_fail (container != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW (container));
+
+ tree_view = GTK_TREE_VIEW (container);
+
+ tmp_list = tree_view->priv->children;
+ while (tmp_list)
+ {
+ child = tmp_list->data;
+ if (child->widget == widget)
+ break;
+ tmp_list = tmp_list->next;
+ }
+
+ if (tmp_list)
+ {
+ gtk_widget_unparent (widget);
+
+ tree_view->priv->children = g_list_remove_link (tree_view->priv->children, tmp_list);
+ g_list_free_1 (tmp_list);
+ g_free (child);
+ }
+}
+
+static void
+gtk_tree_view_forall (GtkContainer *container,
+ gboolean include_internals,
+ GtkCallback callback,
+ gpointer callback_data)
+{
+ GtkTreeView *tree_view;
+ GtkTreeViewChild *child = NULL;
+ GtkTreeViewColumn *column;
+ GList *tmp_list;
+
+ g_return_if_fail (container != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW (container));
+ g_return_if_fail (callback != NULL);
+
+ tree_view = GTK_TREE_VIEW (container);
+
+ tmp_list = tree_view->priv->children;
+ while (tmp_list)
+ {
+ child = tmp_list->data;
+ tmp_list = tmp_list->next;
+
+ (* callback) (child->widget, callback_data);
+ }
+ if (include_internals == FALSE)
+ return;
+
+ for (tmp_list = tree_view->priv->column; tmp_list; tmp_list = tmp_list->next)
+ {
+ column = tmp_list->data;
+ if (column->button)
+ (* callback) (column->button, callback_data);
+ }
+}
+
+/* TreeModel Methods
+ */
+
+static void
+gtk_tree_view_node_changed (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeNode *tree_node,
+ gpointer data)
+{
+ GtkTreeView *tree_view = (GtkTreeView *)data;
+ GtkRBTree *tree;
+ GtkRBNode *node;
+ gint height;
+ gboolean dirty_marked;
+
+ g_return_if_fail (path != NULL || node != NULL);
+
+ if (path == NULL)
+ path = gtk_tree_model_get_path (model, tree_node);
+ else if (tree_node == NULL)
+ tree_node = gtk_tree_model_get_node (model, path);
+
+ if (_gtk_tree_view_find_node (tree_view,
+ path,
+ &tree,
+ &node))
+ /* We aren't actually showing the node */
+ return;
+
+ dirty_marked = gtk_tree_view_discover_dirty_node (tree_view,
+ tree_node,
+ gtk_tree_path_get_depth (path),
+ &height);
+
+ if (GTK_RBNODE_GET_HEIGHT (node) != height + TREE_VIEW_VERTICAL_SEPERATOR)
+ {
+ _gtk_rbtree_node_set_height (tree, node, height + TREE_VIEW_VERTICAL_SEPERATOR);
+ gtk_widget_queue_resize (GTK_WIDGET (data));
+ return;
+ }
+ if (dirty_marked)
+ gtk_widget_queue_resize (GTK_WIDGET (data));
+ else
+ {
+ /* FIXME: just redraw the node */
+ gtk_widget_queue_resize (GTK_WIDGET (data));
+ }
+}
+
+static void
+gtk_tree_view_node_inserted (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeNode *tree_node,
+ gpointer data)
+{
+ GtkTreeView *tree_view = (GtkTreeView *) data;
+ gint *indices;
+ GtkRBTree *tmptree, *tree;
+ GtkRBNode *tmpnode = NULL;
+ gint max_height;
+ gint depth;
+ gint i = 0;
+
+ tmptree = tree = tree_view->priv->tree;
+ g_return_if_fail (path != NULL || tree_node != NULL);
+
+ if (path == NULL)
+ path = gtk_tree_model_get_path (model, tree_node);
+ else if (tree_node == NULL)
+ tree_node = gtk_tree_model_get_node (model, path);
+
+ depth = gtk_tree_path_get_depth (path);
+ indices = gtk_tree_path_get_indices (path);
+
+ /* First, find the parent tree */
+ while (i < depth - 1)
+ {
+ if (tmptree == NULL)
+ {
+ /* We aren't showing the node */
+ return;
+ }
+
+ tmpnode = _gtk_rbtree_find_count (tmptree, indices[i] + 1);
+ if (tmpnode == NULL)
+ {
+ g_warning ("A node was inserted with a parent that's not in the tree.\n" \
+ "This possibly means that a GtkTreeModel inserted a child node\n" \
+ "before the parent was inserted.");
+ return;
+ }
+ else if (!GTK_RBNODE_FLAG_SET (tmpnode, GTK_RBNODE_IS_PARENT))
+ {
+ /* In theory, the model should have emitted child_toggled here. We
+ * try to catch it anyway, just to be safe, in case the model hasn't.
+ */
+ GtkTreePath *tmppath = _gtk_tree_view_find_path (tree_view,
+ tree,
+ tmpnode);
+ gtk_tree_view_node_child_toggled (model, tmppath, NULL, data);
+ gtk_tree_path_free (tmppath);
+ return;
+ }
+
+ tmptree = tmpnode->children;
+ tree = tmptree;
+ i++;
+ }
+
+ if (tree == NULL)
+ return;
+
+ /* next, update the selection */
+ if (tree_view->priv->anchor)
+ {
+ gint *select_indices = gtk_tree_path_get_indices (tree_view->priv->anchor);
+ gint select_depth = gtk_tree_path_get_depth (tree_view->priv->anchor);
+
+ for (i = 0; i < depth && i < select_depth; i++)
+ {
+ if (indices[i] < select_indices[i])
+ {
+ select_indices[i]++;
+ break;
+ }
+ else if (indices[i] > select_indices[i])
+ break;
+ else if (i == depth - 1)
+ {
+ select_indices[i]++;
+ break;
+ }
+ }
+ }
+
+ max_height = gtk_tree_view_insert_node_height (tree_view,
+ tree,
+ tree_node,
+ depth);
+ if (indices[depth - 1] == 0)
+ {
+ tmpnode = _gtk_rbtree_find_count (tree, 1);
+ _gtk_rbtree_insert_before (tree, tmpnode, max_height);
+ }
+ else
+ {
+ tmpnode = _gtk_rbtree_find_count (tree, indices[depth - 1]);
+ _gtk_rbtree_insert_after (tree, tmpnode, max_height);
+ }
+
+ _gtk_tree_view_set_size (tree_view, -1, tree_view->priv->height + max_height);
+}
+
+static void
+gtk_tree_view_node_child_toggled (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeNode *tree_node,
+ gpointer data)
+{
+ GtkTreeView *tree_view = (GtkTreeView *)data;
+ gboolean has_child;
+ GtkRBTree *tree;
+ GtkRBNode *node;
+
+ g_return_if_fail (path != NULL || node != NULL);
+
+ if (path == NULL)
+ path = gtk_tree_model_get_path (model, tree_node);
+ else if (tree_node == NULL)
+ tree_node = gtk_tree_model_get_node (model, path);
+
+ if (_gtk_tree_view_find_node (tree_view,
+ path,
+ &tree,
+ &node))
+ /* We aren't actually showing the node */
+ return;
+
+ has_child = gtk_tree_model_node_has_child (model, tree_node);
+ /* Sanity check.
+ */
+ if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT) == has_child)
+ return;
+
+ if (has_child)
+ GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_PARENT);
+ else
+ GTK_RBNODE_UNSET_FLAG (node, GTK_RBNODE_IS_PARENT);
+
+ if (has_child && GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IS_LIST))
+ {
+ GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_IS_LIST);
+ if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_SHOW_EXPANDERS))
+ {
+ GList *list;
+ for (list = tree_view->priv->column; list; list = list->next)
+ if (GTK_TREE_VIEW_COLUMN (list->data)->visible)
+ {
+ GTK_TREE_VIEW_COLUMN (list->data)->dirty = TRUE;
+ break;
+ }
+ }
+ gtk_widget_queue_resize (GTK_WIDGET (tree_view));
+ }
+ else
+ {
+ /* FIXME: Just redraw the node */
+ gtk_widget_queue_draw (GTK_WIDGET (tree_view));
+ }
+}
+
+static void
+gtk_tree_view_node_deleted (GtkTreeModel *model,
+ GtkTreePath *path,
+ gpointer data)
+{
+ GtkTreeView *tree_view = (GtkTreeView *)data;
+ GtkRBTree *tree;
+ GtkRBNode *node;
+ GList *list;
+
+ g_return_if_fail (path != NULL);
+
+ if (_gtk_tree_view_find_node (tree_view, path, &tree, &node))
+ return;
+
+ /* next, update the selection */
+#if 0
+ if (tree_view->priv->anchor)
+ {
+ gint i;
+ gint *select_indices = gtk_tree_path_get_indices (tree_view->priv->anchor);
+ gint select_depth = gtk_tree_path_get_depth (tree_view->priv->anchor);
+
+ if (gtk_tree_path_compare (path, tree_view->priv->anchor) == 0)
+ {
+
+ }
+ else
+ {
+ for (i = 0; i < depth && i < select_depth; i++)
+ {
+ if (indices[i] < select_indices[i])
+ {
+ select_indices[i] = MAX (select_indices[i], 0);
+ break;
+ }
+ else if (indices[i] > select_indices[i])
+ break;
+ else if (i == depth - 1)
+ {
+ select_indices[i] = MAX (select_indices[i], 0);
+ break;
+ }
+ }
+ }
+ }
+#endif
+
+ for (list = tree_view->priv->column; list; list = list->next)
+ if (((GtkTreeViewColumn *)list->data)->visible &&
+ ((GtkTreeViewColumn *)list->data)->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
+ ((GtkTreeViewColumn *)list->data)->dirty = TRUE;
+
+ if (tree->root->count == 1)
+ _gtk_rbtree_remove (tree);
+ else
+ _gtk_rbtree_remove_node (tree, node);
+
+ _gtk_tree_view_set_size (GTK_TREE_VIEW (data), -1, -1);
+ gtk_widget_queue_resize (data);
+}
+
+/* Internal tree functions */
+static gint
+gtk_tree_view_insert_node_height (GtkTreeView *tree_view,
+ GtkRBTree *tree,
+ GtkTreeNode node,
+ gint depth)
+{
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *cell;
+ gboolean first = TRUE;
+ GList *list;
+ gint max_height = 0;
+
+ /* do stuff with node */
+ for (list = tree_view->priv->column; list; list = list->next)
+ {
+ gint height = 0, width = 0;
+ column = list->data;
+
+ if (!column->visible)
+ continue;
+ if (column->column_type == GTK_TREE_VIEW_COLUMN_FIXED)
+ {
+ first = FALSE;;
+ continue;
+ }
+
+ cell = column->cell;
+ gtk_tree_view_column_set_cell_data (column, tree_view->priv->model, node);
+
+ gtk_cell_renderer_get_size (cell, GTK_WIDGET (tree_view), &width, &height);
+ max_height = MAX (max_height, TREE_VIEW_VERTICAL_SEPERATOR + height);
+
+ if (first == TRUE && TREE_VIEW_DRAW_EXPANDERS (tree_view))
+ column->size = MAX (column->size, depth * tree_view->priv->tab_offset + width);
+ else
+ column->size = MAX (column->size, width);
+
+ first = FALSE;
+ }
+ return max_height;
+
+}
+
+static void
+gtk_tree_view_build_tree (GtkTreeView *tree_view,
+ GtkRBTree *tree,
+ GtkTreeNode node,
+ gint depth,
+ gboolean recurse,
+ gboolean calc_bounds)
+{
+ GtkRBNode *temp = NULL;
+ GtkTreeNode child;
+ gint max_height;
+
+ if (!node)
+ return;
+ do
+ {
+ max_height = 0;
+ if (calc_bounds)
+ max_height = gtk_tree_view_insert_node_height (tree_view,
+ tree,
+ node,
+ depth);
+ temp = _gtk_rbtree_insert_after (tree, temp, max_height);
+ if (recurse)
+ {
+ child = gtk_tree_model_node_children (tree_view->priv->model, node);
+ if (child != NULL)
+ {
+ temp->children = _gtk_rbtree_new ();
+ temp->children->parent_tree = tree;
+ temp->children->parent_node = temp;
+ gtk_tree_view_build_tree (tree_view, temp->children, child, depth + 1, recurse, calc_bounds);
+ }
+ }
+ if (gtk_tree_model_node_has_child (tree_view->priv->model, node))
+ {
+ if ((temp->flags&GTK_RBNODE_IS_PARENT) != GTK_RBNODE_IS_PARENT)
+ temp->flags ^= GTK_RBNODE_IS_PARENT;
+ GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_IS_LIST);
+ }
+ }
+ while (gtk_tree_model_node_next (tree_view->priv->model, &node));
+}
+
+static void
+gtk_tree_view_calc_size (GtkTreeView *tree_view,
+ GtkRBTree *tree,
+ GtkTreeNode node,
+ gint depth)
+{
+ GtkRBNode *temp = tree->root;
+ GtkTreeNode child;
+ GtkCellRenderer *cell;
+ GList *list;
+ GtkTreeViewColumn *column;
+ gint max_height;
+ gint i;
+
+ /* FIXME: Make this function robust against internal inconsistencies! */
+ if (!node)
+ return;
+ TREE_VIEW_INTERNAL_ASSERT_VOID (tree != NULL);
+
+ while (temp->left != tree->nil)
+ temp = temp->left;
+
+ do
+ {
+ max_height = 0;
+ /* Do stuff with node */
+ for (list = tree_view->priv->column, i = 0; i < tree_view->priv->columns; list = list->next, i++)
+ {
+ gint height = 0, width = 0;
+ column = list->data;
+
+ if (!column->visible)
+ continue;
+
+ gtk_tree_view_column_set_cell_data (column, tree_view->priv->model, node);
+ cell = column->cell;
+ gtk_cell_renderer_get_size (cell, GTK_WIDGET (tree_view), &width, &height);
+ max_height = MAX (max_height, TREE_VIEW_VERTICAL_SEPERATOR + height);
+
+ /* FIXME: I'm getting the width of all nodes here. )-: */
+ if (column->dirty == FALSE || column->column_type == GTK_TREE_VIEW_COLUMN_FIXED)
+ continue;
+
+ if (i == 0 && TREE_VIEW_DRAW_EXPANDERS (tree_view))
+ column->size = MAX (column->size, depth * tree_view->priv->tab_offset + width);
+ else
+ column->size = MAX (column->size, width);
+ }
+ _gtk_rbtree_node_set_height (tree, temp, max_height);
+ child = gtk_tree_model_node_children (tree_view->priv->model, node);
+ if (child != NULL && temp->children != NULL)
+ gtk_tree_view_calc_size (tree_view, temp->children, child, depth + 1);
+ temp = _gtk_rbtree_next (tree, temp);
+ }
+ while (gtk_tree_model_node_next (tree_view->priv->model, &node));
+}
+
+static gboolean
+gtk_tree_view_discover_dirty_node (GtkTreeView *tree_view,
+ GtkTreeNode node,
+ gint depth,
+ gint *height)
+{
+ GtkCellRenderer *cell;
+ GtkTreeViewColumn *column;
+ GList *list;
+ gint i;
+ gint retval = FALSE;
+ gint tmpheight;
+
+ /* Do stuff with node */
+ if (height)
+ *height = 0;
+
+ for (i = 0, list = tree_view->priv->column; list; list = list->next, i++)
+ {
+ gint width;
+ column = list->data;
+ if (column->dirty == TRUE || column->column_type == GTK_TREE_VIEW_COLUMN_FIXED)
+ continue;
+ if (!column->visible)
+ continue;
+
+ cell = column->cell;
+ gtk_tree_view_column_set_cell_data (column, tree_view->priv->model, node);
+
+ if (height)
+ {
+ gtk_cell_renderer_get_size (cell, GTK_WIDGET (tree_view), &width, &tmpheight);
+ *height = MAX (*height, tmpheight);
+ }
+ else
+ {
+ gtk_cell_renderer_get_size (cell, GTK_WIDGET (tree_view), &width, NULL);
+ }
+ if (i == 0 && TREE_VIEW_DRAW_EXPANDERS (tree_view))
+ {
+ if (depth * tree_view->priv->tab_offset + width > column->size)
+ {
+ column->dirty = TRUE;
+ retval = TRUE;
+ }
+ }
+ else
+ {
+ if (width > column->size)
+ {
+ column->dirty = TRUE;
+ retval = TRUE;
+ }
+ }
+ }
+
+ return retval;
+}
+
+static void
+gtk_tree_view_discover_dirty (GtkTreeView *tree_view,
+ GtkRBTree *tree,
+ GtkTreeNode node,
+ gint depth)
+{
+ GtkRBNode *temp = tree->root;
+ GtkTreeViewColumn *column;
+ GList *list;
+ GtkTreeNode child;
+ gboolean is_all_dirty;
+
+ /* FIXME: Make this function robust against internal inconsistencies! */
+ if (!node)
+ return;
+ TREE_VIEW_INTERNAL_ASSERT_VOID (tree != NULL);
+
+ while (temp->left != tree->nil)
+ temp = temp->left;
+
+ do
+ {
+ is_all_dirty = TRUE;
+ for (list = tree_view->priv->column; list; list = list->next)
+ {
+ column = list->data;
+ if (column->dirty == FALSE)
+ {
+ is_all_dirty = FALSE;
+ break;
+ }
+ }
+ if (is_all_dirty)
+ return;
+
+ gtk_tree_view_discover_dirty_node (tree_view,
+ node,
+ depth,
+ FALSE);
+ child = gtk_tree_model_node_children (tree_view->priv->model, node);
+ if (child != NULL && temp->children != NULL)
+ gtk_tree_view_discover_dirty (tree_view, temp->children, child, depth + 1);
+ temp = _gtk_rbtree_next (tree, temp);
+ }
+ while (gtk_tree_model_node_next (tree_view->priv->model, &node));
+}
+
+
+static void
+gtk_tree_view_check_dirty (GtkTreeView *tree_view)
+{
+ GtkTreePath *path;
+ GtkTreeNode *tree_node;
+ gboolean dirty = FALSE;
+ GList *list;
+ GtkTreeViewColumn *column;
+
+ for (list = tree_view->priv->column; list; list = list->next)
+ {
+ column = list->data;
+ if (column->dirty)
+ {
+ dirty = TRUE;
+ if (column->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
+ {
+ column->size = column->button->requisition.width;
+ }
+ }
+ }
+ if (dirty == FALSE)
+ return;
+
+ path = gtk_tree_path_new_root ();
+ if (path != NULL)
+ {
+ tree_node = gtk_tree_model_get_node (tree_view->priv->model, path);
+ gtk_tree_path_free (path);
+ gtk_tree_view_calc_size (tree_view, tree_view->priv->tree, tree_node, 1);
+ _gtk_tree_view_set_size (tree_view, -1, -1);
+ }
+
+ for (list = tree_view->priv->column; list; list = list->next)
+ {
+ column = list->data;
+ column->dirty = FALSE;
+ }
+}
+
+static void
+gtk_tree_view_create_button (GtkTreeView *tree_view,
+ gint i)
+{
+ GtkWidget *button;
+ GtkTreeViewColumn *column;
+
+ column = g_list_nth (tree_view->priv->column, i)->data;
+ gtk_widget_push_composite_child ();
+ button = column->button = gtk_button_new ();
+ gtk_widget_pop_composite_child ();
+
+ gtk_widget_set_parent (button, GTK_WIDGET (tree_view));
+
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ (GtkSignalFunc) gtk_tree_view_button_clicked,
+ (gpointer) tree_view);
+}
+
+static void
+gtk_tree_view_create_buttons (GtkTreeView *tree_view)
+{
+ GtkWidget *alignment;
+ GtkWidget *label;
+ GtkRequisition requisition;
+ GList *list;
+ GtkTreeViewColumn *column;
+ gint i;
+
+ for (list = tree_view->priv->column, i = 0; list; list = list->next, i++)
+ {
+ column = list->data;
+
+ gtk_tree_view_create_button (tree_view, i);
+ switch (column->justification)
+ {
+ case GTK_JUSTIFY_LEFT:
+ alignment = gtk_alignment_new (0.0, 0.5, 0.0, 0.0);
+ break;
+ case GTK_JUSTIFY_RIGHT:
+ alignment = gtk_alignment_new (1.0, 0.5, 0.0, 0.0);
+ break;
+ case GTK_JUSTIFY_CENTER:
+ alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
+ break;
+ case GTK_JUSTIFY_FILL:
+ default:
+ alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
+ break;
+ }
+ label = gtk_label_new (column->title);
+
+ gtk_container_add (GTK_CONTAINER (alignment), label);
+ gtk_container_add (GTK_CONTAINER (column->button), alignment);
+
+ gtk_widget_show (label);
+ gtk_widget_show (alignment);
+ gtk_widget_size_request (column->button, &requisition);
+
+ column->size = MAX (column->size, requisition.width);
+ tree_view->priv->header_height = MAX (tree_view->priv->header_height, requisition.height);
+ }
+ if (GTK_WIDGET_REALIZED (tree_view))
+ {
+ gtk_tree_view_realize_buttons (tree_view);
+ if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE))
+ {
+ /* We need to do this twice, as we need to map
+ * all the buttons before we map the columns */
+ for (list = tree_view->priv->column; list; list = list->next)
+ {
+ column = list->data;
+ if (column->visible == FALSE)
+ continue;
+ gtk_widget_map (column->button);
+ }
+ for (list = tree_view->priv->column; list; list = list->next)
+ {
+ column = list->data;
+ if (column->visible == FALSE)
+ continue;
+ if (column->column_type == GTK_TREE_VIEW_COLUMN_RESIZEABLE)
+ {
+ gdk_window_raise (column->window);
+ gdk_window_show (column->window);
+ }
+ else
+ gdk_window_hide (column->window);
+ }
+ }
+ }
+}
+
+static void
+gtk_tree_view_button_clicked (GtkWidget *widget,
+ gpointer data)
+{
+ GList *list;
+ GtkTreeView *tree_view;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW (data));
+
+ tree_view = GTK_TREE_VIEW (data);
+
+ /* find the column who's button was pressed */
+ for (list = tree_view->priv->column; list; list = list->next)
+ if (GTK_TREE_VIEW_COLUMN (list->data)->button == widget)
+ break;
+
+ // gtk_signal_emit (GTK_OBJECT (clist), clist_signals[CLICK_COLUMN], i);
+}
+
+/* Make sure the node is visible vertically */
+static void
+gtk_tree_view_clamp_node_visible (GtkTreeView *tree_view,
+ GtkRBTree *tree,
+ GtkRBNode *node)
+{
+ gint offset;
+
+ offset = _gtk_rbtree_node_find_offset (tree, node);
+
+ /* we reverse the order, b/c in the unusual case of the
+ * node's height being taller then the visible area, we'd rather
+ * have the node flush to the top
+ */
+ if (offset + GTK_RBNODE_GET_HEIGHT (node) >
+ tree_view->priv->vadjustment->value + tree_view->priv->vadjustment->page_size)
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment),
+ offset + GTK_RBNODE_GET_HEIGHT (node) -
+ tree_view->priv->vadjustment->page_size);
+ if (offset < tree_view->priv->vadjustment->value)
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment),
+ offset);
+}
+
+/* This function could be more efficient.
+ * I'll optimize it if profiling seems to imply that
+ * it's important */
+GtkTreePath *
+_gtk_tree_view_find_path (GtkTreeView *tree_view,
+ GtkRBTree *tree,
+ GtkRBNode *node)
+{
+ GtkTreePath *path;
+ GtkRBTree *tmp_tree;
+ GtkRBNode *tmp_node, *last;
+ gint count;
+
+ path = gtk_tree_path_new ();
+
+ g_return_val_if_fail (node != NULL, path);
+ g_return_val_if_fail (node != tree->nil, path);
+
+ count = 1 + node->left->count;
+
+ last = node;
+ tmp_node = node->parent;
+ tmp_tree = tree;
+ while (tmp_tree)
+ {
+ while (tmp_node != tmp_tree->nil)
+ {
+ if (tmp_node->right == last)
+ count += 1 + tmp_node->left->count;
+ last = tmp_node;
+ tmp_node = tmp_node->parent;
+ }
+ gtk_tree_path_prepend_index (path, count - 1);
+ last = tmp_tree->parent_node;
+ tmp_tree = tmp_tree->parent_tree;
+ if (last)
+ {
+ count = 1 + last->left->count;
+ tmp_node = last->parent;
+ }
+ }
+ return path;
+}
+
+/* Returns wether or not it's a parent, or not */
+gboolean
+_gtk_tree_view_find_node (GtkTreeView *tree_view,
+ GtkTreePath *path,
+ GtkRBTree **tree,
+ GtkRBNode **node)
+{
+ GtkRBNode *tmpnode = NULL;
+ GtkRBTree *tmptree = tree_view->priv->tree;
+ gint *indices = gtk_tree_path_get_indices (path);
+ gint depth = gtk_tree_path_get_depth (path);
+ gint i = 0;
+
+ *node = NULL;
+ *tree = NULL;
+
+ do
+ {
+ if (tmptree == NULL)
+ {
+ *node = tmpnode;
+ *tree = tmptree;
+ return TRUE;
+ }
+ tmpnode = _gtk_rbtree_find_count (tmptree, indices[i] + 1);
+ if (++i >= depth)
+ {
+ *node = tmpnode;
+ *tree = tmptree;
+ return FALSE;
+ }
+ tmptree = tmpnode->children;
+ }
+ while (1);
+}
+
+/* x and y are the mouse position
+ */
+static void
+gtk_tree_view_draw_arrow (GtkTreeView *tree_view,
+ GtkRBNode *node,
+ gint offset,
+ gint x,
+ gint y)
+{
+ GdkRectangle area;
+ GtkStateType state;
+ GtkShadowType shadow;
+ GdkPoint points[3];
+
+ area.x = 0;
+ area.y = offset + TREE_VIEW_VERTICAL_SEPERATOR;
+ area.width = tree_view->priv->tab_offset - 2;
+ area.height = GTK_RBNODE_GET_HEIGHT (node) - TREE_VIEW_VERTICAL_SEPERATOR;
+
+ if (node == tree_view->priv->button_pressed_node)
+ {
+ if (x >= area.x && x <= (area.x + area.width) &&
+ y >= area.y && y <= (area.y + area.height))
+ {
+ state = GTK_STATE_ACTIVE;
+ shadow = GTK_SHADOW_IN;
+ }
+ else
+ {
+ state = GTK_STATE_NORMAL;
+ shadow = GTK_SHADOW_OUT;
+ }
+ }
+ else
+ {
+ state = (node==tree_view->priv->prelight_node&&GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT)?GTK_STATE_PRELIGHT:GTK_STATE_NORMAL);
+ shadow = GTK_SHADOW_OUT;
+ }
+
+ if (TRUE ||
+ (((node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT) &&
+ node->children))
+ {
+ points[0].x = area.x + 2;
+ points[0].y = area.y + (area.height - TREE_VIEW_EXPANDER_HEIGHT)/2;
+ points[1].x = points[0].x + TREE_VIEW_EXPANDER_WIDTH/2;
+ points[1].y = points[0].y + TREE_VIEW_EXPANDER_HEIGHT/2;
+ points[2].x = points[0].x;
+ points[2].y = points[0].y + TREE_VIEW_EXPANDER_HEIGHT;
+
+ }
+
+ gdk_draw_polygon (tree_view->priv->bin_window,
+ GTK_WIDGET (tree_view)->style->base_gc[state],
+ TRUE, points, 3);
+ gdk_draw_polygon (tree_view->priv->bin_window,
+ GTK_WIDGET (tree_view)->style->fg_gc[state],
+ FALSE, points, 3);
+
+
+ /* gtk_paint_arrow (GTK_WIDGET (tree_view)->style, */
+ /* tree_view->priv->bin_window, */
+ /* state, */
+ /* shadow, */
+ /* &area, */
+ /* GTK_WIDGET (tree_view), */
+ /* "GtkTreeView", */
+ /* arrow_dir, */
+ /* TRUE, */
+ /* area.x, area.y, */
+ /* area.width, area.height); */
+}
+
+void
+_gtk_tree_view_set_size (GtkTreeView *tree_view,
+ gint width,
+ gint height)
+{
+ GList *list;
+ GtkTreeViewColumn *column;
+ gint i;
+
+ if (tree_view->priv->model == NULL)
+ {
+ tree_view->priv->width = 1;
+ tree_view->priv->height = 1;
+ return;
+ }
+ if (width == -1)
+ {
+ width = 0;
+ for (list = tree_view->priv->column, i = 0; list; list = list->next, i++)
+ {
+ column = list->data;
+ if (!column->visible)
+ continue;
+ width += TREE_VIEW_COLUMN_SIZE (column);
+ }
+ }
+ if (height == -1)
+ height = tree_view->priv->tree->root->offset + TREE_VIEW_VERTICAL_SEPERATOR;
+
+ tree_view->priv->width = width;
+ tree_view->priv->height = height;
+
+ if (tree_view->priv->hadjustment->upper != tree_view->priv->width)
+ {
+ tree_view->priv->hadjustment->upper = tree_view->priv->width;
+ gtk_signal_emit_by_name (GTK_OBJECT (tree_view->priv->hadjustment), "changed");
+ }
+
+ if (tree_view->priv->vadjustment->upper != tree_view->priv->height)
+ {
+ tree_view->priv->vadjustment->upper = tree_view->priv->height;
+ gtk_signal_emit_by_name (GTK_OBJECT (tree_view->priv->vadjustment), "changed");
+ }
+
+ if (GTK_WIDGET_REALIZED (tree_view))
+ {
+ gdk_window_resize (tree_view->priv->bin_window, MAX (width, GTK_WIDGET (tree_view)->allocation.width), height + TREE_VIEW_HEADER_HEIGHT (tree_view));
+ gdk_window_resize (tree_view->priv->header_window, MAX (width, GTK_WIDGET (tree_view)->allocation.width), tree_view->priv->header_height);
+ }
+ gtk_widget_queue_resize (GTK_WIDGET (tree_view));
+}
+
+/* this function returns the new width of the column being resized given
+ * the column and x position of the cursor; the x cursor position is passed
+ * in as a pointer and automagicly corrected if it's beyond min/max limits */
+static gint
+gtk_tree_view_new_column_width (GtkTreeView *tree_view,
+ gint i,
+ gint *x)
+{
+ GtkTreeViewColumn *column;
+ gint width;
+
+ /* first translate the x position from widget->window
+ * to clist->clist_window */
+
+ column = g_list_nth (tree_view->priv->column, i)->data;
+ width = *x - column->button->allocation.x;
+
+ /* Clamp down the value */
+ if (column->min_width == -1)
+ width = MAX (column->button->requisition.width,
+ width);
+ else
+ width = MAX (column->min_width,
+ width);
+ if (column->max_width != -1)
+ width = MIN (width, column->max_width != -1);
+ *x = column->button->allocation.x + width;
+
+ return width;
+}
+
+/* Callbacks */
+static void
+gtk_tree_view_adjustment_changed (GtkAdjustment *adjustment,
+ GtkTreeView *tree_view)
+{
+ if (GTK_WIDGET_REALIZED (tree_view))
+ {
+ gdk_window_move (tree_view->priv->bin_window,
+ - tree_view->priv->hadjustment->value,
+ - tree_view->priv->vadjustment->value);
+ gdk_window_move (tree_view->priv->header_window,
+ - tree_view->priv->hadjustment->value,
+ 0);
+
+ gdk_window_process_updates (tree_view->priv->bin_window, TRUE);
+ gdk_window_process_updates (tree_view->priv->header_window, TRUE);
+ }
+}
+
+
+
+/* Public methods
+ */
+GtkWidget *
+gtk_tree_view_new (void)
+{
+ GtkTreeView *tree_view;
+
+ tree_view = GTK_TREE_VIEW (gtk_type_new (gtk_tree_view_get_type ()));
+
+ return GTK_WIDGET (tree_view);
+}
+
+GtkWidget *
+gtk_tree_view_new_with_model (GtkTreeModel *model)
+{
+ GtkTreeView *tree_view;
+
+ tree_view = GTK_TREE_VIEW (gtk_type_new (gtk_tree_view_get_type ()));
+ gtk_tree_view_set_model (tree_view, model);
+
+ return GTK_WIDGET (tree_view);
+}
+
+GtkTreeModel *
+gtk_tree_view_get_model (GtkTreeView *tree_view)
+{
+ g_return_val_if_fail (tree_view != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
+
+ return tree_view->priv->model;
+}
+
+static void
+gtk_tree_view_set_model_realized (GtkTreeView *tree_view)
+{
+ GtkTreePath *path;
+ GtkTreeNode node;
+
+ tree_view->priv->tree = _gtk_rbtree_new ();
+
+ gtk_signal_connect (GTK_OBJECT (tree_view->priv->model),
+ "node_changed",
+ gtk_tree_view_node_changed,
+ tree_view);
+ gtk_signal_connect (GTK_OBJECT (tree_view->priv->model),
+ "node_inserted",
+ gtk_tree_view_node_inserted,
+ tree_view);
+ gtk_signal_connect (GTK_OBJECT (tree_view->priv->model),
+ "node_child_toggled",
+ gtk_tree_view_node_child_toggled,
+ tree_view);
+ gtk_signal_connect (GTK_OBJECT (tree_view->priv->model),
+ "node_deleted",
+ gtk_tree_view_node_deleted,
+ tree_view);
+
+ if (tree_view->priv->column == NULL)
+ return;
+
+ path = gtk_tree_path_new_root ();
+ if (path == NULL)
+ return;
+
+ node = gtk_tree_model_get_node (tree_view->priv->model, path);
+ gtk_tree_path_free (path);
+ gtk_tree_view_build_tree (tree_view, tree_view->priv->tree, node, 1, FALSE, GTK_WIDGET_REALIZED (tree_view));
+
+ gtk_tree_view_create_buttons (tree_view);
+ GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_MODEL_SETUP);
+}
+
+void
+gtk_tree_view_set_model (GtkTreeView *tree_view, GtkTreeModel *model)
+{
+ GList *list;
+ GtkTreeViewColumn *column;
+
+ g_return_if_fail (tree_view != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+
+ if (tree_view->priv->model != NULL)
+ {
+ for (list = tree_view->priv->column; list; list = list->next)
+ {
+ column = list->data;
+ if (column->button)
+ {
+ gtk_widget_unparent (column->button);
+ gdk_window_set_user_data (column->window, NULL);
+ gdk_window_destroy (column->window);
+ }
+ }
+ if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_MODEL_SETUP))
+ {
+ gtk_signal_disconnect_by_func (GTK_OBJECT (tree_view->priv->model),
+ gtk_tree_view_node_changed,
+ tree_view);
+ gtk_signal_disconnect_by_func (GTK_OBJECT (tree_view->priv->model),
+ gtk_tree_view_node_inserted,
+ tree_view);
+ gtk_signal_disconnect_by_func (GTK_OBJECT (tree_view->priv->model),
+ gtk_tree_view_node_child_toggled,
+ tree_view);
+ gtk_signal_disconnect_by_func (GTK_OBJECT (tree_view->priv->model),
+ gtk_tree_view_node_deleted,
+ tree_view);
+ _gtk_rbtree_free (tree_view->priv->tree);
+ }
+
+ g_list_free (tree_view->priv->column);
+ tree_view->priv->column = NULL;
+ GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_MODEL_SETUP);
+ }
+
+ tree_view->priv->model = model;
+ if (model == NULL)
+ {
+ tree_view->priv->tree = NULL;
+ tree_view->priv->columns = 0;
+ tree_view->priv->column = NULL;
+ if (GTK_WIDGET_REALIZED (tree_view))
+ _gtk_tree_view_set_size (tree_view, 0, 0);
+ return;
+ }
+
+ if (GTK_WIDGET_REALIZED (tree_view))
+ {
+ gtk_tree_view_set_model_realized (tree_view);
+ _gtk_tree_view_set_size (tree_view, -1, -1);
+ }
+}
+
+GtkTreeSelection *
+gtk_tree_view_get_selection (GtkTreeView *tree_view)
+{
+ g_return_val_if_fail (tree_view != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
+
+ if (tree_view->priv->selection == NULL)
+ gtk_tree_selection_new_with_tree_view (tree_view);
+
+ return tree_view->priv->selection;
+}
+
+void
+gtk_tree_view_set_selection (GtkTreeView *tree_view,
+ GtkTreeSelection *selection)
+{
+ g_return_if_fail (tree_view != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+ g_return_if_fail (selection != NULL);
+ g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
+
+ g_object_ref (G_OBJECT (selection));
+
+ if (tree_view->priv->selection != NULL)
+ g_object_unref (G_OBJECT (tree_view->priv->selection));
+
+ tree_view->priv->selection = selection;
+}
+
+GtkAdjustment *
+gtk_tree_view_get_hadjustment (GtkTreeView *tree_view)
+{
+ g_return_val_if_fail (tree_view != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
+
+ return tree_view->priv->hadjustment;
+}
+
+void
+gtk_tree_view_set_hadjustment (GtkTreeView *tree_view,
+ GtkAdjustment *adjustment)
+{
+ g_return_if_fail (tree_view != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+
+ gtk_tree_view_set_adjustments (tree_view,
+ adjustment,
+ tree_view->priv->vadjustment);
+}
+
+GtkAdjustment *
+gtk_tree_view_get_vadjustment (GtkTreeView *tree_view)
+{
+ g_return_val_if_fail (tree_view != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
+
+ return tree_view->priv->vadjustment;
+}
+
+void
+gtk_tree_view_set_vadjustment (GtkTreeView *tree_view,
+ GtkAdjustment *adjustment)
+{
+ g_return_if_fail (tree_view != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+
+ gtk_tree_view_set_adjustments (tree_view,
+ tree_view->priv->hadjustment,
+ adjustment);
+}
+
+static void
+gtk_tree_view_set_adjustments (GtkTreeView *tree_view,
+ GtkAdjustment *hadj,
+ GtkAdjustment *vadj)
+{
+ gboolean need_adjust = FALSE;
+
+ g_return_if_fail (tree_view != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+
+ if (hadj)
+ g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
+ else
+ hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
+ if (vadj)
+ g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
+ else
+ vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
+
+ if (tree_view->priv->hadjustment && (tree_view->priv->hadjustment != hadj))
+ {
+ gtk_signal_disconnect_by_data (GTK_OBJECT (tree_view->priv->hadjustment), tree_view);
+ gtk_object_unref (GTK_OBJECT (tree_view->priv->hadjustment));
+ }
+
+ if (tree_view->priv->vadjustment && (tree_view->priv->vadjustment != vadj))
+ {
+ gtk_signal_disconnect_by_data (GTK_OBJECT (tree_view->priv->vadjustment), tree_view);
+ gtk_object_unref (GTK_OBJECT (tree_view->priv->vadjustment));
+ }
+
+ if (tree_view->priv->hadjustment != hadj)
+ {
+ tree_view->priv->hadjustment = hadj;
+ gtk_object_ref (GTK_OBJECT (tree_view->priv->hadjustment));
+ gtk_object_sink (GTK_OBJECT (tree_view->priv->hadjustment));
+
+ gtk_signal_connect (GTK_OBJECT (tree_view->priv->hadjustment), "value_changed",
+ (GtkSignalFunc) gtk_tree_view_adjustment_changed,
+ tree_view);
+ need_adjust = TRUE;
+ }
+
+ if (tree_view->priv->vadjustment != vadj)
+ {
+ tree_view->priv->vadjustment = vadj;
+ gtk_object_ref (GTK_OBJECT (tree_view->priv->vadjustment));
+ gtk_object_sink (GTK_OBJECT (tree_view->priv->vadjustment));
+
+ gtk_signal_connect (GTK_OBJECT (tree_view->priv->vadjustment), "value_changed",
+ (GtkSignalFunc) gtk_tree_view_adjustment_changed,
+ tree_view);
+ need_adjust = TRUE;
+ }
+
+ if (need_adjust)
+ gtk_tree_view_adjustment_changed (NULL, tree_view);
+}
+
+
+/* Column and header operations */
+
+gboolean
+gtk_tree_view_get_headers_visible (GtkTreeView *tree_view)
+{
+ g_return_val_if_fail (tree_view != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
+
+ return GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE);
+}
+
+void
+gtk_tree_view_set_headers_visible (GtkTreeView *tree_view,
+ gboolean headers_visible)
+{
+ gint x, y;
+ GList *list;
+ GtkTreeViewColumn *column;
+
+ g_return_if_fail (tree_view != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+
+ if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE) == headers_visible)
+ return;
+
+ if (headers_visible)
+ GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE);
+ else
+ GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE);
+
+ if (GTK_WIDGET_REALIZED (tree_view))
+ {
+ gdk_window_get_position (tree_view->priv->bin_window, &x, &y);
+ if (headers_visible)
+ {
+ gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height + TREE_VIEW_HEADER_HEIGHT (tree_view));
+ for (list = tree_view->priv->column; list; list = list->next)
+ {
+ column = list->data;
+ gtk_widget_map (column->button);
+ }
+
+ for (list = tree_view->priv->column; list; list = list->next)
+ {
+ column = list->data;
+ if (column->visible == FALSE)
+ continue;
+ if (column->column_type == GTK_TREE_VIEW_COLUMN_RESIZEABLE)
+ {
+ gdk_window_raise (column->window);
+ gdk_window_show (column->window);
+ }
+ else
+ gdk_window_hide (column->window);
+ }
+ gdk_window_show (tree_view->priv->header_window);
+ }
+ else
+ {
+ gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height);
+ for (list = tree_view->priv->column; list; list = list->next)
+ {
+ column = list->data;
+ gtk_widget_unmap (column->button);
+ }
+ gdk_window_hide (tree_view->priv->header_window);
+ }
+ }
+
+ tree_view->priv->vadjustment->page_size = GTK_WIDGET (tree_view)->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
+ tree_view->priv->vadjustment->page_increment = (GTK_WIDGET (tree_view)->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view)) / 2;
+ tree_view->priv->vadjustment->lower = 0;
+ tree_view->priv->vadjustment->upper = tree_view->priv->height;
+ gtk_signal_emit_by_name (GTK_OBJECT (tree_view->priv->vadjustment), "changed");
+
+ gtk_widget_queue_resize (GTK_WIDGET (tree_view));
+}
+
+
+void
+gtk_tree_view_columns_autosize (GtkTreeView *tree_view)
+{
+ gboolean dirty = FALSE;
+ GList *list;
+ GtkTreeViewColumn *column;
+
+ g_return_if_fail (tree_view != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+
+ for (list = tree_view->priv->column; list; list = list->next)
+ {
+ column = list->data;
+ if (column->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
+ continue;
+ column->dirty = TRUE;
+ dirty = TRUE;
+ }
+
+ if (dirty)
+ gtk_widget_queue_resize (GTK_WIDGET (tree_view));
+}
+
+void
+gtk_tree_view_set_headers_active (GtkTreeView *tree_view,
+ gboolean active)
+{
+ GList *list;
+
+ g_return_if_fail (tree_view != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+ g_return_if_fail (tree_view->priv->model != NULL);
+
+ for (list = tree_view->priv->column; list; list = list->next)
+ gtk_tree_view_column_set_header_active (GTK_TREE_VIEW_COLUMN (list->data), active);
+}
+
+gint
+gtk_tree_view_add_column (GtkTreeView *tree_view,
+ GtkTreeViewColumn *column)
+{
+ g_return_val_if_fail (tree_view != NULL, -1);
+ g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
+ g_return_val_if_fail (column != NULL, -1);
+ g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1);
+ g_return_val_if_fail (column->tree_view == NULL, -1);
+
+ tree_view->priv->column = g_list_append (tree_view->priv->column,
+ column);
+ column->tree_view = GTK_WIDGET (tree_view);
+ return tree_view->priv->columns++;
+}
+
+GtkTreeViewColumn *
+gtk_tree_view_get_column (GtkTreeView *tree_view,
+ gint n)
+{
+ g_return_val_if_fail (tree_view != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
+ g_return_val_if_fail (tree_view->priv->model != NULL, NULL);
+ g_return_val_if_fail (n >= 0 || n < tree_view->priv->columns, NULL);
+
+ if (tree_view->priv->column == NULL)
+ return NULL;
+
+ return GTK_TREE_VIEW_COLUMN (g_list_nth (tree_view->priv->column, n)->data);
+}
+
+void
+gtk_tree_view_move_to (GtkTreeView *tree_view,
+ GtkTreePath *path,
+ gint column,
+ gfloat row_align,
+ gfloat col_align)
+{
+ GtkRBNode *node = NULL;
+ GtkRBTree *tree = NULL;
+
+ g_return_if_fail (tree_view != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+
+ if (column < -1 || column > tree_view->priv->columns)
+ return;
+
+ row_align = CLAMP (row_align, 0, 1);
+ col_align = CLAMP (col_align, 0, 1);
+
+ if (path != NULL)
+ {
+ _gtk_tree_view_find_node (tree_view, path,
+ &tree, &node);
+ if (node == NULL)
+ return;
+ }
+
+ if (tree_view->priv->hadjustment && column >= 0)
+ {
+ GtkTreeViewColumn *col;
+
+ col = g_list_nth (tree_view->priv->column, column)->data;
+ /* FIXME -- write */
+ }
+}
+
+static void
+gtk_tree_view_expand_all_helper (GtkRBTree *tree,
+ GtkRBNode *node,
+ gpointer data)
+{
+ GtkTreeView *tree_view = data;
+
+ if (node->children)
+ _gtk_rbtree_traverse (node->children,
+ node->children->root,
+ G_PRE_ORDER,
+ gtk_tree_view_expand_all_helper,
+ data);
+ else if ((node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT && node->children == NULL)
+ {
+ GtkTreePath *path;
+ GtkTreeNode tree_node;
+
+ node->children = _gtk_rbtree_new ();
+ node->children->parent_tree = tree;
+ node->children->parent_node = node;
+ path = _gtk_tree_view_find_path (tree_view, tree, node);
+ tree_node = gtk_tree_model_get_node (tree_view->priv->model, path);
+ tree_node = gtk_tree_model_node_children (tree_view->priv->model, tree_node);
+ gtk_tree_view_build_tree (tree_view,
+ node->children,
+ tree_node,
+ gtk_tree_path_get_depth (path) + 1,
+ TRUE,
+ GTK_WIDGET_REALIZED (tree_view));
+ gtk_tree_path_free (path);
+ }
+}
+
+void
+gtk_tree_view_expand_all (GtkTreeView *tree_view)
+{
+ g_return_if_fail (tree_view != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+ g_return_if_fail (tree_view->priv->tree != NULL);
+
+ _gtk_rbtree_traverse (tree_view->priv->tree,
+ tree_view->priv->tree->root,
+ G_PRE_ORDER,
+ gtk_tree_view_expand_all_helper,
+ tree_view);
+
+ _gtk_tree_view_set_size (tree_view, -1,-1);
+}
+
+static void
+gtk_tree_view_collapse_all_helper (GtkRBTree *tree,
+ GtkRBNode *node,
+ gpointer data)
+{
+ if (node->children)
+ {
+ GtkTreePath *path;
+ GtkTreeNode *tree_node;
+
+ path = _gtk_tree_view_find_path (GTK_TREE_VIEW (data),
+ node->children,
+ node->children->root);
+ tree_node = gtk_tree_model_get_node (GTK_TREE_VIEW (data)->priv->model, path);
+ gtk_tree_view_discover_dirty (GTK_TREE_VIEW (data),
+ node->children,
+ tree_node,
+ gtk_tree_path_get_depth (path));
+ _gtk_rbtree_remove (node->children);
+ gtk_tree_path_free (path);
+ }
+}
+
+void
+gtk_tree_view_collapse_all (GtkTreeView *tree_view)
+{
+ g_return_if_fail (tree_view != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+ g_return_if_fail (tree_view->priv->tree != NULL);
+
+ _gtk_rbtree_traverse (tree_view->priv->tree,
+ tree_view->priv->tree->root,
+ G_PRE_ORDER,
+ gtk_tree_view_collapse_all_helper,
+ tree_view);
+
+ if (GTK_WIDGET_REALIZED (tree_view))
+ gtk_widget_queue_draw (GTK_WIDGET (tree_view));
+}
diff --git a/gtk/gtktreeview.h b/gtk/gtktreeview.h
new file mode 100644
index 0000000000..bc90c5f01d
--- /dev/null
+++ b/gtk/gtktreeview.h
@@ -0,0 +1,105 @@
+/* gtktreeview.h
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __GTK_TREE_VIEW_H__
+#define __GTK_TREE_VIEW_H__
+
+#include <gtk/gtkcontainer.h>
+#include <gtk/gtktreemodel.h>
+#include <gtk/gtktreeviewcolumn.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define GTK_TYPE_TREE_VIEW (gtk_tree_view_get_type ())
+#define GTK_TREE_VIEW(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_TREE_VIEW, GtkTreeView))
+#define GTK_TREE_VIEW_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_TREE_VIEW, GtkTreeViewClass))
+#define GTK_IS_TREE_VIEW(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_TREE_VIEW))
+#define GTK_IS_TREE_VIEW_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), GTK_TYPE_TREE_VIEW))
+
+typedef struct _GtkTreeView GtkTreeView;
+typedef struct _GtkTreeViewClass GtkTreeViewClass;
+typedef struct _GtkTreeViewPrivate GtkTreeViewPrivate;
+
+typedef struct _GtkTreeSelection GtkTreeSelection;
+typedef struct _GtkTreeSelectionClass GtkTreeSelectionClass;
+
+
+struct _GtkTreeView
+{
+ GtkContainer parent;
+
+ GtkTreeViewPrivate *priv;
+};
+
+struct _GtkTreeViewClass
+{
+ GtkContainerClass parent_class;
+
+ void (*set_scroll_adjustments) (GtkTreeView *tree_view,
+ GtkAdjustment *hadjustment,
+ GtkAdjustment *vadjustment);
+ gint (*expand_row) (GtkTreeView *tree_view,
+ GtkTreeNode *node);
+};
+
+/* Creators */
+GtkType gtk_tree_view_get_type (void);
+GtkWidget *gtk_tree_view_new (void);
+GtkWidget *gtk_tree_view_new_with_model (GtkTreeModel *model);
+GtkTreeModel *gtk_tree_view_get_model (GtkTreeView *tree_view);
+void gtk_tree_view_set_model (GtkTreeView *tree_view,
+ GtkTreeModel *tree_model);
+GtkTreeSelection *gtk_tree_view_get_selection (GtkTreeView *tree_view);
+void gtk_tree_view_set_selection (GtkTreeView *tree_view,
+ GtkTreeSelection *selection);
+GtkAdjustment *gtk_tree_view_get_hadjustment (GtkTreeView *layout);
+void gtk_tree_view_set_hadjustment (GtkTreeView *layout,
+ GtkAdjustment *adjustment);
+GtkAdjustment *gtk_tree_view_get_vadjustment (GtkTreeView *layout);
+void gtk_tree_view_set_vadjustment (GtkTreeView *layout,
+ GtkAdjustment *adjustment);
+gboolean gtk_tree_view_get_headers_visible (GtkTreeView *tree_view);
+void gtk_tree_view_set_headers_visible (GtkTreeView *tree_view,
+ gboolean headers_visible);
+void gtk_tree_view_columns_autosize (GtkTreeView *tree_view);
+void gtk_tree_view_set_headers_active (GtkTreeView *tree_view,
+ gboolean active);
+gint gtk_tree_view_add_column (GtkTreeView *tree_view,
+ GtkTreeViewColumn *column);
+GtkTreeViewColumn *gtk_tree_view_get_column (GtkTreeView *tree_view,
+ gint n);
+
+/* Actions */
+void gtk_tree_view_move_to (GtkTreeView *tree_view,
+ GtkTreePath *path,
+ gint column,
+ gfloat row_align,
+ gfloat col_align);
+void gtk_tree_view_expand_all (GtkTreeView *tree_view);
+void gtk_tree_view_collapse_all (GtkTreeView *tree_view);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_TREE_VIEW_H__ */
+
diff --git a/gtk/gtktreeviewcolumn.c b/gtk/gtktreeviewcolumn.c
new file mode 100644
index 0000000000..248e6552cf
--- /dev/null
+++ b/gtk/gtktreeviewcolumn.c
@@ -0,0 +1,633 @@
+/* gtktreeviewcolumn.c
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gtktreeviewcolumn.h"
+#include "gtktreeprivate.h"
+#include "gtksignal.h"
+#include "gtkbutton.h"
+#include "gtkalignment.h"
+
+enum {
+ CLICKED,
+ LAST_SIGNAL
+};
+
+
+static void gtk_tree_view_column_init (GtkTreeViewColumn *tree_column);
+static void gtk_tree_view_column_class_init (GtkTreeViewColumnClass *klass);
+static void gtk_tree_view_column_set_attributesv (GtkTreeViewColumn *tree_column,
+ va_list args);
+static void gtk_real_tree_column_clicked (GtkTreeViewColumn *tree_column);
+
+
+static GtkObjectClass *parent_class = NULL;
+static guint tree_column_signals[LAST_SIGNAL] = { 0 };
+
+
+GtkType
+gtk_tree_view_column_get_type (void)
+{
+ static GtkType tree_column_type = 0;
+
+ if (!tree_column_type)
+ {
+ static const GTypeInfo tree_column_info =
+ {
+ sizeof (GtkTreeViewColumnClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) gtk_tree_view_column_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GtkTreeViewColumn),
+ 0,
+ (GInstanceInitFunc) gtk_tree_view_column_init,
+ };
+
+ tree_column_type = g_type_register_static (GTK_TYPE_OBJECT, "GtkTreeViewColumn", &tree_column_info);
+ }
+
+ return tree_column_type;
+}
+
+static void
+gtk_tree_view_column_class_init (GtkTreeViewColumnClass *class)
+{
+ GtkObjectClass *object_class;
+
+ object_class = (GtkObjectClass*) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ tree_column_signals[CLICKED] =
+ gtk_signal_new ("clicked",
+ GTK_RUN_FIRST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkTreeViewColumnClass, clicked),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+
+ gtk_object_class_add_signals (object_class, tree_column_signals, LAST_SIGNAL);
+
+ class->clicked = gtk_real_tree_column_clicked;
+}
+
+static void
+gtk_tree_view_column_init (GtkTreeViewColumn *tree_column)
+{
+ tree_column->button = NULL;
+ tree_column->justification = GTK_JUSTIFY_LEFT;
+ tree_column->size = 0;
+ tree_column->min_width = -1;
+ tree_column->max_width = -1;
+ tree_column->cell = NULL;
+ tree_column->attributes = NULL;
+ tree_column->column_type = GTK_TREE_VIEW_COLUMN_AUTOSIZE;
+ tree_column->visible = TRUE;
+ tree_column->button_active = FALSE;
+ tree_column->dirty = TRUE;
+}
+
+/* used to make the buttons 'unclickable' */
+
+static gint
+gtk_tree_view_passive_func (GtkWidget *widget,
+ GdkEvent *event,
+ gpointer data)
+{
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ switch (event->type)
+ {
+ case GDK_MOTION_NOTIFY:
+ case GDK_BUTTON_PRESS:
+ case GDK_2BUTTON_PRESS:
+ case GDK_3BUTTON_PRESS:
+ case GDK_BUTTON_RELEASE:
+ case GDK_ENTER_NOTIFY:
+ case GDK_LEAVE_NOTIFY:
+ return TRUE;
+ default:
+ break;
+ }
+ return FALSE;
+}
+
+static void
+gtk_real_tree_column_clicked (GtkTreeViewColumn *tree_column)
+{
+ g_return_if_fail (tree_column != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+
+}
+
+GtkObject *
+gtk_tree_view_column_new (void)
+{
+ GtkObject *retval;
+
+ retval = GTK_OBJECT (gtk_type_new (GTK_TYPE_TREE_COLUMN));
+
+ return retval;
+}
+
+GtkObject *
+gtk_tree_view_column_new_with_attributes (gchar *title,
+ GtkCellRenderer *cell,
+ ...)
+{
+ GtkObject *retval;
+ va_list args;
+
+ retval = gtk_tree_view_column_new ();
+
+ gtk_tree_view_column_set_title (GTK_TREE_VIEW_COLUMN (retval), title);
+ gtk_tree_view_column_set_cell_renderer (GTK_TREE_VIEW_COLUMN (retval), cell);
+
+ va_start (args, cell);
+ gtk_tree_view_column_set_attributesv (GTK_TREE_VIEW_COLUMN (retval),
+ args);
+ va_end (args);
+
+ return retval;
+}
+
+void
+gtk_tree_view_column_set_cell_renderer (GtkTreeViewColumn *tree_column,
+ GtkCellRenderer *cell)
+{
+ g_return_if_fail (tree_column != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+ if (cell)
+ g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
+
+ if (cell)
+ g_object_ref (G_OBJECT (cell));
+
+ if (tree_column->cell)
+ g_object_unref (G_OBJECT (tree_column->cell));
+
+ tree_column->cell = cell;
+}
+
+void
+gtk_tree_view_column_add_attribute (GtkTreeViewColumn *tree_column,
+ gchar *attribute,
+ gint column)
+{
+ g_return_if_fail (tree_column != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+
+ tree_column->attributes = g_slist_prepend (tree_column->attributes, GINT_TO_POINTER (column));
+ tree_column->attributes = g_slist_prepend (tree_column->attributes, g_strdup (attribute));
+}
+
+static void
+gtk_tree_view_column_set_attributesv (GtkTreeViewColumn *tree_column,
+ va_list args)
+{
+ GSList *list;
+ gchar *attribute;
+ gint column;
+
+ g_return_if_fail (tree_column != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+
+ attribute = va_arg (args, gchar *);
+
+ list = tree_column->attributes;
+
+ while (list && list->next)
+ {
+ g_free (list->data);
+ list = list->next->next;
+ }
+ g_slist_free (tree_column->attributes);
+ tree_column->attributes = NULL;
+
+ while (attribute != NULL)
+ {
+ column = va_arg (args, gint);
+ gtk_tree_view_column_add_attribute (tree_column,
+ attribute,
+ column);
+ attribute = va_arg (args, gchar *);
+ }
+}
+
+void
+gtk_tree_view_column_set_attributes (GtkTreeViewColumn *tree_column,
+ ...)
+{
+ va_list args;
+
+ g_return_if_fail (tree_column != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+
+ va_start (args, tree_column);
+
+ gtk_tree_view_column_set_attributesv (tree_column, args);
+
+ va_end (args);
+}
+
+void
+gtk_tree_view_column_set_cell_data (GtkTreeViewColumn *tree_column,
+ GtkTreeModel *tree_model,
+ GtkTreeNode tree_node)
+{
+ GSList *list;
+ GValue value = { 0, };
+ GObject *cell;
+
+ g_return_if_fail (tree_column != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+ g_return_if_fail (tree_column->cell != NULL);
+
+ if (tree_column->func && (* tree_column->func) (tree_column,
+ tree_model,
+ tree_node,
+ tree_column->func_data))
+ return;
+
+ cell = (GObject *) tree_column->cell;
+ list = tree_column->attributes;
+
+ while (list && list->next)
+ {
+ gtk_tree_model_node_get_value (tree_model,
+ tree_node,
+ GPOINTER_TO_INT (list->next->data),
+ &value);
+ g_object_set_param (cell, (gchar *) list->data, &value);
+ g_value_unset (&value);
+ list = list->next->next;
+ }
+}
+
+/* Options for manipulating the columns */
+
+void
+gtk_tree_view_column_set_visible (GtkTreeViewColumn *tree_column,
+ gboolean visible)
+{
+ g_return_if_fail (tree_column != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+
+ if (tree_column->visible == visible)
+ return;
+
+ tree_column->visible = visible;
+
+ if (visible)
+ {
+ gtk_widget_show (tree_column->button);
+ gdk_window_show (tree_column->window);
+ }
+ else
+ {
+ gtk_widget_hide (tree_column->button);
+ gdk_window_hide (tree_column->window);
+ }
+
+ if (GTK_WIDGET_REALIZED (tree_column->tree_view))
+ {
+ _gtk_tree_view_set_size (GTK_TREE_VIEW (tree_column->tree_view), -1, -1);
+ gtk_widget_queue_resize (tree_column->tree_view);
+ }
+}
+
+gboolean
+gtk_tree_view_column_get_visible (GtkTreeViewColumn *tree_column)
+{
+ g_return_val_if_fail (tree_column != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), FALSE);
+
+ return tree_column->visible;
+}
+
+void
+gtk_tree_view_column_set_col_type (GtkTreeViewColumn *tree_column,
+ GtkTreeViewColumnType type)
+{
+ g_return_if_fail (tree_column != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+
+ if (type == tree_column->column_type)
+ return;
+
+ tree_column->column_type = type;
+ switch (type)
+ {
+ case GTK_TREE_VIEW_COLUMN_AUTOSIZE:
+ tree_column->dirty = TRUE;
+ case GTK_TREE_VIEW_COLUMN_FIXED:
+ gdk_window_hide (tree_column->window);
+ break;
+ default:
+ gdk_window_show (tree_column->window);
+ gdk_window_raise (tree_column->window);
+ break;
+ }
+
+ gtk_widget_queue_resize (tree_column->tree_view);
+}
+
+gint
+gtk_tree_view_column_get_col_type (GtkTreeViewColumn *tree_column)
+{
+ g_return_val_if_fail (tree_column != NULL, 0);
+ g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), 0);
+
+ return tree_column->column_type;
+}
+
+gint
+gtk_tree_view_column_get_preferred_size (GtkTreeViewColumn *tree_column)
+{
+ return 0;
+}
+
+gint
+gtk_tree_view_column_get_size (GtkTreeViewColumn *tree_column)
+{
+ g_return_val_if_fail (tree_column != NULL, 0);
+ g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), 0);
+
+ return tree_column->size;
+}
+
+void
+gtk_tree_view_column_set_size (GtkTreeViewColumn *tree_column,
+ gint size)
+{
+ g_return_if_fail (tree_column != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+ g_return_if_fail (size > 0);
+
+ if (tree_column->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE ||
+ tree_column->size == size)
+ return;
+
+ tree_column->size = size;
+
+ if (GTK_WIDGET_REALIZED (tree_column->tree_view))
+ gtk_widget_queue_resize (tree_column->tree_view);
+}
+
+void
+gtk_tree_view_column_set_min_width (GtkTreeViewColumn *tree_column,
+ gint min_width)
+{
+ gint real_min_width;
+
+ g_return_if_fail (tree_column != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+ g_return_if_fail (min_width >= -1);
+
+ if (min_width == tree_column->min_width)
+ return;
+
+ real_min_width = (tree_column->min_width == -1) ?
+ tree_column->button->requisition.width : tree_column->min_width;
+
+ /* We want to queue a resize if the either the old min_size or the
+ * new min_size determined the size of the column */
+ if (GTK_WIDGET_REALIZED (tree_column->tree_view) &&
+ ((tree_column->min_width > tree_column->size) ||
+ (tree_column->min_width == -1 &&
+ tree_column->button->requisition.width > tree_column->size) ||
+ (min_width > tree_column->size) ||
+ (min_width == -1 &&
+ tree_column->button->requisition.width > tree_column->size)))
+ gtk_widget_queue_resize (tree_column->tree_view);
+
+ if (tree_column->max_width != -1 &&
+ tree_column->max_width < real_min_width)
+ tree_column->max_width = real_min_width;
+
+ tree_column->min_width = min_width;
+}
+
+gint
+gtk_tree_view_column_get_min_width (GtkTreeViewColumn *tree_column)
+{
+ g_return_val_if_fail (tree_column != NULL, -1);
+ g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), -1);
+
+ return tree_column->min_width;
+}
+
+void
+gtk_tree_view_column_set_max_width (GtkTreeViewColumn *tree_column,
+ gint max_width)
+{
+ gint real_min_width;
+
+ g_return_if_fail (tree_column != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+ g_return_if_fail (max_width >= -1);
+
+ if (max_width == tree_column->max_width)
+ return;
+
+ real_min_width = tree_column->min_width == -1 ?
+ tree_column->button->requisition.width : tree_column->min_width;
+
+ if (GTK_WIDGET_REALIZED (tree_column->tree_view) &&
+ ((tree_column->max_width < tree_column->size) ||
+ (max_width != -1 && max_width < tree_column->size)))
+ gtk_widget_queue_resize (tree_column->tree_view);
+
+ tree_column->max_width = max_width;
+
+ if (real_min_width > max_width)
+ tree_column->min_width = max_width;
+}
+
+gint
+gtk_tree_view_column_get_max_width (GtkTreeViewColumn *tree_column)
+{
+ g_return_val_if_fail (tree_column != NULL, -1);
+ g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), -1);
+
+ return tree_column->max_width;
+}
+
+void
+gtk_tree_view_column_set_title (GtkTreeViewColumn *tree_column,
+ gchar *title)
+{
+ g_return_if_fail (tree_column != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+
+ g_free (tree_column->title);
+ if (title)
+ tree_column->title = g_strdup (title);
+ else
+ tree_column->title = NULL;
+}
+
+gchar *
+gtk_tree_view_column_get_title (GtkTreeViewColumn *tree_column)
+{
+ g_return_val_if_fail (tree_column != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), NULL);
+
+ return tree_column->title;
+}
+
+void
+gtk_tree_view_column_set_header_active (GtkTreeViewColumn *tree_column,
+ gboolean active)
+{
+ g_return_if_fail (tree_column != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+
+ if (!tree_column->button)
+ return;
+
+ if (tree_column->button_active == active)
+ return;
+
+ tree_column->button_active = active;
+ if (active)
+ {
+ gtk_signal_disconnect_by_func (GTK_OBJECT (tree_column->button),
+ (GtkSignalFunc) gtk_tree_view_passive_func,
+ NULL);
+
+ GTK_WIDGET_SET_FLAGS (tree_column->button, GTK_CAN_FOCUS);
+
+ if (GTK_WIDGET_VISIBLE (tree_column->tree_view))
+ gtk_widget_queue_draw (tree_column->button);
+ }
+ else
+ {
+ gtk_signal_connect (GTK_OBJECT (tree_column->button),
+ "event",
+ (GtkSignalFunc) gtk_tree_view_passive_func,
+ NULL);
+
+ GTK_WIDGET_UNSET_FLAGS (tree_column->button, GTK_CAN_FOCUS);
+
+ if (GTK_WIDGET_VISIBLE (tree_column->tree_view))
+ gtk_widget_queue_draw (tree_column->button);
+ }
+}
+
+void
+gtk_tree_view_column_set_widget (GtkTreeViewColumn *tree_column,
+ GtkWidget *widget)
+{
+#if 0
+ gint new_button = 0;
+ GtkWidget *old_widget;
+
+ g_return_if_fail (tree_view != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+
+ if (column < 0 || column >= tree_view->priv->columns)
+ return;
+
+ /* if the column button doesn't currently exist,
+ * it has to be created first */
+ if (!column->button)
+ {
+ column_button_create (tree_view, column);
+ new_button = 1;
+ }
+
+ column_title_new (clist, column, NULL);
+
+ /* remove and destroy the old widget */
+ old_widget = GTK_BIN (clist->column[column].button)->child;
+ if (old_widget)
+ gtk_container_remove (GTK_CONTAINER (clist->column[column].button),
+ old_widget);
+
+ /* add and show the widget */
+ if (widget)
+ {
+ gtk_container_add (GTK_CONTAINER (clist->column[column].button), widget);
+ gtk_widget_show (widget);
+ }
+
+ /* if this button didn't previously exist, then the
+ * column button positions have to be re-computed */
+ if (GTK_WIDGET_VISIBLE (clist) && new_button)
+ size_allocate_title_buttons (clist);
+#endif
+}
+
+GtkWidget *
+gtk_tree_view_column_get_widget (GtkTreeViewColumn *tree_column)
+{
+ g_return_val_if_fail (tree_column != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), NULL);
+
+ if (tree_column->button)
+ return GTK_BUTTON (tree_column->button)->child;
+
+ return NULL;
+}
+
+void
+gtk_tree_view_column_set_justification (GtkTreeViewColumn *tree_column,
+ GtkJustification justification)
+{
+ GtkWidget *alignment;
+
+ g_return_if_fail (tree_column != NULL);
+ g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
+
+ if (tree_column->justification == justification)
+ return;
+
+ tree_column->justification = justification;
+
+ /* change the alignment of the button title if it's not a
+ * custom widget */
+ alignment = GTK_BIN (tree_column->button)->child;
+
+ if (GTK_IS_ALIGNMENT (alignment))
+ {
+ switch (tree_column->justification)
+ {
+ case GTK_JUSTIFY_LEFT:
+ gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.0, 0.5, 0.0, 0.0);
+ break;
+
+ case GTK_JUSTIFY_RIGHT:
+ gtk_alignment_set (GTK_ALIGNMENT (alignment), 1.0, 0.5, 0.0, 0.0);
+ break;
+
+ case GTK_JUSTIFY_CENTER:
+ gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.5, 0.5, 0.0, 0.0);
+ break;
+
+ case GTK_JUSTIFY_FILL:
+ gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.5, 0.5, 0.0, 0.0);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
diff --git a/gtk/gtktreeviewcolumn.h b/gtk/gtktreeviewcolumn.h
new file mode 100644
index 0000000000..765b81d3d8
--- /dev/null
+++ b/gtk/gtktreeviewcolumn.h
@@ -0,0 +1,137 @@
+/* gtktreeviewcolumn.h
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_TREE_VIEW_COLUMN_H__
+#define __GTK_TREE_VIEW_COLUMN_H__
+
+#include <gtk/gtkobject.h>
+#include <gtk/gtkcellrenderer.h>
+#include <gtk/gtktreemodel.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define GTK_TYPE_TREE_COLUMN (gtk_tree_view_column_get_type ())
+#define GTK_TREE_VIEW_COLUMN(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_TREE_COLUMN, GtkTreeViewColumn))
+#define GTK_TREE_VIEW_COLUMN_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_TREE_COLUMN, GtkTreeViewColumnClass))
+#define GTK_IS_TREE_VIEW_COLUMN(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_TREE_COLUMN))
+#define GTK_IS_TREE_VIEW_COLUMN_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), GTK_TYPE_TREE_COLUMN))
+
+typedef enum
+{
+ GTK_TREE_VIEW_COLUMN_RESIZEABLE,
+ GTK_TREE_VIEW_COLUMN_AUTOSIZE,
+ GTK_TREE_VIEW_COLUMN_FIXED
+} GtkTreeViewColumnType;
+
+typedef struct _GtkTreeViewColumn GtkTreeViewColumn;
+typedef struct _GtkTreeViewColumnClass GtkTreeViewColumnClass;
+
+typedef gboolean (* GtkTreeViewColumnFunc) (GtkTreeViewColumn *tree_column,
+ GtkTreeModel *tree_model,
+ GtkTreeNode tree_node,
+ gpointer data);
+
+struct _GtkTreeViewColumn
+{
+ GtkObject parent;
+
+ GtkWidget *tree_view;
+ GtkWidget *button;
+ GdkWindow *window;
+ GtkJustification justification;
+
+ gint id;
+ gint size;
+ gint min_width;
+ gint max_width;
+
+ GtkTreeViewColumnFunc *func;
+ gpointer func_data;
+ gchar *title;
+ GtkCellRenderer *cell;
+ GSList *attributes;
+ GtkTreeViewColumnType column_type;
+ guint visible : 1;
+ guint button_active : 1;
+ guint dirty : 1;
+};
+
+struct _GtkTreeViewColumnClass
+{
+ GtkObjectClass parent_class;
+
+ void (*clicked) (GtkTreeViewColumn *tree_column);
+};
+
+
+GtkType gtk_tree_view_column_get_type (void);
+GtkObject *gtk_tree_view_column_new (void);
+GtkObject *gtk_tree_view_column_new_with_attributes (gchar *title,
+ GtkCellRenderer *cell,
+ ...);
+void gtk_tree_view_column_set_cell_renderer (GtkTreeViewColumn *tree_column,
+ GtkCellRenderer *cell);
+void gtk_tree_view_column_add_attribute (GtkTreeViewColumn *tree_column,
+ gchar *attribute,
+ gint column);
+void gtk_tree_view_column_set_attributes (GtkTreeViewColumn *tree_column,
+ ...);
+void gtk_tree_view_column_set_cell_data (GtkTreeViewColumn *tree_column,
+ GtkTreeModel *tree_model,
+ GtkTreeNode tree_node);
+void gtk_tree_view_column_set_visible (GtkTreeViewColumn *tree_column,
+ gboolean visible);
+gboolean gtk_tree_view_column_get_visible (GtkTreeViewColumn *tree_column);
+void gtk_tree_view_column_set_col_type (GtkTreeViewColumn *tree_column,
+ GtkTreeViewColumnType type);
+gint gtk_tree_view_column_get_col_type (GtkTreeViewColumn *tree_column);
+gint gtk_tree_view_column_get_preferred_size (GtkTreeViewColumn *tree_column);
+gint gtk_tree_view_column_get_size (GtkTreeViewColumn *tree_column);
+void gtk_tree_view_column_set_size (GtkTreeViewColumn *tree_column,
+ gint width);
+void gtk_tree_view_column_set_min_width (GtkTreeViewColumn *tree_column,
+ gint min_width);
+gint gtk_tree_view_column_get_min_width (GtkTreeViewColumn *tree_column);
+void gtk_tree_view_column_set_max_width (GtkTreeViewColumn *tree_column,
+ gint max_width);
+gint gtk_tree_view_column_get_max_width (GtkTreeViewColumn *tree_column);
+
+
+/* Options for manipulating the column headers
+ */
+void gtk_tree_view_column_set_title (GtkTreeViewColumn *tree_column,
+ gchar *title);
+gchar *gtk_tree_view_column_get_title (GtkTreeViewColumn *tree_column);
+void gtk_tree_view_column_set_header_active (GtkTreeViewColumn *tree_column,
+ gboolean active);
+void gtk_tree_view_column_set_widget (GtkTreeViewColumn *tree_column,
+ GtkWidget *widget);
+GtkWidget *gtk_tree_view_column_get_widget (GtkTreeViewColumn *tree_column);
+void gtk_tree_view_column_set_justification (GtkTreeViewColumn *tree_column,
+ GtkJustification justification);
+GtkJustification gtk_tree_view_column_get_justification (GtkTreeViewColumn *tree_column);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __GTK_TREE_VIEW_COLUMN_H__ */
diff --git a/gtk/treestoretest.c b/gtk/treestoretest.c
new file mode 100644
index 0000000000..a8bec543b0
--- /dev/null
+++ b/gtk/treestoretest.c
@@ -0,0 +1,206 @@
+#include <gtk/gtk.h>
+#include <stdlib.h>
+
+GtkObject *model;
+
+static void
+row_selected (GtkTreeView *tree_view,
+ GtkTreeModel *tree_model,
+ GtkTreeNode *node,
+ GtkWidget *button)
+{
+ gtk_widget_set_sensitive (button, TRUE);
+}
+
+static void
+row_unselected (GtkTreeView *tree_view,
+ GtkTreeModel *tree_model,
+ GtkTreeNode *node,
+ GtkWidget *button)
+{
+ gtk_widget_set_sensitive (button, FALSE);
+}
+
+static GtkTreeNode *
+node_new ()
+{
+ static GValue value = {0, };
+ static gint i = 0;
+ gchar *str;
+ GtkTreeNode *node = gtk_tree_store_node_new ();
+
+ g_value_init (&value, G_TYPE_STRING);
+ str = g_strdup_printf ("FOO: %d", i++);
+ g_value_set_string (&value, str);
+ g_free (str);
+ gtk_tree_store_node_set_cell (GTK_TREE_STORE (model), node, 0, &value);
+ g_value_unset (&value);
+
+ return node;
+}
+
+static void
+node_remove (GtkWidget *button, GtkTreeView *tree_view)
+{
+ GtkTreeNode *selected = gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)));
+ gtk_tree_store_node_remove (GTK_TREE_STORE (model),
+ selected);
+}
+
+static void
+node_insert (GtkWidget *button, GtkTreeView *tree_view)
+{
+ GtkWidget *entry;
+ GtkTreeNode *selected = gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)));
+
+ entry = gtk_object_get_user_data (GTK_OBJECT (button));
+ gtk_tree_store_node_insert (GTK_TREE_STORE (model),
+ selected,
+ atoi (gtk_entry_get_text (GTK_ENTRY (entry))),
+ node_new ());
+}
+
+static void
+node_insert_before (GtkWidget *button, GtkTreeView *tree_view)
+{
+ GtkTreeNode *selected = gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)));
+ gtk_tree_store_node_insert_before (GTK_TREE_STORE (model),
+ NULL,
+ selected,
+ node_new ());
+}
+
+static void
+node_insert_after (GtkWidget *button, GtkTreeView *tree_view)
+{
+ GtkTreeNode *selected = gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)));
+ gtk_tree_store_node_insert_after (GTK_TREE_STORE (model),
+ NULL,
+ selected,
+ node_new ());
+}
+
+static void
+node_prepend (GtkWidget *button, GtkTreeView *tree_view)
+{
+ GtkTreeNode *selected = gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)));
+ gtk_tree_store_node_prepend (GTK_TREE_STORE (model),
+ selected,
+ node_new ());
+}
+
+static void
+node_append (GtkWidget *button, GtkTreeView *tree_view)
+{
+ GtkTreeNode *selected = gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)));
+ gtk_tree_store_node_append (GTK_TREE_STORE (model),
+ selected,
+ node_new ());
+}
+
+static void
+make_window ()
+{
+ GtkWidget *window;
+ GtkWidget *vbox;
+ GtkWidget *hbox, *entry;
+ GtkWidget *button;
+ GtkWidget *scrolled_window;
+ GtkWidget *tree_view;
+ GtkObject *column;
+ GtkCellRenderer *cell;
+ GtkObject *selection;
+
+ /* Make the Widgets/Objects */
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ vbox = gtk_vbox_new (FALSE, 8);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
+ gtk_window_set_default_size (GTK_WINDOW (window), 300, 350);
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
+ selection = GTK_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)));
+ gtk_tree_selection_set_type (GTK_TREE_SELECTION (selection), GTK_TREE_SELECTION_SINGLE);
+
+ /* Put them together */
+ gtk_container_add (GTK_CONTAINER (scrolled_window), tree_view);
+ gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, TRUE, TRUE, 0);
+ gtk_container_add (GTK_CONTAINER (window), vbox);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_signal_connect (GTK_OBJECT (window), "destroy", gtk_main_quit, NULL);
+
+ /* buttons */
+ button = gtk_button_new_with_label ("gtk_tree_store_node_remove");
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+ gtk_signal_connect (GTK_OBJECT (selection),
+ "row_selected", row_selected, button);
+ gtk_signal_connect (GTK_OBJECT (selection),
+ "row_unselected", row_unselected, button);
+ gtk_signal_connect (GTK_OBJECT (button), "clicked", node_remove, tree_view);
+ gtk_widget_set_sensitive (button, FALSE);
+
+ button = gtk_button_new_with_label ("gtk_tree_store_node_insert");
+ hbox = gtk_hbox_new (FALSE, 8);
+ entry = gtk_entry_new ();
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 0);
+ gtk_object_set_user_data (GTK_OBJECT (button), entry);
+ gtk_signal_connect (GTK_OBJECT (button), "clicked", node_insert, tree_view);
+
+
+ button = gtk_button_new_with_label ("gtk_tree_store_node_insert_before");
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+ gtk_signal_connect (GTK_OBJECT (selection),
+ "row_selected", row_selected, button);
+ gtk_signal_connect (GTK_OBJECT (selection),
+ "row_unselected", row_unselected, button);
+ gtk_signal_connect (GTK_OBJECT (button), "clicked", node_insert_before, tree_view);
+ gtk_widget_set_sensitive (button, FALSE);
+
+ button = gtk_button_new_with_label ("gtk_tree_store_node_insert_after");
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+ gtk_signal_connect (GTK_OBJECT (selection),
+ "row_selected", row_selected, button);
+ gtk_signal_connect (GTK_OBJECT (selection),
+ "row_unselected", row_unselected, button);
+ gtk_signal_connect (GTK_OBJECT (button), "clicked", node_insert_after, tree_view);
+ gtk_widget_set_sensitive (button, FALSE);
+
+ button = gtk_button_new_with_label ("gtk_tree_store_node_prepend");
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+ gtk_signal_connect (GTK_OBJECT (button), "clicked", node_prepend, tree_view);
+
+ button = gtk_button_new_with_label ("gtk_tree_store_node_append");
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+ gtk_signal_connect (GTK_OBJECT (button), "clicked", node_append, tree_view);
+
+ /* The selected column */
+ cell = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("nodes", cell, "text", 0, NULL);
+ gtk_tree_view_add_column (GTK_TREE_VIEW (tree_view), GTK_TREE_VIEW_COLUMN (column));
+
+ /* Show it all */
+ gtk_widget_show_all (window);
+}
+
+int
+main (int argc, char *argv[])
+{
+ gtk_init (&argc, &argv);
+
+ model = gtk_tree_store_new_with_values (2, G_TYPE_STRING, G_TYPE_STRING);
+
+ make_window ();
+ make_window ();
+
+ /* A few to start */
+ gtk_tree_store_node_append (GTK_TREE_STORE (model), NULL, node_new ());
+ gtk_tree_store_node_append (GTK_TREE_STORE (model), NULL, node_new ());
+ gtk_tree_store_node_append (GTK_TREE_STORE (model), NULL, node_new ());
+
+ gtk_main ();
+
+ return 0;
+}
diff --git a/tests/treestoretest.c b/tests/treestoretest.c
new file mode 100644
index 0000000000..a8bec543b0
--- /dev/null
+++ b/tests/treestoretest.c
@@ -0,0 +1,206 @@
+#include <gtk/gtk.h>
+#include <stdlib.h>
+
+GtkObject *model;
+
+static void
+row_selected (GtkTreeView *tree_view,
+ GtkTreeModel *tree_model,
+ GtkTreeNode *node,
+ GtkWidget *button)
+{
+ gtk_widget_set_sensitive (button, TRUE);
+}
+
+static void
+row_unselected (GtkTreeView *tree_view,
+ GtkTreeModel *tree_model,
+ GtkTreeNode *node,
+ GtkWidget *button)
+{
+ gtk_widget_set_sensitive (button, FALSE);
+}
+
+static GtkTreeNode *
+node_new ()
+{
+ static GValue value = {0, };
+ static gint i = 0;
+ gchar *str;
+ GtkTreeNode *node = gtk_tree_store_node_new ();
+
+ g_value_init (&value, G_TYPE_STRING);
+ str = g_strdup_printf ("FOO: %d", i++);
+ g_value_set_string (&value, str);
+ g_free (str);
+ gtk_tree_store_node_set_cell (GTK_TREE_STORE (model), node, 0, &value);
+ g_value_unset (&value);
+
+ return node;
+}
+
+static void
+node_remove (GtkWidget *button, GtkTreeView *tree_view)
+{
+ GtkTreeNode *selected = gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)));
+ gtk_tree_store_node_remove (GTK_TREE_STORE (model),
+ selected);
+}
+
+static void
+node_insert (GtkWidget *button, GtkTreeView *tree_view)
+{
+ GtkWidget *entry;
+ GtkTreeNode *selected = gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)));
+
+ entry = gtk_object_get_user_data (GTK_OBJECT (button));
+ gtk_tree_store_node_insert (GTK_TREE_STORE (model),
+ selected,
+ atoi (gtk_entry_get_text (GTK_ENTRY (entry))),
+ node_new ());
+}
+
+static void
+node_insert_before (GtkWidget *button, GtkTreeView *tree_view)
+{
+ GtkTreeNode *selected = gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)));
+ gtk_tree_store_node_insert_before (GTK_TREE_STORE (model),
+ NULL,
+ selected,
+ node_new ());
+}
+
+static void
+node_insert_after (GtkWidget *button, GtkTreeView *tree_view)
+{
+ GtkTreeNode *selected = gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)));
+ gtk_tree_store_node_insert_after (GTK_TREE_STORE (model),
+ NULL,
+ selected,
+ node_new ());
+}
+
+static void
+node_prepend (GtkWidget *button, GtkTreeView *tree_view)
+{
+ GtkTreeNode *selected = gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)));
+ gtk_tree_store_node_prepend (GTK_TREE_STORE (model),
+ selected,
+ node_new ());
+}
+
+static void
+node_append (GtkWidget *button, GtkTreeView *tree_view)
+{
+ GtkTreeNode *selected = gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)));
+ gtk_tree_store_node_append (GTK_TREE_STORE (model),
+ selected,
+ node_new ());
+}
+
+static void
+make_window ()
+{
+ GtkWidget *window;
+ GtkWidget *vbox;
+ GtkWidget *hbox, *entry;
+ GtkWidget *button;
+ GtkWidget *scrolled_window;
+ GtkWidget *tree_view;
+ GtkObject *column;
+ GtkCellRenderer *cell;
+ GtkObject *selection;
+
+ /* Make the Widgets/Objects */
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ vbox = gtk_vbox_new (FALSE, 8);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
+ gtk_window_set_default_size (GTK_WINDOW (window), 300, 350);
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
+ selection = GTK_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)));
+ gtk_tree_selection_set_type (GTK_TREE_SELECTION (selection), GTK_TREE_SELECTION_SINGLE);
+
+ /* Put them together */
+ gtk_container_add (GTK_CONTAINER (scrolled_window), tree_view);
+ gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, TRUE, TRUE, 0);
+ gtk_container_add (GTK_CONTAINER (window), vbox);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_signal_connect (GTK_OBJECT (window), "destroy", gtk_main_quit, NULL);
+
+ /* buttons */
+ button = gtk_button_new_with_label ("gtk_tree_store_node_remove");
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+ gtk_signal_connect (GTK_OBJECT (selection),
+ "row_selected", row_selected, button);
+ gtk_signal_connect (GTK_OBJECT (selection),
+ "row_unselected", row_unselected, button);
+ gtk_signal_connect (GTK_OBJECT (button), "clicked", node_remove, tree_view);
+ gtk_widget_set_sensitive (button, FALSE);
+
+ button = gtk_button_new_with_label ("gtk_tree_store_node_insert");
+ hbox = gtk_hbox_new (FALSE, 8);
+ entry = gtk_entry_new ();
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 0);
+ gtk_object_set_user_data (GTK_OBJECT (button), entry);
+ gtk_signal_connect (GTK_OBJECT (button), "clicked", node_insert, tree_view);
+
+
+ button = gtk_button_new_with_label ("gtk_tree_store_node_insert_before");
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+ gtk_signal_connect (GTK_OBJECT (selection),
+ "row_selected", row_selected, button);
+ gtk_signal_connect (GTK_OBJECT (selection),
+ "row_unselected", row_unselected, button);
+ gtk_signal_connect (GTK_OBJECT (button), "clicked", node_insert_before, tree_view);
+ gtk_widget_set_sensitive (button, FALSE);
+
+ button = gtk_button_new_with_label ("gtk_tree_store_node_insert_after");
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+ gtk_signal_connect (GTK_OBJECT (selection),
+ "row_selected", row_selected, button);
+ gtk_signal_connect (GTK_OBJECT (selection),
+ "row_unselected", row_unselected, button);
+ gtk_signal_connect (GTK_OBJECT (button), "clicked", node_insert_after, tree_view);
+ gtk_widget_set_sensitive (button, FALSE);
+
+ button = gtk_button_new_with_label ("gtk_tree_store_node_prepend");
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+ gtk_signal_connect (GTK_OBJECT (button), "clicked", node_prepend, tree_view);
+
+ button = gtk_button_new_with_label ("gtk_tree_store_node_append");
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+ gtk_signal_connect (GTK_OBJECT (button), "clicked", node_append, tree_view);
+
+ /* The selected column */
+ cell = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("nodes", cell, "text", 0, NULL);
+ gtk_tree_view_add_column (GTK_TREE_VIEW (tree_view), GTK_TREE_VIEW_COLUMN (column));
+
+ /* Show it all */
+ gtk_widget_show_all (window);
+}
+
+int
+main (int argc, char *argv[])
+{
+ gtk_init (&argc, &argv);
+
+ model = gtk_tree_store_new_with_values (2, G_TYPE_STRING, G_TYPE_STRING);
+
+ make_window ();
+ make_window ();
+
+ /* A few to start */
+ gtk_tree_store_node_append (GTK_TREE_STORE (model), NULL, node_new ());
+ gtk_tree_store_node_append (GTK_TREE_STORE (model), NULL, node_new ());
+ gtk_tree_store_node_append (GTK_TREE_STORE (model), NULL, node_new ());
+
+ gtk_main ();
+
+ return 0;
+}