35#define DT_MASKS_SHAPE_BUTTON_COUNT 5
76 if(GTK_IS_TOGGLE_BUTTON(button) && button != active_button)
77 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),
FALSE);
104 const int button_index)
112 && (visible_form->
type & data->
types[button_index]);
122 if(button_index < 0)
return FALSE;
147 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),
TRUE);
190 gtk_widget_set_halign(data->
box, GTK_ALIGN_END);
191 gtk_widget_set_valign(data->
box, GTK_ALIGN_CENTER);
197 for(
size_t i = 0;
i < button_defs_count;
i++)
200 if(!(config->
flags & def->
flag))
continue;
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);
228 gtk_widget_set_can_focus(button,
FALSE);
229 g_object_set_data(G_OBJECT(button),
"dt-masks-shape-buttons-data", data);
286 if(fabsf(
delta) < 1e-6f)
return;
289 const float scale = exp2f(
delta);
298 g_signal_stop_emission_by_name(widget,
"activate");
306 GdkEvent *
copy = gdk_event_copy(event);
309 double x = 0.0, y = 0.0;
310 gboolean has_coords =
FALSE;
313 case GDK_BUTTON_PRESS:
314 case GDK_2BUTTON_PRESS:
315 case GDK_3BUTTON_PRESS:
316 case GDK_BUTTON_RELEASE:
321 case GDK_MOTION_NOTIFY:
338 if(gtk_widget_translate_coordinates(widget, data->
slider, (
int)
x, (
int)y, &sx, &sy))
342 case GDK_BUTTON_PRESS:
343 case GDK_2BUTTON_PRESS:
344 case GDK_3BUTTON_PRESS:
345 case GDK_BUTTON_RELEASE:
349 case GDK_MOTION_NOTIFY:
363 GdkWindow *slider_window = gtk_widget_get_window(data->
slider);
366 if(
copy->any.window) g_object_unref(
copy->any.window);
367 copy->any.window = g_object_ref(slider_window);
372 gdk_event_free(
copy);
386 float min,
float max,
float step,
float value,
int digits,
387 const char *format,
float factor,
390 GtkWidget *menu_item = gtk_menu_item_new();
393 gtk_widget_set_can_focus(menu_item,
FALSE);
394 g_signal_connect(G_OBJECT(menu_item),
"activate",
396 gtk_widget_add_events(menu_item, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
397 | GDK_POINTER_MOTION_MASK | GDK_SCROLL_MASK);
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);
411 gtk_widget_set_can_focus(slider,
TRUE);
416 data->module =
module;
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",
426 g_signal_connect(G_OBJECT(menu_item),
"button-release-event",
428 g_signal_connect(G_OBJECT(menu_item),
"motion-notify-event",
430 g_signal_connect(G_OBJECT(menu_item),
"scroll-event",
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);
444 GtkWidget *dialog = gtk_message_dialog_new(
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?"));
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);
458 const int response = gtk_dialog_run(GTK_DIALOG(dialog));
459 gtk_widget_destroy(dialog);
469 GtkWidget *dialog = gtk_message_dialog_new(
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."));
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);
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);
491 return response == GTK_RESPONSE_YES;
523 int parentid = 0, formid = 0;
535 int parentid = 0, formid = 0;
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"));
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"));
641#define masks_gtk_menu_item_new_bold(label, selected, state, icon) \
643 gchar *op_label = g_strdup(label); \
644 menu_item = ctx_gtk_check_menu_item_new_with_markup_and_pixbuf(op_label, icon, \
646 _masks_operation_callback, gui, \
648 ((state) == DT_MASKS_STATE_INVERSE)); \
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)); \
658 const float pzx,
const float pzy)
664 gtk_style_context_add_class(gtk_widget_get_style_context(menu),
"dt-masks-context-menu");
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);
691 gboolean form_found =
FALSE;
694 for(GList *fpts = grp->
points; fpts; fpts = g_list_next(fpts))
707 guint list_length = (form_found && grp) ? g_list_length(grp->
points) : 0;
711 gchar *form_name = NULL;
713 form_name = g_strdup(form->
name);
717 form_name = g_strdup(_(
"New "));
721 form_name = g_strconcat(form_name, _(
"circle"), NULL);
724 form_name = g_strconcat(form_name, _(
"ellipse"), NULL);
727 form_name = g_strconcat(form_name, _(
"polygon"), NULL);
730 form_name = g_strconcat(form_name, _(
"brush"), NULL);
733 form_name = g_strconcat(form_name, _(
"gradient"), NULL);
736 form_name = g_strconcat(form_name, _(
"mask"), NULL);
740 form_name = g_strdup(_(
"Unknown shape"));
746 gchar *item_str = NULL;
750 item_str = g_strdup_printf(
"%s %d - ", gui->
node_hovered >= 0 ? _(
"Node") : _(
"Segment"), item_index);
753 item_str = g_strdup(
"");
759 GdkPixbuf *composed_icon = NULL;
764 const int base_w = gdk_pixbuf_get_width(icon);
765 const int base_h = gdk_pixbuf_get_height(icon);
768 const int out_w = base_w + inv_w;
769 const int out_h =
MAX(base_h, inv_h);
771 composed_icon = gdk_pixbuf_new(GDK_COLORSPACE_RGB,
TRUE, 8, out_w, out_h);
774 gdk_pixbuf_fill(composed_icon, 0x00000000);
775 gdk_pixbuf_copy_area(icon, 0, 0, base_w, base_h, composed_icon, 0, 0);
777 icon = composed_icon;
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);
787 gtk_widget_set_sensitive(menu_item,
FALSE);
792 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
806 if(!isfinite(rotation)) rotation = 0.0f;
807 if(rotation > 180.0f) rotation -= 360.0f;
811 isfinite(curvature) ? curvature : 0.0f, 3,
"%", 50.0f,
815 isfinite(fade) ? fade : 1.0f, 3,
"%", 100.0f,
819 rotation, 1,
"\302\260", 1.0f,
831 isfinite(hardness) ? hardness : 1.0f, 3,
"%", 100.0f,
837 if(!isfinite(rotation)) rotation = 0.0f;
838 if(rotation > 180.0f) rotation -= 360.0f;
842 rotation, 1,
"\302\260", 1.0f,
849 isfinite(opacity) ? opacity : 1.0f, 3,
"%", 100.0f,
852 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
859 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
866 if(!
IS_NULL_PTR(module) && module->populate_masks_context_menu)
867 if(module->populate_masks_context_menu(module, menu, form->
formid, pzx, pzy))
869 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_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);
884 gtk_menu_shell_append(GTK_MENU_SHELL(sub_menu), gtk_separator_menu_item_new());
894 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
900 gtk_widget_set_sensitive(menu_item, (form_pos > 0));
902 gtk_widget_set_sensitive(menu_item, (form_pos < list_length - 1));
904 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
925 gtk_widget_set_sensitive(menu_item, gui->
form_selected >= 0);
928 gtk_widget_set_sensitive(menu_item, gui->
form_selected >= 0);
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);
936 gtk_widget_show_all(menu);
void dt_bauhaus_slider_set_digits(GtkWidget *widget, int val)
float dt_bauhaus_slider_get(GtkWidget *widget)
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
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)
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
void dt_bauhaus_slider_set_factor(GtkWidget *widget, float factor)
#define DT_BAUHAUS_WIDGET(obj)
static const float const float const float min
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,...)
void dt_control_queue_redraw_center()
request redraw of center window. This redraws the center view within a gdk critical section to preven...
void void void gboolean dt_gui_widgets_suppressed(void)
static const dt_aligned_pixel_simd_t value
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
#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)
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)
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
#define DT_PIXEL_APPLY_DPI(value)
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)
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
@ DT_MASKS_SHAPE_INDEX_ELLIPSE
@ DT_MASKS_SHAPE_INDEX_CIRCLE
@ DT_MASKS_SHAPE_INDEX_GRADIENT
@ DT_MASKS_SHAPE_INDEX_POLYGON
@ DT_MASKS_STATE_DIFFERENCE
@ DT_MASKS_STATE_INTERSECTION
@ DT_MASKS_STATE_IS_COMBINE_OP
@ DT_MASKS_STATE_EXCLUSION
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_INTERACTION_OPACITY
@ DT_MASKS_INTERACTION_HARDNESS
@ DT_MASKS_INTERACTION_SIZE
@ DT_MASKS_INTERACTION_ROTATION
#define menu_item_set_fake_accel(menu_item, keyval, mods)
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_SHAPE_BUTTONS_GRADIENT
@ DT_MASKS_SHAPE_BUTTONS_CIRCLE
@ DT_MASKS_SHAPE_BUTTONS_BRUSH
@ DT_MASKS_SHAPE_BUTTONS_ELLIPSE
@ DT_MASKS_SHAPE_BUTTONS_POLYGON
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_SCALE
@ DT_MASKS_INCREMENT_ABSOLUTE
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)
static gboolean _masks_gui_menu_item_forward_event(GtkWidget *widget, GdkEvent *event, gpointer user_data)
static void _masks_move_up_down_callback(gpointer user_data, const int up)
static gboolean _masks_shape_button_pressed(GtkWidget *button, GdkEventButton *event, gpointer user_data)
static int _masks_shape_button_index(const dt_masks_shape_buttons_data_t *data, GtkWidget *button)
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....
static gboolean _masks_shape_button_is_current_creation(const dt_masks_shape_buttons_data_t *data, const int button_index)
static void _masks_gui_interaction_apply_value(dt_masks_gui_interaction_slider_t *data, float value)
void dt_masks_shape_buttons_deactivate_all(GtkWidget *active_button)
static void _masks_movedown_callback(GtkWidget *menu, gpointer user_data)
static void _masks_gui_interaction_slider_changed(GtkWidget *widget, gpointer user_data)
GtkWidget * dt_masks_shape_buttons_create(const dt_masks_shape_buttons_config_t *config)
Build a synchronized toolbar for creating masks shapes.
static const dt_masks_shape_button_def_t _masks_shape_button_defs[]
static void _masks_moveup_callback(GtkWidget *menu, gpointer user_data)
static void _masks_shape_buttons_destroy(GtkWidget *widget, dt_masks_shape_buttons_data_t *data)
void _masks_gui_delete_node_callback(GtkWidget *menu, gpointer user_data)
#define masks_gtk_menu_item_new_bold(label, selected, state, icon)
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)
int dt_masks_gui_confirm_delete_form_dialog(const char *form_name)
static void _masks_gui_remove_from_group_callback(GtkWidget *menu, gpointer user_data)
static void _masks_shape_buttons_deactivate(GtkWidget *active_button, dt_masks_shape_buttons_data_t *data)
static void _masks_shape_buttons_deactivate_signal(gpointer instance, GtkWidget *active_button, dt_masks_shape_buttons_data_t *data)
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)
#define DT_MASKS_SHAPE_BUTTON_COUNT
static void _masks_gui_full_delete_callback(GtkWidget *menu, gpointer user_data)
static void _masks_operation_callback(GtkWidget *menu, gpointer user_data)
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)
static void _masks_gui_exit_creation_callback(GtkWidget *menu, gpointer user_data)
static void _masks_gui_interaction_commit(dt_masks_gui_interaction_slider_t *data)
dt_masks_form_t * dt_masks_cow_touch(dt_develop_t *dev, dt_masks_form_t *form)
void copy(double *dest, double *source, size_t num_el)
Copy a flat buffer.
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
@ DT_SIGNAL_MASK_SHAPE_BUTTONS_DEACTIVATE
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
struct _GtkWidget GtkWidget
const float uint32_t state[4]
struct dt_gui_gtk_t * gui
struct dt_control_signal_t * signals
struct dt_bauhaus_t * bauhaus
struct dt_develop_t * develop
struct dt_iop_module_t * gui_module
struct dt_masks_form_gui_t * form_gui
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)
dt_iop_module_t *dt_masks_interaction_t interaction
dt_masks_form_gui_t * gui
dt_masks_increment_t increment
dt_masks_form_group_t * form_group