1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * gimpcombotagentry.c
5 * Copyright (C) 2008 Aurimas Juška <aurisj@svn.gnome.org>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program 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
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21 #include "config.h"
22
23 #include <string.h>
24
25 #include <gegl.h>
26 #include <gtk/gtk.h>
27
28 #include "libgimpwidgets/gimpwidgets.h"
29
30 #include "widgets-types.h"
31
32 #include "core/gimpcontainer.h"
33 #include "core/gimpcontext.h"
34 #include "core/gimptag.h"
35 #include "core/gimptagged.h"
36 #include "core/gimptaggedcontainer.h"
37 #include "core/gimpviewable.h"
38
39 #include "gimptagentry.h"
40 #include "gimptagpopup.h"
41 #include "gimpcombotagentry.h"
42
43
44 static void gimp_combo_tag_entry_constructed (GObject *object);
45 static void gimp_combo_tag_entry_dispose (GObject *object);
46
47 static gboolean gimp_combo_tag_entry_expose (GtkWidget *widget,
48 GdkEventExpose *event);
49 static void gimp_combo_tag_entry_style_set (GtkWidget *widget,
50 GtkStyle *previous_style);
51
52 static void gimp_combo_tag_entry_icon_press (GtkWidget *widget,
53 GtkEntryIconPosition icon_pos,
54 GdkEvent *event,
55 gpointer user_data);
56
57 static void gimp_combo_tag_entry_popup_destroy (GtkWidget *widget,
58 GimpComboTagEntry *entry);
59
60 static void gimp_combo_tag_entry_tag_count_changed (GimpTaggedContainer *container,
61 gint tag_count,
62 GimpComboTagEntry *entry);
63
64
65 G_DEFINE_TYPE (GimpComboTagEntry, gimp_combo_tag_entry, GIMP_TYPE_TAG_ENTRY);
66
67 #define parent_class gimp_combo_tag_entry_parent_class
68
69
70 static void
gimp_combo_tag_entry_class_init(GimpComboTagEntryClass * klass)71 gimp_combo_tag_entry_class_init (GimpComboTagEntryClass *klass)
72 {
73 GObjectClass *object_class = G_OBJECT_CLASS (klass);
74 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
75
76 object_class->constructed = gimp_combo_tag_entry_constructed;
77 object_class->dispose = gimp_combo_tag_entry_dispose;
78
79 widget_class->expose_event = gimp_combo_tag_entry_expose;
80 widget_class->style_set = gimp_combo_tag_entry_style_set;
81 }
82
83 static void
gimp_combo_tag_entry_init(GimpComboTagEntry * entry)84 gimp_combo_tag_entry_init (GimpComboTagEntry *entry)
85 {
86 entry->popup = NULL;
87 entry->normal_item_attr = NULL;
88 entry->selected_item_attr = NULL;
89 entry->insensitive_item_attr = NULL;
90
91 gtk_widget_add_events (GTK_WIDGET (entry),
92 GDK_BUTTON_PRESS_MASK |
93 GDK_POINTER_MOTION_MASK);
94
95 gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry),
96 GTK_ENTRY_ICON_SECONDARY,
97 GIMP_ICON_GO_DOWN);
98
99 g_signal_connect (entry, "icon-press",
100 G_CALLBACK (gimp_combo_tag_entry_icon_press),
101 NULL);
102 }
103
104 static void
gimp_combo_tag_entry_constructed(GObject * object)105 gimp_combo_tag_entry_constructed (GObject *object)
106 {
107 GimpComboTagEntry *entry = GIMP_COMBO_TAG_ENTRY (object);
108
109 G_OBJECT_CLASS (parent_class)->constructed (object);
110
111 g_signal_connect_object (GIMP_TAG_ENTRY (entry)->container,
112 "tag-count-changed",
113 G_CALLBACK (gimp_combo_tag_entry_tag_count_changed),
114 entry, 0);
115 }
116
117 static void
gimp_combo_tag_entry_dispose(GObject * object)118 gimp_combo_tag_entry_dispose (GObject *object)
119 {
120 GimpComboTagEntry *combo_entry = GIMP_COMBO_TAG_ENTRY (object);
121
122 g_clear_object (&combo_entry->arrow_pixbuf);
123
124 if (combo_entry->normal_item_attr)
125 {
126 pango_attr_list_unref (combo_entry->normal_item_attr);
127 combo_entry->normal_item_attr = NULL;
128 }
129
130 if (combo_entry->selected_item_attr)
131 {
132 pango_attr_list_unref (combo_entry->selected_item_attr);
133 combo_entry->selected_item_attr = NULL;
134 }
135
136 if (combo_entry->insensitive_item_attr)
137 {
138 pango_attr_list_unref (combo_entry->insensitive_item_attr);
139 combo_entry->insensitive_item_attr = NULL;
140 }
141
142 G_OBJECT_CLASS (parent_class)->dispose (object);
143 }
144
145 static gboolean
gimp_combo_tag_entry_expose(GtkWidget * widget,GdkEventExpose * event)146 gimp_combo_tag_entry_expose (GtkWidget *widget,
147 GdkEventExpose *event)
148 {
149 GimpComboTagEntry *entry = GIMP_COMBO_TAG_ENTRY (widget);
150
151 if (! entry->arrow_pixbuf)
152 {
153 GtkStyle *style = gtk_widget_get_style (widget);
154 GdkPixmap *pixmap;
155 cairo_t *cr;
156
157 pixmap = gdk_pixmap_new (gtk_widget_get_window (widget), 8, 8, -1);
158
159 cr = gdk_cairo_create (pixmap);
160 gdk_cairo_set_source_color (cr, &style->base[GTK_STATE_NORMAL]);
161 cairo_paint (cr);
162 cairo_destroy (cr);
163
164 gtk_paint_arrow (style, pixmap,
165 GTK_STATE_NORMAL,
166 GTK_SHADOW_NONE, NULL, widget, NULL,
167 GTK_ARROW_DOWN, TRUE,
168 0, 0, 8, 8);
169
170 entry->arrow_pixbuf = gdk_pixbuf_get_from_drawable (NULL, pixmap, NULL,
171 0, 0, 0, 0, 8, 8);
172
173 g_object_unref (pixmap);
174
175 gtk_entry_set_icon_from_pixbuf (GTK_ENTRY (entry),
176 GTK_ENTRY_ICON_SECONDARY,
177 entry->arrow_pixbuf);
178 }
179
180 return GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
181 }
182
183 static void
gimp_combo_tag_entry_style_set(GtkWidget * widget,GtkStyle * previous_style)184 gimp_combo_tag_entry_style_set (GtkWidget *widget,
185 GtkStyle *previous_style)
186 {
187 GimpComboTagEntry *entry = GIMP_COMBO_TAG_ENTRY (widget);
188 GtkStyle *style = gtk_widget_get_style (widget);
189 GdkColor color;
190 PangoAttribute *attribute;
191
192 if (GTK_WIDGET_CLASS (parent_class)->style_set)
193 GTK_WIDGET_CLASS (parent_class)->style_set (widget, previous_style);
194
195 if (entry->normal_item_attr)
196 pango_attr_list_unref (entry->normal_item_attr);
197 entry->normal_item_attr = pango_attr_list_new ();
198
199 if (style->font_desc)
200 {
201 attribute = pango_attr_font_desc_new (style->font_desc);
202 pango_attr_list_insert (entry->normal_item_attr, attribute);
203 }
204 color = style->text[GTK_STATE_NORMAL];
205 attribute = pango_attr_foreground_new (color.red, color.green, color.blue);
206 pango_attr_list_insert (entry->normal_item_attr, attribute);
207
208 if (entry->selected_item_attr)
209 pango_attr_list_unref (entry->selected_item_attr);
210 entry->selected_item_attr = pango_attr_list_copy (entry->normal_item_attr);
211
212 color = style->text[GTK_STATE_SELECTED];
213 attribute = pango_attr_foreground_new (color.red, color.green, color.blue);
214 pango_attr_list_insert (entry->selected_item_attr, attribute);
215 color = style->base[GTK_STATE_SELECTED];
216 attribute = pango_attr_background_new (color.red, color.green, color.blue);
217 pango_attr_list_insert (entry->selected_item_attr, attribute);
218
219 if (entry->insensitive_item_attr)
220 pango_attr_list_unref (entry->insensitive_item_attr);
221 entry->insensitive_item_attr = pango_attr_list_copy (entry->normal_item_attr);
222
223 color = style->text[GTK_STATE_INSENSITIVE];
224 attribute = pango_attr_foreground_new (color.red, color.green, color.blue);
225 pango_attr_list_insert (entry->insensitive_item_attr, attribute);
226 color = style->base[GTK_STATE_INSENSITIVE];
227 attribute = pango_attr_background_new (color.red, color.green, color.blue);
228 pango_attr_list_insert (entry->insensitive_item_attr, attribute);
229
230 entry->selected_item_color = style->base[GTK_STATE_SELECTED];
231
232 g_clear_object (&entry->arrow_pixbuf);
233 }
234
235 /**
236 * gimp_combo_tag_entry_new:
237 * @container: a tagged container to be used.
238 * @mode: tag entry mode to work in.
239 *
240 * Creates a new #GimpComboTagEntry widget which extends #GimpTagEntry by
241 * adding ability to pick tags using popup window (similar to combo box).
242 *
243 * Return value: a new #GimpComboTagEntry widget.
244 **/
245 GtkWidget *
gimp_combo_tag_entry_new(GimpTaggedContainer * container,GimpTagEntryMode mode)246 gimp_combo_tag_entry_new (GimpTaggedContainer *container,
247 GimpTagEntryMode mode)
248 {
249 g_return_val_if_fail (GIMP_IS_TAGGED_CONTAINER (container), NULL);
250
251 return g_object_new (GIMP_TYPE_COMBO_TAG_ENTRY,
252 "container", container,
253 "mode", mode,
254 NULL);
255 }
256
257 static void
gimp_combo_tag_entry_icon_press(GtkWidget * widget,GtkEntryIconPosition icon_pos,GdkEvent * event,gpointer user_data)258 gimp_combo_tag_entry_icon_press (GtkWidget *widget,
259 GtkEntryIconPosition icon_pos,
260 GdkEvent *event,
261 gpointer user_data)
262 {
263 GimpComboTagEntry *entry = GIMP_COMBO_TAG_ENTRY (widget);
264
265 if (! entry->popup)
266 {
267 GimpTaggedContainer *container = GIMP_TAG_ENTRY (entry)->container;
268 gint tag_count;
269
270 tag_count = gimp_tagged_container_get_tag_count (container);
271
272 if (tag_count > 0 && ! GIMP_TAG_ENTRY (entry)->has_invalid_tags)
273 {
274 entry->popup = gimp_tag_popup_new (entry);
275 g_signal_connect (entry->popup, "destroy",
276 G_CALLBACK (gimp_combo_tag_entry_popup_destroy),
277 entry);
278 gimp_tag_popup_show (GIMP_TAG_POPUP (entry->popup));
279 }
280 }
281 else
282 {
283 gtk_widget_destroy (entry->popup);
284 }
285 }
286
287 static void
gimp_combo_tag_entry_popup_destroy(GtkWidget * widget,GimpComboTagEntry * entry)288 gimp_combo_tag_entry_popup_destroy (GtkWidget *widget,
289 GimpComboTagEntry *entry)
290 {
291 entry->popup = NULL;
292 gtk_widget_grab_focus (GTK_WIDGET (entry));
293 }
294
295 static void
gimp_combo_tag_entry_tag_count_changed(GimpTaggedContainer * container,gint tag_count,GimpComboTagEntry * entry)296 gimp_combo_tag_entry_tag_count_changed (GimpTaggedContainer *container,
297 gint tag_count,
298 GimpComboTagEntry *entry)
299 {
300 gboolean sensitive;
301
302 sensitive = tag_count > 0 && ! GIMP_TAG_ENTRY (entry)->has_invalid_tags;
303
304 gtk_entry_set_icon_sensitive (GTK_ENTRY (entry),
305 GTK_ENTRY_ICON_SECONDARY,
306 sensitive);
307 }
308