1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis
3  *
4  * gimpdevicestatus.c
5  * Copyright (C) 2003 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 #undef GSEAL_ENABLE
24 
25 #include <gegl.h>
26 #include <gtk/gtk.h>
27 
28 #include "libgimpcolor/gimpcolor.h"
29 #include "libgimpwidgets/gimpwidgets.h"
30 
31 #include "widgets-types.h"
32 
33 #include "config/gimpguiconfig.h"
34 
35 #include "core/gimp.h"
36 #include "core/gimpbrush.h"
37 #include "core/gimpcontext.h"
38 #include "core/gimpdatafactory.h"
39 #include "core/gimpgradient.h"
40 #include "core/gimplist.h"
41 #include "core/gimppattern.h"
42 #include "core/gimptoolinfo.h"
43 
44 #include "gimpdnd.h"
45 #include "gimpdeviceinfo.h"
46 #include "gimpdevicemanager.h"
47 #include "gimpdevices.h"
48 #include "gimpdevicestatus.h"
49 #include "gimpdialogfactory.h"
50 #include "gimppropwidgets.h"
51 #include "gimpview.h"
52 #include "gimpwidgets-utils.h"
53 #include "gimpwindowstrategy.h"
54 
55 #include "gimp-intl.h"
56 
57 
58 #define CELL_SIZE 20 /* The size of the view cells */
59 
60 
61 enum
62 {
63   PROP_0,
64   PROP_GIMP
65 };
66 
67 
68 struct _GimpDeviceStatusEntry
69 {
70   GimpDeviceInfo  *device_info;
71   GimpContext     *context;
72   GimpToolOptions *tool_options;
73 
74   GtkWidget       *ebox;
75   GtkWidget       *options_hbox;
76   GtkWidget       *tool;
77   GtkWidget       *foreground;
78   GtkWidget       *foreground_none;
79   GtkWidget       *background;
80   GtkWidget       *background_none;
81   GtkWidget       *brush;
82   GtkWidget       *brush_none;
83   GtkWidget       *pattern;
84   GtkWidget       *pattern_none;
85   GtkWidget       *gradient;
86   GtkWidget       *gradient_none;
87 };
88 
89 
90 static void gimp_device_status_constructed     (GObject               *object);
91 static void gimp_device_status_dispose         (GObject               *object);
92 static void gimp_device_status_set_property    (GObject               *object,
93                                                 guint                  property_id,
94                                                 const GValue          *value,
95                                                 GParamSpec            *pspec);
96 
97 static void gimp_device_status_device_add      (GimpContainer         *devices,
98                                                 GimpDeviceInfo        *device_info,
99                                                 GimpDeviceStatus      *status);
100 static void gimp_device_status_device_remove   (GimpContainer         *devices,
101                                                 GimpDeviceInfo        *device_info,
102                                                 GimpDeviceStatus      *status);
103 
104 static void gimp_device_status_notify_device   (GimpDeviceManager     *manager,
105                                                 const GParamSpec      *pspec,
106                                                 GimpDeviceStatus      *status);
107 static void gimp_device_status_config_notify   (GimpGuiConfig         *config,
108                                                 const GParamSpec      *pspec,
109                                                 GimpDeviceStatus      *status);
110 static void gimp_device_status_notify_info     (GimpDeviceInfo        *device_info,
111                                                 const GParamSpec      *pspec,
112                                                 GimpDeviceStatusEntry *entry);
113 static void gimp_device_status_save_clicked    (GtkWidget             *button,
114                                                 GimpDeviceStatus      *status);
115 static void gimp_device_status_view_clicked    (GtkWidget             *widget,
116                                                 GdkModifierType        state,
117                                                 const gchar           *identifier);
118 
119 
G_DEFINE_TYPE(GimpDeviceStatus,gimp_device_status,GIMP_TYPE_EDITOR)120 G_DEFINE_TYPE (GimpDeviceStatus, gimp_device_status, GIMP_TYPE_EDITOR)
121 
122 #define parent_class gimp_device_status_parent_class
123 
124 
125 static void
126 gimp_device_status_class_init (GimpDeviceStatusClass *klass)
127 {
128   GObjectClass *object_class = G_OBJECT_CLASS (klass);
129 
130   object_class->constructed  = gimp_device_status_constructed;
131   object_class->dispose      = gimp_device_status_dispose;
132   object_class->set_property = gimp_device_status_set_property;
133 
134   g_object_class_install_property (object_class, PROP_GIMP,
135                                    g_param_spec_object ("gimp", NULL, NULL,
136                                                         GIMP_TYPE_GIMP,
137                                                         GIMP_PARAM_WRITABLE |
138                                                         G_PARAM_CONSTRUCT_ONLY));
139 }
140 
141 static void
gimp_device_status_init(GimpDeviceStatus * status)142 gimp_device_status_init (GimpDeviceStatus *status)
143 {
144   status->gimp           = NULL;
145   status->current_device = NULL;
146 
147   status->vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
148   gtk_container_set_border_width (GTK_CONTAINER (status->vbox), 2);
149   gtk_box_pack_start (GTK_BOX (status), status->vbox, TRUE, TRUE, 0);
150   gtk_widget_show (status->vbox);
151 
152   status->save_button =
153     gimp_editor_add_button (GIMP_EDITOR (status), GIMP_ICON_DOCUMENT_SAVE,
154                             _("Save device status"), NULL,
155                             G_CALLBACK (gimp_device_status_save_clicked),
156                             NULL,
157                             status);
158 }
159 
160 static void
gimp_device_status_constructed(GObject * object)161 gimp_device_status_constructed (GObject *object)
162 {
163   GimpDeviceStatus *status = GIMP_DEVICE_STATUS (object);
164   GimpContainer    *devices;
165   GList            *list;
166 
167   G_OBJECT_CLASS (parent_class)->constructed (object);
168 
169   gimp_assert (GIMP_IS_GIMP (status->gimp));
170 
171   devices = GIMP_CONTAINER (gimp_devices_get_manager (status->gimp));
172 
173   for (list = GIMP_LIST (devices)->queue->head; list; list = list->next)
174     gimp_device_status_device_add (devices, list->data, status);
175 
176   g_signal_connect_object (devices, "add",
177                            G_CALLBACK (gimp_device_status_device_add),
178                            status, 0);
179   g_signal_connect_object (devices, "remove",
180                            G_CALLBACK (gimp_device_status_device_remove),
181                            status, 0);
182 
183   g_signal_connect (devices, "notify::current-device",
184                     G_CALLBACK (gimp_device_status_notify_device),
185                     status);
186 
187   gimp_device_status_notify_device (GIMP_DEVICE_MANAGER (devices), NULL, status);
188 
189   g_signal_connect_object (status->gimp->config, "notify::devices-share-tool",
190                            G_CALLBACK (gimp_device_status_config_notify),
191                            status, 0);
192 
193   gimp_device_status_config_notify (GIMP_GUI_CONFIG (status->gimp->config),
194                                     NULL, status);
195 }
196 
197 static void
gimp_device_status_dispose(GObject * object)198 gimp_device_status_dispose (GObject *object)
199 {
200   GimpDeviceStatus *status = GIMP_DEVICE_STATUS (object);
201 
202   if (status->devices)
203     {
204       GList *list;
205 
206       for (list = status->devices; list; list = list->next)
207         {
208           GimpDeviceStatusEntry *entry = list->data;
209 
210           g_signal_handlers_disconnect_by_func (entry->device_info,
211                                                 gimp_device_status_notify_info,
212                                                 entry);
213 
214           g_object_unref (entry->context);
215           g_slice_free (GimpDeviceStatusEntry, entry);
216         }
217 
218       g_list_free (status->devices);
219       status->devices = NULL;
220 
221       g_signal_handlers_disconnect_by_func (gimp_devices_get_manager (status->gimp),
222                                             gimp_device_status_notify_device,
223                                             status);
224     }
225 
226   G_OBJECT_CLASS (parent_class)->dispose (object);
227 }
228 
229 static void
gimp_device_status_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)230 gimp_device_status_set_property (GObject      *object,
231                                  guint         property_id,
232                                  const GValue *value,
233                                  GParamSpec   *pspec)
234 {
235   GimpDeviceStatus *status = GIMP_DEVICE_STATUS (object);
236 
237   switch (property_id)
238     {
239     case PROP_GIMP:
240       status->gimp = g_value_get_object (value);
241       break;
242 
243     default:
244       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
245       break;
246     }
247 }
248 
249 static void
pack_prop_widget(GtkBox * hbox,GtkWidget * widget,GtkWidget ** none_widget)250 pack_prop_widget (GtkBox     *hbox,
251                   GtkWidget  *widget,
252                   GtkWidget **none_widget)
253 {
254   GtkSizeGroup *size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
255 
256   gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
257   gtk_size_group_add_widget (size_group, widget);
258   gtk_widget_show (widget);
259 
260   *none_widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
261   gtk_box_pack_start (GTK_BOX (hbox), *none_widget, FALSE, FALSE, 0);
262   gtk_size_group_add_widget (size_group, *none_widget);
263 
264   g_object_unref (size_group);
265 }
266 
267 static void
gimp_device_status_device_add(GimpContainer * devices,GimpDeviceInfo * device_info,GimpDeviceStatus * status)268 gimp_device_status_device_add (GimpContainer    *devices,
269                                GimpDeviceInfo   *device_info,
270                                GimpDeviceStatus *status)
271 {
272   GimpDeviceStatusEntry *entry;
273   GClosure              *closure;
274   GParamSpec            *pspec;
275   GtkWidget             *vbox;
276   GtkWidget             *hbox;
277   GtkWidget             *label;
278   gchar                 *name;
279 
280   entry = g_slice_new0 (GimpDeviceStatusEntry);
281 
282   status->devices = g_list_prepend (status->devices, entry);
283 
284   entry->device_info = device_info;
285   entry->context     = gimp_context_new (GIMP_TOOL_PRESET (device_info)->gimp,
286                                          gimp_object_get_name (device_info),
287                                          NULL);
288 
289   gimp_context_define_properties (entry->context,
290                                   GIMP_CONTEXT_PROP_MASK_TOOL       |
291                                   GIMP_CONTEXT_PROP_MASK_FOREGROUND |
292                                   GIMP_CONTEXT_PROP_MASK_BACKGROUND |
293                                   GIMP_CONTEXT_PROP_MASK_BRUSH      |
294                                   GIMP_CONTEXT_PROP_MASK_PATTERN    |
295                                   GIMP_CONTEXT_PROP_MASK_GRADIENT,
296                                   FALSE);
297 
298   closure = g_cclosure_new (G_CALLBACK (gimp_device_status_notify_info),
299                             entry, NULL);
300   g_object_watch_closure (G_OBJECT (status), closure);
301   g_signal_connect_closure (device_info, "notify", closure,
302                             FALSE);
303 
304   entry->ebox = gtk_event_box_new ();
305   gtk_box_pack_start (GTK_BOX (status->vbox), entry->ebox,
306                       FALSE, FALSE, 0);
307   gtk_widget_show (entry->ebox);
308 
309   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
310   gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
311   gtk_container_add (GTK_CONTAINER (entry->ebox), vbox);
312   gtk_widget_show (vbox);
313 
314   /*  the device name  */
315 
316   if (device_info->display == NULL ||
317       device_info->display == gdk_display_get_default ())
318     name = g_strdup (gimp_object_get_name (device_info));
319   else
320     name = g_strdup_printf ("%s (%s)",
321                             gimp_object_get_name (device_info),
322                             gdk_display_get_name (device_info->display));
323 
324   label = gtk_label_new (name);
325   g_free (name);
326 
327   gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
328   gimp_label_set_attributes (GTK_LABEL (label),
329                              PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD,
330                              -1);
331   gtk_label_set_xalign (GTK_LABEL (label), 0.0);
332   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
333   gtk_widget_show (label);
334 
335   /*  the row of properties  */
336 
337   hbox = entry->options_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
338   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
339   gtk_widget_show (hbox);
340 
341   /*  the tool  */
342 
343   entry->tool = gimp_prop_view_new (G_OBJECT (entry->context), "tool",
344                                     entry->context, CELL_SIZE);
345   gtk_box_pack_start (GTK_BOX (hbox), entry->tool, FALSE, FALSE, 0);
346   gtk_widget_show (entry->tool);
347 
348   /*  the foreground color  */
349 
350   entry->foreground = gimp_prop_color_area_new (G_OBJECT (entry->context),
351                                                 "foreground",
352                                                 CELL_SIZE, CELL_SIZE,
353                                                 GIMP_COLOR_AREA_FLAT);
354   gtk_widget_add_events (entry->foreground,
355                          GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
356   pack_prop_widget (GTK_BOX (hbox), entry->foreground, &entry->foreground_none);
357 
358   /*  the background color  */
359 
360   entry->background = gimp_prop_color_area_new (G_OBJECT (entry->context),
361                                                 "background",
362                                                 CELL_SIZE, CELL_SIZE,
363                                                 GIMP_COLOR_AREA_FLAT);
364   gtk_widget_add_events (entry->background,
365                          GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
366   pack_prop_widget (GTK_BOX (hbox), entry->background, &entry->background_none);
367 
368   /*  the brush  */
369 
370   entry->brush = gimp_prop_view_new (G_OBJECT (entry->context), "brush",
371                                      entry->context, CELL_SIZE);
372   GIMP_VIEW (entry->brush)->clickable  = TRUE;
373   GIMP_VIEW (entry->brush)->show_popup = TRUE;
374   pack_prop_widget (GTK_BOX (hbox), entry->brush, &entry->brush_none);
375 
376   g_signal_connect (entry->brush, "clicked",
377                     G_CALLBACK (gimp_device_status_view_clicked),
378                     "gimp-brush-grid|gimp-brush-list");
379 
380   /*  the pattern  */
381 
382   entry->pattern = gimp_prop_view_new (G_OBJECT (entry->context), "pattern",
383                                        entry->context, CELL_SIZE);
384   GIMP_VIEW (entry->pattern)->clickable  = TRUE;
385   GIMP_VIEW (entry->pattern)->show_popup = TRUE;
386   pack_prop_widget (GTK_BOX (hbox), entry->pattern, &entry->pattern_none);
387 
388   g_signal_connect (entry->pattern, "clicked",
389                     G_CALLBACK (gimp_device_status_view_clicked),
390                     "gimp-pattern-grid|gimp-pattern-list");
391 
392   /*  the gradient  */
393 
394   entry->gradient = gimp_prop_view_new (G_OBJECT (entry->context), "gradient",
395                                         entry->context, 2 * CELL_SIZE);
396   GIMP_VIEW (entry->gradient)->clickable  = TRUE;
397   GIMP_VIEW (entry->gradient)->show_popup = TRUE;
398   pack_prop_widget (GTK_BOX (hbox), entry->gradient, &entry->gradient_none);
399 
400   g_signal_connect (entry->gradient, "clicked",
401                     G_CALLBACK (gimp_device_status_view_clicked),
402                     "gimp-gradient-list|gimp-gradient-grid");
403 
404   pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (device_info),
405                                         "tool-options");
406   gimp_device_status_notify_info (device_info, pspec, entry);
407 }
408 
409 static void
gimp_device_status_device_remove(GimpContainer * devices,GimpDeviceInfo * device_info,GimpDeviceStatus * status)410 gimp_device_status_device_remove (GimpContainer    *devices,
411                                   GimpDeviceInfo   *device_info,
412                                   GimpDeviceStatus *status)
413 {
414   GList *list;
415 
416   for (list = status->devices; list; list = list->next)
417     {
418       GimpDeviceStatusEntry *entry = list->data;
419 
420       if (entry->device_info == device_info)
421         {
422           status->devices = g_list_remove (status->devices, entry);
423 
424           g_signal_handlers_disconnect_by_func (entry->device_info,
425                                                 gimp_device_status_notify_info,
426                                                 entry);
427 
428           g_object_unref (entry->context);
429           g_slice_free (GimpDeviceStatusEntry, entry);
430 
431           return;
432         }
433     }
434 }
435 
436 GtkWidget *
gimp_device_status_new(Gimp * gimp)437 gimp_device_status_new (Gimp *gimp)
438 {
439   g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
440 
441   return g_object_new (GIMP_TYPE_DEVICE_STATUS,
442                        "gimp", gimp,
443                        NULL);
444 }
445 
446 
447 /*  private functions  */
448 
449 static void
gimp_device_status_notify_device(GimpDeviceManager * manager,const GParamSpec * pspec,GimpDeviceStatus * status)450 gimp_device_status_notify_device (GimpDeviceManager *manager,
451                                   const GParamSpec  *pspec,
452                                   GimpDeviceStatus  *status)
453 {
454   GList *list;
455 
456   status->current_device = gimp_device_manager_get_current_device (manager);
457 
458   for (list = status->devices; list; list = list->next)
459     {
460       GimpDeviceStatusEntry *entry = list->data;
461 
462       gtk_widget_set_state (entry->ebox,
463                             entry->device_info == status->current_device ?
464                             GTK_STATE_SELECTED : GTK_STATE_NORMAL);
465     }
466 }
467 
468 static void
gimp_device_status_config_notify(GimpGuiConfig * config,const GParamSpec * pspec,GimpDeviceStatus * status)469 gimp_device_status_config_notify (GimpGuiConfig    *config,
470                                   const GParamSpec *pspec,
471                                   GimpDeviceStatus *status)
472 {
473   gboolean  show_options;
474   GList    *list;
475 
476   show_options = ! GIMP_GUI_CONFIG (status->gimp->config)->devices_share_tool;
477 
478   for (list = status->devices; list; list = list->next)
479     {
480       GimpDeviceStatusEntry *entry = list->data;
481 
482       gtk_widget_set_visible (entry->options_hbox, show_options);
483     }
484 }
485 
486 static void
toggle_prop_visible(GtkWidget * widget,GtkWidget * widget_none,gboolean available)487 toggle_prop_visible (GtkWidget *widget,
488                      GtkWidget *widget_none,
489                      gboolean   available)
490 {
491   gtk_widget_set_visible (widget, available);
492   gtk_widget_set_visible (widget_none, ! available);
493 }
494 
495 static void
gimp_device_status_notify_info(GimpDeviceInfo * device_info,const GParamSpec * pspec,GimpDeviceStatusEntry * entry)496 gimp_device_status_notify_info (GimpDeviceInfo        *device_info,
497                                 const GParamSpec      *pspec,
498                                 GimpDeviceStatusEntry *entry)
499 {
500   GimpToolOptions *tool_options = GIMP_TOOL_PRESET (device_info)->tool_options;
501 
502   if (tool_options != entry->tool_options)
503     {
504       GimpContextPropMask serialize_props;
505 
506       entry->tool_options = tool_options;
507       gimp_context_set_parent (entry->context, GIMP_CONTEXT (tool_options));
508 
509       serialize_props =
510         gimp_context_get_serialize_properties (GIMP_CONTEXT (tool_options));
511 
512       toggle_prop_visible (entry->foreground,
513                            entry->foreground_none,
514                            serialize_props & GIMP_CONTEXT_PROP_MASK_FOREGROUND);
515 
516       toggle_prop_visible (entry->background,
517                            entry->background_none,
518                            serialize_props & GIMP_CONTEXT_PROP_MASK_BACKGROUND);
519 
520       toggle_prop_visible (entry->brush,
521                            entry->brush_none,
522                            serialize_props & GIMP_CONTEXT_PROP_MASK_BRUSH);
523 
524       toggle_prop_visible (entry->pattern,
525                            entry->pattern_none,
526                            serialize_props & GIMP_CONTEXT_PROP_MASK_PATTERN);
527 
528       toggle_prop_visible (entry->gradient,
529                            entry->gradient_none,
530                            serialize_props & GIMP_CONTEXT_PROP_MASK_GRADIENT);
531     }
532 
533   if (! gimp_device_info_get_device (device_info, NULL) ||
534       gimp_device_info_get_mode (device_info) == GDK_MODE_DISABLED)
535     {
536       gtk_widget_hide (entry->ebox);
537     }
538   else
539     {
540       gtk_widget_show (entry->ebox);
541     }
542 
543   if (! strcmp (pspec->name, "tool-options"))
544     {
545       GimpRGB color;
546       guchar  r, g, b;
547       gchar   buf[64];
548 
549       gimp_context_get_foreground (entry->context, &color);
550       gimp_rgb_get_uchar (&color, &r, &g, &b);
551       g_snprintf (buf, sizeof (buf), _("Foreground: %d, %d, %d"), r, g, b);
552       gimp_help_set_help_data (entry->foreground, buf, NULL);
553 
554       gimp_context_get_background (entry->context, &color);
555       gimp_rgb_get_uchar (&color, &r, &g, &b);
556       g_snprintf (buf, sizeof (buf), _("Background: %d, %d, %d"), r, g, b);
557       gimp_help_set_help_data (entry->background, buf, NULL);
558     }
559 }
560 
561 static void
gimp_device_status_save_clicked(GtkWidget * button,GimpDeviceStatus * status)562 gimp_device_status_save_clicked (GtkWidget        *button,
563                                  GimpDeviceStatus *status)
564 {
565   gimp_devices_save (status->gimp, TRUE);
566 }
567 
568 static void
gimp_device_status_view_clicked(GtkWidget * widget,GdkModifierType state,const gchar * identifier)569 gimp_device_status_view_clicked (GtkWidget       *widget,
570                                  GdkModifierType  state,
571                                  const gchar     *identifier)
572 {
573   GimpDeviceStatus  *status;
574   GimpDialogFactory *dialog_factory;
575 
576   status = GIMP_DEVICE_STATUS (gtk_widget_get_ancestor (widget,
577                                                         GIMP_TYPE_DEVICE_STATUS));
578   dialog_factory = gimp_dialog_factory_get_singleton ();
579 
580   gimp_window_strategy_show_dockable_dialog (GIMP_WINDOW_STRATEGY (gimp_get_window_strategy (status->gimp)),
581                                              status->gimp,
582                                              dialog_factory,
583                                              gtk_widget_get_screen (widget),
584                                              gimp_widget_get_monitor (widget),
585                                              identifier);
586 }
587