"Fossies" - the Fresh Open Source Software Archive

Member "darktable-3.6.1/src/gui/accelerators.c" (10 Sep 2021, 45566 Bytes) of package /linux/misc/darktable-3.6.1.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "accelerators.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 3.4.1.1_vs_3.6.0.

    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 
   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 
   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 
   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 
   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 
   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 
   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 
   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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 }
  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 
 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 
 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 
 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 
 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 
 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 
 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 
 1196 gboolean find_accel_internal(GtkAccelKey *key, GClosure *closure, gpointer data)
 1197 {
 1198   return (closure == data);
 1199 }
 1200 
 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 
 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 
 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 
 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 
 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;