Updated Finnish translation
[rhythmbox.git] / widgets / libsexy / sexy-icon-entry.c
blob5f5656c15a41c7a3bef62f34ae9d6022e461efbe
1 /*
2 * @file libsexy/sexy-icon-entry.c Entry widget
4 * @Copyright (C) 2004-2006 Christian Hammond.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
21 #include <libsexy/sexy-icon-entry.h>
22 #include <string.h>
23 #include <gtk/gtk.h>
25 #define ICON_MARGIN 2
26 #define MAX_ICONS 2
28 #define IS_VALID_ICON_ENTRY_POSITION(pos) \
29 ((pos) == SEXY_ICON_ENTRY_PRIMARY || \
30 (pos) == SEXY_ICON_ENTRY_SECONDARY)
32 typedef struct
34 GtkImage *icon;
35 gboolean highlight;
36 gboolean hovered;
37 GdkWindow *window;
39 } SexyIconInfo;
41 struct _SexyIconEntryPriv
43 SexyIconInfo icons[MAX_ICONS];
45 gulong icon_released_id;
48 enum
50 ICON_PRESSED,
51 ICON_RELEASED,
52 LAST_SIGNAL
55 static void sexy_icon_entry_class_init(SexyIconEntryClass *klass);
56 static void sexy_icon_entry_editable_init(GtkEditableClass *iface);
57 static void sexy_icon_entry_init(SexyIconEntry *entry);
58 static void sexy_icon_entry_finalize(GObject *obj);
59 static void sexy_icon_entry_destroy(GtkObject *obj);
60 static void sexy_icon_entry_map(GtkWidget *widget);
61 static void sexy_icon_entry_unmap(GtkWidget *widget);
62 static void sexy_icon_entry_realize(GtkWidget *widget);
63 static void sexy_icon_entry_unrealize(GtkWidget *widget);
64 static void sexy_icon_entry_size_request(GtkWidget *widget,
65 GtkRequisition *requisition);
66 static void sexy_icon_entry_size_allocate(GtkWidget *widget,
67 GtkAllocation *allocation);
68 static gint sexy_icon_entry_expose(GtkWidget *widget, GdkEventExpose *event);
69 static gint sexy_icon_entry_enter_notify(GtkWidget *widget,
70 GdkEventCrossing *event);
71 static gint sexy_icon_entry_leave_notify(GtkWidget *widget,
72 GdkEventCrossing *event);
73 static gint sexy_icon_entry_button_press(GtkWidget *widget,
74 GdkEventButton *event);
75 static gint sexy_icon_entry_button_release(GtkWidget *widget,
76 GdkEventButton *event);
78 static GtkEntryClass *parent_class = NULL;
79 static guint signals[LAST_SIGNAL] = {0};
81 G_DEFINE_TYPE_EXTENDED(SexyIconEntry, sexy_icon_entry, GTK_TYPE_ENTRY,
83 G_IMPLEMENT_INTERFACE(GTK_TYPE_EDITABLE,
84 sexy_icon_entry_editable_init));
86 static void
87 sexy_icon_entry_class_init(SexyIconEntryClass *klass)
89 GObjectClass *gobject_class;
90 GtkObjectClass *object_class;
91 GtkWidgetClass *widget_class;
92 GtkEntryClass *entry_class;
94 parent_class = g_type_class_peek_parent(klass);
96 gobject_class = G_OBJECT_CLASS(klass);
97 object_class = GTK_OBJECT_CLASS(klass);
98 widget_class = GTK_WIDGET_CLASS(klass);
99 entry_class = GTK_ENTRY_CLASS(klass);
101 gobject_class->finalize = sexy_icon_entry_finalize;
103 object_class->destroy = sexy_icon_entry_destroy;
105 widget_class->map = sexy_icon_entry_map;
106 widget_class->unmap = sexy_icon_entry_unmap;
107 widget_class->realize = sexy_icon_entry_realize;
108 widget_class->unrealize = sexy_icon_entry_unrealize;
109 widget_class->size_request = sexy_icon_entry_size_request;
110 widget_class->size_allocate = sexy_icon_entry_size_allocate;
111 widget_class->expose_event = sexy_icon_entry_expose;
112 widget_class->enter_notify_event = sexy_icon_entry_enter_notify;
113 widget_class->leave_notify_event = sexy_icon_entry_leave_notify;
114 widget_class->button_press_event = sexy_icon_entry_button_press;
115 widget_class->button_release_event = sexy_icon_entry_button_release;
118 * SexyIconEntry::icon-pressed:
119 * @entry: The entry on which the signal is emitted.
120 * @icon_pos: The position of the clicked icon.
121 * @button: The mouse button clicked.
123 * The ::icon-pressed signal is emitted when an icon is clicked.
125 signals[ICON_PRESSED] =
126 g_signal_new("icon_pressed",
127 G_TYPE_FROM_CLASS(gobject_class),
128 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
129 G_STRUCT_OFFSET(SexyIconEntryClass, icon_pressed),
130 NULL, NULL,
131 gtk_marshal_VOID__INT_INT,
132 G_TYPE_NONE, 2,
133 G_TYPE_INT,
134 G_TYPE_INT);
137 * SexyIconEntry::icon-released:
138 * @entry: The entry on which the signal is emitted.
139 * @icon_pos: The position of the clicked icon.
140 * @button: The mouse button clicked.
142 * The ::icon-released signal is emitted on the button release from a
143 * mouse click.
145 signals[ICON_RELEASED] =
146 g_signal_new("icon_released",
147 G_TYPE_FROM_CLASS(gobject_class),
148 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
149 G_STRUCT_OFFSET(SexyIconEntryClass, icon_released),
150 NULL, NULL,
151 gtk_marshal_VOID__INT_INT,
152 G_TYPE_NONE, 2,
153 G_TYPE_INT,
154 G_TYPE_INT);
157 static void
158 sexy_icon_entry_editable_init(GtkEditableClass *iface)
162 static void
163 sexy_icon_entry_init(SexyIconEntry *entry)
165 entry->priv = g_new0(SexyIconEntryPriv, 1);
168 static void
169 sexy_icon_entry_finalize(GObject *obj)
171 SexyIconEntry *entry;
173 g_return_if_fail(obj != NULL);
174 g_return_if_fail(SEXY_IS_ICON_ENTRY(obj));
176 entry = SEXY_ICON_ENTRY(obj);
178 g_free(entry->priv);
180 if (G_OBJECT_CLASS(parent_class)->finalize)
181 G_OBJECT_CLASS(parent_class)->finalize(obj);
184 static void
185 sexy_icon_entry_destroy(GtkObject *obj)
187 SexyIconEntry *entry;
189 entry = SEXY_ICON_ENTRY(obj);
191 sexy_icon_entry_set_icon(entry, SEXY_ICON_ENTRY_PRIMARY, NULL);
192 sexy_icon_entry_set_icon(entry, SEXY_ICON_ENTRY_SECONDARY, NULL);
194 if (GTK_OBJECT_CLASS(parent_class)->destroy)
195 GTK_OBJECT_CLASS(parent_class)->destroy(obj);
198 static void
199 sexy_icon_entry_map(GtkWidget *widget)
201 if (GTK_WIDGET_REALIZED(widget) && !GTK_WIDGET_MAPPED(widget))
203 SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
204 int i;
206 GTK_WIDGET_CLASS(parent_class)->map(widget);
208 for (i = 0; i < MAX_ICONS; i++)
210 if (entry->priv->icons[i].icon != NULL)
211 gdk_window_show(entry->priv->icons[i].window);
216 static void
217 sexy_icon_entry_unmap(GtkWidget *widget)
219 if (GTK_WIDGET_MAPPED(widget))
221 SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
222 int i;
224 for (i = 0; i < MAX_ICONS; i++)
226 if (entry->priv->icons[i].icon != NULL)
227 gdk_window_hide(entry->priv->icons[i].window);
230 GTK_WIDGET_CLASS(parent_class)->unmap(widget);
234 static gint
235 get_icon_width(SexyIconEntry *entry, SexyIconEntryPosition icon_pos)
237 GtkRequisition requisition;
238 gint menu_icon_width;
239 gint width;
240 SexyIconInfo *icon_info = &entry->priv->icons[icon_pos];
242 if (icon_info->icon == NULL)
243 return 0;
245 gtk_widget_size_request(GTK_WIDGET(icon_info->icon), &requisition);
246 gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &menu_icon_width, NULL);
248 width = MAX(requisition.width, menu_icon_width);
250 return width;
253 static void
254 get_borders(SexyIconEntry *entry, gint *xborder, gint *yborder)
256 GtkWidget *widget = GTK_WIDGET(entry);
257 gint focus_width;
258 gboolean interior_focus;
260 gtk_widget_style_get(widget,
261 "interior-focus", &interior_focus,
262 "focus-line-width", &focus_width,
263 NULL);
265 if (gtk_entry_get_has_frame(GTK_ENTRY(entry)))
267 *xborder = widget->style->xthickness;
268 *yborder = widget->style->ythickness;
270 else
272 *xborder = 0;
273 *yborder = 0;
276 if (!interior_focus)
278 *xborder += focus_width;
279 *yborder += focus_width;
283 static void
284 get_text_area_size(SexyIconEntry *entry, GtkAllocation *alloc)
286 GtkWidget *widget = GTK_WIDGET(entry);
287 GtkRequisition requisition;
288 gint xborder, yborder;
290 gtk_widget_get_child_requisition(widget, &requisition);
291 get_borders(entry, &xborder, &yborder);
293 alloc->x = xborder;
294 alloc->y = yborder;
295 alloc->width = widget->allocation.width - xborder * 2;
296 alloc->height = requisition.height - yborder * 2;
299 static void
300 get_icon_allocation(SexyIconEntry *icon_entry,
301 gboolean left,
302 GtkAllocation *widget_alloc,
303 GtkAllocation *text_area_alloc,
304 GtkAllocation *allocation,
305 SexyIconEntryPosition *icon_pos)
307 gboolean rtl;
309 rtl = (gtk_widget_get_direction(GTK_WIDGET(icon_entry)) ==
310 GTK_TEXT_DIR_RTL);
312 if (left)
313 *icon_pos = (rtl ? SEXY_ICON_ENTRY_SECONDARY : SEXY_ICON_ENTRY_PRIMARY);
314 else
315 *icon_pos = (rtl ? SEXY_ICON_ENTRY_PRIMARY : SEXY_ICON_ENTRY_SECONDARY);
317 allocation->y = text_area_alloc->y;
318 allocation->width = get_icon_width(icon_entry, *icon_pos);
319 allocation->height = text_area_alloc->height;
321 if (left)
322 allocation->x = text_area_alloc->x + ICON_MARGIN;
323 else
325 allocation->x = text_area_alloc->x + text_area_alloc->width -
326 allocation->width - ICON_MARGIN;
330 static void
331 sexy_icon_entry_realize(GtkWidget *widget)
333 SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
334 GdkWindowAttr attributes;
335 gint attributes_mask;
336 int i;
338 GTK_WIDGET_CLASS(parent_class)->realize(widget);
340 attributes.width = 1;
341 attributes.height = 1;
342 attributes.window_type = GDK_WINDOW_CHILD;
343 attributes.wclass = GDK_INPUT_OUTPUT;
344 attributes.visual = gtk_widget_get_visual(widget);
345 attributes.colormap = gtk_widget_get_colormap(widget);
346 attributes.event_mask = gtk_widget_get_events(widget);
347 attributes.event_mask |=
348 (GDK_EXPOSURE_MASK
349 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
350 | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
352 attributes_mask = GDK_WA_VISUAL | GDK_WA_COLORMAP;
354 for (i = 0; i < MAX_ICONS; i++)
356 SexyIconInfo *icon_info;
358 icon_info = &entry->priv->icons[i];
359 icon_info->window = gdk_window_new(widget->window, &attributes,
360 attributes_mask);
361 gdk_window_set_user_data(icon_info->window, widget);
363 gdk_window_set_background(icon_info->window,
364 &widget->style->base[GTK_WIDGET_STATE(widget)]);
367 gtk_widget_queue_resize(widget);
370 static void
371 sexy_icon_entry_unrealize(GtkWidget *widget)
373 SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
374 int i;
376 GTK_WIDGET_CLASS(parent_class)->unrealize(widget);
378 for (i = 0; i < MAX_ICONS; i++)
380 SexyIconInfo *icon_info = &entry->priv->icons[i];
382 gdk_window_destroy(icon_info->window);
383 icon_info->window = NULL;
387 static void
388 sexy_icon_entry_size_request(GtkWidget *widget, GtkRequisition *requisition)
390 GtkEntry *gtkentry;
391 SexyIconEntry *entry;
392 gint icon_widths = 0;
393 int i;
395 gtkentry = GTK_ENTRY(widget);
396 entry = SEXY_ICON_ENTRY(widget);
398 for (i = 0; i < MAX_ICONS; i++)
400 int icon_width = get_icon_width(entry, i);
402 if (icon_width > 0)
403 icon_widths += icon_width + ICON_MARGIN;
406 GTK_WIDGET_CLASS(parent_class)->size_request(widget, requisition);
408 if (icon_widths > requisition->width)
409 requisition->width += icon_widths;
412 static void
413 place_windows(SexyIconEntry *icon_entry, GtkAllocation *widget_alloc)
415 SexyIconEntryPosition left_icon_pos;
416 SexyIconEntryPosition right_icon_pos;
417 GtkAllocation left_icon_alloc;
418 GtkAllocation right_icon_alloc;
419 GtkAllocation text_area_alloc;
421 get_text_area_size(icon_entry, &text_area_alloc);
422 get_icon_allocation(icon_entry, TRUE, widget_alloc, &text_area_alloc,
423 &left_icon_alloc, &left_icon_pos);
424 get_icon_allocation(icon_entry, FALSE, widget_alloc, &text_area_alloc,
425 &right_icon_alloc, &right_icon_pos);
427 if (left_icon_alloc.width > 0)
429 text_area_alloc.x = left_icon_alloc.x + left_icon_alloc.width +
430 ICON_MARGIN;
433 if (right_icon_alloc.width > 0)
434 text_area_alloc.width -= right_icon_alloc.width + ICON_MARGIN;
436 text_area_alloc.width -= text_area_alloc.x;
438 gdk_window_move_resize(icon_entry->priv->icons[left_icon_pos].window,
439 left_icon_alloc.x, left_icon_alloc.y,
440 left_icon_alloc.width, left_icon_alloc.height);
442 gdk_window_move_resize(icon_entry->priv->icons[right_icon_pos].window,
443 right_icon_alloc.x, right_icon_alloc.y,
444 right_icon_alloc.width, right_icon_alloc.height);
446 gdk_window_move_resize(GTK_ENTRY(icon_entry)->text_area,
447 text_area_alloc.x, text_area_alloc.y,
448 text_area_alloc.width, text_area_alloc.height);
451 static void
452 sexy_icon_entry_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
454 g_return_if_fail(SEXY_IS_ICON_ENTRY(widget));
455 g_return_if_fail(allocation != NULL);
457 widget->allocation = *allocation;
459 GTK_WIDGET_CLASS(parent_class)->size_allocate(widget, allocation);
461 if (GTK_WIDGET_REALIZED(widget))
462 place_windows(SEXY_ICON_ENTRY(widget), allocation);
465 static GdkPixbuf *
466 get_pixbuf_from_icon(SexyIconEntry *entry, SexyIconEntryPosition icon_pos)
468 GdkPixbuf *pixbuf = NULL;
469 gchar *stock_id;
470 SexyIconInfo *icon_info = &entry->priv->icons[icon_pos];
471 GtkIconSize size;
473 switch (gtk_image_get_storage_type(GTK_IMAGE(icon_info->icon)))
475 case GTK_IMAGE_PIXBUF:
476 pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(icon_info->icon));
477 g_object_ref(pixbuf);
478 break;
480 case GTK_IMAGE_STOCK:
481 gtk_image_get_stock(GTK_IMAGE(icon_info->icon), &stock_id, &size);
482 pixbuf = gtk_widget_render_icon(GTK_WIDGET(entry),
483 stock_id, size, NULL);
484 break;
486 default:
487 return NULL;
490 return pixbuf;
493 /* Kudos to the gnome-panel guys. */
494 static void
495 colorshift_pixbuf(GdkPixbuf *dest, GdkPixbuf *src, int shift)
497 gint i, j;
498 gint width, height, has_alpha, src_rowstride, dest_rowstride;
499 guchar *target_pixels;
500 guchar *original_pixels;
501 guchar *pix_src;
502 guchar *pix_dest;
503 int val;
504 guchar r, g, b;
506 has_alpha = gdk_pixbuf_get_has_alpha(src);
507 width = gdk_pixbuf_get_width(src);
508 height = gdk_pixbuf_get_height(src);
509 src_rowstride = gdk_pixbuf_get_rowstride(src);
510 dest_rowstride = gdk_pixbuf_get_rowstride(dest);
511 original_pixels = gdk_pixbuf_get_pixels(src);
512 target_pixels = gdk_pixbuf_get_pixels(dest);
514 for (i = 0; i < height; i++)
516 pix_dest = target_pixels + i * dest_rowstride;
517 pix_src = original_pixels + i * src_rowstride;
519 for (j = 0; j < width; j++)
521 r = *(pix_src++);
522 g = *(pix_src++);
523 b = *(pix_src++);
525 val = r + shift;
526 *(pix_dest++) = CLAMP(val, 0, 255);
528 val = g + shift;
529 *(pix_dest++) = CLAMP(val, 0, 255);
531 val = b + shift;
532 *(pix_dest++) = CLAMP(val, 0, 255);
534 if (has_alpha)
535 *(pix_dest++) = *(pix_src++);
540 static void
541 draw_icon(GtkWidget *widget, SexyIconEntryPosition icon_pos)
543 SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
544 SexyIconInfo *icon_info = &entry->priv->icons[icon_pos];
545 GdkPixbuf *pixbuf;
546 gint x, y, width, height;
548 if (icon_info->icon == NULL || !GTK_WIDGET_REALIZED(widget))
549 return;
551 if ((pixbuf = get_pixbuf_from_icon(entry, icon_pos)) == NULL)
552 return;
554 gdk_drawable_get_size(icon_info->window, &width, &height);
555 if ((width < 2 * ICON_MARGIN) || (height < 2 * ICON_MARGIN))
556 return;
558 if (gdk_pixbuf_get_height(pixbuf) > height)
560 GdkPixbuf *temp_pixbuf;
561 int scale;
563 scale = height - (2 * ICON_MARGIN);
565 temp_pixbuf = gdk_pixbuf_scale_simple(pixbuf, scale, scale,
566 GDK_INTERP_BILINEAR);
568 g_object_unref(pixbuf);
570 pixbuf = temp_pixbuf;
573 x = (width - gdk_pixbuf_get_width(pixbuf)) / 2;
574 y = (height - gdk_pixbuf_get_height(pixbuf)) / 2;
576 if (icon_info->hovered)
578 GdkPixbuf *temp_pixbuf;
580 temp_pixbuf = gdk_pixbuf_copy(pixbuf);
582 colorshift_pixbuf(temp_pixbuf, pixbuf, 30);
584 g_object_unref(pixbuf);
586 pixbuf = temp_pixbuf;
589 gdk_draw_pixbuf(icon_info->window, widget->style->black_gc, pixbuf,
590 0, 0, x, y, -1, -1,
591 GDK_RGB_DITHER_NORMAL, 0, 0);
593 g_object_unref(pixbuf);
596 static gint
597 sexy_icon_entry_expose(GtkWidget *widget, GdkEventExpose *event)
599 SexyIconEntry *entry;
601 g_return_val_if_fail(SEXY_IS_ICON_ENTRY(widget), FALSE);
602 g_return_val_if_fail(event != NULL, FALSE);
604 entry = SEXY_ICON_ENTRY(widget);
606 if (GTK_WIDGET_DRAWABLE(widget))
608 gboolean found = FALSE;
609 int i;
611 for (i = 0; i < MAX_ICONS && !found; i++)
613 SexyIconInfo *icon_info = &entry->priv->icons[i];
615 if (event->window == icon_info->window)
617 gint width;
618 GtkAllocation text_area_alloc;
620 get_text_area_size(entry, &text_area_alloc);
621 gdk_drawable_get_size(icon_info->window, &width, NULL);
623 gtk_paint_flat_box(widget->style, icon_info->window,
624 GTK_WIDGET_STATE(widget), GTK_SHADOW_NONE,
625 NULL, widget, "entry_bg",
626 0, 0, width, text_area_alloc.height);
628 draw_icon(widget, i);
630 found = TRUE;
634 if (!found)
635 GTK_WIDGET_CLASS(parent_class)->expose_event(widget, event);
638 return FALSE;
641 static void
642 update_icon(GObject *obj, GParamSpec *param, SexyIconEntry *entry)
644 if (param != NULL)
646 const char *name = g_param_spec_get_name(param);
648 if (strcmp(name, "pixbuf") && strcmp(name, "stock") &&
649 strcmp(name, "image") && strcmp(name, "pixmap") &&
650 strcmp(name, "icon_set") && strcmp(name, "pixbuf_animation"))
652 return;
656 gtk_widget_queue_resize(GTK_WIDGET(entry));
659 static gint
660 sexy_icon_entry_enter_notify(GtkWidget *widget, GdkEventCrossing *event)
662 SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
663 int i;
665 for (i = 0; i < MAX_ICONS; i++)
667 if (event->window == entry->priv->icons[i].window)
669 if (sexy_icon_entry_get_icon_highlight(entry, i))
671 entry->priv->icons[i].hovered = TRUE;
673 update_icon(NULL, NULL, entry);
675 break;
680 return FALSE;
683 static gint
684 sexy_icon_entry_leave_notify(GtkWidget *widget, GdkEventCrossing *event)
686 SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
687 int i;
689 for (i = 0; i < MAX_ICONS; i++)
691 if (event->window == entry->priv->icons[i].window)
693 if (sexy_icon_entry_get_icon_highlight(entry, i))
695 entry->priv->icons[i].hovered = FALSE;
697 update_icon(NULL, NULL, entry);
699 break;
704 return FALSE;
707 static gint
708 sexy_icon_entry_button_press(GtkWidget *widget, GdkEventButton *event)
710 SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
711 int i;
713 for (i = 0; i < MAX_ICONS; i++)
715 if (event->window == entry->priv->icons[i].window)
717 if (event->button == 1 &&
718 sexy_icon_entry_get_icon_highlight(entry, i))
720 entry->priv->icons[i].hovered = FALSE;
722 update_icon(NULL, NULL, entry);
725 g_signal_emit(entry, signals[ICON_PRESSED], 0, i, event->button);
727 return TRUE;
731 if (GTK_WIDGET_CLASS(parent_class)->button_press_event)
732 return GTK_WIDGET_CLASS(parent_class)->button_press_event(widget,
733 event);
735 return FALSE;
738 static gint
739 sexy_icon_entry_button_release(GtkWidget *widget, GdkEventButton *event)
741 SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
742 int i;
744 for (i = 0; i < MAX_ICONS; i++)
746 GdkWindow *icon_window = entry->priv->icons[i].window;
748 if (event->window == icon_window)
750 int width, height;
751 gdk_drawable_get_size(icon_window, &width, &height);
753 if (event->button == 1 &&
754 sexy_icon_entry_get_icon_highlight(entry, i) &&
755 event->x >= 0 && event->y >= 0 &&
756 event->x <= width && event->y <= height)
758 entry->priv->icons[i].hovered = TRUE;
760 update_icon(NULL, NULL, entry);
763 g_signal_emit(entry, signals[ICON_RELEASED], 0, i, event->button);
765 return TRUE;
769 if (GTK_WIDGET_CLASS(parent_class)->button_release_event)
770 return GTK_WIDGET_CLASS(parent_class)->button_release_event(widget,
771 event);
773 return FALSE;
777 * sexy_icon_entry_new
779 * Creates a new SexyIconEntry widget.
781 * Returns a new #SexyIconEntry.
783 GtkWidget *
784 sexy_icon_entry_new(void)
786 return GTK_WIDGET(g_object_new(SEXY_TYPE_ICON_ENTRY, NULL));
790 * sexy_icon_entry_set_icon
791 * @entry: A #SexyIconEntry.
792 * @position: Icon position.
793 * @icon: A #GtkImage to set as the icon.
795 * Sets the icon shown in the entry
797 void
798 sexy_icon_entry_set_icon(SexyIconEntry *entry, SexyIconEntryPosition icon_pos,
799 GtkImage *icon)
801 SexyIconInfo *icon_info;
803 g_return_if_fail(entry != NULL);
804 g_return_if_fail(SEXY_IS_ICON_ENTRY(entry));
805 g_return_if_fail(IS_VALID_ICON_ENTRY_POSITION(icon_pos));
806 g_return_if_fail(icon == NULL || GTK_IS_IMAGE(icon));
808 icon_info = &entry->priv->icons[icon_pos];
810 if (icon == icon_info->icon)
811 return;
813 if (icon_pos == SEXY_ICON_ENTRY_SECONDARY &&
814 entry->priv->icon_released_id != 0)
816 g_signal_handler_disconnect(entry, entry->priv->icon_released_id);
817 entry->priv->icon_released_id = 0;
820 if (icon == NULL)
822 if (icon_info->icon != NULL)
824 gtk_widget_destroy(GTK_WIDGET(icon_info->icon));
825 icon_info->icon = NULL;
828 * Explicitly check, as the pointer may become invalidated
829 * during destruction.
831 if (icon_info->window != NULL && GDK_IS_WINDOW(icon_info->window))
832 gdk_window_hide(icon_info->window);
835 else
837 if (icon_info->window != NULL && icon_info->icon == NULL)
838 gdk_window_show(icon_info->window);
840 g_signal_connect(G_OBJECT(icon), "notify",
841 G_CALLBACK(update_icon), entry);
843 icon_info->icon = icon;
844 g_object_ref(icon);
847 update_icon(NULL, NULL, entry);
851 * sexy_icon_entry_set_icon_highlight
852 * @entry: A #SexyIconEntry;
853 * @position: Icon position.
854 * @highlight: TRUE if the icon should highlight on mouse-over
856 * Determines whether the icon will highlight on mouse-over.
858 void
859 sexy_icon_entry_set_icon_highlight(SexyIconEntry *entry,
860 SexyIconEntryPosition icon_pos,
861 gboolean highlight)
863 SexyIconInfo *icon_info;
865 g_return_if_fail(entry != NULL);
866 g_return_if_fail(SEXY_IS_ICON_ENTRY(entry));
867 g_return_if_fail(IS_VALID_ICON_ENTRY_POSITION(icon_pos));
869 icon_info = &entry->priv->icons[icon_pos];
871 if (icon_info->highlight == highlight)
872 return;
874 icon_info->highlight = highlight;
878 * sexy_icon_entry_get_icon
879 * @entry: A #SexyIconEntry.
880 * @position: Icon position.
882 * Retrieves the image used for the icon
884 * Returns: A #GtkImage.
886 GtkImage *
887 sexy_icon_entry_get_icon(const SexyIconEntry *entry,
888 SexyIconEntryPosition icon_pos)
890 g_return_val_if_fail(entry != NULL, NULL);
891 g_return_val_if_fail(SEXY_IS_ICON_ENTRY(entry), NULL);
892 g_return_val_if_fail(IS_VALID_ICON_ENTRY_POSITION(icon_pos), NULL);
894 return entry->priv->icons[icon_pos].icon;
898 * sexy_icon_entry_get_icon_highlight
899 * @entry: A #SexyIconEntry.
900 * @position: Icon position.
902 * Retrieves whether entry will highlight the icon on mouseover.
904 * Returns: TRUE if icon highlights.
906 gboolean
907 sexy_icon_entry_get_icon_highlight(const SexyIconEntry *entry,
908 SexyIconEntryPosition icon_pos)
910 g_return_val_if_fail(entry != NULL, FALSE);
911 g_return_val_if_fail(SEXY_IS_ICON_ENTRY(entry), FALSE);
912 g_return_val_if_fail(IS_VALID_ICON_ENTRY_POSITION(icon_pos), FALSE);
914 return entry->priv->icons[icon_pos].highlight;
917 static void
918 clear_button_clicked_cb(SexyIconEntry *icon_entry,
919 SexyIconEntryPosition icon_pos,
920 int button)
922 if (icon_pos != SEXY_ICON_ENTRY_SECONDARY || button != 1)
923 return;
925 gtk_entry_set_text(GTK_ENTRY(icon_entry), "");
929 * sexy_icon_entry_add_clear_button
930 * @icon_entry: A #SexyIconEntry.
932 * A convenience function to add a clear button to the end of the entry.
933 * This is useful for search boxes.
935 void
936 sexy_icon_entry_add_clear_button(SexyIconEntry *icon_entry)
938 GtkWidget *icon;
940 g_return_if_fail(icon_entry != NULL);
941 g_return_if_fail(SEXY_IS_ICON_ENTRY(icon_entry));
943 icon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
944 gtk_widget_show(icon);
945 sexy_icon_entry_set_icon(SEXY_ICON_ENTRY(icon_entry),
946 SEXY_ICON_ENTRY_SECONDARY,
947 GTK_IMAGE(icon));
948 sexy_icon_entry_set_icon_highlight(SEXY_ICON_ENTRY(icon_entry),
949 SEXY_ICON_ENTRY_SECONDARY, TRUE);
951 if (icon_entry->priv->icon_released_id != 0)
953 g_signal_handler_disconnect(icon_entry,
954 icon_entry->priv->icon_released_id);
957 icon_entry->priv->icon_released_id =
958 g_signal_connect(G_OBJECT(icon_entry), "icon_released",
959 G_CALLBACK(clear_button_clicked_cb), NULL);