NetSurf
sexy_icon_entry.c
Go to the documentation of this file.
1/*
2 * libsexy/sexy-icon-entry.c Entry widget
3 * Copyright (C) 2004-2006 Christian Hammond.
4 * modified for NetSurf
5 * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
6 *
7 * This file is part of NetSurf, http://www.netsurf-browser.org/
8 * This file before modifications was originally part of LibSexy,
9 * http://www.chipx86.com/; it is redistributed under GPLv2
10 *
11 * NetSurf is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; version 2 of the License.
14 *
15 * NetSurf is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>
22 * or write to the Free Software Foundation, Inc.,
23 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 */
25
26#include <string.h>
27#include <gtk/gtk.h>
28
29#include "gtk/sexy_icon_entry.h"
30#include "gtk/compat.h"
31
32#define ICON_MARGIN 2
33#define MAX_ICONS 2
34
35#define IS_VALID_ICON_ENTRY_POSITION(pos) \
36 ((pos) == SEXY_ICON_ENTRY_PRIMARY || \
37 (pos) == SEXY_ICON_ENTRY_SECONDARY)
38
39typedef struct
40{
41 GtkImage *icon;
42 gboolean highlight;
43 gboolean hovered;
44 GdkWindow *window;
45
47
49{
51
53};
54
55enum
56{
60};
61
62/* static void sexy_icon_entry_class_init(SexyIconEntryClass *klass); */
63static void sexy_icon_entry_editable_init(GtkEditableClass *iface);
64/* static void sexy_icon_entry_init(SexyIconEntry *entry); */
65static void sexy_icon_entry_finalize(GObject *obj);
66static void sexy_icon_entry_destroy(GtkObject *obj);
67static void sexy_icon_entry_map(GtkWidget *widget);
68static void sexy_icon_entry_unmap(GtkWidget *widget);
69static void sexy_icon_entry_realize(GtkWidget *widget);
70static void sexy_icon_entry_unrealize(GtkWidget *widget);
71static void sexy_icon_entry_size_request(GtkWidget *widget,
72 GtkRequisition *requisition);
73static void sexy_icon_entry_size_allocate(GtkWidget *widget,
74 GtkAllocation *allocation);
75static gint sexy_icon_entry_expose(GtkWidget *widget, GdkEventExpose *event);
76static gint sexy_icon_entry_enter_notify(GtkWidget *widget,
77 GdkEventCrossing *event);
78static gint sexy_icon_entry_leave_notify(GtkWidget *widget,
79 GdkEventCrossing *event);
80static gint sexy_icon_entry_button_press(GtkWidget *widget,
81 GdkEventButton *event);
82static gint sexy_icon_entry_button_release(GtkWidget *widget,
83 GdkEventButton *event);
84
85static GtkEntryClass *parent_class = NULL;
86static guint signals[LAST_SIGNAL] = {0};
87
88G_DEFINE_TYPE_EXTENDED(SexyIconEntry, sexy_icon_entry, GTK_TYPE_ENTRY,
89 0,
90 G_IMPLEMENT_INTERFACE(GTK_TYPE_EDITABLE,
92
93void
95{
96 GObjectClass *gobject_class;
97 GtkObjectClass *object_class;
98 GtkWidgetClass *widget_class;
99 GtkEntryClass *entry_class;
100
101 parent_class = g_type_class_peek_parent(klass);
102
103 gobject_class = G_OBJECT_CLASS(klass);
104 object_class = GTK_OBJECT_CLASS(klass);
105 widget_class = GTK_WIDGET_CLASS(klass);
106 entry_class = GTK_ENTRY_CLASS(klass);
107
108 gobject_class->finalize = sexy_icon_entry_finalize;
109
110 object_class->destroy = sexy_icon_entry_destroy;
111
112 widget_class->map = sexy_icon_entry_map;
113 widget_class->unmap = sexy_icon_entry_unmap;
114 widget_class->realize = sexy_icon_entry_realize;
115 widget_class->unrealize = sexy_icon_entry_unrealize;
116 widget_class->size_request = sexy_icon_entry_size_request;
117 widget_class->size_allocate = sexy_icon_entry_size_allocate;
118 widget_class->expose_event = sexy_icon_entry_expose;
119 widget_class->enter_notify_event = sexy_icon_entry_enter_notify;
120 widget_class->leave_notify_event = sexy_icon_entry_leave_notify;
121 widget_class->button_press_event = sexy_icon_entry_button_press;
122 widget_class->button_release_event = sexy_icon_entry_button_release;
123
124 /*
125 * SexyIconEntry::icon-pressed:
126 * @entry: The entry on which the signal is emitted.
127 * @icon_pos: The position of the clicked icon.
128 * @button: The mouse button clicked.
129 *
130 * The ::icon-pressed signal is emitted when an icon is clicked.
131 */
132 /* signal modified to compile directly in NetSurf - param 8 of
133 * g_signal_new() changed from marshal type to NULL - so there may
134 * well be no working signal */
136 g_signal_new("icon_pressed",
137 G_TYPE_FROM_CLASS(gobject_class),
138 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
139 G_STRUCT_OFFSET(SexyIconEntryClass, icon_pressed),
140 NULL, NULL,
141 NULL,
142 G_TYPE_NONE, 2,
143 G_TYPE_INT,
144 G_TYPE_INT);
145
146 /*
147 * SexyIconEntry::icon-released:
148 * @entry: The entry on which the signal is emitted.
149 * @icon_pos: The position of the clicked icon.
150 * @button: The mouse button clicked.
151 *
152 * The ::icon-released signal is emitted on the button release from a
153 * mouse click.
154 */
155 /* signal modified to compile directly in NetSurf - param 8 of
156 * g_signal_new() changed from marshal type to NULL - so there may
157 * well be no working signal */
159 g_signal_new("icon_released",
160 G_TYPE_FROM_CLASS(gobject_class),
161 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
162 G_STRUCT_OFFSET(SexyIconEntryClass, icon_released),
163 NULL, NULL,
164 NULL,
165 G_TYPE_NONE, 2,
166 G_TYPE_INT,
167 G_TYPE_INT);
168}
169
170static void
171sexy_icon_entry_editable_init(GtkEditableClass *iface)
172{
173};
174
175void
177{
178 entry->priv = g_new0(SexyIconEntryPriv, 1);
179}
180
181static void
183{
184 SexyIconEntry *entry;
185
186 g_return_if_fail(obj != NULL);
187 g_return_if_fail(SEXY_IS_ICON_ENTRY(obj));
188
189 entry = SEXY_ICON_ENTRY(obj);
190
191 g_free(entry->priv);
192
193 if (G_OBJECT_CLASS(parent_class)->finalize)
194 G_OBJECT_CLASS(parent_class)->finalize(obj);
195}
196
197static void
199{
200 SexyIconEntry *entry;
201
202 entry = SEXY_ICON_ENTRY(obj);
203
206
207 if (GTK_OBJECT_CLASS(parent_class)->destroy)
208 GTK_OBJECT_CLASS(parent_class)->destroy(obj);
209}
210
211static void
212sexy_icon_entry_map(GtkWidget *widget)
213{
215 {
216 SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
217 int i;
218
219 GTK_WIDGET_CLASS(parent_class)->map(widget);
220
221 for (i = 0; i < MAX_ICONS; i++)
222 {
223 if (entry->priv->icons[i].icon != NULL)
224 gdk_window_show(entry->priv->icons[i].window);
225 }
226 }
227}
228
229static void
230sexy_icon_entry_unmap(GtkWidget *widget)
231{
232 if (nsgtk_widget_get_mapped(widget))
233 {
234 SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
235 int i;
236
237 for (i = 0; i < MAX_ICONS; i++)
238 {
239 if (entry->priv->icons[i].icon != NULL)
240 gdk_window_hide(entry->priv->icons[i].window);
241 }
242
243 GTK_WIDGET_CLASS(parent_class)->unmap(widget);
244 }
245}
246
247static gint
249{
250 GtkRequisition requisition;
251 gint menu_icon_width;
252 gint width;
253 SexyIconInfo *icon_info = &entry->priv->icons[icon_pos];
254
255 if (icon_info->icon == NULL)
256 return 0;
257
258 gtk_widget_size_request(GTK_WIDGET(icon_info->icon), &requisition);
259 gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &menu_icon_width, NULL);
260
261 width = MAX(requisition.width, menu_icon_width);
262
263 return width;
264}
265
266static void
267get_borders(SexyIconEntry *entry, gint *xborder, gint *yborder)
268{
269 GtkWidget *widget = GTK_WIDGET(entry);
270 gint focus_width;
271 gboolean interior_focus;
272
273 gtk_widget_style_get(widget,
274 "interior-focus", &interior_focus,
275 "focus-line-width", &focus_width,
276 NULL);
277
278 if (gtk_entry_get_has_frame(GTK_ENTRY(entry)))
279 {
280 *xborder = widget->style->xthickness;
281 *yborder = widget->style->ythickness;
282 }
283 else
284 {
285 *xborder = 0;
286 *yborder = 0;
287 }
288
289 if (!interior_focus)
290 {
291 *xborder += focus_width;
292 *yborder += focus_width;
293 }
294}
295
296static void
297get_text_area_size(SexyIconEntry *entry, GtkAllocation *alloc)
298{
299 GtkWidget *widget = GTK_WIDGET(entry);
300 GtkRequisition requisition;
301 gint xborder, yborder;
302
303 gtk_widget_get_child_requisition(widget, &requisition);
304 get_borders(entry, &xborder, &yborder);
305
306 alloc->x = xborder;
307 alloc->y = yborder;
308 alloc->width = widget->allocation.width - xborder * 2;
309 alloc->height = requisition.height - yborder * 2;
310}
311
312static void
314 gboolean left,
315 GtkAllocation *widget_alloc,
316 GtkAllocation *text_area_alloc,
317 GtkAllocation *allocation,
318 SexyIconEntryPosition *icon_pos)
319{
320 gboolean rtl;
321
322 rtl = (gtk_widget_get_direction(GTK_WIDGET(icon_entry)) ==
323 GTK_TEXT_DIR_RTL);
324
325 if (left)
327 else
329
330 allocation->y = text_area_alloc->y;
331 allocation->width = get_icon_width(icon_entry, *icon_pos);
332 allocation->height = text_area_alloc->height;
333
334 if (left)
335 allocation->x = text_area_alloc->x + ICON_MARGIN;
336 else
337 {
338 allocation->x = text_area_alloc->x + text_area_alloc->width -
339 allocation->width - ICON_MARGIN;
340 }
341}
342
343static void
344sexy_icon_entry_realize(GtkWidget *widget)
345{
346 SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
347 GdkWindowAttr attributes;
348 gint attributes_mask;
349 int i;
350
351 GTK_WIDGET_CLASS(parent_class)->realize(widget);
352
353 attributes.x = 0;
354 attributes.y = 0;
355 attributes.width = 1;
356 attributes.height = 1;
357 attributes.window_type = GDK_WINDOW_CHILD;
358 attributes.wclass = GDK_INPUT_OUTPUT;
359 attributes.visual = gtk_widget_get_visual(widget);
360 attributes.colormap = gtk_widget_get_colormap(widget);
361 attributes.event_mask = gtk_widget_get_events(widget);
362 attributes.event_mask |=
363 (GDK_EXPOSURE_MASK
364 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
365 | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
366
367 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
368
369 for (i = 0; i < MAX_ICONS; i++)
370 {
371 SexyIconInfo *icon_info;
372
373 icon_info = &entry->priv->icons[i];
374 icon_info->window = gdk_window_new(widget->window, &attributes,
375 attributes_mask);
376 gdk_window_set_user_data(icon_info->window, widget);
377
378 gdk_window_set_background(icon_info->window,
379 &widget->style->base[nsgtk_widget_get_state(widget)]);
380 }
381
382 gtk_widget_queue_resize(widget);
383}
384
385static void
387{
388 SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
389 int i;
390
391 GTK_WIDGET_CLASS(parent_class)->unrealize(widget);
392
393 for (i = 0; i < MAX_ICONS; i++)
394 {
395 SexyIconInfo *icon_info = &entry->priv->icons[i];
396
397 gdk_window_destroy(icon_info->window);
398 icon_info->window = NULL;
399 }
400}
401
402static void
403sexy_icon_entry_size_request(GtkWidget *widget, GtkRequisition *requisition)
404{
405 GtkEntry *gtkentry;
406 SexyIconEntry *entry;
407 gint icon_widths = 0;
408 int i;
409
410 gtkentry = GTK_ENTRY(widget);
411 entry = SEXY_ICON_ENTRY(widget);
412
413 for (i = 0; i < MAX_ICONS; i++)
414 {
415 int icon_width = get_icon_width(entry, i);
416
417 if (icon_width > 0)
418 icon_widths += icon_width + ICON_MARGIN;
419 }
420
421 GTK_WIDGET_CLASS(parent_class)->size_request(widget, requisition);
422
423 if (icon_widths > requisition->width)
424 requisition->width += icon_widths;
425}
426
427static void
428place_windows(SexyIconEntry *icon_entry, GtkAllocation *widget_alloc)
429{
430 SexyIconEntryPosition left_icon_pos;
431 SexyIconEntryPosition right_icon_pos;
432 GtkAllocation left_icon_alloc;
433 GtkAllocation right_icon_alloc;
434 GtkAllocation text_area_alloc;
435
436 get_text_area_size(icon_entry, &text_area_alloc);
437 get_icon_allocation(icon_entry, TRUE, widget_alloc, &text_area_alloc,
438 &left_icon_alloc, &left_icon_pos);
439 get_icon_allocation(icon_entry, FALSE, widget_alloc, &text_area_alloc,
440 &right_icon_alloc, &right_icon_pos);
441
442 if (left_icon_alloc.width > 0)
443 {
444 text_area_alloc.x = left_icon_alloc.x + left_icon_alloc.width +
446 }
447
448 if (right_icon_alloc.width > 0)
449 text_area_alloc.width -= right_icon_alloc.width + ICON_MARGIN;
450
451 text_area_alloc.width -= text_area_alloc.x;
452
453 gdk_window_move_resize(icon_entry->priv->icons[left_icon_pos].window,
454 left_icon_alloc.x, left_icon_alloc.y,
455 left_icon_alloc.width, left_icon_alloc.height);
456
457 gdk_window_move_resize(icon_entry->priv->icons[right_icon_pos].window,
458 right_icon_alloc.x, right_icon_alloc.y,
459 right_icon_alloc.width, right_icon_alloc.height);
460
461 gdk_window_move_resize(GTK_ENTRY(icon_entry)->text_area,
462 text_area_alloc.x, text_area_alloc.y,
463 text_area_alloc.width, text_area_alloc.height);
464}
465
466static void
467sexy_icon_entry_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
468{
469 g_return_if_fail(SEXY_IS_ICON_ENTRY(widget));
470 g_return_if_fail(allocation != NULL);
471
472 widget->allocation = *allocation;
473
474 GTK_WIDGET_CLASS(parent_class)->size_allocate(widget, allocation);
475
476 if (nsgtk_widget_get_realized(widget))
477 place_windows(SEXY_ICON_ENTRY(widget), allocation);
478}
479
480static GdkPixbuf *
482{
483 GdkPixbuf *pixbuf = NULL;
484 gchar *stock_id;
485 SexyIconInfo *icon_info = &entry->priv->icons[icon_pos];
486 GtkIconSize size;
487
488 switch (gtk_image_get_storage_type(GTK_IMAGE(icon_info->icon)))
489 {
490 case GTK_IMAGE_PIXBUF:
491 pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(icon_info->icon));
492 g_object_ref(pixbuf);
493 break;
494
495 case GTK_IMAGE_STOCK:
496 gtk_image_get_stock(GTK_IMAGE(icon_info->icon), &stock_id, &size);
497 pixbuf = gtk_widget_render_icon(GTK_WIDGET(entry),
498 stock_id, size, NULL);
499 break;
500
501 default:
502 return NULL;
503 }
504
505 return pixbuf;
506}
507
508/* Kudos to the gnome-panel guys. */
509static void
510colorshift_pixbuf(GdkPixbuf *dest, GdkPixbuf *src, int shift)
511{
512 gint i, j;
513 gint width, height, has_alpha, src_rowstride, dest_rowstride;
514 guchar *target_pixels;
515 guchar *original_pixels;
516 guchar *pix_src;
517 guchar *pix_dest;
518 int val;
519 guchar r, g, b;
520
521 has_alpha = gdk_pixbuf_get_has_alpha(src);
522 width = gdk_pixbuf_get_width(src);
523 height = gdk_pixbuf_get_height(src);
524 src_rowstride = gdk_pixbuf_get_rowstride(src);
525 dest_rowstride = gdk_pixbuf_get_rowstride(dest);
526 original_pixels = gdk_pixbuf_get_pixels(src);
527 target_pixels = gdk_pixbuf_get_pixels(dest);
528
529 for (i = 0; i < height; i++)
530 {
531 pix_dest = target_pixels + i * dest_rowstride;
532 pix_src = original_pixels + i * src_rowstride;
533
534 for (j = 0; j < width; j++)
535 {
536 r = *(pix_src++);
537 g = *(pix_src++);
538 b = *(pix_src++);
539
540 val = r + shift;
541 *(pix_dest++) = CLAMP(val, 0, 255);
542
543 val = g + shift;
544 *(pix_dest++) = CLAMP(val, 0, 255);
545
546 val = b + shift;
547 *(pix_dest++) = CLAMP(val, 0, 255);
548
549 if (has_alpha)
550 *(pix_dest++) = *(pix_src++);
551 }
552 }
553}
554
555static void
556draw_icon(GtkWidget *widget, SexyIconEntryPosition icon_pos)
557{
558 SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
559 SexyIconInfo *icon_info = &entry->priv->icons[icon_pos];
560 GdkPixbuf *pixbuf;
561 gint x, y, width, height;
562
563 if (icon_info->icon == NULL || !nsgtk_widget_get_realized(widget))
564 return;
565
566 if ((pixbuf = get_pixbuf_from_icon(entry, icon_pos)) == NULL)
567 return;
568
569 gdk_drawable_get_size(icon_info->window, &width, &height);
570
571 if (width == 1 || height == 1)
572 {
573 /*
574 * size_allocate hasn't been called yet. These are the default values.
575 */
576 return;
577 }
578
579 if (gdk_pixbuf_get_height(pixbuf) > height)
580 {
581 GdkPixbuf *temp_pixbuf;
582 int scale;
583
584 scale = height - (2 * ICON_MARGIN);
585
586 temp_pixbuf = gdk_pixbuf_scale_simple(pixbuf, scale, scale,
587 GDK_INTERP_BILINEAR);
588
589 g_object_unref(pixbuf);
590
591 pixbuf = temp_pixbuf;
592 }
593
594 x = (width - gdk_pixbuf_get_width(pixbuf)) / 2;
595 y = (height - gdk_pixbuf_get_height(pixbuf)) / 2;
596
597 if (icon_info->hovered)
598 {
599 GdkPixbuf *temp_pixbuf;
600
601 temp_pixbuf = gdk_pixbuf_copy(pixbuf);
602
603 colorshift_pixbuf(temp_pixbuf, pixbuf, 30);
604
605 g_object_unref(pixbuf);
606
607 pixbuf = temp_pixbuf;
608 }
609
610 gdk_draw_pixbuf(icon_info->window, widget->style->black_gc, pixbuf,
611 0, 0, x, y, -1, -1,
612 GDK_RGB_DITHER_NORMAL, 0, 0);
613
614 g_object_unref(pixbuf);
615}
616
617static gint
618sexy_icon_entry_expose(GtkWidget *widget, GdkEventExpose *event)
619{
620 SexyIconEntry *entry;
621
622 g_return_val_if_fail(SEXY_IS_ICON_ENTRY(widget), FALSE);
623 g_return_val_if_fail(event != NULL, FALSE);
624
625 entry = SEXY_ICON_ENTRY(widget);
626
627 if (nsgtk_widget_is_drawable(widget))
628 {
629 gboolean found = FALSE;
630 int i;
631
632 for (i = 0; i < MAX_ICONS && !found; i++)
633 {
634 SexyIconInfo *icon_info = &entry->priv->icons[i];
635
636 if (event->window == icon_info->window)
637 {
638 gint width;
639 GtkAllocation text_area_alloc;
640
641 get_text_area_size(entry, &text_area_alloc);
642 gdk_drawable_get_size(icon_info->window, &width, NULL);
643
644 gtk_paint_flat_box(widget->style, icon_info->window,
645 nsgtk_widget_get_state(widget), GTK_SHADOW_NONE,
646 NULL, widget, "entry_bg",
647 0, 0, width, text_area_alloc.height);
648
649 draw_icon(widget, i);
650
651 found = TRUE;
652 }
653 }
654
655 if (!found)
656 GTK_WIDGET_CLASS(parent_class)->expose_event(widget, event);
657 }
658
659 return FALSE;
660}
661
662static void
663update_icon(GObject *obj, GParamSpec *param, SexyIconEntry *entry)
664{
665 if (param != NULL)
666 {
667 const char *name = g_param_spec_get_name(param);
668
669 if (strcmp(name, "pixbuf") && strcmp(name, "stock") &&
670 strcmp(name, "image") && strcmp(name, "pixmap") &&
671 strcmp(name, "icon_set") && strcmp(name, "pixbuf_animation"))
672 {
673 return;
674 }
675 }
676
677 gtk_widget_queue_resize(GTK_WIDGET(entry));
678}
679
680static gint
681sexy_icon_entry_enter_notify(GtkWidget *widget, GdkEventCrossing *event)
682{
683 SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
684 int i;
685
686 for (i = 0; i < MAX_ICONS; i++)
687 {
688 if (event->window == entry->priv->icons[i].window)
689 {
691 {
692 entry->priv->icons[i].hovered = TRUE;
693
694 update_icon(NULL, NULL, entry);
695
696 break;
697 }
698 }
699 }
700
701 return FALSE;
702}
703
704static gint
705sexy_icon_entry_leave_notify(GtkWidget *widget, GdkEventCrossing *event)
706{
707 SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
708 int i;
709
710 for (i = 0; i < MAX_ICONS; i++)
711 {
712 if (event->window == entry->priv->icons[i].window)
713 {
715 {
716 entry->priv->icons[i].hovered = FALSE;
717
718 update_icon(NULL, NULL, entry);
719
720 break;
721 }
722 }
723 }
724
725 return FALSE;
726}
727
728static gint
729sexy_icon_entry_button_press(GtkWidget *widget, GdkEventButton *event)
730{
731 SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
732 int i;
733
734 for (i = 0; i < MAX_ICONS; i++)
735 {
736 if (event->window == entry->priv->icons[i].window)
737 {
738 if (event->button == 1 &&
740 {
741 entry->priv->icons[i].hovered = FALSE;
742
743 update_icon(NULL, NULL, entry);
744 }
745
746 g_signal_emit(entry, signals[ICON_PRESSED], 0, i, event->button);
747
748 return TRUE;
749 }
750 }
751
752 if (GTK_WIDGET_CLASS(parent_class)->button_press_event)
753 return GTK_WIDGET_CLASS(parent_class)->button_press_event(widget,
754 event);
755
756 return FALSE;
757}
758
759static gint
760sexy_icon_entry_button_release(GtkWidget *widget, GdkEventButton *event)
761{
762 SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
763 int i;
764
765 for (i = 0; i < MAX_ICONS; i++)
766 {
767 GdkWindow *icon_window = entry->priv->icons[i].window;
768
769 if (event->window == icon_window)
770 {
771 int width, height;
772 gdk_drawable_get_size(icon_window, &width, &height);
773
774 if (event->button == 1 &&
776 event->x >= 0 && event->y >= 0 &&
777 event->x <= width && event->y <= height)
778 {
779 entry->priv->icons[i].hovered = TRUE;
780
781 update_icon(NULL, NULL, entry);
782 }
783
784 g_signal_emit(entry, signals[ICON_RELEASED], 0, i, event->button);
785
786 return TRUE;
787 }
788 }
789
790 if (GTK_WIDGET_CLASS(parent_class)->button_release_event)
791 return GTK_WIDGET_CLASS(parent_class)->button_release_event(widget,
792 event);
793
794 return FALSE;
795}
796
797/*
798 * sexy_icon_entry_new
799 *
800 * Creates a new SexyIconEntry widget.
801 *
802 * Returns a new #SexyIconEntry.
803 */
804GtkWidget *
806{
807 return GTK_WIDGET(g_object_new(SEXY_TYPE_ICON_ENTRY, NULL));
808}
809
810/*
811 * sexy_icon_entry_set_icon
812 * @param entry A #SexyIconEntry.
813 * @param position Icon position.
814 * @param icon A #GtkImage to set as the icon.
815 *
816 * Sets the icon shown in the entry
817 */
818void
820 GtkImage *icon)
821{
822 SexyIconInfo *icon_info;
823
824 g_return_if_fail(entry != NULL);
825 g_return_if_fail(SEXY_IS_ICON_ENTRY(entry));
826 g_return_if_fail(IS_VALID_ICON_ENTRY_POSITION(icon_pos));
827 g_return_if_fail(icon == NULL || GTK_IS_IMAGE(icon));
828
829 icon_info = &entry->priv->icons[icon_pos];
830
831 if (icon == icon_info->icon)
832 return;
833
834 if (icon_pos == SEXY_ICON_ENTRY_SECONDARY &&
835 entry->priv->icon_released_id != 0)
836 {
837 g_signal_handler_disconnect(entry, entry->priv->icon_released_id);
838 entry->priv->icon_released_id = 0;
839 }
840
841 if (icon == NULL)
842 {
843 if (icon_info->icon != NULL)
844 {
845 gtk_widget_destroy(GTK_WIDGET(icon_info->icon));
846 icon_info->icon = NULL;
847
848 /*
849 * Explicitly check, as the pointer may become invalidated
850 * during destruction.
851 */
852 if (icon_info->window != NULL && GDK_IS_WINDOW(icon_info->window))
853 gdk_window_hide(icon_info->window);
854 }
855 }
856 else
857 {
858 if (icon_info->window != NULL && icon_info->icon == NULL)
859 gdk_window_show(icon_info->window);
860
861 g_signal_connect(G_OBJECT(icon), "notify",
862 G_CALLBACK(update_icon), entry);
863
864 icon_info->icon = icon;
865 g_object_ref(icon);
866 }
867
868 update_icon(NULL, NULL, entry);
869}
870
871/*
872 * sexy_icon_entry_set_icon_highlight
873 * @param entry A #SexyIconEntry;
874 * @param position Icon position.
875 * @param highlight TRUE if the icon should highlight on mouse-over
876 *
877 * Determines whether the icon will highlight on mouse-over.
878 */
879void
881 SexyIconEntryPosition icon_pos,
882 gboolean highlight)
883{
884 SexyIconInfo *icon_info;
885
886 g_return_if_fail(entry != NULL);
887 g_return_if_fail(SEXY_IS_ICON_ENTRY(entry));
888 g_return_if_fail(IS_VALID_ICON_ENTRY_POSITION(icon_pos));
889
890 icon_info = &entry->priv->icons[icon_pos];
891
892 if (icon_info->highlight == highlight)
893 return;
894
895 icon_info->highlight = highlight;
896}
897
898/*
899 * sexy_icon_entry_get_icon
900 * @param entry A #SexyIconEntry.
901 * @param position Icon position.
902 *
903 * Retrieves the image used for the icon
904 *
905 * Returns: A #GtkImage.
906 */
907GtkImage *
909 SexyIconEntryPosition icon_pos)
910{
911 g_return_val_if_fail(entry != NULL, NULL);
912 g_return_val_if_fail(SEXY_IS_ICON_ENTRY(entry), NULL);
913 g_return_val_if_fail(IS_VALID_ICON_ENTRY_POSITION(icon_pos), NULL);
914
915 return entry->priv->icons[icon_pos].icon;
916}
917
918/*
919 * sexy_icon_entry_get_icon_highlight
920 * @param entry A #SexyIconEntry.
921 * @param position Icon position.
922 *
923 * Retrieves whether entry will highlight the icon on mouseover.
924 *
925 * Returns: TRUE if icon highlights.
926 */
927gboolean
929 SexyIconEntryPosition icon_pos)
930{
931 g_return_val_if_fail(entry != NULL, FALSE);
932 g_return_val_if_fail(SEXY_IS_ICON_ENTRY(entry), FALSE);
933 g_return_val_if_fail(IS_VALID_ICON_ENTRY_POSITION(icon_pos), FALSE);
934
935 return entry->priv->icons[icon_pos].highlight;
936}
937
938static void
940 SexyIconEntryPosition icon_pos,
941 int button)
942{
943 if (icon_pos != SEXY_ICON_ENTRY_SECONDARY || button != 1)
944 return;
945
946 gtk_entry_set_text(GTK_ENTRY(icon_entry), "");
947}
948
949/*
950 * sexy_icon_entry_add_clear_button
951 * @param icon_entry A #SexyIconEntry.
952 *
953 * A convenience function to add a clear button to the end of the entry.
954 * This is useful for search boxes.
955 */
956void
958{
959 GtkWidget *icon;
960
961 g_return_if_fail(icon_entry != NULL);
962 g_return_if_fail(SEXY_IS_ICON_ENTRY(icon_entry));
963
965 GTK_ICON_SIZE_MENU);
966 gtk_widget_show(icon);
969 GTK_IMAGE(icon));
972
973 if (icon_entry->priv->icon_released_id != 0)
974 {
975 g_signal_handler_disconnect(icon_entry,
976 icon_entry->priv->icon_released_id);
977 }
978
979 icon_entry->priv->icon_released_id =
980 g_signal_connect(G_OBJECT(icon_entry), "icon_released",
981 G_CALLBACK(clear_button_clicked_cb), NULL);
982}
gboolean nsgtk_widget_is_drawable(GtkWidget *widget)
Definition: compat.c:96
GtkWidget * nsgtk_image_new_from_stock(const gchar *id, GtkIconSize size)
Creates a GtkImage displaying a stock icon.
Definition: compat.c:198
gboolean nsgtk_widget_get_mapped(GtkWidget *widget)
Definition: compat.c:87
gboolean nsgtk_widget_get_realized(GtkWidget *widget)
Definition: compat.c:78
Compatibility functions for older GTK versions (interface)
#define NSGTK_STOCK_CLEAR
Definition: compat.h:56
GtkStateType nsgtk_widget_get_state(GtkWidget *widget)
int width
Definition: gui.c:159
int height
Definition: gui.c:160
static void sexy_icon_entry_size_request(GtkWidget *widget, GtkRequisition *requisition)
static gint sexy_icon_entry_expose(GtkWidget *widget, GdkEventExpose *event)
static void get_icon_allocation(SexyIconEntry *icon_entry, gboolean left, GtkAllocation *widget_alloc, GtkAllocation *text_area_alloc, GtkAllocation *allocation, SexyIconEntryPosition *icon_pos)
static guint signals[LAST_SIGNAL]
static gint sexy_icon_entry_leave_notify(GtkWidget *widget, GdkEventCrossing *event)
static void clear_button_clicked_cb(SexyIconEntry *icon_entry, SexyIconEntryPosition icon_pos, int button)
static void get_borders(SexyIconEntry *entry, gint *xborder, gint *yborder)
void sexy_icon_entry_class_init(SexyIconEntryClass *klass)
#define MAX_ICONS
static void sexy_icon_entry_realize(GtkWidget *widget)
static void update_icon(GObject *obj, GParamSpec *param, SexyIconEntry *entry)
static GdkPixbuf * get_pixbuf_from_icon(SexyIconEntry *entry, SexyIconEntryPosition icon_pos)
static void sexy_icon_entry_unmap(GtkWidget *widget)
G_DEFINE_TYPE_EXTENDED(SexyIconEntry, sexy_icon_entry, GTK_TYPE_ENTRY, 0, G_IMPLEMENT_INTERFACE(GTK_TYPE_EDITABLE, sexy_icon_entry_editable_init))
static gint sexy_icon_entry_enter_notify(GtkWidget *widget, GdkEventCrossing *event)
GtkWidget * sexy_icon_entry_new(void)
static void place_windows(SexyIconEntry *icon_entry, GtkAllocation *widget_alloc)
static gint sexy_icon_entry_button_press(GtkWidget *widget, GdkEventButton *event)
void sexy_icon_entry_init(SexyIconEntry *entry)
static void sexy_icon_entry_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
#define IS_VALID_ICON_ENTRY_POSITION(pos)
gboolean sexy_icon_entry_get_icon_highlight(const SexyIconEntry *entry, SexyIconEntryPosition icon_pos)
static GtkEntryClass * parent_class
void sexy_icon_entry_set_icon(SexyIconEntry *entry, SexyIconEntryPosition icon_pos, GtkImage *icon)
static void sexy_icon_entry_map(GtkWidget *widget)
static gint sexy_icon_entry_button_release(GtkWidget *widget, GdkEventButton *event)
void sexy_icon_entry_add_clear_button(SexyIconEntry *icon_entry)
static void sexy_icon_entry_finalize(GObject *obj)
#define ICON_MARGIN
static void get_text_area_size(SexyIconEntry *entry, GtkAllocation *alloc)
static void sexy_icon_entry_destroy(GtkObject *obj)
@ LAST_SIGNAL
@ ICON_RELEASED
@ ICON_PRESSED
static gint get_icon_width(SexyIconEntry *entry, SexyIconEntryPosition icon_pos)
static void draw_icon(GtkWidget *widget, SexyIconEntryPosition icon_pos)
static void colorshift_pixbuf(GdkPixbuf *dest, GdkPixbuf *src, int shift)
static void sexy_icon_entry_editable_init(GtkEditableClass *iface)
static void sexy_icon_entry_unrealize(GtkWidget *widget)
void sexy_icon_entry_set_icon_highlight(SexyIconEntry *entry, SexyIconEntryPosition icon_pos, gboolean highlight)
GtkImage * sexy_icon_entry_get_icon(const SexyIconEntry *entry, SexyIconEntryPosition icon_pos)
#define SEXY_IS_ICON_ENTRY(obj)
SexyIconEntryPosition
@ SEXY_ICON_ENTRY_SECONDARY
@ SEXY_ICON_ENTRY_PRIMARY
#define SEXY_ICON_ENTRY(obj)
#define SEXY_TYPE_ICON_ENTRY
Interface to utility string handling.
gboolean hovered
GdkWindow * window
GtkImage * icon
gboolean highlight
SexyIconInfo icons[MAX_ICONS]
SexyIconEntryPriv * priv