97 GtkTooltip *
tooltip, gpointer user_data);
99 GtkTreeModel *
model, GtkTreeIter *iter, gpointer data);
103 return _(
"History of changes");
108 static const char *
v[] = {
"darkroom", NULL};
126 self->
data = (
void *)
d;
128 d->selection_reset =
FALSE;
131 gtk_widget_set_name(self->
widget,
"history-ui");
141 d->history_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(
d->history_store));
142 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(
d->history_view),
FALSE);
143 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(
d->history_view),
FALSE);
145 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
d->history_view));
146 gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
150 gtk_widget_set_has_tooltip(
d->history_view,
TRUE);
153 GtkCellRenderer *renderer_num = gtk_cell_renderer_text_new();
154 g_object_set(G_OBJECT(renderer_num),
"xalign", 1.0,
"family",
"monospace", NULL);
155 GtkTreeViewColumn *col_num = gtk_tree_view_column_new_with_attributes(
"n", renderer_num,
159 gtk_tree_view_column_set_sizing(col_num, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
160 gtk_tree_view_append_column(GTK_TREE_VIEW(
d->history_view), col_num);
162 GtkCellRenderer *renderer_label = gtk_cell_renderer_text_new();
163 g_object_set(G_OBJECT(renderer_label),
"ellipsize", PANGO_ELLIPSIZE_END, NULL);
164 GtkTreeViewColumn *col_label = gtk_tree_view_column_new_with_attributes(
"label", renderer_label,
168 gtk_tree_view_column_set_expand(col_label,
TRUE);
169 gtk_tree_view_append_column(GTK_TREE_VIEW(
d->history_view), col_label);
171 GtkCellRenderer *renderer_icon = gtk_cell_renderer_pixbuf_new();
172 GtkTreeViewColumn *col_icon = gtk_tree_view_column_new_with_attributes(
"status", renderer_icon,
175 gtk_tree_view_column_set_sizing(col_icon, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
176 gtk_tree_view_append_column(GTK_TREE_VIEW(
d->history_view), col_icon);
183 gtk_widget_show_all(self->
widget);
195 if(
d &&
d->history_store) g_object_unref(
d->history_store);
199static const char *
_history_icon_name(
const gboolean enabled,
const gboolean default_enabled,
const gboolean always_on,
200 const gboolean deprecated)
202 if(always_on)
return "emblem-readonly";
203 if(deprecated)
return "dialog-warning";
204 if(default_enabled)
return enabled ?
"emblem-ok" :
"process-stop";
205 return enabled ?
"emblem-ok" :
"process-stop";
218 gchar **change_parts = g_malloc0_n(field->
Struct.
entries + 1,
sizeof(
char*));
240 gchar *struct_text = num_parts ? g_strjoinv(
"\n", change_parts) : NULL;
241 g_strfreev(change_parts);
249 const gboolean is_valid =
250 g_utf8_validate((
char *)o, -1, NULL)
251 && g_utf8_validate((
char *)
p, -1, NULL);
253 if(is_valid && strncmp((
char*)o, (
char*)
p, field->
Array.
count))
254 return g_strdup_printf(
"%s\t\"%s\"\t\u2192\t\"%s\"",
d, (
char*)o, (
char*)
p);
258 const int max_elements = 4;
259 gchar **change_parts = g_malloc0_n(max_elements + 1,
sizeof(
char*));
268 if(element_text && ++num_parts <= max_elements)
269 change_parts[num_parts - 1] = element_text;
274 gchar *array_text = NULL;
275 if(num_parts > max_elements)
276 array_text = g_strdup_printf(
"%s\t%d changes",
d, num_parts);
277 else if(num_parts > 0)
278 array_text = g_strjoinv(
"\n", change_parts);
280 g_strfreev(change_parts);
286 if(*(
float*)o != *(
float*)
p && (isfinite(*(
float*)o) || isfinite(*(
float*)
p)))
287 return g_strdup_printf(
"%s\t%.4f\t\u2192\t%.4f",
d, *(
float*)o, *(
float*)
p);
290 if(*(
int*)o != *(
int*)
p)
291 return g_strdup_printf(
"%s\t%d\t\u2192\t%d",
d, *(
int*)o, *(
int*)
p);
294 if(*(
unsigned int*)o != *(
unsigned int*)
p)
295 return g_strdup_printf(
"%s\t%u\t\u2192\t%u",
d, *(
unsigned int*)o, *(
unsigned int*)
p);
298 if(*(
unsigned short int*)o != *(
unsigned short int*)
p)
299 return g_strdup_printf(
"%s\t%hu\t\u2192\t%hu",
d, *(
unsigned short int*)o, *(
unsigned short int*)
p);
302 if(*(uint8_t*)o != *(uint8_t*)
p)
303 return g_strdup_printf(
"%s\t%d\t\u2192\t%d",
d, *(uint8_t*)o, *(uint8_t*)
p);
306 if(*(
char*)o != *(
char*)
p)
307 return g_strdup_printf(
"%s\t'%c'\t\u2192\t'%c'",
d, *(
char *)o, *(
char *)
p);
310 if(*(
float complex*)o != *(
float complex*)
p)
311 return g_strdup_printf(
"%s\t%.4f + %.4fi\t\u2192\t%.4f + %.4fi",
d,
312 creal(*(
float complex*)o), cimag(*(
float complex*)o),
313 creal(*(
float complex*)
p), cimag(*(
float complex*)
p));
316 if(*(
int*)o != *(
int*)
p)
318 const char *old_str = N_(
"unknown"), *new_str = N_(
"unknown");
321 if(
i->value == *(
int*)o)
323 old_str =
i->description;
324 if(!*old_str) old_str =
i->name;
326 if(
i->value == *(
int*)
p)
328 new_str =
i->description;
329 if(!*new_str) new_str =
i->name;
333 return g_strdup_printf(
"%s\t%s\t\u2192\t%s",
d, _(old_str), _(new_str));
337 if(*(gboolean*)o != *(gboolean*)
p)
339 char *old_str = *(gboolean*)o ?
"on" :
"off";
340 char *new_str = *(gboolean*)
p ?
"on" :
"off";
341 return g_strdup_printf(
"%s\t%s\t\u2192\t%s",
d, _(old_str), _(new_str));
350 fprintf(stderr,
"unsupported introspection type \"%s\" encountered in _lib_history_change_text (field %s)\n", field->
header.
type_name, field->
header.
field_name);
362 find_old = g_list_previous(find_old))
365 if(hprev == hitem)
continue;
366 if(hprev->module == hitem->module)
return hprev;
373#define add_blend_history_change(field, format, label) \
374 if((hitem->blend_params->field) != (old_blend->field)) \
376 gchar *full_format = g_strconcat("%s\t", format, "\t\u2192\t", format, NULL); \
377 change_parts[num_parts++] \
378 = g_strdup_printf(full_format, label, (old_blend->field), (hitem->blend_params->field)); \
379 dt_free(full_format); \
380 full_format = NULL; \
383#define add_blend_history_change_enum(field, label, list) \
384 if((hitem->blend_params->field) != (old_blend->field)) \
386 const char *old_str = NULL, *new_str = NULL; \
387 for(const dt_develop_name_value_t *i = list; *i->name; i++) \
389 if(i->value == (old_blend->field)) old_str = i->name; \
390 if(i->value == (hitem->blend_params->field)) new_str = i->name; \
393 change_parts[num_parts++] \
394 = (!old_str || !new_str) \
395 ? g_strdup_printf("%s\t%d\t\u2192\t%d", label, old_blend->field, hitem->blend_params->field) \
396 : g_strdup_printf("%s\t%s\t\u2192\t%s", label, _(g_dpgettext2(NULL, "blendmode", old_str)), \
397 _(g_dpgettext2(NULL, "blendmode", new_str))); \
400#define add_history_change(field, format, label) \
401 if((hitem->field) != (hprev->field)) \
403 gchar *full_format = g_strconcat("%s\t", format, "\t\u2192\t", format, NULL); \
404 change_parts[num_parts++] = g_strdup_printf(full_format, label, (hprev->field), (hitem->field)); \
405 dt_free(full_format); \
406 full_format = NULL; \
409#define add_history_change_string(field, label) \
410 if(strcmp(hitem->field, hprev->field)) \
412 change_parts[num_parts++] \
413 = g_strdup_printf("%s\t\"%s\"\t\u2192\t\"%s\"", label, (hprev->field), (hitem->field)); \
416#define add_history_change_boolean(field, label) \
417 if((hitem->field) != (hprev->field)) \
419 change_parts[num_parts++] = g_strdup_printf("%s\t%s\t\u2192\t%s", label, (hprev->field) ? _("on") : _("off"), \
420 (hitem->field) ? _("on") : _("off")); \
426 if(
IS_NULL_PTR(hitem) || !hitem->module)
return NULL;
430 = (hprev == hitem ||
IS_NULL_PTR(hprev)) ? hitem->module->default_params : hprev->module->
params;
432 = (hprev == hitem ||
IS_NULL_PTR(hprev)) ? hitem->module->default_blendop_params : hprev->module->
blend_params;
437 const gboolean enabled_by_default = (hitem->module->force_enable && hitem->module->force_enable(hitem->module, hitem->
enabled))
438 || hitem->module->default_enabled;
443 if(enabled_by_default)
444 change_parts[num_parts++] = g_strdup_printf(_(
"mandatory module created automatically"));
446 change_parts[num_parts++] = g_strdup_printf(_(
"module created per user request"));
458 if(hitem->module->have_introspection)
461 hitem->
params, old_params);
462 if(!
IS_NULL_PTR(introspection_change)) change_parts[num_parts++] = introspection_change;
486 change_parts[num_parts++] = old_blend->
mask_id == 0
487 ? g_strdup_printf(_(
"a drawn mask was added"))
489 ? g_strdup_printf(_(
"the drawn mask was removed"))
490 : g_strdup_printf(_(
"the drawn mask was changed"));
494 for(
int in_out = 1; in_out >= 0; in_out--)
496 gboolean first =
TRUE;
504 const int oactive = old_blend->
blendif & (1 <<
ch);
507 const int opolarity = old_blend->
blendif & (1 << (
ch + 16));
516 if((oactive || nactive) && (memcmp(of, nf,
sizeof(
float) * 4) || opolarity != npolarity))
520 change_parts[num_parts++] = g_strdup(in_out ? _(
"parametric output mask:") : _(
"parametric input mask:"));
524 for(
int k = 0;
k < 4;
k++)
526 b->scale_print(of[
k], oboost, s[
k][0],
sizeof(s[
k][0]));
527 b->scale_print(nf[
k], nboost, s[
k][1],
sizeof(s[
k][1]));
530 char *opol = !oactive ?
"" : (opolarity ?
"(-)" :
"(+)");
531 char *npol = !nactive ?
"" : (npolarity ?
"(-)" :
"(+)");
533 change_parts[num_parts++] = g_strdup_printf(
"%s\t%s| %s- %s| %s%s\t\u2192\t%s| %s- %s| %s%s", _(b->name),
534 s[0][0], s[1][0], s[2][0], s[3][0], opol,
535 s[0][1], s[1][1], s[2][1], s[3][1], npol);
541 gchar *tooltip_text = g_strjoinv(
"\n", change_parts);
542 g_strfreev(change_parts);
550 const gchar *tooltip_text = g_object_get_data(G_OBJECT(widget),
"tooltip-text");
553 gtk_tooltip_set_text(
tooltip, tooltip_text);
559 GtkTooltip *
tooltip, gpointer user_data)
563 GtkTreeModel *
model = GTK_TREE_MODEL(
d->history_store);
565 GtkTreePath *path = NULL;
569 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
570 if(!gtk_tree_selection_get_selected(selection, &
model, &iter))
return FALSE;
574 if(!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
x, y, &path, NULL, NULL, NULL))
return FALSE;
575 if(!gtk_tree_model_get_iter(
model, &iter, path))
577 gtk_tree_path_free(path);
582 gchar *tooltip_text = NULL;
584 g_object_set_data_full(G_OBJECT(widget),
"tooltip-text", tooltip_text, g_free);
587 if(path) gtk_tree_path_free(path);
592 GtkTreeModel *
model, GtkTreeIter *iter, gpointer data)
594 gboolean enabled =
TRUE;
597 g_object_set(G_OBJECT(renderer),
"foreground-set",
FALSE, NULL);
599 g_object_set(G_OBJECT(renderer),
"foreground-set",
TRUE,
"foreground",
"#888", NULL);
633 if(history_end <= 0)
return;
650 gtk_list_store_append(
d->history_store, &iter);
659 const gchar *hint = _(
"Shift+click: show module without changing history");
662 if(tooltip_text && tooltip_text[0])
664 gchar *tooltip_with_hint = g_strconcat(tooltip_text,
"\n\n", hint, NULL);
666 return tooltip_with_hint;
670 return g_strdup(hint);
675 const gboolean enabled = (hitem->
enabled || (strcmp(hitem->
op_name,
"mask_manager") == 0));
680 label = g_strdup(hitem->
op_name);
685 g_snprintf(number,
sizeof(number),
"%2d", history_end);
690 gtk_list_store_insert(
d->history_store, &iter, 0);
695 tooltip_text ? tooltip_text :
"", -1);
703 const char *icon_name =
_history_icon_name(enabled, hitem->module->default_enabled, hitem->module->hide_enable_button,
706 const gboolean enabled_by_default
707 = ((hitem->module->force_enable && hitem->module->force_enable(hitem->module, hitem->
enabled))
708 || hitem->module->default_enabled);
714 label = g_strdup_printf(
"%s%s", clean_name, star);
716 label = g_strdup_printf(
"%s %s%s", clean_name, hitem->
multi_name, star);
720 g_snprintf(number,
sizeof(number),
"%2d", history_end);
725 gtk_list_store_insert(
d->history_store, &iter, 0);
737 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
d->history_view));
738 GtkTreeModel *
model = GTK_TREE_MODEL(
d->history_store);
741 for(gboolean valid = gtk_tree_model_get_iter_first(
model, &iter); valid; valid = gtk_tree_model_iter_next(
model, &iter))
743 int row_history_end = 0;
745 if(row_history_end == history_end)
747 gtk_tree_selection_select_iter(selection, &iter);
758 d->selection_reset =
TRUE;
759 gtk_list_store_clear(
d->history_store);
776 d->selection_reset =
FALSE;
785 GtkTreeModel *
model = NULL;
787 if(!gtk_tree_selection_get_selected(selection, &
model, &iter))
return;
801 GtkTreePath *path = NULL;
802 if(gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), (gint)e->x, (gint)e->y, &path, NULL, NULL, NULL))
806 GtkTreeModel *
model = GTK_TREE_MODEL(
d->history_store);
808 if(gtk_tree_model_get_iter(
model, &iter, path))
814 gtk_tree_path_free(path);
const char ** description(struct dt_iop_module_t *self)
const dt_develop_name_value_t dt_develop_invert_mask_names[]
const dt_develop_name_value_t dt_develop_mask_mode_names[]
@ DEVELOP_COMBINE_MASKS_POS
const dt_develop_name_value_t dt_develop_blend_colorspace_names[]
dt_develop_blendif_channels_t
const dt_develop_name_value_t dt_develop_blend_mode_flag_names[]
@ DEVELOP_BLEND_MODE_MASK
const dt_develop_name_value_t dt_develop_combine_masks_names[]
const dt_develop_name_value_t dt_develop_blend_mode_names[]
const dt_develop_name_value_t dt_develop_feathering_guide_names[]
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
#define dt_gui_freeze_begin()
#define dt_gui_freeze_end()
#define DT_MODULE(MODVER)
void void void gboolean dt_gui_widgets_suppressed(void)
static gchar * delete_underscore(const char *s)
static gboolean dt_modifier_is(const GdkModifierType state, const GdkModifierType desired_modifier_mask)
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
void dt_dev_pop_history_items_ext(dt_develop_t *dev)
Apply history items to module params up to dev->history_end.
void dt_dev_write_history(dt_develop_t *dev, gboolean async)
Thread-safe wrapper around dt_dev_write_history_ext() for dev->image_storage.id.
void dt_dev_history_pixelpipe_update(dt_develop_t *dev, gboolean rebuild)
Rebuild or resync pixelpipes after backend history changes.
void dt_dev_history_gui_update(dt_develop_t *dev)
Apply history-loaded params to module GUIs.
void dt_dev_set_history_end_ext(struct dt_develop_t *dev, const uint32_t index)
Set the history end index (GUI perspective).
int32_t dt_dev_get_history_end_ext(struct dt_develop_t *dev)
Get the current history end index (GUI perspective).
int dt_dev_get_thumbnail_size(dt_develop_t *dev)
void dt_dev_undo_start_record(dt_develop_t *dev)
void dt_dev_undo_end_record(dt_develop_t *dev)
static int dt_pthread_rwlock_unlock(dt_pthread_rwlock_t *rwlock)
static int dt_pthread_rwlock_rdlock(dt_pthread_rwlock_t *rwlock)
static int dt_pthread_rwlock_wrlock(dt_pthread_rwlock_t *rwlock)
GtkWidget * dt_ui_scroll_wrap(GtkWidget *w, gint min_size, char *config_str, dt_ui_resize_mode_t mode)
Wrap a scrollable content widget in a recessed, vertically resizable scrolled window.
#define DT_GUI_BOX_SPACING
gboolean delete_history_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
void dt_iop_request_focus(dt_iop_module_t *module)
void dt_iop_gui_set_expanded(dt_iop_module_t *module, gboolean expanded, gboolean collapse_others)
@ IOP_FLAGS_SUPPORTS_BLENDING
@ DT_INTROSPECTION_TYPE_BOOL
@ DT_INTROSPECTION_TYPE_ENUM
@ DT_INTROSPECTION_TYPE_OPAQUE
@ DT_INTROSPECTION_TYPE_ARRAY
@ DT_INTROSPECTION_TYPE_CHAR
@ DT_INTROSPECTION_TYPE_FLOAT
@ DT_INTROSPECTION_TYPE_UNION
@ DT_INTROSPECTION_TYPE_UINT
@ DT_INTROSPECTION_TYPE_USHORT
@ DT_INTROSPECTION_TYPE_INT8
@ DT_INTROSPECTION_TYPE_STRUCT
@ DT_INTROSPECTION_TYPE_FLOATCOMPLEX
@ DT_INTROSPECTION_TYPE_INT
void gui_reset(dt_lib_module_t *self)
#define add_blend_history_change_enum(field, label, list)
#define add_history_change_boolean(field, label)
static const char * _history_icon_name(const gboolean enabled, const gboolean default_enabled, const gboolean always_on, const gboolean deprecated)
static gboolean _lib_history_view_query_tooltip(GtkWidget *widget, gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip, gpointer user_data)
static void _lib_history_change_callback(gpointer instance, gpointer user_data)
#define add_history_change(field, format, label)
static gboolean _changes_tooltip_callback(GtkWidget *widget, gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip)
static gchar * _lib_history_change_text(dt_introspection_field_t *field, const char *d, gpointer params, gpointer oldpar)
void gui_cleanup(dt_lib_module_t *self)
static void _history_show_module_for_end(const int history_end)
static void _history_store_add_original(dt_lib_history_t *d)
@ DT_HISTORY_VIEW_COL_HISTORY_END
@ DT_HISTORY_VIEW_COL_TOOLTIP
@ DT_HISTORY_VIEW_COL_COUNT
@ DT_HISTORY_VIEW_COL_LABEL
@ DT_HISTORY_VIEW_COL_ICON_NAME
@ DT_HISTORY_VIEW_COL_ENABLED
@ DT_HISTORY_VIEW_COL_NUMBER
static void _history_select_row_for_end(dt_lib_history_t *d, const int history_end)
uint32_t container(dt_lib_module_t *self)
static void _lib_history_view_cell_set_foreground(GtkTreeViewColumn *column, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
static gchar * _create_tooltip_text(const dt_dev_history_item_t *hitem)
void gui_init(dt_lib_module_t *self)
const char ** views(dt_lib_module_t *self)
static void _history_apply_history_end(const int history_end)
static void _history_store_prepend_item(dt_lib_history_t *d, const dt_dev_history_item_t *hitem, const int history_end)
#define add_blend_history_change(field, format, label)
#define add_history_change_string(field, label)
static const dt_dev_history_item_t * _find_previous_history_step(const dt_dev_history_item_t *hitem)
static gchar * _history_tooltip_with_hint(const dt_dev_history_item_t *hitem)
static void _lib_history_view_selection_changed(GtkTreeSelection *selection, gpointer user_data)
static gboolean _lib_history_view_button_press_callback(GtkWidget *widget, GdkEventButton *e, gpointer user_data)
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
@ DT_SIGNAL_DEVELOP_HISTORY_CHANGE
This signal is raised when develop history is changed no param, no returned value.
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
struct _GtkWidget GtkWidget
struct dt_gui_gtk_t * gui
struct dt_control_signal_t * signals
struct dt_develop_t * develop
struct dt_develop_blend_params_t * blend_params
struct dt_iop_module_t *gboolean enabled
float blendif_parameters[4 *DEVELOP_BLENDIF_SIZE]
float blendif_boost_factors[DEVELOP_BLENDIF_SIZE]
dt_pthread_rwlock_t history_mutex
union dt_introspection_field_t * field
dt_introspection_type_t type
dt_introspection_type_enum_tuple_t * values
union dt_introspection_field_t ** fields
const dt_iop_gui_blendif_channel_t * channel
GtkListStore * history_store
dt_introspection_type_header_t header
dt_introspection_type_array_t Array
dt_introspection_type_enum_t Enum
dt_introspection_type_struct_t Struct
@ DT_UI_CONTAINER_PANEL_LEFT_CENTER