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