1 /* gtkiconlist - gtkiconlist widget for gtk+
2 * Copyright 1999-2001 Adrian E. Feiguin <feiguin@ifir.edu.ar>
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.
26 #include <gdk/gdkkeysyms.h>
27 #include <pango/pango.h>
28 #include "gtkitementry.h"
29 #include "gtkiconlist.h"
30 #include "gtkextra-marshal.h"
33 #define DEFAULT_ROW_SPACING 4
34 #define DEFAULT_COL_SPACING 10
35 #define DEFAULT_TEXT_SPACE 60
36 #define DEFAULT_ICON_BORDER 2
37 #define DEFAULT_WIDTH 150
38 #define DEFAULT_HEIGHT 120
40 #define EVENTS_MASK (GDK_EXPOSURE_MASK | \
41 GDK_POINTER_MOTION_MASK | \
42 GDK_POINTER_MOTION_HINT_MASK | \
43 GDK_BUTTON_PRESS_MASK | \
44 GDK_BUTTON_RELEASE_MASK)
49 _gtkextra_signal_emit(GtkObject *object, guint signal_id, ...);
61 static guint signals[LAST_SIGNAL] = {0};
63 static void gtk_icon_list_class_init (GtkIconListClass *class);
64 static void gtk_icon_list_init (GtkIconList *icon_list);
65 static void gtk_icon_list_destroy (GtkObject *object);
66 static void gtk_icon_list_finalize (GObject *object);
68 static void gtk_icon_list_size_allocate (GtkWidget *widget,
69 GtkAllocation *allocation);
71 static void gtk_icon_list_realize (GtkWidget *widget);
72 static gint gtk_icon_list_expose (GtkWidget *widget,
73 GdkEventExpose *event);
74 static gint gtk_icon_list_button_press (GtkWidget *widget,
75 GdkEventButton *event);
76 static gint deactivate_entry (GtkIconList *iconlist);
77 static gint entry_in (GtkWidget *widget,
78 GdkEventButton *event,
80 static gint entry_changed (GtkWidget *widget,
82 static void select_icon (GtkIconList *iconlist,
83 GtkIconListItem *item,
85 static void unselect_icon (GtkIconList *iconlist,
86 GtkIconListItem *item,
88 static void unselect_all (GtkIconList *iconlist);
89 static void set_labels (GtkIconList *iconlist,
90 GtkIconListItem *item,
92 static GtkIconListItem *get_icon_from_entry (GtkIconList *iconlist,
94 static void reorder_icons (GtkIconList *iconlist);
95 static void item_size_request (GtkIconList *iconlist,
96 GtkIconListItem *item,
97 GtkRequisition *requisition);
98 static void gtk_icon_list_move (GtkIconList *iconlist,
99 GtkIconListItem *icon,
101 static GtkIconListItem *gtk_icon_list_real_add (GtkIconList *iconualist,
106 static GtkIconListItem *gtk_icon_list_put (GtkIconList *iconlist,
112 static gint icon_key_press (GtkWidget *widget,
115 static gint sort_list (gpointer a, gpointer b);
116 static void pixmap_destroy (GtkPixmap* pixmap);
119 static GtkFixedClass *parent_class = NULL;
121 static inline guint STRING_WIDTH(GtkWidget *widget,
122 PangoFontDescription *font, const gchar *text)
127 layout = gtk_widget_create_pango_layout (widget, text);
128 pango_layout_set_font_description (layout, font);
130 pango_layout_get_pixel_extents (layout, NULL, &rect);
132 g_object_unref(G_OBJECT(layout));
138 gtk_icon_list_get_type (void)
140 static GtkType icon_list_type = 0;
144 static const GtkTypeInfo icon_list_info =
147 sizeof (GtkIconList),
148 sizeof (GtkIconListClass),
149 (GtkClassInitFunc) gtk_icon_list_class_init,
150 (GtkObjectInitFunc) gtk_icon_list_init,
151 /* reserved 1*/ NULL,
152 /* reserved 2 */ NULL,
153 (GtkClassInitFunc) NULL,
156 icon_list_type = gtk_type_unique (GTK_TYPE_FIXED, &icon_list_info);
158 return icon_list_type;
161 static GtkIconListItem*
162 gtk_icon_list_item_copy (const GtkIconListItem *item)
164 GtkIconListItem *new_item;
166 g_return_val_if_fail (item != NULL, NULL);
168 new_item = g_new (GtkIconListItem, 1);
176 gtk_icon_list_item_free (GtkIconListItem *item)
178 g_return_if_fail (item != NULL);
185 gtk_icon_list_item_get_type (void)
187 static GType icon_list_item_type;
189 if(!icon_list_item_type)
191 icon_list_item_type = g_boxed_type_register_static("GtkIconListItem", (GBoxedCopyFunc)gtk_icon_list_item_copy, (GBoxedFreeFunc)gtk_icon_list_item_free);
193 return icon_list_item_type;
198 gtk_icon_list_class_init (GtkIconListClass *klass)
200 GtkObjectClass *object_class;
201 GObjectClass *gobject_class;
202 GtkWidgetClass *widget_class;
204 parent_class = gtk_type_class (GTK_TYPE_FIXED);
206 object_class = (GtkObjectClass *) klass;
207 gobject_class = (GObjectClass *) klass;
208 widget_class = (GtkWidgetClass *) klass;
210 object_class->destroy = gtk_icon_list_destroy;
211 gobject_class->finalize = gtk_icon_list_finalize;
213 widget_class->realize = gtk_icon_list_realize;
215 widget_class->size_allocate = gtk_icon_list_size_allocate;
217 widget_class->expose_event = gtk_icon_list_expose;
218 widget_class->button_press_event = gtk_icon_list_button_press;
220 signals[SELECT_ICON] =
221 gtk_signal_new("select_icon",
223 GTK_CLASS_TYPE(object_class),
224 GTK_SIGNAL_OFFSET(GtkIconListClass, select_icon),
225 gtkextra_BOOLEAN__BOXED_BOXED,
227 GTK_TYPE_ICON_LIST_ITEM, GDK_TYPE_EVENT);
229 signals[UNSELECT_ICON] =
230 gtk_signal_new("unselect_icon",
232 GTK_CLASS_TYPE(object_class),
233 GTK_SIGNAL_OFFSET(GtkIconListClass, unselect_icon),
234 gtkextra_VOID__BOXED_BOXED,
236 GTK_TYPE_ICON_LIST_ITEM, GDK_TYPE_EVENT);
238 signals[TEXT_CHANGED] =
239 gtk_signal_new("text_changed",
241 GTK_CLASS_TYPE(object_class),
242 GTK_SIGNAL_OFFSET(GtkIconListClass, text_changed),
243 gtkextra_BOOLEAN__BOXED_STRING,
245 GTK_TYPE_ICON_LIST_ITEM, GTK_TYPE_STRING);
247 signals[ACTIVATE_ICON] =
248 gtk_signal_new("activate_icon",
250 GTK_CLASS_TYPE(object_class),
251 GTK_SIGNAL_OFFSET(GtkIconListClass, activate_icon),
252 gtkextra_BOOLEAN__BOXED,
254 GTK_TYPE_ICON_LIST_ITEM);
256 signals[DEACTIVATE_ICON] =
257 gtk_signal_new("deactivate_icon",
259 GTK_CLASS_TYPE(object_class),
260 GTK_SIGNAL_OFFSET(GtkIconListClass, deactivate_icon),
261 gtkextra_BOOLEAN__BOXED,
263 GTK_TYPE_ICON_LIST_ITEM);
265 signals[CLICK_EVENT] =
266 gtk_signal_new("click_event",
268 GTK_CLASS_TYPE(object_class),
269 GTK_SIGNAL_OFFSET(GtkIconListClass, click_event),
270 gtkextra_VOID__BOXED,
277 gtk_icon_list_freeze(GtkIconList *iconlist)
279 iconlist->freeze_count++;
283 gtk_icon_list_thaw(GtkIconList *iconlist)
285 if(iconlist->freeze_count == 0) return;
286 iconlist->freeze_count--;
287 if(iconlist->freeze_count == 0)
288 reorder_icons(iconlist);
292 gtk_icon_list_update(GtkIconList *iconlist)
294 reorder_icons(iconlist);
298 reorder_icons(GtkIconList *iconlist)
301 GtkIconListItem *item;
308 gint old_width, old_height;
310 widget = GTK_WIDGET(iconlist);
312 if(iconlist->freeze_count > 0) return;
318 old_width = widget->allocation.width;
319 old_height = widget->allocation.height;
320 if(GTK_WIDGET_REALIZED(widget)){
321 if(GTK_IS_VIEWPORT(widget->parent))
322 gdk_window_get_size(GTK_VIEWPORT(widget->parent)->view_window, &old_width, &old_height);
325 y = iconlist->row_spacing;
326 x = iconlist->col_spacing;
328 icons = iconlist->icons;
330 item = (GtkIconListItem *) icons->data;
332 gtk_icon_list_move(iconlist, item, x, y);
334 item_size_request(iconlist, item, &req);
336 vspace = req.height + iconlist->row_spacing;
337 hspace = req.width + iconlist->col_spacing;
339 switch(iconlist->mode){
340 case GTK_ICON_LIST_TEXT_RIGHT:
342 if(y + vspace >= old_height - DEFAULT_COL_SPACING){
344 y = iconlist->row_spacing;
347 case GTK_ICON_LIST_TEXT_BELOW:
348 case GTK_ICON_LIST_ICON:
351 if(x + hspace >= old_width - DEFAULT_COL_SPACING){
352 x = iconlist->col_spacing;
368 gtk_icon_list_move(GtkIconList *iconlist, GtkIconListItem *icon,
371 GtkRequisition req1, req2;
375 gint old_width, old_height, width, height;
385 if(x == old_x && y == old_y) return;
387 item_size_request(iconlist, icon, &req);
388 req1 = icon->pixmap->requisition;
389 req2 = icon->entry->requisition;
390 req2.width = iconlist->text_space;
392 req1.width += 2*iconlist->icon_border;
393 req1.height += 2*iconlist->icon_border;
394 if(iconlist->mode == GTK_ICON_LIST_TEXT_BELOW){
395 req1.width = MAX(req1.width, req.width);
398 if(iconlist->mode == GTK_ICON_LIST_ICON)
399 req2.width = req2.height = 0;
401 old_width = width = GTK_WIDGET(iconlist)->allocation.width;
402 old_height = height = GTK_WIDGET(iconlist)->allocation.height;
404 gtk_fixed_move(GTK_FIXED(iconlist), icon->pixmap,
405 x + req1.width/2 - icon->pixmap->requisition.width/2,
406 y + iconlist->icon_border);
408 icon->pixmap->allocation.x += (x - old_x);
409 icon->pixmap->allocation.y += (y - old_y);
410 icon->entry->allocation.x += (x - old_x);
411 icon->entry->allocation.y += (y - old_y);
412 icon->entry->allocation.width = req2.width;
415 switch(iconlist->mode){
416 case GTK_ICON_LIST_TEXT_BELOW:
417 text = gtk_entry_get_text(GTK_ENTRY(icon->entry));
418 size = STRING_WIDTH(icon->entry, icon->entry->style->font_desc, text);
419 size = MIN(size, req2.width);
421 gtk_fixed_move(GTK_FIXED(iconlist), icon->entry,
422 x - req2.width/2 + req1.width/2,
423 y + req1.height + iconlist->icon_border);
425 if(y + req.height > height)
426 height += req.height;
428 case GTK_ICON_LIST_TEXT_RIGHT:
429 gtk_fixed_move(GTK_FIXED(iconlist), icon->entry,
430 x + req1.width + iconlist->icon_border,
431 y + req1.height/2 - req2.height/2);
433 if(x + req.width > width)
436 case GTK_ICON_LIST_ICON:
440 a = icon->entry->allocation;
442 gtk_widget_size_allocate(icon->pixmap, &icon->pixmap->allocation);
444 gtk_widget_size_allocate(icon->entry, &a);
445 gtk_widget_draw(icon->entry, NULL);
452 gtk_icon_list_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
454 GtkAllocation *old = gtk_object_get_data(GTK_OBJECT(widget),"viewport");
455 GTK_WIDGET_CLASS(parent_class)->size_allocate(widget, allocation);
456 if(GTK_WIDGET_REALIZED(widget) && old){
457 gint new_width, new_height;
458 gdk_window_get_size(GTK_VIEWPORT(widget->parent)->view_window, &new_width, &new_height);
459 if(old->width != new_width || old->height != new_height)
460 reorder_icons(GTK_ICON_LIST(widget));
461 old->width = new_width;
462 old->height = new_height;
468 gtk_icon_list_realize(GtkWidget *widget)
471 GtkIconList *iconlist;
472 GtkIconListItem *item;
475 GTK_WIDGET_CLASS(parent_class)->realize (widget);
477 iconlist = GTK_ICON_LIST(widget);
479 style = gtk_style_copy(widget->style);
481 style->bg[0] = iconlist->background;
482 gtk_widget_set_style(widget, style);
483 gtk_style_set_background(style, widget->window, GTK_STATE_NORMAL);
484 gtk_style_set_background(style, widget->window, GTK_STATE_ACTIVE);
486 icons = iconlist->icons;
488 item = (GtkIconListItem *) icons->data;
489 gtk_widget_draw(item->pixmap, NULL);
490 if(iconlist->mode != GTK_ICON_LIST_ICON){
493 gtk_widget_realize(item->entry);
495 style = gtk_style_copy(item->entry->style);
496 style->bg[GTK_STATE_ACTIVE] = iconlist->background;
497 style->bg[GTK_STATE_NORMAL] = iconlist->background;
498 gtk_widget_set_style(item->entry, style);
499 gtk_widget_show(item->entry);
501 if(item->entry) gtk_widget_draw(item->entry, NULL);
506 if(GTK_IS_VIEWPORT(widget->parent) && GTK_WIDGET_REALIZED(widget->parent)){
507 GtkAllocation *allocation = gtk_object_get_data(GTK_OBJECT(widget),"viewport");
508 gdk_window_get_size(GTK_VIEWPORT(widget->parent)->view_window, &allocation->width, &allocation->height);
510 reorder_icons(iconlist);
516 gtk_icon_list_init (GtkIconList *icon_list)
519 widget = GTK_WIDGET(icon_list);
521 gtk_widget_ensure_style(widget);
522 gdk_color_black(gtk_widget_get_colormap(widget), &widget->style->black);
523 gdk_color_white(gtk_widget_get_colormap(widget), &widget->style->white);
525 gtk_fixed_set_has_window(GTK_FIXED(widget), TRUE);
527 gtk_widget_set_events (widget, gtk_widget_get_events(widget)|
530 icon_list->selection = NULL;
531 icon_list->is_editable = TRUE;
533 icon_list->num_icons = 0;
534 icon_list->background = widget->style->white;
536 icon_list->row_spacing = DEFAULT_ROW_SPACING;
537 icon_list->col_spacing = DEFAULT_COL_SPACING;
538 icon_list->text_space = DEFAULT_TEXT_SPACE;
539 icon_list->icon_border = DEFAULT_ICON_BORDER;
541 icon_list->active_icon = NULL;
542 icon_list->compare_func = (GCompareFunc)sort_list;
546 sort_list(gpointer a, gpointer b)
548 GtkIconListItem *itema;
549 GtkIconListItem *itemb;
551 itema = (GtkIconListItem *)a;
552 itemb = (GtkIconListItem *)b;
554 return (strcmp(itema->label, itemb->label));
558 gtk_icon_list_expose (GtkWidget *widget, GdkEventExpose *event)
560 GtkIconList *icon_list;
562 icon_list = GTK_ICON_LIST(widget);
564 if(!GTK_WIDGET_DRAWABLE(widget)) return FALSE;
566 gtk_paint_flat_box (widget->style, widget->window, GTK_STATE_NORMAL,
567 GTK_SHADOW_NONE, &event->area, widget, "base", 0, 0, -1, -1);
569 GTK_WIDGET_CLASS(parent_class)->expose_event(widget, event);
571 if(icon_list->active_icon && icon_list->active_icon->entry){
572 gdk_draw_rectangle(widget->window,
573 widget->style->black_gc,
575 icon_list->active_icon->entry->allocation.x-2,
576 icon_list->active_icon->entry->allocation.y-2,
577 icon_list->active_icon->entry->allocation.width+4,
578 icon_list->active_icon->entry->allocation.height+4);
585 gtk_icon_list_button_press(GtkWidget *widget, GdkEventButton *event)
587 GtkIconList *iconlist;
588 GtkIconListItem *item;
590 GtkAllocation *allocation;
592 if(!GTK_IS_ICON_LIST(widget)) return FALSE;
594 iconlist = GTK_ICON_LIST(widget);
597 gtk_widget_get_pointer(widget, &x, &y);
598 item = gtk_icon_list_get_icon_at(iconlist, x , y );
601 gtk_signal_emit(GTK_OBJECT(iconlist), signals[CLICK_EVENT],
607 allocation = &item->entry->allocation;
608 if(x >= allocation->x &&
609 x <= allocation->x + allocation->width &&
610 y >= allocation->y &&
611 y <= allocation->y + allocation->height) return FALSE;
615 switch(iconlist->selection_mode){
616 case GTK_SELECTION_SINGLE:
617 case GTK_SELECTION_BROWSE:
618 unselect_all(iconlist);
619 case GTK_SELECTION_MULTIPLE:
620 select_icon(iconlist, item, (GdkEvent *)event);
621 case GTK_SELECTION_NONE:
630 deactivate_entry(GtkIconList *iconlist)
634 gboolean veto = TRUE;
636 if(iconlist->active_icon) {
637 _gtkextra_signal_emit(GTK_OBJECT(iconlist), signals[DEACTIVATE_ICON],
638 iconlist->active_icon, &veto);
639 if(!veto) return FALSE;
641 entry = GTK_ENTRY(iconlist->active_icon->entry);
643 if(!entry || !GTK_WIDGET_REALIZED(entry)) return TRUE;
644 gtk_entry_set_editable(entry, FALSE);
645 gtk_entry_select_region(entry, 0, 0);
646 gtk_item_entry_set_cursor_visible(GTK_ITEM_ENTRY(entry), FALSE);
648 switch(iconlist->mode){
649 case GTK_ICON_LIST_TEXT_RIGHT:
650 gtk_item_entry_set_text(GTK_ITEM_ENTRY(entry),
651 iconlist->active_icon->entry_label,
654 case GTK_ICON_LIST_TEXT_BELOW:
655 gtk_item_entry_set_text(GTK_ITEM_ENTRY(entry),
656 iconlist->active_icon->entry_label,
659 case GTK_ICON_LIST_ICON:
664 if(GTK_WIDGET_REALIZED(iconlist->active_icon->entry)){
665 gc = gdk_gc_new(GTK_WIDGET(iconlist)->window);
666 gdk_gc_set_foreground(gc, &iconlist->background);
667 gdk_draw_rectangle(GTK_WIDGET(iconlist)->window,
670 GTK_WIDGET(entry)->allocation.x-2,
671 GTK_WIDGET(entry)->allocation.y-2,
672 GTK_WIDGET(entry)->allocation.width+4,
673 GTK_WIDGET(entry)->allocation.height+4);
677 iconlist->active_icon->state = GTK_STATE_NORMAL;
678 iconlist->active_icon = NULL;
686 select_icon(GtkIconList *iconlist, GtkIconListItem *item, GdkEvent *event)
688 gboolean veto = TRUE;
690 if(item == NULL) return;
692 _gtkextra_signal_emit(GTK_OBJECT(iconlist), signals[SELECT_ICON], item,
697 if(iconlist->mode != GTK_ICON_LIST_ICON){
698 if(!deactivate_entry(iconlist)) return;
700 if(item->entry && GTK_WIDGET_REALIZED(item->entry)){
701 GtkStyle *style = gtk_style_copy(item->entry->style);
702 style->bg[GTK_STATE_ACTIVE] = style->base[GTK_STATE_SELECTED];
703 style->bg[GTK_STATE_NORMAL] = style->base[GTK_STATE_SELECTED];
704 style->text[GTK_STATE_ACTIVE] = style->text[GTK_STATE_SELECTED];
705 style->text[GTK_STATE_NORMAL] = style->text[GTK_STATE_SELECTED];
706 gtk_widget_set_style(item->entry, style);
707 gtk_style_unref(style);
709 switch(iconlist->mode){
710 case GTK_ICON_LIST_TEXT_RIGHT:
711 gtk_item_entry_set_text(GTK_ITEM_ENTRY(item->entry),
715 case GTK_ICON_LIST_TEXT_BELOW:
716 gtk_item_entry_set_text(GTK_ITEM_ENTRY(item->entry),
720 case GTK_ICON_LIST_ICON:
726 if(item->state == GTK_STATE_SELECTED) return;
727 iconlist->selection = g_list_append(iconlist->selection, item);
729 item->state = GTK_STATE_SELECTED;
730 if(item->entry) gtk_widget_grab_focus(item->entry);
734 unselect_icon(GtkIconList *iconlist, GtkIconListItem *item, GdkEvent *event)
737 GtkIconListItem *icon;
741 if(item->state == GTK_STATE_NORMAL) return;
743 selection = iconlist->selection;
745 icon = (GtkIconListItem *)selection->data;
746 if(item == icon) break;
747 selection = selection->next;
751 iconlist->selection = g_list_remove_link(iconlist->selection, selection);
754 item->state = GTK_STATE_NORMAL;
756 if(iconlist->mode != GTK_ICON_LIST_ICON){
757 if(item->entry && GTK_WIDGET_REALIZED(item->entry)){
758 GtkStyle *style = gtk_style_copy(item->entry->style);
760 style->bg[GTK_STATE_ACTIVE] = iconlist->background;
761 style->bg[GTK_STATE_NORMAL] = iconlist->background;
762 style->text[GTK_STATE_ACTIVE] = GTK_WIDGET(iconlist)->style->text[GTK_STATE_ACTIVE];
763 style->text[GTK_STATE_NORMAL] = GTK_WIDGET(iconlist)->style->text[GTK_STATE_NORMAL];
764 gtk_widget_set_style(item->entry, style);
765 gtk_style_unref(style);
767 gtk_entry_select_region(GTK_ENTRY(item->entry), 0, 0);
768 gtk_entry_set_text(GTK_ENTRY(item->entry), item->entry_label);
769 gtk_entry_set_editable(GTK_ENTRY(item->entry), FALSE);
770 gtk_widget_draw(item->entry, NULL);
775 gtk_signal_emit(GTK_OBJECT(iconlist), signals[UNSELECT_ICON], item, event);
779 unselect_all(GtkIconList *iconlist)
782 GtkIconListItem *item;
784 selection = iconlist->selection;
786 item = (GtkIconListItem *)selection->data;
787 unselect_icon(iconlist, item, NULL);
788 selection = iconlist->selection;
791 g_list_free(iconlist->selection);
792 iconlist->selection = NULL;
796 gtk_icon_list_new (guint icon_width, GtkIconListMode mode)
798 GtkIconList *icon_list;
799 GtkAllocation *allocation;
801 icon_list = gtk_type_new (gtk_icon_list_get_type ());
803 gtk_icon_list_construct(icon_list, icon_width, mode);
804 allocation = g_new0(GtkAllocation, 1);
805 gtk_object_set_data(GTK_OBJECT(icon_list), "viewport", allocation);
807 return GTK_WIDGET (icon_list);
811 gtk_icon_list_construct (GtkIconList *icon_list, guint icon_width, GtkIconListMode mode)
813 icon_list->icon_width = icon_width;
814 icon_list->mode = mode;
815 icon_list->icons = NULL;
816 icon_list->selection = NULL;
817 icon_list->selection_mode = GTK_SELECTION_SINGLE;
821 gtk_icon_list_set_mode (GtkIconList *iconlist, GtkIconListMode mode)
825 iconlist->mode = mode;
827 icons = iconlist->icons;
829 GtkIconListItem *item;
831 item = (GtkIconListItem *)icons->data;
834 case GTK_ICON_LIST_TEXT_RIGHT:
835 gtk_item_entry_set_justification(GTK_ITEM_ENTRY(item->entry),
838 case GTK_ICON_LIST_TEXT_BELOW:
839 gtk_item_entry_set_justification(GTK_ITEM_ENTRY(item->entry),
842 case GTK_ICON_LIST_ICON:
850 reorder_icons(iconlist);
854 gtk_icon_list_get_mode (GtkIconList *iconlist)
856 return(iconlist->mode);
860 gtk_icon_list_set_text_space (GtkIconList *iconlist, guint text_space)
864 iconlist->text_space = text_space;
866 icons = iconlist->icons;
868 GtkIconListItem *item;
870 item = (GtkIconListItem *)icons->data;
872 if(item->entry) GTK_ITEM_ENTRY(item->entry)->text_max_size = text_space;
877 reorder_icons(iconlist);
882 gtk_icon_list_destroy (GtkObject *object)
884 GtkIconList *icon_list;
886 g_return_if_fail (object != NULL);
887 g_return_if_fail (GTK_IS_ICON_LIST (object));
889 icon_list = GTK_ICON_LIST (object);
891 gtk_icon_list_clear(icon_list);
893 if (GTK_OBJECT_CLASS (parent_class)->destroy)
894 (*GTK_OBJECT_CLASS (parent_class)->destroy) (object);
898 gtk_icon_list_finalize (GObject *object)
900 GtkIconList *icon_list;
901 GtkAllocation *allocation;
903 icon_list = GTK_ICON_LIST (object);
905 allocation = gtk_object_get_data(GTK_OBJECT(icon_list), "viewport");
906 if(allocation) g_free(allocation);
907 gtk_object_set_data(GTK_OBJECT(icon_list), "viewport", NULL);
909 if (G_OBJECT_CLASS (parent_class)->finalize)
910 (*G_OBJECT_CLASS (parent_class)->finalize) (object);
914 gtk_icon_list_set_background (GtkIconList *iconlist, GdkColor *color)
919 g_return_if_fail (iconlist != NULL);
920 g_return_if_fail (GTK_IS_ICON_LIST (iconlist));
922 widget = GTK_WIDGET(iconlist);
924 iconlist->background = *color;
926 style = gtk_style_copy(widget->style);
927 style->bg[0] = iconlist->background;
929 gtk_widget_set_style(widget, style);
930 if(widget->window) gdk_window_set_background(widget->window, color);
931 gtk_style_unref(style);
936 entry_changed (GtkWidget *widget, gpointer data)
938 GtkIconList *iconlist;
939 GtkIconListItem *item;
940 gboolean veto = TRUE;
943 iconlist = GTK_ICON_LIST(data);
944 item = get_icon_from_entry(iconlist, widget);
945 text = gtk_entry_get_text(GTK_ENTRY(widget));
947 _gtkextra_signal_emit(GTK_OBJECT(data), signals[TEXT_CHANGED],
950 if(!veto) return veto;
952 if(item->entry && gtk_editable_get_editable(GTK_EDITABLE(item->entry))){
953 if(item->label) g_free(item->label);
954 if(text) item->label = g_strdup(text);
955 if(item->entry_label) g_free(item->entry_label);
956 set_labels(iconlist, item, text);
963 entry_in (GtkWidget *widget, GdkEventButton *event, gpointer data)
965 GtkIconList *iconlist;
966 GtkIconListItem *item;
967 gboolean veto = TRUE;
969 if(!GTK_IS_ENTRY(widget)) return FALSE;
970 iconlist = GTK_ICON_LIST(data);
972 item = get_icon_from_entry(iconlist, widget);
973 if(iconlist->active_icon && iconlist->active_icon->entry == widget)
976 _gtkextra_signal_emit(GTK_OBJECT(iconlist), signals[ACTIVATE_ICON], &item, &veto);
978 if(!veto) return FALSE;
979 if(!deactivate_entry(iconlist)) return FALSE;
981 if(item->state == GTK_STATE_SELECTED){
982 if(iconlist->is_editable && !gtk_editable_get_editable(GTK_EDITABLE(widget))){
983 unselect_all(iconlist);
985 gtk_entry_set_editable(GTK_ENTRY(widget), TRUE);
986 gtk_item_entry_set_cursor_visible(GTK_ITEM_ENTRY(widget), TRUE);
987 if(item->label) gtk_entry_set_text(GTK_ENTRY(widget), item->label);
988 iconlist->active_icon = item;
989 item->state = GTK_STATE_NORMAL;
991 if(GTK_WIDGET_DRAWABLE(widget))
992 gdk_draw_rectangle(GTK_WIDGET(iconlist)->window,
993 widget->style->black_gc,
995 iconlist->active_icon->entry->allocation.x-2,
996 iconlist->active_icon->entry->allocation.y-2,
997 iconlist->active_icon->entry->allocation.width+4,
998 iconlist->active_icon->entry->allocation.height+4);
1000 gtk_signal_emit_stop_by_name(GTK_OBJECT(widget), "button_press_event");
1001 if(iconlist->selection_mode == GTK_SELECTION_SINGLE ||
1002 iconlist->selection_mode == GTK_SELECTION_BROWSE)
1003 unselect_all(iconlist);
1004 select_icon(iconlist, item, (GdkEvent *)event);
1007 if(iconlist->selection_mode == GTK_SELECTION_SINGLE ||
1008 iconlist->selection_mode == GTK_SELECTION_BROWSE)
1009 unselect_all(iconlist);
1010 select_icon(iconlist, item, (GdkEvent *)event);
1017 gtk_icon_list_get_active_icon(GtkIconList *iconlist)
1019 return iconlist->active_icon;
1022 static GtkIconListItem *
1023 get_icon_from_entry(GtkIconList *iconlist, GtkWidget *widget)
1026 GtkIconListItem *item;
1028 icons = iconlist->icons;
1030 item = (GtkIconListItem *) icons->data;
1031 if(widget == item->entry) return item;
1032 icons = icons->next;
1039 gtk_icon_list_get_icon_at(GtkIconList *iconlist, gint x, gint y)
1042 GtkIconListItem *item;
1045 icons = iconlist->icons;
1047 item = (GtkIconListItem *) icons->data;
1048 item_size_request(iconlist, item, &req);
1049 if(x >= item->x && x <= item->x + req.width &&
1050 y >= item->y && y <= item->y + req.height) return item;
1051 icons = icons->next;
1058 gtk_icon_list_add (GtkIconList *iconlist,
1063 GtkIconListItem *item;
1064 GdkColormap *colormap;
1068 colormap = gdk_colormap_get_system();
1069 pixmap = gdk_pixmap_colormap_create_from_xpm(NULL, colormap, &mask, NULL,
1071 item = gtk_icon_list_real_add(iconlist, pixmap, mask, label, link);
1076 gtk_icon_list_add_from_data (GtkIconList *iconlist,
1081 GtkIconListItem *item;
1082 GdkColormap *colormap;
1086 colormap = gdk_colormap_get_system();
1087 pixmap = gdk_pixmap_colormap_create_from_xpm_d(NULL, colormap, &mask, NULL,
1089 item = gtk_icon_list_real_add(iconlist, pixmap, mask, label, link);
1094 gtk_icon_list_add_from_pixmap (GtkIconList *iconlist,
1100 GtkIconListItem *item;
1102 gdk_pixmap_ref(pixmap);
1103 if(mask) gdk_bitmap_ref(mask);
1104 item = gtk_icon_list_real_add(iconlist, pixmap, mask, label, link);
1109 static GtkIconListItem*
1110 gtk_icon_list_real_add (GtkIconList *iconlist,
1116 GtkIconListItem *item;
1117 GtkRequisition requisition;
1124 width = GTK_WIDGET(iconlist)->allocation.width;
1125 height = GTK_WIDGET(iconlist)->allocation.height;
1127 if(iconlist->num_icons > 0){
1128 item = gtk_icon_list_get_nth(iconlist, iconlist->num_icons-1);
1131 item_size_request(iconlist, item, &requisition);
1133 vspace = requisition.height + iconlist->row_spacing;
1134 hspace = requisition.width + iconlist->col_spacing;
1136 switch(iconlist->mode){
1137 case GTK_ICON_LIST_TEXT_RIGHT:
1141 y = iconlist->row_spacing;
1144 case GTK_ICON_LIST_TEXT_BELOW:
1145 case GTK_ICON_LIST_ICON:
1149 x = iconlist->col_spacing;
1155 y = iconlist->row_spacing;
1156 x = iconlist->col_spacing;
1159 item = gtk_icon_list_put(iconlist, x, y, pixmap, mask, label, data);
1163 static GtkIconListItem *
1164 gtk_icon_list_put (GtkIconList *iconlist,
1171 GtkIconListItem *icon;
1172 GtkIconListItem *active_icon;
1174 GtkRequisition req, req1, req2;
1176 gint width, height, old_width, old_height;
1177 GtkAllocation alloc;
1179 widget = GTK_WIDGET(iconlist);
1181 old_width = width = GTK_WIDGET(iconlist)->allocation.width;
1182 old_height = height = GTK_WIDGET(iconlist)->allocation.height;
1184 active_icon = iconlist->active_icon;
1185 gtk_icon_list_set_active_icon(iconlist, NULL);
1187 icon = g_new(GtkIconListItem, 1);
1190 icon->state = GTK_STATE_NORMAL;
1192 icon->entry_label = NULL;
1193 if(label) icon->label = g_strdup(label);
1194 icon->entry = gtk_item_entry_new();
1195 icon->pixmap = gtk_pixmap_new(pixmap, mask);
1198 GTK_ITEM_ENTRY(icon->entry)->text_max_size = iconlist->text_space;
1199 item_size_request(iconlist, icon, &req);
1200 req1 = icon->pixmap->requisition;
1201 req2 = icon->entry->requisition;
1202 req2.width = iconlist->text_space;
1204 req1.width += 2*iconlist->icon_border;
1205 req1.height += 2*iconlist->icon_border;
1206 if(iconlist->mode == GTK_ICON_LIST_TEXT_BELOW){
1207 req1.width = MAX(req1.width, req.width);
1210 if(iconlist->mode == GTK_ICON_LIST_ICON)
1211 req2.width = req2.height = 0;
1213 set_labels(iconlist, icon, label);
1216 if(label) text_width = STRING_WIDTH(icon->entry, icon->entry->style->font_desc, label);
1218 gtk_fixed_put(GTK_FIXED(iconlist), icon->pixmap,
1219 x + req1.width/2 - icon->pixmap->requisition.width/2,
1220 y + iconlist->icon_border);
1222 alloc.x = x + req1.width/2 - icon->pixmap->requisition.width/2;
1223 alloc.y = y + iconlist->icon_border;
1224 alloc.width = req1.width;
1225 alloc.height = req1.height;
1226 gtk_widget_size_allocate(icon->pixmap, &alloc);
1228 switch(iconlist->mode){
1229 case GTK_ICON_LIST_TEXT_BELOW:
1230 gtk_item_entry_set_text(GTK_ITEM_ENTRY(icon->entry), icon->entry_label,
1231 GTK_JUSTIFY_CENTER);
1232 gtk_fixed_put(GTK_FIXED(iconlist), icon->entry,
1233 x - req2.width/2 + req1.width/2,
1234 y + req1.height + iconlist->icon_border);
1235 alloc.x = x - req2.width/2 + req1.width/2;
1236 alloc.y = y + req1.height + iconlist->icon_border;
1237 alloc.width = req2.width;
1238 alloc.height = req2.height;
1239 gtk_widget_size_allocate(icon->entry, &alloc);
1241 if(y + req1.height + iconlist->icon_border + req2.height > height)
1242 height += req1.height + iconlist->icon_border + req2.height;
1244 case GTK_ICON_LIST_TEXT_RIGHT:
1245 gtk_item_entry_set_text(GTK_ITEM_ENTRY(icon->entry), icon->entry_label,
1247 gtk_fixed_put(GTK_FIXED(iconlist), icon->entry,
1248 x + req1.width + iconlist->icon_border,
1249 y + req1.height/2 - req2.height/2);
1250 alloc.x = x + req1.width + iconlist->icon_border;
1251 alloc.y = y + req1.height/2 - req2.height/2;
1252 alloc.width = req2.width;
1253 alloc.height = req2.height;
1254 gtk_widget_size_allocate(icon->entry, &alloc);
1256 if(x + req1.width + iconlist->icon_border + text_width > width)
1257 width += req1.width + iconlist->icon_border + text_width;
1259 case GTK_ICON_LIST_ICON:
1263 if(GTK_WIDGET_REALIZED(iconlist))
1264 if(iconlist->mode != GTK_ICON_LIST_ICON){
1265 GtkStyle *style = gtk_style_copy(icon->entry->style);
1266 style->bg[GTK_STATE_ACTIVE] = iconlist->background;
1267 style->bg[GTK_STATE_NORMAL] = iconlist->background;
1268 gtk_widget_set_style(icon->entry, style);
1269 gtk_style_unref(style);
1270 gtk_widget_show(icon->entry);
1273 gtk_widget_show(icon->pixmap);
1275 if(iconlist->compare_func)
1276 iconlist->icons = g_list_insert_sorted(iconlist->icons, icon, iconlist->compare_func);
1278 iconlist->icons = g_list_append(iconlist->icons, icon);
1280 iconlist->num_icons++;
1282 if(GTK_WIDGET_REALIZED(iconlist))
1283 reorder_icons(iconlist);
1285 gtk_entry_set_editable(GTK_ENTRY(icon->entry), FALSE);
1287 gtk_signal_connect(GTK_OBJECT(icon->entry), "key_press_event",
1288 (GtkSignalFunc)icon_key_press, iconlist);
1289 gtk_signal_connect(GTK_OBJECT(icon->entry), "button_press_event",
1290 (GtkSignalFunc)entry_in, iconlist);
1291 gtk_signal_connect(GTK_OBJECT(icon->entry), "changed",
1292 (GtkSignalFunc)entry_changed, iconlist);
1294 gtk_icon_list_set_active_icon(iconlist, active_icon);
1299 set_labels(GtkIconList *iconlist, GtkIconListItem *icon, const gchar *label)
1304 gchar *entry_label = NULL;
1309 entry_label = (gchar *)g_malloc(strlen(label) + 5);
1310 entry_label[0] = label[0];
1311 entry_label[1] = '\0';
1313 text_width = STRING_WIDTH(icon->entry, icon->entry->style->font_desc, label);
1314 point_width = STRING_WIDTH(icon->entry, icon->entry->style->font_desc, "X");
1316 max_width = iconlist->text_space;
1318 for(n = 0; n < strlen(label); n++){
1319 space = strlen(label) - n + 1;
1321 STRING_WIDTH(icon->entry, icon->entry->style->font_desc, entry_label) +
1322 3 * point_width > max_width)
1324 entry_label[n] = label[n];
1325 entry_label[n + 1] = '\0';
1329 if(strlen(entry_label) < strlen(label))
1330 sprintf(entry_label,"%s...", entry_label);
1332 icon->entry_label = g_strdup(entry_label);
1334 g_free(entry_label);
1338 icon_key_press(GtkWidget *widget, GdkEventKey *key, gpointer data)
1340 GtkIconList *iconlist;
1342 iconlist = GTK_ICON_LIST(data);
1343 if(key->keyval != GDK_Return) return FALSE;
1345 if(iconlist->active_icon)
1346 select_icon(iconlist, iconlist->active_icon, NULL);
1352 item_size_request(GtkIconList *iconlist,
1353 GtkIconListItem *item,
1354 GtkRequisition *requisition)
1356 GtkRequisition req2;
1358 gtk_widget_size_request(item->entry, &req2);
1359 req2.width = iconlist->text_space;
1361 gtk_widget_size_request(item->pixmap, requisition);
1362 requisition->width = MAX(iconlist->icon_width, requisition->width);
1363 requisition->width += 2*iconlist->icon_border;
1364 requisition->height += 2*iconlist->icon_border;
1366 switch(iconlist->mode){
1367 case GTK_ICON_LIST_TEXT_BELOW:
1368 requisition->height += req2.height;
1369 requisition->width = MAX(requisition->width, req2.width);
1371 case GTK_ICON_LIST_TEXT_RIGHT:
1372 requisition->width += req2.width;
1374 case GTK_ICON_LIST_ICON:
1382 gtk_icon_list_set_editable (GtkIconList *iconlist, gboolean editable)
1385 GtkIconListItem *item;
1387 icons = iconlist->icons;
1389 item = (GtkIconListItem *) icons->data;
1390 gtk_entry_set_editable(GTK_ENTRY(item->entry), editable);
1391 icons = icons->next;
1394 iconlist->is_editable = editable;
1398 gtk_icon_list_get_nth(GtkIconList *iconlist, guint n)
1400 return (GtkIconListItem *)g_list_nth_data(iconlist->icons, n);
1404 gtk_icon_list_get_index(GtkIconList *iconlist, GtkIconListItem *item)
1407 GtkIconListItem *icon;
1410 if(item == NULL) return -1;
1412 icons = iconlist->icons;
1415 icon = (GtkIconListItem *) icons->data;
1416 if(item == icon) break;
1417 icons = icons->next;
1426 remove_from_fixed(GtkIconList *iconlist, GtkWidget *widget)
1431 fixed = GTK_FIXED(iconlist);
1432 children = fixed->children;
1434 GtkFixedChild *child;
1436 child = children->data;
1438 if(child->widget == widget){
1439 gtk_widget_unparent(widget);
1440 fixed->children = g_list_remove_link (fixed->children, children);
1441 g_list_free (children);
1447 children = children->next;
1452 pixmap_destroy(GtkPixmap* pixmap)
1454 /* release pixmap */
1456 GdkPixmap* pm = NULL;
1457 GdkBitmap* bm = NULL;
1459 gtk_pixmap_get(pixmap, &pm, &bm);
1461 /* HB: i don't know enough about Gtk+ to call this a design flaw, but it
1462 * appears the pixmaps need to be destroyed by hand ...
1464 if (pm) gdk_pixmap_unref(pm);
1465 if (bm) gdk_pixmap_unref(bm);
1470 gtk_icon_list_remove (GtkIconList *iconlist, GtkIconListItem *item)
1473 GtkIconListItem *icon = 0;
1475 if(item == NULL) return;
1477 icons = iconlist->icons;
1479 icon = (GtkIconListItem *) icons->data;
1480 if(item == icon) break;
1481 icons = icons->next;
1485 if(icon->state == GTK_STATE_SELECTED) unselect_icon(iconlist, icon, NULL);
1486 if(icon == iconlist->active_icon) deactivate_entry(iconlist);
1487 pixmap_destroy(GTK_PIXMAP(icon->pixmap));
1488 if(icon->entry && iconlist->mode != GTK_ICON_LIST_ICON){
1489 remove_from_fixed(iconlist, icon->entry);
1493 remove_from_fixed(iconlist, icon->pixmap);
1494 icon->pixmap = NULL;
1497 g_free(icon->label);
1500 if(icon->entry_label){
1501 g_free(icon->entry_label);
1502 icon->entry_label = NULL;
1506 iconlist->icons = g_list_remove_link(iconlist->icons, icons);
1507 g_list_free_1(icons);
1508 iconlist->num_icons--;
1511 if(iconlist->num_icons == 0){
1512 iconlist->icons = NULL;
1513 iconlist->selection = NULL;
1518 gtk_icon_list_remove_nth (GtkIconList *iconlist, guint n)
1520 GtkIconListItem *item;
1522 item = gtk_icon_list_get_nth(iconlist, n);
1523 gtk_icon_list_remove(iconlist, item);
1527 gtk_icon_list_clear(GtkIconList *iconlist)
1530 GtkIconListItem *icon;
1532 if(iconlist->num_icons == 0) return;
1533 if(!deactivate_entry(iconlist)) return;
1535 unselect_all(iconlist);
1537 icons = iconlist->icons;
1540 icon = (GtkIconListItem *) icons->data;
1541 pixmap_destroy(GTK_PIXMAP(icon->pixmap));
1542 if(icon->entry && iconlist->mode != GTK_ICON_LIST_ICON){
1543 remove_from_fixed(iconlist, icon->entry);
1547 gtk_widget_hide(icon->pixmap);
1548 remove_from_fixed(iconlist, icon->pixmap);
1549 icon->pixmap = NULL;
1552 g_free(icon->label);
1555 if(icon->entry_label){
1556 g_free(icon->entry_label);
1557 icon->entry_label = NULL;
1563 iconlist->icons = g_list_remove_link(iconlist->icons, icons);
1564 g_list_free_1(icons);
1565 icons = iconlist->icons;
1568 iconlist->icons = NULL;
1569 iconlist->selection = NULL;
1570 iconlist->active_icon = NULL;
1571 iconlist->num_icons = 0;
1575 gtk_icon_list_link(GtkIconListItem *item, gpointer data)
1581 gtk_icon_list_get_link(GtkIconListItem *item)
1587 gtk_icon_list_get_icon_from_link(GtkIconList *iconlist, gpointer data)
1590 GtkIconListItem *item;
1592 icons = iconlist->icons;
1594 item = (GtkIconListItem *) icons->data;
1595 if(data == item->link) return item;
1596 icons = icons->next;
1603 gtk_icon_list_get_entry(GtkIconListItem *item)
1609 gtk_icon_list_get_pixmap(GtkIconListItem *item)
1611 return item->pixmap;
1615 gtk_icon_list_set_pixmap(GtkIconListItem *item,
1620 if(item->pixmap) gtk_widget_destroy(item->pixmap);
1621 item->pixmap = gtk_pixmap_new(pixmap, mask);
1626 gtk_icon_list_set_label(GtkIconList *iconlist, GtkIconListItem *item, const gchar *label)
1629 g_free(item->label);
1632 if(item->entry_label){
1633 g_free(item->entry_label);
1634 item->entry_label = NULL;
1636 if(label) item->label = g_strdup(label);
1637 gtk_entry_set_text(GTK_ENTRY(item->entry), label);
1638 set_labels(iconlist, item, label);
1641 /**********************************
1642 * gtk_icon_list_set_icon_width
1643 * gtk_icon_list_set_row_spacing
1644 * gtk_icon_list_set_col_spacing
1645 * gtk_icon_list_set_text_space
1646 * gtk_icon_list_icon_border
1647 **********************************/
1650 gtk_icon_list_set_icon_width(GtkIconList *iconlist, guint width)
1652 iconlist->icon_width = width;
1653 reorder_icons(iconlist);
1657 gtk_icon_list_set_icon_border(GtkIconList *iconlist, guint border)
1659 iconlist->icon_border = border;
1660 reorder_icons(iconlist);
1664 gtk_icon_list_set_row_spacing(GtkIconList *iconlist, guint spacing)
1666 iconlist->row_spacing = spacing;
1667 reorder_icons(iconlist);
1671 gtk_icon_list_set_col_spacing(GtkIconList *iconlist, guint spacing)
1673 iconlist->col_spacing = spacing;
1674 reorder_icons(iconlist);
1678 /**********************************
1679 * gtk_icon_list_set_selection_mode
1680 * gtk_icon_list_select_icon
1681 * gtk_icon_list_unselect_icon
1682 * gtk_icon_list_unselect_all
1683 **********************************/
1686 gtk_icon_list_set_selection_mode(GtkIconList *iconlist, gint mode)
1688 iconlist->selection_mode = mode;
1692 gtk_icon_list_select_icon(GtkIconList *iconlist, GtkIconListItem *item)
1694 select_icon(iconlist, item, NULL);
1698 gtk_icon_list_unselect_icon(GtkIconList *iconlist, GtkIconListItem *item)
1700 unselect_icon(iconlist, item, NULL);
1704 gtk_icon_list_unselect_all(GtkIconList *iconlist)
1706 unselect_all(iconlist);
1710 gtk_icon_list_set_active_icon(GtkIconList *iconlist, GtkIconListItem *icon)
1713 deactivate_entry(iconlist);
1714 unselect_all(iconlist);
1719 icon->state = GTK_STATE_SELECTED;
1720 entry_in(icon->entry, NULL, iconlist);
1721 gtk_widget_grab_focus(icon->entry);
1727 gtk_icon_list_is_editable (GtkIconList *iconlist)
1729 return (iconlist->is_editable);
1733 gtk_icon_list_get_row_spacing (GtkIconList *iconlist)
1735 return(iconlist->row_spacing);
1739 gtk_icon_list_get_col_spacing (GtkIconList *iconlist)
1741 return(iconlist->col_spacing);
1745 gtk_icon_list_get_text_space (GtkIconList *iconlist)
1747 return(iconlist->text_space);
1751 gtk_icon_list_get_icon_border (GtkIconList *iconlist)
1753 return(iconlist->icon_border);
1757 gtk_icon_list_get_icon_width (GtkIconList *iconlist)
1759 return(iconlist->icon_width);