diff options
author | 4 <jrb@redhat.com> | 2000-10-05 01:04:57 +0000 |
---|---|---|
committer | Jonathan Blandford <jrb@src.gnome.org> | 2000-10-05 01:04:57 +0000 |
commit | 3ff4a2c081e5225f010ecaed91b162eabbb9caf5 (patch) | |
tree | f1b889ac680e66a61e54d559baa911e524d10f2a | |
parent | 76806e9a1e2c56f31d7a96c02ea194d3cc5bbc0e (diff) | |
download | gdk-pixbuf-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.
38 files changed, 11465 insertions, 0 deletions
@@ -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 3bb78c444..780d42713 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 3bb78c444..780d42713 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 3bb78c444..780d42713 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 3bb78c444..780d42713 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 3bb78c444..780d42713 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 3bb78c444..780d42713 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 000000000..5a12dc21e --- /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 000000000..2b1612692 --- /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 000000000..c37cb5afb --- /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 000000000..14fd9b579 --- /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 000000000..9925f03ee --- /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 000000000..762705413 --- /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 000000000..3929a1333 --- /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 000000000..fa372b447 --- /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 000000000..6830604b4 --- /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 000000000..0df669646 --- /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 000000000..5fb475570 --- /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 000000000..da643888d --- /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 000000000..4e6d81d88 --- /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 000000000..bcce4fa47 --- /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 000000000..92b170d1f --- /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 000000000..9446510d5 --- /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>K_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 000000000..47f022fc1 --- /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 000000000..823349c48 --- /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 000000000..217734658 --- /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 000000000..e70034943 --- /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 000000000..0274f3289 --- /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)&>K_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 000000000..73d84f076 --- /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 000000000..325cad8a8 --- /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 000000000..b944e4917 --- /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 000000000..dce0bafed --- /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 000000000..f2bf4ad74 --- /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>K_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&>K_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 000000000..bc90c5f01 --- /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 000000000..248e6552c --- /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 000000000..765b81d3d --- /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 000000000..a8bec543b --- /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 000000000..a8bec543b --- /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; +} |