69#ifdef GDK_WINDOWING_QUARTZ
102 GtkTooltip *
tooltip, gpointer user_data);
104 GtkTreeModel *
model, GtkTreeIter *iter, gpointer data);
108 return _(
"History of changes");
113 static const char *
v[] = {
"darkroom", NULL};
131 self->
data = (
void *)
d;
133 d->selection_reset =
FALSE;
136 gtk_widget_set_name(self->
widget,
"history-ui");
146 d->history_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(
d->history_store));
147 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(
d->history_view),
FALSE);
148 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(
d->history_view),
FALSE);
150 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
d->history_view));
151 gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
155 gtk_widget_set_has_tooltip(
d->history_view,
TRUE);
158 GtkCellRenderer *renderer_num = gtk_cell_renderer_text_new();
159 g_object_set(G_OBJECT(renderer_num),
"xalign", 1.0,
"family",
"monospace", NULL);
160 GtkTreeViewColumn *col_num = gtk_tree_view_column_new_with_attributes(
"n", renderer_num,
164 gtk_tree_view_column_set_sizing(col_num, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
165 gtk_tree_view_append_column(GTK_TREE_VIEW(
d->history_view), col_num);
167 GtkCellRenderer *renderer_label = gtk_cell_renderer_text_new();
168 g_object_set(G_OBJECT(renderer_label),
"ellipsize", PANGO_ELLIPSIZE_END, NULL);
169 GtkTreeViewColumn *col_label = gtk_tree_view_column_new_with_attributes(
"label", renderer_label,
173 gtk_tree_view_column_set_expand(col_label,
TRUE);
174 gtk_tree_view_append_column(GTK_TREE_VIEW(
d->history_view), col_label);
176 GtkCellRenderer *renderer_icon = gtk_cell_renderer_pixbuf_new();
177 GtkTreeViewColumn *col_icon = gtk_tree_view_column_new_with_attributes(
"status", renderer_icon,
180 gtk_tree_view_column_set_sizing(col_icon, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
181 gtk_tree_view_append_column(GTK_TREE_VIEW(
d->history_view), col_icon);
188 gtk_widget_show_all(self->
widget);
200 if(
d &&
d->history_store) g_object_unref(
d->history_store);
204static const char *
_history_icon_name(
const gboolean enabled,
const gboolean default_enabled,
const gboolean always_on,
205 const gboolean deprecated)
207 if(always_on)
return "emblem-readonly";
208 if(deprecated)
return "dialog-warning";
209 if(default_enabled)
return enabled ?
"emblem-ok" :
"process-stop";
210 return enabled ?
"emblem-ok" :
"process-stop";
223 gchar **change_parts = g_malloc0_n(field->
Struct.
entries + 1,
sizeof(
char*));
245 gchar *struct_text = num_parts ? g_strjoinv(
"\n", change_parts) : NULL;
246 g_strfreev(change_parts);
254 const gboolean is_valid =
255 g_utf8_validate((
char *)o, -1, NULL)
256 && g_utf8_validate((
char *)
p, -1, NULL);
258 if(is_valid && strncmp((
char*)o, (
char*)
p, field->
Array.
count))
259 return g_strdup_printf(
"%s\t\"%s\"\t\u2192\t\"%s\"",
d, (
char*)o, (
char*)
p);
263 const int max_elements = 4;
264 gchar **change_parts = g_malloc0_n(max_elements + 1,
sizeof(
char*));
273 if(element_text && ++num_parts <= max_elements)
274 change_parts[num_parts - 1] = element_text;
279 gchar *array_text = NULL;
280 if(num_parts > max_elements)
281 array_text = g_strdup_printf(
"%s\t%d changes",
d, num_parts);
282 else if(num_parts > 0)
283 array_text = g_strjoinv(
"\n", change_parts);
285 g_strfreev(change_parts);
291 if(*(
float*)o != *(
float*)
p && (isfinite(*(
float*)o) || isfinite(*(
float*)
p)))
292 return g_strdup_printf(
"%s\t%.4f\t\u2192\t%.4f",
d, *(
float*)o, *(
float*)
p);
295 if(*(
int*)o != *(
int*)
p)
296 return g_strdup_printf(
"%s\t%d\t\u2192\t%d",
d, *(
int*)o, *(
int*)
p);
299 if(*(
unsigned int*)o != *(
unsigned int*)
p)
300 return g_strdup_printf(
"%s\t%u\t\u2192\t%u",
d, *(
unsigned int*)o, *(
unsigned int*)
p);
303 if(*(
unsigned short int*)o != *(
unsigned short int*)
p)
304 return g_strdup_printf(
"%s\t%hu\t\u2192\t%hu",
d, *(
unsigned short int*)o, *(
unsigned short int*)
p);
307 if(*(uint8_t*)o != *(uint8_t*)
p)
308 return g_strdup_printf(
"%s\t%d\t\u2192\t%d",
d, *(uint8_t*)o, *(uint8_t*)
p);
311 if(*(
char*)o != *(
char*)
p)
312 return g_strdup_printf(
"%s\t'%c'\t\u2192\t'%c'",
d, *(
char *)o, *(
char *)
p);
315 if(*(
float complex*)o != *(
float complex*)
p)
316 return g_strdup_printf(
"%s\t%.4f + %.4fi\t\u2192\t%.4f + %.4fi",
d,
317 creal(*(
float complex*)o), cimag(*(
float complex*)o),
318 creal(*(
float complex*)
p), cimag(*(
float complex*)
p));
321 if(*(
int*)o != *(
int*)
p)
323 const char *old_str = N_(
"unknown"), *new_str = N_(
"unknown");
326 if(
i->value == *(
int*)o)
328 old_str =
i->description;
329 if(!*old_str) old_str =
i->name;
331 if(
i->value == *(
int*)
p)
333 new_str =
i->description;
334 if(!*new_str) new_str =
i->name;
338 return g_strdup_printf(
"%s\t%s\t\u2192\t%s",
d, _(old_str), _(new_str));
342 if(*(gboolean*)o != *(gboolean*)
p)
344 char *old_str = *(gboolean*)o ?
"on" :
"off";
345 char *new_str = *(gboolean*)
p ?
"on" :
"off";
346 return g_strdup_printf(
"%s\t%s\t\u2192\t%s",
d, _(old_str), _(new_str));
355 fprintf(stderr,
"unsupported introspection type \"%s\" encountered in _lib_history_change_text (field %s)\n", field->
header.
type_name, field->
header.
field_name);
367 find_old = g_list_previous(find_old))
370 if(hprev == hitem)
continue;
371 if(hprev->module == hitem->module)
return hprev;
378#define add_blend_history_change(field, format, label) \
379 if((hitem->blend_params->field) != (old_blend->field)) \
381 gchar *full_format = g_strconcat("%s\t", format, "\t\u2192\t", format, NULL); \
382 change_parts[num_parts++] \
383 = g_strdup_printf(full_format, label, (old_blend->field), (hitem->blend_params->field)); \
384 dt_free(full_format); \
385 full_format = NULL; \
388#define add_blend_history_change_enum(field, label, list) \
389 if((hitem->blend_params->field) != (old_blend->field)) \
391 const char *old_str = NULL, *new_str = NULL; \
392 for(const dt_develop_name_value_t *i = list; *i->name; i++) \
394 if(i->value == (old_blend->field)) old_str = i->name; \
395 if(i->value == (hitem->blend_params->field)) new_str = i->name; \
398 change_parts[num_parts++] \
399 = (!old_str || !new_str) \
400 ? g_strdup_printf("%s\t%d\t\u2192\t%d", label, old_blend->field, hitem->blend_params->field) \
401 : g_strdup_printf("%s\t%s\t\u2192\t%s", label, _(g_dpgettext2(NULL, "blendmode", old_str)), \
402 _(g_dpgettext2(NULL, "blendmode", new_str))); \
405#define add_history_change(field, format, label) \
406 if((hitem->field) != (hprev->field)) \
408 gchar *full_format = g_strconcat("%s\t", format, "\t\u2192\t", format, NULL); \
409 change_parts[num_parts++] = g_strdup_printf(full_format, label, (hprev->field), (hitem->field)); \
410 dt_free(full_format); \
411 full_format = NULL; \
414#define add_history_change_string(field, label) \
415 if(strcmp(hitem->field, hprev->field)) \
417 change_parts[num_parts++] \
418 = g_strdup_printf("%s\t\"%s\"\t\u2192\t\"%s\"", label, (hprev->field), (hitem->field)); \
421#define add_history_change_boolean(field, label) \
422 if((hitem->field) != (hprev->field)) \
424 change_parts[num_parts++] = g_strdup_printf("%s\t%s\t\u2192\t%s", label, (hprev->field) ? _("on") : _("off"), \
425 (hitem->field) ? _("on") : _("off")); \
431 if(
IS_NULL_PTR(hitem) || !hitem->module)
return NULL;
435 = (hprev == hitem ||
IS_NULL_PTR(hprev)) ? hitem->module->default_params : hprev->module->
params;
437 = (hprev == hitem ||
IS_NULL_PTR(hprev)) ? hitem->module->default_blendop_params : hprev->module->
blend_params;
442 const gboolean enabled_by_default = (hitem->module->force_enable && hitem->module->force_enable(hitem->module, hitem->
enabled))
443 || hitem->module->default_enabled;
448 if(enabled_by_default)
449 change_parts[num_parts++] = g_strdup_printf(_(
"mandatory module created automatically"));
451 change_parts[num_parts++] = g_strdup_printf(_(
"module created per user request"));
463 if(hitem->module->have_introspection)
466 hitem->
params, old_params);
467 if(!
IS_NULL_PTR(introspection_change)) change_parts[num_parts++] = introspection_change;
491 change_parts[num_parts++] = old_blend->
mask_id == 0
492 ? g_strdup_printf(_(
"a drawn mask was added"))
494 ? g_strdup_printf(_(
"the drawn mask was removed"))
495 : g_strdup_printf(_(
"the drawn mask was changed"));
499 for(
int in_out = 1; in_out >= 0; in_out--)
501 gboolean first =
TRUE;
509 const int oactive = old_blend->
blendif & (1 <<
ch);
512 const int opolarity = old_blend->
blendif & (1 << (
ch + 16));
521 if((oactive || nactive) && (memcmp(of, nf,
sizeof(
float) * 4) || opolarity != npolarity))
525 change_parts[num_parts++] = g_strdup(in_out ? _(
"parametric output mask:") : _(
"parametric input mask:"));
529 for(
int k = 0;
k < 4;
k++)
531 b->scale_print(of[
k], oboost, s[
k][0],
sizeof(s[
k][0]));
532 b->scale_print(nf[
k], nboost, s[
k][1],
sizeof(s[
k][1]));
535 char *opol = !oactive ?
"" : (opolarity ?
"(-)" :
"(+)");
536 char *npol = !nactive ?
"" : (npolarity ?
"(-)" :
"(+)");
538 change_parts[num_parts++] = g_strdup_printf(
"%s\t%s| %s- %s| %s%s\t\u2192\t%s| %s- %s| %s%s", _(b->name),
539 s[0][0], s[1][0], s[2][0], s[3][0], opol,
540 s[0][1], s[1][1], s[2][1], s[3][1], npol);
546 gchar *tooltip_text = g_strjoinv(
"\n", change_parts);
547 g_strfreev(change_parts);
555 const gchar *tooltip_text = g_object_get_data(G_OBJECT(widget),
"tooltip-text");
558 gtk_tooltip_set_text(
tooltip, tooltip_text);
564 GtkTooltip *
tooltip, gpointer user_data)
568 GtkTreeModel *
model = GTK_TREE_MODEL(
d->history_store);
570 GtkTreePath *path = NULL;
574 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
575 if(!gtk_tree_selection_get_selected(selection, &
model, &iter))
return FALSE;
579 if(!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
x, y, &path, NULL, NULL, NULL))
return FALSE;
580 if(!gtk_tree_model_get_iter(
model, &iter, path))
582 gtk_tree_path_free(path);
587 gchar *tooltip_text = NULL;
589 g_object_set_data_full(G_OBJECT(widget),
"tooltip-text", tooltip_text, g_free);
592 if(path) gtk_tree_path_free(path);
597 GtkTreeModel *
model, GtkTreeIter *iter, gpointer data)
599 gboolean enabled =
TRUE;
602 g_object_set(G_OBJECT(renderer),
"foreground-set",
FALSE, NULL);
604 g_object_set(G_OBJECT(renderer),
"foreground-set",
TRUE,
"foreground",
"#888", NULL);
638 if(history_end <= 0)
return;
655 gtk_list_store_append(
d->history_store, &iter);
664 const gchar *hint = _(
"Shift+click: show module without changing history");
667 if(tooltip_text && tooltip_text[0])
669 gchar *tooltip_with_hint = g_strconcat(tooltip_text,
"\n\n", hint, NULL);
671 return tooltip_with_hint;
675 return g_strdup(hint);
680 const gboolean enabled = (hitem->
enabled || (strcmp(hitem->
op_name,
"mask_manager") == 0));
685 label = g_strdup(hitem->
op_name);
690 g_snprintf(number,
sizeof(number),
"%2d", history_end);
695 gtk_list_store_insert(
d->history_store, &iter, 0);
700 tooltip_text ? tooltip_text :
"", -1);
708 const char *icon_name =
_history_icon_name(enabled, hitem->module->default_enabled, hitem->module->hide_enable_button,
711 const gboolean enabled_by_default
712 = ((hitem->module->force_enable && hitem->module->force_enable(hitem->module, hitem->
enabled))
713 || hitem->module->default_enabled);
719 label = g_strdup_printf(
"%s%s", clean_name, star);
721 label = g_strdup_printf(
"%s %s%s", clean_name, hitem->
multi_name, star);
725 g_snprintf(number,
sizeof(number),
"%2d", history_end);
730 gtk_list_store_insert(
d->history_store, &iter, 0);
742 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
d->history_view));
743 GtkTreeModel *
model = GTK_TREE_MODEL(
d->history_store);
746 for(gboolean valid = gtk_tree_model_get_iter_first(
model, &iter); valid; valid = gtk_tree_model_iter_next(
model, &iter))
748 int row_history_end = 0;
750 if(row_history_end == history_end)
752 gtk_tree_selection_select_iter(selection, &iter);
763 d->selection_reset =
TRUE;
764 gtk_list_store_clear(
d->history_store);
781 d->selection_reset =
FALSE;
790 GtkTreeModel *
model = NULL;
792 if(!gtk_tree_selection_get_selected(selection, &
model, &iter))
return;
806 GtkTreePath *path = NULL;
807 if(gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), (gint)e->x, (gint)e->y, &path, NULL, NULL, NULL))
811 GtkTreeModel *
model = GTK_TREE_MODEL(
d->history_store);
813 if(gtk_tree_model_get_iter(
model, &iter, path))
819 gtk_tree_path_free(path);
832 gint res = GTK_RESPONSE_YES;
838 GtkWidget *dialog = gtk_message_dialog_new(
839 GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
840 _(
"do you really want to clear history of current image?"));
841#ifdef GDK_WINDOWING_QUARTZ
845 gtk_window_set_title(GTK_WINDOW(dialog), _(
"delete image's history?"));
846 res = gtk_dialog_run(GTK_DIALOG(dialog));
847 gtk_widget_destroy(dialog);
850 if(res == GTK_RESPONSE_YES)
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
void dt_history_delete_on_image_ext(int32_t imgid, gboolean undo)
int dt_conf_get_bool(const char *name)
#define DT_MODULE(MODVER)
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).
#define dt_dev_pixelpipe_resync_history_all(dev)
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)
#define dt_pthread_rwlock_wrlock
#define dt_pthread_rwlock_unlock
#define dt_pthread_rwlock_rdlock
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.
GtkWidget * dt_ui_main_window(dt_ui_t *ui)
get the main window widget
#define DT_GUI_BOX_SPACING
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
void dt_osx_disallow_fullscreen(GtkWidget *widget)
#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