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"
30
/* This already exists in gtk as _gtk_marshal_VOID__ENUM_INT, inline it here for now
31
to avoid separate marshallers file */
33
_egg_marshal_VOID__ENUM_INT (GClosure * closure,
34
GValue * return_value,
36
const GValue * param_values,
37
gpointer invocation_hint,
38
gpointer marshal_data)
40
typedef void (*GMarshalFunc_VOID__ENUM_INT) (gpointer data1, gint arg_1, gint arg_2, gpointer data2);
41
register GMarshalFunc_VOID__ENUM_INT callback;
42
register GCClosure * cc;
43
register gpointer data1;
44
register gpointer data2;
45
cc = (GCClosure *) closure;
46
g_return_if_fail (n_param_values == 3);
47
if (G_CCLOSURE_SWAP_DATA (closure)) {
48
data1 = closure->data;
49
data2 = param_values->data[0].v_pointer;
51
data1 = param_values->data[0].v_pointer;
52
data2 = closure->data;
54
callback = (GMarshalFunc_VOID__ENUM_INT) (marshal_data ? marshal_data : cc->callback);
55
callback (data1, g_value_get_enum (param_values + 1), g_value_get_int (param_values + 2), data2);
58
typedef struct _EggListBoxChildInfo EggListBoxChildInfo;
60
struct _EggListBoxPrivate
63
GHashTable *child_hash;
64
GHashTable *separator_hash;
66
GCompareDataFunc sort_func;
67
gpointer sort_func_target;
68
GDestroyNotify sort_func_target_destroy_notify;
70
EggListBoxFilterFunc filter_func;
71
gpointer filter_func_target;
72
GDestroyNotify filter_func_target_destroy_notify;
74
EggListBoxUpdateSeparatorFunc update_separator_func;
75
gpointer update_separator_func_target;
76
GDestroyNotify update_separator_func_target_destroy_notify;
78
EggListBoxChildInfo *selected_child;
79
EggListBoxChildInfo *prelight_child;
80
EggListBoxChildInfo *cursor_child;
82
gboolean active_child_active;
83
EggListBoxChildInfo *active_child;
85
GtkSelectionMode selection_mode;
87
GtkAdjustment *adjustment;
88
gboolean activate_single_click;
91
GtkWidget *drag_highlighted_widget;
92
guint auto_scroll_timeout_id;
95
struct _EggListBoxChildInfo
107
ACTIVATE_CURSOR_CHILD,
117
G_DEFINE_TYPE (EggListBox, egg_list_box, GTK_TYPE_CONTAINER)
119
static EggListBoxChildInfo *egg_list_box_find_child_at_y (EggListBox *list_box,
121
static EggListBoxChildInfo *egg_list_box_lookup_info (EggListBox *list_box,
123
static void egg_list_box_update_selected (EggListBox *list_box,
124
EggListBoxChildInfo *child);
125
static void egg_list_box_apply_filter_all (EggListBox *list_box);
126
static void egg_list_box_update_separator (EggListBox *list_box,
127
GSequenceIter *iter);
128
static GSequenceIter * egg_list_box_get_next_visible (EggListBox *list_box,
129
GSequenceIter *_iter);
130
static void egg_list_box_apply_filter (EggListBox *list_box,
132
static void egg_list_box_add_move_binding (GtkBindingSet *binding_set,
134
GdkModifierType modmask,
135
GtkMovementStep step,
137
static void egg_list_box_update_cursor (EggListBox *list_box,
138
EggListBoxChildInfo *child);
139
static void egg_list_box_select_and_activate (EggListBox *list_box,
140
EggListBoxChildInfo *child);
141
static void egg_list_box_update_prelight (EggListBox *list_box,
142
EggListBoxChildInfo *child);
143
static void egg_list_box_update_active (EggListBox *list_box,
144
EggListBoxChildInfo *child);
145
static gboolean egg_list_box_real_enter_notify_event (GtkWidget *widget,
146
GdkEventCrossing *event);
147
static gboolean egg_list_box_real_leave_notify_event (GtkWidget *widget,
148
GdkEventCrossing *event);
149
static gboolean egg_list_box_real_motion_notify_event (GtkWidget *widget,
150
GdkEventMotion *event);
151
static gboolean egg_list_box_real_button_press_event (GtkWidget *widget,
152
GdkEventButton *event);
153
static gboolean egg_list_box_real_button_release_event (GtkWidget *widget,
154
GdkEventButton *event);
155
static void egg_list_box_real_show (GtkWidget *widget);
156
static gboolean egg_list_box_real_focus (GtkWidget *widget,
157
GtkDirectionType direction);
158
static GSequenceIter* egg_list_box_get_previous_visible (EggListBox *list_box,
159
GSequenceIter *_iter);
160
static EggListBoxChildInfo *egg_list_box_get_first_visible (EggListBox *list_box);
161
static EggListBoxChildInfo *egg_list_box_get_last_visible (EggListBox *list_box);
162
static gboolean egg_list_box_real_draw (GtkWidget *widget,
164
static void egg_list_box_real_realize (GtkWidget *widget);
165
static void egg_list_box_real_add (GtkContainer *container,
167
static void egg_list_box_real_remove (GtkContainer *container,
169
static void egg_list_box_real_forall_internal (GtkContainer *container,
170
gboolean include_internals,
171
GtkCallback callback,
172
void *callback_target);
173
static void egg_list_box_real_compute_expand_internal (GtkWidget *widget,
176
static GType egg_list_box_real_child_type (GtkContainer *container);
177
static GtkSizeRequestMode egg_list_box_real_get_request_mode (GtkWidget *widget);
178
static void egg_list_box_real_size_allocate (GtkWidget *widget,
179
GtkAllocation *allocation);
180
static void egg_list_box_real_drag_leave (GtkWidget *widget,
181
GdkDragContext *context,
183
static gboolean egg_list_box_real_drag_motion (GtkWidget *widget,
184
GdkDragContext *context,
188
static void egg_list_box_real_activate_cursor_child (EggListBox *list_box);
189
static void egg_list_box_real_toggle_cursor_child (EggListBox *list_box);
190
static void egg_list_box_real_move_cursor (EggListBox *list_box,
191
GtkMovementStep step,
193
static void egg_list_box_finalize (GObject *obj);
196
static void egg_list_box_real_get_preferred_height (GtkWidget *widget,
197
gint *minimum_height,
198
gint *natural_height);
199
static void egg_list_box_real_get_preferred_height_for_width (GtkWidget *widget,
201
gint *minimum_height,
202
gint *natural_height);
203
static void egg_list_box_real_get_preferred_width (GtkWidget *widget,
205
gint *natural_width);
206
static void egg_list_box_real_get_preferred_width_for_height (GtkWidget *widget,
209
gint *natural_width);
211
static guint signals[LAST_SIGNAL] = { 0 };
213
static EggListBoxChildInfo*
214
egg_list_box_child_info_new (GtkWidget *widget)
216
EggListBoxChildInfo *info;
218
info = g_new0 (EggListBoxChildInfo, 1);
219
info->widget = g_object_ref (widget);
224
egg_list_box_child_info_free (EggListBoxChildInfo *info)
226
g_clear_object (&info->widget);
227
g_clear_object (&info->separator);
232
egg_list_box_new (void)
234
return g_object_new (EGG_TYPE_LIST_BOX, NULL);
238
egg_list_box_init (EggListBox *list_box)
240
EggListBoxPrivate *priv;
242
list_box->priv = priv =
243
G_TYPE_INSTANCE_GET_PRIVATE (list_box, EGG_TYPE_LIST_BOX, EggListBoxPrivate);
245
gtk_widget_set_can_focus (GTK_WIDGET (list_box), TRUE);
246
gtk_widget_set_has_window (GTK_WIDGET (list_box), TRUE);
247
gtk_widget_set_redraw_on_allocate (GTK_WIDGET (list_box), TRUE);
248
priv->selection_mode = GTK_SELECTION_SINGLE;
249
priv->activate_single_click = TRUE;
251
priv->children = g_sequence_new ((GDestroyNotify)egg_list_box_child_info_free);
252
priv->child_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
253
priv->separator_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
257
egg_list_box_finalize (GObject *obj)
259
EggListBox *list_box = EGG_LIST_BOX (obj);
260
EggListBoxPrivate *priv = list_box->priv;
262
if (priv->auto_scroll_timeout_id != ((guint) 0))
263
g_source_remove (priv->auto_scroll_timeout_id);
265
if (priv->sort_func_target_destroy_notify != NULL)
266
priv->sort_func_target_destroy_notify (priv->sort_func_target);
267
if (priv->filter_func_target_destroy_notify != NULL)
268
priv->filter_func_target_destroy_notify (priv->filter_func_target);
269
if (priv->update_separator_func_target_destroy_notify != NULL)
270
priv->update_separator_func_target_destroy_notify (priv->update_separator_func_target);
272
g_clear_object (&priv->adjustment);
273
g_clear_object (&priv->drag_highlighted_widget);
275
g_sequence_free (priv->children);
276
g_hash_table_unref (priv->child_hash);
277
g_hash_table_unref (priv->separator_hash);
279
G_OBJECT_CLASS (egg_list_box_parent_class)->finalize (obj);
283
egg_list_box_class_init (EggListBoxClass *klass)
285
GObjectClass *object_class = G_OBJECT_CLASS (klass);
286
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
287
GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
288
GtkBindingSet *binding_set;
290
egg_list_box_parent_class = g_type_class_peek_parent (klass);
292
g_type_class_add_private (klass, sizeof (EggListBoxPrivate));
294
object_class->finalize = egg_list_box_finalize;
295
widget_class->enter_notify_event = egg_list_box_real_enter_notify_event;
296
widget_class->leave_notify_event = egg_list_box_real_leave_notify_event;
297
widget_class->motion_notify_event = egg_list_box_real_motion_notify_event;
298
widget_class->button_press_event = egg_list_box_real_button_press_event;
299
widget_class->button_release_event = egg_list_box_real_button_release_event;
300
widget_class->show = egg_list_box_real_show;
301
widget_class->focus = egg_list_box_real_focus;
302
widget_class->draw = egg_list_box_real_draw;
303
widget_class->realize = egg_list_box_real_realize;
304
widget_class->compute_expand = egg_list_box_real_compute_expand_internal;
305
widget_class->get_request_mode = egg_list_box_real_get_request_mode;
306
widget_class->get_preferred_height = egg_list_box_real_get_preferred_height;
307
widget_class->get_preferred_height_for_width = egg_list_box_real_get_preferred_height_for_width;
308
widget_class->get_preferred_width = egg_list_box_real_get_preferred_width;
309
widget_class->get_preferred_width_for_height = egg_list_box_real_get_preferred_width_for_height;
310
widget_class->size_allocate = egg_list_box_real_size_allocate;
311
widget_class->drag_leave = egg_list_box_real_drag_leave;
312
widget_class->drag_motion = egg_list_box_real_drag_motion;
313
container_class->add = egg_list_box_real_add;
314
container_class->remove = egg_list_box_real_remove;
315
container_class->forall = egg_list_box_real_forall_internal;
316
container_class->child_type = egg_list_box_real_child_type;
317
klass->activate_cursor_child = egg_list_box_real_activate_cursor_child;
318
klass->toggle_cursor_child = egg_list_box_real_toggle_cursor_child;
319
klass->move_cursor = egg_list_box_real_move_cursor;
321
signals[CHILD_SELECTED] =
322
g_signal_new ("child-selected",
325
G_STRUCT_OFFSET (EggListBoxClass, child_selected),
327
g_cclosure_marshal_VOID__OBJECT,
330
signals[CHILD_ACTIVATED] =
331
g_signal_new ("child-activated",
334
G_STRUCT_OFFSET (EggListBoxClass, child_activated),
336
g_cclosure_marshal_VOID__OBJECT,
339
signals[ACTIVATE_CURSOR_CHILD] =
340
g_signal_new ("activate-cursor-child",
342
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
343
G_STRUCT_OFFSET (EggListBoxClass, activate_cursor_child),
345
g_cclosure_marshal_VOID__VOID,
347
signals[TOGGLE_CURSOR_CHILD] =
348
g_signal_new ("toggle-cursor-child",
350
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
351
G_STRUCT_OFFSET (EggListBoxClass, toggle_cursor_child),
353
g_cclosure_marshal_VOID__VOID,
355
signals[MOVE_CURSOR] =
356
g_signal_new ("move-cursor",
358
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
359
G_STRUCT_OFFSET (EggListBoxClass, move_cursor),
361
_egg_marshal_VOID__ENUM_INT,
363
GTK_TYPE_MOVEMENT_STEP, G_TYPE_INT);
365
widget_class->activate_signal = signals[ACTIVATE_CURSOR_CHILD];
367
binding_set = gtk_binding_set_by_class (klass);
368
egg_list_box_add_move_binding (binding_set, GDK_KEY_Home, 0,
369
GTK_MOVEMENT_BUFFER_ENDS, -1);
370
egg_list_box_add_move_binding (binding_set, GDK_KEY_KP_Home, 0,
371
GTK_MOVEMENT_BUFFER_ENDS, -1);
372
egg_list_box_add_move_binding (binding_set, GDK_KEY_End, 0,
373
GTK_MOVEMENT_BUFFER_ENDS, 1);
374
egg_list_box_add_move_binding (binding_set, GDK_KEY_KP_End, 0,
375
GTK_MOVEMENT_BUFFER_ENDS, 1);
376
egg_list_box_add_move_binding (binding_set, GDK_KEY_Up, GDK_CONTROL_MASK,
377
GTK_MOVEMENT_DISPLAY_LINES, -1);
378
egg_list_box_add_move_binding (binding_set, GDK_KEY_KP_Up, GDK_CONTROL_MASK,
379
GTK_MOVEMENT_DISPLAY_LINES, -1);
380
egg_list_box_add_move_binding (binding_set, GDK_KEY_Down, GDK_CONTROL_MASK,
381
GTK_MOVEMENT_DISPLAY_LINES, 1);
382
egg_list_box_add_move_binding (binding_set, GDK_KEY_KP_Down, GDK_CONTROL_MASK,
383
GTK_MOVEMENT_DISPLAY_LINES, 1);
384
egg_list_box_add_move_binding (binding_set, GDK_KEY_Page_Up, 0,
385
GTK_MOVEMENT_PAGES, -1);
386
egg_list_box_add_move_binding (binding_set, GDK_KEY_KP_Page_Up, 0,
387
GTK_MOVEMENT_PAGES, -1);
388
egg_list_box_add_move_binding (binding_set, GDK_KEY_Page_Down, 0,
389
GTK_MOVEMENT_PAGES, 1);
390
egg_list_box_add_move_binding (binding_set, GDK_KEY_KP_Page_Down, 0,
391
GTK_MOVEMENT_PAGES, 1);
392
gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, GDK_CONTROL_MASK,
393
"toggle-cursor-child", 0, NULL);
397
egg_list_box_get_selected_child (EggListBox *list_box)
399
EggListBoxPrivate *priv = list_box->priv;
401
g_return_val_if_fail (list_box != NULL, NULL);
403
if (priv->selected_child != NULL)
404
return priv->selected_child->widget;
410
egg_list_box_get_child_at_y (EggListBox *list_box, gint y)
412
EggListBoxChildInfo *child;
414
g_return_val_if_fail (list_box != NULL, NULL);
416
child = egg_list_box_find_child_at_y (list_box, y);
420
return child->widget;
425
egg_list_box_select_child (EggListBox *list_box, GtkWidget *child)
427
EggListBoxChildInfo *info = NULL;
429
g_return_if_fail (list_box != NULL);
432
info = egg_list_box_lookup_info (list_box, child);
434
egg_list_box_update_selected (list_box, info);
438
egg_list_box_set_adjustment (EggListBox *list_box,
439
GtkAdjustment *adjustment)
441
EggListBoxPrivate *priv = list_box->priv;
443
g_return_if_fail (list_box != NULL);
445
g_object_ref (adjustment);
446
if (priv->adjustment)
447
g_object_unref (priv->adjustment);
448
priv->adjustment = adjustment;
449
gtk_container_set_focus_vadjustment (GTK_CONTAINER (list_box),
454
egg_list_box_add_to_scrolled (EggListBox *list_box,
455
GtkScrolledWindow *scrolled)
457
g_return_if_fail (list_box != NULL);
458
g_return_if_fail (scrolled != NULL);
460
gtk_scrolled_window_add_with_viewport (scrolled,
461
GTK_WIDGET (list_box));
462
egg_list_box_set_adjustment (list_box,
463
gtk_scrolled_window_get_vadjustment (scrolled));
468
egg_list_box_set_selection_mode (EggListBox *list_box, GtkSelectionMode mode)
470
EggListBoxPrivate *priv = list_box->priv;
472
g_return_if_fail (list_box != NULL);
474
if (mode == GTK_SELECTION_MULTIPLE)
476
g_warning ("egg-list-box.vala:115: Multiple selections not supported");
480
priv->selection_mode = mode;
481
if (mode == GTK_SELECTION_NONE)
482
egg_list_box_update_selected (list_box, NULL);
487
egg_list_box_set_filter_func (EggListBox *list_box,
488
EggListBoxFilterFunc f,
490
GDestroyNotify f_target_destroy_notify)
492
EggListBoxPrivate *priv = list_box->priv;
494
g_return_if_fail (list_box != NULL);
496
if (priv->filter_func_target_destroy_notify != NULL)
497
priv->filter_func_target_destroy_notify (priv->filter_func_target);
499
priv->filter_func = f;
500
priv->filter_func_target = f_target;
501
priv->filter_func_target_destroy_notify = f_target_destroy_notify;
503
egg_list_box_refilter (list_box);
507
egg_list_box_set_separator_funcs (EggListBox *list_box,
508
EggListBoxUpdateSeparatorFunc update_separator,
509
void *update_separator_target,
510
GDestroyNotify update_separator_target_destroy_notify)
512
EggListBoxPrivate *priv = list_box->priv;
514
g_return_if_fail (list_box != NULL);
516
if (priv->update_separator_func_target_destroy_notify != NULL)
517
priv->update_separator_func_target_destroy_notify (priv->update_separator_func_target);
519
priv->update_separator_func = update_separator;
520
priv->update_separator_func_target = update_separator_target;
521
priv->update_separator_func_target_destroy_notify = update_separator_target_destroy_notify;
522
egg_list_box_reseparate (list_box);
526
egg_list_box_refilter (EggListBox *list_box)
528
g_return_if_fail (list_box != NULL);
531
egg_list_box_apply_filter_all (list_box);
532
egg_list_box_reseparate (list_box);
533
gtk_widget_queue_resize (GTK_WIDGET (list_box));
537
do_sort (EggListBoxChildInfo *a,
538
EggListBoxChildInfo *b,
539
EggListBox *list_box)
541
EggListBoxPrivate *priv = list_box->priv;
543
return priv->sort_func (a->widget, b->widget,
544
priv->sort_func_target);
548
egg_list_box_resort (EggListBox *list_box)
550
EggListBoxPrivate *priv = list_box->priv;
552
g_return_if_fail (list_box != NULL);
554
g_sequence_sort (priv->children,
555
(GCompareDataFunc)do_sort, list_box);
556
egg_list_box_reseparate (list_box);
557
gtk_widget_queue_resize (GTK_WIDGET (list_box));
561
egg_list_box_reseparate (EggListBox *list_box)
563
EggListBoxPrivate *priv = list_box->priv;
566
g_return_if_fail (list_box != NULL);
568
for (iter = g_sequence_get_begin_iter (priv->children);
569
!g_sequence_iter_is_end (iter);
570
iter = g_sequence_iter_next (iter))
571
egg_list_box_update_separator (list_box, iter);
573
gtk_widget_queue_resize (GTK_WIDGET (list_box));
577
egg_list_box_set_sort_func (EggListBox *list_box,
580
GDestroyNotify f_target_destroy_notify)
582
EggListBoxPrivate *priv = list_box->priv;
584
g_return_if_fail (list_box != NULL);
586
if (priv->sort_func_target_destroy_notify != NULL)
587
priv->sort_func_target_destroy_notify (priv->sort_func_target);
590
priv->sort_func_target = f_target;
591
priv->sort_func_target_destroy_notify = f_target_destroy_notify;
592
egg_list_box_resort (list_box);
596
egg_list_box_child_changed (EggListBox *list_box, GtkWidget *widget)
598
EggListBoxPrivate *priv = list_box->priv;
599
EggListBoxChildInfo *info;
600
GSequenceIter *prev_next, *next;
602
g_return_if_fail (list_box != NULL);
603
g_return_if_fail (widget != NULL);
605
info = egg_list_box_lookup_info (list_box, widget);
609
prev_next = egg_list_box_get_next_visible (list_box, info->iter);
610
if (priv->sort_func != NULL)
612
g_sequence_sort_changed (info->iter,
613
(GCompareDataFunc)do_sort,
615
gtk_widget_queue_resize (GTK_WIDGET (list_box));
617
egg_list_box_apply_filter (list_box, info->widget);
618
if (gtk_widget_get_visible (GTK_WIDGET (list_box)))
620
next = egg_list_box_get_next_visible (list_box, info->iter);
621
egg_list_box_update_separator (list_box, info->iter);
622
egg_list_box_update_separator (list_box, next);
623
egg_list_box_update_separator (list_box, prev_next);
628
egg_list_box_set_activate_on_single_click (EggListBox *list_box,
631
EggListBoxPrivate *priv = list_box->priv;
633
g_return_if_fail (list_box != NULL);
635
priv->activate_single_click = single;
639
egg_list_box_add_move_binding (GtkBindingSet *binding_set,
641
GdkModifierType modmask,
642
GtkMovementStep step,
645
gtk_binding_entry_add_signal (binding_set, keyval, modmask,
646
"move-cursor", (guint) 2, GTK_TYPE_MOVEMENT_STEP, step, G_TYPE_INT, count, NULL);
648
if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
651
gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK,
652
"move-cursor", (guint) 2, GTK_TYPE_MOVEMENT_STEP, step, G_TYPE_INT, count, NULL);
655
static EggListBoxChildInfo*
656
egg_list_box_find_child_at_y (EggListBox *list_box, gint y)
658
EggListBoxPrivate *priv = list_box->priv;
659
EggListBoxChildInfo *child_info;
661
EggListBoxChildInfo *info;
664
for (iter = g_sequence_get_begin_iter (priv->children);
665
!g_sequence_iter_is_end (iter);
666
iter = g_sequence_iter_next (iter))
668
info = (EggListBoxChildInfo*) g_sequence_get (iter);
669
if (y >= info->y && y < (info->y + info->height))
680
egg_list_box_update_cursor (EggListBox *list_box,
681
EggListBoxChildInfo *child)
683
EggListBoxPrivate *priv = list_box->priv;
685
priv->cursor_child = child;
686
gtk_widget_grab_focus (GTK_WIDGET (list_box));
687
gtk_widget_queue_draw (GTK_WIDGET (list_box));
688
if (child != NULL && priv->adjustment != NULL)
690
GtkAllocation allocation;
691
gtk_widget_get_allocation (GTK_WIDGET (list_box), &allocation);
692
gtk_adjustment_clamp_page (priv->adjustment,
693
priv->cursor_child->y + allocation.y,
694
priv->cursor_child->y + allocation.y + priv->cursor_child->height);
699
egg_list_box_update_selected (EggListBox *list_box,
700
EggListBoxChildInfo *child)
702
EggListBoxPrivate *priv = list_box->priv;
704
if (child != priv->selected_child &&
705
(child == NULL || priv->selection_mode != GTK_SELECTION_NONE))
707
priv->selected_child = child;
708
g_signal_emit (list_box, signals[CHILD_SELECTED], 0,
709
(priv->selected_child != NULL) ? priv->selected_child->widget : NULL);
710
gtk_widget_queue_draw (GTK_WIDGET (list_box));
713
egg_list_box_update_cursor (list_box, child);
717
egg_list_box_select_and_activate (EggListBox *list_box, EggListBoxChildInfo *child)
724
egg_list_box_update_selected (list_box, child);
727
g_signal_emit (list_box, signals[CHILD_ACTIVATED], 0, w);
731
egg_list_box_update_prelight (EggListBox *list_box, EggListBoxChildInfo *child)
733
EggListBoxPrivate *priv = list_box->priv;
735
if (child != priv->prelight_child)
737
priv->prelight_child = child;
738
gtk_widget_queue_draw (GTK_WIDGET (list_box));
743
egg_list_box_update_active (EggListBox *list_box, EggListBoxChildInfo *child)
745
EggListBoxPrivate *priv = list_box->priv;
748
val = priv->active_child == child;
749
if (priv->active_child != NULL &&
750
val != priv->active_child_active)
752
priv->active_child_active = val;
753
gtk_widget_queue_draw (GTK_WIDGET (list_box));
758
egg_list_box_real_enter_notify_event (GtkWidget *widget,
759
GdkEventCrossing *event)
761
EggListBox *list_box = EGG_LIST_BOX (widget);
762
EggListBoxChildInfo *child;
765
if (event->window != gtk_widget_get_window (GTK_WIDGET (list_box)))
768
child = egg_list_box_find_child_at_y (list_box, event->y);
769
egg_list_box_update_prelight (list_box, child);
770
egg_list_box_update_active (list_box, child);
776
egg_list_box_real_leave_notify_event (GtkWidget *widget,
777
GdkEventCrossing *event)
779
EggListBox *list_box = EGG_LIST_BOX (widget);
780
EggListBoxChildInfo *child = NULL;
782
if (event->window != gtk_widget_get_window (GTK_WIDGET (list_box)))
785
if (event->detail != GDK_NOTIFY_INFERIOR)
788
child = egg_list_box_find_child_at_y (list_box, event->y);
790
egg_list_box_update_prelight (list_box, child);
791
egg_list_box_update_active (list_box, child);
797
egg_list_box_real_motion_notify_event (GtkWidget *widget,
798
GdkEventMotion *event)
800
EggListBox *list_box = EGG_LIST_BOX (widget);
801
EggListBoxChildInfo *child;
804
child = egg_list_box_find_child_at_y (list_box, event->y);
805
egg_list_box_update_prelight (list_box, child);
806
egg_list_box_update_active (list_box, child);
812
egg_list_box_real_button_press_event (GtkWidget *widget,
813
GdkEventButton *event)
815
EggListBox *list_box = EGG_LIST_BOX (widget);
816
EggListBoxPrivate *priv = list_box->priv;
818
if (event->button == 1)
820
EggListBoxChildInfo *child;
821
child = egg_list_box_find_child_at_y (list_box, event->y);
824
priv->active_child = child;
825
priv->active_child_active = TRUE;
826
gtk_widget_queue_draw (GTK_WIDGET (list_box));
827
if (event->type == GDK_2BUTTON_PRESS &&
828
!priv->activate_single_click &&
829
child->widget != NULL)
830
g_signal_emit (list_box, signals[CHILD_ACTIVATED], 0,
835
Should mark as active while down,
836
and handle grab breaks */
843
egg_list_box_real_button_release_event (GtkWidget *widget,
844
GdkEventButton *event)
846
EggListBox *list_box = EGG_LIST_BOX (widget);
847
EggListBoxPrivate *priv = list_box->priv;
849
if (event->button == 1)
851
if (priv->active_child != NULL &&
852
priv->active_child_active)
854
if (priv->activate_single_click)
855
egg_list_box_select_and_activate (list_box, priv->active_child);
857
egg_list_box_update_selected (list_box, priv->active_child);
859
priv->active_child = NULL;
860
priv->active_child_active = FALSE;
861
gtk_widget_queue_draw (GTK_WIDGET (list_box));
868
egg_list_box_real_show (GtkWidget *widget)
870
EggListBox * list_box = EGG_LIST_BOX (widget);
872
egg_list_box_reseparate (list_box);
874
GTK_WIDGET_CLASS (egg_list_box_parent_class)->show ((GtkWidget*) G_TYPE_CHECK_INSTANCE_CAST (list_box, GTK_TYPE_CONTAINER, GtkContainer));
879
egg_list_box_real_focus (GtkWidget* widget, GtkDirectionType direction)
881
EggListBox *list_box = EGG_LIST_BOX (widget);
882
EggListBoxPrivate *priv = list_box->priv;
883
gboolean had_focus = FALSE;
884
gboolean focus_into = FALSE;
885
GtkWidget* recurse_into;
886
EggListBoxChildInfo *current_focus_child;
887
EggListBoxChildInfo *next_focus_child;
888
gboolean modify_selection_pressed;
889
GdkModifierType state = 0;
894
g_object_get (GTK_WIDGET (list_box), "has-focus", &had_focus, NULL);
895
current_focus_child = NULL;
896
next_focus_child = NULL;
899
/* If on row, going right, enter into possible container */
900
if (direction == GTK_DIR_RIGHT || direction == GTK_DIR_TAB_FORWARD)
902
if (priv->cursor_child != NULL)
903
recurse_into = priv->cursor_child->widget;
905
current_focus_child = priv->cursor_child;
906
/* Unless we're going up/down we're always leaving
908
if (direction != GTK_DIR_UP && direction != GTK_DIR_DOWN)
911
else if (gtk_container_get_focus_child ((GtkContainer*) list_box) != NULL)
913
/* There is a focus child, always navigat inside it first */
914
recurse_into = gtk_container_get_focus_child ((GtkContainer*) list_box);
915
current_focus_child = egg_list_box_lookup_info (list_box, recurse_into);
917
/* If exiting child container to the right, exit row */
918
if (direction == GTK_DIR_RIGHT || direction == GTK_DIR_TAB_FORWARD)
921
/* If exiting child container to the left, select row or out */
922
if (direction == GTK_DIR_LEFT || direction == GTK_DIR_TAB_BACKWARD)
923
next_focus_child = current_focus_child;
927
/* If coming from the left, enter into possible container */
928
if (direction == GTK_DIR_LEFT || direction == GTK_DIR_TAB_BACKWARD)
930
if (priv->selected_child != NULL)
931
recurse_into = priv->selected_child->widget;
935
if (recurse_into != NULL)
937
if (gtk_widget_child_focus (recurse_into, direction))
942
return FALSE; /* Focus is leaving us */
944
/* TODO: This doesn't handle up/down going into a focusable separator */
946
if (next_focus_child == NULL)
948
if (current_focus_child != NULL)
951
if (direction == GTK_DIR_UP)
953
i = egg_list_box_get_previous_visible (list_box, current_focus_child->iter);
955
next_focus_child = g_sequence_get (i);
960
i = egg_list_box_get_next_visible (list_box, current_focus_child->iter);
961
if (!g_sequence_iter_is_end (i))
962
next_focus_child = g_sequence_get (i);
971
case GTK_DIR_TAB_FORWARD:
972
next_focus_child = egg_list_box_get_first_visible (list_box);
975
case GTK_DIR_TAB_BACKWARD:
976
next_focus_child = egg_list_box_get_last_visible (list_box);
979
next_focus_child = priv->selected_child;
980
if (next_focus_child == NULL)
982
egg_list_box_get_first_visible (list_box);
988
if (next_focus_child == NULL)
990
if (direction == GTK_DIR_UP || direction == GTK_DIR_DOWN)
992
gtk_widget_error_bell (GTK_WIDGET (list_box));
999
modify_selection_pressed = FALSE;
1000
if (gtk_get_current_event_state (&state))
1002
GdkModifierType modify_mod_mask;
1004
gtk_widget_get_modifier_mask (GTK_WIDGET (list_box),
1005
GDK_MODIFIER_INTENT_MODIFY_SELECTION);
1006
if ((state & modify_mod_mask) == modify_mod_mask)
1007
modify_selection_pressed = TRUE;
1010
egg_list_box_update_cursor (list_box, next_focus_child);
1011
if (!modify_selection_pressed)
1012
egg_list_box_update_selected (list_box, next_focus_child);
1018
EggListBoxChildInfo *child;
1019
GtkStateFlags state;
1023
child_flags_find_or_add (ChildFlags *array,
1025
EggListBoxChildInfo *to_find)
1029
for (i = 0; i < *array_length; i++)
1031
if (array[i].child == to_find)
1035
*array_length = *array_length + 1;
1036
array[*array_length - 1].child = to_find;
1037
array[*array_length - 1].state = 0;
1038
return &array[*array_length - 1];
1042
egg_list_box_real_draw (GtkWidget* widget, cairo_t* cr)
1044
EggListBox * list_box = EGG_LIST_BOX (widget);
1045
EggListBoxPrivate *priv = list_box->priv;
1046
GtkAllocation allocation = {0};
1047
GtkStyleContext* context;
1048
GtkStateFlags state;
1049
ChildFlags flags[3], *found;
1053
gtk_widget_get_allocation (GTK_WIDGET (list_box), &allocation);
1054
context = gtk_widget_get_style_context (GTK_WIDGET (list_box));
1055
state = gtk_widget_get_state_flags (widget);
1056
gtk_render_background (context, cr, (gdouble) 0, (gdouble) 0, (gdouble) allocation.width, (gdouble) allocation.height);
1059
if (priv->selected_child != NULL)
1061
found = child_flags_find_or_add (flags, &flags_length, priv->selected_child);
1062
found->state |= (state | GTK_STATE_FLAG_SELECTED);
1065
if (priv->prelight_child != NULL)
1067
found = child_flags_find_or_add (flags, &flags_length, priv->prelight_child);
1068
found->state |= (state | GTK_STATE_FLAG_PRELIGHT);
1071
if (priv->active_child != NULL && priv->active_child_active)
1073
found = child_flags_find_or_add (flags, &flags_length, priv->active_child);
1074
found->state |= (state | GTK_STATE_FLAG_ACTIVE);
1077
for (i = 0; i < flags_length; i++)
1079
ChildFlags *flag = &flags[i];
1080
gtk_style_context_save (context);
1081
gtk_style_context_set_state (context, flag->state);
1082
gtk_render_background (context, cr, 0, flag->child->y, allocation.width, flag->child->height);
1083
gtk_style_context_restore (context);
1086
if (gtk_widget_has_visible_focus (GTK_WIDGET (list_box)) && priv->cursor_child != NULL)
1087
gtk_render_focus (context, cr, 0, priv->cursor_child->y, allocation.width, priv->cursor_child->height);
1089
GTK_WIDGET_CLASS (egg_list_box_parent_class)->draw ((GtkWidget*) G_TYPE_CHECK_INSTANCE_CAST (list_box, GTK_TYPE_CONTAINER, GtkContainer), cr);
1096
egg_list_box_real_realize (GtkWidget* widget)
1098
EggListBox *list_box = EGG_LIST_BOX (widget);
1099
GtkAllocation allocation;
1100
GdkWindowAttr attributes = {0};
1103
gtk_widget_get_allocation (GTK_WIDGET (list_box), &allocation);
1104
gtk_widget_set_realized (GTK_WIDGET (list_box), TRUE);
1106
attributes.x = allocation.x;
1107
attributes.y = allocation.y;
1108
attributes.width = allocation.width;
1109
attributes.height = allocation.height;
1110
attributes.window_type = GDK_WINDOW_CHILD;
1111
attributes.event_mask = gtk_widget_get_events (GTK_WIDGET (list_box)) |
1112
GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK |
1113
GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK;
1114
attributes.wclass = GDK_INPUT_OUTPUT;
1116
window = gdk_window_new (gtk_widget_get_parent_window (GTK_WIDGET (list_box)),
1117
&attributes, GDK_WA_X | GDK_WA_Y);
1118
gtk_style_context_set_background (gtk_widget_get_style_context (GTK_WIDGET (list_box)), window);
1119
gdk_window_set_user_data (window, (GObject*) list_box);
1120
gtk_widget_set_window (GTK_WIDGET (list_box), window); /* Passes ownership */
1125
egg_list_box_apply_filter (EggListBox *list_box, GtkWidget *child)
1127
EggListBoxPrivate *priv = list_box->priv;
1131
if (priv->filter_func != NULL)
1132
do_show = priv->filter_func (child, priv->filter_func_target);
1134
gtk_widget_set_child_visible (child, do_show);
1138
egg_list_box_apply_filter_all (EggListBox *list_box)
1140
EggListBoxPrivate *priv = list_box->priv;
1141
EggListBoxChildInfo *child_info;
1142
GSequenceIter *iter;
1144
for (iter = g_sequence_get_begin_iter (priv->children);
1145
!g_sequence_iter_is_end (iter);
1146
iter = g_sequence_iter_next (iter))
1148
child_info = g_sequence_get (iter);
1149
egg_list_box_apply_filter (list_box, child_info->widget);
1153
/* Children are visible if they are shown by the app (visible)
1154
and not filtered out (child_visible) by the listbox */
1156
child_is_visible (GtkWidget *child)
1158
return gtk_widget_get_visible (child) && gtk_widget_get_child_visible (child);
1161
static EggListBoxChildInfo*
1162
egg_list_box_get_first_visible (EggListBox *list_box)
1164
EggListBoxPrivate *priv = list_box->priv;
1165
EggListBoxChildInfo *child_info;
1166
GSequenceIter *iter;
1168
for (iter = g_sequence_get_begin_iter (priv->children);
1169
!g_sequence_iter_is_end (iter);
1170
iter = g_sequence_iter_next (iter))
1172
child_info = g_sequence_get (iter);
1173
if (child_is_visible (child_info->widget))
1181
static EggListBoxChildInfo*
1182
egg_list_box_get_last_visible (EggListBox *list_box)
1184
EggListBoxPrivate *priv = list_box->priv;
1185
EggListBoxChildInfo *child_info;
1186
GSequenceIter *iter;
1188
iter = g_sequence_get_end_iter (priv->children);
1189
while (!g_sequence_iter_is_begin (iter))
1191
iter = g_sequence_iter_prev (iter);
1192
child_info = g_sequence_get (iter);
1193
if (child_is_visible (child_info->widget))
1200
static GSequenceIter*
1201
egg_list_box_get_previous_visible (EggListBox *list_box,
1202
GSequenceIter* iter)
1204
EggListBoxChildInfo *child_info;
1206
if (g_sequence_iter_is_begin (iter))
1211
iter = g_sequence_iter_prev (iter);
1212
child_info = g_sequence_get (iter);
1213
if (child_is_visible (child_info->widget))
1216
while (!g_sequence_iter_is_begin (iter));
1221
static GSequenceIter*
1222
egg_list_box_get_next_visible (EggListBox *list_box, GSequenceIter* iter)
1224
EggListBoxChildInfo *child_info;
1226
if (g_sequence_iter_is_end (iter))
1231
iter = g_sequence_iter_next (iter);
1232
if (!g_sequence_iter_is_end (iter))
1234
child_info = g_sequence_get (iter);
1235
if (child_is_visible (child_info->widget))
1239
while (!g_sequence_iter_is_end (iter));
1246
egg_list_box_update_separator (EggListBox *list_box, GSequenceIter* iter)
1248
EggListBoxPrivate *priv = list_box->priv;
1249
EggListBoxChildInfo *info;
1250
GSequenceIter *before_iter;
1252
GtkWidget *before_child;
1253
EggListBoxChildInfo *before_info;
1254
GtkWidget *old_separator;
1256
if (iter == NULL || g_sequence_iter_is_end (iter))
1259
info = g_sequence_get (iter);
1260
before_iter = egg_list_box_get_previous_visible (list_box, iter);
1261
child = info->widget;
1263
g_object_ref (child);
1264
before_child = NULL;
1265
if (before_iter != NULL)
1267
before_info = g_sequence_get (before_iter);
1268
before_child = before_info->widget;
1270
g_object_ref (before_child);
1273
if (priv->update_separator_func != NULL &&
1274
child_is_visible (child))
1276
old_separator = info->separator;
1278
g_object_ref (old_separator);
1279
priv->update_separator_func (&info->separator,
1282
priv->update_separator_func_target);
1283
if (old_separator != info->separator)
1285
if (old_separator != NULL)
1287
gtk_widget_unparent (old_separator);
1288
g_hash_table_remove (priv->separator_hash, old_separator);
1290
if (info->separator != NULL)
1292
g_hash_table_insert (priv->separator_hash, info->separator, info);
1293
gtk_widget_set_parent (info->separator, GTK_WIDGET (list_box));
1294
gtk_widget_show (info->separator);
1296
gtk_widget_queue_resize (GTK_WIDGET (list_box));
1299
g_object_unref (old_separator);
1303
if (info->separator != NULL)
1305
g_hash_table_remove (priv->separator_hash, info->separator);
1306
gtk_widget_unparent (info->separator);
1307
g_clear_object (&info->separator);
1308
gtk_widget_queue_resize (GTK_WIDGET (list_box));
1312
g_object_unref (before_child);
1314
g_object_unref (child);
1317
static EggListBoxChildInfo*
1318
egg_list_box_lookup_info (EggListBox *list_box, GtkWidget* child)
1320
EggListBoxPrivate *priv = list_box->priv;
1322
return g_hash_table_lookup (priv->child_hash, child);
1326
child_visibility_changed (GObject* object, GParamSpec* pspec, EggListBox *list_box)
1328
EggListBoxChildInfo *info;
1330
if (gtk_widget_get_visible (GTK_WIDGET (list_box)))
1332
info = egg_list_box_lookup_info (list_box, GTK_WIDGET (object));
1335
egg_list_box_update_separator (list_box, info->iter);
1336
egg_list_box_update_separator (list_box,
1337
egg_list_box_get_next_visible (list_box, info->iter));
1343
egg_list_box_real_add (GtkContainer* container, GtkWidget* child)
1345
EggListBox *list_box = EGG_LIST_BOX (container);
1346
EggListBoxPrivate *priv = list_box->priv;
1347
EggListBoxChildInfo *info;
1348
GSequenceIter* iter = NULL;
1349
info = egg_list_box_child_info_new (child);
1350
g_hash_table_insert (priv->child_hash, child, info);
1351
if (priv->sort_func != NULL)
1352
iter = g_sequence_insert_sorted (priv->children, info,
1353
(GCompareDataFunc)do_sort, list_box);
1355
iter = g_sequence_append (priv->children, info);
1358
gtk_widget_set_parent (child, GTK_WIDGET (list_box));
1359
egg_list_box_apply_filter (list_box, child);
1360
if (gtk_widget_get_visible (GTK_WIDGET (list_box)))
1362
egg_list_box_update_separator (list_box, iter);
1363
egg_list_box_update_separator (list_box, egg_list_box_get_next_visible (list_box, iter));
1365
g_signal_connect_object (child, "notify::visible",
1366
(GCallback) child_visibility_changed, list_box, 0);
1370
egg_list_box_real_remove (GtkContainer* container, GtkWidget* child)
1372
EggListBox *list_box = EGG_LIST_BOX (container);
1373
EggListBoxPrivate *priv = list_box->priv;
1374
gboolean was_visible;
1375
EggListBoxChildInfo *info;
1376
GSequenceIter *next;
1378
g_return_if_fail (child != NULL);
1379
was_visible = gtk_widget_get_visible (child);
1381
g_signal_handlers_disconnect_by_func (child, (GCallback) child_visibility_changed, list_box);
1383
info = egg_list_box_lookup_info (list_box, child);
1386
info = g_hash_table_lookup (priv->separator_hash, child);
1389
g_hash_table_remove (priv->separator_hash, child);
1390
g_clear_object (&info->separator);
1391
gtk_widget_unparent (child);
1392
if (was_visible && gtk_widget_get_visible (GTK_WIDGET (list_box)))
1393
gtk_widget_queue_resize (GTK_WIDGET (list_box));
1397
g_warning ("egg-list-box.vala:846: Tried to remove non-child %p\n", child);
1402
if (info->separator != NULL)
1404
g_hash_table_remove (priv->separator_hash, info->separator);
1405
gtk_widget_unparent (info->separator);
1406
g_clear_object (&info->separator);
1409
if (info == priv->selected_child)
1410
egg_list_box_update_selected (list_box, NULL);
1411
if (info == priv->prelight_child)
1412
priv->prelight_child = NULL;
1413
if (info == priv->cursor_child)
1414
priv->cursor_child = NULL;
1415
if (info == priv->active_child)
1416
priv->active_child = NULL;
1418
next = egg_list_box_get_next_visible (list_box, info->iter);
1419
gtk_widget_unparent (child);
1420
g_hash_table_remove (priv->child_hash, child);
1421
g_sequence_remove (info->iter);
1422
if (gtk_widget_get_visible (GTK_WIDGET (list_box)))
1423
egg_list_box_update_separator (list_box, next);
1425
if (was_visible && gtk_widget_get_visible (GTK_WIDGET (list_box)))
1426
gtk_widget_queue_resize (GTK_WIDGET (list_box));
1431
egg_list_box_real_forall_internal (GtkContainer* container,
1432
gboolean include_internals,
1433
GtkCallback callback,
1434
void* callback_target)
1436
EggListBox *list_box = EGG_LIST_BOX (container);
1437
EggListBoxPrivate *priv = list_box->priv;
1438
GSequenceIter *iter;
1439
EggListBoxChildInfo *child_info;
1441
iter = g_sequence_get_begin_iter (priv->children);
1442
while (!g_sequence_iter_is_end (iter))
1444
child_info = g_sequence_get (iter);
1445
iter = g_sequence_iter_next (iter);
1446
if (child_info->separator != NULL && include_internals)
1447
callback (child_info->separator, callback_target);
1448
callback (child_info->widget, callback_target);
1453
egg_list_box_real_compute_expand_internal (GtkWidget* widget,
1457
GTK_WIDGET_CLASS (egg_list_box_parent_class)->compute_expand (widget,
1460
/* We don't expand vertically beyound the minimum size */
1466
egg_list_box_real_child_type (GtkContainer* container)
1468
return GTK_TYPE_WIDGET;
1471
static GtkSizeRequestMode
1472
egg_list_box_real_get_request_mode (GtkWidget* widget)
1474
return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
1478
egg_list_box_real_get_preferred_height (GtkWidget* widget,
1479
gint* minimum_height,
1480
gint* natural_height)
1483
egg_list_box_real_get_preferred_width (widget, NULL, &natural_width);
1484
egg_list_box_real_get_preferred_height_for_width (widget, natural_width,
1485
minimum_height, natural_height);
1489
egg_list_box_real_get_preferred_height_for_width (GtkWidget* widget, gint width,
1490
gint* minimum_height_out, gint* natural_height_out)
1492
EggListBox *list_box = EGG_LIST_BOX (widget);
1493
EggListBoxPrivate *priv = list_box->priv;
1494
GSequenceIter *iter;
1495
gint minimum_height;
1496
gint natural_height;
1497
GtkStyleContext *context;
1503
context = gtk_widget_get_style_context (GTK_WIDGET (list_box));
1504
gtk_style_context_get_style (context,
1505
"focus-line-width", &focus_width,
1506
"focus-padding", &focus_pad, NULL);
1508
for (iter = g_sequence_get_begin_iter (priv->children);
1509
!g_sequence_iter_is_end (iter);
1510
iter = g_sequence_iter_next (iter))
1512
EggListBoxChildInfo *child_info;
1515
child_info = g_sequence_get (iter);
1516
child = child_info->widget;
1518
if (!child_is_visible (child))
1521
if (child_info->separator != NULL)
1523
gtk_widget_get_preferred_height_for_width (child_info->separator, width, &child_min, NULL);
1524
minimum_height += child_min;
1526
gtk_widget_get_preferred_height_for_width (child, width - 2 * (focus_width + focus_pad),
1528
minimum_height += child_min + 2 * (focus_width + focus_pad);
1531
/* We always allocate the minimum height, since handling
1532
expanding rows is way too costly, and unlikely to
1533
be used, as lists are generally put inside a scrolling window
1536
natural_height = minimum_height;
1537
if (minimum_height_out)
1538
*minimum_height_out = minimum_height;
1539
if (natural_height_out)
1540
*natural_height_out = natural_height;
1544
egg_list_box_real_get_preferred_width (GtkWidget* widget, gint* minimum_width_out, gint* natural_width_out)
1546
EggListBox *list_box = EGG_LIST_BOX (widget);
1547
EggListBoxPrivate *priv = list_box->priv;
1550
GtkStyleContext *context;
1553
GSequenceIter *iter;
1554
EggListBoxChildInfo *child_info;
1559
context = gtk_widget_get_style_context (GTK_WIDGET (list_box));
1560
gtk_style_context_get_style (context, "focus-line-width", &focus_width, "focus-padding", &focus_pad, NULL);
1565
for (iter = g_sequence_get_begin_iter (priv->children);
1566
!g_sequence_iter_is_end (iter);
1567
iter = g_sequence_iter_next (iter))
1569
child_info = g_sequence_get (iter);
1570
child = child_info->widget;
1571
if (!child_is_visible (child))
1574
gtk_widget_get_preferred_width (child, &child_min, &child_nat);
1575
minimum_width = MAX (minimum_width, child_min + 2 * (focus_width + focus_pad));
1576
natural_width = MAX (natural_width, child_nat + 2 * (focus_width + focus_pad));
1578
if (child_info->separator != NULL)
1580
gtk_widget_get_preferred_width (child_info->separator, &child_min, &child_nat);
1581
minimum_width = MAX (minimum_width, child_min);
1582
natural_width = MAX (natural_width, child_nat);
1586
if (minimum_width_out)
1587
*minimum_width_out = minimum_width;
1588
if (natural_width_out)
1589
*natural_width_out = natural_width;
1593
egg_list_box_real_get_preferred_width_for_height (GtkWidget *widget, gint height,
1594
gint *minimum_width, gint *natural_width)
1596
EggListBox *list_box = EGG_LIST_BOX (widget);
1597
egg_list_box_real_get_preferred_width (GTK_WIDGET (list_box), minimum_width, natural_width);
1601
egg_list_box_real_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1603
EggListBox *list_box = EGG_LIST_BOX (widget);
1604
EggListBoxPrivate *priv = list_box->priv;
1605
GtkAllocation child_allocation;
1606
GtkAllocation separator_allocation;
1607
EggListBoxChildInfo *child_info;
1610
GSequenceIter *iter;
1611
GtkStyleContext *context;
1617
child_allocation.x = 0;
1618
child_allocation.y = 0;
1619
child_allocation.width = 0;
1620
child_allocation.height = 0;
1622
separator_allocation.x = 0;
1623
separator_allocation.y = 0;
1624
separator_allocation.width = 0;
1625
separator_allocation.height = 0;
1627
gtk_widget_set_allocation (GTK_WIDGET (list_box), allocation);
1628
window = gtk_widget_get_window (GTK_WIDGET (list_box));
1630
gdk_window_move_resize (window,
1631
allocation->x, allocation->y,
1632
allocation->width, allocation->height);
1634
context = gtk_widget_get_style_context (GTK_WIDGET (list_box));
1635
gtk_style_context_get_style (context,
1636
"focus-line-width", &focus_width,
1637
"focus-padding", &focus_pad,
1639
child_allocation.x = 0 + focus_width + focus_pad;
1640
child_allocation.y = 0;
1641
child_allocation.width = allocation->width - 2 * (focus_width + focus_pad);
1642
separator_allocation.x = 0;
1643
separator_allocation.width = allocation->width;
1645
for (iter = g_sequence_get_begin_iter (priv->children);
1646
!g_sequence_iter_is_end (iter);
1647
iter = g_sequence_iter_next (iter))
1649
child_info = g_sequence_get (iter);
1650
child = child_info->widget;
1651
if (!child_is_visible (child))
1653
child_info->y = child_allocation.y;
1654
child_info->height = 0;
1658
if (child_info->separator != NULL)
1660
gtk_widget_get_preferred_height_for_width (child_info->separator,
1661
allocation->width, &child_min, NULL);
1662
separator_allocation.height = child_min;
1663
separator_allocation.y = child_allocation.y;
1664
gtk_widget_size_allocate (child_info->separator,
1665
&separator_allocation);
1666
child_allocation.y += child_min;
1669
child_info->y = child_allocation.y;
1670
child_allocation.y += focus_width + focus_pad;
1672
gtk_widget_get_preferred_height_for_width (child, child_allocation.width, &child_min, NULL);
1673
child_allocation.height = child_min;
1675
child_info->height = child_allocation.height + 2 * (focus_width + focus_pad);
1676
gtk_widget_size_allocate (child, &child_allocation);
1678
child_allocation.y += child_min + focus_width + focus_pad;
1683
egg_list_box_drag_unhighlight_widget (EggListBox *list_box)
1685
EggListBoxPrivate *priv = list_box->priv;
1687
g_return_if_fail (list_box != NULL);
1689
if (priv->drag_highlighted_widget == NULL)
1692
gtk_drag_unhighlight (priv->drag_highlighted_widget);
1693
g_clear_object (&priv->drag_highlighted_widget);
1698
egg_list_box_drag_highlight_widget (EggListBox *list_box, GtkWidget *child)
1700
EggListBoxPrivate *priv = list_box->priv;
1701
GtkWidget *old_highlight;
1703
g_return_if_fail (list_box != NULL);
1704
g_return_if_fail (child != NULL);
1706
if (priv->drag_highlighted_widget == child)
1709
egg_list_box_drag_unhighlight_widget (list_box);
1710
gtk_drag_highlight (child);
1712
old_highlight = priv->drag_highlighted_widget;
1713
priv->drag_highlighted_widget = g_object_ref (child);
1715
g_object_unref (old_highlight);
1719
egg_list_box_real_drag_leave (GtkWidget *widget, GdkDragContext *context, guint time_)
1721
EggListBox *list_box = EGG_LIST_BOX (widget);
1722
EggListBoxPrivate *priv = list_box->priv;
1724
egg_list_box_drag_unhighlight_widget (list_box);
1725
if (priv->auto_scroll_timeout_id != 0) {
1726
g_source_remove (priv->auto_scroll_timeout_id);
1727
priv->auto_scroll_timeout_id = 0;
1733
EggListBox *list_box;
1738
move_data_free (MoveData *data)
1740
g_slice_free (MoveData, data);
1744
drag_motion_timeout (MoveData *data)
1746
EggListBox *list_box = data->list_box;
1747
EggListBoxPrivate *priv = list_box->priv;
1749
gtk_adjustment_set_value (priv->adjustment,
1750
gtk_adjustment_get_value (priv->adjustment) +
1751
gtk_adjustment_get_step_increment (priv->adjustment) * data->move);
1756
egg_list_box_real_drag_motion (GtkWidget *widget, GdkDragContext *context,
1757
gint x, gint y, guint time_)
1759
EggListBox *list_box = EGG_LIST_BOX (widget);
1760
EggListBoxPrivate *priv = list_box->priv;
1765
/* Auto-scroll during Dnd if cursor is moving into the top/bottom portion of the
1767
if (priv->auto_scroll_timeout_id != 0)
1769
g_source_remove (priv->auto_scroll_timeout_id);
1770
priv->auto_scroll_timeout_id = 0;
1773
if (priv->adjustment == NULL)
1776
/* Part of the view triggering auto-scroll */
1780
if (y < (gtk_adjustment_get_value (priv->adjustment) + size))
1785
else if (y > ((gtk_adjustment_get_value (priv->adjustment) + gtk_adjustment_get_page_size (priv->adjustment)) - size))
1794
data = g_slice_new0 (MoveData);
1795
data->list_box = list_box;
1797
priv->auto_scroll_timeout_id =
1798
g_timeout_add_full (G_PRIORITY_DEFAULT, 150, (GSourceFunc)drag_motion_timeout,
1799
data, (GDestroyNotify) move_data_free);
1805
egg_list_box_real_activate_cursor_child (EggListBox *list_box)
1807
EggListBoxPrivate *priv = list_box->priv;
1809
egg_list_box_select_and_activate (list_box, priv->cursor_child);
1813
egg_list_box_real_toggle_cursor_child (EggListBox *list_box)
1815
EggListBoxPrivate *priv = list_box->priv;
1817
if (priv->cursor_child == NULL)
1820
if (priv->selection_mode == GTK_SELECTION_SINGLE &&
1821
priv->selected_child == priv->cursor_child)
1822
egg_list_box_update_selected (list_box, NULL);
1824
egg_list_box_select_and_activate (list_box, priv->cursor_child);
1828
egg_list_box_real_move_cursor (EggListBox *list_box,
1829
GtkMovementStep step,
1832
EggListBoxPrivate *priv = list_box->priv;
1833
GdkModifierType state;
1834
gboolean modify_selection_pressed;
1835
EggListBoxChildInfo *child;
1836
GdkModifierType modify_mod_mask;
1837
EggListBoxChildInfo *prev;
1838
EggListBoxChildInfo *next;
1840
GSequenceIter *iter;
1844
modify_selection_pressed = FALSE;
1846
if (gtk_get_current_event_state (&state))
1848
modify_mod_mask = gtk_widget_get_modifier_mask (GTK_WIDGET (list_box),
1849
GDK_MODIFIER_INTENT_MODIFY_SELECTION);
1850
if ((state & modify_mod_mask) == modify_mod_mask)
1851
modify_selection_pressed = TRUE;
1857
case GTK_MOVEMENT_BUFFER_ENDS:
1859
child = egg_list_box_get_first_visible (list_box);
1861
child = egg_list_box_get_last_visible (list_box);
1863
case GTK_MOVEMENT_DISPLAY_LINES:
1864
if (priv->cursor_child != NULL)
1866
iter = priv->cursor_child->iter;
1868
while (count < 0 && iter != NULL)
1870
iter = egg_list_box_get_previous_visible (list_box, iter);
1873
while (count > 0 && iter != NULL)
1875
iter = egg_list_box_get_next_visible (list_box, iter);
1879
if (iter != NULL && !g_sequence_iter_is_end (iter))
1880
child = g_sequence_get (iter);
1883
case GTK_MOVEMENT_PAGES:
1885
if (priv->adjustment != NULL)
1886
page_size = gtk_adjustment_get_page_increment (priv->adjustment);
1888
if (priv->cursor_child != NULL)
1890
start_y = priv->cursor_child->y;
1892
iter = priv->cursor_child->iter;
1894
child = priv->cursor_child;
1898
while (iter != NULL && !g_sequence_iter_is_begin (iter))
1900
iter = egg_list_box_get_previous_visible (list_box, iter);
1904
prev = g_sequence_get (iter);
1905
if (prev->y < start_y - page_size)
1914
while (iter != NULL && !g_sequence_iter_is_end (iter))
1916
iter = egg_list_box_get_next_visible (list_box, iter);
1917
if (g_sequence_iter_is_end (iter))
1920
next = g_sequence_get (iter);
1921
if (next->y > start_y + page_size)
1928
if (end_y != start_y && priv->adjustment != NULL)
1929
gtk_adjustment_set_value (priv->adjustment,
1930
gtk_adjustment_get_value (priv->adjustment) +
1940
gtk_widget_error_bell (GTK_WIDGET (list_box));
1944
egg_list_box_update_cursor (list_box, child);
1945
if (!modify_selection_pressed)
1946
egg_list_box_update_selected (list_box, child);