Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
masks_gui.c
Go to the documentation of this file.
1/*
2 This file is part of Ansel
3 Copyright (C) 2022-2023, 2025-2026 Aurélien PIERRE.
4 Copyright (C) 2025-2026 Guillaume Stutin.
5
6 Ansel is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 Ansel is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with darktable. If not, see <http://www.gnu.org/licenses/>.
18*/
19#include "common/darktable.h"
20#include "develop/masks.h"
21#include "bauhaus/bauhaus.h"
22#include "common/debug.h"
23#include "control/conf.h"
24#include "control/signal.h"
25#include "develop/imageop_gui.h"
26#include "dtgtk/button.h"
27#include "dtgtk/paint.h"
28#include "gui/actions/menu.h"
29#include "gui/draw.h"
30#include "gui/gtk.h"
31
32#include <math.h>
33#include <stdlib.h>
34
35#define DT_MASKS_SHAPE_BUTTON_COUNT 5
36
46
54
67
69{
70 if(IS_NULL_PTR(data)) return;
71
72 // Walk all buttons in this group so any caller can reset every masks shape toolbar through the shared signal.
73 for(int i = 0; i < DT_MASKS_SHAPE_BUTTON_COUNT; i++)
74 {
75 GtkWidget *button = data->buttons[i];
76 if(GTK_IS_TOGGLE_BUTTON(button) && button != active_button)
77 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE);
78 }
79}
80
81static void _masks_shape_buttons_deactivate_signal(gpointer instance, GtkWidget *active_button,
83{
84 _masks_shape_buttons_deactivate(active_button, data);
85}
86
91
93{
94 if(IS_NULL_PTR(data)) return -1;
95
96 // Search the stored button pointers because callers may keep their own storage arrays.
97 for(int i = 0; i < DT_MASKS_SHAPE_BUTTON_COUNT; i++)
98 if(data->buttons[i] == button) return i;
99
100 return -1;
101}
102
104 const int button_index)
105{
108
109 return !IS_NULL_PTR(mask_gui) && mask_gui->creation
110 && mask_gui->creation_module == data->config.creation_module
111 && !IS_NULL_PTR(visible_form)
112 && (visible_form->type & data->types[button_index]);
113}
114
115static gboolean _masks_shape_button_pressed(GtkWidget *button, GdkEventButton *event, gpointer user_data)
116{
117 if(dt_gui_widgets_suppressed() || event->button != GDK_BUTTON_PRIMARY) return TRUE;
118
120 (dt_masks_shape_buttons_data_t *)g_object_get_data(G_OBJECT(button), "dt-masks-shape-buttons-data");
121 const int button_index = _masks_shape_button_index(data, button);
122 if(button_index < 0) return FALSE;
123
124 dt_masks_type_t type = data->types[button_index];
125 dt_iop_module_t *module = data->config.creation_module;
127
128 if(_masks_shape_button_is_current_creation(data, button_index))
129 {
131 dt_masks_form_exit_creation(module, mask_gui);
132 if(data->config.exited) data->config.exited(button, module, type, data->config.user_data);
134 return TRUE;
135 }
136
137 if(data->config.can_start && !data->config.can_start(button, module, type, data->config.user_data))
138 {
141 return TRUE;
142 }
143
144 if(data->config.form_type) type = data->config.form_type(module, type, data->config.user_data);
145
147 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
148
150 {
151 if(data->config.started)
152 {
153 data->config.started(button, module, type, data->config.user_data);
154 // Force focus back to the drawing area after creation mode enabling
155 gtk_widget_grab_focus(dt_ui_center(darktable.gui->ui));
156 }
157 }
158 else
159 {
161 }
162
164 return TRUE;
165}
166
172
182{
183 if(IS_NULL_PTR(config)) return NULL;
184
186 if(IS_NULL_PTR(data)) return NULL;
187
188 data->config = *config;
189 data->box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
190 gtk_widget_set_halign(data->box, GTK_ALIGN_END);
191 gtk_widget_set_valign(data->box, GTK_ALIGN_CENTER);
192
193 const char *action_section = config->action_section ? config->action_section : N_("shapes");
194 const size_t button_defs_count = sizeof(_masks_shape_button_defs) / sizeof(_masks_shape_button_defs[0]);
195
196 // Create buttons in the same visible order used by the module-local toolbars.
197 for(size_t i = 0; i < button_defs_count; i++)
198 {
200 if(!(config->flags & def->flag)) continue;
201
202 GtkWidget *button = NULL;
203 if(config->owner_module)
204 {
205 const gboolean register_button = (config->register_flags & def->flag);
206 if(register_button)
207 {
208 button = dt_iop_togglebutton_new(config->owner_module, action_section, def->label, def->ctrl_label,
209 G_CALLBACK(_masks_shape_button_pressed), config->local,
210 0, 0, def->paint, data->box);
211 }
212 else
213 {
214 button = dt_iop_togglebutton_new_no_register(config->owner_module, action_section, def->label, def->ctrl_label,
215 G_CALLBACK(_masks_shape_button_pressed), config->local,
216 0, 0, def->paint, data->box);
217 }
218 }
219 else
220 {
221 button = dtgtk_togglebutton_new(def->paint, 0, NULL);
222 gtk_widget_set_tooltip_text(button, _(def->label));
223 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE);
224 gtk_box_pack_end(GTK_BOX(data->box), button, FALSE, FALSE, 0);
225 g_signal_connect(G_OBJECT(button), "button-press-event", G_CALLBACK(_masks_shape_button_pressed), NULL);
226 }
227
228 gtk_widget_set_can_focus(button, FALSE);
229 g_object_set_data(G_OBJECT(button), "dt-masks-shape-buttons-data", data);
230
231 data->buttons[def->index] = button;
232 data->types[def->index] = def->type;
233 if(config->buttons) config->buttons[def->index] = button;
234 if(config->types) config->types[def->index] = def->type;
235 }
236
239 g_signal_connect(G_OBJECT(data->box), "destroy", G_CALLBACK(_masks_shape_buttons_destroy), data);
240
241 return data->box;
242}
243
254
255// Push the new value to history (so the pipeline re-renders) and refresh the mask
256// treeviews (opacity text, etc.).
257//
258// This is called from the slider "value-changed" handler. The bauhaus slider already
259// throttles that emission through dt_gui_throttle_queue() while dragging, so the commit
260// is debounced at the slider-value level: transient values do not flood the pipeline with
261// renders, yet the image updates without waiting for the context menu to be closed.
271
273{
274 if(IS_NULL_PTR(data) || IS_NULL_PTR(data->form_group)) return;
275
276 if(data->increment == DT_MASKS_INCREMENT_ABSOLUTE) // aka opacity
277 {
279 data->increment, 1, data->gui, data->module);
280 data->last_value = value;
282 return;
283 }
284
285 const float delta = value - data->last_value;
286 if(fabsf(delta) < 1e-6f) return;
287
288 // Slider value is a log2 scale factor in [-3;3], so apply the delta in log space.
289 const float scale = exp2f(delta);
291 DT_MASKS_INCREMENT_SCALE, 1.f, data->gui, data->module);
292 data->last_value = value;
294}
295
296static void _masks_gui_menu_item_block_activate(GtkWidget *widget, gpointer user_data)
297{
298 g_signal_stop_emission_by_name(widget, "activate");
299}
300
301static gboolean _masks_gui_menu_item_forward_event(GtkWidget *widget, GdkEvent *event, gpointer user_data)
302{
304 if(IS_NULL_PTR(data) || !data->slider) return FALSE;
305
306 GdkEvent *copy = gdk_event_copy(event);
307 if(IS_NULL_PTR(copy)) return FALSE;
308
309 double x = 0.0, y = 0.0;
310 gboolean has_coords = FALSE;
311 switch(copy->type)
312 {
313 case GDK_BUTTON_PRESS:
314 case GDK_2BUTTON_PRESS:
315 case GDK_3BUTTON_PRESS:
316 case GDK_BUTTON_RELEASE:
317 x = copy->button.x;
318 y = copy->button.y;
319 has_coords = TRUE;
320 break;
321 case GDK_MOTION_NOTIFY:
322 x = copy->motion.x;
323 y = copy->motion.y;
324 has_coords = TRUE;
325 break;
326 case GDK_SCROLL:
327 x = copy->scroll.x;
328 y = copy->scroll.y;
329 has_coords = TRUE;
330 break;
331 default:
332 break;
333 }
334
335 if(has_coords)
336 {
337 int sx = 0, sy = 0;
338 if(gtk_widget_translate_coordinates(widget, data->slider, (int)x, (int)y, &sx, &sy))
339 {
340 switch(copy->type)
341 {
342 case GDK_BUTTON_PRESS:
343 case GDK_2BUTTON_PRESS:
344 case GDK_3BUTTON_PRESS:
345 case GDK_BUTTON_RELEASE:
346 copy->button.x = sx;
347 copy->button.y = sy;
348 break;
349 case GDK_MOTION_NOTIFY:
350 copy->motion.x = sx;
351 copy->motion.y = sy;
352 break;
353 case GDK_SCROLL:
354 copy->scroll.x = sx;
355 copy->scroll.y = sy;
356 break;
357 default:
358 break;
359 }
360 }
361 }
362
363 GdkWindow *slider_window = gtk_widget_get_window(data->slider);
364 if(slider_window)
365 {
366 if(copy->any.window) g_object_unref(copy->any.window);
367 copy->any.window = g_object_ref(slider_window);
368 copy->any.send_event = TRUE;
369 }
370
371 gtk_widget_event(data->slider, copy);
372 gdk_event_free(copy);
373 return TRUE;
374}
375
376static void _masks_gui_interaction_slider_changed(GtkWidget *widget, gpointer user_data)
377{
379 if(IS_NULL_PTR(data) || IS_NULL_PTR(data->form_group)) return;
380
382}
383
385 dt_masks_interaction_t interaction, dt_masks_increment_t increment,
386 float min, float max, float step, float value, int digits,
387 const char *format, float factor,
389{
390 GtkWidget *menu_item = gtk_menu_item_new();
391 GtkWidget *box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
392
393 gtk_widget_set_can_focus(menu_item, FALSE);
394 g_signal_connect(G_OBJECT(menu_item), "activate",
395 G_CALLBACK(_masks_gui_menu_item_block_activate), NULL);
396 gtk_widget_add_events(menu_item, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
397 | GDK_POINTER_MOTION_MASK | GDK_SCROLL_MASK);
398
400 min, max, step, value, digits);
401 dt_bauhaus_widget_set_label(slider, label);
402 dt_bauhaus_slider_set_digits(slider, digits);
403 if(format && format[0] != '\0') dt_bauhaus_slider_set_format(slider, format);
404 if(factor != 1.0f) dt_bauhaus_slider_set_factor(slider, factor);
406 DT_BAUHAUS_WIDGET(slider)->expand = TRUE;
407 gtk_widget_set_hexpand(slider, TRUE);
408 gtk_widget_set_halign(slider, GTK_ALIGN_FILL);
409 gtk_widget_set_valign(slider, GTK_ALIGN_CENTER);
410 gtk_widget_set_size_request(slider, DT_PIXEL_APPLY_DPI(220), DT_PIXEL_APPLY_DPI(28));
411 gtk_widget_set_can_focus(slider, TRUE);
412
414 data->form_group = form_group;
415 data->gui = gui;
416 data->module = module;
417 data->interaction = interaction;
418 data->increment = increment;
419 data->last_value = value;
420 data->slider = slider;
421 g_signal_connect_data(G_OBJECT(slider), "value-changed",
423 data, (GClosureNotify)g_free, 0);
424 g_signal_connect(G_OBJECT(menu_item), "button-press-event",
425 G_CALLBACK(_masks_gui_menu_item_forward_event), data);
426 g_signal_connect(G_OBJECT(menu_item), "button-release-event",
427 G_CALLBACK(_masks_gui_menu_item_forward_event), data);
428 g_signal_connect(G_OBJECT(menu_item), "motion-notify-event",
429 G_CALLBACK(_masks_gui_menu_item_forward_event), data);
430 g_signal_connect(G_OBJECT(menu_item), "scroll-event",
431 G_CALLBACK(_masks_gui_menu_item_forward_event), data);
432
433 gtk_box_pack_start(GTK_BOX(box), slider, TRUE, TRUE, 0);
434 gtk_container_add(GTK_CONTAINER(menu_item), box);
435 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
436
437 return menu_item;
438}
439
441{
442 if(IS_NULL_PTR(darktable.gui) || IS_NULL_PTR(darktable.gui->ui)) return GTK_RESPONSE_NO;
443
444 GtkWidget *dialog = gtk_message_dialog_new(
445 GTK_WINDOW(dt_ui_main_window(darktable.gui->ui)),
446 GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
447 GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, _("Delete the shape '%s' ?"), form_name);
448 gtk_message_dialog_format_secondary_text(
449 GTK_MESSAGE_DIALOG(dialog), "'%s' %s\n\n%s", form_name,
450 _("will no longer be used."),
451 _("Do you want to permanently delete it, or keep it unused for potential reuse?"));
452
453 gtk_dialog_add_button(GTK_DIALOG(dialog), _("Delete shape"), GTK_RESPONSE_YES);
454 gtk_dialog_add_button(GTK_DIALOG(dialog), _("Keep unused shape"), GTK_RESPONSE_NO);
455 gtk_dialog_add_button(GTK_DIALOG(dialog), _("Cancel"), GTK_RESPONSE_CANCEL);
456 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
457
458 const int response = gtk_dialog_run(GTK_DIALOG(dialog));
459 gtk_widget_destroy(dialog);
460
461 return response;
462}
463
464gboolean dt_masks_gui_confirm_permanent_delete(const char *form_name)
465{
466 if(!dt_conf_get_bool("ask_before_delete_mask_shape")) return TRUE;
468
469 GtkWidget *dialog = gtk_message_dialog_new(
470 GTK_WINDOW(dt_ui_main_window(darktable.gui->ui)),
471 GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
472 GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
473 _("Permanently delete the shape '%s' ?"), form_name);
474 gtk_message_dialog_format_secondary_text(
475 GTK_MESSAGE_DIALOG(dialog), "%s",
476 _("It will be removed from every mask using it and from the list of available shapes."));
477
478 GtkWidget *message_area = gtk_message_dialog_get_message_area(GTK_MESSAGE_DIALOG(dialog));
479 GtkWidget *ask_check = gtk_check_button_new_with_label(_("Always ask"));
480 gtk_widget_set_tooltip_text(ask_check,
481 _("when unchecked, mask shapes will be deleted silently from now on without this confirmation.\n"
482 "you can turn it back on from preferences."));
483 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ask_check), TRUE);
484 gtk_box_pack_start(GTK_BOX(message_area), ask_check, FALSE, FALSE, 6);
485 gtk_widget_show(ask_check);
486
487 const gint response = gtk_dialog_run(GTK_DIALOG(dialog));
488 dt_conf_set_bool("ask_before_delete_mask_shape", gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ask_check)));
489 gtk_widget_destroy(dialog);
490
491 return response == GTK_RESPONSE_YES;
492}
493
494// Resolves the group entry selected in gui to (module, sel, parentid, formid). Returns FALSE
495// if nothing usable is selected, in which case *module/*sel are left untouched.
497 dt_masks_form_t **sel, int *parentid, int *formid)
498{
499 if(IS_NULL_PTR(gui) || gui->group_selected < 0) return FALSE;
501 if(IS_NULL_PTR(forms)) return FALSE;
502
504 if(IS_NULL_PTR(fpt)) return FALSE;
506 if(IS_NULL_PTR(mod)) return FALSE;
508 if(IS_NULL_PTR(form)) return FALSE;
509
510 *module = mod;
511 *sel = form;
512 *parentid = fpt->parentid;
513 *formid = fpt->formid;
514 return TRUE;
515}
516
517// "Remove shape from mask": detach from the current group only, keep the form for reuse.
518static void _masks_gui_remove_from_group_callback(GtkWidget *menu, gpointer user_data)
519{
520 dt_masks_form_gui_t *gui = (dt_masks_form_gui_t *)user_data;
521 dt_iop_module_t *module = NULL;
522 dt_masks_form_t *sel = NULL;
523 int parentid = 0, formid = 0;
524 if(!_masks_gui_resolve_selected_shape(gui, &module, &sel, &parentid, &formid)) return;
525
526 dt_masks_remove_shape_from_group(module, sel, parentid, gui, formid);
527}
528
529// "Delete shape": permanently delete, gated by dt_masks_gui_confirm_permanent_delete().
530static void _masks_gui_full_delete_callback(GtkWidget *menu, gpointer user_data)
531{
532 dt_masks_form_gui_t *gui = (dt_masks_form_gui_t *)user_data;
533 dt_iop_module_t *module = NULL;
534 dt_masks_form_t *sel = NULL;
535 int parentid = 0, formid = 0;
536 if(!_masks_gui_resolve_selected_shape(gui, &module, &sel, &parentid, &formid)) return;
537
538 dt_masks_delete_shape(module, sel, parentid, gui, formid);
539}
540
541void _masks_gui_delete_node_callback(GtkWidget *menu, gpointer user_data)
542{
543 dt_masks_form_gui_t *gui = (dt_masks_form_gui_t *)user_data;
544 if(IS_NULL_PTR(gui)) return;
546 if(IS_NULL_PTR(forms)) return;
547
548 dt_iop_module_t *module = darktable.develop->gui_module;
549 if(IS_NULL_PTR(module)) return;
550
551 if(gui->creation)
552 {
553 // Minimum points to create a polygon
554 if(gui->node_dragging < 1)
555 {
556 dt_masks_form_exit_creation(module, gui);
557 return;
558 }
560 if(sel)
561 dt_masks_remove_node(module, sel, 0, gui, 0, gui->node_dragging);
562 gui->node_dragging -= 1;
563 }
564 else if(gui->group_selected >= 0)
565 {
566 // Delete shape from current group
567
569 if(IS_NULL_PTR(fpt)) return;
571 if(sel)
572 dt_masks_remove_node(module, sel, fpt->parentid, gui, gui->group_selected, gui->node_hovered);
573
575 }
576}
577
578static void _masks_gui_exit_creation_callback(GtkWidget *menu, gpointer user_data)
579{
580 dt_masks_form_gui_t *gui = (dt_masks_form_gui_t *)user_data;
581 dt_iop_module_t *module = darktable.develop->gui_module;
582 dt_masks_form_exit_creation(module, gui);
583}
584
585static void _masks_move_up_down_callback(gpointer user_data, const int up)
586{
587 dt_masks_form_gui_t *gui = (dt_masks_form_gui_t *)user_data;
588 if(IS_NULL_PTR(gui)) return;
589 if(gui->group_selected < 0) return;
590
591 dt_iop_module_t *module = darktable.develop->gui_module;
592 if(IS_NULL_PTR(module)) return;
593
595 if(IS_NULL_PTR(forms)) return;
597 if(IS_NULL_PTR(fpt)) return;
599 if(IS_NULL_PTR(grp) || !(grp->type & DT_MASKS_GROUP)) return;
601
602 dt_masks_form_move(grp, fpt->formid, up);
603
605}
606
607static void _masks_moveup_callback(GtkWidget *menu, gpointer user_data)
608{
609 _masks_move_up_down_callback(user_data, 0);
610}
611
612static void _masks_movedown_callback(GtkWidget *menu, gpointer user_data)
613{
614 _masks_move_up_down_callback(user_data, 1);
615}
616
619static void _masks_operation_callback(GtkWidget *menu, gpointer user_data)
620{
621 dt_masks_form_gui_t *gui = (dt_masks_form_gui_t *)user_data;
622 if(IS_NULL_PTR(gui) || IS_NULL_PTR(menu)) return;
623
624 const guint form_pos = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(menu), "form_pos"));
625 const dt_masks_state_t state_op = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu), "state_op"));
626 // Advert the user if it will have no effect
627 if(form_pos == 0 && (state_op & DT_MASKS_STATE_IS_COMBINE_OP) != 0)
628 {
629 dt_control_log(_("Applying a boolean operation has no effect on the first shape of a group.\n"
630 "Move it to at least the 2nd position if you need to use boolean operations"));
631 }
632
633 dt_masks_form_group_t *form_op = (dt_masks_form_group_t *)g_object_get_data(G_OBJECT(menu), "op_form");
634 if(IS_NULL_PTR(form_op)) return;
635
636 apply_operation(form_op, state_op);
637
639}
640
641#define masks_gtk_menu_item_new_bold(label, selected, state, icon) \
642{ \
643 gchar *op_label = g_strdup(label); \
644 menu_item = ctx_gtk_check_menu_item_new_with_markup_and_pixbuf(op_label, icon, \
645 sub_menu, \
646 _masks_operation_callback, gui, \
647 (selected != 0), \
648 ((state) == DT_MASKS_STATE_INVERSE)); \
649 dt_free(op_label); \
650 op_label = NULL; \
651 g_object_set_data(G_OBJECT(menu_item), "state_op", GINT_TO_POINTER(state)); \
652 g_object_set_data(G_OBJECT(menu_item), "op_form", op_form); \
653 g_object_set_data(G_OBJECT(menu_item), "form_pos", GINT_TO_POINTER(form_pos)); \
654}
655
656
658 const float pzx, const float pzy)
659{
660 assert(gui);
661 assert(form);
662 // Always re-create the menu when we show it because we don't bother updating info during the lifetime of the mask
663 GtkWidget *menu = gtk_menu_new();
664 gtk_style_context_add_class(gtk_widget_get_style_context(menu), "dt-masks-context-menu");
665
666 // Create an array of icons for the operations
667 const int bs2 = DT_PIXEL_APPLY_DPI(13);
668 GdkPixbuf *op_icon[DT_MASKS_STATE_EXCLUSION + 1] = { 0 };
669 int width = bs2 * 2;
675
676 // Get the current group to apply operations on it if needed
677 dt_masks_form_group_t *op_form = NULL;
678 dt_masks_form_t *grp = formgroup ? dt_masks_get_from_id(darktable.develop, formgroup->parentid) : NULL;
679 if(grp && (grp->type & DT_MASKS_GROUP))
680 op_form = dt_masks_form_group_from_parentid(grp->formid, form->formid);
681 if(IS_NULL_PTR(op_form) && !gui->creation)
682 {
683 for(size_t k = 0; k < G_N_ELEMENTS(op_icon); k++)
684 g_clear_object(&op_icon[k]);
685 gtk_widget_destroy(menu);
686 return NULL;
687 }
688
689 // Find the position of the current form in the group
690 guint form_pos = 0;
691 gboolean form_found = FALSE;
692 if(grp && (grp->type & DT_MASKS_GROUP))
693 {
694 for(GList *fpts = grp->points; fpts; fpts = g_list_next(fpts))
695 {
696 dt_masks_form_group_t *fpt = (dt_masks_form_group_t *)fpts->data;
697 if(fpt->formid == form->formid)
698 {
699 form_found = TRUE;
700 break;
701 }
702 form_pos++;
703 }
704 }
705
706 // Get the number of shapes in the group
707 guint list_length = (form_found && grp) ? g_list_length(grp->points) : 0;
708
709
710 // Title
711 gchar *form_name = NULL;
712 if(form->name[0])
713 form_name = g_strdup(form->name);
714 else if(gui->creation)
715 {
716 // if no name, we are probably creating a new form, we create one based on the type
717 form_name = g_strdup(_("New "));
718 switch (form->type)
719 {
720 case DT_MASKS_CIRCLE:
721 form_name = g_strconcat(form_name, _("circle"), NULL);
722 break;
723 case DT_MASKS_ELLIPSE:
724 form_name = g_strconcat(form_name, _("ellipse"), NULL);
725 break;
726 case DT_MASKS_POLYGON:
727 form_name = g_strconcat(form_name, _("polygon"), NULL);
728 break;
729 case DT_MASKS_BRUSH:
730 form_name = g_strconcat(form_name, _("brush"), NULL);
731 break;
733 form_name = g_strconcat(form_name, _("gradient"), NULL);
734 break;
735 case DT_MASKS_GROUP:
736 form_name = g_strconcat(form_name, _("mask"), NULL);
737 break;
738 default:
739 dt_free(form_name); // Erase the "New " prefix
740 form_name = g_strdup(_("Unknown shape"));
741 break;
742 }
743 }
744
745 // Create the main label string
746 gchar *item_str = NULL;
747 if(gui->node_hovered >= 0 || gui->seg_hovered >= 0)
748 {
749 const int item_index = (gui->node_hovered >= 0) ? gui->node_hovered : gui->seg_hovered;
750 item_str = g_strdup_printf("%s %d - ", gui->node_hovered >= 0 ? _("Node") : _("Segment"), item_index);
751 }
752 else
753 item_str = g_strdup("");
754
755 // Create an assembled image if we have an inverse state to show
756 const dt_masks_state_t state = IS_NULL_PTR(op_form) ? 0 : op_form->state & DT_MASKS_STATE_IS_COMBINE_OP;
757 const gboolean has_inverse = !IS_NULL_PTR(op_form) && (op_form->state & DT_MASKS_STATE_INVERSE) != 0;
758 GdkPixbuf *icon = (state <= DT_MASKS_STATE_EXCLUSION) ? op_icon[state] : NULL;
759 GdkPixbuf *composed_icon = NULL;
760 if(has_inverse && op_icon[DT_MASKS_STATE_INVERSE])
761 {
762 if(icon)
763 {
764 const int base_w = gdk_pixbuf_get_width(icon);
765 const int base_h = gdk_pixbuf_get_height(icon);
766 const int inv_w = gdk_pixbuf_get_width(op_icon[DT_MASKS_STATE_INVERSE]);
767 const int inv_h = gdk_pixbuf_get_height(op_icon[DT_MASKS_STATE_INVERSE]);
768 const int out_w = base_w + inv_w;
769 const int out_h = MAX(base_h, inv_h);
770
771 composed_icon = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, out_w, out_h);
772 if(composed_icon)
773 {
774 gdk_pixbuf_fill(composed_icon, 0x00000000);
775 gdk_pixbuf_copy_area(icon, 0, 0, base_w, base_h, composed_icon, 0, 0);
776 gdk_pixbuf_copy_area(op_icon[DT_MASKS_STATE_INVERSE], 0, 0, inv_w, inv_h, composed_icon, base_w, 0);
777 icon = composed_icon;
778 }
779 }
780 else
781 icon = op_icon[DT_MASKS_STATE_INVERSE];
782 }
783
784 const gboolean draw_icon = !IS_NULL_PTR(op_form) && form_pos > 0;
785 gchar *title = g_strdup_printf("<b><big>%s%s</big></b>", item_str, form_name);
786 GtkWidget *menu_item = ctx_gtk_menu_item_new_with_markup_and_pixbuf(title, (draw_icon) ? icon : NULL, menu, NULL, gui);
787 gtk_widget_set_sensitive(menu_item, FALSE);
788 dt_free(item_str);
789 dt_free(title);
790 dt_free(form_name);
791
792 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
793
794 // Common menu items
795 if(!gui->creation && (gui->form_selected || gui->node_selected) && op_form)
796 {
798
799 if(form->type & DT_MASKS_GRADIENT)
800 {
801 // For gradients, DT_MASKS_INTERACTION_HARDNESS is the shape curvature and
802 // DT_MASKS_INTERACTION_SIZE is the fade extent -- expose them under their actual names.
806 if(!isfinite(rotation)) rotation = 0.0f;
807 if(rotation > 180.0f) rotation -= 360.0f;
808
810 DT_MASKS_INCREMENT_ABSOLUTE, -2.0f, 2.0f, 0.01f,
811 isfinite(curvature) ? curvature : 0.0f, 3, "%", 50.0f,
814 DT_MASKS_INCREMENT_ABSOLUTE, 0.0f, 1.0f, 0.001f,
815 isfinite(fade) ? fade : 1.0f, 3, "%", 100.0f,
818 DT_MASKS_INCREMENT_ABSOLUTE, -180.0f, 180.0f, 1.0f,
819 rotation, 1, "\302\260", 1.0f,
821 }
822 else
823 {
825
827 DT_MASKS_INCREMENT_SCALE, -4.f, 4.0f, 0.01f, 0.0f, 2, "x", 1.0f,
830 DT_MASKS_INCREMENT_ABSOLUTE, 0.f, 1.0f, 0.01f,
831 isfinite(hardness) ? hardness : 1.0f, 3, "%", 100.0f,
833
834 if(form->type & DT_MASKS_ELLIPSE)
835 {
837 if(!isfinite(rotation)) rotation = 0.0f;
838 if(rotation > 180.0f) rotation -= 360.0f;
839
841 DT_MASKS_INCREMENT_ABSOLUTE, -180.0f, 180.0f, 1.0f,
842 rotation, 1, "\302\260", 1.0f,
844 }
845 }
846
848 DT_MASKS_INCREMENT_ABSOLUTE, 0.0f, 1.0f, 0.01f,
849 isfinite(opacity) ? opacity : 1.0f, 3, "%", 100.0f,
851
852 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
853 }
854
855 // Shape specific menu items
856 if(!IS_NULL_PTR(form) && form->functions && form->functions->populate_context_menu)
857 if(form->functions->populate_context_menu(menu, form, gui, pzx, pzy))
858 {
859 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
860 }
861
862
863 /* Module specific */
864 {
865 dt_iop_module_t *module = darktable.develop->gui_module;
866 if(!IS_NULL_PTR(module) && module->populate_masks_context_menu)
867 if(module->populate_masks_context_menu(module, menu, form->formid, pzx, pzy))
868 {
869 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
870 }
871 }
872
873 /* Operation */
874
875 if(!gui->creation && !(form->type & DT_MASKS_IS_RETOUCHE) && (op_form) && !gui->node_selected)
876 {
877 menu_item = ctx_gtk_menu_item_new_with_markup(_("Operation"), menu, NULL, gui);
878 GtkWidget *sub_menu = gtk_menu_new();
879 gtk_style_context_add_class(gtk_widget_get_style_context(sub_menu), "dt-masks-context-menu");
880 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), sub_menu);
881
883 op_icon[DT_MASKS_STATE_INVERSE]);
884 gtk_menu_shell_append(GTK_MENU_SHELL(sub_menu), gtk_separator_menu_item_new());
886 op_icon[DT_MASKS_STATE_UNION]);
892 op_icon[DT_MASKS_STATE_EXCLUSION]);
893
894 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
895 }
896
897 if(!gui->creation && gui->form_selected)
898 {
899 menu_item = ctx_gtk_menu_item_new_with_markup(_("Move up"), menu, _masks_moveup_callback, gui);
900 gtk_widget_set_sensitive(menu_item, (form_pos > 0));
901 menu_item = ctx_gtk_menu_item_new_with_markup(_("Move down"), menu, _masks_movedown_callback, gui);
902 gtk_widget_set_sensitive(menu_item, (form_pos < list_length - 1));
903
904 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
905 }
906
907 // Risky stuff at the end
908 if(gui->creation)
909 {
910 menu_item = ctx_gtk_menu_item_new_with_markup(_("Done shape creation"), menu,
912 menu_item_set_fake_accel(menu_item, GDK_KEY_Escape, 0);
913 }
914 else
915 {
916 if(gui->node_hovered >= 0)
917 {
918 menu_item = ctx_gtk_menu_item_new_with_markup(_("Delete node"), menu, _masks_gui_delete_node_callback, gui);
919 menu_item_set_fake_accel(menu_item, GDK_KEY_Delete, 0);
920 }
921 else
922 {
923 menu_item = ctx_gtk_menu_item_new_with_markup(_("Remove shape from mask"), menu, _masks_gui_remove_from_group_callback, gui);
924 menu_item_set_fake_accel(menu_item, GDK_KEY_Delete, 0);
925 gtk_widget_set_sensitive(menu_item, gui->form_selected >= 0);
926
927 menu_item = ctx_gtk_menu_item_new_with_markup(_("Delete shape"), menu, _masks_gui_full_delete_callback, gui);
928 gtk_widget_set_sensitive(menu_item, gui->form_selected >= 0);
929 }
930 }
931
932 for(size_t k = 0; k < G_N_ELEMENTS(op_icon); k++)
933 g_clear_object(&op_icon[k]);
934 g_clear_object(&composed_icon);
935
936 gtk_widget_show_all(menu);
937 return menu;
938}
939
940// clang-format off
941// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
942// vim: shiftwidth=2 expandtab tabstop=2 cindent
943// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
944// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void dt_bauhaus_slider_set_digits(GtkWidget *widget, int val)
Definition bauhaus.c:3549
float dt_bauhaus_slider_get(GtkWidget *widget)
Definition bauhaus.c:3486
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:3537
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
Definition bauhaus.c:1656
GtkWidget * dt_bauhaus_slider_new_with_range(dt_bauhaus_t *bh, dt_gui_module_t *self, float min, float max, float step, float defval, int digits)
Definition bauhaus.c:1783
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
Definition bauhaus.c:3613
void dt_bauhaus_slider_set_factor(GtkWidget *widget, float factor)
Definition bauhaus.c:3626
#define DT_BAUHAUS_WIDGET(obj)
Definition bauhaus.h:60
int width
Definition bilateral.h:1
static const float const float const float min
const float max
const float delta
int type
void dt_conf_set_bool(const char *name, int val)
int dt_conf_get_bool(const char *name)
void dt_control_log(const char *msg,...)
Definition control.c:777
void dt_control_queue_redraw_center()
request redraw of center window. This redraws the center view within a gdk critical section to preven...
Definition control.c:877
darktable_t darktable
Definition darktable.c:183
#define dt_free(ptr)
Definition darktable.h:478
void void void gboolean dt_gui_widgets_suppressed(void)
Definition gtk.c:194
static const dt_aligned_pixel_simd_t value
Definition darktable.h:599
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:293
#define dt_dev_add_history_item(dev, module, enable, redraw)
static GdkPixbuf * dt_draw_get_pixbuf_from_cairo(DTGTKCairoPaintIconFunc paint, const int width, const int height)
Definition draw.h:947
void dtgtk_cairo_paint_masks_circle(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_masks_inverse(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_masks_brush(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_masks_difference(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_masks_ellipse(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
Paint a 45 deg-rotated ellipse that touches the unit square boundaries.
void dtgtk_cairo_paint_masks_gradient(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_masks_union(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_masks_exclusion(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_masks_polygon(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_masks_intersection(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void(* DTGTKCairoPaintIconFunc)(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
Definition dtgtk/paint.h:75
GtkWidget * dt_ui_center(dt_ui_t *ui)
get the center drawable widget
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
#define DT_GUI_MODULE(x)
GtkWidget * dt_iop_togglebutton_new_no_register(dt_iop_module_t *self, const char *section, const gchar *label, const gchar *ctrl_label, GCallback callback, gboolean local, guint accel_key, GdkModifierType mods, DTGTKCairoPaintIconFunc paint, GtkWidget *box)
GtkWidget * dt_iop_togglebutton_new(dt_iop_module_t *self, const char *section, const gchar *label, const gchar *ctrl_label, GCallback callback, gboolean local, guint accel_key, GdkModifierType mods, DTGTKCairoPaintIconFunc paint, GtkWidget *box)
static const float x
float *const restrict const size_t k
void apply_operation(struct dt_masks_form_group_t *pt, const dt_masks_state_t apply_state)
Apply a mask state operation on a group entry.
@ DT_MASKS_SHAPE_INDEX_BRUSH
Definition masks.h:945
@ DT_MASKS_SHAPE_INDEX_ELLIPSE
Definition masks.h:943
@ DT_MASKS_SHAPE_INDEX_CIRCLE
Definition masks.h:944
@ DT_MASKS_SHAPE_INDEX_GRADIENT
Definition masks.h:941
@ DT_MASKS_SHAPE_INDEX_POLYGON
Definition masks.h:942
dt_masks_state_t
Definition masks.h:167
@ DT_MASKS_STATE_DIFFERENCE
Definition masks.h:174
@ DT_MASKS_STATE_INVERSE
Definition masks.h:171
@ DT_MASKS_STATE_INTERSECTION
Definition masks.h:173
@ DT_MASKS_STATE_IS_COMBINE_OP
Definition masks.h:178
@ DT_MASKS_STATE_EXCLUSION
Definition masks.h:175
@ DT_MASKS_STATE_UNION
Definition masks.h:172
float dt_masks_form_get_interaction_value(dt_masks_form_group_t *form_group, dt_masks_interaction_t interaction)
gboolean dt_masks_form_exit_creation(dt_iop_module_t *module, dt_masks_form_gui_t *gui)
dt_masks_type_t
Definition masks.h:130
@ DT_MASKS_POLYGON
Definition masks.h:133
@ DT_MASKS_BRUSH
Definition masks.h:138
@ DT_MASKS_ELLIPSE
Definition masks.h:137
@ DT_MASKS_GRADIENT
Definition masks.h:136
@ DT_MASKS_CIRCLE
Definition masks.h:132
@ DT_MASKS_GROUP
Definition masks.h:134
@ DT_MASKS_IS_RETOUCHE
Definition masks.h:147
dt_masks_interaction_t
Definition masks.h:303
@ DT_MASKS_INTERACTION_OPACITY
Definition masks.h:307
@ DT_MASKS_INTERACTION_HARDNESS
Definition masks.h:306
@ DT_MASKS_INTERACTION_SIZE
Definition masks.h:305
@ DT_MASKS_INTERACTION_ROTATION
Definition masks.h:308
#define menu_item_set_fake_accel(menu_item, keyval, mods)
Definition masks.h:1556
dt_masks_form_group_t * dt_masks_form_get_selected_group(const struct dt_masks_form_t *form, const struct dt_masks_form_gui_t *gui)
gboolean dt_masks_remove_shape_from_group(struct dt_iop_module_t *module, dt_masks_form_t *sel, int parent_id, dt_masks_form_gui_t *mask_gui, int form_id)
Remove the form from its current group only, keeping it around unused for potential reuse....
@ DT_MASKS_EVENT_UPDATE
Definition masks.h:161
@ DT_MASKS_EVENT_CHANGE
Definition masks.h:163
@ DT_MASKS_SHAPE_BUTTONS_GRADIENT
Definition masks.h:961
@ DT_MASKS_SHAPE_BUTTONS_CIRCLE
Definition masks.h:953
@ DT_MASKS_SHAPE_BUTTONS_BRUSH
Definition masks.h:959
@ DT_MASKS_SHAPE_BUTTONS_ELLIPSE
Definition masks.h:955
@ DT_MASKS_SHAPE_BUTTONS_POLYGON
Definition masks.h:957
gboolean dt_masks_creation_mode_enter(dt_iop_module_t *module, const dt_masks_type_t type)
Enter mask creation mode for a given shape type.
void dt_masks_form_move(dt_masks_form_t *grp, int formid, int up)
dt_masks_increment_t
Definition masks.h:194
@ DT_MASKS_INCREMENT_SCALE
Definition masks.h:196
@ DT_MASKS_INCREMENT_ABSOLUTE
Definition masks.h:195
float dt_masks_form_set_interaction_value(dt_masks_form_group_t *form_group, dt_masks_interaction_t interaction, float value, dt_masks_increment_t increment, int flow, struct dt_masks_form_gui_t *gui, struct dt_iop_module_t *module)
gboolean dt_masks_delete_shape(struct dt_iop_module_t *module, dt_masks_form_t *sel, int parent_id, dt_masks_form_gui_t *mask_gui, int form_id)
Permanently delete the form from every group and from dev->forms. Gated by dt_masks_gui_confirm_perma...
dt_masks_form_t * dt_masks_get_from_id(dt_develop_t *dev, int id)
dt_masks_form_t * dt_masks_get_visible_form(const struct dt_develop_t *dev)
void dt_masks_remove_node(struct dt_iop_module_t *module, dt_masks_form_t *form, int parentid, dt_masks_form_gui_t *gui, int index, int node_index)
dt_masks_form_group_t * dt_masks_form_group_from_parentid(int parentid, int formid)
Return the group entry for a (parent, form) pair.
static void _masks_gui_menu_item_block_activate(GtkWidget *widget, gpointer user_data)
Definition masks_gui.c:296
static gboolean _masks_gui_menu_item_forward_event(GtkWidget *widget, GdkEvent *event, gpointer user_data)
Definition masks_gui.c:301
static void _masks_move_up_down_callback(gpointer user_data, const int up)
Definition masks_gui.c:585
static gboolean _masks_shape_button_pressed(GtkWidget *button, GdkEventButton *event, gpointer user_data)
Definition masks_gui.c:115
static int _masks_shape_button_index(const dt_masks_shape_buttons_data_t *data, GtkWidget *button)
Definition masks_gui.c:92
gboolean dt_masks_gui_confirm_permanent_delete(const char *form_name)
Ask the user to confirm a permanent shape deletion, gated by the "ask_before_delete_mask_shape" pref....
Definition masks_gui.c:464
static gboolean _masks_shape_button_is_current_creation(const dt_masks_shape_buttons_data_t *data, const int button_index)
Definition masks_gui.c:103
static void _masks_gui_interaction_apply_value(dt_masks_gui_interaction_slider_t *data, float value)
Definition masks_gui.c:272
void dt_masks_shape_buttons_deactivate_all(GtkWidget *active_button)
Definition masks_gui.c:87
static void _masks_movedown_callback(GtkWidget *menu, gpointer user_data)
Definition masks_gui.c:612
static void _masks_gui_interaction_slider_changed(GtkWidget *widget, gpointer user_data)
Definition masks_gui.c:376
GtkWidget * dt_masks_shape_buttons_create(const dt_masks_shape_buttons_config_t *config)
Build a synchronized toolbar for creating masks shapes.
Definition masks_gui.c:181
static const dt_masks_shape_button_def_t _masks_shape_button_defs[]
Definition masks_gui.c:55
static void _masks_moveup_callback(GtkWidget *menu, gpointer user_data)
Definition masks_gui.c:607
static void _masks_shape_buttons_destroy(GtkWidget *widget, dt_masks_shape_buttons_data_t *data)
Definition masks_gui.c:167
void _masks_gui_delete_node_callback(GtkWidget *menu, gpointer user_data)
Definition masks_gui.c:541
#define masks_gtk_menu_item_new_bold(label, selected, state, icon)
Definition masks_gui.c:641
static gboolean _masks_gui_resolve_selected_shape(dt_masks_form_gui_t *gui, dt_iop_module_t **module, dt_masks_form_t **sel, int *parentid, int *formid)
Definition masks_gui.c:496
int dt_masks_gui_confirm_delete_form_dialog(const char *form_name)
Definition masks_gui.c:440
static void _masks_gui_remove_from_group_callback(GtkWidget *menu, gpointer user_data)
Definition masks_gui.c:518
static void _masks_shape_buttons_deactivate(GtkWidget *active_button, dt_masks_shape_buttons_data_t *data)
Definition masks_gui.c:68
static void _masks_shape_buttons_deactivate_signal(gpointer instance, GtkWidget *active_button, dt_masks_shape_buttons_data_t *data)
Definition masks_gui.c:81
GtkWidget * dt_masks_create_menu(dt_masks_form_gui_t *gui, dt_masks_form_t *form, const dt_masks_form_group_t *formgroup, const float pzx, const float pzy)
Definition masks_gui.c:657
#define DT_MASKS_SHAPE_BUTTON_COUNT
Definition masks_gui.c:35
static void _masks_gui_full_delete_callback(GtkWidget *menu, gpointer user_data)
Definition masks_gui.c:530
static void _masks_operation_callback(GtkWidget *menu, gpointer user_data)
Definition masks_gui.c:619
static GtkWidget * _masks_gui_add_interaction_slider(GtkWidget *menu, const char *label, dt_masks_form_group_t *form_group, dt_masks_interaction_t interaction, dt_masks_increment_t increment, float min, float max, float step, float value, int digits, const char *format, float factor, dt_masks_form_gui_t *gui, dt_iop_module_t *module)
Definition masks_gui.c:384
static void _masks_gui_exit_creation_callback(GtkWidget *menu, gpointer user_data)
Definition masks_gui.c:578
static void _masks_gui_interaction_commit(dt_masks_gui_interaction_slider_t *data)
Definition masks_gui.c:262
dt_masks_form_t * dt_masks_cow_touch(dt_develop_t *dev, dt_masks_form_t *form)
GtkWidget * ctx_gtk_menu_item_new_with_markup(const char *label, GtkWidget *menu, void(*activate_callback)(GtkWidget *widget, gpointer user_data), gpointer user_data)
Definition menu.c:172
GtkWidget * ctx_gtk_menu_item_new_with_markup_and_pixbuf(const char *label, GdkPixbuf *icon, GtkWidget *menu, void(*activate_callback)(GtkWidget *widget, gpointer user_data), gpointer user_data)
Definition menu.c:217
const float factor
Definition pdf.h:90
void copy(double *dest, double *source, size_t num_el)
Copy a flat buffer.
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
Definition signal.h:387
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
Definition signal.h:366
@ DT_SIGNAL_MASK_CHANGED
Definition signal.h:312
@ DT_SIGNAL_MASK_SHAPE_BUTTONS_DEACTIVATE
Definition signal.h:313
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
Definition signal.h:376
struct _GtkWidget GtkWidget
Definition splash.h:29
const float uint32_t state[4]
struct dt_gui_gtk_t * gui
Definition darktable.h:803
struct dt_control_signal_t * signals
Definition darktable.h:802
struct dt_bauhaus_t * bauhaus
Definition darktable.h:806
struct dt_develop_t * develop
Definition darktable.h:798
struct dt_iop_module_t * gui_module
Definition develop.h:165
struct dt_masks_form_gui_t * form_gui
Definition develop.h:333
dt_ui_t * ui
Definition gtk.h:165
gboolean node_selected
Definition masks.h:484
dt_iop_module_t * creation_module
Definition masks.h:519
gboolean creation
Definition masks.h:517
gboolean form_selected
Definition masks.h:490
const dt_masks_functions_t * functions
Definition masks.h:381
dt_masks_type_t type
Definition masks.h:380
char name[128]
Definition masks.h:403
GList * points
Definition masks.h:379
int(* populate_context_menu)(GtkWidget *menu, struct dt_masks_form_t *form, struct dt_masks_form_gui_t *gui, const float pzx, const float pzy)
Definition masks.h:373
dt_iop_module_t *dt_masks_interaction_t interaction
Definition masks_gui.c:249
dt_masks_form_gui_t * gui
Definition masks_gui.c:247
dt_masks_increment_t increment
Definition masks_gui.c:250
dt_masks_form_group_t * form_group
Definition masks_gui.c:246
DTGTKCairoPaintIconFunc paint
Definition masks_gui.c:44
dt_iop_module_t * creation_module
Definition masks.h:980
dt_masks_shape_buttons_flags_t register_flags
Definition masks.h:985
dt_masks_shape_buttons_type_f form_type
Definition masks.h:989
dt_masks_shape_buttons_start_f can_start
Definition masks.h:988
dt_masks_shape_buttons_notify_f exited
Definition masks.h:991
dt_masks_shape_buttons_notify_f started
Definition masks.h:990
dt_iop_module_t * owner_module
Definition masks.h:979
dt_masks_shape_buttons_flags_t flags
Definition masks.h:984
dt_masks_shape_buttons_config_t config
Definition masks_gui.c:52
#define MAX(a, b)
Definition thinplate.c:29
GtkWidget * dtgtk_togglebutton_new(DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)