2 * Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library 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 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
20 #include "eggiconlist.h"
23 #include <gdk/gdkkeysyms.h>
24 #include <gtk/gtkbindings.h>
25 #include <gtk/gtkdnd.h>
26 #include <gtk/gtkmain.h>
27 #include <gtk/gtksignal.h>
30 #include "eggmarshalers.h"
32 #define MINIMUM_ICON_ITEM_WIDTH 100
33 #define ICON_TEXT_PADDING 3
35 #define ICON_LIST_ITEM_DATA "egg-icon-list-item-data"
37 struct _EggIconListItem
41 EggIconList *icon_list;
48 GDestroyNotify destroy_notify;
56 gint pixbuf_x, pixbuf_y;
57 gint pixbuf_height, pixbuf_width;
59 gint layout_x, layout_y;
60 gint layout_width, layout_height;
63 guint selected_before_rubberbanding : 1;
66 struct _EggIconListPrivate
70 GtkSelectionMode selection_mode;
72 GdkWindow *bin_window;
78 GtkAdjustment *hadjustment;
79 GtkAdjustment *vadjustment;
83 gboolean rubberbanding;
84 gint rubberband_x1, rubberband_y1;
85 gint rubberband_x2, rubberband_y2;
87 guint scroll_timeout_id;
88 gint scroll_value_diff;
89 gint event_last_x, event_last_y;
91 EggIconListItem *anchor_item;
92 EggIconListItem *cursor_item;
94 guint ctrl_pressed : 1;
95 guint shift_pressed : 1;
97 char *typeahead_string;
101 GtkSortType sort_order;
103 EggIconListItemCompareFunc sort_func;
105 GDestroyNotify sort_destroy_notify;
107 EggIconListItem *last_single_clicked;
114 /* Layout used to draw icon text */
142 /* Icon List Item properties */
149 static void egg_icon_list_class_init (EggIconListClass *klass);
150 static void egg_icon_list_init (EggIconList *icon_list);
152 /* GObject signals */
153 static void egg_icon_list_finalize (GObject *object);
154 static void egg_icon_list_set_property (GObject *object,
158 static void egg_icon_list_get_property (GObject *object,
164 /* GtkWidget signals */
165 static void egg_icon_list_realize (GtkWidget *widget);
166 static void egg_icon_list_unrealize (GtkWidget *widget);
167 static void egg_icon_list_map (GtkWidget *widget);
168 static void egg_icon_list_size_request (GtkWidget *widget,
169 GtkRequisition *requisition);
170 static void egg_icon_list_size_allocate (GtkWidget *widget,
171 GtkAllocation *allocation);
172 static gboolean egg_icon_list_expose (GtkWidget *widget,
173 GdkEventExpose *expose);
174 static gboolean egg_icon_list_motion (GtkWidget *widget,
175 GdkEventMotion *event);
176 static gboolean egg_icon_list_button_press (GtkWidget *widget,
177 GdkEventButton *event);
178 static gboolean egg_icon_list_button_release (GtkWidget *widget,
179 GdkEventButton *event);
180 static gboolean egg_icon_list_key_press (GtkWidget *widget,
184 /* EggIconList signals */
185 static void egg_icon_list_set_adjustments (EggIconList *icon_list,
187 GtkAdjustment *vadj);
188 static void egg_icon_list_real_select_all (EggIconList *icon_list);
189 static void egg_icon_list_real_unselect_all (EggIconList *icon_list);
190 static void egg_icon_list_real_select_cursor_item (EggIconList *icon_list);
191 static void egg_icon_list_real_toggle_cursor_item (EggIconList *icon_list);
192 static void egg_icon_list_select_all_between (EggIconList *icon_list,
193 EggIconListItem *anchor,
194 EggIconListItem *cursor,
197 /* Internal functions */
198 static void egg_icon_list_adjustment_changed (GtkAdjustment *adjustment,
199 EggIconList *icon_list);
200 static void egg_icon_list_layout (EggIconList *icon_list);
201 static void egg_icon_list_paint_item (EggIconList *icon_list,
202 EggIconListItem *item,
204 static void egg_icon_list_paint_rubberband (EggIconList *icon_list,
206 static void egg_icon_list_queue_draw_item (EggIconList *icon_list,
207 EggIconListItem *item);
208 static void egg_icon_list_queue_layout (EggIconList *icon_list);
209 static void egg_icon_list_set_cursor_item (EggIconList *icon_list,
210 EggIconListItem *item);
211 static void egg_icon_list_append_typeahead_string (EggIconList *icon_list,
212 const gchar *string);
213 static void egg_icon_list_select_first_matching_item (EggIconList *icon_list,
214 const char *pattern);
215 static void egg_icon_list_start_rubberbanding (EggIconList *icon_list,
218 static void egg_icon_list_stop_rubberbanding (EggIconList *icon_list);
219 static void egg_icon_list_sort (EggIconList *icon_list);
220 static gint egg_icon_list_sort_func (EggIconListItem *a,
222 EggIconList *icon_list);
223 static void egg_icon_list_insert_item_sorted (EggIconList *icon_list,
224 EggIconListItem *item);
225 static void egg_icon_list_validate (EggIconList *icon_list);
226 static void egg_icon_list_update_rubberband_selection (EggIconList *icon_list);
227 static gboolean egg_icon_list_item_hit_test (EggIconListItem *item,
232 static gboolean egg_icon_list_maybe_begin_dragging_items (EggIconList *icon_list,
233 GdkEventMotion *event);
234 static gboolean egg_icon_list_unselect_all_internal (EggIconList *icon_list,
236 static void egg_icon_list_calculate_item_size (EggIconList *icon_list,
237 EggIconListItem *item);
238 static void rubberbanding (gpointer data);
239 static void egg_icon_list_item_invalidate_size (EggIconListItem *item);
240 static void egg_icon_list_add_move_binding (GtkBindingSet *binding_set,
243 GtkMovementStep step,
245 static gboolean egg_icon_list_real_move_cursor (EggIconList *icon_list,
246 GtkMovementStep step,
248 static void egg_icon_list_move_cursor_up_down (EggIconList *icon_list,
250 static void egg_icon_list_move_cursor_page_up_down (EggIconList *icon_list,
252 static void egg_icon_list_move_cursor_left_right (EggIconList *icon_list,
254 static void egg_icon_list_move_cursor_start_end (EggIconList *icon_list,
256 static void egg_icon_list_scroll_to_item (EggIconList *icon_list,
257 EggIconListItem *item);
259 static GtkContainerClass *parent_class = NULL;
260 static guint icon_list_signals[LAST_SIGNAL] = { 0 };
263 egg_icon_list_item_get_type (void)
265 static GType boxed_type = 0;
268 boxed_type = g_boxed_type_register_static ("EggIconListItem",
269 (GBoxedCopyFunc) egg_icon_list_item_ref,
270 (GBoxedFreeFunc) egg_icon_list_item_unref);
276 egg_icon_list_get_type (void)
278 static GType object_type = 0;
282 static const GTypeInfo object_info =
284 sizeof (EggIconListClass),
285 NULL, /* base_init */
286 NULL, /* base_finalize */
287 (GClassInitFunc) egg_icon_list_class_init,
288 NULL, /* class_finalize */
289 NULL, /* class_data */
290 sizeof (EggIconList),
292 (GInstanceInitFunc) egg_icon_list_init
295 object_type = g_type_register_static (GTK_TYPE_CONTAINER, "EggIconList", &object_info, 0);
302 egg_icon_list_class_init (EggIconListClass *klass)
304 GObjectClass *gobject_class;
305 GtkWidgetClass *widget_class;
306 GtkBindingSet *binding_set;
308 parent_class = g_type_class_peek_parent (klass);
309 binding_set = gtk_binding_set_by_class (klass);
311 gobject_class = (GObjectClass *) klass;
312 widget_class = (GtkWidgetClass *) klass;
314 gobject_class->finalize = egg_icon_list_finalize;
315 gobject_class->set_property = egg_icon_list_set_property;
316 gobject_class->get_property = egg_icon_list_get_property;
318 widget_class->realize = egg_icon_list_realize;
319 widget_class->unrealize = egg_icon_list_unrealize;
320 widget_class->map = egg_icon_list_map;
321 widget_class->size_request = egg_icon_list_size_request;
322 widget_class->size_allocate = egg_icon_list_size_allocate;
323 widget_class->expose_event = egg_icon_list_expose;
324 widget_class->motion_notify_event = egg_icon_list_motion;
325 widget_class->button_press_event = egg_icon_list_button_press;
326 widget_class->button_release_event = egg_icon_list_button_release;
327 widget_class->key_press_event = egg_icon_list_key_press;
329 klass->set_scroll_adjustments = egg_icon_list_set_adjustments;
330 klass->select_all = egg_icon_list_real_select_all;
331 klass->unselect_all = egg_icon_list_real_unselect_all;
332 klass->select_cursor_item = egg_icon_list_real_select_cursor_item;
333 klass->toggle_cursor_item = egg_icon_list_real_toggle_cursor_item;
334 klass->move_cursor = egg_icon_list_real_move_cursor;
337 g_object_class_install_property (gobject_class,
339 g_param_spec_enum ("selection_mode",
341 _("The selection mode"),
342 GTK_TYPE_SELECTION_MODE,
343 GTK_SELECTION_SINGLE,
346 g_object_class_install_property (gobject_class,
348 g_param_spec_boolean ("sorted",
350 _("Icon list is sorted"),
353 g_object_class_install_property (gobject_class,
355 g_param_spec_enum ("sort_order",
357 _("Sort direction the icon list should use"),
360 G_PARAM_READABLE | G_PARAM_WRITABLE));
362 /* Style properties */
363 #define _ICON_LIST_TOP_MARGIN 6
364 #define _ICON_LIST_BOTTOM_MARGIN 6
365 #define _ICON_LIST_LEFT_MARGIN 6
366 #define _ICON_LIST_RIGHT_MARGIN 6
367 #define _ICON_LIST_ICON_PADDING 6
369 gtk_widget_class_install_style_property (widget_class,
370 g_param_spec_int ("icon_padding",
372 _("Number of pixels between icons"),
375 _ICON_LIST_ICON_PADDING,
377 gtk_widget_class_install_style_property (widget_class,
378 g_param_spec_int ("top_margin",
380 _("Number of pixels in top margin"),
383 _ICON_LIST_TOP_MARGIN,
385 gtk_widget_class_install_style_property (widget_class,
386 g_param_spec_int ("bottom_margin",
388 _("Number of pixels in bottom margin"),
391 _ICON_LIST_BOTTOM_MARGIN,
394 gtk_widget_class_install_style_property (widget_class,
395 g_param_spec_int ("left_margin",
397 _("Number of pixels in left margin"),
400 _ICON_LIST_LEFT_MARGIN,
402 gtk_widget_class_install_style_property (widget_class,
403 g_param_spec_int ("right_margin",
405 _("Number of pixels in right margin"),
408 _ICON_LIST_RIGHT_MARGIN,
411 gtk_widget_class_install_style_property (widget_class,
412 g_param_spec_boxed ("selection_box_color",
413 _("Selection Box Color"),
414 _("Color of the selection box"),
418 gtk_widget_class_install_style_property (widget_class,
419 g_param_spec_uchar ("selection_box_alpha",
420 _("Selection Box Alpha"),
421 _("Opacity of the selection box"),
427 widget_class->set_scroll_adjustments_signal =
428 g_signal_new ("set_scroll_adjustments",
429 G_TYPE_FROM_CLASS (gobject_class),
431 G_STRUCT_OFFSET (EggIconListClass, set_scroll_adjustments),
433 _egg_marshal_VOID__OBJECT_OBJECT,
435 GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
437 icon_list_signals[ITEM_ACTIVATED] =
438 g_signal_new ("item_activated",
439 G_TYPE_FROM_CLASS (gobject_class),
441 G_STRUCT_OFFSET (EggIconListClass, item_activated),
443 g_cclosure_marshal_VOID__BOXED,
445 EGG_TYPE_ICON_LIST_ITEM);
447 icon_list_signals[SELECTION_CHANGED] =
448 g_signal_new ("selection_changed",
449 G_TYPE_FROM_CLASS (gobject_class),
451 G_STRUCT_OFFSET (EggIconListClass, selection_changed),
453 g_cclosure_marshal_VOID__VOID,
456 icon_list_signals[ITEM_ADDED] =
457 g_signal_new ("item_added",
458 G_TYPE_FROM_CLASS (gobject_class),
460 G_STRUCT_OFFSET (EggIconListClass, item_added),
462 g_cclosure_marshal_VOID__BOXED,
463 G_TYPE_NONE, 1, EGG_TYPE_ICON_LIST_ITEM);
465 icon_list_signals[ITEM_REMOVED] =
466 g_signal_new ("item_removed",
467 G_TYPE_FROM_CLASS (gobject_class),
469 G_STRUCT_OFFSET (EggIconListClass, item_removed),
471 g_cclosure_marshal_VOID__BOXED,
472 G_TYPE_NONE, 1, EGG_TYPE_ICON_LIST_ITEM);
474 icon_list_signals[SELECT_ALL] =
475 g_signal_new ("select_all",
476 G_TYPE_FROM_CLASS (gobject_class),
477 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
478 G_STRUCT_OFFSET (EggIconListClass, select_all),
480 g_cclosure_marshal_VOID__VOID,
483 icon_list_signals[UNSELECT_ALL] =
484 g_signal_new ("unselect_all",
485 G_TYPE_FROM_CLASS (gobject_class),
486 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
487 G_STRUCT_OFFSET (EggIconListClass, unselect_all),
489 g_cclosure_marshal_VOID__VOID,
492 icon_list_signals[SELECT_CURSOR_ITEM] =
493 g_signal_new ("select_cursor_item",
494 G_TYPE_FROM_CLASS (gobject_class),
495 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
496 G_STRUCT_OFFSET (EggIconListClass, select_cursor_item),
498 g_cclosure_marshal_VOID__VOID,
501 icon_list_signals[SELECT_CURSOR_ITEM] =
502 g_signal_new ("toggle_cursor_item",
503 G_TYPE_FROM_CLASS (gobject_class),
504 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
505 G_STRUCT_OFFSET (EggIconListClass, toggle_cursor_item),
507 g_cclosure_marshal_VOID__VOID,
510 icon_list_signals[MOVE_CURSOR] =
511 g_signal_new ("move_cursor",
512 G_TYPE_FROM_CLASS (gobject_class),
513 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
514 G_STRUCT_OFFSET (EggIconListClass, move_cursor),
516 _egg_marshal_BOOLEAN__ENUM_INT,
518 GTK_TYPE_MOVEMENT_STEP,
522 gtk_binding_entry_add_signal (binding_set, GDK_a, GDK_CONTROL_MASK, "select_all", 0);
523 gtk_binding_entry_add_signal (binding_set, GDK_a, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "unselect_all", 0);
524 gtk_binding_entry_add_signal (binding_set, GDK_space, 0, "select_cursor_item", 0);
525 gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_CONTROL_MASK, "toggle_cursor_item", 0);
527 egg_icon_list_add_move_binding (binding_set, GDK_Up, 0,
528 GTK_MOVEMENT_DISPLAY_LINES, -1);
529 egg_icon_list_add_move_binding (binding_set, GDK_KP_Up, 0,
530 GTK_MOVEMENT_DISPLAY_LINES, -1);
532 egg_icon_list_add_move_binding (binding_set, GDK_Down, 0,
533 GTK_MOVEMENT_DISPLAY_LINES, 1);
534 egg_icon_list_add_move_binding (binding_set, GDK_KP_Down, 0,
535 GTK_MOVEMENT_DISPLAY_LINES, 1);
537 egg_icon_list_add_move_binding (binding_set, GDK_p, GDK_CONTROL_MASK,
538 GTK_MOVEMENT_DISPLAY_LINES, -1);
540 egg_icon_list_add_move_binding (binding_set, GDK_n, GDK_CONTROL_MASK,
541 GTK_MOVEMENT_DISPLAY_LINES, 1);
543 egg_icon_list_add_move_binding (binding_set, GDK_Home, 0,
544 GTK_MOVEMENT_BUFFER_ENDS, -1);
545 egg_icon_list_add_move_binding (binding_set, GDK_KP_Home, 0,
546 GTK_MOVEMENT_BUFFER_ENDS, -1);
548 egg_icon_list_add_move_binding (binding_set, GDK_End, 0,
549 GTK_MOVEMENT_BUFFER_ENDS, 1);
550 egg_icon_list_add_move_binding (binding_set, GDK_KP_End, 0,
551 GTK_MOVEMENT_BUFFER_ENDS, 1);
553 egg_icon_list_add_move_binding (binding_set, GDK_Page_Up, 0,
554 GTK_MOVEMENT_PAGES, -1);
555 egg_icon_list_add_move_binding (binding_set, GDK_KP_Page_Up, 0,
556 GTK_MOVEMENT_PAGES, -1);
558 egg_icon_list_add_move_binding (binding_set, GDK_Page_Down, 0,
559 GTK_MOVEMENT_PAGES, 1);
560 egg_icon_list_add_move_binding (binding_set, GDK_KP_Page_Down, 0,
561 GTK_MOVEMENT_PAGES, 1);
563 egg_icon_list_add_move_binding (binding_set, GDK_Right, 0,
564 GTK_MOVEMENT_VISUAL_POSITIONS, 1);
565 egg_icon_list_add_move_binding (binding_set, GDK_Left, 0,
566 GTK_MOVEMENT_VISUAL_POSITIONS, -1);
568 egg_icon_list_add_move_binding (binding_set, GDK_KP_Right, 0,
569 GTK_MOVEMENT_VISUAL_POSITIONS, 1);
570 egg_icon_list_add_move_binding (binding_set, GDK_KP_Left, 0,
571 GTK_MOVEMENT_VISUAL_POSITIONS, -1);
575 egg_icon_list_init (EggIconList *icon_list)
577 icon_list->priv = g_new0 (EggIconListPrivate, 1);
578 GTK_WIDGET_SET_FLAGS (icon_list, GTK_CAN_FOCUS);
580 icon_list->priv->width = 0;
581 icon_list->priv->height = 0;
582 icon_list->priv->selection_mode = GTK_SELECTION_SINGLE;
583 icon_list->priv->sort_order = GTK_SORT_ASCENDING;
584 icon_list->priv->pressed_button = -1;
585 icon_list->priv->press_start_x = -1;
586 icon_list->priv->press_start_y = -1;
587 icon_list->priv->layout = gtk_widget_create_pango_layout (GTK_WIDGET (icon_list), NULL);
588 pango_layout_set_wrap (icon_list->priv->layout, PANGO_WRAP_CHAR);
590 egg_icon_list_set_adjustments (icon_list, NULL, NULL);
594 /* GObject methods */
596 egg_icon_list_finalize (GObject *object)
598 EggIconList *icon_list;
600 icon_list = EGG_ICON_LIST (object);
602 /* FIXME: Put in destroy */
604 if (icon_list->priv->layout_idle_id != 0)
605 g_source_remove (icon_list->priv->layout_idle_id);
607 if (icon_list->priv->scroll_timeout_id != 0)
608 g_source_remove (icon_list->priv->scroll_timeout_id);
610 g_free (icon_list->priv);
612 (G_OBJECT_CLASS (parent_class)->finalize) (object);
617 egg_icon_list_set_property (GObject *object,
622 EggIconList *icon_list;
624 icon_list = EGG_ICON_LIST (object);
628 case PROP_SELECTION_MODE:
629 egg_icon_list_set_selection_mode (icon_list, g_value_get_enum (value));
632 egg_icon_list_set_sorted (icon_list, g_value_get_boolean (value));
634 case PROP_SORT_ORDER:
635 egg_icon_list_set_sort_order (icon_list, g_value_get_enum (value));
638 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
644 egg_icon_list_get_property (GObject *object,
649 EggIconList *icon_list;
651 icon_list = EGG_ICON_LIST (object);
655 case PROP_SELECTION_MODE:
656 g_value_set_enum (value, icon_list->priv->selection_mode);
659 g_value_set_boolean (value, icon_list->priv->sorted);
661 case PROP_SORT_ORDER:
662 g_value_set_enum (value, icon_list->priv->sort_order);
665 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
670 /* GtkWidget signals */
672 egg_icon_list_realize (GtkWidget *widget)
674 EggIconList *icon_list;
675 GdkWindowAttr attributes;
676 gint attributes_mask;
678 icon_list = EGG_ICON_LIST (widget);
680 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
682 /* Make the main, clipping window */
683 attributes.window_type = GDK_WINDOW_CHILD;
684 attributes.x = widget->allocation.x;
685 attributes.y = widget->allocation.y;
686 attributes.width = widget->allocation.width;
687 attributes.height = widget->allocation.height;
688 attributes.wclass = GDK_INPUT_OUTPUT;
689 attributes.visual = gtk_widget_get_visual (widget);
690 attributes.colormap = gtk_widget_get_colormap (widget);
691 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
693 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
695 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
696 &attributes, attributes_mask);
697 gdk_window_set_user_data (widget->window, widget);
699 /* Make the window for the icon list */
702 attributes.width = MAX (icon_list->priv->width, widget->allocation.width);
703 attributes.height = MAX (icon_list->priv->height, widget->allocation.height);
704 attributes.event_mask = (GDK_EXPOSURE_MASK |
706 GDK_POINTER_MOTION_MASK |
707 GDK_BUTTON_PRESS_MASK |
708 GDK_BUTTON_RELEASE_MASK |
710 GDK_KEY_RELEASE_MASK) |
711 gtk_widget_get_events (widget);
713 icon_list->priv->bin_window = gdk_window_new (widget->window,
714 &attributes, attributes_mask);
715 gdk_window_set_user_data (icon_list->priv->bin_window, widget);
717 widget->style = gtk_style_attach (widget->style, widget->window);
718 gdk_window_set_background (icon_list->priv->bin_window, &widget->style->base[widget->state]);
719 gdk_window_set_background (widget->window, &widget->style->base[widget->state]);
725 egg_icon_list_unrealize (GtkWidget *widget)
727 EggIconList *icon_list;
729 icon_list = EGG_ICON_LIST (widget);
731 gdk_window_set_user_data (icon_list->priv->bin_window, NULL);
732 gdk_window_destroy (icon_list->priv->bin_window);
733 icon_list->priv->bin_window = NULL;
735 /* GtkWidget::unrealize destroys children and widget->window */
736 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
737 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
741 egg_icon_list_map (GtkWidget *widget)
743 EggIconList *icon_list;
745 icon_list = EGG_ICON_LIST (widget);
747 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
749 gdk_window_show (icon_list->priv->bin_window);
750 gdk_window_show (widget->window);
754 egg_icon_list_size_request (GtkWidget *widget,
755 GtkRequisition *requisition)
757 EggIconList *icon_list;
759 icon_list = EGG_ICON_LIST (widget);
761 requisition->width = icon_list->priv->width;
762 requisition->height = icon_list->priv->height;
766 egg_icon_list_size_allocate (GtkWidget *widget,
767 GtkAllocation *allocation)
769 EggIconList *icon_list;
771 widget->allocation = *allocation;
773 icon_list = EGG_ICON_LIST (widget);
775 if (GTK_WIDGET_REALIZED (widget))
777 gdk_window_move_resize (widget->window,
778 allocation->x, allocation->y,
779 allocation->width, allocation->height);
780 gdk_window_resize (icon_list->priv->bin_window,
781 MAX (icon_list->priv->width, allocation->width),
782 MAX (icon_list->priv->height, allocation->height));
785 icon_list->priv->hadjustment->page_size = allocation->width;
786 icon_list->priv->hadjustment->page_increment = allocation->width * 0.9;
787 icon_list->priv->hadjustment->step_increment = allocation->width * 0.1;
788 icon_list->priv->hadjustment->lower = 0;
789 icon_list->priv->hadjustment->upper = MAX (allocation->width, icon_list->priv->width);
790 gtk_adjustment_changed (icon_list->priv->hadjustment);
792 icon_list->priv->vadjustment->page_size = allocation->height;
793 icon_list->priv->vadjustment->page_increment = allocation->height * 0.9;
794 icon_list->priv->vadjustment->step_increment = allocation->width * 0.1;
795 icon_list->priv->vadjustment->lower = 0;
796 icon_list->priv->vadjustment->upper = MAX (allocation->height, icon_list->priv->height);
797 gtk_adjustment_changed (icon_list->priv->vadjustment);
799 egg_icon_list_layout (icon_list);
803 egg_icon_list_expose (GtkWidget *widget,
804 GdkEventExpose *expose)
806 EggIconList *icon_list;
809 icon_list = EGG_ICON_LIST (widget);
811 if (expose->window != icon_list->priv->bin_window)
814 for (icons = icon_list->priv->items; icons; icons = icons->next) {
815 EggIconListItem *item = icons->data;
816 GdkRectangle item_rectangle;
818 item_rectangle.x = item->x;
819 item_rectangle.y = item->y;
820 item_rectangle.width = item->width;
821 item_rectangle.height = item->height;
823 if (gdk_region_rect_in (expose->region, &item_rectangle) == GDK_OVERLAP_RECTANGLE_OUT)
826 egg_icon_list_paint_item (icon_list, item, &expose->area);
829 if (icon_list->priv->rubberbanding)
831 GdkRectangle *rectangles;
834 gdk_region_get_rectangles (expose->region,
838 while (n_rectangles--)
839 egg_icon_list_paint_rubberband (icon_list, &rectangles[n_rectangles]);
848 scroll_timeout (gpointer data)
850 EggIconList *icon_list;
855 value = MIN (icon_list->priv->vadjustment->value +
856 icon_list->priv->scroll_value_diff,
857 icon_list->priv->vadjustment->upper -
858 icon_list->priv->vadjustment->page_size);
860 gtk_adjustment_set_value (icon_list->priv->vadjustment,
863 rubberbanding (icon_list);
869 egg_icon_list_motion (GtkWidget *widget,
870 GdkEventMotion *event)
872 EggIconList *icon_list;
875 icon_list = EGG_ICON_LIST (widget);
877 egg_icon_list_maybe_begin_dragging_items (icon_list, event);
879 if (icon_list->priv->rubberbanding)
881 rubberbanding (widget);
883 abs_y = event->y - icon_list->priv->height *
884 (icon_list->priv->vadjustment->value /
885 (icon_list->priv->vadjustment->upper -
886 icon_list->priv->vadjustment->lower));
888 if (abs_y < 0 || abs_y > widget->allocation.height)
890 if (icon_list->priv->scroll_timeout_id == 0)
891 icon_list->priv->scroll_timeout_id = g_timeout_add (30, scroll_timeout, icon_list);
894 icon_list->priv->scroll_value_diff = abs_y;
896 icon_list->priv->scroll_value_diff = abs_y - widget->allocation.height;
898 icon_list->priv->event_last_x = event->x;
899 icon_list->priv->event_last_y = event->y;
901 else if (icon_list->priv->scroll_timeout_id != 0)
903 g_source_remove (icon_list->priv->scroll_timeout_id);
905 icon_list->priv->scroll_timeout_id = 0;
913 egg_icon_list_button_press (GtkWidget *widget,
914 GdkEventButton *event)
916 EggIconList *icon_list;
917 EggIconListItem *item;
918 gboolean dirty = FALSE;
920 icon_list = EGG_ICON_LIST (widget);
922 if (event->window != icon_list->priv->bin_window)
925 if (!GTK_WIDGET_HAS_FOCUS (widget))
926 gtk_widget_grab_focus (widget);
928 if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
931 item = egg_icon_list_get_item_at_pos (icon_list,
936 if (icon_list->priv->selection_mode == GTK_SELECTION_NONE)
938 egg_icon_list_set_cursor_item (icon_list, item);
940 else if (icon_list->priv->selection_mode == GTK_SELECTION_MULTIPLE &&
941 (event->state & GDK_SHIFT_MASK))
943 egg_icon_list_unselect_all_internal (icon_list, FALSE);
945 egg_icon_list_set_cursor_item (icon_list, item);
946 if (!icon_list->priv->anchor_item)
947 icon_list->priv->anchor_item = item;
949 egg_icon_list_select_all_between (icon_list,
950 icon_list->priv->anchor_item,
956 if (icon_list->priv->selection_mode == GTK_SELECTION_MULTIPLE &&
957 (event->state & GDK_CONTROL_MASK))
959 item->selected = !item->selected;
960 egg_icon_list_queue_draw_item (icon_list, item);
967 egg_icon_list_unselect_all_internal (icon_list, FALSE);
969 item->selected = TRUE;
970 egg_icon_list_queue_draw_item (icon_list, item);
974 egg_icon_list_set_cursor_item (icon_list, item);
975 icon_list->priv->anchor_item = item;
978 /* Save press to possibly begin a drag */
979 if (icon_list->priv->pressed_button < 0)
981 icon_list->priv->pressed_button = event->button;
982 icon_list->priv->press_start_x = event->x;
983 icon_list->priv->press_start_y = event->y;
986 if (!icon_list->priv->last_single_clicked)
987 icon_list->priv->last_single_clicked = item;
991 if (icon_list->priv->selection_mode != GTK_SELECTION_BROWSE &&
992 !(event->state & GDK_CONTROL_MASK))
994 dirty = egg_icon_list_unselect_all_internal (icon_list, FALSE);
997 if (icon_list->priv->selection_mode == GTK_SELECTION_MULTIPLE)
998 egg_icon_list_start_rubberbanding (icon_list, event->x, event->y);
1003 if (event->button == 1 && event->type == GDK_2BUTTON_PRESS)
1005 item = egg_icon_list_get_item_at_pos (icon_list,
1006 event->x, event->y);
1008 if (item && item == icon_list->priv->last_single_clicked)
1010 egg_icon_list_item_activated (icon_list, item);
1013 icon_list->priv->last_single_clicked = NULL;
1017 g_signal_emit (icon_list, icon_list_signals[SELECTION_CHANGED], 0);
1023 egg_icon_list_button_release (GtkWidget *widget,
1024 GdkEventButton *event)
1026 EggIconList *icon_list;
1028 icon_list = EGG_ICON_LIST (widget);
1030 if (icon_list->priv->pressed_button == event->button)
1031 icon_list->priv->pressed_button = -1;
1033 egg_icon_list_stop_rubberbanding (icon_list);
1035 if (icon_list->priv->scroll_timeout_id != 0)
1037 g_source_remove (icon_list->priv->scroll_timeout_id);
1038 icon_list->priv->scroll_timeout_id = 0;
1046 egg_icon_list_key_press (GtkWidget *widget,
1049 if ((* GTK_WIDGET_CLASS (parent_class)->key_press_event) (widget, event))
1054 if ((event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) == 0)
1055 egg_icon_list_append_typeahead_string (EGG_ICON_LIST (widget), event->string);
1061 egg_icon_list_select_first_matching_item (EggIconList *icon_list,
1062 const char *pattern)
1066 if (pattern == NULL)
1069 for (items = icon_list->priv->items; items; items = items->next)
1071 EggIconListItem *item = items->data;
1073 if (strncmp (pattern, item->label, strlen (pattern)) == 0)
1075 egg_icon_list_select_item (icon_list, item);
1082 rubberbanding (gpointer data)
1084 EggIconList *icon_list;
1086 GdkRectangle old_area;
1087 GdkRectangle new_area;
1088 GdkRectangle common;
1089 GdkRegion *invalid_region;
1091 icon_list = EGG_ICON_LIST (data);
1093 gdk_window_get_pointer (icon_list->priv->bin_window, &x, &y, NULL);
1098 old_area.x = MIN (icon_list->priv->rubberband_x1,
1099 icon_list->priv->rubberband_x2);
1100 old_area.y = MIN (icon_list->priv->rubberband_y1,
1101 icon_list->priv->rubberband_y2);
1102 old_area.width = ABS (icon_list->priv->rubberband_x2 -
1103 icon_list->priv->rubberband_x1) + 1;
1104 old_area.height = ABS (icon_list->priv->rubberband_y2 -
1105 icon_list->priv->rubberband_y1) + 1;
1107 new_area.x = MIN (icon_list->priv->rubberband_x1, x);
1108 new_area.y = MIN (icon_list->priv->rubberband_y1, y);
1109 new_area.width = ABS (x - icon_list->priv->rubberband_x1) + 1;
1110 new_area.height = ABS (y - icon_list->priv->rubberband_y1) + 1;
1112 invalid_region = gdk_region_rectangle (&old_area);
1113 gdk_region_union_with_rect (invalid_region, &new_area);
1115 gdk_rectangle_intersect (&old_area, &new_area, &common);
1116 if (common.width > 2 && common.height > 2)
1118 GdkRegion *common_region;
1120 /* make sure the border is invalidated */
1126 common_region = gdk_region_rectangle (&common);
1128 gdk_region_subtract (invalid_region, common_region);
1129 gdk_region_destroy (common_region);
1132 gdk_window_invalidate_region (icon_list->priv->bin_window, invalid_region, TRUE);
1134 gdk_region_destroy (invalid_region);
1136 icon_list->priv->rubberband_x2 = x;
1137 icon_list->priv->rubberband_y2 = y;
1139 egg_icon_list_update_rubberband_selection (icon_list);
1143 egg_icon_list_start_rubberbanding (EggIconList *icon_list,
1149 g_assert (!icon_list->priv->rubberbanding);
1151 for (items = icon_list->priv->items; items; items = items->next)
1153 EggIconListItem *item = items->data;
1155 item->selected_before_rubberbanding = item->selected;
1158 icon_list->priv->rubberband_x1 = x;
1159 icon_list->priv->rubberband_y1 = y;
1160 icon_list->priv->rubberband_x2 = x;
1161 icon_list->priv->rubberband_y2 = y;
1163 icon_list->priv->rubberbanding = TRUE;
1165 gtk_grab_add (GTK_WIDGET (icon_list));
1169 egg_icon_list_stop_rubberbanding (EggIconList *icon_list)
1171 if (!icon_list->priv->rubberbanding)
1174 icon_list->priv->rubberbanding = FALSE;
1176 gtk_grab_remove (GTK_WIDGET (icon_list));
1178 gtk_widget_queue_draw (GTK_WIDGET (icon_list));
1182 egg_icon_list_sort_func (EggIconListItem *a,
1184 EggIconList *icon_list)
1188 result = (* icon_list->priv->sort_func) (icon_list, a, b,
1189 icon_list->priv->sort_data);
1191 if (icon_list->priv->sort_order == GTK_SORT_DESCENDING)
1198 egg_icon_list_insert_item_sorted (EggIconList *icon_list,
1199 EggIconListItem *item)
1202 GList *tmp_list = icon_list->priv->items;
1205 egg_icon_list_validate (icon_list);
1207 list = g_list_alloc ();
1209 item->icon_list = icon_list;
1211 egg_icon_list_item_ref (item);
1213 if (!icon_list->priv->items)
1215 icon_list->priv->items = list;
1216 icon_list->priv->last_item = list;
1217 icon_list->priv->item_count += 1;
1219 egg_icon_list_validate (icon_list);
1224 cmp = egg_icon_list_sort_func (item, tmp_list->data, icon_list);
1226 while ((tmp_list->next) && (cmp > 0))
1228 tmp_list = tmp_list->next;
1229 cmp = egg_icon_list_sort_func (item, tmp_list->data, icon_list);
1232 if ((!tmp_list->next) && (cmp > 0))
1234 tmp_list->next = list;
1235 list->prev = tmp_list;
1236 icon_list->priv->last_item = list;
1237 icon_list->priv->item_count += 1;
1238 egg_icon_list_validate (icon_list);
1245 tmp_list->prev->next = list;
1246 list->prev = tmp_list->prev;
1249 list->next = tmp_list;
1250 tmp_list->prev = list;
1252 if (tmp_list == icon_list->priv->items)
1253 icon_list->priv->items = list;
1255 icon_list->priv->item_count += 1;
1256 egg_icon_list_validate (icon_list);
1258 egg_icon_list_queue_layout (icon_list);
1263 egg_icon_list_sort (EggIconList *icon_list)
1265 egg_icon_list_validate (icon_list);
1267 /* FIXME: We can optimize this */
1268 icon_list->priv->items = g_list_sort_with_data (icon_list->priv->items,
1269 (GCompareDataFunc)egg_icon_list_sort_func,
1271 icon_list->priv->last_item = g_list_last (icon_list->priv->items);
1273 egg_icon_list_validate (icon_list);
1274 egg_icon_list_queue_layout (icon_list);
1279 egg_icon_list_validate (EggIconList *icon_list)
1285 for (list = icon_list->priv->items; list; list = list->next)
1287 EggIconListItem *item = list->data;
1289 g_print ("%s\n", egg_icon_list_item_get_label (item));
1294 g_assert (g_list_length (icon_list->priv->items) == icon_list->priv->item_count);
1295 g_assert (g_list_last (icon_list->priv->items) == icon_list->priv->last_item);
1296 g_assert (g_list_first (icon_list->priv->last_item) == icon_list->priv->items);
1300 egg_icon_list_update_rubberband_selection (EggIconList *icon_list)
1303 gint x, y, width, height;
1304 gboolean dirty = FALSE;
1306 x = MIN (icon_list->priv->rubberband_x1,
1307 icon_list->priv->rubberband_x2);
1308 y = MIN (icon_list->priv->rubberband_y1,
1309 icon_list->priv->rubberband_y2);
1310 width = ABS (icon_list->priv->rubberband_x1 -
1311 icon_list->priv->rubberband_x2);
1312 height = ABS (icon_list->priv->rubberband_y1 -
1313 icon_list->priv->rubberband_y2);
1315 for (items = icon_list->priv->items; items; items = items->next)
1317 EggIconListItem *item = items->data;
1321 is_in = egg_icon_list_item_hit_test (item, x, y, width, height);
1323 selected = is_in ^ item->selected_before_rubberbanding;
1325 if (item->selected != selected)
1327 item->selected = selected;
1329 egg_icon_list_queue_draw_item (icon_list, item);
1334 g_signal_emit (icon_list, icon_list_signals[SELECTION_CHANGED], 0);
1338 egg_icon_list_item_hit_test (EggIconListItem *item,
1344 /* First try the pixbuf */
1345 if (MIN (x + width, item->pixbuf_x + item->pixbuf_width) - MAX (x, item->pixbuf_x) > 0 &&
1346 MIN (y + height, item->pixbuf_y + item->pixbuf_height) - MAX (y, item->pixbuf_y) > 0)
1349 /* Then try the text */
1350 if (MIN (x + width, item->layout_x + item->layout_width) - MAX (x, item->layout_x) > 0 &&
1351 MIN (y + height, item->layout_y + item->layout_height) - MAX (y, item->layout_y) > 0)
1358 egg_icon_list_maybe_begin_dragging_items (EggIconList *icon_list,
1359 GdkEventMotion *event)
1361 gboolean retval = FALSE;
1363 if (icon_list->priv->pressed_button < 0)
1366 if (!gtk_drag_check_threshold (GTK_WIDGET (icon_list),
1367 icon_list->priv->press_start_x,
1368 icon_list->priv->press_start_y,
1369 event->x, event->y))
1372 button = icon_list->priv->pressed_button;
1373 icon_list->priv->pressed_button = -1;
1376 static GtkTargetEntry row_targets[] = {
1377 { "EGG_ICON_LIST_ITEMS", GTK_TARGET_SAME_APP, 0 }
1379 GtkTargetList *target_list;
1380 GdkDragContext *context;
1381 EggIconListItem *item;
1385 target_list = gtk_target_list_new (row_targets, G_N_ELEMENTS (row_targets));
1387 context = gtk_drag_begin (GTK_WIDGET (icon_list),
1388 target_list, GDK_ACTION_MOVE,
1392 item = egg_icon_list_get_item_at_pos (icon_list,
1393 icon_list->priv->press_start_x,
1394 icon_list->priv->press_start_y);
1395 g_assert (item != NULL);
1396 gtk_drag_set_icon_pixbuf (context, egg_icon_list_item_get_icon (item),
1398 event->y - item->y);
1406 egg_icon_list_unselect_all_internal (EggIconList *icon_list,
1409 gboolean dirty = FALSE;
1412 for (items = icon_list->priv->items; items; items = items->next)
1414 EggIconListItem *item = items->data;
1418 item->selected = FALSE;
1420 egg_icon_list_queue_draw_item (icon_list, item);
1425 g_signal_emit (icon_list, icon_list_signals[SELECTION_CHANGED], 0);
1431 /* EggIconList signals */
1433 egg_icon_list_set_adjustments (EggIconList *icon_list,
1434 GtkAdjustment *hadj,
1435 GtkAdjustment *vadj)
1437 gboolean need_adjust = FALSE;
1440 g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
1442 hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
1444 g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
1446 vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
1448 if (icon_list->priv->hadjustment && (icon_list->priv->hadjustment != hadj))
1450 g_signal_handlers_disconnect_matched (icon_list->priv->hadjustment, G_SIGNAL_MATCH_DATA,
1451 0, 0, NULL, NULL, icon_list);
1452 g_object_unref (icon_list->priv->hadjustment);
1455 if (icon_list->priv->vadjustment && (icon_list->priv->vadjustment != vadj))
1457 g_signal_handlers_disconnect_matched (icon_list->priv->vadjustment, G_SIGNAL_MATCH_DATA,
1458 0, 0, NULL, NULL, icon_list);
1459 g_object_unref (icon_list->priv->vadjustment);
1462 if (icon_list->priv->hadjustment != hadj)
1464 icon_list->priv->hadjustment = hadj;
1465 g_object_ref (icon_list->priv->hadjustment);
1466 gtk_object_sink (GTK_OBJECT (icon_list->priv->hadjustment));
1468 g_signal_connect (icon_list->priv->hadjustment, "value_changed",
1469 G_CALLBACK (egg_icon_list_adjustment_changed),
1474 if (icon_list->priv->vadjustment != vadj)
1476 icon_list->priv->vadjustment = vadj;
1477 g_object_ref (icon_list->priv->vadjustment);
1478 gtk_object_sink (GTK_OBJECT (icon_list->priv->vadjustment));
1480 g_signal_connect (icon_list->priv->vadjustment, "value_changed",
1481 G_CALLBACK (egg_icon_list_adjustment_changed),
1487 egg_icon_list_adjustment_changed (NULL, icon_list);
1491 egg_icon_list_real_select_all (EggIconList *icon_list)
1493 if (icon_list->priv->selection_mode != GTK_SELECTION_MULTIPLE)
1496 egg_icon_list_select_all (icon_list);
1500 egg_icon_list_real_unselect_all (EggIconList *icon_list)
1502 if (icon_list->priv->selection_mode == GTK_SELECTION_BROWSE)
1505 egg_icon_list_unselect_all (icon_list);
1509 egg_icon_list_real_select_cursor_item (EggIconList *icon_list)
1511 egg_icon_list_unselect_all (icon_list);
1513 if (icon_list->priv->cursor_item != NULL)
1514 egg_icon_list_select_item (icon_list, icon_list->priv->cursor_item);
1518 egg_icon_list_real_toggle_cursor_item (EggIconList *icon_list)
1520 if (icon_list->priv->selection_mode == GTK_SELECTION_NONE)
1523 /* FIXME: Use another function here */
1524 if (icon_list->priv->cursor_item != NULL)
1526 if (icon_list->priv->selection_mode == GTK_SELECTION_BROWSE)
1527 icon_list->priv->cursor_item->selected = TRUE;
1529 icon_list->priv->cursor_item->selected = !icon_list->priv->cursor_item->selected;
1531 egg_icon_list_queue_draw_item (icon_list, icon_list->priv->cursor_item);
1535 /* Internal functions */
1537 egg_icon_list_adjustment_changed (GtkAdjustment *adjustment,
1538 EggIconList *icon_list)
1540 if (GTK_WIDGET_REALIZED (icon_list))
1542 gdk_window_move (icon_list->priv->bin_window,
1543 - icon_list->priv->hadjustment->value,
1544 - icon_list->priv->vadjustment->value);
1545 gdk_window_process_updates (icon_list->priv->bin_window, TRUE);
1550 egg_icon_list_layout_single_row (EggIconList *icon_list, GList *first_item, gint *y, gint *maximum_width, gint row)
1552 gint x, current_width, max_height, max_pixbuf_height;
1553 GList *items, *last_item;
1555 gint left_margin, right_margin;
1556 gint maximum_layout_width;
1558 gboolean rtl = gtk_widget_get_direction (GTK_WIDGET (icon_list)) == GTK_TEXT_DIR_RTL;
1563 max_pixbuf_height = 0;
1567 gtk_widget_style_get (GTK_WIDGET (icon_list),
1568 "icon_padding", &icon_padding,
1569 "left_margin", &left_margin,
1570 "right_margin", &right_margin,
1574 current_width += left_margin + right_margin;
1579 EggIconListItem *item = items->data;
1581 egg_icon_list_calculate_item_size (icon_list, item);
1583 current_width += MAX (item->width, MINIMUM_ICON_ITEM_WIDTH);
1585 /* Don't add padding to the first or last icon */
1587 if (current_width > GTK_WIDGET (icon_list)->allocation.width &&
1588 items != first_item)
1591 maximum_layout_width = MAX (item->pixbuf_width, MINIMUM_ICON_ITEM_WIDTH);
1594 item->x = rtl ? GTK_WIDGET (icon_list)->allocation.width - item->width - x : x;
1595 if (item->width < MINIMUM_ICON_ITEM_WIDTH) {
1597 item->x -= (MINIMUM_ICON_ITEM_WIDTH - item->width) / 2;
1599 item->x += (MINIMUM_ICON_ITEM_WIDTH - item->width) / 2;
1600 x += (MINIMUM_ICON_ITEM_WIDTH - item->width);
1603 item->pixbuf_x = item->x + (item->width - item->pixbuf_width) / 2;
1604 item->layout_x = item->x + (item->width - item->layout_width) / 2;
1608 max_height = MAX (max_height, item->height);
1609 max_pixbuf_height = MAX (max_pixbuf_height, item->pixbuf_height);
1611 if (current_width > *maximum_width)
1612 *maximum_width = current_width;
1618 items = items->next;
1623 *y += max_height + icon_padding;
1625 /* Now go through the row again and align the icons */
1626 for (items = first_item; items != last_item; items = items->next)
1628 EggIconListItem *item = items->data;
1630 item->pixbuf_y = item->y + (max_pixbuf_height - item->pixbuf_height);
1631 item->layout_y = item->pixbuf_y + item->pixbuf_height + ICON_TEXT_PADDING;
1632 /* Update the bounding box */
1633 item->y = item->pixbuf_y;
1635 /* We may want to readjust the new y coordinate. */
1636 if (item->y + item->height > *y)
1637 *y = item->y + item->height;
1640 item->col = col - 1 - item->col;
1647 egg_icon_list_set_adjustment_upper (GtkAdjustment *adj,
1650 if (upper != adj->upper)
1652 gdouble min = MAX (0.0, upper - adj->page_size);
1653 gboolean value_changed = FALSE;
1657 if (adj->value > min)
1660 value_changed = TRUE;
1663 gtk_adjustment_changed (adj);
1666 gtk_adjustment_value_changed (adj);
1671 egg_icon_list_layout (EggIconList *icon_list)
1673 gint y = 0, maximum_width = 0;
1676 gint top_margin, bottom_margin;
1679 widget = GTK_WIDGET (icon_list);
1680 icons = icon_list->priv->items;
1682 gtk_widget_style_get (widget,
1683 "top_margin", &top_margin,
1684 "bottom_margin", &bottom_margin,
1691 icons = egg_icon_list_layout_single_row (icon_list, icons, &y, &maximum_width, row);
1694 while (icons != NULL);
1696 if (maximum_width != icon_list->priv->width)
1698 icon_list->priv->width = maximum_width;
1702 if (y != icon_list->priv->height)
1704 icon_list->priv->height = y;
1707 egg_icon_list_set_adjustment_upper (icon_list->priv->hadjustment, icon_list->priv->width);
1708 egg_icon_list_set_adjustment_upper (icon_list->priv->vadjustment, icon_list->priv->height);
1710 if (GTK_WIDGET_REALIZED (icon_list))
1712 gdk_window_resize (icon_list->priv->bin_window,
1713 MAX (icon_list->priv->width, widget->allocation.width),
1714 MAX (icon_list->priv->height, widget->allocation.height));
1717 if (icon_list->priv->layout_idle_id != 0)
1719 g_source_remove (icon_list->priv->layout_idle_id);
1720 icon_list->priv->layout_idle_id = 0;
1723 gtk_widget_queue_draw (GTK_WIDGET (icon_list));
1726 /* Creates or updates the pango layout and calculates the size */
1728 egg_icon_list_calculate_item_size (EggIconList *icon_list, EggIconListItem *item)
1730 int layout_width, layout_height;
1731 int maximum_layout_width;
1733 if (item->width != -1 && item->width != -1)
1736 item->pixbuf_width = gdk_pixbuf_get_width (item->icon);
1737 item->pixbuf_height = gdk_pixbuf_get_height (item->icon);
1739 maximum_layout_width = MAX (item->pixbuf_width, MINIMUM_ICON_ITEM_WIDTH);
1741 pango_layout_set_text (icon_list->priv->layout, item->label, -1);
1743 pango_layout_set_alignment (icon_list->priv->layout, PANGO_ALIGN_CENTER);
1744 pango_layout_set_width (icon_list->priv->layout, maximum_layout_width * PANGO_SCALE);
1746 pango_layout_get_pixel_size (icon_list->priv->layout, &layout_width, &layout_height);
1748 item->width = MAX ((layout_width + 2 * ICON_TEXT_PADDING), item->pixbuf_width);
1749 item->height = layout_height + 2 * ICON_TEXT_PADDING + item->pixbuf_height;
1750 item->layout_width = layout_width;
1751 item->layout_height = layout_height;
1755 egg_icon_list_item_invalidate_size (EggIconListItem *item)
1762 create_colorized_pixbuf (GdkPixbuf *src, GdkColor *new_color)
1765 gint width, height, has_alpha, src_row_stride, dst_row_stride;
1766 gint red_value, green_value, blue_value;
1767 guchar *target_pixels;
1768 guchar *original_pixels;
1773 red_value = new_color->red / 255.0;
1774 green_value = new_color->green / 255.0;
1775 blue_value = new_color->blue / 255.0;
1777 dest = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (src),
1778 gdk_pixbuf_get_has_alpha (src),
1779 gdk_pixbuf_get_bits_per_sample (src),
1780 gdk_pixbuf_get_width (src),
1781 gdk_pixbuf_get_height (src));
1783 has_alpha = gdk_pixbuf_get_has_alpha (src);
1784 width = gdk_pixbuf_get_width (src);
1785 height = gdk_pixbuf_get_height (src);
1786 src_row_stride = gdk_pixbuf_get_rowstride (src);
1787 dst_row_stride = gdk_pixbuf_get_rowstride (dest);
1788 target_pixels = gdk_pixbuf_get_pixels (dest);
1789 original_pixels = gdk_pixbuf_get_pixels (src);
1791 for (i = 0; i < height; i++) {
1792 pixdest = target_pixels + i*dst_row_stride;
1793 pixsrc = original_pixels + i*src_row_stride;
1794 for (j = 0; j < width; j++) {
1795 *pixdest++ = (*pixsrc++ * red_value) >> 8;
1796 *pixdest++ = (*pixsrc++ * green_value) >> 8;
1797 *pixdest++ = (*pixsrc++ * blue_value) >> 8;
1799 *pixdest++ = *pixsrc++;
1807 egg_icon_list_paint_item (EggIconList *icon_list,
1808 EggIconListItem *item,
1814 if (GTK_WIDGET_HAS_FOCUS (icon_list))
1815 state = GTK_STATE_SELECTED;
1817 state = GTK_STATE_ACTIVE;
1820 pixbuf = create_colorized_pixbuf (item->icon,
1821 >K_WIDGET (icon_list)->style->base[state]);
1823 pixbuf = g_object_ref (item->icon);
1825 gdk_draw_pixbuf (icon_list->priv->bin_window, NULL, pixbuf,
1827 item->pixbuf_x, item->pixbuf_y,
1828 item->pixbuf_width, item->pixbuf_height,
1829 GDK_RGB_DITHER_NORMAL,
1830 item->pixbuf_width, item->pixbuf_height);
1831 g_object_unref (pixbuf);
1835 gdk_draw_rectangle (icon_list->priv->bin_window,
1836 GTK_WIDGET (icon_list)->style->base_gc[state],
1838 item->layout_x - ICON_TEXT_PADDING,
1839 item->layout_y - ICON_TEXT_PADDING,
1840 item->layout_width + 2 * ICON_TEXT_PADDING,
1841 item->layout_height + 2 * ICON_TEXT_PADDING);
1844 pango_layout_set_text (icon_list->priv->layout, item->label, -1);
1845 gdk_draw_layout (icon_list->priv->bin_window,
1846 GTK_WIDGET (icon_list)->style->text_gc[item->selected ? state : GTK_STATE_NORMAL],
1847 item->layout_x - ((item->width - item->layout_width) / 2) - (MAX (item->pixbuf_width, MINIMUM_ICON_ITEM_WIDTH) - item->width) / 2,
1849 icon_list->priv->layout);
1851 if (GTK_WIDGET_HAS_FOCUS (icon_list) &&
1852 item == icon_list->priv->cursor_item)
1853 gtk_paint_focus (GTK_WIDGET (icon_list)->style,
1854 icon_list->priv->bin_window,
1855 item->selected ? GTK_STATE_SELECTED : GTK_STATE_NORMAL,
1857 GTK_WIDGET (icon_list),
1859 item->layout_x - ICON_TEXT_PADDING,
1860 item->layout_y - ICON_TEXT_PADDING,
1861 item->layout_width + 2 * ICON_TEXT_PADDING,
1862 item->layout_height + 2 * ICON_TEXT_PADDING);
1866 egg_gdk_color_to_rgb (const GdkColor *color)
1869 result = (0xff0000 | (color->red & 0xff00));
1871 result |= ((color->green & 0xff00) | (color->blue >> 8));
1876 egg_icon_list_paint_rubberband (EggIconList *icon_list,
1882 GdkRectangle rubber_rect;
1883 GdkColor *fill_color_gdk;
1885 guchar fill_color_alpha;
1887 rubber_rect.x = MIN (icon_list->priv->rubberband_x1, icon_list->priv->rubberband_x2);
1888 rubber_rect.y = MIN (icon_list->priv->rubberband_y1, icon_list->priv->rubberband_y2);
1889 rubber_rect.width = ABS (icon_list->priv->rubberband_x1 - icon_list->priv->rubberband_x2) + 1;
1890 rubber_rect.height = ABS (icon_list->priv->rubberband_y1 - icon_list->priv->rubberband_y2) + 1;
1892 if (!gdk_rectangle_intersect (&rubber_rect, area, &rect))
1895 gtk_widget_style_get (GTK_WIDGET (icon_list),
1896 "selection_box_color", &fill_color_gdk,
1897 "selection_box_alpha", &fill_color_alpha,
1900 if (!fill_color_gdk) {
1901 fill_color_gdk = gdk_color_copy (>K_WIDGET (icon_list)->style->base[GTK_STATE_SELECTED]);
1904 fill_color = egg_gdk_color_to_rgb (fill_color_gdk) << 8 | fill_color_alpha;
1906 pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, rect.width, rect.height);
1907 gdk_pixbuf_fill (pixbuf, fill_color);
1909 gdk_draw_pixbuf (icon_list->priv->bin_window, NULL, pixbuf,
1912 rect.width, rect.height,
1913 GDK_RGB_DITHER_NONE,
1915 g_object_unref (pixbuf);
1916 gc = gdk_gc_new (icon_list->priv->bin_window);
1917 gdk_gc_set_rgb_fg_color (gc, fill_color_gdk);
1918 gdk_gc_set_clip_rectangle (gc, &rect);
1919 gdk_draw_rectangle (icon_list->priv->bin_window,
1921 rubber_rect.x, rubber_rect.y,
1922 rubber_rect.width - 1, rubber_rect.height - 1);
1923 gdk_color_free (fill_color_gdk);
1924 g_object_unref (gc);
1928 egg_icon_list_queue_draw_item (EggIconList *icon_list,
1929 EggIconListItem *item)
1935 rect.width = item->width;
1936 rect.height = item->height;
1938 gdk_window_invalidate_rect (icon_list->priv->bin_window, &rect, TRUE);
1942 layout_callback (gpointer user_data)
1944 EggIconList *icon_list;
1946 icon_list = EGG_ICON_LIST (user_data);
1948 icon_list->priv->layout_idle_id = 0;
1950 egg_icon_list_layout (icon_list);
1956 egg_icon_list_queue_layout (EggIconList *icon_list)
1958 if (icon_list->priv->layout_idle_id != 0)
1961 icon_list->priv->layout_idle_id = g_idle_add (layout_callback, icon_list);
1965 egg_icon_list_set_cursor_item (EggIconList *icon_list,
1966 EggIconListItem *item)
1968 if (icon_list->priv->cursor_item == item)
1971 if (icon_list->priv->cursor_item != NULL)
1972 egg_icon_list_queue_draw_item (icon_list, icon_list->priv->cursor_item);
1974 icon_list->priv->cursor_item = item;
1975 egg_icon_list_queue_draw_item (icon_list, item);
1979 egg_icon_list_append_typeahead_string (EggIconList *icon_list,
1980 const gchar *string)
1983 char *typeahead_string;
1985 if (strlen (string) == 0)
1988 for (i = 0; i < strlen (string); i++)
1990 if (!g_ascii_isprint (string[i]))
1994 typeahead_string = g_strconcat (icon_list->priv->typeahead_string ?
1995 icon_list->priv->typeahead_string : "",
1997 g_free (icon_list->priv->typeahead_string);
1998 icon_list->priv->typeahead_string = typeahead_string;
2000 egg_icon_list_select_first_matching_item (icon_list,
2001 icon_list->priv->typeahead_string);
2003 g_print ("wooo: \"%s\"\n", typeahead_string);
2008 egg_icon_list_new (void)
2010 EggIconList *icon_list;
2012 icon_list = g_object_new (EGG_TYPE_ICON_LIST, NULL);
2014 return GTK_WIDGET (icon_list);
2018 egg_icon_list_item_new (GdkPixbuf *icon,
2021 EggIconListItem *item;
2023 item = g_new0 (EggIconListItem, 1);
2025 item->ref_count = 1;
2028 item->label = g_strdup (label);
2029 item->icon = g_object_ref (icon);
2035 egg_icon_list_item_ref (EggIconListItem *item)
2037 g_return_if_fail (item != NULL);
2039 item->ref_count += 1;
2043 egg_icon_list_item_unref (EggIconListItem *item)
2045 g_return_if_fail (item != NULL);
2047 item->ref_count -= 1;
2049 if (item->ref_count == 0)
2051 if (item->destroy_notify)
2052 item->destroy_notify (item->user_data);
2054 g_free (item->label);
2055 g_object_unref (item->icon);
2062 egg_icon_list_item_set_data (EggIconListItem *item,
2065 egg_icon_list_item_set_data_full (item, data, NULL);
2069 egg_icon_list_item_set_data_full (EggIconListItem *item,
2071 GDestroyNotify destroy_notify)
2073 g_return_if_fail (item != NULL);
2075 if (item->destroy_notify)
2076 item->destroy_notify (item->user_data);
2078 item->destroy_notify = destroy_notify;
2079 item->user_data = data;
2083 egg_icon_list_item_get_data (EggIconListItem *item)
2085 g_return_val_if_fail (item != NULL, NULL);
2087 return item->user_data;
2091 egg_icon_list_item_set_label (EggIconListItem *item,
2094 g_return_if_fail (item != NULL);
2095 g_return_if_fail (label != NULL);
2097 if (strcmp (item->label, label) == 0)
2100 g_free (item->label);
2101 item->label = g_strdup (label);
2102 egg_icon_list_item_invalidate_size (item);
2104 egg_icon_list_queue_layout (item->icon_list);
2106 g_object_notify (G_OBJECT (item), "label");
2109 G_CONST_RETURN gchar *
2110 egg_icon_list_item_get_label (EggIconListItem *item)
2112 g_return_val_if_fail (item != NULL, NULL);
2118 egg_icon_list_item_set_icon (EggIconListItem *item,
2121 g_return_if_fail (item != NULL);
2123 if (icon == item->icon)
2126 g_object_unref (item->icon);
2127 item->icon = g_object_ref (icon);
2129 egg_icon_list_item_invalidate_size (item);
2131 egg_icon_list_queue_layout (item->icon_list);
2135 egg_icon_list_item_get_icon (EggIconListItem *item)
2137 g_return_val_if_fail (item != NULL, NULL);
2143 egg_icon_list_append_item (EggIconList *icon_list,
2144 EggIconListItem *item)
2148 g_return_if_fail (EGG_IS_ICON_LIST (icon_list));
2149 g_return_if_fail (item != NULL);
2150 g_return_if_fail (item->icon_list == NULL);
2152 if (icon_list->priv->sorted)
2154 egg_icon_list_insert_item_sorted (icon_list, item);
2158 egg_icon_list_validate (icon_list);
2160 list = g_list_alloc ();
2162 item->icon_list = icon_list;
2164 egg_icon_list_item_ref (item);
2166 if (icon_list->priv->last_item)
2168 icon_list->priv->last_item->next = list;
2169 list->prev = icon_list->priv->last_item;
2172 icon_list->priv->items = list;
2174 icon_list->priv->last_item = list;
2175 icon_list->priv->item_count += 1;
2177 egg_icon_list_validate (icon_list);
2179 g_signal_emit (icon_list, icon_list_signals[ITEM_ADDED], 0, item);
2181 egg_icon_list_queue_layout (icon_list);
2185 egg_icon_list_prepend_item (EggIconList *icon_list,
2186 EggIconListItem *item)
2190 g_return_if_fail (EGG_IS_ICON_LIST (icon_list));
2191 g_return_if_fail (item != NULL);
2192 g_return_if_fail (item->icon_list == NULL);
2194 egg_icon_list_validate (icon_list);
2196 list = g_list_alloc ();
2198 item->icon_list = icon_list;
2200 egg_icon_list_item_ref (item);
2202 if (icon_list->priv->last_item == NULL)
2203 icon_list->priv->last_item = list;
2205 if (icon_list->priv->items)
2206 icon_list->priv->items->prev = list;
2208 list->next = icon_list->priv->items;
2209 icon_list->priv->items = list;
2210 icon_list->priv->item_count += 1;
2212 egg_icon_list_validate (icon_list);
2214 g_signal_emit (icon_list, icon_list_signals[ITEM_ADDED], 0, item);
2216 egg_icon_list_queue_layout (icon_list);
2222 egg_icon_list_insert_item_before (EggIconList *icon_list,
2223 EggIconListItem *sibling,
2224 EggIconListItem *item)
2228 g_return_if_fail (EGG_IS_ICON_LIST (icon_list));
2229 g_return_if_fail (item != NULL);
2230 g_return_if_fail (item->icon_list == NULL);
2232 if (icon_list->priv->sorted)
2234 egg_icon_list_insert_item_sorted (icon_list, item);
2238 if (sibling == NULL)
2239 egg_icon_list_append_item (icon_list, item);
2241 egg_icon_list_validate (icon_list);
2243 list = g_list_alloc ();
2245 item->icon_list = icon_list;
2247 egg_icon_list_item_ref (item);
2249 list->prev = sibling->list->prev;
2250 list->next = sibling->list;
2251 sibling->list->prev->next = list;
2252 sibling->list->prev = list;
2254 if (sibling->list == icon_list->priv->items)
2255 icon_list->priv->items = list;
2257 icon_list->priv->item_count += 1;
2258 egg_icon_list_validate (icon_list);
2260 g_signal_emit (icon_list, icon_list_signals[ITEM_ADDED], 0, item);
2262 egg_icon_list_queue_layout (icon_list);
2266 egg_icon_list_insert_item_after (EggIconList *icon_list,
2267 EggIconListItem *sibling,
2268 EggIconListItem *item)
2272 g_return_if_fail (EGG_IS_ICON_LIST (icon_list));
2273 g_return_if_fail (item != NULL);
2274 g_return_if_fail (item->icon_list == NULL);
2276 if (icon_list->priv->sorted)
2278 egg_icon_list_insert_item_sorted (icon_list, item);
2282 if (sibling == NULL)
2284 egg_icon_list_prepend_item (icon_list, item);
2288 egg_icon_list_validate (icon_list);
2290 list = g_list_alloc ();
2292 item->icon_list = icon_list;
2294 egg_icon_list_item_ref (item);
2296 list->next = sibling->list->next;
2297 list->prev = sibling->list;
2298 sibling->list->next->prev = list;
2299 sibling->list->next = list;
2301 if (sibling->list == icon_list->priv->last_item)
2302 icon_list->priv->last_item = list;
2304 icon_list->priv->item_count += 1;
2305 egg_icon_list_validate (icon_list);
2306 g_signal_emit (icon_list, icon_list_signals[ITEM_ADDED], 0, item);
2308 egg_icon_list_queue_layout (icon_list);
2312 egg_icon_list_remove_item (EggIconList *icon_list,
2313 EggIconListItem *item)
2315 g_return_if_fail (EGG_IS_ICON_LIST (icon_list));
2316 g_return_if_fail (item != NULL);
2317 g_return_if_fail (item->icon_list == icon_list);
2319 egg_icon_list_validate (icon_list);
2321 if (item->list->prev)
2322 item->list->prev->next = item->list->next;
2323 if (item->list->next)
2324 item->list->next->prev = item->list->prev;
2326 if (item->list == icon_list->priv->items)
2327 icon_list->priv->items = item->list->next;
2328 if (item->list == icon_list->priv->last_item)
2329 icon_list->priv->last_item = item->list->prev;
2331 g_list_free_1 (item->list);
2333 item->icon_list = NULL;
2334 egg_icon_list_item_invalidate_size (item);
2336 icon_list->priv->item_count -= 1;
2337 egg_icon_list_validate (icon_list);
2339 g_signal_emit (icon_list, icon_list_signals[ITEM_REMOVED], 0, item);
2343 item->selected = FALSE;
2345 g_signal_emit (icon_list, icon_list_signals[SELECTION_CHANGED], 0);
2349 if (icon_list->priv->cursor_item == item)
2350 g_error ("FIXME: Move to first focused item");
2353 if (icon_list->priv->last_single_clicked == item)
2354 icon_list->priv->last_single_clicked = NULL;
2356 egg_icon_list_item_unref (item);
2358 egg_icon_list_queue_layout (icon_list);
2362 egg_icon_list_clear (EggIconList *icon_list)
2366 g_return_if_fail (EGG_IS_ICON_LIST (icon_list));
2368 items = g_list_copy (icon_list->priv->items);
2372 EggIconListItem *item = items->data;
2374 egg_icon_list_remove_item (icon_list, item);
2375 items = items->next;
2382 egg_icon_list_get_item_at_pos (EggIconList *icon_list,
2388 g_return_val_if_fail (EGG_IS_ICON_LIST (icon_list), NULL);
2390 for (items = icon_list->priv->items; items; items = items->next)
2392 EggIconListItem *item = items->data;
2394 if (x > item->x && x < item->x + item->width &&
2395 y > item->y && y < item->y + item->height)
2397 gint layout_x = item->x + (item->width - item->layout_width) / 2;
2398 /* Check if the mouse is inside the icon or the label */
2399 if ((x > item->pixbuf_x && x < item->pixbuf_x + item->pixbuf_width &&
2400 y > item->pixbuf_y && y < item->pixbuf_y + item->pixbuf_height) ||
2401 (x > layout_x - ICON_TEXT_PADDING &&
2402 x < layout_x + item->layout_width + ICON_TEXT_PADDING * 2 &&
2403 y > item->layout_y - ICON_TEXT_PADDING
2404 && y < item->layout_y + item->layout_height + ICON_TEXT_PADDING * 2))
2413 egg_icon_list_get_item_count (EggIconList *icon_list)
2415 g_return_val_if_fail (EGG_IS_ICON_LIST (icon_list), 0);
2417 return icon_list->priv->item_count;
2421 egg_icon_list_foreach (EggIconList *icon_list,
2422 EggIconListForeachFunc func,
2427 for (list = icon_list->priv->items; list; list = list->next)
2428 (* func) (icon_list, list->data, data);
2432 egg_icon_list_selected_foreach (EggIconList *icon_list,
2433 EggIconListForeachFunc func,
2438 for (list = icon_list->priv->items; list; list = list->next)
2440 EggIconListItem *item = list->data;
2443 (* func) (icon_list, list->data, data);
2448 egg_icon_list_get_selected (EggIconList *icon_list)
2450 GList *list, *selected = NULL;
2452 g_return_val_if_fail (EGG_IS_ICON_LIST (icon_list), NULL);
2454 for (list = icon_list->priv->items; list; list = list->next)
2456 EggIconListItem *item = list->data;
2459 selected = g_list_prepend (selected, item);
2462 return g_list_reverse (selected);
2466 egg_icon_list_set_selection_mode (EggIconList *icon_list,
2467 GtkSelectionMode mode)
2469 g_return_if_fail (EGG_IS_ICON_LIST (icon_list));
2471 if (mode == icon_list->priv->selection_mode)
2474 if (mode == GTK_SELECTION_NONE ||
2475 icon_list->priv->selection_mode == GTK_SELECTION_MULTIPLE)
2476 egg_icon_list_unselect_all (icon_list);
2478 icon_list->priv->selection_mode = mode;
2480 g_object_notify (G_OBJECT (icon_list), "selection_mode");
2484 egg_icon_list_get_selection_mode (EggIconList *icon_list)
2486 g_return_val_if_fail (EGG_IS_ICON_LIST (icon_list), GTK_SELECTION_SINGLE);
2488 return icon_list->priv->selection_mode;
2492 egg_icon_list_select_item (EggIconList *icon_list,
2493 EggIconListItem *item)
2495 g_return_if_fail (EGG_IS_ICON_LIST (icon_list));
2496 g_return_if_fail (item != NULL);
2501 if (icon_list->priv->selection_mode == GTK_SELECTION_NONE)
2503 else if (icon_list->priv->selection_mode != GTK_SELECTION_MULTIPLE)
2504 egg_icon_list_unselect_all_internal (icon_list, FALSE);
2506 item->selected = TRUE;
2508 g_signal_emit (icon_list, icon_list_signals[SELECTION_CHANGED], 0);
2510 egg_icon_list_queue_draw_item (icon_list, item);
2515 egg_icon_list_unselect_item (EggIconList *icon_list,
2516 EggIconListItem *item)
2518 g_return_if_fail (EGG_IS_ICON_LIST (icon_list));
2519 g_return_if_fail (item != NULL);
2521 if (!item->selected)
2524 if (icon_list->priv->selection_mode == GTK_SELECTION_NONE ||
2525 icon_list->priv->selection_mode == GTK_SELECTION_BROWSE)
2528 item->selected = FALSE;
2530 g_signal_emit (icon_list, icon_list_signals[SELECTION_CHANGED], 0);
2532 egg_icon_list_queue_draw_item (icon_list, item);
2536 egg_icon_list_item_is_selected (EggIconListItem *item)
2538 g_return_val_if_fail (item != NULL, FALSE);
2540 return item->selected;
2544 egg_icon_list_unselect_all (EggIconList *icon_list)
2546 g_return_if_fail (EGG_IS_ICON_LIST (icon_list));
2548 egg_icon_list_unselect_all_internal (icon_list, TRUE);
2552 egg_icon_list_select_all (EggIconList *icon_list)
2555 gboolean dirty = FALSE;
2557 g_return_if_fail (EGG_IS_ICON_LIST (icon_list));
2559 for (items = icon_list->priv->items; items; items = items->next)
2561 EggIconListItem *item = items->data;
2563 if (!item->selected)
2566 item->selected = TRUE;
2567 egg_icon_list_queue_draw_item (icon_list, item);
2572 g_signal_emit (icon_list, icon_list_signals[SELECTION_CHANGED], 0);
2576 egg_icon_list_set_sorted (EggIconList *icon_list,
2579 g_return_if_fail (EGG_IS_ICON_LIST (icon_list));
2580 g_return_if_fail (icon_list->priv->sort_func != NULL);
2582 if (icon_list->priv->sorted == sorted)
2585 icon_list->priv->sorted = sorted;
2586 g_object_notify (G_OBJECT (icon_list), "sorted");
2588 if (icon_list->priv->sorted)
2589 egg_icon_list_sort (icon_list);
2593 egg_icon_list_get_sorted (EggIconList *icon_list)
2595 g_return_val_if_fail (EGG_IS_ICON_LIST (icon_list), FALSE);
2597 return icon_list->priv->sorted;
2601 egg_icon_list_set_sort_func (EggIconList *icon_list,
2602 EggIconListItemCompareFunc func,
2604 GDestroyNotify destroy_notify)
2606 g_return_if_fail (EGG_IS_ICON_LIST (icon_list));
2607 g_return_if_fail (func != NULL);
2609 if (icon_list->priv->sort_destroy_notify &&
2610 icon_list->priv->sort_data)
2611 (* icon_list->priv->sort_destroy_notify) (icon_list->priv->sort_data);
2613 icon_list->priv->sort_func = func;
2614 icon_list->priv->sort_data = data;
2615 icon_list->priv->sort_destroy_notify = destroy_notify;
2619 egg_icon_list_set_sort_order (EggIconList *icon_list,
2622 g_return_if_fail (EGG_IS_ICON_LIST (icon_list));
2624 if (icon_list->priv->sort_order == order)
2627 icon_list->priv->sort_order = order;
2629 if (icon_list->priv->sorted)
2630 egg_icon_list_sort (icon_list);
2632 g_object_notify (G_OBJECT (icon_list), "sort_order");
2636 egg_icon_list_get_sort_order (EggIconList *icon_list)
2638 g_return_val_if_fail (EGG_IS_ICON_LIST (icon_list), GTK_SORT_ASCENDING);
2640 return icon_list->priv->sort_order;
2644 egg_icon_list_item_activated (EggIconList *icon_list,
2645 EggIconListItem *item)
2647 g_signal_emit (G_OBJECT (icon_list), icon_list_signals[ITEM_ACTIVATED], 0, item);
2651 egg_icon_list_get_items (EggIconList *icon_list)
2653 g_return_val_if_fail (EGG_IS_ICON_LIST (icon_list), NULL);
2655 return icon_list->priv->items;
2659 egg_icon_list_item_get_icon_list (EggIconListItem *item)
2661 g_return_val_if_fail (item != NULL, NULL);
2663 return item->icon_list;
2667 egg_icon_list_add_move_binding (GtkBindingSet *binding_set,
2670 GtkMovementStep step,
2674 gtk_binding_entry_add_signal (binding_set, keyval, modmask,
2679 gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK,
2684 if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2687 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
2692 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK,
2699 egg_icon_list_real_move_cursor (EggIconList *icon_list,
2700 GtkMovementStep step,
2703 GdkModifierType state;
2705 g_return_val_if_fail (EGG_ICON_LIST (icon_list), FALSE);
2706 g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS ||
2707 step == GTK_MOVEMENT_VISUAL_POSITIONS ||
2708 step == GTK_MOVEMENT_DISPLAY_LINES ||
2709 step == GTK_MOVEMENT_PAGES ||
2710 step == GTK_MOVEMENT_BUFFER_ENDS, FALSE);
2712 if (!GTK_WIDGET_HAS_FOCUS (GTK_WIDGET (icon_list)))
2715 gtk_widget_grab_focus (GTK_WIDGET (icon_list));
2717 if (gtk_get_current_event_state (&state))
2719 if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2720 icon_list->priv->ctrl_pressed = TRUE;
2721 if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2722 icon_list->priv->shift_pressed = TRUE;
2724 /* else we assume not pressed */
2728 case GTK_MOVEMENT_LOGICAL_POSITIONS:
2729 case GTK_MOVEMENT_VISUAL_POSITIONS:
2730 egg_icon_list_move_cursor_left_right (icon_list, count);
2732 case GTK_MOVEMENT_DISPLAY_LINES:
2733 egg_icon_list_move_cursor_up_down (icon_list, count);
2735 case GTK_MOVEMENT_PAGES:
2736 egg_icon_list_move_cursor_page_up_down (icon_list, count);
2738 case GTK_MOVEMENT_BUFFER_ENDS:
2739 egg_icon_list_move_cursor_start_end (icon_list, count);
2742 g_assert_not_reached ();
2745 icon_list->priv->ctrl_pressed = FALSE;
2746 icon_list->priv->shift_pressed = FALSE;
2751 static EggIconListItem *
2752 find_item (EggIconList *icon_list,
2753 EggIconListItem *current,
2759 EggIconListItem *item;
2761 /* FIXME: this could be more efficient
2763 row = current->row + row_ofs;
2764 col = current->col + col_ofs;
2766 for (items = icon_list->priv->items; items; items = items->next)
2769 if (item->row == row && item->col == col)
2777 static EggIconListItem *
2778 find_item_page_up_down (EggIconList *icon_list,
2779 EggIconListItem *current,
2786 y = current->y + count * icon_list->priv->vadjustment->page_size;
2788 item = g_list_find (icon_list->priv->items, current);
2793 for (next = item->next; next; next = next->next)
2795 if (((EggIconListItem *)next->data)->col == col)
2798 if (!next || ((EggIconListItem *)next->data)->y > y)
2808 for (next = item->prev; next; next = next->prev)
2810 if (((EggIconListItem *)next->data)->col == col)
2813 if (!next || ((EggIconListItem *)next->data)->y < y)
2827 egg_icon_list_select_all_between (EggIconList *icon_list,
2828 EggIconListItem *anchor,
2829 EggIconListItem *cursor,
2833 EggIconListItem *item;
2834 gint row1, row2, col1, col2;
2836 if (anchor->row < cursor->row)
2847 if (anchor->col < cursor->col)
2858 for (items = icon_list->priv->items; items; items = items->next)
2862 if (row1 <= item->row && item->row <= row2 &&
2863 col1 <= item->col && item->col <= col2)
2865 item->selected = TRUE;
2867 egg_icon_list_queue_draw_item (icon_list, item);
2872 g_signal_emit (icon_list, icon_list_signals[SELECTION_CHANGED], 0);
2876 egg_icon_list_move_cursor_up_down (EggIconList *icon_list,
2879 EggIconListItem *item;
2881 if (!GTK_WIDGET_HAS_FOCUS (icon_list))
2884 if (!icon_list->priv->cursor_item)
2889 list = icon_list->priv->items;
2891 list = g_list_last (icon_list->priv->items);
2896 item = find_item (icon_list,
2897 icon_list->priv->cursor_item,
2903 if (icon_list->priv->ctrl_pressed ||
2904 !icon_list->priv->shift_pressed ||
2905 !icon_list->priv->anchor_item ||
2906 icon_list->priv->selection_mode != GTK_SELECTION_MULTIPLE)
2907 icon_list->priv->anchor_item = item;
2909 egg_icon_list_set_cursor_item (icon_list, item);
2911 if (!icon_list->priv->ctrl_pressed &&
2912 icon_list->priv->selection_mode != GTK_SELECTION_NONE)
2914 egg_icon_list_unselect_all (icon_list);
2915 egg_icon_list_select_all_between (icon_list,
2916 icon_list->priv->anchor_item,
2920 egg_icon_list_scroll_to_item (icon_list, item);
2924 egg_icon_list_move_cursor_page_up_down (EggIconList *icon_list,
2927 EggIconListItem *item;
2929 if (!GTK_WIDGET_HAS_FOCUS (icon_list))
2932 if (!icon_list->priv->cursor_item)
2937 list = icon_list->priv->items;
2939 list = g_list_last (icon_list->priv->items);
2944 item = find_item_page_up_down (icon_list,
2945 icon_list->priv->cursor_item,
2951 if (icon_list->priv->ctrl_pressed ||
2952 !icon_list->priv->shift_pressed ||
2953 !icon_list->priv->anchor_item ||
2954 icon_list->priv->selection_mode != GTK_SELECTION_MULTIPLE)
2955 icon_list->priv->anchor_item = item;
2957 egg_icon_list_set_cursor_item (icon_list, item);
2959 if (!icon_list->priv->ctrl_pressed &&
2960 icon_list->priv->selection_mode != GTK_SELECTION_NONE)
2962 egg_icon_list_unselect_all (icon_list);
2963 egg_icon_list_select_all_between (icon_list,
2964 icon_list->priv->anchor_item,
2968 egg_icon_list_scroll_to_item (icon_list, item);
2972 egg_icon_list_move_cursor_left_right (EggIconList *icon_list,
2975 EggIconListItem *item;
2977 if (!GTK_WIDGET_HAS_FOCUS (icon_list))
2980 if (!icon_list->priv->cursor_item)
2985 list = icon_list->priv->items;
2987 list = g_list_last (icon_list->priv->items);
2992 item = find_item (icon_list,
2993 icon_list->priv->cursor_item,
2999 if (icon_list->priv->ctrl_pressed ||
3000 !icon_list->priv->shift_pressed ||
3001 !icon_list->priv->anchor_item ||
3002 icon_list->priv->selection_mode != GTK_SELECTION_MULTIPLE)
3003 icon_list->priv->anchor_item = item;
3005 egg_icon_list_set_cursor_item (icon_list, item);
3007 if (!icon_list->priv->ctrl_pressed &&
3008 icon_list->priv->selection_mode != GTK_SELECTION_NONE)
3010 egg_icon_list_unselect_all (icon_list);
3011 egg_icon_list_select_all_between (icon_list,
3012 icon_list->priv->anchor_item,
3016 egg_icon_list_scroll_to_item (icon_list, item);
3020 egg_icon_list_move_cursor_start_end (EggIconList *icon_list,
3023 EggIconListItem *item;
3026 if (!GTK_WIDGET_HAS_FOCUS (icon_list))
3030 list = icon_list->priv->items;
3032 list = g_list_last (icon_list->priv->items);
3039 if (icon_list->priv->ctrl_pressed ||
3040 !icon_list->priv->shift_pressed ||
3041 !icon_list->priv->anchor_item ||
3042 icon_list->priv->selection_mode != GTK_SELECTION_MULTIPLE)
3043 icon_list->priv->anchor_item = item;
3045 egg_icon_list_set_cursor_item (icon_list, item);
3047 if (!icon_list->priv->ctrl_pressed &&
3048 icon_list->priv->selection_mode != GTK_SELECTION_NONE)
3050 egg_icon_list_unselect_all (icon_list);
3051 egg_icon_list_select_all_between (icon_list,
3052 icon_list->priv->anchor_item,
3056 egg_icon_list_scroll_to_item (icon_list, item);
3060 egg_icon_list_scroll_to_item (EggIconList *icon_list,
3061 EggIconListItem *item)
3066 gdk_window_get_geometry (icon_list->priv->bin_window, NULL, &y, NULL, &height, NULL);
3068 if (y + item->y < 0)
3070 value = icon_list->priv->vadjustment->value + y + item->y;
3071 gtk_adjustment_set_value (icon_list->priv->vadjustment, value);
3073 else if (y + item->y + item->height > GTK_WIDGET (icon_list)->allocation.height)
3075 value = icon_list->priv->vadjustment->value + y + item->y + item->height
3076 - GTK_WIDGET (icon_list)->allocation.height;
3077 gtk_adjustment_set_value (icon_list->priv->vadjustment, value);