[egg-list-box/flow-box-enhancements] Reorganize the code
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [egg-list-box/flow-box-enhancements] Reorganize the code
- Date: Sun, 29 Sep 2013 16:57:07 +0000 (UTC)
commit b985f1f934c8e1842f2b888fc72dcee0f03e92d0
Author: Matthias Clasen <mclasen redhat com>
Date: Sun Sep 29 12:56:02 2013 -0400
Reorganize the code
Separate EggFlowBoxChild from EggFlowBox code, and
keep things a bit more organized.
egg-flow-box.c | 2673 +++++++++++++++++++++++++++++---------------------------
1 files changed, 1389 insertions(+), 1284 deletions(-)
---
diff --git a/egg-flow-box.c b/egg-flow-box.c
index e09e25d..a589b8a 100644
--- a/egg-flow-box.c
+++ b/egg-flow-box.c
@@ -23,6 +23,8 @@
* William Jon McCann <jmccann redhat com>
*/
+/* Preamble {{{1 */
+
/**
* SECTION:eggflowbox
* @Short_Description: A container that allows reflowing its children
@@ -49,41 +51,6 @@
* Also see #GtkListBox.
*/
-/**
- * EggFlowBoxForeachFunc:
- * @box: a #EggFlowBox
- * @child: a #EggFlowBoxChild
- * @user_data: (closure): user data
- *
- * A function used by egg_flow_box_selected_foreach().
- * It will be called on every selected child of tht @box.
- */
-
-/**
- * EggFlowBoxFilterFunc:
- * @child: a #EggFlowBoxChild that may be filtered
- * @user_data: (closure): user data
- *
- * A function that will be called whenrever a child changes
- * or is added. It lets you control if the child should be
- * visible or not.
- *
- * Returns: %TRUE if the row should be visible, %FALSE otherwise
- */
-
-/**
- * EggFlowBoxSortFunc:
- * @child1: the first child
- * @child2: the second child
- * @user_data: (closure): user data
- *
- * A function to compare two children to determine which
- * should come first.
- *
- * Returns: < 0 if @child1 should be before @child2, 0 if
- * the are equal, and > 0 otherwise
- */
-
#include <config.h>
#include <gtk/gtk.h>
@@ -122,144 +89,185 @@ _egg_marshal_VOID__ENUM_INT (GClosure * closure,
#define I_(msgid) (msgid)
#define P_(msgid) (msgid)
-#define DEFAULT_MAX_CHILDREN_PER_LINE 7
-#define RUBBERBAND_START_DISTANCE 32
-#define AUTOSCROLL_FAST_DISTANCE 32
-#define AUTOSCROLL_FACTOR 20
-#define AUTOSCROLL_FACTOR_FAST 10
+/* Forward declarations and utilities {{{1 */
-enum {
- CHILD_ACTIVATED,
- SELECTED_CHILDREN_CHANGED,
- ACTIVATE_CURSOR_CHILD,
- TOGGLE_CURSOR_CHILD,
- MOVE_CURSOR,
- SELECT_ALL,
- UNSELECT_ALL,
- LAST_SIGNAL
-};
+static void egg_flow_box_update_cursor (EggFlowBox *box,
+ EggFlowBoxChild *child);
+static void egg_flow_box_select_and_activate (EggFlowBox *box,
+ EggFlowBoxChild *child);
+static void egg_flow_box_update_selection (EggFlowBox *box,
+ EggFlowBoxChild *child,
+ gboolean modify,
+ gboolean extend);
+static void egg_flow_box_apply_filter (EggFlowBox *box,
+ EggFlowBoxChild *child);
+static void egg_flow_box_apply_sort (EggFlowBox *box,
+ EggFlowBoxChild *child);
+static gint egg_flow_box_sort (EggFlowBoxChild *a,
+ EggFlowBoxChild *b,
+ EggFlowBox *box);
-enum {
- CHILD_ACTIVATE,
- CHILD_LAST_SIGNAL
-};
+static void
+get_current_selection_modifiers (GtkWidget *widget,
+ gboolean *modify,
+ gboolean *extend)
+{
+ GdkModifierType state = 0;
+ GdkModifierType mask;
-enum {
- PROP_0,
- PROP_ORIENTATION,
- PROP_HOMOGENEOUS,
- PROP_HALIGN_POLICY,
- PROP_VALIGN_POLICY,
- PROP_COLUMN_SPACING,
- PROP_ROW_SPACING,
- PROP_MIN_CHILDREN_PER_LINE,
- PROP_MAX_CHILDREN_PER_LINE,
- PROP_SELECTION_MODE,
- PROP_ACTIVATE_ON_SINGLE_CLICK
-};
+ *modify = FALSE;
+ *extend = FALSE;
-typedef struct _EggFlowBoxPrivate EggFlowBoxPrivate;
-struct _EggFlowBoxPrivate {
- GtkOrientation orientation;
- gboolean homogeneous;
+ if (gtk_get_current_event_state (&state))
+ {
+ mask = gtk_widget_get_modifier_mask (widget, GDK_MODIFIER_INTENT_MODIFY_SELECTION);
+ if ((state & mask) == mask)
+ *modify = TRUE;
+ mask = gtk_widget_get_modifier_mask (widget, GDK_MODIFIER_INTENT_EXTEND_SELECTION);
+ if ((state & mask) == mask)
+ *extend = TRUE;
+ }
+}
- guint row_spacing;
- guint column_spacing;
+static void
+path_from_horizontal_line_rects (cairo_t *cr,
+ GdkRectangle *lines,
+ gint n_lines)
+{
+ gint start_line, end_line;
+ GdkRectangle *r;
+ gint i;
- EggFlowBoxChild *prelight_child;
- EggFlowBoxChild *cursor_child;
- EggFlowBoxChild *selected_child;
+ /* Join rows vertically by extending to the middle */
+ for (i = 0; i < n_lines - 1; i++)
+ {
+ GdkRectangle *r1 = &lines[i];
+ GdkRectangle *r2 = &lines[i+1];
+ gint gap, old;
- gboolean active_child_active;
- EggFlowBoxChild *active_child;
+ gap = r2->y - (r1->y + r1->height);
+ r1->height += gap / 2;
+ old = r2->y;
+ r2->y = r1->y + r1->height;
+ r2->height += old - r2->y;
+ }
- GtkSelectionMode selection_mode;
+ cairo_new_path (cr);
+ start_line = 0;
- GtkAdjustment *hadjustment;
- GtkAdjustment *vadjustment;
- gboolean activate_on_single_click;
+ do
+ {
+ for (i = start_line; i < n_lines; i++)
+ {
+ r = &lines[i];
+ if (i == start_line)
+ cairo_move_to (cr, r->x + r->width, r->y);
+ else
+ cairo_line_to (cr, r->x + r->width, r->y);
+ cairo_line_to (cr, r->x + r->width, r->y + r->height);
- guint16 min_children_per_line;
- guint16 max_children_per_line;
- guint16 cur_children_per_line;
+ if (i < n_lines - 1 &&
+ (r->x + r->width < lines[i+1].x ||
+ r->x > lines[i+1].x + lines[i+1].width))
+ {
+ i++;
+ break;
+ }
+ }
+ end_line = i;
+ for (i = end_line - 1; i >= start_line; i--)
+ {
+ r = &lines[i];
+ cairo_line_to (cr, r->x, r->y + r->height);
+ cairo_line_to (cr, r->x, r->y);
+ }
+ cairo_close_path (cr);
+ start_line = end_line;
+ }
+ while (end_line < n_lines);
+}
- GSequence *children;
+static void
+path_from_vertical_line_rects (cairo_t *cr,
+ GdkRectangle *lines,
+ gint n_lines)
+{
+ gint start_line, end_line;
+ GdkRectangle *r;
+ gint i;
- EggFlowBoxFilterFunc filter_func;
- gpointer filter_data;
- GDestroyNotify filter_destroy;
+ /* Join rows horizontall by extending to the middle */
+ for (i = 0; i < n_lines - 1; i++)
+ {
+ GdkRectangle *r1 = &lines[i];
+ GdkRectangle *r2 = &lines[i+1];
+ gint gap, old;
- EggFlowBoxSortFunc sort_func;
- gpointer sort_data;
- GDestroyNotify sort_destroy;
+ gap = r2->x - (r1->x + r1->width);
+ r1->width += gap / 2;
+ old = r2->x;
+ r2->x = r1->x + r1->width;
+ r2->width += old - r2->x;
+ }
- gboolean track_motion;
- gboolean rubberband_select;
- EggFlowBoxChild *rubberband_first;
- EggFlowBoxChild *rubberband_last;
- gint button_down_x;
- gint button_down_y;
- GdkDevice *rubberband_device;
+ cairo_new_path (cr);
+ start_line = 0;
- GtkScrollType autoscroll_mode;
- guint autoscroll_id;
+ do
+ {
+ for (i = start_line; i < n_lines; i++)
+ {
+ r = &lines[i];
+ if (i == start_line)
+ cairo_move_to (cr, r->x, r->y + r->height);
+ else
+ cairo_line_to (cr, r->x, r->y + r->height);
+ cairo_line_to (cr, r->x + r->width, r->y + r->height);
+
+ if (i < n_lines - 1 &&
+ (r->y + r->height < lines[i+1].y ||
+ r->y > lines[i+1].y + lines[i+1].height))
+ {
+ i++;
+ break;
+ }
+ }
+ end_line = i;
+ for (i = end_line - 1; i >= start_line; i--)
+ {
+ r = &lines[i];
+ cairo_line_to (cr, r->x + r->width, r->y);
+ cairo_line_to (cr, r->x, r->y);
+ }
+ cairo_close_path (cr);
+ start_line = end_line;
+ }
+ while (end_line < n_lines);
+}
+
+/* EggFlowBoxChild {{{1 */
+
+/* GObject boilerplate {{{2 */
+
+enum {
+ CHILD_ACTIVATE,
+ CHILD_LAST_SIGNAL
};
+static guint child_signals[CHILD_LAST_SIGNAL] = { 0 };
+
typedef struct _EggFlowBoxChildPrivate EggFlowBoxChildPrivate;
struct _EggFlowBoxChildPrivate
{
GSequenceIter *iter;
- guint selected : 1;
+ gboolean selected;
};
-static guint signals[LAST_SIGNAL] = { 0 };
-static guint child_signals[CHILD_LAST_SIGNAL] = { 0 };
-
-G_DEFINE_TYPE_WITH_CODE (EggFlowBox, egg_flow_box, GTK_TYPE_CONTAINER,
- G_ADD_PRIVATE (EggFlowBox)
- G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL))
-G_DEFINE_TYPE_WITH_PRIVATE (EggFlowBoxChild, egg_flow_box_child, GTK_TYPE_BIN)
-
-#define BOX_PRIV(box) ((EggFlowBoxPrivate*)egg_flow_box_get_instance_private ((EggFlowBox*)(box)))
#define CHILD_PRIV(child) ((EggFlowBoxChildPrivate*)egg_flow_box_child_get_instance_private
((EggFlowBoxChild*)(child)))
-#define ORIENTATION_ALIGN(box) \
- (BOX_PRIV(box)->orientation == GTK_ORIENTATION_HORIZONTAL \
- ? gtk_widget_get_halign (GTK_WIDGET (box)) \
- : gtk_widget_get_valign (GTK_WIDGET (box)))
-
-#define OPPOSING_ORIENTATION_ALIGN(box) \
- (BOX_PRIV(box)->orientation == GTK_ORIENTATION_HORIZONTAL \
- ? gtk_widget_get_valign (GTK_WIDGET (box)) \
- : gtk_widget_get_halign (GTK_WIDGET (box)))
-
-
-static void egg_flow_box_update_cursor (EggFlowBox *box,
- EggFlowBoxChild *child);
-static void egg_flow_box_select_and_activate (EggFlowBox *box,
- EggFlowBoxChild *child);
-static void egg_flow_box_update_selection (EggFlowBox *box,
- EggFlowBoxChild *child,
- gboolean modify,
- gboolean extend);
-static void egg_flow_box_apply_filter (EggFlowBox *box,
- EggFlowBoxChild *child);
-static gint egg_flow_box_sort (EggFlowBoxChild *a,
- EggFlowBoxChild *b,
- EggFlowBox *box);
-
-
-static void
-egg_flow_box_child_init (EggFlowBoxChild *child)
-{
- GtkStyleContext *context;
-
- gtk_widget_set_can_focus (GTK_WIDGET (child), TRUE);
- gtk_widget_set_redraw_on_allocate (GTK_WIDGET (child), TRUE);
+G_DEFINE_TYPE_WITH_PRIVATE (EggFlowBoxChild, egg_flow_box_child, GTK_TYPE_BIN)
- context = gtk_widget_get_style_context (GTK_WIDGET (child));
- gtk_style_context_add_class (context, "grid-child");
-}
+/* Internal API {{{2 */
static EggFlowBox *
egg_flow_box_child_get_box (EggFlowBoxChild *child)
@@ -274,28 +282,6 @@ egg_flow_box_child_get_box (EggFlowBoxChild *child)
}
static void
-get_current_selection_modifiers (GtkWidget *widget,
- gboolean *modify,
- gboolean *extend)
-{
- GdkModifierType state = 0;
- GdkModifierType mask;
-
- *modify = FALSE;
- *extend = FALSE;
-
- if (gtk_get_current_event_state (&state))
- {
- mask = gtk_widget_get_modifier_mask (widget, GDK_MODIFIER_INTENT_MODIFY_SELECTION);
- if ((state & mask) == mask)
- *modify = TRUE;
- mask = gtk_widget_get_modifier_mask (widget, GDK_MODIFIER_INTENT_EXTEND_SELECTION);
- if ((state & mask) == mask)
- *extend = TRUE;
- }
-}
-
-static void
egg_flow_box_child_set_focus (EggFlowBoxChild *child)
{
EggFlowBox *box = egg_flow_box_child_get_box (child);
@@ -310,6 +296,8 @@ egg_flow_box_child_set_focus (EggFlowBoxChild *child)
egg_flow_box_update_selection (box, child, FALSE, FALSE);
}
+/* GtkWidget implementation {{{2 */
+
static gboolean
egg_flow_box_child_focus (GtkWidget *widget,
GtkDirectionType direction)
@@ -406,6 +394,8 @@ egg_flow_box_child_draw (GtkWidget *widget,
return TRUE;
}
+/* Size allocation {{{3 */
+
static void
egg_flow_box_child_get_full_border (EggFlowBoxChild *child,
GtkBorder *full_border)
@@ -600,6 +590,8 @@ egg_flow_box_child_size_allocate (GtkWidget *widget,
}
}
+/* GObject implementation {{{2 */
+
static void
egg_flow_box_child_class_init (EggFlowBoxChildClass *class)
{
@@ -630,6 +622,20 @@ egg_flow_box_child_class_init (EggFlowBoxChildClass *class)
gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_LIST_ITEM);
}
+static void
+egg_flow_box_child_init (EggFlowBoxChild *child)
+{
+ GtkStyleContext *context;
+
+ gtk_widget_set_can_focus (GTK_WIDGET (child), TRUE);
+ gtk_widget_set_redraw_on_allocate (GTK_WIDGET (child), TRUE);
+
+ context = gtk_widget_get_style_context (GTK_WIDGET (child));
+ gtk_style_context_add_class (context, "grid-child");
+}
+
+/* Public API {{{2 */
+
/**
* egg_flow_box_child_new:
*
@@ -718,95 +724,113 @@ egg_flow_box_child_changed (EggFlowBoxChild *child)
if (box == NULL)
return;
- if (BOX_PRIV (box)->sort_func != NULL)
- {
- g_sequence_sort_changed (CHILD_PRIV (child)->iter,
- (GCompareDataFunc)egg_flow_box_sort, box);
- gtk_widget_queue_resize (GTK_WIDGET (box));
- }
-
+ egg_flow_box_apply_sort (box, child);
egg_flow_box_apply_filter (box, child);
}
-void
-egg_flow_box_set_hadjustment (EggFlowBox *box,
- GtkAdjustment *adjustment)
-{
- EggFlowBoxPrivate *priv;
+/* EggFlowBox {{{1 */
- g_return_if_fail (EGG_IS_FLOW_BOX (box));
- g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
+/* Constants {{{2 */
- priv = BOX_PRIV (box);
+#define DEFAULT_MAX_CHILDREN_PER_LINE 7
+#define RUBBERBAND_START_DISTANCE 32
+#define AUTOSCROLL_FAST_DISTANCE 32
+#define AUTOSCROLL_FACTOR 20
+#define AUTOSCROLL_FACTOR_FAST 10
- g_object_ref (adjustment);
- if (priv->hadjustment)
- g_object_unref (priv->hadjustment);
- priv->hadjustment = adjustment;
- gtk_container_set_focus_hadjustment (GTK_CONTAINER (box), adjustment);
-}
+/* GObject boilerplate {{{2 */
-void
-egg_flow_box_set_vadjustment (EggFlowBox *box,
- GtkAdjustment *adjustment)
-{
- EggFlowBoxPrivate *priv;
+enum {
+ CHILD_ACTIVATED,
+ SELECTED_CHILDREN_CHANGED,
+ ACTIVATE_CURSOR_CHILD,
+ TOGGLE_CURSOR_CHILD,
+ MOVE_CURSOR,
+ SELECT_ALL,
+ UNSELECT_ALL,
+ LAST_SIGNAL
+};
- g_return_if_fail (EGG_IS_FLOW_BOX (box));
- g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
+static guint signals[LAST_SIGNAL] = { 0 };
- priv = BOX_PRIV (box);
+enum {
+ PROP_0,
+ PROP_ORIENTATION,
+ PROP_HOMOGENEOUS,
+ PROP_HALIGN_POLICY,
+ PROP_VALIGN_POLICY,
+ PROP_COLUMN_SPACING,
+ PROP_ROW_SPACING,
+ PROP_MIN_CHILDREN_PER_LINE,
+ PROP_MAX_CHILDREN_PER_LINE,
+ PROP_SELECTION_MODE,
+ PROP_ACTIVATE_ON_SINGLE_CLICK
+};
- g_object_ref (adjustment);
- if (priv->vadjustment)
- g_object_unref (priv->vadjustment);
- priv->vadjustment = adjustment;
- gtk_container_set_focus_vadjustment (GTK_CONTAINER (box), adjustment);
-}
+typedef struct _EggFlowBoxPrivate EggFlowBoxPrivate;
+struct _EggFlowBoxPrivate {
+ GtkOrientation orientation;
+ gboolean homogeneous;
-/**
- * egg_flow_box_get_homogeneous:
- * @box: a #EggFlowBox
- *
- * Returns whether the box is homogeneous (all children are the
- * same size). See gtk_box_set_homogeneous().
- *
- * Return value: %TRUE if the box is homogeneous.
- **/
-gboolean
-egg_flow_box_get_homogeneous (EggFlowBox *box)
-{
- g_return_val_if_fail (EGG_IS_FLOW_BOX (box), FALSE);
+ guint row_spacing;
+ guint column_spacing;
- return BOX_PRIV (box)->homogeneous;
-}
+ EggFlowBoxChild *prelight_child;
+ EggFlowBoxChild *cursor_child;
+ EggFlowBoxChild *selected_child;
-/**
- * egg_flow_box_set_homogeneous:
- * @box: a #EggFlowBox
- * @homogeneous: a boolean value, %TRUE to create equal allotments,
- * %FALSE for variable allotments
- *
- * Sets the #EggFlowBox:homogeneous property of @box, controlling
- * whether or not all children of @box are given equal space
- * in the box.
- */
-void
-egg_flow_box_set_homogeneous (EggFlowBox *box,
- gboolean homogeneous)
-{
- g_return_if_fail (EGG_IS_FLOW_BOX (box));
+ gboolean active_child_active;
+ EggFlowBoxChild *active_child;
- homogeneous = homogeneous != FALSE;
+ GtkSelectionMode selection_mode;
- if (BOX_PRIV (box)->homogeneous != homogeneous)
- {
- BOX_PRIV (box)->homogeneous = homogeneous;
+ GtkAdjustment *hadjustment;
+ GtkAdjustment *vadjustment;
+ gboolean activate_on_single_click;
- g_object_notify (G_OBJECT (box), "homogeneous");
- gtk_widget_queue_resize (GTK_WIDGET (box));
- }
-}
+ guint16 min_children_per_line;
+ guint16 max_children_per_line;
+ guint16 cur_children_per_line;
+
+ GSequence *children;
+
+ EggFlowBoxFilterFunc filter_func;
+ gpointer filter_data;
+ GDestroyNotify filter_destroy;
+
+ EggFlowBoxSortFunc sort_func;
+ gpointer sort_data;
+ GDestroyNotify sort_destroy;
+
+ gboolean track_motion;
+ gboolean rubberband_select;
+ EggFlowBoxChild *rubberband_first;
+ EggFlowBoxChild *rubberband_last;
+ gint button_down_x;
+ gint button_down_y;
+ GdkDevice *rubberband_device;
+
+ GtkScrollType autoscroll_mode;
+ guint autoscroll_id;
+};
+
+#define BOX_PRIV(box) ((EggFlowBoxPrivate*)egg_flow_box_get_instance_private ((EggFlowBox*)(box)))
+
+G_DEFINE_TYPE_WITH_CODE (EggFlowBox, egg_flow_box, GTK_TYPE_CONTAINER,
+ G_ADD_PRIVATE (EggFlowBox)
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL))
+
+/* Internal API, utilities {{{2 */
+
+#define ORIENTATION_ALIGN(box) \
+ (BOX_PRIV(box)->orientation == GTK_ORIENTATION_HORIZONTAL \
+ ? gtk_widget_get_halign (GTK_WIDGET (box)) \
+ : gtk_widget_get_valign (GTK_WIDGET (box)))
+
+#define OPPOSING_ORIENTATION_ALIGN(box) \
+ (BOX_PRIV(box)->orientation == GTK_ORIENTATION_HORIZONTAL \
+ ? gtk_widget_get_valign (GTK_WIDGET (box)) \
+ : gtk_widget_get_halign (GTK_WIDGET (box)))
/* Children are visible if they are shown by the app (visible)
* and not filtered out (child_visible) by the box
@@ -838,6 +862,379 @@ get_visible_children (EggFlowBox *box)
return i;
}
+static EggFlowBoxChild *
+egg_flow_box_find_child_at_pos (EggFlowBox *box,
+ gint x,
+ gint y)
+{
+ GtkWidget *child;
+ GSequenceIter *iter;
+ GtkAllocation allocation;
+
+ for (iter = g_sequence_get_begin_iter (BOX_PRIV (box)->children);
+ !g_sequence_iter_is_end (iter);
+ iter = g_sequence_iter_next (iter))
+ {
+ child = g_sequence_get (iter);
+ if (!child_is_visible (child))
+ continue;
+ gtk_widget_get_allocation (child, &allocation);
+ if (x >= allocation.x && x < (allocation.x + allocation.width) &&
+ y >= allocation.y && y < (allocation.y + allocation.height))
+ return EGG_FLOW_BOX_CHILD (child);
+ }
+
+ return NULL;
+}
+
+static void
+egg_flow_box_update_prelight (EggFlowBox *box,
+ EggFlowBoxChild *child)
+{
+ EggFlowBoxPrivate *priv = BOX_PRIV (box);
+
+ if (child != priv->prelight_child)
+ {
+ priv->prelight_child = child;
+ gtk_widget_queue_draw (GTK_WIDGET (box));
+ }
+}
+
+static void
+egg_flow_box_update_active (EggFlowBox *box,
+ EggFlowBoxChild *child)
+{
+ EggFlowBoxPrivate *priv = BOX_PRIV (box);
+ gboolean val;
+
+ val = priv->active_child == child;
+ if (priv->active_child != NULL &&
+ val != priv->active_child_active)
+ {
+ priv->active_child_active = val;
+ gtk_widget_queue_draw (GTK_WIDGET (box));
+ }
+}
+
+/* Selection utilities {{{3 */
+
+static gboolean
+egg_flow_box_child_set_selected (EggFlowBoxChild *child,
+ gboolean selected)
+{
+ EggFlowBox *box;
+
+ if (CHILD_PRIV (child)->selected != selected)
+ {
+ CHILD_PRIV (child)->selected = selected;
+ if (selected)
+ gtk_widget_set_state_flags (GTK_WIDGET (child),
+ GTK_STATE_FLAG_SELECTED, FALSE);
+ else
+ gtk_widget_unset_state_flags (GTK_WIDGET (child),
+ GTK_STATE_FLAG_SELECTED);
+
+ box = egg_flow_box_child_get_box (child);
+ _egg_flow_box_accessible_selection_changed (GTK_WIDGET (box));
+
+ gtk_widget_queue_draw (GTK_WIDGET (child));
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+egg_flow_box_unselect_all_internal (EggFlowBox *box)
+{
+ EggFlowBoxChild *child;
+ GSequenceIter *iter;
+ gboolean dirty = FALSE;
+
+ if (BOX_PRIV (box)->selection_mode == GTK_SELECTION_NONE)
+ return FALSE;
+
+ for (iter = g_sequence_get_begin_iter (BOX_PRIV (box)->children);
+ !g_sequence_iter_is_end (iter);
+ iter = g_sequence_iter_next (iter))
+ {
+ child = g_sequence_get (iter);
+ dirty |= egg_flow_box_child_set_selected (child, FALSE);
+ }
+
+ return dirty;
+}
+
+static void
+egg_flow_box_unselect_child_info (EggFlowBox *box,
+ EggFlowBoxChild *child)
+{
+ if (!CHILD_PRIV (child)->selected)
+ return;
+
+ if (BOX_PRIV (box)->selection_mode == GTK_SELECTION_NONE)
+ return;
+ else if (BOX_PRIV (box)->selection_mode != GTK_SELECTION_MULTIPLE)
+ egg_flow_box_unselect_all_internal (box);
+ else
+ egg_flow_box_child_set_selected (child, FALSE);
+
+ g_signal_emit (box, signals[SELECTED_CHILDREN_CHANGED], 0);
+}
+
+static void
+egg_flow_box_update_cursor (EggFlowBox *box,
+ EggFlowBoxChild *child)
+{
+ BOX_PRIV (box)->cursor_child = child;
+ gtk_widget_grab_focus (GTK_WIDGET (child));
+ gtk_widget_queue_draw (GTK_WIDGET (child));
+ _egg_flow_box_accessible_update_cursor (GTK_WIDGET (box), GTK_WIDGET (child));
+}
+
+static void
+egg_flow_box_select_child_info (EggFlowBox *box,
+ EggFlowBoxChild *child)
+{
+ if (CHILD_PRIV (child)->selected)
+ return;
+
+ if (BOX_PRIV (box)->selection_mode == GTK_SELECTION_NONE)
+ return;
+ if (BOX_PRIV (box)->selection_mode != GTK_SELECTION_MULTIPLE)
+ egg_flow_box_unselect_all_internal (box);
+
+ egg_flow_box_child_set_selected (child, TRUE);
+ BOX_PRIV (box)->selected_child = child;
+
+ g_signal_emit (box, signals[SELECTED_CHILDREN_CHANGED], 0);
+
+ egg_flow_box_update_cursor (box, child);
+}
+
+static void
+egg_flow_box_select_all_between (EggFlowBox *box,
+ EggFlowBoxChild *child1,
+ EggFlowBoxChild *child2)
+{
+ GSequenceIter *iter, *iter1, *iter2;
+
+ if (child1)
+ iter1 = CHILD_PRIV (child1)->iter;
+ else
+ iter1 = g_sequence_get_begin_iter (BOX_PRIV (box)->children);
+
+ if (child2)
+ iter2 = CHILD_PRIV (child2)->iter;
+ else
+ iter2 = g_sequence_get_end_iter (BOX_PRIV (box)->children);
+
+ if (g_sequence_iter_compare (iter2, iter1) < 0)
+ {
+ iter = iter1;
+ iter1 = iter2;
+ iter2 = iter;
+ }
+
+ for (iter = iter1;
+ !g_sequence_iter_is_end (iter);
+ iter = g_sequence_iter_next (iter))
+ {
+ GtkWidget *child;
+
+ child = g_sequence_get (iter);
+ if (child_is_visible (child))
+ egg_flow_box_child_set_selected (EGG_FLOW_BOX_CHILD (child), TRUE);
+
+ if (g_sequence_iter_compare (iter, iter2) == 0)
+ break;
+ }
+}
+
+static void
+egg_flow_box_update_selection (EggFlowBox *box,
+ EggFlowBoxChild *child,
+ gboolean modify,
+ gboolean extend)
+{
+ EggFlowBoxPrivate *priv = BOX_PRIV (box);
+
+ egg_flow_box_update_cursor (box, child);
+
+ if (priv->selection_mode == GTK_SELECTION_NONE)
+ return;
+
+ if (priv->selection_mode == GTK_SELECTION_BROWSE)
+ {
+ egg_flow_box_unselect_all_internal (box);
+ egg_flow_box_child_set_selected (child, TRUE);
+ priv->selected_child = child;
+ }
+ else if (priv->selection_mode == GTK_SELECTION_SINGLE)
+ {
+ gboolean was_selected;
+
+ was_selected = CHILD_PRIV (child)->selected;
+ egg_flow_box_unselect_all_internal (box);
+ egg_flow_box_child_set_selected (child, modify ? !was_selected : TRUE);
+ priv->selected_child = CHILD_PRIV (child)->selected ? child : NULL;
+ }
+ else /* GTK_SELECTION_MULTIPLE */
+ {
+ if (extend)
+ {
+ egg_flow_box_unselect_all_internal (box);
+ if (priv->selected_child == NULL)
+ {
+ egg_flow_box_child_set_selected (child, TRUE);
+ priv->selected_child = child;
+ }
+ else
+ egg_flow_box_select_all_between (box, priv->selected_child, child);
+ }
+ else
+ {
+ egg_flow_box_child_set_selected (child, modify ? !CHILD_PRIV (child)->selected : TRUE);
+ priv->selected_child = child;
+ }
+ }
+
+ g_signal_emit (box, signals[SELECTED_CHILDREN_CHANGED], 0);
+}
+
+static void
+egg_flow_box_select_and_activate (EggFlowBox *box,
+ EggFlowBoxChild *child)
+{
+ if (child != NULL)
+ {
+ egg_flow_box_select_child_info (box, child);
+ g_signal_emit (box, signals[CHILD_ACTIVATED], 0, child);
+ }
+}
+
+/* Focus utilities {{{3 */
+
+static GSequenceIter *
+egg_flow_box_get_previous_focusable (EggFlowBox *box,
+ GSequenceIter *iter)
+{
+ EggFlowBoxChild *child;
+
+ while (!g_sequence_iter_is_begin (iter))
+ {
+ iter = g_sequence_iter_prev (iter);
+ child = g_sequence_get (iter);
+ if (child_is_visible (GTK_WIDGET (child)) &&
+ gtk_widget_is_sensitive (GTK_WIDGET (child)))
+ return iter;
+ }
+
+ return NULL;
+}
+
+static GSequenceIter *
+egg_flow_box_get_next_focusable (EggFlowBox *box,
+ GSequenceIter *iter)
+{
+ EggFlowBoxChild *child;
+
+ while (TRUE)
+ {
+ iter = g_sequence_iter_next (iter);
+ if (g_sequence_iter_is_end (iter))
+ return iter;
+ child = g_sequence_get (iter);
+ if (child_is_visible (GTK_WIDGET (child)) &&
+ gtk_widget_is_sensitive (GTK_WIDGET (child)))
+ return iter;
+ }
+
+ return NULL;
+}
+
+static GSequenceIter *
+egg_flow_box_get_first_focusable (EggFlowBox *box)
+{
+ GSequenceIter *iter;
+ EggFlowBoxChild *child;
+
+ iter = g_sequence_get_begin_iter (BOX_PRIV (box)->children);
+ child = g_sequence_get (iter);
+ if (child_is_visible (GTK_WIDGET (child)) &&
+ gtk_widget_is_sensitive (GTK_WIDGET (child)))
+ return iter;
+
+ return egg_flow_box_get_next_focusable (box, iter);
+}
+
+static GSequenceIter *
+egg_flow_box_get_last_focusable (EggFlowBox *box)
+{
+ GSequenceIter *iter;
+
+ iter = g_sequence_get_end_iter (BOX_PRIV (box)->children);
+ return egg_flow_box_get_previous_focusable (box, iter);
+}
+
+
+static GSequenceIter *
+egg_flow_box_get_above_focusable (EggFlowBox *box,
+ GSequenceIter *iter)
+{
+ EggFlowBoxChild *child = NULL;
+ gint i;
+
+ while (TRUE)
+ {
+ i = 0;
+ while (i < BOX_PRIV (box)->cur_children_per_line)
+ {
+ if (g_sequence_iter_is_begin (iter))
+ return NULL;
+ iter = g_sequence_iter_prev (iter);
+ child = g_sequence_get (iter);
+ if (child_is_visible (GTK_WIDGET (child)))
+ i++;
+ }
+ if (child && gtk_widget_get_sensitive (GTK_WIDGET (child)))
+ return iter;
+ }
+
+ return NULL;
+}
+
+static GSequenceIter *
+egg_flow_box_get_below_focusable (EggFlowBox *box,
+ GSequenceIter *iter)
+{
+ EggFlowBoxChild *child;
+ gint i;
+
+ while (TRUE)
+ {
+ i = 0;
+ while (i < BOX_PRIV (box)->cur_children_per_line)
+ {
+ iter = g_sequence_iter_next (iter);
+ if (g_sequence_iter_is_end (iter))
+ return iter;
+ child = g_sequence_get (iter);
+ if (child_is_visible (GTK_WIDGET (child)))
+ i++;
+ }
+ if (child && gtk_widget_get_sensitive (GTK_WIDGET (child)))
+ return iter;
+ }
+
+ return NULL;
+}
+
+/* GtkWidget implementation {{{2 */
+
+/* Size allocation {{{3 */
+
/* Used in columned modes where all items share at least their
* equal widths or heights
*/
@@ -1504,79 +1901,6 @@ egg_flow_box_size_allocate (GtkWidget *widget,
g_free (line_sizes);
}
-static void
-egg_flow_box_add (GtkContainer *container,
- GtkWidget *child)
-{
- egg_flow_box_insert (EGG_FLOW_BOX (container), child, -1);
-}
-
-static void
-egg_flow_box_remove (GtkContainer *container,
- GtkWidget *widget)
-{
- EggFlowBox *box = EGG_FLOW_BOX (container);
- EggFlowBoxPrivate *priv = BOX_PRIV (box);
- gboolean was_visible;
- gboolean was_selected;
- EggFlowBoxChild *child;
-
- if (EGG_IS_FLOW_BOX_CHILD (widget))
- child = EGG_FLOW_BOX_CHILD (widget);
- else
- {
- child = (EggFlowBoxChild*)gtk_widget_get_parent (widget);
- if (!EGG_IS_FLOW_BOX_CHILD (child))
- {
- g_warning ("Tried to remove non-child %p\n", widget);
- return;
- }
- }
-
- was_visible = child_is_visible (GTK_WIDGET (child));
- was_selected = CHILD_PRIV (child)->selected;
-
- if (child == priv->prelight_child)
- priv->prelight_child = NULL;
- if (child == priv->active_child)
- priv->active_child = NULL;
- if (child == priv->selected_child)
- priv->selected_child = NULL;
-
- gtk_widget_unparent (GTK_WIDGET (child));
- g_sequence_remove (CHILD_PRIV (child)->iter);
-
- if (was_visible && gtk_widget_get_visible (GTK_WIDGET (box)))
- gtk_widget_queue_resize (GTK_WIDGET (box));
-
- if (was_selected)
- g_signal_emit (box, signals[SELECTED_CHILDREN_CHANGED], 0);
-}
-
-static void
-egg_flow_box_forall (GtkContainer *container,
- gboolean include_internals,
- GtkCallback callback,
- gpointer callback_target)
-{
- GSequenceIter *iter;
- GtkWidget *child;
-
- iter = g_sequence_get_begin_iter (BOX_PRIV (container)->children);
- while (!g_sequence_iter_is_end (iter))
- {
- child = g_sequence_get (iter);
- iter = g_sequence_iter_next (iter);
- callback (child, callback_target);
- }
-}
-
-static GType
-egg_flow_box_child_type (GtkContainer *container)
-{
- return EGG_TYPE_FLOW_BOX_CHILD;
-}
-
static GtkSizeRequestMode
egg_flow_box_get_request_mode (GtkWidget *widget)
{
@@ -2152,379 +2476,126 @@ egg_flow_box_get_preferred_width_for_height (GtkWidget *widget,
*natural_width = nat_width;
}
-/**
- * egg_flow_box_set_row_spacing:
- * @box: An #EggFlowBox
- * @spacing: The spacing to use
- *
- * Sets the vertical space to add between children.
- */
-void
-egg_flow_box_set_row_spacing (EggFlowBox *box,
- guint spacing)
-{
- g_return_if_fail (EGG_IS_FLOW_BOX (box));
-
- if (BOX_PRIV (box)->row_spacing != spacing)
- {
- BOX_PRIV (box)->row_spacing = spacing;
-
- gtk_widget_queue_resize (GTK_WIDGET (box));
- g_object_notify (G_OBJECT (box), "vertical-spacing");
- }
-}
-
-/**
- * egg_flow_box_get_row_spacing:
- * @box: An #EggFlowBox
- *
- * Gets the vertical spacing.
- *
- * Returns: The vertical spacing.
- */
-guint
-egg_flow_box_get_row_spacing (EggFlowBox *box)
-{
- g_return_val_if_fail (EGG_IS_FLOW_BOX (box), FALSE);
-
- return BOX_PRIV (box)->row_spacing;
-}
+/* Drawing {{{3 */
-/**
- * egg_flow_box_set_column_spacing:
- * @box: An #EggFlowBox
- * @spacing: The spacing to use
- *
- * Sets the horizontal space to add between children.
- */
-void
-egg_flow_box_set_column_spacing (EggFlowBox *box,
- guint spacing)
-{
- g_return_if_fail (EGG_IS_FLOW_BOX (box));
-
- if (BOX_PRIV (box)->column_spacing != spacing)
- {
- BOX_PRIV (box)->column_spacing = spacing;
-
- gtk_widget_queue_resize (GTK_WIDGET (box));
- g_object_notify (G_OBJECT (box), "horizontal-spacing");
- }
-}
-
-/**
- * egg_flow_box_get_column_spacing:
- * @box: An #EggFlowBox
- *
- * Gets the horizontal spacing.
- *
- * Returns: The horizontal spacing.
- */
-guint
-egg_flow_box_get_column_spacing (EggFlowBox *box)
-{
- g_return_val_if_fail (EGG_IS_FLOW_BOX (box), FALSE);
-
- return BOX_PRIV (box)->column_spacing;
-}
-
-/**
- * egg_flow_box_set_min_children_per_line:
- * @box: An #EggFlowBox
- * @n_children: The minimum number of children per line
- *
- * Sets the minimum number of children to line up
- * in @box's orientation before flowing.
- */
-void
-egg_flow_box_set_min_children_per_line (EggFlowBox *box,
- guint n_children)
-{
- g_return_if_fail (EGG_IS_FLOW_BOX (box));
-
- if (BOX_PRIV (box)->min_children_per_line != n_children)
- {
- BOX_PRIV (box)->min_children_per_line = n_children;
-
- gtk_widget_queue_resize (GTK_WIDGET (box));
- g_object_notify (G_OBJECT (box), "min-children-per-line");
- }
-}
-
-/**
- * egg_flow_box_get_min_children_per_line:
- * @box: An #EggFlowBox
- *
- * Gets the minimum number of children per line.
- *
- * Returns: The minimum number of children per line.
- */
-guint
-egg_flow_box_get_min_children_per_line (EggFlowBox *box)
+static gboolean
+egg_flow_box_draw (GtkWidget *widget,
+ cairo_t *cr)
{
- g_return_val_if_fail (EGG_IS_FLOW_BOX (box), FALSE);
+ EggFlowBox *box = EGG_FLOW_BOX (widget);
+ EggFlowBoxPrivate *priv = BOX_PRIV (box);
+ GtkAllocation allocation = { 0, };
+ GtkStyleContext* context;
- return BOX_PRIV (box)->min_children_per_line;
-}
+ gtk_widget_get_allocation (GTK_WIDGET (box), &allocation);
+ context = gtk_widget_get_style_context (GTK_WIDGET (box));
+ gtk_render_background (context, cr, 0, 0, allocation.width, allocation.height);
-/**
- * egg_flow_box_set_max_children_per_line:
- * @box: An #EggFlowBox
- * @n_children: The maximum number of children per line.
- *
- * Sets the maximum number of children to request and
- * allocate space for in @box's orientation.
- *
- * Setting the maximum number of children per line
- * limits the overall natural size request to be no more
- * than @n_children children long in the given orientation.
- */
-void
-egg_flow_box_set_max_children_per_line (EggFlowBox *box,
- guint n_children)
-{
- g_return_if_fail (EGG_IS_FLOW_BOX (box));
+ GTK_WIDGET_CLASS (egg_flow_box_parent_class)->draw (widget, cr);
- if (BOX_PRIV (box)->max_children_per_line != n_children)
+ if (priv->rubberband_first && priv->rubberband_last)
{
- BOX_PRIV (box)->max_children_per_line = n_children;
-
- gtk_widget_queue_resize (GTK_WIDGET (box));
- g_object_notify (G_OBJECT (box), "max-children-per-line");
- }
-}
+ GSequenceIter *iter, *iter1, *iter2;
+ GdkRectangle line_rect, rect;
+ GArray *lines;
+ gboolean vertical;
-/**
- * egg_flow_box_get_max_children_per_line:
- * @box: An #EggFlowBox
- *
- * Gets the maximum number of children per line.
- *
- * Returns: The maximum number of children per line.
- */
-guint
-egg_flow_box_get_max_children_per_line (EggFlowBox *box)
-{
- g_return_val_if_fail (EGG_IS_FLOW_BOX (box), FALSE);
+ vertical = priv->orientation == GTK_ORIENTATION_VERTICAL;
- return BOX_PRIV (box)->max_children_per_line;
-}
+ cairo_save (cr);
-/**
- * egg_flow_box_set_activate_on_single_click:
- * @box: An #EggFlowBox
- * @single: %TRUE to emit child-activated on a single click
- *
- * If @single is %TRUE, rows will be activated when you click
- * on them, otherwise you need to double-click.
- */
-void
-egg_flow_box_set_activate_on_single_click (EggFlowBox *box,
- gboolean single)
-{
- g_return_if_fail (EGG_IS_FLOW_BOX (box));
+ context = gtk_widget_get_style_context (widget);
+ gtk_style_context_save (context);
+ gtk_style_context_add_class (context, GTK_STYLE_CLASS_RUBBERBAND);
- single = single != FALSE;
+ iter1 = CHILD_PRIV (priv->rubberband_first)->iter;
+ iter2 = CHILD_PRIV (priv->rubberband_last)->iter;
- if (BOX_PRIV (box)->activate_on_single_click != single)
- {
- BOX_PRIV (box)->activate_on_single_click = single;
- g_object_notify (G_OBJECT (box), "activate-on-single-click");
- }
-}
+ if (g_sequence_iter_compare (iter2, iter1) < 0)
+ {
+ iter = iter1;
+ iter1 = iter2;
+ iter2 = iter;
+ }
-/**
- * egg_flow_box_get_activate_on_single_click:
- * @box: An #EggFlowBox
- *
- * Returns whether rows activate on single clicks.
- *
- * Returns: %TRUE if rows are activated on single click,
- * %FALSE otherwise
- */
-gboolean
-egg_flow_box_get_activate_on_single_click (EggFlowBox *box)
-{
- g_return_val_if_fail (EGG_IS_FLOW_BOX (box), FALSE);
+ line_rect.width = 0;
+ lines = g_array_new (FALSE, FALSE, sizeof (GdkRectangle));
- return BOX_PRIV (box)->activate_on_single_click;
-}
+ for (iter = iter1;
+ !g_sequence_iter_is_end (iter);
+ iter = g_sequence_iter_next (iter))
+ {
+ GtkWidget *child;
-static void
-egg_flow_box_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
-{
- EggFlowBox *box = EGG_FLOW_BOX (object);
- EggFlowBoxPrivate *priv = BOX_PRIV (box);
+ child = g_sequence_get (iter);
+ gtk_widget_get_allocation (GTK_WIDGET (child), &rect);
+ if (line_rect.width == 0)
+ line_rect = rect;
+ else
+ {
+ if ((vertical && rect.x == line_rect.x) ||
+ (!vertical && rect.y == line_rect.y))
+ gdk_rectangle_union (&rect, &line_rect, &line_rect);
+ else
+ {
+ g_array_append_val (lines, line_rect);
+ line_rect = rect;
+ }
+ }
- switch (prop_id)
- {
- case PROP_ORIENTATION:
- g_value_set_enum (value, priv->orientation);
- break;
- case PROP_HOMOGENEOUS:
- g_value_set_boolean (value, priv->homogeneous);
- break;
- case PROP_COLUMN_SPACING:
- g_value_set_uint (value, priv->column_spacing);
- break;
- case PROP_ROW_SPACING:
- g_value_set_uint (value, priv->row_spacing);
- break;
- case PROP_MIN_CHILDREN_PER_LINE:
- g_value_set_uint (value, priv->min_children_per_line);
- break;
- case PROP_MAX_CHILDREN_PER_LINE:
- g_value_set_uint (value, priv->max_children_per_line);
- break;
- case PROP_SELECTION_MODE:
- g_value_set_enum (value, priv->selection_mode);
- break;
- case PROP_ACTIVATE_ON_SINGLE_CLICK:
- g_value_set_boolean (value, priv->activate_on_single_click);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
+ if (g_sequence_iter_compare (iter, iter2) == 0)
+ break;
+ }
-static void
-egg_flow_box_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- EggFlowBox *box = EGG_FLOW_BOX (object);
- EggFlowBoxPrivate *priv = BOX_PRIV (box);
+ if (line_rect.width != 0)
+ g_array_append_val (lines, line_rect);
- switch (prop_id)
- {
- case PROP_ORIENTATION:
- priv->orientation = g_value_get_enum (value);
- /* Re-box the children in the new orientation */
- gtk_widget_queue_resize (GTK_WIDGET (box));
- break;
- case PROP_HOMOGENEOUS:
- egg_flow_box_set_homogeneous (box, g_value_get_boolean (value));
- break;
- case PROP_COLUMN_SPACING:
- egg_flow_box_set_column_spacing (box, g_value_get_uint (value));
- break;
- case PROP_ROW_SPACING:
- egg_flow_box_set_row_spacing (box, g_value_get_uint (value));
- break;
- case PROP_MIN_CHILDREN_PER_LINE:
- egg_flow_box_set_min_children_per_line (box, g_value_get_uint (value));
- break;
- case PROP_MAX_CHILDREN_PER_LINE:
- egg_flow_box_set_max_children_per_line (box, g_value_get_uint (value));
- break;
- case PROP_SELECTION_MODE:
- egg_flow_box_set_selection_mode (box, g_value_get_enum (value));
- break;
- case PROP_ACTIVATE_ON_SINGLE_CLICK:
- egg_flow_box_set_activate_on_single_click (box, g_value_get_boolean (value));
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
+ if (lines->len > 0)
+ {
+ GtkStateFlags state;
+ cairo_path_t *path;
+ GtkBorder border;
+ GdkRGBA border_color;
-static EggFlowBoxChild *
-egg_flow_box_find_child_at_pos (EggFlowBox *box,
- gint x,
- gint y)
-{
- GtkWidget *child;
- GSequenceIter *iter;
- GtkAllocation allocation;
+ if (vertical)
+ path_from_vertical_line_rects (cr, (GdkRectangle *)lines->data, lines->len);
+ else
+ path_from_horizontal_line_rects (cr, (GdkRectangle *)lines->data, lines->len);
- for (iter = g_sequence_get_begin_iter (BOX_PRIV (box)->children);
- !g_sequence_iter_is_end (iter);
- iter = g_sequence_iter_next (iter))
- {
- child = g_sequence_get (iter);
- if (!child_is_visible (child))
- continue;
- gtk_widget_get_allocation (child, &allocation);
- if (x >= allocation.x && x < (allocation.x + allocation.width) &&
- y >= allocation.y && y < (allocation.y + allocation.height))
- return EGG_FLOW_BOX_CHILD (child);
- }
+ /* For some reason we need to copy and reapply the path,
+ * or it gets eaten by gtk_render_background()
+ */
+ path = cairo_copy_path (cr);
- return NULL;
-}
+ cairo_save (cr);
+ cairo_clip (cr);
+ gtk_widget_get_allocation (widget, &allocation);
+ gtk_render_background (context, cr,
+ 0, 0,
+ allocation.width, allocation.height);
+ cairo_restore (cr);
-static void
-egg_flow_box_update_prelight (EggFlowBox *box,
- EggFlowBoxChild *child)
-{
- EggFlowBoxPrivate *priv = BOX_PRIV (box);
+ cairo_append_path (cr, path);
+ cairo_path_destroy (path);
- if (child != priv->prelight_child)
- {
- priv->prelight_child = child;
- gtk_widget_queue_draw (GTK_WIDGET (box));
- }
-}
+ state = gtk_widget_get_state_flags (widget);
+ gtk_style_context_get_border_color (context, state, &border_color);
+ gtk_style_context_get_border (context, state, &border);
-static void
-egg_flow_box_update_active (EggFlowBox *box,
- EggFlowBoxChild *child)
-{
- EggFlowBoxPrivate *priv = BOX_PRIV (box);
- gboolean val;
+ cairo_set_line_width (cr, border.left);
+ gdk_cairo_set_source_rgba (cr, &border_color);
+ cairo_stroke (cr);
+ }
+ g_array_free (lines, TRUE);
- val = priv->active_child == child;
- if (priv->active_child != NULL &&
- val != priv->active_child_active)
- {
- priv->active_child_active = val;
- gtk_widget_queue_draw (GTK_WIDGET (box));
+ gtk_style_context_restore (context);
+ cairo_restore (cr);
}
-}
-
-static gboolean
-egg_flow_box_enter_notify_event (GtkWidget *widget,
- GdkEventCrossing *event)
-{
- EggFlowBox *box = EGG_FLOW_BOX (widget);
- EggFlowBoxChild *child;
-
- if (event->window != gtk_widget_get_window (GTK_WIDGET (box)))
- return FALSE;
- child = egg_flow_box_find_child_at_pos (box, event->x, event->y);
- egg_flow_box_update_prelight (box, child);
- egg_flow_box_update_active (box, child);
-
- return FALSE;
+ return TRUE;
}
-static gboolean
-egg_flow_box_leave_notify_event (GtkWidget *widget,
- GdkEventCrossing *event)
-{
- EggFlowBox *box = EGG_FLOW_BOX (widget);
- EggFlowBoxChild *child = NULL;
-
- if (event->window != gtk_widget_get_window (GTK_WIDGET (box)))
- return FALSE;
-
- if (event->detail != GDK_NOTIFY_INFERIOR)
- child = NULL;
- else
- child = egg_flow_box_find_child_at_pos (box, event->x, event->y);
-
- egg_flow_box_update_prelight (box, child);
- egg_flow_box_update_active (box, child);
-
- return FALSE;
-}
+/* Autoscrolling {{{3 */
static void
remove_autoscroll (EggFlowBox *box)
@@ -2680,6 +2751,46 @@ update_autoscroll_mode (EggFlowBox *box,
}
}
+/* Event handling {{{3 */
+
+static gboolean
+egg_flow_box_enter_notify_event (GtkWidget *widget,
+ GdkEventCrossing *event)
+{
+ EggFlowBox *box = EGG_FLOW_BOX (widget);
+ EggFlowBoxChild *child;
+
+ if (event->window != gtk_widget_get_window (GTK_WIDGET (box)))
+ return FALSE;
+
+ child = egg_flow_box_find_child_at_pos (box, event->x, event->y);
+ egg_flow_box_update_prelight (box, child);
+ egg_flow_box_update_active (box, child);
+
+ return FALSE;
+}
+
+static gboolean
+egg_flow_box_leave_notify_event (GtkWidget *widget,
+ GdkEventCrossing *event)
+{
+ EggFlowBox *box = EGG_FLOW_BOX (widget);
+ EggFlowBoxChild *child = NULL;
+
+ if (event->window != gtk_widget_get_window (GTK_WIDGET (box)))
+ return FALSE;
+
+ if (event->detail != GDK_NOTIFY_INFERIOR)
+ child = NULL;
+ else
+ child = egg_flow_box_find_child_at_pos (box, event->x, event->y);
+
+ egg_flow_box_update_prelight (box, child);
+ egg_flow_box_update_active (box, child);
+
+ return FALSE;
+}
+
static gboolean
egg_flow_box_motion_notify_event (GtkWidget *widget,
GdkEventMotion *event)
@@ -2777,202 +2888,6 @@ egg_flow_box_button_press_event (GtkWidget *widget,
}
static gboolean
-egg_flow_box_child_set_selected (EggFlowBoxChild *child,
- gboolean selected)
-{
- EggFlowBox *box;
-
- if (CHILD_PRIV (child)->selected != selected)
- {
- CHILD_PRIV (child)->selected = selected;
- if (selected)
- gtk_widget_set_state_flags (GTK_WIDGET (child),
- GTK_STATE_FLAG_SELECTED, FALSE);
- else
- gtk_widget_unset_state_flags (GTK_WIDGET (child),
- GTK_STATE_FLAG_SELECTED);
-
- box = egg_flow_box_child_get_box (child);
- _egg_flow_box_accessible_selection_changed (GTK_WIDGET (box));
-
- gtk_widget_queue_draw (GTK_WIDGET (child));
-
- return TRUE;
- }
-
- return FALSE;
-}
-
-static gboolean
-egg_flow_box_unselect_all_internal (EggFlowBox *box)
-{
- EggFlowBoxChild *child;
- GSequenceIter *iter;
- gboolean dirty = FALSE;
-
- if (BOX_PRIV (box)->selection_mode == GTK_SELECTION_NONE)
- return FALSE;
-
- for (iter = g_sequence_get_begin_iter (BOX_PRIV (box)->children);
- !g_sequence_iter_is_end (iter);
- iter = g_sequence_iter_next (iter))
- {
- child = g_sequence_get (iter);
- dirty |= egg_flow_box_child_set_selected (child, FALSE);
- }
-
- return dirty;
-}
-
-static void
-egg_flow_box_unselect_child_info (EggFlowBox *box,
- EggFlowBoxChild *child)
-{
- if (!CHILD_PRIV (child)->selected)
- return;
-
- if (BOX_PRIV (box)->selection_mode == GTK_SELECTION_NONE)
- return;
- else if (BOX_PRIV (box)->selection_mode != GTK_SELECTION_MULTIPLE)
- egg_flow_box_unselect_all_internal (box);
- else
- egg_flow_box_child_set_selected (child, FALSE);
-
- g_signal_emit (box, signals[SELECTED_CHILDREN_CHANGED], 0);
-}
-
-static void
-egg_flow_box_update_cursor (EggFlowBox *box,
- EggFlowBoxChild *child)
-{
- BOX_PRIV (box)->cursor_child = child;
- gtk_widget_grab_focus (GTK_WIDGET (child));
- gtk_widget_queue_draw (GTK_WIDGET (child));
- _egg_flow_box_accessible_update_cursor (GTK_WIDGET (box), GTK_WIDGET (child));
-}
-
-static void
-egg_flow_box_select_child_info (EggFlowBox *box,
- EggFlowBoxChild *child)
-{
- if (CHILD_PRIV (child)->selected)
- return;
-
- if (BOX_PRIV (box)->selection_mode == GTK_SELECTION_NONE)
- return;
- if (BOX_PRIV (box)->selection_mode != GTK_SELECTION_MULTIPLE)
- egg_flow_box_unselect_all_internal (box);
-
- egg_flow_box_child_set_selected (child, TRUE);
- BOX_PRIV (box)->selected_child = child;
-
- g_signal_emit (box, signals[SELECTED_CHILDREN_CHANGED], 0);
-
- egg_flow_box_update_cursor (box, child);
-}
-
-static void
-egg_flow_box_select_all_between (EggFlowBox *box,
- EggFlowBoxChild *child1,
- EggFlowBoxChild *child2)
-{
- GSequenceIter *iter, *iter1, *iter2;
-
- if (child1)
- iter1 = CHILD_PRIV (child1)->iter;
- else
- iter1 = g_sequence_get_begin_iter (BOX_PRIV (box)->children);
-
- if (child2)
- iter2 = CHILD_PRIV (child2)->iter;
- else
- iter2 = g_sequence_get_end_iter (BOX_PRIV (box)->children);
-
- if (g_sequence_iter_compare (iter2, iter1) < 0)
- {
- iter = iter1;
- iter1 = iter2;
- iter2 = iter;
- }
-
- for (iter = iter1;
- !g_sequence_iter_is_end (iter);
- iter = g_sequence_iter_next (iter))
- {
- GtkWidget *child;
-
- child = g_sequence_get (iter);
- if (child_is_visible (child))
- egg_flow_box_child_set_selected (EGG_FLOW_BOX_CHILD (child), TRUE);
-
- if (g_sequence_iter_compare (iter, iter2) == 0)
- break;
- }
-}
-
-static void
-egg_flow_box_update_selection (EggFlowBox *box,
- EggFlowBoxChild *child,
- gboolean modify,
- gboolean extend)
-{
- EggFlowBoxPrivate *priv = BOX_PRIV (box);
-
- egg_flow_box_update_cursor (box, child);
-
- if (priv->selection_mode == GTK_SELECTION_NONE)
- return;
-
- if (priv->selection_mode == GTK_SELECTION_BROWSE)
- {
- egg_flow_box_unselect_all_internal (box);
- egg_flow_box_child_set_selected (child, TRUE);
- priv->selected_child = child;
- }
- else if (priv->selection_mode == GTK_SELECTION_SINGLE)
- {
- gboolean was_selected;
-
- was_selected = CHILD_PRIV (child)->selected;
- egg_flow_box_unselect_all_internal (box);
- egg_flow_box_child_set_selected (child, modify ? !was_selected : TRUE);
- priv->selected_child = CHILD_PRIV (child)->selected ? child : NULL;
- }
- else /* GTK_SELECTION_MULTIPLE */
- {
- if (extend)
- {
- egg_flow_box_unselect_all_internal (box);
- if (priv->selected_child == NULL)
- {
- egg_flow_box_child_set_selected (child, TRUE);
- priv->selected_child = child;
- }
- else
- egg_flow_box_select_all_between (box, priv->selected_child, child);
- }
- else
- {
- egg_flow_box_child_set_selected (child, modify ? !CHILD_PRIV (child)->selected : TRUE);
- priv->selected_child = child;
- }
- }
-
- g_signal_emit (box, signals[SELECTED_CHILDREN_CHANGED], 0);
-}
-
-static void
-egg_flow_box_select_and_activate (EggFlowBox *box,
- EggFlowBoxChild *child)
-{
- if (child != NULL)
- {
- egg_flow_box_select_child_info (box, child);
- g_signal_emit (box, signals[CHILD_ACTIVATED], 0, child);
- }
-}
-
-static gboolean
egg_flow_box_button_release_event (GtkWidget *widget,
GdkEventButton *event)
{
@@ -3012,121 +2927,127 @@ egg_flow_box_button_release_event (GtkWidget *widget,
return FALSE;
}
-static GSequenceIter *
-egg_flow_box_get_previous_focusable (EggFlowBox *box,
- GSequenceIter *iter)
-{
- EggFlowBoxChild *child;
-
- while (!g_sequence_iter_is_begin (iter))
- {
- iter = g_sequence_iter_prev (iter);
- child = g_sequence_get (iter);
- if (child_is_visible (GTK_WIDGET (child)) &&
- gtk_widget_is_sensitive (GTK_WIDGET (child)))
- return iter;
- }
-
- return NULL;
-}
+/* Realize and map {{{3 */
-static GSequenceIter *
-egg_flow_box_get_next_focusable (EggFlowBox *box,
- GSequenceIter *iter)
+static void
+egg_flow_box_realize (GtkWidget *widget)
{
- EggFlowBoxChild *child;
+ EggFlowBox *box = EGG_FLOW_BOX (widget);
+ GtkAllocation allocation;
+ GdkWindowAttr attributes = {0};
+ GdkWindow *window;
- while (TRUE)
- {
- iter = g_sequence_iter_next (iter);
- if (g_sequence_iter_is_end (iter))
- return iter;
- child = g_sequence_get (iter);
- if (child_is_visible (GTK_WIDGET (child)) &&
- gtk_widget_is_sensitive (GTK_WIDGET (child)))
- return iter;
- }
+ gtk_widget_get_allocation (GTK_WIDGET (box), &allocation);
+ gtk_widget_set_realized (GTK_WIDGET (box), TRUE);
- return NULL;
+ 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 (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 (box)),
+ &attributes, GDK_WA_X | GDK_WA_Y);
+ gtk_style_context_set_background (gtk_widget_get_style_context (GTK_WIDGET (box)), window);
+ gtk_widget_register_window (GTK_WIDGET (box), window);
+ gtk_widget_set_window (GTK_WIDGET (box), window);
}
-static GSequenceIter *
-egg_flow_box_get_first_focusable (EggFlowBox *box)
+static void
+egg_flow_box_unmap (GtkWidget *widget)
{
- GSequenceIter *iter;
- EggFlowBoxChild *child;
+ EggFlowBox *box = EGG_FLOW_BOX (widget);
- iter = g_sequence_get_begin_iter (BOX_PRIV (box)->children);
- child = g_sequence_get (iter);
- if (child_is_visible (GTK_WIDGET (child)) &&
- gtk_widget_is_sensitive (GTK_WIDGET (child)))
- return iter;
+ remove_autoscroll (box);
- return egg_flow_box_get_next_focusable (box, iter);
+ GTK_WIDGET_CLASS (egg_flow_box_parent_class)->unmap (widget);
}
-static GSequenceIter *
-egg_flow_box_get_last_focusable (EggFlowBox *box)
-{
- GSequenceIter *iter;
+/* GtkContainer implementation {{{2 */
- iter = g_sequence_get_end_iter (BOX_PRIV (box)->children);
- return egg_flow_box_get_previous_focusable (box, iter);
+static void
+egg_flow_box_add (GtkContainer *container,
+ GtkWidget *child)
+{
+ egg_flow_box_insert (EGG_FLOW_BOX (container), child, -1);
}
-
-static GSequenceIter *
-egg_flow_box_get_above_focusable (EggFlowBox *box,
- GSequenceIter *iter)
+static void
+egg_flow_box_remove (GtkContainer *container,
+ GtkWidget *widget)
{
- EggFlowBoxChild *child = NULL;
- gint i;
+ EggFlowBox *box = EGG_FLOW_BOX (container);
+ EggFlowBoxPrivate *priv = BOX_PRIV (box);
+ gboolean was_visible;
+ gboolean was_selected;
+ EggFlowBoxChild *child;
- while (TRUE)
+ if (EGG_IS_FLOW_BOX_CHILD (widget))
+ child = EGG_FLOW_BOX_CHILD (widget);
+ else
{
- i = 0;
- while (i < BOX_PRIV (box)->cur_children_per_line)
+ child = (EggFlowBoxChild*)gtk_widget_get_parent (widget);
+ if (!EGG_IS_FLOW_BOX_CHILD (child))
{
- if (g_sequence_iter_is_begin (iter))
- return NULL;
- iter = g_sequence_iter_prev (iter);
- child = g_sequence_get (iter);
- if (child_is_visible (GTK_WIDGET (child)))
- i++;
+ g_warning ("Tried to remove non-child %p\n", widget);
+ return;
}
- if (child && gtk_widget_get_sensitive (GTK_WIDGET (child)))
- return iter;
}
- return NULL;
+ was_visible = child_is_visible (GTK_WIDGET (child));
+ was_selected = CHILD_PRIV (child)->selected;
+
+ if (child == priv->prelight_child)
+ priv->prelight_child = NULL;
+ if (child == priv->active_child)
+ priv->active_child = NULL;
+ if (child == priv->selected_child)
+ priv->selected_child = NULL;
+
+ gtk_widget_unparent (GTK_WIDGET (child));
+ g_sequence_remove (CHILD_PRIV (child)->iter);
+
+ if (was_visible && gtk_widget_get_visible (GTK_WIDGET (box)))
+ gtk_widget_queue_resize (GTK_WIDGET (box));
+
+ if (was_selected)
+ g_signal_emit (box, signals[SELECTED_CHILDREN_CHANGED], 0);
}
-static GSequenceIter *
-egg_flow_box_get_below_focusable (EggFlowBox *box,
- GSequenceIter *iter)
+static void
+egg_flow_box_forall (GtkContainer *container,
+ gboolean include_internals,
+ GtkCallback callback,
+ gpointer callback_target)
{
- EggFlowBoxChild *child;
- gint i;
+ GSequenceIter *iter;
+ GtkWidget *child;
- while (TRUE)
+ iter = g_sequence_get_begin_iter (BOX_PRIV (container)->children);
+ while (!g_sequence_iter_is_end (iter))
{
- i = 0;
- while (i < BOX_PRIV (box)->cur_children_per_line)
- {
- iter = g_sequence_iter_next (iter);
- if (g_sequence_iter_is_end (iter))
- return iter;
- child = g_sequence_get (iter);
- if (child_is_visible (GTK_WIDGET (child)))
- i++;
- }
- if (child && gtk_widget_get_sensitive (GTK_WIDGET (child)))
- return iter;
+ child = g_sequence_get (iter);
+ iter = g_sequence_iter_next (iter);
+ callback (child, callback_target);
}
+}
- return NULL;
+static GType
+egg_flow_box_child_type (GtkContainer *container)
+{
+ return EGG_TYPE_FLOW_BOX_CHILD;
}
+/* Keynav {{{2 */
+
static gboolean
egg_flow_box_focus (GtkWidget *widget,
GtkDirectionType direction)
@@ -3441,279 +3362,98 @@ egg_flow_box_move_cursor (EggFlowBox *box,
egg_flow_box_update_selection (box, child, modify_selection, extend_selection);
}
+/* Selection {{{2 */
+
static void
-path_from_horizontal_line_rects (cairo_t *cr,
- GdkRectangle *lines,
- gint n_lines)
+egg_flow_box_selected_children_changed (EggFlowBox *box)
{
- gint start_line, end_line;
- GdkRectangle *r;
- gint i;
-
- /* Join rows vertically by extending to the middle */
- for (i = 0; i < n_lines - 1; i++)
- {
- GdkRectangle *r1 = &lines[i];
- GdkRectangle *r2 = &lines[i+1];
- gint gap, old;
-
- gap = r2->y - (r1->y + r1->height);
- r1->height += gap / 2;
- old = r2->y;
- r2->y = r1->y + r1->height;
- r2->height += old - r2->y;
- }
-
- cairo_new_path (cr);
- start_line = 0;
-
- do
- {
- for (i = start_line; i < n_lines; i++)
- {
- r = &lines[i];
- if (i == start_line)
- cairo_move_to (cr, r->x + r->width, r->y);
- else
- cairo_line_to (cr, r->x + r->width, r->y);
- cairo_line_to (cr, r->x + r->width, r->y + r->height);
-
- if (i < n_lines - 1 &&
- (r->x + r->width < lines[i+1].x ||
- r->x > lines[i+1].x + lines[i+1].width))
- {
- i++;
- break;
- }
- }
- end_line = i;
- for (i = end_line - 1; i >= start_line; i--)
- {
- r = &lines[i];
- cairo_line_to (cr, r->x, r->y + r->height);
- cairo_line_to (cr, r->x, r->y);
- }
- cairo_close_path (cr);
- start_line = end_line;
- }
- while (end_line < n_lines);
+ _egg_flow_box_accessible_selection_changed (GTK_WIDGET (box));
}
+/* GObject implementation {{{2 */
+
static void
-path_from_vertical_line_rects (cairo_t *cr,
- GdkRectangle *lines,
- gint n_lines)
+egg_flow_box_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
{
- gint start_line, end_line;
- GdkRectangle *r;
- gint i;
-
- /* Join rows horizontall by extending to the middle */
- for (i = 0; i < n_lines - 1; i++)
- {
- GdkRectangle *r1 = &lines[i];
- GdkRectangle *r2 = &lines[i+1];
- gint gap, old;
-
- gap = r2->x - (r1->x + r1->width);
- r1->width += gap / 2;
- old = r2->x;
- r2->x = r1->x + r1->width;
- r2->width += old - r2->x;
- }
-
- cairo_new_path (cr);
- start_line = 0;
+ EggFlowBox *box = EGG_FLOW_BOX (object);
+ EggFlowBoxPrivate *priv = BOX_PRIV (box);
- do
+ switch (prop_id)
{
- for (i = start_line; i < n_lines; i++)
- {
- r = &lines[i];
- if (i == start_line)
- cairo_move_to (cr, r->x, r->y + r->height);
- else
- cairo_line_to (cr, r->x, r->y + r->height);
- cairo_line_to (cr, r->x + r->width, r->y + r->height);
-
- if (i < n_lines - 1 &&
- (r->y + r->height < lines[i+1].y ||
- r->y > lines[i+1].y + lines[i+1].height))
- {
- i++;
- break;
- }
- }
- end_line = i;
- for (i = end_line - 1; i >= start_line; i--)
- {
- r = &lines[i];
- cairo_line_to (cr, r->x + r->width, r->y);
- cairo_line_to (cr, r->x, r->y);
- }
- cairo_close_path (cr);
- start_line = end_line;
+ case PROP_ORIENTATION:
+ g_value_set_enum (value, priv->orientation);
+ break;
+ case PROP_HOMOGENEOUS:
+ g_value_set_boolean (value, priv->homogeneous);
+ break;
+ case PROP_COLUMN_SPACING:
+ g_value_set_uint (value, priv->column_spacing);
+ break;
+ case PROP_ROW_SPACING:
+ g_value_set_uint (value, priv->row_spacing);
+ break;
+ case PROP_MIN_CHILDREN_PER_LINE:
+ g_value_set_uint (value, priv->min_children_per_line);
+ break;
+ case PROP_MAX_CHILDREN_PER_LINE:
+ g_value_set_uint (value, priv->max_children_per_line);
+ break;
+ case PROP_SELECTION_MODE:
+ g_value_set_enum (value, priv->selection_mode);
+ break;
+ case PROP_ACTIVATE_ON_SINGLE_CLICK:
+ g_value_set_boolean (value, priv->activate_on_single_click);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
}
- while (end_line < n_lines);
}
-static gboolean
-egg_flow_box_draw (GtkWidget *widget,
- cairo_t *cr)
+static void
+egg_flow_box_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
{
- EggFlowBox *box = EGG_FLOW_BOX (widget);
+ EggFlowBox *box = EGG_FLOW_BOX (object);
EggFlowBoxPrivate *priv = BOX_PRIV (box);
- GtkAllocation allocation = { 0, };
- GtkStyleContext* context;
-
- gtk_widget_get_allocation (GTK_WIDGET (box), &allocation);
- context = gtk_widget_get_style_context (GTK_WIDGET (box));
- gtk_render_background (context, cr, 0, 0, allocation.width, allocation.height);
-
- GTK_WIDGET_CLASS (egg_flow_box_parent_class)->draw (widget, cr);
- if (priv->rubberband_first && priv->rubberband_last)
+ switch (prop_id)
{
- GSequenceIter *iter, *iter1, *iter2;
- GdkRectangle line_rect, rect;
- GArray *lines;
- gboolean vertical;
-
- vertical = priv->orientation == GTK_ORIENTATION_VERTICAL;
-
- cairo_save (cr);
-
- context = gtk_widget_get_style_context (widget);
- gtk_style_context_save (context);
- gtk_style_context_add_class (context, GTK_STYLE_CLASS_RUBBERBAND);
-
- iter1 = CHILD_PRIV (priv->rubberband_first)->iter;
- iter2 = CHILD_PRIV (priv->rubberband_last)->iter;
-
- if (g_sequence_iter_compare (iter2, iter1) < 0)
- {
- iter = iter1;
- iter1 = iter2;
- iter2 = iter;
- }
-
- line_rect.width = 0;
- lines = g_array_new (FALSE, FALSE, sizeof (GdkRectangle));
-
- for (iter = iter1;
- !g_sequence_iter_is_end (iter);
- iter = g_sequence_iter_next (iter))
- {
- GtkWidget *child;
-
- child = g_sequence_get (iter);
- gtk_widget_get_allocation (GTK_WIDGET (child), &rect);
- if (line_rect.width == 0)
- line_rect = rect;
- else
- {
- if ((vertical && rect.x == line_rect.x) ||
- (!vertical && rect.y == line_rect.y))
- gdk_rectangle_union (&rect, &line_rect, &line_rect);
- else
- {
- g_array_append_val (lines, line_rect);
- line_rect = rect;
- }
- }
-
- if (g_sequence_iter_compare (iter, iter2) == 0)
- break;
- }
-
- if (line_rect.width != 0)
- g_array_append_val (lines, line_rect);
-
- if (lines->len > 0)
- {
- GtkStateFlags state;
- cairo_path_t *path;
- GtkBorder border;
- GdkRGBA border_color;
-
- if (vertical)
- path_from_vertical_line_rects (cr, (GdkRectangle *)lines->data, lines->len);
- else
- path_from_horizontal_line_rects (cr, (GdkRectangle *)lines->data, lines->len);
-
- /* For some reason we need to copy and reapply the path,
- * or it gets eaten by gtk_render_background()
- */
- path = cairo_copy_path (cr);
-
- cairo_save (cr);
- cairo_clip (cr);
- gtk_widget_get_allocation (widget, &allocation);
- gtk_render_background (context, cr,
- 0, 0,
- allocation.width, allocation.height);
- cairo_restore (cr);
-
- cairo_append_path (cr, path);
- cairo_path_destroy (path);
-
- state = gtk_widget_get_state_flags (widget);
- gtk_style_context_get_border_color (context, state, &border_color);
- gtk_style_context_get_border (context, state, &border);
-
- cairo_set_line_width (cr, border.left);
- gdk_cairo_set_source_rgba (cr, &border_color);
- cairo_stroke (cr);
- }
- g_array_free (lines, TRUE);
-
- gtk_style_context_restore (context);
- cairo_restore (cr);
+ case PROP_ORIENTATION:
+ priv->orientation = g_value_get_enum (value);
+ /* Re-box the children in the new orientation */
+ gtk_widget_queue_resize (GTK_WIDGET (box));
+ break;
+ case PROP_HOMOGENEOUS:
+ egg_flow_box_set_homogeneous (box, g_value_get_boolean (value));
+ break;
+ case PROP_COLUMN_SPACING:
+ egg_flow_box_set_column_spacing (box, g_value_get_uint (value));
+ break;
+ case PROP_ROW_SPACING:
+ egg_flow_box_set_row_spacing (box, g_value_get_uint (value));
+ break;
+ case PROP_MIN_CHILDREN_PER_LINE:
+ egg_flow_box_set_min_children_per_line (box, g_value_get_uint (value));
+ break;
+ case PROP_MAX_CHILDREN_PER_LINE:
+ egg_flow_box_set_max_children_per_line (box, g_value_get_uint (value));
+ break;
+ case PROP_SELECTION_MODE:
+ egg_flow_box_set_selection_mode (box, g_value_get_enum (value));
+ break;
+ case PROP_ACTIVATE_ON_SINGLE_CLICK:
+ egg_flow_box_set_activate_on_single_click (box, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
}
-
- return TRUE;
-}
-
-static void
-egg_flow_box_realize (GtkWidget *widget)
-{
- EggFlowBox *box = EGG_FLOW_BOX (widget);
- GtkAllocation allocation;
- GdkWindowAttr attributes = {0};
- GdkWindow *window;
-
- gtk_widget_get_allocation (GTK_WIDGET (box), &allocation);
- gtk_widget_set_realized (GTK_WIDGET (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 (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 (box)),
- &attributes, GDK_WA_X | GDK_WA_Y);
- gtk_style_context_set_background (gtk_widget_get_style_context (GTK_WIDGET (box)), window);
- gtk_widget_register_window (GTK_WIDGET (box), window);
- gtk_widget_set_window (GTK_WIDGET (box), window);
-}
-
-static void
-egg_flow_box_unmap (GtkWidget *widget)
-{
- EggFlowBox *box = EGG_FLOW_BOX (widget);
-
- remove_autoscroll (box);
-
- GTK_WIDGET_CLASS (egg_flow_box_parent_class)->unmap (widget);
}
static void
@@ -3734,12 +3474,6 @@ egg_flow_box_finalize (GObject *obj)
}
static void
-egg_flow_box_selected_children_changed (EggFlowBox *box)
-{
- _egg_flow_box_accessible_selection_changed (GTK_WIDGET (box));
-}
-
-static void
egg_flow_box_class_init (EggFlowBoxClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
@@ -3998,6 +3732,8 @@ egg_flow_box_init (EggFlowBox *box)
priv->children = g_sequence_new (NULL);
}
+
+/* Public API {{{1 */
/**
* egg_flow_box_new:
@@ -4013,6 +3749,403 @@ egg_flow_box_new (void)
}
/**
+ * egg_flow_box_insert:
+ * @box: a #EggFlowBox
+ * @widget: the #GtkWidget to add
+ * @position: the position to insert @child in
+ *
+ * Inserts the @child into @box at @position.
+ *
+ * If a sort function is set, the widget will actually be inserted
+ * at the calculated position and this function has the same effect
+ * as gtk_container_add().
+ *
+ * If @position is -1, or larger than the total number of children
+ * in the @box, then the @child will be appended to the end.
+ */
+void
+egg_flow_box_insert (EggFlowBox *box,
+ GtkWidget *widget,
+ gint position)
+{
+ EggFlowBoxPrivate *priv;
+ EggFlowBoxChild *child;
+ GSequenceIter *iter;
+
+ g_return_if_fail (EGG_IS_FLOW_BOX (box));
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ priv = BOX_PRIV (box);
+
+ if (EGG_IS_FLOW_BOX_CHILD (widget))
+ child = EGG_FLOW_BOX_CHILD (widget);
+ else
+ {
+ child = EGG_FLOW_BOX_CHILD (egg_flow_box_child_new ());
+ gtk_widget_show (GTK_WIDGET (child));
+ gtk_container_add (GTK_CONTAINER (child), widget);
+ }
+
+ if (priv->sort_func != NULL)
+ iter = g_sequence_insert_sorted (priv->children, child,
+ (GCompareDataFunc)egg_flow_box_sort, box);
+ else if (position == 0)
+ iter = g_sequence_prepend (priv->children, child);
+ else if (position == -1)
+ iter = g_sequence_append (priv->children, child);
+ else
+ {
+ GSequenceIter *pos;
+ pos = g_sequence_get_iter_at_pos (priv->children, position);
+ iter = g_sequence_insert_before (pos, child);
+ }
+
+ CHILD_PRIV (child)->iter = iter;
+ gtk_widget_set_parent (GTK_WIDGET (child), GTK_WIDGET (box));
+ egg_flow_box_apply_filter (box, child);
+}
+
+/**
+ * egg_flow_box_get_child_at_index:
+ * @box: a #EggFlowBox
+ * @idx: the position of the child
+ *
+ * Gets the nth child in the @box.
+ *
+ * Returns: (transfer none): the child #GtkWidget
+ */
+EggFlowBoxChild *
+egg_flow_box_get_child_at_index (EggFlowBox *box,
+ gint idx)
+{
+ GSequenceIter *iter;
+
+ g_return_val_if_fail (EGG_IS_FLOW_BOX (box), NULL);
+
+ iter = g_sequence_get_iter_at_pos (BOX_PRIV (box)->children, idx);
+ if (iter)
+ return g_sequence_get (iter);
+
+ return NULL;
+}
+
+/**
+ * egg_flow_box_set_hadjustment:
+ * @box: a #EggFlowBox
+ * @adjustment: an adjustment which should be adjusted
+ * when the focus is moved among the descendents of @container
+ *
+ * Hooks up an adjustment to focus handling in @box. The
+ * adjustment is also used for autoscrolling during
+ * rubberband selection. See gtk_scrolled_window_get_hadjustment()
+ * for a typical way of obtaining the adjustment, and
+ * egg_flow_box_set_vadjustment()for setting the vertical
+ * adjustment.
+ *
+ * The adjustments have to be in pixel units and in the same
+ * coordinate system as the allocation for immediate children
+ * of the box.
+ */
+void
+egg_flow_box_set_hadjustment (EggFlowBox *box,
+ GtkAdjustment *adjustment)
+{
+ EggFlowBoxPrivate *priv;
+
+ g_return_if_fail (EGG_IS_FLOW_BOX (box));
+ g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
+
+ priv = BOX_PRIV (box);
+
+ g_object_ref (adjustment);
+ if (priv->hadjustment)
+ g_object_unref (priv->hadjustment);
+ priv->hadjustment = adjustment;
+ gtk_container_set_focus_hadjustment (GTK_CONTAINER (box), adjustment);
+}
+
+/**
+ * egg_flow_box_set_vadjustment:
+ * @box: a #EggFlowBox
+ * @adjustment: an adjustment which should be adjusted
+ * when the focus is moved among the descendents of @container
+ *
+ * Hooks up an adjustment to focus handling in @box. The
+ * adjustment is also used for autoscrolling during
+ * rubberband selection. See gtk_scrolled_window_get_vadjustment()
+ * for a typical way of obtaining the adjustment, and
+ * egg_flow_box_set_hadjustment()for setting the horizontal.
+ * adjustment.
+ *
+ * The adjustments have to be in pixel units and in the same
+ * coordinate system as the allocation for immediate children
+ * of the box.
+ */
+void
+egg_flow_box_set_vadjustment (EggFlowBox *box,
+ GtkAdjustment *adjustment)
+{
+ EggFlowBoxPrivate *priv;
+
+ g_return_if_fail (EGG_IS_FLOW_BOX (box));
+ g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
+
+ priv = BOX_PRIV (box);
+
+ g_object_ref (adjustment);
+ if (priv->vadjustment)
+ g_object_unref (priv->vadjustment);
+ priv->vadjustment = adjustment;
+ gtk_container_set_focus_vadjustment (GTK_CONTAINER (box), adjustment);
+}
+
+/* Setters and getters {{{2 */
+
+/**
+ * egg_flow_box_get_homogeneous:
+ * @box: a #EggFlowBox
+ *
+ * Returns whether the box is homogeneous (all children are the
+ * same size). See gtk_box_set_homogeneous().
+ *
+ * Return value: %TRUE if the box is homogeneous.
+ **/
+gboolean
+egg_flow_box_get_homogeneous (EggFlowBox *box)
+{
+ g_return_val_if_fail (EGG_IS_FLOW_BOX (box), FALSE);
+
+ return BOX_PRIV (box)->homogeneous;
+}
+
+/**
+ * egg_flow_box_set_homogeneous:
+ * @box: a #EggFlowBox
+ * @homogeneous: a boolean value, %TRUE to create equal allotments,
+ * %FALSE for variable allotments
+ *
+ * Sets the #EggFlowBox:homogeneous property of @box, controlling
+ * whether or not all children of @box are given equal space
+ * in the box.
+ */
+void
+egg_flow_box_set_homogeneous (EggFlowBox *box,
+ gboolean homogeneous)
+{
+ g_return_if_fail (EGG_IS_FLOW_BOX (box));
+
+ homogeneous = homogeneous != FALSE;
+
+ if (BOX_PRIV (box)->homogeneous != homogeneous)
+ {
+ BOX_PRIV (box)->homogeneous = homogeneous;
+
+ g_object_notify (G_OBJECT (box), "homogeneous");
+ gtk_widget_queue_resize (GTK_WIDGET (box));
+ }
+}
+
+/**
+ * egg_flow_box_set_row_spacing:
+ * @box: An #EggFlowBox
+ * @spacing: The spacing to use
+ *
+ * Sets the vertical space to add between children.
+ */
+void
+egg_flow_box_set_row_spacing (EggFlowBox *box,
+ guint spacing)
+{
+ g_return_if_fail (EGG_IS_FLOW_BOX (box));
+
+ if (BOX_PRIV (box)->row_spacing != spacing)
+ {
+ BOX_PRIV (box)->row_spacing = spacing;
+
+ gtk_widget_queue_resize (GTK_WIDGET (box));
+ g_object_notify (G_OBJECT (box), "vertical-spacing");
+ }
+}
+
+/**
+ * egg_flow_box_get_row_spacing:
+ * @box: An #EggFlowBox
+ *
+ * Gets the vertical spacing.
+ *
+ * Returns: The vertical spacing.
+ */
+guint
+egg_flow_box_get_row_spacing (EggFlowBox *box)
+{
+ g_return_val_if_fail (EGG_IS_FLOW_BOX (box), FALSE);
+
+ return BOX_PRIV (box)->row_spacing;
+}
+
+/**
+ * egg_flow_box_set_column_spacing:
+ * @box: An #EggFlowBox
+ * @spacing: The spacing to use
+ *
+ * Sets the horizontal space to add between children.
+ */
+void
+egg_flow_box_set_column_spacing (EggFlowBox *box,
+ guint spacing)
+{
+ g_return_if_fail (EGG_IS_FLOW_BOX (box));
+
+ if (BOX_PRIV (box)->column_spacing != spacing)
+ {
+ BOX_PRIV (box)->column_spacing = spacing;
+
+ gtk_widget_queue_resize (GTK_WIDGET (box));
+ g_object_notify (G_OBJECT (box), "horizontal-spacing");
+ }
+}
+
+/**
+ * egg_flow_box_get_column_spacing:
+ * @box: An #EggFlowBox
+ *
+ * Gets the horizontal spacing.
+ *
+ * Returns: The horizontal spacing.
+ */
+guint
+egg_flow_box_get_column_spacing (EggFlowBox *box)
+{
+ g_return_val_if_fail (EGG_IS_FLOW_BOX (box), FALSE);
+
+ return BOX_PRIV (box)->column_spacing;
+}
+
+/**
+ * egg_flow_box_set_min_children_per_line:
+ * @box: An #EggFlowBox
+ * @n_children: The minimum number of children per line
+ *
+ * Sets the minimum number of children to line up
+ * in @box's orientation before flowing.
+ */
+void
+egg_flow_box_set_min_children_per_line (EggFlowBox *box,
+ guint n_children)
+{
+ g_return_if_fail (EGG_IS_FLOW_BOX (box));
+
+ if (BOX_PRIV (box)->min_children_per_line != n_children)
+ {
+ BOX_PRIV (box)->min_children_per_line = n_children;
+
+ gtk_widget_queue_resize (GTK_WIDGET (box));
+ g_object_notify (G_OBJECT (box), "min-children-per-line");
+ }
+}
+
+/**
+ * egg_flow_box_get_min_children_per_line:
+ * @box: An #EggFlowBox
+ *
+ * Gets the minimum number of children per line.
+ *
+ * Returns: The minimum number of children per line.
+ */
+guint
+egg_flow_box_get_min_children_per_line (EggFlowBox *box)
+{
+ g_return_val_if_fail (EGG_IS_FLOW_BOX (box), FALSE);
+
+ return BOX_PRIV (box)->min_children_per_line;
+}
+
+/**
+ * egg_flow_box_set_max_children_per_line:
+ * @box: An #EggFlowBox
+ * @n_children: The maximum number of children per line.
+ *
+ * Sets the maximum number of children to request and
+ * allocate space for in @box's orientation.
+ *
+ * Setting the maximum number of children per line
+ * limits the overall natural size request to be no more
+ * than @n_children children long in the given orientation.
+ */
+void
+egg_flow_box_set_max_children_per_line (EggFlowBox *box,
+ guint n_children)
+{
+ g_return_if_fail (EGG_IS_FLOW_BOX (box));
+
+ if (BOX_PRIV (box)->max_children_per_line != n_children)
+ {
+ BOX_PRIV (box)->max_children_per_line = n_children;
+
+ gtk_widget_queue_resize (GTK_WIDGET (box));
+ g_object_notify (G_OBJECT (box), "max-children-per-line");
+ }
+}
+
+/**
+ * egg_flow_box_get_max_children_per_line:
+ * @box: An #EggFlowBox
+ *
+ * Gets the maximum number of children per line.
+ *
+ * Returns: The maximum number of children per line.
+ */
+guint
+egg_flow_box_get_max_children_per_line (EggFlowBox *box)
+{
+ g_return_val_if_fail (EGG_IS_FLOW_BOX (box), FALSE);
+
+ return BOX_PRIV (box)->max_children_per_line;
+}
+
+/**
+ * egg_flow_box_set_activate_on_single_click:
+ * @box: An #EggFlowBox
+ * @single: %TRUE to emit child-activated on a single click
+ *
+ * If @single is %TRUE, rows will be activated when you click
+ * on them, otherwise you need to double-click.
+ */
+void
+egg_flow_box_set_activate_on_single_click (EggFlowBox *box,
+ gboolean single)
+{
+ g_return_if_fail (EGG_IS_FLOW_BOX (box));
+
+ single = single != FALSE;
+
+ if (BOX_PRIV (box)->activate_on_single_click != single)
+ {
+ BOX_PRIV (box)->activate_on_single_click = single;
+ g_object_notify (G_OBJECT (box), "activate-on-single-click");
+ }
+}
+
+/**
+ * egg_flow_box_get_activate_on_single_click:
+ * @box: An #EggFlowBox
+ *
+ * Returns whether rows activate on single clicks.
+ *
+ * Returns: %TRUE if rows are activated on single click,
+ * %FALSE otherwise
+ */
+gboolean
+egg_flow_box_get_activate_on_single_click (EggFlowBox *box)
+{
+ g_return_val_if_fail (EGG_IS_FLOW_BOX (box), FALSE);
+
+ return BOX_PRIV (box)->activate_on_single_click;
+}
+
+/* Selection handling {{{2 */
+
+/**
* egg_flow_box_get_selected_children:
* @box: a #EggFlowBox
*
@@ -4125,6 +4258,16 @@ egg_flow_box_unselect_all (EggFlowBox *box)
}
/**
+ * EggFlowBoxForeachFunc:
+ * @box: a #EggFlowBox
+ * @child: a #EggFlowBoxChild
+ * @user_data: (closure): user data
+ *
+ * A function used by egg_flow_box_selected_foreach().
+ * It will be called on every selected child of tht @box.
+ */
+
+/**
* egg_flow_box_selected_foreach:
* @box: a #EggFlowBox
* @func: (scope call): the function to call for each selected child
@@ -4205,6 +4348,8 @@ egg_flow_box_get_selection_mode (EggFlowBox *box)
return BOX_PRIV (box)->selection_mode;
}
+/* Filtering {{{2 */
+
static void
egg_flow_box_apply_filter (EggFlowBox *box,
EggFlowBoxChild *child)
@@ -4236,6 +4381,18 @@ egg_flow_box_apply_filter_all (EggFlowBox *box)
}
/**
+ * EggFlowBoxFilterFunc:
+ * @child: a #EggFlowBoxChild that may be filtered
+ * @user_data: (closure): user data
+ *
+ * A function that will be called whenrever a child changes
+ * or is added. It lets you control if the child should be
+ * visible or not.
+ *
+ * Returns: %TRUE if the row should be visible, %FALSE otherwise
+ */
+
+/**
* egg_flow_box_set_filter_func:
* @box: a #EggFlowBox
* @filter_func: (closure user_data) (allow-none): callback that
@@ -4295,6 +4452,33 @@ egg_flow_box_invalidate_filter (EggFlowBox *box)
gtk_widget_queue_resize (GTK_WIDGET (box));
}
+/* Sorting {{{2 */
+
+static void
+egg_flow_box_apply_sort (EggFlowBox *box,
+ EggFlowBoxChild *child)
+{
+ if (BOX_PRIV (box)->sort_func != NULL)
+ {
+ g_sequence_sort_changed (CHILD_PRIV (child)->iter,
+ (GCompareDataFunc)egg_flow_box_sort, box);
+ gtk_widget_queue_resize (GTK_WIDGET (box));
+ }
+}
+
+/**
+ * EggFlowBoxSortFunc:
+ * @child1: the first child
+ * @child2: the second child
+ * @user_data: (closure): user data
+ *
+ * A function to compare two children to determine which
+ * should come first.
+ *
+ * Returns: < 0 if @child1 should be before @child2, 0 if
+ * the are equal, and > 0 otherwise
+ */
+
/**
* egg_flow_box_set_sort_func:
* @box: a #EggFlowBox
@@ -4369,83 +4553,4 @@ egg_flow_box_invalidate_sort (EggFlowBox *box)
}
}
-/**
- * egg_flow_box_insert:
- * @box: a #EggFlowBox
- * @widget: the #GtkWidget to add
- * @position: the position to insert @child in
- *
- * Inserts the @child into @box at @position.
- *
- * If a sort function is set, the widget will actually be inserted
- * at the calculated position and this function has the same effect
- * as gtk_container_add().
- *
- * If @position is -1, or larger than the total number of children
- * in the @box, then the @child will be appended to the end.
- */
-void
-egg_flow_box_insert (EggFlowBox *box,
- GtkWidget *widget,
- gint position)
-{
- EggFlowBoxPrivate *priv;
- EggFlowBoxChild *child;
- GSequenceIter *iter;
-
- g_return_if_fail (EGG_IS_FLOW_BOX (box));
- g_return_if_fail (GTK_IS_WIDGET (widget));
-
- priv = BOX_PRIV (box);
-
- if (EGG_IS_FLOW_BOX_CHILD (widget))
- child = EGG_FLOW_BOX_CHILD (widget);
- else
- {
- child = EGG_FLOW_BOX_CHILD (egg_flow_box_child_new ());
- gtk_widget_show (GTK_WIDGET (child));
- gtk_container_add (GTK_CONTAINER (child), widget);
- }
-
- if (priv->sort_func != NULL)
- iter = g_sequence_insert_sorted (priv->children, child,
- (GCompareDataFunc)egg_flow_box_sort, box);
- else if (position == 0)
- iter = g_sequence_prepend (priv->children, child);
- else if (position == -1)
- iter = g_sequence_append (priv->children, child);
- else
- {
- GSequenceIter *pos;
- pos = g_sequence_get_iter_at_pos (priv->children, position);
- iter = g_sequence_insert_before (pos, child);
- }
-
- CHILD_PRIV (child)->iter = iter;
- gtk_widget_set_parent (GTK_WIDGET (child), GTK_WIDGET (box));
- egg_flow_box_apply_filter (box, child);
-}
-
-/**
- * egg_flow_box_get_child_at_index:
- * @box: a #EggFlowBox
- * @idx: the position of the child
- *
- * Gets the nth child in the @box.
- *
- * Returns: (transfer none): the child #GtkWidget
- */
-EggFlowBoxChild *
-egg_flow_box_get_child_at_index (EggFlowBox *box,
- gint idx)
-{
- GSequenceIter *iter;
-
- g_return_val_if_fail (EGG_IS_FLOW_BOX (box), NULL);
-
- iter = g_sequence_get_iter_at_pos (BOX_PRIV (box)->children, idx);
- if (iter)
- return g_sequence_get (iter);
-
- return NULL;
-}
+/* vim:set foldmethod=marker: */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]