2
* Copyright (C) 2012 Alexander Larsson <alexl@redhat.com>
4
* This library is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU Lesser General Public
6
* License as published by the Free Software Foundation; either
7
* version 2 of the License, or (at your option) any later version.
9
* This library is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
* Lesser General Public License for more details.
14
* You should have received a copy of the GNU Lesser General Public
15
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
19
#include <glib-object.h>
26
#include <gobject/gvaluecollector.h>
28
#include "egg-list-box.h"
29
#include "egg-list-box-accessible.h"
31
/* This already exists in gtk as _gtk_marshal_VOID__ENUM_INT, inline it here for now
32
to avoid separate marshallers file */
34
_egg_marshal_VOID__ENUM_INT (GClosure * closure,
35
GValue * return_value,
37
const GValue * param_values,
38
gpointer invocation_hint,
39
gpointer marshal_data)
41
typedef void (*GMarshalFunc_VOID__ENUM_INT) (gpointer data1, gint arg_1, gint arg_2, gpointer data2);
42
register GMarshalFunc_VOID__ENUM_INT callback;
43
register GCClosure * cc;
44
register gpointer data1;
45
register gpointer data2;
46
cc = (GCClosure *) closure;
47
g_return_if_fail (n_param_values == 3);
48
if (G_CCLOSURE_SWAP_DATA (closure)) {
49
data1 = closure->data;
50
data2 = param_values->data[0].v_pointer;
52
data1 = param_values->data[0].v_pointer;
53
data2 = closure->data;
55
callback = (GMarshalFunc_VOID__ENUM_INT) (marshal_data ? marshal_data : cc->callback);
56
callback (data1, g_value_get_enum (param_values + 1), g_value_get_int (param_values + 2), data2);
59
typedef struct _EggListBoxChildInfo EggListBoxChildInfo;
61
struct _EggListBoxPrivate
64
GHashTable *child_hash;
65
GHashTable *separator_hash;
67
GCompareDataFunc sort_func;
68
gpointer sort_func_target;
69
GDestroyNotify sort_func_target_destroy_notify;
71
EggListBoxFilterFunc filter_func;
72
gpointer filter_func_target;
73
GDestroyNotify filter_func_target_destroy_notify;
75
EggListBoxUpdateSeparatorFunc update_separator_func;
76
gpointer update_separator_func_target;
77
GDestroyNotify update_separator_func_target_destroy_notify;
79
EggListBoxChildInfo *selected_child;
80
EggListBoxChildInfo *prelight_child;
81
EggListBoxChildInfo *cursor_child;
83
gboolean active_child_active;
84
EggListBoxChildInfo *active_child;
86
GtkSelectionMode selection_mode;
88
GtkAdjustment *adjustment;
89
gboolean activate_single_click;
92
GtkWidget *drag_highlighted_widget;
93
guint auto_scroll_timeout_id;
96
struct _EggListBoxChildInfo
100
GtkWidget *separator;
108
ACTIVATE_CURSOR_CHILD,
118
PROP_ACTIVATE_ON_SINGLE_CLICK,
122
G_DEFINE_TYPE (EggListBox, egg_list_box, GTK_TYPE_CONTAINER)
124
static EggListBoxChildInfo *egg_list_box_find_child_at_y (EggListBox *list_box,
126
static EggListBoxChildInfo *egg_list_box_lookup_info (EggListBox *list_box,
128
static void egg_list_box_update_selected (EggListBox *list_box,
129
EggListBoxChildInfo *child);
130
static void egg_list_box_apply_filter_all (EggListBox *list_box);
131
static void egg_list_box_update_separator (EggListBox *list_box,
132
GSequenceIter *iter);
133
static GSequenceIter * egg_list_box_get_next_visible (EggListBox *list_box,
134
GSequenceIter *_iter);
135
static void egg_list_box_apply_filter (EggListBox *list_box,
137
static void egg_list_box_add_move_binding (GtkBindingSet *binding_set,
139
GdkModifierType modmask,
140
GtkMovementStep step,
142
static void egg_list_box_update_cursor (EggListBox *list_box,
143
EggListBoxChildInfo *child);
144
static void egg_list_box_select_and_activate (EggListBox *list_box,
145
EggListBoxChildInfo *child);
146
static void egg_list_box_update_prelight (EggListBox *list_box,
147
EggListBoxChildInfo *child);
148
static void egg_list_box_update_active (EggListBox *list_box,
149
EggListBoxChildInfo *child);
150
static gboolean egg_list_box_real_enter_notify_event (GtkWidget *widget,
151
GdkEventCrossing *event);
152
static gboolean egg_list_box_real_leave_notify_event (GtkWidget *widget,
153
GdkEventCrossing *event);
154
static gboolean egg_list_box_real_motion_notify_event (GtkWidget *widget,
155
GdkEventMotion *event);
156
static gboolean egg_list_box_real_button_press_event (GtkWidget *widget,
157
GdkEventButton *event);
158
static gboolean egg_list_box_real_button_release_event (GtkWidget *widget,
159
GdkEventButton *event);
160
static void egg_list_box_real_show (GtkWidget *widget);
161
static gboolean egg_list_box_real_focus (GtkWidget *widget,
162
GtkDirectionType direction);
163
static GSequenceIter* egg_list_box_get_previous_visible (EggListBox *list_box,
164
GSequenceIter *_iter);
165
static EggListBoxChildInfo *egg_list_box_get_first_visible (EggListBox *list_box);
166
static EggListBoxChildInfo *egg_list_box_get_last_visible (EggListBox *list_box);
167
static gboolean egg_list_box_real_draw (GtkWidget *widget,
169
static void egg_list_box_real_realize (GtkWidget *widget);
170
static void egg_list_box_real_add (GtkContainer *container,
172
static void egg_list_box_real_remove (GtkContainer *container,
174
static void egg_list_box_real_forall_internal (GtkContainer *container,
175
gboolean include_internals,
176
GtkCallback callback,
177
void *callback_target);
178
static void egg_list_box_real_compute_expand_internal (GtkWidget *widget,
181
static GType egg_list_box_real_child_type (GtkContainer *container);
182
static GtkSizeRequestMode egg_list_box_real_get_request_mode (GtkWidget *widget);
183
static void egg_list_box_real_size_allocate (GtkWidget *widget,
184
GtkAllocation *allocation);
185
static void egg_list_box_real_drag_leave (GtkWidget *widget,
186
GdkDragContext *context,
188
static gboolean egg_list_box_real_drag_motion (GtkWidget *widget,
189
GdkDragContext *context,
193
static void egg_list_box_real_activate_cursor_child (EggListBox *list_box);
194
static void egg_list_box_real_toggle_cursor_child (EggListBox *list_box);
195
static void egg_list_box_real_move_cursor (EggListBox *list_box,
196
GtkMovementStep step,
198
static void egg_list_box_real_refilter (EggListBox *list_box);
199
static void egg_list_box_finalize (GObject *obj);
202
static void egg_list_box_real_get_preferred_height (GtkWidget *widget,
203
gint *minimum_height,
204
gint *natural_height);
205
static void egg_list_box_real_get_preferred_height_for_width (GtkWidget *widget,
207
gint *minimum_height,
208
gint *natural_height);
209
static void egg_list_box_real_get_preferred_width (GtkWidget *widget,
211
gint *natural_width);
212
static void egg_list_box_real_get_preferred_width_for_height (GtkWidget *widget,
215
gint *natural_width);
217
static GParamSpec *properties[LAST_PROPERTY] = { NULL, };
218
static guint signals[LAST_SIGNAL] = { 0 };
220
static EggListBoxChildInfo*
221
egg_list_box_child_info_new (GtkWidget *widget)
223
EggListBoxChildInfo *info;
225
info = g_new0 (EggListBoxChildInfo, 1);
226
info->widget = g_object_ref (widget);
231
egg_list_box_child_info_free (EggListBoxChildInfo *info)
233
g_clear_object (&info->widget);
234
g_clear_object (&info->separator);
239
egg_list_box_new (void)
241
return g_object_new (EGG_TYPE_LIST_BOX, NULL);
245
egg_list_box_init (EggListBox *list_box)
247
EggListBoxPrivate *priv;
249
list_box->priv = priv =
250
G_TYPE_INSTANCE_GET_PRIVATE (list_box, EGG_TYPE_LIST_BOX, EggListBoxPrivate);
252
gtk_widget_set_can_focus (GTK_WIDGET (list_box), TRUE);
253
gtk_widget_set_has_window (GTK_WIDGET (list_box), TRUE);
254
gtk_widget_set_redraw_on_allocate (GTK_WIDGET (list_box), TRUE);
255
priv->selection_mode = GTK_SELECTION_SINGLE;
256
priv->activate_single_click = TRUE;
258
priv->children = g_sequence_new ((GDestroyNotify)egg_list_box_child_info_free);
259
priv->child_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
260
priv->separator_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
264
egg_list_box_get_property (GObject *obj,
269
EggListBox *list_box = EGG_LIST_BOX (obj);
273
case PROP_SELECTION_MODE:
274
g_value_set_enum (value, list_box->priv->selection_mode);
276
case PROP_ACTIVATE_ON_SINGLE_CLICK:
277
g_value_set_boolean (value, list_box->priv->activate_single_click);
280
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
286
egg_list_box_set_property (GObject *obj,
291
EggListBox *list_box = EGG_LIST_BOX (obj);
295
case PROP_SELECTION_MODE:
296
egg_list_box_set_selection_mode (list_box, g_value_get_enum (value));
298
case PROP_ACTIVATE_ON_SINGLE_CLICK:
299
egg_list_box_set_activate_on_single_click (list_box, g_value_get_boolean (value));
302
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
308
egg_list_box_finalize (GObject *obj)
310
EggListBox *list_box = EGG_LIST_BOX (obj);
311
EggListBoxPrivate *priv = list_box->priv;
313
if (priv->auto_scroll_timeout_id != ((guint) 0))
314
g_source_remove (priv->auto_scroll_timeout_id);
316
if (priv->sort_func_target_destroy_notify != NULL)
317
priv->sort_func_target_destroy_notify (priv->sort_func_target);
318
if (priv->filter_func_target_destroy_notify != NULL)
319
priv->filter_func_target_destroy_notify (priv->filter_func_target);
320
if (priv->update_separator_func_target_destroy_notify != NULL)
321
priv->update_separator_func_target_destroy_notify (priv->update_separator_func_target);
323
g_clear_object (&priv->adjustment);
324
g_clear_object (&priv->drag_highlighted_widget);
326
g_sequence_free (priv->children);
327
g_hash_table_unref (priv->child_hash);
328
g_hash_table_unref (priv->separator_hash);
330
G_OBJECT_CLASS (egg_list_box_parent_class)->finalize (obj);
334
egg_list_box_class_init (EggListBoxClass *klass)
336
GObjectClass *object_class = G_OBJECT_CLASS (klass);
337
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
338
GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
339
GtkBindingSet *binding_set;
341
egg_list_box_parent_class = g_type_class_peek_parent (klass);
343
g_type_class_add_private (klass, sizeof (EggListBoxPrivate));
345
gtk_widget_class_set_accessible_type (widget_class, EGG_TYPE_LIST_BOX_ACCESSIBLE);
347
object_class->get_property = egg_list_box_get_property;
348
object_class->set_property = egg_list_box_set_property;
349
object_class->finalize = egg_list_box_finalize;
350
widget_class->enter_notify_event = egg_list_box_real_enter_notify_event;
351
widget_class->leave_notify_event = egg_list_box_real_leave_notify_event;
352
widget_class->motion_notify_event = egg_list_box_real_motion_notify_event;
353
widget_class->button_press_event = egg_list_box_real_button_press_event;
354
widget_class->button_release_event = egg_list_box_real_button_release_event;
355
widget_class->show = egg_list_box_real_show;
356
widget_class->focus = egg_list_box_real_focus;
357
widget_class->draw = egg_list_box_real_draw;
358
widget_class->realize = egg_list_box_real_realize;
359
widget_class->compute_expand = egg_list_box_real_compute_expand_internal;
360
widget_class->get_request_mode = egg_list_box_real_get_request_mode;
361
widget_class->get_preferred_height = egg_list_box_real_get_preferred_height;
362
widget_class->get_preferred_height_for_width = egg_list_box_real_get_preferred_height_for_width;
363
widget_class->get_preferred_width = egg_list_box_real_get_preferred_width;
364
widget_class->get_preferred_width_for_height = egg_list_box_real_get_preferred_width_for_height;
365
widget_class->size_allocate = egg_list_box_real_size_allocate;
366
widget_class->drag_leave = egg_list_box_real_drag_leave;
367
widget_class->drag_motion = egg_list_box_real_drag_motion;
368
container_class->add = egg_list_box_real_add;
369
container_class->remove = egg_list_box_real_remove;
370
container_class->forall = egg_list_box_real_forall_internal;
371
container_class->child_type = egg_list_box_real_child_type;
372
klass->activate_cursor_child = egg_list_box_real_activate_cursor_child;
373
klass->toggle_cursor_child = egg_list_box_real_toggle_cursor_child;
374
klass->move_cursor = egg_list_box_real_move_cursor;
375
klass->refilter = egg_list_box_real_refilter;
377
properties[PROP_SELECTION_MODE] =
378
g_param_spec_enum ("selection-mode",
380
"The selection mode",
381
GTK_TYPE_SELECTION_MODE,
382
GTK_SELECTION_SINGLE,
385
properties[PROP_ACTIVATE_ON_SINGLE_CLICK] =
386
g_param_spec_boolean ("activate-on-single-click",
387
"Activate on Single Click",
388
"Activate row on a single click",
392
g_object_class_install_properties (object_class, LAST_PROPERTY, properties);
394
signals[CHILD_SELECTED] =
395
g_signal_new ("child-selected",
398
G_STRUCT_OFFSET (EggListBoxClass, child_selected),
400
g_cclosure_marshal_VOID__OBJECT,
403
signals[CHILD_ACTIVATED] =
404
g_signal_new ("child-activated",
407
G_STRUCT_OFFSET (EggListBoxClass, child_activated),
409
g_cclosure_marshal_VOID__OBJECT,
412
signals[ACTIVATE_CURSOR_CHILD] =
413
g_signal_new ("activate-cursor-child",
415
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
416
G_STRUCT_OFFSET (EggListBoxClass, activate_cursor_child),
418
g_cclosure_marshal_VOID__VOID,
420
signals[TOGGLE_CURSOR_CHILD] =
421
g_signal_new ("toggle-cursor-child",
423
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
424
G_STRUCT_OFFSET (EggListBoxClass, toggle_cursor_child),
426
g_cclosure_marshal_VOID__VOID,
428
signals[MOVE_CURSOR] =
429
g_signal_new ("move-cursor",
431
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
432
G_STRUCT_OFFSET (EggListBoxClass, move_cursor),
434
_egg_marshal_VOID__ENUM_INT,
436
GTK_TYPE_MOVEMENT_STEP, G_TYPE_INT);
438
g_signal_new ("refilter",
441
G_STRUCT_OFFSET (EggListBoxClass, refilter),
443
g_cclosure_marshal_VOID__VOID,
446
widget_class->activate_signal = signals[ACTIVATE_CURSOR_CHILD];
448
binding_set = gtk_binding_set_by_class (klass);
449
egg_list_box_add_move_binding (binding_set, GDK_KEY_Home, 0,
450
GTK_MOVEMENT_BUFFER_ENDS, -1);
451
egg_list_box_add_move_binding (binding_set, GDK_KEY_KP_Home, 0,
452
GTK_MOVEMENT_BUFFER_ENDS, -1);
453
egg_list_box_add_move_binding (binding_set, GDK_KEY_End, 0,
454
GTK_MOVEMENT_BUFFER_ENDS, 1);
455
egg_list_box_add_move_binding (binding_set, GDK_KEY_KP_End, 0,
456
GTK_MOVEMENT_BUFFER_ENDS, 1);
457
egg_list_box_add_move_binding (binding_set, GDK_KEY_Up, GDK_CONTROL_MASK,
458
GTK_MOVEMENT_DISPLAY_LINES, -1);
459
egg_list_box_add_move_binding (binding_set, GDK_KEY_KP_Up, GDK_CONTROL_MASK,
460
GTK_MOVEMENT_DISPLAY_LINES, -1);
461
egg_list_box_add_move_binding (binding_set, GDK_KEY_Down, GDK_CONTROL_MASK,
462
GTK_MOVEMENT_DISPLAY_LINES, 1);
463
egg_list_box_add_move_binding (binding_set, GDK_KEY_KP_Down, GDK_CONTROL_MASK,
464
GTK_MOVEMENT_DISPLAY_LINES, 1);
465
egg_list_box_add_move_binding (binding_set, GDK_KEY_Page_Up, 0,
466
GTK_MOVEMENT_PAGES, -1);
467
egg_list_box_add_move_binding (binding_set, GDK_KEY_KP_Page_Up, 0,
468
GTK_MOVEMENT_PAGES, -1);
469
egg_list_box_add_move_binding (binding_set, GDK_KEY_Page_Down, 0,
470
GTK_MOVEMENT_PAGES, 1);
471
egg_list_box_add_move_binding (binding_set, GDK_KEY_KP_Page_Down, 0,
472
GTK_MOVEMENT_PAGES, 1);
473
gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, GDK_CONTROL_MASK,
474
"toggle-cursor-child", 0, NULL);
478
* egg_list_box_get_selected_child:
479
* @self: An #EggListBox.
481
* Gets the selected child.
483
* Return value: (transfer none): The selected #GtkWidget.
486
egg_list_box_get_selected_child (EggListBox *list_box)
488
EggListBoxPrivate *priv = list_box->priv;
490
g_return_val_if_fail (list_box != NULL, NULL);
492
if (priv->selected_child != NULL)
493
return priv->selected_child->widget;
499
* egg_list_box_get_child_at_y:
500
* @self: An #EggListBox.
503
* Gets the child at the position.
505
* Return value: (transfer none): The child #GtkWidget.
508
egg_list_box_get_child_at_y (EggListBox *list_box, gint y)
510
EggListBoxChildInfo *child;
512
g_return_val_if_fail (list_box != NULL, NULL);
514
child = egg_list_box_find_child_at_y (list_box, y);
518
return child->widget;
523
egg_list_box_select_child (EggListBox *list_box, GtkWidget *child)
525
EggListBoxChildInfo *info = NULL;
527
g_return_if_fail (list_box != NULL);
530
info = egg_list_box_lookup_info (list_box, child);
532
egg_list_box_update_selected (list_box, info);
536
egg_list_box_set_adjustment (EggListBox *list_box,
537
GtkAdjustment *adjustment)
539
EggListBoxPrivate *priv = list_box->priv;
541
g_return_if_fail (list_box != NULL);
543
g_object_ref (adjustment);
544
if (priv->adjustment)
545
g_object_unref (priv->adjustment);
546
priv->adjustment = adjustment;
547
gtk_container_set_focus_vadjustment (GTK_CONTAINER (list_box),
552
egg_list_box_add_to_scrolled (EggListBox *list_box,
553
GtkScrolledWindow *scrolled)
555
g_return_if_fail (list_box != NULL);
556
g_return_if_fail (scrolled != NULL);
558
gtk_container_add (GTK_CONTAINER (scrolled), GTK_WIDGET (list_box));
559
egg_list_box_set_adjustment (list_box,
560
gtk_scrolled_window_get_vadjustment (scrolled));
565
egg_list_box_set_selection_mode (EggListBox *list_box, GtkSelectionMode mode)
567
EggListBoxPrivate *priv = list_box->priv;
569
g_return_if_fail (list_box != NULL);
571
if (mode == GTK_SELECTION_MULTIPLE)
573
g_warning ("Multiple selections not supported");
577
if (priv->selection_mode == mode)
580
priv->selection_mode = mode;
581
if (mode == GTK_SELECTION_NONE)
582
egg_list_box_update_selected (list_box, NULL);
584
g_object_notify_by_pspec (G_OBJECT (list_box), properties[PROP_SELECTION_MODE]);
589
egg_list_box_set_filter_func (EggListBox *list_box,
590
EggListBoxFilterFunc f,
592
GDestroyNotify f_target_destroy_notify)
594
EggListBoxPrivate *priv = list_box->priv;
596
g_return_if_fail (list_box != NULL);
598
if (priv->filter_func_target_destroy_notify != NULL)
599
priv->filter_func_target_destroy_notify (priv->filter_func_target);
601
priv->filter_func = f;
602
priv->filter_func_target = f_target;
603
priv->filter_func_target_destroy_notify = f_target_destroy_notify;
605
egg_list_box_refilter (list_box);
609
egg_list_box_set_separator_funcs (EggListBox *list_box,
610
EggListBoxUpdateSeparatorFunc update_separator,
611
void *update_separator_target,
612
GDestroyNotify update_separator_target_destroy_notify)
614
EggListBoxPrivate *priv = list_box->priv;
616
g_return_if_fail (list_box != NULL);
618
if (priv->update_separator_func_target_destroy_notify != NULL)
619
priv->update_separator_func_target_destroy_notify (priv->update_separator_func_target);
621
priv->update_separator_func = update_separator;
622
priv->update_separator_func_target = update_separator_target;
623
priv->update_separator_func_target_destroy_notify = update_separator_target_destroy_notify;
624
egg_list_box_reseparate (list_box);
628
egg_list_box_real_refilter (EggListBox *list_box)
630
egg_list_box_apply_filter_all (list_box);
631
egg_list_box_reseparate (list_box);
632
gtk_widget_queue_resize (GTK_WIDGET (list_box));
636
egg_list_box_refilter (EggListBox *list_box)
638
g_return_if_fail (list_box != NULL);
640
g_signal_emit (list_box, signals[REFILTER], 0);
644
do_sort (EggListBoxChildInfo *a,
645
EggListBoxChildInfo *b,
646
EggListBox *list_box)
648
EggListBoxPrivate *priv = list_box->priv;
650
return priv->sort_func (a->widget, b->widget,
651
priv->sort_func_target);
655
egg_list_box_resort (EggListBox *list_box)
657
EggListBoxPrivate *priv = list_box->priv;
659
g_return_if_fail (list_box != NULL);
661
g_sequence_sort (priv->children,
662
(GCompareDataFunc)do_sort, list_box);
663
egg_list_box_reseparate (list_box);
664
gtk_widget_queue_resize (GTK_WIDGET (list_box));
668
egg_list_box_reseparate (EggListBox *list_box)
670
EggListBoxPrivate *priv = list_box->priv;
673
g_return_if_fail (list_box != NULL);
675
for (iter = g_sequence_get_begin_iter (priv->children);
676
!g_sequence_iter_is_end (iter);
677
iter = g_sequence_iter_next (iter))
678
egg_list_box_update_separator (list_box, iter);
680
gtk_widget_queue_resize (GTK_WIDGET (list_box));
684
egg_list_box_set_sort_func (EggListBox *list_box,
687
GDestroyNotify f_target_destroy_notify)
689
EggListBoxPrivate *priv = list_box->priv;
691
g_return_if_fail (list_box != NULL);
693
if (priv->sort_func_target_destroy_notify != NULL)
694
priv->sort_func_target_destroy_notify (priv->sort_func_target);
697
priv->sort_func_target = f_target;
698
priv->sort_func_target_destroy_notify = f_target_destroy_notify;
699
egg_list_box_resort (list_box);
703
egg_list_box_child_changed (EggListBox *list_box, GtkWidget *widget)
705
EggListBoxPrivate *priv = list_box->priv;
706
EggListBoxChildInfo *info;
707
GSequenceIter *prev_next, *next;
709
g_return_if_fail (list_box != NULL);
710
g_return_if_fail (widget != NULL);
712
info = egg_list_box_lookup_info (list_box, widget);
716
prev_next = egg_list_box_get_next_visible (list_box, info->iter);
717
if (priv->sort_func != NULL)
719
g_sequence_sort_changed (info->iter,
720
(GCompareDataFunc)do_sort,
722
gtk_widget_queue_resize (GTK_WIDGET (list_box));
724
egg_list_box_apply_filter (list_box, info->widget);
725
if (gtk_widget_get_visible (GTK_WIDGET (list_box)))
727
next = egg_list_box_get_next_visible (list_box, info->iter);
728
egg_list_box_update_separator (list_box, info->iter);
729
egg_list_box_update_separator (list_box, next);
730
egg_list_box_update_separator (list_box, prev_next);
735
egg_list_box_set_activate_on_single_click (EggListBox *list_box,
738
EggListBoxPrivate *priv = list_box->priv;
740
g_return_if_fail (list_box != NULL);
742
single = single != FALSE;
744
if (priv->activate_single_click == single)
747
priv->activate_single_click = single;
749
g_object_notify_by_pspec (G_OBJECT (list_box), properties[PROP_ACTIVATE_ON_SINGLE_CLICK]);
753
egg_list_box_add_move_binding (GtkBindingSet *binding_set,
755
GdkModifierType modmask,
756
GtkMovementStep step,
759
gtk_binding_entry_add_signal (binding_set, keyval, modmask,
760
"move-cursor", (guint) 2, GTK_TYPE_MOVEMENT_STEP, step, G_TYPE_INT, count, NULL);
762
if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
765
gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK,
766
"move-cursor", (guint) 2, GTK_TYPE_MOVEMENT_STEP, step, G_TYPE_INT, count, NULL);
769
static EggListBoxChildInfo*
770
egg_list_box_find_child_at_y (EggListBox *list_box, gint y)
772
EggListBoxPrivate *priv = list_box->priv;
773
EggListBoxChildInfo *child_info;
775
EggListBoxChildInfo *info;
778
for (iter = g_sequence_get_begin_iter (priv->children);
779
!g_sequence_iter_is_end (iter);
780
iter = g_sequence_iter_next (iter))
782
info = (EggListBoxChildInfo*) g_sequence_get (iter);
783
if (y >= info->y && y < (info->y + info->height))
794
egg_list_box_update_cursor (EggListBox *list_box,
795
EggListBoxChildInfo *child)
797
EggListBoxPrivate *priv = list_box->priv;
799
priv->cursor_child = child;
800
gtk_widget_grab_focus (GTK_WIDGET (list_box));
801
gtk_widget_queue_draw (GTK_WIDGET (list_box));
802
if (child != NULL && priv->adjustment != NULL)
804
GtkAllocation allocation;
805
gtk_widget_get_allocation (GTK_WIDGET (list_box), &allocation);
806
gtk_adjustment_clamp_page (priv->adjustment,
807
priv->cursor_child->y + allocation.y,
808
priv->cursor_child->y + allocation.y + priv->cursor_child->height);
810
_egg_list_box_accessible_update_cursor (list_box, child ? child->widget : NULL);
814
egg_list_box_update_selected (EggListBox *list_box,
815
EggListBoxChildInfo *child)
817
EggListBoxPrivate *priv = list_box->priv;
819
if (child != priv->selected_child &&
820
(child == NULL || priv->selection_mode != GTK_SELECTION_NONE))
822
priv->selected_child = child;
823
g_signal_emit (list_box, signals[CHILD_SELECTED], 0,
824
(priv->selected_child != NULL) ? priv->selected_child->widget : NULL);
825
gtk_widget_queue_draw (GTK_WIDGET (list_box));
827
_egg_list_box_accessible_update_selected (list_box, child ? child->widget : NULL);
829
egg_list_box_update_cursor (list_box, child);
833
egg_list_box_select_and_activate (EggListBox *list_box, EggListBoxChildInfo *child)
840
egg_list_box_update_selected (list_box, child);
843
g_signal_emit (list_box, signals[CHILD_ACTIVATED], 0, w);
847
egg_list_box_update_prelight (EggListBox *list_box, EggListBoxChildInfo *child)
849
EggListBoxPrivate *priv = list_box->priv;
851
if (child != priv->prelight_child)
853
priv->prelight_child = child;
854
gtk_widget_queue_draw (GTK_WIDGET (list_box));
859
egg_list_box_update_active (EggListBox *list_box, EggListBoxChildInfo *child)
861
EggListBoxPrivate *priv = list_box->priv;
864
val = priv->active_child == child;
865
if (priv->active_child != NULL &&
866
val != priv->active_child_active)
868
priv->active_child_active = val;
869
gtk_widget_queue_draw (GTK_WIDGET (list_box));
874
egg_list_box_real_enter_notify_event (GtkWidget *widget,
875
GdkEventCrossing *event)
877
EggListBox *list_box = EGG_LIST_BOX (widget);
878
EggListBoxChildInfo *child;
881
if (event->window != gtk_widget_get_window (GTK_WIDGET (list_box)))
884
child = egg_list_box_find_child_at_y (list_box, event->y);
885
egg_list_box_update_prelight (list_box, child);
886
egg_list_box_update_active (list_box, child);
892
egg_list_box_real_leave_notify_event (GtkWidget *widget,
893
GdkEventCrossing *event)
895
EggListBox *list_box = EGG_LIST_BOX (widget);
896
EggListBoxChildInfo *child = NULL;
898
if (event->window != gtk_widget_get_window (GTK_WIDGET (list_box)))
901
if (event->detail != GDK_NOTIFY_INFERIOR)
904
child = egg_list_box_find_child_at_y (list_box, event->y);
906
egg_list_box_update_prelight (list_box, child);
907
egg_list_box_update_active (list_box, child);
913
egg_list_box_real_motion_notify_event (GtkWidget *widget,
914
GdkEventMotion *event)
916
EggListBox *list_box = EGG_LIST_BOX (widget);
917
EggListBoxChildInfo *child;
918
GdkWindow *window, *event_window;
922
window = gtk_widget_get_window (GTK_WIDGET (list_box));
923
event_window = event->window;
924
relative_y = event->y;
926
while ((event_window != NULL) && (event_window != window))
928
gdk_window_coords_to_parent (event_window, 0, relative_y, NULL, &parent_y);
929
relative_y = parent_y;
930
event_window = gdk_window_get_effective_parent (event_window);
933
child = egg_list_box_find_child_at_y (list_box, relative_y);
934
egg_list_box_update_prelight (list_box, child);
935
egg_list_box_update_active (list_box, child);
941
egg_list_box_real_button_press_event (GtkWidget *widget,
942
GdkEventButton *event)
944
EggListBox *list_box = EGG_LIST_BOX (widget);
945
EggListBoxPrivate *priv = list_box->priv;
947
if (event->button == 1)
949
EggListBoxChildInfo *child;
950
child = egg_list_box_find_child_at_y (list_box, event->y);
953
priv->active_child = child;
954
priv->active_child_active = TRUE;
955
gtk_widget_queue_draw (GTK_WIDGET (list_box));
956
if (event->type == GDK_2BUTTON_PRESS &&
957
!priv->activate_single_click)
958
g_signal_emit (list_box, signals[CHILD_ACTIVATED], 0,
963
Should mark as active while down,
964
and handle grab breaks */
971
egg_list_box_real_button_release_event (GtkWidget *widget,
972
GdkEventButton *event)
974
EggListBox *list_box = EGG_LIST_BOX (widget);
975
EggListBoxPrivate *priv = list_box->priv;
977
if (event->button == 1)
979
if (priv->active_child != NULL &&
980
priv->active_child_active)
982
if (priv->activate_single_click)
983
egg_list_box_select_and_activate (list_box, priv->active_child);
985
egg_list_box_update_selected (list_box, priv->active_child);
987
priv->active_child = NULL;
988
priv->active_child_active = FALSE;
989
gtk_widget_queue_draw (GTK_WIDGET (list_box));
996
egg_list_box_real_show (GtkWidget *widget)
998
EggListBox * list_box = EGG_LIST_BOX (widget);
1000
egg_list_box_reseparate (list_box);
1002
GTK_WIDGET_CLASS (egg_list_box_parent_class)->show ((GtkWidget*) G_TYPE_CHECK_INSTANCE_CAST (list_box, GTK_TYPE_CONTAINER, GtkContainer));
1007
egg_list_box_real_focus (GtkWidget* widget, GtkDirectionType direction)
1009
EggListBox *list_box = EGG_LIST_BOX (widget);
1010
EggListBoxPrivate *priv = list_box->priv;
1011
gboolean had_focus = FALSE;
1012
gboolean focus_into = FALSE;
1013
GtkWidget* recurse_into;
1014
EggListBoxChildInfo *current_focus_child;
1015
EggListBoxChildInfo *next_focus_child;
1016
gboolean modify_selection_pressed;
1017
GdkModifierType state = 0;
1019
recurse_into = NULL;
1022
g_object_get (GTK_WIDGET (list_box), "has-focus", &had_focus, NULL);
1023
current_focus_child = NULL;
1024
next_focus_child = NULL;
1027
/* If on row, going right, enter into possible container */
1028
if (direction == GTK_DIR_RIGHT || direction == GTK_DIR_TAB_FORWARD)
1030
if (priv->cursor_child != NULL)
1031
recurse_into = priv->cursor_child->widget;
1033
current_focus_child = priv->cursor_child;
1034
/* Unless we're going up/down we're always leaving
1036
if (direction != GTK_DIR_UP && direction != GTK_DIR_DOWN)
1039
else if (gtk_container_get_focus_child ((GtkContainer*) list_box) != NULL)
1041
/* There is a focus child, always navigat inside it first */
1042
recurse_into = gtk_container_get_focus_child ((GtkContainer*) list_box);
1043
current_focus_child = egg_list_box_lookup_info (list_box, recurse_into);
1045
/* If exiting child container to the right, exit row */
1046
if (direction == GTK_DIR_RIGHT || direction == GTK_DIR_TAB_FORWARD)
1049
/* If exiting child container to the left, select row or out */
1050
if (direction == GTK_DIR_LEFT || direction == GTK_DIR_TAB_BACKWARD)
1051
next_focus_child = current_focus_child;
1055
/* If coming from the left, enter into possible container */
1056
if (direction == GTK_DIR_LEFT || direction == GTK_DIR_TAB_BACKWARD)
1058
if (priv->selected_child != NULL)
1059
recurse_into = priv->selected_child->widget;
1063
if (recurse_into != NULL)
1065
if (gtk_widget_child_focus (recurse_into, direction))
1070
return FALSE; /* Focus is leaving us */
1072
/* TODO: This doesn't handle up/down going into a focusable separator */
1074
if (next_focus_child == NULL)
1076
if (current_focus_child != NULL)
1079
if (direction == GTK_DIR_UP)
1081
i = egg_list_box_get_previous_visible (list_box, current_focus_child->iter);
1083
next_focus_child = g_sequence_get (i);
1088
i = egg_list_box_get_next_visible (list_box, current_focus_child->iter);
1089
if (!g_sequence_iter_is_end (i))
1090
next_focus_child = g_sequence_get (i);
1099
case GTK_DIR_TAB_BACKWARD:
1100
next_focus_child = priv->selected_child;
1101
if (next_focus_child == NULL)
1102
next_focus_child = egg_list_box_get_last_visible (list_box);
1105
next_focus_child = priv->selected_child;
1106
if (next_focus_child == NULL)
1108
egg_list_box_get_first_visible (list_box);
1114
if (next_focus_child == NULL)
1116
if (direction == GTK_DIR_UP || direction == GTK_DIR_DOWN)
1118
if (gtk_widget_keynav_failed (GTK_WIDGET (list_box), direction))
1125
modify_selection_pressed = FALSE;
1126
if (gtk_get_current_event_state (&state))
1128
GdkModifierType modify_mod_mask;
1130
gtk_widget_get_modifier_mask (GTK_WIDGET (list_box),
1131
GDK_MODIFIER_INTENT_MODIFY_SELECTION);
1132
if ((state & modify_mod_mask) == modify_mod_mask)
1133
modify_selection_pressed = TRUE;
1136
if (modify_selection_pressed)
1137
egg_list_box_update_cursor (list_box, next_focus_child);
1139
egg_list_box_update_selected (list_box, next_focus_child);
1145
EggListBoxChildInfo *child;
1146
GtkStateFlags state;
1150
child_flags_find_or_add (ChildFlags *array,
1152
EggListBoxChildInfo *to_find)
1156
for (i = 0; i < *array_length; i++)
1158
if (array[i].child == to_find)
1162
*array_length = *array_length + 1;
1163
array[*array_length - 1].child = to_find;
1164
array[*array_length - 1].state = 0;
1165
return &array[*array_length - 1];
1169
egg_list_box_real_draw (GtkWidget* widget, cairo_t* cr)
1171
EggListBox * list_box = EGG_LIST_BOX (widget);
1172
EggListBoxPrivate *priv = list_box->priv;
1173
GtkAllocation allocation = {0};
1174
GtkStyleContext* context;
1175
GtkStateFlags state;
1176
ChildFlags flags[3], *found;
1181
gtk_widget_get_allocation (GTK_WIDGET (list_box), &allocation);
1182
context = gtk_widget_get_style_context (GTK_WIDGET (list_box));
1183
state = gtk_widget_get_state_flags (widget);
1184
gtk_render_background (context, cr, (gdouble) 0, (gdouble) 0, (gdouble) allocation.width, (gdouble) allocation.height);
1187
if (priv->selected_child != NULL)
1189
found = child_flags_find_or_add (flags, &flags_length, priv->selected_child);
1190
found->state |= (state | GTK_STATE_FLAG_SELECTED);
1193
if (priv->prelight_child != NULL)
1195
found = child_flags_find_or_add (flags, &flags_length, priv->prelight_child);
1196
found->state |= (state | GTK_STATE_FLAG_PRELIGHT);
1199
if (priv->active_child != NULL && priv->active_child_active)
1201
found = child_flags_find_or_add (flags, &flags_length, priv->active_child);
1202
found->state |= (state | GTK_STATE_FLAG_ACTIVE);
1205
for (i = 0; i < flags_length; i++)
1207
ChildFlags *flag = &flags[i];
1208
gtk_style_context_save (context);
1209
gtk_style_context_set_state (context, flag->state);
1210
gtk_render_background (context, cr, 0, flag->child->y, allocation.width, flag->child->height);
1211
gtk_style_context_restore (context);
1214
if (gtk_widget_has_visible_focus (GTK_WIDGET (list_box)) && priv->cursor_child != NULL)
1216
gtk_style_context_get_style (context,
1217
"focus-padding", &focus_pad,
1219
gtk_render_focus (context, cr, focus_pad, priv->cursor_child->y + focus_pad,
1220
allocation.width - 2 * focus_pad, priv->cursor_child->height - 2 * focus_pad);
1223
GTK_WIDGET_CLASS (egg_list_box_parent_class)->draw ((GtkWidget*) G_TYPE_CHECK_INSTANCE_CAST (list_box, GTK_TYPE_CONTAINER, GtkContainer), cr);
1230
egg_list_box_real_realize (GtkWidget* widget)
1232
EggListBox *list_box = EGG_LIST_BOX (widget);
1233
GtkAllocation allocation;
1234
GdkWindowAttr attributes = {0};
1237
gtk_widget_get_allocation (GTK_WIDGET (list_box), &allocation);
1238
gtk_widget_set_realized (GTK_WIDGET (list_box), TRUE);
1240
attributes.x = allocation.x;
1241
attributes.y = allocation.y;
1242
attributes.width = allocation.width;
1243
attributes.height = allocation.height;
1244
attributes.window_type = GDK_WINDOW_CHILD;
1245
attributes.event_mask = gtk_widget_get_events (GTK_WIDGET (list_box)) |
1246
GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK |
1247
GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK;
1248
attributes.wclass = GDK_INPUT_OUTPUT;
1250
window = gdk_window_new (gtk_widget_get_parent_window (GTK_WIDGET (list_box)),
1251
&attributes, GDK_WA_X | GDK_WA_Y);
1252
gtk_style_context_set_background (gtk_widget_get_style_context (GTK_WIDGET (list_box)), window);
1253
gdk_window_set_user_data (window, (GObject*) list_box);
1254
gtk_widget_set_window (GTK_WIDGET (list_box), window); /* Passes ownership */
1259
egg_list_box_apply_filter (EggListBox *list_box, GtkWidget *child)
1261
EggListBoxPrivate *priv = list_box->priv;
1265
if (priv->filter_func != NULL)
1266
do_show = priv->filter_func (child, priv->filter_func_target);
1268
gtk_widget_set_child_visible (child, do_show);
1272
egg_list_box_apply_filter_all (EggListBox *list_box)
1274
EggListBoxPrivate *priv = list_box->priv;
1275
EggListBoxChildInfo *child_info;
1276
GSequenceIter *iter;
1278
for (iter = g_sequence_get_begin_iter (priv->children);
1279
!g_sequence_iter_is_end (iter);
1280
iter = g_sequence_iter_next (iter))
1282
child_info = g_sequence_get (iter);
1283
egg_list_box_apply_filter (list_box, child_info->widget);
1287
/* Children are visible if they are shown by the app (visible)
1288
and not filtered out (child_visible) by the listbox */
1290
child_is_visible (GtkWidget *child)
1292
return gtk_widget_get_visible (child) && gtk_widget_get_child_visible (child);
1295
static EggListBoxChildInfo*
1296
egg_list_box_get_first_visible (EggListBox *list_box)
1298
EggListBoxPrivate *priv = list_box->priv;
1299
EggListBoxChildInfo *child_info;
1300
GSequenceIter *iter;
1302
for (iter = g_sequence_get_begin_iter (priv->children);
1303
!g_sequence_iter_is_end (iter);
1304
iter = g_sequence_iter_next (iter))
1306
child_info = g_sequence_get (iter);
1307
if (child_is_visible (child_info->widget))
1315
static EggListBoxChildInfo*
1316
egg_list_box_get_last_visible (EggListBox *list_box)
1318
EggListBoxPrivate *priv = list_box->priv;
1319
EggListBoxChildInfo *child_info;
1320
GSequenceIter *iter;
1322
iter = g_sequence_get_end_iter (priv->children);
1323
while (!g_sequence_iter_is_begin (iter))
1325
iter = g_sequence_iter_prev (iter);
1326
child_info = g_sequence_get (iter);
1327
if (child_is_visible (child_info->widget))
1334
static GSequenceIter*
1335
egg_list_box_get_previous_visible (EggListBox *list_box,
1336
GSequenceIter* iter)
1338
EggListBoxChildInfo *child_info;
1340
if (g_sequence_iter_is_begin (iter))
1345
iter = g_sequence_iter_prev (iter);
1346
child_info = g_sequence_get (iter);
1347
if (child_is_visible (child_info->widget))
1350
while (!g_sequence_iter_is_begin (iter));
1355
static GSequenceIter*
1356
egg_list_box_get_next_visible (EggListBox *list_box, GSequenceIter* iter)
1358
EggListBoxChildInfo *child_info;
1360
if (g_sequence_iter_is_end (iter))
1365
iter = g_sequence_iter_next (iter);
1366
if (!g_sequence_iter_is_end (iter))
1368
child_info = g_sequence_get (iter);
1369
if (child_is_visible (child_info->widget))
1373
while (!g_sequence_iter_is_end (iter));
1380
egg_list_box_update_separator (EggListBox *list_box, GSequenceIter* iter)
1382
EggListBoxPrivate *priv = list_box->priv;
1383
EggListBoxChildInfo *info;
1384
GSequenceIter *before_iter;
1386
GtkWidget *before_child;
1387
EggListBoxChildInfo *before_info;
1388
GtkWidget *old_separator;
1390
if (iter == NULL || g_sequence_iter_is_end (iter))
1393
info = g_sequence_get (iter);
1394
before_iter = egg_list_box_get_previous_visible (list_box, iter);
1395
child = info->widget;
1397
g_object_ref (child);
1398
before_child = NULL;
1399
if (before_iter != NULL)
1401
before_info = g_sequence_get (before_iter);
1402
before_child = before_info->widget;
1404
g_object_ref (before_child);
1407
if (priv->update_separator_func != NULL &&
1408
child_is_visible (child))
1410
old_separator = info->separator;
1412
g_object_ref (old_separator);
1413
priv->update_separator_func (&info->separator,
1416
priv->update_separator_func_target);
1417
if (old_separator != info->separator)
1419
if (old_separator != NULL)
1421
gtk_widget_unparent (old_separator);
1422
g_hash_table_remove (priv->separator_hash, old_separator);
1424
if (info->separator != NULL)
1426
g_hash_table_insert (priv->separator_hash, info->separator, info);
1427
gtk_widget_set_parent (info->separator, GTK_WIDGET (list_box));
1428
gtk_widget_show (info->separator);
1430
gtk_widget_queue_resize (GTK_WIDGET (list_box));
1433
g_object_unref (old_separator);
1437
if (info->separator != NULL)
1439
g_hash_table_remove (priv->separator_hash, info->separator);
1440
gtk_widget_unparent (info->separator);
1441
g_clear_object (&info->separator);
1442
gtk_widget_queue_resize (GTK_WIDGET (list_box));
1446
g_object_unref (before_child);
1448
g_object_unref (child);
1451
static EggListBoxChildInfo*
1452
egg_list_box_lookup_info (EggListBox *list_box, GtkWidget* child)
1454
EggListBoxPrivate *priv = list_box->priv;
1456
return g_hash_table_lookup (priv->child_hash, child);
1460
child_visibility_changed (GObject* object, GParamSpec* pspec, EggListBox *list_box)
1462
EggListBoxChildInfo *info;
1464
if (gtk_widget_get_visible (GTK_WIDGET (list_box)))
1466
info = egg_list_box_lookup_info (list_box, GTK_WIDGET (object));
1469
egg_list_box_update_separator (list_box, info->iter);
1470
egg_list_box_update_separator (list_box,
1471
egg_list_box_get_next_visible (list_box, info->iter));
1477
egg_list_box_real_add (GtkContainer* container, GtkWidget* child)
1479
EggListBox *list_box = EGG_LIST_BOX (container);
1480
EggListBoxPrivate *priv = list_box->priv;
1481
EggListBoxChildInfo *info;
1482
GSequenceIter* iter = NULL;
1483
info = egg_list_box_child_info_new (child);
1484
g_hash_table_insert (priv->child_hash, child, info);
1485
if (priv->sort_func != NULL)
1486
iter = g_sequence_insert_sorted (priv->children, info,
1487
(GCompareDataFunc)do_sort, list_box);
1489
iter = g_sequence_append (priv->children, info);
1492
gtk_widget_set_parent (child, GTK_WIDGET (list_box));
1493
egg_list_box_apply_filter (list_box, child);
1494
if (gtk_widget_get_visible (GTK_WIDGET (list_box)))
1496
egg_list_box_update_separator (list_box, iter);
1497
egg_list_box_update_separator (list_box, egg_list_box_get_next_visible (list_box, iter));
1499
g_signal_connect_object (child, "notify::visible",
1500
(GCallback) child_visibility_changed, list_box, 0);
1504
egg_list_box_real_remove (GtkContainer* container, GtkWidget* child)
1506
EggListBox *list_box = EGG_LIST_BOX (container);
1507
EggListBoxPrivate *priv = list_box->priv;
1508
gboolean was_visible;
1509
EggListBoxChildInfo *info;
1510
GSequenceIter *next;
1512
g_return_if_fail (child != NULL);
1513
was_visible = gtk_widget_get_visible (child);
1515
g_signal_handlers_disconnect_by_func (child, (GCallback) child_visibility_changed, list_box);
1517
info = egg_list_box_lookup_info (list_box, child);
1520
info = g_hash_table_lookup (priv->separator_hash, child);
1523
g_hash_table_remove (priv->separator_hash, child);
1524
g_clear_object (&info->separator);
1525
gtk_widget_unparent (child);
1526
if (was_visible && gtk_widget_get_visible (GTK_WIDGET (list_box)))
1527
gtk_widget_queue_resize (GTK_WIDGET (list_box));
1531
g_warning ("egg-list-box.vala:846: Tried to remove non-child %p\n", child);
1536
if (info->separator != NULL)
1538
g_hash_table_remove (priv->separator_hash, info->separator);
1539
gtk_widget_unparent (info->separator);
1540
g_clear_object (&info->separator);
1543
if (info == priv->selected_child)
1544
egg_list_box_update_selected (list_box, NULL);
1545
if (info == priv->prelight_child)
1546
priv->prelight_child = NULL;
1547
if (info == priv->cursor_child)
1548
priv->cursor_child = NULL;
1549
if (info == priv->active_child)
1550
priv->active_child = NULL;
1552
next = egg_list_box_get_next_visible (list_box, info->iter);
1553
gtk_widget_unparent (child);
1554
g_hash_table_remove (priv->child_hash, child);
1555
g_sequence_remove (info->iter);
1556
if (gtk_widget_get_visible (GTK_WIDGET (list_box)))
1557
egg_list_box_update_separator (list_box, next);
1559
if (was_visible && gtk_widget_get_visible (GTK_WIDGET (list_box)))
1560
gtk_widget_queue_resize (GTK_WIDGET (list_box));
1565
egg_list_box_real_forall_internal (GtkContainer* container,
1566
gboolean include_internals,
1567
GtkCallback callback,
1568
void* callback_target)
1570
EggListBox *list_box = EGG_LIST_BOX (container);
1571
EggListBoxPrivate *priv = list_box->priv;
1572
GSequenceIter *iter;
1573
EggListBoxChildInfo *child_info;
1575
iter = g_sequence_get_begin_iter (priv->children);
1576
while (!g_sequence_iter_is_end (iter))
1578
child_info = g_sequence_get (iter);
1579
iter = g_sequence_iter_next (iter);
1580
if (child_info->separator != NULL && include_internals)
1581
callback (child_info->separator, callback_target);
1582
callback (child_info->widget, callback_target);
1587
egg_list_box_real_compute_expand_internal (GtkWidget* widget,
1591
GTK_WIDGET_CLASS (egg_list_box_parent_class)->compute_expand (widget,
1594
/* We don't expand vertically beyound the minimum size */
1600
egg_list_box_real_child_type (GtkContainer* container)
1602
return GTK_TYPE_WIDGET;
1605
static GtkSizeRequestMode
1606
egg_list_box_real_get_request_mode (GtkWidget* widget)
1608
return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
1612
egg_list_box_real_get_preferred_height (GtkWidget* widget,
1613
gint* minimum_height,
1614
gint* natural_height)
1617
egg_list_box_real_get_preferred_width (widget, NULL, &natural_width);
1618
egg_list_box_real_get_preferred_height_for_width (widget, natural_width,
1619
minimum_height, natural_height);
1623
egg_list_box_real_get_preferred_height_for_width (GtkWidget* widget, gint width,
1624
gint* minimum_height_out, gint* natural_height_out)
1626
EggListBox *list_box = EGG_LIST_BOX (widget);
1627
EggListBoxPrivate *priv = list_box->priv;
1628
GSequenceIter *iter;
1629
gint minimum_height;
1630
gint natural_height;
1631
GtkStyleContext *context;
1637
context = gtk_widget_get_style_context (GTK_WIDGET (list_box));
1638
gtk_style_context_get_style (context,
1639
"focus-line-width", &focus_width,
1640
"focus-padding", &focus_pad, NULL);
1642
for (iter = g_sequence_get_begin_iter (priv->children);
1643
!g_sequence_iter_is_end (iter);
1644
iter = g_sequence_iter_next (iter))
1646
EggListBoxChildInfo *child_info;
1649
child_info = g_sequence_get (iter);
1650
child = child_info->widget;
1652
if (!child_is_visible (child))
1655
if (child_info->separator != NULL)
1657
gtk_widget_get_preferred_height_for_width (child_info->separator, width, &child_min, NULL);
1658
minimum_height += child_min;
1660
gtk_widget_get_preferred_height_for_width (child, width - 2 * (focus_width + focus_pad),
1662
minimum_height += child_min + 2 * (focus_width + focus_pad);
1665
/* We always allocate the minimum height, since handling
1666
expanding rows is way too costly, and unlikely to
1667
be used, as lists are generally put inside a scrolling window
1670
natural_height = minimum_height;
1671
if (minimum_height_out)
1672
*minimum_height_out = minimum_height;
1673
if (natural_height_out)
1674
*natural_height_out = natural_height;
1678
egg_list_box_real_get_preferred_width (GtkWidget* widget, gint* minimum_width_out, gint* natural_width_out)
1680
EggListBox *list_box = EGG_LIST_BOX (widget);
1681
EggListBoxPrivate *priv = list_box->priv;
1684
GtkStyleContext *context;
1687
GSequenceIter *iter;
1688
EggListBoxChildInfo *child_info;
1693
context = gtk_widget_get_style_context (GTK_WIDGET (list_box));
1694
gtk_style_context_get_style (context, "focus-line-width", &focus_width, "focus-padding", &focus_pad, NULL);
1699
for (iter = g_sequence_get_begin_iter (priv->children);
1700
!g_sequence_iter_is_end (iter);
1701
iter = g_sequence_iter_next (iter))
1703
child_info = g_sequence_get (iter);
1704
child = child_info->widget;
1705
if (!child_is_visible (child))
1708
gtk_widget_get_preferred_width (child, &child_min, &child_nat);
1709
minimum_width = MAX (minimum_width, child_min + 2 * (focus_width + focus_pad));
1710
natural_width = MAX (natural_width, child_nat + 2 * (focus_width + focus_pad));
1712
if (child_info->separator != NULL)
1714
gtk_widget_get_preferred_width (child_info->separator, &child_min, &child_nat);
1715
minimum_width = MAX (minimum_width, child_min);
1716
natural_width = MAX (natural_width, child_nat);
1720
if (minimum_width_out)
1721
*minimum_width_out = minimum_width;
1722
if (natural_width_out)
1723
*natural_width_out = natural_width;
1727
egg_list_box_real_get_preferred_width_for_height (GtkWidget *widget, gint height,
1728
gint *minimum_width, gint *natural_width)
1730
EggListBox *list_box = EGG_LIST_BOX (widget);
1731
egg_list_box_real_get_preferred_width (GTK_WIDGET (list_box), minimum_width, natural_width);
1735
egg_list_box_real_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1737
EggListBox *list_box = EGG_LIST_BOX (widget);
1738
EggListBoxPrivate *priv = list_box->priv;
1739
GtkAllocation child_allocation;
1740
GtkAllocation separator_allocation;
1741
EggListBoxChildInfo *child_info;
1744
GSequenceIter *iter;
1745
GtkStyleContext *context;
1751
child_allocation.x = 0;
1752
child_allocation.y = 0;
1753
child_allocation.width = 0;
1754
child_allocation.height = 0;
1756
separator_allocation.x = 0;
1757
separator_allocation.y = 0;
1758
separator_allocation.width = 0;
1759
separator_allocation.height = 0;
1761
gtk_widget_set_allocation (GTK_WIDGET (list_box), allocation);
1762
window = gtk_widget_get_window (GTK_WIDGET (list_box));
1764
gdk_window_move_resize (window,
1765
allocation->x, allocation->y,
1766
allocation->width, allocation->height);
1768
context = gtk_widget_get_style_context (GTK_WIDGET (list_box));
1769
gtk_style_context_get_style (context,
1770
"focus-line-width", &focus_width,
1771
"focus-padding", &focus_pad,
1773
child_allocation.x = 0 + focus_width + focus_pad;
1774
child_allocation.y = 0;
1775
child_allocation.width = allocation->width - 2 * (focus_width + focus_pad);
1776
separator_allocation.x = 0;
1777
separator_allocation.width = allocation->width;
1779
for (iter = g_sequence_get_begin_iter (priv->children);
1780
!g_sequence_iter_is_end (iter);
1781
iter = g_sequence_iter_next (iter))
1783
child_info = g_sequence_get (iter);
1784
child = child_info->widget;
1785
if (!child_is_visible (child))
1787
child_info->y = child_allocation.y;
1788
child_info->height = 0;
1792
if (child_info->separator != NULL)
1794
gtk_widget_get_preferred_height_for_width (child_info->separator,
1795
allocation->width, &child_min, NULL);
1796
separator_allocation.height = child_min;
1797
separator_allocation.y = child_allocation.y;
1798
gtk_widget_size_allocate (child_info->separator,
1799
&separator_allocation);
1800
child_allocation.y += child_min;
1803
child_info->y = child_allocation.y;
1804
child_allocation.y += focus_width + focus_pad;
1806
gtk_widget_get_preferred_height_for_width (child, child_allocation.width, &child_min, NULL);
1807
child_allocation.height = child_min;
1809
child_info->height = child_allocation.height + 2 * (focus_width + focus_pad);
1810
gtk_widget_size_allocate (child, &child_allocation);
1812
child_allocation.y += child_min + focus_width + focus_pad;
1817
egg_list_box_drag_unhighlight_widget (EggListBox *list_box)
1819
EggListBoxPrivate *priv = list_box->priv;
1821
g_return_if_fail (list_box != NULL);
1823
if (priv->drag_highlighted_widget == NULL)
1826
gtk_drag_unhighlight (priv->drag_highlighted_widget);
1827
g_clear_object (&priv->drag_highlighted_widget);
1832
egg_list_box_drag_highlight_widget (EggListBox *list_box, GtkWidget *child)
1834
EggListBoxPrivate *priv = list_box->priv;
1835
GtkWidget *old_highlight;
1837
g_return_if_fail (list_box != NULL);
1838
g_return_if_fail (child != NULL);
1840
if (priv->drag_highlighted_widget == child)
1843
egg_list_box_drag_unhighlight_widget (list_box);
1844
gtk_drag_highlight (child);
1846
old_highlight = priv->drag_highlighted_widget;
1847
priv->drag_highlighted_widget = g_object_ref (child);
1849
g_object_unref (old_highlight);
1853
egg_list_box_real_drag_leave (GtkWidget *widget, GdkDragContext *context, guint time_)
1855
EggListBox *list_box = EGG_LIST_BOX (widget);
1856
EggListBoxPrivate *priv = list_box->priv;
1858
egg_list_box_drag_unhighlight_widget (list_box);
1859
if (priv->auto_scroll_timeout_id != 0) {
1860
g_source_remove (priv->auto_scroll_timeout_id);
1861
priv->auto_scroll_timeout_id = 0;
1867
EggListBox *list_box;
1872
move_data_free (MoveData *data)
1874
g_slice_free (MoveData, data);
1878
drag_motion_timeout (MoveData *data)
1880
EggListBox *list_box = data->list_box;
1881
EggListBoxPrivate *priv = list_box->priv;
1883
gtk_adjustment_set_value (priv->adjustment,
1884
gtk_adjustment_get_value (priv->adjustment) +
1885
gtk_adjustment_get_step_increment (priv->adjustment) * data->move);
1890
egg_list_box_real_drag_motion (GtkWidget *widget, GdkDragContext *context,
1891
gint x, gint y, guint time_)
1893
EggListBox *list_box = EGG_LIST_BOX (widget);
1894
EggListBoxPrivate *priv = list_box->priv;
1899
/* Auto-scroll during Dnd if cursor is moving into the top/bottom portion of the
1901
if (priv->auto_scroll_timeout_id != 0)
1903
g_source_remove (priv->auto_scroll_timeout_id);
1904
priv->auto_scroll_timeout_id = 0;
1907
if (priv->adjustment == NULL)
1910
/* Part of the view triggering auto-scroll */
1914
if (y < (gtk_adjustment_get_value (priv->adjustment) + size))
1919
else if (y > ((gtk_adjustment_get_value (priv->adjustment) + gtk_adjustment_get_page_size (priv->adjustment)) - size))
1928
data = g_slice_new0 (MoveData);
1929
data->list_box = list_box;
1931
priv->auto_scroll_timeout_id =
1932
g_timeout_add_full (G_PRIORITY_DEFAULT, 150, (GSourceFunc)drag_motion_timeout,
1933
data, (GDestroyNotify) move_data_free);
1939
egg_list_box_real_activate_cursor_child (EggListBox *list_box)
1941
EggListBoxPrivate *priv = list_box->priv;
1943
egg_list_box_select_and_activate (list_box, priv->cursor_child);
1947
egg_list_box_real_toggle_cursor_child (EggListBox *list_box)
1949
EggListBoxPrivate *priv = list_box->priv;
1951
if (priv->cursor_child == NULL)
1954
if (priv->selection_mode == GTK_SELECTION_SINGLE &&
1955
priv->selected_child == priv->cursor_child)
1956
egg_list_box_update_selected (list_box, NULL);
1958
egg_list_box_select_and_activate (list_box, priv->cursor_child);
1962
egg_list_box_real_move_cursor (EggListBox *list_box,
1963
GtkMovementStep step,
1966
EggListBoxPrivate *priv = list_box->priv;
1967
GdkModifierType state;
1968
gboolean modify_selection_pressed;
1969
EggListBoxChildInfo *child;
1970
GdkModifierType modify_mod_mask;
1971
EggListBoxChildInfo *prev;
1972
EggListBoxChildInfo *next;
1974
GSequenceIter *iter;
1978
modify_selection_pressed = FALSE;
1980
if (gtk_get_current_event_state (&state))
1982
modify_mod_mask = gtk_widget_get_modifier_mask (GTK_WIDGET (list_box),
1983
GDK_MODIFIER_INTENT_MODIFY_SELECTION);
1984
if ((state & modify_mod_mask) == modify_mod_mask)
1985
modify_selection_pressed = TRUE;
1991
case GTK_MOVEMENT_BUFFER_ENDS:
1993
child = egg_list_box_get_first_visible (list_box);
1995
child = egg_list_box_get_last_visible (list_box);
1997
case GTK_MOVEMENT_DISPLAY_LINES:
1998
if (priv->cursor_child != NULL)
2000
iter = priv->cursor_child->iter;
2002
while (count < 0 && iter != NULL)
2004
iter = egg_list_box_get_previous_visible (list_box, iter);
2007
while (count > 0 && iter != NULL)
2009
iter = egg_list_box_get_next_visible (list_box, iter);
2013
if (iter != NULL && !g_sequence_iter_is_end (iter))
2014
child = g_sequence_get (iter);
2017
case GTK_MOVEMENT_PAGES:
2019
if (priv->adjustment != NULL)
2020
page_size = gtk_adjustment_get_page_increment (priv->adjustment);
2022
if (priv->cursor_child != NULL)
2024
start_y = priv->cursor_child->y;
2026
iter = priv->cursor_child->iter;
2028
child = priv->cursor_child;
2032
while (iter != NULL && !g_sequence_iter_is_begin (iter))
2034
iter = egg_list_box_get_previous_visible (list_box, iter);
2038
prev = g_sequence_get (iter);
2039
if (prev->y < start_y - page_size)
2048
while (iter != NULL && !g_sequence_iter_is_end (iter))
2050
iter = egg_list_box_get_next_visible (list_box, iter);
2051
if (g_sequence_iter_is_end (iter))
2054
next = g_sequence_get (iter);
2055
if (next->y > start_y + page_size)
2062
if (end_y != start_y && priv->adjustment != NULL)
2063
gtk_adjustment_set_value (priv->adjustment,
2064
gtk_adjustment_get_value (priv->adjustment) +
2072
if (child == NULL || child == priv->cursor_child)
2074
GtkDirectionType direction = count < 0 ? GTK_DIR_UP : GTK_DIR_DOWN;
2076
if (!gtk_widget_keynav_failed (GTK_WIDGET (list_box), direction))
2078
GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (list_box));
2081
gtk_widget_child_focus (toplevel,
2082
direction == GTK_DIR_UP ?
2083
GTK_DIR_TAB_BACKWARD :
2084
GTK_DIR_TAB_FORWARD);
2091
egg_list_box_update_cursor (list_box, child);
2092
if (!modify_selection_pressed)
2093
egg_list_box_update_selected (list_box, child);