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