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/darktable.h"
56#include "common/sentry.h"
57#include "common/telemetry.h"
58#include "gui/gdkkeys.h"
59#include "libs/lib.h"
60#include "common/debug.h"
61#include "common/module.h"
62#include "control/conf.h"
63#include "control/control.h"
64#include "develop/develop.h"
65#include "dtgtk/button.h"
66#include "dtgtk/expander.h"
67#include "dtgtk/icon.h"
68
70#include "libs/colorpicker.h"
71#include "gui/gtk.h"
72#include "gui/presets.h"
73#ifdef GDK_WINDOWING_QUARTZ
74#include "osx/osx.h"
75#endif
76#include <stdbool.h>
77#include <stdlib.h>
78
79static sqlite3_stmt *_lib_presets_remove_stmt = NULL;
80static sqlite3_stmt *_lib_presets_add_stmt = NULL;
81static sqlite3_stmt *_lib_presets_select_stmt = NULL;
82static sqlite3_stmt *_lib_presets_delete_operation_stmt = NULL;
83
85{
87 {
88 sqlite3_finalize(_lib_presets_remove_stmt);
90 }
92 {
93 sqlite3_finalize(_lib_presets_add_stmt);
95 }
97 {
98 sqlite3_finalize(_lib_presets_select_stmt);
100 }
102 {
103 sqlite3_finalize(_lib_presets_delete_operation_stmt);
105 }
106}
107
114
123
135
137{
138 if(!module->views)
139 {
140 fprintf(stderr, "module %s doesn't have views flags\n", module->name(module));
141 return FALSE;
142 }
143
144 const char **views = module->views(module);
145 for(const char **iter = views; *iter; iter++)
146 {
147 if(!strcmp(*iter, "*") || !strcmp(*iter, view->module_name)) return TRUE;
148 }
149 return FALSE;
150}
151
153static void dt_lib_unload_module(dt_lib_module_t *module);
154
156{
157 sqlite3_stmt *stmt;
158 // clang-format off
161 "SELECT name, op_params, writeprotect"
162 " FROM data.presets"
163 " WHERE operation=?1 AND op_version=?2",
164 -1, &stmt, NULL);
165 // clang-format on
166 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 1, minfo->plugin_name, -1, SQLITE_TRANSIENT);
167 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, minfo->version);
168 gchar *name = NULL;
169 // collect all presets for op from db
170 while(sqlite3_step(stmt) == SQLITE_ROW)
171 {
172 void *op_params = (void *)sqlite3_column_blob(stmt, 1);
173 int32_t op_params_size = sqlite3_column_bytes(stmt, 1);
174 if(op_params_size == minfo->params_size && !memcmp(minfo->params, op_params, op_params_size))
175 {
176 name = g_strdup((char *)sqlite3_column_text(stmt, 0));
177 break;
178 }
179 }
180 sqlite3_finalize(stmt);
181 return name;
182}
183
184static void edit_preset(const char *name_in, dt_lib_module_info_t *minfo)
185{
186 // get the original name of the preset
187 gchar *name = NULL;
188 if(IS_NULL_PTR(name_in))
189 {
191 if(IS_NULL_PTR(name)) return;
192 }
193 else
194 name = g_strdup(name_in);
195
196 // find the rowid of the preset
197 int rowid = -1;
198 sqlite3_stmt *stmt;
199 // clang-format off
201 "SELECT rowid"
202 " FROM data.presets"
203 " WHERE name = ?1 AND operation = ?2 AND op_version = ?3",
204 -1, &stmt, NULL);
205 // clang-format on
206 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 1, name, -1, SQLITE_TRANSIENT);
207 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, minfo->plugin_name, -1, SQLITE_TRANSIENT);
208 DT_DEBUG_SQLITE3_BIND_INT(stmt, 3, minfo->version);
209 if(sqlite3_step(stmt) == SQLITE_ROW)
210 {
211 rowid = sqlite3_column_int(stmt, 0);
212 }
213 sqlite3_finalize(stmt);
214
215 // if we don't have a valid rowid, just exit, there's a problem !
216 if(rowid < 0) return;
217
219 dt_gui_presets_show_edit_dialog(name, minfo->plugin_name, rowid, NULL, NULL, TRUE, TRUE, FALSE,
220 GTK_WINDOW(window));
221}
222
223static void menuitem_update_preset(GtkMenuItem *menuitem, dt_lib_module_info_t *minfo)
224{
225 char *name = g_object_get_data(G_OBJECT(menuitem), "dt-preset-name");
226
227 gint res = GTK_RESPONSE_YES;
228
229 if(dt_conf_get_bool("plugins/lighttable/preset/ask_before_delete_preset"))
230 {
232 GtkWidget *dialog
233 = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION,
234 GTK_BUTTONS_YES_NO, _("do you really want to update the preset `%s'?"), name);
235#ifdef GDK_WINDOWING_QUARTZ
237#endif
238 gtk_window_set_title(GTK_WINDOW(dialog), _("update preset?"));
239 res = gtk_dialog_run(GTK_DIALOG(dialog));
240 gtk_widget_destroy(dialog);
241 }
242
243 if(res == GTK_RESPONSE_YES)
244 {
245 // commit all the module fields
246 sqlite3_stmt *stmt;
247 // clang-format off
249 "UPDATE data.presets"
250 " SET op_version=?2, op_params=?3"
251 " WHERE name=?4 AND operation=?1",
252 -1, &stmt, NULL);
253 // clang-format on
254
255 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 1, minfo->plugin_name, -1, SQLITE_TRANSIENT);
256 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, minfo->version);
257 DT_DEBUG_SQLITE3_BIND_BLOB(stmt, 3, minfo->params, minfo->params_size, SQLITE_TRANSIENT);
258 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 4, name, -1, SQLITE_TRANSIENT);
259 sqlite3_step(stmt);
260 sqlite3_finalize(stmt);
262 g_strdup(minfo->plugin_name));
263 }
264}
265
266static void menuitem_new_preset(GtkMenuItem *menuitem, dt_lib_module_info_t *minfo)
267{
268 dt_lib_presets_remove(_("new preset"), minfo->plugin_name, minfo->version);
269
270 // add new preset
271 sqlite3_stmt *stmt;
272 // clang-format off
275 "INSERT INTO data.presets (name, description, operation, op_version, op_params,"
276 " blendop_params, blendop_version, enabled, model, maker, lens,"
277 " iso_min, iso_max, exposure_min, exposure_max, aperture_min, aperture_max,"
278 " focal_length_min, focal_length_max, writeprotect, "
279 " autoapply, filter, def, format)"
280 " VALUES (?1, '', ?2, ?3, ?4, NULL, 0, 1, '%', "
281 " '%', '%', 0, 340282346638528859812000000000000000000, 0, 100000000, 0, 100000000,"
282 " 0, 1000, 0, 0, 0, 0, 0)",
283 -1, &stmt, NULL);
284 // clang-format on
285 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 1, _("new preset"), -1, SQLITE_STATIC);
286 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, minfo->plugin_name, -1, SQLITE_TRANSIENT);
287 DT_DEBUG_SQLITE3_BIND_INT(stmt, 3, minfo->version);
288 DT_DEBUG_SQLITE3_BIND_BLOB(stmt, 4, minfo->params, minfo->params_size, SQLITE_TRANSIENT);
289 sqlite3_step(stmt);
290 sqlite3_finalize(stmt);
291 // create a shortcut for the new entry
292
293 // then show edit dialog
294 edit_preset(_("new preset"), minfo);
295}
296
297static void menuitem_edit_preset(GtkMenuItem *menuitem, dt_lib_module_info_t *minfo)
298{
299 edit_preset(NULL, minfo);
300}
301
302static void menuitem_manage_presets(GtkMenuItem *menuitem, dt_lib_module_info_t *minfo)
303{
304 if(minfo->module->manage_presets) minfo->module->manage_presets(minfo->module);
305}
306
307static void menuitem_delete_preset(GtkMenuItem *menuitem, dt_lib_module_info_t *minfo)
308{
309 gchar *name = get_active_preset_name(minfo);
310 if(IS_NULL_PTR(name)) return;
311
312 gint res = GTK_RESPONSE_YES;
313
314 if(dt_conf_get_bool("plugins/lighttable/preset/ask_before_delete_preset"))
315 {
317 GtkWidget *dialog
318 = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION,
319 GTK_BUTTONS_YES_NO, _("do you really want to delete the preset `%s'?"), name);
320#ifdef GDK_WINDOWING_QUARTZ
322#endif
323 gtk_window_set_title(GTK_WINDOW(dialog), _("delete preset?"));
324 res = gtk_dialog_run(GTK_DIALOG(dialog));
325 gtk_widget_destroy(dialog);
326 }
327
328 if(res == GTK_RESPONSE_YES)
329 {
331
333 g_strdup(minfo->plugin_name));
334 }
335 dt_free(name);
336}
337
338gchar *dt_lib_presets_duplicate(const gchar *preset, const gchar *module_name, int module_version)
339{
340 sqlite3_stmt *stmt;
341
342 // find the new name
343 int i = 0;
344 gboolean ko = TRUE;
345 while(ko)
346 {
347 i++;
348 gchar *tx = g_strdup_printf("%s_%d", preset, i);
349 // clang-format off
352 "SELECT name"
353 " FROM data.presets"
354 " WHERE operation = ?1 AND op_version = ?2 AND name = ?3", -1, &stmt, NULL);
355 // clang-format on
356 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 1, module_name, -1, SQLITE_TRANSIENT);
357 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, module_version);
358 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 3, tx, -1, SQLITE_TRANSIENT);
359 if(sqlite3_step(stmt) != SQLITE_ROW) ko = FALSE;
360 sqlite3_finalize(stmt);
361 dt_free(tx);
362 }
363 gchar *nname = g_strdup_printf("%s_%d", preset, i);
364
365 // and we duplicate the entry
366 // clang-format off
369 "INSERT INTO data.presets"
370 " (name, description, operation, op_version, op_params, "
371 " blendop_params, blendop_version, enabled, model, maker, lens, "
372 " iso_min, iso_max, exposure_min, exposure_max, aperture_min, aperture_max, "
373 " focal_length_min, focal_length_max, writeprotect, "
374 " autoapply, filter, def, format) "
375 "SELECT ?1, description, operation, op_version, op_params, "
376 " blendop_params, blendop_version, enabled, model, maker, lens, "
377 " iso_min, iso_max, exposure_min, exposure_max, aperture_min, aperture_max, "
378 " focal_length_min, focal_length_max, 0, "
379 " autoapply, filter, def, format"
380 " FROM data.presets"
381 " WHERE operation = ?2 AND op_version = ?3 AND name = ?4",
382 -1, &stmt, NULL);
383 // clang-format on
384 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 1, nname, -1, SQLITE_TRANSIENT);
385 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, module_name, -1, SQLITE_TRANSIENT);
386 DT_DEBUG_SQLITE3_BIND_INT(stmt, 3, module_version);
387 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 4, preset, -1, SQLITE_TRANSIENT);
388 sqlite3_step(stmt);
389 sqlite3_finalize(stmt);
390
391 return nname;
392}
393
394void dt_lib_presets_remove(const gchar *preset, const gchar *module_name, int module_version)
395{
397 {
398 // clang-format off
401 "DELETE FROM data.presets"
402 " WHERE name=?1 AND operation=?2 AND op_version=?3 AND writeprotect=0", -1, &_lib_presets_remove_stmt,
403 NULL);
404 // clang-format on
405 }
406 sqlite3_stmt *stmt = _lib_presets_remove_stmt;
407 sqlite3_reset(stmt);
408 sqlite3_clear_bindings(stmt);
409 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 1, preset, -1, SQLITE_TRANSIENT);
410 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, module_name, -1, SQLITE_TRANSIENT);
411 DT_DEBUG_SQLITE3_BIND_INT(stmt, 3, module_version);
412 sqlite3_step(stmt);
413}
414
415gboolean dt_lib_presets_apply(const gchar *preset, const gchar *module_name, int module_version)
416{
417 gboolean ret = TRUE;
418 sqlite3_stmt *stmt;
419 // clang-format off
422 "SELECT op_params, writeprotect"
423 " FROM data.presets"
424 " WHERE operation = ?1 AND op_version = ?2 AND name = ?3",
425 -1, &stmt, NULL);
426 // clang-format on
427 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 1, module_name, -1, SQLITE_TRANSIENT);
428 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, module_version);
429 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 3, preset, -1, SQLITE_TRANSIENT);
430
431 int res = 0;
432 if(sqlite3_step(stmt) == SQLITE_ROW)
433 {
434 const void *blob = sqlite3_column_blob(stmt, 0);
435 int length = sqlite3_column_bytes(stmt, 0);
436 int writeprotect = sqlite3_column_int(stmt, 1);
437 if(blob)
438 {
439 for(const GList *it = darktable.lib->plugins; it; it = g_list_next(it))
440 {
441 dt_lib_module_t *module = (dt_lib_module_t *)it->data;
442 if(!strncmp(module->plugin_name, module_name, 128))
443 {
444 gchar *tx = g_strdup_printf("plugins/darkroom/%s/last_preset", module_name);
446 dt_free(tx);
447 res = module->set_params(module, blob, length);
448 break;
449 }
450 }
451 }
452
453 if(!writeprotect) dt_gui_store_last_preset(preset);
454 }
455 else
456 ret = FALSE;
457 sqlite3_finalize(stmt);
458 if(res)
459 {
460 dt_control_log(_("deleting preset for obsolete module"));
461 dt_lib_presets_remove(preset, module_name, module_version);
462 }
463 return ret;
464}
465
466void dt_lib_presets_update(const gchar *preset, const gchar *module_name, int module_version, const gchar *newname,
467 const gchar *desc, const void *params, const int32_t params_size)
468{
469 sqlite3_stmt *stmt;
470 // clang-format off
472 "UPDATE data.presets"
473 " SET name = ?1, description = ?2, op_params = ?3"
474 " WHERE operation = ?4 AND op_version = ?5 AND name = ?6",
475 -1, &stmt, NULL);
476 // clang-format on
477 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 1, newname, -1, SQLITE_TRANSIENT);
478 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, desc, -1, SQLITE_TRANSIENT);
479 DT_DEBUG_SQLITE3_BIND_BLOB(stmt, 3, params, params_size, SQLITE_TRANSIENT);
480 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 4, module_name, -1, SQLITE_TRANSIENT);
481 DT_DEBUG_SQLITE3_BIND_INT(stmt, 5, module_version);
482 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 6, preset, -1, SQLITE_TRANSIENT);
483 sqlite3_step(stmt);
484 sqlite3_finalize(stmt);
485}
486
487static void pick_callback(GtkMenuItem *menuitem, dt_lib_module_info_t *minfo)
488{
489 // apply preset via set_params
490 const char *pn = g_object_get_data(G_OBJECT(menuitem), "dt-preset-name");
491 dt_lib_presets_apply(pn, minfo->plugin_name, minfo->version);
492}
493
494static void free_module_info(GtkWidget *widget, gpointer user_data)
495{
496 dt_lib_module_info_t *minfo = (dt_lib_module_info_t *)user_data;
497 dt_free(minfo->plugin_name);
498 dt_free(minfo->params);
499 dt_free(minfo);
500}
501
503{
504 GtkMenu *menu = darktable.gui->presets_popup_menu;
505 if(menu) gtk_widget_destroy(GTK_WIDGET(menu));
506 darktable.gui->presets_popup_menu = GTK_MENU(gtk_menu_new());
508
509 const gboolean hide_default = dt_conf_get_bool("plugins/lighttable/hide_default_presets");
510 const gboolean default_first = dt_conf_get_bool("modules/default_presets_first");
511
512 g_signal_connect(G_OBJECT(menu), "destroy", G_CALLBACK(free_module_info), minfo);
513
514 GtkWidget *mi;
515 int active_preset = -1, cnt = 0;
516 gboolean selected_writeprotect = FALSE;
517 sqlite3_stmt *stmt;
518 // order like the pref value
519 // clang-format off
520 gchar *query = g_strdup_printf("SELECT name, op_params, writeprotect, description"
521 " FROM data.presets"
522 " WHERE operation=?1 AND op_version=?2"
523 " ORDER BY writeprotect %s, LOWER(name), rowid",
524 default_first ? "DESC" : "ASC");
525 // clang-format on
527 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 1, minfo->plugin_name, -1, SQLITE_TRANSIENT);
528 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, minfo->version);
529 dt_free(query);
530
531 // collect all presets for op from db
532 int found = 0;
533 int last_wp = -1;
534 while(sqlite3_step(stmt) == SQLITE_ROW)
535 {
536 // default vs built-in stuff
537 const gboolean writeprotect = sqlite3_column_int(stmt, 2);
538 if(hide_default && writeprotect)
539 {
540 // skip default module if set to hide them.
541 continue;
542 }
543 if(last_wp == -1)
544 {
545 last_wp = writeprotect;
546 }
547 else if(last_wp != writeprotect)
548 {
549 last_wp = writeprotect;
550 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
551 }
552
553 void *op_params = (void *)sqlite3_column_blob(stmt, 1);
554 int32_t op_params_size = sqlite3_column_bytes(stmt, 1);
555 const char *name = (char *)sqlite3_column_text(stmt, 0);
556
557 if(darktable.gui->last_preset && strcmp(darktable.gui->last_preset, name) == 0) found = 1;
558
559 // selected in bold:
560 // printf("comparing %d bytes to %d\n", op_params_size, minfo->params_size);
561 // for(int k=0;k<op_params_size && !memcmp(minfo->params, op_params, k);k++) printf("compare [%c %c] %d:
562 // %d\n",
563 // ((const char*)(minfo->params))[k],
564 // ((const char*)(op_params))[k],
565 // k, memcmp(minfo->params, op_params, k));
566 if(op_params_size == minfo->params_size && !memcmp(minfo->params, op_params, op_params_size))
567 {
568 active_preset = cnt;
569 selected_writeprotect = writeprotect;
570 mi = gtk_check_menu_item_new_with_label(name);
571
572 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mi), TRUE);
573 dt_gui_add_class(mi, "active_menu_item");
574 }
575 else
576 {
577 mi = gtk_menu_item_new_with_label((const char *)name);
578 }
579 g_object_set_data_full(G_OBJECT(mi), "dt-preset-name", g_strdup(name), g_free);
580 g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(pick_callback), minfo);
581 gtk_widget_set_tooltip_text(mi, (const char *)sqlite3_column_text(stmt, 3));
582 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
583 cnt++;
584 }
585 sqlite3_finalize(stmt);
586
587 if(cnt > 0)
588 {
589 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
590 cnt = 0;
591 }
592
593 if(minfo->module->manage_presets)
594 {
595 mi = gtk_menu_item_new_with_label(_("manage presets..."));
596 g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(menuitem_manage_presets), minfo);
597 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
598 cnt++;
599 }
600 else if(active_preset >= 0) // FIXME: this doesn't seem to work.
601 {
602 if(!selected_writeprotect)
603 {
604 mi = gtk_menu_item_new_with_label(_("edit this preset.."));
605 g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(menuitem_edit_preset), minfo);
606 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
607
608 mi = gtk_menu_item_new_with_label(_("delete this preset"));
609 g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(menuitem_delete_preset), minfo);
610 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
611 cnt++;
612 }
613 }
614 else
615 {
616 mi = gtk_menu_item_new_with_label(_("store new preset.."));
617 if(minfo->params_size == 0)
618 {
619 gtk_widget_set_sensitive(GTK_WIDGET(mi), FALSE);
620 gtk_widget_set_tooltip_text(mi, _("nothing to save"));
621 }
622 else
623 g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(menuitem_new_preset), minfo);
624 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
625
626 if(darktable.gui->last_preset && found)
627 {
628 char *markup = g_markup_printf_escaped("%s <span weight=\"bold\">%s</span>", _("update preset"),
630 mi = gtk_menu_item_new_with_label("");
631 gtk_widget_set_sensitive(GTK_WIDGET(mi), minfo->params_size > 0);
632 gtk_label_set_markup(GTK_LABEL(gtk_bin_get_child(GTK_BIN(mi))), markup);
633 g_object_set_data_full(G_OBJECT(mi), "dt-preset-name", g_strdup(darktable.gui->last_preset), g_free);
634 g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(menuitem_update_preset), minfo);
635 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
636 dt_free(markup);
637 }
638 cnt++;
639 }
640
641 if(minfo->module->set_preferences)
642 {
643 if(cnt>0)
644 {
645 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
646 }
647 minfo->module->set_preferences(GTK_MENU_SHELL(menu), minfo->module);
648 }
649}
650
651gint dt_lib_sort_plugins(gconstpointer a, gconstpointer b)
652{
653 const dt_lib_module_t *am = (const dt_lib_module_t *)a;
654 const dt_lib_module_t *bm = (const dt_lib_module_t *)b;
655 const int apos = (am && am->position) ? am->position() : 0;
656 const int bpos = (bm && bm->position) ? bm->position() : 0;
657 return apos - bpos;
658}
659
660/* default expandable implementation */
662{
663 return TRUE;
664}
665
666/* default autoapply implementation */
668{
669 return FALSE;
670}
671
672int default_lib_focus(dt_gui_module_t *m, gboolean toogle)
673{
674 dt_lib_module_t *module = (dt_lib_module_t *) m;
676 return 1;
677}
678
679static int _lib_plugin_body_button_press(GtkWidget *w, GdkEventButton *e, gpointer user_data)
680{
681 /* Reset the scrolling focus. If the click happened on any bauhaus element,
682 * its internal button_press method will set it for itself */
684 int handled = FALSE;
685 return handled;
686}
687
688static int dt_lib_load_module(void *m, const char *libname, const char *module_name)
689{
690 dt_lib_module_t *module = (dt_lib_module_t *)m;
691
692#define INCLUDE_API_FROM_MODULE_LOAD "lib_load_module"
693#include "libs/lib_api.h"
694
695 // Load modules only if they belong to a loaded view.
696 // Designed for print settings, which loads all installed CUPS printers at gui_init() time,
697 // which can take some time.
698 gboolean load = FALSE;
699 for(const char **view_m = module->views(module); *view_m; ++view_m)
700 {
701 for(GList *iter = darktable.view_manager->views; iter; iter = g_list_next(iter))
702 {
703 dt_view_t *view = (dt_view_t *)iter->data;
704 if(!g_strcmp0(view->module_name, *view_m) || !g_strcmp0("*", *view_m) || !g_strcmp0("special", *view_m))
705 {
706 load = TRUE;
707 break;
708 }
709 }
710 if(load) break;
711 }
712
713 if(!load) return 1;
714
715 g_strlcpy(module->plugin_name, module_name, sizeof(module->plugin_name));
716
717 if(((!module->get_params || !module->set_params)
718 && (module->legacy_params || module->set_params || module->get_params))
719 || (!module->init_presets && module->manage_presets))
720 {
721 fprintf(stderr,"[dt_lib_load_module] illegal method combination in '%s'\n", module->plugin_name);
722 }
723
724 if(!module->get_params || !module->set_params)
725 {
726 // need all at the same time, or none, note that in this case
727 // all the presets for the corresponding module will be deleted.
728 // see: dt_lib_init_presets.
729 module->legacy_params = NULL;
730 module->set_params = NULL;
731 module->get_params = NULL;
732 module->manage_presets = NULL;
733 }
734
735 module->widget = NULL;
736 module->expander = NULL;
737 module->arrow = NULL;
738 module->reset_button = NULL;
739 module->presets_button = NULL;
740
741 if(module->init) module->init(module);
742
743 /* pass on the dt_gui_module_t args for bauhaus widgets */
744 module->common_fields.name = g_strdup(module->name(module));
745 module->common_fields.view = NULL; // view is set at gui_init time
746 module->common_fields.widget_list = NULL;
747 module->common_fields.widget_list_bh = NULL;
748 module->common_fields.focus = module->lib_focus;
749 module->common_fields.deprecated = FALSE;
750
751 return 0;
752}
753
754static void *_update_params(dt_lib_module_t *module,
755 const void *const old_params, size_t old_params_size, int old_version,
756 int target_version, size_t *new_size)
757{
758 // make a copy of the old params so we can free it in the loop
759 void *params = malloc(old_params_size);
760 if(IS_NULL_PTR(params)) return NULL;
761 memcpy(params, old_params, old_params_size);
762 while(old_version < target_version)
763 {
764 size_t size;
765 int version;
766 void *new_params = module->legacy_params(module, params, old_params_size, old_version, &version, &size);
767 dt_free(params);
768 if(IS_NULL_PTR(new_params)) return NULL;
769 params = new_params;
770 old_version = version;
771 old_params_size = size;
772 }
773 *new_size = old_params_size;
774 return params;
775}
776
778{
779 // since lighttable presets can't end up in styles or any other place outside of the presets table it is
780 // sufficient
781 // to update that very table here and assume that everything is up to date elsewhere.
782 // the intended logic is as follows:
783 // - no set_params -> delete all presets
784 // - op_version >= module_version -> done
785 // - op_version < module_version ->
786 // - module has legacy_params -> try to update
787 // - module doesn't have legacy_params -> delete it
788
789 if(IS_NULL_PTR(module->set_params))
790 {
792 {
793 // clang-format off
795 "DELETE FROM data.presets"
796 " WHERE operation=?1", -1,
798 // clang-format on
799 }
800 sqlite3_stmt *stmt = _lib_presets_delete_operation_stmt;
801 sqlite3_reset(stmt);
802 sqlite3_clear_bindings(stmt);
803 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 1, module->plugin_name, -1, SQLITE_TRANSIENT);
804 sqlite3_step(stmt);
805 }
806 else
807 {
809 {
810 // clang-format off
812 "SELECT rowid, op_version, op_params, name"
813 " FROM data.presets"
814 " WHERE operation=?1",
815 -1, &_lib_presets_select_stmt, NULL);
816 // clang-format on
817 }
818 sqlite3_stmt *stmt = _lib_presets_select_stmt;
819 sqlite3_reset(stmt);
820 sqlite3_clear_bindings(stmt);
821 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 1, module->plugin_name, -1, SQLITE_TRANSIENT);
822 while(sqlite3_step(stmt) == SQLITE_ROW)
823 {
824 int rowid = sqlite3_column_int(stmt, 0);
825 int op_version = sqlite3_column_int(stmt, 1);
826 void *op_params = (void *)sqlite3_column_blob(stmt, 2);
827 size_t op_params_size = sqlite3_column_bytes(stmt, 2);
828 const char *name = (char *)sqlite3_column_text(stmt, 3);
829
830 int version = module->version();
831
832 if(op_version < version)
833 {
834 size_t new_params_size = 0;
835 void *new_params = NULL;
836
837 if(module->legacy_params
838 && (new_params = _update_params(module, op_params, op_params_size, op_version, version, &new_params_size)))
839 {
840 // write the updated preset back to db
841 fprintf(stderr,
842 "[lighttable_init_presets] updating '%s' preset '%s' from version %d to version %d\n",
843 module->plugin_name, name, op_version, version);
844 sqlite3_stmt *innerstmt;
845 // clang-format off
847 "UPDATE data.presets"
848 " SET op_version=?1, op_params=?2"
849 " WHERE rowid=?3", -1,
850 &innerstmt, NULL);
851 // clang-format on
852 DT_DEBUG_SQLITE3_BIND_INT(innerstmt, 1, version);
853 DT_DEBUG_SQLITE3_BIND_BLOB(innerstmt, 2, new_params, new_params_size, SQLITE_TRANSIENT);
854 DT_DEBUG_SQLITE3_BIND_INT(innerstmt, 3, rowid);
855 sqlite3_step(innerstmt);
856 sqlite3_finalize(innerstmt);
857 }
858 else
859 {
860 // delete the preset
861 fprintf(stderr, "[lighttable_init_presets] Can't upgrade '%s' preset '%s' from version %d to %d, "
862 "no legacy_params() implemented or unable to update\n",
863 module->plugin_name, name, op_version, version);
864 sqlite3_stmt *innerstmt;
865 // clang-format off
867 "DELETE FROM data.presets"
868 " WHERE rowid=?1", -1,
869 &innerstmt, NULL);
870 // clang-format on
871 DT_DEBUG_SQLITE3_BIND_INT(innerstmt, 1, rowid);
872 sqlite3_step(innerstmt);
873 sqlite3_finalize(innerstmt);
874 }
875 dt_free(new_params);
876 }
877 }
878 sqlite3_reset(stmt);
879 }
880
881 if(module->init_presets)
882 module->init_presets(module);
883
885 g_strdup(module->plugin_name));
886}
887
888
889static gboolean _lib_plugin_focus_accel(GtkAccelGroup *accel_group, GObject *accelerable, guint keyval,
890 GdkModifierType modifier, gpointer data)
891{
892 dt_gui_module_t *module = (dt_gui_module_t *)data;
893 if(!module || !module->focus) return FALSE;
894 return module->focus(module, FALSE);
895}
896
897
898static void dt_lib_init_module(void *m)
899{
900 dt_lib_module_t *module = (dt_lib_module_t *)m;
901 dt_lib_init_presets(module);
902
903 if(darktable.gui)
904 {
905 module->gui_init(module);
906 if(module->widget) g_object_ref_sink(module->widget);
907
908 // TODO: look for active view. Do we know it at init time ?
909 module->common_fields.view = g_strdup(_("Lighttable"));
910
911 if(!module->views || (module->expandable && !module->expandable(module))) return; // We are done
912 // Else: add accel pathes
913
914 gchar *clean_name = delete_underscore(module->name(module));
915 dt_capitalize_label(clean_name);
916
917 // slash is not allowed in module names because that makes accel pathes fail
918 assert(g_strrstr(clean_name, "/") == NULL);
919
920 const char **views = module->views(module);
921
922 // We add one accel path per view
923 for(const char **view = views; *view; ++view)
924 {
925 GtkAccelGroup *accel_group = NULL;
926 gchar *label = NULL;
927 if(!g_strcmp0(*view, "darkroom"))
928 {
929 accel_group = darktable.gui->accels->darkroom_accels;
930 label = g_strdup("Darkroom/Toolboxes");
931 }
932 else if(!g_strcmp0(*view, "lighttable"))
933 {
934 accel_group = darktable.gui->accels->lighttable_accels;
935 label = g_strdup("Lighttable/Toolboxes");
936 }
937 else if(!g_strcmp0(*view, "map"))
938 {
939 accel_group = darktable.gui->accels->map_accels;
940 label = g_strdup("Map/Toolboxes");
941 }
942 else if(!g_strcmp0(*view, "print"))
943 {
944 accel_group = darktable.gui->accels->print_accels;
945 label = g_strdup("Print/Toolboxes");
946 }
947 else if(!g_strcmp0(*view, "slideshow"))
948 {
949 accel_group = darktable.gui->accels->slideshow_accels;
950 label = g_strdup("Slideshow/Toolboxes");
951 }
952
953 if(accel_group && label)
955 accel_group, label, clean_name, 0, 0, FALSE,
956 _("Focuses the module"));
957 dt_free(label);
958 }
959
960 dt_free(clean_name);
961 }
962}
963
965{
966 GtkWidget *retained_widget = module->widget;
967
968 if(darktable.gui && darktable.gui->accels && module->views)
969 {
970 gchar *clean_name = delete_underscore(module->name(module));
971 dt_capitalize_label(clean_name);
972
973 const char **views = module->views(module);
974 for(const char **view = views; view && *view; ++view)
975 {
976 const char *scope = NULL;
977 if(!g_strcmp0(*view, "darkroom")) scope = "Darkroom/Toolboxes";
978 else if(!g_strcmp0(*view, "lighttable")) scope = "Lighttable/Toolboxes";
979 else if(!g_strcmp0(*view, "map")) scope = "Map/Toolboxes";
980 else if(!g_strcmp0(*view, "print")) scope = "Print/Toolboxes";
981 else if(!g_strcmp0(*view, "slideshow")) scope = "Slideshow/Toolboxes";
982
983 if(scope)
984 {
985 gchar *path = dt_accels_build_path(scope, clean_name);
987 dt_free(path);
988 }
989 }
990
991 dt_free(clean_name);
992 }
993
995 g_list_free(m->widget_list);
996 m->widget_list = NULL;
997 g_list_free(m->widget_list_bh);
998 m->widget_list_bh = NULL;
999 dt_free(m->name);
1000 dt_free(m->view);
1001
1002 if(!IS_NULL_PTR(module->expander) && GTK_IS_WIDGET(module->expander))
1003 {
1004 GtkWidget *expander = module->expander;
1005 g_object_ref_sink(expander);
1006 gtk_widget_destroy(expander);
1007 g_object_unref(expander);
1008 }
1009 else if(!IS_NULL_PTR(module->widget) && GTK_IS_WIDGET(module->widget))
1010 {
1011 GtkWidget *widget = module->widget;
1012 g_object_ref_sink(widget);
1013 gtk_widget_destroy(widget);
1014 g_object_unref(widget);
1015 }
1016 module->expander = NULL;
1017 module->widget = NULL;
1018 if(!IS_NULL_PTR(retained_widget) && G_IS_OBJECT(retained_widget))
1019 g_object_unref(retained_widget);
1020
1021 if(module->module) g_module_close(module->module);
1022}
1023
1024static void dt_lib_gui_reset_callback(GtkButton *button, gpointer user_data)
1025{
1026 dt_lib_module_t *module = (dt_lib_module_t *)user_data;
1027 module->gui_reset(module);
1028}
1029
1030static void presets_popup_callback(GtkButton *button, dt_lib_module_t *module)
1031{
1033
1034 mi->plugin_name = g_strdup(module->plugin_name);
1035 mi->version = module->version();
1036 mi->module = module;
1037 mi->params = module->get_params ? module->get_params(module, &mi->params_size) : NULL;
1038 if(IS_NULL_PTR(mi->params))
1039 {
1040 // this is a valid case, for example in location.c when nothing got selected
1041 // fprintf(stderr, "something went wrong: &params=%p, size=%i\n", mi->params, mi->params_size);
1042 mi->params_size = 0;
1043 }
1045
1046 dt_gui_menu_popup(darktable.gui->presets_popup_menu, GTK_WIDGET(button), GDK_GRAVITY_SOUTH_EAST, GDK_GRAVITY_NORTH_EAST);
1047
1048 if(button) dtgtk_button_set_active(DTGTK_BUTTON(button), FALSE);
1049}
1050
1051static void _lib_module_expander_gone(gpointer user_data, GObject *where_the_object_was)
1052{
1053 dt_lib_module_t *module = (dt_lib_module_t *)user_data;
1054 if(IS_NULL_PTR(module)) return;
1055 if(module->expander == (GtkWidget *)where_the_object_was) module->expander = NULL;
1056 if(darktable.gui)
1057 {
1058 if(darktable.gui->scroll_to[0] == (GtkWidget *)where_the_object_was) darktable.gui->scroll_to[0] = NULL;
1059 if(darktable.gui->scroll_to[1] == (GtkWidget *)where_the_object_was) darktable.gui->scroll_to[1] = NULL;
1060 }
1061}
1062
1063
1064void dt_lib_gui_set_expanded(dt_lib_module_t *module, gboolean expanded)
1065{
1066 if(IS_NULL_PTR(module->expander)) return;
1067 if(!DTGTK_IS_EXPANDER(module->expander))
1068 {
1069 module->expander = NULL;
1070 return;
1071 }
1072
1074
1075 /* record lib panel usage for crash reports and usage analytics */
1076 if(expanded)
1077 {
1080 }
1081
1082 /* show / hide plugin widget */
1083 if(expanded)
1084 {
1085 /* register to receive draw events */
1086 darktable.lib->gui_module = module;
1087 darktable.gui->scroll_to[1] = module->expander;
1088 gtk_widget_grab_focus(GTK_WIDGET(module->expander));
1089 }
1090 else
1091 {
1092 if(darktable.lib->gui_module == module)
1093 {
1094 darktable.lib->gui_module = NULL;
1096 }
1098 }
1099
1100 if(expanded)
1101 dt_gui_add_class(module->expander, "expanded");
1102 else
1103 dt_gui_remove_class(module->expander, "expanded");
1104
1105 // Update expander arrow state
1106 // Note: directions are inverted in drawing method for some reason.
1109 (expanded ? CPF_DIRECTION_UP : CPF_DIRECTION_LEFT),
1110 NULL);
1111
1112 /* store expanded state of module */
1113 char var[1024];
1115 snprintf(var, sizeof(var), "plugins/%s/%s/expanded", current_view->module_name, module->plugin_name);
1116 dt_conf_set_bool(var, expanded);
1117}
1118
1120{
1121 if(!module->expandable(module)) return true;
1122 if(IS_NULL_PTR(module->expander)) return true;
1123 if(!DTGTK_IS_EXPANDER(module->expander))
1124 {
1125 module->expander = NULL;
1126 if(!module->widget)
1127 {
1128 char var[1024];
1130 if(IS_NULL_PTR(current_view)) return true;
1131 snprintf(var, sizeof(var), "plugins/%s/%s/expanded", current_view->module_name, module->plugin_name);
1132 return dt_conf_get_bool(var);
1133 }
1134 return true;
1135 }
1136 if(!module->widget)
1137 {
1138 char var[1024];
1140 snprintf(var, sizeof(var), "plugins/%s/%s/expanded", current_view->module_name, module->plugin_name);
1141 return dt_conf_get_bool(var);
1142 }
1144}
1145
1146static void _toggle_expanded(dt_lib_module_t *module, gboolean close_all)
1147{
1148 if(close_all)
1149 {
1151 gboolean all_other_closed = TRUE;
1152 uint32_t container = module->container(module);
1153
1154 for(const GList *it = darktable.lib->plugins; it; it = g_list_next(it))
1155 {
1157
1158 if(m != module && container == m->container(m) && m->expandable(m) && dt_lib_is_visible_in_view(m, v))
1159 {
1160 if(m->expander && DTGTK_IS_EXPANDER(m->expander))
1161 all_other_closed = all_other_closed && !dtgtk_expander_get_expanded(DTGTK_EXPANDER(m->expander));
1162
1164 }
1165 }
1166 if(all_other_closed)
1168 else
1170 }
1171 else
1172 {
1173 /* else just toggle */
1175 }
1176}
1177
1178static void expand_callback(GtkButton *button, dt_lib_module_t *module)
1179{
1180 _toggle_expanded(module, FALSE);
1181}
1182
1183static gboolean _lib_plugin_header_button_press(GtkWidget *w, GdkEventButton *e, gpointer user_data)
1184{
1185 if(e->type == GDK_2BUTTON_PRESS || e->type == GDK_3BUTTON_PRESS) return TRUE;
1186
1187 dt_lib_module_t *module = (dt_lib_module_t *)user_data;
1188
1189 /* Reset the scrolling focus. If the click happened on any bauhaus element,
1190 * its internal button_press method will set it for itself */
1192
1193 if(e->button == 1)
1194 {
1195 /* bail out if module is static */
1196 if(!module->expandable(module)) return FALSE;
1197
1198 // make gtk scroll to the module once it updated its allocation size
1199 uint32_t container = module->container(module);
1201 darktable.gui->scroll_to[0] = module->expander;
1203 darktable.gui->scroll_to[1] = module->expander;
1204
1205 gtk_widget_grab_focus(GTK_WIDGET(module->expander));
1206
1207 /* handle shiftclick on expander, hide all except this */
1208 _toggle_expanded(module, dt_modifier_is(e->state, GDK_SHIFT_MASK));
1209
1210 return TRUE;
1211 }
1212 else if(e->button == 3)
1213 {
1214 if(gtk_widget_get_sensitive(module->presets_button))
1215 presets_popup_callback(NULL, module);
1216
1217 return TRUE;
1218 }
1219 return FALSE;
1220}
1221
1222#if 0
1223static void show_module_callback(dt_lib_module_t *module)
1224{
1225 /* bail out if module is static */
1226 if(!module->expandable(module)) return;
1227
1228 // make gtk scroll to the module once it updated its allocation size
1229 uint32_t container = module->container(module);
1231 darktable.gui->scroll_to[0] = module->expander;
1233 darktable.gui->scroll_to[1] = module->expander;
1234
1236}
1237
1238#endif
1239
1241{
1242 /* check if module is expandable */
1243 if(!module->expandable(module))
1244 {
1245 if(module->presets_button)
1246 {
1247 // FIXME separately define as darkroom widget shortcut/action, because not automatically registered via lib
1248 // if presets btn has been loaded to be shown outside expander
1249 g_signal_connect(G_OBJECT(module->presets_button), "clicked", G_CALLBACK(presets_popup_callback), module);
1250 }
1251 module->expander = NULL;
1252 return NULL;
1253 }
1254
1255 GtkWidget *header = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING / 2.);
1256 gtk_widget_set_name(GTK_WIDGET(header), "module-header");
1257
1258 GtkWidget *expander = dtgtk_expander_new(header, module->widget);
1259 dt_gui_add_class(expander, "dt_module_frame");
1260 dt_gui_add_class(expander, "dt_lib_module");
1261
1264 GtkWidget *pluginui_frame = dtgtk_expander_get_frame(DTGTK_EXPANDER(expander));
1265 dt_gui_add_class(pluginui_frame, "dt_plugin_ui");
1266
1267 /* setup the header box */
1268 g_signal_connect(G_OBJECT(header_evb), "button-press-event", G_CALLBACK(_lib_plugin_header_button_press),
1269 module);
1270
1271 /* connect mouse button callbacks for focus and presets */
1272 g_signal_connect(G_OBJECT(body_evb), "button-press-event", G_CALLBACK(_lib_plugin_body_button_press), module);
1273 gtk_widget_add_events(body_evb, GDK_POINTER_MOTION_MASK);
1274
1275 /*
1276 * initialize the header widgets
1277 */
1278
1279 /* add collapsing arrow */
1280 if(module->expandable(module))
1281 {
1282 module->arrow = dtgtk_button_new(dtgtk_cairo_paint_arrow, 0, NULL);
1283 g_signal_connect(G_OBJECT(module->arrow), "clicked", G_CALLBACK(expand_callback), module);
1284 gtk_box_pack_start(GTK_BOX(header), module->arrow, FALSE, FALSE, 0);
1285 dt_gui_add_class(module->arrow, "dt-collapse-arrow");
1286 }
1287
1288 /* add module label */
1289 GtkWidget *label = gtk_label_new("");
1290 GtkWidget *label_evb = gtk_event_box_new();
1291 gtk_container_add(GTK_CONTAINER(label_evb), label);
1292 gchar *mname = g_markup_escape_text(module->name(module), -1);
1293 dt_capitalize_label(mname);
1294 gtk_label_set_markup(GTK_LABEL(label), mname);
1295 dt_free(mname);
1296 gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_END);
1297 g_object_set(G_OBJECT(label), "halign", GTK_ALIGN_START, "xalign", 0.0, (gchar *)0);
1298 gtk_widget_set_name(label, "lib-panel-label");
1299 gtk_box_pack_start(GTK_BOX(header), label_evb, FALSE, FALSE, 0);
1300
1301 /* add preset button if module has implementation */
1302 module->presets_button = dtgtk_button_new(dtgtk_cairo_paint_presets, 0, NULL);
1303 g_signal_connect(G_OBJECT(module->presets_button), "clicked", G_CALLBACK(presets_popup_callback), module);
1304 if(!module->get_params && !module->set_preferences) gtk_widget_set_sensitive(GTK_WIDGET(module->presets_button), FALSE);
1305 gtk_box_pack_end(GTK_BOX(header), module->presets_button, FALSE, FALSE, 0);
1306
1307 /* add reset button if module has implementation */
1308 module->reset_button = dtgtk_button_new(dtgtk_cairo_paint_reset, 0, NULL);
1309 g_signal_connect(G_OBJECT(module->reset_button), "clicked", G_CALLBACK(dt_lib_gui_reset_callback), module);
1310 if(!module->gui_reset) gtk_widget_set_sensitive(GTK_WIDGET(module->reset_button), FALSE);
1311 gtk_box_pack_end(GTK_BOX(header), module->reset_button, FALSE, FALSE, 0);
1312
1313 gtk_widget_show_all(GTK_WIDGET(module->widget));
1314 dt_gui_add_class(module->widget, "dt_plugin_ui_main");
1315 module->expander = expander;
1316 g_object_weak_ref(G_OBJECT(expander), _lib_module_expander_gone, module);
1317
1318 gtk_widget_set_hexpand(module->widget, FALSE);
1319 gtk_widget_set_vexpand(module->widget, FALSE);
1320
1321 return module->expander;
1322}
1323
1325{
1326 // Setting everything to null initially
1327 memset(lib, 0, sizeof(dt_lib_t));
1328 darktable.lib->plugins = dt_module_load_modules("/plugins/lighttable", sizeof(dt_lib_module_t),
1330}
1331
1333{
1334 while(lib->plugins)
1335 {
1336 dt_lib_module_t *module = (dt_lib_module_t *)(lib->plugins->data);
1337 if(module)
1338 {
1339 if(module->gui_cleanup)
1340 module->gui_cleanup(module);
1341 module->data = NULL;
1342 dt_free(module->common_fields.view);
1343 module->common_fields.view = NULL;
1344 dt_lib_unload_module(module);
1345 dt_free(module);
1346 }
1347 lib->plugins = g_list_delete_link(lib->plugins, lib->plugins);
1348 }
1349
1351}
1352
1353void dt_lib_presets_add(const char *name, const char *plugin_name, const int32_t version, const void *params,
1354 const int32_t params_size, gboolean readonly)
1355{
1356 dt_lib_presets_remove(name, plugin_name, version);
1358 {
1359 // clang-format off
1362 "INSERT INTO data.presets"
1363 " (name, description, operation, op_version, op_params, "
1364 " blendop_params, blendop_version, enabled, model, maker, lens, "
1365 " iso_min, iso_max, exposure_min, exposure_max, aperture_min, aperture_max, "
1366 " focal_length_min, focal_length_max, writeprotect, "
1367 " autoapply, filter, def, format)"
1368 " VALUES "
1369 " (?1, '', ?2, ?3, ?4, NULL, 0, 1, '%', "
1370 " '%', '%', 0, 340282346638528859812000000000000000000, 0, 10000000, 0, 100000000, 0,"
1371 " 1000, ?5, 0, 0, 0, 0)",
1372 -1, &_lib_presets_add_stmt, NULL);
1373 // clang-format on
1374 }
1375 sqlite3_stmt *stmt = _lib_presets_add_stmt;
1376 sqlite3_reset(stmt);
1377 sqlite3_clear_bindings(stmt);
1378 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 1, name, -1, SQLITE_TRANSIENT);
1379 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, plugin_name, -1, SQLITE_TRANSIENT);
1380 DT_DEBUG_SQLITE3_BIND_INT(stmt, 3, version);
1381 DT_DEBUG_SQLITE3_BIND_BLOB(stmt, 4, params, params_size, SQLITE_TRANSIENT);
1382 DT_DEBUG_SQLITE3_BIND_INT(stmt, 5, readonly);
1383 sqlite3_step(stmt);
1384}
1385
1386static gchar *_get_lib_view_path(dt_lib_module_t *module, char *suffix)
1387{
1388 if(IS_NULL_PTR(darktable.view_manager) || !module || IS_NULL_PTR(suffix) || module->plugin_name[0] == '\0') return NULL;
1390 if(IS_NULL_PTR(cv) || cv->module_name[0] == '\0') return NULL;
1391 // in lighttable, we store panels states per layout
1392 char lay[32] = "";
1393 if(g_strcmp0(cv->module_name, "lighttable") == 0)
1394 {
1395 g_snprintf(lay, sizeof(lay), "%d/", 0);
1396 }
1397 else if(g_strcmp0(cv->module_name, "darkroom") == 0)
1398 {
1399 g_snprintf(lay, sizeof(lay), "%d/", dt_view_darkroom_get_layout(darktable.view_manager));
1400 }
1401
1402 return g_strdup_printf("plugins/%s/%s%s%s", cv->module_name, lay, module->plugin_name, suffix);
1403}
1404
1406{
1407 gchar *key = _get_lib_view_path(module, "_visible");
1408 gboolean ret = TRUE; /* if not key found, always make module visible */
1410 dt_free(key);
1411
1412 return ret;
1413}
1414
1415void dt_lib_set_visible(dt_lib_module_t *module, gboolean visible)
1416{
1417 gchar *key = _get_lib_view_path(module, "_visible");
1418 GtkWidget *widget;
1419 if(key)
1420 {
1421 dt_conf_set_bool(key, visible);
1422 dt_free(key);
1423 }
1424 if(module->widget)
1425 {
1426 if(module->expander)
1427 widget = module->expander;
1428 else
1429 widget = module->widget;
1430
1431
1432 if(visible)
1433 gtk_widget_show(GTK_WIDGET(widget));
1434 else
1435 gtk_widget_hide(GTK_WIDGET(widget));
1436 }
1437}
1438
1439gchar *dt_lib_get_localized_name(const gchar *plugin_name)
1440{
1441 // Prepare mapping op -> localized name
1442 static GHashTable *module_names = NULL;
1443 if(IS_NULL_PTR(module_names))
1444 {
1445 module_names = g_hash_table_new(g_str_hash, g_str_equal);
1446 for(const GList *lib = darktable.lib->plugins; lib; lib = g_list_next(lib))
1447 {
1448 dt_lib_module_t *module = (dt_lib_module_t *)lib->data;
1449 g_hash_table_insert(module_names, module->plugin_name, g_strdup(module->name(module)));
1450 }
1451 }
1452
1453 return (gchar *)g_hash_table_lookup(module_names, plugin_name);
1454}
1455
1457{
1458 dt_develop_t *const dev = darktable.develop;
1459 dt_colorpicker_sample_t *const sample = dev ? dev->color_picker.primary_sample : NULL;
1460 if(IS_NULL_PTR(sample)) return;
1461
1462 dt_boundingbox_t raw_box = { box[0], box[1], box[2], box[3] };
1464
1465 gboolean changed = (sample->size != DT_LIB_COLORPICKER_SIZE_BOX);
1467 for(int k = 0; k < 4; k++)
1468 {
1469 changed |= (sample->box[k] != raw_box[k]);
1470 sample->box[k] = raw_box[k];
1471 }
1472
1473 if(!changed) return;
1476}
1477
1478void dt_lib_colorpicker_set_point(dt_lib_t *lib, const float pos[2])
1479{
1480 dt_develop_t *const dev = darktable.develop;
1481 dt_colorpicker_sample_t *const sample = dev ? dev->color_picker.primary_sample : NULL;
1482 if(IS_NULL_PTR(sample)) return;
1483
1484 float raw_pos[2] = { pos[0], pos[1] };
1486
1487 const gboolean changed = sample->size != DT_LIB_COLORPICKER_SIZE_POINT
1488 || sample->point[0] != raw_pos[0]
1489 || sample->point[1] != raw_pos[1];
1491 sample->point[0] = raw_pos[0];
1492 sample->point[1] = raw_pos[1];
1493
1494 if(!changed) return;
1497}
1498
1500{
1501 /* hide/show modules as last config */
1502 for(GList *iter = darktable.lib->plugins; iter; iter = g_list_next(iter))
1503 {
1504 dt_lib_module_t *plugin = (dt_lib_module_t *)(iter->data);
1505 if(strcmp(plugin->plugin_name, name) == 0)
1506 return plugin;
1507 }
1508
1509 return NULL;
1510}
1511
1512/* callback function for delayed update after user interaction */
1513static gboolean _postponed_update(gpointer data)
1514{
1515 dt_lib_module_t *self = (dt_lib_module_t *)data;
1516 self->timeout_handle = 0;
1517 if(self->_postponed_update)
1518 self->_postponed_update(self);
1519
1520 return FALSE; // cancel the timer
1521}
1522
1525{
1526 if(mod->timeout_handle)
1527 {
1528 // here we're making sure the event fires at last hover
1529 // and we won't have avalanche of events in the mean time.
1530 g_source_remove(mod->timeout_handle);
1531 }
1532 mod->_postponed_update = update_fn;
1533 mod->timeout_handle = g_timeout_add(dt_conf_get_int("processing/timeout"), _postponed_update, mod);
1534}
1535
1537{
1538 mod->_postponed_update = NULL;
1539 if(mod->timeout_handle)
1540 {
1541 g_source_remove(mod->timeout_handle);
1542 mod->timeout_handle = 0;
1543 }
1544}
1545
1547{
1548 return mod->preset_autoapply(mod);
1549}
1550
1551gboolean dt_handle_dialog_enter(GtkWidget *widget, GdkEventKey *event, gpointer data)
1552{
1553 guint key = dt_keys_mainpad_alternatives(event->keyval);
1554
1555 if(key == GDK_KEY_Return)
1556 {
1557 gtk_dialog_response(GTK_DIALOG(widget), GTK_RESPONSE_ACCEPT);
1558 return TRUE;
1559 }
1560 return FALSE;
1561}
1562
1563GtkWidget *dt_action_button_new(dt_lib_module_t *self, const gchar *label, gpointer callback, gpointer data, const gchar *tooltip, guint accel_key, GdkModifierType mods)
1564{
1565 gchar *label_copy = g_strdup(label);
1566 dt_capitalize_label(label_copy);
1567 GtkWidget *button = gtk_button_new_with_label(label_copy);
1568 dt_free(label_copy);
1569
1570 gtk_widget_set_valign(GTK_WIDGET(button), GTK_ALIGN_CENTER);
1571 gtk_widget_set_halign(GTK_WIDGET(button), GTK_ALIGN_CENTER);
1572 gtk_widget_set_vexpand(GTK_WIDGET(button), FALSE);
1573 gtk_widget_set_hexpand(GTK_WIDGET(button), FALSE);
1574
1575 gtk_label_set_ellipsize(GTK_LABEL(gtk_bin_get_child(GTK_BIN(button))), PANGO_ELLIPSIZE_END);
1576
1577 if(tooltip) gtk_widget_set_tooltip_text(button, tooltip);
1578
1579 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(callback), data);
1580
1581 return button;
1582}
1583
1584
1585
1586// clang-format off
1587// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1588// vim: shiftwidth=2 expandtab tabstop=2 cindent
1589// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1590// 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, 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....
#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:565
uint32_t container(dt_lib_module_t *self)
const char ** views(dt_lib_module_t *self)
#define m
Definition basecurve.c:278
void dtgtk_button_set_paint(GtkDarktableButton *button, DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
Definition button.c:168
void dtgtk_button_set_active(GtkDarktableButton *button, gboolean active)
Definition button.c:176
#define DTGTK_BUTTON(obj)
Definition button.h:39
void dt_iop_color_picker_request_update(void)
@ DT_LIB_COLORPICKER_SIZE_POINT
Definition colorpicker.h:37
@ DT_LIB_COLORPICKER_SIZE_BOX
Definition colorpicker.h:38
char * key
char * name
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)
int dt_conf_get_int(const char *name)
void dt_conf_set_string(const char *name, const char *val)
void dt_control_log(const char *msg,...)
Definition control.c:761
void dt_control_queue_redraw()
request redraw of the workspace. This redraws the whole workspace within a gdk critical section to pr...
Definition control.c:856
uint32_t view(const dt_view_t *self)
Definition darkroom.c:227
darktable_t darktable
Definition darktable.c:181
float dt_boundingbox_t[4]
Definition darktable.h:709
#define dt_free(ptr)
Definition darktable.h:456
static gchar * delete_underscore(const char *s)
Definition darktable.h:1083
static gboolean dt_modifier_is(const GdkModifierType state, const GdkModifierType desired_modifier_mask)
Definition darktable.h:893
#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 darktable.h:281
sqlite3 * dt_database_get(const dt_database_t *db)
Definition database.c:3646
#define DT_DEBUG_SQLITE3_BIND_BLOB(a, b, c, d, e)
Definition debug.h:119
#define DT_DEBUG_SQLITE3_PREPARE_V2(a, b, c, d, e)
Definition debug.h:107
#define DT_DEBUG_SQLITE3_BIND_TEXT(a, b, c, d, e)
Definition debug.h:118
#define DT_DEBUG_SQLITE3_BIND_INT(a, b, c)
Definition debug.h:115
void dt_dev_coordinates_image_norm_to_raw_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1124
void dtgtk_cairo_paint_arrow(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
@ CPF_DIRECTION_LEFT
Definition dtgtk/paint.h:63
@ CPF_DIRECTION_UP
Definition dtgtk/paint.h:61
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:30
#define DTGTK_IS_EXPANDER(obj)
Definition expander.h:31
static guint dt_keys_mainpad_alternatives(const guint key_val)
Remap keypad keys to usual mainpad ones.
Definition gdkkeys.h:113
void dt_gui_menu_popup(GtkMenu *menu, GtkWidget *button, GdkGravity widget_anchor, GdkGravity menu_anchor)
Definition gtk.c:2953
void dt_gui_remove_class(GtkWidget *widget, const gchar *class_name)
Definition gtk.c:143
void dt_capitalize_label(gchar *text)
Definition gtk.c:3150
void dt_gui_store_last_preset(const char *name)
Definition gtk.c:475
void dt_gui_refocus_center()
Definition gtk.c:3234
void dt_gui_add_class(GtkWidget *widget, const gchar *class_name)
Definition gtk.c:133
GtkWidget * dt_ui_main_window(dt_ui_t *ui)
get the main window widget
#define DT_GUI_BOX_SPACING
Definition gtk.h:109
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)
const char * tooltip
Definition image.h:251
struct dt_iop_tonecurve_params_t preset
const float v
gboolean dt_handle_dialog_enter(GtkWidget *widget, GdkEventKey *event, gpointer data)
Definition lib.c:1551
static gboolean _lib_plugin_header_button_press(GtkWidget *w, GdkEventButton *e, gpointer user_data)
Definition lib.c:1183
static gboolean default_expandable(dt_lib_module_t *self)
Definition lib.c:661
static void dt_lib_gui_reset_callback(GtkButton *button, gpointer user_data)
Definition lib.c:1024
gboolean dt_lib_presets_can_autoapply(dt_lib_module_t *mod)
Definition lib.c:1546
static void free_module_info(GtkWidget *widget, gpointer user_data)
Definition lib.c:494
static gboolean default_preset_autoapply(dt_lib_module_t *self)
Definition lib.c:667
dt_action_element_lib_t
Definition lib.c:109
@ DT_ACTION_ELEMENT_RESET
Definition lib.c:111
@ DT_ACTION_ELEMENT_PRESETS
Definition lib.c:112
@ DT_ACTION_ELEMENT_SHOW
Definition lib.c:110
static sqlite3_stmt * _lib_presets_select_stmt
Definition lib.c:81
static int _lib_plugin_body_button_press(GtkWidget *w, GdkEventButton *e, gpointer user_data)
Definition lib.c:679
gint dt_lib_sort_plugins(gconstpointer a, gconstpointer b)
Definition lib.c:651
void dt_lib_colorpicker_set_box_area(dt_lib_t *lib, const dt_boundingbox_t box)
Definition lib.c:1456
void dt_lib_cancel_postponed_update(dt_lib_module_t *mod)
Definition lib.c:1536
gboolean dt_lib_gui_get_expanded(dt_lib_module_t *module)
Definition lib.c:1119
void dt_lib_init(dt_lib_t *lib)
Definition lib.c:1324
void dt_lib_presets_remove(const gchar *preset, const gchar *module_name, int module_version)
Definition lib.c:394
void dt_lib_init_presets(dt_lib_module_t *module)
Definition lib.c:777
static int dt_lib_load_module(void *m, const char *libname, const char *module_name)
Definition lib.c:688
static void menuitem_delete_preset(GtkMenuItem *menuitem, dt_lib_module_info_t *minfo)
Definition lib.c:307
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:1563
gboolean dt_lib_is_visible_in_view(dt_lib_module_t *module, const dt_view_t *view)
Definition lib.c:136
static void expand_callback(GtkButton *button, dt_lib_module_t *module)
Definition lib.c:1178
static void _lib_module_expander_gone(gpointer user_data, GObject *where_the_object_was)
Definition lib.c:1051
static void dt_lib_unload_module(dt_lib_module_t *module)
Definition lib.c:964
void dt_lib_queue_postponed_update(dt_lib_module_t *mod, void(*update_fn)(dt_lib_module_t *self))
Definition lib.c:1524
static void _toggle_expanded(dt_lib_module_t *module, gboolean close_all)
Definition lib.c:1146
static gboolean _postponed_update(gpointer data)
Definition lib.c:1513
static void dt_lib_presets_popup_menu_show(dt_lib_module_info_t *minfo)
Definition lib.c:502
static void menuitem_update_preset(GtkMenuItem *menuitem, dt_lib_module_info_t *minfo)
Definition lib.c:223
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:1353
static sqlite3_stmt * _lib_presets_remove_stmt
Definition lib.c:79
static void menuitem_edit_preset(GtkMenuItem *menuitem, dt_lib_module_info_t *minfo)
Definition lib.c:297
gboolean dt_lib_presets_apply(const gchar *preset, const gchar *module_name, int module_version)
Definition lib.c:415
void dt_lib_colorpicker_set_point(dt_lib_t *lib, const float pos[2])
Definition lib.c:1478
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:754
gboolean dt_lib_is_visible(dt_lib_module_t *module)
Definition lib.c:1405
static void presets_popup_callback(GtkButton *button, dt_lib_module_t *module)
Definition lib.c:1030
gchar * dt_lib_presets_duplicate(const gchar *preset, const gchar *module_name, int module_version)
Definition lib.c:338
void dt_lib_gui_set_expanded(dt_lib_module_t *module, gboolean expanded)
Definition lib.c:1064
static sqlite3_stmt * _lib_presets_add_stmt
Definition lib.c:80
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:466
static void menuitem_new_preset(GtkMenuItem *menuitem, dt_lib_module_info_t *minfo)
Definition lib.c:266
int default_lib_focus(dt_gui_module_t *m, gboolean toogle)
Definition lib.c:672
static gboolean _lib_plugin_focus_accel(GtkAccelGroup *accel_group, GObject *accelerable, guint keyval, GdkModifierType modifier, gpointer data)
Definition lib.c:889
dt_lib_module_t * dt_lib_get_module(const char *name)
Definition lib.c:1499
void dt_lib_set_visible(dt_lib_module_t *module, gboolean visible)
Definition lib.c:1415
static void menuitem_manage_presets(GtkMenuItem *menuitem, dt_lib_module_info_t *minfo)
Definition lib.c:302
static void dt_lib_init_module(void *m)
Definition lib.c:898
static void _lib_presets_stmt_cleanup(void)
Definition lib.c:84
void dt_lib_cleanup(dt_lib_t *lib)
Definition lib.c:1332
static gchar * get_active_preset_name(dt_lib_module_info_t *minfo)
Definition lib.c:155
static gchar * _get_lib_view_path(dt_lib_module_t *module, char *suffix)
Definition lib.c:1386
static sqlite3_stmt * _lib_presets_delete_operation_stmt
Definition lib.c:82
GtkWidget * dt_lib_gui_get_expander(dt_lib_module_t *module)
Definition lib.c:1240
static void edit_preset(const char *name_in, dt_lib_module_info_t *minfo)
Definition lib.c:184
static void pick_callback(GtkMenuItem *menuitem, dt_lib_module_info_t *minfo)
Definition lib.c:487
gchar * dt_lib_get_localized_name(const gchar *plugin_name)
Definition lib.c:1439
float *const restrict const size_t k
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))
Definition module.c:36
void dt_osx_disallow_fullscreen(GtkWidget *widget)
Definition osx.mm:104
void dt_sentry_record_module_usage(const char *category, const char *name)
Definition sentry.c:492
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
Definition signal.h:347
@ DT_SIGNAL_PRESETS_CHANGED
Definition signal.h:162
struct _GtkWidget GtkWidget
Definition splash.h:29
struct dt_lib_t * lib
Definition darktable.h:771
struct dt_gui_gtk_t * gui
Definition darktable.h:775
const struct dt_database_t * db
Definition darktable.h:779
struct dt_control_signal_t * signals
Definition darktable.h:774
struct dt_develop_t * develop
Definition darktable.h:770
struct dt_view_manager_t * view_manager
Definition darktable.h:772
GtkAccelGroup * slideshow_accels
GtkAccelGroup * map_accels
GtkAccelGroup * print_accels
GtkAccelGroup * lighttable_accels
GtkAccelGroup * darkroom_accels
dt_lib_colorpicker_size_t size
Definition colorpicker.h:63
dt_boundingbox_t box
Definition colorpicker.h:62
struct dt_develop_t::@19 color_picker
Authoritative darkroom color-picker state.
struct dt_colorpicker_sample_t * primary_sample
Definition develop.h:391
dt_accels_t * accels
Definition gtk.h:194
GtkMenu * presets_popup_menu
Definition gtk.h:169
GtkWidget * has_scroll_focus
Definition gtk.h:228
dt_ui_t * ui
Definition gtk.h:164
GtkWidget * scroll_to[2]
Definition gtk.h:221
char * last_preset
Definition gtk.h:170
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:117
int32_t version
Definition lib.c:118
GtkWidget * expander
Definition lib.h:86
GtkWidget * reset_button
Definition lib.h:93
GtkWidget * presets_button
Definition lib.h:94
void(* _postponed_update)(struct dt_lib_module_t *self)
Definition lib.h:88
char plugin_name[128]
Definition lib.h:82
dt_gui_module_t common_fields
Definition lib.h:72
GModule *void * data
Definition lib.h:80
GtkWidget * arrow
Definition lib.h:92
GtkWidget * widget
Definition lib.h:84
guint timeout_handle
Definition lib.h:90
dt_lib_module_t *gint old_id
Definition lib.c:133
Definition lib.h:54
struct dt_lib_module_t * gui_module
Definition lib.h:56
GList * plugins
Definition lib.h:55
GList * views
Definition view.h:200
GModule *void * data
Definition view.h:157
char module_name[64]
Definition view.h:153
void dt_telemetry_record_module_usage(const char *category, const char *name)
Definition telemetry.c:422
dt_darkroom_layout_t dt_view_darkroom_get_layout(dt_view_manager_t *vm)
Definition view.c:1332
const dt_view_t * dt_view_manager_get_current_view(dt_view_manager_t *vm)
Definition view.c:140
@ DT_UI_CONTAINER_PANEL_RIGHT_CENTER
@ DT_UI_CONTAINER_PANEL_LEFT_CENTER