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