1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * gimp-tool-options-manager.c
5 * Copyright (C) 2018 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
26 #include "libgimpconfig/gimpconfig.h"
27
28 #include "tools-types.h"
29
30 #include "config/gimpcoreconfig.h"
31
32 #include "core/gimp.h"
33 #include "core/gimpcontext.h"
34 #include "core/gimptoolinfo.h"
35
36 #include "paint/gimppaintoptions.h"
37
38 #include "widgets/gimpwidgets-utils.h"
39
40 #include "gimp-tool-options-manager.h"
41
42
43 typedef struct _GimpToolOptionsManager GimpToolOptionsManager;
44
45 struct _GimpToolOptionsManager
46 {
47 Gimp *gimp;
48 GimpPaintOptions *global_paint_options;
49 GimpContextPropMask global_props;
50
51 GimpToolInfo *active_tool;
52 };
53
54
55 static GQuark manager_quark = 0;
56
57
58 /* local function prototypes */
59
60 static GimpContextPropMask
61 tool_options_manager_get_global_props
62 (GimpCoreConfig *config);
63
64 static void tool_options_manager_global_notify (GimpCoreConfig *config,
65 const GParamSpec *pspec,
66 GimpToolOptionsManager *manager);
67 static void tool_options_manager_paint_options_notify
68 (GimpPaintOptions *src,
69 const GParamSpec *pspec,
70 GimpPaintOptions *dest);
71
72 static void tool_options_manager_copy_paint_props
73 (GimpPaintOptions *src,
74 GimpPaintOptions *dest,
75 GimpContextPropMask prop_mask);
76
77 static void tool_options_manager_tool_changed (GimpContext *user_context,
78 GimpToolInfo *tool_info,
79 GimpToolOptionsManager *manager);
80
81
82 /* public functions */
83
84 void
gimp_tool_options_manager_init(Gimp * gimp)85 gimp_tool_options_manager_init (Gimp *gimp)
86 {
87 GimpToolOptionsManager *manager;
88 GimpContext *user_context;
89 GimpCoreConfig *config;
90 GList *list;
91
92 g_return_if_fail (GIMP_IS_GIMP (gimp));
93 g_return_if_fail (manager_quark == 0);
94
95 manager_quark = g_quark_from_static_string ("gimp-tool-options-manager");
96
97 config = gimp->config;
98
99 manager = g_slice_new0 (GimpToolOptionsManager);
100
101 manager->gimp = gimp;
102
103 manager->global_paint_options =
104 g_object_new (GIMP_TYPE_PAINT_OPTIONS,
105 "gimp", gimp,
106 "name", "tool-options-manager-global-paint-options",
107 NULL);
108
109 manager->global_props = tool_options_manager_get_global_props (config);
110
111 g_object_set_qdata (G_OBJECT (gimp), manager_quark, manager);
112
113 user_context = gimp_get_user_context (gimp);
114
115 for (list = gimp_get_tool_info_iter (gimp);
116 list;
117 list = g_list_next (list))
118 {
119 GimpToolInfo *tool_info = list->data;
120
121 /* the global props that are actually used by the tool are
122 * always shared with the user context by undefining them...
123 */
124 gimp_context_define_properties (GIMP_CONTEXT (tool_info->tool_options),
125 manager->global_props &
126 tool_info->context_props,
127 FALSE);
128
129 /* ...and setting the user context as parent
130 */
131 gimp_context_set_parent (GIMP_CONTEXT (tool_info->tool_options),
132 user_context);
133
134 /* make sure paint tools also share their brush, dynamics,
135 * gradient properties if the resp. context properties are
136 * global
137 */
138 if (GIMP_IS_PAINT_OPTIONS (tool_info->tool_options))
139 {
140 g_signal_connect (tool_info->tool_options, "notify",
141 G_CALLBACK (tool_options_manager_paint_options_notify),
142 manager->global_paint_options);
143
144 g_signal_connect (manager->global_paint_options, "notify",
145 G_CALLBACK (tool_options_manager_paint_options_notify),
146 tool_info->tool_options);
147
148 tool_options_manager_copy_paint_props (manager->global_paint_options,
149 GIMP_PAINT_OPTIONS (tool_info->tool_options),
150 tool_info->context_props &
151 manager->global_props);
152 }
153 }
154
155 g_signal_connect (gimp->config, "notify::global-brush",
156 G_CALLBACK (tool_options_manager_global_notify),
157 manager);
158 g_signal_connect (gimp->config, "notify::global-dynamics",
159 G_CALLBACK (tool_options_manager_global_notify),
160 manager);
161 g_signal_connect (gimp->config, "notify::global-pattern",
162 G_CALLBACK (tool_options_manager_global_notify),
163 manager);
164 g_signal_connect (gimp->config, "notify::global-palette",
165 G_CALLBACK (tool_options_manager_global_notify),
166 manager);
167 g_signal_connect (gimp->config, "notify::global-gradient",
168 G_CALLBACK (tool_options_manager_global_notify),
169 manager);
170 g_signal_connect (gimp->config, "notify::global-font",
171 G_CALLBACK (tool_options_manager_global_notify),
172 manager);
173
174 g_signal_connect (user_context, "tool-changed",
175 G_CALLBACK (tool_options_manager_tool_changed),
176 manager);
177
178 tool_options_manager_tool_changed (user_context,
179 gimp_context_get_tool (user_context),
180 manager);
181 }
182
183 void
gimp_tool_options_manager_exit(Gimp * gimp)184 gimp_tool_options_manager_exit (Gimp *gimp)
185 {
186 GimpToolOptionsManager *manager;
187 GimpContext *user_context;
188 GList *list;
189
190 g_return_if_fail (GIMP_IS_GIMP (gimp));
191
192 manager = g_object_get_qdata (G_OBJECT (gimp), manager_quark);
193
194 g_return_if_fail (manager != NULL);
195
196 user_context = gimp_get_user_context (gimp);
197
198 g_signal_handlers_disconnect_by_func (user_context,
199 tool_options_manager_tool_changed,
200 manager);
201
202 g_signal_handlers_disconnect_by_func (gimp->config,
203 tool_options_manager_global_notify,
204 manager);
205
206 for (list = gimp_get_tool_info_iter (gimp);
207 list;
208 list = g_list_next (list))
209 {
210 GimpToolInfo *tool_info = list->data;
211
212 gimp_context_set_parent (GIMP_CONTEXT (tool_info->tool_options), NULL);
213
214 if (GIMP_IS_PAINT_OPTIONS (tool_info->tool_options))
215 {
216 g_signal_handlers_disconnect_by_func (tool_info->tool_options,
217 tool_options_manager_paint_options_notify,
218 manager->global_paint_options);
219
220 g_signal_handlers_disconnect_by_func (manager->global_paint_options,
221 tool_options_manager_paint_options_notify,
222 tool_info->tool_options);
223 }
224 }
225
226 g_clear_object (&manager->global_paint_options);
227
228 g_slice_free (GimpToolOptionsManager, manager);
229
230 g_object_set_qdata (G_OBJECT (gimp), manager_quark, NULL);
231 }
232
233
234 /* private functions */
235
236 static GimpContextPropMask
tool_options_manager_get_global_props(GimpCoreConfig * config)237 tool_options_manager_get_global_props (GimpCoreConfig *config)
238 {
239 GimpContextPropMask global_props = 0;
240
241 /* FG and BG are always shared between all tools */
242 global_props |= GIMP_CONTEXT_PROP_MASK_FOREGROUND;
243 global_props |= GIMP_CONTEXT_PROP_MASK_BACKGROUND;
244
245 if (config->global_brush)
246 global_props |= GIMP_CONTEXT_PROP_MASK_BRUSH;
247 if (config->global_dynamics)
248 global_props |= GIMP_CONTEXT_PROP_MASK_DYNAMICS;
249 if (config->global_pattern)
250 global_props |= GIMP_CONTEXT_PROP_MASK_PATTERN;
251 if (config->global_palette)
252 global_props |= GIMP_CONTEXT_PROP_MASK_PALETTE;
253 if (config->global_gradient)
254 global_props |= GIMP_CONTEXT_PROP_MASK_GRADIENT;
255 if (config->global_font)
256 global_props |= GIMP_CONTEXT_PROP_MASK_FONT;
257
258 return global_props;
259 }
260
261 static void
tool_options_manager_global_notify(GimpCoreConfig * config,const GParamSpec * pspec,GimpToolOptionsManager * manager)262 tool_options_manager_global_notify (GimpCoreConfig *config,
263 const GParamSpec *pspec,
264 GimpToolOptionsManager *manager)
265 {
266 GimpContextPropMask global_props;
267 GimpContextPropMask enabled_global_props;
268 GimpContextPropMask disabled_global_props;
269 GList *list;
270
271 global_props = tool_options_manager_get_global_props (config);
272
273 enabled_global_props = global_props & ~manager->global_props;
274 disabled_global_props = manager->global_props & ~global_props;
275
276 /* copy the newly enabled global props to all tool options, and
277 * disconnect the newly disabled ones from the user context
278 */
279 for (list = gimp_get_tool_info_iter (manager->gimp);
280 list;
281 list = g_list_next (list))
282 {
283 GimpToolInfo *tool_info = list->data;
284
285 /* don't change the active tool, it is always fully connected
286 * to the user_context anyway because we set its
287 * defined/undefined context props in tool_changed()
288 */
289 if (tool_info == manager->active_tool)
290 continue;
291
292 /* defining the newly disabled ones disconnects them from the
293 * parent user context
294 */
295 gimp_context_define_properties (GIMP_CONTEXT (tool_info->tool_options),
296 tool_info->context_props &
297 disabled_global_props,
298 TRUE);
299
300 /* undefining the newly enabled ones copies the value from the
301 * parent user context
302 */
303 gimp_context_define_properties (GIMP_CONTEXT (tool_info->tool_options),
304 tool_info->context_props &
305 enabled_global_props,
306 FALSE);
307
308 if (GIMP_IS_PAINT_OPTIONS (tool_info->tool_options))
309 tool_options_manager_copy_paint_props (manager->global_paint_options,
310 GIMP_PAINT_OPTIONS (tool_info->tool_options),
311 tool_info->context_props &
312 enabled_global_props);
313 }
314
315 manager->global_props = global_props;
316 }
317
318 static void
tool_options_manager_paint_options_notify(GimpPaintOptions * src,const GParamSpec * pspec,GimpPaintOptions * dest)319 tool_options_manager_paint_options_notify (GimpPaintOptions *src,
320 const GParamSpec *pspec,
321 GimpPaintOptions *dest)
322 {
323 Gimp *gimp = GIMP_CONTEXT (src)->gimp;
324 GimpCoreConfig *config = gimp->config;
325 GimpToolOptionsManager *manager;
326 GimpToolInfo *tool_info;
327 GimpContextPropMask prop_mask = 0;
328 gboolean active = FALSE;
329
330 manager = g_object_get_qdata (G_OBJECT (gimp), manager_quark);
331
332 /* one of the options is the global one, the other is the tool's,
333 * get the tool_info from the tool's options
334 */
335 if (manager->global_paint_options == src)
336 tool_info = gimp_context_get_tool (GIMP_CONTEXT (dest));
337 else
338 tool_info = gimp_context_get_tool (GIMP_CONTEXT (src));
339
340 if (tool_info == manager->active_tool)
341 active = TRUE;
342
343 if ((active || config->global_brush) &&
344 tool_info->context_props & GIMP_CONTEXT_PROP_MASK_BRUSH)
345 {
346 prop_mask |= GIMP_CONTEXT_PROP_MASK_BRUSH;
347 }
348
349 if ((active || config->global_dynamics) &&
350 tool_info->context_props & GIMP_CONTEXT_PROP_MASK_DYNAMICS)
351 {
352 prop_mask |= GIMP_CONTEXT_PROP_MASK_DYNAMICS;
353 }
354
355 if ((active || config->global_gradient) &&
356 tool_info->context_props & GIMP_CONTEXT_PROP_MASK_GRADIENT)
357 {
358 prop_mask |= GIMP_CONTEXT_PROP_MASK_GRADIENT;
359 }
360
361 if (gimp_paint_options_is_prop (pspec->name, prop_mask))
362 {
363 GValue value = G_VALUE_INIT;
364
365 g_value_init (&value, pspec->value_type);
366
367 g_object_get_property (G_OBJECT (src), pspec->name, &value);
368
369 g_signal_handlers_block_by_func (dest,
370 tool_options_manager_paint_options_notify,
371 src);
372
373 g_object_set_property (G_OBJECT (dest), pspec->name, &value);
374
375 g_signal_handlers_unblock_by_func (dest,
376 tool_options_manager_paint_options_notify,
377 src);
378
379 g_value_unset (&value);
380 }
381 }
382
383 static void
tool_options_manager_copy_paint_props(GimpPaintOptions * src,GimpPaintOptions * dest,GimpContextPropMask prop_mask)384 tool_options_manager_copy_paint_props (GimpPaintOptions *src,
385 GimpPaintOptions *dest,
386 GimpContextPropMask prop_mask)
387 {
388 g_signal_handlers_block_by_func (dest,
389 tool_options_manager_paint_options_notify,
390 src);
391
392 gimp_paint_options_copy_props (src, dest, prop_mask);
393
394 g_signal_handlers_unblock_by_func (dest,
395 tool_options_manager_paint_options_notify,
396 src);
397 }
398
399 static void
tool_options_manager_tool_changed(GimpContext * user_context,GimpToolInfo * tool_info,GimpToolOptionsManager * manager)400 tool_options_manager_tool_changed (GimpContext *user_context,
401 GimpToolInfo *tool_info,
402 GimpToolOptionsManager *manager)
403 {
404 if (tool_info == manager->active_tool)
405 return;
406
407 /* FIXME: gimp_busy HACK
408 * the tool manager will stop the emission, so simply return
409 */
410 if (user_context->gimp->busy)
411 return;
412
413 if (manager->active_tool)
414 {
415 GimpToolInfo *active = manager->active_tool;
416
417 /* disconnect the old active tool from all context properties
418 * it uses, but are not currently global
419 */
420 gimp_context_define_properties (GIMP_CONTEXT (active->tool_options),
421 active->context_props &
422 ~manager->global_props,
423 TRUE);
424 }
425
426 manager->active_tool = tool_info;
427
428 if (manager->active_tool)
429 {
430 GimpToolInfo *active = manager->active_tool;
431
432 /* make sure the tool options GUI always exists, this call
433 * creates it if needed, so tools always have their option GUI
434 * available even if the tool options dockable is not open, see
435 * for example issue #3435
436 */
437 gimp_tools_get_tool_options_gui (active->tool_options);
438
439 /* copy the new tool's context properties that are not
440 * currently global to the user context, so they get used by
441 * everything
442 */
443 gimp_context_copy_properties (GIMP_CONTEXT (active->tool_options),
444 gimp_get_user_context (manager->gimp),
445 active->context_props &
446 ~manager->global_props);
447
448 if (GIMP_IS_PAINT_OPTIONS (active->tool_options))
449 tool_options_manager_copy_paint_props (GIMP_PAINT_OPTIONS (active->tool_options),
450 manager->global_paint_options,
451 active->context_props &
452 ~manager->global_props);
453
454 /* then, undefine these properties so the tool syncs with the
455 * user context automatically
456 */
457 gimp_context_define_properties (GIMP_CONTEXT (active->tool_options),
458 active->context_props &
459 ~manager->global_props,
460 FALSE);
461 }
462 }
463