Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
imageop.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2009-2014 johannes hanika.
4 Copyright (C) 2010-2011 Bruce Guenter.
5 Copyright (C) 2010-2012 Henrik Andersson.
6 Copyright (C) 2010 Stuart Henderson.
7 Copyright (C) 2011 Antony Dovgal.
8 Copyright (C) 2011 Brian Teague.
9 Copyright (C) 2011-2012 Jérémy Rosen.
10 Copyright (C) 2011 Robert Bieber.
11 Copyright (C) 2011 Rostyslav Pidgornyi.
12 Copyright (C) 2011-2020 Tobias Ellinghaus.
13 Copyright (C) 2011-2015, 2017, 2019 Ulrich Pegelow.
14 Copyright (C) 2012-2014, 2019-2022 Aldric Renaudin.
15 Copyright (C) 2012 Edouard Gomez.
16 Copyright (C) 2012-2013, 2015, 2018, 2020 parafin.
17 Copyright (C) 2012-2013, 2018-2022 Pascal Obry.
18 Copyright (C) 2012 Richard Wonka.
19 Copyright (C) 2013-2016 Roman Lebedev.
20 Copyright (C) 2013 Simon Spannagel.
21 Copyright (C) 2013 Thomas Pryds.
22 Copyright (C) 2014, 2017, 2021 Dan Torop.
23 Copyright (C) 2014 Mikhail Trishchenkov.
24 Copyright (C) 2014-2016 Pedro Côrte-Real.
25 Copyright (C) 2017 Dominik Markiewicz.
26 Copyright (C) 2017-2019 Edgardo Hoszowski.
27 Copyright (C) 2017-2020 Heiko Bauke.
28 Copyright (C) 2017 Peter Budai.
29 Copyright (C) 2018 Matthieu Moy.
30 Copyright (C) 2018 rawfiner.
31 Copyright (C) 2019-2026 Aurélien PIERRE.
32 Copyright (C) 2019-2022 Diederik Ter Rahe.
33 Copyright (C) 2019 Jacques Le Clerc.
34 Copyright (C) 2020-2022 Chris Elston.
35 Copyright (C) 2020-2022 Hanno Schwalm.
36 Copyright (C) 2020 Harold le Clément de Saint-Marcq.
37 Copyright (C) 2020-2021 Hubert Kowalski.
38 Copyright (C) 2020-2021 Marco.
39 Copyright (C) 2020 Philippe Weyland.
40 Copyright (C) 2020-2021 Ralf Brown.
41 Copyright (C) 2021 Mark-64.
42 Copyright (C) 2021-2022 Philipp Lutz.
43 Copyright (C) 2021 Sakari Kapanen.
44 Copyright (C) 2022 Martin Bařinka.
45 Copyright (C) 2022 Miloš Komarčević.
46 Copyright (C) 2022 Nicolas Auffray.
47 Copyright (C) 2023 Alynx Zhou.
48 Copyright (C) 2023 Luca Zulberti.
49 Copyright (C) 2025-2026 Guillaume Stutin.
50 Copyright (C) 2025 Miguel Moquillon.
51
52 darktable is free software: you can redistribute it and/or modify
53 it under the terms of the GNU General Public License as published by
54 the Free Software Foundation, either version 3 of the License, or
55 (at your option) any later version.
56
57 darktable is distributed in the hope that it will be useful,
58 but WITHOUT ANY WARRANTY; without even the implied warranty of
59 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
60 GNU General Public License for more details.
61
62 You should have received a copy of the GNU General Public License
63 along with darktable. If not, see <http://www.gnu.org/licenses/>.
64*/
65
66#include "common/darktable.h"
67#include "common/sentry.h"
68#include "common/telemetry.h"
69#include "develop/imageop.h"
70#include "bauhaus/bauhaus.h"
71#include "common/collection.h"
72#include "common/debug.h"
73#include "common/exif.h"
74#include "common/history.h"
75#include "common/imagebuf.h"
78#include "common/module.h"
79#include "common/opencl.h"
81#include "control/control.h"
82#include "control/signal.h"
83#include "develop/blend.h"
84#include "develop/develop.h"
85#include "develop/format.h"
86#include "develop/masks.h"
87#include "develop/tiling.h"
88#include "gui/gdkkeys.h"
89#include "gui/presets.h"
90#include "dtgtk/button.h"
91#include "dtgtk/expander.h"
93#include "dtgtk/icon.h"
94
96#include "gui/gtk.h"
97#include "gui/gui_throttle.h"
98#include "gui/presets.h"
99#ifdef GDK_WINDOWING_QUARTZ
100#include "osx/osx.h"
101#endif
102
103#include <assert.h>
104#include <gmodule.h>
105#include <math.h>
106#include <complex.h>
107#include <stdlib.h>
108
109static sqlite3_stmt *_iop_presets_select_stmt = NULL;
110#include <string.h>
111#include <strings.h>
112#include <time.h>
113
114#define DT_IOP_HEADER_MENU_OPEN "dt-module-header-menu-open"
115#define DT_IOP_HEADER_MENU_DISMISS_CLICK "dt-module-header-menu-dismiss-click"
116#define DT_IOP_HEADER_IGNORE_RELEASE "dt-module-header-ignore-release"
117
123
124static gboolean _iop_plugin_focus_accel(GtkAccelGroup *accel_group, GObject *accelerable, guint keyval,
125 GdkModifierType modifier, gpointer data);
126static gboolean _iop_plugin_enable_accel(GtkAccelGroup *accel_group, GObject *accelerable, guint keyval,
127 GdkModifierType modifier, gpointer data);
128static gboolean _iop_plugin_header_button_release(GtkWidget *w, GdkEventButton *e, gpointer user_data);
129static void _gui_set_single_expanded(dt_iop_module_t *module, gboolean expanded);
130
131float dt_dev_get_module_scale(const dt_dev_pixelpipe_t *const pipe, const dt_iop_roi_t *const roi_in)
132{
133 return pipe->iscale / roi_in->scale;
134}
135
136uint32_t dt_dev_get_roi_filters(const dt_dev_pixelpipe_iop_t *const piece, const dt_iop_roi_t *const roi_in)
137{
138 return dt_rawspeed_crop_dcraw_filters(piece->dsc_in.filters, roi_in->x, roi_in->y);
139}
140
141
151
152static void _iop_modify_roi_in(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe,
153 struct dt_dev_pixelpipe_iop_t *piece,
154 const dt_iop_roi_t *roi_out, dt_iop_roi_t *roi_in)
155{
156 *roi_in = *roi_out;
157}
158
159static void _iop_modify_roi_out(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe,
160 struct dt_dev_pixelpipe_iop_t *piece,
161 dt_iop_roi_t *roi_out, const dt_iop_roi_t *roi_in)
162{
163 *roi_out = *roi_in;
164}
165
166/* default group for modules which do not implement the default_group() function */
167static int default_default_group(void)
168{
169 return IOP_GROUP_TECHNICAL;
170}
171
172/* default flags for modules which does not implement the flags() function */
173static int default_flags(void)
174{
175 return 0;
176}
177
178/* default operation tags for modules which does not implement the flags() function */
180{
181 return 0;
182}
183
184/* default operation tags filter for modules which does not implement the flags() function */
186{
187 return 0;
188}
189
190static const char **default_description(struct dt_iop_module_t *self)
191{
192 return NULL;
193}
194
195static const char *default_aliases(void)
196{
197 return "";
198}
199
200static const char *default_deprecated_msg(void)
201{
202 return NULL;
203}
204
205static gboolean default_has_defaults(struct dt_iop_module_t *self)
206{
207 return memcmp(self->params, self->default_params, self->params_size) == 0;
208}
209
211 const dt_dev_pixelpipe_iop_t *piece)
212{
213 (void)self;
214 (void)pipe;
215 (void)piece;
216 return FALSE;
217}
218
219static void default_commit_params(struct dt_iop_module_t *self, dt_iop_params_t *params,
221{
222 memcpy(piece->data, params, self->params_size);
223}
224
225// WARNING: this works only if the data struct has the same size
226// as the param structure. You need to implement your own IOP module
227// method if they don't match !!!
230{
231 size_t data_size = (size_t)self->params_size;
232 piece->data = dt_calloc_align(data_size);
233 piece->data_size = data_size;
234}
235
238{
239 dt_free_align(piece->data);
240 piece->data = NULL;
241}
242
244{
246}
247
249{
250 dt_free(module->params);
251 dt_free(module->default_params);
252}
253
254static void _iop_color_picker_data_ready_callback(gpointer instance, gpointer user_data)
255{
256 dt_iop_module_t *const module = user_data;
257 GtkWidget *picker = NULL;
258 dt_dev_pixelpipe_t *pipe = NULL;
259 const dt_dev_pixelpipe_iop_t *piece = NULL;
260 if(IS_NULL_PTR(module) || IS_NULL_PTR(module->color_picker_apply)) return;
261 if(dt_iop_color_picker_get_ready_data(module, &picker, &pipe, &piece)) return;
262
263 dt_print(DT_DEBUG_DEV, "[picker] dispatch module=%s picker=%p pipe=%p hash=%" PRIu64 "\n",
264 module->op, (void *)picker, (void *)pipe, piece ? piece->global_hash : 0);
265
266 if(!module->blend_data || !blend_color_picker_apply(module, picker, pipe, (dt_dev_pixelpipe_iop_t *)piece))
267 module->color_picker_apply(module, picker, pipe, (dt_dev_pixelpipe_iop_t *)piece);
268}
269
270
272 const dt_dev_pixelpipe_iop_t *piece, float *points,
273 size_t points_count)
274{
275 (void)self;
276 (void)pipe;
277 (void)piece;
278 (void)points;
279 (void)points_count;
280 return 1;
281}
283 const dt_dev_pixelpipe_iop_t *piece, float *points,
284 size_t points_count)
285{
286 (void)self;
287 (void)pipe;
288 (void)piece;
289 (void)points;
290 (void)points_count;
291 return 1;
292}
293
294static int default_process(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe,
295 const struct dt_dev_pixelpipe_iop_t *piece,
296 const void *const i, void *const o)
297{
298 const struct dt_iop_roi_t *const roi_in = &piece->roi_in;
299 const struct dt_iop_roi_t *const roi_out = &piece->roi_out;
300 if(roi_in->width <= 1 || roi_in->height <= 1 || roi_out->width <= 1 || roi_out->height <= 1) return 0;
301 return self->process_plain(self, pipe, piece, i, o);
302}
303
309{
310 return NULL;
311}
312static void *default_get_p(const void *param, const char *name)
313{
314 return NULL;
315}
317{
318 return NULL;
319}
320
322{
323 size_t param_size = module->so->get_introspection()->size;
324 module->params_size = param_size;
325 /* Keep ownership explicit: if init is re-entered on the same instance,
326 * release previous params before rebuilding defaults from introspection. */
327 if(!IS_NULL_PTR(module->params))
328 {
329 dt_free(module->params);
330 module->params = NULL;
331 }
332 if(!IS_NULL_PTR(module->default_params))
333 {
334 dt_free(module->default_params);
335 module->default_params = NULL;
336 }
337 module->params = (dt_iop_params_t *)calloc(1, param_size);
338 module->default_params = (dt_iop_params_t *)calloc(1, param_size);
339
340 module->default_enabled = 0;
341 module->gui_data = NULL;
342
343 dt_introspection_field_t *i = module->so->get_introspection_linear();
344 while(i->header.type != DT_INTROSPECTION_TYPE_NONE)
345 {
346 switch(i->header.type)
347 {
349 *(float*)((uint8_t *)module->default_params + i->header.offset) = i->Float.Default;
350 break;
352 *(int*)((uint8_t *)module->default_params + i->header.offset) = i->Int.Default;
353 break;
355 *(unsigned int*)((uint8_t *)module->default_params + i->header.offset) = i->UInt.Default;
356 break;
358 *(unsigned short*)((uint8_t *)module->default_params + i->header.offset) = i->UShort.Default;
359 break;
361 *(int*)((uint8_t *)module->default_params + i->header.offset) = i->Enum.Default;
362 break;
364 *(gboolean*)((uint8_t *)module->default_params + i->header.offset) = i->Bool.Default;
365 break;
367 *(char*)((uint8_t *)module->default_params + i->header.offset) = i->Char.Default;
368 break;
370 memset((uint8_t *)module->default_params + i->header.offset, 0, i->header.size);
371 break;
373 {
374 if(i->Array.type == DT_INTROSPECTION_TYPE_CHAR) break;
375
376 size_t element_size = i->Array.field->header.size;
377 if(element_size % sizeof(int))
378 {
379 int8_t *p = (int8_t *)module->default_params + i->header.offset;
380 for (size_t c = element_size; c < i->header.size; c++, p++)
381 p[element_size] = *p;
382 }
383 else
384 {
385 element_size /= sizeof(int);
386 size_t num_ints = i->header.size / sizeof(int);
387
388 int *p = (int *)((uint8_t *)module->default_params + i->header.offset);
389 for (size_t c = element_size; c < num_ints; c++, p++)
390 p[element_size] = *p;
391 }
392 }
393 break;
395 // ignore STRUCT; nothing to do
396 break;
397 default:
398 fprintf(stderr, "unsupported introspection type \"%s\" encountered in dt_iop_default_init (field %s)\n", i->header.type_name, i->header.field_name);
399 break;
400 }
401
402 i++;
403 }
404}
405
406int default_iop_focus(dt_gui_module_t *m, gboolean toggle)
407{
408 dt_iop_module_t *module = (dt_iop_module_t *) m;
409
410 // Expand and scroll
412 {
415 }
416 else if(toggle)
417 {
421 }
422
423 return 1;
424}
425
426int dt_iop_load_module_so(void *m, const char *libname, const char *module_name)
427{
428 dt_iop_module_so_t *module = (dt_iop_module_so_t *)m;
429 g_strlcpy(module->op, module_name, sizeof(module->op));
430
431#define INCLUDE_API_FROM_MODULE_LOAD "iop_load_module"
432#include "iop/iop_api.h"
433
434 if(IS_NULL_PTR(module->init)) module->init = dt_iop_default_init;
435 if(IS_NULL_PTR(module->modify_roi_in)) module->modify_roi_in = _iop_modify_roi_in;
436 if(IS_NULL_PTR(module->modify_roi_out)) module->modify_roi_out = _iop_modify_roi_out;
437
438 #ifdef HAVE_OPENCL
439 if(IS_NULL_PTR(module->process_tiling_cl)) module->process_tiling_cl = darktable.opencl->inited ? default_process_tiling_cl : NULL;
440 if(!darktable.opencl->inited) module->process_cl = NULL;
441 #endif // HAVE_OPENCL
442
443 module->process_plain = module->process;
444 module->process = default_process;
445
446 module->data = NULL;
447
448 // the introspection api
449 module->have_introspection = FALSE;
450 if(module->introspection_init)
451 {
452 if(!module->introspection_init(module, DT_INTROSPECTION_VERSION))
453 {
454 // set the introspection related fields in module
455 module->have_introspection = TRUE;
456
457 if(module->get_p == default_get_p ||
458 module->get_f == default_get_f ||
459 module->get_introspection_linear == default_get_introspection_linear ||
460 module->get_introspection == default_get_introspection)
461 goto api_h_error;
462 }
463 else
464 fprintf(stderr, "[iop_load_module] failed to initialize introspection for operation `%s'\n", module_name);
465 }
466
467 if(module->init_global) module->init_global(module);
468 return 0;
469}
470
472{
473 module->dev = dev;
474 module->widget = NULL;
475 module->header = NULL;
476 module->off = NULL;
477 module->hide_enable_button = 0;
478 module->request_color_pick = DT_REQUEST_COLORPICK_OFF;
479 module->request_histogram = DT_REQUEST_ONLY_IN_GUI;
480 module->histogram_stats.bins_count = 0;
481 module->histogram_stats.pixels = 0;
482 module->multi_priority = 0;
483 module->iop_order = 0;
484 for(int k = 0; k < 3; k++)
485 {
486 module->picked_color[k] = module->picked_output_color[k] = 0.0f;
487 module->picked_color_min[k] = module->picked_output_color_min[k] = 666.0f;
488 module->picked_color_max[k] = module->picked_output_color_max[k] = -666.0f;
489 }
490 module->histogram_cst = IOP_CS_NONE;
491 module->histogram = NULL;
492 module->histogram_max[0] = module->histogram_max[1] = module->histogram_max[2] = module->histogram_max[3]
493 = 0;
494 module->histogram_middle_grey = FALSE;
495 module->request_mask_display = DT_DEV_PIXELPIPE_DISPLAY_NONE;
496 module->bypass_cache = FALSE;
497 module->bypass_cache_variant = 0;
498 module->enabled = module->default_enabled = module->workflow_enabled = 0; // all modules disabled by default.
499 g_strlcpy(module->op, so->op, 20);
500 module->raster_mask.source.users = g_hash_table_new(NULL, NULL);
501 module->raster_mask.source.masks = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, dt_free_gpointer);
502 module->raster_mask.sink.source = NULL;
503 module->raster_mask.sink.id = 0;
504
505 // only reference cached results of dlopen:
506 module->module = so->module;
507 module->so = so;
508
509#define INCLUDE_API_FROM_MODULE_LOAD_BY_SO
510#include "iop/iop_api.h"
511
512 module->version = so->version;
513 module->process_plain = so->process_plain;
514 module->have_introspection = so->have_introspection;
515
516 module->reset_button = NULL;
517 module->presets_button = NULL;
518 module->fusion_slider = NULL;
519
520 module->global_data = so->data;
521
522 // now init the instance:
523 module->init(module);
524 module->hash = DT_PIXELPIPE_CACHE_HASH_INVALID;
525 module->blendop_hash = DT_PIXELPIPE_CACHE_HASH_INVALID;
526
527 if(module->params_size == 0)
528 {
529 fprintf(stderr, "[iop_load_module] `%s' needs to have a params size > 0!\n", so->op);
530 return 1; // empty params hurt us in many places, just add a dummy value
531 }
532
533 /* Allocate params only when module init did not allocate them already.
534 * Some init paths (notably default_init) already own the params buffers. */
535 if(IS_NULL_PTR(module->params))
536 module->params = calloc(1, module->params_size);
537 if(IS_NULL_PTR(module->default_params))
538 module->default_params = calloc(1, module->params_size);
539 module->blend_params = calloc(1, sizeof(dt_develop_blend_params_t));
540 module->default_blendop_params = calloc(1, sizeof(dt_develop_blend_params_t));
541
542 // Don't init defaults here, it's done when reading/initing history
543
544 /* pass on the dt_gui_module_t args for bauhaus widgets
545 * only when a GUI lifetime exists for this module instance. */
546 if(IS_NULL_PTR(module->dev) || module->dev->gui_attached)
547 {
548 module->common_fields.name = delete_underscore(module->name());
549 module->common_fields.view = g_strdup(_("Darkroom")); // IOP modules belong necessarily to darkroom
550 }
551 else
552 {
553 module->common_fields.name = NULL;
554 module->common_fields.view = NULL;
555 }
556 module->common_fields.widget_list = NULL;
557 module->common_fields.widget_list_bh = NULL;
558 module->common_fields.focus = module->iop_focus;
559 module->common_fields.deprecated = (module->flags() & IOP_FLAGS_DEPRECATED) == IOP_FLAGS_DEPRECATED;
560
561 return 0;
562}
563
565 struct dt_dev_pixelpipe_iop_t *piece)
566{
567 module->init_pipe(module, pipe, piece);
569}
570
583 struct dt_dev_pixelpipe_iop_t *piece)
584{
585 if(IS_NULL_PTR(piece)) return;
586
588 {
590 "[dt_iop_cleanup_pipe] missing module, skipping module pipe cleanup\n");
591 }
592 else if(IS_NULL_PTR(pipe))
593 {
595 "[dt_iop_cleanup_pipe] missing pipe for `%s`, skipping module pipe cleanup\n",
596 module->op);
597 }
598 else
599 {
600 dt_iop_module_so_t *module_so = module->so;
601 gboolean module_so_loaded = FALSE;
602
603 /* Search the process-owned descriptor list by address before reading the
604 * candidate. A removed instance may outlive its GUI, but its descriptor
605 * must remain one of the objects loaded at startup. */
606 for(GList *iop = g_list_first(darktable.iop); iop; iop = g_list_next(iop))
607 {
608 if(iop->data == module_so)
609 {
611 break;
612 }
613 }
614
617 "[dt_iop_cleanup_pipe] invalid shared-object descriptor %p, skipping module pipe cleanup\n",
618 (void *)module_so);
619 else if(module->cleanup_pipe != module_so->cleanup_pipe)
621 "[dt_iop_cleanup_pipe] stale cleanup callback for `%s`, skipping module pipe cleanup\n",
622 module_so->op);
623 else
624 module_so->cleanup_pipe(module, pipe, piece);
625 }
626
627 if(!IS_NULL_PTR(piece->blendop_data))
628 {
630 piece->blendop_data = NULL;
631 }
632}
633
634static void _gui_delete_callback(GtkButton *button, dt_iop_module_t *module)
635{
636 dt_develop_t *dev = module->dev;
637
638 // we search another module with the same base
639 // we want the next module if any or the previous one
640 GList *modules = module->dev->iop;
641 dt_iop_module_t *next = NULL;
642 int find = 0;
643 while(modules)
644 {
646 if(mod == module)
647 {
648 find = 1;
649 if(next) break;
650 }
651 else if(mod->instance == module->instance)
652 {
653 next = mod;
654 if(find) break;
655 }
657 }
658 if(IS_NULL_PTR(next)) return; // what happened ???
659
660 if(module->module_will_remove && !module->module_will_remove(module)) return;
661
663
664 // we must pay attention if priority is 0
665 const gboolean is_zero = (module->multi_priority == 0);
666
667 // We are about to destroy this module GUI. Drop darkroom focus first so the
668 // center expose callback cannot call module->gui_post_expose() with a stale
669 // module->gui_data pointer during teardown-triggered redraws.
671
673
674 // we remove the plugin effectively
676 {
677 // we just hide the module to avoid lots of gtk critical warnings
678 gtk_widget_hide(module->expander);
679
682 }
683
684 // we remove all references in the history stack and dev->iop
685 // this will inform that a module has been removed from history
686 // we do it here so we have the multi_priorities to reconstruct
687 // de deleted module if the user undo it
689
690 // if module was priority 0, then we set next to priority 0
691 if(is_zero)
692 {
693 // we want the first one in history
694 dt_iop_module_t *first = NULL;
695 GList *history = dev->history;
696 while(history)
697 {
698 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
699 if(!hist || !hist->module)
700 {
701 history = g_list_next(history);
702 continue;
703 }
704 if(hist->module->instance == module->instance && hist->module != module)
705 {
706 first = hist->module;
707 break;
708 }
709 history = g_list_next(history);
710 }
711 if(IS_NULL_PTR(first)) first = next;
712
713 // we set priority of first to 0
715
716 // we change this in the history stack too
717 for(history = dev->history; history; history = g_list_next(history))
718 {
719 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
720 if(hist->module == first) hist->multi_priority = 0;
721 }
722 }
723
724 // Commit undo snapshot for the whole delete operation (module removal + multi_priority adjustments).
726
727 // Save history
729
730 // don't delete the module, a pipe may still need it
731 dev->alliop = g_list_append(dev->alliop, module);
732
733 /* redraw */
736
738}
739
741{
742 GtkWidget *expander = module->expander;
743 return (expander && gtk_widget_is_visible(expander) && !dt_iop_is_hidden(module));
744}
745
747 gboolean enable, gboolean write_history, const char *reason)
748{
749 if(IS_NULL_PTR(dev)) return FALSE;
750
753
754 if(!IS_NULL_PTR(reason)) dt_ioppr_check_iop_order(dev, 0, reason);
755
757 return TRUE;
758}
759
766
773
775{
776 dt_iop_module_t *prev = NULL;
778 {
780 if(mod == module)
781 break;
783 prev = mod;
784 }
785 return prev;
786}
787
789{
790 dt_iop_module_t *next = NULL;
792 {
794 if(mod == module)
795 break;
797 next = mod;
798 }
799 return next;
800}
801
803{
804 // make sure the duplicated module appears in the history
805 dt_dev_add_history_item(base->dev, base, FALSE, FALSE);
806
807 // first we create the new module
809 dt_iop_module_t *module = dt_dev_module_duplicate(base->dev, base);
811 if(IS_NULL_PTR(module)) return NULL;
812
813 // we set the gui part of it
814 /* initialize gui if iop have one defined */
816 {
817 // make sure gui_init and reload defaults is called safely
819
820 /* add module to right panel */
822 darktable.gui->scroll_to_header_once = module->expander;
823
824 dt_iop_reload_defaults(module); // some modules like profiled denoise update the gui in reload_defaults
825
826 if(copy_params)
827 {
828 memcpy(module->params, base->params, module->params_size);
830 {
832 if(base->blend_params->mask_id > 0)
833 {
834 module->blend_params->mask_id = 0;
836 }
837 }
838 }
839
842 if(base != module && !IS_NULL_PTR(base->expander)) _gui_set_single_expanded(base, FALSE);
844
845 if(module->dev->gui_attached)
847
848 // we save the new instance creation
850 }
851
852 /* update ui to new parameters */
854
855 return module;
856}
857
859
868static gboolean _rename_module_idle(gpointer user_data)
869{
870 dt_iop_module_t *module = (dt_iop_module_t *)user_data;
872
873 return G_SOURCE_REMOVE;
874}
875
876static void _gui_copy_callback(GtkButton *button, gpointer user_data)
877{
878 dt_iop_module_t *module = dt_iop_gui_duplicate(user_data, FALSE);
879 if(IS_NULL_PTR(module)) return;
880
882}
883
884static void _gui_duplicate_callback(GtkButton *button, gpointer user_data)
885{
886 dt_iop_module_t *module = dt_iop_gui_duplicate(user_data, TRUE);
887 if(IS_NULL_PTR(module)) return;
888
890}
891
893{
894 int ended = 0;
895 guint key = dt_keys_mainpad_alternatives(event->keyval);
896
897 if(event->type == GDK_FOCUS_CHANGE || key == GDK_KEY_Return)
898 {
900 {
901 // name is not empty, set new multi_name
902
903 const gchar *name = gtk_entry_get_text(GTK_ENTRY(entry));
904
905 // restore saved 1st character of instance name (without it the same name wouls still produce unnecessary copy + add history item)
906 module->multi_name[0] = module->multi_name[sizeof(module->multi_name) - 1];
907 module->multi_name[sizeof(module->multi_name) - 1] = 0;
908
909 if(g_strcmp0(module->multi_name, name) != 0)
910 {
911 g_strlcpy(module->multi_name, name, sizeof(module->multi_name));
913 }
914 }
915 else
916 {
917 // clear out multi-name (set 1st char to 0)
918 module->multi_name[0] = 0;
920 }
921
922 ended = 1;
923 }
924 else if(key == GDK_KEY_Escape)
925 {
926 // restore saved 1st character of instance name
927 module->multi_name[0] = module->multi_name[sizeof(module->multi_name) - 1];
928 module->multi_name[sizeof(module->multi_name) - 1] = 0;
929
930 ended = 1;
931 }
932
933 if(ended)
934 {
936 gtk_widget_destroy(entry);
939 return TRUE;
940 }
941
942 return FALSE; /* event not handled */
943}
944
946{
947 int width = 0;
948 GtkBorder padding;
949
953 &padding);
954 gtk_widget_set_size_request(entry, width + padding.left + padding.right + 1, -1);
955
956 return TRUE;
957}
958
960{
962 if(focused && GTK_IS_ENTRY(focused)) return;
963
964 GtkWidget *entry = gtk_entry_new();
966
967 gtk_widget_set_name(entry, "iop-panel-label");
969 gtk_entry_set_max_length(GTK_ENTRY(entry), sizeof(module->multi_name) - 1);
970 gtk_entry_set_text(GTK_ENTRY(entry), module->multi_name);
971
972 // remove instance name but save 1st character in case of escape
973 module->multi_name[sizeof(module->multi_name) - 1] = module->multi_name[0];
974 module->multi_name[0] = 0;
976
978 g_signal_connect(entry, "key-press-event", G_CALLBACK(_rename_module_key_press), module);
979 g_signal_connect(entry, "focus-out-event", G_CALLBACK(_rename_module_key_press), module);
980 g_signal_connect(entry, "style-updated", G_CALLBACK(_rename_module_resize), module);
982
983 gtk_box_pack_start(GTK_BOX(module->header), entry, TRUE, TRUE, 0);
984 gtk_widget_show(entry);
986}
987
988static void _gui_rename_callback(GtkButton *button, dt_iop_module_t *module)
989{
991}
992
993static gboolean _iop_plugin_header_menu_dismiss_idle(gpointer user_data)
994{
995 GtkWidget *expander = GTK_WIDGET(user_data);
996 if(GTK_IS_WIDGET(expander))
998
999 g_object_unref(expander);
1000 return G_SOURCE_REMOVE;
1001}
1002
1003static void _iop_plugin_header_menu_deactivate(GtkWidget *menu, gpointer user_data)
1004{
1005 GtkWidget *expander = GTK_WIDGET(user_data);
1006 if(!GTK_IS_WIDGET(expander)) return;
1007
1017}
1018
1019static gboolean _gui_multiinstance_callback(GtkButton *button, GdkEventButton *event, gpointer user_data)
1020{
1021 dt_iop_module_t *module = (dt_iop_module_t *)user_data;
1022
1023 if(!IS_NULL_PTR(event) && event->button == 3)
1024 {
1025 if(!(module->flags() & IOP_FLAGS_ONE_INSTANCE)) _gui_copy_callback(button, user_data);
1026 return TRUE;
1027 }
1028 else if(!IS_NULL_PTR(event) && event->button == 2)
1029 {
1030 return FALSE;
1031 }
1032
1034 GtkWidget *item;
1035
1036 item = gtk_menu_item_new_with_label(_("new instance"));
1037 // gtk_widget_set_tooltip_text(item, _("add a new instance of this module to the pipe"));
1039 gtk_widget_set_sensitive(item, module->multi_show_new);
1040 gtk_menu_shell_append(menu, item);
1041
1042 item = gtk_menu_item_new_with_label(_("duplicate instance"));
1043 // gtk_widget_set_tooltip_text(item, _("add a copy of this instance to the pipe"));
1045 gtk_widget_set_sensitive(item, module->multi_show_new);
1046 gtk_menu_shell_append(menu, item);
1047
1048 item = gtk_menu_item_new_with_label(_("delete"));
1049 // gtk_widget_set_tooltip_text(item, _("delete this instance"));
1051 gtk_widget_set_sensitive(item, module->multi_show_close);
1052 gtk_menu_shell_append(menu, item);
1053
1055
1056 item = gtk_menu_item_new_with_label(_("rename"));
1058 gtk_menu_shell_append(menu, item);
1059
1060 if(!IS_NULL_PTR(module->expander))
1061 {
1063 g_signal_connect_data(G_OBJECT(menu), "deactivate",
1066 }
1067
1069
1070 // make sure the button is deactivated now that the menu is opened
1071 if(button) dtgtk_button_set_active(DTGTK_BUTTON(button), FALSE);
1072 return TRUE;
1073}
1074
1075static void _gui_off_callback(GtkToggleButton *togglebutton, gpointer user_data)
1076{
1077 dt_iop_module_t *module = (dt_iop_module_t *)user_data;
1078
1080 {
1089
1091 {
1092 module->enabled = 1;
1096 }
1097 else
1098 {
1099 module->enabled = 0;
1100
1101 // if current module is set as the CAT instance, remove that setting
1102 if(module->dev->proxy.chroma_adaptation == module)
1103 module->dev->proxy.chroma_adaptation = NULL;
1104
1106 }
1107 }
1108
1109 char tooltip[512];
1111 snprintf(tooltip, sizeof(tooltip), module->enabled ? _("%s is switched on") : _("%s is switched off"),
1112 module_label);
1116}
1117
1119{
1120 gboolean is_hidden = TRUE;
1121 if(!(module->flags() & IOP_FLAGS_HIDDEN))
1122 {
1123 if(IS_NULL_PTR(module->gui_init))
1124 g_debug("Module '%s' is not hidden and lacks implementation of gui_init()...", module->op);
1125 else if(!module->gui_cleanup)
1126 g_debug("Module '%s' is not hidden and lacks implementation of gui_cleanup()...", module->op);
1127 else
1128 is_hidden = FALSE;
1129 }
1130 return is_hidden;
1131}
1132
1134{
1135 return dt_iop_so_is_hidden(module->so);
1136}
1137
1139{
1140 return !dt_iop_is_hidden(module) && !IS_NULL_PTR(module->expander) && gtk_widget_is_visible(module->expander);
1141}
1142
1144{
1146 lab = gtk_bin_get_child(GTK_BIN(lab));
1147 gtk_widget_set_name(lab, "iop-panel-label");
1148
1149 char *module_name = dt_history_item_get_label(module);
1150 dt_capitalize_label(module_name);
1152 dt_free(module_name);
1153
1154 // Module name hasn't changed or no instance name: abort now
1155 if(!g_strcmp0(module_name, gtk_label_get_text(GTK_LABEL(lab))) || module->multi_name[0] == '\0')
1156 return;
1157
1159 if(mod->instance_name)
1160 {
1161 char *instance_path = dt_accels_build_path(_("Darkroom/Modules/Instances"), mod->instance_name);
1164 dt_free(mod->instance_name);
1165 }
1166
1167 gchar *clean_name = delete_underscore(module->name());
1169
1170 mod->instance_name
1171 = g_strdup_printf("%s/%s", clean_name, (module->multi_name[0] != '\0') ? module->multi_name : "0");
1172
1174 darktable.gui->accels->darkroom_accels, _("Darkroom/Modules/Instances"),
1175 mod->instance_name);
1176
1178
1180 g_object_set(G_OBJECT(lab), "xalign", 0.0, (gchar *)0);
1181}
1182
1184{
1185 if (IS_NULL_PTR(module->header)) /* some modules such as overexposed don't actually have a header */
1186 return;
1187
1188 // set panel name to display correct multi-instance
1192}
1193
1195{
1196 // set on/off icon
1197 if(module->default_enabled && module->hide_enable_button)
1198 {
1200 }
1201 else if(!module->default_enabled && module->hide_enable_button)
1202 {
1204 }
1205 else
1206 {
1208 }
1209}
1210
1212{
1213 if(IS_NULL_PTR(module)) return;
1214
1215 if(module->off)
1216 {
1218 if(module->hide_enable_button)
1220 else
1222
1223 dt_gui_remove_class(GTK_WIDGET(module->off), "dt_iop_enable_forced_on");
1224 dt_gui_remove_class(GTK_WIDGET(module->off), "dt_iop_enable_forced_off");
1225 if(module->hide_enable_button)
1226 {
1228 module->enabled ? "dt_iop_enable_forced_on" : "dt_iop_enable_forced_off");
1229 }
1230
1232 }
1233}
1234
1236{
1237 // Suppress widget value-changed callbacks for the whole GUI build. Setting a slider's soft
1238 // range (etc.) in gui_init emits "value-changed", which would re-enter the module's
1239 // gui_changed handler before its sibling widgets exist and crash on dt_bauhaus_*(NULL)
1240 // (Sentry #129494618, #129578628). The scope guard releases the freeze automatically on every
1241 // exit path, and the central depth is GUI-thread-only and self-healing, so it cannot drift.
1243
1244 // Add the accelerators
1245 if(!dt_iop_is_hidden(module) && !(module->flags() & IOP_FLAGS_DEPRECATED))
1246 {
1247 gchar *clean_name = delete_underscore(module->name());
1249
1250 // slash is not allowed in module names because that makes accel pathes fail
1251 assert(g_strrstr(clean_name, "/") == NULL);
1252
1254 const gchar *const main_scope = _("Darkroom/Modules");
1256
1258
1259 // NOTE: we should enable the accel only if the module is disable-able, but this property is set at runtime
1260 // in reload_defaults(), which depends on the image metadata for each pipeline.
1261 // We have no way of knowing here at init time.
1262 // if(!module->hide_enable_button)
1263 dt_accels_new_darkroom_action(_iop_plugin_enable_accel, module, mod->accel_path, _("Enable"), 0, 0, _("Enables the module"));
1264
1266 }
1267
1268 // We absolutely need to init the module controls after the module object
1269 if(module->gui_init) module->gui_init(module);
1270 if(module->color_picker_apply)
1271 {
1274 }
1275 // the freeze ends here as the scope guard goes out of scope
1276}
1277
1279{
1280 // Suppress GUI callbacks while a module's reload_defaults() rewrites widget defaults. This is
1281 // a no-op off the GUI thread (worker-thread thumbnail/export devs have no widgets, so they
1282 // must never touch -- and race/drift -- the shared depth); the central API enforces that.
1283 // The previous root cause of NULL-bauhaus-widget crashes was exactly this counter drifting
1284 // (Sentry #129494618, #129578628, #129908540). Scope the freeze to the params work so the
1285 // header update below still runs unsuppressed, exactly as before.
1286 {
1288
1289 if(module->reload_defaults)
1290 {
1291 // report if reload_defaults was called unnecessarily => this should be considered a bug
1292 // the whole point of reload_defaults is to update defaults _based on current image_
1293 // any required initialisation should go in init (and not be performed repeatedly here)
1294 if(module->dev)
1295 {
1296 module->reload_defaults(module);
1297 dt_print(DT_DEBUG_PARAMS, "[params] defaults reloaded for %s\n", module->op);
1298 }
1299 else
1300 {
1301 fprintf(stderr, "reload_defaults should not be called without image.\n");
1302 }
1303 }
1305 }
1306
1308}
1309
1310
1312{
1313 // Skip auto-preset regeneration when the build + UI language haven't changed.
1314 if(module_so->init_presets && dt_gui_presets_autogen_enabled())
1315 module_so->init_presets(module_so);
1316
1317 // this seems like a reasonable place to check for and update legacy
1318 // presets.
1319
1320 const int32_t module_version = module_so->version();
1322 {
1325 "SELECT name, op_version, op_params, blendop_version, blendop_params FROM data.presets WHERE operation = ?1",
1327 }
1332
1333 while(sqlite3_step(stmt) == SQLITE_ROW)
1334 {
1335 const char *name = (char *)sqlite3_column_text(stmt, 0);
1337 const void *old_params = (void *)sqlite3_column_blob(stmt, 2);
1338 const int32_t old_params_size = sqlite3_column_bytes(stmt, 2);
1340 const void *old_blend_params = (void *)sqlite3_column_blob(stmt, 4);
1342
1343 if(old_params_version == 0)
1344 {
1345 // this preset doesn't have a version. go digging through the database
1346 // to find a history entry that matches the preset params, and get
1347 // the module version from that.
1348
1351 "SELECT module FROM main.history WHERE operation = ?1 AND op_params = ?2", -1,
1352 &stmt2, NULL);
1355
1357 {
1359 }
1360 else
1361 {
1362 fprintf(stderr, "[imageop_init_presets] WARNING: Could not find versioning information for '%s' "
1363 "preset '%s'\nUntil some is found, the preset will be unavailable.\n(To make it "
1364 "return, please load an image that uses the preset.)\n",
1365 module_so->op, name);
1367 continue;
1368 }
1369
1371
1372 // we found an old params version. Update the database with it.
1373
1374 fprintf(stderr, "[imageop_init_presets] Found version %d for '%s' preset '%s'\n", old_params_version,
1375 module_so->op, name);
1376
1378 "UPDATE data.presets SET op_version=?1 WHERE operation=?2 AND name=?3", -1,
1379 &stmt2, NULL);
1383
1386 }
1387
1388 if(module_version > old_params_version && !IS_NULL_PTR(module_so->legacy_params))
1389 {
1390 // we need a dt_iop_module_t for legacy_params()
1391 dt_iop_module_t *module;
1392 module = (dt_iop_module_t *)calloc(1, sizeof(dt_iop_module_t));
1394 {
1395 dt_free(module);
1396 continue;
1397 }
1398/*
1399 module->init(module);
1400 if(module->params_size == 0)
1401 {
1402 dt_iop_cleanup_module(module);
1403 dt_free(module);
1404 continue;
1405 }
1406 // we call reload_defaults() in case the module defines it
1407 if(module->reload_defaults) module->reload_defaults(module); // why not call dt_iop_reload_defaults? (if needed at all)
1408*/
1409
1410 const int32_t new_params_size = module->params_size;
1411 void *new_params = calloc(1, new_params_size);
1412
1413 // convert the old params to new
1414 if(module->legacy_params(module, old_params, old_params_version, new_params, module_version))
1415 {
1416 dt_free(new_params);
1418 dt_free(module);
1419 continue;
1420 }
1421
1422 fprintf(stderr, "[imageop_init_presets] updating '%s' preset '%s' from version %d to version %d\nto:'%s'",
1423 module_so->op, name, old_params_version, module_version,
1425
1426 // and write the new params back to the database
1428 // clang-format off
1430 "SET op_version=?1, op_params=?2 "
1431 "WHERE operation=?3 AND name=?4",
1432 -1, &stmt2, NULL);
1433 // clang-format on
1434 DT_DEBUG_SQLITE3_BIND_INT(stmt2, 1, module->version());
1438
1441
1442 dt_free(new_params);
1444 dt_free(module);
1445 }
1446 else if(module_version > old_params_version)
1447 {
1448 fprintf(stderr, "[imageop_init_presets] Can't upgrade '%s' preset '%s' from version %d to %d, no "
1449 "legacy_params() implemented \n",
1450 module_so->op, name, old_params_version, module_version);
1451 }
1452
1454 {
1456 "[imageop_init_presets] updating '%s' preset '%s' from blendop version %d to version %d\n",
1458
1459 // we need a dt_iop_module_t for dt_develop_blend_legacy_params()
1460 // using dt_develop_blend_legacy_params_by_so won't help as we need "module" anyway
1461 dt_iop_module_t *module;
1462 module = (dt_iop_module_t *)calloc(1, sizeof(dt_iop_module_t));
1464 {
1465 dt_free(module);
1466 continue;
1467 }
1468
1469 if(module->params_size == 0)
1470 {
1472 dt_free(module);
1473 continue;
1474 }
1476
1477 // convert the old blend params to new
1482 {
1483 // do nothing
1484 }
1485 else
1486 {
1487 memcpy(new_blend_params, module->default_blendop_params, sizeof(dt_develop_blend_params_t));
1488 }
1489
1490 // and write the new blend params back to the database
1492 // clang-format off
1494 "SET blendop_version=?1, blendop_params=?2 "
1495 "WHERE operation=?3 AND name=?4",
1496 -1, &stmt2, NULL);
1497 // clang-format on
1503
1506
1509 dt_free(module);
1510 }
1511 }
1513}
1514
1515
1516static void _init_module_so(void *m)
1517{
1518 dt_iop_module_so_t *module = (dt_iop_module_so_t *)m;
1519
1521
1522 // do not init accelerators if there is no gui
1523 if(darktable.gui)
1524 {
1525 // create a gui and have the widgets register their accelerators
1527
1529 {
1531
1532 static gboolean blending_accels_initialized = FALSE;
1534 {
1535 dt_iop_colorspace_type_t cst = module->blend_colorspace(module_instance, NULL, NULL);
1536
1537 if((module->flags() & IOP_FLAGS_SUPPORTS_BLENDING) &&
1538 !(module->flags() & IOP_FLAGS_NO_MASKS) &&
1539 (cst == IOP_CS_LAB || dt_iop_colorspace_is_rgb(cst)))
1540 {
1543
1545 }
1546 }
1547
1549
1551
1552 }
1553
1555 }
1556}
1557
1559{
1560 // Batch presets initialization in a single transaction to avoid per-module BEGIN/COMMIT overhead.
1565}
1566
1568{
1569 memset(module, 0, sizeof(dt_iop_module_t));
1571 {
1572 dt_free(module);
1573 return 1;
1574 }
1575 return 0;
1576}
1577
1579{
1580 // Safety net: dt_iop_cleanup_module() is the one choke point every module teardown path (darkroom
1581 // leave(), studio_capture's viewer teardown, dev->alliop reclaim...) calls right before dt_free(module).
1582 // Some of those paths skip dt_iop_gui_cleanup_module() entirely (e.g. studio_capture never runs
1583 // gui_init on its modules), and dt_iop_gui_cleanup_module() itself only invokes the module's own
1584 // gui_cleanup() -- which does the DT_DEBUG_CONTROL_SIGNAL_DISCONNECT() calls -- when gui_data is
1585 // non-NULL. Any signal a module connected in gui_init() (module_moved_callback in lut3d.c,
1586 // _develop_ui_pipe_started_callback in toneequal.c...) is broadcast globally on darktable.signals,
1587 // so a single missed disconnect leaves a dangling self pointer that a later, unrelated dev's signal
1588 // raise will invoke -- SIGSEGV on the freed instance. Disconnecting everything keyed on this module's
1589 // address here, unconditionally, guarantees no module can be invoked again once freed.
1591
1592 module->cleanup(module);
1593
1594 if(!IS_NULL_PTR(module->common_fields.name))
1595 {
1596 dt_free(module->common_fields.name);
1597 module->common_fields.name = NULL;
1598 }
1599 if(!IS_NULL_PTR(module->common_fields.view))
1600 {
1601 dt_free(module->common_fields.view);
1602 module->common_fields.view = NULL;
1603 }
1604
1605 dt_free(module->blend_params);
1606 dt_free(module->default_blendop_params);
1607
1608 // don't have a picker pointing to a disappeared module
1612 {
1618 }
1619
1620 dt_free(module->histogram);
1621 g_hash_table_destroy(module->raster_mask.source.users);
1622 g_hash_table_destroy(module->raster_mask.source.masks);
1623 module->raster_mask.source.users = NULL;
1624 module->raster_mask.source.masks = NULL;
1625}
1626
1628{
1630 {
1633 }
1634
1635 while(darktable.iop)
1636 {
1637 dt_iop_module_so_t *module = (dt_iop_module_so_t *)darktable.iop->data;
1638 if(module->cleanup_global) module->cleanup_global(module);
1639 if(module->module) g_module_close(module->module);
1640 dt_free(darktable.iop->data);
1642 }
1643}
1644
1646{
1647 (void)mask_mode;
1648 static const int key = 0;
1649
1650 gboolean drawn_used = FALSE;
1651 gboolean parametric_used = FALSE;
1653
1655 {
1657 g_hash_table_insert(module->raster_mask.source.masks, GINT_TO_POINTER(key), modulename);
1658 }
1659 else
1660 {
1661 g_hash_table_remove(module->raster_mask.source.masks, GINT_TO_POINTER(key));
1662 }
1663}
1664
1666{
1667 if(IS_NULL_PTR(module)) return FALSE;
1668
1669 const gboolean mask_mode_raster = module->blend_params
1670 && ((module->blend_params->mask_mode & DEVELOP_MASK_RASTER) == DEVELOP_MASK_RASTER);
1671 const gboolean has_raster_sink = (!IS_NULL_PTR(module->raster_mask.sink.source));
1672
1674}
1675
1677{
1678 gboolean raster_used = FALSE;
1679 gboolean drawn_used = FALSE;
1680 gboolean parametric_used = FALSE;
1681 if(IS_NULL_PTR(module)) return FALSE;
1682
1683 const gboolean supports_blending
1684 = ((module->flags() & IOP_FLAGS_SUPPORTS_BLENDING) == IOP_FLAGS_SUPPORTS_BLENDING);
1685 const gboolean internal_masks = ((module->flags() & IOP_FLAGS_INTERNAL_MASKS) == IOP_FLAGS_INTERNAL_MASKS);
1686
1688 if(IS_NULL_PTR(module->blend_params)) return internal_masks;
1689
1691
1695
1697}
1698
1703
1704// make sure that blend_params are in sync with the iop struct
1706{
1707 if(module->raster_mask.sink.source)
1708 g_hash_table_remove(module->raster_mask.sink.source->raster_mask.source.users, module);
1709
1710 if(module->blend_params != blendop_params)
1711 memcpy(module->blend_params, blendop_params, sizeof(dt_develop_blend_params_t));
1712
1713 if(blendop_params->blend_cst == DEVELOP_BLEND_CS_NONE)
1714 {
1715 module->blend_params->blend_cst = dt_develop_blend_default_module_blend_colorspace(module);
1716 }
1717 dt_iop_set_mask_mode(module, blendop_params->mask_mode);
1718
1719 // This assumes that the module providing raster mask to the current one is ALWAYS
1720 // MANDATORILY before the current one BOTH in history order AND in pipe order,
1721 // because the current function is run in history order when we load/reload/pop history
1722 if(module->dev)
1723 for(GList *iter = g_list_first(module->dev->iop); iter; iter = g_list_next(iter))
1724 {
1726 if(!strcmp(m->op, blendop_params->raster_mask_source))
1727 {
1728 if(m->multi_priority == blendop_params->raster_mask_instance)
1729 {
1730 g_hash_table_insert(m->raster_mask.source.users, module, GINT_TO_POINTER(blendop_params->raster_mask_id));
1731 dt_print(DT_DEBUG_MASKS, "[raster masks] Committing raster mask from %s (%s) into %s (%s)\n", m->op, m->multi_name, module->op,
1732 module->multi_name);
1733 module->raster_mask.sink.source = m;
1734 module->raster_mask.sink.id = blendop_params->raster_mask_id;
1735 return;
1736 }
1737 }
1738 }
1739 // else if no module->dev, it means we are only loading module's .so
1740
1741 module->raster_mask.sink.source = NULL;
1742 module->raster_mask.sink.id = 0;
1743}
1744
1745gboolean _iop_validate_params(dt_introspection_field_t *field, gpointer params, gboolean report)
1746{
1747 dt_iop_params_t *p = (dt_iop_params_t *)((uint8_t *)params + field->header.offset);
1748
1749 gboolean all_ok = TRUE;
1750
1751 switch(field->header.type)
1752 {
1754 for(int i = 0; i < field->Struct.entries; i++)
1755 {
1756 dt_introspection_field_t *entry = field->Struct.fields[i];
1757
1758 all_ok &= _iop_validate_params(entry, params, report);
1759 }
1760 break;
1762 all_ok = FALSE;
1763 for(int i = field->Union.entries - 1; i >= 0 ; i--)
1764 {
1765 dt_introspection_field_t *entry = field->Union.fields[i];
1766
1767 if(_iop_validate_params(entry, params, report && i == 0))
1768 {
1769 all_ok = TRUE;
1770 break;
1771 }
1772 }
1773 break;
1776 {
1777 if(!memchr(p, '\0', field->Array.count))
1778 {
1779 if(report)
1780 fprintf(stderr, "validation check failed in _iop_validate_params for type \"%s\"; string not null terminated.\n",
1781 field->header.type_name);
1782 all_ok = FALSE;
1783 }
1784 }
1785 else
1786 {
1787 for(int i = 0, item_offset = 0; i < field->Array.count; i++, item_offset += field->Array.field->header.size)
1788 {
1789 if(!_iop_validate_params(field->Array.field, (uint8_t *)params + item_offset, report))
1790 {
1791 if(report)
1792 fprintf(stderr, "validation check failed in _iop_validate_params for type \"%s\", for array element \"%d\"\n",
1793 field->header.type_name, i);
1794 all_ok = FALSE;
1795 break;
1796 }
1797 }
1798 }
1799 break;
1801 all_ok = isnan(*(float*)p) || ((*(float*)p >= field->Float.Min && *(float*)p <= field->Float.Max));
1802 break;
1804 all_ok = (*(int*)p >= field->Int.Min && *(int*)p <= field->Int.Max);
1805 break;
1807 all_ok = (*(unsigned int*)p >= field->UInt.Min && *(unsigned int*)p <= field->UInt.Max);
1808 break;
1810 all_ok = (*(unsigned short int*)p >= field->UShort.Min && *(unsigned short int*)p <= field->UShort.Max);
1811 break;
1813 all_ok = (*(uint8_t*)p >= field->Int8.Min && *(uint8_t*)p <= field->Int8.Max);
1814 break;
1816 all_ok = (*(char*)p >= field->Char.Min && *(char*)p <= field->Char.Max);
1817 break;
1819 all_ok = creal(*(float complex*)p) >= creal(field->FloatComplex.Min) &&
1820 creal(*(float complex*)p) <= creal(field->FloatComplex.Max) &&
1821 cimag(*(float complex*)p) >= cimag(field->FloatComplex.Min) &&
1822 cimag(*(float complex*)p) <= cimag(field->FloatComplex.Max);
1823 break;
1825 all_ok = FALSE;
1826 for(dt_introspection_type_enum_tuple_t *i = field->Enum.values; i && i->name; i++)
1827 {
1828 if(i->value == *(int*)p)
1829 {
1830 all_ok = TRUE;
1831 break;
1832 }
1833 }
1834 break;
1836 // *(gboolean*)p
1837 break;
1839 // TODO: special case float2
1840 break;
1841 default:
1842 fprintf(stderr, "unsupported introspection type \"%s\" encountered in _iop_validate_params (field %s)\n",
1843 field->header.type_name, field->header.name);
1844 all_ok = FALSE;
1845 break;
1846 }
1847
1848 if(!all_ok && report)
1849 fprintf(stderr, "validation check failed in _iop_validate_params for type \"%s\"%s%s\n",
1850 field->header.type_name, (*field->header.name ? ", field: " : ""), field->header.name);
1851
1852 return all_ok;
1853}
1854
1855
1857{
1858 // Use module fingerprints to determine if two instances are actually the same
1859 return mod_1 == mod_2
1860 && mod_1->instance == mod_2->instance
1861 && mod_1->multi_priority == mod_2->multi_priority
1862 && mod_1->iop_order == mod_2->iop_order;
1863}
1864
1865
1866void _hash_raster_masks(gpointer key, gpointer value, uint64_t *hash)
1867{
1868 dt_iop_module_t *module = (dt_iop_module_t *)key;
1869
1870 // Use only "constant" module params with regard to the pipeline
1871 // init/resync aka we can't use any module pre-computed hash.
1872 *hash = dt_hash(*hash, (char *)module->op, sizeof(module->op));
1873 *hash = dt_hash(*hash, (char *)&module->iop_order, sizeof(module->iop_order));
1874 *hash = dt_hash(*hash, (char *)&module->instance, sizeof(module->instance));
1875 *hash = dt_hash(*hash, (char *)&module->multi_priority, sizeof(module->multi_priority));
1876 *hash = dt_hash(*hash, (char *)module->blend_params, sizeof(dt_develop_blend_params_t));
1877}
1878
1879
1881{
1882 // Blend params are always inited even when module doesn't support blending
1883 hash = dt_hash(hash, (char *)module->blend_params, sizeof(dt_develop_blend_params_t));
1884
1885 if(module->flags() & IOP_FLAGS_SUPPORTS_BLENDING)
1886 {
1887 // Drawn masks from dev for this module
1888 if(!IS_NULL_PTR(masks))
1889 {
1890 dt_masks_form_t *grp = dt_masks_get_from_id_ext(masks, module->blend_params->mask_id);
1891 hash = dt_masks_group_get_hash(hash, grp);
1892 }
1893
1894 // else : no module->dev when running from init_default_params()
1895
1896 // If module PROVIDES raster masks to others later in the pipe:
1897 // Account for later modules that reuse the raster mask provided by the current module.
1898 // This is a little cache invalidation trick: we change the final piece hash of this module,
1899 // to signal to the pipeline that it needs to recompute from lower than just the last changed module,
1900 // if that module references the raster mask produced here.
1901 // This contains the list of consumer modules:
1902 g_hash_table_foreach(module->raster_mask.source.users, (GHFunc)_hash_raster_masks, (gpointer)&hash);
1903
1904 // module->raster_mask.source.masks contains only one mask as of now,
1905 // aka its blendop output, so no need to iterate over that.
1906
1907 // If module CONSUMES raster masks from a module earlier in the pipe:
1908 // Account for its blendops.
1909 dt_iop_module_t *raster_source = module->raster_mask.sink.source;
1910 if(raster_source)
1911 {
1912 // Drawn masks
1913 if(!IS_NULL_PTR(masks))
1914 {
1915 dt_masks_form_t *raster_grp = dt_masks_get_from_id_ext(masks, raster_source->blend_params->mask_id);
1916 hash = dt_masks_group_get_hash(hash, raster_grp);
1917 }
1918
1919 // Blending
1920 hash = dt_hash(hash, (char *)raster_source->blend_params, sizeof(dt_develop_blend_params_t));
1921 }
1922 }
1923
1924 module->blendop_hash = hash;
1925}
1926
1927
1929{
1930 // Uniform way of getting the full state hash of user-defined parameters,
1931 // including masks and blending.
1932 // WARNING: doesn't take into account parameters dynamically set at runtime.
1933
1934 uint64_t hash = dt_hash(5381, (char *)module->op, sizeof(dt_dev_operation_t));
1935 hash = dt_hash(hash, (char *)&module->enabled, sizeof(gboolean));
1936 hash = dt_hash(hash, (char *)&module->instance, sizeof(int32_t));
1937 hash = dt_hash(hash, (char *)&module->multi_priority, sizeof(int));
1938 hash = dt_hash(hash, (char *)&module->iop_order, sizeof(int));
1939
1940 // Compute stand-alone blendop hash (mask hash) from the above
1941 // save to module->blendop_hash
1942 dt_iop_compute_blendop_hash(module, hash, masks);
1943
1944 // Finish our module-wide (output) hash
1945 hash = dt_hash(hash, (char *)module->params, module->params_size);
1946 hash = dt_hash(hash, (char *)&module->blendop_hash, sizeof(uint64_t));
1947
1948 module->hash = hash;
1949}
1950
1952 dt_develop_blend_params_t *blendop_params, dt_dev_pixelpipe_t *pipe,
1954{
1955 // We need to commit also modules that are disabled because some of them
1956 // may self-enabled at commit time, depending on image input.
1957 // 1. commit params
1958 memcpy(piece->blendop_data, blendop_params, sizeof(dt_develop_blend_params_t));
1959
1960#ifdef HAVE_OPENCL
1961 // assume process_cl is ready, commit_params can overwrite this.
1962 if(module->process_cl)
1963 piece->process_cl_ready = 1;
1964
1965 piece->cache_output_on_ram = 0;
1966#endif // HAVE_OPENCL
1967
1968 // register if module allows tiling, commit_params can overwrite this.
1969 if(module->flags() & IOP_FLAGS_ALLOW_TILING)
1970 piece->process_tiling_ready = 1;
1971
1972 if(darktable.unmuted & DT_DEBUG_PARAMS && module->so->get_introspection())
1973 _iop_validate_params(module->so->get_introspection()->field, params, TRUE);
1974
1975 module->commit_params(module, params, pipe, piece);
1976
1977 gchar *string = g_strdup_printf("/plugins/%s/opencl", module->op);
1978
1979 if(!dt_conf_key_exists(string) || !dt_conf_key_not_empty(string))
1980 dt_conf_set_bool(string, TRUE);
1981
1982 piece->process_cl_ready &= dt_conf_get_bool(string);
1983 dt_free(string);
1984
1985 //uint64_t old_hash = module->hash;
1986
1987 // 2. Update the internal hash
1988 // We need to update the blendop params dynamically, because drawn masks (forms)
1989 // belong to pipeline not to modules user params, and raster masks travel through the pipe.
1990 // So, module's blendops depend on the current and whole state of dev->forms if they use them
1992
1993 uint64_t hash = module->hash;
1994
1995 //if(old_hash != hash)
1996 // fprintf(stdout, "WARNING: hash changed at history -> pipeline commit time for %s\n", module->op);
1997
1998 /* Some modules seal part of their effective runtime contract only in commit_params(),
1999 * after reading GUI-only state, pipe mode, or other non-history inputs. Those modules
2000 * must opt in explicitly, because piece->data may also contain transient pointers or
2001 * scratch state for other modules and therefore cannot be hashed blindly. */
2002 if(module->runtime_data_hash(module, pipe, piece))
2003 {
2004 hash = dt_hash(hash, (const char *)piece->data, piece->data_size);
2005 }
2006
2007 piece->global_hash = piece->hash = hash;
2008 piece->global_mask_hash = piece->blendop_hash = module->blendop_hash;
2009
2010 dt_print(DT_DEBUG_PARAMS, "[pixelpipe] params commit for %s (%s) in pipe %s with hash %" PRIu64 "\n",
2011 module->op, module->multi_name,
2012 dt_pixelpipe_get_pipe_name(pipe->type), piece->hash);
2013}
2014
2021static void _iop_gui_widget_gone(gpointer user_data, GObject *where_the_object_was)
2022{
2023 dt_iop_module_t *module = (dt_iop_module_t *)user_data;
2024 if(IS_NULL_PTR(module)) return;
2025
2026 if(module->header == (GtkWidget *)where_the_object_was) module->header = NULL;
2027 if(module->expander == (GtkWidget *)where_the_object_was) module->expander = NULL;
2028
2029 if(IS_NULL_PTR(darktable.gui)) return;
2030
2034}
2035
2037{
2038 if(IS_NULL_PTR(module)) return;
2040 if(!IS_NULL_PTR(module->dev) && module->dev->gui_module == module) module->dev->gui_module = NULL;
2041
2042 // remove multiple delayed gtk_widget_queue_draw triggers
2043 if(module->widget)
2044 while(g_idle_remove_by_data(module->widget));
2045
2046 // Detach accels. accel_path is only ever set by dt_iop_gui_init() (never for a module that was
2047 // only backend-loaded, e.g. studio_capture's dev->iop -- see studio-capture.md): a NULL path
2048 // here means there is nothing registered to remove, and dt_accels_remove_accel() would
2049 // otherwise g_strrstr() every entry in accels->acceleratables against a NULL needle, tripping
2050 // a GLib-CRITICAL for each one.
2051 if(!dt_iop_is_hidden(module) && !(module->flags() & IOP_FLAGS_DEPRECATED) && !IS_NULL_PTR(mod->accel_path))
2052 {
2054 dt_free(mod->accel_path);
2055 }
2056
2057 if(mod->instance_name)
2058 {
2059 char *instance_path = dt_accels_build_path(_("Darkroom/Modules/Instances"), mod->instance_name);
2062 }
2063
2064 dt_free(mod->instance_name);
2065
2066 // widget_list doesn't own the widget referenced, so don't deep_free
2068 g_list_free(m->widget_list);
2069 m->widget_list = NULL;
2070 g_list_free(m->widget_list_bh);
2071 m->widget_list_bh = NULL;
2072 dt_free(m->name);
2073 m->name = NULL;
2074 dt_free(m->view);
2075 m->view = NULL;
2076
2077 if(module->color_picker_apply)
2078 {
2080 }
2081 // History refresh can delete pipeline-only modules created for ordering/history
2082 // resolution. They have a module GUI cleanup callback but no module GUI data.
2083 if(module->gui_cleanup && !IS_NULL_PTR(module->gui_data))
2084 module->gui_cleanup(module);
2086
2087 // size-allocate callbacks can still read scroll targets while GTK tears down widgets
2089 {
2090 if(darktable.gui->scroll_to[0] == module->header || darktable.gui->scroll_to[0] == module->expander)
2092 if(darktable.gui->scroll_to[1] == module->header || darktable.gui->scroll_to[1] == module->expander)
2094 if(darktable.gui->scroll_to_header_once == module->expander)
2096 }
2097
2098 /* Release the transient widget tree explicitly. In normal GUI lifetime, these
2099 * widgets are parented and get destroyed by container teardown. During module
2100 * probe/init paths, they can stay unparented and would otherwise leak. */
2101 if(!IS_NULL_PTR(module->expander) && GTK_IS_WIDGET(module->expander))
2102 {
2103 GtkWidget *expander = module->expander;
2104 g_object_ref_sink(expander);
2105 gtk_widget_destroy(expander);
2106 g_object_unref(expander);
2107 }
2108 else
2109 {
2110 if(!IS_NULL_PTR(module->header) && GTK_IS_WIDGET(module->header))
2111 {
2112 GtkWidget *header = module->header;
2113 g_object_ref_sink(header);
2114 gtk_widget_destroy(header);
2115 g_object_unref(header);
2116 }
2117
2118 if(!IS_NULL_PTR(module->widget) && GTK_IS_WIDGET(module->widget))
2119 {
2120 GtkWidget *widget = module->widget;
2121 g_object_ref_sink(widget);
2122 gtk_widget_destroy(widget);
2123 g_object_unref(widget);
2124 }
2125 }
2126
2127 module->widget = NULL;
2128 module->header = NULL;
2129 module->expander = NULL;
2130 module->off = NULL;
2131}
2132
2134{
2137 {
2138 if(module->gui_data)
2139 {
2141
2142 if(module->params && module->gui_update)
2143 module->gui_update(module);
2144
2147 }
2149 }
2151}
2152
2154{
2156 if(module->gui_reset && !dt_iop_is_hidden(module)) module->gui_reset(module);
2158}
2159
2160static void _gui_reset_callback(GtkButton *button, GdkEventButton *event, dt_iop_module_t *module)
2161{
2162 // never use the callback if module is always disabled
2163 const gboolean disabled = !module->default_enabled && module->hide_enable_button;
2164 if(disabled) return;
2165
2166 //Ctrl is used to apply any auto-presets to the current module
2167 //If Ctrl was not pressed, or no auto-presets were applied, reset the module parameters
2168 // FIXME: can we stop with all the easter-eggs key modifiers doing undocumented stuff all along ?
2170 {
2171 /* Resetting a module's parameters does not change GUI focus, so the
2172 focus-loss cleanup in dt_iop_request_focus() never runs here: an active
2173 eye-dropper (this module's own, or its blending/masking parametric-mask
2174 picker -- both share dev->color_picker) and any live mask-shape editing
2175 state (edit mode, shape buttons, mask/channel display) must be turned
2176 off explicitly, or they keep sampling/drawing on the image against
2177 parameters that no longer exist. */
2180
2181 // if a drawn mask is set, remove it from the list
2182 if(module->blend_params->mask_id > 0)
2183 {
2185 // FIXME: ask the user if he wants to delete the mask, or just unlink them.
2188 }
2189 /* reset to default params */
2191 dt_iop_commit_blend_params(module, module->default_blendop_params);
2192
2193 /* reset ui to its defaults */
2195
2196 /* update ui to default params*/
2198
2200 }
2201}
2202
2203static void _presets_popup_callback(GtkButton *button, dt_iop_module_t *module)
2204{
2205 const gboolean disabled = !module->default_enabled && module->hide_enable_button;
2206 if(disabled) return;
2207
2209
2211 {
2216 }
2217
2219}
2220
2222{
2224
2226
2227 darktable.develop->gui_module = module;
2228 if(!IS_NULL_PTR(module))
2229 {
2230 const gboolean scroll_new_instance_to_header
2231 = (darktable.gui->scroll_to_header_once == module->expander
2232 && !IS_NULL_PTR(module->header) && GTK_IS_WIDGET(module->header));
2233 darktable.gui->scroll_to[1] = scroll_new_instance_to_header ? module->header : module->expander;
2234 }
2235
2236 /* lets lose the focus of previous focus module*/
2238 {
2242 {
2245 }
2246
2247 if(out_focus_module->gui_focus)
2249
2250 /* A module that loses focus (switching to another module, or collapsing the
2251 focused module, which calls dt_iop_request_focus(NULL)) must not leave an
2252 active picker sampling/drawn on the image behind it: the picker's own
2253 "must have GUI focus" invariant only hides its on-screen overlay, it does
2254 not stop it or reset the toggle button, so a stale enabled picker can
2255 resurface unexpectedly (e.g. when the module's GUI is rebuilt). A real
2256 reset (keep = FALSE) is required here, not the "preserve across a soft
2257 refresh" keep = TRUE used by gui_update()/gui_changed() call sites. */
2260
2262
2263 /* reset mask view */
2265
2266 /* do stuff needed in the blending gui */
2268
2269 /* redraw the expander */
2271
2272 /* and finally collection restore hinter messages */
2274
2275 // we also remove the focus css class
2277 dt_gui_remove_class(iop_w, "dt_module_focus");
2278 }
2279
2280 /* set the focus on module */
2281 if(!IS_NULL_PTR(module))
2282 {
2283 // In case we tried giving focus to a module that is not in the visible tab
2285
2287
2288 if(module->gui_focus) module->gui_focus(module, TRUE);
2289
2290 /* redraw the expander */
2291 gtk_widget_queue_draw(module->expander);
2292 gtk_widget_grab_focus(module->expander);
2293
2294 /* set the focus on the first child to enable arrow-key navigation and accessibility stuff */
2295 GList *widget_list = ((dt_gui_module_t *)module)->widget_list;
2296 if(widget_list)
2297 {
2298 GList *first_child = g_list_first(widget_list);
2299 if(first_child)
2300 {
2301 GtkWidget *widget = (GtkWidget *)first_child->data;
2302 if(widget) gtk_widget_grab_focus(widget);
2303 }
2304 }
2305
2306 // we also add the focus css class
2308 dt_gui_add_class(iop_w, "dt_module_focus");
2309 }
2310
2314}
2315
2316/*
2317 * NEW EXPANDER
2318 */
2319
2320static void _gui_set_single_expanded(dt_iop_module_t *module, gboolean expanded)
2321{
2322 if(IS_NULL_PTR(module->expander)) return;
2323
2324 /* update expander arrow state */
2326
2327 /* store expanded state of module.
2328 * we do that first, so update_expanded won't think it should be visible
2329 * and undo our changes right away. */
2330 module->expanded = expanded;
2331
2332 /* show / hide plugin widget */
2333 if(expanded)
2334 {
2335 /* set this module to receive focus / draw events*/
2337
2338 /* focus the current module */
2339 for(int k = 0; k < DT_UI_CONTAINER_SIZE; k++)
2341
2342 /* redraw center, iop might have post expose */
2344 }
2345 else
2346 {
2347 if(module->dev->gui_module == module)
2348 {
2351 }
2352 }
2353
2354 if(expanded)
2355 dt_gui_add_class(module->expander, "expanded");
2356 else
2357 dt_gui_remove_class(module->expander, "expanded");
2358
2359 char var[1024];
2360 snprintf(var, sizeof(var), "plugins/darkroom/%s/expanded", module->op);
2361 dt_conf_set_bool(var, expanded);
2362}
2363
2366{
2367 for(GList *iop = g_list_first(darktable.develop->iop); iop; iop = g_list_next(iop))
2368 {
2369 dt_iop_module_t *m = (dt_iop_module_t *)iop->data;
2370
2371 // Handle invisible modules
2372 if(IS_NULL_PTR(m) || !m->expander) continue;
2373
2374 if(dim && m != module)
2376 else
2378 }
2379}
2380
2382{
2383 if(IS_NULL_PTR(module) || !module->expander) return;
2384 if(collapse_others)
2385 {
2386 for(GList *iop = g_list_first(darktable.develop->iop); iop; iop = g_list_next(iop))
2387 {
2388 dt_iop_module_t *m = (dt_iop_module_t *)iop->data;
2390 }
2391 }
2392
2394 _iop_dim_all_but((expanded) ? module : NULL, expanded);
2396}
2397
2399{
2400 if(IS_NULL_PTR(module->expander)) return;
2401
2402 const gboolean expanded = module->expanded;
2403
2405}
2406
2407static gboolean _iop_plugin_body_button_press(GtkWidget *w, GdkEventButton *e, gpointer user_data)
2408{
2409 dt_iop_module_t *module = (dt_iop_module_t *)user_data;
2410
2411 /* Reset the scrolling focus. If the click happened on any bauhaus element,
2412 * its internal button_press method will set it for itself */
2414
2415 gboolean handled = FALSE;
2416
2417 if(e->button == 1)
2418 {
2420 handled = TRUE;
2421 }
2422 else if(e->button == 3)
2423 {
2425 handled = TRUE;
2426 }
2427 return handled;
2428}
2429
2430static gboolean _iop_plugin_header_activate(GtkWidget* self, gboolean group_cycling, gpointer user_data)
2431{
2432 dt_gui_module_t *module = (dt_gui_module_t *)user_data;
2433 if(IS_NULL_PTR(module) || !module->focus) return FALSE;
2434 return module->focus(module, TRUE);
2435}
2436
2437static gboolean _iop_plugin_header_child_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
2438{
2439 dt_iop_module_t *module = (dt_iop_module_t *)user_data;
2440 if(module && module->expander)
2441 g_object_set_data(G_OBJECT(module->expander), "dt-module-header-child-click", GINT_TO_POINTER(TRUE));
2442
2443 return FALSE;
2444}
2445
2447 GdkModifierType modifier, gpointer data)
2448{
2449 dt_gui_module_t *module = (dt_gui_module_t *)data;
2450 dt_iop_module_t *iop = (dt_iop_module_t *)data;
2451 if(IS_NULL_PTR(module) || !module->focus) return FALSE;
2452
2453 // Accel search explicitly targets a module, so allow modulegroups to leave
2454 // the Pipeline tab once for this focus request.
2455 if(iop->expander)
2456 g_object_set_data(G_OBJECT(iop->expander), "dt-modulegroups-switch-from-active-once",
2458
2459 return module->focus(module, FALSE);
2460}
2461
2463 GdkModifierType modifier, gpointer data)
2464{
2465 dt_iop_module_t *module = (dt_iop_module_t *)data;
2466 if(IS_NULL_PTR(module)) return FALSE;
2467
2468 // Direct actions from accel search should prioritize Pipeline when they focus
2469 // the edited module right after applying the change.
2470 if(!IS_NULL_PTR(module->expander))
2471 g_object_set_data(G_OBJECT(module->expander), "dt-modulegroups-prefer-active-once",
2473
2474 // Kind of ugly to go through history to change module GUI state
2475 // FIXME: we should have a GUI callback that enables module and dispatches history instead,
2476 // history should not care about module GUI. This is a wrong, reversed inheritance.
2477 if(!module->hide_enable_button)
2478 {
2479 module->enabled = TRUE;
2481 }
2482
2485 return TRUE;
2486}
2487
2488static gboolean _iop_plugin_header_button_press(GtkWidget *w, GdkEventButton *e, gpointer user_data)
2489{
2490 if(e->type == GDK_2BUTTON_PRESS || e->type == GDK_3BUTTON_PRESS) return TRUE;
2491
2492 dt_iop_module_t *module = (dt_iop_module_t *)user_data;
2493 if(IS_NULL_PTR(module)) return FALSE;
2494
2495 if(!IS_NULL_PTR(module->expander)
2498 {
2500 return TRUE;
2501 }
2502
2503 /* Reset the scrolling focus. If the click happened on any bauhaus element,
2504 * its internal button_press method will set it for itself */
2506
2507 if(e->button == 1)
2508 {
2509 if(module->expander) g_object_set_data(G_OBJECT(module->expander), "dt-module-dragged", NULL);
2510
2511 if(!dt_modifier_is(e->state, GDK_CONTROL_MASK))
2512 {
2513 return FALSE;
2514 }
2515 else
2516 {
2517 if(module->expander)
2520 return TRUE;
2521 }
2522 }
2523 else if(e->button == 3)
2524 {
2526
2527 return TRUE;
2528 }
2529 return FALSE;
2530}
2531
2532static gboolean _iop_plugin_header_button_release(GtkWidget *w, GdkEventButton *e, gpointer user_data)
2533{
2534 if(e->button != 1 || e->type != GDK_BUTTON_RELEASE) return FALSE;
2535
2536 dt_iop_module_t *module = (dt_iop_module_t *)user_data;
2537 if(IS_NULL_PTR(module) || !module->expander) return FALSE;
2538
2540 {
2542 return TRUE;
2543 }
2544
2547 {
2548 return TRUE;
2549 }
2550
2551 if(g_object_get_data(G_OBJECT(module->expander), "dt-module-header-child-click"))
2552 {
2553 g_object_set_data(G_OBJECT(module->expander), "dt-module-header-child-click", NULL);
2554 return FALSE;
2555 }
2556
2557 if(g_object_get_data(G_OBJECT(module->expander), "dt-module-dragged"))
2558 {
2559 g_object_set_data(G_OBJECT(module->expander), "dt-module-dragged", NULL);
2560 return TRUE;
2561 }
2562
2563 // make gtk scroll to the module once it updated its allocation size
2564 const gboolean collapse_others = dt_modifier_is(e->state, GDK_SHIFT_MASK) ? FALSE : TRUE;
2567
2568 return TRUE;
2569}
2570
2572{
2573 if(dt_gui_widgets_suppressed()) return;
2574
2577
2578 module->request_mask_display
2579 &= ~(DT_DEV_PIXELPIPE_DISPLAY_MASK | DT_DEV_PIXELPIPE_DISPLAY_CHANNEL
2580 | DT_DEV_PIXELPIPE_DISPLAY_ANY | DT_DEV_PIXELPIPE_DISPLAY_STICKY);
2581
2582 if(is_active)
2583 module->request_mask_display |= DT_DEV_PIXELPIPE_DISPLAY_MASK;
2584
2586
2587 // set the module show mask button too
2588 if(bd->showmask)
2590
2592 if(GTK_IS_TOGGLE_BUTTON(bd->filter[0].channel_display))
2593 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->filter[0].channel_display), FALSE);
2594 if(GTK_IS_TOGGLE_BUTTON(bd->filter[1].channel_display))
2595 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->filter[1].channel_display), FALSE);
2597
2600}
2601
2603 gboolean *drawn_used, gboolean *parametric_used)
2604{
2609
2610 if(IS_NULL_PTR(module) || IS_NULL_PTR(module->blend_params)) return;
2611
2612 const dt_iop_gui_blend_data_t *bd = (const dt_iop_gui_blend_data_t *)module->blend_data;
2613 gboolean top = FALSE;
2614 gboolean raster = FALSE;
2615 gboolean drawn = FALSE;
2616 gboolean parametric = FALSE;
2618
2619 // look if the user disabled masks modes
2620
2621 // raster mask must be enabled and a raster mask must be selected in the combo
2622 if(!IS_NULL_PTR(bd) && GTK_IS_TOGGLE_BUTTON(bd->raster_enable)
2624 raster = FALSE;
2625
2626 if(!IS_NULL_PTR(bd) && GTK_IS_TOGGLE_BUTTON(bd->masks_enable)
2628 drawn = FALSE;
2629
2630 if(!IS_NULL_PTR(bd) && GTK_IS_TOGGLE_BUTTON(bd->blendif_enable)
2632 parametric = FALSE;
2633
2638}
2639
2640static gboolean _mask_indicator_tooltip(GtkWidget *treeview, gint x, gint y, gboolean kb_mode,
2642{
2643 (void)treeview;
2644 (void)x;
2645 (void)y;
2646 (void)kb_mode;
2647
2648 gboolean res = FALSE;
2649 if(module->mask_indicator)
2650 {
2651 gchar *type = _("unknown mask");
2652 gchar *text;
2655 if(!top_enabled) return FALSE;
2656
2658 type=_("drawn + parametric mask");
2659 else if(drawn_used)
2660 type=_("drawn mask");
2661 else if(parametric_used)
2662 type=_("parametric mask");
2663 else if(raster_used)
2664 type=_("raster mask");
2665 else
2666 return FALSE;
2667 gchar *part1 = g_strdup_printf(_("this module has a '%s'"), type);
2668 gchar *part2 = NULL;
2669 if(raster_used && module->raster_mask.sink.source)
2670 {
2671 gchar *source = dt_history_item_get_name(module->raster_mask.sink.source);
2672 part2 = g_strdup_printf(_("taken from module %s"), source);
2673 dt_free(source);
2674 }
2675
2676 if(part2)
2677 {
2678 gchar *details = g_strdup_printf("%s\n%s", part2, _("click to display (module must be activated first)"));
2679 dt_free(part2);
2680 part2 = details;
2681 }
2682 else
2683 {
2684 part2 = g_strdup(_("click to display (module must be activated first)"));
2685 }
2686
2687 if(part2)
2688 text = g_strconcat(part1, "\n", part2, NULL);
2689 else
2690 text = g_strdup(part1);
2691
2693 res = TRUE;
2694 dt_free(part1);
2695 dt_free(part2);
2696 dt_free(text);
2697 }
2698 return res;
2699}
2700
2702{
2703 if(IS_NULL_PTR(module) || IS_NULL_PTR(module->mask_indicator)) return;
2704
2705 const gboolean support_blending = (module->flags() & IOP_FLAGS_SUPPORTS_BLENDING) == IOP_FLAGS_SUPPORTS_BLENDING;
2706
2707 if(!support_blending || !module->blend_params)
2708 {
2709 gtk_widget_set_visible(GTK_WIDGET(module->mask_indicator), FALSE);
2712 return;
2713 }
2714
2717
2718 const gboolean use_masks = top_enabled && (raster_used || drawn_used || parametric_used);
2719
2721 gtk_widget_set_sensitive(GTK_WIDGET(module->mask_indicator), module->enabled);
2723}
2724
2725gboolean _iop_tooltip_callback(GtkWidget *widget, gint x, gint y, gboolean keyboard_mode,
2726 GtkTooltip *tooltip, gpointer user_data)
2727{
2728 dt_iop_module_t *module = (dt_iop_module_t *)user_data;
2729
2730 const char **des = module->description(module);
2731
2732 if(IS_NULL_PTR(des)) return FALSE;
2733
2735 GtkWidget *grid = gtk_grid_new();
2739
2740 GtkWidget *label = gtk_label_new(des[0] ? des[0] : "");
2744 // if there is no more description, do not add a separator
2745 if(des[1]) dt_gui_add_class(label, "dt_section_label");
2746 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
2747
2751
2752 const char *icon_purpose = "\342\237\263";
2753 const char *icon_input = "\342\207\245";
2754 const char *icon_process = "\342\237\264";
2755 const char *icon_output = "\342\206\246";
2756
2758 const char *ilabs[4] = {_("Purpose"), _("Input"), _("Process"), _("Output")};
2759
2760 for(int k=1; k<5; k++)
2761 {
2762 if(des[k])
2763 {
2764 label = gtk_label_new(icons[k-1]);
2766 gtk_grid_attach(GTK_GRID(grid), label, 0, k, 1, 1);
2768
2769 label = gtk_label_new(ilabs[k-1]);
2771 gtk_grid_attach(GTK_GRID(grid), label, 1, k, 1, 1);
2773
2774 label = gtk_label_new(":");
2776 gtk_grid_attach(GTK_GRID(grid), label, 2, k, 1, 1);
2778
2779 label = gtk_label_new(des[k]);
2781 gtk_grid_attach(GTK_GRID(grid), label, 3, k, 1, 1);
2783 }
2784 }
2785
2786 gtk_box_pack_start(GTK_BOX(vbox), grid, FALSE, FALSE, 0);
2787 gtk_widget_show_all(vbox);
2789
2790 return TRUE;
2791}
2792
2794{
2796 gtk_widget_set_name(GTK_WIDGET(header), "module-header");
2797
2799 GtkWidget *expander = dtgtk_expander_new(header, iopw);
2800 dt_gui_add_class(expander, "dt_module_frame");
2801 dt_gui_add_class(expander, "dt_iop_module");
2802
2806
2807 dt_gui_add_class(pluginui_frame, "dt_plugin_ui");
2808
2809 module->header = header;
2810
2811 /* setup the header box */
2812 g_signal_connect(G_OBJECT(header_evb), "button-press-event", G_CALLBACK(_iop_plugin_header_button_press), module);
2813 g_signal_connect(G_OBJECT(header_evb), "button-release-event", G_CALLBACK(_iop_plugin_header_button_release), module);
2814 g_signal_connect(G_OBJECT(header_evb), "mnemonic-activate", G_CALLBACK(_iop_plugin_header_activate), module);
2816
2817 /* connect mouse button callbacks for focus and presets */
2818 g_signal_connect(G_OBJECT(body_evb), "button-press-event", G_CALLBACK(_iop_plugin_body_button_press), module);
2820
2821 /*
2822 * initialize the header widgets
2823 */
2825
2826 /* init empty place for icon, this is then set in CSS if needed */
2827 char w_name[256] = { 0 };
2828 snprintf(w_name, sizeof(w_name), "iop-panel-icon-%s", module->op);
2831
2832 /* add module label */
2836 gtk_container_add(GTK_CONTAINER(lab), label);
2837 gtk_label_set_mnemonic_widget(GTK_LABEL(label), header_evb);
2838
2839 if((module->flags() & IOP_FLAGS_DEPRECATED) && module->deprecated_msg())
2840 gtk_widget_set_tooltip_text(lab, module->deprecated_msg());
2841 else
2842 {
2843 gtk_widget_set_name(lab, "iop_description");
2845 }
2846
2847 /* add mask preview button */
2849
2852 g_signal_connect(G_OBJECT(hw[IOP_MODULE_MASK]), "button-press-event",
2854 g_signal_connect(G_OBJECT(hw[IOP_MODULE_MASK]), "query-tooltip",
2856 module->mask_indicator = hw[IOP_MODULE_MASK];
2857
2858 /* add multi instances menu button */
2860 module->multimenu_button = GTK_WIDGET(hw[IOP_MODULE_INSTANCE]);
2862 _("multiple instance actions\nright-click creates new instance"));
2863 g_signal_connect(G_OBJECT(hw[IOP_MODULE_INSTANCE]), "button-press-event",
2866 module);
2867
2869
2870 /* add reset button */
2872 module->reset_button = GTK_WIDGET(hw[IOP_MODULE_RESET]);
2873 gtk_widget_set_tooltip_text(GTK_WIDGET(hw[IOP_MODULE_RESET]), _("reset parameters\nctrl+click to reapply any automatic presets"));
2874 g_signal_connect(G_OBJECT(hw[IOP_MODULE_RESET]), "button-press-event",
2877
2878 /* add preset button if module has implementation */
2880 module->presets_button = GTK_WIDGET(hw[IOP_MODULE_PRESETS]);
2881 if(!(module->flags() & IOP_FLAGS_ONE_INSTANCE))
2882 gtk_widget_set_tooltip_text(GTK_WIDGET(hw[IOP_MODULE_PRESETS]), _("presets\nright-click to apply on new instance"));
2883 g_signal_connect(G_OBJECT(hw[IOP_MODULE_PRESETS]), "button-press-event",
2886
2887 /* add enabled button */
2889
2890 dt_gui_add_class(switch_button, "dt_iop_enable_button");
2893 g_signal_connect(G_OBJECT(switch_button), "button-press-event",
2896
2897 module->off = DTGTK_TOGGLEBUTTON(switch_button);
2899
2900 /* Wrap the switch in a plain box so the CSS spacing trick that visually tucks it
2901 against the header edge (negative margin, see ansel.css) lives on the wrapper,
2902 not on the button itself. dtgtk_togglebutton's custom draw() reinterprets its own
2903 CSS margin a second time (as a paint-only offset) to size its icon -- if that same
2904 margin also carries a position nudge, the icon gets painted outside the button's
2905 real GTK allocation, which is what drives hover/click hit-testing. Keeping the
2906 button's own margin at 0 keeps its drawing and its hit-region intrinsically in
2907 sync; the wrapper absorbs the nudge at the layout level instead. */
2909 dt_gui_add_class(hw[IOP_MODULE_SWITCH], "dt_iop_enable_button_box");
2911
2912 /* reorder header, for now, iop are always in the right panel */
2913 for(int i = 0; i <= IOP_MODULE_LABEL; i++)
2914 {
2915 if(hw[i])
2916 gtk_box_pack_start(GTK_BOX(header), hw[i], FALSE, FALSE, 0);
2917 }
2918 for(int i = IOP_MODULE_LAST - 1; i > IOP_MODULE_LABEL; i--)
2919 {
2920 if(hw[i])
2921 gtk_box_pack_end(GTK_BOX(header), hw[i], FALSE, FALSE, 0);
2922 }
2923
2924 dt_gui_add_help_link(header, dt_get_help_url("module_header"));
2925 // for the module label, point to module specific help page
2927
2930
2931 // show deprecated message if any
2932 if(module->deprecated_msg())
2933 {
2934 GtkWidget *lb = gtk_label_new(module->deprecated_msg());
2937 dt_gui_add_class(lb, "dt_warning");
2939 gtk_widget_show(lb);
2940 }
2941
2942 /* initialize blending state if supported; the detached widget is hosted by the masks lib */
2945 dt_gui_add_class(module->widget, "dt_plugin_ui_main");
2948
2949 module->expander = expander;
2952
2953 /* update header */
2955
2958
2960}
2961
2966
2968{
2969 // return gtkframe (pluginui_frame)
2971}
2972
2973void dt_iop_nap(int32_t usec)
2974{
2975 if(usec <= 0) return;
2976
2977 // relinquish processor
2978 sched_yield();
2979
2980 // additionally wait the given amount of time
2981 g_usleep(usec);
2982}
2983
2985{
2986 return module->bypass_cache;
2987}
2988
2990{
2991 module->bypass_cache = state;
2992
2993 if(state && module->dev)
2994 {
2995 // Disable other modules bypass if set.
2996 for(GList *iop = g_list_last(module->dev->iop);
2997 iop;
2998 iop = g_list_previous(iop))
2999 {
3000 dt_iop_module_t *current = (dt_iop_module_t *)iop->data;
3001 if(current != module && current->bypass_cache) current->bypass_cache = FALSE;
3002 }
3003 }
3004}
3005
3007{
3008 module->bypass_cache_variant = variant;
3009}
3010
3011
3016
3018{
3019 dt_iop_module_t *result = NULL;
3020
3021 for(GList *modules = iop_list; modules; modules = g_list_next(modules))
3022 {
3024 if(strcmp(mod->op, op) == 0)
3025 {
3026 result = mod;
3027 break;
3028 }
3029 }
3030
3031 return result;
3032}
3033
3038
3039int dt_iop_get_module_flags(const char *op)
3040{
3042 while(modules)
3043 {
3044 dt_iop_module_so_t *module = (dt_iop_module_so_t *)modules->data;
3045 if(!strcmp(module->op, op)) return module->flags();
3047 }
3048 return 0;
3049}
3050
3051// to be called before issuing any query based on memory.darktable_iop_names
3053{
3054 if(IS_NULL_PTR(darktable.iop)) return;
3055
3056 // Faster than building a huge VALUES string: reuse a prepared statement and bind per module.
3060 "INSERT INTO memory.darktable_iop_names (operation, name) VALUES (?1, ?2)",
3061 -1, &stmt, NULL);
3062 if(IS_NULL_PTR(stmt)) return;
3063
3065 for(GList *iop = darktable.iop; iop; iop = g_list_next(iop))
3066 {
3067 dt_iop_module_so_t *module = (dt_iop_module_so_t *)iop->data;
3073 }
3076}
3077
3078const gchar *dt_iop_get_localized_name(const gchar *op)
3079{
3080 // Prepare mapping op -> localized name
3081 static GHashTable *module_names = NULL;
3083 {
3085 for(GList *iop = darktable.iop; iop; iop = g_list_next(iop))
3086 {
3087 dt_iop_module_so_t *module = (dt_iop_module_so_t *)iop->data;
3089 }
3090 }
3091 if(!IS_NULL_PTR(op))
3092 {
3093 return (gchar *)g_hash_table_lookup(module_names, op);
3094 }
3095 else {
3096 return _("ERROR");
3097 }
3098}
3099
3100const gchar *dt_iop_get_localized_aliases(const gchar *op)
3101{
3102 // Prepare mapping op -> localized name
3103 static GHashTable *module_aliases = NULL;
3105 {
3107 for(GList *iop = darktable.iop; iop; iop = g_list_next(iop))
3108 {
3109 dt_iop_module_so_t *module = (dt_iop_module_so_t *)iop->data;
3111 }
3112 }
3113 if(!IS_NULL_PTR(op))
3114 {
3115 return (gchar *)g_hash_table_lookup(module_aliases, op);
3116 }
3117 else {
3118 return _("ERROR");
3119 }
3120}
3121
3123{
3125 gpointer key, value;
3126
3127 g_hash_table_iter_init(&iter, module->raster_mask.source.users);
3129 {
3131
3133
3134 // also fix history entries
3135 for(GList *hiter = module->dev->history; hiter; hiter = g_list_next(hiter))
3136 {
3138 if(hist->module == sink_module)
3140 }
3141 }
3142
3143 module->multi_priority = new_priority;
3144}
3145
3147{
3149 gpointer key, value;
3150
3151 g_hash_table_iter_init(&iter, module->raster_mask.source.users);
3153 {
3154 if(GPOINTER_TO_INT(value) == id)
3155 return TRUE;
3156 }
3157 return FALSE;
3158}
3159
3160dt_iop_module_t *dt_iop_get_module_by_op_priority(GList *modules, const char *operation, const int multi_priority)
3161{
3163
3164 for(GList *m = modules; m; m = g_list_next(m))
3165 {
3167
3168 if(strcmp(mod->op, operation) == 0
3169 && (mod->multi_priority == multi_priority || multi_priority == -1))
3170 {
3171 mod_ret = mod;
3172 break;
3173 }
3174 }
3175 return mod_ret;
3176}
3177
3178dt_iop_module_t *dt_iop_get_module_by_instance_name(GList *modules, const char *operation, const char *multi_name)
3179{
3181
3182 for(GList *m = modules; m; m = g_list_next(m))
3183 {
3185
3186 if((strcmp(mod->op, operation) == 0)
3187 && ((IS_NULL_PTR(multi_name)) || (strcmp(mod->multi_name, multi_name) == 0)))
3188 {
3189 mod_ret = mod;
3190 break;
3191 }
3192 }
3193 return mod_ret;
3194}
3195
3197{
3198 gboolean is_first = TRUE;
3199 GList *iop = modules;
3200 while(iop)
3201 {
3202 dt_iop_module_t *m = (dt_iop_module_t *)iop->data;
3203 if(!strcmp(m->op, module->op))
3204 {
3205 is_first = (m == module);
3206 break;
3207 }
3208 iop = g_list_next(iop);
3209 }
3210
3211 return is_first;
3212}
3213
3215{
3216 dt_iop_module_t *self = (dt_iop_module_t*)data;
3218}
3219
3220const char **dt_iop_set_description(dt_iop_module_t *module, const char *main_text, const char *purpose, const char *input, const char *process,
3221 const char *output)
3222{
3223 static const char *str_out[5] = {NULL, NULL, NULL, NULL, NULL};
3224
3225 str_out[0] = main_text;
3226 str_out[1] = purpose;
3227 str_out[2] = input;
3228 str_out[3] = process;
3229 str_out[4] = output;
3230
3231 return (const char **)str_out;
3232}
3233
3235{
3236 if(IS_NULL_PTR(action)) return;
3237 dt_iop_module_t *module = (dt_iop_module_t *)action;
3238
3239 if(module->gui_changed) module->gui_changed(module, widget, data);
3240
3242
3244
3245 if(!IS_NULL_PTR(widget) && g_object_get_data(G_OBJECT(widget), "dt-blendop-header-update"))
3247 else
3249}
3250
3251
3253{
3255
3256 for(GList *w = g_list_first(m->widget_list_bh); w; w = g_list_next(w))
3257 {
3258 GtkWidget *widget = (GtkWidget *)w->data;
3259 struct dt_bauhaus_widget_t *bhw = DT_BAUHAUS_WIDGET(widget);
3260 if(IS_NULL_PTR(bhw)) continue;
3261
3262 switch(bhw->type)
3263 {
3264 case DT_BAUHAUS_SLIDER:
3265 switch(bhw->field_type)
3266 {
3268 dt_bauhaus_slider_set(widget, *(float *)bhw->field);
3269 break;
3271 dt_bauhaus_slider_set(widget, *(int *)bhw->field);
3272 break;
3274 dt_bauhaus_slider_set(widget, *(unsigned short *)bhw->field);
3275 break;
3276 default:
3277 fprintf(stderr, "[dt_bauhaus_update_module] unsupported slider data type\n");
3278 }
3279 break;
3281 switch(bhw->field_type)
3282 {
3284 dt_bauhaus_combobox_set_from_value(widget, *(int *)bhw->field);
3285 break;
3287 dt_bauhaus_combobox_set(widget, *(int *)bhw->field);
3288 break;
3290 dt_bauhaus_combobox_set(widget, *(unsigned int *)bhw->field);
3291 break;
3293 dt_bauhaus_combobox_set(widget, *(gboolean *)bhw->field);
3294 break;
3295 default:
3296 fprintf(stderr, "[dt_bauhaus_update_module] unsupported combo data type\n");
3297 }
3298 break;
3299 default:
3300 fprintf(stderr, "[dt_bauhaus_update_module] invalid bauhaus widget type encountered\n");
3301 }
3302 }
3303}
3304
3306{
3308 dt_iop_module_t *module = (dt_iop_module_t *)w->module;
3309 if(IS_NULL_PTR(w->field) || IS_NULL_PTR(module)) return;
3310
3311 switch(w->type)
3312 {
3313 case DT_BAUHAUS_SLIDER:
3314 {
3315 float val = dt_bauhaus_slider_get(widget);
3316 switch(w->field_type)
3317 {
3319 {
3320 float *f = w->field, prevf = *f; *f = val;
3321 if(*f != prevf) dt_iop_gui_changed(module, widget, &prevf);
3322 break;
3323 }
3325 {
3326 int *i = w->field, previ = *i; *i = val;
3327 if(*i != previ) dt_iop_gui_changed(module, widget, &previ);
3328 break;
3329 }
3331 {
3332 unsigned short *s = w->field, prevs = *s; *s = val;
3333 if(*s != prevs) dt_iop_gui_changed(module, widget, &prevs);
3334 break;
3335 }
3336 default:
3337 fprintf(stderr, "[_bauhaus_slider_value_change] unsupported slider data type\n");
3338 }
3339 break;
3340 }
3342 {
3344 switch(w->field_type)
3345 {
3347 {
3348 if(d->active >= 0)
3349 {
3350 const dt_bauhaus_combobox_entry_t *entry = g_ptr_array_index(d->entries, d->active);
3351 int *e = w->field, preve = *e; *e = GPOINTER_TO_INT(entry->data);
3352 if(*e != preve) dt_iop_gui_changed(module, widget, &preve);
3353 }
3354 break;
3355 }
3357 {
3358 int *i = w->field, previ = *i; *i = d->active;
3359 if(*i != previ) dt_iop_gui_changed(module, widget, &previ);
3360 break;
3361 }
3363 {
3364 unsigned int *u = w->field, prevu = *u; *u = d->active;
3365 if(*u != prevu) dt_iop_gui_changed(module, widget, &prevu);
3366 break;
3367 }
3369 {
3370 gboolean *b = w->field, prevb = *b; *b = d->active;
3371 if(*b != prevb) dt_iop_gui_changed(module, widget, &prevb);
3372 break;
3373 }
3374 default:
3375 fprintf(stderr, "[_bauhaus_combobox_set] unsupported combo data type\n");
3376 }
3377 break;
3378 }
3379 default:
3380 fprintf(stderr, "[dt_bauhaus_value_changed_default_callback] invalid bauhaus widget type encountered for %s %s: %i\n", w->label, w->module->name, w->type);
3381 }
3382}
3383
3384// clang-format off
3385// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
3386// vim: shiftwidth=2 expandtab tabstop=2 cindent
3387// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
3388// 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.
void dt_accels_new_virtual_instance_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)
gchar * dt_accels_build_path(const gchar *scope, const gchar *feature)
void dt_accels_remove_accel(dt_accels_t *accels, const char *path, gpointer data)
Recursively remove all accels for all shortcuts containing path. This is unneeded for accels attached...
__DT_CLONE_TARGETS__ int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid)
Definition ashift.c:3153
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
#define m
Definition basecurve.c:278
float dt_bauhaus_slider_get(GtkWidget *widget)
Definition bauhaus.c:3486
gboolean dt_bauhaus_combobox_set_from_value(GtkWidget *widget, int value)
Definition bauhaus.c:2333
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:3537
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
Definition bauhaus.c:2304
@ DT_BAUHAUS_COMBOBOX
Definition bauhaus.h:87
@ DT_BAUHAUS_SLIDER
Definition bauhaus.h:86
#define DT_BAUHAUS_WIDGET(obj)
Definition bauhaus.h:60
int width
Definition bilateral.h:1
void dt_develop_blend_init_blend_parameters(dt_develop_blend_params_t *blend_params, dt_develop_blend_colorspace_t cst)
Definition blend.c:158
dt_develop_blend_colorspace_t dt_develop_blend_default_module_blend_colorspace(dt_iop_module_t *module)
Definition blend.c:136
int dt_develop_blend_version(void)
Definition blend.c:1651
int dt_develop_blend_legacy_params(dt_iop_module_t *module, const void *const old_params, const int old_version, void *new_params, const int new_version, const int length)
Definition blend.c:1715
void dt_develop_blend_get_mask_usage(const dt_iop_module_t *module, const dt_develop_blend_params_t *params, gboolean *top_enabled, gboolean *raster_used, gboolean *drawn_used, gboolean *parametric_used)
Definition blend.c:247
gboolean blend_color_picker_apply(dt_iop_module_t *module, GtkWidget *picker, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
void dt_iop_gui_init_blending(dt_iop_module_t *module)
Definition blend_gui.c:5070
dt_develop_blend_colorspace_t
Definition blend.h:55
@ DEVELOP_BLEND_CS_NONE
Definition blend.h:56
void dt_iop_gui_update_blending(dt_iop_module_t *module)
Definition blend_gui.c:4382
void dt_iop_gui_blending_lose_focus(dt_iop_module_t *module)
Definition blend_gui.c:4670
void dt_iop_gui_cleanup_blending(dt_iop_module_t *module)
Definition blend_gui.c:4231
void dt_iop_gui_blending_reload_defaults(dt_iop_module_t *module)
Definition blend_gui.c:4731
void dtgtk_button_set_active(GtkDarktableButton *button, gboolean active)
Definition button.c:176
GtkWidget * dtgtk_button_new(DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
Definition button.c:134
#define DTGTK_BUTTON(obj)
Definition button.h:39
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
void dt_collection_hint_message(const dt_collection_t *collection)
dt_iop_colorspace_type_t
@ IOP_CS_LAB
void dt_iop_color_picker_reset(dt_iop_module_t *module, gboolean keep)
int dt_iop_color_picker_get_ready_data(const dt_iop_module_t *module, GtkWidget **picker, dt_dev_pixelpipe_t **pipe, const dt_dev_pixelpipe_iop_t **piece)
const dt_aligned_pixel_t f
const float top
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
char * key
int type
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)
gboolean dt_conf_key_not_empty(const char *name)
void dt_control_queue_redraw_center()
request redraw of center window. This redraws the center view within a gdk critical section to preven...
Definition control.c:877
#define dt_control_queue_cursor(cursor)
Definition control.h:135
darktable_t darktable
Definition darktable.c:183
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1600
#define dt_free_align(ptr)
Definition darktable.h:503
static void * dt_calloc_align(size_t size)
Definition darktable.h:510
@ DT_DEBUG_PARAMS
Definition darktable.h:758
@ DT_DEBUG_ALWAYS
Definition darktable.h:735
@ DT_DEBUG_DEV
Definition darktable.h:739
@ DT_DEBUG_MASKS
Definition darktable.h:749
#define dt_gui_freeze_begin()
Definition darktable.h:900
#define dt_gui_freeze_end()
Definition darktable.h:901
#define dt_gui_widget_freeze()
Definition darktable.h:913
#define dt_free(ptr)
Definition darktable.h:478
void void void gboolean dt_gui_widgets_suppressed(void)
Definition gtk.c:194
static gchar * delete_underscore(const char *s)
Definition darktable.h:1210
static uint64_t dt_hash(uint64_t hash, const char *str, size_t size)
Definition darktable.h:1123
static const dt_aligned_pixel_simd_t value
Definition darktable.h:599
static gboolean dt_modifier_is(const GdkModifierType state, const GdkModifierType desired_modifier_mask)
Definition darktable.h:955
#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:293
void dt_database_end_transaction_batch(const struct dt_database_t *db)
Definition database.c:4771
void dt_database_begin_transaction_batch(const struct dt_database_t *db)
Definition database.c:4751
sqlite3 * dt_database_get(const dt_database_t *db)
Definition database.c:3653
#define dt_database_start_transaction(db)
Definition database.h:77
#define dt_database_release_transaction(db)
Definition database.h:78
#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_write_history(dt_develop_t *dev, gboolean async)
Thread-safe wrapper around dt_dev_write_history_ext() for dev->image_storage.id.
#define dt_dev_add_history_item(dev, module, enable, redraw)
void dt_iop_params_t
Definition dev_history.h:42
#define dt_dev_pixelpipe_rebuild_all(dev)
#define dt_dev_pixelpipe_update_history_main(dev)
void dt_dev_signal_modules_moved(dt_develop_t *dev)
Definition develop.c:1672
void dt_dev_modulegroups_switch_tab(dt_develop_t *dev, dt_iop_module_t *module)
Definition develop.c:1234
void dt_dev_module_remove(dt_develop_t *dev, dt_iop_module_t *module)
Definition develop.c:1342
void dt_dev_undo_start_record(dt_develop_t *dev)
Definition develop.c:1678
gchar * dt_history_item_get_label(const struct dt_iop_module_t *module)
Definition develop.c:1511
void dt_dev_undo_end_record(dt_develop_t *dev)
Definition develop.c:1689
gchar * dt_history_item_get_name(const struct dt_iop_module_t *module)
Definition develop.c:1557
@ DT_DEV_PIXELPIPE_DISPLAY_NONE
Definition develop.h:117
void dtgtk_cairo_paint_module_switch_on(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_presets(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_showmask(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_multiinstance(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_module_switch(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_reset(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
char * dt_exif_xmp_encode(const unsigned char *input, const int len, int *output_len)
Definition exif.cc:2250
GtkWidget * dtgtk_expander_get_header_event_box(GtkDarktableExpander *expander)
Definition expander.c:49
GtkWidget * dtgtk_expander_get_body_event_box(GtkDarktableExpander *expander)
Definition expander.c:63
GtkWidget * dtgtk_expander_get_frame(GtkDarktableExpander *expander)
Definition expander.c:35
GtkWidget * dtgtk_expander_get_body(GtkDarktableExpander *expander)
Definition expander.c:56
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
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:3185
void dt_gui_remove_class(GtkWidget *widget, const gchar *class_name)
Definition gtk.c:143
GtkWidget * dt_gui_container_nth_child(GtkContainer *container, int which)
Definition gtk.c:3124
void dt_capitalize_label(gchar *text)
Definition gtk.c:3382
void dt_gui_add_help_link(GtkWidget *widget, char *link)
Definition gtk.c:2232
void dt_gui_refocus_center()
Definition gtk.c:3482
void dt_accels_disconnect_on_text_input(GtkWidget *widget)
Disconnects accels when a text or search entry gets the focus, and reconnects them when it looses it....
Definition gtk.c:3473
void dt_gui_add_class(GtkWidget *widget, const gchar *class_name)
Definition gtk.c:133
void dt_ui_container_focus_widget(dt_ui_t *ui, const dt_ui_container_t c, GtkWidget *w)
gives a widget focus in the container
Definition gtk.c:1804
#define dt_accels_new_darkroom_action(a, b, c, d, e, f, g)
Definition gtk.h:448
#define DT_GUI_BOX_SPACING
Definition gtk.h:109
#define DT_PIXEL_APPLY_DPI(value)
Definition gtk.h:90
gboolean dt_gui_presets_autoapply_for_module(dt_iop_module_t *module)
void dt_gui_presets_popup_menu_show_for_module(dt_iop_module_t *module)
gboolean dt_gui_presets_autogen_enabled()
#define DT_GUI_MODULE(x)
void dt_gui_throttle_cancel(gpointer source)
static gboolean enable(dt_image_t *image)
Definition highlights.c:653
const char * tooltip
Definition image.h:251
uint32_t dt_rawspeed_crop_dcraw_filters(uint32_t filters, uint32_t crop_x, uint32_t crop_y)
static gboolean _iop_plugin_enable_accel(GtkAccelGroup *accel_group, GObject *accelerable, guint keyval, GdkModifierType modifier, gpointer data)
Definition imageop.c:2462
static int default_operation_tags(void)
Definition imageop.c:179
dt_iop_module_t * dt_iop_get_module_from_list(GList *iop_list, const char *op)
Definition imageop.c:3017
static void _gui_reset_callback(GtkButton *button, GdkEventButton *event, dt_iop_module_t *module)
Definition imageop.c:2160
dt_iop_module_t * dt_iop_get_module(const char *op)
Definition imageop.c:3034
static int default_distort_backtransform(dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, float *points, size_t points_count)
Definition imageop.c:282
void dt_iop_set_darktable_iop_table()
Definition imageop.c:3052
gboolean dt_iop_module_needs_mask_history_ext(const dt_iop_module_t *module, gboolean *raster, gboolean *drawn, gboolean *parametric)
Definition imageop.c:1676
static gboolean _gui_multiinstance_callback(GtkButton *button, GdkEventButton *event, gpointer user_data)
Definition imageop.c:1019
static const char * default_aliases(void)
Definition imageop.c:195
void dt_iop_gui_cleanup_module(dt_iop_module_t *module)
Definition imageop.c:2036
static void _init_module_so(void *m)
Definition imageop.c:1516
void dt_iop_load_default_params(dt_iop_module_t *module)
Definition imageop.c:142
dt_iop_module_t * dt_iop_get_module_by_instance_name(GList *modules, const char *operation, const char *multi_name)
Definition imageop.c:3178
static int default_distort_transform(dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, float *points, size_t points_count)
Definition imageop.c:271
static gboolean _iop_plugin_header_button_release(GtkWidget *w, GdkEventButton *e, gpointer user_data)
Definition imageop.c:2532
GtkWidget * dt_iop_gui_get_pluginui(dt_iop_module_t *module)
Definition imageop.c:2967
static int default_default_group(void)
Definition imageop.c:167
gboolean dt_iop_is_raster_mask_used(dt_iop_module_t *module, int id)
Definition imageop.c:3146
static void _iop_plugin_header_menu_deactivate(GtkWidget *menu, gpointer user_data)
Definition imageop.c:1003
void dt_iop_commit_params(dt_iop_module_t *module, dt_iop_params_t *params, dt_develop_blend_params_t *blendop_params, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition imageop.c:1951
void dt_iop_compute_module_hash(dt_iop_module_t *module, GList *masks)
Definition imageop.c:1928
void dt_iop_add_remove_mask_indicator(dt_iop_module_t *module)
Definition imageop.c:2701
void dt_iop_reload_defaults(dt_iop_module_t *module)
Definition imageop.c:1278
void _hash_raster_masks(gpointer key, gpointer value, uint64_t *hash)
Definition imageop.c:1866
void dt_iop_gui_set_expander(dt_iop_module_t *module)
Definition imageop.c:2793
void dt_iop_throttled_history_update(gpointer data)
Definition imageop.c:3214
void dt_iop_compute_blendop_hash(dt_iop_module_t *module, uint64_t hash, GList *masks)
Definition imageop.c:1880
dt_iop_module_t * dt_iop_gui_get_next_visible_module(dt_iop_module_t *module)
Definition imageop.c:788
void dt_iop_init_pipe(struct dt_iop_module_t *module, struct dt_dev_pixelpipe_t *pipe, struct dt_dev_pixelpipe_iop_t *piece)
Definition imageop.c:564
static gboolean _rename_module_key_press(GtkWidget *entry, GdkEventKey *event, dt_iop_module_t *module)
Definition imageop.c:892
static gboolean _iop_plugin_header_activate(GtkWidget *self, gboolean group_cycling, gpointer user_data)
Definition imageop.c:2430
static void _iop_panel_label(dt_iop_module_t *module)
Definition imageop.c:1143
gboolean dt_iop_is_first_instance(GList *modules, dt_iop_module_t *module)
Definition imageop.c:3196
static int default_flags(void)
Definition imageop.c:173
int dt_iop_load_module_so(void *m, const char *libname, const char *module_name)
Definition imageop.c:426
void dt_iop_gui_init(dt_iop_module_t *module)
Definition imageop.c:1235
void dt_iop_commit_blend_params(dt_iop_module_t *module, const dt_develop_blend_params_t *blendop_params)
Definition imageop.c:1705
uint32_t dt_dev_get_roi_filters(const dt_dev_pixelpipe_iop_t *const piece, const dt_iop_roi_t *const roi_in)
Definition imageop.c:136
void dt_iop_cleanup_module(dt_iop_module_t *module)
Definition imageop.c:1578
static gboolean _iop_plugin_focus_accel(GtkAccelGroup *accel_group, GObject *accelerable, guint keyval, GdkModifierType modifier, gpointer data)
Definition imageop.c:2446
static void _iop_modify_roi_out(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, struct dt_dev_pixelpipe_iop_t *piece, dt_iop_roi_t *roi_out, const dt_iop_roi_t *roi_in)
Definition imageop.c:159
gboolean dt_iop_module_has_raster_mask(const dt_iop_module_t *module)
Definition imageop.c:1665
void dt_iop_gui_update(dt_iop_module_t *module)
Definition imageop.c:2133
const gchar * dt_iop_get_localized_aliases(const gchar *op)
Definition imageop.c:3100
const gchar * dt_iop_get_localized_name(const gchar *op)
Definition imageop.c:3078
void dt_bauhaus_update_module(dt_iop_module_t *self)
Definition imageop.c:3252
void dt_iop_set_cache_bypass_variant(dt_iop_module_t *module, int variant)
Definition imageop.c:3006
static void default_commit_params(struct dt_iop_module_t *self, dt_iop_params_t *params, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition imageop.c:219
static dt_introspection_field_t * default_get_f(const char *name)
Definition imageop.c:316
int dt_iop_load_module(dt_iop_module_t *module, dt_iop_module_so_t *module_so, dt_develop_t *dev)
Definition imageop.c:1567
gboolean _iop_validate_params(dt_introspection_field_t *field, gpointer params, gboolean report)
Definition imageop.c:1745
int dt_iop_load_module_by_so(dt_iop_module_t *module, dt_iop_module_so_t *so, dt_develop_t *dev)
Definition imageop.c:471
static void default_cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition imageop.c:236
static void default_gui_cleanup(dt_iop_module_t *self)
Definition imageop.c:243
static void _display_mask_indicator_callback(GtkToggleButton *bt, dt_iop_module_t *module)
Definition imageop.c:2571
void dt_iop_gui_reset(dt_iop_module_t *module)
Definition imageop.c:2153
static void default_init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition imageop.c:228
gboolean dt_iop_module_needs_mask_history(const dt_iop_module_t *module)
Definition imageop.c:1699
void dt_iop_load_modules_so(void)
Definition imageop.c:1558
void dt_iop_nap(int32_t usec)
Definition imageop.c:2973
static void default_cleanup(dt_iop_module_t *module)
Definition imageop.c:248
void dt_iop_default_init(dt_iop_module_t *module)
Definition imageop.c:321
void _iop_dim_all_but(dt_iop_module_t *module, gboolean dim)
Definition imageop.c:2365
void dt_iop_gui_update_expanded(dt_iop_module_t *module)
Definition imageop.c:2398
void dt_iop_gui_update_header(dt_iop_module_t *module)
Definition imageop.c:1183
void dt_iop_unload_modules_so()
Definition imageop.c:1627
static gboolean _iop_plugin_header_menu_dismiss_idle(gpointer user_data)
Definition imageop.c:993
void dt_iop_gui_set_enable_button(dt_iop_module_t *module)
Definition imageop.c:1211
int default_iop_focus(dt_gui_module_t *m, gboolean toggle)
Definition imageop.c:406
void dt_iop_cleanup_pipe(struct dt_iop_module_t *module, struct dt_dev_pixelpipe_t *pipe, struct dt_dev_pixelpipe_iop_t *piece)
Release module-owned resources for one pixelpipe node.
Definition imageop.c:582
static const char ** default_description(struct dt_iop_module_t *self)
Definition imageop.c:190
static void _gui_copy_callback(GtkButton *button, gpointer user_data)
Definition imageop.c:876
gboolean dt_iop_is_visible(dt_iop_module_t *module)
Definition imageop.c:1138
void dt_iop_request_focus(dt_iop_module_t *module)
Definition imageop.c:2221
dt_iop_module_t * dt_iop_gui_get_previous_visible_module(dt_iop_module_t *module)
Definition imageop.c:774
static const char * default_deprecated_msg(void)
Definition imageop.c:200
gboolean dt_iop_is_hidden(dt_iop_module_t *module)
Definition imageop.c:1133
static gboolean default_has_defaults(struct dt_iop_module_t *self)
Definition imageop.c:205
static gboolean _iop_plugin_body_button_press(GtkWidget *w, GdkEventButton *e, gpointer user_data)
Definition imageop.c:2407
const char ** dt_iop_set_description(dt_iop_module_t *module, const char *main_text, const char *purpose, const char *input, const char *process, const char *output)
Definition imageop.c:3220
GtkWidget * dt_iop_gui_get_widget(dt_iop_module_t *module)
Definition imageop.c:2962
gboolean dt_iop_so_is_hidden(dt_iop_module_so_t *module)
Definition imageop.c:1118
static void _gui_set_single_expanded(dt_iop_module_t *module, gboolean expanded)
Definition imageop.c:2320
float dt_dev_get_module_scale(const dt_dev_pixelpipe_t *const pipe, const dt_iop_roi_t *const roi_in)
Definition imageop.c:131
void dt_iop_gui_changed(dt_iop_module_t *action, GtkWidget *widget, gpointer data)
Definition imageop.c:3234
int dt_iop_get_module_flags(const char *op)
Definition imageop.c:3039
static void _iop_color_picker_data_ready_callback(gpointer instance, gpointer user_data)
Definition imageop.c:254
static void _mask_indicator_get_usage(dt_iop_module_t *module, gboolean *top_enabled, gboolean *raster_used, gboolean *drawn_used, gboolean *parametric_used)
Definition imageop.c:2602
static gboolean _iop_plugin_header_child_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
Definition imageop.c:2437
static gboolean _mask_indicator_tooltip(GtkWidget *treeview, gint x, gint y, gboolean kb_mode, GtkTooltip *tooltip, dt_iop_module_t *module)
Definition imageop.c:2640
void dt_iop_gui_set_enable_button_icon(GtkWidget *w, dt_iop_module_t *module)
Definition imageop.c:1194
static gboolean default_runtime_data_hash(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition imageop.c:210
gboolean dt_iop_gui_move_module_before(dt_iop_module_t *module, dt_iop_module_t *module_next, const char *reason)
Move a module before another one and commit the GUI-side effects.
Definition imageop.c:760
void dt_bauhaus_value_changed_default_callback(GtkWidget *widget)
Definition imageop.c:3305
dt_iop_module_t * dt_iop_get_colorout_module(void)
Definition imageop.c:3012
gboolean dt_iop_check_modules_equal(dt_iop_module_t *mod_1, dt_iop_module_t *mod_2)
Definition imageop.c:1856
#define DT_IOP_HEADER_IGNORE_RELEASE
Definition imageop.c:116
static void _presets_popup_callback(GtkButton *button, dt_iop_module_t *module)
Definition imageop.c:2203
static int default_operation_tags_filter(void)
Definition imageop.c:185
#define DT_IOP_HEADER_MENU_DISMISS_CLICK
Definition imageop.c:115
static sqlite3_stmt * _iop_presets_select_stmt
Definition imageop.c:109
static void _iop_gui_widget_gone(gpointer user_data, GObject *where_the_object_was)
Clear GUI pointers that still reference one iop widget being finalized.
Definition imageop.c:2021
static gboolean _rename_module_resize(GtkWidget *entry, GdkEventKey *event, dt_iop_module_t *module)
Definition imageop.c:945
void dt_iop_gui_set_expanded(dt_iop_module_t *module, gboolean expanded, gboolean collapse_others)
Definition imageop.c:2381
static void _gui_delete_callback(GtkButton *button, dt_iop_module_t *module)
Definition imageop.c:634
void dt_iop_set_cache_bypass(dt_iop_module_t *module, gboolean state)
Definition imageop.c:2989
static dt_introspection_t * default_get_introspection(void)
Definition imageop.c:308
gboolean dt_iop_get_cache_bypass(dt_iop_module_t *module)
Definition imageop.c:2984
gboolean dt_iop_gui_move_module_after(dt_iop_module_t *module, dt_iop_module_t *module_prev, const char *reason)
Move a module after another one and commit the GUI-side effects.
Definition imageop.c:767
void dt_iop_set_mask_mode(dt_iop_module_t *module, int mask_mode)
Definition imageop.c:1645
static gboolean _rename_module_idle(gpointer user_data)
Definition imageop.c:868
static gboolean _iop_plugin_header_button_press(GtkWidget *w, GdkEventButton *e, gpointer user_data)
Definition imageop.c:2488
static void _iop_modify_roi_in(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, struct dt_dev_pixelpipe_iop_t *piece, const dt_iop_roi_t *roi_out, dt_iop_roi_t *roi_in)
Definition imageop.c:152
gboolean _iop_tooltip_callback(GtkWidget *widget, gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip, gpointer user_data)
Definition imageop.c:2725
static void _init_presets(dt_iop_module_so_t *module_so)
Definition imageop.c:1311
void dt_iop_gui_rename_module(dt_iop_module_t *module)
Definition imageop.c:959
dt_iop_module_t * dt_iop_gui_duplicate(dt_iop_module_t *base, gboolean copy_params)
Definition imageop.c:802
gboolean dt_iop_gui_module_is_visible(dt_iop_module_t *module)
Definition imageop.c:740
dt_iop_module_t * dt_iop_get_module_by_op_priority(GList *modules, const char *operation, const int multi_priority)
Definition imageop.c:3160
gboolean dt_iop_gui_commit_iop_order_change(dt_develop_t *dev, dt_iop_module_t *module, gboolean enable, gboolean write_history, const char *reason)
Commit the GUI-side consequences of an IOP-order change.
Definition imageop.c:746
static void _gui_duplicate_callback(GtkButton *button, gpointer user_data)
Definition imageop.c:884
static void * default_get_p(const void *param, const char *name)
Definition imageop.c:312
static dt_introspection_field_t * default_get_introspection_linear(void)
Definition imageop.c:304
void dt_iop_update_multi_priority(dt_iop_module_t *module, int new_priority)
Definition imageop.c:3122
static void _gui_off_callback(GtkToggleButton *togglebutton, gpointer user_data)
Definition imageop.c:1075
static void _gui_rename_callback(GtkButton *button, dt_iop_module_t *module)
Definition imageop.c:988
#define DT_IOP_HEADER_MENU_OPEN
Definition imageop.c:114
static int default_process(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, const struct dt_dev_pixelpipe_iop_t *piece, const void *const i, void *const o)
Definition imageop.c:294
static gboolean dt_iop_colorspace_is_rgb(const dt_iop_colorspace_type_t cst)
Definition imageop.h:243
#define IOP_GUI_FREE
Definition imageop.h:641
@ IOP_MODULE_ICON
Definition imageop.h:106
@ IOP_MODULE_PRESETS
Definition imageop.h:111
@ IOP_MODULE_RESET
Definition imageop.h:110
@ IOP_MODULE_LABEL
Definition imageop.h:107
@ IOP_MODULE_MASK
Definition imageop.h:108
@ IOP_MODULE_LAST
Definition imageop.h:112
@ IOP_MODULE_SWITCH
Definition imageop.h:105
@ IOP_MODULE_INSTANCE
Definition imageop.h:109
@ IOP_FLAGS_HIDDEN
Definition imageop.h:200
@ IOP_FLAGS_DEPRECATED
Definition imageop.h:198
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:197
@ IOP_FLAGS_ALLOW_TILING
Definition imageop.h:199
@ IOP_FLAGS_ONE_INSTANCE
Definition imageop.h:202
@ IOP_FLAGS_NO_MASKS
Definition imageop.h:205
@ IOP_GROUP_TECHNICAL
Definition imageop.h:173
#define DT_INTROSPECTION_VERSION
@ DT_INTROSPECTION_TYPE_BOOL
@ DT_INTROSPECTION_TYPE_ENUM
@ DT_INTROSPECTION_TYPE_OPAQUE
@ DT_INTROSPECTION_TYPE_NONE
@ DT_INTROSPECTION_TYPE_ARRAY
@ DT_INTROSPECTION_TYPE_CHAR
@ DT_INTROSPECTION_TYPE_FLOAT
@ DT_INTROSPECTION_TYPE_UNION
@ DT_INTROSPECTION_TYPE_UINT
@ DT_INTROSPECTION_TYPE_USHORT
@ DT_INTROSPECTION_TYPE_INT8
@ DT_INTROSPECTION_TYPE_STRUCT
@ DT_INTROSPECTION_TYPE_FLOATCOMPLEX
@ DT_INTROSPECTION_TYPE_INT
gboolean dt_ioppr_move_iop_after(struct dt_develop_t *dev, dt_iop_module_t *module, dt_iop_module_t *module_prev)
Move a module instance after another module in the pipe.
Definition iop_order.c:2257
gboolean dt_ioppr_move_iop_before(struct dt_develop_t *dev, dt_iop_module_t *module, dt_iop_module_t *module_next)
Move a module instance before another module in the pipe.
Definition iop_order.c:2225
int dt_ioppr_check_iop_order(dt_develop_t *dev, const int32_t imgid, const char *msg)
Debug helper to validate the current order for a develop context.
Definition iop_order.c:2383
static const float x
float *const restrict const size_t k
void dt_masks_iop_use_same_as(struct dt_iop_module_t *module, struct dt_iop_module_t *src)
void dt_masks_reset_form_gui(void)
@ DT_MASKS_EVENT_RESET
Definition masks.h:164
dt_masks_form_t * dt_masks_get_from_id_ext(GList *forms, int id)
dt_masks_form_t * dt_masks_get_from_id(dt_develop_t *dev, int id)
void dt_masks_form_delete(struct dt_iop_module_t *module, dt_masks_form_t *grp, dt_masks_form_t *form)
void dt_masks_group_update_name(dt_iop_module_t *module)
uint64_t dt_masks_group_get_hash(uint64_t hash, dt_masks_form_t *form)
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
char * dt_pixelpipe_get_pipe_name(dt_dev_pixelpipe_type_t pipe_type)
void dt_sentry_record_module_usage(const char *category, const char *name)
Definition sentry.c:633
char dt_dev_operation_t[20]
Definition settings.h:38
void dt_control_signal_disconnect_all(const struct dt_control_signal_t *ctlsig, gpointer user_data)
Definition signal.c:482
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
Definition signal.h:387
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
Definition signal.h:366
@ DT_SIGNAL_MASK_CHANGED
Definition signal.h:312
@ DT_SIGNAL_DEVELOP_MASKS_GUI_CHANGED
Definition signal.h:316
@ DT_SIGNAL_CONTROL_PICKERDATA_READY
This signal is raised when new color picker data are available in darkroom. no param,...
Definition signal.h:293
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
Definition signal.h:376
struct _GtkWidget GtkWidget
Definition splash.h:29
const float uint32_t state[4]
const float const float param
unsigned __int64 uint64_t
Definition strptime.c:75
struct dt_gui_gtk_t * gui
Definition darktable.h:803
struct dt_collection_t * collection
Definition darktable.h:809
GList * iop
Definition darktable.h:789
const struct dt_database_t * db
Definition darktable.h:807
struct dt_control_signal_t * signals
Definition darktable.h:802
struct dt_opencl_t * opencl
Definition darktable.h:813
int32_t unmuted
Definition darktable.h:788
struct dt_develop_t * develop
Definition darktable.h:798
GtkAccelGroup * darkroom_accels
Definition bauhaus.h:130
gpointer data
Definition bauhaus.h:137
char label[256]
Definition bauhaus.h:186
dt_bauhaus_data_t data
Definition bauhaus.h:218
dt_gui_module_t *gpointer field
Definition bauhaus.h:181
dt_introspection_type_t field_type
Definition bauhaus.h:183
dt_bauhaus_type_t type
Definition bauhaus.h:177
struct dt_develop_blend_params_t * blend_params
Definition dev_history.h:56
dt_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
dt_dev_pixelpipe_type_t type
int32_t gui_attached
Definition develop.h:162
struct dt_develop_t::@19 color_picker
Authoritative darkroom color-picker state.
GList * iop
Definition develop.h:285
gboolean update_pending
Definition develop.h:394
GtkWidget * widget
Definition develop.h:390
struct dt_iop_module_t * gui_module
Definition develop.h:165
GList * history
Definition develop.h:275
GList * alliop
Definition develop.h:287
struct dt_iop_module_t *struct dt_iop_color_picker_t * picker
Definition develop.h:389
gboolean enabled
Definition develop.h:393
GList * forms
Definition develop.h:327
void * blend_params
Definition develop.h:313
dt_accels_t * accels
Definition gtk.h:199
GtkMenu * presets_popup_menu
Definition gtk.h:170
GtkWidget * has_scroll_focus
Definition gtk.h:234
dt_ui_t * ui
Definition gtk.h:165
GtkWidget * scroll_to[2]
Definition gtk.h:227
GtkWidget * scroll_to_header_once
Definition gtk.h:228
The dt_gui_module_t type is the intersection between a dt_lib_module_t and a dt_iop_module_t structur...
union dt_introspection_field_t * field
dt_introspection_type_t type
dt_introspection_type_enum_tuple_t * values
dt_introspection_type_t type
union dt_introspection_field_t ** fields
union dt_introspection_field_t ** fields
uint32_t filters
Definition format.h:60
dt_iop_module_t * self
Definition imageop.c:120
GModule *dt_dev_operation_t op
Definition imageop.h:260
dt_iop_params_t * default_params
Definition imageop.h:344
struct dt_develop_blend_params_t * blend_params
Definition imageop.h:353
struct dt_develop_t * dev
Definition imageop.h:333
gboolean bypass_cache
Definition imageop.h:300
int(* process_plain)(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, const struct dt_dev_pixelpipe_iop_t *piece, const void *const i, void *const o)
Definition imageop.h:410
GtkWidget * expander
Definition imageop.h:382
struct dt_develop_blend_params_t * default_blendop_params
Definition imageop.h:353
int32_t params_size
Definition imageop.h:346
dt_iop_params_t * params
Definition imageop.h:344
Region of interest passed through the pixelpipe.
Definition imageop.h:72
double scale
Definition imageop.h:74
int inited
Definition opencl.h:259
void dt_telemetry_record_module_usage(const char *category, const char *name)
Definition telemetry.c:460
void dtgtk_togglebutton_set_paint(GtkDarktableToggleButton *button, DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
GtkWidget * dtgtk_togglebutton_new(DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
#define DTGTK_TOGGLEBUTTON(obj)
dt_bauhaus_combobox_data_t combobox
Definition bauhaus.h:160
dt_introspection_type_float_complex_t FloatComplex
dt_introspection_type_header_t header
dt_introspection_type_ushort_t UShort
dt_introspection_type_float_t Float
dt_introspection_type_union_t Union
dt_introspection_type_uint_t UInt
dt_introspection_type_int_t Int
dt_introspection_type_char_t Char
dt_introspection_type_int8_t Int8
dt_introspection_type_array_t Array
dt_introspection_type_enum_t Enum
dt_introspection_type_struct_t Struct
char * dt_get_help_url(char *name)
void dt_ui_container_add_widget(dt_ui_t *ui, const dt_ui_container_t c, GtkWidget *w)
@ DT_UI_CONTAINER_SIZE
@ DT_UI_CONTAINER_PANEL_RIGHT_CENTER