Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
lib.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2009-2011, 2013-2014 johannes hanika.
4 Copyright (C) 2010-2012 Henrik Andersson.
5 Copyright (C) 2010 Richard Hughes.
6 Copyright (C) 2010 Stuart Henderson.
7 Copyright (C) 2010-2018 Tobias Ellinghaus.
8 Copyright (C) 2011 Antony Dovgal.
9 Copyright (C) 2011 Brian Teague.
10 Copyright (C) 2011-2012, 2014-2015 Jérémy Rosen.
11 Copyright (C) 2011 Robert Bieber.
12 Copyright (C) 2011-2012 Ulrich Pegelow.
13 Copyright (C) 2012 Ivan Tarozzi.
14 Copyright (C) 2012 Petr Styblo.
15 Copyright (C) 2012 Richard Wonka.
16 Copyright (C) 2013, 2018-2022 Pascal Obry.
17 Copyright (C) 2013-2017 Roman Lebedev.
18 Copyright (C) 2013 Simon Spannagel.
19 Copyright (C) 2014 Edouard Gomez.
20 Copyright (C) 2014 Pascal de Bruijn.
21 Copyright (C) 2017, 2021 Dan Torop.
22 Copyright (C) 2017-2018 parafin.
23 Copyright (C) 2019-2022 Aldric Renaudin.
24 Copyright (C) 2019, 2022-2023, 2025-2026 Aurélien PIERRE.
25 Copyright (C) 2019 Edgardo Hoszowski.
26 Copyright (C) 2019 Heiko Bauke.
27 Copyright (C) 2019-2022 Philippe Weyland.
28 Copyright (C) 2019 Sam Smith.
29 Copyright (C) 2020 Chris Elston.
30 Copyright (C) 2020-2022 Diederik Ter Rahe.
31 Copyright (C) 2020-2021 Hubert Kowalski.
32 Copyright (C) 2020 Marco.
33 Copyright (C) 2020 Matt Maguire.
34 Copyright (C) 2020-2021 Ralf Brown.
35 Copyright (C) 2021 Arnaud TANGUY.
36 Copyright (C) 2021 Bill Ferguson.
37 Copyright (C) 2021 Marco Carrarini.
38 Copyright (C) 2021-2022 Nicolas Auffray.
39 Copyright (C) 2021 wpferguson.
40 Copyright (C) 2022 Martin Bařinka.
41
42 darktable is free software: you can redistribute it and/or modify
43 it under the terms of the GNU General Public License as published by
44 the Free Software Foundation, either version 3 of the License, or
45 (at your option) any later version.
46
47 darktable is distributed in the hope that it will be useful,
48 but WITHOUT ANY WARRANTY; without even the implied warranty of
49 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
50 GNU General Public License for more details.
51
52 You should have received a copy of the GNU General Public License
53 along with darktable. If not, see <http://www.gnu.org/licenses/>.
54*/
55#include "common/sentry.h"
57#include "common/telemetry.h"
58#include "widgets/gdkkeys.h"
59#include "libs/lib.h"
60#include "views/view.h"
62#include "system/macros.h"
63#include "common/module.h"
64#include "common/module_versioning.h" // dt_version(), used by libs/lib_api.h below
65#include "common/conf.h"
66#include "control/control.h"
67#include "develop/develop.h"
68#include "widgets/button.h"
69#include "widgets/expander.h"
70
72#include "libs/colorpicker.h"
73#include "gui/application.h"
74#include "gui/presets.h"
75#ifdef GDK_WINDOWING_QUARTZ
76#include "osx/osx.h"
77#endif
78#include <stdbool.h>
79#include <stdlib.h>
80#include "widgets/label.h"
81#include "widgets/popup.h"
83#include "control/signal.h"
84
85
92
101
113
115{
116 // No view means nothing is visible in it. Callers in views/view.c guard before calling,
117 // but _toggle_expanded() passes dt_view_manager_get_current_view() straight through, and
118 // that is NULL mid-switch -- the loop below reads view->module_name.
119 if(IS_NULL_PTR(view)) return FALSE;
120
121 if(!module->views)
122 {
123 fprintf(stderr, "module %s doesn't have views flags\n", module->name(module));
124 return FALSE;
125 }
126
127 const char **views = module->views(module);
128 for(const char **iter = views; *iter; iter++)
129 {
130 if(!strcmp(*iter, "*") || !strcmp(*iter, view->module_name)) return TRUE;
131 }
132 return FALSE;
133}
134
136static void dt_lib_unload_module(dt_lib_module_t *module);
137
139{
140 gchar *name = NULL;
141
142 // collect all presets for op from db, and find the one whose params we are showing
143 GList *presets = dt_preset_repository_list_for_module(minfo->plugin_name, minfo->version,
144 FALSE, FALSE);
145 for(GList *l = presets; l; l = g_list_next(l))
146 {
147 const dt_module_preset_t *p = (const dt_module_preset_t *)l->data;
148 if(p->op_params_size == minfo->params_size && !memcmp(minfo->params, p->op_params, p->op_params_size))
149 {
150 name = g_strdup(p->name);
151 break;
152 }
153 }
154 g_list_free_full(presets, dt_module_preset_free);
155 return name;
156}
157
158static void edit_preset(const char *name_in, dt_lib_module_info_t *minfo)
159{
160 // get the original name of the preset
161 gchar *name = NULL;
162 if(IS_NULL_PTR(name_in))
163 {
165 if(IS_NULL_PTR(name)) return;
166 }
167 else
168 name = g_strdup(name_in);
169
170 // find the rowid of the preset
171 const int rowid = dt_preset_repository_find_rowid(minfo->plugin_name, minfo->version, name);
172
173 // if we don't have a valid rowid, just exit, there's a problem !
174 if(rowid < 0) return;
175
177 dt_gui_presets_show_edit_dialog(name, minfo->plugin_name, rowid, NULL, NULL, TRUE, TRUE, FALSE,
178 GTK_WINDOW(window));
179}
180
181static void menuitem_update_preset(GtkMenuItem *menuitem, dt_lib_module_info_t *minfo)
182{
183 char *name = g_object_get_data(G_OBJECT(menuitem), "dt-preset-name");
184
185 gint res = GTK_RESPONSE_YES;
186
187 if(dt_conf_get_bool("plugins/lighttable/preset/ask_before_delete_preset"))
188 {
190 GtkWidget *dialog
191 = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION,
192 GTK_BUTTONS_YES_NO, _("do you really want to update the preset `%s'?"), name);
193#ifdef GDK_WINDOWING_QUARTZ
195#endif
196 gtk_window_set_title(GTK_WINDOW(dialog), _("update preset?"));
197 res = gtk_dialog_run(GTK_DIALOG(dialog));
198 gtk_widget_destroy(dialog);
199 }
200
201 if(res == GTK_RESPONSE_YES)
202 {
203 // commit all the module fields
205 minfo->params, minfo->params_size);
207 g_strdup(minfo->plugin_name));
208 }
209}
210
211static void menuitem_new_preset(GtkMenuItem *menuitem, dt_lib_module_info_t *minfo)
212{
213 dt_lib_presets_remove(_("new preset"), minfo->plugin_name, minfo->version);
214
215 // add new preset
216 dt_preset_repository_add_module_preset(_("new preset"), minfo->plugin_name, minfo->version,
217 minfo->params, minfo->params_size);
218 // create a shortcut for the new entry
219
220 // then show edit dialog
221 edit_preset(_("new preset"), minfo);
222}
223
224static void menuitem_edit_preset(GtkMenuItem *menuitem, dt_lib_module_info_t *minfo)
225{
226 edit_preset(NULL, minfo);
227}
228
229static void menuitem_manage_presets(GtkMenuItem *menuitem, dt_lib_module_info_t *minfo)
230{
231 if(minfo->module->manage_presets) minfo->module->manage_presets(minfo->module);
232}
233
234static void menuitem_delete_preset(GtkMenuItem *menuitem, dt_lib_module_info_t *minfo)
235{
236 gchar *name = get_active_preset_name(minfo);
237 if(IS_NULL_PTR(name)) return;
238
239 gint res = GTK_RESPONSE_YES;
240
241 if(dt_conf_get_bool("plugins/lighttable/preset/ask_before_delete_preset"))
242 {
244 GtkWidget *dialog
245 = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION,
246 GTK_BUTTONS_YES_NO, _("do you really want to delete the preset `%s'?"), name);
247#ifdef GDK_WINDOWING_QUARTZ
249#endif
250 gtk_window_set_title(GTK_WINDOW(dialog), _("delete preset?"));
251 res = gtk_dialog_run(GTK_DIALOG(dialog));
252 gtk_widget_destroy(dialog);
253 }
254
255 if(res == GTK_RESPONSE_YES)
256 {
258
260 g_strdup(minfo->plugin_name));
261 }
262 dt_free(name);
263}
264
265gchar *dt_lib_presets_duplicate(const gchar *preset, const gchar *module_name, int module_version)
266{
267 // find the new name
268 int i = 0;
269 gboolean ko = TRUE;
270 while(ko)
271 {
272 i++;
273 gchar *tx = g_strdup_printf("%s_%d", preset, i);
274 if(!dt_preset_repository_module_preset_exists(module_name, module_version, tx)) ko = FALSE;
275 dt_free(tx);
276 }
277 gchar *nname = g_strdup_printf("%s_%d", preset, i);
278
279 // and we duplicate the entry
280 dt_preset_repository_duplicate_module_preset(module_name, module_version, preset, nname);
281
282 return nname;
283}
284
285void dt_lib_presets_remove(const gchar *preset, const gchar *module_name, int module_version)
286{
287 dt_preset_repository_delete_module_preset(module_name, module_version, preset);
288}
289
290gboolean dt_lib_presets_apply(const gchar *preset, const gchar *module_name, int module_version)
291{
292 gboolean ret = TRUE;
293 dt_module_preset_t *p = dt_preset_repository_get_module_preset(module_name, module_version, preset);
294
295 int res = 0;
296 if(p)
297 {
298 const void *blob = p->op_params;
299 int length = p->op_params_size;
300 int writeprotect = p->writeprotect;
301 if(blob)
302 {
303 for(const GList *it = dt_lib_get_global()->plugins; it; it = g_list_next(it))
304 {
305 dt_lib_module_t *module = (dt_lib_module_t *)it->data;
306 if(!strncmp(module->plugin_name, module_name, 128))
307 {
308 gchar *tx = g_strdup_printf("plugins/darkroom/%s/last_preset", module_name);
310 dt_free(tx);
311 res = module->set_params(module, blob, length);
312 break;
313 }
314 }
315 }
316
317 if(!writeprotect) dt_gui_store_last_preset(preset);
318 }
319 else
320 ret = FALSE;
322 if(res)
323 {
324 dt_control_log(_("deleting preset for obsolete module"));
325 dt_lib_presets_remove(preset, module_name, module_version);
326 }
327 return ret;
328}
329
330void dt_lib_presets_update(const gchar *preset, const gchar *module_name, int module_version, const gchar *newname,
331 const gchar *desc, const void *params, const int32_t params_size)
332{
333 dt_preset_repository_rename_module_preset(module_name, module_version, preset, newname,
334 desc, params, params_size);
335}
336
337static void pick_callback(GtkMenuItem *menuitem, dt_lib_module_info_t *minfo)
338{
339 // apply preset via set_params
340 const char *pn = g_object_get_data(G_OBJECT(menuitem), "dt-preset-name");
341 dt_lib_presets_apply(pn, minfo->plugin_name, minfo->version);
342}
343
344static void free_module_info(GtkWidget *widget, gpointer user_data)
345{
346 dt_lib_module_info_t *minfo = (dt_lib_module_info_t *)user_data;
347 dt_free(minfo->plugin_name);
348 dt_free(minfo->params);
349 dt_free(minfo);
350}
351
353{
354 GtkMenu *menu = dt_gui_get_global()->presets_popup_menu;
355 if(menu) gtk_widget_destroy(GTK_WIDGET(menu));
356 dt_gui_get_global()->presets_popup_menu = GTK_MENU(gtk_menu_new());
358
359 const gboolean hide_default = dt_conf_get_bool("plugins/lighttable/hide_default_presets");
360 const gboolean default_first = dt_conf_get_bool("modules/default_presets_first");
361
362 g_signal_connect(G_OBJECT(menu), "destroy", G_CALLBACK(free_module_info), minfo);
363
364 GtkWidget *mi;
365 int active_preset = -1, cnt = 0;
366 gboolean selected_writeprotect = FALSE;
367 // order like the pref value
368 GList *presets = dt_preset_repository_list_for_module(minfo->plugin_name, minfo->version,
369 TRUE, default_first);
370
371 // collect all presets for op from db
372 int found = 0;
373 int last_wp = -1;
374 for(GList *l = presets; l; l = g_list_next(l))
375 {
376 const dt_module_preset_t *p = (const dt_module_preset_t *)l->data;
377 // default vs built-in stuff
378 const gboolean writeprotect = p->writeprotect;
379 if(hide_default && writeprotect)
380 {
381 // skip default module if set to hide them.
382 continue;
383 }
384 if(last_wp == -1)
385 {
386 last_wp = writeprotect;
387 }
388 else if(last_wp != writeprotect)
389 {
390 last_wp = writeprotect;
391 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
392 }
393
394 void *op_params = p->op_params;
395 int32_t op_params_size = p->op_params_size;
396 const char *name = p->name;
397
398 if(dt_gui_get_global()->last_preset && strcmp(dt_gui_get_global()->last_preset, name) == 0) found = 1;
399
400 // selected in bold:
401 // printf("comparing %d bytes to %d\n", op_params_size, minfo->params_size);
402 // for(int k=0;k<op_params_size && !memcmp(minfo->params, op_params, k);k++) printf("compare [%c %c] %d:
403 // %d\n",
404 // ((const char*)(minfo->params))[k],
405 // ((const char*)(op_params))[k],
406 // k, memcmp(minfo->params, op_params, k));
407 if(op_params_size == minfo->params_size && !memcmp(minfo->params, op_params, op_params_size))
408 {
409 active_preset = cnt;
410 selected_writeprotect = writeprotect;
411 mi = gtk_check_menu_item_new_with_label(name);
412
413 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mi), TRUE);
414 dt_gui_add_class(mi, "active_menu_item");
415 }
416 else
417 {
418 mi = gtk_menu_item_new_with_label((const char *)name);
419 }
420 g_object_set_data_full(G_OBJECT(mi), "dt-preset-name", g_strdup(name), g_free);
421 g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(pick_callback), minfo);
422 gtk_widget_set_tooltip_text(mi, p->description);
423 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
424 cnt++;
425 }
426 g_list_free_full(presets, dt_module_preset_free);
427
428 if(cnt > 0)
429 {
430 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
431 cnt = 0;
432 }
433
434 if(minfo->module->manage_presets)
435 {
436 mi = gtk_menu_item_new_with_label(_("manage presets..."));
437 g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(menuitem_manage_presets), minfo);
438 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
439 cnt++;
440 }
441 else if(active_preset >= 0) // FIXME: this doesn't seem to work.
442 {
443 if(!selected_writeprotect)
444 {
445 mi = gtk_menu_item_new_with_label(_("edit this preset.."));
446 g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(menuitem_edit_preset), minfo);
447 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
448
449 mi = gtk_menu_item_new_with_label(_("delete this preset"));
450 g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(menuitem_delete_preset), minfo);
451 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
452 cnt++;
453 }
454 }
455 else
456 {
457 mi = gtk_menu_item_new_with_label(_("store new preset.."));
458 if(minfo->params_size == 0)
459 {
460 gtk_widget_set_sensitive(GTK_WIDGET(mi), FALSE);
461 gtk_widget_set_tooltip_text(mi, _("nothing to save"));
462 }
463 else
464 g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(menuitem_new_preset), minfo);
465 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
466
467 if(dt_gui_get_global()->last_preset && found)
468 {
469 char *markup = g_markup_printf_escaped("%s <span weight=\"bold\">%s</span>", _("update preset"),
470 dt_gui_get_global()->last_preset);
471 mi = gtk_menu_item_new_with_label("");
472 gtk_widget_set_sensitive(GTK_WIDGET(mi), minfo->params_size > 0);
473 gtk_label_set_markup(GTK_LABEL(gtk_bin_get_child(GTK_BIN(mi))), markup);
474 g_object_set_data_full(G_OBJECT(mi), "dt-preset-name", g_strdup(dt_gui_get_global()->last_preset), g_free);
475 g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(menuitem_update_preset), minfo);
476 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
477 dt_free(markup);
478 }
479 cnt++;
480 }
481
482 if(minfo->module->set_preferences)
483 {
484 if(cnt>0)
485 {
486 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
487 }
488 minfo->module->set_preferences(GTK_MENU_SHELL(menu), minfo->module);
489 }
490}
491
492gint dt_lib_sort_plugins(gconstpointer a, gconstpointer b)
493{
494 const dt_lib_module_t *am = (const dt_lib_module_t *)a;
495 const dt_lib_module_t *bm = (const dt_lib_module_t *)b;
496 const int apos = (am && am->position) ? am->position() : 0;
497 const int bpos = (bm && bm->position) ? bm->position() : 0;
498 return apos - bpos;
499}
500
501/* default expandable implementation */
503{
504 return TRUE;
505}
506
507/* default autoapply implementation */
509{
510 return FALSE;
511}
512
513int default_lib_focus(dt_gui_module_t *m, gboolean toogle)
514{
515 dt_lib_module_t *module = (dt_lib_module_t *) m;
517 return 1;
518}
519
520static int _lib_plugin_body_button_press(GtkWidget *w, GdkEventButton *e, gpointer user_data)
521{
522 /* Reset the scrolling focus. If the click happened on any bauhaus element,
523 * its internal button_press method will set it for itself */
525 int handled = FALSE;
526 return handled;
527}
528
529static int dt_lib_load_module(void *m, const char *libname, const char *module_name)
530{
531 dt_lib_module_t *module = (dt_lib_module_t *)m;
532
533#define INCLUDE_API_FROM_MODULE_LOAD "lib_load_module"
534#include "libs/lib_api.h"
535
536 // Load modules only if they belong to a loaded view.
537 // Designed for print settings, which loads all installed CUPS printers at gui_init() time,
538 // which can take some time.
539 gboolean load = FALSE;
540 for(const char **view_m = module->views(module); *view_m; ++view_m)
541 {
542 for(GList *iter = dt_view_manager_get_global()->views; iter; iter = g_list_next(iter))
543 {
544 dt_view_t *view = (dt_view_t *)iter->data;
545 if(!g_strcmp0(view->module_name, *view_m) || !g_strcmp0("*", *view_m) || !g_strcmp0("special", *view_m))
546 {
547 load = TRUE;
548 break;
549 }
550 }
551 if(load) break;
552 }
553
554 if(!load) return 1;
555
556 g_strlcpy(module->plugin_name, module_name, sizeof(module->plugin_name));
557
558 if(((!module->get_params || !module->set_params)
559 && (module->legacy_params || module->set_params || module->get_params))
560 || (!module->init_presets && module->manage_presets))
561 {
562 fprintf(stderr,"[dt_lib_load_module] illegal method combination in '%s'\n", module->plugin_name);
563 }
564
565 if(!module->get_params || !module->set_params)
566 {
567 // need all at the same time, or none, note that in this case
568 // all the presets for the corresponding module will be deleted.
569 // see: dt_lib_init_presets.
570 module->legacy_params = NULL;
571 module->set_params = NULL;
572 module->get_params = NULL;
573 module->manage_presets = NULL;
574 }
575
576 module->widget = NULL;
577 module->expander = NULL;
578 module->arrow = NULL;
579 module->reset_button = NULL;
580 module->presets_button = NULL;
581
582 if(module->init) module->init(module);
583
584 /* pass on the dt_gui_module_t args for bauhaus widgets */
585 module->common_fields.name = g_strdup(module->name(module));
586 module->common_fields.view = NULL; // view is set at gui_init time
587 module->common_fields.widget_list = NULL;
588 module->common_fields.widget_list_bh = NULL;
589 module->common_fields.focus = module->lib_focus;
590 module->common_fields.deprecated = FALSE;
591
592 return 0;
593}
594
595static void *_update_params(dt_lib_module_t *module,
596 const void *const old_params, size_t old_params_size, int old_version,
597 int target_version, size_t *new_size)
598{
599 // make a copy of the old params so we can free it in the loop
600 void *params = malloc(old_params_size);
601 if(IS_NULL_PTR(params)) return NULL;
602 memcpy(params, old_params, old_params_size);
603 while(old_version < target_version)
604 {
605 size_t size;
606 int version;
607 void *new_params = module->legacy_params(module, params, old_params_size, old_version, &version, &size);
608 dt_free(params);
609 if(IS_NULL_PTR(new_params)) return NULL;
610 params = new_params;
611 old_version = version;
612 old_params_size = size;
613 }
614 *new_size = old_params_size;
615 return params;
616}
617
619{
620 // since lighttable presets can't end up in styles or any other place outside of the presets table it is
621 // sufficient
622 // to update that very table here and assume that everything is up to date elsewhere.
623 // the intended logic is as follows:
624 // - no set_params -> delete all presets
625 // - op_version >= module_version -> done
626 // - op_version < module_version ->
627 // - module has legacy_params -> try to update
628 // - module doesn't have legacy_params -> delete it
629
630 if(IS_NULL_PTR(module->set_params))
631 {
633 }
634 else
635 {
636 GList *presets = dt_preset_repository_list_all_versions(module->plugin_name);
637 for(GList *l = presets; l; l = g_list_next(l))
638 {
639 const dt_module_preset_t *p = (const dt_module_preset_t *)l->data;
640 const int version = module->version();
641
642 if(p->op_version < version)
643 {
644 size_t new_params_size = 0;
645 void *new_params = NULL;
646
647 if(module->legacy_params
648 && (new_params = _update_params(module, p->op_params, p->op_params_size, p->op_version, version,
649 &new_params_size)))
650 {
651 // write the updated preset back to db
652 fprintf(stderr,
653 "[lighttable_init_presets] updating '%s' preset '%s' from version %d to version %d\n",
654 module->plugin_name, p->name, p->op_version, version);
655 dt_preset_repository_update_params_by_rowid(p->rowid, version, new_params, new_params_size);
656 }
657 else
658 {
659 // delete the preset
660 fprintf(stderr, "[lighttable_init_presets] Can't upgrade '%s' preset '%s' from version %d to %d, "
661 "no legacy_params() implemented or unable to update\n",
662 module->plugin_name, p->name, p->op_version, version);
664 }
665 dt_free(new_params);
666 }
667 }
668 g_list_free_full(presets, dt_module_preset_free);
669 }
670
671 if(module->init_presets)
672 module->init_presets(module);
673
675 g_strdup(module->plugin_name));
676}
677
678
679static gboolean _lib_plugin_focus_accel(GtkAccelGroup *accel_group, GObject *accelerable, guint keyval,
680 GdkModifierType modifier, gpointer data)
681{
682 dt_gui_module_t *module = (dt_gui_module_t *)data;
683 if(!module || !module->focus) return FALSE;
684 return module->focus(module, FALSE);
685}
686
687
688static void dt_lib_init_module(void *m)
689{
690 dt_lib_module_t *module = (dt_lib_module_t *)m;
691 dt_lib_init_presets(module);
692
694 {
695 module->gui_init(module);
696 if(module->widget) g_object_ref_sink(module->widget);
697
698 // TODO: look for active view. Do we know it at init time ?
699 module->common_fields.view = g_strdup(_("Lighttable"));
700
701 if(!module->views || (module->expandable && !module->expandable(module))) return; // We are done
702 // Else: add accel pathes
703
704 gchar *clean_name = delete_underscore(module->name(module));
705 dt_capitalize_label(clean_name);
706
707 // slash is not allowed in module names because that makes accel pathes fail
708 assert(g_strrstr(clean_name, "/") == NULL);
709
710 const char **views = module->views(module);
711
712 // We add one accel path per view
713 for(const char **view = views; *view; ++view)
714 {
715 GtkAccelGroup *accel_group = NULL;
716 gchar *label = NULL;
717 if(!g_strcmp0(*view, "darkroom"))
718 {
719 accel_group = dt_gui_get_accels()->darkroom_accels;
720 label = g_strdup("Darkroom/Toolboxes");
721 }
722 else if(!g_strcmp0(*view, "lighttable"))
723 {
724 accel_group = dt_gui_get_accels()->lighttable_accels;
725 label = g_strdup("Lighttable/Toolboxes");
726 }
727 else if(!g_strcmp0(*view, "map"))
728 {
729 accel_group = dt_gui_get_accels()->map_accels;
730 label = g_strdup("Map/Toolboxes");
731 }
732 else if(!g_strcmp0(*view, "print"))
733 {
734 accel_group = dt_gui_get_accels()->print_accels;
735 label = g_strdup("Print/Toolboxes");
736 }
737 else if(!g_strcmp0(*view, "slideshow"))
738 {
739 accel_group = dt_gui_get_accels()->slideshow_accels;
740 label = g_strdup("Slideshow/Toolboxes");
741 }
742
743 if(accel_group && label)
745 accel_group, label, clean_name, 0, 0, FALSE,
746 _("Focuses the module"));
747 dt_free(label);
748 }
749
750 dt_free(clean_name);
751 }
752}
753
755{
756 GtkWidget *retained_widget = module->widget;
757
758 if(dt_gui_get_global() && dt_gui_get_accels() && module->views)
759 {
760 gchar *clean_name = delete_underscore(module->name(module));
761 dt_capitalize_label(clean_name);
762
763 const char **views = module->views(module);
764 for(const char **view = views; view && *view; ++view)
765 {
766 const char *scope = NULL;
767 if(!g_strcmp0(*view, "darkroom")) scope = "Darkroom/Toolboxes";
768 else if(!g_strcmp0(*view, "lighttable")) scope = "Lighttable/Toolboxes";
769 else if(!g_strcmp0(*view, "map")) scope = "Map/Toolboxes";
770 else if(!g_strcmp0(*view, "print")) scope = "Print/Toolboxes";
771 else if(!g_strcmp0(*view, "slideshow")) scope = "Slideshow/Toolboxes";
772
773 if(scope)
774 {
775 gchar *path = dt_accels_build_path(scope, clean_name);
777 dt_free(path);
778 }
779 }
780
781 dt_free(clean_name);
782 }
783
785 g_list_free(m->widget_list);
786 m->widget_list = NULL;
787 g_list_free(m->widget_list_bh);
788 m->widget_list_bh = NULL;
789 dt_free(m->name);
790 dt_free(m->view);
791
792 if(!IS_NULL_PTR(module->expander) && GTK_IS_WIDGET(module->expander))
793 {
794 GtkWidget *expander = module->expander;
795 g_object_ref_sink(expander);
796 gtk_widget_destroy(expander);
797 g_object_unref(expander);
798 }
799 else if(!IS_NULL_PTR(module->widget) && GTK_IS_WIDGET(module->widget))
800 {
801 GtkWidget *widget = module->widget;
802 g_object_ref_sink(widget);
803 gtk_widget_destroy(widget);
804 g_object_unref(widget);
805 }
806 module->expander = NULL;
807 module->widget = NULL;
808 if(!IS_NULL_PTR(retained_widget) && G_IS_OBJECT(retained_widget))
809 g_object_unref(retained_widget);
810
811 if(module->module) g_module_close(module->module);
812}
813
814static void dt_lib_gui_reset_callback(GtkButton *button, gpointer user_data)
815{
816 dt_lib_module_t *module = (dt_lib_module_t *)user_data;
817 module->gui_reset(module);
818}
819
820static void presets_popup_callback(GtkButton *button, dt_lib_module_t *module)
821{
823
824 mi->plugin_name = g_strdup(module->plugin_name);
825 mi->version = module->version();
826 mi->module = module;
827 mi->params = module->get_params ? module->get_params(module, &mi->params_size) : NULL;
828 if(IS_NULL_PTR(mi->params))
829 {
830 // this is a valid case, for example in location.c when nothing got selected
831 // fprintf(stderr, "something went wrong: &params=%p, size=%i\n", mi->params, mi->params_size);
832 mi->params_size = 0;
833 }
835
836 dt_gui_menu_popup(dt_gui_get_global()->presets_popup_menu, GTK_WIDGET(button), GDK_GRAVITY_SOUTH_EAST, GDK_GRAVITY_NORTH_EAST);
837
838 if(button) dtgtk_button_set_active(DTGTK_BUTTON(button), FALSE);
839}
840
841static void _lib_module_expander_gone(gpointer user_data, GObject *where_the_object_was)
842{
843 dt_lib_module_t *module = (dt_lib_module_t *)user_data;
844 if(IS_NULL_PTR(module)) return;
845 if(module->expander == (GtkWidget *)where_the_object_was) module->expander = NULL;
847 {
848 if(dt_gui_get_global()->scroll_to[0] == (GtkWidget *)where_the_object_was) dt_gui_get_global()->scroll_to[0] = NULL;
849 if(dt_gui_get_global()->scroll_to[1] == (GtkWidget *)where_the_object_was) dt_gui_get_global()->scroll_to[1] = NULL;
850 }
851}
852
853
854void dt_lib_gui_set_expanded(dt_lib_module_t *module, gboolean expanded)
855{
856 if(IS_NULL_PTR(module->expander)) return;
857 if(!DTGTK_IS_EXPANDER(module->expander))
858 {
859 module->expander = NULL;
860 return;
861 }
862
864
865 /* record lib panel usage for crash reports and usage analytics */
866 if(expanded)
867 {
870 }
871
872 /* show / hide plugin widget */
873 if(expanded)
874 {
875 /* register to receive draw events */
876 dt_lib_get_global()->gui_module = module;
877 dt_gui_get_global()->scroll_to[1] = module->expander;
878 gtk_widget_grab_focus(GTK_WIDGET(module->expander));
879 }
880 else
881 {
882 if(dt_lib_get_global()->gui_module == module)
883 {
886 }
888 }
889
890 if(expanded)
891 dt_gui_add_class(module->expander, "expanded");
892 else
893 dt_gui_remove_class(module->expander, "expanded");
894
895 // Update expander arrow state
896 // Note: directions are inverted in drawing method for some reason.
899 (expanded ? CPF_DIRECTION_UP : CPF_DIRECTION_LEFT),
900 NULL);
901
902 /* store expanded state of module */
903 char var[1024];
905 // There is no current view while the view manager is switching between them, and the conf
906 // key is built from its name -- so with no view there is no key to write, and nothing to
907 // store. dt_lib_gui_get_expanded() below makes the same check on the reading side, where
908 // omitting it crashed (Sentry 139067518).
909 if(IS_NULL_PTR(current_view)) return;
910 snprintf(var, sizeof(var), "plugins/%s/%s/expanded", current_view->module_name, module->plugin_name);
911 dt_conf_set_bool(var, expanded);
912}
913
915{
916 if(!module->expandable(module)) return true;
917 if(IS_NULL_PTR(module->expander)) return true;
918 if(!DTGTK_IS_EXPANDER(module->expander))
919 {
920 module->expander = NULL;
921 if(!module->widget)
922 {
923 char var[1024];
925 if(IS_NULL_PTR(current_view)) return true;
926 snprintf(var, sizeof(var), "plugins/%s/%s/expanded", current_view->module_name, module->plugin_name);
927 return dt_conf_get_bool(var);
928 }
929 return true;
930 }
931 if(!module->widget)
932 {
933 char var[1024];
935 // Same check the identical block above already makes. There is no current view while the
936 // manager is switching between them, and this is called from the darkroom expose --
937 // Sentry 139067518: EXCEPTION_ACCESS_VIOLATION reading current_view->module_name here.
938 if(IS_NULL_PTR(current_view)) return true;
939 snprintf(var, sizeof(var), "plugins/%s/%s/expanded", current_view->module_name, module->plugin_name);
940 return dt_conf_get_bool(var);
941 }
943}
944
945static void _toggle_expanded(dt_lib_module_t *module, gboolean close_all)
946{
947 if(close_all)
948 {
950 gboolean all_other_closed = TRUE;
951 uint32_t container = module->container(module);
952
953 for(const GList *it = dt_lib_get_global()->plugins; it; it = g_list_next(it))
954 {
956
957 if(m != module && container == m->container(m) && m->expandable(m) && dt_lib_is_visible_in_view(m, v))
958 {
959 if(m->expander && DTGTK_IS_EXPANDER(m->expander))
960 all_other_closed = all_other_closed && !dtgtk_expander_get_expanded(DTGTK_EXPANDER(m->expander));
961
963 }
964 }
965 if(all_other_closed)
967 else
969 }
970 else
971 {
972 /* else just toggle */
974 }
975}
976
977static void expand_callback(GtkButton *button, dt_lib_module_t *module)
978{
979 _toggle_expanded(module, FALSE);
980}
981
982static gboolean _lib_plugin_header_button_press(GtkWidget *w, GdkEventButton *e, gpointer user_data)
983{
984 if(e->type == GDK_2BUTTON_PRESS || e->type == GDK_3BUTTON_PRESS) return TRUE;
985
986 dt_lib_module_t *module = (dt_lib_module_t *)user_data;
987
988 /* Reset the scrolling focus. If the click happened on any bauhaus element,
989 * its internal button_press method will set it for itself */
991
992 if(e->button == 1)
993 {
994 /* bail out if module is static */
995 if(!module->expandable(module)) return FALSE;
996
997 // make gtk scroll to the module once it updated its allocation size
998 uint32_t container = module->container(module);
1000 dt_gui_get_global()->scroll_to[0] = module->expander;
1002 dt_gui_get_global()->scroll_to[1] = module->expander;
1003
1004 gtk_widget_grab_focus(GTK_WIDGET(module->expander));
1005
1006 /* handle shiftclick on expander, hide all except this */
1007 _toggle_expanded(module, dt_modifier_is(e->state, GDK_SHIFT_MASK));
1008
1009 return TRUE;
1010 }
1011 else if(e->button == 3)
1012 {
1013 if(gtk_widget_get_sensitive(module->presets_button))
1014 presets_popup_callback(NULL, module);
1015
1016 return TRUE;
1017 }
1018 return FALSE;
1019}
1020
1021#if 0
1022static void show_module_callback(dt_lib_module_t *module)
1023{
1024 /* bail out if module is static */
1025 if(!module->expandable(module)) return;
1026
1027 // make gtk scroll to the module once it updated its allocation size
1028 uint32_t container = module->container(module);
1030 dt_gui_get_global()->scroll_to[0] = module->expander;
1032 dt_gui_get_global()->scroll_to[1] = module->expander;
1033
1035}
1036
1037#endif
1038
1040{
1041 /* check if module is expandable */
1042 if(!module->expandable(module))
1043 {
1044 if(module->presets_button)
1045 {
1046 // FIXME separately define as darkroom widget shortcut/action, because not automatically registered via lib
1047 // if presets btn has been loaded to be shown outside expander
1048 g_signal_connect(G_OBJECT(module->presets_button), "clicked", G_CALLBACK(presets_popup_callback), module);
1049 }
1050 module->expander = NULL;
1051 return NULL;
1052 }
1053
1054 GtkWidget *header = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING / 2.);
1055 gtk_widget_set_name(GTK_WIDGET(header), "module-header");
1056
1057 GtkWidget *expander = dtgtk_expander_new(header, module->widget);
1058 dt_gui_add_class(expander, "dt_module_frame");
1059 dt_gui_add_class(expander, "dt_lib_module");
1060
1063 GtkWidget *pluginui_frame = dtgtk_expander_get_frame(DTGTK_EXPANDER(expander));
1064 dt_gui_add_class(pluginui_frame, "dt_plugin_ui");
1065
1066 /* setup the header box */
1067 g_signal_connect(G_OBJECT(header_evb), "button-press-event", G_CALLBACK(_lib_plugin_header_button_press),
1068 module);
1069
1070 /* connect mouse button callbacks for focus and presets */
1071 g_signal_connect(G_OBJECT(body_evb), "button-press-event", G_CALLBACK(_lib_plugin_body_button_press), module);
1072 gtk_widget_add_events(body_evb, GDK_POINTER_MOTION_MASK);
1073
1074 /*
1075 * initialize the header widgets
1076 */
1077
1078 /* add collapsing arrow */
1079 if(module->expandable(module))
1080 {
1081 module->arrow = dtgtk_button_new(dtgtk_cairo_paint_arrow, 0, NULL);
1082 g_signal_connect(G_OBJECT(module->arrow), "clicked", G_CALLBACK(expand_callback), module);
1083 gtk_box_pack_start(GTK_BOX(header), module->arrow, FALSE, FALSE, 0);
1084 dt_gui_add_class(module->arrow, "dt-collapse-arrow");
1085 }
1086
1087 /* add module label */
1088 GtkWidget *label = gtk_label_new("");
1089 GtkWidget *label_evb = gtk_event_box_new();
1090 gtk_container_add(GTK_CONTAINER(label_evb), label);
1091 gchar *mname = g_markup_escape_text(module->name(module), -1);
1092 dt_capitalize_label(mname);
1093 gtk_label_set_markup(GTK_LABEL(label), mname);
1094 dt_free(mname);
1095 gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_END);
1096 g_object_set(G_OBJECT(label), "halign", GTK_ALIGN_START, "xalign", 0.0, (gchar *)0);
1097 gtk_widget_set_name(label, "lib-panel-label");
1098 gtk_box_pack_start(GTK_BOX(header), label_evb, FALSE, FALSE, 0);
1099
1100 /* add preset button if module has implementation */
1101 module->presets_button = dtgtk_button_new(dtgtk_cairo_paint_presets, 0, NULL);
1102 g_signal_connect(G_OBJECT(module->presets_button), "clicked", G_CALLBACK(presets_popup_callback), module);
1103 if(!module->get_params && !module->set_preferences) gtk_widget_set_sensitive(GTK_WIDGET(module->presets_button), FALSE);
1104 gtk_box_pack_end(GTK_BOX(header), module->presets_button, FALSE, FALSE, 0);
1105
1106 /* add reset button if module has implementation */
1107 module->reset_button = dtgtk_button_new(dtgtk_cairo_paint_reset, 0, NULL);
1108 g_signal_connect(G_OBJECT(module->reset_button), "clicked", G_CALLBACK(dt_lib_gui_reset_callback), module);
1109 if(!module->gui_reset) gtk_widget_set_sensitive(GTK_WIDGET(module->reset_button), FALSE);
1110 gtk_box_pack_end(GTK_BOX(header), module->reset_button, FALSE, FALSE, 0);
1111
1112 gtk_widget_show_all(GTK_WIDGET(module->widget));
1113 dt_gui_add_class(module->widget, "dt_plugin_ui_main");
1114 module->expander = expander;
1115 g_object_weak_ref(G_OBJECT(expander), _lib_module_expander_gone, module);
1116
1117 gtk_widget_set_hexpand(module->widget, FALSE);
1118 gtk_widget_set_vexpand(module->widget, FALSE);
1119
1120 return module->expander;
1121}
1122
1124{
1125 // Setting everything to null initially
1126 memset(lib, 0, sizeof(dt_lib_t));
1127 dt_lib_get_global()->plugins = dt_module_load_modules("/plugins/lighttable", sizeof(dt_lib_module_t),
1129}
1130
1132{
1133 while(lib->plugins)
1134 {
1135 dt_lib_module_t *module = (dt_lib_module_t *)(lib->plugins->data);
1136 if(module)
1137 {
1138 if(module->gui_cleanup)
1139 module->gui_cleanup(module);
1140 module->data = NULL;
1141 dt_free(module->common_fields.view);
1142 module->common_fields.view = NULL;
1143 dt_lib_unload_module(module);
1144 dt_free(module);
1145 }
1146 lib->plugins = g_list_delete_link(lib->plugins, lib->plugins);
1147 }
1148
1150}
1151
1152void dt_lib_presets_add(const char *name, const char *plugin_name, const int32_t version, const void *params,
1153 const int32_t params_size, gboolean readonly)
1154{
1155 dt_lib_presets_remove(name, plugin_name, version);
1156 dt_preset_repository_add_shipped_preset(name, plugin_name, version, params, params_size, readonly);
1157}
1158
1159static gchar *_get_lib_view_path(dt_lib_module_t *module, char *suffix)
1160{
1161 if(IS_NULL_PTR(dt_view_manager_get_global()) || !module || IS_NULL_PTR(suffix) || module->plugin_name[0] == '\0') return NULL;
1163 if(IS_NULL_PTR(cv) || cv->module_name[0] == '\0') return NULL;
1164 // in lighttable, we store panels states per layout
1165 char lay[32] = "";
1166 if(g_strcmp0(cv->module_name, "lighttable") == 0)
1167 {
1168 g_snprintf(lay, sizeof(lay), "%d/", 0);
1169 }
1170 else if(g_strcmp0(cv->module_name, "darkroom") == 0)
1171 {
1172 g_snprintf(lay, sizeof(lay), "%d/", dt_view_darkroom_get_layout(dt_view_manager_get_global()));
1173 }
1174
1175 return g_strdup_printf("plugins/%s/%s%s%s", cv->module_name, lay, module->plugin_name, suffix);
1176}
1177
1179{
1180 gchar *key = _get_lib_view_path(module, "_visible");
1181 gboolean ret = TRUE; /* if not key found, always make module visible */
1183 dt_free(key);
1184
1185 return ret;
1186}
1187
1188void dt_lib_set_visible(dt_lib_module_t *module, gboolean visible)
1189{
1190 gchar *key = _get_lib_view_path(module, "_visible");
1191 GtkWidget *widget;
1192 if(key)
1193 {
1194 dt_conf_set_bool(key, visible);
1195 dt_free(key);
1196 }
1197 if(module->widget)
1198 {
1199 if(module->expander)
1200 widget = module->expander;
1201 else
1202 widget = module->widget;
1203
1204
1205 if(visible)
1206 gtk_widget_show(GTK_WIDGET(widget));
1207 else
1208 gtk_widget_hide(GTK_WIDGET(widget));
1209 }
1210}
1211
1212gchar *dt_lib_get_localized_name(const gchar *plugin_name)
1213{
1214 // Prepare mapping op -> localized name
1215 static GHashTable *module_names = NULL;
1216 if(IS_NULL_PTR(module_names))
1217 {
1218 module_names = g_hash_table_new(g_str_hash, g_str_equal);
1219 for(const GList *lib = dt_lib_get_global()->plugins; lib; lib = g_list_next(lib))
1220 {
1221 dt_lib_module_t *module = (dt_lib_module_t *)lib->data;
1222 g_hash_table_insert(module_names, module->plugin_name, g_strdup(module->name(module)));
1223 }
1224 }
1225
1226 return (gchar *)g_hash_table_lookup(module_names, plugin_name);
1227}
1228
1230{
1231 dt_develop_t *const dev = dt_dev_get_global();
1232 dt_colorpicker_sample_t *const sample = dev ? dev->color_picker.primary_sample : NULL;
1233 if(IS_NULL_PTR(sample)) return;
1234
1235 dt_boundingbox_t raw_box = { box[0], box[1], box[2], box[3] };
1237
1238 gboolean changed = (sample->size != DT_LIB_COLORPICKER_SIZE_BOX);
1240 for(int k = 0; k < 4; k++)
1241 {
1242 changed |= (sample->box[k] != raw_box[k]);
1243 sample->box[k] = raw_box[k];
1244 }
1245
1246 if(!changed) return;
1249}
1250
1251void dt_lib_colorpicker_set_point(dt_lib_t *lib, const float pos[2])
1252{
1253 dt_develop_t *const dev = dt_dev_get_global();
1254 dt_colorpicker_sample_t *const sample = dev ? dev->color_picker.primary_sample : NULL;
1255 if(IS_NULL_PTR(sample)) return;
1256
1257 float raw_pos[2] = { pos[0], pos[1] };
1259
1260 const gboolean changed = sample->size != DT_LIB_COLORPICKER_SIZE_POINT
1261 || sample->point[0] != raw_pos[0]
1262 || sample->point[1] != raw_pos[1];
1264 sample->point[0] = raw_pos[0];
1265 sample->point[1] = raw_pos[1];
1266
1267 if(!changed) return;
1270}
1271
1273{
1274 /* hide/show modules as last config */
1275 for(GList *iter = dt_lib_get_global()->plugins; iter; iter = g_list_next(iter))
1276 {
1277 dt_lib_module_t *plugin = (dt_lib_module_t *)(iter->data);
1278 if(strcmp(plugin->plugin_name, name) == 0)
1279 return plugin;
1280 }
1281
1282 return NULL;
1283}
1284
1285/* callback function for delayed update after user interaction */
1286static gboolean _postponed_update(gpointer data)
1287{
1288 dt_lib_module_t *self = (dt_lib_module_t *)data;
1289 self->timeout_handle = 0;
1290 if(self->_postponed_update)
1291 self->_postponed_update(self);
1292
1293 return FALSE; // cancel the timer
1294}
1295
1298{
1299 if(mod->timeout_handle)
1300 {
1301 // here we're making sure the event fires at last hover
1302 // and we won't have avalanche of events in the mean time.
1303 g_source_remove(mod->timeout_handle);
1304 }
1305 mod->_postponed_update = update_fn;
1306 mod->timeout_handle = g_timeout_add(dt_conf_get_int("processing/timeout"), _postponed_update, mod);
1307}
1308
1310{
1311 mod->_postponed_update = NULL;
1312 if(mod->timeout_handle)
1313 {
1314 g_source_remove(mod->timeout_handle);
1315 mod->timeout_handle = 0;
1316 }
1317}
1318
1320{
1321 return mod->preset_autoapply(mod);
1322}
1323
1324gboolean dt_handle_dialog_enter(GtkWidget *widget, GdkEventKey *event, gpointer data)
1325{
1326 guint key = dt_keys_mainpad_alternatives(event->keyval);
1327
1328 if(key == GDK_KEY_Return)
1329 {
1330 gtk_dialog_response(GTK_DIALOG(widget), GTK_RESPONSE_ACCEPT);
1331 return TRUE;
1332 }
1333 return FALSE;
1334}
1335
1336GtkWidget *dt_action_button_new(dt_lib_module_t *self, const gchar *label, gpointer callback, gpointer data, const gchar *tooltip, guint accel_key, GdkModifierType mods)
1337{
1338 gchar *label_copy = g_strdup(label);
1339 dt_capitalize_label(label_copy);
1340 GtkWidget *button = gtk_button_new_with_label(label_copy);
1341 dt_free(label_copy);
1342
1343 gtk_widget_set_valign(GTK_WIDGET(button), GTK_ALIGN_CENTER);
1344 gtk_widget_set_halign(GTK_WIDGET(button), GTK_ALIGN_CENTER);
1345 gtk_widget_set_vexpand(GTK_WIDGET(button), FALSE);
1346 gtk_widget_set_hexpand(GTK_WIDGET(button), FALSE);
1347
1348 gtk_label_set_ellipsize(GTK_LABEL(gtk_bin_get_child(GTK_BIN(button))), PANGO_ELLIPSIZE_END);
1349
1350 if(tooltip) gtk_widget_set_tooltip_text(button, tooltip);
1351
1352 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(callback), data);
1353
1354 return button;
1355}
1356
1357
1358
1359// clang-format off
1360// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1361// vim: shiftwidth=2 expandtab tabstop=2 cindent
1362// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1363// clang-format on
void dt_accels_remove_shortcut(dt_accels_t *accels, const char *path)
Remove the shortcut object identified by path and all its accels.
gchar * dt_accels_build_path(const gchar *scope, const gchar *feature)
void dt_accels_new_action_shortcut(dt_accels_t *accels, gboolean(*action_callback)(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data), gpointer data, GtkWidget *target_widget, GtkAccelGroup *accel_group, const gchar *action_scope, const gchar *action_name, guint key_val, GdkModifierType accel_mods, const gboolean lock, const char *description)
Register a new shortcut for a generic action, setting up its path, default keys and accel group....
GtkWidget * dt_gui_main_window(void)
struct dt_accels_t * dt_gui_get_accels(void)
void dt_gui_store_last_preset(const char *name)
void dt_gui_refocus_center()
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
size_t params_size(dt_imageio_module_format_t *self)
Definition avif.c:571
uint32_t container(dt_lib_module_t *self)
const char ** views(dt_lib_module_t *self)
#define m
Definition basecurve.c:283
void dtgtk_button_set_paint(GtkDarktableButton *button, DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
Definition button.c:169
void dtgtk_button_set_active(GtkDarktableButton *button, gboolean active)
Definition button.c:177
#define DTGTK_BUTTON(obj)
Definition button.h:40
void dt_iop_color_picker_request_update(void)
@ DT_LIB_COLORPICKER_SIZE_POINT
Definition colorpicker.h:38
@ DT_LIB_COLORPICKER_SIZE_BOX
Definition colorpicker.h:39
const float v
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
void dt_conf_set_bool(const char *name, int val)
int dt_conf_get_bool(const char *name)
int dt_conf_key_exists(const char *key)
Does key have a value?
int dt_conf_get_int(const char *name)
Integer for name, clamped to the bounds declared in the XML.
void dt_conf_set_string(const char *name, const char *val)
void dt_control_log(const char *msg,...)
Definition control.c:824
void dt_control_queue_redraw()
Request a redraw of the whole workspace.
Definition control.c:919
struct dt_develop_t * dt_dev_get_global(void)
Definition darktable.c:528
struct dt_lib_t * dt_lib_get_global(void)
Definition darktable.c:621
struct dt_view_manager_t * dt_view_manager_get_global(void)
Definition darktable.c:599
struct dt_gui_gtk_t * dt_gui_get_global(void)
Definition darktable.c:523
void dt_dev_coordinates_image_norm_to_raw_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1271
GtkWidget * window
GtkWidget * view
the tree view
const int res
Definition dtpthread.h:351
GtkWidget * dtgtk_expander_get_header_event_box(GtkDarktableExpander *expander)
Definition expander.c:49
gboolean dtgtk_expander_get_expanded(GtkDarktableExpander *expander)
Definition expander.c:89
GtkWidget * dtgtk_expander_get_body_event_box(GtkDarktableExpander *expander)
Definition expander.c:63
GtkWidget * dtgtk_expander_get_frame(GtkDarktableExpander *expander)
Definition expander.c:35
void dtgtk_expander_set_expanded(GtkDarktableExpander *expander, gboolean expanded)
Definition expander.c:70
GtkWidget * dtgtk_expander_new(GtkWidget *header, GtkWidget *body)
Definition expander.c:101
#define DTGTK_EXPANDER(obj)
Definition expander.h:31
#define DTGTK_IS_EXPANDER(obj)
Definition expander.h:32
static guint dt_keys_mainpad_alternatives(const guint key_val)
Remap keypad keys to usual mainpad ones.
Definition gdkkeys.h:118
void dt_gui_presets_show_edit_dialog(const char *name_in, const char *module_name, int rowid, GCallback final_callback, gpointer data, gboolean allow_name_change, gboolean allow_desc_change, gboolean allow_remove, GtkWindow *parent)
#define DT_GUI_MODULE(x)
float dt_boundingbox_t[4]
Definition image.h:83
const char * tooltip
Definition image.h:310
struct dt_iop_tonecurve_params_t preset
gchar * delete_underscore(const char *s)
Definition label.c:75
gboolean dt_handle_dialog_enter(GtkWidget *widget, GdkEventKey *event, gpointer data)
Definition lib.c:1324
static gboolean _lib_plugin_header_button_press(GtkWidget *w, GdkEventButton *e, gpointer user_data)
Definition lib.c:982
static gboolean default_expandable(dt_lib_module_t *self)
Definition lib.c:502
static void dt_lib_gui_reset_callback(GtkButton *button, gpointer user_data)
Definition lib.c:814
gboolean dt_lib_presets_can_autoapply(dt_lib_module_t *mod)
Definition lib.c:1319
static void free_module_info(GtkWidget *widget, gpointer user_data)
Definition lib.c:344
static gboolean default_preset_autoapply(dt_lib_module_t *self)
Definition lib.c:508
dt_action_element_lib_t
Definition lib.c:87
@ DT_ACTION_ELEMENT_RESET
Definition lib.c:89
@ DT_ACTION_ELEMENT_PRESETS
Definition lib.c:90
@ DT_ACTION_ELEMENT_SHOW
Definition lib.c:88
static int _lib_plugin_body_button_press(GtkWidget *w, GdkEventButton *e, gpointer user_data)
Definition lib.c:520
gint dt_lib_sort_plugins(gconstpointer a, gconstpointer b)
Definition lib.c:492
void dt_lib_colorpicker_set_box_area(dt_lib_t *lib, const dt_boundingbox_t box)
Definition lib.c:1229
void dt_lib_cancel_postponed_update(dt_lib_module_t *mod)
Definition lib.c:1309
gboolean dt_lib_gui_get_expanded(dt_lib_module_t *module)
Definition lib.c:914
void dt_lib_init(dt_lib_t *lib)
Definition lib.c:1123
void dt_lib_presets_remove(const gchar *preset, const gchar *module_name, int module_version)
Definition lib.c:285
void dt_lib_init_presets(dt_lib_module_t *module)
Definition lib.c:618
static int dt_lib_load_module(void *m, const char *libname, const char *module_name)
Definition lib.c:529
static void menuitem_delete_preset(GtkMenuItem *menuitem, dt_lib_module_info_t *minfo)
Definition lib.c:234
GtkWidget * dt_action_button_new(dt_lib_module_t *self, const gchar *label, gpointer callback, gpointer data, const gchar *tooltip, guint accel_key, GdkModifierType mods)
Definition lib.c:1336
gboolean dt_lib_is_visible_in_view(dt_lib_module_t *module, const dt_view_t *view)
Definition lib.c:114
static void expand_callback(GtkButton *button, dt_lib_module_t *module)
Definition lib.c:977
static void _lib_module_expander_gone(gpointer user_data, GObject *where_the_object_was)
Definition lib.c:841
static void dt_lib_unload_module(dt_lib_module_t *module)
Definition lib.c:754
void dt_lib_queue_postponed_update(dt_lib_module_t *mod, void(*update_fn)(dt_lib_module_t *self))
Definition lib.c:1297
static void _toggle_expanded(dt_lib_module_t *module, gboolean close_all)
Definition lib.c:945
static gboolean _postponed_update(gpointer data)
Definition lib.c:1286
static void dt_lib_presets_popup_menu_show(dt_lib_module_info_t *minfo)
Definition lib.c:352
static void menuitem_update_preset(GtkMenuItem *menuitem, dt_lib_module_info_t *minfo)
Definition lib.c:181
void dt_lib_presets_add(const char *name, const char *plugin_name, const int32_t version, const void *params, const int32_t params_size, gboolean readonly)
Definition lib.c:1152
static void menuitem_edit_preset(GtkMenuItem *menuitem, dt_lib_module_info_t *minfo)
Definition lib.c:224
gboolean dt_lib_presets_apply(const gchar *preset, const gchar *module_name, int module_version)
Definition lib.c:290
void dt_lib_colorpicker_set_point(dt_lib_t *lib, const float pos[2])
Definition lib.c:1251
static void * _update_params(dt_lib_module_t *module, const void *const old_params, size_t old_params_size, int old_version, int target_version, size_t *new_size)
Definition lib.c:595
gboolean dt_lib_is_visible(dt_lib_module_t *module)
Definition lib.c:1178
static void presets_popup_callback(GtkButton *button, dt_lib_module_t *module)
Definition lib.c:820
gchar * dt_lib_presets_duplicate(const gchar *preset, const gchar *module_name, int module_version)
Definition lib.c:265
void dt_lib_gui_set_expanded(dt_lib_module_t *module, gboolean expanded)
Definition lib.c:854
void dt_lib_presets_update(const gchar *preset, const gchar *module_name, int module_version, const gchar *newname, const gchar *desc, const void *params, const int32_t params_size)
Definition lib.c:330
static void menuitem_new_preset(GtkMenuItem *menuitem, dt_lib_module_info_t *minfo)
Definition lib.c:211
int default_lib_focus(dt_gui_module_t *m, gboolean toogle)
Definition lib.c:513
static gboolean _lib_plugin_focus_accel(GtkAccelGroup *accel_group, GObject *accelerable, guint keyval, GdkModifierType modifier, gpointer data)
Definition lib.c:679
dt_lib_module_t * dt_lib_get_module(const char *name)
Definition lib.c:1272
void dt_lib_set_visible(dt_lib_module_t *module, gboolean visible)
Definition lib.c:1188
static void menuitem_manage_presets(GtkMenuItem *menuitem, dt_lib_module_info_t *minfo)
Definition lib.c:229
static void dt_lib_init_module(void *m)
Definition lib.c:688
void dt_lib_cleanup(dt_lib_t *lib)
Definition lib.c:1131
static gchar * get_active_preset_name(dt_lib_module_info_t *minfo)
Definition lib.c:138
static gchar * _get_lib_view_path(dt_lib_module_t *module, char *suffix)
Definition lib.c:1159
GtkWidget * dt_lib_gui_get_expander(dt_lib_module_t *module)
Definition lib.c:1039
static void edit_preset(const char *name_in, dt_lib_module_info_t *minfo)
Definition lib.c:158
static void pick_callback(GtkMenuItem *menuitem, dt_lib_module_info_t *minfo)
Definition lib.c:337
gchar * dt_lib_get_localized_name(const gchar *plugin_name)
Definition lib.c:1212
float *const restrict const size_t k
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
char * key
size_t size
Definition mipmap_cache.c:3
GList * dt_module_load_modules(const char *subdir, size_t module_size, int(*load_module_so)(void *module, const char *libname, const char *plugin_name), void(*init_module)(void *module), gint(*sort_modules)(gconstpointer a, gconstpointer b))
Load every module the given directory's manifest lists.
Definition module.c:106
void dt_osx_disallow_fullscreen(GtkWidget *widget)
Definition osx.mm:105
const char * name
Definition pdf.h:90
void dt_gui_menu_popup(GtkMenu *menu, GtkWidget *button, GdkGravity widget_anchor, GdkGravity menu_anchor)
Definition popup.c:53
void dt_preset_repository_delete_module_preset(const char *operation, const int op_version, const char *name)
Delete (operation, op_version, name) unless it is write-protected.
void dt_module_preset_free(gpointer data)
Release one dt_module_preset_t. Suits g_list_free_full().
GList * dt_preset_repository_list_all_versions(const char *operation)
Every preset of operation at ANY version, with rowid and op_version filled in, SHIPPED FIRST then by ...
GList * dt_preset_repository_list_for_module(const char *operation, const int op_version, const gboolean with_description, const gboolean shipped_first)
Every preset of (operation, op_version).
void dt_preset_repository_cleanup(void)
Finalise the cached statements. See dt_colorlabel_repository_cleanup().
dt_module_preset_t * dt_preset_repository_get_module_preset(const char *operation, const int op_version, const char *name)
One preset by name, or NULL. Free with dt_module_preset_free().
void dt_preset_repository_update_params_by_rowid(const int rowid, const int op_version, const void *params, const int params_size)
Replace the parameters of one row, by rowid.
int dt_preset_repository_find_rowid(const char *operation, const int op_version, const char *name)
rowid of (operation, op_version, name), or -1.
void dt_preset_repository_update_module_params(const char *operation, const char *name, const int op_version, const void *params, const int params_size)
Replace the parameters (and version) of (operation, name).
void dt_preset_repository_add_module_preset(const char *name, const char *operation, const int op_version, const void *params, const int params_size)
Create an empty user preset holding params, as the "new preset" menu item does.
void dt_preset_repository_delete_by_rowid(const int rowid)
Delete one row by rowid.
void dt_preset_repository_delete_all_for_module(const char *operation)
Delete every preset of operation, whatever its version or protection.
gboolean dt_preset_repository_module_preset_exists(const char *operation, const int op_version, const char *name)
TRUE when (operation, op_version, name) exists.
void dt_preset_repository_rename_module_preset(const char *operation, const int op_version, const char *old_name, const char *new_name, const char *description, const void *params, const int params_size)
Replace name, description and parameters of (operation, op_version, old_name).
void dt_preset_repository_add_shipped_preset(const char *name, const char *operation, const int op_version, const void *params, const int params_size, const int writeprotect)
Create a preset from code rather than from the menu – the built-in presets a module registers at star...
void dt_preset_repository_duplicate_module_preset(const char *operation, const int op_version, const char *name, const char *new_name)
Copy (operation, op_version, name) to new_name, unprotected.
data.presets: a module's saved parameters, with the conditions under which it auto-applies.
void dt_sentry_record_module_usage(const char *category, const char *name)
Definition sentry.c:692
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
Definition signal.h:386
struct dt_control_signal_t * dt_control_signal_get_global(void)
Definition darktable.c:616
@ DT_SIGNAL_PRESETS_CHANGED
Definition signal.h:165
GtkAccelGroup * slideshow_accels
GtkAccelGroup * map_accels
GtkAccelGroup * print_accels
GtkAccelGroup * lighttable_accels
GtkAccelGroup * darkroom_accels
dt_lib_colorpicker_size_t size
Definition colorpicker.h:64
dt_boundingbox_t box
Definition colorpicker.h:63
struct dt_colorpicker_sample_t * primary_sample
Definition develop.h:374
struct dt_develop_t::@13 color_picker
Authoritative darkroom color-picker state.
GtkMenu * presets_popup_menu
GtkWidget * scroll_to[2]
The dt_gui_module_t type is the intersection between a dt_lib_module_t and a dt_iop_module_t structur...
char * plugin_name
Definition lib.c:95
int32_t version
Definition lib.c:96
char * params
Definition lib.c:97
GtkWidget * expander
Definition lib.h:85
GtkWidget * reset_button
Definition lib.h:92
GtkWidget * presets_button
Definition lib.h:93
void(* _postponed_update)(struct dt_lib_module_t *self)
Definition lib.h:87
char plugin_name[128]
Definition lib.h:81
dt_gui_module_t common_fields
Definition lib.h:71
GModule *void * data
Definition lib.h:79
GtkWidget * arrow
Definition lib.h:91
GtkWidget * widget
Definition lib.h:83
guint timeout_handle
Definition lib.h:89
dt_lib_module_t *gint old_id
Definition lib.c:111
Definition lib.h:53
struct dt_lib_module_t * gui_module
Definition lib.h:55
GList * plugins
Definition lib.h:54
GList * views
Definition view.h:192
GModule *void * data
Definition view.h:159
char module_name[64]
Definition view.h:155
void dt_telemetry_record_module_usage(const char *category, const char *name)
Definition telemetry.c:460
dt_darkroom_layout_t dt_view_darkroom_get_layout(dt_view_manager_t *vm)
Definition view.c:1335
const dt_view_t * dt_view_manager_get_current_view(dt_view_manager_t *vm)
Definition view.c:139
void dt_widget_set_scroll_focus(GtkWidget *widget)
static gboolean dt_modifier_is(GdkModifierType state, const GdkModifierType desired_modifier_mask)
#define DT_GUI_BOX_SPACING
void dt_gui_remove_class(GtkWidget *widget, const gchar *class_name)
void dt_capitalize_label(gchar *text)
void dt_gui_add_class(GtkWidget *widget, const gchar *class_name)
void dtgtk_cairo_paint_arrow(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
@ CPF_DIRECTION_LEFT
@ CPF_DIRECTION_UP
@ DT_UI_CONTAINER_PANEL_RIGHT_CENTER
@ DT_UI_CONTAINER_PANEL_LEFT_CENTER