Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
ioporder.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2019-2021 Pascal Obry.
4 Copyright (C) 2020-2021 Aldric Renaudin.
5 Copyright (C) 2020-2021 Hubert Kowalski.
6 Copyright (C) 2020 Philippe Weyland.
7 Copyright (C) 2021, 2023, 2025-2026 Aurélien PIERRE.
8 Copyright (C) 2021 Bill Ferguson.
9 Copyright (C) 2021 Chris Elston.
10 Copyright (C) 2021 Diederik Ter Rahe.
11 Copyright (C) 2022 Martin Bařinka.
12
13 darktable is free software: you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation, either version 3 of the License, or
16 (at your option) any later version.
17
18 darktable is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with darktable. If not, see <http://www.gnu.org/licenses/>.
25*/
26
27#include "develop/imageop_gui.h"
31#include "develop/iop_order.h"
32#include "system/macros.h"
34#include "control/signal.h"
35#include "develop/develop.h"
36#include "pixel/format.h"
37#include "develop/imageop.h"
39#include "widgets/button.h"
40#include "widgets/paint.h"
42#include "gui/application.h"
43#include "gui/presets.h"
44#include "libs/lib.h"
45
46#include <gtk/gtk.h>
47#include <limits.h>
48#include <math.h>
49#include <sqlite3.h>
50#include <stdlib.h>
51#include <string.h>
52#include "widgets/label.h"
53#include "widgets/popup.h"
55
56DT_MODULE(1)
57
58#define DT_IOPORDER_GRAPH_NODE_WIDTH DT_PIXEL_APPLY_DPI(260)
59#define DT_IOPORDER_GRAPH_NODE_STEP DT_PIXEL_APPLY_DPI(300)
60#define DT_IOPORDER_GRAPH_LEFT_MARGIN DT_PIXEL_APPLY_DPI(60)
61#define DT_IOPORDER_GRAPH_TOP_MARGIN DT_PIXEL_APPLY_DPI(68)
62#define DT_IOPORDER_GRAPH_MIN_WIDTH DT_PIXEL_APPLY_DPI(640)
63#define DT_IOPORDER_GRAPH_HEIGHT DT_PIXEL_APPLY_DPI(540)
64#define DT_IOPORDER_BAND_HEIGHT DT_PIXEL_APPLY_DPI(26)
65#define DT_IOPORDER_MASK_ARROW_OFFSET DT_PIXEL_APPLY_DPI(12)
66#define DT_IOPORDER_MASK_BOTTOM_MARGIN DT_PIXEL_APPLY_DPI(56)
67
72
84
85static const GtkTargetEntry _ioporder_target_list[] = {
86 { "ioporder-node", GTK_TARGET_SAME_APP, DT_IOPORDER_DND_TARGET_NODE }
87};
88static const guint _ioporder_target_count = G_N_ELEMENTS(_ioporder_target_list);
89
104
120
121static void _ioporder_drag_data_get(GtkWidget *widget, GdkDragContext *context,
122 GtkSelectionData *selection_data, guint info, guint time,
123 gpointer user_data);
124static void _ioporder_drag_begin(GtkWidget *widget, GdkDragContext *context, gpointer user_data);
125static void _ioporder_drag_end(GtkWidget *widget, GdkDragContext *context, gpointer user_data);
126static gboolean _ioporder_drag_motion(GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
127 guint time, gpointer user_data);
128static gboolean _ioporder_drag_drop(GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
129 guint time, gpointer user_data);
130static void _ioporder_drag_leave(GtkWidget *widget, GdkDragContext *dc, guint time, gpointer user_data);
131static void _ioporder_drag_data_received(GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
132 GtkSelectionData *selection_data, guint info, guint time,
133 gpointer user_data);
134static void _ioporder_free_graph_node(gpointer data);
135static void _ioporder_popup_destroy(GtkWidget *widget, gpointer user_data);
136
146static gboolean _ioporder_module_in_history(const dt_iop_module_t *module)
147{
148 if(IS_NULL_PTR(dt_dev_get_global()) || !module) return FALSE;
149
150 /* Walk the whole history stack and look for the exact module instance. */
151 for(GList *history = g_list_last(dt_dev_get_global()->history); history; history = g_list_previous(history))
152 {
153 const dt_dev_history_item_t *hitem = (dt_dev_history_item_t *)history->data;
154 if(hitem && hitem->module == module) return TRUE;
155 }
156
157 return FALSE;
158}
159
170{
171 if(IS_NULL_PTR(module)) return FALSE;
172 if(dt_iop_is_hidden((dt_iop_module_t *)module)) return FALSE;
173 return _ioporder_module_in_history(module) || module->enabled;
174}
175
186{
187 if(IS_NULL_PTR(dt_dev_get_global()) || IS_NULL_PTR(dt_dev_get_global()->preview_pipe) || !module) return NULL;
188
189 /* Search the preview pipe nodes for the piece instantiated from this module. */
190 for(GList *nodes = dt_dev_get_global()->preview_pipe->nodes; nodes; nodes = g_list_next(nodes))
191 {
193 if(piece && piece->module == module) return piece;
194 }
195
196 return NULL;
197}
198
205static const char *_ioporder_type_to_string(const dt_iop_buffer_type_t datatype)
206{
207 switch(datatype)
208 {
209 case TYPE_FLOAT:
210 return _("32-bit float");
211 case TYPE_UINT16:
212 return _("16-bit integer");
213 case TYPE_UINT8:
214 return _("8-bit integer");
215 case TYPE_UNKNOWN:
216 default:
217 return _("unknown type");
218 }
219}
220
228{
229 switch(cst)
230 {
231 case IOP_CS_RAW:
232 return _("RAW");
233 case IOP_CS_RGB:
234 return _("RGB");
236 return _("Display RGB");
237 case IOP_CS_LAB:
238 return _("CIE Lab");
239 case IOP_CS_LCH:
240 return _("LCh");
241 case IOP_CS_HSL:
242 return _("HSL");
243 case IOP_CS_JZCZHZ:
244 return _("JzCzHz");
245 case IOP_CS_NONE:
246 return _("none");
247 default:
248 return _("unknown colorspace");
249 }
250}
251
259{
260 if(IS_NULL_PTR(dsc)) return g_strdup(_("no runtime descriptor"));
261
262 if(dsc->cst != IOP_CS_RAW)
263 {
264 if(dsc->filters == 9u) return g_strdup(_("X-Trans passthrough"));
265 if(dsc->filters != 0u) return g_strdup(_("Bayer passthrough"));
266 return g_strdup(_("no RAW flags"));
267 }
268
269 if(dsc->filters == 9u) return g_strdup(_("RAW X-Trans"));
270 if(dsc->filters != 0u) return g_strdup(_("RAW Bayer"));
271 return g_strdup(_("RAW monochrome"));
272}
273
285static gchar *_ioporder_descriptor_to_text(const char *prefix, const dt_iop_buffer_dsc_t *dsc,
286 const char *display_colorspace)
287{
288 if(IS_NULL_PTR(dsc))
289 return g_strdup_printf(_("%s:\n- runtime descriptor unavailable"), prefix);
290
291 gchar *raw_flags = _ioporder_raw_flags_to_string(dsc);
292 gchar *text = g_strdup_printf(_("%s:\n\t- %s\n\t- %u channels\n\t- %s\n"
293 "\t- max RGB:\n\t\t%.3f\n\t\t%.3f\n\t\t%.3f\n\t- %s"),
294 prefix, _ioporder_type_to_string(dsc->datatype), dsc->channels,
295 display_colorspace ? display_colorspace : _("runtime unavailable"),
296 dsc->processed_maximum[0], dsc->processed_maximum[1], dsc->processed_maximum[2],
297 raw_flags);
298 dt_free(raw_flags);
299 return text;
300}
301
317 const gboolean colorin_crossed,
318 const dt_iop_module_t *module,
319 const dt_iop_buffer_dsc_t *dsc,
320 const gboolean after_module)
321{
323
327 if(dsc->cst == IOP_CS_RGB)
328 {
329 const gboolean in_pipeline_rgb = colorin_crossed || (after_module && !strcmp(module->op, "colorin"));
330 if(raw_input && !in_pipeline_rgb) return DT_IOPORDER_RUNTIME_BAND_SENSOR_RGB;
332 }
334}
335
347 const dt_iop_buffer_dsc_t *dsc)
348{
349 switch(kind)
350 {
352 return _("RAW");
354 return _("Sensor RGB");
356 return _("Pipeline RGB");
358 return _("Display RGB");
360 return _("CIE Lab 1976");
362 return _("runtime unavailable");
364 return dsc ? _ioporder_colorspace_to_string(dsc->cst) : _("runtime unavailable");
366 default:
367 return _("runtime unavailable");
368 }
369}
370
378{
379 if(IS_NULL_PTR(color)) return;
380
381 switch(kind)
382 {
384 *color = (GdkRGBA){ 0.30, 0.30, 0.30, 0.90 };
385 break;
387 *color = (GdkRGBA){ 0.14, 0.56, 0.20, 0.96 };
388 break;
390 *color = (GdkRGBA){ 0.10, 0.38, 0.78, 0.96 };
391 break;
393 *color = (GdkRGBA){ 0.78, 0.38, 0.10, 0.96 };
394 break;
396 *color = (GdkRGBA){ 0.72, 0.54, 0.10, 0.96 };
397 break;
401 default:
402 *color = (GdkRGBA){ 0.38, 0.38, 0.38, 0.90 };
403 break;
404 }
405}
406
419 const dt_iop_order_iccprofile_info_t *profile_b)
420{
421 if(profile_a == profile_b) return TRUE;
422 if(IS_NULL_PTR(profile_a) || IS_NULL_PTR(profile_b)) return FALSE;
423
424 return profile_a->type == profile_b->type && !strcmp(profile_a->filename, profile_b->filename);
425}
426
442 const gboolean colorin_crossed,
443 const dt_iop_module_t *module,
444 const dt_iop_buffer_dsc_t *dsc,
445 const gboolean after_module)
446{
447 if(IS_NULL_PTR(dt_dev_get_global()) || IS_NULL_PTR(dt_dev_get_global()->preview_pipe) || !module || IS_NULL_PTR(dsc)) return NULL;
448 if(!dt_iop_colorspace_is_rgb(dsc->cst)) return NULL;
449
450 if(dsc->cst == IOP_CS_RGB_DISPLAY)
452
453 const gboolean in_pipeline_rgb = colorin_crossed || (after_module && !strcmp(module->op, "colorin"));
454 if(!in_pipeline_rgb)
456
457 (void)raw_input;
459}
460
472static gchar *_ioporder_runtime_band_text(const char *label,
473 const dt_iop_order_iccprofile_info_t *profile_info)
474{
475 if(IS_NULL_PTR(label)) return g_strdup(_("runtime unavailable"));
476 if(IS_NULL_PTR(profile_info) || profile_info->type == DT_COLORSPACE_NONE) return g_strdup(label);
477
478 const char *profile_name = dt_colorspaces_get_name(profile_info->type, profile_info->filename);
479 if(IS_NULL_PTR(profile_name) || profile_name[0] == '\0') return g_strdup(label);
480
481 return g_strdup_printf("%s - %s", label, profile_name);
482}
483
494{
496
497 if(IS_NULL_PTR(dt_dev_get_global()) || !dt_dev_get_global()->iop_order_list)
498 {
499 d->current_mode = DT_IOP_ORDER_ANSEL_RAW;
500 return g_strdup(_(dt_iop_order_string(DT_IOP_ORDER_ANSEL_RAW)));
501 }
502
504
506 {
507 d->current_mode = kind;
508 return g_strdup(_(dt_iop_order_string(kind)));
509 }
510
511 gchar *iop_order_list = dt_ioppr_serialize_text_iop_order_list(dt_dev_get_global()->iop_order_list);
512 gchar *name = NULL;
513 int index = 0;
514
515 // Shipped presets first, so two presets with identical content resolve to the shipped one --
516 // the loop below stops at the first match.
517 GList *presets = dt_preset_repository_list_all_versions("ioporder");
518
519 /* Compare the current order text against every saved preset serialization. */
520 for(GList *l = presets; l; l = g_list_next(l))
521 {
522 const dt_module_preset_t *pr = (const dt_module_preset_t *)l->data;
523 const char *params = (const char *)pr->op_params;
524 const int32_t params_len = pr->op_params_size;
525 const char *preset_name = pr->name;
526 GList *preset_list = dt_ioppr_deserialize_iop_order_list(params, params_len);
527 gchar *preset_text = dt_ioppr_serialize_text_iop_order_list(preset_list);
528 g_list_free_full(preset_list, dt_free_gpointer);
529 preset_list = NULL;
530 index++;
531
532 if(!g_strcmp0(iop_order_list, preset_text))
533 {
534 d->current_mode = index;
535 name = g_strdup(preset_name);
536 dt_free(preset_text);
537 break;
538 }
539
540 dt_free(preset_text);
541 }
542 g_list_free_full(presets, dt_module_preset_free);
543 dt_free(iop_order_list);
544
545 if(name) return name;
546
547 d->current_mode = DT_IOP_ORDER_CUSTOM;
548 return g_strdup(_(dt_iop_order_string(DT_IOP_ORDER_CUSTOM)));
549}
550
561{
563 if(!d->preset_combo || IS_NULL_PTR(d->toolbar_label)) return;
564
565 gchar *current_name = _ioporder_get_current_order_name(self);
566
567 gtk_label_set_text(GTK_LABEL(d->toolbar_label), current_name);
568
569 d->refreshing_toolbar = TRUE;
570 gtk_combo_box_text_remove_all(GTK_COMBO_BOX_TEXT(d->preset_combo));
571 gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(d->preset_combo), "__custom__", _("custom order"));
572
573 gchar *active_id = g_strdup("__custom__");
574
575 GList *presets = dt_preset_repository_list_for_version("ioporder", self->version());
576
577 /* Rebuild the preset list in DB order and keep the current one selected. */
578 for(GList *l = presets; l; l = g_list_next(l))
579 {
580 const dt_module_preset_t *pr = (const dt_module_preset_t *)l->data;
581 const char *preset_name = pr->name;
582 gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(d->preset_combo), preset_name, preset_name);
583 if(!g_strcmp0(current_name, preset_name))
584 {
585 dt_free(active_id);
586 active_id = g_strdup(preset_name);
587 }
588 }
589 g_list_free_full(presets, dt_module_preset_free);
590
591 gtk_combo_box_set_active_id(GTK_COMBO_BOX(d->preset_combo), active_id);
592 d->refreshing_toolbar = FALSE;
593
594 dt_free(active_id);
595 dt_free(current_name);
596}
597
604{
605 if(IS_NULL_PTR(d) || IS_NULL_PTR(d->graph_fixed)) return;
606
607 GList *children = gtk_container_get_children(GTK_CONTAINER(d->graph_fixed));
608 for(GList *iter = children; iter; iter = g_list_next(iter))
609 {
610 gtk_widget_destroy(GTK_WIDGET(iter->data));
611 }
612 g_list_free(children);
613 children = NULL;
614
615 g_list_free_full(d->nodes, _ioporder_free_graph_node);
616 d->nodes = NULL;
617}
618
631static void _ioporder_popup_destroy(GtkWidget *widget, gpointer user_data)
632{
633 dt_lib_ioporder_t *d = (dt_lib_ioporder_t *)user_data;
634 if(IS_NULL_PTR(d)) return;
635
636 g_list_free_full(d->nodes, _ioporder_free_graph_node);
637 d->nodes = NULL;
638
639 d->window = NULL;
640 d->toolbar_label = NULL;
641 d->preset_combo = NULL;
642 d->graph_scroll = NULL;
643 d->graph_overlay = NULL;
644 d->graph_drawing = NULL;
645 d->graph_fixed = NULL;
646 d->drag_source = NULL;
647 d->drag_dest = NULL;
648}
649
658static void _ioporder_free_graph_node(gpointer data)
659{
661 if(IS_NULL_PTR(node)) return;
662
663 dt_free(node->endpoint_label);
664 dt_free(node);
665}
666
677{
678 if(!DTGTK_IS_TOGGLEBUTTON(widget) || !module) return;
679
680 if(module->hide_enable_button)
681 {
683 }
684 else
685 {
687 }
688}
689
699static void _ioporder_node_toggle_enable(GtkToggleButton *togglebutton, gpointer user_data)
700{
701 const dt_ioporder_graph_node_t *node = (const dt_ioporder_graph_node_t *)user_data;
702 if(!node || !node->module->gui || !GTK_IS_TOGGLE_BUTTON(node->module->gui->off)) return;
703
704 const gboolean active = gtk_toggle_button_get_active(togglebutton);
705 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(node->module->gui->off)) != active)
706 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(node->module->gui->off), active);
707}
708
715static void _ioporder_node_toggle_mask(GtkToggleButton *togglebutton, gpointer user_data)
716{
717 const dt_ioporder_graph_node_t *node = (const dt_ioporder_graph_node_t *)user_data;
718 if(!node || !node->module->gui || !GTK_IS_TOGGLE_BUTTON(node->module->gui->mask_indicator)) return;
719
720 const gboolean active = gtk_toggle_button_get_active(togglebutton);
721 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(node->module->gui->mask_indicator)) != active)
722 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(node->module->gui->mask_indicator), active);
723}
724
731static void _ioporder_node_show_presets(GtkButton *button, gpointer user_data)
732{
733 const dt_ioporder_graph_node_t *node = (const dt_ioporder_graph_node_t *)user_data;
734 if(!node || !node->module) return;
735
737 dt_gui_menu_popup(dt_gui_get_global()->presets_popup_menu, GTK_WIDGET(button),
738 GDK_GRAVITY_SOUTH_EAST, GDK_GRAVITY_NORTH_EAST);
739
741}
742
752 const char *display_in,
753 const char *display_out)
754{
756 node->module = module;
757 node->piece = piece;
758
759 GtkWidget *event_box = gtk_event_box_new();
760 GtkWidget *frame = gtk_frame_new(NULL);
761 GtkWidget *body = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
762 GtkWidget *header = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING / 2.);
763 gchar *clean_name = delete_underscore(module->name());
764 gchar **split_name = g_strsplit(clean_name, "-", -1);
765 gchar *module_name = g_strjoinv(" ", split_name);
766 GtkWidget *label = gtk_label_new(module_name);
767 gchar *instance_name = dt_dev_get_multi_name(module);
768 GtkWidget *instance = gtk_label_new(instance_name);
772
773 dt_gui_add_class(frame, "dt_module_frame");
774 dt_gui_add_class(frame, "dt_iop_module");
775 gtk_widget_set_name(body, "module-header");
776
777 dt_gui_add_class(enable, "dt_iop_enable_button");
778
779 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
780
781 gtk_widget_set_size_request(event_box, DT_IOPORDER_GRAPH_NODE_WIDTH, -1);
782 gtk_widget_set_hexpand(event_box, FALSE);
783 gtk_widget_set_halign(event_box, GTK_ALIGN_START);
784
785 dt_capitalize_label(module_name);
786 gtk_label_set_text(GTK_LABEL(label), module_name);
787 gtk_widget_set_halign(label, GTK_ALIGN_START);
788 gtk_widget_set_valign(label, GTK_ALIGN_CENTER);
789 gtk_label_set_xalign(GTK_LABEL(label), 0.0f);
790 gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_END);
791 gtk_widget_set_name(label, "iop-panel-label");
792
793 gtk_widget_set_halign(instance, GTK_ALIGN_START);
794 gtk_widget_set_visible(instance, instance_name[0] != '\0');
795 gtk_label_set_xalign(GTK_LABEL(instance), 0.0f);
796 gtk_label_set_ellipsize(GTK_LABEL(instance), PANGO_ELLIPSIZE_MIDDLE);
797 gtk_label_set_line_wrap(GTK_LABEL(instance), FALSE);
798
800 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(enable), module->enabled);
801 gtk_widget_set_sensitive(enable, !module->hide_enable_button);
802 gtk_widget_set_valign(enable, GTK_ALIGN_CENTER);
803
804 const gboolean show_masks = (module->flags() & IOP_FLAGS_SUPPORTS_BLENDING) == IOP_FLAGS_SUPPORTS_BLENDING
805 && module->blend_params
806 && module->blend_params->mask_mode > DEVELOP_MASK_ENABLED;
807 const gboolean show_mask_preview = module->request_mask_display
808 & (DT_DEV_PIXELPIPE_DISPLAY_MASK | DT_DEV_PIXELPIPE_DISPLAY_CHANNEL);
809 gtk_widget_set_visible(mask, show_masks);
810 gtk_widget_set_sensitive(mask, show_masks && module->enabled);
811 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mask), show_mask_preview);
812 gtk_widget_set_valign(mask, GTK_ALIGN_CENTER);
813 gtk_widget_set_valign(presets, GTK_ALIGN_CENTER);
814
815 gtk_widget_set_tooltip_text(enable, _("toggle module"));
816 gtk_widget_set_tooltip_text(mask, _("display mask"));
817 gtk_widget_set_tooltip_text(presets, _("module presets"));
818
819 gtk_box_pack_start(GTK_BOX(header), enable, FALSE, FALSE, 0);
820 gtk_box_pack_start(GTK_BOX(header), label, TRUE, TRUE, 0);
821 gtk_box_pack_end(GTK_BOX(header), presets, FALSE, FALSE, 0);
822 gtk_box_pack_end(GTK_BOX(header), mask, FALSE, FALSE, 0);
823
824 gchar *text_in = _ioporder_descriptor_to_text(_("In"), piece ? &piece->dsc_in : NULL, display_in);
825 gchar *text_out = _ioporder_descriptor_to_text(_("Out"), piece ? &piece->dsc_out : NULL, display_out);
826 GtkWidget *info_in = gtk_label_new(text_in);
827 GtkWidget *info_out = gtk_label_new(text_out);
828 dt_free(text_in);
829 dt_free(text_out);
830 dt_free(instance_name);
831 g_strfreev(split_name);
832 dt_free(module_name);
833 dt_free(clean_name);
834
835 gtk_label_set_xalign(GTK_LABEL(info_in), 0.0f);
836 gtk_label_set_xalign(GTK_LABEL(info_out), 0.0f);
837 gtk_label_set_line_wrap(GTK_LABEL(info_in), TRUE);
838 gtk_label_set_line_wrap(GTK_LABEL(info_out), TRUE);
839 gtk_label_set_justify(GTK_LABEL(info_in), GTK_JUSTIFY_LEFT);
840 gtk_label_set_justify(GTK_LABEL(info_out), GTK_JUSTIFY_LEFT);
841
842 gtk_box_pack_start(GTK_BOX(body), header, FALSE, FALSE, 0);
843 gtk_box_pack_start(GTK_BOX(body), instance, FALSE, FALSE, 0);
844 gtk_box_pack_start(GTK_BOX(body), info_in, FALSE, FALSE, 0);
845 gtk_box_pack_start(GTK_BOX(body), info_out, FALSE, FALSE, 0);
846 gtk_container_add(GTK_CONTAINER(frame), body);
847 gtk_container_add(GTK_CONTAINER(event_box), frame);
848
849 g_signal_connect(enable, "toggled", G_CALLBACK(_ioporder_node_toggle_enable), node);
850 g_signal_connect(mask, "toggled", G_CALLBACK(_ioporder_node_toggle_mask), node);
851 g_signal_connect(presets, "clicked", G_CALLBACK(_ioporder_node_show_presets), node);
852
853 node->event_box = event_box;
854 node->header = header;
855 node->title = label;
856 node->instance = instance;
857 node->info_in = info_in;
858 node->info_out = info_out;
859
860 g_object_set_data(G_OBJECT(event_box), "dt-ioporder-module", module);
861
862 return node;
863}
864
875{
877 node->is_endpoint = TRUE;
878 node->endpoint_label = g_strdup(label);
879
880 GtkWidget *event_box = gtk_event_box_new();
881 GtkWidget *frame = gtk_frame_new(NULL);
882 GtkWidget *body = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
883 GtkWidget *title = gtk_label_new(label);
884
885 dt_gui_add_class(frame, "dt_module_frame");
886 dt_gui_add_class(frame, "dt_iop_module");
887 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
888 gtk_widget_set_size_request(event_box, DT_PIXEL_APPLY_DPI(180), DT_PIXEL_APPLY_DPI(64));
889 gtk_widget_set_halign(event_box, GTK_ALIGN_START);
890 gtk_widget_set_valign(event_box, GTK_ALIGN_CENTER);
891 gtk_widget_set_margin_top(body, DT_PIXEL_APPLY_DPI(12));
892 gtk_widget_set_margin_bottom(body, DT_PIXEL_APPLY_DPI(12));
893 gtk_widget_set_margin_start(body, DT_PIXEL_APPLY_DPI(12));
894 gtk_widget_set_margin_end(body, DT_PIXEL_APPLY_DPI(12));
895 gtk_widget_set_name(body, "ioporder-endpoint");
896
898 gtk_label_set_text(GTK_LABEL(title), node->endpoint_label);
899 gtk_widget_set_halign(title, GTK_ALIGN_CENTER);
900 gtk_widget_set_valign(title, GTK_ALIGN_CENTER);
901 gtk_label_set_xalign(GTK_LABEL(title), 0.5f);
902 gtk_widget_set_name(title, "iop-panel-label");
903
904 gtk_box_pack_start(GTK_BOX(body), title, TRUE, TRUE, 0);
905 gtk_container_add(GTK_CONTAINER(frame), body);
906 gtk_container_add(GTK_CONTAINER(event_box), frame);
907
908 node->event_box = event_box;
909 node->header = body;
910 node->title = title;
911
912 return node;
913}
914
925{
927 if(IS_NULL_PTR(d) || IS_NULL_PTR(d->graph_fixed) || IS_NULL_PTR(d->graph_overlay)) return;
928
931
933 int visible_count = 0;
934 const gboolean raw_input = dt_dev_get_global() && dt_dev_get_global()->image_storage.dsc.cst == IOP_CS_RAW;
935 gboolean colorin_crossed = FALSE;
936 const int endpoint_height = DT_PIXEL_APPLY_DPI(64);
937 const int base_x = x;
938 int screen_x = x;
939 int endpoint_y = (DT_IOPORDER_GRAPH_HEIGHT) / 2;
940 int module_header_center_y = 0;
941
943
944 /* Walk the sorted module list and instantiate one graph node per visible module. */
945 for(GList *modules = dt_dev_get_global()->iop; modules; modules = g_list_next(modules))
946 {
947 dt_iop_module_t *module = (dt_iop_module_t *)modules->data;
948 if(!_ioporder_module_is_graph_visible(module)) continue;
949
951 const dt_iop_buffer_dsc_t *const dsc_in = piece ? &piece->dsc_in : NULL;
952 const dt_iop_buffer_dsc_t *const dsc_out = piece ? &piece->dsc_out : NULL;
953 const char *display_in
954 = _ioporder_runtime_band_label(_ioporder_runtime_band_kind(raw_input, colorin_crossed, module, dsc_in, FALSE),
955 dsc_in);
956 const char *display_out
957 = _ioporder_runtime_band_label(_ioporder_runtime_band_kind(raw_input, colorin_crossed, module, dsc_out, TRUE),
958 dsc_out);
959 dt_ioporder_graph_node_t *node = _ioporder_create_graph_node(module, piece, display_in, display_out);
960 d->nodes = g_list_append(d->nodes, node);
961
962 gtk_drag_source_set(node->event_box, GDK_BUTTON1_MASK, _ioporder_target_list, _ioporder_target_count,
963 GDK_ACTION_COPY);
964 gtk_drag_dest_set(node->event_box, GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
966 g_signal_connect(node->event_box, "drag-begin", G_CALLBACK(_ioporder_drag_begin), self);
967 g_signal_connect(node->event_box, "drag-data-get", G_CALLBACK(_ioporder_drag_data_get), self);
968 g_signal_connect(node->event_box, "drag-end", G_CALLBACK(_ioporder_drag_end), self);
969 g_signal_connect(node->event_box, "drag-motion", G_CALLBACK(_ioporder_drag_motion), self);
970 g_signal_connect(node->event_box, "drag-drop", G_CALLBACK(_ioporder_drag_drop), self);
971 g_signal_connect(node->event_box, "drag-data-received", G_CALLBACK(_ioporder_drag_data_received), self);
972 g_signal_connect(node->event_box, "drag-leave", G_CALLBACK(_ioporder_drag_leave), self);
973
974 gtk_fixed_put(GTK_FIXED(d->graph_fixed), node->event_box, x, DT_IOPORDER_GRAPH_TOP_MARGIN);
975 gtk_widget_show_all(node->event_box);
976 if(module_header_center_y == 0)
977 {
978 GtkRequisition minimum = { 0 }, natural = { 0 };
979 gtk_widget_get_preferred_size(node->header, &minimum, &natural);
980 module_header_center_y = DT_IOPORDER_GRAPH_TOP_MARGIN + natural.height / 2;
981 }
982
984 visible_count++;
985 if(!strcmp(module->op, "colorin")) colorin_crossed = TRUE;
986 }
987
988 if(module_header_center_y > 0)
989 endpoint_y = module_header_center_y - endpoint_height / 2;
990
991 dt_ioporder_graph_node_t *base_node = _ioporder_create_endpoint_node(_("base image"));
992 d->nodes = g_list_prepend(d->nodes, base_node);
993 gtk_fixed_put(GTK_FIXED(d->graph_fixed), base_node->event_box, base_x, endpoint_y);
994 gtk_widget_show_all(base_node->event_box);
995 visible_count++;
996
997 screen_x = x;
999 d->nodes = g_list_append(d->nodes, screen_node);
1000 gtk_fixed_put(GTK_FIXED(d->graph_fixed), screen_node->event_box, screen_x, endpoint_y);
1001 gtk_widget_show_all(screen_node->event_box);
1002 visible_count++;
1003
1004 const int total_width = MAX(DT_IOPORDER_GRAPH_MIN_WIDTH,
1007
1008 gtk_widget_set_size_request(d->graph_overlay, total_width, DT_IOPORDER_GRAPH_HEIGHT);
1009 gtk_widget_set_size_request(d->graph_drawing, total_width, DT_IOPORDER_GRAPH_HEIGHT);
1010 gtk_widget_set_size_request(d->graph_fixed, total_width, DT_IOPORDER_GRAPH_HEIGHT);
1011 if(d->window && d->graph_scroll)
1012 {
1013 GdkDisplay *display = gtk_widget_get_display(d->window);
1014 GdkWindow *window = gtk_widget_get_window(d->window);
1015 if(IS_NULL_PTR(window))
1016 window = gtk_widget_get_window(dt_gui_main_window());
1017
1018 GdkMonitor *monitor = (display && window) ? gdk_display_get_monitor_at_window(display, window) : NULL;
1019 if(IS_NULL_PTR(monitor) && display)
1020 {
1021 monitor = gdk_display_get_primary_monitor(display);
1022 if(IS_NULL_PTR(monitor) && gdk_display_get_n_monitors(display) > 0)
1023 monitor = gdk_display_get_monitor(display, 0);
1024 }
1025
1026 GdkRectangle geometry = { 0 };
1027 if(monitor)
1028 gdk_monitor_get_workarea(monitor, &geometry);
1029
1030 const int viewport_width = geometry.width > 0 ? (int)(geometry.width * 0.88) : DT_PIXEL_APPLY_DPI(1120);
1031 const int viewport_height = geometry.height > 0 ? (int)(geometry.height * 0.70) : DT_PIXEL_APPLY_DPI(440);
1032 const int graph_width = MIN(total_width, viewport_width);
1033 const int graph_height = MIN(DT_IOPORDER_GRAPH_HEIGHT, viewport_height);
1034 const int wanted_width = graph_width + DT_PIXEL_APPLY_DPI(40);
1035 const int wanted_height = graph_height + DT_PIXEL_APPLY_DPI(110);
1036
1037 gtk_widget_set_size_request(d->graph_scroll, graph_width, graph_height);
1038 gtk_window_resize(GTK_WINDOW(d->window), wanted_width, wanted_height);
1039 }
1040 gtk_widget_queue_draw(d->graph_drawing);
1041}
1042
1053static void _ioporder_draw_rounded_rect(cairo_t *cr, const double x, const double y,
1054 const double width, const double height, const double radius)
1055{
1056 const double r = MIN(radius, MIN(width, height) * 0.5);
1057
1058 cairo_new_sub_path(cr);
1059 cairo_arc(cr, x + width - r, y + r, r, -M_PI_2, 0.0);
1060 cairo_arc(cr, x + width - r, y + height - r, r, 0.0, M_PI_2);
1061 cairo_arc(cr, x + r, y + height - r, r, M_PI_2, M_PI);
1062 cairo_arc(cr, x + r, y + r, r, M_PI, 3.0 * M_PI_2);
1063 cairo_close_path(cr);
1064}
1065
1075static void _ioporder_draw_label(GtkWidget *widget, cairo_t *cr, const double x, const double y, const char *text)
1076{
1077 if(IS_NULL_PTR(text) || !widget) return;
1078
1079 PangoLayout *layout = gtk_widget_create_pango_layout(widget, text);
1080 cairo_move_to(cr, x, y);
1081 pango_cairo_show_layout(cr, layout);
1082 g_object_unref(layout);
1083}
1084
1094static void _ioporder_draw_sequence_arrow(cairo_t *cr, const double x1, const double y1,
1095 const double x2, const double y2)
1096{
1097 cairo_move_to(cr, x1, y1);
1098 cairo_line_to(cr, x2, y2);
1099 cairo_stroke(cr);
1100
1101 const double angle = atan2(y2 - y1, x2 - x1);
1102 const double arrow = DT_PIXEL_APPLY_DPI(8);
1103
1104 cairo_move_to(cr, x2, y2);
1105 cairo_line_to(cr, x2 - arrow * cos(angle - M_PI / 6.0), y2 - arrow * sin(angle - M_PI / 6.0));
1106 cairo_move_to(cr, x2, y2);
1107 cairo_line_to(cr, x2 - arrow * cos(angle + M_PI / 6.0), y2 - arrow * sin(angle + M_PI / 6.0));
1108 cairo_stroke(cr);
1109}
1110
1121static void _ioporder_draw_mask_arrow(GtkWidget *widget, cairo_t *cr, const double sx, const double sy,
1122 const double dx, const double dy)
1123{
1124 GtkAllocation area = { 0 };
1125 gtk_widget_get_allocation(widget, &area);
1126
1127 const double span = fabs(dx - sx);
1128 const double lift = DT_PIXEL_APPLY_DPI(36) + span * 0.20;
1129 const double cy = MIN(area.height - DT_IOPORDER_MASK_BOTTOM_MARGIN, MAX(sy, dy) + lift);
1130 const double c1x = sx + (dx - sx) * 0.25;
1131 const double c2x = sx + (dx - sx) * 0.75;
1132
1133 cairo_move_to(cr, sx, sy);
1134 cairo_curve_to(cr, c1x, cy, c2x, cy, dx, dy);
1135 cairo_stroke(cr);
1136
1137 const double arrow = DT_PIXEL_APPLY_DPI(8);
1138 const double tx = dx - c2x;
1139 const double ty = dy - cy;
1140 const double angle = atan2(ty, tx);
1141
1142 cairo_move_to(cr, dx, dy);
1143 cairo_line_to(cr, dx - arrow * cos(angle - M_PI / 6.0), dy - arrow * sin(angle - M_PI / 6.0));
1144 cairo_move_to(cr, dx, dy);
1145 cairo_line_to(cr, dx - arrow * cos(angle + M_PI / 6.0), dy - arrow * sin(angle + M_PI / 6.0));
1146 cairo_stroke(cr);
1147
1148 const double mx = (sx + dx) * 0.5;
1149 const double my = MIN(area.height - DT_PIXEL_APPLY_DPI(18), cy + DT_PIXEL_APPLY_DPI(8));
1150 cairo_save(cr);
1151 cairo_set_source_rgba(cr, 0.12, 0.12, 0.12, 0.85);
1154 cairo_fill(cr);
1155 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
1156 dtgtk_cairo_paint_showmask(cr, (int)(mx - DT_PIXEL_APPLY_DPI(7)), (int)(my - DT_PIXEL_APPLY_DPI(7)),
1157 DT_PIXEL_APPLY_DPI(14), DT_PIXEL_APPLY_DPI(14), 0, NULL);
1158 cairo_restore(cr);
1159
1160 (void)widget;
1161}
1162
1171static gboolean _ioporder_graph_draw(GtkWidget *widget, cairo_t *cr, gpointer user_data)
1172{
1173 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
1175 if(IS_NULL_PTR(d)) return FALSE;
1176
1177 GtkAllocation area = { 0 };
1178 gtk_widget_get_allocation(widget, &area);
1179
1180 gtk_render_background(gtk_widget_get_style_context(widget), cr, 0.0, 0.0, area.width, area.height);
1181
1182 if(!d->nodes)
1183 {
1184 cairo_set_source_rgb(cr, 0.85, 0.85, 0.85);
1186 _("No active or history modules are currently available."));
1187 return FALSE;
1188 }
1189
1190 const gboolean raw_input = dt_dev_get_global() && dt_dev_get_global()->image_storage.dsc.cst == IOP_CS_RAW;
1191
1192 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(2));
1193
1194 gboolean colorin_crossed = FALSE;
1197 const dt_iop_order_iccprofile_info_t *segment_profile = NULL;
1198 gchar *segment_label = NULL;
1199 GdkRGBA segment_color = { 0 };
1200 double segment_x = 0.0;
1201 double segment_width = 0.0;
1202
1203 /* Draw the runtime colorspace band by merging consecutive nodes with the same runtime state. */
1204 for(GList *iter = d->nodes; iter; iter = g_list_next(iter))
1205 {
1206 const dt_ioporder_graph_node_t *node = (const dt_ioporder_graph_node_t *)iter->data;
1207 if(node->is_endpoint || !node->module) continue;
1208 GtkAllocation alloc = { 0 };
1209 gtk_widget_get_allocation(node->event_box, &alloc);
1210
1211 const dt_iop_buffer_dsc_t *const dsc_out = node->piece ? &node->piece->dsc_out : NULL;
1212 const dt_ioporder_runtime_band_kind_t band_kind
1213 = _ioporder_runtime_band_kind(raw_input, colorin_crossed, node->module, dsc_out, TRUE);
1214 const dt_iop_order_iccprofile_info_t *profile_info
1215 = _ioporder_runtime_band_profile_info(raw_input, colorin_crossed, node->module, dsc_out, TRUE);
1216 const char *band_label = _ioporder_runtime_band_label(band_kind, dsc_out);
1217 gchar *band_text = _ioporder_runtime_band_text(band_label, profile_info);
1218 GdkRGBA band_color = { 0 };
1219 _ioporder_runtime_band_color(band_kind, &band_color);
1220
1221 if(IS_NULL_PTR(segment_label) || segment_kind != band_kind || segment_cst != (dsc_out ? dsc_out->cst : IOP_CS_NONE)
1222 || !_ioporder_same_runtime_profile(segment_profile, profile_info))
1223 {
1224 if(segment_label)
1225 {
1226 cairo_set_source_rgba(cr, segment_color.red, segment_color.green, segment_color.blue, segment_color.alpha);
1229 cairo_fill(cr);
1230 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
1231 _ioporder_draw_label(widget, cr, segment_x + DT_PIXEL_APPLY_DPI(10), DT_PIXEL_APPLY_DPI(22), segment_label);
1232 dt_free(segment_label);
1233 }
1234
1235 segment_kind = band_kind;
1236 segment_cst = dsc_out ? dsc_out->cst : IOP_CS_NONE;
1237 segment_profile = profile_info;
1238 segment_label = band_text;
1239 segment_color = band_color;
1240 segment_x = alloc.x;
1241 segment_width = alloc.width;
1242 }
1243 else
1244 {
1245 dt_free(band_text);
1246 segment_width = alloc.x + alloc.width - segment_x;
1247 }
1248 if(!strcmp(node->module->op, "colorin")) colorin_crossed = TRUE;
1249 }
1250
1251 if(segment_label)
1252 {
1253 cairo_set_source_rgba(cr, segment_color.red, segment_color.green, segment_color.blue, segment_color.alpha);
1256 cairo_fill(cr);
1257 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
1258 _ioporder_draw_label(widget, cr, segment_x + DT_PIXEL_APPLY_DPI(10), DT_PIXEL_APPLY_DPI(22), segment_label);
1259 dt_free(segment_label);
1260 }
1261
1262 cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.22);
1263 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(2));
1264
1265 /* Draw every fence as a background boundary that modules must not cross. */
1266 for(GList *iter = d->nodes; iter; iter = g_list_next(iter))
1267 {
1268 const dt_ioporder_graph_node_t *node = (const dt_ioporder_graph_node_t *)iter->data;
1269 if(node->is_endpoint || !node->module) continue;
1270
1271 GtkAllocation alloc = { 0 };
1272 gtk_widget_get_allocation(node->event_box, &alloc);
1273 const double x = alloc.x - DT_PIXEL_APPLY_DPI(12);
1274 cairo_move_to(cr, x, DT_PIXEL_APPLY_DPI(12));
1275 cairo_line_to(cr, x, area.height - DT_PIXEL_APPLY_DPI(12));
1276 }
1277 cairo_stroke(cr);
1278
1279 cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.90);
1280 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(2));
1281
1282 /* Consecutive nodes are laid out left-to-right in processing order, so we
1283 * keep the arrow head on the right-hand successor. */
1284 for(GList *iter = d->nodes; iter && g_list_next(iter); iter = g_list_next(iter))
1285 {
1286 const dt_ioporder_graph_node_t *node_a = (const dt_ioporder_graph_node_t *)iter->data;
1287 const dt_ioporder_graph_node_t *node_b = (const dt_ioporder_graph_node_t *)g_list_next(iter)->data;
1288 GtkAllocation alloc_a = { 0 }, alloc_b = { 0 }, head_a = { 0 }, head_b = { 0 };
1289 gtk_widget_get_allocation(node_a->event_box, &alloc_a);
1290 gtk_widget_get_allocation(node_b->event_box, &alloc_b);
1291 gtk_widget_get_allocation(node_a->header, &head_a);
1292 gtk_widget_get_allocation(node_b->header, &head_b);
1293 const double arrow_y_a = node_a->is_endpoint ? alloc_a.y + alloc_a.height * 0.5
1294 : alloc_a.y + head_a.y + head_a.height * 0.5;
1295 const double arrow_y_b = node_b->is_endpoint ? alloc_b.y + alloc_b.height * 0.5
1296 : alloc_b.y + head_b.y + head_b.height * 0.5;
1297
1298 _ioporder_draw_sequence_arrow(cr, alloc_a.x + alloc_a.width + DT_PIXEL_APPLY_DPI(6),
1299 arrow_y_a,
1300 alloc_b.x - DT_PIXEL_APPLY_DPI(6),
1301 arrow_y_b);
1302 }
1303
1304 cairo_set_source_rgba(cr, 0.95, 0.54, 0.13, 0.92);
1305 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(3));
1306
1307 /* Walk every visible module and draw one orange dependency arrow per raster-mask consumer. */
1308 for(GList *iter = d->nodes; iter; iter = g_list_next(iter))
1309 {
1310 const dt_ioporder_graph_node_t *source = (const dt_ioporder_graph_node_t *)iter->data;
1311 if(source->is_endpoint || !source->module) continue;
1312 GtkAllocation source_alloc = { 0 };
1313 gtk_widget_get_allocation(source->event_box, &source_alloc);
1314
1315 for(GList *consumers = d->nodes; consumers; consumers = g_list_next(consumers))
1316 {
1317 const dt_ioporder_graph_node_t *sink = (const dt_ioporder_graph_node_t *)consumers->data;
1318 if(sink->is_endpoint || !sink->module) continue;
1319 if(sink->module->raster_mask.sink.source != source->module) continue;
1320
1321 GtkAllocation sink_alloc = { 0 };
1322 gtk_widget_get_allocation(sink->event_box, &sink_alloc);
1323
1324 _ioporder_draw_mask_arrow(widget, cr,
1325 source_alloc.x + source_alloc.width * 0.5,
1326 source_alloc.y + source_alloc.height + DT_IOPORDER_MASK_ARROW_OFFSET,
1327 sink_alloc.x + sink_alloc.width * 0.5,
1328 sink_alloc.y + sink_alloc.height + DT_IOPORDER_MASK_ARROW_OFFSET);
1329 }
1330 }
1331
1332 if(d->drag_source && d->drag_dest && d->drag_source != d->drag_dest)
1333 {
1334 cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.95);
1335 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(4));
1336
1337 /* Materialize the drop position with a bright insertion line. */
1338 for(GList *iter = d->nodes; iter; iter = g_list_next(iter))
1339 {
1340 const dt_ioporder_graph_node_t *node = (const dt_ioporder_graph_node_t *)iter->data;
1341 if(node->module != d->drag_dest) continue;
1342
1343 GtkAllocation alloc = { 0 };
1344 gtk_widget_get_allocation(node->event_box, &alloc);
1345 const double x = d->drag_source->iop_order < d->drag_dest->iop_order
1346 ? alloc.x + alloc.width + DT_PIXEL_APPLY_DPI(10)
1347 : alloc.x - DT_PIXEL_APPLY_DPI(10);
1348 cairo_move_to(cr, x, DT_PIXEL_APPLY_DPI(12));
1349 cairo_line_to(cr, x, area.height - DT_PIXEL_APPLY_DPI(12));
1350 cairo_stroke(cr);
1351 break;
1352 }
1353 }
1354
1355 return FALSE;
1356}
1357
1368static void _ioporder_drag_data_get(GtkWidget *widget, GdkDragContext *context,
1369 GtkSelectionData *selection_data, guint info, guint time,
1370 gpointer user_data)
1371{
1372 guint value = 1;
1373 gtk_selection_data_set(selection_data, gdk_atom_intern("ioporder-node", TRUE), 32,
1374 (const guchar *)&value, 1);
1375}
1376
1384static void _ioporder_drag_begin(GtkWidget *widget, GdkDragContext *context, gpointer user_data)
1385{
1386 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
1388 d->drag_source = (dt_iop_module_t *)g_object_get_data(G_OBJECT(widget), "dt-ioporder-module");
1389 d->drag_dest = NULL;
1390 gtk_widget_queue_draw(d->graph_drawing);
1391}
1392
1400static void _ioporder_drag_end(GtkWidget *widget, GdkDragContext *context, gpointer user_data)
1401{
1402 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
1404 d->drag_source = NULL;
1405 d->drag_dest = NULL;
1406 gtk_widget_queue_draw(d->graph_drawing);
1407}
1408
1420static gboolean _ioporder_drag_motion(GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
1421 guint time, gpointer user_data)
1422{
1423 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
1425 dt_iop_module_t *module_src = d->drag_source;
1426 dt_iop_module_t *module_dest = (dt_iop_module_t *)g_object_get_data(G_OBJECT(widget), "dt-ioporder-module");
1427
1428 gboolean can_move = FALSE;
1429 if(module_src && module_dest && module_src != module_dest)
1430 {
1431 if(module_src->iop_order < module_dest->iop_order)
1432 can_move = dt_ioppr_check_can_move_after_iop(dt_dev_get_global()->iop, module_src, module_dest);
1433 else
1434 can_move = dt_ioppr_check_can_move_before_iop(dt_dev_get_global()->iop, module_src, module_dest);
1435 }
1436
1437 d->drag_dest = can_move ? module_dest : NULL;
1438 gtk_widget_queue_draw(d->graph_drawing);
1439 gdk_drag_status(dc, can_move ? GDK_ACTION_COPY : 0, time);
1440 return can_move;
1441}
1442
1454static gboolean _ioporder_drag_drop(GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
1455 guint time, gpointer user_data)
1456{
1457 gtk_drag_get_data(widget, dc, gdk_atom_intern("ioporder-node", TRUE), time);
1458 return TRUE;
1459}
1460
1469static void _ioporder_drag_leave(GtkWidget *widget, GdkDragContext *dc, guint time, gpointer user_data)
1470{
1471 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
1473 d->drag_dest = NULL;
1474 gtk_widget_queue_draw(d->graph_drawing);
1475}
1476
1493static void _ioporder_drag_data_received(GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
1494 GtkSelectionData *selection_data, guint info, guint time,
1495 gpointer user_data)
1496{
1497 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
1499 dt_iop_module_t *module_src = d->drag_source;
1500 dt_iop_module_t *module_dest = (dt_iop_module_t *)g_object_get_data(G_OBJECT(widget), "dt-ioporder-module");
1501
1502 if(module_src && module_dest && module_src != module_dest)
1503 {
1504 if(module_src->iop_order < module_dest->iop_order)
1505 dt_iop_gui_move_module_after(module_src, module_dest, "_ioporder_drag_data_received");
1506 else
1507 dt_iop_gui_move_module_before(module_src, module_dest, "_ioporder_drag_data_received");
1508 }
1509
1510 gtk_drag_finish(dc, TRUE, FALSE, time);
1511 d->drag_source = NULL;
1512 d->drag_dest = NULL;
1513
1515}
1516
1523static void _ioporder_add_preset(GtkButton *button, gpointer user_data)
1524{
1525 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
1527 GtkWindow *parent = GTK_WINDOW(d->window ? d->window : dt_gui_main_window());
1528 GtkWidget *dialog = gtk_dialog_new_with_buttons(_("save module order preset"), parent,
1529 GTK_DIALOG_DESTROY_WITH_PARENT,
1530 _("_cancel"), GTK_RESPONSE_CANCEL,
1531 _("_save"), GTK_RESPONSE_ACCEPT, NULL);
1532 GtkWidget *content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1533 GtkWidget *entry = gtk_entry_new();
1534
1535 gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
1536 gtk_widget_set_hexpand(entry, TRUE);
1537 gtk_widget_set_tooltip_text(entry, _("preset name"));
1538 gtk_box_pack_start(GTK_BOX(content), entry, FALSE, FALSE, DT_PIXEL_APPLY_DPI(8));
1539 gtk_widget_show_all(dialog);
1540
1541 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1542 g_signal_connect(entry, "key-press-event", G_CALLBACK(dt_handle_dialog_enter), dialog);
1543
1544 if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
1545 {
1546 const char *preset_name = gtk_entry_get_text(GTK_ENTRY(entry));
1547 if(preset_name && preset_name[0] != '\0')
1548 {
1549 int size = 0;
1550 void *params = self->get_params(self, &size);
1551 dt_lib_presets_add(preset_name, self->plugin_name, self->version(), params, size, FALSE);
1552 dt_free(params);
1554 }
1555 }
1556
1557 gtk_widget_destroy(dialog);
1559}
1560
1567static void _ioporder_reset_order(GtkButton *button, gpointer user_data)
1568{
1569 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
1570 self->gui_reset(self);
1572}
1573
1580static void _ioporder_apply_preset(GtkComboBox *combo, gpointer user_data)
1581{
1582 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
1584 if(d->refreshing_toolbar) return;
1585
1586 const gchar *preset_name = gtk_combo_box_get_active_id(combo);
1587 if(!preset_name || !strcmp(preset_name, "__custom__"))
1588 return;
1589
1590 dt_lib_presets_apply(preset_name, self->plugin_name, self->version());
1591 dt_iop_gui_commit_iop_order_change(dt_dev_get_global(), NULL, FALSE, TRUE, "_ioporder_apply_preset");
1593}
1594
1604{
1606 if(d->window) return;
1607
1608 GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1609 GtkWidget *root = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
1610 GtkWidget *toolbar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
1611 GtkWidget *label = gtk_label_new("");
1612 GtkWidget *add_preset = gtk_button_new_with_label(_("add preset"));
1613 GtkWidget *preset_combo = gtk_combo_box_text_new();
1614 GtkWidget *reset = gtk_button_new_with_label(_("reset"));
1615 GtkWidget *scroll = gtk_scrolled_window_new(NULL, NULL);
1616 GtkWidget *overlay = gtk_overlay_new();
1617 GtkWidget *drawing = gtk_drawing_area_new();
1618 GtkWidget *fixed = gtk_fixed_new();
1619
1620 gtk_window_set_title(GTK_WINDOW(window), _("module order"));
1621 gtk_window_set_default_size(GTK_WINDOW(window), DT_PIXEL_APPLY_DPI(1120), DT_PIXEL_APPLY_DPI(440));
1622 gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(dt_gui_main_window()));
1623 gtk_window_set_destroy_with_parent(GTK_WINDOW(window), TRUE);
1624 gtk_container_set_border_width(GTK_CONTAINER(root), DT_PIXEL_APPLY_DPI(8));
1625
1626 gtk_widget_set_halign(label, GTK_ALIGN_START);
1627 gtk_label_set_xalign(GTK_LABEL(label), 0.0f);
1628
1629 gtk_widget_set_hexpand(preset_combo, TRUE);
1630 gtk_widget_set_hexpand(scroll, TRUE);
1631 gtk_widget_set_vexpand(scroll, TRUE);
1632 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1633 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll), GTK_SHADOW_IN);
1634
1635 gtk_widget_set_size_request(overlay, DT_IOPORDER_GRAPH_MIN_WIDTH, DT_IOPORDER_GRAPH_HEIGHT);
1636 gtk_widget_set_size_request(drawing, DT_IOPORDER_GRAPH_MIN_WIDTH, DT_IOPORDER_GRAPH_HEIGHT);
1637 gtk_widget_set_size_request(fixed, DT_IOPORDER_GRAPH_MIN_WIDTH, DT_IOPORDER_GRAPH_HEIGHT);
1638
1639 gtk_box_pack_start(GTK_BOX(toolbar), label, FALSE, FALSE, 0);
1640 gtk_box_pack_start(GTK_BOX(toolbar), add_preset, FALSE, FALSE, 0);
1641 gtk_box_pack_start(GTK_BOX(toolbar), preset_combo, TRUE, TRUE, 0);
1642 gtk_box_pack_start(GTK_BOX(toolbar), reset, FALSE, FALSE, 0);
1643
1644 gtk_overlay_add_overlay(GTK_OVERLAY(overlay), fixed);
1645 gtk_container_add(GTK_CONTAINER(overlay), drawing);
1646 gtk_container_add(GTK_CONTAINER(scroll), overlay);
1647
1648 gtk_box_pack_start(GTK_BOX(root), toolbar, FALSE, FALSE, 0);
1649 gtk_box_pack_start(GTK_BOX(root), scroll, TRUE, TRUE, 0);
1650 gtk_container_add(GTK_CONTAINER(window), root);
1651
1652 g_signal_connect(window, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
1653 g_signal_connect(window, "destroy", G_CALLBACK(_ioporder_popup_destroy), d);
1654 g_signal_connect(add_preset, "clicked", G_CALLBACK(_ioporder_add_preset), self);
1655 g_signal_connect(reset, "clicked", G_CALLBACK(_ioporder_reset_order), self);
1656 g_signal_connect(preset_combo, "changed", G_CALLBACK(_ioporder_apply_preset), self);
1657 g_signal_connect(drawing, "draw", G_CALLBACK(_ioporder_graph_draw), self);
1658
1659 d->window = window;
1660 d->toolbar_label = label;
1661 d->preset_combo = preset_combo;
1662 d->graph_scroll = scroll;
1663 d->graph_overlay = overlay;
1664 d->graph_drawing = drawing;
1665 d->graph_fixed = fixed;
1666
1669}
1670
1680{
1682
1685
1686 gtk_widget_show_all(d->window);
1687 gtk_window_present(GTK_WINDOW(d->window));
1688}
1689
1696static void _ioporder_refresh_callback(gpointer instance, gpointer user_data)
1697{
1698 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
1700 if(d->window && gtk_widget_get_visible(d->window)) _ioporder_rebuild_graph(self);
1701}
1702
1710static void _ioporder_presets_changed_callback(gpointer instance, gpointer module_name, gpointer user_data)
1711{
1712 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
1714 const char *changed_module = (const char *)module_name;
1715 if(changed_module && strcmp(changed_module, self->plugin_name) != 0) return;
1716
1718 if(d->window && gtk_widget_get_visible(d->window)) _ioporder_rebuild_graph(self);
1719}
1720
1721const char *name(struct dt_lib_module_t *self)
1722{
1723 return _("module order");
1724}
1725
1726const char **views(dt_lib_module_t *self)
1727{
1728 static const char *v[] = { "special", NULL };
1729 return v;
1730}
1731
1733{
1735}
1736
1738{
1739 return 0;
1740}
1741
1743{
1744 return 0;
1745}
1746
1767
1769{
1770 if(IS_NULL_PTR(self->data)) return;
1772 if(IS_NULL_PTR(d)) return;
1773
1776
1778 if(d->window) gtk_widget_destroy(d->window);
1779 dt_free(self->data);
1780 self->data = NULL;
1781}
1782
1784{
1787 if(IS_NULL_PTR(iop_order_list)) return;
1788
1789 const int32_t imgid = dt_dev_get_global()->image_storage.id;
1790 dt_ioppr_change_iop_order(dt_dev_get_global(), imgid, iop_order_list);
1791 dt_iop_gui_commit_iop_order_change(dt_dev_get_global(), NULL, FALSE, FALSE, "dt_lib_ioporder_gui_reset");
1792
1793 d->current_mode = DT_IOP_ORDER_ANSEL_RAW;
1794 g_list_free_full(iop_order_list, dt_free_gpointer);
1795}
1796
1798{
1799 size_t size = 0;
1800 char *params = NULL;
1801 GList *list = NULL;
1802
1804 params = dt_ioppr_serialize_iop_order_list(list, &size);
1805 dt_lib_presets_add(_("legacy"), self->plugin_name, self->version(), params, (int32_t)size, TRUE);
1806 dt_free(params);
1807 g_list_free_full(list, dt_free_gpointer);
1808
1810 params = dt_ioppr_serialize_iop_order_list(list, &size);
1811 dt_lib_presets_add(_("v3.0 for RAW input (default)"), self->plugin_name, self->version(), params,
1812 (int32_t)size, TRUE);
1813 dt_free(params);
1814 g_list_free_full(list, dt_free_gpointer);
1815
1817 params = dt_ioppr_serialize_iop_order_list(list, &size);
1818 dt_lib_presets_add(_("v3.0 for JPEG/non-RAW input"), self->plugin_name, self->version(), params,
1819 (int32_t)size, TRUE);
1820 dt_free(params);
1821 g_list_free_full(list, dt_free_gpointer);
1822
1824 params = dt_ioppr_serialize_iop_order_list(list, &size);
1825 dt_lib_presets_add(_("Ansel v0.1 for RAW input (default)"), self->plugin_name, self->version(), params,
1826 (int32_t)size, TRUE);
1827 dt_free(params);
1828 g_list_free_full(list, dt_free_gpointer);
1829
1831 params = dt_ioppr_serialize_iop_order_list(list, &size);
1832 dt_lib_presets_add(_("Ansel v0.1 for JPEG/non-RAW input"), self->plugin_name, self->version(), params,
1833 (int32_t)size, TRUE);
1834 dt_free(params);
1835 g_list_free_full(list, dt_free_gpointer);
1836}
1837
1838int set_params(dt_lib_module_t *self, const void *params, int size)
1839{
1840 if(IS_NULL_PTR(params)) return 1;
1841
1842 GList *iop_order_list = dt_ioppr_deserialize_iop_order_list(params, (size_t)size);
1843 if(IS_NULL_PTR(iop_order_list)) return 1;
1844
1845 const int32_t imgid = dt_dev_get_global()->image_storage.id;
1846 dt_ioppr_change_iop_order(dt_dev_get_global(), imgid, iop_order_list);
1847 dt_iop_gui_commit_iop_order_change(dt_dev_get_global(), NULL, FALSE, FALSE, "dt_lib_ioporder_set_params");
1848 g_list_free_full(iop_order_list, dt_free_gpointer);
1849 return 0;
1850}
1851
1853{
1854 size_t p_size = 0;
1855 void *params = dt_ioppr_serialize_iop_order_list(dt_dev_get_global()->iop_order_list, &p_size);
1856 *size = (int)p_size;
1857 return params;
1858}
1859
1861{
1862 return TRUE;
1863}
1864
1865// clang-format off
1866// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1867// vim: shiftwidth=2 expandtab tabstop=2 cindent
1868// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1869// clang-format on
GtkWidget * dt_gui_main_window(void)
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void dtgtk_button_set_active(GtkDarktableButton *button, gboolean active)
Definition button.c:177
GtkWidget * dtgtk_button_new(DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
Definition button.c:135
#define DTGTK_IS_BUTTON(obj)
Definition button.h:43
#define DTGTK_BUTTON(obj)
Definition button.h:40
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
static const dt_adaptation_t kind
dt_iop_colorspace_type_t
@ IOP_CS_RAW
@ IOP_CS_LCH
@ IOP_CS_JZCZHZ
@ IOP_CS_RGB
@ IOP_CS_HSL
@ IOP_CS_LAB
@ IOP_CS_NONE
static void add_preset(dt_iop_module_so_t *self, const char *name, const char *pi, const int version, const char *bpi, const int blendop_version)
static const float x
const float v
const char * dt_colorspaces_get_name(dt_colorspaces_color_profile_type_t type, const char *filename)
Printable name for a profile identity, without touching the profile list.
The colour-profile module's API: which profiles exist, and how to apply one.
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
void reset(dt_view_t *self)
Definition darkroom.c:1222
struct dt_develop_t * dt_dev_get_global(void)
Definition darktable.c:528
struct dt_gui_gtk_t * dt_gui_get_global(void)
Definition darktable.c:523
dt_iop_order_iccprofile_info_t * dt_ioppr_get_pipe_output_profile_info(const struct dt_dev_pixelpipe_t *pipe)
dt_iop_order_iccprofile_info_t * dt_ioppr_get_pipe_work_profile_info(const struct dt_dev_pixelpipe_t *pipe)
dt_iop_order_iccprofile_info_t * dt_ioppr_get_pipe_input_profile_info(const struct dt_dev_pixelpipe_t *pipe)
gchar * dt_dev_get_multi_name(const struct dt_iop_module_t *module)
Definition develop.c:1637
GtkWidget * geometry
its size, under the preview
GtkWidget * window
static gboolean dt_iop_colorspace_is_rgb(const dt_iop_colorspace_type_t cst)
Definition format.h:75
@ IOP_CS_RGB_DISPLAY
Definition format.h:71
dt_iop_buffer_type_t
Definition format.h:54
@ TYPE_FLOAT
Definition format.h:56
@ TYPE_UNKNOWN
Definition format.h:55
@ TYPE_UINT8
Definition format.h:58
@ TYPE_UINT16
Definition format.h:57
GdkRGBA color[]
Definition geotagging.c:541
void dt_gui_presets_popup_menu_show_for_module(dt_iop_module_t *module)
static gboolean enable(const dt_image_t *image)
Definition highlights.c:879
gboolean dt_iop_is_hidden(dt_iop_module_t *module)
Definition imageop.c:736
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:707
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:714
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:693
void * dt_ioppr_serialize_iop_order_list(GList *iop_order_list, size_t *size)
Serialize an order list into a binary blob (used for presets).
Definition iop_order.c:2465
GList * dt_ioppr_get_iop_order_list_version(dt_iop_order_t version)
Return the built-in order list for a given version.
Definition iop_order.c:1066
gboolean dt_ioppr_check_can_move_before_iop(GList *iop_list, dt_iop_module_t *module, dt_iop_module_t *module_next)
Validate whether module can be moved before module_next.
Definition iop_order.c:1989
gboolean dt_ioppr_check_can_move_after_iop(GList *iop_list, dt_iop_module_t *module, dt_iop_module_t *module_prev)
Validate whether module can be moved after module_prev.
Definition iop_order.c:2158
dt_iop_order_t dt_ioppr_get_iop_order_list_kind(GList *iop_order_list)
Determine the kind of an order list by inspecting its content.
Definition iop_order.c:951
char * dt_ioppr_serialize_text_iop_order_list(GList *iop_order_list)
Serialize an order list to a text representation.
Definition iop_order.c:2507
const char * dt_iop_order_string(const dt_iop_order_t order)
Return the human-readable name for an IOP order enum value.
Definition iop_order.c:82
GList * dt_ioppr_deserialize_iop_order_list(const char *buf, size_t size)
Deserialize an order list from a binary blob.
Definition iop_order.c:2606
void dt_ioppr_change_iop_order(struct dt_develop_t *dev, const int32_t imgid, GList *new_iop_list)
Replace the current order list with a new one and persist it.
Definition iop_order.c:1360
dt_iop_order_t
Definition iop_order.h:149
@ DT_IOP_ORDER_ANSEL_RAW
Definition iop_order.h:154
@ DT_IOP_ORDER_LEGACY
Definition iop_order.h:151
@ DT_IOP_ORDER_V30_JPG
Definition iop_order.h:153
@ DT_IOP_ORDER_V30
Definition iop_order.h:152
@ DT_IOP_ORDER_CUSTOM
Definition iop_order.h:150
@ DT_IOP_ORDER_ANSEL_JPG
Definition iop_order.h:155
static gboolean _ioporder_drag_drop(GtkWidget *widget, GdkDragContext *dc, gint x, gint y, guint time, gpointer user_data)
Request the drag payload when dropping on a graph node.
Definition ioporder.c:1454
static gchar * _ioporder_descriptor_to_text(const char *prefix, const dt_iop_buffer_dsc_t *dsc, const char *display_colorspace)
Build the descriptor list shown below each module node.
Definition ioporder.c:285
#define DT_IOPORDER_GRAPH_HEIGHT
Definition ioporder.c:63
static void _ioporder_free_graph_node(gpointer data)
Release one graph node descriptor.
Definition ioporder.c:658
void gui_reset(dt_lib_module_t *self)
Definition ioporder.c:1783
#define DT_IOPORDER_GRAPH_LEFT_MARGIN
Definition ioporder.c:60
int set_params(dt_lib_module_t *self, const void *params, int size)
Definition ioporder.c:1838
static gboolean _ioporder_module_in_history(const dt_iop_module_t *module)
Return TRUE when a module already exists in history.
Definition ioporder.c:146
void * get_params(dt_lib_module_t *self, int *size)
Definition ioporder.c:1852
#define DT_IOPORDER_MASK_ARROW_OFFSET
Definition ioporder.c:65
static gchar * _ioporder_get_current_order_name(dt_lib_module_t *self)
Retrieve the display name of the current pipeline order.
Definition ioporder.c:493
static void _ioporder_add_preset(GtkButton *button, gpointer user_data)
Save the current pipeline order as a named preset.
Definition ioporder.c:1523
static void _ioporder_init_popup(dt_lib_module_t *self)
Build the popup window lazily on first use.
Definition ioporder.c:1603
#define DT_IOPORDER_BAND_HEIGHT
Definition ioporder.c:64
static dt_ioporder_runtime_band_kind_t _ioporder_runtime_band_kind(const gboolean raw_input, const gboolean colorin_crossed, const dt_iop_module_t *module, const dt_iop_buffer_dsc_t *dsc, const gboolean after_module)
Classify one runtime descriptor into a stable band kind.
Definition ioporder.c:316
static gboolean _ioporder_same_runtime_profile(const dt_iop_order_iccprofile_info_t *profile_a, const dt_iop_order_iccprofile_info_t *profile_b)
Compare two profile descriptors for lifecycle segment merging.
Definition ioporder.c:418
static const char * _ioporder_colorspace_to_string(const dt_iop_colorspace_type_t cst)
Return a human-readable colorspace string for a pixel descriptor.
Definition ioporder.c:227
#define DT_IOPORDER_MASK_BOTTOM_MARGIN
Definition ioporder.c:66
static void _ioporder_reset_order(GtkButton *button, gpointer user_data)
Reset the current order to the default v3.0 order.
Definition ioporder.c:1567
static void _ioporder_set_enable_button_icon(GtkWidget *widget, dt_iop_module_t *module)
Apply the same enable-button icon policy used by module headers.
Definition ioporder.c:676
static void _ioporder_refresh_callback(gpointer instance, gpointer user_data)
Central refresh callback for develop-side state changes.
Definition ioporder.c:1696
dt_ioporder_runtime_band_kind_t
Definition ioporder.c:74
@ DT_IOPORDER_RUNTIME_BAND_UNAVAILABLE
Definition ioporder.c:76
@ DT_IOPORDER_RUNTIME_BAND_LAB
Definition ioporder.c:81
@ DT_IOPORDER_RUNTIME_BAND_NONE
Definition ioporder.c:75
@ DT_IOPORDER_RUNTIME_BAND_PIPELINE_RGB
Definition ioporder.c:79
@ DT_IOPORDER_RUNTIME_BAND_RAW
Definition ioporder.c:77
@ DT_IOPORDER_RUNTIME_BAND_SENSOR_RGB
Definition ioporder.c:78
@ DT_IOPORDER_RUNTIME_BAND_DISPLAY_RGB
Definition ioporder.c:80
@ DT_IOPORDER_RUNTIME_BAND_OTHER
Definition ioporder.c:82
static void _ioporder_draw_rounded_rect(cairo_t *cr, const double x, const double y, const double width, const double height, const double radius)
Draw a filled rounded rectangle.
Definition ioporder.c:1053
static void _ioporder_draw_label(GtkWidget *widget, cairo_t *cr, const double x, const double y, const char *text)
Draw a short label with the widget font on the graph background.
Definition ioporder.c:1075
void show_popup(dt_lib_module_t *self)
Open the popup window owned by the ioporder lib.
Definition ioporder.c:1679
static void _ioporder_draw_sequence_arrow(cairo_t *cr, const double x1, const double y1, const double x2, const double y2)
Draw a straight sequence arrow between two consecutive module nodes.
Definition ioporder.c:1094
static void _ioporder_refresh_toolbar(dt_lib_module_t *self)
Refresh the preset combo and status label in the popup toolbar.
Definition ioporder.c:560
void gui_cleanup(dt_lib_module_t *self)
Definition ioporder.c:1768
static void _ioporder_node_toggle_enable(GtkToggleButton *togglebutton, gpointer user_data)
Proxy the module enable state through the real darkroom header widget.
Definition ioporder.c:699
static void _ioporder_apply_preset(GtkComboBox *combo, gpointer user_data)
Apply the preset selected in the popup toolbar combo box.
Definition ioporder.c:1580
static gboolean _ioporder_drag_motion(GtkWidget *widget, GdkDragContext *dc, gint x, gint y, guint time, gpointer user_data)
Validate the potential drop target while dragging across graph nodes.
Definition ioporder.c:1420
static dt_dev_pixelpipe_iop_t * _ioporder_get_preview_piece(dt_iop_module_t *module)
Map a module instance to its preview-pipe piece.
Definition ioporder.c:185
static void _ioporder_drag_leave(GtkWidget *widget, GdkDragContext *dc, guint time, gpointer user_data)
Clear drop feedback when leaving a graph node during drag.
Definition ioporder.c:1469
static void _ioporder_runtime_band_color(const dt_ioporder_runtime_band_kind_t kind, GdkRGBA *color)
Pick the color used by the colorspace lifecycle band.
Definition ioporder.c:377
static dt_ioporder_graph_node_t * _ioporder_create_graph_node(dt_iop_module_t *module, dt_dev_pixelpipe_iop_t *piece, const char *display_in, const char *display_out)
Create one interactive graph node mirroring a module header.
Definition ioporder.c:750
static const char * _ioporder_type_to_string(const dt_iop_buffer_type_t datatype)
Return a human-readable datatype string for a pixel descriptor.
Definition ioporder.c:205
static const dt_iop_order_iccprofile_info_t * _ioporder_runtime_band_profile_info(const gboolean raw_input, const gboolean colorin_crossed, const dt_iop_module_t *module, const dt_iop_buffer_dsc_t *dsc, const gboolean after_module)
Pick the RGB profile metadata that matches one runtime band segment.
Definition ioporder.c:441
static void _ioporder_clear_graph(dt_lib_ioporder_t *d)
Destroy every node widget and free the node descriptors.
Definition ioporder.c:603
void init_presets(dt_lib_module_t *self)
Definition ioporder.c:1797
#define DT_IOPORDER_GRAPH_NODE_STEP
Definition ioporder.c:59
static void _ioporder_rebuild_graph(dt_lib_module_t *self)
Refresh the full graph contents from the current darkroom pipeline.
Definition ioporder.c:924
static gchar * _ioporder_raw_flags_to_string(const dt_iop_buffer_dsc_t *dsc)
Format the RAW-specific flags carried by a pixel descriptor.
Definition ioporder.c:258
uint32_t container(dt_lib_module_t *self)
Definition ioporder.c:1732
gboolean preset_autoapply(dt_lib_module_t *self)
Definition ioporder.c:1860
static void _ioporder_draw_mask_arrow(GtkWidget *widget, cairo_t *cr, const double sx, const double sy, const double dx, const double dy)
Draw a curved raster-mask dependency arrow between two nodes.
Definition ioporder.c:1121
static gchar * _ioporder_runtime_band_text(const char *label, const dt_iop_order_iccprofile_info_t *profile_info)
Build the text shown in one colorspace-band segment.
Definition ioporder.c:472
static dt_ioporder_graph_node_t * _ioporder_create_endpoint_node(const char *label)
Create a compact endpoint node used for the graph boundaries.
Definition ioporder.c:874
static void _ioporder_node_toggle_mask(GtkToggleButton *togglebutton, gpointer user_data)
Proxy raster/drawn mask preview toggling through the real module header.
Definition ioporder.c:715
static void _ioporder_node_show_presets(GtkButton *button, gpointer user_data)
Open the standard module preset popup from a graph node.
Definition ioporder.c:731
static const guint _ioporder_target_count
Definition ioporder.c:88
void gui_init(dt_lib_module_t *self)
Definition ioporder.c:1747
#define DT_IOPORDER_GRAPH_MIN_WIDTH
Definition ioporder.c:62
int position()
Definition ioporder.c:1742
const char ** views(dt_lib_module_t *self)
Definition ioporder.c:1726
static void _ioporder_drag_begin(GtkWidget *widget, GdkDragContext *context, gpointer user_data)
Track the module being dragged from the graph.
Definition ioporder.c:1384
static void _ioporder_presets_changed_callback(gpointer instance, gpointer module_name, gpointer user_data)
Refresh the preset toolbar when ioporder presets change.
Definition ioporder.c:1710
int expandable(dt_lib_module_t *self)
Definition ioporder.c:1737
#define DT_IOPORDER_GRAPH_NODE_WIDTH
Definition ioporder.c:58
static gboolean _ioporder_module_is_graph_visible(const dt_iop_module_t *module)
Apply the active-pipe visibility policy used by modulegroups.
Definition ioporder.c:169
#define DT_IOPORDER_GRAPH_TOP_MARGIN
Definition ioporder.c:61
dt_ioporder_dnd_target_t
Definition ioporder.c:69
@ DT_IOPORDER_DND_TARGET_NODE
Definition ioporder.c:70
static gboolean _ioporder_graph_draw(GtkWidget *widget, cairo_t *cr, gpointer user_data)
Paint the graph background, runtime bands, fences, and arrows.
Definition ioporder.c:1171
static const char * _ioporder_runtime_band_label(const dt_ioporder_runtime_band_kind_t kind, const dt_iop_buffer_dsc_t *dsc)
Return the user-visible label for one runtime band kind.
Definition ioporder.c:346
static void _ioporder_popup_destroy(GtkWidget *widget, gpointer user_data)
Drop cached popup widget pointers once GTK destroys the popup.
Definition ioporder.c:631
static void _ioporder_drag_end(GtkWidget *widget, GdkDragContext *context, gpointer user_data)
Reset drag feedback when the source drag ends.
Definition ioporder.c:1400
static void _ioporder_drag_data_received(GtkWidget *widget, GdkDragContext *dc, gint x, gint y, GtkSelectionData *selection_data, guint info, guint time, gpointer user_data)
Commit a module reorder after dropping on another graph node.
Definition ioporder.c:1493
static const GtkTargetEntry _ioporder_target_list[]
Definition ioporder.c:85
static void _ioporder_drag_data_get(GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data, guint info, guint time, gpointer user_data)
Handle drag-data export for the graph node drag and drop.
Definition ioporder.c:1368
gchar * delete_underscore(const char *s)
Definition label.c:75
gboolean dt_handle_dialog_enter(GtkWidget *widget, GdkEventKey *event, gpointer data)
Definition lib.c:1324
void dt_lib_presets_add(const char *name, const char *plugin_name, const int32_t version, const void *params, const int32_t params_size, gboolean readonly)
Definition lib.c:1152
gboolean dt_lib_presets_apply(const gchar *preset, const gchar *module_name, int module_version)
Definition lib.c:290
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
#define M_PI
Definition math.h:47
static void dt_free_gpointer(gpointer ptr)
g_free() one pointer, with the signature GDestroyNotify wants.
Definition mem_alloc.h:184
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
size_t size
Definition mipmap_cache.c:3
#define DT_MODULE(MODVER)
Instantiate the version handshake every module must export. Use once, at file scope,...
const char * name
Definition pdf.h:90
void dt_gui_menu_popup(GtkMenu *menu, GtkWidget *button, GdkGravity widget_anchor, GdkGravity menu_anchor)
Definition popup.c:53
void dt_module_preset_free(gpointer data)
Release one dt_module_preset_t. Suits g_list_free_full().
GList * dt_preset_repository_list_all_versions(const char *operation)
Every preset of operation at ANY version, with rowid and op_version filled in, SHIPPED FIRST then by ...
GList * dt_preset_repository_list_for_version(const char *operation, const int op_version)
As above, restricted to one op_version. Same columns, same order.
data.presets: a module's saved parameters, with the conditions under which it auto-applies.
@ DT_COLORSPACE_NONE
No profile / "take it from the image settings". Never matches a list entry.
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
Definition signal.h:407
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
Definition signal.h:386
struct dt_control_signal_t * dt_control_signal_get_global(void)
Definition darktable.c:616
@ DT_SIGNAL_DEVELOP_INITIALIZE
This signal is raised when darktable.develop is initialized.
Definition signal.h:172
@ DT_SIGNAL_DEVELOP_HISTORY_CHANGE
This signal is raised when develop history is changed no param, no returned value.
Definition signal.h:207
@ DT_SIGNAL_DEVELOP_IMAGE_CHANGED
This signal is raised when image is changed in darkroom.
Definition signal.h:224
@ DT_SIGNAL_DEVELOP_PREVIEW_PIPE_FINISHED
This signal is raised when develop preview pipe process is finished no param, no returned value.
Definition signal.h:177
@ DT_SIGNAL_DEVELOP_MODULE_MOVED
This signal is raised when order of modules in pipeline is changed.
Definition signal.h:221
@ DT_SIGNAL_PRESETS_CHANGED
Definition signal.h:165
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
Definition signal.h:396
static const dt_aligned_pixel_simd_t value
Definition simd.h:144
const float r
dt_iop_buffer_dsc_t dsc_out
dt_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
dt_image_t image_storage
Definition develop.h:225
GList * iop
Definition develop.h:269
dt_iop_buffer_dsc_t dsc
Definition image.h:419
int32_t id
Definition image.h:401
uint32_t filters
Definition format.h:89
unsigned int channels
Definition format.h:83
dt_iop_buffer_type_t datatype
Definition format.h:85
dt_aligned_pixel_t processed_maximum
Definition format.h:114
int32_t hide_enable_button
Definition imageop.h:270
gboolean enabled
Definition imageop.h:313
dt_dev_operation_t op
Definition imageop.h:259
A profile reduced to the arithmetic the pixel loop can run: two matrices and six tone-curve LUTs,...
dt_colorspaces_color_profile_type_t type
Profile identity, half of the memo key. DT_COLORSPACE_NONE means "unset" and makes every apply functi...
char filename[DT_IOP_COLOR_ICC_LEN]
ICC file name, the other half of the memo key; "" for every built-in. Compared with strcmp,...
dt_iop_module_t *dt_dev_pixelpipe_iop_t * piece
Definition ioporder.c:94
GtkWidget * event_box
Definition ioporder.c:97
GtkWidget * toolbar_label
Definition ioporder.c:110
dt_iop_module_t * drag_dest
Definition ioporder.c:118
GtkWidget * graph_drawing
Definition ioporder.c:114
GtkWidget * graph_overlay
Definition ioporder.c:113
GtkWidget * graph_fixed
Definition ioporder.c:115
GtkWidget * graph_scroll
Definition ioporder.c:112
dt_iop_module_t * drag_source
Definition ioporder.c:117
GtkWidget * preset_combo
Definition ioporder.c:111
GtkWidget * window
Definition ioporder.c:109
gboolean refreshing_toolbar
Definition ioporder.c:108
char plugin_name[128]
Definition lib.h:81
GModule *void * data
Definition lib.h:79
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29
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)
#define DTGTK_IS_TOGGLEBUTTON(obj)
#define DT_GUI_BOX_SPACING
#define DT_PIXEL_APPLY_DPI(value)
void dt_capitalize_label(gchar *text)
void dt_gui_add_class(GtkWidget *widget, const gchar *class_name)
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_module_switch(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
@ DT_UI_CONTAINER_PANEL_LEFT_CENTER