Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
hist_dialog.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2012-2013, 2015, 2019-2022 Pascal Obry.
4 Copyright (C) 2013-2016 Tobias Ellinghaus.
5 Copyright (C) 2014, 2016 Roman Lebedev.
6 Copyright (C) 2015 Patrick Plenefisch.
7 Copyright (C) 2017 Chris AtLee.
8 Copyright (C) 2017, 2022 luzpaz.
9 Copyright (C) 2017 parafin.
10 Copyright (C) 2019, 2023, 2025 Aurélien PIERRE.
11 Copyright (C) 2019 Hanno Schwalm.
12 Copyright (C) 2020 Aldric Renaudin.
13 Copyright (C) 2020 Hubert Kowalski.
14 Copyright (C) 2021 Chris Elston.
15 Copyright (C) 2021 domosbg.
16 Copyright (C) 2021 Ralf Brown.
17 Copyright (C) 2022 Martin Bařinka.
18
19 darktable is free software: you can redistribute it and/or modify
20 it under the terms of the GNU General Public License as published by
21 the Free Software Foundation, either version 3 of the License, or
22 (at your option) any later version.
23
24 darktable is distributed in the hope that it will be useful,
25 but WITHOUT ANY WARRANTY; without even the implied warranty of
26 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 GNU General Public License for more details.
28
29 You should have received a copy of the GNU General Public License
30 along with darktable. If not, see <http://www.gnu.org/licenses/>.
31*/
32#ifdef HAVE_CONFIG_H
33#include "config.h"
34#endif
35#include "system/macros.h"
36#include "system/mem_alloc.h"
37#include "history/history.h"
39#include "develop/imageop.h"
40#include "develop/dev_history.h" // dt_history_module_skip_copy, previously reached through gui/draw.h
41#include "gui/application.h"
42#include "gui/hist_dialog.h"
43#include "widgets/draw.h"
44#include "widgets/dialog.h"
45#include "widgets/label.h"
47#ifdef GDK_WINDOWING_QUARTZ
48#include "osx/osx.h"
49#endif
50
59
61{
62 GList *result = NULL;
63
64 /* run through all items and add active ones to result */
65 GtkTreeIter iter;
66 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(d->items));
67 if(gtk_tree_model_get_iter_first(model, &iter))
68 {
69 do
70 {
71 gboolean active = FALSE;
72 gint num = 0;
73 gtk_tree_model_get(model, &iter, DT_HIST_ITEMS_COL_ENABLED, &active, DT_HIST_ITEMS_COL_NUM, &num, -1);
74 if(active && num >= 0)
75 result = g_list_prepend(result, GINT_TO_POINTER(num));
76
77 } while(gtk_tree_model_iter_next(model, &iter));
78 }
79 return g_list_reverse(result); // list was built in reverse order, so un-reverse it
80}
81
82static void _gui_hist_set_items(dt_history_copy_item_t *d, gboolean active)
83{
84 /* run through all items and set active status */
85 GtkTreeIter iter;
86 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(d->items));
87 if(gtk_tree_model_get_iter_first(model, &iter))
88 {
89 do
90 {
91 gtk_list_store_set(GTK_LIST_STORE(model), &iter, DT_HIST_ITEMS_COL_ENABLED, active, -1);
92 } while(gtk_tree_model_iter_next(model, &iter));
93 }
94}
95
96static void _gui_hist_copy_response(GtkDialog *dialog, gint response_id, dt_history_copy_item_t *g)
97{
98 switch(response_id)
99 {
100 case GTK_RESPONSE_CANCEL:
101 break;
102
103 case GTK_RESPONSE_YES:
105 break;
106
107 case GTK_RESPONSE_NONE:
109 break;
110
111 case GTK_RESPONSE_OK:
112 g->selops = _gui_hist_get_active_items(g);
113 break;
114 }
115}
116
117static void _gui_hist_item_toggled(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
118{
120
121 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(d->items));
122 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
123 GtkTreeIter iter;
124 gboolean toggle_item;
125
126 gtk_tree_model_get_iter(model, &iter, path);
127 gtk_tree_model_get(model, &iter, DT_HIST_ITEMS_COL_ENABLED, &toggle_item, -1);
128
129 toggle_item = (toggle_item == TRUE) ? FALSE : TRUE;
130
131 gtk_list_store_set(GTK_LIST_STORE(model), &iter, DT_HIST_ITEMS_COL_ENABLED, toggle_item, -1);
132 gtk_tree_path_free(path);
133}
134
135static gboolean _gui_is_set(GList *selops, unsigned int num)
136{
137 /* nothing to filter */
138 if(IS_NULL_PTR(selops)) return TRUE;
139
140 for(GList *l = selops; l; l = g_list_next(l))
141 {
142 if(l->data)
143 {
144 unsigned int lnum = GPOINTER_TO_UINT(l->data);
145 if(lnum == num) return TRUE;
146 }
147 }
148 return FALSE;
149}
150
151void
152tree_on_row_activated(GtkTreeView *treeview,
153 GtkTreePath *path,
154 GtkTreeViewColumn *col,
155 gpointer userdata)
156{
157 GtkDialog *dialog = GTK_DIALOG(userdata);
158 GtkTreeModel *model = gtk_tree_view_get_model(treeview);
159 GtkTreeIter iter;
160
161 // unselect all items
162
163 if(gtk_tree_model_get_iter_first(model, &iter))
164 {
165 do
166 {
167 gtk_list_store_set(GTK_LIST_STORE(model), &iter, DT_HIST_ITEMS_COL_ENABLED, FALSE, -1);
168
169 } while(gtk_tree_model_iter_next(model, &iter));
170 }
171
172 // select now the one that got double-clicked
173
174 if (gtk_tree_model_get_iter(model, &iter, path))
175 {
176 gtk_list_store_set(GTK_LIST_STORE(model), &iter, DT_HIST_ITEMS_COL_ENABLED, TRUE, -1);
177 // and finally close the dialog
178 g_signal_emit_by_name(dialog, "response", GTK_RESPONSE_OK, NULL);
179 }
180}
181
182int dt_gui_hist_dialog_new(dt_history_copy_item_t *d, int32_t imgid, gboolean iscopy)
183{
184 int res;
186
187 GtkDialog *dialog = GTK_DIALOG(gtk_dialog_new_with_buttons(
188 iscopy ? _("Select development parts to copy") : _("Select development parts to paste"),
189 GTK_WINDOW(window), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
190 _("_cancel"), GTK_RESPONSE_CANCEL,
191 _("select _all"), GTK_RESPONSE_YES,
192 _("select _none"), GTK_RESPONSE_NONE,
193 _("_ok"), GTK_RESPONSE_OK,
194 NULL));
195
196 gtk_dialog_set_default_response(dialog, GTK_RESPONSE_OK);
197
198#ifdef GDK_WINDOWING_QUARTZ
199 dt_osx_disallow_fullscreen(GTK_WIDGET(dialog));
200#endif
201
202 GtkContainer *content_area = GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog)));
203
204 GtkWidget *scroll = gtk_scrolled_window_new(NULL, NULL);
205 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
206 gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scroll), DT_PIXEL_APPLY_DPI(450));
207 dt_gui_add_class(scroll, "dt_recessed_scroll");
208
209 /* create the list of items */
210 d->items = GTK_TREE_VIEW(gtk_tree_view_new());
211 gtk_container_add(GTK_CONTAINER(scroll), GTK_WIDGET(d->items));
212 gtk_box_pack_start(GTK_BOX(content_area), GTK_WIDGET(scroll), TRUE, TRUE, 0);
213
214 GtkListStore *liststore
215 = gtk_list_store_new(DT_HIST_ITEMS_NUM_COLS,
216 G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_UINT);
217
218 /* enabled */
219 GtkCellRenderer *renderer = gtk_cell_renderer_toggle_new();
220 gtk_cell_renderer_toggle_set_activatable(GTK_CELL_RENDERER_TOGGLE(renderer), TRUE);
221 g_object_set_data(G_OBJECT(renderer), "column", (gint *)DT_HIST_ITEMS_COL_ENABLED);
222 g_signal_connect(renderer, "toggled", G_CALLBACK(_gui_hist_item_toggled), d);
223
224 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(d->items), -1, _("Include"), renderer, "active",
226
227 /* active */
228 renderer = gtk_cell_renderer_pixbuf_new();
229 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes("", renderer, "pixbuf",
231 gtk_tree_view_append_column(GTK_TREE_VIEW(d->items), column);
232 gtk_tree_view_column_set_alignment(column, 0.5);
233 gtk_tree_view_column_set_clickable(column, FALSE);
234 gtk_tree_view_column_set_min_width(column, DT_PIXEL_APPLY_DPI(30));
235
236 /* name */
237 renderer = gtk_cell_renderer_text_new();
238 g_object_set_data(G_OBJECT(renderer), "column", (gint *)DT_HIST_ITEMS_COL_NAME);
239 g_object_set(renderer, "xalign", 0.0, (gchar *)0);
240 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(d->items), -1, _("Module"), renderer, "text",
242
243 gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(d->items)), GTK_SELECTION_SINGLE);
244 gtk_tree_view_set_model(GTK_TREE_VIEW(d->items), GTK_TREE_MODEL(liststore));
245
246 GdkPixbuf *is_active_pb = dt_draw_paint_to_pixbuf(GTK_WIDGET(dialog), 10, 0, dtgtk_cairo_paint_switch);
247 GdkPixbuf *is_inactive_pb = dt_draw_paint_to_pixbuf(GTK_WIDGET(dialog), 10, 0, dtgtk_cairo_paint_switch_inactive);
248
249 /* fill list with history items */
250 GList *items = dt_history_get_items(imgid, FALSE);
251 if(items)
252 {
253 GtkTreeIter iter;
254
255 for(const GList *items_iter = items; items_iter; items_iter = g_list_next(items_iter))
256 {
257 const dt_history_item_t *item = (dt_history_item_t *)items_iter->data;
258 const int flags = dt_iop_get_module_flags(item->op);
259
260 if(!(flags & IOP_FLAGS_HIDDEN))
261 {
262 const gboolean is_safe = !dt_history_module_skip_copy(flags);
263 gchar *clean_name = delete_underscore(item->name);
264
265 gtk_list_store_append(GTK_LIST_STORE(liststore), &iter);
266 gtk_list_store_set(GTK_LIST_STORE(liststore), &iter,
267 DT_HIST_ITEMS_COL_ENABLED, iscopy ? is_safe : _gui_is_set(d->selops, item->num),
268 DT_HIST_ITEMS_COL_ISACTIVE, (gboolean)item->enabled ? is_active_pb : is_inactive_pb,
269 DT_HIST_ITEMS_COL_NAME, clean_name,
270 DT_HIST_ITEMS_COL_NUM, (gint)item->num,
271 -1);
272 dt_free(clean_name);
273 }
274 }
275 g_list_free_full(items, dt_history_item_free);
276 items = NULL;
277 }
278 else
279 {
280 dt_control_log(_("can't copy history out of unaltered image"));
281 return GTK_RESPONSE_CANCEL;
282 }
283
284 g_signal_connect(GTK_TREE_VIEW(d->items), "row-activated", (GCallback)tree_on_row_activated, GTK_WIDGET(dialog));
285 g_object_unref(liststore);
286
287 g_signal_connect(dialog, "response", G_CALLBACK(_gui_hist_copy_response), d);
288
289 gtk_widget_show_all(GTK_WIDGET(dialog));
290
291 while(1)
292 {
293 res = gtk_dialog_run(GTK_DIALOG(dialog));
294 if(res == GTK_RESPONSE_CANCEL || res == GTK_RESPONSE_DELETE_EVENT || res == GTK_RESPONSE_OK) break;
295 }
296
297 GtkWindow *dialog_parent = gtk_window_get_transient_for(GTK_WINDOW(dialog));
298 gtk_widget_destroy(GTK_WIDGET(dialog));
299 dt_gui_refocus_parent(dialog_parent);
300
301 g_object_unref(is_active_pb);
302 g_object_unref(is_inactive_pb);
303 return res;
304}
305
306// clang-format off
307// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
308// vim: shiftwidth=2 expandtab tabstop=2 cindent
309// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
310// clang-format on
GtkWidget * dt_gui_main_window(void)
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
void dt_control_log(const char *msg,...)
Definition control.c:824
gboolean dt_history_module_skip_copy(const int flags)
Determine whether a module should be skipped during history copy.
void dt_gui_refocus_parent(GtkWindow *parent)
Definition dialog.c:64
GtkWidget * window
static GdkPixbuf * dt_draw_paint_to_pixbuf(GtkWidget *widget, const guint pixbuf_size, const int flags, void(*dtgtk_cairo_paint_fct)(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data))
Definition draw.h:514
const int res
Definition dtpthread.h:351
const dt_collection_sort_t items[]
Definition filter.c:102
int dt_gui_hist_dialog_new(dt_history_copy_item_t *d, int32_t imgid, gboolean iscopy)
static void _gui_hist_item_toggled(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
static GList * _gui_hist_get_active_items(dt_history_copy_item_t *d)
Definition hist_dialog.c:60
enum _style_items_columns_t _styles_columns_t
static void _gui_hist_copy_response(GtkDialog *dialog, gint response_id, dt_history_copy_item_t *g)
Definition hist_dialog.c:96
static void _gui_hist_set_items(dt_history_copy_item_t *d, gboolean active)
Definition hist_dialog.c:82
static gboolean _gui_is_set(GList *selops, unsigned int num)
_style_items_columns_t
Definition hist_dialog.c:52
@ DT_HIST_ITEMS_NUM_COLS
Definition hist_dialog.c:57
@ DT_HIST_ITEMS_COL_ISACTIVE
Definition hist_dialog.c:54
@ DT_HIST_ITEMS_COL_ENABLED
Definition hist_dialog.c:53
@ DT_HIST_ITEMS_COL_NAME
Definition hist_dialog.c:55
@ DT_HIST_ITEMS_COL_NUM
Definition hist_dialog.c:56
void tree_on_row_activated(GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *col, gpointer userdata)
GList * dt_history_get_items(const int32_t imgid, gboolean enabled)
void dt_history_item_free(gpointer data)
int dt_iop_get_module_flags(const char *op)
Definition imageop.c:1719
@ IOP_FLAGS_HIDDEN
Definition imageop.h:189
const char * model
gchar * delete_underscore(const char *s)
Definition label.c:75
#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 macros.h:96
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
dt_mipmap_buffer_dsc_flags flags
Definition mipmap_cache.c:4
void dt_osx_disallow_fullscreen(GtkWidget *widget)
Definition osx.mm:105
gboolean enabled
Definition history.h:83
Telling the user something happened.
#define DT_PIXEL_APPLY_DPI(value)
void dt_gui_add_class(GtkWidget *widget, const gchar *class_name)
void dtgtk_cairo_paint_switch(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_switch_inactive(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)