34#define DT_MASKS_SHAPE_BUTTON_COUNT 5
75 if(GTK_IS_TOGGLE_BUTTON(button) && button != active_button)
76 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),
FALSE);
103 const int button_index)
111 && (visible_form->
type & data->
types[button_index]);
121 if(button_index < 0)
return FALSE;
146 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),
TRUE);
189 gtk_widget_set_halign(data->
box, GTK_ALIGN_END);
190 gtk_widget_set_valign(data->
box, GTK_ALIGN_CENTER);
196 for(
size_t i = 0;
i < button_defs_count;
i++)
199 if(!(config->
flags & def->
flag))
continue;
221 gtk_widget_set_tooltip_text(button, _(def->
label));
222 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),
FALSE);
223 gtk_box_pack_end(GTK_BOX(data->
box), button,
FALSE,
FALSE, 0);
227 gtk_widget_set_can_focus(button,
FALSE);
228 g_object_set_data(G_OBJECT(button),
"dt-masks-shape-buttons-data", data);
285 if(fabsf(
delta) < 1e-6f)
return;
288 const float scale = exp2f(
delta);
297 g_signal_stop_emission_by_name(widget,
"activate");
305 GdkEvent *
copy = gdk_event_copy(event);
308 double x = 0.0, y = 0.0;
309 gboolean has_coords =
FALSE;
312 case GDK_BUTTON_PRESS:
313 case GDK_2BUTTON_PRESS:
314 case GDK_3BUTTON_PRESS:
315 case GDK_BUTTON_RELEASE:
320 case GDK_MOTION_NOTIFY:
337 if(gtk_widget_translate_coordinates(widget, data->
slider, (
int)
x, (
int)y, &sx, &sy))
341 case GDK_BUTTON_PRESS:
342 case GDK_2BUTTON_PRESS:
343 case GDK_3BUTTON_PRESS:
344 case GDK_BUTTON_RELEASE:
348 case GDK_MOTION_NOTIFY:
362 GdkWindow *slider_window = gtk_widget_get_window(data->
slider);
365 if(
copy->any.window) g_object_unref(
copy->any.window);
366 copy->any.window = g_object_ref(slider_window);
371 gdk_event_free(
copy);
385 float min,
float max,
float step,
float value,
int digits,
386 const char *format,
float factor,
389 GtkWidget *menu_item = gtk_menu_item_new();
392 gtk_widget_set_can_focus(menu_item,
FALSE);
393 g_signal_connect(G_OBJECT(menu_item),
"activate",
395 gtk_widget_add_events(menu_item, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
396 | GDK_POINTER_MOTION_MASK | GDK_SCROLL_MASK);
406 gtk_widget_set_hexpand(slider,
TRUE);
407 gtk_widget_set_halign(slider, GTK_ALIGN_FILL);
408 gtk_widget_set_valign(slider, GTK_ALIGN_CENTER);
410 gtk_widget_set_can_focus(slider,
TRUE);
415 data->module =
module;
420 g_signal_connect_data(G_OBJECT(slider),
"value-changed",
422 data, (GClosureNotify)g_free, 0);
423 g_signal_connect(G_OBJECT(menu_item),
"button-press-event",
425 g_signal_connect(G_OBJECT(menu_item),
"button-release-event",
427 g_signal_connect(G_OBJECT(menu_item),
"motion-notify-event",
429 g_signal_connect(G_OBJECT(menu_item),
"scroll-event",
432 gtk_box_pack_start(GTK_BOX(box), slider,
TRUE,
TRUE, 0);
433 gtk_container_add(GTK_CONTAINER(menu_item), box);
434 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
443 GtkWidget *dialog = gtk_message_dialog_new(
445 GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
446 GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, _(
"Delete the shape '%s' ?"), form_name);
447 gtk_message_dialog_format_secondary_text(
448 GTK_MESSAGE_DIALOG(dialog),
"'%s' %s\n\n%s", form_name,
449 _(
"will no longer be used."),
450 _(
"Do you want to permanently delete it, or keep it unused for potential reuse?"));
452 gtk_dialog_add_button(GTK_DIALOG(dialog), _(
"Delete shape"), GTK_RESPONSE_YES);
453 gtk_dialog_add_button(GTK_DIALOG(dialog), _(
"Keep unused shape"), GTK_RESPONSE_NO);
454 gtk_dialog_add_button(GTK_DIALOG(dialog), _(
"Cancel"), GTK_RESPONSE_CANCEL);
455 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
457 const int response = gtk_dialog_run(GTK_DIALOG(dialog));
458 gtk_widget_destroy(dialog);
481 const int formid = fpt->
formid;
570 const guint form_pos = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(menu),
"form_pos"));
571 const dt_masks_state_t state_op = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu),
"state_op"));
575 dt_control_log(_(
"Applying a boolean operation has no effect on the first shape of a group.\n"
576 "Move it to at least the 2nd position if you need to use boolean operations"));
587#define masks_gtk_menu_item_new_bold(label, selected, state, icon) \
589 gchar *op_label = g_strdup(label); \
590 menu_item = ctx_gtk_check_menu_item_new_with_markup_and_pixbuf(op_label, icon, \
592 _masks_operation_callback, gui, \
594 ((state) == DT_MASKS_STATE_INVERSE)); \
597 g_object_set_data(G_OBJECT(menu_item), "state_op", GINT_TO_POINTER(state)); \
598 g_object_set_data(G_OBJECT(menu_item), "op_form", op_form); \
599 g_object_set_data(G_OBJECT(menu_item), "form_pos", GINT_TO_POINTER(form_pos)); \
604 const float pzx,
const float pzy)
610 gtk_style_context_add_class(gtk_widget_get_style_context(menu),
"dt-masks-context-menu");
629 for(
size_t k = 0;
k < G_N_ELEMENTS(op_icon);
k++)
630 g_clear_object(&op_icon[
k]);
631 gtk_widget_destroy(menu);
637 gboolean form_found =
FALSE;
640 for(GList *fpts = grp->
points; fpts; fpts = g_list_next(fpts))
653 guint list_length = (form_found && grp) ? g_list_length(grp->
points) : 0;
657 gchar *form_name = NULL;
659 form_name = g_strdup(form->
name);
663 form_name = g_strdup(_(
"New "));
667 form_name = g_strconcat(form_name, _(
"circle"), NULL);
670 form_name = g_strconcat(form_name, _(
"ellipse"), NULL);
673 form_name = g_strconcat(form_name, _(
"polygon"), NULL);
676 form_name = g_strconcat(form_name, _(
"brush"), NULL);
679 form_name = g_strconcat(form_name, _(
"gradient"), NULL);
682 form_name = g_strconcat(form_name, _(
"mask"), NULL);
686 form_name = g_strdup(_(
"Unknown shape"));
692 gchar *item_str = NULL;
696 item_str = g_strdup_printf(
"%s %d - ", gui->
node_hovered >= 0 ? _(
"Node") : _(
"Segment"), item_index);
699 item_str = g_strdup(
"");
705 GdkPixbuf *composed_icon = NULL;
710 const int base_w = gdk_pixbuf_get_width(icon);
711 const int base_h = gdk_pixbuf_get_height(icon);
714 const int out_w = base_w + inv_w;
715 const int out_h =
MAX(base_h, inv_h);
717 composed_icon = gdk_pixbuf_new(GDK_COLORSPACE_RGB,
TRUE, 8, out_w, out_h);
720 gdk_pixbuf_fill(composed_icon, 0x00000000);
721 gdk_pixbuf_copy_area(icon, 0, 0, base_w, base_h, composed_icon, 0, 0);
723 icon = composed_icon;
730 const gboolean draw_icon = !
IS_NULL_PTR(op_form) && form_pos > 0;
731 gchar *title = g_strdup_printf(
"<b><big>%s%s</big></b>", item_str, form_name);
733 gtk_widget_set_sensitive(menu_item,
FALSE);
738 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
751 isfinite(hardness) ? hardness : 1.0f, 3,
"%", 100.0f,
755 isfinite(opacity) ? opacity : 1.0f, 3,
"%", 100.0f,
758 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
765 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
772 if(!
IS_NULL_PTR(module) && module->populate_masks_context_menu)
773 if(module->populate_masks_context_menu(module, menu, form->
formid, pzx, pzy))
775 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
785 gtk_style_context_add_class(gtk_widget_get_style_context(sub_menu),
"dt-masks-context-menu");
786 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), sub_menu);
790 gtk_menu_shell_append(GTK_MENU_SHELL(sub_menu), gtk_separator_menu_item_new());
800 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
806 gtk_widget_set_sensitive(menu_item, (form_pos > 0));
808 gtk_widget_set_sensitive(menu_item, (form_pos < list_length - 1));
810 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
831 gtk_widget_set_sensitive(menu_item, gui->
form_selected >= 0);
835 for(
size_t k = 0;
k < G_N_ELEMENTS(op_icon);
k++)
836 g_clear_object(&op_icon[
k]);
837 g_clear_object(&composed_icon);
839 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_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...
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
gboolean dt_masks_remove_or_delete(struct dt_iop_module_t *module, dt_masks_form_t *sel, int parent_id, dt_masks_form_gui_t *mask_gui, int form_id)
If the form to remove is used once, ask to the user if he wants to delete it from the list or just re...
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
#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)
@ 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)
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_gui_delete_form_callback(GtkWidget *menu, 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)
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)
int dt_masks_gui_confirm_delete_form_dialog(const char *form_name)
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_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)
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