Integrate adding files with the file manager
[anjuta-git-plugin.git] / plugins / debug-manager / sexy-icon-entry.c
blob06d75c4641c05da74b7a45174e097041c9b2dc00
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 * @file libsexy/sexy-icon-entry.c Entry widget
5 * @Copyright (C) 2004-2006 Christian Hammond.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
22 #include <sexy-icon-entry.h>
23 #include <string.h>
24 #include <gtk/gtk.h>
26 #include "anjuta-marshal.h"
28 #define ICON_MARGIN 2
29 #define MAX_ICONS 2
31 #define IS_VALID_ICON_ENTRY_POSITION(pos) \
32 ((pos) == SEXY_ICON_ENTRY_PRIMARY || \
33 (pos) == SEXY_ICON_ENTRY_SECONDARY)
35 typedef struct
37 GtkImage *icon;
38 gboolean highlight;
39 gboolean hovered;
40 GdkWindow *window;
42 } SexyIconInfo;
44 struct _SexyIconEntryPriv
46 SexyIconInfo icons[MAX_ICONS];
48 gulong icon_released_id;
51 enum
53 ICON_PRESSED,
54 ICON_RELEASED,
55 LAST_SIGNAL
58 static void sexy_icon_entry_class_init(SexyIconEntryClass *klass);
59 static void sexy_icon_entry_editable_init(GtkEditableClass *iface);
60 static void sexy_icon_entry_init(SexyIconEntry *entry);
61 static void sexy_icon_entry_finalize(GObject *obj);
62 static void sexy_icon_entry_destroy(GtkObject *obj);
63 static void sexy_icon_entry_map(GtkWidget *widget);
64 static void sexy_icon_entry_unmap(GtkWidget *widget);
65 static void sexy_icon_entry_realize(GtkWidget *widget);
66 static void sexy_icon_entry_unrealize(GtkWidget *widget);
67 static void sexy_icon_entry_size_request(GtkWidget *widget,
68 GtkRequisition *requisition);
69 static void sexy_icon_entry_size_allocate(GtkWidget *widget,
70 GtkAllocation *allocation);
71 static gint sexy_icon_entry_expose(GtkWidget *widget, GdkEventExpose *event);
72 static gint sexy_icon_entry_enter_notify(GtkWidget *widget,
73 GdkEventCrossing *event);
74 static gint sexy_icon_entry_leave_notify(GtkWidget *widget,
75 GdkEventCrossing *event);
76 static gint sexy_icon_entry_button_press(GtkWidget *widget,
77 GdkEventButton *event);
78 static gint sexy_icon_entry_button_release(GtkWidget *widget,
79 GdkEventButton *event);
81 static GtkEntryClass *parent_class = NULL;
82 static guint signals[LAST_SIGNAL] = {0};
84 G_DEFINE_TYPE_EXTENDED(SexyIconEntry, sexy_icon_entry, GTK_TYPE_ENTRY,
86 G_IMPLEMENT_INTERFACE(GTK_TYPE_EDITABLE,
87 sexy_icon_entry_editable_init));
89 static void
90 sexy_icon_entry_class_init(SexyIconEntryClass *klass)
92 GObjectClass *gobject_class;
93 GtkObjectClass *object_class;
94 GtkWidgetClass *widget_class;
95 GtkEntryClass *entry_class;
97 parent_class = g_type_class_peek_parent(klass);
99 gobject_class = G_OBJECT_CLASS(klass);
100 object_class = GTK_OBJECT_CLASS(klass);
101 widget_class = GTK_WIDGET_CLASS(klass);
102 entry_class = GTK_ENTRY_CLASS(klass);
104 gobject_class->finalize = sexy_icon_entry_finalize;
106 object_class->destroy = sexy_icon_entry_destroy;
108 widget_class->map = sexy_icon_entry_map;
109 widget_class->unmap = sexy_icon_entry_unmap;
110 widget_class->realize = sexy_icon_entry_realize;
111 widget_class->unrealize = sexy_icon_entry_unrealize;
112 widget_class->size_request = sexy_icon_entry_size_request;
113 widget_class->size_allocate = sexy_icon_entry_size_allocate;
114 widget_class->expose_event = sexy_icon_entry_expose;
115 widget_class->enter_notify_event = sexy_icon_entry_enter_notify;
116 widget_class->leave_notify_event = sexy_icon_entry_leave_notify;
117 widget_class->button_press_event = sexy_icon_entry_button_press;
118 widget_class->button_release_event = sexy_icon_entry_button_release;
121 * SexyIconEntry::icon-pressed:
122 * @entry: The entry on which the signal is emitted.
123 * @icon_pos: The position of the clicked icon.
124 * @button: The mouse button clicked.
126 * The ::icon-pressed signal is emitted when an icon is clicked.
128 signals[ICON_PRESSED] =
129 g_signal_new("icon_pressed",
130 G_TYPE_FROM_CLASS(gobject_class),
131 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
132 G_STRUCT_OFFSET(SexyIconEntryClass, icon_pressed),
133 NULL, NULL,
134 anjuta_marshal_VOID__INT_INT,
135 G_TYPE_NONE, 2,
136 G_TYPE_INT,
137 G_TYPE_INT);
140 * SexyIconEntry::icon-released:
141 * @entry: The entry on which the signal is emitted.
142 * @icon_pos: The position of the clicked icon.
143 * @button: The mouse button clicked.
145 * The ::icon-released signal is emitted on the button release from a
146 * mouse click.
148 signals[ICON_RELEASED] =
149 g_signal_new("icon_released",
150 G_TYPE_FROM_CLASS(gobject_class),
151 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
152 G_STRUCT_OFFSET(SexyIconEntryClass, icon_released),
153 NULL, NULL,
154 anjuta_marshal_VOID__INT_INT,
155 G_TYPE_NONE, 2,
156 G_TYPE_INT,
157 G_TYPE_INT);
160 static void
161 sexy_icon_entry_editable_init(GtkEditableClass *iface)
165 static void
166 sexy_icon_entry_init(SexyIconEntry *entry)
168 entry->priv = g_new0(SexyIconEntryPriv, 1);
171 static void
172 sexy_icon_entry_finalize(GObject *obj)
174 SexyIconEntry *entry;
176 g_return_if_fail(obj != NULL);
177 g_return_if_fail(SEXY_IS_ICON_ENTRY(obj));
179 entry = SEXY_ICON_ENTRY(obj);
181 g_free(entry->priv);
183 if (G_OBJECT_CLASS(parent_class)->finalize)
184 G_OBJECT_CLASS(parent_class)->finalize(obj);
187 static void
188 sexy_icon_entry_destroy(GtkObject *obj)
190 SexyIconEntry *entry;
192 entry = SEXY_ICON_ENTRY(obj);
194 sexy_icon_entry_set_icon(entry, SEXY_ICON_ENTRY_PRIMARY, NULL);
195 sexy_icon_entry_set_icon(entry, SEXY_ICON_ENTRY_SECONDARY, NULL);
197 if (GTK_OBJECT_CLASS(parent_class)->destroy)
198 GTK_OBJECT_CLASS(parent_class)->destroy(obj);
201 static void
202 sexy_icon_entry_map(GtkWidget *widget)
204 if (GTK_WIDGET_REALIZED(widget) && !GTK_WIDGET_MAPPED(widget))
206 SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
207 int i;
209 GTK_WIDGET_CLASS(parent_class)->map(widget);
211 for (i = 0; i < MAX_ICONS; i++)
213 if (entry->priv->icons[i].icon != NULL)
214 gdk_window_show(entry->priv->icons[i].window);
219 static void
220 sexy_icon_entry_unmap(GtkWidget *widget)
222 if (GTK_WIDGET_MAPPED(widget))
224 SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
225 int i;
227 for (i = 0; i < MAX_ICONS; i++)
229 if (entry->priv->icons[i].icon != NULL)
230 gdk_window_hide(entry->priv->icons[i].window);
233 GTK_WIDGET_CLASS(parent_class)->unmap(widget);
237 static gint
238 get_icon_width(SexyIconEntry *entry, SexyIconEntryPosition icon_pos)
240 GtkRequisition requisition;
241 gint menu_icon_width;
242 gint width;
243 SexyIconInfo *icon_info = &entry->priv->icons[icon_pos];
245 if (icon_info->icon == NULL)
246 return 0;
248 gtk_widget_size_request(GTK_WIDGET(icon_info->icon), &requisition);
249 gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &menu_icon_width, NULL);
251 width = MAX(requisition.width, menu_icon_width);
253 return width;
256 static void
257 get_borders(SexyIconEntry *entry, gint *xborder, gint *yborder)
259 GtkWidget *widget = GTK_WIDGET(entry);
260 gint focus_width;
261 gboolean interior_focus;
263 gtk_widget_style_get(widget,
264 "interior-focus", &interior_focus,
265 "focus-line-width", &focus_width,
266 NULL);
268 if (gtk_entry_get_has_frame(GTK_ENTRY(entry)))
270 *xborder = widget->style->xthickness;
271 *yborder = widget->style->ythickness;
273 else
275 *xborder = 0;
276 *yborder = 0;
279 if (!interior_focus)
281 *xborder += focus_width;
282 *yborder += focus_width;
286 static void
287 get_text_area_size(SexyIconEntry *entry, GtkAllocation *alloc)
289 GtkWidget *widget = GTK_WIDGET(entry);
290 GtkRequisition requisition;
291 gint xborder, yborder;
293 gtk_widget_get_child_requisition(widget, &requisition);
294 get_borders(entry, &xborder, &yborder);
296 alloc->x = xborder;
297 alloc->y = yborder;
298 alloc->width = widget->allocation.width - xborder * 2;
299 alloc->height = requisition.height - yborder * 2;
302 static void
303 get_icon_allocation(SexyIconEntry *icon_entry,
304 gboolean left,
305 GtkAllocation *widget_alloc,
306 GtkAllocation *text_area_alloc,
307 GtkAllocation *allocation,
308 SexyIconEntryPosition *icon_pos)
310 gboolean rtl;
312 rtl = (gtk_widget_get_direction(GTK_WIDGET(icon_entry)) ==
313 GTK_TEXT_DIR_RTL);
315 if (left)
316 *icon_pos = (rtl ? SEXY_ICON_ENTRY_SECONDARY : SEXY_ICON_ENTRY_PRIMARY);
317 else
318 *icon_pos = (rtl ? SEXY_ICON_ENTRY_PRIMARY : SEXY_ICON_ENTRY_SECONDARY);
320 allocation->y = text_area_alloc->y;
321 allocation->width = get_icon_width(icon_entry, *icon_pos);
322 allocation->height = text_area_alloc->height;
324 if (left)
325 allocation->x = text_area_alloc->x + ICON_MARGIN;
326 else
328 allocation->x = text_area_alloc->x + text_area_alloc->width -
329 allocation->width - ICON_MARGIN;
333 static void
334 sexy_icon_entry_realize(GtkWidget *widget)
336 SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
337 GdkWindowAttr attributes;
338 gint attributes_mask;
339 int i;
341 GTK_WIDGET_CLASS(parent_class)->realize(widget);
343 attributes.x = 0;
344 attributes.y = 0;
345 attributes.width = 1;
346 attributes.height = 1;
347 attributes.window_type = GDK_WINDOW_CHILD;
348 attributes.wclass = GDK_INPUT_OUTPUT;
349 attributes.visual = gtk_widget_get_visual(widget);
350 attributes.colormap = gtk_widget_get_colormap(widget);
351 attributes.event_mask = gtk_widget_get_events(widget);
352 attributes.event_mask |=
353 (GDK_EXPOSURE_MASK
354 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
355 | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
357 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
359 for (i = 0; i < MAX_ICONS; i++)
361 SexyIconInfo *icon_info;
363 icon_info = &entry->priv->icons[i];
364 icon_info->window = gdk_window_new(widget->window, &attributes,
365 attributes_mask);
366 gdk_window_set_user_data(icon_info->window, widget);
368 gdk_window_set_background(icon_info->window,
369 &widget->style->base[GTK_WIDGET_STATE(widget)]);
372 gtk_widget_queue_resize(widget);
375 static void
376 sexy_icon_entry_unrealize(GtkWidget *widget)
378 SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
379 int i;
381 GTK_WIDGET_CLASS(parent_class)->unrealize(widget);
383 for (i = 0; i < MAX_ICONS; i++)
385 SexyIconInfo *icon_info = &entry->priv->icons[i];
387 gdk_window_destroy(icon_info->window);
388 icon_info->window = NULL;
392 static void
393 sexy_icon_entry_size_request(GtkWidget *widget, GtkRequisition *requisition)
395 GtkEntry *gtkentry;
396 SexyIconEntry *entry;
397 gint icon_widths = 0;
398 int i;
400 gtkentry = GTK_ENTRY(widget);
401 entry = SEXY_ICON_ENTRY(widget);
403 for (i = 0; i < MAX_ICONS; i++)
405 int icon_width = get_icon_width(entry, i);
407 if (icon_width > 0)
408 icon_widths += icon_width + ICON_MARGIN;
411 GTK_WIDGET_CLASS(parent_class)->size_request(widget, requisition);
413 if (icon_widths > requisition->width)
414 requisition->width += icon_widths;
417 static void
418 place_windows(SexyIconEntry *icon_entry, GtkAllocation *widget_alloc)
420 SexyIconEntryPosition left_icon_pos;
421 SexyIconEntryPosition right_icon_pos;
422 GtkAllocation left_icon_alloc;
423 GtkAllocation right_icon_alloc;
424 GtkAllocation text_area_alloc;
426 get_text_area_size(icon_entry, &text_area_alloc);
427 get_icon_allocation(icon_entry, TRUE, widget_alloc, &text_area_alloc,
428 &left_icon_alloc, &left_icon_pos);
429 get_icon_allocation(icon_entry, FALSE, widget_alloc, &text_area_alloc,
430 &right_icon_alloc, &right_icon_pos);
432 if (left_icon_alloc.width > 0)
434 text_area_alloc.x = left_icon_alloc.x + left_icon_alloc.width +
435 ICON_MARGIN;
438 if (right_icon_alloc.width > 0)
439 text_area_alloc.width -= right_icon_alloc.width + ICON_MARGIN;
441 text_area_alloc.width -= text_area_alloc.x;
443 gdk_window_move_resize(icon_entry->priv->icons[left_icon_pos].window,
444 left_icon_alloc.x, left_icon_alloc.y,
445 left_icon_alloc.width, left_icon_alloc.height);
447 gdk_window_move_resize(icon_entry->priv->icons[right_icon_pos].window,
448 right_icon_alloc.x, right_icon_alloc.y,
449 right_icon_alloc.width, right_icon_alloc.height);
451 gdk_window_move_resize(GTK_ENTRY(icon_entry)->text_area,
452 text_area_alloc.x, text_area_alloc.y,
453 text_area_alloc.width, text_area_alloc.height);
456 static void
457 sexy_icon_entry_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
459 g_return_if_fail(SEXY_IS_ICON_ENTRY(widget));
460 g_return_if_fail(allocation != NULL);
462 widget->allocation = *allocation;
464 GTK_WIDGET_CLASS(parent_class)->size_allocate(widget, allocation);
466 if (GTK_WIDGET_REALIZED(widget))
467 place_windows(SEXY_ICON_ENTRY(widget), allocation);
470 static GdkPixbuf *
471 get_pixbuf_from_icon(SexyIconEntry *entry, SexyIconEntryPosition icon_pos)
473 GdkPixbuf *pixbuf = NULL;
474 const gchar *stock_id;
475 SexyIconInfo *icon_info = &entry->priv->icons[icon_pos];
476 GtkIconSize size;
477 int w, h;
479 switch (gtk_image_get_storage_type(GTK_IMAGE(icon_info->icon)))
481 case GTK_IMAGE_PIXBUF:
482 pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(icon_info->icon));
483 g_object_ref(pixbuf);
484 break;
486 case GTK_IMAGE_STOCK:
487 gtk_image_get_stock(GTK_IMAGE(icon_info->icon), (char**)&stock_id, &size);
488 pixbuf = gtk_widget_render_icon(GTK_WIDGET(entry),
489 stock_id, size, NULL);
490 break;
492 case GTK_IMAGE_ICON_NAME:
493 gtk_image_get_icon_name (GTK_IMAGE(icon_info->icon), &stock_id, &size);
494 gtk_icon_size_lookup (size, &w, &h);
495 pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (), stock_id, size, 0, NULL);
496 break;
498 default:
499 return NULL;
502 return pixbuf;
505 /* Kudos to the gnome-panel guys. */
506 static void
507 colorshift_pixbuf(GdkPixbuf *dest, GdkPixbuf *src, int shift)
509 gint i, j;
510 gint width, height, has_alpha, src_rowstride, dest_rowstride;
511 guchar *target_pixels;
512 guchar *original_pixels;
513 guchar *pix_src;
514 guchar *pix_dest;
515 int val;
516 guchar r, g, b;
518 has_alpha = gdk_pixbuf_get_has_alpha(src);
519 width = gdk_pixbuf_get_width(src);
520 height = gdk_pixbuf_get_height(src);
521 src_rowstride = gdk_pixbuf_get_rowstride(src);
522 dest_rowstride = gdk_pixbuf_get_rowstride(dest);
523 original_pixels = gdk_pixbuf_get_pixels(src);
524 target_pixels = gdk_pixbuf_get_pixels(dest);
526 for (i = 0; i < height; i++)
528 pix_dest = target_pixels + i * dest_rowstride;
529 pix_src = original_pixels + i * src_rowstride;
531 for (j = 0; j < width; j++)
533 r = *(pix_src++);
534 g = *(pix_src++);
535 b = *(pix_src++);
537 val = r + shift;
538 *(pix_dest++) = CLAMP(val, 0, 255);
540 val = g + shift;
541 *(pix_dest++) = CLAMP(val, 0, 255);
543 val = b + shift;
544 *(pix_dest++) = CLAMP(val, 0, 255);
546 if (has_alpha)
547 *(pix_dest++) = *(pix_src++);
552 static void
553 draw_icon(GtkWidget *widget, SexyIconEntryPosition icon_pos)
555 SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
556 SexyIconInfo *icon_info = &entry->priv->icons[icon_pos];
557 GdkPixbuf *pixbuf;
558 gint x, y, width, height;
560 if (icon_info->icon == NULL || !GTK_WIDGET_REALIZED(widget))
561 return;
563 if ((pixbuf = get_pixbuf_from_icon(entry, icon_pos)) == NULL)
564 return;
566 gdk_drawable_get_size(icon_info->window, &width, &height);
568 if (width == 1 || height == 1)
571 * size_allocate hasn't been called yet. These are the default values.
573 return;
576 if (gdk_pixbuf_get_height(pixbuf) > height)
578 GdkPixbuf *temp_pixbuf;
579 int scale;
581 scale = height - (2 * ICON_MARGIN);
583 temp_pixbuf = gdk_pixbuf_scale_simple(pixbuf, scale, scale,
584 GDK_INTERP_BILINEAR);
586 g_object_unref(pixbuf);
588 pixbuf = temp_pixbuf;
591 x = (width - gdk_pixbuf_get_width(pixbuf)) / 2;
592 y = (height - gdk_pixbuf_get_height(pixbuf)) / 2;
594 if (icon_info->hovered)
596 GdkPixbuf *temp_pixbuf;
598 temp_pixbuf = gdk_pixbuf_copy(pixbuf);
600 colorshift_pixbuf(temp_pixbuf, pixbuf, 30);
602 g_object_unref(pixbuf);
604 pixbuf = temp_pixbuf;
607 gdk_draw_pixbuf(icon_info->window, widget->style->black_gc, pixbuf,
608 0, 0, x, y, -1, -1,
609 GDK_RGB_DITHER_NORMAL, 0, 0);
611 g_object_unref(pixbuf);
614 static gint
615 sexy_icon_entry_expose(GtkWidget *widget, GdkEventExpose *event)
617 SexyIconEntry *entry;
619 g_return_val_if_fail(SEXY_IS_ICON_ENTRY(widget), FALSE);
620 g_return_val_if_fail(event != NULL, FALSE);
622 entry = SEXY_ICON_ENTRY(widget);
624 if (GTK_WIDGET_DRAWABLE(widget))
626 gboolean found = FALSE;
627 int i;
629 for (i = 0; i < MAX_ICONS && !found; i++)
631 SexyIconInfo *icon_info = &entry->priv->icons[i];
633 if (event->window == icon_info->window)
635 gint width;
636 GtkAllocation text_area_alloc;
638 get_text_area_size(entry, &text_area_alloc);
639 gdk_drawable_get_size(icon_info->window, &width, NULL);
641 gtk_paint_flat_box(widget->style, icon_info->window,
642 GTK_WIDGET_STATE(widget), GTK_SHADOW_NONE,
643 NULL, widget, "entry_bg",
644 0, 0, width, text_area_alloc.height);
646 draw_icon(widget, i);
648 found = TRUE;
652 if (!found)
653 GTK_WIDGET_CLASS(parent_class)->expose_event(widget, event);
656 return FALSE;
659 static void
660 update_icon(GObject *obj, GParamSpec *param, SexyIconEntry *entry)
662 if (param != NULL)
664 const char *name = g_param_spec_get_name(param);
666 if (strcmp(name, "pixbuf") && strcmp(name, "stock") &&
667 strcmp(name, "image") && strcmp(name, "pixmap") &&
668 strcmp(name, "icon-set") && strcmp(name, "pixbuf-animation") &&
669 strcmp(name, "icon-name"))
671 return;
675 gtk_widget_queue_resize(GTK_WIDGET(entry));
678 static gint
679 sexy_icon_entry_enter_notify(GtkWidget *widget, GdkEventCrossing *event)
681 SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
682 int i;
684 for (i = 0; i < MAX_ICONS; i++)
686 if (event->window == entry->priv->icons[i].window)
688 if (sexy_icon_entry_get_icon_highlight(entry, i))
690 entry->priv->icons[i].hovered = TRUE;
692 update_icon(NULL, NULL, entry);
694 break;
699 return FALSE;
702 static gint
703 sexy_icon_entry_leave_notify(GtkWidget *widget, GdkEventCrossing *event)
705 SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
706 int i;
708 for (i = 0; i < MAX_ICONS; i++)
710 if (event->window == entry->priv->icons[i].window)
712 if (sexy_icon_entry_get_icon_highlight(entry, i))
714 entry->priv->icons[i].hovered = FALSE;
716 update_icon(NULL, NULL, entry);
718 break;
723 return FALSE;
726 static gint
727 sexy_icon_entry_button_press(GtkWidget *widget, GdkEventButton *event)
729 SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
730 int i;
732 for (i = 0; i < MAX_ICONS; i++)
734 if (event->window == entry->priv->icons[i].window)
736 if (event->button == 1 &&
737 sexy_icon_entry_get_icon_highlight(entry, i))
739 entry->priv->icons[i].hovered = FALSE;
741 update_icon(NULL, NULL, entry);
744 g_signal_emit(entry, signals[ICON_PRESSED], 0, i, event->button);
746 return TRUE;
750 if (GTK_WIDGET_CLASS(parent_class)->button_press_event)
751 return GTK_WIDGET_CLASS(parent_class)->button_press_event(widget,
752 event);
754 return FALSE;
757 static gint
758 sexy_icon_entry_button_release(GtkWidget *widget, GdkEventButton *event)
760 SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
761 int i;
763 for (i = 0; i < MAX_ICONS; i++)
765 GdkWindow *icon_window = entry->priv->icons[i].window;
767 if (event->window == icon_window)
769 int width, height;
770 gdk_drawable_get_size(icon_window, &width, &height);
772 if (event->button == 1 &&
773 sexy_icon_entry_get_icon_highlight(entry, i) &&
774 event->x >= 0 && event->y >= 0 &&
775 event->x <= width && event->y <= height)
777 entry->priv->icons[i].hovered = TRUE;
779 update_icon(NULL, NULL, entry);
782 g_signal_emit(entry, signals[ICON_RELEASED], 0, i, event->button);
784 return TRUE;
788 if (GTK_WIDGET_CLASS(parent_class)->button_release_event)
789 return GTK_WIDGET_CLASS(parent_class)->button_release_event(widget,
790 event);
792 return FALSE;
796 * sexy_icon_entry_new
798 * Creates a new SexyIconEntry widget.
800 * Returns a new #SexyIconEntry.
802 GtkWidget *
803 sexy_icon_entry_new(void)
805 return GTK_WIDGET(g_object_new(SEXY_TYPE_ICON_ENTRY, NULL));
809 * sexy_icon_entry_set_icon
810 * @entry: A #SexyIconEntry.
811 * @position: Icon position.
812 * @icon: A #GtkImage to set as the icon.
814 * Sets the icon shown in the entry
816 void
817 sexy_icon_entry_set_icon(SexyIconEntry *entry, SexyIconEntryPosition icon_pos,
818 GtkImage *icon)
820 SexyIconInfo *icon_info;
822 g_return_if_fail(entry != NULL);
823 g_return_if_fail(SEXY_IS_ICON_ENTRY(entry));
824 g_return_if_fail(IS_VALID_ICON_ENTRY_POSITION(icon_pos));
825 g_return_if_fail(icon == NULL || GTK_IS_IMAGE(icon));
827 icon_info = &entry->priv->icons[icon_pos];
829 if (icon == icon_info->icon)
830 return;
832 if (icon_pos == SEXY_ICON_ENTRY_SECONDARY &&
833 entry->priv->icon_released_id != 0)
835 g_signal_handler_disconnect(entry, entry->priv->icon_released_id);
836 entry->priv->icon_released_id = 0;
839 if (icon == NULL)
841 if (icon_info->icon != NULL)
843 gtk_widget_destroy(GTK_WIDGET(icon_info->icon));
844 icon_info->icon = NULL;
847 * Explicitly check, as the pointer may become invalidated
848 * during destruction.
850 if (icon_info->window != NULL && GDK_IS_WINDOW(icon_info->window))
851 gdk_window_hide(icon_info->window);
854 else
856 if (icon_info->window != NULL && icon_info->icon == NULL)
857 gdk_window_show(icon_info->window);
859 g_signal_connect(G_OBJECT(icon), "notify",
860 G_CALLBACK(update_icon), entry);
862 icon_info->icon = icon;
863 g_object_ref(icon);
866 update_icon(NULL, NULL, entry);
870 * sexy_icon_entry_set_icon_highlight
871 * @entry: A #SexyIconEntry;
872 * @position: Icon position.
873 * @highlight: TRUE if the icon should highlight on mouse-over
875 * Determines whether the icon will highlight on mouse-over.
877 void
878 sexy_icon_entry_set_icon_highlight(SexyIconEntry *entry,
879 SexyIconEntryPosition icon_pos,
880 gboolean highlight)
882 SexyIconInfo *icon_info;
884 g_return_if_fail(entry != NULL);
885 g_return_if_fail(SEXY_IS_ICON_ENTRY(entry));
886 g_return_if_fail(IS_VALID_ICON_ENTRY_POSITION(icon_pos));
888 icon_info = &entry->priv->icons[icon_pos];
890 if (icon_info->highlight == highlight)
891 return;
893 icon_info->highlight = highlight;
897 * sexy_icon_entry_get_icon
898 * @entry: A #SexyIconEntry.
899 * @position: Icon position.
901 * Retrieves the image used for the icon
903 * Returns: A #GtkImage.
905 GtkImage *
906 sexy_icon_entry_get_icon(const SexyIconEntry *entry,
907 SexyIconEntryPosition icon_pos)
909 g_return_val_if_fail(entry != NULL, NULL);
910 g_return_val_if_fail(SEXY_IS_ICON_ENTRY(entry), NULL);
911 g_return_val_if_fail(IS_VALID_ICON_ENTRY_POSITION(icon_pos), NULL);
913 return entry->priv->icons[icon_pos].icon;
917 * sexy_icon_entry_get_icon_highlight
918 * @entry: A #SexyIconEntry.
919 * @position: Icon position.
921 * Retrieves whether entry will highlight the icon on mouseover.
923 * Returns: TRUE if icon highlights.
925 gboolean
926 sexy_icon_entry_get_icon_highlight(const SexyIconEntry *entry,
927 SexyIconEntryPosition icon_pos)
929 g_return_val_if_fail(entry != NULL, FALSE);
930 g_return_val_if_fail(SEXY_IS_ICON_ENTRY(entry), FALSE);
931 g_return_val_if_fail(IS_VALID_ICON_ENTRY_POSITION(icon_pos), FALSE);
933 return entry->priv->icons[icon_pos].highlight;
936 static void
937 clear_button_clicked_cb(SexyIconEntry *icon_entry,
938 SexyIconEntryPosition icon_pos,
939 int button)
941 if (icon_pos != SEXY_ICON_ENTRY_SECONDARY || button != 1)
942 return;
944 gtk_entry_set_text(GTK_ENTRY(icon_entry), "");
948 * sexy_icon_entry_add_clear_button
949 * @icon_entry: A #SexyIconEntry.
951 * A convenience function to add a clear button to the end of the entry.
952 * This is useful for search boxes.
954 void
955 sexy_icon_entry_add_clear_button(SexyIconEntry *icon_entry)
957 GtkWidget *icon;
959 g_return_if_fail(icon_entry != NULL);
960 g_return_if_fail(SEXY_IS_ICON_ENTRY(icon_entry));
962 icon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
963 gtk_widget_show(icon);
964 sexy_icon_entry_set_icon(SEXY_ICON_ENTRY(icon_entry),
965 SEXY_ICON_ENTRY_SECONDARY,
966 GTK_IMAGE(icon));
967 sexy_icon_entry_set_icon_highlight(SEXY_ICON_ENTRY(icon_entry),
968 SEXY_ICON_ENTRY_SECONDARY, TRUE);
970 if (icon_entry->priv->icon_released_id != 0)
972 g_signal_handler_disconnect(icon_entry,
973 icon_entry->priv->icon_released_id);
976 icon_entry->priv->icon_released_id =
977 g_signal_connect(G_OBJECT(icon_entry), "icon_released",
978 G_CALLBACK(clear_button_clicked_cb), NULL);