1 /* LIBGIMP - The GIMP Library
2  * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
3  *
4  * gimpimagecombobox.c
5  * Copyright (C) 2004 Sven Neumann <sven@gimp.org>
6  *
7  * This library is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 3 of the License, or (at your option) any later version.
11  *
12  * This library 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 GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library.  If not, see
19  * <https://www.gnu.org/licenses/>.
20  */
21 
22 #include "config.h"
23 
24 #include <stdlib.h>
25 
26 #include <gegl.h>
27 #include <gtk/gtk.h>
28 
29 #include "libgimpwidgets/gimpwidgets.h"
30 
31 #include "gimp.h"
32 
33 #include "gimpuitypes.h"
34 #include "gimpimagecombobox.h"
35 #include "gimppixbuf.h"
36 
37 
38 /**
39  * SECTION: gimpimagecombobox
40  * @title: GimpImageComboBox
41  * @short_description: A widget providing a popup menu of images.
42  *
43  * A widget providing a popup menu of images.
44  **/
45 
46 
47 #define THUMBNAIL_SIZE   24
48 #define WIDTH_REQUEST   200
49 
50 
51 typedef struct _GimpImageComboBoxClass GimpImageComboBoxClass;
52 
53 struct _GimpImageComboBox
54 {
55   GimpIntComboBox          parent_instance;
56 
57   GimpImageConstraintFunc  constraint;
58   gpointer                 data;
59 };
60 
61 struct _GimpImageComboBoxClass
62 {
63   GimpIntComboBoxClass  parent_class;
64 };
65 
66 
67 static void  gimp_image_combo_box_populate  (GimpImageComboBox       *combo_box);
68 static void  gimp_image_combo_box_model_add (GtkListStore            *store,
69                                              gint                     num_images,
70                                              gint32                  *images,
71                                              GimpImageConstraintFunc  constraint,
72                                              gpointer                 data);
73 
74 static void  gimp_image_combo_box_drag_data_received (GtkWidget        *widget,
75                                                       GdkDragContext   *context,
76                                                       gint              x,
77                                                       gint              y,
78                                                       GtkSelectionData *selection,
79                                                       guint             info,
80                                                       guint             time);
81 
82 static void  gimp_image_combo_box_changed   (GimpImageComboBox *combo_box);
83 
84 
85 static const GtkTargetEntry target = { "application/x-gimp-image-id", 0 };
86 
87 
G_DEFINE_TYPE(GimpImageComboBox,gimp_image_combo_box,GIMP_TYPE_INT_COMBO_BOX)88 G_DEFINE_TYPE (GimpImageComboBox, gimp_image_combo_box, GIMP_TYPE_INT_COMBO_BOX)
89 
90 
91 static void
92 gimp_image_combo_box_class_init (GimpImageComboBoxClass *klass)
93 {
94   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
95 
96   widget_class->drag_data_received = gimp_image_combo_box_drag_data_received;
97 }
98 
99 static void
gimp_image_combo_box_init(GimpImageComboBox * combo_box)100 gimp_image_combo_box_init (GimpImageComboBox *combo_box)
101 {
102   gtk_drag_dest_set (GTK_WIDGET (combo_box),
103                      GTK_DEST_DEFAULT_HIGHLIGHT |
104                      GTK_DEST_DEFAULT_MOTION |
105                      GTK_DEST_DEFAULT_DROP,
106                      &target, 1,
107                      GDK_ACTION_COPY);
108 }
109 
110 /**
111  * gimp_image_combo_box_new:
112  * @constraint: a #GimpImageConstraintFunc or %NULL
113  * @data:       a pointer that is passed to @constraint
114  *
115  * Creates a new #GimpIntComboBox filled with all currently opened
116  * images. If a @constraint function is specified, it is called for
117  * each image and only if the function returns %TRUE, the image is
118  * added to the combobox.
119  *
120  * You should use gimp_int_combo_box_connect() to initialize and
121  * connect the combo. Use gimp_int_combo_box_set_active() to get the
122  * active image ID and gimp_int_combo_box_get_active() to retrieve the
123  * ID of the selected image.
124  *
125  * Return value: a new #GimpIntComboBox.
126  *
127  * Since: 2.2
128  **/
129 GtkWidget *
gimp_image_combo_box_new(GimpImageConstraintFunc constraint,gpointer data)130 gimp_image_combo_box_new (GimpImageConstraintFunc constraint,
131                           gpointer                data)
132 {
133   GimpImageComboBox *combo_box;
134 
135   combo_box = g_object_new (GIMP_TYPE_IMAGE_COMBO_BOX,
136                             "width-request", WIDTH_REQUEST,
137                             "ellipsize",     PANGO_ELLIPSIZE_MIDDLE,
138                             NULL);
139 
140   combo_box->constraint = constraint;
141   combo_box->data       = data;
142 
143   gimp_image_combo_box_populate (combo_box);
144 
145   g_signal_connect (combo_box, "changed",
146                     G_CALLBACK (gimp_image_combo_box_changed),
147                     NULL);
148 
149   return GTK_WIDGET (combo_box);
150 }
151 
152 static void
gimp_image_combo_box_populate(GimpImageComboBox * combo_box)153 gimp_image_combo_box_populate (GimpImageComboBox *combo_box)
154 {
155   GtkTreeModel *model;
156   GtkTreeIter   iter;
157   gint32       *images;
158   gint          num_images;
159 
160   model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box));
161 
162   images = gimp_image_list (&num_images);
163 
164   gimp_image_combo_box_model_add (GTK_LIST_STORE (model),
165                                   num_images, images,
166                                   combo_box->constraint,
167                                   combo_box->data);
168 
169   g_free (images);
170 
171   if (gtk_tree_model_get_iter_first (model, &iter))
172     gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo_box), &iter);
173 }
174 
175 static void
gimp_image_combo_box_model_add(GtkListStore * store,gint num_images,gint32 * images,GimpImageConstraintFunc constraint,gpointer data)176 gimp_image_combo_box_model_add (GtkListStore            *store,
177                                 gint                     num_images,
178                                 gint32                  *images,
179                                 GimpImageConstraintFunc  constraint,
180                                 gpointer                 data)
181 {
182   GtkTreeIter  iter;
183   gint         i;
184 
185   for (i = 0; i < num_images; i++)
186     {
187       if (! constraint || (* constraint) (images[i], data))
188         {
189           gchar     *image_name = gimp_image_get_name (images[i]);
190           gchar     *label;
191           GdkPixbuf *thumb;
192 
193           label = g_strdup_printf ("%s-%d", image_name, images[i]);
194 
195           g_free (image_name);
196 
197           thumb = gimp_image_get_thumbnail (images[i],
198                                             THUMBNAIL_SIZE, THUMBNAIL_SIZE,
199                                             GIMP_PIXBUF_SMALL_CHECKS);
200 
201           gtk_list_store_append (store, &iter);
202           gtk_list_store_set (store, &iter,
203                               GIMP_INT_STORE_VALUE,  images[i],
204                               GIMP_INT_STORE_LABEL,  label,
205                               GIMP_INT_STORE_PIXBUF, thumb,
206                               -1);
207 
208           if (thumb)
209             g_object_unref (thumb);
210 
211           g_free (label);
212         }
213     }
214 }
215 
216 static void
gimp_image_combo_box_drag_data_received(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection,guint info,guint time)217 gimp_image_combo_box_drag_data_received (GtkWidget        *widget,
218                                          GdkDragContext   *context,
219                                          gint              x,
220                                          gint              y,
221                                          GtkSelectionData *selection,
222                                          guint             info,
223                                          guint             time)
224 {
225   gint   length = gtk_selection_data_get_length (selection);
226   gchar *str;
227 
228   if (gtk_selection_data_get_format (selection) != 8 || length < 1)
229     {
230       g_warning ("%s: received invalid image ID data", G_STRFUNC);
231       return;
232     }
233 
234   str = g_strndup ((const gchar *) gtk_selection_data_get_data (selection),
235                    length);
236 
237   if (g_utf8_validate (str, -1, NULL))
238     {
239       gint pid;
240       gint ID;
241 
242       if (sscanf (str, "%i:%i", &pid, &ID) == 2 &&
243           pid == gimp_getpid ())
244         {
245           gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (widget), ID);
246         }
247     }
248 
249   g_free (str);
250 }
251 
252 static void
gimp_image_combo_box_changed(GimpImageComboBox * combo_box)253 gimp_image_combo_box_changed (GimpImageComboBox *combo_box)
254 {
255   gint image_ID;
256 
257   if (gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (combo_box),
258                                      &image_ID))
259     {
260       if (! gimp_image_is_valid (image_ID))
261         {
262           GtkTreeModel *model;
263 
264           model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box));
265 
266           g_signal_stop_emission_by_name (combo_box, "changed");
267 
268           gtk_list_store_clear (GTK_LIST_STORE (model));
269           gimp_image_combo_box_populate (combo_box);
270         }
271     }
272 }
273