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 "common/darktable.h"
36#include "common/history.h"
37#include "common/styles.h"
38#include "control/control.h"
39#include "develop/imageop.h"
40#include "gui/gtk.h"
41#include "gui/hist_dialog.h"
42#include "gui/styles.h"
43#include "gui/draw.h"
44#ifdef GDK_WINDOWING_QUARTZ
45#include "osx/osx.h"
46#endif
47
56
58{
59 GList *result = NULL;
60
61 /* run through all items and add active ones to result */
62 GtkTreeIter iter;
63 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(d->items));
64 if(gtk_tree_model_get_iter_first(model, &iter))
65 {
66 do
67 {
68 gboolean active = FALSE;
69 gint num = 0;
70 gtk_tree_model_get(model, &iter, DT_HIST_ITEMS_COL_ENABLED, &active, DT_HIST_ITEMS_COL_NUM, &num, -1);
71 if(active && num >= 0)
72 result = g_list_prepend(result, GINT_TO_POINTER(num));
73
74 } while(gtk_tree_model_iter_next(model, &iter));
75 }
76 return g_list_reverse(result); // list was built in reverse order, so un-reverse it
77}
78
79static void _gui_hist_set_items(dt_history_copy_item_t *d, gboolean active)
80{
81 /* run through all items and set active status */
82 GtkTreeIter iter;
83 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(d->items));
84 if(gtk_tree_model_get_iter_first(model, &iter))
85 {
86 do
87 {
88 gtk_list_store_set(GTK_LIST_STORE(model), &iter, DT_HIST_ITEMS_COL_ENABLED, active, -1);
89 } while(gtk_tree_model_iter_next(model, &iter));
90 }
91}
92
93static void _gui_hist_copy_response(GtkDialog *dialog, gint response_id, dt_history_copy_item_t *g)
94{
95 switch(response_id)
96 {
97 case GTK_RESPONSE_CANCEL:
98 break;
99
100 case GTK_RESPONSE_YES:
102 break;
103
104 case GTK_RESPONSE_NONE:
106 break;
107
108 case GTK_RESPONSE_OK:
109 g->selops = _gui_hist_get_active_items(g);
110 break;
111 }
112}
113
114static void _gui_hist_item_toggled(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
115{
117
118 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(d->items));
119 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
120 GtkTreeIter iter;
121 gboolean toggle_item;
122
123 gtk_tree_model_get_iter(model, &iter, path);
124 gtk_tree_model_get(model, &iter, DT_HIST_ITEMS_COL_ENABLED, &toggle_item, -1);
125
126 toggle_item = (toggle_item == TRUE) ? FALSE : TRUE;
127
128 gtk_list_store_set(GTK_LIST_STORE(model), &iter, DT_HIST_ITEMS_COL_ENABLED, toggle_item, -1);
129 gtk_tree_path_free(path);
130}
131
132static gboolean _gui_is_set(GList *selops, unsigned int num)
133{
134 /* nothing to filter */
135 if(IS_NULL_PTR(selops)) return TRUE;
136
137 for(GList *l = selops; l; l = g_list_next(l))
138 {
139 if(l->data)
140 {
141 unsigned int lnum = GPOINTER_TO_UINT(l->data);
142 if(lnum == num) return TRUE;
143 }
144 }
145 return FALSE;
146}
147
148void
149tree_on_row_activated(GtkTreeView *treeview,
150 GtkTreePath *path,
151 GtkTreeViewColumn *col,
152 gpointer userdata)
153{
154 GtkDialog *dialog = GTK_DIALOG(userdata);
155 GtkTreeModel *model = gtk_tree_view_get_model(treeview);
156 GtkTreeIter iter;
157
158 // unselect all items
159
160 if(gtk_tree_model_get_iter_first(model, &iter))
161 {
162 do
163 {
164 gtk_list_store_set(GTK_LIST_STORE(model), &iter, DT_HIST_ITEMS_COL_ENABLED, FALSE, -1);
165
166 } while(gtk_tree_model_iter_next(model, &iter));
167 }
168
169 // select now the one that got double-clicked
170
171 if (gtk_tree_model_get_iter(model, &iter, path))
172 {
173 gtk_list_store_set(GTK_LIST_STORE(model), &iter, DT_HIST_ITEMS_COL_ENABLED, TRUE, -1);
174 // and finally close the dialog
175 g_signal_emit_by_name(dialog, "response", GTK_RESPONSE_OK, NULL);
176 }
177}
178
179int dt_gui_hist_dialog_new(dt_history_copy_item_t *d, int32_t imgid, gboolean iscopy)
180{
181 int res;
183
184 GtkDialog *dialog = GTK_DIALOG(gtk_dialog_new_with_buttons(
185 iscopy ? _("Select development parts to copy") : _("Select development parts to paste"),
186 GTK_WINDOW(window), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
187 _("_cancel"), GTK_RESPONSE_CANCEL,
188 _("select _all"), GTK_RESPONSE_YES,
189 _("select _none"), GTK_RESPONSE_NONE,
190 _("_ok"), GTK_RESPONSE_OK,
191 NULL));
192
193 gtk_dialog_set_default_response(dialog, GTK_RESPONSE_OK);
194
195#ifdef GDK_WINDOWING_QUARTZ
196 dt_osx_disallow_fullscreen(GTK_WIDGET(dialog));
197#endif
198
199 GtkContainer *content_area = GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog)));
200
201 GtkWidget *scroll = gtk_scrolled_window_new(NULL, NULL);
202 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
203 gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scroll), DT_PIXEL_APPLY_DPI(450));
204 dt_gui_add_class(scroll, "dt_recessed_scroll");
205
206 /* create the list of items */
207 d->items = GTK_TREE_VIEW(gtk_tree_view_new());
208 gtk_container_add(GTK_CONTAINER(scroll), GTK_WIDGET(d->items));
209 gtk_box_pack_start(GTK_BOX(content_area), GTK_WIDGET(scroll), TRUE, TRUE, 0);
210
211 GtkListStore *liststore
212 = gtk_list_store_new(DT_HIST_ITEMS_NUM_COLS,
213 G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_UINT);
214
215 /* enabled */
216 GtkCellRenderer *renderer = gtk_cell_renderer_toggle_new();
217 gtk_cell_renderer_toggle_set_activatable(GTK_CELL_RENDERER_TOGGLE(renderer), TRUE);
218 g_object_set_data(G_OBJECT(renderer), "column", (gint *)DT_HIST_ITEMS_COL_ENABLED);
219 g_signal_connect(renderer, "toggled", G_CALLBACK(_gui_hist_item_toggled), d);
220
221 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(d->items), -1, _("Include"), renderer, "active",
223
224 /* active */
225 renderer = gtk_cell_renderer_pixbuf_new();
226 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes("", renderer, "pixbuf",
228 gtk_tree_view_append_column(GTK_TREE_VIEW(d->items), column);
229 gtk_tree_view_column_set_alignment(column, 0.5);
230 gtk_tree_view_column_set_clickable(column, FALSE);
231 gtk_tree_view_column_set_min_width(column, DT_PIXEL_APPLY_DPI(30));
232
233 /* name */
234 renderer = gtk_cell_renderer_text_new();
235 g_object_set_data(G_OBJECT(renderer), "column", (gint *)DT_HIST_ITEMS_COL_NAME);
236 g_object_set(renderer, "xalign", 0.0, (gchar *)0);
237 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(d->items), -1, _("Module"), renderer, "text",
239
240 gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(d->items)), GTK_SELECTION_SINGLE);
241 gtk_tree_view_set_model(GTK_TREE_VIEW(d->items), GTK_TREE_MODEL(liststore));
242
243 GdkPixbuf *is_active_pb = dt_draw_paint_to_pixbuf(GTK_WIDGET(dialog), 10, 0, dtgtk_cairo_paint_switch);
244 GdkPixbuf *is_inactive_pb = dt_draw_paint_to_pixbuf(GTK_WIDGET(dialog), 10, 0, dtgtk_cairo_paint_switch_inactive);
245
246 /* fill list with history items */
247 GList *items = dt_history_get_items(imgid, FALSE);
248 if(items)
249 {
250 GtkTreeIter iter;
251
252 for(const GList *items_iter = items; items_iter; items_iter = g_list_next(items_iter))
253 {
254 const dt_history_item_t *item = (dt_history_item_t *)items_iter->data;
255 const int flags = dt_iop_get_module_flags(item->op);
256
257 if(!(flags & IOP_FLAGS_HIDDEN))
258 {
259 const gboolean is_safe = !dt_history_module_skip_copy(flags);
260 gchar *clean_name = delete_underscore(item->name);
261
262 gtk_list_store_append(GTK_LIST_STORE(liststore), &iter);
263 gtk_list_store_set(GTK_LIST_STORE(liststore), &iter,
264 DT_HIST_ITEMS_COL_ENABLED, iscopy ? is_safe : _gui_is_set(d->selops, item->num),
265 DT_HIST_ITEMS_COL_ISACTIVE, (gboolean)item->enabled ? is_active_pb : is_inactive_pb,
266 DT_HIST_ITEMS_COL_NAME, clean_name,
267 DT_HIST_ITEMS_COL_NUM, (gint)item->num,
268 -1);
269 dt_free(clean_name);
270 }
271 }
272 g_list_free_full(items, dt_history_item_free);
273 items = NULL;
274 }
275 else
276 {
277 dt_control_log(_("can't copy history out of unaltered image"));
278 return GTK_RESPONSE_CANCEL;
279 }
280
281 g_signal_connect(GTK_TREE_VIEW(d->items), "row-activated", (GCallback)tree_on_row_activated, GTK_WIDGET(dialog));
282 g_object_unref(liststore);
283
284 g_signal_connect(dialog, "response", G_CALLBACK(_gui_hist_copy_response), d);
285
286 gtk_widget_show_all(GTK_WIDGET(dialog));
287
288 while(1)
289 {
290 res = gtk_dialog_run(GTK_DIALOG(dialog));
291 if(res == GTK_RESPONSE_CANCEL || res == GTK_RESPONSE_DELETE_EVENT || res == GTK_RESPONSE_OK) break;
292 }
293
294 gtk_widget_destroy(GTK_WIDGET(dialog));
295
296 g_object_unref(is_active_pb);
297 g_object_unref(is_inactive_pb);
298 return res;
299}
300
301// clang-format off
302// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
303// vim: shiftwidth=2 expandtab tabstop=2 cindent
304// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
305// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
GList * dt_history_get_items(const int32_t imgid, gboolean enabled)
void dt_history_item_free(gpointer data)
void dt_control_log(const char *msg,...)
Definition control.c:761
darktable_t darktable
Definition darktable.c:181
#define dt_free(ptr)
Definition darktable.h:456
static gchar * delete_underscore(const char *s)
Definition darktable.h:1083
#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 darktable.h:281
gboolean dt_history_module_skip_copy(const int flags)
Determine whether a module should be skipped during history copy.
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:499
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)
const dt_collection_sort_t items[]
Definition filter.c:95
void dt_gui_add_class(GtkWidget *widget, const gchar *class_name)
Definition gtk.c:133
GtkWidget * dt_ui_main_window(dt_ui_t *ui)
get the main window widget
#define DT_PIXEL_APPLY_DPI(value)
Definition gtk.h:90
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:57
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:93
static void _gui_hist_set_items(dt_history_copy_item_t *d, gboolean active)
Definition hist_dialog.c:79
static gboolean _gui_is_set(GList *selops, unsigned int num)
_style_items_columns_t
Definition hist_dialog.c:49
@ DT_HIST_ITEMS_NUM_COLS
Definition hist_dialog.c:54
@ DT_HIST_ITEMS_COL_ISACTIVE
Definition hist_dialog.c:51
@ DT_HIST_ITEMS_COL_ENABLED
Definition hist_dialog.c:50
@ DT_HIST_ITEMS_COL_NAME
Definition hist_dialog.c:52
@ DT_HIST_ITEMS_COL_NUM
Definition hist_dialog.c:53
void tree_on_row_activated(GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *col, gpointer userdata)
int dt_iop_get_module_flags(const char *op)
Definition imageop.c:2960
@ IOP_FLAGS_HIDDEN
Definition imageop.h:170
const char * model
dt_mipmap_buffer_dsc_flags flags
Definition mipmap_cache.c:4
void dt_osx_disallow_fullscreen(GtkWidget *widget)
Definition osx.mm:104
struct _GtkWidget GtkWidget
Definition splash.h:29
struct dt_gui_gtk_t * gui
Definition darktable.h:775
dt_ui_t * ui
Definition gtk.h:164
gboolean enabled
Definition history.h:111