1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * gimppickablepopup.c
5 * Copyright (C) 2014 Michael Natterer <mitch@gimp.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 <gegl.h>
24 #include <gtk/gtk.h>
25 #include <gdk/gdkkeysyms.h>
26
27 #include "libgimpwidgets/gimpwidgets.h"
28
29 #include "widgets-types.h"
30
31 #include "core/gimp.h"
32 #include "core/gimpcontext.h"
33 #include "core/gimpimage.h"
34 #include "core/gimppickable.h"
35 #include "core/gimpviewable.h"
36
37 #include "gimpcontainertreeview.h"
38 #include "gimpcontainerview.h"
39 #include "gimppickablepopup.h"
40 #include "gimpviewrenderer.h"
41
42 #include "gimp-intl.h"
43
44
45 enum
46 {
47 PROP_0,
48 PROP_CONTEXT,
49 PROP_PICKABLE,
50 PROP_VIEW_SIZE,
51 PROP_VIEW_BORDER_WIDTH
52 };
53
54 struct _GimpPickablePopupPrivate
55 {
56 GimpPickable *pickable;
57 GimpContext *context;
58
59 gint view_size;
60 gint view_border_width;
61
62 GtkWidget *image_view;
63 GtkWidget *layer_view;
64 GtkWidget *channel_view;
65 GtkWidget *layer_label;
66 };
67
68
69 static void gimp_pickable_popup_constructed (GObject *object);
70 static void gimp_pickable_popup_finalize (GObject *object);
71 static void gimp_pickable_popup_set_property (GObject *object,
72 guint property_id,
73 const GValue *value,
74 GParamSpec *pspec);
75 static void gimp_pickable_popup_get_property (GObject *object,
76 guint property_id,
77 GValue *value,
78 GParamSpec *pspec);
79
80 static void gimp_pickable_popup_image_changed (GimpContext *context,
81 GimpImage *image,
82 GimpPickablePopup *popup);
83 static void gimp_pickable_popup_item_activate (GimpContainerView *view,
84 GimpPickable *pickable,
85 gpointer unused,
86 GimpPickablePopup *popup);
87
88
G_DEFINE_TYPE_WITH_PRIVATE(GimpPickablePopup,gimp_pickable_popup,GIMP_TYPE_POPUP)89 G_DEFINE_TYPE_WITH_PRIVATE (GimpPickablePopup, gimp_pickable_popup,
90 GIMP_TYPE_POPUP)
91
92 #define parent_class gimp_pickable_popup_parent_class
93
94
95 static void
96 gimp_pickable_popup_class_init (GimpPickablePopupClass *klass)
97 {
98 GObjectClass *object_class = G_OBJECT_CLASS (klass);
99
100 object_class->constructed = gimp_pickable_popup_constructed;
101 object_class->finalize = gimp_pickable_popup_finalize;
102 object_class->get_property = gimp_pickable_popup_get_property;
103 object_class->set_property = gimp_pickable_popup_set_property;
104
105 g_object_class_install_property (object_class, PROP_CONTEXT,
106 g_param_spec_object ("context",
107 NULL, NULL,
108 GIMP_TYPE_CONTEXT,
109 GIMP_PARAM_READWRITE |
110 G_PARAM_CONSTRUCT_ONLY));
111
112 g_object_class_install_property (object_class, PROP_PICKABLE,
113 g_param_spec_object ("pickable",
114 NULL, NULL,
115 GIMP_TYPE_PICKABLE,
116 GIMP_PARAM_READABLE));
117
118 g_object_class_install_property (object_class, PROP_VIEW_SIZE,
119 g_param_spec_int ("view-size",
120 NULL, NULL,
121 1, GIMP_VIEWABLE_MAX_PREVIEW_SIZE,
122 GIMP_VIEW_SIZE_MEDIUM,
123 GIMP_PARAM_READWRITE |
124 G_PARAM_CONSTRUCT));
125
126 g_object_class_install_property (object_class, PROP_VIEW_BORDER_WIDTH,
127 g_param_spec_int ("view-border-width",
128 NULL, NULL,
129 0,
130 GIMP_VIEW_MAX_BORDER_WIDTH,
131 1,
132 GIMP_PARAM_READWRITE |
133 G_PARAM_CONSTRUCT));
134 }
135
136 static void
gimp_pickable_popup_init(GimpPickablePopup * popup)137 gimp_pickable_popup_init (GimpPickablePopup *popup)
138 {
139 popup->priv = gimp_pickable_popup_get_instance_private (popup);
140
141 popup->priv->view_size = GIMP_VIEW_SIZE_SMALL;
142 popup->priv->view_border_width = 1;
143
144 gtk_window_set_resizable (GTK_WINDOW (popup), FALSE);
145 }
146
147 static void
gimp_pickable_popup_constructed(GObject * object)148 gimp_pickable_popup_constructed (GObject *object)
149 {
150 GimpPickablePopup *popup = GIMP_PICKABLE_POPUP (object);
151 GtkWidget *frame;
152 GtkWidget *hbox;
153 GtkWidget *vbox;
154 GtkWidget *label;
155 GtkWidget *notebook;
156 GimpImage *image;
157
158 G_OBJECT_CLASS (parent_class)->constructed (object);
159
160 gimp_assert (GIMP_IS_CONTEXT (popup->priv->context));
161
162 frame = gtk_frame_new (NULL);
163 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
164 gtk_container_add (GTK_CONTAINER (popup), frame);
165 gtk_widget_show (frame);
166
167 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
168 gtk_container_set_border_width (GTK_CONTAINER (hbox), 6);
169 gtk_container_add (GTK_CONTAINER (frame), hbox);
170 gtk_widget_show (hbox);
171
172 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
173 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
174 gtk_widget_show (vbox);
175
176 label = gtk_label_new (_("Images"));
177 gtk_label_set_xalign (GTK_LABEL (label), 0.0);
178 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
179 gtk_widget_show (label);
180
181 popup->priv->image_view =
182 gimp_container_tree_view_new (popup->priv->context->gimp->images,
183 popup->priv->context,
184 popup->priv->view_size,
185 popup->priv->view_border_width);
186 gimp_container_box_set_size_request (GIMP_CONTAINER_BOX (popup->priv->image_view),
187 4 * (popup->priv->view_size +
188 2 * popup->priv->view_border_width),
189 4 * (popup->priv->view_size +
190 2 * popup->priv->view_border_width));
191 gtk_box_pack_start (GTK_BOX (vbox), popup->priv->image_view, TRUE, TRUE, 0);
192 gtk_widget_show (popup->priv->image_view);
193
194 g_signal_connect_object (popup->priv->image_view, "activate-item",
195 G_CALLBACK (gimp_pickable_popup_item_activate),
196 G_OBJECT (popup), 0);
197
198 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
199 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
200 gtk_widget_show (vbox);
201
202 popup->priv->layer_label = label =
203 gtk_label_new (_("Select an image in the left pane"));
204 gtk_label_set_xalign (GTK_LABEL (label), 0.0);
205 gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
206 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
207 gtk_widget_show (label);
208
209 notebook = gtk_notebook_new ();
210 gtk_box_pack_start (GTK_BOX (vbox), notebook, TRUE, TRUE, 0);
211 gtk_widget_show (notebook);
212
213 popup->priv->layer_view =
214 gimp_container_tree_view_new (NULL,
215 popup->priv->context,
216 popup->priv->view_size,
217 popup->priv->view_border_width);
218 gtk_tree_view_set_show_expanders (GTK_TREE_VIEW (GIMP_CONTAINER_TREE_VIEW (popup->priv->layer_view)->view),
219 TRUE);
220 gimp_container_box_set_size_request (GIMP_CONTAINER_BOX (popup->priv->layer_view),
221 4 * (popup->priv->view_size +
222 2 * popup->priv->view_border_width),
223 4 * (popup->priv->view_size +
224 2 * popup->priv->view_border_width));
225 gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
226 popup->priv->layer_view,
227 gtk_label_new (_("Layers")));
228 gtk_widget_show (popup->priv->layer_view);
229
230 g_signal_connect_object (popup->priv->layer_view, "activate-item",
231 G_CALLBACK (gimp_pickable_popup_item_activate),
232 G_OBJECT (popup), 0);
233
234 popup->priv->channel_view =
235 gimp_container_tree_view_new (NULL,
236 popup->priv->context,
237 popup->priv->view_size,
238 popup->priv->view_border_width);
239 gimp_container_box_set_size_request (GIMP_CONTAINER_BOX (popup->priv->channel_view),
240 4 * (popup->priv->view_size +
241 2 * popup->priv->view_border_width),
242 4 * (popup->priv->view_size +
243 2 * popup->priv->view_border_width));
244 gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
245 popup->priv->channel_view,
246 gtk_label_new (_("Channels")));
247 gtk_widget_show (popup->priv->channel_view);
248
249 g_signal_connect_object (popup->priv->channel_view, "activate-item",
250 G_CALLBACK (gimp_pickable_popup_item_activate),
251 G_OBJECT (popup), 0);
252
253 g_signal_connect_object (popup->priv->context, "image-changed",
254 G_CALLBACK (gimp_pickable_popup_image_changed),
255 G_OBJECT (popup), 0);
256
257 image = gimp_context_get_image (popup->priv->context);
258 gimp_pickable_popup_image_changed (popup->priv->context, image, popup);
259 }
260
261 static void
gimp_pickable_popup_finalize(GObject * object)262 gimp_pickable_popup_finalize (GObject *object)
263 {
264 GimpPickablePopup *popup = GIMP_PICKABLE_POPUP (object);
265
266 g_clear_object (&popup->priv->pickable);
267 g_clear_object (&popup->priv->context);
268
269 G_OBJECT_CLASS (parent_class)->finalize (object);
270 }
271
272 static void
gimp_pickable_popup_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)273 gimp_pickable_popup_set_property (GObject *object,
274 guint property_id,
275 const GValue *value,
276 GParamSpec *pspec)
277 {
278 GimpPickablePopup *popup = GIMP_PICKABLE_POPUP (object);
279
280 switch (property_id)
281 {
282 case PROP_CONTEXT:
283 if (popup->priv->context)
284 g_object_unref (popup->priv->context);
285 popup->priv->context = g_value_dup_object (value);
286 break;
287
288 case PROP_VIEW_SIZE:
289 popup->priv->view_size = g_value_get_int (value);
290 break;
291
292 case PROP_VIEW_BORDER_WIDTH:
293 popup->priv->view_border_width = g_value_get_int (value);
294 break;
295
296 case PROP_PICKABLE:
297 default:
298 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
299 break;
300 }
301 }
302
303 static void
gimp_pickable_popup_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)304 gimp_pickable_popup_get_property (GObject *object,
305 guint property_id,
306 GValue *value,
307 GParamSpec *pspec)
308 {
309 GimpPickablePopup *popup = GIMP_PICKABLE_POPUP (object);
310
311 switch (property_id)
312 {
313 case PROP_CONTEXT:
314 g_value_set_object (value, popup->priv->context);
315 break;
316
317 case PROP_PICKABLE:
318 g_value_set_object (value, popup->priv->pickable);
319 break;
320
321 case PROP_VIEW_SIZE:
322 g_value_set_int (value, popup->priv->view_size);
323 break;
324
325 case PROP_VIEW_BORDER_WIDTH:
326 g_value_set_int (value, popup->priv->view_border_width);
327 break;
328
329 default:
330 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
331 break;
332 }
333 }
334
335 GtkWidget *
gimp_pickable_popup_new(GimpContext * context,gint view_size,gint view_border_width)336 gimp_pickable_popup_new (GimpContext *context,
337 gint view_size,
338 gint view_border_width)
339 {
340 g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
341 g_return_val_if_fail (view_size > 0 &&
342 view_size <= GIMP_VIEWABLE_MAX_POPUP_SIZE, NULL);
343 g_return_val_if_fail (view_border_width >= 0 &&
344 view_border_width <= GIMP_VIEW_MAX_BORDER_WIDTH,
345 NULL);
346
347 return g_object_new (GIMP_TYPE_PICKABLE_POPUP,
348 "type", GTK_WINDOW_POPUP,
349 "context", context,
350 "view-size", view_size,
351 "view-border-width", view_border_width,
352 NULL);
353 }
354
355 GimpPickable *
gimp_pickable_popup_get_pickable(GimpPickablePopup * popup)356 gimp_pickable_popup_get_pickable (GimpPickablePopup *popup)
357 {
358 GtkWidget *focus;
359 GimpPickable *pickable = NULL;
360
361 g_return_val_if_fail (GIMP_IS_PICKABLE_POPUP (popup), NULL);
362
363 focus = gtk_window_get_focus (GTK_WINDOW (popup));
364
365 if (focus && gtk_widget_is_ancestor (focus, popup->priv->image_view))
366 {
367 pickable = GIMP_PICKABLE (gimp_context_get_image (popup->priv->context));
368 }
369 else if (focus && gtk_widget_is_ancestor (focus, popup->priv->layer_view))
370 {
371 GList *selected;
372
373 if (gimp_container_view_get_selected (GIMP_CONTAINER_VIEW (popup->priv->layer_view),
374 &selected))
375 {
376 pickable = selected->data;
377 g_list_free (selected);
378 }
379 }
380 else if (focus && gtk_widget_is_ancestor (focus, popup->priv->channel_view))
381 {
382 GList *selected;
383
384 if (gimp_container_view_get_selected (GIMP_CONTAINER_VIEW (popup->priv->channel_view),
385 &selected))
386 {
387 pickable = selected->data;
388 g_list_free (selected);
389 }
390 }
391
392 return pickable;
393 }
394
395
396 /* private functions */
397
398 static void
gimp_pickable_popup_image_changed(GimpContext * context,GimpImage * image,GimpPickablePopup * popup)399 gimp_pickable_popup_image_changed (GimpContext *context,
400 GimpImage *image,
401 GimpPickablePopup *popup)
402 {
403 GimpContainer *layers = NULL;
404 GimpContainer *channels = NULL;
405
406 if (image)
407 {
408 gchar *desc;
409
410 layers = gimp_image_get_layers (image);
411 channels = gimp_image_get_channels (image);
412
413 desc = gimp_viewable_get_description (GIMP_VIEWABLE (image), NULL);
414 gtk_label_set_text (GTK_LABEL (popup->priv->layer_label), desc);
415 g_free (desc);
416 }
417 else
418 {
419 gtk_label_set_text (GTK_LABEL (popup->priv->layer_label),
420 _("Select an image in the left pane"));
421 }
422
423 gimp_container_view_set_container (GIMP_CONTAINER_VIEW (popup->priv->layer_view),
424 layers);
425 gimp_container_view_set_container (GIMP_CONTAINER_VIEW (popup->priv->channel_view),
426 channels);
427 }
428
429 static void
gimp_pickable_popup_item_activate(GimpContainerView * view,GimpPickable * pickable,gpointer unused,GimpPickablePopup * popup)430 gimp_pickable_popup_item_activate (GimpContainerView *view,
431 GimpPickable *pickable,
432 gpointer unused,
433 GimpPickablePopup *popup)
434 {
435 g_signal_emit_by_name (popup, "confirm");
436 }
437