1 /*
2 This file is part of darktable,
3 Copyright (C) 2011-2021 darktable developers.
4
5 darktable is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 darktable is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with darktable. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "gui/accelerators.h"
20 #include "common/darktable.h"
21 #include "common/debug.h"
22 #include "common/utility.h"
23 #include "control/control.h"
24 #include "develop/blend.h"
25
26 #include "bauhaus/bauhaus.h"
27
28 #include <assert.h>
29 #include <gtk/gtk.h>
30
31 typedef struct _accel_iop_t
32 {
33 dt_accel_t *accel;
34 GClosure *closure;
35 } _accel_iop_t;
36
dt_accel_path_global(char * s,size_t n,const char * path)37 void dt_accel_path_global(char *s, size_t n, const char *path)
38 {
39 snprintf(s, n, "<Darktable>/%s/%s", "global", path);
40 }
41
dt_accel_path_view(char * s,size_t n,char * module,const char * path)42 void dt_accel_path_view(char *s, size_t n, char *module, const char *path)
43 {
44 snprintf(s, n, "<Darktable>/%s/%s/%s", "views", module, path);
45 }
46
dt_accel_path_iop(char * s,size_t n,char * module,const char * path)47 void dt_accel_path_iop(char *s, size_t n, char *module, const char *path)
48 {
49 if(path)
50 {
51
52 gchar **split_paths = g_strsplit(path, "`", 4);
53 gchar **used_paths = split_paths;
54 // transitionally keep "preset" translated in keyboardrc to avoid breakage for now
55 // this also needs to be amended in preferences
56 if(!strcmp(split_paths[0], "preset"))
57 {
58 g_free(split_paths[0]);
59 split_paths[0] = g_strdup(_("preset"));
60 }
61 else if(!strcmp(split_paths[0], "blend"))
62 {
63 module = "blending";
64 used_paths++;
65 }
66
67 for(gchar **cur_path = used_paths; *cur_path; cur_path++)
68 {
69 gchar *after_context = strchr(*cur_path,'|');
70 if(after_context) memmove(*cur_path, after_context + 1, strlen(after_context));
71 }
72 gchar *joined_paths = g_strjoinv("/", used_paths);
73 snprintf(s, n, "<Darktable>/%s/%s/%s", "image operations", module, joined_paths);
74 g_free(joined_paths);
75 g_strfreev(split_paths);
76 }
77 else
78 snprintf(s, n, "<Darktable>/%s/%s", "image operations", module);
79 }
80
dt_accel_path_lib(char * s,size_t n,char * module,const char * path)81 void dt_accel_path_lib(char *s, size_t n, char *module, const char *path)
82 {
83 snprintf(s, n, "<Darktable>/%s/%s/%s", "modules", module, path);
84 }
85
dt_accel_path_lua(char * s,size_t n,const char * path)86 void dt_accel_path_lua(char *s, size_t n, const char *path)
87 {
88 snprintf(s, n, "<Darktable>/%s/%s", "lua", path);
89 }
90
dt_accel_path_manual(char * s,size_t n,const char * full_path)91 void dt_accel_path_manual(char *s, size_t n, const char *full_path)
92 {
93 snprintf(s, n, "<Darktable>/%s", full_path);
94 }
95
dt_accel_path_global_translated(char * s,size_t n,const char * path)96 static void dt_accel_path_global_translated(char *s, size_t n, const char *path)
97 {
98 snprintf(s, n, "<Darktable>/%s/%s", C_("accel", "global"), g_dpgettext2(NULL, "accel", path));
99 }
100
dt_accel_path_view_translated(char * s,size_t n,dt_view_t * module,const char * path)101 static void dt_accel_path_view_translated(char *s, size_t n, dt_view_t *module, const char *path)
102 {
103 snprintf(s, n, "<Darktable>/%s/%s/%s", C_("accel", "views"), module->name(module),
104 g_dpgettext2(NULL, "accel", path));
105 }
106
dt_accel_path_iop_translated(char * s,size_t n,dt_iop_module_so_t * module,const char * path)107 static void dt_accel_path_iop_translated(char *s, size_t n, dt_iop_module_so_t *module, const char *path)
108 {
109 gchar *module_clean = g_strdelimit(g_strdup(module->name()), "/", '-');
110
111 if(path)
112 {
113 gchar **split_paths = g_strsplit(path, "`", 4);
114 gchar **used_paths = split_paths;
115 if(!strcmp(split_paths[0], "blend"))
116 {
117 g_free(module_clean);
118 module_clean = g_strconcat(_("blending"), " ", NULL);
119 used_paths++;
120 }
121 for(gchar **cur_path = used_paths; *cur_path; cur_path++)
122 {
123 gchar *saved_path = *cur_path;
124 *cur_path = g_strdelimit(g_strconcat(Q_(*cur_path), (strcmp(*cur_path, "preset") ? NULL : " "), NULL), "/", '`');
125 g_free(saved_path);
126 }
127 gchar *joined_paths = g_strjoinv("/", used_paths);
128 snprintf(s, n, "<Darktable>/%s/%s/%s", C_("accel", "processing modules"), module_clean, joined_paths);
129 g_free(joined_paths);
130 g_strfreev(split_paths);
131 }
132 else
133 snprintf(s, n, "<Darktable>/%s/%s", C_("accel", "processing modules"), module_clean);
134
135 g_free(module_clean);
136 }
137
dt_accel_path_lib_translated(char * s,size_t n,dt_lib_module_t * module,const char * path)138 static void dt_accel_path_lib_translated(char *s, size_t n, dt_lib_module_t *module, const char *path)
139 {
140 snprintf(s, n, "<Darktable>/%s/%s/%s", C_("accel", "utility modules"), module->name(module),
141 g_dpgettext2(NULL, "accel", path));
142 }
143
dt_accel_path_lua_translated(char * s,size_t n,const char * path)144 static void dt_accel_path_lua_translated(char *s, size_t n, const char *path)
145 {
146 snprintf(s, n, "<Darktable>/%s/%s", C_("accel", "lua"), g_dpgettext2(NULL, "accel", path));
147 }
148
dt_accel_path_manual_translated(char * s,size_t n,const char * full_path)149 static void dt_accel_path_manual_translated(char *s, size_t n, const char *full_path)
150 {
151 snprintf(s, n, "<Darktable>/%s", g_dpgettext2(NULL, "accel", full_path));
152 }
153
dt_accel_register_global(const gchar * path,guint accel_key,GdkModifierType mods)154 void dt_accel_register_global(const gchar *path, guint accel_key, GdkModifierType mods)
155 {
156 gchar accel_path[256];
157 dt_accel_t *accel = (dt_accel_t *)g_malloc0(sizeof(dt_accel_t));
158
159 dt_accel_path_global(accel_path, sizeof(accel_path), path);
160 gtk_accel_map_add_entry(accel_path, accel_key, mods);
161
162 g_strlcpy(accel->path, accel_path, sizeof(accel->path));
163 dt_accel_path_global_translated(accel_path, sizeof(accel_path), path);
164 g_strlcpy(accel->translated_path, accel_path, sizeof(accel->translated_path));
165
166 *(accel->module) = '\0';
167 accel->local = FALSE;
168 accel->views = DT_VIEW_DARKROOM | DT_VIEW_LIGHTTABLE | DT_VIEW_TETHERING | DT_VIEW_MAP | DT_VIEW_PRINT | DT_VIEW_SLIDESHOW;
169 darktable.control->accelerator_list = g_list_prepend(darktable.control->accelerator_list, accel);
170 }
171
dt_accel_register_view(dt_view_t * self,const gchar * path,guint accel_key,GdkModifierType mods)172 void dt_accel_register_view(dt_view_t *self, const gchar *path, guint accel_key, GdkModifierType mods)
173 {
174 gchar accel_path[256];
175 dt_accel_t *accel = (dt_accel_t *)g_malloc0(sizeof(dt_accel_t));
176
177 dt_accel_path_view(accel_path, sizeof(accel_path), self->module_name, path);
178 gtk_accel_map_add_entry(accel_path, accel_key, mods);
179
180 g_strlcpy(accel->path, accel_path, sizeof(accel->path));
181 dt_accel_path_view_translated(accel_path, sizeof(accel_path), self, path);
182 g_strlcpy(accel->translated_path, accel_path, sizeof(accel->translated_path));
183
184 g_strlcpy(accel->module, self->module_name, sizeof(accel->module));
185 accel->local = FALSE;
186 accel->views = self->view(self);
187 darktable.control->accelerator_list = g_list_prepend(darktable.control->accelerator_list, accel);
188 }
189
dt_accel_register_iop(dt_iop_module_so_t * so,gboolean local,const gchar * path,guint accel_key,GdkModifierType mods)190 void dt_accel_register_iop(dt_iop_module_so_t *so, gboolean local, const gchar *path, guint accel_key,
191 GdkModifierType mods)
192 {
193 dt_accel_t *accel = (dt_accel_t *)g_malloc0(sizeof(dt_accel_t));
194
195 dt_accel_path_iop(accel->path, sizeof(accel->path), so->op, path);
196 gtk_accel_map_add_entry(accel->path, accel_key, mods);
197 dt_accel_path_iop_translated(accel->translated_path, sizeof(accel->translated_path), so, path);
198
199 g_strlcpy(accel->module, so->op, sizeof(accel->module));
200 accel->local = local;
201 accel->views = DT_VIEW_DARKROOM;
202 darktable.control->accelerator_list = g_list_prepend(darktable.control->accelerator_list, accel);
203 }
204
dt_accel_register_lib_as_view(gchar * view_name,const gchar * path,guint accel_key,GdkModifierType mods)205 void dt_accel_register_lib_as_view(gchar *view_name, const gchar *path, guint accel_key, GdkModifierType mods)
206 {
207 //register a lib shortcut but place it in the path of a view
208 gchar accel_path[256];
209 dt_accel_path_view(accel_path, sizeof(accel_path), view_name, path);
210 if (dt_accel_find_by_path(accel_path)) return; // return if nothing to add, to avoid multiple entries
211
212 dt_accel_t *accel = (dt_accel_t *)g_malloc0(sizeof(dt_accel_t));
213 gtk_accel_map_add_entry(accel_path, accel_key, mods);
214 g_strlcpy(accel->path, accel_path, sizeof(accel->path));
215
216 snprintf(accel_path, sizeof(accel_path), "<Darktable>/%s/%s/%s", C_("accel", "views"),
217 g_dgettext(NULL, view_name),
218 g_dpgettext2(NULL, "accel", path));
219
220 g_strlcpy(accel->translated_path, accel_path, sizeof(accel->translated_path));
221
222 g_strlcpy(accel->module, view_name, sizeof(accel->module));
223 accel->local = FALSE;
224
225 if(strcmp(view_name, "lighttable") == 0)
226 accel->views = DT_VIEW_LIGHTTABLE;
227 else if(strcmp(view_name, "darkroom") == 0)
228 accel->views = DT_VIEW_DARKROOM;
229 else if(strcmp(view_name, "print") == 0)
230 accel->views = DT_VIEW_PRINT;
231 else if(strcmp(view_name, "slideshow") == 0)
232 accel->views = DT_VIEW_SLIDESHOW;
233 else if(strcmp(view_name, "map") == 0)
234 accel->views = DT_VIEW_MAP;
235 else if(strcmp(view_name, "tethering") == 0)
236 accel->views = DT_VIEW_TETHERING;
237
238 darktable.control->accelerator_list = g_list_prepend(darktable.control->accelerator_list, accel);
239 }
240
dt_accel_register_lib_for_views(dt_lib_module_t * self,dt_view_type_flags_t views,const gchar * path,guint accel_key,GdkModifierType mods)241 void dt_accel_register_lib_for_views(dt_lib_module_t *self, dt_view_type_flags_t views, const gchar *path,
242 guint accel_key, GdkModifierType mods)
243 {
244 gchar accel_path[256];
245 dt_accel_path_lib(accel_path, sizeof(accel_path), self->plugin_name, path);
246 if (dt_accel_find_by_path(accel_path)) return; // return if nothing to add, to avoid multiple entries
247
248 dt_accel_t *accel = (dt_accel_t *)g_malloc0(sizeof(dt_accel_t));
249
250 gtk_accel_map_add_entry(accel_path, accel_key, mods);
251 g_strlcpy(accel->path, accel_path, sizeof(accel->path));
252 dt_accel_path_lib_translated(accel_path, sizeof(accel_path), self, path);
253 g_strlcpy(accel->translated_path, accel_path, sizeof(accel->translated_path));
254
255 g_strlcpy(accel->module, self->plugin_name, sizeof(accel->module));
256 accel->local = FALSE;
257 // we get the views in which the lib will be displayed
258 accel->views = views;
259 darktable.control->accelerator_list = g_list_prepend(darktable.control->accelerator_list, accel);
260 }
261
dt_accel_register_lib(dt_lib_module_t * self,const gchar * path,guint accel_key,GdkModifierType mods)262 void dt_accel_register_lib(dt_lib_module_t *self, const gchar *path, guint accel_key, GdkModifierType mods)
263 {
264 dt_view_type_flags_t v = 0;
265 int i=0;
266 const gchar **views = self->views(self);
267 while (views[i])
268 {
269 if(strcmp(views[i], "lighttable") == 0)
270 v |= DT_VIEW_LIGHTTABLE;
271 else if(strcmp(views[i], "darkroom") == 0)
272 v |= DT_VIEW_DARKROOM;
273 else if(strcmp(views[i], "print") == 0)
274 v |= DT_VIEW_PRINT;
275 else if(strcmp(views[i], "slideshow") == 0)
276 v |= DT_VIEW_SLIDESHOW;
277 else if(strcmp(views[i], "map") == 0)
278 v |= DT_VIEW_MAP;
279 else if(strcmp(views[i], "tethering") == 0)
280 v |= DT_VIEW_TETHERING;
281 else if(strcmp(views[i], "*") == 0)
282 v |= DT_VIEW_DARKROOM | DT_VIEW_LIGHTTABLE | DT_VIEW_TETHERING | DT_VIEW_MAP | DT_VIEW_PRINT
283 | DT_VIEW_SLIDESHOW;
284 i++;
285 }
286 dt_accel_register_lib_for_views(self, v, path, accel_key, mods);
287 }
288
289 const gchar *_common_actions[]
290 = { NC_("accel", "show module"),
291 NC_("accel", "enable module"),
292 NC_("accel", "focus module"),
293 NC_("accel", "reset module parameters"),
294 NC_("accel", "show preset menu"),
295 NULL };
296
297 const gchar *_slider_actions[]
298 = { NC_("accel", "increase"),
299 NC_("accel", "decrease"),
300 NC_("accel", "reset"),
301 NC_("accel", "edit"),
302 NC_("accel", "dynamic"),
303 NULL };
304
305 const gchar *_combobox_actions[]
306 = { NC_("accel", "next"),
307 NC_("accel", "previous"),
308 NC_("accel", "dynamic"),
309 NULL };
310
_accel_register_actions_iop(dt_iop_module_so_t * so,gboolean local,const gchar * path,const char ** actions)311 void _accel_register_actions_iop(dt_iop_module_so_t *so, gboolean local, const gchar *path, const char **actions)
312 {
313 gchar accel_path[256];
314 gchar accel_path_trans[256];
315 dt_accel_path_iop(accel_path, sizeof(accel_path), so->op, path);
316 dt_accel_path_iop_translated(accel_path_trans, sizeof(accel_path_trans), so, path);
317
318 for(const char **action = actions; *action; action++)
319 {
320 dt_accel_t *accel = (dt_accel_t *)g_malloc0(sizeof(dt_accel_t));
321 snprintf(accel->path, sizeof(accel->path), "%s/%s", accel_path, *action);
322 gtk_accel_map_add_entry(accel->path, 0, 0);
323 snprintf(accel->translated_path, sizeof(accel->translated_path), "%s/%s ", accel_path_trans,
324 g_dpgettext2(NULL, "accel", *action));
325 g_strlcpy(accel->module, so->op, sizeof(accel->module));
326 accel->local = local;
327 accel->views = DT_VIEW_DARKROOM;
328
329 darktable.control->accelerator_list = g_list_prepend(darktable.control->accelerator_list, accel);
330 }
331 }
332
dt_accel_register_common_iop(dt_iop_module_so_t * so)333 void dt_accel_register_common_iop(dt_iop_module_so_t *so)
334 {
335 _accel_register_actions_iop(so, FALSE, NULL, _common_actions);
336 }
337
dt_accel_register_combobox_iop(dt_iop_module_so_t * so,gboolean local,const gchar * path)338 void dt_accel_register_combobox_iop(dt_iop_module_so_t *so, gboolean local, const gchar *path)
339 {
340 _accel_register_actions_iop(so, local, path, _combobox_actions);
341 }
342
dt_accel_register_slider_iop(dt_iop_module_so_t * so,gboolean local,const gchar * path)343 void dt_accel_register_slider_iop(dt_iop_module_so_t *so, gboolean local, const gchar *path)
344 {
345 _accel_register_actions_iop(so, local, path, _slider_actions);
346 }
347
dt_accel_register_lua(const gchar * path,guint accel_key,GdkModifierType mods)348 void dt_accel_register_lua(const gchar *path, guint accel_key, GdkModifierType mods)
349 {
350 gchar accel_path[256];
351 dt_accel_t *accel = (dt_accel_t *)g_malloc0(sizeof(dt_accel_t));
352
353 dt_accel_path_lua(accel_path, sizeof(accel_path), path);
354 gtk_accel_map_add_entry(accel_path, accel_key, mods);
355
356 g_strlcpy(accel->path, accel_path, sizeof(accel->path));
357 dt_accel_path_lua_translated(accel_path, sizeof(accel_path), path);
358 g_strlcpy(accel->translated_path, accel_path, sizeof(accel->translated_path));
359
360 *(accel->module) = '\0';
361 accel->local = FALSE;
362 accel->views = DT_VIEW_DARKROOM | DT_VIEW_LIGHTTABLE | DT_VIEW_TETHERING | DT_VIEW_MAP | DT_VIEW_PRINT | DT_VIEW_SLIDESHOW;
363 darktable.control->accelerator_list = g_list_prepend(darktable.control->accelerator_list, accel);
364 }
365
dt_accel_register_manual(const gchar * full_path,dt_view_type_flags_t views,guint accel_key,GdkModifierType mods)366 void dt_accel_register_manual(const gchar *full_path, dt_view_type_flags_t views, guint accel_key,
367 GdkModifierType mods)
368 {
369 gchar accel_path[256];
370 dt_accel_t *accel = (dt_accel_t *)g_malloc0(sizeof(dt_accel_t));
371
372 dt_accel_path_manual(accel_path, sizeof(accel_path), full_path);
373 gtk_accel_map_add_entry(accel_path, accel_key, mods);
374
375 g_strlcpy(accel->path, accel_path, sizeof(accel->path));
376 dt_accel_path_manual_translated(accel_path, sizeof(accel_path), full_path);
377 g_strlcpy(accel->translated_path, accel_path, sizeof(accel->translated_path));
378
379 *(accel->module) = '\0';
380 accel->local = FALSE;
381 accel->views = views;
382 darktable.control->accelerator_list = g_list_prepend(darktable.control->accelerator_list, accel);
383 }
384
_lookup_accel(const gchar * path)385 static dt_accel_t *_lookup_accel(const gchar *path)
386 {
387 for(const GList *l = darktable.control->accelerator_list; l; l = g_list_next(l))
388 {
389 dt_accel_t *accel = (dt_accel_t *)l->data;
390 if(accel && !strcmp(accel->path, path)) return accel;
391 }
392 return NULL;
393 }
394
dt_accel_connect_global(const gchar * path,GClosure * closure)395 void dt_accel_connect_global(const gchar *path, GClosure *closure)
396 {
397 gchar accel_path[256];
398 dt_accel_path_global(accel_path, sizeof(accel_path), path);
399 dt_accel_t *laccel = _lookup_accel(accel_path);
400 laccel->closure = closure;
401 gtk_accel_group_connect_by_path(darktable.control->accelerators, accel_path, closure);
402 }
403
dt_accel_connect_view(dt_view_t * self,const gchar * path,GClosure * closure)404 void dt_accel_connect_view(dt_view_t *self, const gchar *path, GClosure *closure)
405 {
406 gchar accel_path[256];
407 dt_accel_path_view(accel_path, sizeof(accel_path), self->module_name, path);
408 gtk_accel_group_connect_by_path(darktable.control->accelerators, accel_path, closure);
409 dt_accel_t *laccel = _lookup_accel(accel_path);
410 laccel->closure = closure;
411
412 self->accel_closures = g_slist_prepend(self->accel_closures, laccel);
413 }
414
dt_accel_connect_lib_as_view(dt_lib_module_t * module,gchar * view_name,const gchar * path,GClosure * closure)415 dt_accel_t *dt_accel_connect_lib_as_view(dt_lib_module_t *module, gchar *view_name, const gchar *path, GClosure *closure)
416 {
417 gchar accel_path[256];
418 dt_accel_path_view(accel_path, sizeof(accel_path), view_name, path);
419 gtk_accel_group_connect_by_path(darktable.control->accelerators, accel_path, closure);
420
421 dt_accel_t *accel = _lookup_accel(accel_path);
422 if(!accel) return NULL; // this happens when the path doesn't match any accel (typos, ...)
423
424 accel->closure = closure;
425
426 module->accel_closures = g_slist_prepend(module->accel_closures, accel);
427 return accel;
428 }
429
dt_accel_connect_lib_as_global(dt_lib_module_t * module,const gchar * path,GClosure * closure)430 dt_accel_t *dt_accel_connect_lib_as_global(dt_lib_module_t *module, const gchar *path, GClosure *closure)
431 {
432 gchar accel_path[256];
433 dt_accel_path_global(accel_path, sizeof(accel_path), path);
434
435 dt_accel_t *accel = _lookup_accel(accel_path);
436 if(!accel) return NULL; // this happens when the path doesn't match any accel (typos, ...)
437
438 gtk_accel_group_connect_by_path(darktable.control->accelerators, accel_path, closure);
439
440 accel->closure = closure;
441
442 module->accel_closures = g_slist_prepend(module->accel_closures, accel);
443 return accel;
444 }
445
_store_iop_accel_closure(dt_iop_module_t * module,gchar * accel_path,GClosure * closure)446 static dt_accel_t *_store_iop_accel_closure(dt_iop_module_t *module, gchar *accel_path, GClosure *closure)
447 {
448 // Looking up the entry in the global accelerators list
449 dt_accel_t *accel = _lookup_accel(accel_path);
450 if(!accel) return NULL; // this happens when the path doesn't match any accel (typos, ...)
451
452 GSList **save_list = accel->local ? &module->accel_closures_local : &module->accel_closures;
453
454 _accel_iop_t *stored_accel = g_malloc(sizeof(_accel_iop_t));
455 stored_accel->accel = accel;
456 stored_accel->closure = closure;
457
458 g_closure_ref(closure);
459 g_closure_sink(closure);
460 *save_list = g_slist_prepend(*save_list, stored_accel);
461
462 return accel;
463 }
464
dt_accel_connect_iop(dt_iop_module_t * module,const gchar * path,GClosure * closure)465 dt_accel_t *dt_accel_connect_iop(dt_iop_module_t *module, const gchar *path, GClosure *closure)
466 {
467 gchar accel_path[256];
468 dt_accel_path_iop(accel_path, sizeof(accel_path), module->op, path);
469
470 return _store_iop_accel_closure(module, accel_path, closure);
471 }
472
dt_accel_connect_lib(dt_lib_module_t * module,const gchar * path,GClosure * closure)473 dt_accel_t *dt_accel_connect_lib(dt_lib_module_t *module, const gchar *path, GClosure *closure)
474 {
475 gchar accel_path[256];
476 dt_accel_path_lib(accel_path, sizeof(accel_path), module->plugin_name, path);
477 gtk_accel_group_connect_by_path(darktable.control->accelerators, accel_path, closure);
478
479 dt_accel_t *accel = _lookup_accel(accel_path);
480 if(!accel) return NULL; // this happens when the path doesn't match any accel (typos, ...)
481
482 accel->closure = closure;
483
484 module->accel_closures = g_slist_prepend(module->accel_closures, accel);
485 return accel;
486 }
487
dt_accel_connect_lua(const gchar * path,GClosure * closure)488 void dt_accel_connect_lua(const gchar *path, GClosure *closure)
489 {
490 gchar accel_path[256];
491 dt_accel_path_lua(accel_path, sizeof(accel_path), path);
492 dt_accel_t *laccel = _lookup_accel(accel_path);
493 laccel->closure = closure;
494 gtk_accel_group_connect_by_path(darktable.control->accelerators, accel_path, closure);
495 }
496
dt_accel_connect_manual(GSList ** list_ptr,const gchar * full_path,GClosure * closure)497 void dt_accel_connect_manual(GSList **list_ptr, const gchar *full_path, GClosure *closure)
498 {
499 gchar accel_path[256];
500 dt_accel_path_manual(accel_path, sizeof(accel_path), full_path);
501 dt_accel_t *accel = _lookup_accel(accel_path);
502 accel->closure = closure;
503 gtk_accel_group_connect_by_path(darktable.control->accelerators, accel_path, closure);
504 *list_ptr = g_slist_prepend(*list_ptr, accel);
505 }
506
_press_button_callback(GtkAccelGroup * accel_group,GObject * acceleratable,guint keyval,GdkModifierType modifier,gpointer data)507 static gboolean _press_button_callback(GtkAccelGroup *accel_group, GObject *acceleratable, guint keyval,
508 GdkModifierType modifier, gpointer data)
509 {
510 if(!(GTK_IS_BUTTON(data))) return FALSE;
511
512 gtk_button_clicked(GTK_BUTTON(data));
513 return TRUE;
514 }
515
_tooltip_callback(GtkWidget * widget,gint x,gint y,gboolean keyboard_mode,GtkTooltip * tooltip,gpointer user_data)516 static gboolean _tooltip_callback(GtkWidget *widget, gint x, gint y, gboolean keyboard_mode,
517 GtkTooltip *tooltip, gpointer user_data)
518 {
519 char *text = gtk_widget_get_tooltip_text(widget);
520
521 GtkAccelKey key;
522 dt_accel_t *accel = g_object_get_data(G_OBJECT(widget), "dt-accel");
523 if(accel && gtk_accel_map_lookup_entry(accel->path, &key))
524 {
525 gchar *key_name = gtk_accelerator_get_label(key.accel_key, key.accel_mods);
526 if(key_name && *key_name)
527 {
528 char *tmp = g_strdup_printf(_("%s\n(shortcut: %s)"), text, key_name);
529 g_free(text);
530 text = tmp;
531 }
532 g_free(key_name);
533 }
534
535 gtk_tooltip_set_text(tooltip, text);
536 g_free(text);
537 return TRUE;
538 }
539
dt_accel_connect_button_iop(dt_iop_module_t * module,const gchar * path,GtkWidget * button)540 void dt_accel_connect_button_iop(dt_iop_module_t *module, const gchar *path, GtkWidget *button)
541 {
542 GClosure *closure = g_cclosure_new(G_CALLBACK(_press_button_callback), button, NULL);
543 dt_accel_t *accel = dt_accel_connect_iop(module, path, closure);
544 g_object_set_data(G_OBJECT(button), "dt-accel", accel);
545
546 if(gtk_widget_get_has_tooltip(button))
547 g_signal_connect(G_OBJECT(button), "query-tooltip", G_CALLBACK(_tooltip_callback), NULL);
548 }
549
dt_accel_connect_button_lib(dt_lib_module_t * module,const gchar * path,GtkWidget * button)550 void dt_accel_connect_button_lib(dt_lib_module_t *module, const gchar *path, GtkWidget *button)
551 {
552 GClosure *closure = g_cclosure_new(G_CALLBACK(_press_button_callback), button, NULL);
553 dt_accel_t *accel = dt_accel_connect_lib(module, path, closure);
554 g_object_set_data(G_OBJECT(button), "dt-accel", accel);
555
556 if(gtk_widget_get_has_tooltip(button))
557 g_signal_connect(G_OBJECT(button), "query-tooltip", G_CALLBACK(_tooltip_callback), NULL);
558 }
559
dt_accel_connect_button_lib_as_global(dt_lib_module_t * module,const gchar * path,GtkWidget * button)560 void dt_accel_connect_button_lib_as_global(dt_lib_module_t *module, const gchar *path, GtkWidget *button)
561 {
562 GClosure *closure = g_cclosure_new(G_CALLBACK(_press_button_callback), button, NULL);
563 dt_accel_t *accel = dt_accel_connect_lib_as_global(module, path, closure);
564 g_object_set_data(G_OBJECT(button), "dt-accel", accel);
565
566 if(gtk_widget_get_has_tooltip(button))
567 g_signal_connect(G_OBJECT(button), "query-tooltip", G_CALLBACK(_tooltip_callback), NULL);
568 }
569
bauhaus_slider_edit_callback(GtkAccelGroup * accel_group,GObject * acceleratable,guint keyval,GdkModifierType modifier,gpointer data)570 static gboolean bauhaus_slider_edit_callback(GtkAccelGroup *accel_group, GObject *acceleratable, guint keyval,
571 GdkModifierType modifier, gpointer data)
572 {
573 GtkWidget *slider = GTK_WIDGET(data);
574
575 dt_bauhaus_show_popup(DT_BAUHAUS_WIDGET(slider));
576
577 return TRUE;
578 }
579
dt_accel_widget_toast(GtkWidget * widget)580 void dt_accel_widget_toast(GtkWidget *widget)
581 {
582 dt_bauhaus_widget_t *w = (dt_bauhaus_widget_t *)DT_BAUHAUS_WIDGET(widget);
583
584 if(!darktable.gui->reset)
585 {
586 char *text = NULL;
587
588 switch(w->type){
589 case DT_BAUHAUS_SLIDER:
590 {
591 text = dt_bauhaus_slider_get_text(widget);
592 break;
593 }
594 case DT_BAUHAUS_COMBOBOX:
595 text = g_strdup(dt_bauhaus_combobox_get_text(widget));
596 break;
597 default: //literally impossible but hey
598 return;
599 break;
600 }
601
602 if(w->label[0] != '\0')
603 { // label is not empty
604 if(w->module && w->module->multi_name[0] != '\0')
605 dt_toast_log(_("%s %s / %s: %s"), w->module->name(), w->module->multi_name, w->label, text);
606 else if(w->module && !strstr(w->module->name(), w->label))
607 dt_toast_log(_("%s / %s: %s"), w->module->name(), w->label, text);
608 else
609 dt_toast_log(_("%s: %s"), w->label, text);
610 }
611 else
612 { //label is empty
613 if(w->module && w->module->multi_name[0] != '\0')
614 dt_toast_log(_("%s %s / %s"), w->module->name(), w->module->multi_name, text);
615 else if(w->module)
616 dt_toast_log(_("%s / %s"), w->module->name(), text);
617 else
618 dt_toast_log("%s", text);
619 }
620
621 g_free(text);
622 }
623
624 }
625
dt_accel_get_slider_scale_multiplier()626 float dt_accel_get_slider_scale_multiplier()
627 {
628 const int slider_precision = dt_conf_get_int("accel/slider_precision");
629
630 if(slider_precision == DT_IOP_PRECISION_COARSE)
631 {
632 return dt_conf_get_float("darkroom/ui/scale_rough_step_multiplier");
633 }
634 else if(slider_precision == DT_IOP_PRECISION_FINE)
635 {
636 return dt_conf_get_float("darkroom/ui/scale_precise_step_multiplier");
637 }
638
639 return dt_conf_get_float("darkroom/ui/scale_step_multiplier");
640 }
641
_widget_invisible(GtkWidget * w)642 static gboolean _widget_invisible(GtkWidget *w)
643 {
644 return (!gtk_widget_get_visible(w) ||
645 !gtk_widget_get_visible(gtk_widget_get_parent(w)));
646 }
647
bauhaus_slider_increase_callback(GtkAccelGroup * accel_group,GObject * acceleratable,guint keyval,GdkModifierType modifier,gpointer data)648 static gboolean bauhaus_slider_increase_callback(GtkAccelGroup *accel_group, GObject *acceleratable,
649 guint keyval, GdkModifierType modifier, gpointer data)
650 {
651 GtkWidget *slider = GTK_WIDGET(data);
652
653 if(_widget_invisible(slider)) return TRUE;
654
655 float value = dt_bauhaus_slider_get(slider);
656 float step = dt_bauhaus_slider_get_step(slider);
657 float multiplier = dt_accel_get_slider_scale_multiplier();
658
659 const float min_visible = powf(10.0f, -dt_bauhaus_slider_get_digits(slider));
660 if(fabsf(step*multiplier) < min_visible)
661 multiplier = min_visible / fabsf(step);
662
663 dt_bauhaus_slider_set(slider, value + step * multiplier);
664
665 g_signal_emit_by_name(G_OBJECT(slider), "value-changed");
666
667 dt_accel_widget_toast(slider);
668 return TRUE;
669 }
670
bauhaus_slider_decrease_callback(GtkAccelGroup * accel_group,GObject * acceleratable,guint keyval,GdkModifierType modifier,gpointer data)671 static gboolean bauhaus_slider_decrease_callback(GtkAccelGroup *accel_group, GObject *acceleratable,
672 guint keyval, GdkModifierType modifier, gpointer data)
673 {
674 GtkWidget *slider = GTK_WIDGET(data);
675
676 if(_widget_invisible(slider)) return TRUE;
677
678 float value = dt_bauhaus_slider_get(slider);
679 float step = dt_bauhaus_slider_get_step(slider);
680 float multiplier = dt_accel_get_slider_scale_multiplier();
681
682 const float min_visible = powf(10.0f, -dt_bauhaus_slider_get_digits(slider));
683 if(fabsf(step*multiplier) < min_visible)
684 multiplier = min_visible / fabsf(step);
685
686 dt_bauhaus_slider_set(slider, value - step * multiplier);
687
688 g_signal_emit_by_name(G_OBJECT(slider), "value-changed");
689
690 dt_accel_widget_toast(slider);
691 return TRUE;
692 }
693
bauhaus_slider_reset_callback(GtkAccelGroup * accel_group,GObject * acceleratable,guint keyval,GdkModifierType modifier,gpointer data)694 static gboolean bauhaus_slider_reset_callback(GtkAccelGroup *accel_group, GObject *acceleratable,
695 guint keyval, GdkModifierType modifier, gpointer data)
696 {
697 GtkWidget *slider = GTK_WIDGET(data);
698
699 if(_widget_invisible(slider)) return TRUE;
700
701 dt_bauhaus_slider_reset(slider);
702
703 g_signal_emit_by_name(G_OBJECT(slider), "value-changed");
704
705 dt_accel_widget_toast(slider);
706 return TRUE;
707 }
708
bauhaus_dynamic_callback(GtkAccelGroup * accel_group,GObject * acceleratable,guint keyval,GdkModifierType modifier,gpointer data)709 static gboolean bauhaus_dynamic_callback(GtkAccelGroup *accel_group, GObject *acceleratable,
710 guint keyval, GdkModifierType modifier, gpointer data)
711 {
712 if(DT_IS_BAUHAUS_WIDGET(data))
713 {
714 dt_bauhaus_widget_t *widget = DT_BAUHAUS_WIDGET(data);
715
716 if(_widget_invisible(GTK_WIDGET(widget))) return TRUE;
717
718 darktable.view_manager->current_view->dynamic_accel_current = GTK_WIDGET(widget);
719
720 gchar *txt = g_strdup_printf (_("scroll to change <b>%s</b> of module %s %s"),
721 dt_bauhaus_widget_get_label(GTK_WIDGET(widget)),
722 widget->module->name(), widget->module->multi_name);
723 dt_control_hinter_message(darktable.control, txt);
724 g_free(txt);
725 }
726 else
727 dt_control_hinter_message(darktable.control, "");
728
729 return TRUE;
730 }
731
bauhaus_combobox_next_callback(GtkAccelGroup * accel_group,GObject * acceleratable,guint keyval,GdkModifierType modifier,gpointer data)732 static gboolean bauhaus_combobox_next_callback(GtkAccelGroup *accel_group, GObject *acceleratable,
733 guint keyval, GdkModifierType modifier, gpointer data)
734 {
735 GtkWidget *combobox = GTK_WIDGET(data);
736
737 if(_widget_invisible(combobox)) return TRUE;
738
739 const int currentval = dt_bauhaus_combobox_get(combobox);
740 const int nextval = currentval + 1 >= dt_bauhaus_combobox_length(combobox) ? 0 : currentval + 1;
741 dt_bauhaus_combobox_set(combobox, nextval);
742
743 dt_accel_widget_toast(combobox);
744
745 return TRUE;
746 }
747
bauhaus_combobox_prev_callback(GtkAccelGroup * accel_group,GObject * acceleratable,guint keyval,GdkModifierType modifier,gpointer data)748 static gboolean bauhaus_combobox_prev_callback(GtkAccelGroup *accel_group, GObject *acceleratable,
749 guint keyval, GdkModifierType modifier, gpointer data)
750 {
751 GtkWidget *combobox = GTK_WIDGET(data);
752
753 if(_widget_invisible(combobox)) return TRUE;
754
755 const int currentval = dt_bauhaus_combobox_get(combobox);
756 const int prevval = currentval - 1 < 0 ? dt_bauhaus_combobox_length(combobox) : currentval - 1;
757 dt_bauhaus_combobox_set(combobox, prevval);
758
759 dt_accel_widget_toast(combobox);
760
761 return TRUE;
762 }
763
_accel_connect_actions_iop(dt_iop_module_t * module,const gchar * path,GtkWidget * w,const gchar * actions[],void * callbacks[])764 void _accel_connect_actions_iop(dt_iop_module_t *module, const gchar *path,
765 GtkWidget *w, const gchar *actions[], void *callbacks[])
766 {
767 gchar accel_path[256];
768 dt_accel_path_iop(accel_path, sizeof(accel_path) - 1, module->op, path);
769 size_t path_len = strlen(accel_path);
770 accel_path[path_len++] = '/';
771
772 for(const char **action = actions; *action; action++, callbacks++)
773 {
774 strncpy(accel_path + path_len, *action, sizeof(accel_path) - path_len);
775
776 GClosure *closure = g_cclosure_new(G_CALLBACK(*callbacks), (gpointer)w, NULL);
777
778 _store_iop_accel_closure(module, accel_path, closure);
779 }
780 }
781
dt_accel_connect_combobox_iop(dt_iop_module_t * module,const gchar * path,GtkWidget * combobox)782 void dt_accel_connect_combobox_iop(dt_iop_module_t *module, const gchar *path, GtkWidget *combobox)
783 {
784 assert(DT_IS_BAUHAUS_WIDGET(combobox));
785
786 void *combobox_callbacks[]
787 = { bauhaus_combobox_next_callback,
788 bauhaus_combobox_prev_callback,
789 bauhaus_dynamic_callback };
790
791 _accel_connect_actions_iop(module, path, combobox, _combobox_actions, combobox_callbacks);
792 }
793
dt_accel_connect_slider_iop(dt_iop_module_t * module,const gchar * path,GtkWidget * slider)794 void dt_accel_connect_slider_iop(dt_iop_module_t *module, const gchar *path, GtkWidget *slider)
795 {
796 assert(DT_IS_BAUHAUS_WIDGET(slider));
797
798 void *slider_callbacks[]
799 = { bauhaus_slider_increase_callback,
800 bauhaus_slider_decrease_callback,
801 bauhaus_slider_reset_callback,
802 bauhaus_slider_edit_callback,
803 bauhaus_dynamic_callback };
804
805 _accel_connect_actions_iop(module, path, slider, _slider_actions, slider_callbacks);
806 }
807
dt_accel_connect_instance_iop(dt_iop_module_t * module)808 void dt_accel_connect_instance_iop(dt_iop_module_t *module)
809 {
810 for(GSList *l = module->accel_closures; l; l = g_slist_next(l))
811 {
812 _accel_iop_t *stored_accel = (_accel_iop_t *)l->data;
813 if(stored_accel && stored_accel->accel && stored_accel->closure)
814 {
815
816 if(stored_accel->accel->closure)
817 gtk_accel_group_disconnect(darktable.control->accelerators, stored_accel->accel->closure);
818
819 stored_accel->accel->closure = stored_accel->closure;
820
821 gtk_accel_group_connect_by_path(darktable.control->accelerators,
822 stored_accel->accel->path, stored_accel->closure);
823 }
824 }
825 }
826
dt_accel_connect_locals_iop(dt_iop_module_t * module)827 void dt_accel_connect_locals_iop(dt_iop_module_t *module)
828 {
829 for(GSList *l = module->accel_closures_local; l; l = g_slist_next(l))
830 {
831 _accel_iop_t *accel = (_accel_iop_t *)l->data;
832 if(accel)
833 {
834 gtk_accel_group_connect_by_path(darktable.control->accelerators, accel->accel->path, accel->closure);
835 }
836 }
837
838 module->local_closures_connected = TRUE;
839 }
840
dt_accel_disconnect_list(GSList ** list_ptr)841 void dt_accel_disconnect_list(GSList **list_ptr)
842 {
843 GSList *list = *list_ptr;
844 while(list)
845 {
846 dt_accel_t *accel = (dt_accel_t *)list->data;
847 if(accel) gtk_accel_group_disconnect(darktable.control->accelerators, accel->closure);
848 list = g_slist_delete_link(list, list);
849 }
850 *list_ptr = NULL;
851 }
852
dt_accel_disconnect_locals_iop(dt_iop_module_t * module)853 void dt_accel_disconnect_locals_iop(dt_iop_module_t *module)
854 {
855 if(!module->local_closures_connected) return;
856
857 for(GSList *l = module->accel_closures_local; l; l = g_slist_next(l))
858 {
859 _accel_iop_t *accel = (_accel_iop_t *)l->data;
860 if(accel)
861 {
862 gtk_accel_group_disconnect(darktable.control->accelerators, accel->closure);
863 }
864 }
865
866 module->local_closures_connected = FALSE;
867 }
868
_free_iop_accel(gpointer data)869 void _free_iop_accel(gpointer data)
870 {
871 _accel_iop_t *accel = (_accel_iop_t *) data;
872
873 if(accel->accel->closure == accel->closure)
874 {
875 gtk_accel_group_disconnect(darktable.control->accelerators, accel->closure);
876 accel->accel->closure = NULL;
877 }
878
879 if(accel->closure->ref_count != 1)
880 fprintf(stderr, "iop accel refcount %d %s\n", accel->closure->ref_count, accel->accel->path);
881
882 g_closure_unref(accel->closure);
883
884 g_free(accel);
885 }
886
dt_accel_cleanup_closures_iop(dt_iop_module_t * module)887 void dt_accel_cleanup_closures_iop(dt_iop_module_t *module)
888 {
889 dt_accel_disconnect_locals_iop(module);
890
891 g_slist_free_full(module->accel_closures, _free_iop_accel);
892 g_slist_free_full(module->accel_closures_local, _free_iop_accel);
893 module->accel_closures = NULL;
894 module->accel_closures_local = NULL;
895 }
896
897 typedef struct
898 {
899 dt_iop_module_t *module;
900 char *name;
901 } preset_iop_module_callback_description;
902
preset_iop_module_callback_destroyer(gpointer data,GClosure * closure)903 static void preset_iop_module_callback_destroyer(gpointer data, GClosure *closure)
904 {
905 preset_iop_module_callback_description *callback_description
906 = (preset_iop_module_callback_description *)data;
907 g_free(callback_description->name);
908 g_free(data);
909 }
910
preset_iop_module_callback(GtkAccelGroup * accel_group,GObject * acceleratable,guint keyval,GdkModifierType modifier,gpointer data)911 static gboolean preset_iop_module_callback(GtkAccelGroup *accel_group, GObject *acceleratable, guint keyval,
912 GdkModifierType modifier, gpointer data)
913 {
914 preset_iop_module_callback_description *callback_description
915 = (preset_iop_module_callback_description *)data;
916 dt_iop_module_t *module = callback_description->module;
917 const char *name = callback_description->name;
918
919 sqlite3_stmt *stmt;
920 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "SELECT op_params, enabled, blendop_params, "
921 "blendop_version FROM data.presets "
922 "WHERE operation = ?1 AND name = ?2",
923 -1, &stmt, NULL);
924 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 1, module->op, -1, SQLITE_TRANSIENT);
925 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, name, -1, SQLITE_TRANSIENT);
926
927 if(sqlite3_step(stmt) == SQLITE_ROW)
928 {
929 const void *op_params = sqlite3_column_blob(stmt, 0);
930 int op_length = sqlite3_column_bytes(stmt, 0);
931 int enabled = sqlite3_column_int(stmt, 1);
932 const void *blendop_params = sqlite3_column_blob(stmt, 2);
933 int bl_length = sqlite3_column_bytes(stmt, 2);
934 int blendop_version = sqlite3_column_int(stmt, 3);
935 if(op_params && (op_length == module->params_size))
936 {
937 memcpy(module->params, op_params, op_length);
938 module->enabled = enabled;
939 }
940 if(blendop_params && (blendop_version == dt_develop_blend_version())
941 && (bl_length == sizeof(dt_develop_blend_params_t)))
942 {
943 memcpy(module->blend_params, blendop_params, sizeof(dt_develop_blend_params_t));
944 }
945 else if(blendop_params
946 && dt_develop_blend_legacy_params(module, blendop_params, blendop_version, module->blend_params,
947 dt_develop_blend_version(), bl_length) == 0)
948 {
949 // do nothing
950 }
951 else
952 {
953 memcpy(module->blend_params, module->default_blendop_params, sizeof(dt_develop_blend_params_t));
954 }
955 }
956 sqlite3_finalize(stmt);
957 dt_iop_gui_update(module);
958 dt_dev_add_history_item(darktable.develop, module, FALSE);
959 gtk_widget_queue_draw(module->widget);
960 return TRUE;
961 }
962
dt_accel_connect_preset_iop(dt_iop_module_t * module,const gchar * path)963 void dt_accel_connect_preset_iop(dt_iop_module_t *module, const gchar *path)
964 {
965 char build_path[1024];
966 gchar *name = g_strdup(path);
967 snprintf(build_path, sizeof(build_path), "%s`%s", N_("preset"), name);
968 preset_iop_module_callback_description *callback_description
969 = g_malloc(sizeof(preset_iop_module_callback_description));
970 callback_description->module = module;
971 callback_description->name = name;
972
973 GClosure *closure = g_cclosure_new(G_CALLBACK(preset_iop_module_callback), callback_description,
974 preset_iop_module_callback_destroyer);
975 dt_accel_connect_iop(module, build_path, closure);
976 }
977
978
979
980 typedef struct
981 {
982 dt_lib_module_t *module;
983 char *name;
984 } preset_lib_module_callback_description;
985
preset_lib_module_callback_destroyer(gpointer data,GClosure * closure)986 static void preset_lib_module_callback_destroyer(gpointer data, GClosure *closure)
987 {
988 preset_lib_module_callback_description *callback_description
989 = (preset_lib_module_callback_description *)data;
990 g_free(callback_description->name);
991 g_free(data);
992 }
preset_lib_module_callback(GtkAccelGroup * accel_group,GObject * acceleratable,guint keyval,GdkModifierType modifier,gpointer data)993 static gboolean preset_lib_module_callback(GtkAccelGroup *accel_group, GObject *acceleratable, guint keyval,
994 GdkModifierType modifier, gpointer data)
995
996 {
997 preset_lib_module_callback_description *callback_description
998 = (preset_lib_module_callback_description *)data;
999 dt_lib_module_t *module = callback_description->module;
1000 const char *pn = callback_description->name;
1001
1002 sqlite3_stmt *stmt;
1003 DT_DEBUG_SQLITE3_PREPARE_V2(
1004 dt_database_get(darktable.db),
1005 "SELECT op_params FROM data.presets WHERE operation = ?1 AND op_version = ?2 AND name = ?3", -1, &stmt,
1006 NULL);
1007 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 1, module->plugin_name, -1, SQLITE_TRANSIENT);
1008 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, module->version());
1009 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 3, pn, -1, SQLITE_TRANSIENT);
1010
1011 int res = 0;
1012 if(sqlite3_step(stmt) == SQLITE_ROW)
1013 {
1014 const void *blob = sqlite3_column_blob(stmt, 0);
1015 int length = sqlite3_column_bytes(stmt, 0);
1016 if(blob)
1017 {
1018 for(const GList *it = darktable.lib->plugins; it; it = g_list_next(it))
1019 {
1020 dt_lib_module_t *search_module = (dt_lib_module_t *)it->data;
1021 if(!strncmp(search_module->plugin_name, module->plugin_name, 128))
1022 {
1023 res = module->set_params(module, blob, length);
1024 break;
1025 }
1026 }
1027 }
1028 }
1029 sqlite3_finalize(stmt);
1030 if(res)
1031 {
1032 dt_control_log(_("deleting preset for obsolete module"));
1033 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
1034 "DELETE FROM data.presets WHERE operation = ?1 AND op_version = ?2 AND name = ?3",
1035 -1, &stmt, NULL);
1036 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 1, module->plugin_name, -1, SQLITE_TRANSIENT);
1037 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, module->version());
1038 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 3, pn, -1, SQLITE_TRANSIENT);
1039 sqlite3_step(stmt);
1040 sqlite3_finalize(stmt);
1041 }
1042 return TRUE;
1043 }
1044
dt_accel_connect_preset_lib(dt_lib_module_t * module,const gchar * path)1045 void dt_accel_connect_preset_lib(dt_lib_module_t *module, const gchar *path)
1046 {
1047 char build_path[1024];
1048 gchar *name = g_strdup(path);
1049 snprintf(build_path, sizeof(build_path), "%s/%s", _("preset"), name);
1050 preset_lib_module_callback_description *callback_description
1051 = g_malloc(sizeof(preset_lib_module_callback_description));
1052 callback_description->module = module;
1053 callback_description->name = name;
1054
1055 GClosure *closure = g_cclosure_new(G_CALLBACK(preset_lib_module_callback), callback_description,
1056 preset_lib_module_callback_destroyer);
1057 dt_accel_connect_lib(module, build_path, closure);
1058 }
1059
dt_accel_deregister_iop(dt_iop_module_t * module,const gchar * path)1060 void dt_accel_deregister_iop(dt_iop_module_t *module, const gchar *path)
1061 {
1062 char build_path[1024];
1063 dt_accel_path_iop(build_path, sizeof(build_path), module->op, path);
1064
1065 dt_accel_t *accel = NULL;
1066
1067 for(const GList *modules = darktable.develop->iop; modules; modules = g_list_next(modules))
1068 {
1069 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
1070
1071 if(mod->so == module->so)
1072 {
1073 GSList **current_list = &mod->accel_closures;
1074 GSList *l = *current_list;
1075 while(l)
1076 {
1077 _accel_iop_t *iop_accel = (_accel_iop_t *)l->data;
1078
1079 if(iop_accel && iop_accel->accel && !strncmp(iop_accel->accel->path, build_path, 1024))
1080 {
1081 accel = iop_accel->accel;
1082
1083 if(iop_accel->closure == accel->closure || (accel->local && module->local_closures_connected))
1084 gtk_accel_group_disconnect(darktable.control->accelerators, iop_accel->closure);
1085
1086 *current_list = g_slist_delete_link(*current_list, l);
1087
1088 g_closure_unref(iop_accel->closure);
1089
1090 g_free(iop_accel);
1091
1092 break;
1093 }
1094
1095 l = g_slist_next(l);
1096 // if we've run out of global accelerators, switch to processing the local accelerators
1097 if(!l && current_list == &mod->accel_closures) l = *(current_list = &module->accel_closures_local);
1098 }
1099 }
1100 }
1101
1102 if(accel)
1103 {
1104 darktable.control->accelerator_list = g_list_remove(darktable.control->accelerator_list, accel);
1105
1106 g_free(accel);
1107 }
1108 }
1109
dt_accel_deregister_lib(dt_lib_module_t * module,const gchar * path)1110 void dt_accel_deregister_lib(dt_lib_module_t *module, const gchar *path)
1111 {
1112 char build_path[1024];
1113 dt_accel_path_lib(build_path, sizeof(build_path), module->plugin_name, path);
1114 for(GSList *l = module->accel_closures; l; l = g_slist_next(l))
1115 {
1116 dt_accel_t *accel = (dt_accel_t *)l->data;
1117 if(accel && !strncmp(accel->path, build_path, 1024))
1118 {
1119 module->accel_closures = g_slist_delete_link(module->accel_closures, l);
1120 gtk_accel_group_disconnect(darktable.control->accelerators, accel->closure);
1121 break;
1122 }
1123 }
1124 for(GList *ll = darktable.control->accelerator_list; ll; ll = g_list_next(ll))
1125 {
1126 dt_accel_t *accel = (dt_accel_t *)ll->data;
1127 if(accel && !strncmp(accel->path, build_path, 1024))
1128 {
1129 darktable.control->accelerator_list = g_list_delete_link(darktable.control->accelerator_list, ll);
1130 g_free(accel);
1131 break;
1132 }
1133 }
1134 }
1135
dt_accel_deregister_global(const gchar * path)1136 void dt_accel_deregister_global(const gchar *path)
1137 {
1138 char build_path[1024];
1139 dt_accel_path_global(build_path, sizeof(build_path), path);
1140 for(GList *l = darktable.control->accelerator_list; l; l = g_list_next(l))
1141 {
1142 dt_accel_t *accel = (dt_accel_t *)l->data;
1143 if(accel && !strncmp(accel->path, build_path, 1024))
1144 {
1145 darktable.control->accelerator_list = g_list_delete_link(darktable.control->accelerator_list, l);
1146 gtk_accel_group_disconnect(darktable.control->accelerators, accel->closure);
1147 g_free(accel);
1148 break;
1149 }
1150 }
1151 }
1152
dt_accel_deregister_lua(const gchar * path)1153 void dt_accel_deregister_lua(const gchar *path)
1154 {
1155 char build_path[1024];
1156 dt_accel_path_lua(build_path, sizeof(build_path), path);
1157 for(GList *l = darktable.control->accelerator_list; l; l = g_list_next(l))
1158 {
1159 dt_accel_t *accel = (dt_accel_t *)l->data;
1160 if(accel && !strncmp(accel->path, build_path, 1024))
1161 {
1162 darktable.control->accelerator_list = g_list_delete_link(darktable.control->accelerator_list, l);
1163 gtk_accel_group_disconnect(darktable.control->accelerators, accel->closure);
1164 g_free(accel);
1165 break;
1166 }
1167 }
1168 }
1169
dt_accel_deregister_manual(GSList * list,const gchar * full_path)1170 void dt_accel_deregister_manual(GSList *list, const gchar *full_path)
1171 {
1172 char build_path[1024];
1173 dt_accel_path_manual(build_path, sizeof(build_path), full_path);
1174 for(GSList *l = list; l; l = g_slist_next(l))
1175 {
1176 dt_accel_t *accel = (dt_accel_t *)l->data;
1177 if(accel && !strncmp(accel->path, build_path, 1024))
1178 {
1179 list = g_slist_delete_link(list, l);
1180 gtk_accel_group_disconnect(darktable.control->accelerators, accel->closure);
1181 break;
1182 }
1183 }
1184 for(GList *ll = darktable.control->accelerator_list; ll; ll = g_list_next(ll))
1185 {
1186 dt_accel_t *accel = (dt_accel_t *)ll->data;
1187 if(accel && !strncmp(accel->path, build_path, 1024))
1188 {
1189 darktable.control->accelerator_list = g_list_delete_link(darktable.control->accelerator_list, ll);
1190 g_free(accel);
1191 break;
1192 }
1193 }
1194 }
1195
find_accel_internal(GtkAccelKey * key,GClosure * closure,gpointer data)1196 gboolean find_accel_internal(GtkAccelKey *key, GClosure *closure, gpointer data)
1197 {
1198 return (closure == data);
1199 }
1200
dt_accel_rename_preset_iop(dt_iop_module_t * module,const gchar * path,const gchar * new_path)1201 void dt_accel_rename_preset_iop(dt_iop_module_t *module, const gchar *path, const gchar *new_path)
1202 {
1203 char *path_preset = g_strdup_printf("%s`%s", N_("preset"), path);
1204
1205 char build_path[1024];
1206 dt_accel_path_iop(build_path, sizeof(build_path), module->op, path_preset);
1207
1208 for(GSList *l = module->accel_closures; l; l = g_slist_next(l))
1209 {
1210 _accel_iop_t *iop_accel = (_accel_iop_t *)l->data;
1211 if(iop_accel && iop_accel->accel && !strncmp(iop_accel->accel->path, build_path, 1024))
1212 {
1213 GtkAccelKey tmp_key
1214 = *(gtk_accel_group_find(darktable.control->accelerators, find_accel_internal, iop_accel->closure));
1215 gboolean local = iop_accel->accel->local;
1216
1217 dt_accel_deregister_iop(module, path_preset);
1218
1219 snprintf(build_path, sizeof(build_path), "%s`%s", N_("preset"), new_path);
1220 dt_accel_register_iop(module->so, local, build_path, tmp_key.accel_key, tmp_key.accel_mods);
1221
1222 for(const GList *modules = darktable.develop->iop; modules; modules = g_list_next(modules))
1223 {
1224 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
1225 if(mod->so == module->so)
1226 dt_accel_connect_preset_iop(mod, new_path);
1227 }
1228
1229 break;
1230 }
1231 }
1232
1233 g_free(path_preset);
1234
1235 dt_accel_connect_instance_iop(module);
1236 }
1237
dt_accel_rename_preset_lib(dt_lib_module_t * module,const gchar * path,const gchar * new_path)1238 void dt_accel_rename_preset_lib(dt_lib_module_t *module, const gchar *path, const gchar *new_path)
1239 {
1240 char build_path[1024];
1241 dt_accel_path_lib(build_path, sizeof(build_path), module->plugin_name, path);
1242 for(GSList *l = module->accel_closures; l; l = g_slist_next(l))
1243 {
1244 dt_accel_t *accel = (dt_accel_t *)l->data;
1245 if(accel && !strncmp(accel->path, build_path, 1024))
1246 {
1247 GtkAccelKey tmp_key
1248 = *(gtk_accel_group_find(darktable.control->accelerators, find_accel_internal, accel->closure));
1249 dt_accel_deregister_lib(module, path);
1250 snprintf(build_path, sizeof(build_path), "%s/%s", _("preset"), new_path);
1251 dt_accel_register_lib(module, build_path, tmp_key.accel_key, tmp_key.accel_mods);
1252 dt_accel_connect_preset_lib(module, new_path);
1253 break;
1254 }
1255 }
1256 }
1257
dt_accel_rename_global(const gchar * path,const gchar * new_path)1258 void dt_accel_rename_global(const gchar *path, const gchar *new_path)
1259 {
1260 char build_path[1024];
1261 dt_accel_path_global(build_path, sizeof(build_path), path);
1262 for(GList *l = darktable.control->accelerator_list; l; l = g_list_next(l))
1263 {
1264 dt_accel_t *accel = (dt_accel_t *)l->data;
1265 if(accel && !strncmp(accel->path, build_path, 1024))
1266 {
1267 GtkAccelKey tmp_key
1268 = *(gtk_accel_group_find(darktable.control->accelerators, find_accel_internal, accel->closure));
1269 GClosure* closure = g_closure_ref(accel->closure);
1270 dt_accel_deregister_global(path);
1271 dt_accel_register_global(new_path, tmp_key.accel_key, tmp_key.accel_mods);
1272 dt_accel_connect_global(new_path, closure);
1273 g_closure_unref(closure);
1274 break;
1275 }
1276 }
1277 }
1278
dt_accel_rename_lua(const gchar * path,const gchar * new_path)1279 void dt_accel_rename_lua(const gchar *path, const gchar *new_path)
1280 {
1281 char build_path[1024];
1282 dt_accel_path_lua(build_path, sizeof(build_path), path);
1283 for(GList *l = darktable.control->accelerator_list; l; l = g_list_next(l))
1284 {
1285 dt_accel_t *accel = (dt_accel_t *)l->data;
1286 if(accel && !strncmp(accel->path, build_path, 1024))
1287 {
1288 GtkAccelKey tmp_key
1289 = *(gtk_accel_group_find(darktable.control->accelerators, find_accel_internal, accel->closure));
1290 GClosure* closure = g_closure_ref(accel->closure);
1291 dt_accel_deregister_lua(path);
1292 dt_accel_register_lua(new_path, tmp_key.accel_key, tmp_key.accel_mods);
1293 dt_accel_connect_lua(new_path, closure);
1294 g_closure_unref(closure);
1295 break;
1296 }
1297 }
1298 }
1299
dt_accel_find_by_path(const gchar * path)1300 dt_accel_t *dt_accel_find_by_path(const gchar *path)
1301 {
1302 return _lookup_accel(path);
1303 }
1304 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
1305 // vim: shiftwidth=2 expandtab tabstop=2 cindent
1306 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1307