1 /* LIBGIMP - The GIMP Library
2  * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
3  *
4  * gimppaletteselectbutton.c
5  * Copyright (C) 2004  Michael Natterer <mitch@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  * Library 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 <gegl.h>
25 #include <gtk/gtk.h>
26 
27 #include "libgimpwidgets/gimpwidgets.h"
28 
29 #include "gimp.h"
30 
31 #include "gimpuitypes.h"
32 #include "gimppaletteselectbutton.h"
33 #include "gimpuimarshal.h"
34 
35 #include "libgimp-intl.h"
36 
37 
38 /**
39  * SECTION: gimppaletteselectbutton
40  * @title: GimpPaletteSelect
41  * @short_description: A button which pops up a palette select dialog.
42  *
43  * A button which pops up a palette select dialog.
44  **/
45 
46 
47 #define GIMP_PALETTE_SELECT_BUTTON_GET_PRIVATE(obj) ((GimpPaletteSelectButtonPrivate *) gimp_palette_select_button_get_instance_private ((GimpPaletteSelectButton *) (obj)))
48 
49 typedef struct _GimpPaletteSelectButtonPrivate GimpPaletteSelectButtonPrivate;
50 
51 struct _GimpPaletteSelectButtonPrivate
52 {
53   gchar     *title;
54 
55   gchar     *palette_name;      /* Local copy */
56 
57   GtkWidget *inside;
58   GtkWidget *label;
59 };
60 
61 enum
62 {
63   PALETTE_SET,
64   LAST_SIGNAL
65 };
66 
67 enum
68 {
69   PROP_0,
70   PROP_TITLE,
71   PROP_PALETTE_NAME
72 };
73 
74 
75 /*  local function prototypes  */
76 
77 static void   gimp_palette_select_button_finalize     (GObject      *object);
78 
79 static void   gimp_palette_select_button_set_property (GObject      *object,
80                                                        guint         property_id,
81                                                        const GValue *value,
82                                                        GParamSpec   *pspec);
83 static void   gimp_palette_select_button_get_property (GObject      *object,
84                                                        guint         property_id,
85                                                        GValue       *value,
86                                                        GParamSpec   *pspec);
87 
88 static void   gimp_palette_select_button_clicked  (GimpPaletteSelectButton *button);
89 
90 static void   gimp_palette_select_button_callback (const gchar *palette_name,
91                                                    gboolean     dialog_closing,
92                                                    gpointer     user_data);
93 
94 static void   gimp_palette_select_drag_data_received (GimpPaletteSelectButton *button,
95                                                       GdkDragContext          *context,
96                                                       gint                     x,
97                                                       gint                     y,
98                                                       GtkSelectionData        *selection,
99                                                       guint                    info,
100                                                       guint                    time);
101 
102 static GtkWidget * gimp_palette_select_button_create_inside (GimpPaletteSelectButton *palette_button);
103 
104 
105 static const GtkTargetEntry target = { "application/x-gimp-palette-name", 0 };
106 
107 static guint palette_button_signals[LAST_SIGNAL] = { 0 };
108 
109 
G_DEFINE_TYPE_WITH_PRIVATE(GimpPaletteSelectButton,gimp_palette_select_button,GIMP_TYPE_SELECT_BUTTON)110 G_DEFINE_TYPE_WITH_PRIVATE (GimpPaletteSelectButton, gimp_palette_select_button,
111                             GIMP_TYPE_SELECT_BUTTON)
112 
113 
114 static void
115 gimp_palette_select_button_class_init (GimpPaletteSelectButtonClass *klass)
116 {
117   GObjectClass          *object_class        = G_OBJECT_CLASS (klass);
118   GimpSelectButtonClass *select_button_class = GIMP_SELECT_BUTTON_CLASS (klass);
119 
120   object_class->finalize     = gimp_palette_select_button_finalize;
121   object_class->set_property = gimp_palette_select_button_set_property;
122   object_class->get_property = gimp_palette_select_button_get_property;
123 
124   select_button_class->select_destroy = gimp_palette_select_destroy;
125 
126   klass->palette_set = NULL;
127 
128   /**
129    * GimpPaletteSelectButton:title:
130    *
131    * The title to be used for the palette selection popup dialog.
132    *
133    * Since: 2.4
134    */
135   g_object_class_install_property (object_class, PROP_TITLE,
136                                    g_param_spec_string ("title",
137                                                         "Title",
138                                                         "The title to be used for the palette selection popup dialog",
139                                                         _("Palette Selection"),
140                                                         GIMP_PARAM_READWRITE |
141                                                         G_PARAM_CONSTRUCT_ONLY));
142 
143   /**
144    * GimpPaletteSelectButton:palette-name:
145    *
146    * The name of the currently selected palette.
147    *
148    * Since: 2.4
149    */
150   g_object_class_install_property (object_class, PROP_PALETTE_NAME,
151                                    g_param_spec_string ("palette-name",
152                                                         "Palette name",
153                                                         "The name of the currently selected palette",
154                                                         NULL,
155                                                         GIMP_PARAM_READWRITE));
156 
157   /**
158    * GimpPaletteSelectButton::palette-set:
159    * @widget: the object which received the signal.
160    * @palette_name: the name of the currently selected palette.
161    * @dialog_closing: whether the dialog was closed or not.
162    *
163    * The ::palette-set signal is emitted when the user selects a palette.
164    *
165    * Since: 2.4
166    */
167   palette_button_signals[PALETTE_SET] =
168     g_signal_new ("palette-set",
169                   G_TYPE_FROM_CLASS (klass),
170                   G_SIGNAL_RUN_FIRST,
171                   G_STRUCT_OFFSET (GimpPaletteSelectButtonClass, palette_set),
172                   NULL, NULL,
173                   _gimpui_marshal_VOID__STRING_BOOLEAN,
174                   G_TYPE_NONE, 2,
175                   G_TYPE_STRING,
176                   G_TYPE_BOOLEAN);
177 }
178 
179 static void
gimp_palette_select_button_init(GimpPaletteSelectButton * button)180 gimp_palette_select_button_init (GimpPaletteSelectButton *button)
181 {
182   GimpPaletteSelectButtonPrivate *priv;
183 
184   priv = GIMP_PALETTE_SELECT_BUTTON_GET_PRIVATE (button);
185 
186   priv->palette_name = NULL;
187 
188   priv->inside = gimp_palette_select_button_create_inside (button);
189   gtk_container_add (GTK_CONTAINER (button), priv->inside);
190 }
191 
192 /**
193  * gimp_palette_select_button_new:
194  * @title:        Title of the dialog to use or %NULL to use the default title.
195  * @palette_name: Initial palette name.
196  *
197  * Creates a new #GtkWidget that completely controls the selection of
198  * a palette.  This widget is suitable for placement in a table in a
199  * plug-in dialog.
200  *
201  * Returns: A #GtkWidget that you can use in your UI.
202  *
203  * Since: 2.4
204  */
205 GtkWidget *
gimp_palette_select_button_new(const gchar * title,const gchar * palette_name)206 gimp_palette_select_button_new (const gchar *title,
207                                 const gchar *palette_name)
208 {
209   GtkWidget *button;
210 
211   if (title)
212     button = g_object_new (GIMP_TYPE_PALETTE_SELECT_BUTTON,
213                            "title",        title,
214                            "palette-name", palette_name,
215                            NULL);
216   else
217     button = g_object_new (GIMP_TYPE_PALETTE_SELECT_BUTTON,
218                            "palette-name", palette_name,
219                            NULL);
220 
221   return button;
222 }
223 
224 /**
225  * gimp_palette_select_button_get_palette:
226  * @button: A #GimpPaletteSelectButton
227  *
228  * Retrieves the name of currently selected palette.
229  *
230  * Returns: an internal copy of the palette name which must not be freed.
231  *
232  * Since: 2.4
233  */
234 const gchar *
gimp_palette_select_button_get_palette(GimpPaletteSelectButton * button)235 gimp_palette_select_button_get_palette (GimpPaletteSelectButton *button)
236 {
237   GimpPaletteSelectButtonPrivate *priv;
238 
239   g_return_val_if_fail (GIMP_IS_PALETTE_SELECT_BUTTON (button), NULL);
240 
241   priv = GIMP_PALETTE_SELECT_BUTTON_GET_PRIVATE (button);
242   return priv->palette_name;
243 }
244 
245 /**
246  * gimp_palette_select_button_set_palette:
247  * @button: A #GimpPaletteSelectButton
248  * @palette_name: Palette name to set; %NULL means no change.
249  *
250  * Sets the current palette for the palette select button.
251  *
252  * Since: 2.4
253  */
254 void
gimp_palette_select_button_set_palette(GimpPaletteSelectButton * button,const gchar * palette_name)255 gimp_palette_select_button_set_palette (GimpPaletteSelectButton *button,
256                                         const gchar             *palette_name)
257 {
258   GimpSelectButton *select_button;
259 
260   g_return_if_fail (GIMP_IS_PALETTE_SELECT_BUTTON (button));
261 
262   select_button = GIMP_SELECT_BUTTON (button);
263 
264   if (select_button->temp_callback)
265     {
266       gimp_palettes_set_popup (select_button->temp_callback, palette_name);
267     }
268   else
269     {
270       gchar *name;
271       gint   num_colors;
272 
273       if (palette_name && *palette_name)
274         name = g_strdup (palette_name);
275       else
276         name = gimp_context_get_palette ();
277 
278       if (gimp_palette_get_info (name, &num_colors))
279         gimp_palette_select_button_callback (name, FALSE, button);
280 
281       g_free (name);
282     }
283 }
284 
285 
286 /*  private functions  */
287 
288 static void
gimp_palette_select_button_finalize(GObject * object)289 gimp_palette_select_button_finalize (GObject *object)
290 {
291   GimpPaletteSelectButtonPrivate *priv;
292 
293   priv = GIMP_PALETTE_SELECT_BUTTON_GET_PRIVATE (object);
294 
295   g_clear_pointer (&priv->palette_name, g_free);
296   g_clear_pointer (&priv->title,        g_free);
297 
298   G_OBJECT_CLASS (gimp_palette_select_button_parent_class)->finalize (object);
299 }
300 
301 static void
gimp_palette_select_button_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)302 gimp_palette_select_button_set_property (GObject      *object,
303                                          guint         property_id,
304                                          const GValue *value,
305                                          GParamSpec   *pspec)
306 {
307   GimpPaletteSelectButton        *button;
308   GimpPaletteSelectButtonPrivate *priv;
309 
310   button = GIMP_PALETTE_SELECT_BUTTON (object);
311   priv = GIMP_PALETTE_SELECT_BUTTON_GET_PRIVATE (button);
312 
313   switch (property_id)
314     {
315     case PROP_TITLE:
316       priv->title = g_value_dup_string (value);
317       break;
318     case PROP_PALETTE_NAME:
319       gimp_palette_select_button_set_palette (button,
320                                               g_value_get_string (value));
321       break;
322     default:
323       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
324       break;
325     }
326 }
327 
328 static void
gimp_palette_select_button_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)329 gimp_palette_select_button_get_property (GObject    *object,
330                                          guint       property_id,
331                                          GValue     *value,
332                                          GParamSpec *pspec)
333 {
334   GimpPaletteSelectButton        *button;
335   GimpPaletteSelectButtonPrivate *priv;
336 
337   button = GIMP_PALETTE_SELECT_BUTTON (object);
338   priv = GIMP_PALETTE_SELECT_BUTTON_GET_PRIVATE (button);
339 
340   switch (property_id)
341     {
342     case PROP_TITLE:
343       g_value_set_string (value, priv->title);
344       break;
345     case PROP_PALETTE_NAME:
346       g_value_set_string (value, priv->palette_name);
347       break;
348     default:
349       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
350       break;
351     }
352 }
353 
354 static void
gimp_palette_select_button_callback(const gchar * palette_name,gboolean dialog_closing,gpointer user_data)355 gimp_palette_select_button_callback (const gchar *palette_name,
356                                      gboolean     dialog_closing,
357                                      gpointer     user_data)
358 {
359   GimpPaletteSelectButton        *button;
360   GimpPaletteSelectButtonPrivate *priv;
361   GimpSelectButton               *select_button;
362 
363   button = GIMP_PALETTE_SELECT_BUTTON (user_data);
364 
365   priv = GIMP_PALETTE_SELECT_BUTTON_GET_PRIVATE (button);
366   select_button = GIMP_SELECT_BUTTON (button);
367 
368   g_free (priv->palette_name);
369   priv->palette_name = g_strdup (palette_name);
370 
371   gtk_label_set_text (GTK_LABEL (priv->label), palette_name);
372 
373   if (dialog_closing)
374     select_button->temp_callback = NULL;
375 
376   g_signal_emit (button, palette_button_signals[PALETTE_SET], 0,
377                  palette_name, dialog_closing);
378   g_object_notify (G_OBJECT (button), "palette-name");
379 }
380 
381 static void
gimp_palette_select_button_clicked(GimpPaletteSelectButton * button)382 gimp_palette_select_button_clicked (GimpPaletteSelectButton *button)
383 {
384   GimpPaletteSelectButtonPrivate *priv;
385   GimpSelectButton               *select_button;
386 
387   priv = GIMP_PALETTE_SELECT_BUTTON_GET_PRIVATE (button);
388   select_button = GIMP_SELECT_BUTTON (button);
389 
390   if (select_button->temp_callback)
391     {
392       /*  calling gimp_palettes_set_popup() raises the dialog  */
393       gimp_palettes_set_popup (select_button->temp_callback,
394                                priv->palette_name);
395     }
396   else
397     {
398       select_button->temp_callback =
399         gimp_palette_select_new (priv->title, priv->palette_name,
400                                  gimp_palette_select_button_callback,
401                                  button);
402     }
403 }
404 
405 static void
gimp_palette_select_drag_data_received(GimpPaletteSelectButton * button,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection,guint info,guint time)406 gimp_palette_select_drag_data_received (GimpPaletteSelectButton *button,
407                                         GdkDragContext          *context,
408                                         gint                     x,
409                                         gint                     y,
410                                         GtkSelectionData        *selection,
411                                         guint                    info,
412                                         guint                    time)
413 {
414   gint   length = gtk_selection_data_get_length (selection);
415   gchar *str;
416 
417   if (gtk_selection_data_get_format (selection) != 8 || length < 1)
418     {
419       g_warning ("%s: received invalid palette data", G_STRFUNC);
420       return;
421     }
422 
423   str = g_strndup ((const gchar *) gtk_selection_data_get_data (selection),
424                    length);
425 
426   if (g_utf8_validate (str, -1, NULL))
427     {
428       gint     pid;
429       gpointer unused;
430       gint     name_offset = 0;
431 
432       if (sscanf (str, "%i:%p:%n", &pid, &unused, &name_offset) >= 2 &&
433           pid == gimp_getpid () && name_offset > 0)
434         {
435           gchar *name = str + name_offset;
436 
437           gimp_palette_select_button_set_palette (button, name);
438         }
439     }
440 
441   g_free (str);
442 }
443 
444 static GtkWidget *
gimp_palette_select_button_create_inside(GimpPaletteSelectButton * palette_button)445 gimp_palette_select_button_create_inside (GimpPaletteSelectButton *palette_button)
446 {
447   GtkWidget                      *button;
448   GtkWidget                      *hbox;
449   GtkWidget                      *image;
450   GimpPaletteSelectButtonPrivate *priv;
451 
452   priv = GIMP_PALETTE_SELECT_BUTTON_GET_PRIVATE (palette_button);
453 
454   gtk_widget_push_composite_child ();
455 
456   button = gtk_button_new ();
457 
458   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
459   gtk_container_add (GTK_CONTAINER (button), hbox);
460 
461   image = gtk_image_new_from_icon_name (GIMP_ICON_PALETTE,
462                                         GTK_ICON_SIZE_BUTTON);
463   gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
464 
465   priv->label = gtk_label_new (priv->palette_name);
466   gtk_box_pack_start (GTK_BOX (hbox), priv->label, TRUE, TRUE, 4);
467 
468   gtk_widget_show_all (button);
469 
470   g_signal_connect_swapped (button, "clicked",
471                             G_CALLBACK (gimp_palette_select_button_clicked),
472                             palette_button);
473 
474   gtk_drag_dest_set (GTK_WIDGET (button),
475                      GTK_DEST_DEFAULT_HIGHLIGHT |
476                      GTK_DEST_DEFAULT_MOTION |
477                      GTK_DEST_DEFAULT_DROP,
478                      &target, 1,
479                      GDK_ACTION_COPY);
480 
481   g_signal_connect_swapped (button, "drag-data-received",
482                             G_CALLBACK (gimp_palette_select_drag_data_received),
483                             palette_button);
484 
485   gtk_widget_pop_composite_child ();
486 
487   return button;
488 }
489