2
* Copyright (c) 2011 Red Hat, Inc.
4
* This program is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU Lesser General Public License as published by
6
* the Free Software Foundation; either version 2 of the License, or (at your
7
* option) any later version.
9
* This program is distributed in the hope that it will be useful, but
10
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12
* License for more details.
14
* You should have received a copy of the GNU Lesser General Public License
15
* along with this program; if not, write to the Free Software Foundation,
16
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
* Author: Cosimo Cecchi <cosimoc@redhat.com>
22
#include "gd-tagged-entry.h"
26
G_DEFINE_TYPE (GdTaggedEntry, gd_tagged_entry, GTK_TYPE_SEARCH_ENTRY)
28
#define BUTTON_INTERNAL_SPACING 6
37
GdkPixbuf *close_pixbuf;
38
GtkStateFlags last_button_state;
41
struct _GdTaggedEntryPrivate {
44
GdTaggedEntryTag *in_child;
45
gboolean in_child_button;
46
gboolean in_child_active;
47
gboolean in_child_button_active;
48
gboolean button_visible;
53
SIGNAL_TAG_BUTTON_CLICKED,
59
PROP_TAG_BUTTON_VISIBLE,
63
static guint signals[LAST_SIGNAL] = { 0, };
64
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
66
static void gd_tagged_entry_get_text_area_size (GtkEntry *entry,
71
static gint gd_tagged_entry_tag_get_width (GdTaggedEntryTag *tag,
72
GdTaggedEntry *entry);
73
static GtkStyleContext * gd_tagged_entry_tag_get_context (GdTaggedEntry *entry);
76
gd_tagged_entry_tag_get_margin (GdTaggedEntry *entry,
79
GtkStyleContext *context;
81
context = gd_tagged_entry_tag_get_context (entry);
82
gtk_style_context_get_margin (context, 0, margin);
83
g_object_unref (context);
87
gd_tagged_entry_tag_ensure_close_pixbuf (GdTaggedEntryTag *tag,
88
GtkStyleContext *context)
93
if (tag->close_pixbuf != NULL)
96
gtk_icon_size_lookup (GTK_ICON_SIZE_MENU,
99
info = gtk_icon_theme_lookup_icon (gtk_icon_theme_get_default (),
100
"window-close-symbolic",
102
GTK_ICON_LOOKUP_GENERIC_FALLBACK);
105
gtk_icon_info_load_symbolic_for_context (info, context,
108
/* FIXME: we need a fallback icon in case the icon is not found */
112
gd_tagged_entry_tag_panel_get_height (GdTaggedEntry *entry)
114
GtkWidget *widget = GTK_WIDGET (entry);
115
gint height, req_height;
116
GtkRequisition requisition;
117
GtkAllocation allocation;
120
gtk_widget_get_allocation (widget, &allocation);
121
gtk_widget_get_preferred_size (widget, &requisition, NULL);
122
gd_tagged_entry_tag_get_margin (entry, &margin);
124
/* the tag panel height is the whole entry height, minus the tag margins */
125
req_height = requisition.height - gtk_widget_get_margin_top (widget) - gtk_widget_get_margin_bottom (widget);
126
height = MIN (req_height, allocation.height) - margin.top - margin.bottom;
132
gd_tagged_entry_tag_panel_get_position (GdTaggedEntry *self,
136
GtkWidget *widget = GTK_WIDGET (self);
137
gint text_x, text_y, text_width, text_height, req_height;
138
GtkAllocation allocation;
139
GtkRequisition requisition;
142
gtk_widget_get_allocation (widget, &allocation);
143
gtk_widget_get_preferred_size (widget, &requisition, NULL);
144
req_height = requisition.height - gtk_widget_get_margin_top (widget) - gtk_widget_get_margin_bottom (widget);
146
gd_tagged_entry_get_text_area_size (GTK_ENTRY (self), &text_x, &text_y, &text_width, &text_height);
147
gd_tagged_entry_tag_get_margin (self, &margin);
149
/* allocate the panel immediately after the text area */
151
*x_out = allocation.x + text_x + text_width;
153
*y_out = allocation.y + margin.top + (gint) floor ((allocation.height - req_height) / 2);
157
gd_tagged_entry_tag_panel_get_width (GdTaggedEntry *self)
159
GdTaggedEntryTag *tag;
165
for (l = self->priv->tags; l != NULL; l = l->next)
168
width += gd_tagged_entry_tag_get_width (tag, self);
175
gd_tagged_entry_tag_ensure_layout (GdTaggedEntryTag *tag,
176
GdTaggedEntry *entry)
178
if (tag->layout != NULL)
181
tag->layout = pango_layout_new (gtk_widget_get_pango_context (GTK_WIDGET (entry)));
182
pango_layout_set_text (tag->layout, tag->label, -1);
186
gd_tagged_entry_tag_get_state (GdTaggedEntryTag *tag,
187
GdTaggedEntry *entry)
189
GtkStateFlags state = GTK_STATE_FLAG_NORMAL;
191
if (entry->priv->in_child == tag)
192
state |= GTK_STATE_FLAG_PRELIGHT;
194
if (entry->priv->in_child_active)
195
state |= GTK_STATE_FLAG_ACTIVE;
201
gd_tagged_entry_tag_get_button_state (GdTaggedEntryTag *tag,
202
GdTaggedEntry *entry)
204
GtkStateFlags state = GTK_STATE_FLAG_NORMAL;
206
if (entry->priv->in_child == tag &&
207
entry->priv->in_child_button)
208
state |= GTK_STATE_FLAG_PRELIGHT;
210
if (entry->priv->in_child_button_active)
211
state |= GTK_STATE_FLAG_ACTIVE;
216
static GtkStyleContext *
217
gd_tagged_entry_tag_get_context (GdTaggedEntry *entry)
219
GtkWidget *widget = GTK_WIDGET (entry);
222
GtkStyleContext *retval;
224
retval = gtk_style_context_new ();
225
path = gtk_widget_path_copy (gtk_widget_get_path (widget));
227
pos = gtk_widget_path_append_type (path, GD_TYPE_TAGGED_ENTRY);
228
gtk_widget_path_iter_add_class (path, pos, "documents-entry-tag");
230
gtk_style_context_set_path (retval, path);
232
gtk_widget_path_unref (path);
238
gd_tagged_entry_tag_get_width (GdTaggedEntryTag *tag,
239
GdTaggedEntry *entry)
241
GtkBorder button_padding, button_border, button_margin;
242
GtkStyleContext *context;
247
gd_tagged_entry_tag_ensure_layout (tag, entry);
248
pango_layout_get_pixel_size (tag->layout, &layout_width, NULL);
250
context = gd_tagged_entry_tag_get_context (entry);
251
state = gd_tagged_entry_tag_get_state (tag, entry);
253
gtk_style_context_get_padding (context, state, &button_padding);
254
gtk_style_context_get_border (context, state, &button_border);
255
gtk_style_context_get_margin (context, state, &button_margin);
257
gd_tagged_entry_tag_ensure_close_pixbuf (tag, context);
259
g_object_unref (context);
262
if (entry->priv->button_visible)
263
button_width = gdk_pixbuf_get_width (tag->close_pixbuf) + BUTTON_INTERNAL_SPACING;
265
return layout_width + button_padding.left + button_padding.right +
266
button_border.left + button_border.right +
267
button_margin.left + button_margin.right +
272
gd_tagged_entry_tag_get_size (GdTaggedEntryTag *tag,
273
GdTaggedEntry *entry,
277
gint width, panel_height;
279
width = gd_tagged_entry_tag_get_width (tag, entry);
280
panel_height = gd_tagged_entry_tag_panel_get_height (entry);
285
*height_out = panel_height;
289
gd_tagged_entry_tag_get_relative_allocations (GdTaggedEntryTag *tag,
290
GdTaggedEntry *entry,
291
GtkStyleContext *context,
292
GtkAllocation *background_allocation_out,
293
GtkAllocation *layout_allocation_out,
294
GtkAllocation *button_allocation_out)
296
GtkAllocation background_allocation, layout_allocation, button_allocation;
297
gint width, height, x, y, pix_width, pix_height;
298
gint layout_width, layout_height;
299
GtkBorder padding, border;
302
width = gdk_window_get_width (tag->window);
303
height = gdk_window_get_height (tag->window);
305
state = gd_tagged_entry_tag_get_state (tag, entry);
306
gtk_style_context_get_margin (context, state, &padding);
308
width -= padding.left + padding.right;
309
height -= padding.top + padding.bottom;
313
background_allocation.x = x;
314
background_allocation.y = y;
315
background_allocation.width = width;
316
background_allocation.height = height;
318
layout_allocation = button_allocation = background_allocation;
320
gtk_style_context_get_padding (context, state, &padding);
321
gtk_style_context_get_border (context, state, &border);
323
gd_tagged_entry_tag_ensure_layout (tag, entry);
324
pango_layout_get_pixel_size (tag->layout, &layout_width, &layout_height);
326
layout_allocation.x += border.left + padding.left;
327
layout_allocation.y += (layout_allocation.height - layout_height) / 2;
329
if (entry->priv->button_visible)
331
pix_width = gdk_pixbuf_get_width (tag->close_pixbuf);
332
pix_height = gdk_pixbuf_get_height (tag->close_pixbuf);
340
button_allocation.x += width - pix_width - border.right - padding.right;
341
button_allocation.y += (height - pix_height) / 2;
342
button_allocation.width = pix_width;
343
button_allocation.height = pix_height;
345
if (background_allocation_out)
346
*background_allocation_out = background_allocation;
347
if (layout_allocation_out)
348
*layout_allocation_out = layout_allocation;
349
if (button_allocation_out)
350
*button_allocation_out = button_allocation;
354
gd_tagged_entry_tag_event_is_button (GdTaggedEntryTag *tag,
355
GdTaggedEntry *entry,
359
GtkAllocation button_allocation;
360
GtkStyleContext *context;
362
if (!entry->priv->button_visible)
365
context = gd_tagged_entry_tag_get_context (entry);
366
gd_tagged_entry_tag_get_relative_allocations (tag, entry, context, NULL, NULL, &button_allocation);
368
g_object_unref (context);
370
/* see if the event falls into the button allocation */
371
if ((event_x >= button_allocation.x &&
372
event_x <= button_allocation.x + button_allocation.width) &&
373
(event_y >= button_allocation.y &&
374
event_y <= button_allocation.y + button_allocation.height))
381
gd_tagged_entry_tag_draw (GdTaggedEntryTag *tag,
383
GdTaggedEntry *entry)
385
GtkStyleContext *context;
387
GtkAllocation background_allocation, layout_allocation, button_allocation;
389
context = gd_tagged_entry_tag_get_context (entry);
390
gd_tagged_entry_tag_get_relative_allocations (tag, entry, context,
391
&background_allocation,
396
gtk_cairo_transform_to_window (cr, GTK_WIDGET (entry), tag->window);
398
gtk_style_context_save (context);
400
state = gd_tagged_entry_tag_get_state (tag, entry);
401
gtk_style_context_set_state (context, state);
402
gtk_render_background (context, cr,
403
background_allocation.x, background_allocation.y,
404
background_allocation.width, background_allocation.height);
405
gtk_render_frame (context, cr,
406
background_allocation.x, background_allocation.y,
407
background_allocation.width, background_allocation.height);
409
gtk_render_layout (context, cr,
410
layout_allocation.x, layout_allocation.y,
413
gtk_style_context_restore (context);
415
if (!entry->priv->button_visible)
418
gtk_style_context_add_class (context, GTK_STYLE_CLASS_BUTTON);
419
state = gd_tagged_entry_tag_get_button_state (tag, entry);
420
gtk_style_context_set_state (context, state);
422
/* if the state changed since last time we draw the pixbuf,
423
* clear and redraw it.
425
if (state != tag->last_button_state)
427
g_clear_object (&tag->close_pixbuf);
428
gd_tagged_entry_tag_ensure_close_pixbuf (tag, context);
430
tag->last_button_state = state;
433
gtk_render_background (context, cr,
434
button_allocation.x, button_allocation.y,
435
button_allocation.width, button_allocation.height);
436
gtk_render_frame (context, cr,
437
button_allocation.x, button_allocation.y,
438
button_allocation.width, button_allocation.height);
440
gtk_render_icon (context, cr,
442
button_allocation.x, button_allocation.y);
447
g_object_unref (context);
451
gd_tagged_entry_tag_unrealize (GdTaggedEntryTag *tag)
453
if (tag->window == NULL)
456
gdk_window_set_user_data (tag->window, NULL);
457
gdk_window_destroy (tag->window);
462
gd_tagged_entry_tag_realize (GdTaggedEntryTag *tag,
463
GdTaggedEntry *entry)
465
GtkWidget *widget = GTK_WIDGET (entry);
466
GdkWindowAttr attributes;
467
gint attributes_mask;
468
gint tag_width, tag_height;
470
if (tag->window != NULL)
473
attributes.window_type = GDK_WINDOW_CHILD;
474
attributes.wclass = GDK_INPUT_ONLY;
475
attributes.event_mask = gtk_widget_get_events (widget);
476
attributes.event_mask |= GDK_BUTTON_PRESS_MASK
477
| GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK
478
| GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK;
480
gd_tagged_entry_tag_get_size (tag, entry, &tag_width, &tag_height);
483
attributes.width = tag_width;
484
attributes.height = tag_height;
486
attributes_mask = GDK_WA_X | GDK_WA_Y;
488
tag->window = gdk_window_new (gtk_widget_get_window (widget),
489
&attributes, attributes_mask);
490
gdk_window_set_user_data (tag->window, widget);
493
static GdTaggedEntryTag *
494
gd_tagged_entry_tag_new (const gchar *id,
497
GdTaggedEntryTag *tag;
499
tag = g_slice_new0 (GdTaggedEntryTag);
501
tag->id = g_strdup (id);
502
tag->label = g_strdup (label);
503
tag->last_button_state = GTK_STATE_FLAG_NORMAL;
509
gd_tagged_entry_tag_free (gpointer _tag)
511
GdTaggedEntryTag *tag = _tag;
513
if (tag->window != NULL)
514
gd_tagged_entry_tag_unrealize (tag);
516
g_clear_object (&tag->layout);
517
g_clear_object (&tag->close_pixbuf);
521
g_slice_free (GdTaggedEntryTag, tag);
525
gd_tagged_entry_draw (GtkWidget *widget,
528
GdTaggedEntry *self = GD_TAGGED_ENTRY (widget);
529
GdTaggedEntryTag *tag;
532
GTK_WIDGET_CLASS (gd_tagged_entry_parent_class)->draw (widget, cr);
534
for (l = self->priv->tags; l != NULL; l = l->next)
537
gd_tagged_entry_tag_draw (tag, cr, self);
544
gd_tagged_entry_map (GtkWidget *widget)
546
GdTaggedEntry *self = GD_TAGGED_ENTRY (widget);
547
GdTaggedEntryTag *tag;
550
if (gtk_widget_get_realized (widget) && !gtk_widget_get_mapped (widget))
552
GTK_WIDGET_CLASS (gd_tagged_entry_parent_class)->map (widget);
554
for (l = self->priv->tags; l != NULL; l = l->next)
557
gdk_window_show (tag->window);
563
gd_tagged_entry_unmap (GtkWidget *widget)
565
GdTaggedEntry *self = GD_TAGGED_ENTRY (widget);
566
GdTaggedEntryTag *tag;
569
if (gtk_widget_get_mapped (widget))
571
for (l = self->priv->tags; l != NULL; l = l->next)
574
gdk_window_hide (tag->window);
577
GTK_WIDGET_CLASS (gd_tagged_entry_parent_class)->unmap (widget);
582
gd_tagged_entry_realize (GtkWidget *widget)
584
GdTaggedEntry *self = GD_TAGGED_ENTRY (widget);
585
GdTaggedEntryTag *tag;
588
GTK_WIDGET_CLASS (gd_tagged_entry_parent_class)->realize (widget);
590
for (l = self->priv->tags; l != NULL; l = l->next)
593
gd_tagged_entry_tag_realize (tag, self);
598
gd_tagged_entry_unrealize (GtkWidget *widget)
600
GdTaggedEntry *self = GD_TAGGED_ENTRY (widget);
601
GdTaggedEntryTag *tag;
604
GTK_WIDGET_CLASS (gd_tagged_entry_parent_class)->unrealize (widget);
606
for (l = self->priv->tags; l != NULL; l = l->next)
609
gd_tagged_entry_tag_unrealize (tag);
614
gd_tagged_entry_get_text_area_size (GtkEntry *entry,
620
GdTaggedEntry *self = GD_TAGGED_ENTRY (entry);
621
gint tag_panel_width;
623
GTK_ENTRY_CLASS (gd_tagged_entry_parent_class)->get_text_area_size (entry, x, y, width, height);
625
tag_panel_width = gd_tagged_entry_tag_panel_get_width (self);
628
*width -= tag_panel_width;
632
gd_tagged_entry_size_allocate (GtkWidget *widget,
633
GtkAllocation *allocation)
635
GdTaggedEntry *self = GD_TAGGED_ENTRY (widget);
636
gint x, y, width, height;
637
GdTaggedEntryTag *tag;
640
gtk_widget_set_allocation (widget, allocation);
641
GTK_WIDGET_CLASS (gd_tagged_entry_parent_class)->size_allocate (widget, allocation);
643
if (gtk_widget_get_realized (widget))
645
gd_tagged_entry_tag_panel_get_position (self, &x, &y);
647
for (l = self->priv->tags; l != NULL; l = l->next)
650
gd_tagged_entry_tag_get_size (tag, self, &width, &height);
651
gdk_window_move_resize (tag->window, x, y, width, height);
656
gtk_widget_queue_draw (widget);
661
gd_tagged_entry_get_preferred_width (GtkWidget *widget,
665
GdTaggedEntry *self = GD_TAGGED_ENTRY (widget);
666
gint tag_panel_width;
668
GTK_WIDGET_CLASS (gd_tagged_entry_parent_class)->get_preferred_width (widget, minimum, natural);
670
tag_panel_width = gd_tagged_entry_tag_panel_get_width (self);
673
*minimum += tag_panel_width;
675
*natural += tag_panel_width;
679
gd_tagged_entry_finalize (GObject *obj)
681
GdTaggedEntry *self = GD_TAGGED_ENTRY (obj);
683
if (self->priv->tags != NULL)
685
g_list_free_full (self->priv->tags, gd_tagged_entry_tag_free);
686
self->priv->tags = NULL;
689
G_OBJECT_CLASS (gd_tagged_entry_parent_class)->finalize (obj);
692
static GdTaggedEntryTag *
693
gd_tagged_entry_find_tag_by_id (GdTaggedEntry *self,
696
GdTaggedEntryTag *tag = NULL, *elem;
699
for (l = self->priv->tags; l != NULL; l = l->next)
702
if (g_strcmp0 (elem->id, id) == 0)
712
static GdTaggedEntryTag *
713
gd_tagged_entry_find_tag_by_window (GdTaggedEntry *self,
716
GdTaggedEntryTag *tag = NULL, *elem;
719
for (l = self->priv->tags; l != NULL; l = l->next)
722
if (elem->window == window)
733
gd_tagged_entry_enter_notify (GtkWidget *widget,
734
GdkEventCrossing *event)
736
GdTaggedEntry *self = GD_TAGGED_ENTRY (widget);
737
GdTaggedEntryTag *tag;
739
tag = gd_tagged_entry_find_tag_by_window (self, event->window);
743
self->priv->in_child = tag;
744
gtk_widget_queue_draw (widget);
747
return GTK_WIDGET_CLASS (gd_tagged_entry_parent_class)->enter_notify_event (widget, event);
751
gd_tagged_entry_leave_notify (GtkWidget *widget,
752
GdkEventCrossing *event)
754
GdTaggedEntry *self = GD_TAGGED_ENTRY (widget);
756
if (self->priv->in_child != NULL)
758
self->priv->in_child = NULL;
759
gtk_widget_queue_draw (widget);
762
return GTK_WIDGET_CLASS (gd_tagged_entry_parent_class)->leave_notify_event (widget, event);
766
gd_tagged_entry_motion_notify (GtkWidget *widget,
767
GdkEventMotion *event)
769
GdTaggedEntry *self = GD_TAGGED_ENTRY (widget);
770
GdTaggedEntryTag *tag;
772
tag = gd_tagged_entry_find_tag_by_window (self, event->window);
776
gdk_event_request_motions (event);
778
self->priv->in_child = tag;
779
self->priv->in_child_button = gd_tagged_entry_tag_event_is_button (tag, self, event->x, event->y);
780
gtk_widget_queue_draw (widget);
785
return GTK_WIDGET_CLASS (gd_tagged_entry_parent_class)->motion_notify_event (widget, event);
789
gd_tagged_entry_button_release_event (GtkWidget *widget,
790
GdkEventButton *event)
792
GdTaggedEntry *self = GD_TAGGED_ENTRY (widget);
793
GdTaggedEntryTag *tag;
796
tag = gd_tagged_entry_find_tag_by_window (self, event->window);
800
id_quark = g_quark_from_string (tag->id);
801
self->priv->in_child_active = FALSE;
803
if (gd_tagged_entry_tag_event_is_button (tag, self, event->x, event->y))
805
self->priv->in_child_button_active = FALSE;
806
g_signal_emit (self, signals[SIGNAL_TAG_BUTTON_CLICKED], id_quark, tag->id);
810
g_signal_emit (self, signals[SIGNAL_TAG_CLICKED], id_quark, tag->id);
813
gtk_widget_queue_draw (widget);
818
return GTK_WIDGET_CLASS (gd_tagged_entry_parent_class)->button_release_event (widget, event);
822
gd_tagged_entry_button_press_event (GtkWidget *widget,
823
GdkEventButton *event)
825
GdTaggedEntry *self = GD_TAGGED_ENTRY (widget);
826
GdTaggedEntryTag *tag;
828
tag = gd_tagged_entry_find_tag_by_window (self, event->window);
832
if (gd_tagged_entry_tag_event_is_button (tag, self, event->x, event->y))
833
self->priv->in_child_button_active = TRUE;
835
self->priv->in_child_active = TRUE;
837
gtk_widget_queue_draw (widget);
842
return GTK_WIDGET_CLASS (gd_tagged_entry_parent_class)->button_press_event (widget, event);
846
gd_tagged_entry_init (GdTaggedEntry *self)
848
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GD_TYPE_TAGGED_ENTRY, GdTaggedEntryPrivate);
849
self->priv->button_visible = TRUE;
853
gd_tagged_entry_get_property (GObject *object,
858
GdTaggedEntry *self = GD_TAGGED_ENTRY (object);
862
case PROP_TAG_BUTTON_VISIBLE:
863
g_value_set_boolean (value, gd_tagged_entry_get_tag_button_visible (self));
866
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
871
gd_tagged_entry_set_property (GObject *object,
876
GdTaggedEntry *self = GD_TAGGED_ENTRY (object);
880
case PROP_TAG_BUTTON_VISIBLE:
881
gd_tagged_entry_set_tag_button_visible (self, g_value_get_boolean (value));
884
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
889
gd_tagged_entry_class_init (GdTaggedEntryClass *klass)
891
GtkWidgetClass *wclass = GTK_WIDGET_CLASS (klass);
892
GtkEntryClass *eclass = GTK_ENTRY_CLASS (klass);
893
GObjectClass *oclass = G_OBJECT_CLASS (klass);
895
oclass->finalize = gd_tagged_entry_finalize;
896
oclass->set_property = gd_tagged_entry_set_property;
897
oclass->get_property = gd_tagged_entry_get_property;
899
wclass->realize = gd_tagged_entry_realize;
900
wclass->unrealize = gd_tagged_entry_unrealize;
901
wclass->map = gd_tagged_entry_map;
902
wclass->unmap = gd_tagged_entry_unmap;
903
wclass->size_allocate = gd_tagged_entry_size_allocate;
904
wclass->get_preferred_width = gd_tagged_entry_get_preferred_width;
905
wclass->draw = gd_tagged_entry_draw;
906
wclass->enter_notify_event = gd_tagged_entry_enter_notify;
907
wclass->leave_notify_event = gd_tagged_entry_leave_notify;
908
wclass->motion_notify_event = gd_tagged_entry_motion_notify;
909
wclass->button_press_event = gd_tagged_entry_button_press_event;
910
wclass->button_release_event = gd_tagged_entry_button_release_event;
912
eclass->get_text_area_size = gd_tagged_entry_get_text_area_size;
914
signals[SIGNAL_TAG_CLICKED] =
915
g_signal_new ("tag-clicked",
916
GD_TYPE_TAGGED_ENTRY,
917
G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED,
921
signals[SIGNAL_TAG_BUTTON_CLICKED] =
922
g_signal_new ("tag-button-clicked",
923
GD_TYPE_TAGGED_ENTRY,
924
G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED,
929
properties[PROP_TAG_BUTTON_VISIBLE] =
930
g_param_spec_boolean ("tag-close-visible", "Tag close icon visibility",
931
"Whether the close button should be shown in tags.", TRUE,
932
G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS);
934
g_type_class_add_private (klass, sizeof (GdTaggedEntryPrivate));
935
g_object_class_install_properties (oclass, NUM_PROPERTIES, properties);
939
gd_tagged_entry_new (void)
941
return g_object_new (GD_TYPE_TAGGED_ENTRY, NULL);
945
gd_tagged_entry_add_tag (GdTaggedEntry *self,
949
GdTaggedEntryTag *tag;
951
if (gd_tagged_entry_find_tag_by_id (self, id) != NULL)
954
tag = gd_tagged_entry_tag_new (id, name);
955
self->priv->tags = g_list_append (self->priv->tags, tag);
957
if (gtk_widget_get_mapped (GTK_WIDGET (self)))
959
gd_tagged_entry_tag_realize (tag, self);
960
gdk_window_show_unraised (tag->window);
963
gtk_widget_queue_resize (GTK_WIDGET (self));
969
gd_tagged_entry_remove_tag (GdTaggedEntry *self,
972
GdTaggedEntryTag *tag;
973
gboolean res = FALSE;
975
tag = gd_tagged_entry_find_tag_by_id (self, id);
980
self->priv->tags = g_list_remove (self->priv->tags, tag);
981
gd_tagged_entry_tag_free (tag);
983
gtk_widget_queue_resize (GTK_WIDGET (self));
990
gd_tagged_entry_set_tag_label (GdTaggedEntry *self,
994
GdTaggedEntryTag *tag;
995
gboolean res = FALSE;
997
tag = gd_tagged_entry_find_tag_by_id (self, tag_id);
1003
if (g_strcmp0 (tag->label, label) != 0)
1005
g_free (tag->label);
1006
tag->label = g_strdup (label);
1007
g_clear_object (&tag->layout);
1009
gtk_widget_queue_resize (GTK_WIDGET (self));
1017
gd_tagged_entry_set_tag_button_visible (GdTaggedEntry *self,
1020
g_return_if_fail (GD_IS_TAGGED_ENTRY (self));
1022
if (self->priv->button_visible == visible)
1025
self->priv->button_visible = visible;
1026
gtk_widget_queue_resize (GTK_WIDGET (self));
1028
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TAG_BUTTON_VISIBLE]);
1032
gd_tagged_entry_get_tag_button_visible (GdTaggedEntry *self)
1034
g_return_val_if_fail (GD_IS_TAGGED_ENTRY (self), FALSE);
1036
return self->priv->button_visible;