summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gtk/Makefile.am2
-rw-r--r--gtk/a11y/Makefile.am3
-rw-r--r--gtk/a11y/gtklistboxaccessible.c185
-rw-r--r--gtk/a11y/gtklistboxaccessible.h58
-rw-r--r--gtk/a11y/gtklistboxaccessibleprivate.h31
-rw-r--r--gtk/gtk-a11y.h1
-rw-r--r--gtk/gtk.h1
-rw-r--r--gtk/gtklistbox.c2323
-rw-r--r--gtk/gtklistbox.h178
9 files changed, 2782 insertions, 0 deletions
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 6884575502..0b3de0ab6b 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -265,6 +265,7 @@ gtk_public_h_sources = \
gtklayout.h \
gtklevelbar.h \
gtklinkbutton.h \
+ gtklistbox.h \
gtkliststore.h \
gtklockbutton.h \
gtkmain.h \
@@ -757,6 +758,7 @@ gtk_base_c_sources = \
gtklayout.c \
gtklevelbar.c \
gtklinkbutton.c \
+ gtklistbox.c \
gtkliststore.c \
gtklockbutton.c \
gtkmain.c \
diff --git a/gtk/a11y/Makefile.am b/gtk/a11y/Makefile.am
index 5a7385bded..9f643b8596 100644
--- a/gtk/a11y/Makefile.am
+++ b/gtk/a11y/Makefile.am
@@ -25,6 +25,7 @@ gtka11y_c_sources = \
gtklabelaccessible.c \
gtklevelbaraccessible.c \
gtklinkbuttonaccessible.c \
+ gtklistboxaccessible.c \
gtklockbuttonaccessible.c \
gtkmenuaccessible.c \
gtkmenushellaccessible.c \
@@ -72,6 +73,7 @@ gtka11yinclude_HEADERS = \
gtklabelaccessible.h \
gtklevelbaraccessible.h \
gtklinkbuttonaccessible.h \
+ gtklistboxaccessible.h \
gtklockbuttonaccessible.h \
gtkmenuaccessible.h \
gtkmenuitemaccessible.h \
@@ -107,6 +109,7 @@ gtka11y_private_h_sources = \
gtkcolorswatchaccessibleprivate.h \
gtkiconviewaccessibleprivate.h \
gtklabelaccessibleprivate.h \
+ gtklistboxaccessibleprivate.h \
gtklockbuttonaccessibleprivate.h \
gtktextviewaccessibleprivate.h \
gtktreeviewaccessibleprivate.h \
diff --git a/gtk/a11y/gtklistboxaccessible.c b/gtk/a11y/gtklistboxaccessible.c
new file mode 100644
index 0000000000..f4f2690489
--- /dev/null
+++ b/gtk/a11y/gtklistboxaccessible.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gtklistboxaccessibleprivate.h"
+
+#include "gtk/gtklistbox.h"
+
+static void atk_selection_interface_init (AtkSelectionIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GtkListBoxAccessible, gtk_list_box_accessible, GTK_TYPE_CONTAINER_ACCESSIBLE,
+ G_IMPLEMENT_INTERFACE (ATK_TYPE_SELECTION, atk_selection_interface_init))
+
+static void
+gtk_list_box_accessible_init (GtkListBoxAccessible *accessible)
+{
+}
+
+static void
+gtk_list_box_accessible_initialize (AtkObject *obj,
+ gpointer data)
+{
+ ATK_OBJECT_CLASS (gtk_list_box_accessible_parent_class)->initialize (obj, data);
+
+ obj->role = ATK_ROLE_LIST_BOX;
+}
+
+static AtkStateSet*
+gtk_list_box_accessible_ref_state_set (AtkObject *obj)
+{
+ AtkStateSet *state_set;
+ GtkWidget *widget;
+
+ state_set = ATK_OBJECT_CLASS (gtk_list_box_accessible_parent_class)->ref_state_set (obj);
+ widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
+
+ if (widget != NULL)
+ atk_state_set_add_state (state_set, ATK_STATE_MANAGES_DESCENDANTS);
+
+ return state_set;
+}
+
+static void
+gtk_list_box_accessible_class_init (GtkListBoxAccessibleClass *klass)
+{
+ AtkObjectClass *object_class = ATK_OBJECT_CLASS (klass);
+
+ object_class->initialize = gtk_list_box_accessible_initialize;
+ object_class->ref_state_set = gtk_list_box_accessible_ref_state_set;
+}
+
+static gboolean
+gtk_list_box_accessible_add_selection (AtkSelection *selection,
+ gint idx)
+{
+ GtkWidget *box;
+ GtkListBoxRow *row;
+
+ box = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
+ if (box == NULL)
+ return FALSE;
+
+ row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (box), idx);
+ if (row)
+ {
+ gtk_list_box_select_row (GTK_LIST_BOX (box), row);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean
+gtk_list_box_accessible_clear_selection (AtkSelection *selection)
+{
+ GtkWidget *box;
+
+ box = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
+ if (box == NULL)
+ return FALSE;
+
+ gtk_list_box_select_row (GTK_LIST_BOX (box), NULL);
+ return TRUE;
+}
+
+static AtkObject *
+gtk_list_box_accessible_ref_selection (AtkSelection *selection,
+ gint idx)
+{
+ GtkWidget *box;
+ GtkListBoxRow *row;
+ AtkObject *accessible;
+
+ if (idx != 0)
+ return NULL;
+
+ box = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
+ if (box == NULL)
+ return NULL;
+
+ row = gtk_list_box_get_selected_row (GTK_LIST_BOX (box));
+ if (row == NULL)
+ return NULL;
+
+ accessible = gtk_widget_get_accessible (GTK_WIDGET (row));
+ g_object_ref (accessible);
+ return accessible;
+}
+
+static gint
+gtk_list_box_accessible_get_selection_count (AtkSelection *selection)
+{
+ GtkWidget *box;
+ GtkListBoxRow *row;
+
+ box = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
+ if (box == NULL)
+ return 0;
+
+ row = gtk_list_box_get_selected_row (GTK_LIST_BOX (box));
+ if (row == NULL)
+ return 0;
+
+ return 1;
+}
+
+static gboolean
+gtk_list_box_accessible_is_child_selected (AtkSelection *selection,
+ gint idx)
+{
+ GtkWidget *box;
+ GtkListBoxRow *row;
+
+ box = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
+ if (box == NULL)
+ return FALSE;
+
+ row = gtk_list_box_get_selected_row (GTK_LIST_BOX (box));
+ if (row == NULL)
+ return FALSE;
+
+ return row == gtk_list_box_get_row_at_index (GTK_LIST_BOX (box), idx);
+}
+
+static void atk_selection_interface_init (AtkSelectionIface *iface)
+{
+ iface->add_selection = gtk_list_box_accessible_add_selection;
+ iface->clear_selection = gtk_list_box_accessible_clear_selection;
+ iface->ref_selection = gtk_list_box_accessible_ref_selection;
+ iface->get_selection_count = gtk_list_box_accessible_get_selection_count;
+ iface->is_child_selected = gtk_list_box_accessible_is_child_selected;
+}
+
+void
+_gtk_list_box_accessible_selection_changed (GtkListBox *box)
+{
+ AtkObject *accessible;
+ accessible = gtk_widget_get_accessible (GTK_WIDGET (box));
+ g_signal_emit_by_name (accessible, "selection-changed");
+}
+
+void
+_gtk_list_box_accessible_update_cursor (GtkListBox *box,
+ GtkListBoxRow *row)
+{
+ AtkObject *accessible;
+ AtkObject *descendant;
+ accessible = gtk_widget_get_accessible (GTK_WIDGET (box));
+ descendant = row ? gtk_widget_get_accessible (GTK_WIDGET (row)) : NULL;
+ g_signal_emit_by_name (accessible, "active-descendant-changed", descendant);
+}
diff --git a/gtk/a11y/gtklistboxaccessible.h b/gtk/a11y/gtklistboxaccessible.h
new file mode 100644
index 0000000000..c0efbee6d3
--- /dev/null
+++ b/gtk/a11y/gtklistboxaccessible.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GTK_LIST_BOX_ACCESSIBLE_H__
+#define __GTK_LIST_BOX_ACCESSIBLE_H__
+
+#if !defined (__GTK_A11Y_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk-a11y.h> can be included directly."
+#endif
+
+#include <gtk/gtk.h>
+#include <gtk/a11y/gtkcontaineraccessible.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_LIST_BOX_ACCESSIBLE (gtk_list_box_accessible_get_type ())
+#define GTK_LIST_BOX_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_LIST_BOX_ACCESSIBLE, GtkListBoxAccessible))
+#define GTK_LIST_BOX_ACCESSIBLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_LIST_BOX_ACCESSIBLE, GtkListBoxAccessibleClass))
+#define GTK_IS_LIST_BOX_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_LIST_BOX_ACCESSIBLE))
+#define GTK_IS_LIST_BOX_ACCESSIBLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_LIST_BOX_ACCESSIBLE))
+#define GTK_LIST_BOX_ACCESSIBLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_LIST_BOX_ACCESSIBLE, GtkListBoxAccessibleClass))
+
+typedef struct _GtkListBoxAccessible GtkListBoxAccessible;
+typedef struct _GtkListBoxAccessibleClass GtkListBoxAccessibleClass;
+typedef struct _GtkListBoxAccessiblePrivate GtkListBoxAccessiblePrivate;
+
+struct _GtkListBoxAccessible
+{
+ GtkContainerAccessible parent;
+
+ GtkListBoxAccessiblePrivate *priv;
+};
+
+struct _GtkListBoxAccessibleClass
+{
+ GtkContainerAccessibleClass parent_class;
+};
+
+GDK_AVAILABLE_IN_ALL
+GType gtk_list_box_accessible_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GTK_LIST_BOX_ACCESSIBLE_H__ */
diff --git a/gtk/a11y/gtklistboxaccessibleprivate.h b/gtk/a11y/gtklistboxaccessibleprivate.h
new file mode 100644
index 0000000000..b85c2d40cf
--- /dev/null
+++ b/gtk/a11y/gtklistboxaccessibleprivate.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GTK_LIST_BOX_ACCESSIBLE_PRIVATE_H__
+#define __GTK_LIST_BOX_ACCESSIBLE_PRIVATE_H__
+
+#include <gtk/a11y/gtklistboxaccessible.h>
+
+G_BEGIN_DECLS
+
+void _gtk_list_box_accessible_update_selected (GtkListBox *box, GtkListBoxRow *child);
+void _gtk_list_box_accessible_update_cursor (GtkListBox *box, GtkListBoxRow *child);
+void _gtk_list_box_accessible_selection_changed (GtkListBox *box);
+
+G_END_DECLS
+
+#endif /* __GTK_LIST_BOX_ACCESSIBLE_PRIVATE_H__ */
diff --git a/gtk/gtk-a11y.h b/gtk/gtk-a11y.h
index dd47ddde7e..34278da7cd 100644
--- a/gtk/gtk-a11y.h
+++ b/gtk/gtk-a11y.h
@@ -46,6 +46,7 @@
#include <gtk/a11y/gtklabelaccessible.h>
#include <gtk/a11y/gtklevelbaraccessible.h>
#include <gtk/a11y/gtklinkbuttonaccessible.h>
+#include <gtk/a11y/gtklistboxaccessible.h>
#include <gtk/a11y/gtklockbuttonaccessible.h>
#include <gtk/a11y/gtkmenuaccessible.h>
#include <gtk/a11y/gtkmenuitemaccessible.h>
diff --git a/gtk/gtk.h b/gtk/gtk.h
index a0d2b4288e..1d4863906a 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -124,6 +124,7 @@
#include <gtk/gtklayout.h>
#include <gtk/gtklevelbar.h>
#include <gtk/gtklinkbutton.h>
+#include <gtk/gtklistbox.h>
#include <gtk/gtkliststore.h>
#include <gtk/gtklockbutton.h>
#include <gtk/gtkmain.h>
diff --git a/gtk/gtklistbox.c b/gtk/gtklistbox.c
new file mode 100644
index 0000000000..42987a5ffb
--- /dev/null
+++ b/gtk/gtklistbox.c
@@ -0,0 +1,2323 @@
+/*
+ * Copyright (C) 2012 Alexander Larsson <alexl@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+#include <gtkmarshalers.h>
+#include <float.h>
+#include <math.h>
+#include <string.h>
+
+#include "a11y/gtklistboxaccessibleprivate.h"
+
+/**
+ * SECTION:gtklistbox
+ * @Short_description: A list container
+ * @Title: GtkListBox
+ *
+ */
+
+struct _GtkListBoxPrivate
+{
+ GSequence *children;
+ GHashTable *separator_hash;
+
+ GtkListBoxSortFunc sort_func;
+ gpointer sort_func_target;
+ GDestroyNotify sort_func_target_destroy_notify;
+
+ GtkListBoxFilterFunc filter_func;
+ gpointer filter_func_target;
+ GDestroyNotify filter_func_target_destroy_notify;
+
+ GtkListBoxUpdateSeparatorFunc update_separator_func;
+ gpointer update_separator_func_target;
+ GDestroyNotify update_separator_func_target_destroy_notify;
+
+ GtkListBoxRow *selected_row;
+ GtkListBoxRow *prelight_row;
+ GtkListBoxRow *cursor_row;
+
+ gboolean active_row_active;
+ GtkListBoxRow *active_row;
+
+ GtkSelectionMode selection_mode;
+
+ GtkAdjustment *adjustment;
+ gboolean activate_single_click;
+
+ /* DnD */
+ GtkListBoxRow *drag_highlighted_row;
+ guint auto_scroll_timeout_id;
+};
+
+struct _GtkListBoxRowPrivate
+{
+ GSequenceIter *iter;
+ GtkWidget *separator;
+ gint y;
+ gint height;
+};
+
+enum {
+ ROW_SELECTED,
+ ROW_ACTIVATED,
+ ACTIVATE_CURSOR_ROW,
+ TOGGLE_CURSOR_ROW,
+ MOVE_CURSOR,
+ REFILTER,
+ LAST_SIGNAL
+};
+
+enum {
+ PROP_0,
+ PROP_SELECTION_MODE,
+ PROP_ACTIVATE_ON_SINGLE_CLICK,
+ LAST_PROPERTY
+};
+
+G_DEFINE_TYPE (GtkListBox, gtk_list_box, GTK_TYPE_CONTAINER)
+G_DEFINE_TYPE (GtkListBoxRow, gtk_list_box_row, GTK_TYPE_BIN)
+
+static void gtk_list_box_update_selected (GtkListBox *list_box,
+ GtkListBoxRow *row);
+static void gtk_list_box_apply_filter_all (GtkListBox *list_box);
+static void gtk_list_box_update_separator (GtkListBox *list_box,
+ GSequenceIter *iter);
+static GSequenceIter * gtk_list_box_get_next_visible (GtkListBox *list_box,
+ GSequenceIter *_iter);
+static void gtk_list_box_apply_filter (GtkListBox *list_box,
+ GtkListBoxRow *row);
+static void gtk_list_box_add_move_binding (GtkBindingSet *binding_set,
+ guint keyval,
+ GdkModifierType modmask,
+ GtkMovementStep step,
+ gint count);
+static void gtk_list_box_update_cursor (GtkListBox *list_box,
+ GtkListBoxRow *row);
+static void gtk_list_box_select_and_activate (GtkListBox *list_box,
+ GtkListBoxRow *row);
+static void gtk_list_box_update_prelight (GtkListBox *list_box,
+ GtkListBoxRow *row);
+static void gtk_list_box_update_active (GtkListBox *list_box,
+ GtkListBoxRow *row);
+static gboolean gtk_list_box_real_enter_notify_event (GtkWidget *widget,
+ GdkEventCrossing *event);
+static gboolean gtk_list_box_real_leave_notify_event (GtkWidget *widget,
+ GdkEventCrossing *event);
+static gboolean gtk_list_box_real_motion_notify_event (GtkWidget *widget,
+ GdkEventMotion *event);
+static gboolean gtk_list_box_real_button_press_event (GtkWidget *widget,
+ GdkEventButton *event);
+static gboolean gtk_list_box_real_button_release_event (GtkWidget *widget,
+ GdkEventButton *event);
+static void gtk_list_box_real_show (GtkWidget *widget);
+static gboolean gtk_list_box_real_focus (GtkWidget *widget,
+ GtkDirectionType direction);
+static GSequenceIter* gtk_list_box_get_previous_visible (GtkListBox *list_box,
+ GSequenceIter *_iter);
+static GtkListBoxRow *gtk_list_box_get_first_visible (GtkListBox *list_box);
+static GtkListBoxRow *gtk_list_box_get_last_visible (GtkListBox *list_box);
+static gboolean gtk_list_box_real_draw (GtkWidget *widget,
+ cairo_t *cr);
+static void gtk_list_box_real_realize (GtkWidget *widget);
+static void gtk_list_box_real_add (GtkContainer *container,
+ GtkWidget *widget);
+static void gtk_list_box_real_remove (GtkContainer *container,
+ GtkWidget *widget);
+static void gtk_list_box_real_forall_internal (GtkContainer *container,
+ gboolean include_internals,
+ GtkCallback callback,
+ void *callback_target);
+static void gtk_list_box_real_compute_expand_internal (GtkWidget *widget,
+ gboolean *hexpand,
+ gboolean *vexpand);
+static GType gtk_list_box_real_child_type (GtkContainer *container);
+static GtkSizeRequestMode gtk_list_box_real_get_request_mode (GtkWidget *widget);
+static void gtk_list_box_real_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+static void gtk_list_box_real_drag_leave (GtkWidget *widget,
+ GdkDragContext *context,
+ guint time_);
+static gboolean gtk_list_box_real_drag_motion (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time_);
+static void gtk_list_box_real_activate_cursor_row (GtkListBox *list_box);
+static void gtk_list_box_real_toggle_cursor_row (GtkListBox *list_box);
+static void gtk_list_box_real_move_cursor (GtkListBox *list_box,
+ GtkMovementStep step,
+ gint count);
+static void gtk_list_box_real_refilter (GtkListBox *list_box);
+static void gtk_list_box_finalize (GObject *obj);
+
+
+static void gtk_list_box_real_get_preferred_height (GtkWidget *widget,
+ gint *minimum_height,
+ gint *natural_height);
+static void gtk_list_box_real_get_preferred_height_for_width (GtkWidget *widget,
+ gint width,
+ gint *minimum_height,
+ gint *natural_height);
+static void gtk_list_box_real_get_preferred_width (GtkWidget *widget,
+ gint *minimum_width,
+ gint *natural_width);
+static void gtk_list_box_real_get_preferred_width_for_height (GtkWidget *widget,
+ gint height,
+ gint *minimum_width,
+ gint *natural_width);
+
+static GParamSpec *properties[LAST_PROPERTY] = { NULL, };
+static guint signals[LAST_SIGNAL] = { 0 };
+
+GtkWidget *
+gtk_list_box_new (void)
+{
+ return g_object_new (GTK_TYPE_LIST_BOX, NULL);
+}
+
+static void
+gtk_list_box_init (GtkListBox *list_box)
+{
+ GtkListBoxPrivate *priv;
+
+ list_box->priv = priv =
+ G_TYPE_INSTANCE_GET_PRIVATE (list_box, GTK_TYPE_LIST_BOX, GtkListBoxPrivate);
+
+ gtk_widget_set_has_window (GTK_WIDGET (list_box), TRUE);
+ gtk_widget_set_redraw_on_allocate (GTK_WIDGET (list_box), TRUE);
+ priv->selection_mode = GTK_SELECTION_SINGLE;
+ priv->activate_single_click = TRUE;
+
+ priv->children = g_sequence_new (NULL);
+ priv->separator_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
+}
+
+static void
+gtk_list_box_get_property (GObject *obj,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkListBox *list_box = GTK_LIST_BOX (obj);
+
+ switch (property_id)
+ {
+ case PROP_SELECTION_MODE:
+ g_value_set_enum (value, list_box->priv->selection_mode);
+ break;
+ case PROP_ACTIVATE_ON_SINGLE_CLICK:
+ g_value_set_boolean (value, list_box->priv->activate_single_click);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_list_box_set_property (GObject *obj,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkListBox *list_box = GTK_LIST_BOX (obj);
+
+ switch (property_id)
+ {
+ case PROP_SELECTION_MODE:
+ gtk_list_box_set_selection_mode (list_box, g_value_get_enum (value));
+ break;
+ case PROP_ACTIVATE_ON_SINGLE_CLICK:
+ gtk_list_box_set_activate_on_single_click (list_box, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_list_box_finalize (GObject *obj)
+{
+ GtkListBox *list_box = GTK_LIST_BOX (obj);
+ GtkListBoxPrivate *priv = list_box->priv;
+
+ if (priv->auto_scroll_timeout_id != ((guint) 0))
+ g_source_remove (priv->auto_scroll_timeout_id);
+
+ if (priv->sort_func_target_destroy_notify != NULL)
+ priv->sort_func_target_destroy_notify (priv->sort_func_target);
+ if (priv->filter_func_target_destroy_notify != NULL)
+ priv->filter_func_target_destroy_notify (priv->filter_func_target);
+ if (priv->update_separator_func_target_destroy_notify != NULL)
+ priv->update_separator_func_target_destroy_notify (priv->update_separator_func_target);
+
+ g_clear_object (&priv->adjustment);
+ g_clear_object (&priv->drag_highlighted_row);
+
+ g_sequence_free (priv->children);
+ g_hash_table_unref (priv->separator_hash);
+
+ G_OBJECT_CLASS (gtk_list_box_parent_class)->finalize (obj);
+}
+
+static void
+gtk_list_box_class_init (GtkListBoxClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
+ GtkBindingSet *binding_set;
+
+ g_type_class_add_private (klass, sizeof (GtkListBoxPrivate));
+
+ gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_LIST_BOX_ACCESSIBLE);
+
+ object_class->get_property = gtk_list_box_get_property;
+ object_class->set_property = gtk_list_box_set_property;
+ object_class->finalize = gtk_list_box_finalize;
+ widget_class->enter_notify_event = gtk_list_box_real_enter_notify_event;
+ widget_class->leave_notify_event = gtk_list_box_real_leave_notify_event;
+ widget_class->motion_notify_event = gtk_list_box_real_motion_notify_event;
+ widget_class->button_press_event = gtk_list_box_real_button_press_event;
+ widget_class->button_release_event = gtk_list_box_real_button_release_event;
+ widget_class->show = gtk_list_box_real_show;
+ widget_class->focus = gtk_list_box_real_focus;
+ widget_class->draw = gtk_list_box_real_draw;
+ widget_class->realize = gtk_list_box_real_realize;
+ widget_class->compute_expand = gtk_list_box_real_compute_expand_internal;
+ widget_class->get_request_mode = gtk_list_box_real_get_request_mode;
+ widget_class->get_preferred_height = gtk_list_box_real_get_preferred_height;
+ widget_class->get_preferred_height_for_width = gtk_list_box_real_get_preferred_height_for_width;
+ widget_class->get_preferred_width = gtk_list_box_real_get_preferred_width;
+ widget_class->get_preferred_width_for_height = gtk_list_box_real_get_preferred_width_for_height;
+ widget_class->size_allocate = gtk_list_box_real_size_allocate;
+ widget_class->drag_leave = gtk_list_box_real_drag_leave;
+ widget_class->drag_motion = gtk_list_box_real_drag_motion;
+ container_class->add = gtk_list_box_real_add;
+ container_class->remove = gtk_list_box_real_remove;
+ container_class->forall = gtk_list_box_real_forall_internal;
+ container_class->child_type = gtk_list_box_real_child_type;
+ klass->activate_cursor_row = gtk_list_box_real_activate_cursor_row;
+ klass->toggle_cursor_row = gtk_list_box_real_toggle_cursor_row;
+ klass->move_cursor = gtk_list_box_real_move_cursor;
+ klass->refilter = gtk_list_box_real_refilter;
+
+ properties[PROP_SELECTION_MODE] =
+ g_param_spec_enum ("selection-mode",
+ "Selection mode",
+ "The selection mode",
+ GTK_TYPE_SELECTION_MODE,
+ GTK_SELECTION_SINGLE,
+ G_PARAM_READWRITE);
+
+ properties[PROP_ACTIVATE_ON_SINGLE_CLICK] =
+ g_param_spec_boolean ("activate-on-single-click",
+ "Activate on Single Click",
+ "Activate row on a single click",
+ TRUE,
+ G_PARAM_READWRITE);
+
+ g_object_class_install_properties (object_class, LAST_PROPERTY, properties);
+
+ signals[ROW_SELECTED] =
+ g_signal_new ("row-selected",
+ GTK_TYPE_LIST_BOX,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkListBoxClass, row_selected),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ GTK_TYPE_WIDGET);
+ signals[ROW_ACTIVATED] =
+ g_signal_new ("row-activated",
+ GTK_TYPE_LIST_BOX,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkListBoxClass, row_activated),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ GTK_TYPE_WIDGET);
+ signals[ACTIVATE_CURSOR_ROW] =
+ g_signal_new ("activate-cursor-row",
+ GTK_TYPE_LIST_BOX,
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (GtkListBoxClass, activate_cursor_row),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ signals[TOGGLE_CURSOR_ROW] =
+ g_signal_new ("toggle-cursor-row",
+ GTK_TYPE_LIST_BOX,
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (GtkListBoxClass, toggle_cursor_row),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ signals[MOVE_CURSOR] =
+ g_signal_new ("move-cursor",
+ GTK_TYPE_LIST_BOX,
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (GtkListBoxClass, move_cursor),
+ NULL, NULL,
+ _gtk_marshal_VOID__ENUM_INT,
+ G_TYPE_NONE, 2,
+ GTK_TYPE_MOVEMENT_STEP, G_TYPE_INT);
+ signals[REFILTER] =
+ g_signal_new ("refilter",
+ GTK_TYPE_LIST_BOX,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkListBoxClass, refilter),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ widget_class->activate_signal = signals[ACTIVATE_CURSOR_ROW];
+
+ binding_set = gtk_binding_set_by_class (klass);
+ gtk_list_box_add_move_binding (binding_set, GDK_KEY_Home, 0,
+ GTK_MOVEMENT_BUFFER_ENDS, -1);
+ gtk_list_box_add_move_binding (binding_set, GDK_KEY_KP_Home, 0,
+ GTK_MOVEMENT_BUFFER_ENDS, -1);
+ gtk_list_box_add_move_binding (binding_set, GDK_KEY_End, 0,
+ GTK_MOVEMENT_BUFFER_ENDS, 1);
+ gtk_list_box_add_move_binding (binding_set, GDK_KEY_KP_End, 0,
+ GTK_MOVEMENT_BUFFER_ENDS, 1);
+ gtk_list_box_add_move_binding (binding_set, GDK_KEY_Up, GDK_CONTROL_MASK,
+ GTK_MOVEMENT_DISPLAY_LINES, -1);
+ gtk_list_box_add_move_binding (binding_set, GDK_KEY_KP_Up, GDK_CONTROL_MASK,
+ GTK_MOVEMENT_DISPLAY_LINES, -1);
+ gtk_list_box_add_move_binding (binding_set, GDK_KEY_Down, GDK_CONTROL_MASK,
+ GTK_MOVEMENT_DISPLAY_LINES, 1);
+ gtk_list_box_add_move_binding (binding_set, GDK_KEY_KP_Down, GDK_CONTROL_MASK,
+ GTK_MOVEMENT_DISPLAY_LINES, 1);
+ gtk_list_box_add_move_binding (binding_set, GDK_KEY_Page_Up, 0,
+ GTK_MOVEMENT_PAGES, -1);
+ gtk_list_box_add_move_binding (binding_set, GDK_KEY_KP_Page_Up, 0,
+ GTK_MOVEMENT_PAGES, -1);
+ gtk_list_box_add_move_binding (binding_set, GDK_KEY_Page_Down, 0,
+ GTK_MOVEMENT_PAGES, 1);
+ gtk_list_box_add_move_binding (binding_set, GDK_KEY_KP_Page_Down, 0,
+ GTK_MOVEMENT_PAGES, 1);
+ gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, GDK_CONTROL_MASK,
+ "toggle-cursor-row", 0, NULL);
+}
+
+/**
+ * gtk_list_box_get_selected_row:
+ * @list_box: An #GtkListBox.
+ *
+ * Gets the selected row.
+ *
+ * Return value: (transfer none): The selected #GtkWidget.
+ **/
+GtkListBoxRow *
+gtk_list_box_get_selected_row (GtkListBox *list_box)
+{
+ GtkListBoxPrivate *priv = list_box->priv;
+
+ g_return_val_if_fail (list_box != NULL, NULL);
+
+ if (priv->selected_row != NULL)
+ return priv->selected_row;
+
+ return NULL;
+}
+
+/**
+ * gtk_list_box_get_row_at_index:
+ * @list_box: An #GtkListBox.
+ * @index: the index of the row
+ *
+ * Gets the n:th child in the list (not counting separators).
+ *
+ * Return value: (transfer none): The child #GtkWidget.
+ **/
+GtkListBoxRow *
+gtk_list_box_get_row_at_index (GtkListBox *list_box, gint index)
+{
+ GtkListBoxPrivate *priv = list_box->priv;
+ GSequenceIter *iter;
+
+ g_return_val_if_fail (list_box != NULL, NULL);
+
+ iter = g_sequence_get_iter_at_pos (priv->children, index);
+ if (iter)
+ return g_sequence_get (iter);
+
+ return NULL;
+}
+
+/**
+ * gtk_list_box_get_row_at_y:
+ * @list_box: An #GtkListBox.
+ * @y: position
+ *
+ * Gets the row at the y position.
+ *
+ * Return value: (transfer none): The row.
+ **/
+GtkListBoxRow *
+gtk_list_box_get_row_at_y (GtkListBox *list_box, gint y)
+{
+ GtkListBoxRow *row, *found_row;
+ GtkListBoxRowPrivate *row_priv;
+ GtkListBoxPrivate *priv = list_box->priv;
+ GSequenceIter *iter;
+
+ g_return_val_if_fail (list_box != NULL, NULL);
+
+ /* TODO: This should use g_sequence_search */
+
+ found_row = NULL;
+ for (iter = g_sequence_get_begin_iter (priv->children);
+ !g_sequence_iter_is_end (iter);
+ iter = g_sequence_iter_next (iter))
+ {
+ row = (GtkListBoxRow*) g_sequence_get (iter);
+ row_priv = row->priv;
+ if (y >= row_priv->y && y < (row_priv->y + row_priv->height))
+ {
+ found_row = row;
+ break;
+ }
+ }
+
+ return found_row;
+}
+
+
+/**
+ * gtk_list_box_select_row:
+ * @list_box: a #GtkListBox
+ * @row: (allow-none): The row to select or %NULL
+ */
+void
+gtk_list_box_select_row (GtkListBox *list_box, GtkListBoxRow *row)
+{
+ g_return_if_fail (list_box != NULL);
+
+ gtk_list_box_update_selected (list_box, row);
+}
+
+void
+gtk_list_box_set_adjustment (GtkListBox *list_box,
+ GtkAdjustment *adjustment)
+{
+ GtkListBoxPrivate *priv = list_box->priv;
+
+ g_return_if_fail (list_box != NULL);
+
+ g_object_ref (adjustment);
+ if (priv->adjustment)
+ g_object_unref (priv->adjustment);
+ priv->adjustment = adjustment;
+ gtk_container_set_focus_vadjustment (GTK_CONTAINER (list_box),
+ adjustment);
+}
+
+GtkAdjustment *
+gtk_list_box_get_adjustment (GtkListBox *list_box)
+{
+ GtkListBoxPrivate *priv = list_box->priv;
+
+ g_return_val_if_fail (list_box != NULL, NULL);
+
+ return priv->adjustment;
+}
+
+void
+gtk_list_box_add_to_scrolled (GtkListBox *list_box,
+ GtkScrolledWindow *scrolled)
+{
+ g_return_if_fail (list_box != NULL);
+ g_return_if_fail (scrolled != NULL);
+
+ gtk_container_add (GTK_CONTAINER (scrolled), GTK_WIDGET (list_box));
+ gtk_list_box_set_adjustment (list_box,
+ gtk_scrolled_window_get_vadjustment (scrolled));
+}
+
+
+void
+gtk_list_box_set_selection_mode (GtkListBox *list_box, GtkSelectionMode mode)
+{
+ GtkListBoxPrivate *priv = list_box->priv;
+
+ g_return_if_fail (list_box != NULL);
+
+ if (mode == GTK_SELECTION_MULTIPLE)
+ {
+ g_warning ("Multiple selections not supported");
+ return;
+ }
+
+ if (priv->selection_mode == mode)
+ return;
+
+ priv->selection_mode = mode;
+ if (mode == GTK_SELECTION_NONE)
+ gtk_list_box_update_selected (list_box, NULL);
+
+ g_object_notify_by_pspec (G_OBJECT (list_box), properties[PROP_SELECTION_MODE]);
+}
+
+GtkSelectionMode
+gtk_list_box_get_selection_mode (GtkListBox *list_box)
+{
+ GtkListBoxPrivate *priv = list_box->priv;
+
+ g_return_val_if_fail (list_box != NULL, 0);
+
+ return priv->selection_mode;
+}
+
+/**
+ * gtk_list_box_set_filter_func:
+ * @filter_func: (allow-none):
+ * @user_data:
+ * @destroy:
+ */
+void
+gtk_list_box_set_filter_func (GtkListBox *list_box,
+ GtkListBoxFilterFunc filter_func,
+ gpointer user_data,
+ GDestroyNotify destroy)
+{
+ GtkListBoxPrivate *priv = list_box->priv;
+
+ g_return_if_fail (list_box != NULL);
+
+ if (priv->filter_func_target_destroy_notify != NULL)
+ priv->filter_func_target_destroy_notify (priv->filter_func_target);
+
+ priv->filter_func = filter_func;
+ priv->filter_func_target = user_data;
+ priv->filter_func_target_destroy_notify = destroy;
+
+ gtk_list_box_refilter (list_box);
+}
+
+/**
+ * gtk_list_box_set_separator_func:
+ * @update_separator: (allow-none):
+ * @user_data:
+ * @destroy:
+ */
+void
+gtk_list_box_set_separator_func (GtkListBox *list_box,
+ GtkListBoxUpdateSeparatorFunc update_separator,
+ gpointer user_data,
+ GDestroyNotify destroy)
+{
+ GtkListBoxPrivate *priv = list_box->priv;
+
+ g_return_if_fail (list_box != NULL);
+
+ if (priv->update_separator_func_target_destroy_notify != NULL)
+ priv->update_separator_func_target_destroy_notify (priv->update_separator_func_target);
+
+ priv->update_separator_func = update_separator;
+ priv->update_separator_func_target = user_data;
+ priv->update_separator_func_target_destroy_notify = destroy;
+ gtk_list_box_reseparate (list_box);
+}
+
+static void
+gtk_list_box_real_refilter (GtkListBox *list_box)
+{
+ gtk_list_box_apply_filter_all (list_box);
+ gtk_list_box_reseparate (list_box);
+ gtk_widget_queue_resize (GTK_WIDGET (list_box));
+}
+
+void
+gtk_list_box_refilter (GtkListBox *list_box)
+{
+ g_return_if_fail (list_box != NULL);
+
+ g_signal_emit (list_box, signals[REFILTER], 0);
+}
+
+static gint
+do_sort (GtkListBoxRow *a,
+ GtkListBoxRow *b,
+ GtkListBox *list_box)
+{
+ GtkListBoxPrivate *priv = list_box->priv;
+
+ return priv->sort_func (a, b, priv->sort_func_target);
+}
+
+void
+gtk_list_box_resort (GtkListBox *list_box)
+{
+ GtkListBoxPrivate *priv = list_box->priv;
+
+ g_return_if_fail (list_box != NULL);
+
+ g_sequence_sort (priv->children,
+ (GCompareDataFunc)do_sort, list_box);
+ gtk_list_box_reseparate (list_box);
+ gtk_widget_queue_resize (GTK_WIDGET (list_box));
+}
+
+void
+gtk_list_box_reseparate (GtkListBox *list_box)
+{
+ GtkListBoxPrivate *priv = list_box->priv;
+ GSequenceIter *iter;
+
+ g_return_if_fail (list_box != NULL);
+
+ for (iter = g_sequence_get_begin_iter (priv->children);
+ !g_sequence_iter_is_end (iter);
+ iter = g_sequence_iter_next (iter))
+ gtk_list_box_update_separator (list_box, iter);
+
+ gtk_widget_queue_resize (GTK_WIDGET (list_box));
+}
+
+/**
+ * gtk_list_box_set_sort_func:
+ * @sort_func: (closure user_data) (allow-none):
+ * @user_data:
+ * @destroy:
+ */
+void
+gtk_list_box_set_sort_func (GtkListBox *list_box,
+ GtkListBoxSortFunc sort_func,
+ gpointer user_data,
+ GDestroyNotify destroy)
+{
+ GtkListBoxPrivate *priv = list_box->priv;
+
+ g_return_if_fail (list_box != NULL);
+
+ if (priv->sort_func_target_destroy_notify != NULL)
+ priv->sort_func_target_destroy_notify (priv->sort_func_target);
+
+ priv->sort_func = sort_func;
+ priv->sort_func_target = user_data;
+ priv->sort_func_target_destroy_notify = destroy;
+ gtk_list_box_resort (list_box);
+}
+
+static void
+gtk_list_box_got_row_changed (GtkListBox *list_box, GtkListBoxRow *row)
+{
+ GtkListBoxPrivate *priv = list_box->priv;
+ GSequenceIter *prev_next, *next;
+
+ g_return_if_fail (list_box != NULL);
+ g_return_if_fail (row != NULL);
+
+ prev_next = gtk_list_box_get_next_visible (list_box, row->priv->iter);
+ if (priv->sort_func != NULL)
+ {
+ g_sequence_sort_changed (row->priv->iter,
+ (GCompareDataFunc)do_sort,
+ list_box);
+ gtk_widget_queue_resize (GTK_WIDGET (list_box));
+ }
+ gtk_list_box_apply_filter (list_box, row);
+ if (gtk_widget_get_visible (GTK_WIDGET (list_box)))
+ {
+ next = gtk_list_box_get_next_visible (list_box, row->priv->iter);
+ gtk_list_box_update_separator (list_box, row->priv->iter);
+ gtk_list_box_update_separator (list_box, next);
+ gtk_list_box_update_separator (list_box, prev_next);
+ }
+}
+
+void
+gtk_list_box_set_activate_on_single_click (GtkListBox *list_box,
+ gboolean single)
+{
+ GtkListBoxPrivate *priv = list_box->priv;
+
+ g_return_if_fail (list_box != NULL);
+
+ single = single != FALSE;
+
+ if (priv->activate_single_click == single)
+ return;
+
+ priv->activate_single_click = single;
+
+ g_object_notify_by_pspec (G_OBJECT (list_box), properties[PROP_ACTIVATE_ON_SINGLE_CLICK]);
+}
+
+static void
+gtk_list_box_add_move_binding (GtkBindingSet *binding_set,
+ guint keyval,
+ GdkModifierType modmask,
+ GtkMovementStep step,
+ gint count)
+{
+ gtk_binding_entry_add_signal (binding_set, keyval, modmask,
+ "move-cursor", (guint) 2, GTK_TYPE_MOVEMENT_STEP, step, G_TYPE_INT, count, NULL);
+
+ if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
+ return;
+
+ gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK,
+ "move-cursor", (guint) 2, GTK_TYPE_MOVEMENT_STEP, step, G_TYPE_INT, count, NULL);
+}
+
+static void
+gtk_list_box_update_cursor (GtkListBox *list_box,
+ GtkListBoxRow *row)
+{
+ GtkListBoxPrivate *priv = list_box->priv;
+
+ priv->cursor_row = row;
+ gtk_widget_grab_focus (GTK_WIDGET (row));
+ gtk_widget_queue_draw (GTK_WIDGET (row));
+ if (row != NULL && priv->adjustment != NULL)
+ {
+ GtkAllocation allocation;
+ gtk_widget_get_allocation (GTK_WIDGET (list_box), &allocation);
+ gtk_adjustment_clamp_page (priv->adjustment,
+ priv->cursor_row->priv->y + allocation.y,
+ priv->cursor_row->priv->y + allocation.y + priv->cursor_row->priv->height);
+ }
+ _gtk_list_box_accessible_update_cursor (list_box, row);
+}
+
+static void
+gtk_list_box_update_selected (GtkListBox *list_box,
+ GtkListBoxRow *row)
+{
+ GtkListBoxPrivate *priv = list_box->priv;
+
+ if (row != priv->selected_row &&
+ (row == NULL || priv->selection_mode != GTK_SELECTION_NONE))
+ {
+ if (priv->selected_row)
+ gtk_widget_unset_state_flags (GTK_WIDGET (priv->selected_row),
+ GTK_STATE_FLAG_SELECTED);
+ priv->selected_row = row;
+ if (priv->selected_row)
+ gtk_widget_set_state_flags (GTK_WIDGET (priv->selected_row),
+ GTK_STATE_FLAG_SELECTED,
+ FALSE);
+ g_signal_emit (list_box, signals[ROW_SELECTED], 0,
+ priv->selected_row);
+ gtk_widget_queue_draw (GTK_WIDGET (list_box));
+ }
+ _gtk_list_box_accessible_selection_changed (list_box);
+ if (row != NULL)
+ gtk_list_box_update_cursor (list_box, row);
+}
+
+static void
+gtk_list_box_select_and_activate (GtkListBox *list_box, GtkListBoxRow *row)
+{
+ gtk_list_box_update_selected (list_box, row);
+
+ if (row != NULL)
+ g_signal_emit (list_box, signals[ROW_ACTIVATED], 0, row);
+}
+
+static void
+gtk_list_box_update_prelight (GtkListBox *list_box, GtkListBoxRow *row)
+{
+ GtkListBoxPrivate *priv = list_box->priv;
+
+ if (row != priv->prelight_row)
+ {
+ if (priv->prelight_row)
+ gtk_widget_unset_state_flags (GTK_WIDGET (priv->prelight_row),
+ GTK_STATE_FLAG_PRELIGHT);
+ priv->prelight_row = row;
+ if (priv->prelight_row)
+ gtk_widget_set_state_flags (GTK_WIDGET (priv->prelight_row),
+ GTK_STATE_FLAG_PRELIGHT,
+ FALSE);
+ gtk_widget_queue_draw (GTK_WIDGET (list_box));
+ }
+}
+
+static void
+gtk_list_box_update_active (GtkListBox *list_box, GtkListBoxRow *row)
+{
+ GtkListBoxPrivate *priv = list_box->priv;
+ gboolean val;
+
+ val = priv->active_row == row;
+ if (priv->active_row != NULL &&
+ val != priv->active_row_active)
+ {
+ priv->active_row_active = val;
+ if (priv->active_row_active)
+ gtk_widget_set_state_flags (GTK_WIDGET (priv->active_row),
+ GTK_STATE_FLAG_ACTIVE,
+ FALSE);
+ else
+ gtk_widget_unset_state_flags (GTK_WIDGET (priv->active_row),
+ GTK_STATE_FLAG_ACTIVE);
+ gtk_widget_queue_draw (GTK_WIDGET (list_box));
+ }
+}
+
+static gboolean
+gtk_list_box_real_enter_notify_event (GtkWidget *widget,
+ GdkEventCrossing *event)
+{
+ GtkListBox *list_box = GTK_LIST_BOX (widget);
+ GtkListBoxRow *row;
+
+
+ if (event->window != gtk_widget_get_window (GTK_WIDGET (list_box)))
+ return FALSE;
+
+ row = gtk_list_box_get_row_at_y (list_box, event->y);
+ gtk_list_box_update_prelight (list_box, row);
+ gtk_list_box_update_active (list_box, row);
+
+ return FALSE;
+}
+
+static gboolean
+gtk_list_box_real_leave_notify_event (GtkWidget *widget,
+ GdkEventCrossing *event)
+{
+ GtkListBox *list_box = GTK_LIST_BOX (widget);
+ GtkListBoxRow *row = NULL;
+
+ if (event->window != gtk_widget_get_window (GTK_WIDGET (list_box)))
+ return FALSE;
+
+ if (event->detail != GDK_NOTIFY_INFERIOR)
+ row = NULL;
+ else
+ row = gtk_list_box_get_row_at_y (list_box, event->y);
+
+ gtk_list_box_update_prelight (list_box, row);
+ gtk_list_box_update_active (list_box, row);
+
+ return FALSE;
+}
+
+static gboolean
+gtk_list_box_real_motion_notify_event (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ GtkListBox *list_box = GTK_LIST_BOX (widget);
+ GtkListBoxRow *row;
+ GdkWindow *window, *event_window;
+ gint relative_y;
+ gdouble parent_y;
+
+ window = gtk_widget_get_window (GTK_WIDGET (list_box));
+ event_window = event->window;
+ relative_y = event->y;
+
+ while ((event_window != NULL) && (event_window != window))
+ {
+ gdk_window_coords_to_parent (event_window, 0, relative_y, NULL, &parent_y);
+ relative_y = parent_y;
+ event_window = gdk_window_get_effective_parent (event_window);
+ }
+
+ row = gtk_list_box_get_row_at_y (list_box, relative_y);
+ gtk_list_box_update_prelight (list_box, row);
+ gtk_list_box_update_active (list_box, row);
+
+ return FALSE;
+}
+
+static gboolean
+gtk_list_box_real_button_press_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GtkListBox *list_box = GTK_LIST_BOX (widget);
+ GtkListBoxPrivate *priv = list_box->priv;
+
+ if (event->button == GDK_BUTTON_PRIMARY)
+ {
+ GtkListBoxRow *row;
+ row = gtk_list_box_get_row_at_y (list_box, event->y);
+ if (row != NULL)
+ {
+ priv->active_row = row;
+ priv->active_row_active = TRUE;
+ gtk_widget_set_state_flags (GTK_WIDGET (priv->active_row),
+ GTK_STATE_FLAG_ACTIVE,
+ FALSE);
+ gtk_widget_queue_draw (GTK_WIDGET (list_box));
+ if (event->type == GDK_2BUTTON_PRESS &&
+ !priv->activate_single_click)
+ g_signal_emit (list_box, signals[ROW_ACTIVATED], 0,
+ row);
+
+ }
+ /* TODO:
+ Should mark as active while down,
+ and handle grab breaks */
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gtk_list_box_real_button_release_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GtkListBox *list_box = GTK_LIST_BOX (widget);
+ GtkListBoxPrivate *priv = list_box->priv;
+
+ if (event->button == GDK_BUTTON_PRIMARY)
+ {
+ if (priv->active_row != NULL &&
+ priv->active_row_active)
+ {
+ if (priv->activate_single_click)
+ gtk_list_box_select_and_activate (list_box, priv->active_row);
+ else
+ gtk_list_box_update_selected (list_box, priv->active_row);
+ }
+ if (priv->active_row_active)
+ gtk_widget_unset_state_flags (GTK_WIDGET (priv->active_row),
+ GTK_STATE_FLAG_ACTIVE);
+ priv->active_row = NULL;
+ priv->active_row_active = FALSE;
+ gtk_widget_queue_draw (GTK_WIDGET (list_box));
+ }
+
+ return FALSE;
+}
+
+static void
+gtk_list_box_real_show (GtkWidget *widget)
+{
+ GtkListBox * list_box = GTK_LIST_BOX (widget);
+
+ gtk_list_box_reseparate (list_box);
+
+ GTK_WIDGET_CLASS (gtk_list_box_parent_class)->show (widget);
+}
+
+static gboolean
+gtk_list_box_real_focus (GtkWidget* widget, GtkDirectionType direction)
+{
+ GtkListBox *list_box = GTK_LIST_BOX (widget);
+ GtkListBoxPrivate *priv = list_box->priv;
+ GtkWidget *focus_child;
+ GtkListBoxRow *next_focus_row;
+
+ focus_child = gtk_container_get_focus_child ((GtkContainer*) list_box);
+ next_focus_row = NULL;
+ if (focus_child != NULL)
+ {
+ GSequenceIter* i;
+
+ if (gtk_widget_child_focus (focus_child, direction))
+ return TRUE;
+
+ if (direction == GTK_DIR_UP || direction == GTK_DIR_TAB_BACKWARD)
+ {
+ i = gtk_list_box_get_previous_visible (list_box, priv->cursor_row->priv->iter);
+ if (i != NULL)
+ next_focus_row = g_sequence_get (i);
+ }
+ else if (direction == GTK_DIR_DOWN || direction == GTK_DIR_TAB_FORWARD)
+ {
+ i = gtk_list_box_get_next_visible (list_box, priv->cursor_row->priv->iter);
+ if (!g_sequence_iter_is_end (i))
+ next_focus_row = g_sequence_get (i);
+ }
+ }
+ else
+ {
+ /* No current focus row */
+
+ switch (direction)
+ {
+ case GTK_DIR_UP:
+ case GTK_DIR_TAB_BACKWARD:
+ next_focus_row = priv->selected_row;
+ if (next_focus_row == NULL)
+ next_focus_row = gtk_list_box_get_last_visible (list_box);
+ break;
+ default:
+ next_focus_row = priv->selected_row;
+ if (next_focus_row == NULL)
+ next_focus_row =
+ gtk_list_box_get_first_visible (list_box);
+ break;
+ }
+ }
+
+ if (next_focus_row == NULL)
+ {
+ if (direction == GTK_DIR_UP || direction == GTK_DIR_DOWN)
+ {
+ if (gtk_widget_keynav_failed (GTK_WIDGET (list_box), direction))
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ if (gtk_widget_child_focus (GTK_WIDGET (next_focus_row), direction))
+ return TRUE;
+
+ return TRUE;
+}
+
+static gboolean
+gtk_list_box_real_draw (GtkWidget* widget, cairo_t* cr)
+{
+ GtkAllocation allocation;
+ GtkStyleContext *context;
+
+ gtk_widget_get_allocation (widget, &allocation);
+ context = gtk_widget_get_style_context (widget);
+ gtk_render_background (context, cr, 0, 0, allocation.width, allocation.height);
+
+ GTK_WIDGET_CLASS (gtk_list_box_parent_class)->draw (widget, cr);
+
+ return TRUE;
+}
+
+static void
+gtk_list_box_real_realize (GtkWidget* widget)
+{
+ GtkListBox *list_box = GTK_LIST_BOX (widget);
+ GtkAllocation allocation;
+ GdkWindowAttr attributes = {0};
+ GdkWindow *window;
+
+ gtk_widget_get_allocation (GTK_WIDGET (list_box), &allocation);
+ gtk_widget_set_realized (GTK_WIDGET (list_box), TRUE);
+
+ attributes.x = allocation.x;
+ attributes.y = allocation.y;
+ attributes.width = allocation.width;
+ attributes.height = allocation.height;
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.event_mask = gtk_widget_get_events (GTK_WIDGET (list_box)) |
+ GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK |
+ GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+
+ window = gdk_window_new (gtk_widget_get_parent_window (GTK_WIDGET (list_box)),
+ &attributes, GDK_WA_X | GDK_WA_Y);
+ gtk_style_context_set_background (gtk_widget_get_style_context (GTK_WIDGET (list_box)), window);
+ gdk_window_set_user_data (window, (GObject*) list_box);
+ gtk_widget_set_window (GTK_WIDGET (list_box), window); /* Passes ownership */
+}
+
+
+static void
+gtk_list_box_apply_filter (GtkListBox *list_box, GtkListBoxRow *row)
+{
+ GtkListBoxPrivate *priv = list_box->priv;
+ gboolean do_show;
+
+ do_show = TRUE;
+ if (priv->filter_func != NULL)
+ do_show = priv->filter_func (row, priv->filter_func_target);
+
+ gtk_widget_set_child_visible (GTK_WIDGET (row), do_show);
+}
+
+static void
+gtk_list_box_apply_filter_all (GtkListBox *list_box)
+{
+ GtkListBoxPrivate *priv = list_box->priv;
+ GtkListBoxRow *row;
+ GSequenceIter *iter;
+
+ for (iter = g_sequence_get_begin_iter (priv->children);
+ !g_sequence_iter_is_end (iter);
+ iter = g_sequence_iter_next (iter))
+ {
+ row = g_sequence_get (iter);
+ gtk_list_box_apply_filter (list_box, row);
+ }
+}
+
+/* Children are visible if they are shown by the app (visible)
+ and not filtered out (child_visible) by the listbox */
+static gboolean
+row_is_visible (GtkListBoxRow *row)
+{
+ return gtk_widget_get_visible (GTK_WIDGET (row)) && gtk_widget_get_child_visible (GTK_WIDGET (row));
+}
+
+static GtkListBoxRow *
+gtk_list_box_get_first_visible (GtkListBox *list_box)
+{
+ GtkListBoxPrivate *priv = list_box->priv;
+ GtkListBoxRow *row;
+ GSequenceIter *iter;
+
+ for (iter = g_sequence_get_begin_iter (priv->children);
+ !g_sequence_iter_is_end (iter);
+ iter = g_sequence_iter_next (iter))
+ {
+ row = g_sequence_get (iter);
+ if (row_is_visible (row))
+ return row;
+ }
+
+ return NULL;
+}
+
+
+static GtkListBoxRow *
+gtk_list_box_get_last_visible (GtkListBox *list_box)
+{
+ GtkListBoxPrivate *priv = list_box->priv;
+ GtkListBoxRow *row;
+ GSequenceIter *iter;
+
+ iter = g_sequence_get_end_iter (priv->children);
+ while (!g_sequence_iter_is_begin (iter))
+ {
+ iter = g_sequence_iter_prev (iter);
+ row = g_sequence_get (iter);
+ if (row_is_visible (row))
+ return row;
+ }
+
+ return NULL;
+}
+
+static GSequenceIter*
+gtk_list_box_get_previous_visible (GtkListBox *list_box,
+ GSequenceIter* iter)
+{
+ GtkListBoxRow *row;
+
+ if (g_sequence_iter_is_begin (iter))
+ return NULL;
+
+ do
+ {
+ iter = g_sequence_iter_prev (iter);
+ row = g_sequence_get (iter);
+ if (row_is_visible (row))
+ return iter;
+ }
+ while (!g_sequence_iter_is_begin (iter));
+
+ return NULL;
+}
+
+static GSequenceIter*
+gtk_list_box_get_next_visible (GtkListBox *list_box, GSequenceIter* iter)
+{
+ GtkListBoxRow *row;
+
+ if (g_sequence_iter_is_end (iter))
+ return iter;
+
+ do
+ {
+ iter = g_sequence_iter_next (iter);
+ if (!g_sequence_iter_is_end (iter))
+ {
+ row = g_sequence_get (iter);
+ if (row_is_visible (row))
+ return iter;
+ }
+ }
+ while (!g_sequence_iter_is_end (iter));
+
+ return iter;
+}
+
+
+static void
+gtk_list_box_update_separator (GtkListBox *list_box, GSequenceIter* iter)
+{
+ GtkListBoxPrivate *priv = list_box->priv;
+ GtkListBoxRow *row;
+ GSequenceIter *before_iter;
+ GtkListBoxRow *before_row;
+ GtkWidget *old_separator;
+
+ if (iter == NULL || g_sequence_iter_is_end (iter))
+ return;
+
+ row = g_sequence_get (iter);
+ before_iter = gtk_list_box_get_previous_visible (list_box, iter);
+ if (row)
+ g_object_ref (row);
+ before_row = NULL;
+ if (before_iter != NULL)
+ {
+ before_row = g_sequence_get (before_iter);
+ if (before_row)
+ g_object_ref (before_row);
+ }
+
+ if (priv->update_separator_func != NULL &&
+ row_is_visible (row))
+ {
+ old_separator = row->priv->separator;
+ if (old_separator)
+ g_object_ref (old_separator);
+ priv->update_separator_func (row,
+ before_row,
+ priv->update_separator_func_target);
+ if (old_separator != row->priv->separator)
+ {
+ if (old_separator != NULL)
+ {
+ gtk_widget_unparent (old_separator);
+ g_hash_table_remove (priv->separator_hash, old_separator);
+ }
+ if (row->priv->separator != NULL)
+ {
+ g_hash_table_insert (priv->separator_hash, row->priv->separator, row);
+ gtk_widget_set_parent (row->priv->separator, GTK_WIDGET (list_box));
+ gtk_widget_show (row->priv->separator);
+ }
+ gtk_widget_queue_resize (GTK_WIDGET (list_box));
+ }
+ if (old_separator)
+ g_object_unref (old_separator);
+ }
+ else
+ {
+ if (row->priv->separator != NULL)
+ {
+ g_hash_table_remove (priv->separator_hash, row->priv->separator);
+ gtk_widget_unparent (row->priv->separator);
+ gtk_list_box_row_set_separator (row, NULL);
+ gtk_widget_queue_resize (GTK_WIDGET (list_box));
+ }
+ }
+ if (before_row)
+ g_object_unref (before_row);
+ if (row)
+ g_object_unref (row);
+}
+
+static void
+gtk_list_box_row_visibility_changed (GtkListBox *list_box, GtkListBoxRow *row)
+{
+ if (gtk_widget_get_visible (GTK_WIDGET (list_box)))
+ {
+ gtk_list_box_update_separator (list_box, row->priv->iter);
+ gtk_list_box_update_separator (list_box,
+ gtk_list_box_get_next_visible (list_box, row->priv->iter));
+ }
+}
+
+static void
+gtk_list_box_real_add (GtkContainer* container, GtkWidget *child)
+{
+ GtkListBox *list_box = GTK_LIST_BOX (container);
+ GtkListBoxPrivate *priv = list_box->priv;
+ GtkListBoxRow *row;
+ GSequenceIter* iter = NULL;
+
+ if (GTK_IS_LIST_BOX_ROW (child))
+ row = GTK_LIST_BOX_ROW (child);
+ else
+ {
+ row = GTK_LIST_BOX_ROW (gtk_list_box_row_new ());
+ gtk_widget_show (GTK_WIDGET (row));
+ gtk_container_add (GTK_CONTAINER (row), child);
+ }
+
+ if (priv->sort_func != NULL)
+ iter = g_sequence_insert_sorted (priv->children, row,
+ (GCompareDataFunc)do_sort, list_box);
+ else
+ iter = g_sequence_append (priv->children, row);
+
+ row->priv->iter = iter;
+ gtk_widget_set_parent (GTK_WIDGET (row), GTK_WIDGET (list_box));
+ gtk_list_box_apply_filter (list_box, row);
+ gtk_list_box_row_visibility_changed (list_box, row);
+}
+
+static void
+gtk_list_box_real_remove (GtkContainer* container, GtkWidget* child)
+{
+ GtkListBox *list_box = GTK_LIST_BOX (container);
+ GtkListBoxPrivate *priv = list_box->priv;
+ gboolean was_visible;
+ GtkListBoxRow *row;
+ GSequenceIter *next;
+
+ g_return_if_fail (child != NULL);
+ was_visible = gtk_widget_get_visible (child);
+
+ if (!GTK_IS_LIST_BOX_ROW (child))
+ {
+ row = g_hash_table_lookup (priv->separator_hash, child);
+ if (row != NULL)
+ {
+ g_hash_table_remove (priv->separator_hash, child);
+ g_clear_object (&row->priv->separator);
+ gtk_widget_unparent (child);
+ if (was_visible && gtk_widget_get_visible (GTK_WIDGET (list_box)))
+ gtk_widget_queue_resize (GTK_WIDGET (list_box));
+ }
+ else
+ {
+ g_warning ("Tried to remove non-child %p\n", child);
+ }
+ return;
+ }
+
+ row = GTK_LIST_BOX_ROW (child);
+ if (g_sequence_iter_get_sequence (row->priv->iter) != priv->children)
+ {
+ g_warning ("Tried to remove non-child %p\n", child);
+ return;
+ }
+
+ if (row->priv->separator != NULL)
+ {
+ g_hash_table_remove (priv->separator_hash, row->priv->separator);
+ gtk_widget_unparent (row->priv->separator);
+ g_clear_object (&row->priv->separator);
+ }
+
+ if (row == priv->selected_row)
+ gtk_list_box_update_selected (list_box, NULL);
+ if (row == priv->prelight_row) {
+ gtk_widget_unset_state_flags (GTK_WIDGET (priv->prelight_row),
+ GTK_STATE_FLAG_PRELIGHT);
+ priv->prelight_row = NULL;
+ }
+ if (row == priv->cursor_row)
+ priv->cursor_row = NULL;
+ if (row == priv->active_row) {
+ if (priv->active_row_active)
+ gtk_widget_unset_state_flags (GTK_WIDGET (priv->active_row),
+ GTK_STATE_FLAG_ACTIVE);
+ priv->active_row = NULL;
+ }
+
+ next = gtk_list_box_get_next_visible (list_box, row->priv->iter);
+ gtk_widget_unparent (child);
+ g_sequence_remove (row->priv->iter);
+ if (gtk_widget_get_visible (GTK_WIDGET (list_box)))
+ gtk_list_box_update_separator (list_box, next);
+
+ if (was_visible && gtk_widget_get_visible (GTK_WIDGET (list_box)))
+ gtk_widget_queue_resize (GTK_WIDGET (list_box));
+}
+
+
+static void
+gtk_list_box_real_forall_internal (GtkContainer* container,
+ gboolean include_internals,
+ GtkCallback callback,
+ void* callback_target)
+{
+ GtkListBox *list_box = GTK_LIST_BOX (container);
+ GtkListBoxPrivate *priv = list_box->priv;
+ GSequenceIter *iter;
+ GtkListBoxRow *row;
+
+ iter = g_sequence_get_begin_iter (priv->children);
+ while (!g_sequence_iter_is_end (iter))
+ {
+ row = g_sequence_get (iter);
+ iter = g_sequence_iter_next (iter);
+ if (row->priv->separator != NULL && include_internals)
+ callback (row->priv->separator, callback_target);
+ callback (GTK_WIDGET (row), callback_target);
+ }
+}
+
+static void
+gtk_list_box_real_compute_expand_internal (GtkWidget* widget,
+ gboolean* hexpand,
+ gboolean* vexpand)
+{
+ GTK_WIDGET_CLASS (gtk_list_box_parent_class)->compute_expand (widget,
+ hexpand, vexpand);
+
+ /* We don't expand vertically beyound the minimum size */
+ if (vexpand)
+ *vexpand = FALSE;
+}
+
+static GType
+gtk_list_box_real_child_type (GtkContainer* container)
+{
+ /* We really support any type but we wrap it in a row. But that is more
+ like a C helper function, in an abstract sense we only support
+ row children, so that is what tools accessing this should use. */
+ return GTK_TYPE_LIST_BOX_ROW;
+}
+
+static GtkSizeRequestMode
+gtk_list_box_real_get_request_mode (GtkWidget* widget)
+{
+ return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
+}
+
+static void
+gtk_list_box_real_get_preferred_height (GtkWidget* widget,
+ gint* minimum_height,
+ gint* natural_height)
+{
+ gint natural_width;
+ gtk_list_box_real_get_preferred_width (widget, NULL, &natural_width);
+ gtk_list_box_real_get_preferred_height_for_width (widget, natural_width,
+ minimum_height, natural_height);
+}
+
+static void
+gtk_list_box_real_get_preferred_height_for_width (GtkWidget* widget, gint width,
+ gint* minimum_height_out, gint* natural_height_out)
+{
+ GtkListBox *list_box = GTK_LIST_BOX (widget);
+ GtkListBoxPrivate *priv = list_box->priv;
+ GSequenceIter *iter;
+ gint minimum_height;
+ gint natural_height;
+
+ minimum_height = 0;
+
+ for (iter = g_sequence_get_begin_iter (priv->children);
+ !g_sequence_iter_is_end (iter);
+ iter = g_sequence_iter_next (iter))
+ {
+ GtkListBoxRow *row;
+ gint row_min = 0;
+
+ row = g_sequence_get (iter);
+ if (!row_is_visible (row))
+ continue;
+
+ if (row->priv->separator != NULL)
+ {
+ gtk_widget_get_preferred_height_for_width (row->priv->separator, width, &row_min, NULL);
+ minimum_height += row_min;
+ }
+ gtk_widget_get_preferred_height_for_width (GTK_WIDGET (row), width,
+ &row_min, NULL);
+ minimum_height += row_min;
+ }
+
+ /* We always allocate the minimum height, since handling
+ expanding rows is way too costly, and unlikely to
+ be used, as lists are generally put inside a scrolling window
+ anyway.
+ */
+ natural_height = minimum_height;
+ if (minimum_height_out)
+ *minimum_height_out = minimum_height;
+ if (natural_height_out)
+ *natural_height_out = natural_height;
+}
+
+static void
+gtk_list_box_real_get_preferred_width (GtkWidget* widget, gint* minimum_width_out, gint* natural_width_out)
+{
+ GtkListBox *list_box = GTK_LIST_BOX (widget);
+ GtkListBoxPrivate *priv = list_box->priv;
+ gint minimum_width;
+ gint natural_width;
+ GSequenceIter *iter;
+ GtkListBoxRow *row;
+ gint row_min;
+ gint row_nat;
+
+ minimum_width = 0;
+ natural_width = 0;
+
+ for (iter = g_sequence_get_begin_iter (priv->children);
+ !g_sequence_iter_is_end (iter);
+ iter = g_sequence_iter_next (iter))
+ {
+ row = g_sequence_get (iter);
+ if (!row_is_visible (row))
+ continue;
+
+ gtk_widget_get_preferred_width (GTK_WIDGET (row), &row_min, &row_nat);
+ minimum_width = MAX (minimum_width, row_min);
+ natural_width = MAX (natural_width, row_nat);
+
+ if (row->priv->separator != NULL)
+ {
+ gtk_widget_get_preferred_width (row->priv->separator, &row_min, &row_nat);
+ minimum_width = MAX (minimum_width, row_min);
+ natural_width = MAX (natural_width, row_nat);
+ }
+ }
+
+ if (minimum_width_out)
+ *minimum_width_out = minimum_width;
+ if (natural_width_out)
+ *natural_width_out = natural_width;
+}
+
+static void
+gtk_list_box_real_get_preferred_width_for_height (GtkWidget *widget, gint height,
+ gint *minimum_width, gint *natural_width)
+{
+ GtkListBox *list_box = GTK_LIST_BOX (widget);
+ gtk_list_box_real_get_preferred_width (GTK_WIDGET (list_box), minimum_width, natural_width);
+}
+
+static void
+gtk_list_box_real_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
+{
+ GtkListBox *list_box = GTK_LIST_BOX (widget);
+ GtkListBoxPrivate *priv = list_box->priv;
+ GtkAllocation child_allocation;
+ GtkAllocation separator_allocation;
+ GtkListBoxRow *row;
+ GdkWindow *window;
+ GSequenceIter *iter;
+ int child_min;
+
+
+ child_allocation.x = 0;
+ child_allocation.y = 0;
+ child_allocation.width = 0;
+ child_allocation.height = 0;
+
+ separator_allocation.x = 0;
+ separator_allocation.y = 0;
+ separator_allocation.width = 0;
+ separator_allocation.height = 0;
+
+ gtk_widget_set_allocation (GTK_WIDGET (list_box), allocation);
+ window = gtk_widget_get_window (GTK_WIDGET (list_box));
+ if (window != NULL)
+ gdk_window_move_resize (window,
+ allocation->x, allocation->y,
+ allocation->width, allocation->height);
+
+ child_allocation.x = 0;
+ child_allocation.y = 0;
+ child_allocation.width = allocation->width;
+ separator_allocation.x = 0;
+ separator_allocation.width = allocation->width;
+
+ for (iter = g_sequence_get_begin_iter (priv->children);
+ !g_sequence_iter_is_end (iter);
+ iter = g_sequence_iter_next (iter))
+ {
+ row = g_sequence_get (iter);
+ if (!row_is_visible (row))
+ {
+ row->priv->y = child_allocation.y;
+ row->priv->height = 0;
+ continue;
+ }
+
+ if (row->priv->separator != NULL)
+ {
+ gtk_widget_get_preferred_height_for_width (row->priv->separator,
+ allocation->width, &child_min, NULL);
+ separator_allocation.height = child_min;
+ separator_allocation.y = child_allocation.y;
+ gtk_widget_size_allocate (row->priv->separator,
+ &separator_allocation);
+ child_allocation.y += child_min;
+ }
+
+ row->priv->y = child_allocation.y;
+
+ gtk_widget_get_preferred_height_for_width (GTK_WIDGET (row), child_allocation.width, &child_min, NULL);
+ child_allocation.height = child_min;
+
+ row->priv->height = child_allocation.height;
+ gtk_widget_size_allocate (GTK_WIDGET (row), &child_allocation);
+
+ child_allocation.y += child_min;
+ }
+}
+
+void
+gtk_list_box_drag_unhighlight_row (GtkListBox *list_box)
+{
+ GtkListBoxPrivate *priv = list_box->priv;
+
+ g_return_if_fail (list_box != NULL);
+
+ if (priv->drag_highlighted_row == NULL)
+ return;
+
+ gtk_drag_unhighlight (GTK_WIDGET (priv->drag_highlighted_row));
+ g_clear_object (&priv->drag_highlighted_row);
+}
+
+
+void
+gtk_list_box_drag_highlight_row (GtkListBox *list_box, GtkListBoxRow *row)
+{
+ GtkListBoxPrivate *priv = list_box->priv;
+ GtkListBoxRow *old_highlight;
+
+ g_return_if_fail (list_box != NULL);
+ g_return_if_fail (row != NULL);
+
+ if (priv->drag_highlighted_row == row)
+ return;
+
+ gtk_list_box_drag_unhighlight_row (list_box);
+ gtk_drag_highlight (GTK_WIDGET (row));
+
+ old_highlight = priv->drag_highlighted_row;
+ priv->drag_highlighted_row = g_object_ref (row);
+ if (old_highlight)
+ g_object_unref (old_highlight);
+}
+
+static void
+gtk_list_box_real_drag_leave (GtkWidget *widget, GdkDragContext *context, guint time_)
+{
+ GtkListBox *list_box = GTK_LIST_BOX (widget);
+ GtkListBoxPrivate *priv = list_box->priv;
+
+ gtk_list_box_drag_unhighlight_row (list_box);
+ if (priv->auto_scroll_timeout_id != 0) {
+ g_source_remove (priv->auto_scroll_timeout_id);
+ priv->auto_scroll_timeout_id = 0;
+ }
+}
+
+typedef struct
+{
+ GtkListBox *list_box;
+ gint move;
+} MoveData;
+
+static void
+move_data_free (MoveData *data)
+{
+ g_slice_free (MoveData, data);
+}
+
+static gboolean
+drag_motion_timeout (MoveData *data)
+{
+ GtkListBox *list_box = data->list_box;
+ GtkListBoxPrivate *priv = list_box->priv;
+
+ gtk_adjustment_set_value (priv->adjustment,
+ gtk_adjustment_get_value (priv->adjustment) +
+ gtk_adjustment_get_step_increment (priv->adjustment) * data->move);
+ return TRUE;
+}
+
+static gboolean
+gtk_list_box_real_drag_motion (GtkWidget *widget, GdkDragContext *context,
+ gint x, gint y, guint time_)
+{
+ GtkListBox *list_box = GTK_LIST_BOX (widget);
+ GtkListBoxPrivate *priv = list_box->priv;
+ int move;
+ MoveData *data;
+ gdouble size;
+
+ /* Auto-scroll during Dnd if cursor is moving into the top/bottom portion of the
+ * box. */
+ if (priv->auto_scroll_timeout_id != 0)
+ {
+ g_source_remove (priv->auto_scroll_timeout_id);
+ priv->auto_scroll_timeout_id = 0;
+ }
+
+ if (priv->adjustment == NULL)
+ return FALSE;
+
+ /* Part of the view triggering auto-scroll */
+ size = 30;
+ move = 0;
+
+ if (y < (gtk_adjustment_get_value (priv->adjustment) + size))
+ {
+ /* Scroll up */
+ move = -1;
+ }
+ else if (y > ((gtk_adjustment_get_value (priv->adjustment) + gtk_adjustment_get_page_size (priv->adjustment)) - size))
+ {
+ /* Scroll down */
+ move = 1;
+ }
+
+ if (move == 0)
+ return FALSE;
+
+ data = g_slice_new0 (MoveData);
+ data->list_box = list_box;
+
+ priv->auto_scroll_timeout_id =
+ g_timeout_add_full (G_PRIORITY_DEFAULT, 150, (GSourceFunc)drag_motion_timeout,
+ data, (GDestroyNotify) move_data_free);
+
+ return FALSE;
+}
+
+static void
+gtk_list_box_real_activate_cursor_row (GtkListBox *list_box)
+{
+ GtkListBoxPrivate *priv = list_box->priv;
+
+ gtk_list_box_select_and_activate (list_box, priv->cursor_row);
+}
+
+static void
+gtk_list_box_real_toggle_cursor_row (GtkListBox *list_box)
+{
+ GtkListBoxPrivate *priv = list_box->priv;
+
+ if (priv->cursor_row == NULL)
+ return;
+
+ if (priv->selection_mode == GTK_SELECTION_SINGLE &&
+ priv->selected_row == priv->cursor_row)
+ gtk_list_box_update_selected (list_box, NULL);
+ else
+ gtk_list_box_select_and_activate (list_box, priv->cursor_row);
+}
+
+static void
+gtk_list_box_real_move_cursor (GtkListBox *list_box,
+ GtkMovementStep step,
+ gint count)
+{
+ GtkListBoxPrivate *priv = list_box->priv;
+ GdkModifierType state;
+ gboolean modify_selection_pressed;
+ GtkListBoxRow *row;
+ GdkModifierType modify_mod_mask;
+ GtkListBoxRow *prev;
+ GtkListBoxRow *next;
+ gint page_size;
+ GSequenceIter *iter;
+ gint start_y;
+ gint end_y;
+
+ modify_selection_pressed = FALSE;
+
+ if (gtk_get_current_event_state (&state))
+ {
+ modify_mod_mask = gtk_widget_get_modifier_mask (GTK_WIDGET (list_box),
+ GDK_MODIFIER_INTENT_MODIFY_SELECTION);
+ if ((state & modify_mod_mask) == modify_mod_mask)
+ modify_selection_pressed = TRUE;
+ }
+
+ row = NULL;
+ switch (step)
+ {
+ case GTK_MOVEMENT_BUFFER_ENDS:
+ if (count < 0)
+ row = gtk_list_box_get_first_visible (list_box);
+ else
+ row = gtk_list_box_get_last_visible (list_box);
+ break;
+ case GTK_MOVEMENT_DISPLAY_LINES:
+ if (priv->cursor_row != NULL)
+ {
+ iter = priv->cursor_row->priv->iter;
+
+ while (count < 0 && iter != NULL)
+ {
+ iter = gtk_list_box_get_previous_visible (list_box, iter);
+ count = count + 1;
+ }
+ while (count > 0 && iter != NULL)
+ {
+ iter = gtk_list_box_get_next_visible (list_box, iter);
+ count = count - 1;
+ }
+
+ if (iter != NULL && !g_sequence_iter_is_end (iter))
+ row = g_sequence_get (iter);
+ }
+ break;
+ case GTK_MOVEMENT_PAGES:
+ page_size = 100;
+ if (priv->adjustment != NULL)
+ page_size = gtk_adjustment_get_page_increment (priv->adjustment);
+
+ if (priv->cursor_row != NULL)
+ {
+ start_y = priv->cursor_row->priv->y;
+ end_y = start_y;
+ iter = priv->cursor_row->priv->iter;
+
+ row = priv->cursor_row;
+ if (count < 0)
+ {
+ /* Up */
+ while (iter != NULL && !g_sequence_iter_is_begin (iter))
+ {
+ iter = gtk_list_box_get_previous_visible (list_box, iter);
+ if (iter == NULL)
+ break;
+
+ prev = g_sequence_get (iter);
+ if (prev->priv->y < start_y - page_size)
+ break;
+
+ row = prev;
+ }
+ }
+ else
+ {
+ /* Down */
+ while (iter != NULL && !g_sequence_iter_is_end (iter))
+ {
+ iter = gtk_list_box_get_next_visible (list_box, iter);
+ if (g_sequence_iter_is_end (iter))
+ break;
+
+ next = g_sequence_get (iter);
+ if (next->priv->y > start_y + page_size)
+ break;
+
+ row = next;
+ }
+ }
+ end_y = row->priv->y;
+ if (end_y != start_y && priv->adjustment != NULL)
+ gtk_adjustment_set_value (priv->adjustment,
+ gtk_adjustment_get_value (priv->adjustment) +
+ end_y - start_y);
+ }
+ break;
+ default:
+ return;
+ }
+
+ if (row == NULL || row == priv->cursor_row)
+ {
+ GtkDirectionType direction = count < 0 ? GTK_DIR_UP : GTK_DIR_DOWN;
+
+ if (!gtk_widget_keynav_failed (GTK_WIDGET (list_box), direction))
+ {
+ GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (list_box));
+
+ if (toplevel)
+ gtk_widget_child_focus (toplevel,
+ direction == GTK_DIR_UP ?
+ GTK_DIR_TAB_BACKWARD :
+ GTK_DIR_TAB_FORWARD);
+
+ }
+
+ return;
+ }
+
+ gtk_list_box_update_cursor (list_box, row);
+ if (!modify_selection_pressed)
+ gtk_list_box_update_selected (list_box, row);
+}
+
+
+GtkWidget *
+gtk_list_box_row_new (void)
+{
+ return g_object_new (GTK_TYPE_LIST_BOX_ROW, NULL);
+}
+
+static void
+gtk_list_box_row_init (GtkListBoxRow *row)
+{
+ GtkListBoxRowPrivate *priv;
+
+ row->priv = priv =
+ G_TYPE_INSTANCE_GET_PRIVATE (row, GTK_TYPE_LIST_BOX_ROW, GtkListBoxRowPrivate);
+
+ gtk_widget_set_can_focus (GTK_WIDGET (row), TRUE);
+ gtk_widget_set_redraw_on_allocate (GTK_WIDGET (row), TRUE);
+}
+
+static void
+gtk_list_box_row_get_property (GObject *obj,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_list_box_row_set_property (GObject *obj,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
+ break;
+ }
+}
+
+static GtkListBox *
+gtk_list_box_row_get_box (GtkListBoxRow *row)
+{
+ GtkWidget *parent;
+
+ parent = gtk_widget_get_parent (GTK_WIDGET (row));
+ if (parent && GTK_IS_LIST_BOX (parent))
+ return GTK_LIST_BOX (parent);
+
+ return NULL;
+}
+
+static void
+gtk_list_box_row_set_focus (GtkListBoxRow *row)
+{
+ GtkListBox *list_box = gtk_list_box_row_get_box (row);
+ GdkModifierType state = 0;
+ gboolean modify_selection_pressed;
+
+ modify_selection_pressed = FALSE;
+ if (gtk_get_current_event_state (&state))
+ {
+ GdkModifierType modify_mod_mask;
+ modify_mod_mask =
+ gtk_widget_get_modifier_mask (GTK_WIDGET (list_box),
+ GDK_MODIFIER_INTENT_MODIFY_SELECTION);
+ if ((state & modify_mod_mask) == modify_mod_mask)
+ modify_selection_pressed = TRUE;
+ }
+
+ if (modify_selection_pressed)
+ gtk_list_box_update_cursor (list_box, row);
+ else
+ gtk_list_box_update_selected (list_box, row);
+}
+
+static gboolean
+gtk_list_box_row_real_focus (GtkWidget* widget, GtkDirectionType direction)
+{
+ GtkListBoxRow *row = GTK_LIST_BOX_ROW (widget);
+ gboolean had_focus = FALSE;
+ GtkWidget *child;
+
+ child = gtk_bin_get_child (GTK_BIN (widget));
+
+ g_object_get (widget, "has-focus", &had_focus, NULL);
+ if (had_focus)
+ {
+ /* If on row, going right, enter into possible container */
+ if (child &&
+ (direction == GTK_DIR_RIGHT || direction == GTK_DIR_TAB_FORWARD))
+ {
+ if (gtk_widget_child_focus (GTK_WIDGET (child), direction))
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+ else if (gtk_container_get_focus_child (GTK_CONTAINER (row)) != NULL)
+ {
+ /* Child has focus, always navigate inside it first */
+
+ if (gtk_widget_child_focus (child, direction))
+ return TRUE;
+
+ /* If exiting child container to the left, select row */
+ if (direction == GTK_DIR_LEFT || direction == GTK_DIR_TAB_BACKWARD)
+ {
+ gtk_list_box_row_set_focus (row);
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+ else
+ {
+ /* If coming from the left, enter into possible container */
+ if (child &&
+ (direction == GTK_DIR_LEFT || direction == GTK_DIR_TAB_BACKWARD))
+ {
+ if (gtk_widget_child_focus (child, direction))
+ return TRUE;
+ }
+
+ gtk_list_box_row_set_focus (row);
+ return TRUE;
+ }
+}
+
+static void
+gtk_list_box_row_real_show (GtkWidget *widget)
+{
+ GtkListBoxRow *row = GTK_LIST_BOX_ROW (widget);
+ GtkListBox *list_box;;
+
+ GTK_WIDGET_CLASS (gtk_list_box_row_parent_class)->show (widget);
+
+ list_box = gtk_list_box_row_get_box (row);
+ if (list_box)
+ gtk_list_box_row_visibility_changed (list_box, row);
+}
+
+static void
+gtk_list_box_row_real_hide (GtkWidget *widget)
+{
+ GtkListBoxRow *row = GTK_LIST_BOX_ROW (widget);
+ GtkListBox *list_box;
+
+ GTK_WIDGET_CLASS (gtk_list_box_row_parent_class)->hide (widget);
+
+ list_box = gtk_list_box_row_get_box (row);
+ if (list_box)
+ gtk_list_box_row_visibility_changed (list_box, row);
+}
+
+static gboolean
+gtk_list_box_row_real_draw (GtkWidget* widget, cairo_t* cr)
+{
+ GtkListBoxRow *row = GTK_LIST_BOX_ROW (widget);
+ GtkAllocation allocation = {0};
+ GtkStyleContext* context;
+ GtkStateFlags state;
+ GtkBorder border;
+ gint focus_pad;
+
+ gtk_widget_get_allocation (widget, &allocation);
+ context = gtk_widget_get_style_context (widget);
+ state = gtk_widget_get_state_flags (widget);
+
+ gtk_render_background (context, cr, (gdouble) 0, (gdouble) 0, (gdouble) allocation.width, (gdouble) allocation.height);
+ gtk_render_frame (context, cr, (gdouble) 0, (gdouble) 0, (gdouble) allocation.width, (gdouble) allocation.height);
+
+ if (gtk_widget_has_visible_focus (GTK_WIDGET (row)))
+ {
+ gtk_style_context_get_border (context, state, &border);
+
+ gtk_style_context_get_style (context,
+ "focus-padding", &focus_pad,
+ NULL);
+ gtk_render_focus (context, cr, border.left + focus_pad, border.top + focus_pad,
+ allocation.width - 2 * focus_pad - border.left - border.right,
+ allocation.height - 2 * focus_pad - border.top - border.bottom);
+ }
+
+ GTK_WIDGET_CLASS (gtk_list_box_row_parent_class)->draw (widget, cr);
+
+ return TRUE;
+}
+
+static void
+gtk_list_box_row_get_full_border (GtkListBoxRow *row,
+ GtkBorder *full_border)
+{
+ GtkWidget *widget = GTK_WIDGET (row);
+ GtkStyleContext *context;
+ GtkStateFlags state;
+ GtkBorder padding, border;
+ int focus_width, focus_pad;
+
+ context = gtk_widget_get_style_context (widget);
+ state = gtk_style_context_get_state (context);
+
+ gtk_style_context_get_padding (context, state, &padding);
+ gtk_style_context_get_border (context, state, &border);
+ gtk_style_context_get_style (context,
+ "focus-line-width", &focus_width,
+ "focus-padding", &focus_pad,
+ NULL);
+
+ full_border->left = padding.left + border.left + focus_width + focus_pad;
+ full_border->right = padding.right + border.right + focus_width + focus_pad;
+ full_border->top = padding.top + border.top + focus_width + focus_pad;
+ full_border->bottom = padding.bottom + border.bottom + focus_width + focus_pad;
+}
+
+static void gtk_list_box_row_real_get_preferred_height_for_width (GtkWidget* widget, gint width,
+ gint* minimum_height_out, gint* natural_height_out);
+static void gtk_list_box_row_real_get_preferred_width (GtkWidget* widget,
+ gint* minimum_width_out, gint* natural_width_out);
+
+static void
+gtk_list_box_row_real_get_preferred_height (GtkWidget* widget,
+ gint* minimum_height,
+ gint* natural_height)
+{
+ gint natural_width;
+ gtk_list_box_row_real_get_preferred_width (widget, NULL, &natural_width);
+ gtk_list_box_row_real_get_preferred_height_for_width (widget, natural_width,
+ minimum_height, natural_height);
+}
+
+static void
+gtk_list_box_row_real_get_preferred_height_for_width (GtkWidget* widget, gint width,
+ gint* minimum_height_out, gint* natural_height_out)
+{
+ GtkListBoxRow *row = GTK_LIST_BOX_ROW (widget);
+ GtkWidget *child;
+ gint child_min = 0, child_natural = 0;
+ GtkBorder full_border;
+
+ gtk_list_box_row_get_full_border (row, &full_border);
+
+ child = gtk_bin_get_child (GTK_BIN (row));
+ if (child && gtk_widget_get_visible (child))
+ gtk_widget_get_preferred_height_for_width (child, width - full_border.left - full_border.right,
+ &child_min, &child_natural);
+
+ if (minimum_height_out)
+ *minimum_height_out = full_border.top + child_min + full_border.bottom;
+ if (natural_height_out)
+ *natural_height_out = full_border.top + child_natural + full_border.bottom;
+}
+
+static void
+gtk_list_box_row_real_get_preferred_width (GtkWidget* widget, gint* minimum_width_out, gint* natural_width_out)
+{
+ GtkListBoxRow *row = GTK_LIST_BOX_ROW (widget);
+ GtkWidget *child;
+ gint child_min = 0, child_natural = 0;
+ GtkBorder full_border;
+
+ gtk_list_box_row_get_full_border (row, &full_border);
+
+ child = gtk_bin_get_child (GTK_BIN (row));
+ if (child && gtk_widget_get_visible (child))
+ gtk_widget_get_preferred_width (child,
+ &child_min, &child_natural);
+
+ if (minimum_width_out)
+ *minimum_width_out = full_border.left + child_min + full_border.right;
+ if (natural_width_out)
+ *natural_width_out = full_border.left + child_natural + full_border.bottom;
+}
+
+static void
+gtk_list_box_row_real_get_preferred_width_for_height (GtkWidget *widget, gint height,
+ gint *minimum_width, gint *natural_width)
+{
+ gtk_list_box_row_real_get_preferred_width (widget, minimum_width, natural_width);
+}
+
+static void
+gtk_list_box_row_real_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
+{
+ GtkListBoxRow *row = GTK_LIST_BOX_ROW (widget);
+ GtkWidget *child;
+
+ gtk_widget_set_allocation (widget, allocation);
+
+ child = gtk_bin_get_child (GTK_BIN (row));
+ if (child && gtk_widget_get_visible (child))
+ {
+ GtkAllocation child_allocation;
+ GtkBorder border;
+
+ gtk_list_box_row_get_full_border (row, &border);
+
+ child_allocation.x = allocation->x + border.left;
+ child_allocation.y = allocation->y + border.top;
+ child_allocation.width = allocation->width - border.left - border.right;
+ child_allocation.height = allocation->height - border.top - border.bottom;
+
+ child_allocation.width = MAX (1, child_allocation.width);
+ child_allocation.height = MAX (1, child_allocation.height);
+
+ gtk_widget_size_allocate (child, &child_allocation);
+ }
+}
+
+void
+gtk_list_box_row_changed (GtkListBoxRow *row)
+{
+ GtkListBox *list_box = gtk_list_box_row_get_box (row);
+
+ if (list_box)
+ gtk_list_box_got_row_changed (GTK_LIST_BOX (list_box), row);
+}
+
+/**
+ * gtk_list_box_row_get_separator:
+ *
+ * Return value: (transfer none): The current separator.
+ */
+GtkWidget *
+gtk_list_box_row_get_separator (GtkListBoxRow *row)
+{
+ return row->priv->separator;
+}
+
+/**
+ * gtk_list_box_row_set_separator:
+ * @separator: (allow-none):
+ *
+ */
+void
+gtk_list_box_row_set_separator (GtkListBoxRow *row,
+ GtkWidget *separator)
+{
+ if (row->priv->separator)
+ g_object_unref (row->priv->separator);
+
+ row->priv->separator = separator;
+
+ if (separator)
+ g_object_ref (separator);
+}
+
+
+static void
+gtk_list_box_row_finalize (GObject *obj)
+{
+ GtkListBoxRow *row = GTK_LIST_BOX_ROW (obj);
+ GtkListBoxRowPrivate *priv = row->priv;
+
+ g_clear_object (&priv->separator);
+
+ G_OBJECT_CLASS (gtk_list_box_row_parent_class)->finalize (obj);
+}
+
+static void
+gtk_list_box_row_class_init (GtkListBoxRowClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (GtkListBoxRowPrivate));
+
+ object_class->get_property = gtk_list_box_row_get_property;
+ object_class->set_property = gtk_list_box_row_set_property;
+ object_class->finalize = gtk_list_box_row_finalize;
+
+ widget_class->show = gtk_list_box_row_real_show;
+ widget_class->hide = gtk_list_box_row_real_hide;
+ widget_class->draw = gtk_list_box_row_real_draw;
+ widget_class->get_preferred_height = gtk_list_box_row_real_get_preferred_height;
+ widget_class->get_preferred_height_for_width = gtk_list_box_row_real_get_preferred_height_for_width;
+ widget_class->get_preferred_width = gtk_list_box_row_real_get_preferred_width;
+ widget_class->get_preferred_width_for_height = gtk_list_box_row_real_get_preferred_width_for_height;
+ widget_class->size_allocate = gtk_list_box_row_real_size_allocate;
+ widget_class->focus = gtk_list_box_row_real_focus;
+}
diff --git a/gtk/gtklistbox.h b/gtk/gtklistbox.h
new file mode 100644
index 0000000000..3b20f40458
--- /dev/null
+++ b/gtk/gtklistbox.h
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2013 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 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 Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ *
+ */
+
+#ifndef __GTK_LIST_BOX_H__
+#define __GTK_LIST_BOX_H__
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gtk/gtkscrolledwindow.h>
+
+G_BEGIN_DECLS
+
+
+#define GTK_TYPE_LIST_BOX (gtk_list_box_get_type ())
+#define GTK_LIST_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_LIST_BOX, GtkListBox))
+#define GTK_LIST_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_LIST_BOX, GtkListBoxClass))
+#define GTK_IS_LIST_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_LIST_BOX))
+#define GTK_IS_LIST_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_LIST_BOX))
+#define GTK_LIST_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_LIST_BOX, GtkListBoxClass))
+
+typedef struct _GtkListBox GtkListBox;
+typedef struct _GtkListBoxClass GtkListBoxClass;
+typedef struct _GtkListBoxPrivate GtkListBoxPrivate;
+
+typedef struct _GtkListBoxRow GtkListBoxRow;
+typedef struct _GtkListBoxRowClass GtkListBoxRowClass;
+typedef struct _GtkListBoxRowPrivate GtkListBoxRowPrivate;
+
+struct _GtkListBox
+{
+ GtkContainer parent_instance;
+
+ GtkListBoxPrivate * priv;
+};
+
+struct _GtkListBoxClass
+{
+ GtkContainerClass parent_class;
+
+ void (*row_selected) (GtkListBox* list_box, GtkListBoxRow* row);
+ void (*row_activated) (GtkListBox* list_box, GtkListBoxRow* row);
+ void (*activate_cursor_row) (GtkListBox* list_box);
+ void (*toggle_cursor_row) (GtkListBox* list_box);
+ void (*move_cursor) (GtkListBox* list_box, GtkMovementStep step, gint count);
+ void (*refilter) (GtkListBox* list_box);
+
+ /* Padding for future expansion */
+ void (*_gtk_reserved1) (void);
+ void (*_gtk_reserved2) (void);
+ void (*_gtk_reserved3) (void);
+ void (*_gtk_reserved4) (void);
+ void (*_gtk_reserved5) (void);
+ void (*_gtk_reserved6) (void);
+};
+
+#define GTK_TYPE_LIST_BOX_ROW (gtk_list_box_row_get_type ())
+#define GTK_LIST_BOX_ROW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_LIST_BOX_ROW, GtkListBoxRow))
+#define GTK_LIST_BOX_ROW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_LIST_BOX_ROW, GtkListBoxRowClass))
+#define GTK_IS_LIST_BOX_ROW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_LIST_BOX_ROW))
+#define GTK_IS_LIST_BOX_ROW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_LIST_BOX_ROW))
+#define GTK_LIST_BOX_ROW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_LIST_BOX_ROW, GtkListBoxRowClass))
+
+struct _GtkListBoxRow
+{
+ GtkBin parent_instance;
+
+ GtkListBoxRowPrivate * priv;
+};
+
+struct _GtkListBoxRowClass
+{
+ GtkBinClass parent_class;
+
+ /* Padding for future expansion */
+ void (*_gtk_reserved1) (void);
+ void (*_gtk_reserved2) (void);
+ void (*_gtk_reserved3) (void);
+};
+
+typedef gboolean (*GtkListBoxFilterFunc) (GtkListBoxRow* row, gpointer user_data);
+typedef gint (*GtkListBoxSortFunc) (GtkListBoxRow* row1, GtkListBoxRow* row2, gpointer user_data);
+typedef void (*GtkListBoxUpdateSeparatorFunc) (GtkListBoxRow* row, GtkListBoxRow* before, gpointer user_data);
+
+GDK_AVAILABLE_IN_3_10
+GType gtk_list_box_row_get_type (void) G_GNUC_CONST;
+GDK_AVAILABLE_IN_3_10
+GtkWidget* gtk_list_box_row_new (void);
+GDK_AVAILABLE_IN_3_10
+GtkWidget* gtk_list_box_row_get_separator (GtkListBoxRow *row);
+GDK_AVAILABLE_IN_3_10
+void gtk_list_box_row_set_separator (GtkListBoxRow *row,
+ GtkWidget *separator);
+GDK_AVAILABLE_IN_3_10
+void gtk_list_box_row_changed (GtkListBoxRow *row);
+
+
+GDK_AVAILABLE_IN_3_10
+GType gtk_list_box_get_type (void) G_GNUC_CONST;
+GDK_AVAILABLE_IN_3_10
+GtkListBoxRow* gtk_list_box_get_selected_row (GtkListBox *list_box);
+GDK_AVAILABLE_IN_3_10
+GtkListBoxRow* gtk_list_box_get_row_at_index (GtkListBox *list_box,
+ int index);
+GDK_AVAILABLE_IN_3_10
+GtkListBoxRow* gtk_list_box_get_row_at_y (GtkListBox *list_box,
+ gint y);
+GDK_AVAILABLE_IN_3_10
+void gtk_list_box_select_row (GtkListBox *list_box,
+ GtkListBoxRow *row);
+GDK_AVAILABLE_IN_3_10
+void gtk_list_box_set_adjustment (GtkListBox *list_box,
+ GtkAdjustment *adjustment);
+GDK_AVAILABLE_IN_3_10
+GtkAdjustment *gtk_list_box_get_adjustment (GtkListBox *list_box);
+GDK_AVAILABLE_IN_3_10
+void gtk_list_box_add_to_scrolled (GtkListBox *list_box,
+ GtkScrolledWindow *scrolled);
+GDK_AVAILABLE_IN_3_10
+void gtk_list_box_set_selection_mode (GtkListBox *list_box,
+ GtkSelectionMode mode);
+GDK_AVAILABLE_IN_3_10
+GtkSelectionMode gtk_list_box_get_selection_mode (GtkListBox *list_box);
+GDK_AVAILABLE_IN_3_10
+void gtk_list_box_set_filter_func (GtkListBox *list_box,
+ GtkListBoxFilterFunc filter_func,
+ gpointer user_data,
+ GDestroyNotify destroy);
+GDK_AVAILABLE_IN_3_10
+void gtk_list_box_set_separator_func (GtkListBox *list_box,
+ GtkListBoxUpdateSeparatorFunc update_separator,
+ gpointer user_data,
+ GDestroyNotify destroy);
+GDK_AVAILABLE_IN_3_10
+void gtk_list_box_refilter (GtkListBox *list_box);
+GDK_AVAILABLE_IN_3_10
+void gtk_list_box_resort (GtkListBox *list_box);
+GDK_AVAILABLE_IN_3_10
+void gtk_list_box_reseparate (GtkListBox *list_box);
+GDK_AVAILABLE_IN_3_10
+void gtk_list_box_set_sort_func (GtkListBox *list_box,
+ GtkListBoxSortFunc sort_func,
+ gpointer user_data,
+ GDestroyNotify destroy);
+GDK_AVAILABLE_IN_3_10
+void gtk_list_box_set_activate_on_single_click (GtkListBox *list_box,
+ gboolean single);
+GDK_AVAILABLE_IN_3_10
+void gtk_list_box_drag_unhighlight_row (GtkListBox *list_box);
+GDK_AVAILABLE_IN_3_10
+void gtk_list_box_drag_highlight_row (GtkListBox *list_box,
+ GtkListBoxRow *row);
+GDK_AVAILABLE_IN_3_10
+GtkWidget* gtk_list_box_new (void);
+
+
+G_END_DECLS
+
+#endif