summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gtk/Makefile.am7
-rw-r--r--gtk/gtk.h3
-rw-r--r--gtk/gtkcelllayout.c168
-rw-r--r--gtk/gtkcelllayout.h96
-rw-r--r--gtk/gtkentry.c256
-rw-r--r--gtk/gtkentry.h5
-rw-r--r--gtk/gtkentrycompletion.c956
-rw-r--r--gtk/gtkentrycompletion.h103
-rw-r--r--gtk/gtkentryprivate.h65
-rw-r--r--gtk/gtkmarshalers.list1
-rw-r--r--gtk/gtktreemodelfilter.c2746
-rw-r--r--gtk/gtktreemodelfilter.h99
-rw-r--r--gtk/gtktreeviewcolumn.c332
-rw-r--r--po/POTFILES.in3
14 files changed, 4727 insertions, 113 deletions
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 3d0fc73cc..abca8097b 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -102,6 +102,7 @@ gtk_public_h_sources = \
gtkbutton.h \
gtkcalendar.h \
gtkcelleditable.h \
+ gtkcelllayout.h \
gtkcellrenderer.h \
gtkcellrendererpixbuf.h \
gtkcellrenderertext.h \
@@ -124,6 +125,7 @@ gtk_public_h_sources = \
gtkdrawingarea.h \
gtkeditable.h \
gtkentry.h \
+ gtkentrycompletion.h \
gtkenums.h \
gtkeventbox.h \
gtkexpander.h \
@@ -221,6 +223,7 @@ gtk_public_h_sources = \
gtktreednd.h \
gtktreeitem.h \
gtktreemodel.h \
+ gtktreemodelfilter.h \
gtktreemodelsort.h \
gtktreeselection.h \
gtktreesortable.h \
@@ -241,6 +244,7 @@ gtk_public_h_sources = \
# GTK+ header files that don't get installed
gtk_private_h_sources = \
+ gtkentryprivate.h \
gtkrbtree.h \
gtktextbtree.h \
gtktextchildprivate.h \
@@ -277,6 +281,7 @@ gtk_c_sources = \
gtkbox.c \
gtkbutton.c \
gtkcalendar.c \
+ gtkcelllayout.c \
gtkcellrenderer.c \
gtkcelleditable.c \
gtkcellrenderertext.c \
@@ -299,6 +304,7 @@ gtk_c_sources = \
gtkdrawingarea.c \
gtkeditable.c \
gtkentry.c \
+ gtkentrycompletion.c \
gtkeventbox.c \
gtkexpander.c \
gtkfilesel.c \
@@ -403,6 +409,7 @@ gtk_c_sources = \
gtktreedatalist.c \
gtktreednd.c \
gtktreemodel.c \
+ gtktreemodelfilter.c \
gtktreemodelsort.c \
gtktreeselection.c \
gtktreesortable.c \
diff --git a/gtk/gtk.h b/gtk/gtk.h
index 5115dbe4e..051839def 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -43,6 +43,7 @@
#include <gtk/gtkbox.h>
#include <gtk/gtkbutton.h>
#include <gtk/gtkcalendar.h>
+#include <gtk/gtkcelllayout.h>
#include <gtk/gtkcellrenderer.h>
#include <gtk/gtkcellrendererpixbuf.h>
#include <gtk/gtkcellrenderertext.h>
@@ -63,6 +64,7 @@
#include <gtk/gtkdrawingarea.h>
#include <gtk/gtkeditable.h>
#include <gtk/gtkentry.h>
+#include <gtk/gtkentrycompletion.h>
#include <gtk/gtkenums.h>
#include <gtk/gtkeventbox.h>
#include <gtk/gtkexpander.h>
@@ -152,6 +154,7 @@
#include <gtk/gtktreednd.h>
#include <gtk/gtktreeitem.h>
#include <gtk/gtktreemodel.h>
+#include <gtk/gtktreemodelfilter.h>
#include <gtk/gtktreemodelsort.h>
#include <gtk/gtktreeselection.h>
#include <gtk/gtktreestore.h>
diff --git a/gtk/gtkcelllayout.c b/gtk/gtkcelllayout.c
new file mode 100644
index 000000000..043b7073a
--- /dev/null
+++ b/gtk/gtkcelllayout.c
@@ -0,0 +1,168 @@
+/* gtkcelllayout.c
+ * Copyright (C) 2003 Kristian Rietveld <kris@gtk.org>
+ *
+ * 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 "gtkcelllayout.h"
+
+GType
+gtk_cell_layout_get_type (void)
+{
+ static GType cell_layout_type = 0;
+
+ if (! cell_layout_type)
+ {
+ static const GTypeInfo cell_layout_info =
+ {
+ sizeof (GtkCellLayoutIface),
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ 0,
+ 0,
+ NULL
+ };
+
+ cell_layout_type =
+ g_type_register_static (G_TYPE_INTERFACE, "GtkCellLayout",
+ &cell_layout_info, 0);
+
+ g_type_interface_add_prerequisite (cell_layout_type, G_TYPE_OBJECT);
+ }
+
+ return cell_layout_type;
+}
+
+
+void
+gtk_cell_layout_pack_start (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ gboolean expand)
+{
+ g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
+
+ (* GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->pack_start) (cell_layout,
+ cell,
+ expand);
+}
+
+void
+gtk_cell_layout_pack_end (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ gboolean expand)
+{
+ g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
+
+ (* GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->pack_end) (cell_layout,
+ cell,
+ expand);
+}
+
+void
+gtk_cell_layout_clear (GtkCellLayout *cell_layout)
+{
+ g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout));
+
+ (* GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->clear) (cell_layout);
+}
+
+static void
+gtk_cell_layout_set_attributesv (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ va_list args)
+{
+ gchar *attribute;
+ gint column;
+ GtkCellLayoutIface *iface;
+
+ attribute = va_arg (args, gchar *);
+
+ iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout);
+
+ (* iface->clear_attributes) (cell_layout, cell);
+
+ while (attribute != NULL)
+ {
+ column = va_arg (args, gint);
+ (* iface->add_attribute) (cell_layout, cell, attribute, column);
+ attribute = va_arg (args, gchar *);
+ }
+}
+
+void
+gtk_cell_layout_set_attributes (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ ...)
+{
+ va_list args;
+
+ g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
+
+ va_start (args, cell);
+ gtk_cell_layout_set_attributesv (cell_layout, cell, args);
+ va_end (args);
+}
+
+void
+gtk_cell_layout_add_attribute (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ const gchar *attribute,
+ gint column)
+{
+ g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
+ g_return_if_fail (attribute != NULL);
+ g_return_if_fail (column >= 0);
+
+ (* GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->add_attribute) (cell_layout,
+ cell,
+ attribute,
+ column);
+}
+
+void
+gtk_cell_layout_set_cell_data_func (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ GtkCellLayoutDataFunc func,
+ gpointer func_data,
+ GDestroyNotify destroy)
+{
+ g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
+
+ (* GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->set_cell_data_func) (cell_layout,
+ cell,
+ func,
+ func_data,
+ destroy);
+}
+
+void
+gtk_cell_layout_clear_attributes (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell)
+{
+ g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
+
+ (* GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->clear_attributes) (cell_layout,
+ cell);
+}
diff --git a/gtk/gtkcelllayout.h b/gtk/gtkcelllayout.h
new file mode 100644
index 000000000..5e60e2e36
--- /dev/null
+++ b/gtk/gtkcelllayout.h
@@ -0,0 +1,96 @@
+/* gtkcelllayout.h
+ * Copyright (C) 2003 Kristian Rietveld <kris@gtk.org>
+ *
+ * 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_LAYOUT_H__
+#define __GTK_CELL_LAYOUT_H__
+
+#include <glib-object.h>
+
+#include <gtk/gtkcellrenderer.h>
+#include <gtk/gtktreeviewcolumn.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_CELL_LAYOUT (gtk_cell_layout_get_type ())
+#define GTK_CELL_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_LAYOUT, GtkCellLayout))
+#define GTK_IS_CELL_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_LAYOUT))
+#define GTK_CELL_LAYOUT_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GTK_TYPE_CELL_LAYOUT, GtkCellLayoutIface))
+
+typedef struct _GtkCellLayout GtkCellLayout; /* dummy typedef */
+typedef struct _GtkCellLayoutIface GtkCellLayoutIface;
+
+/* keep in sync with GtkTreeCellDataFunc */
+typedef void (* GtkCellLayoutDataFunc) (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gpointer data);
+
+struct _GtkCellLayoutIface
+{
+ GTypeInterface g_iface;
+
+ /* Virtual Table */
+ void (* pack_start) (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ gboolean expand);
+ void (* pack_end) (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ gboolean expand);
+ void (* clear) (GtkCellLayout *cell_layout);
+ void (* add_attribute) (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ const gchar *attribute,
+ gint column);
+ void (* set_cell_data_func) (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ GtkCellLayoutDataFunc func,
+ gpointer func_data,
+ GDestroyNotify destroy);
+ void (* clear_attributes) (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell);
+};
+
+GType gtk_cell_layout_get_type (void);
+void gtk_cell_layout_pack_start (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ gboolean expand);
+void gtk_cell_layout_pack_end (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ gboolean expand);
+void gtk_cell_layout_clear (GtkCellLayout *cell_layout);
+void gtk_cell_layout_set_attributes (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ ...);
+void gtk_cell_layout_add_attribute (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ const gchar *attribute,
+ gint column);
+void gtk_cell_layout_set_cell_data_func (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ GtkCellLayoutDataFunc func,
+ gpointer func_data,
+ GDestroyNotify destroy);
+void gtk_cell_layout_clear_attributes (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell);
+
+
+G_END_DECLS
+
+#endif /* __GTK_CELL_LAYOUT_H__ */
diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c
index 24fa80877..2ec396e4e 100644
--- a/gtk/gtkentry.c
+++ b/gtk/gtkentry.c
@@ -47,10 +47,17 @@
#include "gtkstock.h"
#include "gtktextutil.h"
#include "gtkwindow.h"
+#include "gtktreeview.h"
+#include "gtktreeselection.h"
+#include "gtkentryprivate.h"
+#include "gtkcelllayout.h"
+
+#define GTK_ENTRY_COMPLETION_KEY "gtk-entry-completion-key"
#define MIN_ENTRY_WIDTH 150
#define DRAW_TIMEOUT 20
#define INNER_BORDER 2
+#define COMPLETION_TIMEOUT 300
/* Initial size of buffer, in bytes */
#define MIN_SIZE 16
@@ -936,6 +943,8 @@ gtk_entry_finalize (GObject *object)
{
GtkEntry *entry = GTK_ENTRY (object);
+ gtk_entry_set_completion (entry, NULL);
+
if (entry->cached_layout)
g_object_unref (entry->cached_layout);
@@ -1664,6 +1673,7 @@ gtk_entry_focus_out (GtkWidget *widget,
GdkEventFocus *event)
{
GtkEntry *entry = GTK_ENTRY (widget);
+ GtkEntryCompletion *completion;
gtk_widget_queue_draw (widget);
@@ -1675,6 +1685,10 @@ gtk_entry_focus_out (GtkWidget *widget,
g_signal_handlers_disconnect_by_func (gdk_keymap_get_for_display (gtk_widget_get_display (widget)),
gtk_entry_keymap_direction_changed,
entry);
+
+ completion = gtk_entry_get_completion (entry);
+ if (completion)
+ _gtk_entry_completion_popdown (completion);
return FALSE;
}
@@ -4451,3 +4465,245 @@ gtk_entry_pend_cursor_blink (GtkEntry *entry)
show_cursor (entry);
}
}
+
+/* completion */
+static gint
+gtk_entry_completion_timeout (gpointer data)
+{
+ GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (data);
+
+ GDK_THREADS_ENTER ();
+
+ completion->priv->completion_timeout = 0;
+
+ if (strlen (gtk_entry_get_text (GTK_ENTRY (completion->priv->entry))) >= completion->priv->minimum_key_length)
+ {
+ gint matches;
+ gint actions;
+ GtkTreeSelection *s;
+
+ gtk_entry_completion_complete (completion);
+ matches = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->filter_model), NULL);
+
+ gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view)));
+
+ s = gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->action_view));
+
+ gtk_tree_selection_unselect_all (s);
+
+ actions = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->actions), NULL);
+
+ if (matches > 0 || actions > 0)
+ _gtk_entry_completion_popup (completion);
+ }
+
+ GDK_THREADS_LEAVE ();
+
+ return FALSE;
+}
+
+static gboolean
+gtk_entry_completion_key_press (GtkWidget *widget,
+ GdkEventKey *event,
+ gpointer user_data)
+{
+ gint matches, actions = 0;
+ GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
+
+ if (!GTK_WIDGET_MAPPED (completion->priv->popup_window))
+ return FALSE;
+
+ matches = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->filter_model), NULL);
+
+ if (completion->priv->actions)
+ actions = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->actions), NULL);
+
+ if (event->keyval == GDK_Up || event->keyval == GDK_Down)
+ {
+ GtkTreePath *path = NULL;
+
+ if (event->keyval == GDK_Up)
+ {
+ completion->priv->current_selected--;
+ if (completion->priv->current_selected < 0)
+ completion->priv->current_selected = 0;
+ }
+ else
+ {
+ completion->priv->current_selected++;
+ if (completion->priv->current_selected >= matches + actions)
+ completion->priv->current_selected = 0;
+ }
+
+ if (completion->priv->current_selected < matches)
+ {
+ gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->action_view)));
+
+ path = gtk_tree_path_new_from_indices (completion->priv->current_selected, -1);
+ gtk_tree_view_set_cursor (GTK_TREE_VIEW (completion->priv->tree_view),
+ path, NULL, FALSE);
+ }
+ else if (completion->priv->current_selected - matches >= 0)
+ {
+ gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view)));
+
+ path = gtk_tree_path_new_from_indices (completion->priv->current_selected - matches, -1);
+ gtk_tree_view_set_cursor (GTK_TREE_VIEW (completion->priv->action_view),
+ path, NULL, FALSE);
+ }
+
+ gtk_tree_path_free (path);
+
+ return TRUE;
+ }
+ else if (event->keyval == GDK_ISO_Enter ||
+ event->keyval == GDK_Return ||
+ event->keyval == GDK_Escape)
+ {
+ _gtk_entry_completion_popdown (completion);
+
+ if (event->keyval == GDK_Escape)
+ return TRUE;
+
+ if (completion->priv->current_selected < matches)
+ {
+ GtkTreeIter iter;
+ GtkTreeModel *model = NULL;
+ GtkTreeSelection *sel;
+ gboolean entry_set;
+
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view));
+ gtk_tree_selection_get_selected (sel, &model, &iter);
+
+ g_signal_emit_by_name (completion, "match_selected",
+ model, &iter, &entry_set);
+
+ if (!entry_set)
+ {
+ gchar *str = NULL;
+
+ gtk_tree_model_get (model, &iter,
+ completion->priv->text_column, &str,
+ -1);
+
+ g_signal_handler_block (widget, completion->priv->changed_id);
+ gtk_entry_set_text (GTK_ENTRY (widget), str);
+ g_signal_handler_unblock (widget, completion->priv->changed_id);
+
+ /* move the cursor to the end */
+ gtk_editable_set_position (GTK_EDITABLE (widget), -1);
+
+ g_free (str);
+ }
+
+ return TRUE;
+ }
+ else if (completion->priv->current_selected - matches >= 0)
+ {
+ GtkTreePath *path;
+
+ path = gtk_tree_path_new_from_indices (completion->priv->current_selected - matches, -1);
+
+ g_signal_emit_by_name (completion, "action_activated",
+ gtk_tree_path_get_indices (path)[0]);
+ gtk_tree_path_free (path);
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+gtk_entry_completion_changed (GtkWidget *entry,
+ gpointer user_data)
+{
+ GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
+
+ if (GTK_WIDGET_MAPPED (completion->priv->popup_window))
+ _gtk_entry_completion_popdown (completion);
+
+ /* (re)install completion timeout */
+ if (completion->priv->completion_timeout)
+ g_source_remove (completion->priv->completion_timeout);
+
+ if (!gtk_entry_get_text (GTK_ENTRY (entry)))
+ return;
+
+ /* no need to normalize for this test */
+ if (! strcmp ("", gtk_entry_get_text (GTK_ENTRY (entry))))
+ return;
+
+ completion->priv->completion_timeout =
+ g_timeout_add (COMPLETION_TIMEOUT,
+ gtk_entry_completion_timeout,
+ completion);
+}
+
+void
+gtk_entry_set_completion (GtkEntry *entry,
+ GtkEntryCompletion *completion)
+{
+ GtkEntryCompletion *old;
+
+ g_return_if_fail (GTK_IS_ENTRY (entry));
+ g_return_if_fail (!completion || GTK_IS_ENTRY_COMPLETION (completion));
+
+ old = gtk_entry_get_completion (entry);
+
+ if (old)
+ {
+ if (old->priv->completion_timeout)
+ {
+ g_source_remove (old->priv->completion_timeout);
+ old->priv->completion_timeout = 0;
+ }
+
+ if (GTK_WIDGET_MAPPED (old->priv->popup_window))
+ _gtk_entry_completion_popdown (old);
+
+ gtk_cell_layout_clear (GTK_CELL_LAYOUT (old));
+ old->priv->text_column = -1;
+
+ if (g_signal_handler_is_connected (entry, old->priv->changed_id))
+ g_signal_handler_disconnect (entry, old->priv->changed_id);
+ if (g_signal_handler_is_connected (entry, old->priv->key_press_id))
+ g_signal_handler_disconnect (entry, old->priv->key_press_id);
+
+ old->priv->entry = NULL;
+
+ g_object_unref (old);
+ }
+
+ if (!completion)
+ {
+ g_object_set_data (G_OBJECT (entry), GTK_ENTRY_COMPLETION_KEY, NULL);
+ return;
+ }
+
+ /* hook into the entry */
+ g_object_ref (completion);
+
+ completion->priv->changed_id =
+ g_signal_connect (entry, "changed",
+ G_CALLBACK (gtk_entry_completion_changed), completion);
+
+ completion->priv->key_press_id =
+ g_signal_connect (entry, "key_press_event",
+ G_CALLBACK (gtk_entry_completion_key_press), completion);
+
+ completion->priv->entry = GTK_WIDGET (entry);
+ g_object_set_data (G_OBJECT (entry), GTK_ENTRY_COMPLETION_KEY, completion);
+}
+
+GtkEntryCompletion *
+gtk_entry_get_completion (GtkEntry *entry)
+{
+ GtkEntryCompletion *completion;
+
+ g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
+
+ completion = GTK_ENTRY_COMPLETION (g_object_get_data (G_OBJECT (entry),
+ GTK_ENTRY_COMPLETION_KEY));
+
+ return completion;
+}
diff --git a/gtk/gtkentry.h b/gtk/gtkentry.h
index c8f4ef1a3..d7b2f2547 100644
--- a/gtk/gtkentry.h
+++ b/gtk/gtkentry.h
@@ -32,6 +32,7 @@
#include <gtk/gtkeditable.h>
#include <gtk/gtkimcontext.h>
#include <gtk/gtkmenu.h>
+#include <gtk/gtkentrycompletion.h>
#include <pango/pango.h>
#ifdef __cplusplus
@@ -182,6 +183,10 @@ void gtk_entry_get_layout_offsets (GtkEntry *entry,
gint *x,
gint *y);
+void gtk_entry_set_completion (GtkEntry *entry,
+ GtkEntryCompletion *completion);
+GtkEntryCompletion *gtk_entry_get_completion (GtkEntry *entry);
+
/* Deprecated compatibility functions
*/
diff --git a/gtk/gtkentrycompletion.c b/gtk/gtkentrycompletion.c
new file mode 100644
index 000000000..d4c4ad3d2
--- /dev/null
+++ b/gtk/gtkentrycompletion.c
@@ -0,0 +1,956 @@
+/* gtkentrycompletion.c
+ * Copyright (C) 2003 Kristian Rietveld <kris@gtk.org>
+ *
+ * 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 "gtkentrycompletion.h"
+#include "gtkentryprivate.h"
+#include "gtkcelllayout.h"
+
+#include <gtk/gtkintl.h>
+#include <gtk/gtkcellrenderertext.h>
+#include <gtk/gtktreeselection.h>
+#include <gtk/gtktreeview.h>
+#include <gtk/gtkscrolledwindow.h>
+#include <gtk/gtkvbox.h>
+#include <gtk/gtkwindow.h>
+#include <gtk/gtkentry.h>
+#include <gtk/gtkmain.h>
+#include <gtk/gtksignal.h>
+#include <gtk/gtkmarshalers.h>
+
+#include <string.h>
+
+
+/* signals */
+enum
+{
+ MATCH_SELECTED,
+ ACTION_ACTIVATED,
+ LAST_SIGNAL
+};
+
+/* properties */
+enum
+{
+ PROP_0,
+ PROP_MODEL,
+ PROP_MINIMUM_KEY_LENGTH
+};
+
+
+static void gtk_entry_completion_class_init (GtkEntryCompletionClass *klass);
+static void gtk_entry_completion_cell_layout_init (GtkCellLayoutIface *iface);
+static void gtk_entry_completion_init (GtkEntryCompletion *completion);
+static void gtk_entry_completion_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gtk_entry_completion_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gtk_entry_completion_finalize (GObject *object);
+
+static void gtk_entry_completion_pack_start (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ gboolean expand);
+static void gtk_entry_completion_pack_end (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ gboolean expand);
+static void gtk_entry_completion_clear (GtkCellLayout *cell_layout);
+static void gtk_entry_completion_add_attribute (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ const char *attribute,
+ gint column);
+static void gtk_entry_completion_set_cell_data_func (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ GtkCellLayoutDataFunc func,
+ gpointer func_data,
+ GDestroyNotify destroy);
+static void gtk_entry_completion_clear_attributes (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell);
+
+static gboolean gtk_entry_completion_visible_func (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data);
+static gboolean gtk_entry_completion_popup_key_press (GtkWidget *widget,
+ GdkEventKey *event,
+ gpointer user_data);
+static gboolean gtk_entry_completion_popup_button_press (GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer user_data);
+static gboolean gtk_entry_completion_list_button_press (GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer user_data);
+static gboolean gtk_entry_completion_action_button_press (GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer user_data);
+
+static void gtk_entry_completion_insert_action (GtkEntryCompletion *completion,
+ gint index,
+ gchar *string,
+ gboolean markup);
+static void gtk_entry_completion_action_data_func (GtkTreeViewColumn *tree_column,
+ GtkCellRenderer *cell,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data);
+
+
+static guint entry_completion_signals[LAST_SIGNAL] = { 0 };
+
+
+GType
+gtk_entry_completion_get_type (void)
+{
+ static GType entry_completion_type = 0;
+
+ if (!entry_completion_type)
+ {
+ static const GTypeInfo entry_completion_info =
+ {
+ sizeof (GtkEntryCompletionClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) gtk_entry_completion_class_init,
+ NULL,
+ NULL,
+ sizeof (GtkEntryCompletion),
+ 0,
+ (GInstanceInitFunc) gtk_entry_completion_init
+ };
+
+ static const GInterfaceInfo cell_layout_info =
+ {
+ (GInterfaceInitFunc) gtk_entry_completion_cell_layout_init,
+ NULL,
+ NULL
+ };
+
+ entry_completion_type =
+ g_type_register_static (G_TYPE_OBJECT, "GtkEntryCompletion",
+ &entry_completion_info, 0);
+
+ g_type_add_interface_static (entry_completion_type,
+ GTK_TYPE_CELL_LAYOUT,
+ &cell_layout_info);
+ }
+
+ return entry_completion_type;
+}
+
+static void
+gtk_entry_completion_class_init (GtkEntryCompletionClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = (GObjectClass *)klass;
+
+ object_class->set_property = gtk_entry_completion_set_property;
+ object_class->get_property = gtk_entry_completion_get_property;
+ object_class->finalize = gtk_entry_completion_finalize;
+
+ entry_completion_signals[MATCH_SELECTED] =
+ g_signal_new ("match_selected",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkEntryCompletionClass, match_selected),
+ _gtk_boolean_handled_accumulator, NULL,
+ _gtk_marshal_BOOLEAN__OBJECT_BOXED,
+ G_TYPE_BOOLEAN, 2,
+ GTK_TYPE_TREE_MODEL,
+ GTK_TYPE_TREE_ITER);
+ entry_completion_signals[ACTION_ACTIVATED] =
+ g_signal_new ("action_activated",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkEntryCompletionClass, action_activated),
+ NULL, NULL,
+ _gtk_marshal_NONE__INT,
+ G_TYPE_NONE, 1,
+ G_TYPE_INT);
+
+ g_object_class_install_property (object_class,
+ PROP_MODEL,
+ g_param_spec_object ("model",
+ _("Completion Model"),
+ _("The model to find matches in"),
+ GTK_TYPE_TREE_MODEL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_MINIMUM_KEY_LENGTH,
+ g_param_spec_int ("minimum_key_length",
+ _("Minimum Key Length"),
+ _("Minimum length of the search key in order to look up matches"),
+ -1,
+ G_MAXINT,
+ 1,
+ G_PARAM_READWRITE));
+
+ g_type_class_add_private (object_class, sizeof (GtkEntryCompletionPrivate));
+}
+
+static void
+gtk_entry_completion_cell_layout_init (GtkCellLayoutIface *iface)
+{
+ iface->pack_start = gtk_entry_completion_pack_start;
+ iface->pack_end = gtk_entry_completion_pack_end;
+ iface->clear = gtk_entry_completion_clear;
+ iface->add_attribute = gtk_entry_completion_add_attribute;
+ iface->set_cell_data_func = gtk_entry_completion_set_cell_data_func;
+ iface->clear_attributes = gtk_entry_completion_clear_attributes;
+}
+
+static void
+gtk_entry_completion_init (GtkEntryCompletion *completion)
+{
+ GtkCellRenderer *cell;
+ GtkTreeSelection *sel;
+ GtkEntryCompletionPrivate *priv;
+
+ /* yes, also priv, need to keep the code readable */
+ priv = completion->priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (completion);
+
+ priv->minimum_key_length = 1;
+ priv->text_column = -1;
+
+ /* completions */
+ priv->filter_model = NULL;
+
+ priv->tree_view = gtk_tree_view_new ();
+ g_signal_connect (priv->tree_view, "button_press_event",
+ G_CALLBACK (gtk_entry_completion_list_button_press),
+ completion);
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view), FALSE);
+
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
+ gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
+ gtk_tree_selection_unselect_all (sel);
+
+ priv->column = gtk_tree_view_column_new ();
+ gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view), priv->column);
+
+ priv->scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
+ GTK_POLICY_NEVER,
+ GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->scrolled_window),
+ GTK_SHADOW_ETCHED_IN);
+
+
+ /* actions */
+ priv->actions = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
+
+ priv->action_view =
+ gtk_tree_view_new_with_model (GTK_TREE_MODEL (priv->actions));
+ g_signal_connect (priv->action_view, "button_press_event",
+ G_CALLBACK (gtk_entry_completion_action_button_press),
+ completion);
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->action_view), FALSE);
+
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->action_view));
+ gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
+ gtk_tree_selection_unselect_all (sel);
+
+ cell = gtk_cell_renderer_text_new ();
+ g_object_set (cell, "cell_background_gdk",
+ &priv->tree_view->style->bg[GTK_STATE_NORMAL],
+ NULL);
+ gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (priv->action_view),
+ 0, "",
+ cell,
+ gtk_entry_completion_action_data_func,
+ NULL,
+ NULL);
+
+ /* pack it all */
+ priv->popup_window = gtk_window_new (GTK_WINDOW_POPUP);
+ gtk_window_set_resizable (GTK_WINDOW (priv->popup_window), FALSE);
+ g_signal_connect (priv->popup_window, "key_press_event",
+ G_CALLBACK (gtk_entry_completion_popup_key_press),
+ completion);
+ g_signal_connect (priv->popup_window, "button_press_event",
+ G_CALLBACK (gtk_entry_completion_popup_button_press),
+ completion);
+
+ priv->vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (priv->popup_window), priv->vbox);
+
+ gtk_container_add (GTK_CONTAINER (priv->scrolled_window), priv->tree_view);
+ gtk_box_pack_start (GTK_BOX (priv->vbox), priv->scrolled_window,
+ TRUE, TRUE, 0);
+
+ /* we don't want to see the action treeview when no actions have
+ * been inserted, so we pack the action treeview after the first
+ * action has been added
+ */
+}
+
+static void
+gtk_entry_completion_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (object);
+
+ switch (prop_id)
+ {
+ case PROP_MODEL:
+ gtk_entry_completion_set_model (completion,
+ g_value_get_object (value));
+ break;
+
+ case PROP_MINIMUM_KEY_LENGTH:
+ gtk_entry_completion_set_minimum_key_length (completion,
+ g_value_get_int (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_entry_completion_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (object);
+
+ switch (prop_id)
+ {
+ case PROP_MODEL:
+ g_value_set_object (value,
+ gtk_entry_completion_get_model (completion));
+ break;
+
+ case PROP_MINIMUM_KEY_LENGTH:
+ g_value_set_int (value, gtk_entry_completion_get_minimum_key_length (completion));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_entry_completion_finalize (GObject *object)
+{
+ GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (object);
+
+ if (completion->priv->tree_view)
+ gtk_widget_destroy (completion->priv->tree_view);
+
+ if (completion->priv->entry)
+ gtk_entry_set_completion (GTK_ENTRY (completion->priv->entry), NULL);
+
+ if (completion->priv->actions)
+ g_object_unref (completion->priv->actions);
+
+ if (completion->priv->case_normalized_key)
+ g_free (completion->priv->case_normalized_key);
+
+ if (completion->priv->popup_window)
+ gtk_widget_destroy (completion->priv->popup_window);
+}
+
+/* implement cell layout interface */
+static void
+gtk_entry_completion_pack_start (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ gboolean expand)
+{
+ GtkEntryCompletionPrivate *priv;
+
+ g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
+
+ priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
+
+ gtk_tree_view_column_pack_start (priv->column, cell, expand);
+}
+
+static void
+gtk_entry_completion_pack_end (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ gboolean expand)
+{
+ GtkEntryCompletionPrivate *priv;
+
+ g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
+
+ priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
+
+ gtk_tree_view_column_pack_end (priv->column, cell, expand);
+}
+
+static void
+gtk_entry_completion_clear (GtkCellLayout *cell_layout)
+{
+ GtkEntryCompletionPrivate *priv;
+
+ g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
+
+ priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
+
+ gtk_tree_view_column_clear (priv->column);
+}
+
+static void
+gtk_entry_completion_add_attribute (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ const gchar *attribute,
+ gint column)
+{
+ GtkEntryCompletionPrivate *priv;
+
+ g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
+
+ priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
+
+ gtk_tree_view_column_add_attribute (priv->column, cell, attribute, column);
+}
+
+static void
+gtk_entry_completion_set_cell_data_func (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ GtkCellLayoutDataFunc func,
+ gpointer func_data,
+ GDestroyNotify destroy)
+{
+ GtkEntryCompletionPrivate *priv;
+
+ g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
+
+ priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
+
+ gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->column),
+ cell, func, func_data, destroy);
+}
+
+static void
+gtk_entry_completion_clear_attributes (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell)
+{
+ GtkEntryCompletionPrivate *priv;
+
+ g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
+
+ priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
+
+ gtk_tree_view_column_clear_attributes (priv->column, cell);
+}
+
+/* all those callbacks */
+static gboolean
+gtk_entry_completion_default_completion_func (GtkEntryCompletion *completion,
+ const gchar *key,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ gchar *item = NULL;
+ gchar *normalized_string;
+ gchar *case_normalized_string;
+
+ gboolean ret = FALSE;
+
+ GtkTreeModel *model;
+
+ model = gtk_tree_model_filter_get_model (completion->priv->filter_model);
+
+ gtk_tree_model_get (model, iter,
+ completion->priv->text_column, &item,
+ -1);
+
+ normalized_string = g_utf8_normalize (item, -1, G_NORMALIZE_ALL);
+ case_normalized_string = g_utf8_casefold (normalized_string, -1);
+
+ if (!strncmp (key, case_normalized_string, strlen (key)))
+ ret = TRUE;
+
+ g_free (item);
+ g_free (normalized_string);
+ g_free (case_normalized_string);
+
+ return ret;
+}
+
+static gboolean
+gtk_entry_completion_visible_func (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ gboolean ret = FALSE;
+
+ GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (data);
+
+ if (!completion->priv->case_normalized_key)
+ return ret;
+
+ if (completion->priv->text_column >= 0)
+ ret = gtk_entry_completion_default_completion_func (completion,
+ completion->priv->case_normalized_key,
+ iter,
+ NULL);
+
+ else if (completion->priv->match_func)
+ ret = (* completion->priv->match_func) (completion,
+ completion->priv->case_normalized_key,
+ iter,
+ completion->priv->match_data);
+
+ return ret;
+}
+
+static gboolean
+gtk_entry_completion_popup_key_press (GtkWidget *widget,
+ GdkEventKey *event,
+ gpointer user_data)
+{
+ GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
+
+ if (!GTK_WIDGET_MAPPED (completion->priv->popup_window))
+ return FALSE;
+
+ /* propagate event to the entry */
+ gtk_widget_event (completion->priv->entry, (GdkEvent *)event);
+
+ return TRUE;
+}
+
+static gboolean
+gtk_entry_completion_popup_button_press (GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer user_data)
+{
+ GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
+
+ if (!GTK_WIDGET_MAPPED (completion->priv->popup_window))
+ return FALSE;
+
+ /* if we come here, it's usually time to popdown */
+ _gtk_entry_completion_popdown (completion);
+
+ return TRUE;
+}
+
+static gboolean
+gtk_entry_completion_list_button_press (GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer user_data)
+{
+ GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
+ GtkTreePath *path = NULL;
+
+ if (!GTK_WIDGET_MAPPED (completion->priv->popup_window))
+ return FALSE;
+
+ if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
+ event->x, event->y,
+ &path, NULL, NULL, NULL))
+ {
+ gboolean entry_set;
+ GtkTreeIter iter;
+
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (completion->priv->filter_model),
+ &iter, path);
+ gtk_tree_path_free (path);
+
+ g_signal_emit (completion, entry_completion_signals[MATCH_SELECTED],
+ 0, GTK_TREE_MODEL (completion->priv->filter_model),
+ &iter, &entry_set);
+
+ if (!entry_set)
+ {
+ gchar *str = NULL;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (completion->priv->filter_model),
+ &iter,
+ completion->priv->text_column, &str,
+ -1);
+
+ g_signal_handler_block (completion->priv->entry,
+ completion->priv->changed_id);
+ gtk_entry_set_text (GTK_ENTRY (completion->priv->entry), str);
+ g_signal_handler_unblock (completion->priv->entry,
+ completion->priv->changed_id);
+
+ /* move cursor to the end */
+ gtk_editable_set_position (GTK_EDITABLE (completion->priv->entry),
+ -1);
+
+ g_free (str);
+ }
+
+ _gtk_entry_completion_popdown (completion);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gtk_entry_completion_action_button_press (GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer user_data)
+{
+ GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
+ GtkTreePath *path = NULL;
+
+ if (!GTK_WIDGET_MAPPED (completion->priv->popup_window))
+ return FALSE;
+
+ if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
+ event->x, event->y,
+ &path, NULL, NULL, NULL))
+ {
+ g_signal_emit (completion, entry_completion_signals[ACTION_ACTIVATED],
+ 0, gtk_tree_path_get_indices (path)[0]);
+ gtk_tree_path_free (path);
+
+ _gtk_entry_completion_popdown (completion);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+gtk_entry_completion_action_data_func (GtkTreeViewColumn *tree_column,
+ GtkCellRenderer *cell,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ gchar *string = NULL;
+ gboolean markup;
+
+ gtk_tree_model_get (model, iter,
+ 0, &string,
+ 1, &markup,
+ -1);
+
+ if (!string)
+ return;
+
+ if (markup)
+ g_object_set (G_OBJECT (cell),
+ "text", NULL,
+ "markup", string,
+ NULL);
+ else
+ g_object_set (G_OBJECT (cell),
+ "markup", NULL,
+ "text", string,
+ NULL);
+}
+
+/* public API */
+GtkEntryCompletion *
+gtk_entry_completion_new (void)
+{
+ GtkEntryCompletion *completion;
+
+ completion = g_object_new (GTK_TYPE_ENTRY_COMPLETION, NULL);
+
+ return completion;
+}
+
+GtkWidget *
+gtk_entry_completion_get_entry (GtkEntryCompletion *completion)
+{
+ g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), NULL);
+
+ return completion->priv->entry;
+}
+
+void
+gtk_entry_completion_set_model (GtkEntryCompletion *completion,
+ GtkTreeModel *model)
+{
+ g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
+ g_return_if_fail (GTK_IS_TREE_MODEL (model));
+
+ /* code will unref the old filter model (if any) */
+ completion->priv->filter_model =
+ GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (model, NULL));
+ gtk_tree_model_filter_set_visible_func (completion->priv->filter_model,
+ gtk_entry_completion_visible_func,
+ completion,
+ NULL);
+ gtk_tree_view_set_model (GTK_TREE_VIEW (completion->priv->tree_view),
+ GTK_TREE_MODEL (completion->priv->filter_model));
+ g_object_unref (G_OBJECT (completion->priv->filter_model));
+}
+
+GtkTreeModel *
+gtk_entry_completion_get_model (GtkEntryCompletion *completion)
+{
+ g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), NULL);
+
+ return gtk_tree_model_filter_get_model (completion->priv->filter_model);
+}
+
+void
+gtk_entry_completion_set_match_func (GtkEntryCompletion *completion,
+ GtkEntryCompletionMatchFunc func,
+ gpointer func_data,
+ GDestroyNotify func_notify)
+{
+ g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
+
+ if (completion->priv->match_notify)
+ (* completion->priv->match_notify) (completion->priv->match_data);
+
+ completion->priv->match_func = func;
+ completion->priv->match_data = func_data;
+ completion->priv->match_notify = func_notify;
+}
+
+void
+gtk_entry_completion_set_minimum_key_length (GtkEntryCompletion *completion,
+ gint length)
+{
+ g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
+ g_return_if_fail (length >= 1);
+
+ completion->priv->minimum_key_length = length;
+}
+
+gint
+gtk_entry_completion_get_minimum_key_length (GtkEntryCompletion *completion)
+{
+ g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), 0);
+
+ return completion->priv->minimum_key_length;
+}
+
+void
+gtk_entry_completion_complete (GtkEntryCompletion *completion)
+{
+ gchar *tmp;
+
+ g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
+ g_return_if_fail (completion->priv->filter_model != NULL);
+
+ if (completion->priv->case_normalized_key)
+ g_free (completion->priv->case_normalized_key);
+
+ tmp = g_utf8_normalize (gtk_entry_get_text (GTK_ENTRY (completion->priv->entry)),
+ -1, G_NORMALIZE_ALL);
+ completion->priv->case_normalized_key = g_utf8_casefold (tmp, -1);
+ g_free (tmp);
+
+ gtk_tree_model_filter_refilter (completion->priv->filter_model);
+}
+
+static void
+gtk_entry_completion_insert_action (GtkEntryCompletion *completion,
+ gint index,
+ gchar *string,
+ gboolean markup)
+{
+ GtkTreeIter iter;
+
+ gtk_list_store_insert (completion->priv->actions, &iter, index);
+ gtk_list_store_set (completion->priv->actions, &iter,
+ 0, string,
+ 1, markup,
+ -1);
+
+ if (!completion->priv->action_view->parent)
+ {
+ GtkTreePath *path = gtk_tree_path_new_from_indices (0, -1);
+
+ gtk_tree_view_set_cursor (GTK_TREE_VIEW (completion->priv->action_view),
+ path, NULL, FALSE);
+ gtk_tree_path_free (path);
+
+ gtk_box_pack_start (GTK_BOX (completion->priv->vbox),
+ completion->priv->action_view, FALSE, FALSE, 0);
+ gtk_widget_show (completion->priv->action_view);
+ }
+}
+
+void
+gtk_entry_completion_insert_action_text (GtkEntryCompletion *completion,
+ gint index,
+ gchar *text)
+{
+ g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
+ g_return_if_fail (text != NULL);
+
+ gtk_entry_completion_insert_action (completion, index, text, FALSE);
+}
+
+void
+gtk_entry_completion_insert_action_markup (GtkEntryCompletion *completion,
+ gint index,
+ gchar *markup)
+{
+ g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
+ g_return_if_fail (markup != NULL);
+
+ gtk_entry_completion_insert_action (completion, index, markup, TRUE);
+}
+
+void
+gtk_entry_completion_delete_action (GtkEntryCompletion *completion,
+ gint index)
+{
+ GtkTreeIter iter;
+
+ g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
+ g_return_if_fail (index >= 0);
+
+ gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (completion->priv->actions),
+ &iter, NULL, index);
+ gtk_list_store_remove (completion->priv->actions, &iter);
+}
+
+void
+gtk_entry_completion_set_text_column (GtkEntryCompletion *completion,
+ gint column)
+{
+ GtkCellRenderer *cell;
+
+ g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
+ g_return_if_fail (column >= 0);
+
+ completion->priv->text_column = column;
+
+ cell = gtk_cell_renderer_text_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (completion),
+ cell, TRUE);
+ gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (completion),
+ cell,
+ "text", column);
+}
+
+/* private */
+
+/* lame copy from gtkentry.c */
+static void
+get_borders (GtkEntry *entry,
+ gint *xborder,
+ gint *yborder)
+{
+ GtkWidget *widget = GTK_WIDGET (entry);
+ gint focus_width;
+ gboolean interior_focus;
+
+ gtk_widget_style_get (widget,
+ "interior-focus", &interior_focus,
+ "focus-line-width", &focus_width,
+ NULL);
+
+ if (entry->has_frame)
+ {
+ *xborder = widget->style->xthickness;
+ *yborder = widget->style->ythickness;
+ }
+ else
+ {
+ *xborder = 0;
+ *yborder = 0;
+ }
+
+ if (!interior_focus)
+ {
+ *xborder += focus_width;
+ *yborder += focus_width;
+ }
+}
+
+/* this function is a bit nasty */
+void
+_gtk_entry_completion_popup (GtkEntryCompletion *completion)
+{
+ gint x, y, x_border, y_border;
+ gint items;
+ gint height;
+ GtkTreePath *path;
+
+ if (GTK_WIDGET_MAPPED (completion->priv->popup_window))
+ return;
+
+ gtk_widget_show_all (completion->priv->vbox);
+
+ gdk_window_get_origin (completion->priv->entry->window, &x, &y);
+ get_borders (GTK_ENTRY (completion->priv->entry), &x_border, &y_border);
+
+ items = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->filter_model), NULL);
+
+ items = MIN (items, 15);
+
+ gtk_tree_view_column_cell_get_size (completion->priv->column, NULL,
+ NULL, NULL, NULL, &height);
+
+ gtk_widget_set_size_request (completion->priv->tree_view,
+ completion->priv->entry->allocation.width - 2 * x_border,
+ items * height);
+
+ if (items <= 0)
+ gtk_widget_hide (completion->priv->scrolled_window);
+
+ /* default on the first match */
+ path = gtk_tree_path_new_from_indices (0, -1);
+ gtk_tree_view_set_cursor (GTK_TREE_VIEW (completion->priv->tree_view), path,
+ NULL, FALSE);
+ completion->priv->current_selected = 0;
+ gtk_tree_path_free (path);
+
+ items = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->actions), NULL);
+
+ if (items)
+ {
+ gtk_tree_view_column_cell_get_size (gtk_tree_view_get_column (GTK_TREE_VIEW (completion->priv->action_view), 0),
+ NULL, NULL, NULL, NULL,
+ &height);
+
+ gtk_widget_set_size_request (completion->priv->action_view,
+ completion->priv->entry->allocation.width - 2 * x_border,
+ items * height);
+ }
+
+ x += x_border;
+ y += 2 * y_border;
+
+ gtk_window_move (GTK_WINDOW (completion->priv->popup_window), x, y + height);
+
+ gtk_widget_show (completion->priv->popup_window);
+
+ gtk_grab_add (completion->priv->popup_window);
+ gdk_pointer_grab (completion->priv->popup_window->window, TRUE,
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_POINTER_MOTION_MASK,
+ NULL, NULL, GDK_CURRENT_TIME);
+}
+
+void
+_gtk_entry_completion_popdown (GtkEntryCompletion *completion)
+{
+ gdk_pointer_ungrab (GDK_CURRENT_TIME);
+ gtk_grab_remove (completion->priv->popup_window);
+
+ gtk_widget_hide (completion->priv->popup_window);
+}
diff --git a/gtk/gtkentrycompletion.h b/gtk/gtkentrycompletion.h
new file mode 100644
index 000000000..ecf255778
--- /dev/null
+++ b/gtk/gtkentrycompletion.h
@@ -0,0 +1,103 @@
+/* gtkentrycompletion.h
+ * Copyright (C) 2003 Kristian Rietveld <kris@gtk.org>
+ *
+ * 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_ENTRY_COMPLETION_H__
+#define __GTK_ENTRY_COMPLETION_H__
+
+#include <glib-object.h>
+
+#include <gtk/gtktreemodel.h>
+#include <gtk/gtkliststore.h>
+#include <gtk/gtktreeviewcolumn.h>
+#include <gtk/gtktreemodelfilter.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_ENTRY_COMPLETION (gtk_entry_completion_get_type ())
+#define GTK_ENTRY_COMPLETION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_ENTRY_COMPLETION, GtkEntryCompletion))
+#define GTK_ENTRY_COMPLETION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_ENTRY_COMPLETION, GtkEntryCompletionClass))
+#define GTK_IS_ENTRY_COMPLETION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_ENTRY_COMPLETION))
+#define GTK_IS_ENTRY_COMPLETION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_ENTRY_COMPLETION))
+#define GTK_ENTRY_COMPLETION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_ENTRY_COMPLETION, GtkEntryCompletionClass))
+#define GTK_ENTRY_COMPLETION_GET_PRIVATE(obj)(G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_ENTRY_COMPLETION, GtkEntryCompletionPrivate))
+
+typedef struct _GtkEntryCompletion GtkEntryCompletion;
+typedef struct _GtkEntryCompletionClass GtkEntryCompletionClass;
+typedef struct _GtkEntryCompletionPrivate GtkEntryCompletionPrivate;
+
+typedef gboolean (* GtkEntryCompletionMatchFunc) (GtkEntryCompletion *completion,
+ const gchar *key,
+ GtkTreeIter *iter,
+ gpointer user_data);
+
+
+struct _GtkEntryCompletion
+{
+ GObject parent_instance;
+
+ /*< private >*/
+ GtkEntryCompletionPrivate *priv;
+};
+
+struct _GtkEntryCompletionClass
+{
+ GObjectClass parent_class;
+
+ gboolean (* match_selected) (GtkEntryCompletion *completion,
+ GtkTreeModel *model,
+ GtkTreeIter *iter);
+ void (* action_activated) (GtkEntryCompletion *completion,
+ gint index);
+};
+
+/* core */
+GType gtk_entry_completion_get_type (void);
+GtkEntryCompletion *gtk_entry_completion_new (void);
+
+GtkWidget *gtk_entry_completion_get_entry (GtkEntryCompletion *entry);
+
+void gtk_entry_completion_set_model (GtkEntryCompletion *completion,
+ GtkTreeModel *model);
+GtkTreeModel *gtk_entry_completion_get_model (GtkEntryCompletion *completion);
+
+void gtk_entry_completion_set_match_func (GtkEntryCompletion *completion,
+ GtkEntryCompletionMatchFunc func,
+ gpointer func_data,
+ GDestroyNotify func_notify);
+void gtk_entry_completion_set_minimum_key_length (GtkEntryCompletion *completion,
+ gint length);
+gint gtk_entry_completion_get_minimum_key_length (GtkEntryCompletion *completion);
+void gtk_entry_completion_complete (GtkEntryCompletion *completion);
+
+void gtk_entry_completion_insert_action_text (GtkEntryCompletion *completion,
+ gint index,
+ gchar *text);
+void gtk_entry_completion_insert_action_markup (GtkEntryCompletion *completion,
+ gint index,
+ gchar *markup);
+void gtk_entry_completion_delete_action (GtkEntryCompletion *completion,
+ gint index);
+
+/* convenience */
+void gtk_entry_completion_set_text_column (GtkEntryCompletion *completion,
+ gint column);
+
+G_END_DECLS
+
+#endif /* __GTK_ENTRY_COMPLETION_H__ */
diff --git a/gtk/gtkentryprivate.h b/gtk/gtkentryprivate.h
new file mode 100644
index 000000000..741ec3803
--- /dev/null
+++ b/gtk/gtkentryprivate.h
@@ -0,0 +1,65 @@
+/* gtkentryprivate.h
+ * Copyright (C) 2003 Kristian Rietveld <kris@gtk.org>
+ *
+ * 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_ENTRY_PRIVATE_H__
+#define __GTK_ENTRY_PRIVATE_H__
+
+#include <gtk/gtktreeviewcolumn.h>
+#include <gtk/gtktreemodelfilter.h>
+#include <gtk/gtkliststore.h>
+#include <gtk/gtkentrycompletion.h>
+
+G_BEGIN_DECLS
+
+struct _GtkEntryCompletionPrivate
+{
+ GtkWidget *entry;
+
+ GtkWidget *tree_view;
+ GtkTreeViewColumn *column;
+ GtkTreeModelFilter *filter_model;
+ GtkListStore *actions;
+
+ GtkEntryCompletionMatchFunc match_func;
+ gpointer match_data;
+ GDestroyNotify match_notify;
+
+ gint minimum_key_length;
+ gint text_column;
+ gint current_selected;
+
+ gchar *case_normalized_key;
+
+ /* only used by GtkEntry when attached: */
+ GtkWidget *popup_window;
+ GtkWidget *vbox;
+ GtkWidget *scrolled_window;
+ GtkWidget *action_view;
+
+ gulong completion_timeout;
+ gulong changed_id;
+ gulong key_press_id;
+};
+
+void _gtk_entry_completion_popup (GtkEntryCompletion *completion);
+void _gtk_entry_completion_popdown (GtkEntryCompletion *completion);
+
+G_END_DECLS
+
+#endif /* __GTK_ENTRY_PRIVATE_H__ */
diff --git a/gtk/gtkmarshalers.list b/gtk/gtkmarshalers.list
index c474a4a68..601139e44 100644
--- a/gtk/gtkmarshalers.list
+++ b/gtk/gtkmarshalers.list
@@ -28,6 +28,7 @@ BOOLEAN:ENUM,INT
BOOLEAN:OBJECT,UINT,FLAGS
BOOLEAN:OBJECT,INT,INT,UINT
BOOLEAN:OBJECT,STRING,STRING,BOXED
+BOOLEAN:OBJECT,BOXED
BOOLEAN:OBJECT,BOXED,BOXED
BOOLEAN:OBJECT,STRING,STRING
BOOLEAN:INT,INT
diff --git a/gtk/gtktreemodelfilter.c b/gtk/gtktreemodelfilter.c
new file mode 100644
index 000000000..3e5d52ce8
--- /dev/null
+++ b/gtk/gtktreemodelfilter.c
@@ -0,0 +1,2746 @@
+/* gtktreemodelfilter.c
+ * Copyright (C) 2000,2001 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ * Copyright (C) 2001-2003 Kristian Rietveld <kris@gtk.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gtktreemodelfilter.h"
+#include <gtk/gtkintl.h>
+#include <string.h>
+
+/* ITER FORMAT:
+ *
+ * iter->stamp = filter->priv->stamp
+ * iter->user_data = FilterLevel
+ * iter->user_data2 = FilterElt
+ */
+
+/* all paths, iters, etc prefixed with c_ are paths, iters, etc relative to the
+ * child model.
+ */
+
+typedef struct _FilterElt FilterElt;
+typedef struct _FilterLevel FilterLevel;
+
+struct _FilterElt
+{
+ GtkTreeIter iter;
+ FilterLevel *children;
+ gint offset;
+ gint ref_count;
+ gint zero_ref_count;
+ gboolean visible;
+};
+
+struct _FilterLevel
+{
+ GArray *array;
+ gint ref_count;
+
+ FilterElt *parent_elt;
+ FilterLevel *parent_level;
+};
+
+struct _GtkTreeModelFilterPrivate
+{
+ gpointer root;
+ gint stamp;
+ guint child_flags;
+ GtkTreeModel *child_model;
+ gint zero_ref_count;
+
+ guint root_level_visible;
+
+ GtkTreePath *virtual_root;
+
+ GtkTreeModelFilterVisibleFunc visible_func;
+ gpointer visible_data;
+ GtkDestroyNotify visible_destroy;
+
+ gint modify_n_columns;
+ GType *modify_types;
+ GtkTreeModelFilterModifyFunc modify_func;
+ gpointer modify_data;
+ gpointer modify_destroy;
+
+ gint visible_column;
+
+ gboolean visible_method_set;
+ gboolean modify_func_set;
+
+ /* signal ids */
+ guint changed_id;
+ guint inserted_id;
+ guint has_child_toggled_id;
+ guint deleted_id;
+ guint reordered_id;
+};
+
+/* properties */
+enum
+{
+ PROP_0,
+ PROP_CHILD_MODEL,
+ PROP_VIRTUAL_ROOT
+};
+
+#define GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS(filter) \
+ (((GtkTreeModelFilter *)filter)->priv->child_flags & GTK_TREE_MODEL_ITERS_PERSIST)
+
+#define FILTER_ELT(filter_elt) ((FilterElt *)filter_elt)
+#define FILTER_LEVEL(filter_level) ((FilterLevel *)filter_level)
+
+/* general code (object/interface init, properties, etc) */
+static void gtk_tree_model_filter_init (GtkTreeModelFilter *filter);
+static void gtk_tree_model_filter_class_init (GtkTreeModelFilterClass *filter_class);
+static void gtk_tree_model_filter_tree_model_init (GtkTreeModelIface *iface);
+static void gtk_tree_model_filter_finalize (GObject *object);
+static void gtk_tree_model_filter_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gtk_tree_model_filter_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+/* signal handlers */
+static void gtk_tree_model_filter_row_changed (GtkTreeModel *c_model,
+ GtkTreePath *c_path,
+ GtkTreeIter *c_iter,
+ gpointer data);
+static void gtk_tree_model_filter_row_inserted (GtkTreeModel *c_model,
+ GtkTreePath *c_path,
+ GtkTreeIter *c_iter,
+ gpointer data);
+static void gtk_tree_model_filter_row_has_child_toggled (GtkTreeModel *c_model,
+ GtkTreePath *c_path,
+ GtkTreeIter *c_iter,
+ gpointer data);
+static void gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model,
+ GtkTreePath *c_path,
+ gpointer data);
+static void gtk_tree_model_filter_rows_reordered (GtkTreeModel *c_model,
+ GtkTreePath *c_path,
+ GtkTreeIter *c_iter,
+ gint *new_order,
+ gpointer data);
+
+/* GtkTreeModel interface */
+static guint gtk_tree_model_filter_get_flags (GtkTreeModel *model);
+static gint gtk_tree_model_filter_get_n_columns (GtkTreeModel *model);
+static GType gtk_tree_model_filter_get_column_type (GtkTreeModel *model,
+ gint index);
+static gboolean gtk_tree_model_filter_get_iter (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ GtkTreePath *path);
+static GtkTreePath *gtk_tree_model_filter_get_path (GtkTreeModel *model,
+ GtkTreeIter *iter);
+static void gtk_tree_model_filter_get_value (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gint column,
+ GValue *value);
+static gboolean gtk_tree_model_filter_iter_next (GtkTreeModel *model,
+ GtkTreeIter *iter);
+static gboolean gtk_tree_model_filter_iter_children (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent);
+static gboolean gtk_tree_model_filter_iter_has_child (GtkTreeModel *model,
+ GtkTreeIter *iter);
+static gint gtk_tree_model_filter_iter_n_children (GtkTreeModel *model,
+ GtkTreeIter *iter);
+static gboolean gtk_tree_model_filter_iter_nth_child (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ gint n);
+static gboolean gtk_tree_model_filter_iter_parent (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ GtkTreeIter *child);
+static void gtk_tree_model_filter_ref_node (GtkTreeModel *model,
+ GtkTreeIter *iter);
+static void gtk_tree_model_filter_unref_node (GtkTreeModel *model,
+ GtkTreeIter *iter);
+
+
+/* private functions */
+static void gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter,
+ FilterLevel *parent_level,
+ FilterElt *parent_elt);
+static void gtk_tree_model_filter_free_level (GtkTreeModelFilter *filter,
+ FilterLevel *filter_level);
+
+static GtkTreePath *gtk_tree_model_filter_elt_get_path (FilterLevel *level,
+ FilterElt *elt,
+ GtkTreePath *root);
+
+static GtkTreePath *gtk_tree_model_filter_add_root (GtkTreePath *src,
+ GtkTreePath *root);
+static GtkTreePath *gtk_tree_model_filter_remove_root (GtkTreePath *src,
+ GtkTreePath *root);
+
+static void gtk_tree_model_filter_increment_stamp (GtkTreeModelFilter *filter);
+
+static gboolean gtk_tree_model_filter_visible (GtkTreeModelFilter *filter,
+ GtkTreeIter *child_iter);
+static void gtk_tree_model_filter_clear_cache_helper (GtkTreeModelFilter *filter,
+ FilterLevel *level);
+
+static void gtk_tree_model_filter_real_unref_node (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gboolean propagate_unref);
+
+static void gtk_tree_model_filter_set_model (GtkTreeModelFilter *filter,
+ GtkTreeModel *child_model);
+static void gtk_tree_model_filter_set_root (GtkTreeModelFilter *filter,
+ GtkTreePath *root);
+
+static GtkTreePath *gtk_real_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filter,
+ GtkTreePath *child_path,
+ gboolean build_levels,
+ gboolean fetch_childs);
+
+static FilterElt *gtk_tree_model_filter_fetch_child (GtkTreeModelFilter *filter,
+ FilterLevel *level,
+ gint offset,
+ gint *index);
+static void gtk_tree_model_filter_remove_node (GtkTreeModelFilter *filter,
+ GtkTreeIter *iter,
+ gboolean emit_signal);
+static void gtk_tree_model_filter_update_childs (GtkTreeModelFilter *filter,
+ FilterLevel *level,
+ FilterElt *elt);
+static FilterElt *bsearch_elt_with_offset (GArray *array,
+ gint offset,
+ gint *index);
+
+
+static GObjectClass *parent_class = NULL;
+
+GType
+gtk_tree_model_filter_get_type (void)
+{
+ static GType tree_model_filter_type = 0;
+
+ if (!tree_model_filter_type)
+ {
+ static const GTypeInfo tree_model_filter_info =
+ {
+ sizeof (GtkTreeModelFilterClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) gtk_tree_model_filter_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GtkTreeModelFilter),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gtk_tree_model_filter_init
+ };
+
+ static const GInterfaceInfo tree_model_info =
+ {
+ (GInterfaceInitFunc) gtk_tree_model_filter_tree_model_init,
+ NULL,
+ NULL
+ };
+
+ tree_model_filter_type = g_type_register_static (G_TYPE_OBJECT,
+ "GtkTreeModelFilter",
+ &tree_model_filter_info, 0);
+
+ g_type_add_interface_static (tree_model_filter_type,
+ GTK_TYPE_TREE_MODEL,
+ &tree_model_info);
+ }
+
+ return tree_model_filter_type;
+}
+
+static void
+gtk_tree_model_filter_init (GtkTreeModelFilter *filter)
+{
+ filter->priv = GTK_TREE_MODEL_FILTER_GET_PRIVATE (filter);
+
+ filter->priv->visible_column = -1;
+ filter->priv->zero_ref_count = 0;
+ filter->priv->visible_method_set = FALSE;
+ filter->priv->modify_func_set = FALSE;
+}
+
+static void
+gtk_tree_model_filter_class_init (GtkTreeModelFilterClass *filter_class)
+{
+ GObjectClass *object_class;
+
+ object_class = (GObjectClass *) filter_class;
+ parent_class = g_type_class_peek_parent (filter_class);
+
+ object_class->set_property = gtk_tree_model_filter_set_property;
+ object_class->get_property = gtk_tree_model_filter_get_property;
+
+ object_class->finalize = gtk_tree_model_filter_finalize;
+
+ /* Properties -- FIXME: disabled translations for now, until I can come up with a
+ * better description
+ */
+ g_object_class_install_property (object_class,
+ PROP_CHILD_MODEL,
+ g_param_spec_object ("child_model",
+ ("The child model"),
+ ("The model for the filtermodel to filter"),
+ GTK_TYPE_TREE_MODEL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (object_class,
+ PROP_VIRTUAL_ROOT,
+ g_param_spec_boxed ("virtual_root",
+ ("The virtual root"),
+ ("The virtual root (relative to the child model) for this filtermodel"),
+ GTK_TYPE_TREE_PATH,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_type_class_add_private (object_class, sizeof (GtkTreeModelFilterPrivate));
+}
+
+static void
+gtk_tree_model_filter_tree_model_init (GtkTreeModelIface *iface)
+{
+ iface->get_flags = gtk_tree_model_filter_get_flags;
+ iface->get_n_columns = gtk_tree_model_filter_get_n_columns;
+ iface->get_column_type = gtk_tree_model_filter_get_column_type;
+ iface->get_iter = gtk_tree_model_filter_get_iter;
+ iface->get_path = gtk_tree_model_filter_get_path;
+ iface->get_value = gtk_tree_model_filter_get_value;
+ iface->iter_next = gtk_tree_model_filter_iter_next;
+ iface->iter_children = gtk_tree_model_filter_iter_children;
+ iface->iter_has_child = gtk_tree_model_filter_iter_has_child;
+ iface->iter_n_children = gtk_tree_model_filter_iter_n_children;
+ iface->iter_nth_child = gtk_tree_model_filter_iter_nth_child;
+ iface->iter_parent = gtk_tree_model_filter_iter_parent;
+ iface->ref_node = gtk_tree_model_filter_ref_node;
+ iface->unref_node = gtk_tree_model_filter_unref_node;
+}
+
+
+static void
+gtk_tree_model_filter_finalize (GObject *object)
+{
+ GtkTreeModelFilter *filter = (GtkTreeModelFilter *) object;
+
+ gtk_tree_model_filter_set_model (filter, NULL);
+
+ if (filter->priv->virtual_root)
+ gtk_tree_path_free (filter->priv->virtual_root);
+
+ if (filter->priv->root)
+ gtk_tree_model_filter_free_level (filter, filter->priv->root);
+
+ if (filter->priv->modify_types)
+ g_free (filter->priv->modify_types);
+
+ /* must chain up */
+ parent_class->finalize (object);
+}
+
+static void
+gtk_tree_model_filter_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (object);
+
+ switch (prop_id)
+ {
+ case PROP_CHILD_MODEL:
+ gtk_tree_model_filter_set_model (filter, g_value_get_object (value));
+ break;
+ case PROP_VIRTUAL_ROOT:
+ gtk_tree_model_filter_set_root (filter, g_value_get_boxed (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_tree_model_filter_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (object);
+
+ switch (prop_id)
+ {
+ case PROP_CHILD_MODEL:
+ g_value_set_object (value, filter->priv->child_model);
+ break;
+ case PROP_VIRTUAL_ROOT:
+ g_value_set_boxed (value, filter->priv->virtual_root);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/* helpers */
+
+static void
+gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter,
+ FilterLevel *parent_level,
+ FilterElt *parent_elt)
+{
+ GtkTreeIter iter;
+ GtkTreeIter root;
+ FilterLevel *new_level;
+ gint length = 0;
+ gint i;
+
+ g_assert (filter->priv->child_model != NULL);
+
+ if (!parent_level)
+ {
+ if (filter->priv->virtual_root)
+ {
+ if (gtk_tree_model_get_iter (filter->priv->child_model, &root, filter->priv->virtual_root) == FALSE)
+ return;
+ length = gtk_tree_model_iter_n_children (filter->priv->child_model, &root);
+
+ if (gtk_tree_model_iter_children (filter->priv->child_model, &iter, &root) == FALSE)
+ return;
+ }
+ else
+ {
+ if (!gtk_tree_model_get_iter_first (filter->priv->child_model, &iter))
+ return;
+ length = gtk_tree_model_iter_n_children (filter->priv->child_model, NULL);
+ }
+ }
+ else
+ {
+ GtkTreeIter parent_iter;
+ GtkTreeIter child_parent_iter;
+
+ parent_iter.stamp = filter->priv->stamp;
+ parent_iter.user_data = parent_level;
+ parent_iter.user_data2 = parent_elt;
+
+ gtk_tree_model_filter_convert_iter_to_child_iter (filter,
+ &child_parent_iter,
+ &parent_iter);
+ if (gtk_tree_model_iter_children (filter->priv->child_model, &iter, &child_parent_iter) == FALSE)
+ return;
+
+ /* stamp may have changed */
+ gtk_tree_model_filter_convert_iter_to_child_iter (filter,
+ &child_parent_iter,
+ &parent_iter);
+ length = gtk_tree_model_iter_n_children (filter->priv->child_model, &child_parent_iter);
+ }
+
+ g_return_if_fail (length > 0);
+
+ new_level = g_new (FilterLevel, 1);
+ new_level->array = g_array_sized_new (FALSE, FALSE,
+ sizeof (FilterElt),
+ length);
+ new_level->ref_count = 0;
+ new_level->parent_elt = parent_elt;
+ new_level->parent_level = parent_level;
+
+ if (parent_elt)
+ parent_elt->children = new_level;
+ else
+ filter->priv->root = new_level;
+
+ /* increase the count of zero ref_counts */
+ while (parent_level)
+ {
+ parent_elt->zero_ref_count++;
+
+ parent_elt = parent_level->parent_elt;
+ parent_level = parent_level->parent_level;
+ }
+ filter->priv->zero_ref_count++;
+
+ i = 0;
+
+ if (!new_level->parent_level)
+ filter->priv->root_level_visible = 0;
+
+ do
+ {
+ if (gtk_tree_model_filter_visible (filter, &iter))
+ {
+ FilterElt filter_elt;
+
+ filter_elt.offset = i;
+ filter_elt.zero_ref_count = 0;
+ filter_elt.ref_count = 0;
+ filter_elt.children = NULL;
+ filter_elt.visible = TRUE;
+
+ if (GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS (filter))
+ filter_elt.iter = iter;
+
+ g_array_append_val (new_level->array, filter_elt);
+
+ if (!new_level->parent_level)
+ filter->priv->root_level_visible++;
+ }
+ i++;
+ }
+ while (gtk_tree_model_iter_next (filter->priv->child_model, &iter));
+}
+
+static void
+gtk_tree_model_filter_free_level (GtkTreeModelFilter *filter,
+ FilterLevel *filter_level)
+{
+ gint i;
+
+ g_assert (filter_level);
+
+ if (filter_level->ref_count == 0)
+ {
+ FilterLevel *parent_level = filter_level->parent_level;
+ FilterElt *parent_elt = filter_level->parent_elt;
+
+ do
+ {
+ if (parent_elt)
+ parent_elt->zero_ref_count--;
+
+ if (parent_level)
+ {
+ parent_elt = parent_level->parent_elt;
+ parent_level = parent_level->parent_level;
+ }
+ }
+ while (parent_level);
+ filter->priv->zero_ref_count--;
+ }
+
+ for (i = 0; i < filter_level->array->len; i++)
+ {
+ if (g_array_index (filter_level->array, FilterElt, i).children)
+ gtk_tree_model_filter_free_level (filter,
+ FILTER_LEVEL (g_array_index (filter_level->array, FilterElt, i).children));
+ }
+
+ if (!filter_level->parent_level)
+ filter->priv->root_level_visible = 0;
+
+ if (filter_level->parent_elt)
+ filter_level->parent_elt->children = NULL;
+ else
+ filter->priv->root = NULL;
+
+ g_array_free (filter_level->array, TRUE);
+ filter_level->array = NULL;
+
+ g_free (filter_level);
+ filter_level = NULL;
+}
+
+static GtkTreePath *
+gtk_tree_model_filter_elt_get_path (FilterLevel *level,
+ FilterElt *elt,
+ GtkTreePath *root)
+{
+ FilterLevel *walker = level;
+ FilterElt *walker2 = elt;
+ GtkTreePath *path;
+ GtkTreePath *real_path;
+
+ g_return_val_if_fail (level != NULL, NULL);
+ g_return_val_if_fail (elt != NULL, NULL);
+
+ path = gtk_tree_path_new ();
+
+ while (walker)
+ {
+ gtk_tree_path_prepend_index (path, walker2->offset);
+
+ walker2 = walker->parent_elt;
+ walker = walker->parent_level;
+ }
+
+ if (root)
+ {
+ real_path = gtk_tree_path_copy (root);
+
+ gtk_tree_model_filter_add_root (real_path, path);
+ gtk_tree_path_free (path);
+ return real_path;
+ }
+
+ return path;
+}
+
+static GtkTreePath *
+gtk_tree_model_filter_add_root (GtkTreePath *src,
+ GtkTreePath *root)
+{
+ GtkTreePath *retval;
+ gint i;
+
+ retval = gtk_tree_path_copy (root);
+
+ for (i = 0; i < gtk_tree_path_get_depth (src); i++)
+ gtk_tree_path_append_index (retval, gtk_tree_path_get_indices (src)[i]);
+
+ return retval;
+}
+
+static GtkTreePath *
+gtk_tree_model_filter_remove_root (GtkTreePath *src,
+ GtkTreePath *root)
+{
+ GtkTreePath *retval;
+ gint i;
+ gint depth;
+ gint *indices;
+
+ if (gtk_tree_path_get_depth (src) <= gtk_tree_path_get_depth (root))
+ return NULL;
+
+ depth = gtk_tree_path_get_depth (src);
+ indices = gtk_tree_path_get_indices (src);
+
+ for (i = 0; i < gtk_tree_path_get_depth (root); i++)
+ if (indices[i] != gtk_tree_path_get_indices (root)[i])
+ return NULL;
+
+ retval = gtk_tree_path_new ();
+
+ for (; i < depth; i++)
+ gtk_tree_path_append_index (retval, indices[i]);
+
+ return retval;
+}
+
+static void
+gtk_tree_model_filter_increment_stamp (GtkTreeModelFilter *filter)
+{
+ do
+ {
+ filter->priv->stamp++;
+ }
+ while (filter->priv->stamp == 0);
+
+ gtk_tree_model_filter_clear_cache (filter);
+}
+
+static gboolean
+gtk_tree_model_filter_visible (GtkTreeModelFilter *filter,
+ GtkTreeIter *child_iter)
+{
+ if (filter->priv->visible_func)
+ {
+ return (filter->priv->visible_func (filter->priv->child_model,
+ child_iter,
+ filter->priv->visible_data));
+ }
+ else if (filter->priv->visible_column >= 0)
+ {
+ GValue val = {0, };
+
+ gtk_tree_model_get_value (filter->priv->child_model, child_iter,
+ filter->priv->visible_column, &val);
+
+ if (g_value_get_boolean (&val))
+ {
+ g_value_unset (&val);
+ return TRUE;
+ }
+
+ g_value_unset (&val);
+ return FALSE;
+ }
+
+ /* no filter thing set, so always visible */
+ return TRUE;
+}
+
+static void
+gtk_tree_model_filter_clear_cache_helper (GtkTreeModelFilter *filter,
+ FilterLevel *level)
+{
+ gint i;
+
+ g_assert (level);
+
+ for (i = 0; i < level->array->len; i++)
+ {
+ if (g_array_index (level->array, FilterElt, i).zero_ref_count > 0)
+ gtk_tree_model_filter_clear_cache_helper (filter, g_array_index (level->array, FilterElt, i).children);
+ }
+
+ if (level->ref_count == 0 && level != filter->priv->root)
+ {
+ gtk_tree_model_filter_free_level (filter, level);
+ return;
+ }
+}
+
+static FilterElt *
+gtk_tree_model_filter_fetch_child (GtkTreeModelFilter *filter,
+ FilterLevel *level,
+ gint offset,
+ gint *index)
+{
+ gint i = 0;
+ gint start, middle, end;
+ gint len;
+ GtkTreePath *c_path = NULL;
+ GtkTreeIter c_iter;
+ GtkTreePath *c_parent_path = NULL;
+ GtkTreeIter c_parent_iter;
+ FilterElt elt;
+
+ /* check if child exists and is visible */
+ if (level->parent_elt)
+ {
+ c_parent_path =
+ gtk_tree_model_filter_elt_get_path (level->parent_level,
+ level->parent_elt,
+ filter->priv->virtual_root);
+ if (!c_parent_path)
+ return NULL;
+ }
+ else
+ {
+ if (filter->priv->virtual_root)
+ c_parent_path = gtk_tree_path_copy (filter->priv->virtual_root);
+ else
+ c_parent_path = NULL;
+ }
+
+ if (c_parent_path)
+ {
+ gtk_tree_model_get_iter (filter->priv->child_model,
+ &c_parent_iter,
+ c_parent_path);
+ len = gtk_tree_model_iter_n_children (filter->priv->child_model,
+ &c_parent_iter);
+
+ c_path = gtk_tree_path_copy (c_parent_path);
+ gtk_tree_path_free (c_parent_path);
+ }
+ else
+ {
+ len = gtk_tree_model_iter_n_children (filter->priv->child_model, NULL);
+ c_path = gtk_tree_path_new ();
+ }
+
+ gtk_tree_path_append_index (c_path, offset);
+ gtk_tree_model_get_iter (filter->priv->child_model, &c_iter, c_path);
+ gtk_tree_path_free (c_path);
+
+ if (offset >= len || !gtk_tree_model_filter_visible (filter, &c_iter))
+ return NULL;
+
+ /* add child */
+ elt.offset = offset;
+ elt.zero_ref_count = 0;
+ elt.ref_count = 0;
+ elt.children = NULL;
+ /* visibility should be FALSE as we don't emit row_inserted */
+ elt.visible = FALSE;
+
+ if (GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS (filter))
+ elt.iter = c_iter;
+
+ /* find index (binary search on offset) */
+ start = 0;
+ end = level->array->len;
+
+ if (start != end)
+ {
+ while (start != end)
+ {
+ middle = (start + end) / 2;
+
+ if (g_array_index (level->array, FilterElt, middle).offset <= offset)
+ start = middle + 1;
+ else
+ end = middle;
+ }
+
+ if (g_array_index (level->array, FilterElt, middle).offset <= offset)
+ i = middle + 1;
+ else
+ i = middle;
+ }
+ else
+ i = 0;
+
+ g_array_insert_val (level->array, i, elt);
+ *index = i;
+
+ for (i = MAX (--i, 0); i < level->array->len; i++)
+ {
+ FilterElt *e = &(g_array_index (level->array, FilterElt, i));
+ if (e->children)
+ e->children->parent_elt = e;
+ }
+
+ return &g_array_index (level->array, FilterElt, *index);
+}
+
+static void
+gtk_tree_model_filter_remove_node (GtkTreeModelFilter *filter,
+ GtkTreeIter *iter,
+ gboolean emit_signal)
+{
+ FilterElt *elt, *parent;
+ FilterLevel *level, *parent_level;
+ gint offset, i, length, level_refcount;
+
+ /* FIXME: this function is very ugly. I need to rethink and
+ * rewrite it someday.
+ */
+
+ level = FILTER_LEVEL (iter->user_data);
+ elt = FILTER_ELT (iter->user_data2);
+
+ parent = level->parent_elt;
+ parent_level = level->parent_level;
+ length = level->array->len;
+ offset = elt->offset;
+
+ /* ref counting */
+ while (elt->ref_count > 0)
+ gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter),
+ iter, FALSE);
+
+ level_refcount = level->ref_count;
+
+ /* do the ref counting first! this touches the stamp */
+ if (emit_signal)
+ {
+ GtkTreePath *path;
+
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), iter);
+ gtk_tree_model_filter_increment_stamp (filter);
+ gtk_tree_model_row_deleted (GTK_TREE_MODEL (filter), path);
+ gtk_tree_path_free (path);
+ }
+
+ if ((length == 1 || level_refcount == 0) &&
+ emit_signal && iter->user_data != filter->priv->root)
+ {
+ /* above code destroyed the level */
+ goto emit_has_child_toggled;
+ }
+
+ if (length == 1)
+ {
+ /* kill the level */
+ gtk_tree_model_filter_free_level (filter, level);
+
+ if (!filter->priv->root)
+ /* we killed the root */
+ return;
+ }
+ else
+ {
+ FilterElt *tmp;
+
+ /* remove the node */
+ tmp = bsearch_elt_with_offset (level->array, elt->offset, &i);
+
+ if (tmp)
+ {
+ g_array_remove_index (level->array, i);
+
+ for (i = MAX (--i, 0); i < level->array->len; i++)
+ {
+ /* NOTE: here we do *not* decrease offsets, because the node was
+ * not removed from the child model
+ */
+ elt = &g_array_index (level->array, FilterElt, i);
+ if (elt->children)
+ elt->children->parent_elt = elt;
+ }
+ }
+ }
+
+emit_has_child_toggled:
+ /* children are being handled first, so we can check it this way
+ *
+ * yes this if-statement is ugly
+ */
+ if ((parent && parent->children && parent->children->array->len <= 1) ||
+ (length == 1 && emit_signal && iter->user_data != filter->priv->root))
+ {
+ /* latest child has been removed, level has been destroyed */
+ GtkTreeIter piter;
+ GtkTreePath *ppath;
+
+ piter.stamp = filter->priv->stamp;
+ piter.user_data = parent_level;
+ piter.user_data2 = parent;
+
+ ppath = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &piter);
+
+ gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (filter),
+ ppath, &piter);
+ gtk_tree_path_free (ppath);
+ }
+}
+
+static void
+gtk_tree_model_filter_update_childs (GtkTreeModelFilter *filter,
+ FilterLevel *level,
+ FilterElt *elt)
+{
+ GtkTreeIter c_iter;
+ GtkTreeIter iter;
+
+ if (!elt->visible)
+ return;
+
+ iter.stamp = filter->priv->stamp;
+ iter.user_data = level;
+ iter.user_data2 = elt;
+
+ gtk_tree_model_filter_convert_iter_to_child_iter (filter, &c_iter, &iter);
+
+ if (gtk_tree_model_iter_has_child (filter->priv->child_model, &c_iter))
+ {
+ GtkTreePath *path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter),
+ &iter);
+ gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (filter),
+ path,
+ &iter);
+ if (path)
+ gtk_tree_path_free (path);
+ }
+}
+
+static FilterElt *
+bsearch_elt_with_offset (GArray *array,
+ gint offset,
+ gint *index)
+{
+ gint start, middle, end;
+ FilterElt *elt;
+
+ start = 0;
+ end = array->len;
+
+ if (array->len < 1)
+ return NULL;
+
+ if (start == end)
+ {
+ elt = &g_array_index (array, FilterElt, 0);
+
+ if (elt->offset == offset)
+ {
+ *index = 0;
+ return elt;
+ }
+ else
+ return NULL;
+ }
+
+ while (start != end)
+ {
+ middle = (start + end) / 2;
+
+ elt = &g_array_index (array, FilterElt, middle);
+
+ if (elt->offset < offset)
+ start = middle + 1;
+ else if (elt->offset > offset)
+ end = middle;
+ else
+ break;
+ }
+
+ if (elt->offset == offset)
+ {
+ *index = middle;
+ return elt;
+ }
+
+ return NULL;
+}
+
+/* TreeModel signals */
+static void
+gtk_tree_model_filter_row_changed (GtkTreeModel *c_model,
+ GtkTreePath *c_path,
+ GtkTreeIter *c_iter,
+ gpointer data)
+{
+ GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data);
+ GtkTreeIter iter;
+ GtkTreeIter childs;
+ GtkTreeIter real_c_iter;
+ GtkTreePath *path = NULL;
+
+ FilterElt *elt;
+ FilterLevel *level;
+
+ gboolean requested_state;
+ gboolean current_state;
+ gboolean free_c_path = FALSE;
+
+ g_return_if_fail (c_path != NULL || c_iter != NULL);
+
+ if (!c_path)
+ {
+ c_path = gtk_tree_model_get_path (c_model, c_iter);
+ free_c_path = TRUE;
+ }
+
+ if (c_iter)
+ real_c_iter = *c_iter;
+ else
+ gtk_tree_model_get_iter (c_model, &real_c_iter, c_path);
+
+ /* what's the requested state? */
+ requested_state = gtk_tree_model_filter_visible (filter, &real_c_iter);
+
+ /* now, let's see whether the item is there */
+ path = gtk_real_tree_model_filter_convert_child_path_to_path (filter,
+ c_path,
+ FALSE,
+ FALSE);
+
+ if (path)
+ {
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), &iter, path);
+ current_state = FILTER_ELT (iter.user_data2)->visible;
+ }
+ else
+ current_state = FALSE;
+
+ if (current_state == FALSE && requested_state == FALSE)
+ /* no changes required */
+ goto done;
+
+ if (current_state == TRUE && requested_state == FALSE)
+ {
+ /* get rid of this node */
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), &iter, path);
+ gtk_tree_model_filter_remove_node (filter, &iter, TRUE);
+
+ level = FILTER_LEVEL (iter.user_data);
+
+ if (!level->parent_level)
+ filter->priv->root_level_visible--;
+
+ goto done;
+ }
+
+ if (current_state == TRUE && requested_state == TRUE)
+ {
+ /* progate the signal */
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), &iter, path);
+ gtk_tree_model_row_changed (GTK_TREE_MODEL (filter), path, &iter);
+
+ level = FILTER_LEVEL (iter.user_data);
+ elt = FILTER_ELT (iter.user_data2);
+
+ /* and update the childs */
+ if (gtk_tree_model_iter_children (c_model, &childs, &real_c_iter))
+ gtk_tree_model_filter_update_childs (filter, level, elt);
+
+ goto done;
+ }
+
+ /* only current == FALSE and requested == TRUE is left,
+ * pull in the child
+ */
+ g_return_if_fail (current_state == FALSE && requested_state == TRUE);
+
+ /* make sure the new item has been pulled in */
+ if (!filter->priv->root)
+ {
+ gint i;
+ FilterLevel *root;
+
+ gtk_tree_model_filter_build_level (filter, NULL, NULL);
+
+ root = FILTER_LEVEL (filter->priv->root);
+
+ if (root)
+ {
+ for (i = 0; i < root->array->len; i++)
+ g_array_index (root->array, FilterElt, i).visible = FALSE;
+ filter->priv->root_level_visible = 0;
+ }
+ }
+
+ if (!path)
+ path = gtk_real_tree_model_filter_convert_child_path_to_path (filter,
+ c_path,
+ FALSE,
+ TRUE);
+
+ g_return_if_fail (path != NULL);
+
+ gtk_tree_model_filter_increment_stamp (filter);
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), &iter, path);
+
+ level = FILTER_LEVEL (iter.user_data);
+ elt = FILTER_ELT (iter.user_data2);
+
+ elt->visible = TRUE;
+
+ if (!level->parent_level)
+ filter->priv->root_level_visible++;
+
+ /* update stamp */
+ gtk_tree_model_row_inserted (GTK_TREE_MODEL (filter), path, &iter);
+
+ if (gtk_tree_model_iter_children (c_model, &childs, c_iter))
+ gtk_tree_model_filter_update_childs (filter, level, elt);
+
+done:
+ if (path)
+ gtk_tree_path_free (path);
+
+ if (free_c_path)
+ gtk_tree_path_free (c_path);
+}
+
+static void
+gtk_tree_model_filter_row_inserted (GtkTreeModel *c_model,
+ GtkTreePath *c_path,
+ GtkTreeIter *c_iter,
+ gpointer data)
+{
+ GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data);
+ GtkTreePath *path;
+ GtkTreePath *real_path;
+ GtkTreeIter iter;
+
+ GtkTreeIter real_c_iter;
+
+ FilterElt *elt;
+ FilterLevel *level;
+ FilterLevel *parent_level;
+
+ gint i = 0, offset, index = -1;
+
+ gboolean free_c_path = FALSE;
+
+ g_return_if_fail (c_path != NULL || c_iter != NULL);
+
+ if (!c_path)
+ {
+ c_path = gtk_tree_model_get_path (c_model, c_iter);
+ free_c_path = TRUE;
+ }
+
+ if (c_iter)
+ real_c_iter = *c_iter;
+ else
+ gtk_tree_model_get_iter (c_model, &real_c_iter, c_path);
+
+ /* the row has already been inserted. so we need to fixup the
+ * virtual root here first
+ */
+ if (filter->priv->virtual_root)
+ {
+ if (gtk_tree_path_get_depth (filter->priv->virtual_root) >=
+ gtk_tree_path_get_depth (c_path))
+ {
+ gint level;
+ gint *v_indices, *c_indices;
+
+ level = gtk_tree_path_get_depth (c_path) - 1;
+ v_indices = gtk_tree_path_get_indices (filter->priv->virtual_root);
+ c_indices = gtk_tree_path_get_indices (c_path);
+
+ if (v_indices[level] >= c_indices[level])
+ (v_indices[level])++;
+ }
+ }
+
+ if (!filter->priv->root)
+ {
+ gtk_tree_model_filter_build_level (filter, NULL, NULL);
+ /* that already put the inserted iter in the level */
+
+ goto done_and_emit;
+ }
+
+ parent_level = level = FILTER_LEVEL (filter->priv->root);
+
+ /* subtract virtual root if necessary */
+ if (filter->priv->virtual_root)
+ {
+ real_path = gtk_tree_model_filter_remove_root (c_path,
+ filter->priv->virtual_root);
+ /* not our kiddo */
+ if (!real_path)
+ goto done;
+ }
+ else
+ real_path = gtk_tree_path_copy (c_path);
+
+ if (gtk_tree_path_get_depth (real_path) - 1 >= 1)
+ {
+ /* find the parent level */
+ while (i < gtk_tree_path_get_depth (real_path) - 1)
+ {
+ gint j;
+
+ if (!level)
+ /* we don't cover this signal */
+ goto done;
+
+ elt = bsearch_elt_with_offset (level->array,
+ gtk_tree_path_get_indices (real_path)[i],
+ &j);
+
+ if (!elt)
+ /* parent is probably being filtered out */
+ goto done;
+
+ if (!elt->children)
+ {
+ GtkTreePath *tmppath;
+ GtkTreeIter tmpiter;
+
+ tmpiter.stamp = filter->priv->stamp;
+ tmpiter.user_data = level;
+ tmpiter.user_data2 = elt;
+
+ tmppath = gtk_tree_model_get_path (GTK_TREE_MODEL (data),
+ &tmpiter);
+
+ if (tmppath)
+ {
+ gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (data),
+ tmppath, &tmpiter);
+ gtk_tree_path_free (tmppath);
+ }
+
+ /* not covering this signal */
+ goto done;
+ }
+
+ level = elt->children;
+ parent_level = level;
+ i++;
+ }
+ }
+
+ if (!parent_level)
+ goto done;
+
+ /* let's try to insert the value */
+ offset = gtk_tree_path_get_indices (real_path)[gtk_tree_path_get_depth (real_path) - 1];
+
+ /* update the offsets, yes if we didn't insert the node above, there will
+ * be a gap here. This will be filled with the node (via fetch_child) when
+ * it becomes visible
+ */
+ for (i = 0; i < level->array->len; i++)
+ {
+ FilterElt *e = &g_array_index (level->array, FilterElt, i);
+ if ((e->offset >= offset))
+ e->offset++;
+ }
+
+ /* only insert when visible */
+ if (gtk_tree_model_filter_visible (filter, &real_c_iter))
+ {
+ FilterElt felt;
+
+ if (GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS (filter))
+ felt.iter = real_c_iter;
+
+ felt.offset = offset;
+ felt.zero_ref_count = 0;
+ felt.ref_count = 0;
+ felt.visible = TRUE;
+ felt.children = NULL;
+
+ for (i = 0; i < level->array->len; i++)
+ if (g_array_index (level->array, FilterElt, i).offset > offset)
+ break;
+
+ g_array_insert_val (level->array, i, felt);
+ index = i;
+
+ if (!level->parent_level)
+ filter->priv->root_level_visible++;
+ }
+
+ /* another iteration to update the references of childs to parents. */
+ for (i = 0; i < level->array->len; i++)
+ {
+ FilterElt *e = &g_array_index (level->array, FilterElt, i);
+ if (e->children)
+ e->children->parent_elt = e;
+ }
+
+ /* don't emit the signal if we aren't visible */
+ if (!gtk_tree_model_filter_visible (filter, &real_c_iter))
+ goto done;
+
+done_and_emit:
+ /* NOTE: pass c_path here and NOT real_path. This function does
+ * root subtraction itself
+ */
+ path = gtk_real_tree_model_filter_convert_child_path_to_path (filter,
+ c_path,
+ FALSE, TRUE);
+
+ if (!path)
+ return;
+
+ gtk_tree_model_filter_increment_stamp (filter);
+
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path);
+ gtk_tree_model_row_inserted (GTK_TREE_MODEL (data), path, &iter);
+
+done:
+ if (free_c_path)
+ gtk_tree_path_free (c_path);
+}
+
+static void
+gtk_tree_model_filter_row_has_child_toggled (GtkTreeModel *c_model,
+ GtkTreePath *c_path,
+ GtkTreeIter *c_iter,
+ gpointer data)
+{
+ GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data);
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ g_return_if_fail (c_path != NULL && c_iter != NULL);
+
+ /* FIXME: does this code work? */
+
+ if (!gtk_tree_model_filter_visible (filter, c_iter))
+ return;
+
+ path = gtk_real_tree_model_filter_convert_child_path_to_path (filter,
+ c_path,
+ FALSE,
+ TRUE);
+ if (!path)
+ return;
+
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path);
+ gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (data), path, &iter);
+
+ gtk_tree_path_free (path);
+}
+
+static void
+gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model,
+ GtkTreePath *c_path,
+ gpointer data)
+{
+ GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data);
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ FilterElt *elt;
+ FilterLevel *level;
+ gint offset;
+ gboolean emit_signal = TRUE;
+ gint i;
+
+ g_return_if_fail (c_path != NULL);
+
+ /* special case the deletion of an ancestor of the virtual root */
+ if (filter->priv->virtual_root &&
+ (gtk_tree_path_is_ancestor (c_path, filter->priv->virtual_root) ||
+ !gtk_tree_path_compare (c_path, filter->priv->virtual_root)))
+ {
+ gint i;
+ GtkTreePath *path;
+ FilterLevel *level = FILTER_LEVEL (filter->priv->root);
+
+ if (!level)
+ return;
+
+ /* remove everything in the filter model
+ *
+ * For now, we just iterate over the root level and emit a
+ * row_deleted for each FilterElt. Not sure if this is correct.
+ */
+
+ gtk_tree_model_filter_increment_stamp (filter);
+ path = gtk_tree_path_new ();
+ gtk_tree_path_append_index (path, 0);
+
+ for (i = 0; i < level->array->len; i++)
+ gtk_tree_model_row_deleted (GTK_TREE_MODEL (data), path);
+
+ gtk_tree_path_free (path);
+ gtk_tree_model_filter_free_level (filter, filter->priv->root);
+
+ return;
+ }
+
+ /* fixup virtual root */
+ if (filter->priv->virtual_root)
+ {
+ if (gtk_tree_path_get_depth (filter->priv->virtual_root) >=
+ gtk_tree_path_get_depth (c_path))
+ {
+ gint level;
+ gint *v_indices, *c_indices;
+
+ level = gtk_tree_path_get_depth (c_path) - 1;
+ v_indices = gtk_tree_path_get_indices (filter->priv->virtual_root);
+ c_indices = gtk_tree_path_get_indices (c_path);
+
+ if (v_indices[level] > c_indices[level])
+ (v_indices[level])--;
+ }
+ }
+
+ path = gtk_real_tree_model_filter_convert_child_path_to_path (filter,
+ c_path,
+ FALSE,
+ FALSE);
+ if (!path)
+ {
+ path = gtk_real_tree_model_filter_convert_child_path_to_path (filter,
+ c_path,
+ FALSE,
+ TRUE);
+
+ if (!path)
+ {
+ /* fixup the offsets */
+ GtkTreePath *real_path;
+
+ if (!filter->priv->root)
+ return;
+
+ level = FILTER_LEVEL (filter->priv->root);
+
+ /* subtract vroot if necessary */
+ if (filter->priv->virtual_root)
+ {
+ real_path = gtk_tree_model_filter_remove_root (c_path,
+ filter->priv->virtual_root);
+ /* we don't handle this */
+ if (!real_path)
+ return;
+ }
+ else
+ real_path = gtk_tree_path_copy (c_path);
+
+ i = 0;
+ if (gtk_tree_path_get_depth (real_path) - 1 >= 1)
+ {
+ while (i < gtk_tree_path_get_depth (real_path) - 1)
+ {
+ gint j;
+
+ if (!level)
+ {
+ /* we don't cover this */
+ gtk_tree_path_free (real_path);
+ return;
+ }
+
+ elt = bsearch_elt_with_offset (level->array,
+ gtk_tree_path_get_indices (real_path)[i],
+ &j);
+
+ if (!elt || !elt->children)
+ {
+ /* parent is filtered out, so no level */
+ gtk_tree_path_free (real_path);
+ return;
+ }
+
+ level = elt->children;
+ i++;
+ }
+ }
+
+ offset = gtk_tree_path_get_indices (real_path)[gtk_tree_path_get_depth (real_path) - 1];
+ gtk_tree_path_free (real_path);
+
+ if (!level)
+ return;
+
+ /* we need:
+ * - the offset of the removed item
+ * - the level
+ */
+ for (i = 0; i < level->array->len; i++)
+ {
+ elt = &g_array_index (level->array, FilterElt, i);
+ if (elt->offset > offset)
+ elt->offset--;
+ if (elt->children)
+ elt->children->parent_elt = elt;
+ }
+
+ return;
+ }
+
+ emit_signal = FALSE;
+ }
+
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path);
+
+ level = FILTER_LEVEL (iter.user_data);
+ elt = FILTER_ELT (iter.user_data2);
+ offset = elt->offset;
+
+ if (!level->parent_level && elt->visible)
+ filter->priv->root_level_visible--;
+
+ if (emit_signal)
+ {
+ if (level->ref_count == 0 && level != filter->priv->root)
+ {
+ gtk_tree_model_filter_increment_stamp (filter);
+ gtk_tree_model_row_deleted (GTK_TREE_MODEL (data), path);
+
+ gtk_tree_path_free (path);
+ return;
+ }
+
+ gtk_tree_model_filter_increment_stamp (filter);
+ gtk_tree_model_row_deleted (GTK_TREE_MODEL (data), path);
+ iter.stamp = filter->priv->stamp;
+
+ while (elt->ref_count > 0)
+ gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (data), &iter,
+ FALSE);
+ }
+
+ if (level->array->len == 1)
+ {
+ /* kill level */
+ gtk_tree_model_filter_free_level (filter, level);
+ }
+ else
+ {
+ FilterElt *tmp;
+
+ /* remove the row */
+ tmp = bsearch_elt_with_offset (level->array, elt->offset, &i);
+
+ offset = tmp->offset;
+ g_array_remove_index (level->array, i);
+
+ for (i = MAX (--i, 0); i < level->array->len; i++)
+ {
+ elt = &g_array_index (level->array, FilterElt, i);
+ if (elt->offset > offset)
+ elt->offset--;
+ if (elt->children)
+ elt->children->parent_elt = elt;
+ }
+ }
+
+ gtk_tree_path_free (path);
+}
+
+static void
+gtk_tree_model_filter_rows_reordered (GtkTreeModel *c_model,
+ GtkTreePath *c_path,
+ GtkTreeIter *c_iter,
+ gint *new_order,
+ gpointer data)
+{
+ FilterElt *elt;
+ FilterLevel *level;
+ GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data);
+
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ gint *tmp_array;
+ gint i, j, elt_count;
+ gint length;
+
+ GArray *new_array;
+
+ g_return_if_fail (new_order != NULL);
+
+ if (c_path == NULL || gtk_tree_path_get_indices (c_path) == NULL)
+ {
+ if (!filter->priv->root)
+ return;
+
+ length = gtk_tree_model_iter_n_children (c_model, NULL);
+
+ if (filter->priv->virtual_root)
+ {
+ gint new_pos = -1;
+
+ /* reorder root level of path */
+ for (i = 0; i < length; i++)
+ if (new_order[i] == gtk_tree_path_get_indices (filter->priv->virtual_root)[0])
+ new_pos = i;
+
+ if (new_pos < 0)
+ return;
+
+ gtk_tree_path_get_indices (filter->priv->virtual_root)[0] = new_pos;
+ return;
+ }
+
+ path = gtk_tree_path_new ();
+ level = FILTER_LEVEL (filter->priv->root);
+ }
+ else
+ {
+ GtkTreeIter child_iter;
+
+ /* virtual root anchor reordering */
+ if (filter->priv->virtual_root &&
+ gtk_tree_path_get_depth (c_path) <
+ gtk_tree_path_get_depth (filter->priv->virtual_root))
+ {
+ gint new_pos = -1;
+ gint length;
+ gint level;
+ GtkTreeIter real_c_iter;
+
+ level = gtk_tree_path_get_depth (c_path);
+
+ if (c_iter)
+ real_c_iter = *c_iter;
+ else
+ gtk_tree_model_get_iter (c_model, &real_c_iter, c_path);
+
+ length = gtk_tree_model_iter_n_children (c_model, &real_c_iter);
+
+ for (i = 0; i < length; i++)
+ if (new_order[i] == gtk_tree_path_get_indices (filter->priv->virtual_root)[level])
+ new_pos = i;
+
+ if (new_pos < 0)
+ return;
+
+ gtk_tree_path_get_indices (filter->priv->virtual_root)[level] = new_pos;
+ return;
+ }
+
+ path = gtk_real_tree_model_filter_convert_child_path_to_path (filter,
+ c_path,
+ FALSE,
+ FALSE);
+ if (!path && filter->priv->virtual_root &&
+ gtk_tree_path_compare (c_path, filter->priv->virtual_root))
+ return;
+
+ if (!path && !filter->priv->virtual_root)
+ return;
+
+ if (!path)
+ {
+ /* root level mode */
+ if (!c_iter)
+ gtk_tree_model_get_iter (c_model, c_iter, c_path);
+ length = gtk_tree_model_iter_n_children (c_model, c_iter);
+ path = gtk_tree_path_new ();
+ level = FILTER_LEVEL (filter->priv->root);
+ }
+ else
+ {
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path);
+
+ level = FILTER_LEVEL (iter.user_data);
+ elt = FILTER_ELT (iter.user_data2);
+
+ if (!elt->children)
+ {
+ gtk_tree_path_free (path);
+ return;
+ }
+
+ level = elt->children;
+
+ gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (filter), &child_iter, &iter);
+ length = gtk_tree_model_iter_n_children (c_model, &child_iter);
+ }
+ }
+
+ if (level->array->len < 1)
+ return;
+
+ /* NOTE: we do not bail out here if level->array->len < 2 like
+ * GtkTreeModelSort does. This because we do some special tricky
+ * reordering.
+ */
+
+ /* construct a new array */
+ new_array = g_array_sized_new (FALSE, FALSE, sizeof (FilterElt),
+ level->array->len);
+ tmp_array = g_new (gint, level->array->len);
+
+ for (i = 0, elt_count = 0; i < length; i++)
+ {
+ FilterElt *e = NULL;
+ gint old_offset = -1;
+
+ for (j = 0; j < level->array->len; j++)
+ if (g_array_index (level->array, FilterElt, j).offset == new_order[i])
+ {
+ e = &g_array_index (level->array, FilterElt, j);
+ old_offset = j;
+ break;
+ }
+
+ if (!e)
+ continue;
+
+ tmp_array[elt_count] = old_offset;
+ g_array_append_val (new_array, *e);
+ g_array_index (new_array, FilterElt, elt_count).offset = i;
+ elt_count++;
+ }
+
+ g_array_free (level->array, TRUE);
+ level->array = new_array;
+
+ /* fix up stuff */
+ for (i = 0; i < level->array->len; i++)
+ {
+ FilterElt *e = &g_array_index (level->array, FilterElt, i);
+ if (e->children)
+ e->children->parent_elt = e;
+ }
+
+ /* emit rows_reordered */
+ if (!gtk_tree_path_get_indices (path))
+ gtk_tree_model_rows_reordered (GTK_TREE_MODEL (data), path, NULL,
+ tmp_array);
+ else
+ gtk_tree_model_rows_reordered (GTK_TREE_MODEL (data), path, &iter,
+ tmp_array);
+
+ /* done */
+ g_free (tmp_array);
+ gtk_tree_path_free (path);
+}
+
+/* TreeModelIface implementation */
+static guint
+gtk_tree_model_filter_get_flags (GtkTreeModel *model)
+{
+ g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), 0);
+
+ return 0;
+}
+
+static gint
+gtk_tree_model_filter_get_n_columns (GtkTreeModel *model)
+{
+ GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model;
+
+ g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), 0);
+ g_return_val_if_fail (filter->priv->child_model != NULL, 0);
+
+ if (filter->priv->child_model == NULL)
+ return 0;
+
+ /* so we can't modify the modify func after this ... */
+ filter->priv->modify_func_set = TRUE;
+
+ if (filter->priv->modify_n_columns > 0)
+ return filter->priv->modify_n_columns;
+
+ return gtk_tree_model_get_n_columns (filter->priv->child_model);
+}
+
+static GType
+gtk_tree_model_filter_get_column_type (GtkTreeModel *model,
+ gint index)
+{
+ GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model;
+
+ g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), G_TYPE_INVALID);
+ g_return_val_if_fail (filter->priv->child_model != NULL, G_TYPE_INVALID);
+
+ /* so we can't modify the modify func after this ... */
+ filter->priv->modify_func_set = TRUE;
+
+ if (filter->priv->modify_types)
+ {
+ g_return_val_if_fail (index < filter->priv->modify_n_columns, G_TYPE_INVALID);
+
+ return filter->priv->modify_types[index];
+ }
+
+ return gtk_tree_model_get_column_type (filter->priv->child_model, index);
+}
+
+static gboolean
+gtk_tree_model_filter_get_iter (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ GtkTreePath *path)
+{
+ GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model;
+ gint *indices;
+ FilterLevel *level;
+ gint depth, i;
+
+ g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE);
+ g_return_val_if_fail (filter->priv->child_model != NULL, FALSE);
+
+ indices = gtk_tree_path_get_indices (path);
+
+ if (filter->priv->root == NULL)
+ gtk_tree_model_filter_build_level (filter, NULL, NULL);
+ level = FILTER_LEVEL (filter->priv->root);
+
+ depth = gtk_tree_path_get_depth (path);
+ if (!depth)
+ {
+ iter->stamp = 0;
+ return FALSE;
+ }
+
+ for (i = 0; i < depth - 1; i++)
+ {
+ if (!level || indices[i] >= level->array->len)
+ {
+ return FALSE;
+ }
+
+ if (!g_array_index (level->array, FilterElt, indices[i]).children)
+ gtk_tree_model_filter_build_level (filter, level,
+ &g_array_index (level->array,
+ FilterElt,
+ indices[i]));
+ level = g_array_index (level->array, FilterElt, indices[i]).children;
+ }
+
+ if (!level || indices[i] >= level->array->len)
+ {
+ iter->stamp = 0;
+ return FALSE;
+ }
+
+ iter->stamp = filter->priv->stamp;
+ iter->user_data = level;
+ iter->user_data2 = &g_array_index (level->array, FilterElt,
+ indices[depth - 1]);
+
+ return TRUE;
+}
+
+static GtkTreePath *
+gtk_tree_model_filter_get_path (GtkTreeModel *model,
+ GtkTreeIter *iter)
+{
+ GtkTreePath *retval;
+ FilterLevel *level;
+ FilterElt *elt;
+
+ g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), NULL);
+ g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->child_model != NULL, NULL);
+ g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->stamp == iter->stamp, NULL);
+
+ retval = gtk_tree_path_new ();
+ level = iter->user_data;
+ elt = iter->user_data2;
+
+ while (level)
+ {
+ gtk_tree_path_prepend_index (retval,
+ elt - FILTER_ELT (level->array->data));
+ elt = level->parent_elt;
+ level = level->parent_level;
+ }
+
+ return retval;
+}
+
+static void
+gtk_tree_model_filter_get_value (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gint column,
+ GValue *value)
+{
+ GtkTreeIter child_iter;
+ GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (model);
+
+ g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (model));
+ g_return_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->child_model != NULL);
+ g_return_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->stamp == iter->stamp);
+
+ if (filter->priv->modify_func)
+ {
+ g_return_if_fail (column < filter->priv->modify_n_columns);
+
+ g_value_init (value, filter->priv->modify_types[column]);
+ filter->priv->modify_func (model,
+ iter,
+ value,
+ column,
+ filter->priv->modify_data);
+
+ return;
+ }
+
+ gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, iter);
+ gtk_tree_model_get_value (GTK_TREE_MODEL_FILTER (model)->priv->child_model,
+ &child_iter, column, value);
+}
+
+static gboolean
+gtk_tree_model_filter_iter_next (GtkTreeModel *model,
+ GtkTreeIter *iter)
+{
+ FilterLevel *level;
+ FilterElt *elt;
+
+ g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE);
+ g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->child_model != NULL, FALSE);
+ g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->stamp == iter->stamp, FALSE);
+
+ level = iter->user_data;
+ elt = iter->user_data2;
+
+ if (elt - FILTER_ELT (level->array->data) >= level->array->len - 1)
+ {
+ iter->stamp = 0;
+ return FALSE;
+ }
+
+ iter->user_data2 = elt + 1;
+
+ return TRUE;
+}
+
+static gboolean
+gtk_tree_model_filter_iter_children (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent)
+{
+ GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model;
+ FilterLevel *level;
+
+ iter->stamp = 0;
+ g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE);
+ g_return_val_if_fail (filter->priv->child_model != NULL, FALSE);
+ if (parent)
+ g_return_val_if_fail (filter->priv->stamp == parent->stamp, FALSE);
+
+ if (!parent)
+ {
+ if (!filter->priv->root)
+ gtk_tree_model_filter_build_level (filter, NULL, NULL);
+ if (!filter->priv->root)
+ return FALSE;
+
+ level = filter->priv->root;
+ iter->stamp = filter->priv->stamp;
+ iter->user_data = level;
+ iter->user_data2 = level->array->data;
+ }
+ else
+ {
+ if (FILTER_ELT (parent->user_data2)->children == NULL)
+ gtk_tree_model_filter_build_level (filter,
+ FILTER_LEVEL (parent->user_data),
+ FILTER_ELT (parent->user_data2));
+ if (FILTER_ELT (parent->user_data2)->children == NULL)
+ return FALSE;
+
+ /* empty array? */
+ if (FILTER_ELT (parent->user_data2)->children->array->len <= 0)
+ return FALSE;
+
+ iter->stamp = filter->priv->stamp;
+ iter->user_data = FILTER_ELT (parent->user_data2)->children;
+ iter->user_data2 = FILTER_LEVEL (iter->user_data)->array->data;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gtk_tree_model_filter_iter_has_child (GtkTreeModel *model,
+ GtkTreeIter *iter)
+{
+ GtkTreeIter child_iter;
+ GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model;
+ FilterElt *elt;
+
+ g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE);
+ g_return_val_if_fail (filter->priv->child_model != NULL, FALSE);
+ g_return_val_if_fail (filter->priv->stamp == iter->stamp, FALSE);
+
+ filter = GTK_TREE_MODEL_FILTER (model);
+
+ gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, iter);
+ elt = FILTER_ELT (iter->user_data2);
+
+ /* we need to build the level to check if not all children are filtered
+ * out
+ */
+ if (!elt->children
+ && gtk_tree_model_iter_has_child (filter->priv->child_model, &child_iter))
+ gtk_tree_model_filter_build_level (filter, FILTER_LEVEL (iter->user_data),
+ elt);
+
+ /* FIXME: we should prolly count the visible nodes here, just like in
+ * _iter_n_children.
+ */
+ if (elt->children && elt->children->array->len > 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+static gint
+gtk_tree_model_filter_iter_n_children (GtkTreeModel *model,
+ GtkTreeIter *iter)
+{
+ GtkTreeIter child_iter;
+ GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model;
+ FilterElt *elt;
+
+ g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), 0);
+ g_return_val_if_fail (filter->priv->child_model != NULL, 0);
+ if (iter)
+ g_return_val_if_fail (filter->priv->stamp == iter->stamp, 0);
+
+ if (!iter)
+ {
+ if (!filter->priv->root)
+ gtk_tree_model_filter_build_level (filter, NULL, NULL);
+
+ /* count visible nodes */
+ return filter->priv->root_level_visible;
+ }
+
+ elt = FILTER_ELT (iter->user_data2);
+ gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, iter);
+
+ if (!elt->children &&
+ gtk_tree_model_iter_has_child (filter->priv->child_model, &child_iter))
+ gtk_tree_model_filter_build_level (filter,
+ FILTER_LEVEL (iter->user_data),
+ elt);
+
+ if (elt->children && elt->children->array->len)
+ {
+ int i = 0;
+ int count = 0;
+ GArray *a = elt->children->array;
+
+ /* count visible nodes */
+ for (i = 0; i < a->len; i++)
+ if (g_array_index (a, FilterElt, i).visible)
+ count++;
+
+ return count;
+ }
+
+ return 0;
+}
+
+static gboolean
+gtk_tree_model_filter_iter_nth_child (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ gint n)
+{
+ FilterLevel *level;
+ GtkTreeIter children;
+
+ g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE);
+ if (parent)
+ g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->stamp == parent->stamp, FALSE);
+
+ /* use this instead of has_Child to force us to build the level, if needed */
+ if (gtk_tree_model_filter_iter_children (model, &children, parent) == FALSE)
+ {
+ iter->stamp = 0;
+ return FALSE;
+ }
+
+ level = children.user_data;
+ if (n >= level->array->len)
+ {
+ iter->stamp = 0;
+ return FALSE;
+ }
+
+ iter->stamp = GTK_TREE_MODEL_FILTER (model)->priv->stamp;
+ iter->user_data = level;
+ iter->user_data2 = &g_array_index (level->array, FilterElt, n);
+
+ return TRUE;
+}
+
+static gboolean
+gtk_tree_model_filter_iter_parent (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ GtkTreeIter *child)
+{
+ FilterLevel *level;
+
+ iter->stamp = 0;
+ g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE);
+ g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->child_model != NULL, FALSE);
+ g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->stamp == child->stamp, FALSE);
+
+ level = child->user_data;
+
+ if (level->parent_level)
+ {
+ iter->stamp = GTK_TREE_MODEL_FILTER (model)->priv->stamp;
+ iter->user_data = level->parent_level;
+ iter->user_data2 = level->parent_elt;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+gtk_tree_model_filter_ref_node (GtkTreeModel *model,
+ GtkTreeIter *iter)
+{
+ GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model;
+ GtkTreeIter child_iter;
+ FilterLevel *level;
+ FilterElt *elt;
+
+ g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (model));
+ g_return_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->child_model != NULL);
+ g_return_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->stamp == iter->stamp);
+
+ gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, iter);
+
+ gtk_tree_model_ref_node (filter->priv->child_model, &child_iter);
+
+ level = iter->user_data;
+ elt = iter->user_data2;
+
+ elt->ref_count++;
+ level->ref_count++;
+ if (level->ref_count == 1)
+ {
+ FilterLevel *parent_level = level->parent_level;
+ FilterElt *parent_elt = level->parent_elt;
+
+ /* we were at zero -- time to decrease the zero_ref_count val */
+ do
+ {
+ if (parent_elt)
+ parent_elt->zero_ref_count--;
+
+ if (parent_level)
+ {
+ parent_elt = parent_level->parent_elt;
+ parent_level = parent_level->parent_level;
+ }
+ }
+ while (parent_level);
+ filter->priv->zero_ref_count--;
+ }
+}
+
+static void
+gtk_tree_model_filter_unref_node (GtkTreeModel *model,
+ GtkTreeIter *iter)
+{
+ gtk_tree_model_filter_real_unref_node (model, iter, TRUE);
+}
+
+static void
+gtk_tree_model_filter_real_unref_node (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gboolean propagate_unref)
+{
+ GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model;
+ FilterLevel *level;
+ FilterElt *elt;
+
+ g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (model));
+ g_return_if_fail (filter->priv->child_model != NULL);
+ g_return_if_fail (filter->priv->stamp == iter->stamp);
+
+ if (propagate_unref)
+ {
+ GtkTreeIter child_iter;
+ gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, iter);
+ gtk_tree_model_unref_node (filter->priv->child_model, &child_iter);
+ }
+
+ level = iter->user_data;
+ elt = iter->user_data2;
+
+ g_return_if_fail (elt->ref_count > 0);
+
+ elt->ref_count--;
+ level->ref_count--;
+ if (level->ref_count == 0)
+ {
+ FilterLevel *parent_level = level->parent_level;
+ FilterElt *parent_elt = level->parent_elt;
+
+ /* we are at zero -- time to increase the zero_ref_count val */
+ while (parent_level)
+ {
+ parent_elt->zero_ref_count++;
+
+ parent_elt = parent_level->parent_elt;
+ parent_level = parent_level->parent_level;
+ }
+ filter->priv->zero_ref_count++;
+ }
+}
+
+/* bits and pieces */
+static void
+gtk_tree_model_filter_set_model (GtkTreeModelFilter *filter,
+ GtkTreeModel *child_model)
+{
+ g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter));
+
+ if (filter->priv->child_model)
+ {
+ g_signal_handler_disconnect (G_OBJECT (filter->priv->child_model),
+ filter->priv->changed_id);
+ g_signal_handler_disconnect (G_OBJECT (filter->priv->child_model),
+ filter->priv->inserted_id);
+ g_signal_handler_disconnect (G_OBJECT (filter->priv->child_model),
+ filter->priv->has_child_toggled_id);
+ g_signal_handler_disconnect (G_OBJECT (filter->priv->child_model),
+ filter->priv->deleted_id);
+ g_signal_handler_disconnect (G_OBJECT (filter->priv->child_model),
+ filter->priv->reordered_id);
+
+ /* reset our state */
+ if (filter->priv->root)
+ gtk_tree_model_filter_free_level (filter, filter->priv->root);
+
+ filter->priv->root = NULL;
+ g_object_unref (G_OBJECT (filter->priv->child_model));
+ filter->priv->visible_column = -1;
+ /* FIXME: destroy more crack here? the funcs? */
+ }
+
+ filter->priv->child_model = child_model;
+
+ if (child_model)
+ {
+ g_object_ref (G_OBJECT (filter->priv->child_model));
+ filter->priv->changed_id =
+ g_signal_connect (child_model, "row_changed",
+ G_CALLBACK (gtk_tree_model_filter_row_changed),
+ filter);
+ filter->priv->inserted_id =
+ g_signal_connect (child_model, "row_inserted",
+ G_CALLBACK (gtk_tree_model_filter_row_inserted),
+ filter);
+ filter->priv->has_child_toggled_id =
+ g_signal_connect (child_model, "row_has_child_toggled",
+ G_CALLBACK (gtk_tree_model_filter_row_has_child_toggled),
+ filter);
+ filter->priv->deleted_id =
+ g_signal_connect (child_model, "row_deleted",
+ G_CALLBACK (gtk_tree_model_filter_row_deleted),
+ filter);
+ filter->priv->reordered_id =
+ g_signal_connect (child_model, "rows_reordered",
+ G_CALLBACK (gtk_tree_model_filter_rows_reordered),
+ filter);
+
+ filter->priv->child_flags = gtk_tree_model_get_flags (child_model);
+ filter->priv->stamp = g_random_int ();
+ }
+}
+
+static void
+gtk_tree_model_filter_set_root (GtkTreeModelFilter *filter,
+ GtkTreePath *root)
+{
+ g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter));
+
+ if (!root)
+ filter->priv->virtual_root = NULL;
+ else
+ filter->priv->virtual_root = gtk_tree_path_copy (root);
+}
+
+/* public API */
+
+/**
+ * gtk_tree_model_filter_new:
+ * @child_model: A #GtkTreeModel.
+ * @root: A #GtkTreePath or %NULL.
+ *
+ * Creates a new #GtkTreeModel, with @child_model as the child_model
+ * and @root as the virtual root.
+ *
+ * Return value: A new #GtkTreeModel.
+ */
+GtkTreeModel *
+gtk_tree_model_filter_new (GtkTreeModel *child_model,
+ GtkTreePath *root)
+{
+ GtkTreeModel *retval;
+
+ g_return_val_if_fail (GTK_IS_TREE_MODEL (child_model), NULL);
+
+ retval = GTK_TREE_MODEL (g_object_new (gtk_tree_model_filter_get_type (), NULL));
+
+ gtk_tree_model_filter_set_model (GTK_TREE_MODEL_FILTER (retval),
+ child_model);
+ gtk_tree_model_filter_set_root (GTK_TREE_MODEL_FILTER (retval), root);
+
+ return retval;
+}
+
+/**
+ * gtk_tree_model_filter_get_model:
+ * @filter: A #GtkTreeModelFilter.
+ *
+ * Returns a pointer to the child model of @filter.
+ *
+ * Return value: A pointer to a #GtkTreeModel.
+ */
+GtkTreeModel *
+gtk_tree_model_filter_get_model (GtkTreeModelFilter *filter)
+{
+ g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (filter), NULL);
+
+ return filter->priv->child_model;
+}
+
+/**
+ * gtk_tree_model_filter_set_visible_func:
+ * @filter: A #GtkTreeModelFilter.
+ * @func: A #GtkTreeModelFilterVisibleFunc, the visible function.
+ * @data: User data to pass to the visible function, or %NULL.
+ * @destroy: Destroy notifier of @data, or %NULL.
+ *
+ * Sets the visible function used when filtering the @filter to be @func. The
+ * function should return %TRUE if the given row should be visible and
+ * %FALSE otherwise.
+ */
+void
+gtk_tree_model_filter_set_visible_func (GtkTreeModelFilter *filter,
+ GtkTreeModelFilterVisibleFunc func,
+ gpointer data,
+ GtkDestroyNotify destroy)
+{
+ g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter));
+ g_return_if_fail (func != NULL);
+ g_return_if_fail (filter->priv->visible_method_set == FALSE);
+
+ if (filter->priv->visible_func)
+ {
+ GtkDestroyNotify d = filter->priv->visible_destroy;
+
+ filter->priv->visible_destroy = NULL;
+ d (filter->priv->visible_data);
+ }
+
+ filter->priv->visible_func = func;
+ filter->priv->visible_data = data;
+ filter->priv->visible_destroy = destroy;
+
+ filter->priv->visible_method_set = TRUE;
+}
+
+/**
+ * gtk_tree_model_filter_set_modify_func:
+ * @filter: A #GtkTreeModelFilter.
+ * @n_columns: The number of columns in the filter model.
+ * @types: The #GType<!-- -->s of the columns.
+ * @func: A #GtkTreeModelFilterModifyFunc, or %NULL.
+ * @data: User data to pass to the modify function, or %NULL.
+ * @destroy: Destroy notifier of @data, or %NULL.
+ *
+ * Sets the @filter to have @n_columns columns with @types. If @func
+ * is not %NULL, it will set @func to be the modify function of @filter.
+ */
+void
+gtk_tree_model_filter_set_modify_func (GtkTreeModelFilter *filter,
+ gint n_columns,
+ GType *types,
+ GtkTreeModelFilterModifyFunc func,
+ gpointer data,
+ GtkDestroyNotify destroy)
+{
+ g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter));
+ g_return_if_fail (func != NULL);
+ g_return_if_fail (filter->priv->modify_func_set == FALSE);
+
+ if (filter->priv->modify_destroy)
+ {
+ GtkDestroyNotify d = filter->priv->modify_destroy;
+
+ filter->priv->modify_destroy = NULL;
+ d (filter->priv->modify_data);
+ }
+
+ filter->priv->modify_n_columns = n_columns;
+ filter->priv->modify_types = g_new0 (GType, n_columns);
+ memcpy (filter->priv->modify_types, types, sizeof (GType) * n_columns);
+ filter->priv->modify_func = func;
+ filter->priv->modify_data = data;
+ filter->priv->modify_destroy = destroy;
+
+ filter->priv->modify_func_set = TRUE;
+}
+
+/**
+ * gtk_tree_model_filter_set_visible_column:
+ * @filter: A #GtkTreeModelFilter.
+ * @column: A #gint which is the column containing the visible information.
+ *
+ * Sets @column of the child_model to be the column where @filter should
+ * look for visibility information. @columns should be a column of type
+ * %G_TYPE_BOOLEAN, where %TRUE means that a row is visible, and %FALSE
+ * if not.
+ */
+void
+gtk_tree_model_filter_set_visible_column (GtkTreeModelFilter *filter,
+ gint column)
+{
+ g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter));
+ g_return_if_fail (column >= 0);
+ g_return_if_fail (filter->priv->visible_method_set == FALSE);
+
+ filter->priv->visible_column = column;
+
+ filter->priv->visible_method_set = TRUE;
+}
+
+/* conversion */
+
+/**
+ * gtk_tree_model_filter_convert_child_iter_to_iter:
+ * @filter: A #GtkTreeModelFilter.
+ * @filter_iter: An uninitialized #GtkTreeIter.
+ * @child_iter: A valid #GtkTreeIter pointing to a row on the child model.
+ *
+ * Sets @filter_iter to point to the row in @filter that corresponds to the
+ * row pointed at by @child_iter.
+ */
+void
+gtk_tree_model_filter_convert_child_iter_to_iter (GtkTreeModelFilter *filter,
+ GtkTreeIter *filter_iter,
+ GtkTreeIter *child_iter)
+{
+ GtkTreePath *child_path, *path;
+
+ g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter));
+ g_return_if_fail (filter->priv->child_model != NULL);
+ g_return_if_fail (filter_iter != NULL);
+ g_return_if_fail (child_iter != NULL);
+
+ filter_iter->stamp = 0;
+
+ child_path = gtk_tree_model_get_path (filter->priv->child_model, child_iter);
+ g_return_if_fail (child_path != NULL);
+
+ path = gtk_tree_model_filter_convert_child_path_to_path (filter,
+ child_path);
+ gtk_tree_path_free (child_path);
+ g_return_if_fail (path != NULL);
+
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), filter_iter, path);
+ gtk_tree_path_free (path);
+}
+
+/**
+ * gtk_tree_model_filter_convert_iter_to_child_iter:
+ * @filter: A #GtkTreeModelFilter.
+ * @child_iter: An uninitialized #GtkTreeIter.
+ * @filter_iter: A valid #GtkTreeIter pointing to a row on @filter.
+ *
+ * Sets @child_iter to point to the row pointed to by @filter_iter.
+ */
+void
+gtk_tree_model_filter_convert_iter_to_child_iter (GtkTreeModelFilter *filter,
+ GtkTreeIter *child_iter,
+ GtkTreeIter *filter_iter)
+{
+ g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter));
+ g_return_if_fail (filter->priv->child_model != NULL);
+ g_return_if_fail (child_iter != NULL);
+ g_return_if_fail (filter_iter != NULL);
+ g_return_if_fail (filter_iter->stamp == filter->priv->stamp);
+
+ if (GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS (filter))
+ {
+ *child_iter = FILTER_ELT (filter_iter->user_data2)->iter;
+ }
+ else
+ {
+ GtkTreePath *path;
+
+ path = gtk_tree_model_filter_elt_get_path (filter_iter->user_data,
+ filter_iter->user_data2,
+ NULL);
+ gtk_tree_model_get_iter (filter->priv->child_model, child_iter, path);
+ gtk_tree_path_free (path);
+ }
+}
+
+static GtkTreePath *
+gtk_real_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filter,
+ GtkTreePath *child_path,
+ gboolean build_levels,
+ gboolean fetch_childs)
+{
+ gint *child_indices;
+ GtkTreePath *retval;
+ GtkTreePath *real_path;
+ FilterLevel *level;
+ FilterElt *tmp;
+ gint i;
+
+ g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (filter), NULL);
+ g_return_val_if_fail (filter->priv->child_model != NULL, NULL);
+ g_return_val_if_fail (child_path != NULL, NULL);
+
+ if (!filter->priv->virtual_root)
+ real_path = gtk_tree_path_copy (child_path);
+ else
+ real_path = gtk_tree_model_filter_remove_root (child_path,
+ filter->priv->virtual_root);
+
+ if (!real_path)
+ return NULL;
+
+ retval = gtk_tree_path_new ();
+ child_indices = gtk_tree_path_get_indices (real_path);
+
+ if (filter->priv->root == NULL && build_levels)
+ gtk_tree_model_filter_build_level (filter, NULL, NULL);
+ level = FILTER_LEVEL (filter->priv->root);
+
+ for (i = 0; i < gtk_tree_path_get_depth (real_path); i++)
+ {
+ gint j;
+ gboolean found_child = FALSE;
+
+ if (!level)
+ {
+ gtk_tree_path_free (real_path);
+ gtk_tree_path_free (retval);
+ return NULL;
+ }
+
+ tmp = bsearch_elt_with_offset (level->array, child_indices[i], &j);
+ if (tmp)
+ {
+ gtk_tree_path_append_index (retval, j);
+ if (!tmp->children && build_levels)
+ gtk_tree_model_filter_build_level (filter, level, tmp);
+ level = tmp->children;
+ found_child = TRUE;
+ }
+
+ if (!found_child && fetch_childs)
+ {
+ tmp = gtk_tree_model_filter_fetch_child (filter, level,
+ child_indices[i],
+ &j);
+
+ /* didn't find the child, let's try to bring it back */
+ if (!tmp || tmp->offset != child_indices[i])
+ {
+ /* not there */
+ gtk_tree_path_free (real_path);
+ gtk_tree_path_free (retval);
+ return NULL;
+ }
+
+ gtk_tree_path_append_index (retval, j);
+ if (!tmp->children && build_levels)
+ gtk_tree_model_filter_build_level (filter, level, tmp);
+ level = tmp->children;
+ found_child = TRUE;
+ }
+ else if (!found_child && !fetch_childs)
+ {
+ /* no path */
+ gtk_tree_path_free (real_path);
+ gtk_tree_path_free (retval);
+ return NULL;
+ }
+ }
+
+ gtk_tree_path_free (real_path);
+ return retval;
+}
+
+/**
+ * gtk_tree_model_filter_convert_child_path_to_path:
+ * @filter: A #GtkTreeModelFilter.
+ * @child_path: A #GtkTreePath to convert.
+ *
+ * Converts @child_path to a path relative to @filter. That is, @child_path
+ * points to a path in the child model. The rerturned path will point to the
+ * same row in the filtered model. If @child_path isn't a valid path on the
+ * child model, then %NULL is returned.
+ *
+ * Return value: A newly allocated #GtkTreePath, or %NULL.
+ */
+GtkTreePath *
+gtk_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filter,
+ GtkTreePath *child_path)
+{
+ /* this function does the sanity checks */
+ return gtk_real_tree_model_filter_convert_child_path_to_path (filter,
+ child_path,
+ TRUE,
+ TRUE);
+}
+
+/**
+ * gtk_tree_model_filter_convert_path_to_child_path:
+ * @filter: A #GtkTreeModelFilter.
+ * @filter_path: A #GtkTreePath to convert.
+ *
+ * Converts @filter_path to a path on the child model of @filter. That is,
+ * @filter_path points to a location in @filter. The returned path will
+ * point to the same location in the model not being filtered. If @filter_path
+ * does not point to a location in the child model, %NULL is returned.
+ *
+ * Return value: A newly allocated #GtkTreePath, or %NULL.
+ */
+GtkTreePath *
+gtk_tree_model_filter_convert_path_to_child_path (GtkTreeModelFilter *filter,
+ GtkTreePath *filter_path)
+{
+ gint *filter_indices;
+ GtkTreePath *retval;
+ FilterLevel *level;
+ gint i;
+
+ g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (filter), NULL);
+ g_return_val_if_fail (filter->priv->child_model != NULL, NULL);
+ g_return_val_if_fail (filter_path != NULL, NULL);
+
+ /* convert path */
+ retval = gtk_tree_path_new ();
+ filter_indices = gtk_tree_path_get_indices (filter_path);
+ if (!filter->priv->root)
+ gtk_tree_model_filter_build_level (filter, NULL, NULL);
+ level = FILTER_LEVEL (filter->priv->root);
+
+ for (i = 0; i < gtk_tree_path_get_depth (filter_path); i++)
+ {
+ gint count = filter_indices[i];
+
+ if (!level || level->array->len <= filter_indices[i])
+ {
+ gtk_tree_path_free (retval);
+ return NULL;
+ }
+
+ if (g_array_index (level->array, FilterElt, count).children == NULL)
+ gtk_tree_model_filter_build_level (filter, level, &g_array_index (level->array, FilterElt, count));
+
+ if (!level || level->array->len <= filter_indices[i])
+ {
+ gtk_tree_path_free (retval);
+ return NULL;
+ }
+
+ gtk_tree_path_append_index (retval, g_array_index (level->array, FilterElt, count).offset);
+ level = g_array_index (level->array, FilterElt, count).children;
+ }
+
+ /* apply vroot */
+
+ if (filter->priv->virtual_root)
+ {
+ GtkTreePath *real_retval;
+
+ real_retval = gtk_tree_model_filter_add_root (retval,
+ filter->priv->virtual_root);
+ gtk_tree_path_free (retval);
+
+ return real_retval;
+ }
+
+ return retval;
+}
+
+static gboolean
+gtk_tree_model_filter_refilter_helper (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ /* evil, don't try this at home, but certainly speeds things up */
+ gtk_tree_model_filter_row_changed (model, path, iter, data);
+
+ return FALSE;
+}
+
+/**
+ * gtk_tree_model_filter_refilter:
+ * @filter: A #GtkTreeModelFilter.
+ *
+ * Emits ::row_changed for each row in the child model, which causes
+ * the filter to re-evaluate whether a row is visible or not.
+ */
+void
+gtk_tree_model_filter_refilter (GtkTreeModelFilter *filter)
+{
+ g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter));
+
+ /* S L O W */
+ gtk_tree_model_foreach (filter->priv->child_model,
+ gtk_tree_model_filter_refilter_helper,
+ filter);
+}
+
+/**
+ * gtk_tree_model_filter_clear_cache:
+ * @filter: A #GtkTreeModelFilter.
+ *
+ * This function should almost never be called. It clears the @filter
+ * of any cached iterators that haven't been reffed with
+ * gtk_tree_model_ref_node(). This might be useful if the child model
+ * being filtered is static (and doesn't change often) and there has been
+ * a lot of unreffed access to nodes. As a side effect of this function,
+ * all unreffed itters will be invalid.
+ */
+void
+gtk_tree_model_filter_clear_cache (GtkTreeModelFilter *filter)
+{
+ g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter));
+
+ if (filter->priv->zero_ref_count)
+ gtk_tree_model_filter_clear_cache_helper (filter,
+ FILTER_LEVEL (filter->priv->root));
+}
diff --git a/gtk/gtktreemodelfilter.h b/gtk/gtktreemodelfilter.h
new file mode 100644
index 000000000..6445f0b72
--- /dev/null
+++ b/gtk/gtktreemodelfilter.h
@@ -0,0 +1,99 @@
+/* gtktreemodelfilter.h
+ * Copyright (C) 2000,2001 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ * Copyright (C) 2001-2003 Kristian Rietveld <kris@gtk.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_TREE_MODEL_FILTER_H__
+#define __GTK_TREE_MODEL_FILTER_H__
+
+#include <gtk/gtktreemodel.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_TREE_MODEL_FILTER (gtk_tree_model_filter_get_type ())
+#define GTK_TREE_MODEL_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TREE_MODEL_FILTER, GtkTreeModelFilter))
+#define GTK_TREE_MODEL_FILTER_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), GTK_TYPE_TREE_MODEL_FILTER, GtkTreeModelFilterClass))
+#define GTK_IS_TREE_MODEL_FILTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TREE_MODEL_FILTER))
+#define GTK_IS_TREE_MODEL_FILTER_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), GTK_TYPE_TREE_MODEL_FILTER))
+#define GTK_TREE_MODEL_FILTER_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TREE_MODEL_FILTER, GtkTreeModelFilterClass))
+#define GTK_TREE_MODEL_FILTER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_TREE_MODEL_FILTER, GtkTreeModelFilterPrivate))
+
+typedef gboolean (* GtkTreeModelFilterVisibleFunc) (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data);
+typedef void (* GtkTreeModelFilterModifyFunc) (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ GValue *value,
+ gint column,
+ gpointer data);
+
+typedef struct _GtkTreeModelFilter GtkTreeModelFilter;
+typedef struct _GtkTreeModelFilterClass GtkTreeModelFilterClass;
+typedef struct _GtkTreeModelFilterPrivate GtkTreeModelFilterPrivate;
+
+struct _GtkTreeModelFilter
+{
+ GObject parent;
+
+ /*< private >*/
+ GtkTreeModelFilterPrivate *priv;
+};
+
+struct _GtkTreeModelFilterClass
+{
+ GObjectClass parent_class;
+};
+
+/* base */
+GType gtk_tree_model_filter_get_type (void);
+GtkTreeModel *gtk_tree_model_filter_new (GtkTreeModel *child_model,
+ GtkTreePath *root);
+void gtk_tree_model_filter_set_visible_func (GtkTreeModelFilter *filter,
+ GtkTreeModelFilterVisibleFunc func,
+ gpointer data,
+ GtkDestroyNotify destroy);
+void gtk_tree_model_filter_set_modify_func (GtkTreeModelFilter *filter,
+ gint n_columns,
+ GType *types,
+ GtkTreeModelFilterModifyFunc func,
+ gpointer data,
+ GtkDestroyNotify destroy);
+void gtk_tree_model_filter_set_visible_column (GtkTreeModelFilter *filter,
+ gint column);
+
+GtkTreeModel *gtk_tree_model_filter_get_model (GtkTreeModelFilter *filter);
+
+/* conversion */
+void gtk_tree_model_filter_convert_child_iter_to_iter (GtkTreeModelFilter *filter,
+ GtkTreeIter *filter_iter,
+ GtkTreeIter *child_iter);
+void gtk_tree_model_filter_convert_iter_to_child_iter (GtkTreeModelFilter *filter,
+ GtkTreeIter *child_iter,
+ GtkTreeIter *filter_iter);
+GtkTreePath *gtk_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filter,
+ GtkTreePath *child_path);
+GtkTreePath *gtk_tree_model_filter_convert_path_to_child_path (GtkTreeModelFilter *path,
+ GtkTreePath *filter_path);
+
+/* extras */
+void gtk_tree_model_filter_refilter (GtkTreeModelFilter *filter);
+void gtk_tree_model_filter_clear_cache (GtkTreeModelFilter *filter);
+
+G_END_DECLS
+
+#endif /* __GTK_TREE_MODEL_FILTER_H__ */
diff --git a/gtk/gtktreeviewcolumn.c b/gtk/gtktreeviewcolumn.c
index ba372b88a..cba68c0e6 100644
--- a/gtk/gtktreeviewcolumn.c
+++ b/gtk/gtktreeviewcolumn.c
@@ -21,6 +21,7 @@
#include "gtktreeviewcolumn.h"
#include "gtktreeview.h"
#include "gtktreeprivate.h"
+#include "gtkcelllayout.h"
#include "gtkbutton.h"
#include "gtkalignment.h"
#include "gtklabel.h"
@@ -74,6 +75,7 @@ struct _GtkTreeViewColumnCellInfo
/* Type methods */
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_cell_layout_init (GtkCellLayoutIface *iface);
/* GObject methods */
static void gtk_tree_view_column_set_property (GObject *object,
@@ -86,6 +88,26 @@ static void gtk_tree_view_column_get_property (GObject
GParamSpec *pspec);
static void gtk_tree_view_column_finalize (GObject *object);
+/* GtkCellLayout implementation */
+static void gtk_tree_view_column_cell_layout_pack_start (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ gboolean expand);
+static void gtk_tree_view_column_cell_layout_pack_end (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ gboolean expand);
+static void gtk_tree_view_column_cell_layout_clear (GtkCellLayout *cell_layout);
+static void gtk_tree_view_column_cell_layout_add_attribute (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ const gchar *attribute,
+ gint column);
+static void gtk_tree_view_column_cell_layout_set_cell_data_func (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ GtkCellLayoutDataFunc func,
+ gpointer func_data,
+ GDestroyNotify destroy);
+static void gtk_tree_view_column_cell_layout_clear_attributes (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell);
+
/* Button handling code */
static void gtk_tree_view_column_create_button (GtkTreeViewColumn *tree_column);
static void gtk_tree_view_column_update_button (GtkTreeViewColumn *tree_column);
@@ -145,12 +167,23 @@ gtk_tree_view_column_get_type (void)
NULL, /* class_data */
sizeof (GtkTreeViewColumn),
0,
- (GInstanceInitFunc) gtk_tree_view_column_init,
+ (GInstanceInitFunc) gtk_tree_view_column_init
+ };
+
+ static const GInterfaceInfo cell_layout_info =
+ {
+ (GInterfaceInitFunc) gtk_tree_view_column_cell_layout_init,
+ NULL,
+ NULL
};
tree_column_type =
g_type_register_static (GTK_TYPE_OBJECT, "GtkTreeViewColumn",
&tree_column_info, 0);
+
+ g_type_add_interface_static (tree_column_type,
+ GTK_TYPE_CELL_LAYOUT,
+ &cell_layout_info);
}
return tree_column_type;
@@ -315,6 +348,17 @@ gtk_tree_view_column_class_init (GtkTreeViewColumnClass *class)
}
static void
+gtk_tree_view_column_cell_layout_init (GtkCellLayoutIface *iface)
+{
+ iface->pack_start = gtk_tree_view_column_cell_layout_pack_start;
+ iface->pack_end = gtk_tree_view_column_cell_layout_pack_end;
+ iface->clear = gtk_tree_view_column_cell_layout_clear;
+ iface->add_attribute = gtk_tree_view_column_cell_layout_add_attribute;
+ iface->set_cell_data_func = gtk_tree_view_column_cell_layout_set_cell_data_func;
+ iface->clear_attributes = gtk_tree_view_column_cell_layout_clear_attributes;
+}
+
+static void
gtk_tree_view_column_init (GtkTreeViewColumn *tree_column)
{
tree_column->button = NULL;
@@ -552,6 +596,169 @@ gtk_tree_view_column_get_property (GObject *object,
}
}
+/* Implementation of GtkCellLayout interface
+ */
+
+static void
+gtk_tree_view_column_cell_layout_pack_start (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ gboolean expand)
+{
+ GtkTreeViewColumn *column;
+ GtkTreeViewColumnCellInfo *cell_info;
+
+ g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (cell_layout));
+ column = GTK_TREE_VIEW_COLUMN (cell_layout);
+ g_return_if_fail (! gtk_tree_view_column_get_cell_info (column, cell));
+
+ g_object_ref (cell);
+ gtk_object_sink (GTK_OBJECT (cell));
+
+ cell_info = g_new0 (GtkTreeViewColumnCellInfo, 1);
+ cell_info->cell = cell;
+ cell_info->expand = expand ? TRUE : FALSE;
+ cell_info->pack = GTK_PACK_START;
+ cell_info->has_focus = 0;
+ cell_info->attributes = NULL;
+
+ column->cell_list = g_list_append (column->cell_list, cell_info);
+}
+
+static void
+gtk_tree_view_column_cell_layout_pack_end (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ gboolean expand)
+{
+ GtkTreeViewColumn *column;
+ GtkTreeViewColumnCellInfo *cell_info;
+
+ g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (cell_layout));
+ column = GTK_TREE_VIEW_COLUMN (cell_layout);
+ g_return_if_fail (! gtk_tree_view_column_get_cell_info (column, cell));
+
+ g_object_ref (cell);
+ gtk_object_sink (GTK_OBJECT (cell));
+
+ cell_info = g_new0 (GtkTreeViewColumnCellInfo, 1);
+ cell_info->cell = cell;
+ cell_info->expand = expand ? TRUE : FALSE;
+ cell_info->pack = GTK_PACK_END;
+ cell_info->has_focus = 0;
+ cell_info->attributes = NULL;
+
+ column->cell_list = g_list_append (column->cell_list, cell_info);
+}
+
+static void
+gtk_tree_view_column_cell_layout_clear (GtkCellLayout *cell_layout)
+{
+ GList *list;
+ GtkTreeViewColumn *column;
+
+ g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (cell_layout));
+ column = GTK_TREE_VIEW_COLUMN (cell_layout);
+
+ for (list = column->cell_list; list; list = list->next)
+ {
+ GtkTreeViewColumnCellInfo *info = (GtkTreeViewColumnCellInfo *)list->data;
+
+ gtk_tree_view_column_clear_attributes (column, info->cell);
+ g_object_unref (info->cell);
+ g_free (info);
+ }
+
+ g_list_free (column->cell_list);
+ column->cell_list = NULL;
+}
+
+static void
+gtk_tree_view_column_cell_layout_add_attribute (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ const gchar *attribute,
+ gint column)
+{
+ GtkTreeViewColumn *tree_column;
+ GtkTreeViewColumnCellInfo *info;
+
+ g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (cell_layout));
+ tree_column = GTK_TREE_VIEW_COLUMN (cell_layout);
+
+ info = gtk_tree_view_column_get_cell_info (tree_column, cell);
+ g_return_if_fail (info != NULL);
+
+ info->attributes = g_slist_prepend (info->attributes, GINT_TO_POINTER (column));
+ info->attributes = g_slist_prepend (info->attributes, g_strdup (attribute));
+
+ if (tree_column->tree_view)
+ _gtk_tree_view_column_cell_set_dirty (tree_column, TRUE);
+}
+
+static void
+gtk_tree_view_column_cell_layout_set_cell_data_func (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ GtkCellLayoutDataFunc func,
+ gpointer func_data,
+ GDestroyNotify destroy)
+{
+ GtkTreeViewColumn *column;
+ GtkTreeViewColumnCellInfo *info;
+
+ g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (cell_layout));
+ column = GTK_TREE_VIEW_COLUMN (cell_layout);
+
+ info = gtk_tree_view_column_get_cell_info (column, cell);
+ g_return_if_fail (info != NULL);
+
+ if (info->destroy)
+ {
+ GDestroyNotify d = info->destroy;
+
+ info->destroy = NULL;
+ d (info->func_data);
+ }
+
+ info->func = (GtkTreeCellDataFunc)func;
+ info->func_data = func_data;
+ info->destroy = destroy;
+
+ if (column->tree_view)
+ _gtk_tree_view_column_cell_set_dirty (column, TRUE);
+}
+
+static void
+gtk_tree_view_column_cell_layout_clear_attributes (GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell_renderer)
+{
+ GtkTreeViewColumn *column;
+ GtkTreeViewColumnCellInfo *info;
+
+ g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (cell_layout));
+ column = GTK_TREE_VIEW_COLUMN (cell_layout);
+
+ info = gtk_tree_view_column_get_cell_info (column, cell_renderer);
+ gtk_tree_view_column_clear_attributes_by_info (column, info);
+}
+
+static void
+gtk_tree_view_column_clear_attributes_by_info (GtkTreeViewColumn *tree_column,
+ GtkTreeViewColumnCellInfo *info)
+{
+ GSList *list;
+
+ list = info->attributes;
+
+ while (list && list->next)
+ {
+ g_free (list->data);
+ list = list->next->next;
+ }
+ g_slist_free (info->attributes);
+ info->attributes = NULL;
+
+ if (tree_column->tree_view)
+ _gtk_tree_view_column_cell_set_dirty (tree_column, TRUE);
+}
+
/* Helper functions
*/
@@ -1246,23 +1453,7 @@ gtk_tree_view_column_pack_start (GtkTreeViewColumn *tree_column,
GtkCellRenderer *cell,
gboolean expand)
{
- GtkTreeViewColumnCellInfo *cell_info;
-
- g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
- g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
- g_return_if_fail (! gtk_tree_view_column_get_cell_info (tree_column, cell));
-
- g_object_ref (cell);
- gtk_object_sink (GTK_OBJECT (cell));
-
- cell_info = g_new0 (GtkTreeViewColumnCellInfo, 1);
- cell_info->cell = cell;
- cell_info->expand = expand ? TRUE : FALSE;
- cell_info->pack = GTK_PACK_START;
- cell_info->has_focus = 0;
- cell_info->attributes = NULL;
-
- tree_column->cell_list = g_list_append (tree_column->cell_list, cell_info);
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (tree_column), cell, expand);
}
/**
@@ -1280,26 +1471,9 @@ gtk_tree_view_column_pack_end (GtkTreeViewColumn *tree_column,
GtkCellRenderer *cell,
gboolean expand)
{
- GtkTreeViewColumnCellInfo *cell_info;
-
- g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
- g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
- g_return_if_fail (! gtk_tree_view_column_get_cell_info (tree_column, cell));
-
- g_object_ref (cell);
- gtk_object_sink (GTK_OBJECT (cell));
-
- cell_info = g_new0 (GtkTreeViewColumnCellInfo, 1);
- cell_info->cell = cell;
- cell_info->expand = expand ? TRUE : FALSE;
- cell_info->pack = GTK_PACK_END;
- cell_info->has_focus = 0;
- cell_info->attributes = NULL;
-
- tree_column->cell_list = g_list_append (tree_column->cell_list, cell_info);
+ gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (tree_column), cell, expand);
}
-
/**
* gtk_tree_view_column_clear:
* @tree_column: A #GtkTreeViewColumn
@@ -1309,20 +1483,7 @@ gtk_tree_view_column_pack_end (GtkTreeViewColumn *tree_column,
void
gtk_tree_view_column_clear (GtkTreeViewColumn *tree_column)
{
- GList *list;
- g_return_if_fail (tree_column != NULL);
-
- for (list = tree_column->cell_list; list; list = list->next)
- {
- GtkTreeViewColumnCellInfo *info = (GtkTreeViewColumnCellInfo *)list->data;
-
- gtk_tree_view_column_clear_attributes (tree_column, info->cell);
- g_object_unref (info->cell);
- g_free (info);
- }
-
- g_list_free (tree_column->cell_list);
- tree_column->cell_list = NULL;
+ gtk_cell_layout_clear (GTK_CELL_LAYOUT (tree_column));
}
/**
@@ -1371,18 +1532,8 @@ gtk_tree_view_column_add_attribute (GtkTreeViewColumn *tree_column,
const gchar *attribute,
gint column)
{
- GtkTreeViewColumnCellInfo *info;
-
- g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
- info = gtk_tree_view_column_get_cell_info (tree_column, cell_renderer);
- g_return_if_fail (info != NULL);
-
- info->attributes = g_slist_prepend (info->attributes, GINT_TO_POINTER (column));
- info->attributes = g_slist_prepend (info->attributes, g_strdup (attribute));
-
- if (tree_column->tree_view)
- _gtk_tree_view_column_cell_set_dirty (tree_column, TRUE);
-
+ gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (tree_column),
+ cell_renderer, attribute, column);
}
static void
@@ -1454,28 +1605,10 @@ gtk_tree_view_column_set_cell_data_func (GtkTreeViewColumn *tree_column,
gpointer func_data,
GtkDestroyNotify destroy)
{
- GtkTreeViewColumnCellInfo *info;
-
- g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
- g_return_if_fail (GTK_IS_CELL_RENDERER (cell_renderer));
- info = gtk_tree_view_column_get_cell_info (tree_column, cell_renderer);
-
- g_return_if_fail (info != NULL);
-
- if (info->destroy)
- {
- GtkDestroyNotify d = info->destroy;
-
- info->destroy = NULL;
- d (info->func_data);
- }
-
- info->func = func;
- info->func_data = func_data;
- info->destroy = destroy;
-
- if (tree_column->tree_view)
- _gtk_tree_view_column_cell_set_dirty (tree_column, TRUE);
+ gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (tree_column),
+ cell_renderer,
+ (GtkCellLayoutDataFunc)func,
+ func_data, destroy);
}
@@ -1491,37 +1624,10 @@ void
gtk_tree_view_column_clear_attributes (GtkTreeViewColumn *tree_column,
GtkCellRenderer *cell_renderer)
{
- GtkTreeViewColumnCellInfo *info;
-
- g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
- g_return_if_fail (GTK_IS_CELL_RENDERER (cell_renderer));
-
- info = gtk_tree_view_column_get_cell_info (tree_column, cell_renderer);
-
- gtk_tree_view_column_clear_attributes_by_info (tree_column, info);
+ gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (tree_column),
+ cell_renderer);
}
-static void
-gtk_tree_view_column_clear_attributes_by_info (GtkTreeViewColumn *tree_column,
- GtkTreeViewColumnCellInfo *info)
-{
- GSList *list;
-
- list = info->attributes;
-
- while (list && list->next)
- {
- g_free (list->data);
- list = list->next->next;
- }
- g_slist_free (info->attributes);
- info->attributes = NULL;
-
- if (tree_column->tree_view)
- _gtk_tree_view_column_cell_set_dirty (tree_column, TRUE);
-}
-
-
/**
* gtk_tree_view_column_set_spacing:
* @tree_column: A #GtkTreeViewColumn.
diff --git a/po/POTFILES.in b/po/POTFILES.in
index cebfa125a..ffa3d0b5d 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -28,6 +28,7 @@ gtk/gtkbbox.c
gtk/gtkbox.c
gtk/gtkbutton.c
gtk/gtkcalendar.c
+gtk/gtkcelllayout.c
gtk/gtkcellrenderer.c
gtk/gtkcellrendererpixbuf.c
gtk/gtkcellrenderertext.c
@@ -41,6 +42,7 @@ gtk/gtkcontainer.c
gtk/gtkcurve.c
gtk/gtkdialog.c
gtk/gtkentry.c
+gtk/gtkentrycompletion.c
gtk/gtkfilesel.c
gtk/gtkfixed.c
gtk/gtkfontsel.c
@@ -89,6 +91,7 @@ gtk/gtkthemes.c
gtk/gtktipsquery.c
gtk/gtktogglebutton.c
gtk/gtktoolbar.c
+gtk/gtktreemodelfilter.c
gtk/gtktreemodelsort.c
gtk/gtktreeview.c
gtk/gtktreeviewcolumn.c