source: trunk/gtk/sexy-icon-entry.c @ 6998

Last change on this file since 6998 was 6998, checked in by charles, 14 years ago

(gtk) don't use code marked as deprecated in gtk 2.14

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