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 "bauhaus/bauhaus.h"
28#include "common/colorspaces.h"
29#include "common/darktable.h"
30#include "common/debug.h"
31#include "common/history.h"
32#include "common/iop_order.h"
33#include "control/signal.h"
34#include "develop/develop.h"
35#include "develop/format.h"
36#include "develop/imageop.h"
38#include "dtgtk/button.h"
39#include "dtgtk/paint.h"
40#include "dtgtk/togglebutton.h"
41#include "gui/gtk.h"
42#include "gui/presets.h"
43#include "libs/lib.h"
44
45#include <gtk/gtk.h>
46#include <limits.h>
47#include <math.h>
48#include <stdlib.h>
49#include <string.h>
50
51DT_MODULE(1)
52
53#define DT_IOPORDER_GRAPH_NODE_WIDTH DT_PIXEL_APPLY_DPI(260)
54#define DT_IOPORDER_GRAPH_NODE_STEP DT_PIXEL_APPLY_DPI(300)
55#define DT_IOPORDER_GRAPH_LEFT_MARGIN DT_PIXEL_APPLY_DPI(60)
56#define DT_IOPORDER_GRAPH_TOP_MARGIN DT_PIXEL_APPLY_DPI(68)
57#define DT_IOPORDER_GRAPH_MIN_WIDTH DT_PIXEL_APPLY_DPI(640)
58#define DT_IOPORDER_GRAPH_HEIGHT DT_PIXEL_APPLY_DPI(540)
59#define DT_IOPORDER_BAND_HEIGHT DT_PIXEL_APPLY_DPI(26)
60#define DT_IOPORDER_MASK_ARROW_OFFSET DT_PIXEL_APPLY_DPI(12)
61#define DT_IOPORDER_MASK_BOTTOM_MARGIN DT_PIXEL_APPLY_DPI(56)
62
67
79
80static const GtkTargetEntry _ioporder_target_list[] = {
81 { "ioporder-node", GTK_TARGET_SAME_APP, DT_IOPORDER_DND_TARGET_NODE }
82};
83static const guint _ioporder_target_count = G_N_ELEMENTS(_ioporder_target_list);
84
99
115
116static void _ioporder_drag_data_get(GtkWidget *widget, GdkDragContext *context,
117 GtkSelectionData *selection_data, guint info, guint time,
118 gpointer user_data);
119static void _ioporder_drag_begin(GtkWidget *widget, GdkDragContext *context, gpointer user_data);
120static void _ioporder_drag_end(GtkWidget *widget, GdkDragContext *context, gpointer user_data);
121static gboolean _ioporder_drag_motion(GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
122 guint time, gpointer user_data);
123static gboolean _ioporder_drag_drop(GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
124 guint time, gpointer user_data);
125static void _ioporder_drag_leave(GtkWidget *widget, GdkDragContext *dc, guint time, gpointer user_data);
126static void _ioporder_drag_data_received(GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
127 GtkSelectionData *selection_data, guint info, guint time,
128 gpointer user_data);
129static void _ioporder_free_graph_node(gpointer data);
130static void _ioporder_popup_destroy(GtkWidget *widget, gpointer user_data);
131
141static gboolean _ioporder_module_in_history(const dt_iop_module_t *module)
142{
143 if(IS_NULL_PTR(darktable.develop) || !module) return FALSE;
144
145 /* Walk the whole history stack and look for the exact module instance. */
146 for(GList *history = g_list_last(darktable.develop->history); history; history = g_list_previous(history))
147 {
148 const dt_dev_history_item_t *hitem = (dt_dev_history_item_t *)history->data;
149 if(hitem && hitem->module == module) return TRUE;
150 }
151
152 return FALSE;
153}
154
165{
166 if(IS_NULL_PTR(module)) return FALSE;
167 if(dt_iop_is_hidden((dt_iop_module_t *)module)) return FALSE;
168 return _ioporder_module_in_history(module) || module->enabled;
169}
170
181{
182 if(IS_NULL_PTR(darktable.develop) || IS_NULL_PTR(darktable.develop->preview_pipe) || !module) return NULL;
183
184 /* Search the preview pipe nodes for the piece instantiated from this module. */
185 for(GList *nodes = darktable.develop->preview_pipe->nodes; nodes; nodes = g_list_next(nodes))
186 {
188 if(piece && piece->module == module) return piece;
189 }
190
191 return NULL;
192}
193
200static const char *_ioporder_type_to_string(const dt_iop_buffer_type_t datatype)
201{
202 switch(datatype)
203 {
204 case TYPE_FLOAT:
205 return _("32-bit float");
206 case TYPE_UINT16:
207 return _("16-bit integer");
208 case TYPE_UINT8:
209 return _("8-bit integer");
210 case TYPE_UNKNOWN:
211 default:
212 return _("unknown type");
213 }
214}
215
223{
224 switch(cst)
225 {
226 case IOP_CS_RAW:
227 return _("RAW");
228 case IOP_CS_RGB:
229 return _("RGB");
231 return _("Display RGB");
232 case IOP_CS_LAB:
233 return _("CIE Lab");
234 case IOP_CS_LCH:
235 return _("LCh");
236 case IOP_CS_HSL:
237 return _("HSL");
238 case IOP_CS_JZCZHZ:
239 return _("JzCzHz");
240 case IOP_CS_NONE:
241 return _("none");
242 default:
243 return _("unknown colorspace");
244 }
245}
246
254{
255 if(IS_NULL_PTR(dsc)) return g_strdup(_("no runtime descriptor"));
256
257 if(dsc->cst != IOP_CS_RAW)
258 {
259 if(dsc->filters == 9u) return g_strdup(_("X-Trans passthrough"));
260 if(dsc->filters != 0u) return g_strdup(_("Bayer passthrough"));
261 return g_strdup(_("no RAW flags"));
262 }
263
264 if(dsc->filters == 9u) return g_strdup(_("RAW X-Trans"));
265 if(dsc->filters != 0u) return g_strdup(_("RAW Bayer"));
266 return g_strdup(_("RAW monochrome"));
267}
268
280static gchar *_ioporder_descriptor_to_text(const char *prefix, const dt_iop_buffer_dsc_t *dsc,
281 const char *display_colorspace)
282{
283 if(IS_NULL_PTR(dsc))
284 return g_strdup_printf(_("%s:\n- runtime descriptor unavailable"), prefix);
285
286 gchar *raw_flags = _ioporder_raw_flags_to_string(dsc);
287 gchar *text = g_strdup_printf(_("%s:\n\t- %s\n\t- %u channels\n\t- %s\n"
288 "\t- max RGB:\n\t\t%.3f\n\t\t%.3f\n\t\t%.3f\n\t- %s"),
289 prefix, _ioporder_type_to_string(dsc->datatype), dsc->channels,
290 display_colorspace ? display_colorspace : _("runtime unavailable"),
291 dsc->processed_maximum[0], dsc->processed_maximum[1], dsc->processed_maximum[2],
292 raw_flags);
293 dt_free(raw_flags);
294 return text;
295}
296
312 const gboolean colorin_crossed,
313 const dt_iop_module_t *module,
314 const dt_iop_buffer_dsc_t *dsc,
315 const gboolean after_module)
316{
318
322 if(dsc->cst == IOP_CS_RGB)
323 {
324 const gboolean in_pipeline_rgb = colorin_crossed || (after_module && !strcmp(module->op, "colorin"));
325 if(raw_input && !in_pipeline_rgb) return DT_IOPORDER_RUNTIME_BAND_SENSOR_RGB;
327 }
329}
330
342 const dt_iop_buffer_dsc_t *dsc)
343{
344 switch(kind)
345 {
347 return _("RAW");
349 return _("Sensor RGB");
351 return _("Pipeline RGB");
353 return _("Display RGB");
355 return _("CIE Lab 1976");
357 return _("runtime unavailable");
359 return dsc ? _ioporder_colorspace_to_string(dsc->cst) : _("runtime unavailable");
361 default:
362 return _("runtime unavailable");
363 }
364}
365
373{
374 if(IS_NULL_PTR(color)) return;
375
376 switch(kind)
377 {
379 *color = (GdkRGBA){ 0.30, 0.30, 0.30, 0.90 };
380 break;
382 *color = (GdkRGBA){ 0.14, 0.56, 0.20, 0.96 };
383 break;
385 *color = (GdkRGBA){ 0.10, 0.38, 0.78, 0.96 };
386 break;
388 *color = (GdkRGBA){ 0.78, 0.38, 0.10, 0.96 };
389 break;
391 *color = (GdkRGBA){ 0.72, 0.54, 0.10, 0.96 };
392 break;
396 default:
397 *color = (GdkRGBA){ 0.38, 0.38, 0.38, 0.90 };
398 break;
399 }
400}
401
414 const dt_iop_order_iccprofile_info_t *profile_b)
415{
416 if(profile_a == profile_b) return TRUE;
417 if(IS_NULL_PTR(profile_a) || IS_NULL_PTR(profile_b)) return FALSE;
418
419 return profile_a->type == profile_b->type && !strcmp(profile_a->filename, profile_b->filename);
420}
421
437 const gboolean colorin_crossed,
438 const dt_iop_module_t *module,
439 const dt_iop_buffer_dsc_t *dsc,
440 const gboolean after_module)
441{
442 if(IS_NULL_PTR(darktable.develop) || IS_NULL_PTR(darktable.develop->preview_pipe) || !module || IS_NULL_PTR(dsc)) return NULL;
443 if(!dt_iop_colorspace_is_rgb(dsc->cst)) return NULL;
444
445 if(dsc->cst == IOP_CS_RGB_DISPLAY)
447
448 const gboolean in_pipeline_rgb = colorin_crossed || (after_module && !strcmp(module->op, "colorin"));
449 if(!in_pipeline_rgb)
451
452 (void)raw_input;
454}
455
467static gchar *_ioporder_runtime_band_text(const char *label,
468 const dt_iop_order_iccprofile_info_t *profile_info)
469{
470 if(IS_NULL_PTR(label)) return g_strdup(_("runtime unavailable"));
471 if(IS_NULL_PTR(profile_info) || profile_info->type == DT_COLORSPACE_NONE) return g_strdup(label);
472
473 const char *profile_name = dt_colorspaces_get_name(profile_info->type, profile_info->filename);
474 if(IS_NULL_PTR(profile_name) || profile_name[0] == '\0') return g_strdup(label);
475
476 return g_strdup_printf("%s - %s", label, profile_name);
477}
478
489{
491
493 {
494 d->current_mode = DT_IOP_ORDER_ANSEL_RAW;
495 return g_strdup(_(dt_iop_order_string(DT_IOP_ORDER_ANSEL_RAW)));
496 }
497
499
501 {
502 d->current_mode = kind;
503 return g_strdup(_(dt_iop_order_string(kind)));
504 }
505
507 gchar *name = NULL;
508 int index = 0;
509 sqlite3_stmt *stmt;
510
511 // clang-format off
513 "SELECT op_params, name"
514 " FROM data.presets"
515 " WHERE operation='ioporder'"
516 " ORDER BY writeprotect DESC, LOWER(name), rowid",
517 -1, &stmt, NULL);
518 // clang-format on
519
520 /* Compare the current order text against every saved preset serialization. */
521 while(sqlite3_step(stmt) == SQLITE_ROW)
522 {
523 const char *params = (const char *)sqlite3_column_blob(stmt, 0);
524 const int32_t params_len = sqlite3_column_bytes(stmt, 0);
525 const char *preset_name = (const char *)sqlite3_column_text(stmt, 1);
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
543 sqlite3_finalize(stmt);
544 dt_free(iop_order_list);
545
546 if(name) return name;
547
548 d->current_mode = DT_IOP_ORDER_CUSTOM;
549 return g_strdup(_(dt_iop_order_string(DT_IOP_ORDER_CUSTOM)));
550}
551
562{
564 if(!d->preset_combo || IS_NULL_PTR(d->toolbar_label)) return;
565
566 gchar *current_name = _ioporder_get_current_order_name(self);
567
568 gtk_label_set_text(GTK_LABEL(d->toolbar_label), current_name);
569
570 d->refreshing_toolbar = TRUE;
571 gtk_combo_box_text_remove_all(GTK_COMBO_BOX_TEXT(d->preset_combo));
572 gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(d->preset_combo), "__custom__", _("custom order"));
573
574 gchar *active_id = g_strdup("__custom__");
575 sqlite3_stmt *stmt;
576 // clang-format off
578 "SELECT name"
579 " FROM data.presets"
580 " WHERE operation='ioporder' AND op_version=?1"
581 " ORDER BY writeprotect DESC, LOWER(name), rowid",
582 -1, &stmt, NULL);
583 // clang-format on
584 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, self->version());
585
586 /* Rebuild the preset list in DB order and keep the current one selected. */
587 while(sqlite3_step(stmt) == SQLITE_ROW)
588 {
589 const char *preset_name = (const char *)sqlite3_column_text(stmt, 0);
590 gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(d->preset_combo), preset_name, preset_name);
591 if(!g_strcmp0(current_name, preset_name))
592 {
593 dt_free(active_id);
594 active_id = g_strdup(preset_name);
595 }
596 }
597 sqlite3_finalize(stmt);
598
599 gtk_combo_box_set_active_id(GTK_COMBO_BOX(d->preset_combo), active_id);
600 d->refreshing_toolbar = FALSE;
601
602 dt_free(active_id);
603 dt_free(current_name);
604}
605
612{
613 if(IS_NULL_PTR(d) || IS_NULL_PTR(d->graph_fixed)) return;
614
615 GList *children = gtk_container_get_children(GTK_CONTAINER(d->graph_fixed));
616 for(GList *iter = children; iter; iter = g_list_next(iter))
617 {
618 gtk_widget_destroy(GTK_WIDGET(iter->data));
619 }
620 g_list_free(children);
621 children = NULL;
622
623 g_list_free_full(d->nodes, _ioporder_free_graph_node);
624 d->nodes = NULL;
625}
626
639static void _ioporder_popup_destroy(GtkWidget *widget, gpointer user_data)
640{
641 dt_lib_ioporder_t *d = (dt_lib_ioporder_t *)user_data;
642 if(IS_NULL_PTR(d)) return;
643
644 g_list_free_full(d->nodes, _ioporder_free_graph_node);
645 d->nodes = NULL;
646
647 d->window = NULL;
648 d->toolbar_label = NULL;
649 d->preset_combo = NULL;
650 d->graph_scroll = NULL;
651 d->graph_overlay = NULL;
652 d->graph_drawing = NULL;
653 d->graph_fixed = NULL;
654 d->drag_source = NULL;
655 d->drag_dest = NULL;
656}
657
666static void _ioporder_free_graph_node(gpointer data)
667{
669 if(IS_NULL_PTR(node)) return;
670
671 dt_free(node->endpoint_label);
672 dt_free(node);
673}
674
685{
686 if(!DTGTK_IS_TOGGLEBUTTON(widget) || !module) return;
687
688 if(module->hide_enable_button)
689 {
691 }
692 else
693 {
695 }
696}
697
707static void _ioporder_node_toggle_enable(GtkToggleButton *togglebutton, gpointer user_data)
708{
709 const dt_ioporder_graph_node_t *node = (const dt_ioporder_graph_node_t *)user_data;
710 if(!node || !GTK_IS_TOGGLE_BUTTON(node->module->off)) return;
711
712 const gboolean active = gtk_toggle_button_get_active(togglebutton);
713 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(node->module->off)) != active)
714 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(node->module->off), active);
715}
716
723static void _ioporder_node_toggle_mask(GtkToggleButton *togglebutton, gpointer user_data)
724{
725 const dt_ioporder_graph_node_t *node = (const dt_ioporder_graph_node_t *)user_data;
726 if(!node || !GTK_IS_TOGGLE_BUTTON(node->module->mask_indicator)) return;
727
728 const gboolean active = gtk_toggle_button_get_active(togglebutton);
729 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(node->module->mask_indicator)) != active)
730 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(node->module->mask_indicator), active);
731}
732
739static void _ioporder_node_show_presets(GtkButton *button, gpointer user_data)
740{
741 const dt_ioporder_graph_node_t *node = (const dt_ioporder_graph_node_t *)user_data;
742 if(!node || !node->module) return;
743
746 GDK_GRAVITY_SOUTH_EAST, GDK_GRAVITY_NORTH_EAST);
747
749}
750
760 const char *display_in,
761 const char *display_out)
762{
764 node->module = module;
765 node->piece = piece;
766
767 GtkWidget *event_box = gtk_event_box_new();
768 GtkWidget *frame = gtk_frame_new(NULL);
769 GtkWidget *body = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
770 GtkWidget *header = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING / 2.);
771 gchar *clean_name = delete_underscore(module->name());
772 gchar **split_name = g_strsplit(clean_name, "-", -1);
773 gchar *module_name = g_strjoinv(" ", split_name);
774 GtkWidget *label = gtk_label_new(module_name);
775 gchar *instance_name = dt_dev_get_multi_name(module);
776 GtkWidget *instance = gtk_label_new(instance_name);
780
781 dt_gui_add_class(frame, "dt_module_frame");
782 dt_gui_add_class(frame, "dt_iop_module");
783 gtk_widget_set_name(body, "module-header");
784
785 dt_gui_add_class(enable, "dt_iop_enable_button");
786
787 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
788
789 gtk_widget_set_size_request(event_box, DT_IOPORDER_GRAPH_NODE_WIDTH, -1);
790 gtk_widget_set_hexpand(event_box, FALSE);
791 gtk_widget_set_halign(event_box, GTK_ALIGN_START);
792
793 dt_capitalize_label(module_name);
794 gtk_label_set_text(GTK_LABEL(label), module_name);
795 gtk_widget_set_halign(label, GTK_ALIGN_START);
796 gtk_widget_set_valign(label, GTK_ALIGN_CENTER);
797 gtk_label_set_xalign(GTK_LABEL(label), 0.0f);
798 gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_END);
799 gtk_widget_set_name(label, "iop-panel-label");
800
801 gtk_widget_set_halign(instance, GTK_ALIGN_START);
802 gtk_widget_set_visible(instance, instance_name[0] != '\0');
803 gtk_label_set_xalign(GTK_LABEL(instance), 0.0f);
804 gtk_label_set_ellipsize(GTK_LABEL(instance), PANGO_ELLIPSIZE_MIDDLE);
805 gtk_label_set_line_wrap(GTK_LABEL(instance), FALSE);
806
808 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(enable), module->enabled);
809 gtk_widget_set_sensitive(enable, !module->hide_enable_button);
810 gtk_widget_set_valign(enable, GTK_ALIGN_CENTER);
811
812 const gboolean show_masks = (module->flags() & IOP_FLAGS_SUPPORTS_BLENDING) == IOP_FLAGS_SUPPORTS_BLENDING
813 && module->blend_params
814 && module->blend_params->mask_mode > DEVELOP_MASK_ENABLED;
815 const gboolean show_mask_preview = module->request_mask_display
816 & (DT_DEV_PIXELPIPE_DISPLAY_MASK | DT_DEV_PIXELPIPE_DISPLAY_CHANNEL);
817 gtk_widget_set_visible(mask, show_masks);
818 gtk_widget_set_sensitive(mask, show_masks && module->enabled);
819 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mask), show_mask_preview);
820 gtk_widget_set_valign(mask, GTK_ALIGN_CENTER);
821 gtk_widget_set_valign(presets, GTK_ALIGN_CENTER);
822
823 gtk_widget_set_tooltip_text(enable, _("toggle module"));
824 gtk_widget_set_tooltip_text(mask, _("display mask"));
825 gtk_widget_set_tooltip_text(presets, _("module presets"));
826
827 gtk_box_pack_start(GTK_BOX(header), enable, FALSE, FALSE, 0);
828 gtk_box_pack_start(GTK_BOX(header), label, TRUE, TRUE, 0);
829 gtk_box_pack_end(GTK_BOX(header), presets, FALSE, FALSE, 0);
830 gtk_box_pack_end(GTK_BOX(header), mask, FALSE, FALSE, 0);
831
832 gchar *text_in = _ioporder_descriptor_to_text(_("In"), piece ? &piece->dsc_in : NULL, display_in);
833 gchar *text_out = _ioporder_descriptor_to_text(_("Out"), piece ? &piece->dsc_out : NULL, display_out);
834 GtkWidget *info_in = gtk_label_new(text_in);
835 GtkWidget *info_out = gtk_label_new(text_out);
836 dt_free(text_in);
837 dt_free(text_out);
838 dt_free(instance_name);
839 g_strfreev(split_name);
840 dt_free(module_name);
841 dt_free(clean_name);
842
843 gtk_label_set_xalign(GTK_LABEL(info_in), 0.0f);
844 gtk_label_set_xalign(GTK_LABEL(info_out), 0.0f);
845 gtk_label_set_line_wrap(GTK_LABEL(info_in), TRUE);
846 gtk_label_set_line_wrap(GTK_LABEL(info_out), TRUE);
847 gtk_label_set_justify(GTK_LABEL(info_in), GTK_JUSTIFY_LEFT);
848 gtk_label_set_justify(GTK_LABEL(info_out), GTK_JUSTIFY_LEFT);
849
850 gtk_box_pack_start(GTK_BOX(body), header, FALSE, FALSE, 0);
851 gtk_box_pack_start(GTK_BOX(body), instance, FALSE, FALSE, 0);
852 gtk_box_pack_start(GTK_BOX(body), info_in, FALSE, FALSE, 0);
853 gtk_box_pack_start(GTK_BOX(body), info_out, FALSE, FALSE, 0);
854 gtk_container_add(GTK_CONTAINER(frame), body);
855 gtk_container_add(GTK_CONTAINER(event_box), frame);
856
857 g_signal_connect(enable, "toggled", G_CALLBACK(_ioporder_node_toggle_enable), node);
858 g_signal_connect(mask, "toggled", G_CALLBACK(_ioporder_node_toggle_mask), node);
859 g_signal_connect(presets, "clicked", G_CALLBACK(_ioporder_node_show_presets), node);
860
861 node->event_box = event_box;
862 node->header = header;
863 node->title = label;
864 node->instance = instance;
865 node->info_in = info_in;
866 node->info_out = info_out;
867
868 g_object_set_data(G_OBJECT(event_box), "dt-ioporder-module", module);
869
870 return node;
871}
872
883{
885 node->is_endpoint = TRUE;
886 node->endpoint_label = g_strdup(label);
887
888 GtkWidget *event_box = gtk_event_box_new();
889 GtkWidget *frame = gtk_frame_new(NULL);
890 GtkWidget *body = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
891 GtkWidget *title = gtk_label_new(label);
892
893 dt_gui_add_class(frame, "dt_module_frame");
894 dt_gui_add_class(frame, "dt_iop_module");
895 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
896 gtk_widget_set_size_request(event_box, DT_PIXEL_APPLY_DPI(180), DT_PIXEL_APPLY_DPI(64));
897 gtk_widget_set_halign(event_box, GTK_ALIGN_START);
898 gtk_widget_set_valign(event_box, GTK_ALIGN_CENTER);
899 gtk_widget_set_margin_top(body, DT_PIXEL_APPLY_DPI(12));
900 gtk_widget_set_margin_bottom(body, DT_PIXEL_APPLY_DPI(12));
901 gtk_widget_set_margin_start(body, DT_PIXEL_APPLY_DPI(12));
902 gtk_widget_set_margin_end(body, DT_PIXEL_APPLY_DPI(12));
903 gtk_widget_set_name(body, "ioporder-endpoint");
904
906 gtk_label_set_text(GTK_LABEL(title), node->endpoint_label);
907 gtk_widget_set_halign(title, GTK_ALIGN_CENTER);
908 gtk_widget_set_valign(title, GTK_ALIGN_CENTER);
909 gtk_label_set_xalign(GTK_LABEL(title), 0.5f);
910 gtk_widget_set_name(title, "iop-panel-label");
911
912 gtk_box_pack_start(GTK_BOX(body), title, TRUE, TRUE, 0);
913 gtk_container_add(GTK_CONTAINER(frame), body);
914 gtk_container_add(GTK_CONTAINER(event_box), frame);
915
916 node->event_box = event_box;
917 node->header = body;
918 node->title = title;
919
920 return node;
921}
922
933{
935 if(IS_NULL_PTR(d) || IS_NULL_PTR(d->graph_fixed) || IS_NULL_PTR(d->graph_overlay)) return;
936
939
941 int visible_count = 0;
942 const gboolean raw_input = darktable.develop && darktable.develop->image_storage.dsc.cst == IOP_CS_RAW;
943 gboolean colorin_crossed = FALSE;
944 const int endpoint_height = DT_PIXEL_APPLY_DPI(64);
945 const int base_x = x;
946 int screen_x = x;
947 int endpoint_y = (DT_IOPORDER_GRAPH_HEIGHT) / 2;
948 int module_header_center_y = 0;
949
951
952 /* Walk the sorted module list and instantiate one graph node per visible module. */
953 for(GList *modules = darktable.develop->iop; modules; modules = g_list_next(modules))
954 {
955 dt_iop_module_t *module = (dt_iop_module_t *)modules->data;
956 if(!_ioporder_module_is_graph_visible(module)) continue;
957
959 const dt_iop_buffer_dsc_t *const dsc_in = piece ? &piece->dsc_in : NULL;
960 const dt_iop_buffer_dsc_t *const dsc_out = piece ? &piece->dsc_out : NULL;
961 const char *display_in
962 = _ioporder_runtime_band_label(_ioporder_runtime_band_kind(raw_input, colorin_crossed, module, dsc_in, FALSE),
963 dsc_in);
964 const char *display_out
965 = _ioporder_runtime_band_label(_ioporder_runtime_band_kind(raw_input, colorin_crossed, module, dsc_out, TRUE),
966 dsc_out);
967 dt_ioporder_graph_node_t *node = _ioporder_create_graph_node(module, piece, display_in, display_out);
968 d->nodes = g_list_append(d->nodes, node);
969
970 gtk_drag_source_set(node->event_box, GDK_BUTTON1_MASK, _ioporder_target_list, _ioporder_target_count,
971 GDK_ACTION_COPY);
972 gtk_drag_dest_set(node->event_box, GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
974 g_signal_connect(node->event_box, "drag-begin", G_CALLBACK(_ioporder_drag_begin), self);
975 g_signal_connect(node->event_box, "drag-data-get", G_CALLBACK(_ioporder_drag_data_get), self);
976 g_signal_connect(node->event_box, "drag-end", G_CALLBACK(_ioporder_drag_end), self);
977 g_signal_connect(node->event_box, "drag-motion", G_CALLBACK(_ioporder_drag_motion), self);
978 g_signal_connect(node->event_box, "drag-drop", G_CALLBACK(_ioporder_drag_drop), self);
979 g_signal_connect(node->event_box, "drag-data-received", G_CALLBACK(_ioporder_drag_data_received), self);
980 g_signal_connect(node->event_box, "drag-leave", G_CALLBACK(_ioporder_drag_leave), self);
981
982 gtk_fixed_put(GTK_FIXED(d->graph_fixed), node->event_box, x, DT_IOPORDER_GRAPH_TOP_MARGIN);
983 gtk_widget_show_all(node->event_box);
984 if(module_header_center_y == 0)
985 {
986 GtkRequisition minimum = { 0 }, natural = { 0 };
987 gtk_widget_get_preferred_size(node->header, &minimum, &natural);
988 module_header_center_y = DT_IOPORDER_GRAPH_TOP_MARGIN + natural.height / 2;
989 }
990
992 visible_count++;
993 if(!strcmp(module->op, "colorin")) colorin_crossed = TRUE;
994 }
995
996 if(module_header_center_y > 0)
997 endpoint_y = module_header_center_y - endpoint_height / 2;
998
999 dt_ioporder_graph_node_t *base_node = _ioporder_create_endpoint_node(_("base image"));
1000 d->nodes = g_list_prepend(d->nodes, base_node);
1001 gtk_fixed_put(GTK_FIXED(d->graph_fixed), base_node->event_box, base_x, endpoint_y);
1002 gtk_widget_show_all(base_node->event_box);
1003 visible_count++;
1004
1005 screen_x = x;
1006 dt_ioporder_graph_node_t *screen_node = _ioporder_create_endpoint_node(_("screen"));
1007 d->nodes = g_list_append(d->nodes, screen_node);
1008 gtk_fixed_put(GTK_FIXED(d->graph_fixed), screen_node->event_box, screen_x, endpoint_y);
1009 gtk_widget_show_all(screen_node->event_box);
1010 visible_count++;
1011
1012 const int total_width = MAX(DT_IOPORDER_GRAPH_MIN_WIDTH,
1015
1016 gtk_widget_set_size_request(d->graph_overlay, total_width, DT_IOPORDER_GRAPH_HEIGHT);
1017 gtk_widget_set_size_request(d->graph_drawing, total_width, DT_IOPORDER_GRAPH_HEIGHT);
1018 gtk_widget_set_size_request(d->graph_fixed, total_width, DT_IOPORDER_GRAPH_HEIGHT);
1019 if(d->window && d->graph_scroll)
1020 {
1021 GdkDisplay *display = gtk_widget_get_display(d->window);
1022 GdkWindow *window = gtk_widget_get_window(d->window);
1023 if(IS_NULL_PTR(window))
1024 window = gtk_widget_get_window(dt_ui_main_window(darktable.gui->ui));
1025
1026 GdkMonitor *monitor = (display && window) ? gdk_display_get_monitor_at_window(display, window) : NULL;
1027 if(IS_NULL_PTR(monitor) && display)
1028 {
1029 monitor = gdk_display_get_primary_monitor(display);
1030 if(IS_NULL_PTR(monitor) && gdk_display_get_n_monitors(display) > 0)
1031 monitor = gdk_display_get_monitor(display, 0);
1032 }
1033
1034 GdkRectangle geometry = { 0 };
1035 if(monitor)
1036 gdk_monitor_get_workarea(monitor, &geometry);
1037
1038 const int viewport_width = geometry.width > 0 ? (int)(geometry.width * 0.88) : DT_PIXEL_APPLY_DPI(1120);
1039 const int viewport_height = geometry.height > 0 ? (int)(geometry.height * 0.70) : DT_PIXEL_APPLY_DPI(440);
1040 const int graph_width = MIN(total_width, viewport_width);
1041 const int graph_height = MIN(DT_IOPORDER_GRAPH_HEIGHT, viewport_height);
1042 const int wanted_width = graph_width + DT_PIXEL_APPLY_DPI(40);
1043 const int wanted_height = graph_height + DT_PIXEL_APPLY_DPI(110);
1044
1045 gtk_widget_set_size_request(d->graph_scroll, graph_width, graph_height);
1046 gtk_window_resize(GTK_WINDOW(d->window), wanted_width, wanted_height);
1047 }
1048 gtk_widget_queue_draw(d->graph_drawing);
1049}
1050
1061static void _ioporder_draw_rounded_rect(cairo_t *cr, const double x, const double y,
1062 const double width, const double height, const double radius)
1063{
1064 const double r = MIN(radius, MIN(width, height) * 0.5);
1065
1066 cairo_new_sub_path(cr);
1067 cairo_arc(cr, x + width - r, y + r, r, -M_PI_2, 0.0);
1068 cairo_arc(cr, x + width - r, y + height - r, r, 0.0, M_PI_2);
1069 cairo_arc(cr, x + r, y + height - r, r, M_PI_2, M_PI);
1070 cairo_arc(cr, x + r, y + r, r, M_PI, 3.0 * M_PI_2);
1071 cairo_close_path(cr);
1072}
1073
1083static void _ioporder_draw_label(GtkWidget *widget, cairo_t *cr, const double x, const double y, const char *text)
1084{
1085 if(IS_NULL_PTR(text) || !widget) return;
1086
1087 PangoLayout *layout = gtk_widget_create_pango_layout(widget, text);
1088 cairo_move_to(cr, x, y);
1089 pango_cairo_show_layout(cr, layout);
1090 g_object_unref(layout);
1091}
1092
1102static void _ioporder_draw_sequence_arrow(cairo_t *cr, const double x1, const double y1,
1103 const double x2, const double y2)
1104{
1105 cairo_move_to(cr, x1, y1);
1106 cairo_line_to(cr, x2, y2);
1107 cairo_stroke(cr);
1108
1109 const double angle = atan2(y2 - y1, x2 - x1);
1110 const double arrow = DT_PIXEL_APPLY_DPI(8);
1111
1112 cairo_move_to(cr, x2, y2);
1113 cairo_line_to(cr, x2 - arrow * cos(angle - M_PI / 6.0), y2 - arrow * sin(angle - M_PI / 6.0));
1114 cairo_move_to(cr, x2, y2);
1115 cairo_line_to(cr, x2 - arrow * cos(angle + M_PI / 6.0), y2 - arrow * sin(angle + M_PI / 6.0));
1116 cairo_stroke(cr);
1117}
1118
1129static void _ioporder_draw_mask_arrow(GtkWidget *widget, cairo_t *cr, const double sx, const double sy,
1130 const double dx, const double dy)
1131{
1132 GtkAllocation area = { 0 };
1133 gtk_widget_get_allocation(widget, &area);
1134
1135 const double span = fabs(dx - sx);
1136 const double lift = DT_PIXEL_APPLY_DPI(36) + span * 0.20;
1137 const double cy = MIN(area.height - DT_IOPORDER_MASK_BOTTOM_MARGIN, MAX(sy, dy) + lift);
1138 const double c1x = sx + (dx - sx) * 0.25;
1139 const double c2x = sx + (dx - sx) * 0.75;
1140
1141 cairo_move_to(cr, sx, sy);
1142 cairo_curve_to(cr, c1x, cy, c2x, cy, dx, dy);
1143 cairo_stroke(cr);
1144
1145 const double arrow = DT_PIXEL_APPLY_DPI(8);
1146 const double tx = dx - c2x;
1147 const double ty = dy - cy;
1148 const double angle = atan2(ty, tx);
1149
1150 cairo_move_to(cr, dx, dy);
1151 cairo_line_to(cr, dx - arrow * cos(angle - M_PI / 6.0), dy - arrow * sin(angle - M_PI / 6.0));
1152 cairo_move_to(cr, dx, dy);
1153 cairo_line_to(cr, dx - arrow * cos(angle + M_PI / 6.0), dy - arrow * sin(angle + M_PI / 6.0));
1154 cairo_stroke(cr);
1155
1156 const double mx = (sx + dx) * 0.5;
1157 const double my = MIN(area.height - DT_PIXEL_APPLY_DPI(18), cy + DT_PIXEL_APPLY_DPI(8));
1158 cairo_save(cr);
1159 cairo_set_source_rgba(cr, 0.12, 0.12, 0.12, 0.85);
1162 cairo_fill(cr);
1163 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
1164 dtgtk_cairo_paint_showmask(cr, (int)(mx - DT_PIXEL_APPLY_DPI(7)), (int)(my - DT_PIXEL_APPLY_DPI(7)),
1165 DT_PIXEL_APPLY_DPI(14), DT_PIXEL_APPLY_DPI(14), 0, NULL);
1166 cairo_restore(cr);
1167
1168 (void)widget;
1169}
1170
1179static gboolean _ioporder_graph_draw(GtkWidget *widget, cairo_t *cr, gpointer user_data)
1180{
1181 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
1183 if(IS_NULL_PTR(d)) return FALSE;
1184
1185 GtkAllocation area = { 0 };
1186 gtk_widget_get_allocation(widget, &area);
1187
1188 gtk_render_background(gtk_widget_get_style_context(widget), cr, 0.0, 0.0, area.width, area.height);
1189
1190 if(!d->nodes)
1191 {
1192 cairo_set_source_rgb(cr, 0.85, 0.85, 0.85);
1194 _("No active or history modules are currently available."));
1195 return FALSE;
1196 }
1197
1198 const gboolean raw_input = darktable.develop && darktable.develop->image_storage.dsc.cst == IOP_CS_RAW;
1199
1200 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(2));
1201
1202 gboolean colorin_crossed = FALSE;
1205 const dt_iop_order_iccprofile_info_t *segment_profile = NULL;
1206 gchar *segment_label = NULL;
1207 GdkRGBA segment_color = { 0 };
1208 double segment_x = 0.0;
1209 double segment_width = 0.0;
1210
1211 /* Draw the runtime colorspace band by merging consecutive nodes with the same runtime state. */
1212 for(GList *iter = d->nodes; iter; iter = g_list_next(iter))
1213 {
1214 const dt_ioporder_graph_node_t *node = (const dt_ioporder_graph_node_t *)iter->data;
1215 if(node->is_endpoint || !node->module) continue;
1216 GtkAllocation alloc = { 0 };
1217 gtk_widget_get_allocation(node->event_box, &alloc);
1218
1219 const dt_iop_buffer_dsc_t *const dsc_out = node->piece ? &node->piece->dsc_out : NULL;
1220 const dt_ioporder_runtime_band_kind_t band_kind
1221 = _ioporder_runtime_band_kind(raw_input, colorin_crossed, node->module, dsc_out, TRUE);
1222 const dt_iop_order_iccprofile_info_t *profile_info
1223 = _ioporder_runtime_band_profile_info(raw_input, colorin_crossed, node->module, dsc_out, TRUE);
1224 const char *band_label = _ioporder_runtime_band_label(band_kind, dsc_out);
1225 gchar *band_text = _ioporder_runtime_band_text(band_label, profile_info);
1226 GdkRGBA band_color = { 0 };
1227 _ioporder_runtime_band_color(band_kind, &band_color);
1228
1229 if(IS_NULL_PTR(segment_label) || segment_kind != band_kind || segment_cst != (dsc_out ? dsc_out->cst : IOP_CS_NONE)
1230 || !_ioporder_same_runtime_profile(segment_profile, profile_info))
1231 {
1232 if(segment_label)
1233 {
1234 cairo_set_source_rgba(cr, segment_color.red, segment_color.green, segment_color.blue, segment_color.alpha);
1237 cairo_fill(cr);
1238 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
1239 _ioporder_draw_label(widget, cr, segment_x + DT_PIXEL_APPLY_DPI(10), DT_PIXEL_APPLY_DPI(22), segment_label);
1240 dt_free(segment_label);
1241 }
1242
1243 segment_kind = band_kind;
1244 segment_cst = dsc_out ? dsc_out->cst : IOP_CS_NONE;
1245 segment_profile = profile_info;
1246 segment_label = band_text;
1247 segment_color = band_color;
1248 segment_x = alloc.x;
1249 segment_width = alloc.width;
1250 }
1251 else
1252 {
1253 dt_free(band_text);
1254 segment_width = alloc.x + alloc.width - segment_x;
1255 }
1256 if(!strcmp(node->module->op, "colorin")) colorin_crossed = TRUE;
1257 }
1258
1259 if(segment_label)
1260 {
1261 cairo_set_source_rgba(cr, segment_color.red, segment_color.green, segment_color.blue, segment_color.alpha);
1264 cairo_fill(cr);
1265 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
1266 _ioporder_draw_label(widget, cr, segment_x + DT_PIXEL_APPLY_DPI(10), DT_PIXEL_APPLY_DPI(22), segment_label);
1267 dt_free(segment_label);
1268 }
1269
1270 cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.22);
1271 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(2));
1272
1273 /* Draw every fence as a background boundary that modules must not cross. */
1274 for(GList *iter = d->nodes; iter; iter = g_list_next(iter))
1275 {
1276 const dt_ioporder_graph_node_t *node = (const dt_ioporder_graph_node_t *)iter->data;
1277 if(node->is_endpoint || !node->module) continue;
1278
1279 GtkAllocation alloc = { 0 };
1280 gtk_widget_get_allocation(node->event_box, &alloc);
1281 const double x = alloc.x - DT_PIXEL_APPLY_DPI(12);
1282 cairo_move_to(cr, x, DT_PIXEL_APPLY_DPI(12));
1283 cairo_line_to(cr, x, area.height - DT_PIXEL_APPLY_DPI(12));
1284 }
1285 cairo_stroke(cr);
1286
1287 cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.90);
1288 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(2));
1289
1290 /* Consecutive nodes are laid out left-to-right in processing order, so we
1291 * keep the arrow head on the right-hand successor. */
1292 for(GList *iter = d->nodes; iter && g_list_next(iter); iter = g_list_next(iter))
1293 {
1294 const dt_ioporder_graph_node_t *node_a = (const dt_ioporder_graph_node_t *)iter->data;
1295 const dt_ioporder_graph_node_t *node_b = (const dt_ioporder_graph_node_t *)g_list_next(iter)->data;
1296 GtkAllocation alloc_a = { 0 }, alloc_b = { 0 }, head_a = { 0 }, head_b = { 0 };
1297 gtk_widget_get_allocation(node_a->event_box, &alloc_a);
1298 gtk_widget_get_allocation(node_b->event_box, &alloc_b);
1299 gtk_widget_get_allocation(node_a->header, &head_a);
1300 gtk_widget_get_allocation(node_b->header, &head_b);
1301 const double arrow_y_a = node_a->is_endpoint ? alloc_a.y + alloc_a.height * 0.5
1302 : alloc_a.y + head_a.y + head_a.height * 0.5;
1303 const double arrow_y_b = node_b->is_endpoint ? alloc_b.y + alloc_b.height * 0.5
1304 : alloc_b.y + head_b.y + head_b.height * 0.5;
1305
1306 _ioporder_draw_sequence_arrow(cr, alloc_a.x + alloc_a.width + DT_PIXEL_APPLY_DPI(6),
1307 arrow_y_a,
1308 alloc_b.x - DT_PIXEL_APPLY_DPI(6),
1309 arrow_y_b);
1310 }
1311
1312 cairo_set_source_rgba(cr, 0.95, 0.54, 0.13, 0.92);
1313 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(3));
1314
1315 /* Walk every visible module and draw one orange dependency arrow per raster-mask consumer. */
1316 for(GList *iter = d->nodes; iter; iter = g_list_next(iter))
1317 {
1318 const dt_ioporder_graph_node_t *source = (const dt_ioporder_graph_node_t *)iter->data;
1319 if(source->is_endpoint || !source->module) continue;
1320 GtkAllocation source_alloc = { 0 };
1321 gtk_widget_get_allocation(source->event_box, &source_alloc);
1322
1323 for(GList *consumers = d->nodes; consumers; consumers = g_list_next(consumers))
1324 {
1325 const dt_ioporder_graph_node_t *sink = (const dt_ioporder_graph_node_t *)consumers->data;
1326 if(sink->is_endpoint || !sink->module) continue;
1327 if(sink->module->raster_mask.sink.source != source->module) continue;
1328
1329 GtkAllocation sink_alloc = { 0 };
1330 gtk_widget_get_allocation(sink->event_box, &sink_alloc);
1331
1332 _ioporder_draw_mask_arrow(widget, cr,
1333 source_alloc.x + source_alloc.width * 0.5,
1334 source_alloc.y + source_alloc.height + DT_IOPORDER_MASK_ARROW_OFFSET,
1335 sink_alloc.x + sink_alloc.width * 0.5,
1336 sink_alloc.y + sink_alloc.height + DT_IOPORDER_MASK_ARROW_OFFSET);
1337 }
1338 }
1339
1340 if(d->drag_source && d->drag_dest && d->drag_source != d->drag_dest)
1341 {
1342 cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.95);
1343 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(4));
1344
1345 /* Materialize the drop position with a bright insertion line. */
1346 for(GList *iter = d->nodes; iter; iter = g_list_next(iter))
1347 {
1348 const dt_ioporder_graph_node_t *node = (const dt_ioporder_graph_node_t *)iter->data;
1349 if(node->module != d->drag_dest) continue;
1350
1351 GtkAllocation alloc = { 0 };
1352 gtk_widget_get_allocation(node->event_box, &alloc);
1353 const double x = d->drag_source->iop_order < d->drag_dest->iop_order
1354 ? alloc.x + alloc.width + DT_PIXEL_APPLY_DPI(10)
1355 : alloc.x - DT_PIXEL_APPLY_DPI(10);
1356 cairo_move_to(cr, x, DT_PIXEL_APPLY_DPI(12));
1357 cairo_line_to(cr, x, area.height - DT_PIXEL_APPLY_DPI(12));
1358 cairo_stroke(cr);
1359 break;
1360 }
1361 }
1362
1363 return FALSE;
1364}
1365
1376static void _ioporder_drag_data_get(GtkWidget *widget, GdkDragContext *context,
1377 GtkSelectionData *selection_data, guint info, guint time,
1378 gpointer user_data)
1379{
1380 guint value = 1;
1381 gtk_selection_data_set(selection_data, gdk_atom_intern("ioporder-node", TRUE), 32,
1382 (const guchar *)&value, 1);
1383}
1384
1392static void _ioporder_drag_begin(GtkWidget *widget, GdkDragContext *context, gpointer user_data)
1393{
1394 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
1396 d->drag_source = (dt_iop_module_t *)g_object_get_data(G_OBJECT(widget), "dt-ioporder-module");
1397 d->drag_dest = NULL;
1398 gtk_widget_queue_draw(d->graph_drawing);
1399}
1400
1408static void _ioporder_drag_end(GtkWidget *widget, GdkDragContext *context, gpointer user_data)
1409{
1410 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
1412 d->drag_source = NULL;
1413 d->drag_dest = NULL;
1414 gtk_widget_queue_draw(d->graph_drawing);
1415}
1416
1428static gboolean _ioporder_drag_motion(GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
1429 guint time, gpointer user_data)
1430{
1431 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
1433 dt_iop_module_t *module_src = d->drag_source;
1434 dt_iop_module_t *module_dest = (dt_iop_module_t *)g_object_get_data(G_OBJECT(widget), "dt-ioporder-module");
1435
1436 gboolean can_move = FALSE;
1437 if(module_src && module_dest && module_src != module_dest)
1438 {
1439 if(module_src->iop_order < module_dest->iop_order)
1440 can_move = dt_ioppr_check_can_move_after_iop(darktable.develop->iop, module_src, module_dest);
1441 else
1442 can_move = dt_ioppr_check_can_move_before_iop(darktable.develop->iop, module_src, module_dest);
1443 }
1444
1445 d->drag_dest = can_move ? module_dest : NULL;
1446 gtk_widget_queue_draw(d->graph_drawing);
1447 gdk_drag_status(dc, can_move ? GDK_ACTION_COPY : 0, time);
1448 return can_move;
1449}
1450
1462static gboolean _ioporder_drag_drop(GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
1463 guint time, gpointer user_data)
1464{
1465 gtk_drag_get_data(widget, dc, gdk_atom_intern("ioporder-node", TRUE), time);
1466 return TRUE;
1467}
1468
1477static void _ioporder_drag_leave(GtkWidget *widget, GdkDragContext *dc, guint time, gpointer user_data)
1478{
1479 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
1481 d->drag_dest = NULL;
1482 gtk_widget_queue_draw(d->graph_drawing);
1483}
1484
1501static void _ioporder_drag_data_received(GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
1502 GtkSelectionData *selection_data, guint info, guint time,
1503 gpointer user_data)
1504{
1505 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
1507 dt_iop_module_t *module_src = d->drag_source;
1508 dt_iop_module_t *module_dest = (dt_iop_module_t *)g_object_get_data(G_OBJECT(widget), "dt-ioporder-module");
1509
1510 if(module_src && module_dest && module_src != module_dest)
1511 {
1512 if(module_src->iop_order < module_dest->iop_order)
1513 dt_iop_gui_move_module_after(module_src, module_dest, "_ioporder_drag_data_received");
1514 else
1515 dt_iop_gui_move_module_before(module_src, module_dest, "_ioporder_drag_data_received");
1516 }
1517
1518 gtk_drag_finish(dc, TRUE, FALSE, time);
1519 d->drag_source = NULL;
1520 d->drag_dest = NULL;
1521
1523}
1524
1531static void _ioporder_add_preset(GtkButton *button, gpointer user_data)
1532{
1533 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
1535 GtkWindow *parent = GTK_WINDOW(d->window ? d->window : dt_ui_main_window(darktable.gui->ui));
1536 GtkWidget *dialog = gtk_dialog_new_with_buttons(_("save module order preset"), parent,
1537 GTK_DIALOG_DESTROY_WITH_PARENT,
1538 _("_cancel"), GTK_RESPONSE_CANCEL,
1539 _("_save"), GTK_RESPONSE_ACCEPT, NULL);
1540 GtkWidget *content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1541 GtkWidget *entry = gtk_entry_new();
1542
1543 gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
1544 gtk_widget_set_hexpand(entry, TRUE);
1545 gtk_widget_set_tooltip_text(entry, _("preset name"));
1546 gtk_box_pack_start(GTK_BOX(content), entry, FALSE, FALSE, DT_PIXEL_APPLY_DPI(8));
1547 gtk_widget_show_all(dialog);
1548
1549 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1550 g_signal_connect(entry, "key-press-event", G_CALLBACK(dt_handle_dialog_enter), dialog);
1551
1552 if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
1553 {
1554 const char *preset_name = gtk_entry_get_text(GTK_ENTRY(entry));
1555 if(preset_name && preset_name[0] != '\0')
1556 {
1557 int size = 0;
1558 void *params = self->get_params(self, &size);
1559 dt_lib_presets_add(preset_name, self->plugin_name, self->version(), params, size, FALSE);
1560 dt_free(params);
1562 }
1563 }
1564
1565 gtk_widget_destroy(dialog);
1567}
1568
1575static void _ioporder_reset_order(GtkButton *button, gpointer user_data)
1576{
1577 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
1578 self->gui_reset(self);
1580}
1581
1588static void _ioporder_apply_preset(GtkComboBox *combo, gpointer user_data)
1589{
1590 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
1592 if(d->refreshing_toolbar) return;
1593
1594 const gchar *preset_name = gtk_combo_box_get_active_id(combo);
1595 if(!preset_name || !strcmp(preset_name, "__custom__"))
1596 return;
1597
1598 dt_lib_presets_apply(preset_name, self->plugin_name, self->version());
1599 dt_iop_gui_commit_iop_order_change(darktable.develop, NULL, FALSE, TRUE, "_ioporder_apply_preset");
1601}
1602
1612{
1614 if(d->window) return;
1615
1616 GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1617 GtkWidget *root = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
1618 GtkWidget *toolbar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
1619 GtkWidget *label = gtk_label_new("");
1620 GtkWidget *add_preset = gtk_button_new_with_label(_("add preset"));
1621 GtkWidget *preset_combo = gtk_combo_box_text_new();
1622 GtkWidget *reset = gtk_button_new_with_label(_("reset"));
1623 GtkWidget *scroll = gtk_scrolled_window_new(NULL, NULL);
1624 GtkWidget *overlay = gtk_overlay_new();
1625 GtkWidget *drawing = gtk_drawing_area_new();
1626 GtkWidget *fixed = gtk_fixed_new();
1627
1628 gtk_window_set_title(GTK_WINDOW(window), _("module order"));
1629 gtk_window_set_default_size(GTK_WINDOW(window), DT_PIXEL_APPLY_DPI(1120), DT_PIXEL_APPLY_DPI(440));
1630 gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(dt_ui_main_window(darktable.gui->ui)));
1631 gtk_window_set_destroy_with_parent(GTK_WINDOW(window), TRUE);
1632 gtk_container_set_border_width(GTK_CONTAINER(root), DT_PIXEL_APPLY_DPI(8));
1633
1634 gtk_widget_set_halign(label, GTK_ALIGN_START);
1635 gtk_label_set_xalign(GTK_LABEL(label), 0.0f);
1636
1637 gtk_widget_set_hexpand(preset_combo, TRUE);
1638 gtk_widget_set_hexpand(scroll, TRUE);
1639 gtk_widget_set_vexpand(scroll, TRUE);
1640 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1641 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll), GTK_SHADOW_IN);
1642
1643 gtk_widget_set_size_request(overlay, DT_IOPORDER_GRAPH_MIN_WIDTH, DT_IOPORDER_GRAPH_HEIGHT);
1644 gtk_widget_set_size_request(drawing, DT_IOPORDER_GRAPH_MIN_WIDTH, DT_IOPORDER_GRAPH_HEIGHT);
1645 gtk_widget_set_size_request(fixed, DT_IOPORDER_GRAPH_MIN_WIDTH, DT_IOPORDER_GRAPH_HEIGHT);
1646
1647 gtk_box_pack_start(GTK_BOX(toolbar), label, FALSE, FALSE, 0);
1648 gtk_box_pack_start(GTK_BOX(toolbar), add_preset, FALSE, FALSE, 0);
1649 gtk_box_pack_start(GTK_BOX(toolbar), preset_combo, TRUE, TRUE, 0);
1650 gtk_box_pack_start(GTK_BOX(toolbar), reset, FALSE, FALSE, 0);
1651
1652 gtk_overlay_add_overlay(GTK_OVERLAY(overlay), fixed);
1653 gtk_container_add(GTK_CONTAINER(overlay), drawing);
1654 gtk_container_add(GTK_CONTAINER(scroll), overlay);
1655
1656 gtk_box_pack_start(GTK_BOX(root), toolbar, FALSE, FALSE, 0);
1657 gtk_box_pack_start(GTK_BOX(root), scroll, TRUE, TRUE, 0);
1658 gtk_container_add(GTK_CONTAINER(window), root);
1659
1660 g_signal_connect(window, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
1661 g_signal_connect(window, "destroy", G_CALLBACK(_ioporder_popup_destroy), d);
1662 g_signal_connect(add_preset, "clicked", G_CALLBACK(_ioporder_add_preset), self);
1663 g_signal_connect(reset, "clicked", G_CALLBACK(_ioporder_reset_order), self);
1664 g_signal_connect(preset_combo, "changed", G_CALLBACK(_ioporder_apply_preset), self);
1665 g_signal_connect(drawing, "draw", G_CALLBACK(_ioporder_graph_draw), self);
1666
1667 d->window = window;
1668 d->toolbar_label = label;
1669 d->preset_combo = preset_combo;
1670 d->graph_scroll = scroll;
1671 d->graph_overlay = overlay;
1672 d->graph_drawing = drawing;
1673 d->graph_fixed = fixed;
1674
1677}
1678
1688{
1690
1693
1694 gtk_widget_show_all(d->window);
1695 gtk_window_present(GTK_WINDOW(d->window));
1696}
1697
1704static void _ioporder_refresh_callback(gpointer instance, gpointer user_data)
1705{
1706 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
1708 if(d->window && gtk_widget_get_visible(d->window)) _ioporder_rebuild_graph(self);
1709}
1710
1718static void _ioporder_presets_changed_callback(gpointer instance, gpointer module_name, gpointer user_data)
1719{
1720 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
1722 const char *changed_module = (const char *)module_name;
1723 if(changed_module && strcmp(changed_module, self->plugin_name) != 0) return;
1724
1726 if(d->window && gtk_widget_get_visible(d->window)) _ioporder_rebuild_graph(self);
1727}
1728
1729const char *name(struct dt_lib_module_t *self)
1730{
1731 return _("module order");
1732}
1733
1734const char **views(dt_lib_module_t *self)
1735{
1736 static const char *v[] = { "special", NULL };
1737 return v;
1738}
1739
1741{
1743}
1744
1746{
1747 return 0;
1748}
1749
1751{
1752 return 0;
1753}
1754
1775
1777{
1778 if(IS_NULL_PTR(self->data)) return;
1780 if(IS_NULL_PTR(d)) return;
1781
1784
1786 if(d->window) gtk_widget_destroy(d->window);
1787 dt_free(self->data);
1788 self->data = NULL;
1789}
1790
1792{
1795 if(IS_NULL_PTR(iop_order_list)) return;
1796
1797 const int32_t imgid = darktable.develop->image_storage.id;
1798 dt_ioppr_change_iop_order(darktable.develop, imgid, iop_order_list);
1799 dt_iop_gui_commit_iop_order_change(darktable.develop, NULL, FALSE, FALSE, "dt_lib_ioporder_gui_reset");
1800
1801 d->current_mode = DT_IOP_ORDER_ANSEL_RAW;
1802 g_list_free_full(iop_order_list, dt_free_gpointer);
1803}
1804
1806{
1807 size_t size = 0;
1808 char *params = NULL;
1809 GList *list = NULL;
1810
1812 params = dt_ioppr_serialize_iop_order_list(list, &size);
1813 dt_lib_presets_add(_("legacy"), self->plugin_name, self->version(), params, (int32_t)size, TRUE);
1814 dt_free(params);
1815 g_list_free_full(list, dt_free_gpointer);
1816
1818 params = dt_ioppr_serialize_iop_order_list(list, &size);
1819 dt_lib_presets_add(_("v3.0 for RAW input (default)"), self->plugin_name, self->version(), params,
1820 (int32_t)size, TRUE);
1821 dt_free(params);
1822 g_list_free_full(list, dt_free_gpointer);
1823
1825 params = dt_ioppr_serialize_iop_order_list(list, &size);
1826 dt_lib_presets_add(_("v3.0 for JPEG/non-RAW input"), self->plugin_name, self->version(), params,
1827 (int32_t)size, TRUE);
1828 dt_free(params);
1829 g_list_free_full(list, dt_free_gpointer);
1830
1832 params = dt_ioppr_serialize_iop_order_list(list, &size);
1833 dt_lib_presets_add(_("Ansel v0.1 for RAW input (default)"), self->plugin_name, self->version(), params,
1834 (int32_t)size, TRUE);
1835 dt_free(params);
1836 g_list_free_full(list, dt_free_gpointer);
1837
1839 params = dt_ioppr_serialize_iop_order_list(list, &size);
1840 dt_lib_presets_add(_("Ansel v0.1 for JPEG/non-RAW input"), self->plugin_name, self->version(), params,
1841 (int32_t)size, TRUE);
1842 dt_free(params);
1843 g_list_free_full(list, dt_free_gpointer);
1844}
1845
1846int set_params(dt_lib_module_t *self, const void *params, int size)
1847{
1848 if(IS_NULL_PTR(params)) return 1;
1849
1850 GList *iop_order_list = dt_ioppr_deserialize_iop_order_list(params, (size_t)size);
1851 if(IS_NULL_PTR(iop_order_list)) return 1;
1852
1853 const int32_t imgid = darktable.develop->image_storage.id;
1854 dt_ioppr_change_iop_order(darktable.develop, imgid, iop_order_list);
1855 dt_iop_gui_commit_iop_order_change(darktable.develop, NULL, FALSE, FALSE, "dt_lib_ioporder_set_params");
1856 g_list_free_full(iop_order_list, dt_free_gpointer);
1857 return 0;
1858}
1859
1861{
1862 size_t p_size = 0;
1864 *size = (int)p_size;
1865 return params;
1866}
1867
1869{
1870 return TRUE;
1871}
1872
1873// clang-format off
1874// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1875// vim: shiftwidth=2 expandtab tabstop=2 cindent
1876// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1877// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
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_IS_BUTTON(obj)
Definition button.h:42
#define DTGTK_BUTTON(obj)
Definition button.h:39
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)
const char * dt_colorspaces_get_name(dt_colorspaces_color_profile_type_t type, const char *filename)
@ DT_COLORSPACE_NONE
Definition colorspaces.h:82
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
char * name
void reset(dt_view_t *self)
Definition darkroom.c:1266
darktable_t darktable
Definition darktable.c:181
#define DT_MODULE(MODVER)
Definition darktable.h:140
static void dt_free_gpointer(gpointer ptr)
Definition darktable.h:463
#define dt_free(ptr)
Definition darktable.h:456
static gchar * delete_underscore(const char *s)
Definition darktable.h:1083
static const dt_aligned_pixel_simd_t value
Definition darktable.h:577
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:281
sqlite3 * dt_database_get(const dt_database_t *db)
Definition database.c:3646
#define DT_DEBUG_SQLITE3_PREPARE_V2(a, b, c, d, e)
Definition debug.h:107
#define DT_DEBUG_SQLITE3_BIND_INT(a, b, c)
Definition debug.h:115
gchar * dt_dev_get_multi_name(const struct dt_iop_module_t *module)
Definition develop.c:1471
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_iop_buffer_type_t
Definition format.h:44
@ TYPE_FLOAT
Definition format.h:46
@ TYPE_UNKNOWN
Definition format.h:45
@ TYPE_UINT8
Definition format.h:48
@ TYPE_UINT16
Definition format.h:47
void dt_gui_menu_popup(GtkMenu *menu, GtkWidget *button, GdkGravity widget_anchor, GdkGravity menu_anchor)
Definition gtk.c:2953
void dt_capitalize_label(gchar *text)
Definition gtk.c:3150
void dt_gui_add_class(GtkWidget *widget, const gchar *class_name)
Definition gtk.c:133
GtkWidget * dt_ui_main_window(dt_ui_t *ui)
get the main window widget
#define DT_GUI_BOX_SPACING
Definition gtk.h:109
#define DT_PIXEL_APPLY_DPI(value)
Definition gtk.h:90
void dt_gui_presets_popup_menu_show_for_module(dt_iop_module_t *module)
static gboolean enable(dt_image_t *image)
gboolean dt_iop_is_hidden(dt_iop_module_t *module)
Definition imageop.c:1127
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:754
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:761
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:740
static gboolean dt_iop_colorspace_is_rgb(const dt_iop_colorspace_type_t cst)
Definition imageop.h:213
@ IOP_CS_RGB_DISPLAY
Definition imageop.h:210
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:2491
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:1044
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:2015
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:2184
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:905
char * dt_ioppr_serialize_text_iop_order_list(GList *iop_order_list)
Serialize an order list to a text representation.
Definition iop_order.c:2533
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:80
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:2632
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:1386
dt_iop_order_t
Definition iop_order.h:143
@ DT_IOP_ORDER_ANSEL_RAW
Definition iop_order.h:148
@ DT_IOP_ORDER_LEGACY
Definition iop_order.h:145
@ DT_IOP_ORDER_V30_JPG
Definition iop_order.h:147
@ DT_IOP_ORDER_V30
Definition iop_order.h:146
@ DT_IOP_ORDER_CUSTOM
Definition iop_order.h:144
@ DT_IOP_ORDER_ANSEL_JPG
Definition iop_order.h:149
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)
static const float x
const float v
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:1462
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:280
#define DT_IOPORDER_GRAPH_HEIGHT
Definition ioporder.c:58
static void _ioporder_free_graph_node(gpointer data)
Release one graph node descriptor.
Definition ioporder.c:666
void gui_reset(dt_lib_module_t *self)
Definition ioporder.c:1791
#define DT_IOPORDER_GRAPH_LEFT_MARGIN
Definition ioporder.c:55
int set_params(dt_lib_module_t *self, const void *params, int size)
Definition ioporder.c:1846
static gboolean _ioporder_module_in_history(const dt_iop_module_t *module)
Return TRUE when a module already exists in history.
Definition ioporder.c:141
void * get_params(dt_lib_module_t *self, int *size)
Definition ioporder.c:1860
#define DT_IOPORDER_MASK_ARROW_OFFSET
Definition ioporder.c:60
static gchar * _ioporder_get_current_order_name(dt_lib_module_t *self)
Retrieve the display name of the current pipeline order.
Definition ioporder.c:488
static void _ioporder_add_preset(GtkButton *button, gpointer user_data)
Save the current pipeline order as a named preset.
Definition ioporder.c:1531
static void _ioporder_init_popup(dt_lib_module_t *self)
Build the popup window lazily on first use.
Definition ioporder.c:1611
#define DT_IOPORDER_BAND_HEIGHT
Definition ioporder.c:59
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:311
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:413
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:222
#define DT_IOPORDER_MASK_BOTTOM_MARGIN
Definition ioporder.c:61
static void _ioporder_reset_order(GtkButton *button, gpointer user_data)
Reset the current order to the default v3.0 order.
Definition ioporder.c:1575
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:684
static void _ioporder_refresh_callback(gpointer instance, gpointer user_data)
Central refresh callback for develop-side state changes.
Definition ioporder.c:1704
dt_ioporder_runtime_band_kind_t
Definition ioporder.c:69
@ DT_IOPORDER_RUNTIME_BAND_UNAVAILABLE
Definition ioporder.c:71
@ DT_IOPORDER_RUNTIME_BAND_LAB
Definition ioporder.c:76
@ DT_IOPORDER_RUNTIME_BAND_NONE
Definition ioporder.c:70
@ DT_IOPORDER_RUNTIME_BAND_PIPELINE_RGB
Definition ioporder.c:74
@ DT_IOPORDER_RUNTIME_BAND_RAW
Definition ioporder.c:72
@ DT_IOPORDER_RUNTIME_BAND_SENSOR_RGB
Definition ioporder.c:73
@ DT_IOPORDER_RUNTIME_BAND_DISPLAY_RGB
Definition ioporder.c:75
@ DT_IOPORDER_RUNTIME_BAND_OTHER
Definition ioporder.c:77
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:1061
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:1083
void show_popup(dt_lib_module_t *self)
Open the popup window owned by the ioporder lib.
Definition ioporder.c:1687
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:1102
static void _ioporder_refresh_toolbar(dt_lib_module_t *self)
Refresh the preset combo and status label in the popup toolbar.
Definition ioporder.c:561
void gui_cleanup(dt_lib_module_t *self)
Definition ioporder.c:1776
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:707
static void _ioporder_apply_preset(GtkComboBox *combo, gpointer user_data)
Apply the preset selected in the popup toolbar combo box.
Definition ioporder.c:1588
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:1428
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:180
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:1477
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:372
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:758
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:200
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:436
static void _ioporder_clear_graph(dt_lib_ioporder_t *d)
Destroy every node widget and free the node descriptors.
Definition ioporder.c:611
void init_presets(dt_lib_module_t *self)
Definition ioporder.c:1805
#define DT_IOPORDER_GRAPH_NODE_STEP
Definition ioporder.c:54
static void _ioporder_rebuild_graph(dt_lib_module_t *self)
Refresh the full graph contents from the current darkroom pipeline.
Definition ioporder.c:932
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:253
uint32_t container(dt_lib_module_t *self)
Definition ioporder.c:1740
gboolean preset_autoapply(dt_lib_module_t *self)
Definition ioporder.c:1868
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:1129
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:467
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:882
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:723
static void _ioporder_node_show_presets(GtkButton *button, gpointer user_data)
Open the standard module preset popup from a graph node.
Definition ioporder.c:739
static const guint _ioporder_target_count
Definition ioporder.c:83
void gui_init(dt_lib_module_t *self)
Definition ioporder.c:1755
#define DT_IOPORDER_GRAPH_MIN_WIDTH
Definition ioporder.c:57
int position()
Definition ioporder.c:1750
const char ** views(dt_lib_module_t *self)
Definition ioporder.c:1734
static void _ioporder_drag_begin(GtkWidget *widget, GdkDragContext *context, gpointer user_data)
Track the module being dragged from the graph.
Definition ioporder.c:1392
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:1718
int expandable(dt_lib_module_t *self)
Definition ioporder.c:1745
#define DT_IOPORDER_GRAPH_NODE_WIDTH
Definition ioporder.c:53
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:164
#define DT_IOPORDER_GRAPH_TOP_MARGIN
Definition ioporder.c:56
dt_ioporder_dnd_target_t
Definition ioporder.c:64
@ DT_IOPORDER_DND_TARGET_NODE
Definition ioporder.c:65
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:1179
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:341
static void _ioporder_popup_destroy(GtkWidget *widget, gpointer user_data)
Drop cached popup widget pointers once GTK destroys the popup.
Definition ioporder.c:639
static void _ioporder_drag_end(GtkWidget *widget, GdkDragContext *context, gpointer user_data)
Reset drag feedback when the source drag ends.
Definition ioporder.c:1408
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:1501
static const GtkTargetEntry _ioporder_target_list[]
Definition ioporder.c:80
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:1376
gboolean dt_handle_dialog_enter(GtkWidget *widget, GdkEventKey *event, gpointer data)
Definition lib.c:1551
void dt_lib_presets_add(const char *name, const char *plugin_name, const int32_t version, const void *params, const int32_t params_size, gboolean readonly)
Definition lib.c:1353
gboolean dt_lib_presets_apply(const gchar *preset, const gchar *module_name, int module_version)
Definition lib.c:415
#define M_PI
Definition math.h:45
size_t size
Definition mipmap_cache.c:3
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
Definition signal.h:368
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
Definition signal.h:347
@ DT_SIGNAL_DEVELOP_INITIALIZE
This signal is raised when darktable.develop is initialized.
Definition signal.h:169
@ DT_SIGNAL_DEVELOP_HISTORY_CHANGE
This signal is raised when develop history is changed no param, no returned value.
Definition signal.h:204
@ DT_SIGNAL_DEVELOP_IMAGE_CHANGED
This signal is raised when image is changed in darkroom.
Definition signal.h:221
@ 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:174
@ DT_SIGNAL_DEVELOP_MODULE_MOVED
This signal is raised when order of modules in pipeline is changed.
Definition signal.h:218
@ DT_SIGNAL_PRESETS_CHANGED
Definition signal.h:162
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
Definition signal.h:357
struct _GtkWidget GtkWidget
Definition splash.h:29
const float r
struct dt_gui_gtk_t * gui
Definition darktable.h:775
const struct dt_database_t * db
Definition darktable.h:779
struct dt_control_signal_t * signals
Definition darktable.h:774
struct dt_develop_t * develop
Definition darktable.h:770
dt_iop_buffer_dsc_t dsc_out
dt_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
GList * iop_order_list
Definition develop.h:285
dt_image_t image_storage
Definition develop.h:259
GList * iop
Definition develop.h:279
struct dt_dev_pixelpipe_t * preview_pipe
Definition develop.h:247
GList * history
Definition develop.h:275
GtkMenu * presets_popup_menu
Definition gtk.h:169
dt_ui_t * ui
Definition gtk.h:164
dt_iop_buffer_dsc_t dsc
Definition image.h:337
int32_t id
Definition image.h:319
uint32_t filters
Definition format.h:60
unsigned int channels
Definition format.h:54
dt_iop_buffer_type_t datatype
Definition format.h:56
dt_aligned_pixel_t processed_maximum
Definition format.h:85
int32_t hide_enable_button
Definition imageop.h:262
GModule *dt_dev_operation_t op
Definition imageop.h:256
gboolean enabled
Definition imageop.h:298
dt_colorspaces_color_profile_type_t type
Definition iop_profile.h:53
char filename[DT_IOP_COLOR_ICC_LEN]
Definition iop_profile.h:54
dt_iop_module_t *dt_dev_pixelpipe_iop_t * piece
Definition ioporder.c:89
GtkWidget * event_box
Definition ioporder.c:92
GtkWidget * toolbar_label
Definition ioporder.c:105
dt_iop_module_t * drag_dest
Definition ioporder.c:113
GtkWidget * graph_drawing
Definition ioporder.c:109
GtkWidget * graph_overlay
Definition ioporder.c:108
GtkWidget * graph_fixed
Definition ioporder.c:110
GtkWidget * graph_scroll
Definition ioporder.c:107
dt_iop_module_t * drag_source
Definition ioporder.c:112
GtkWidget * preset_combo
Definition ioporder.c:106
GtkWidget * window
Definition ioporder.c:104
gboolean refreshing_toolbar
Definition ioporder.c:103
char plugin_name[128]
Definition lib.h:82
GModule *void * data
Definition lib.h:80
#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)
@ DT_UI_CONTAINER_PANEL_LEFT_CENTER