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