Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
edit.c
Go to the documentation of this file.
1/*
2 This file is part of the Ansel project.
3 Copyright (C) 2023-2025 Aurélien PIERRE.
4 Copyright (C) 2023 Luca Zulberti.
5
6 Ansel is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 Ansel is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Ansel. If not, see <http://www.gnu.org/licenses/>.
18*/
19#include "common/conf.h"
20#include "common/paths.h" // DT_PATH_MAX
21#include "common/act_on.h"
25#include "gui/actions/menu.h"
26#ifdef __APPLE__
27#include "osx/osx.h" // dt_osx_disallow_fullscreen(), used under GDK_WINDOWING_QUARTZ below
28#endif
29#include "gui/preferences.h"
30#include "common/undo.h"
31#include "common/selection.h"
32#include "caches/image_cache.h"
33#include "history/history.h"
36#include "develop/dev_history.h"
37#include "develop/develop.h"
38#include "control/control.h"
40#include <gtk/gtk.h>
41#include <glib.h>
42#include "gui/application.h"
44#include "common/glib_utils.h"
45
46
48
49static gboolean undo_sensitive_callback()
50{
52 if(IS_NULL_PTR(vm)) return FALSE;
54 if(IS_NULL_PTR(cv)) return FALSE;
55
56 gboolean sensitive = FALSE;
57
58 if(!strcmp(cv->module_name, "lighttable"))
60 else if(!strcmp(cv->module_name, "darkroom"))
62 else if(!strcmp(cv->module_name, "darkroom"))
64
65 return sensitive;
66}
67
68/* Popping a lighttable undo record runs on the GUI thread, and a batch is not cheap: undoing a
69 * removal of 984 images took 23 s, measured, with nothing repainting and the application looking
70 * hung. Say so before blocking -- the toast is posted and the main loop pumped just enough to
71 * paint it -- then let the progress cursor carry the wait, the way every other synchronous
72 * GUI-thread operation here does (libs/snapshots.c, libs/duplicate.c, common/iop-autoset.c).
73 *
74 * Only above a threshold: an ordinary rating change is one record and instant, and announcing
75 * that would be noise on every Ctrl+Z. The pump is bounded and happens BEFORE any data is
76 * touched, so it cannot re-enter the pop it is announcing. */
77#define DT_UNDO_ANNOUNCE_FROM 20
78
79static gboolean _undo_announce(const int records, const char *message)
80{
81 if(records < DT_UNDO_ANNOUNCE_FROM) return FALSE;
82
83 dt_control_log("%s", message);
84 for(int i = 0; i < 32 && gtk_events_pending(); i++) gtk_main_iteration_do(FALSE);
86 return TRUE;
87}
88
89static void _undo_announce_end(const gboolean announced)
90{
91 if(announced) dt_control_commit_cursor();
92}
93
94static gboolean undo_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
95{
97 if(IS_NULL_PTR(vm) || !undo_sensitive_callback()) return FALSE;
99 if(IS_NULL_PTR(cv)) return FALSE;
100
101 uint32_t filter = 0;
102 if(!strcmp(cv->module_name, "lighttable")) filter = DT_UNDO_LIGHTTABLE;
103 else if(!strcmp(cv->module_name, "darkroom")) filter = DT_UNDO_DEVELOP;
104 else if(!strcmp(cv->module_name, "map")) filter = DT_UNDO_MAP;
105
106 if(filter)
107 {
108 const int records = dt_undo_list_length(dt_undo_get_global(), filter);
109 const gboolean announced = _undo_announce(records, _("undoing, please wait…"));
111 _undo_announce_end(announced);
112 }
113 // Beware: it needs to block callbacks declared in view, which may not be loaded.
114 // Another piece of shitty peculiar design that doesn't comply with the logic of the rest of the soft.
115 // That's what you get from ignoring modularity principles.
116 // For now we just ignore the peculiar stuff, no idea how annoying it is, seems it's only GUI candy.
117
118 return TRUE;
119}
120
121
122static gboolean redo_sensitive_callback()
123{
125 if(IS_NULL_PTR(vm)) return FALSE;
127 if(IS_NULL_PTR(cv)) return FALSE;
128
129 gboolean sensitive = FALSE;
130
131 if(!strcmp(cv->module_name, "lighttable"))
133 else if(!strcmp(cv->module_name, "darkroom"))
135 else if(!strcmp(cv->module_name, "darkroom"))
137
138 return sensitive;
139}
140
141
142static gboolean redo_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
143{
145 if(IS_NULL_PTR(vm) || !redo_sensitive_callback()) return FALSE;
147 if(IS_NULL_PTR(cv)) return FALSE;
148
149 uint32_t filter = 0;
150 if(!strcmp(cv->module_name, "lighttable")) filter = DT_UNDO_LIGHTTABLE;
151 else if(!strcmp(cv->module_name, "darkroom")) filter = DT_UNDO_DEVELOP;
152 else if(!strcmp(cv->module_name, "map")) filter = DT_UNDO_MAP;
153
154 if(filter)
155 {
156 const int records = dt_redo_list_length(dt_undo_get_global(), filter);
157 const gboolean announced = _undo_announce(records, _("redoing, please wait…"));
159 _undo_announce_end(announced);
160 }
161 // see undo_callback()
162
163 return TRUE;
164}
165
166static gboolean compress_history_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
167{
168 GList *imgs = dt_act_on_get_images();
169 if(IS_NULL_PTR(imgs)) return FALSE;
170
171 gboolean is_darkroom_image_in_list = dt_dev_history_is_image_in_dev(imgs);
172
173 if(is_darkroom_image_in_list)
174 {
180
181 // Avoid running a headless compression for the current darkroom image: the history module
182 // (src/libs/history.c) compresses directly from the loaded pipeline.
183 imgs = g_list_remove(imgs, GINT_TO_POINTER(dev->image_storage.id));
184 }
185
186 if(imgs) dt_history_compress_on_list(imgs);
187
188 g_list_free(imgs);
189 imgs = NULL;
190 return TRUE;
191}
192
193// delete_history_callback() sits in gui/common/history_actions_gui.c rather than here with
194// its siblings: it is shared verbatim with the darkroom history module's reset button
195// (libs/history.c), so neither menu owns it.
196
197static gboolean copy_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
198{
199 // Allow copy only when exactly one file is selected
201 {
202 dt_control_log(_("Copy is allowed only with exactly one image selected"));
203 return FALSE;
204 }
205
207 gboolean is_darkroom_image_in_list = dt_dev_history_is_image_in_dev(imgs);
208 g_list_free(imgs);
209 imgs = NULL;
210
211 if(is_darkroom_image_in_list)
212 {
213 // Copy/paste reloads the source history from the database right away.
214 // Flush the current darkroom history synchronously here so the copied
215 // source matches the edit stack currently shown in the GUI.
217 }
218
220 return TRUE;
221}
222
223
224static gboolean copy_parts_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
225{
226 // Allow copy only when exactly one file is selected
228 {
229 dt_control_log(_("Copy is allowed only with exactly one image selected"));
230 return FALSE;
231 }
232
234 gboolean is_darkroom_image_in_list = dt_dev_history_is_image_in_dev(imgs);
235 g_list_free(imgs);
236 imgs = NULL;
237
238 if(is_darkroom_image_in_list)
239 {
240 // Selective copy opens the same immediate DB read path as full copy.
241 // Keep the persisted history in sync with the current darkroom stack
242 // before building the copy/paste state from this image.
244 }
245
247 return TRUE;
248}
249
250
252{
254}
255
256static gboolean paste_all_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
257{
259 {
260 dt_control_log(_("Paste needs selected images to work"));
261 return FALSE;
262 }
263
264 if(dt_conf_get_bool("history/paste/ask"))
265 {
266 if(!dt_gui_merge_options_dialog(_("Paste history — merge settings"),
267 "history/paste/mode",
268 "history/paste/copy_iop_order",
269 "history/paste/ask",
270 TRUE))
271 return FALSE;
272 }
273
275
276 // We don't allow pasting on darkroom image
278 imgs = g_list_remove(imgs, GINT_TO_POINTER(dt_dev_get_global()->image_storage.id));
279
280 if(imgs) dt_history_paste_on_list(imgs);
281
282 g_list_free(imgs);
283 imgs = NULL;
284 return TRUE;
285}
286
287static gboolean paste_parts_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
288{
290 {
291 dt_control_log(_("Paste needs selected images to work"));
292 return FALSE;
293 }
294
295 if(dt_conf_get_bool("history/paste/ask"))
296 {
297 if(!dt_gui_merge_options_dialog(_("Paste history (parts) — merge settings"),
298 "history/paste/mode",
299 "history/paste/copy_iop_order",
300 "history/paste/ask",
301 TRUE))
302 return FALSE;
303 }
304
306
308 {
309 g_list_free(imgs);
310 imgs = NULL;
311 return FALSE;
312 }
313
314 // We don't allow pasting on darkroom image
316 imgs = g_list_remove(imgs, GINT_TO_POINTER(dt_dev_get_global()->image_storage.id));
317
318 if(imgs) dt_history_paste_parts_on_list(imgs);
319
321 g_list_free(imgs);
322 imgs = NULL;
323 return TRUE;
324}
325
326static gboolean load_xmp_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
327{
328 // dt_selection_get_list() only sees the lighttable selection, which is typically empty
329 // while in darkroom (the menu entry's sensitivity check, has_active_images(), already
330 // falls back to the active darkroom image via dt_act_on_get_images_nb() -- fetch the
331 // same way here or this silently no-ops in darkroom).
332 GList *imgs = dt_act_on_get_images();
333 if(IS_NULL_PTR(imgs)) return FALSE;
334
335 const int act_on_one = g_list_is_singleton(imgs); // list length == 1?
337 GtkFileChooserNative *filechooser = gtk_file_chooser_native_new(
338 _("open sidecar file"), GTK_WINDOW(win), GTK_FILE_CHOOSER_ACTION_OPEN,
339 _("_open"), _("_cancel"));
340 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(filechooser), FALSE);
341
342 if(act_on_one)
343 {
344 //single image to load xmp to, assume we want to load from same dir
345 const int32_t imgid = GPOINTER_TO_INT(imgs->data);
346 const dt_image_t *img = dt_image_cache_get(imgid, 'r');
347 if(img && img->film_id != -1)
348 {
349 char pathname[DT_PATH_MAX] = { 0 };
350 dt_image_film_roll_directory(img, pathname, sizeof(pathname));
351 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(filechooser), pathname);
352 }
353 else
354 {
355 // handle situation where there's some problem with cache/film_id
356 // i guess that's impossible, but better safe than sorry ;)
357 dt_conf_get_folder_to_file_chooser("ui_last/import_path", GTK_FILE_CHOOSER(filechooser));
358 }
360 }
361 else
362 {
363 // multiple images, use "last import" preference
364 dt_conf_get_folder_to_file_chooser("ui_last/import_path", GTK_FILE_CHOOSER(filechooser));
365 }
366
367 GtkFileFilter *filter;
368 filter = GTK_FILE_FILTER(gtk_file_filter_new());
369 gtk_file_filter_add_pattern(filter, "*.xmp");
370 gtk_file_filter_add_pattern(filter, "*.XMP");
371 gtk_file_filter_set_name(filter, _("XMP sidecar files"));
372 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(filechooser), filter);
373
374 filter = GTK_FILE_FILTER(gtk_file_filter_new());
375 gtk_file_filter_add_pattern(filter, "*");
376 gtk_file_filter_set_name(filter, _("all files"));
377 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(filechooser), filter);
378
379 if(gtk_native_dialog_run(GTK_NATIVE_DIALOG(filechooser)) == GTK_RESPONSE_ACCEPT)
380 {
381 gchar *dtfilename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filechooser));
382 if(dt_history_load_and_apply_on_list(dtfilename, imgs) != 0)
383 {
384 GtkWidget *dialog
385 = gtk_message_dialog_new(GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
386 GTK_BUTTONS_CLOSE, _("error loading file '%s'"), dtfilename);
387#ifdef GDK_WINDOWING_QUARTZ
389#endif
390 gtk_dialog_run(GTK_DIALOG(dialog));
391 gtk_widget_destroy(dialog);
392
393 }
394 else
395 {
397 }
398 if(!act_on_one)
399 {
400 //remember last import path if applying history to multiple images
401 dt_conf_set_folder_from_file_chooser("ui_last/import_path", GTK_FILE_CHOOSER(filechooser));
402 }
403 dt_free(dtfilename);
404 }
405
408
409 g_object_unref(filechooser);
410 g_list_free(imgs);
411 imgs = NULL;
412 return TRUE;
413}
414
415static gboolean duplicate_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
416{
418 {
421 {
422 // Duplication copies history from the source image into the new version.
423 // When the source is the current darkroom image, persist its live history
424 // before the background duplicate job reloads it from the database.
426 }
427 g_list_free(imgs);
428 imgs = NULL;
429
431 return TRUE;
432 }
433
434 dt_control_log(_("Duplication needs selected images to work"));
435 return FALSE;
436}
437
438static gboolean new_history_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
439{
441 {
444 {
445 // Creating a new duplicate version still starts from the current source
446 // image state, so flush the live darkroom history before duplicating it.
448 }
449 g_list_free(imgs);
450 imgs = NULL;
451
453 return TRUE;
454 }
455
456 dt_control_log(_("Creating new historys needs selected images to work"));
457 return TRUE;
458}
459
460
461static gboolean shortcuts_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
462{
464 return TRUE;
465}
466
467static gboolean history_append_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
468{
469 dt_conf_set_int("history/paste/mode", DT_HISTORY_MERGE_APPEND);
470 return TRUE;
471}
472
474{
475 return dt_conf_get_int("history/paste/mode") == DT_HISTORY_MERGE_APPEND;
476}
477
478static gboolean history_prepend_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
479{
480 dt_conf_set_int("history/paste/mode", DT_HISTORY_MERGE_PREPEND);
481 return TRUE;
482}
483
485{
486 return dt_conf_get_int("history/paste/mode") == DT_HISTORY_MERGE_PREPEND;
487}
488
489static gboolean history_replace_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval,
490 GdkModifierType mods, gpointer user_data)
491{
492 dt_conf_set_int("history/paste/mode", DT_HISTORY_MERGE_REPLACE);
493 return TRUE;
494}
495
497{
498 return dt_conf_get_int("history/paste/mode") == DT_HISTORY_MERGE_REPLACE;
499}
500
501static gboolean copy_iop_order_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
502{
503 dt_conf_set_bool("history/paste/copy_iop_order", !dt_conf_get_bool("history/paste/copy_iop_order"));
504 return TRUE;
505}
506
508{
509 return dt_conf_get_bool("history/paste/copy_iop_order");
510}
511
512static gboolean paste_ask_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
513{
514 dt_conf_set_bool("history/paste/ask", !dt_conf_get_bool("history/paste/ask"));
515 return TRUE;
516}
517
518static gboolean paste_ask_checked_callback(GtkWidget *widget)
519{
520 return dt_conf_get_bool("history/paste/ask");
521}
522
523void append_edit(GtkWidget **menus, GList **lists, const dt_menus_t index)
524{
525 add_sub_menu_entry(menus, lists, _("Undo"), index, NULL, undo_callback, NULL, NULL, undo_sensitive_callback, GDK_KEY_z, DT_PRIMARY_MASK);
526
527 add_sub_menu_entry(menus, lists, _("Redo"), index, NULL, redo_callback, NULL, NULL, redo_sensitive_callback, GDK_KEY_y, DT_PRIMARY_MASK);
528
529 add_menu_separator(menus[index]);
530
531 add_sub_menu_entry(menus, lists, _("Copy history (all)"), index, NULL, copy_callback, NULL, NULL, has_selection, GDK_KEY_c, DT_PRIMARY_MASK);
532
533 add_sub_menu_entry(menus, lists, _("Copy history (parts)..."), index, NULL, copy_parts_callback, NULL, NULL, has_selection, GDK_KEY_c, DT_PRIMARY_MASK | GDK_SHIFT_MASK);
534
535 add_sub_menu_entry(menus, lists, _("Paste history (all)"), index, NULL, paste_all_callback, NULL, NULL,
537
538 add_sub_menu_entry(menus, lists, _("Paste history (parts)..."), index, NULL, paste_parts_callback, NULL, NULL,
539 paste_sensitive_callback, GDK_KEY_v, DT_PRIMARY_MASK | GDK_SHIFT_MASK);
540
541 add_menu_separator(menus[index]);
542
543 // History merging options
544
545 add_top_submenu_entry(menus, lists, _("History pasting mode"), index);
546 GtkWidget *parent = get_last_widget(lists);
547
548 add_sub_sub_menu_entry(menus, parent, lists, _("Prepend"), index, NULL,
550 gtk_widget_set_tooltip_text(get_last_widget(lists),
551 _("Paste copied history BEFORE the current history.\n"
552 "CURRENT EDITS are applied afterwards and win conflicts."));
553
554 add_sub_sub_menu_entry(menus, parent, lists, _("Append"), index, NULL,
556 gtk_widget_set_tooltip_text(get_last_widget(lists),
557 _("Paste copied history AFTER the current history.\n"
558 "COPIED EDITS are applied afterwards and win conflicts."));
559
560 add_sub_sub_menu_entry(menus, parent, lists, _("Replace"), index, NULL,
562 gtk_widget_set_tooltip_text(get_last_widget(lists),
563 _("Discard the current history and replace it entirely with the copied history."));
564
565 add_top_submenu_entry(menus, lists, _("Nodes pasting mode"), index);
566 parent = get_last_widget(lists);
567
568 add_sub_sub_menu_entry(menus, parent, lists, _("Copy module order"), index, NULL,
570
571 add_sub_menu_entry(menus, lists, _("Ask merge settings before paste"), index, NULL,
573
574 add_menu_separator(menus[index]);
575
576 add_sub_menu_entry(menus, lists, _("Load history from XMP..."), index, NULL,
577 load_xmp_callback, NULL, NULL, has_active_images, 0, 0);
578
579 add_sub_menu_entry(menus, lists, _("Create new history"), index, NULL,
581
582 add_sub_menu_entry(menus, lists, _("Duplicate existing history"), index, NULL,
583 duplicate_callback, NULL, NULL, has_active_images, GDK_KEY_d, DT_PRIMARY_MASK);
584
585 add_sub_menu_entry(menus, lists, _("Compress history"), index, NULL,
587
588 add_sub_menu_entry(menus, lists, _("Delete history"), index, NULL,
589 delete_history_callback, NULL, NULL, has_active_images, 0, 0);
590
591 add_menu_separator(menus[index]);
592
593 add_sub_menu_entry(menus, lists, _("Preferences..."), index, NULL, GET_ACCEL_WRAPPER(dt_gui_preferences_show), NULL, NULL, NULL, 0, 0);
594 add_sub_menu_entry(menus, lists, _("Keyboard shortcuts..."), index, NULL, shortcuts_callback, NULL, NULL, NULL, 0, 0);
595}
void dt_accels_window(dt_accels_t *accels, GtkWindow *main_window)
Show the modal dialog listing all available keyboard shortcuts and letting user to set them.
Handle default and user-set shortcuts (accelerators)
#define DT_PRIMARY_MASK
GList * dt_act_on_get_images()
Definition act_on.c:38
GtkWidget * dt_gui_main_window(void)
struct dt_accels_t * dt_gui_get_accels(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_conf_set_bool(const char *name, int val)
int dt_conf_get_bool(const char *name)
void dt_conf_set_int(const char *name, int val)
int dt_conf_get_int(const char *name)
Integer for name, clamped to the bounds declared in the XML.
void dt_conf_set_folder_from_file_chooser(const char *name, GtkFileChooser *chooser)
gboolean dt_conf_get_folder_to_file_chooser(const char *name, GtkFileChooser *chooser)
void dt_image_film_roll_directory(const dt_image_t *img, char *pathname, size_t pathname_len)
void dt_control_log(const char *msg,...)
Definition control.c:824
void dt_control_queue_redraw_center()
Request a redraw of the centre view.
Definition control.c:924
void dt_control_change_cursor_by_name_and_flush(const char *curs_str)
Apply a named cursor immediatelly and flush display updates for immediate feedback.
Definition control.c:335
void dt_control_commit_cursor()
Definition control.c:343
void dt_control_duplicate_images(gboolean virgin)
struct dt_develop_t * dt_dev_get_global(void)
Definition darktable.c:528
struct dt_view_manager_t * dt_view_manager_get_global(void)
Definition darktable.c:599
void dt_apply_dev_history_update(dt_develop_t *dev)
Reload the current darkroom history and refresh every dependent GUI.
gboolean dt_dev_history_is_image_in_dev(GList *imgs)
Check whether any image of the list is the one currently loaded in the darkroom.
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_undo_start_record(dt_develop_t *dev)
Definition develop.c:1854
void dt_dev_undo_end_record(dt_develop_t *dev)
Definition develop.c:1865
static gboolean redo_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition edit.c:142
static gboolean _undo_announce(const int records, const char *message)
Definition edit.c:79
static gboolean history_prepend_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition edit.c:478
static gboolean paste_sensitive_callback()
Definition edit.c:251
static gboolean new_history_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition edit.c:438
static gboolean history_replace_checked_callback(GtkWidget *widget)
Definition edit.c:496
static gboolean undo_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition edit.c:94
static gboolean compress_history_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition edit.c:166
static void _undo_announce_end(const gboolean announced)
Definition edit.c:89
static gboolean copy_iop_order_checked_callback(GtkWidget *widget)
Definition edit.c:507
static gboolean copy_parts_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition edit.c:224
static gboolean paste_all_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition edit.c:256
static gboolean redo_sensitive_callback()
Definition edit.c:122
static gboolean history_append_checked_callback(GtkWidget *widget)
Definition edit.c:473
static gboolean copy_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition edit.c:197
static gboolean duplicate_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition edit.c:415
static gboolean paste_ask_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition edit.c:512
static gboolean paste_parts_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition edit.c:287
void append_edit(GtkWidget **menus, GList **lists, const dt_menus_t index)
Definition edit.c:523
static gboolean shortcuts_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition edit.c:461
#define DT_UNDO_ANNOUNCE_FROM
Definition edit.c:77
static gboolean history_append_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition edit.c:467
static gboolean copy_iop_order_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition edit.c:501
static gboolean undo_sensitive_callback()
Definition edit.c:49
static gboolean paste_ask_checked_callback(GtkWidget *widget)
Definition edit.c:518
static gboolean load_xmp_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition edit.c:326
static gboolean history_prepend_checked_callback(GtkWidget *widget)
Definition edit.c:484
static gboolean history_replace_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition edit.c:489
#define g_list_is_singleton(list)
Definition glib_utils.h:33
gboolean dt_history_paste_on_list(const GList *list)
gboolean dt_history_copy(int32_t imgid)
gboolean dt_history_paste_parts_on_list(const GList *list)
dt_history_copy_item_t * dt_history_copy_paste_get(void)
The history clipboard: what "copy history" put there, for "paste" to read.
int dt_history_load_and_apply_on_list(gchar *filename, const GList *list)
int dt_history_compress_on_list(const GList *imgs)
void dt_history_compress_on_image(const int32_t imgid)
gboolean delete_history_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
gboolean dt_history_paste_parts_prepare(void)
gboolean dt_history_copy_parts(int32_t imgid)
@ DT_HISTORY_MERGE_REPLACE
@ DT_HISTORY_MERGE_PREPEND
@ DT_HISTORY_MERGE_APPEND
gboolean dt_gui_merge_options_dialog(const char *title, const char *mode_key, const char *iop_order_key, const char *ask_key, const gboolean iop_order_available)
Show a modal dialog to pick merge mode and pipeline order before a paste or style apply.
dt_image_t * dt_image_cache_get(const int32_t imgid, char mode)
void dt_image_cache_read_release(const dt_image_t *img)
#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
gboolean has_selection()
Definition menu.c:653
void add_sub_sub_menu_entry(GtkWidget **menus, GtkWidget *parent, GList **lists, const gchar *label, const dt_menus_t index, void *data, gboolean(*action_callback)(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data), gboolean(*checked_callback)(GtkWidget *widget), gboolean(*active_callback)(GtkWidget *widget), gboolean(*sensitive_callback)(GtkWidget *widget), guint key_val, GdkModifierType mods)
Definition menu.c:607
GtkWidget * get_last_widget(GList **list)
Definition menu.c:641
void add_sub_menu_entry(GtkWidget **menus, GList **lists, const gchar *label, const dt_menus_t index, void *data, gboolean(*action_callback)(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data), gboolean(*checked_callback)(GtkWidget *widget), gboolean(*active_callback)(GtkWidget *widget), gboolean(*sensitive_callback)(GtkWidget *widget), guint key_val, GdkModifierType mods)
Definition menu.c:565
void add_top_submenu_entry(GtkWidget **menus, GList **lists, const gchar *label, const dt_menus_t index)
Definition menu.c:538
void add_menu_separator(GtkWidget *menu)
Definition menu.c:621
gboolean has_active_images()
Definition menu.c:659
#define GET_ACCEL_WRAPPER(cb)
Definition menu.h:239
#define MAKE_ACCEL_WRAPPER(cb)
Definition menu.h:227
dt_menus_t
Definition menu.h:42
void dt_osx_disallow_fullscreen(GtkWidget *widget)
Definition osx.mm:105
#define DT_PATH_MAX
Buffer size for a filesystem path anywhere in Ansel.
Definition paths.h:57
void dt_gui_preferences_show()
int32_t dt_selection_get_first_id(struct dt_selection_t *selection)
Definition selection.c:103
struct dt_selection_t * dt_selection_get_global(void)
Definition selection.c:80
int dt_selection_get_length(struct dt_selection_t *selection)
Definition selection.c:193
GList * dt_selection_get_list(struct dt_selection_t *selection)
Definition selection.c:185
dt_image_t image_storage
Definition develop.h:225
int32_t film_id
Definition image.h:401
int32_t id
Definition image.h:401
char module_name[64]
Definition view.h:155
int dt_redo_list_length(dt_undo_t *self, uint32_t filter)
Definition undo.c:341
void dt_undo_do_redo(dt_undo_t *self, uint32_t filter)
Definition undo.c:272
gboolean dt_is_redo_list_populated(dt_undo_t *self, uint32_t filter)
Definition undo.c:331
int dt_undo_list_length(dt_undo_t *self, uint32_t filter)
How many records match filter, so a caller can tell a one-record undo from a batch of a thousand....
Definition undo.c:336
void dt_undo_do_undo(dt_undo_t *self, uint32_t filter)
Definition undo.c:277
gboolean dt_is_undo_list_populated(dt_undo_t *self, uint32_t filter)
Definition undo.c:326
@ DT_UNDO_LIGHTTABLE
Definition undo.h:56
@ DT_UNDO_MAP
Definition undo.h:60
@ DT_UNDO_DEVELOP
Definition undo.h:54
dt_undo_t * dt_undo_get_global(void)
Definition darktable.c:611
Telling the user something happened.
const dt_view_t * dt_view_manager_get_current_view(dt_view_manager_t *vm)
Definition view.c:139