Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
file.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 lologor.
5 Copyright (C) 2023 Luca Zulberti.
6 Copyright (C) 2024, 2026 Guillaume Stutin.
7
8 Ansel is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 Ansel is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with Ansel. If not, see <http://www.gnu.org/licenses/>.
20*/
21#include "system/macros.h"
22#include "common/paths.h" // DT_PATH_MAX
23#include "common/conf.h"
25#include "system/mem_alloc.h"
27#include "control/signal.h"
28#include "gui/application.h"
29#include "gui/closing.h"
31#include "gui/actions/menu.h"
32#ifdef __APPLE__
33#include "osx/osx.h" // dt_osx_disallow_fullscreen(), used under GDK_WINDOWING_QUARTZ below
34#endif
35#include "common/collection.h"
36#include "common/image.h"
37#include "common/selection.h"
38#include "libs/collect.h"
39#include "gui/import.h"
40#include "libs/lib.h"
41#include "control/control.h"
44
45
46static void pretty_print_collection(const char *buf, char *out, size_t outsize)
47{
48 memset(out, 0, outsize);
49
50 if(IS_NULL_PTR(buf) || buf[0] == '\0') return;
51
52 int num_rules = 0;
53 char str[400] = { 0 };
54 int mode, item;
55 int c;
56 sscanf(buf, "%d", &num_rules);
57 while(buf[0] != '\0' && buf[0] != ':') buf++;
58 if(buf[0] == ':') buf++;
59
60 for(int k = 0; k < num_rules; k++)
61 {
62 const int n = sscanf(buf, "%d:%d:%399[^$]", &mode, &item, str);
63
64 if(n == 3)
65 {
66 if(k > 0) switch(mode)
67 {
69 c = g_strlcpy(out, _(" and "), outsize);
70 out += c;
71 outsize -= c;
72 break;
74 c = g_strlcpy(out, _(" or "), outsize);
75 out += c;
76 outsize -= c;
77 break;
78 default: // case DT_LIB_COLLECT_MODE_AND_NOT:
79 c = g_strlcpy(out, _(" but not "), outsize);
80 out += c;
81 outsize -= c;
82 break;
83 }
84 int i = 0;
85 while(str[i] != '\0' && str[i] != '$') i++;
86 if(str[i] == '$') str[i] = '\0';
87
88 c = snprintf(out, outsize, "%s %s", item < DT_COLLECTION_PROP_LAST ? dt_collection_name(item) : "???",
89 item == 0 ? dt_image_film_roll_name(str) : str);
90 out += c;
91 outsize -= c;
92 }
93 while(buf[0] != '$' && buf[0] != '\0') buf++;
94 if(buf[0] == '$') buf++;
95 }
96}
97
98
99static gboolean update_collection_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
100{
101 // Grab the position of current menuitem in menu list
102 const int index = GPOINTER_TO_INT(get_custom_data(GTK_WIDGET(user_data)));
103
104 // Grab the corresponding config line
105 char confname[200];
106 snprintf(confname, sizeof(confname), "plugins/lighttable/recentcollect/line%1d", index);
107 const char *collection = dt_conf_get_string_const(confname);
108
109 snprintf(confname, sizeof(confname), "plugins/lighttable/recentcollect/pos%1d", index);
110
111 // Update the collection to the value defined in config line
112 dt_collection_deserialize(collection);
113
114 return TRUE;
115}
116
117
118void init_collection_line(gpointer instance,
119 dt_collection_change_t query_change,
120 dt_collection_properties_t changed_property, gpointer imgs, int next,
121 gpointer user_data)
122{
123 GtkWidget *widget = GTK_WIDGET(user_data);
124
125 // Grab the position of current menuitem in menu list
126 const int index = GPOINTER_TO_INT(get_custom_data(widget));
127
128 // Grab the corresponding config line
129 char confname[200];
130 snprintf(confname, sizeof(confname), "plugins/lighttable/recentcollect/line%1d", index);
131
132 // Get the human-readable name of the collection
133 const char *collection = dt_conf_get_string_const(confname);
134
135
136 if(collection && collection[0] != '\0')
137 {
138 char label[2048] = { 0 };
139 pretty_print_collection(collection, label, sizeof(label));
140 dt_capitalize_label(label);
141
142 // Update the menu entry label for current collection name. Escape it: a collection value
143 // can contain markup-significant characters (e.g. the < > operators in date/numeric rules).
144 GtkWidget *child = gtk_bin_get_child(GTK_BIN(widget));
145 gchar *escaped = g_markup_escape_text(label, -1);
146 gtk_label_set_markup(GTK_LABEL(child), escaped);
147 g_free(escaped);
148 }
149}
150
151void _close_export_popup(GtkWidget *dialog, gint response_id, gpointer data)
152{
153 // We need to increase the reference count of the module,
154 // then remove it from the popup before closing it,
155 // otherwise it gets destroyed along with it.
156 dt_gui_get_global()->export_popup.module = (GtkWidget *)g_object_ref(dt_gui_get_global()->export_popup.module);
157 GtkWidget *content = gtk_dialog_get_content_area(GTK_DIALOG(dt_gui_get_global()->export_popup.window));
158 gtk_container_remove(GTK_CONTAINER(content), dt_gui_get_global()->export_popup.module);
160}
161
162static gboolean export_files_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
163{
164 if(dt_gui_get_global()->export_popup.window)
165 {
166 // if not NULL, we already have a popup open and can't re-instanciate a live GtkWidget
167 gtk_window_present_with_time(GTK_WINDOW(dt_gui_get_global()->export_popup.window), GDK_CURRENT_TIME);
168 return TRUE;
169 }
170
171 dt_lib_module_t *module = dt_lib_get_module("export");
172 if(IS_NULL_PTR(module)) return TRUE;
173
174 // get_expander actually builds the expander, it's not a getter despite what the name suggests.
175 // On first run we need to build, an the following runs, just fetch it
178 : dt_lib_gui_get_expander(module);
179 if(IS_NULL_PTR(w)) return TRUE;
180
181 // Save the module
182 dt_gui_get_global()->export_popup.module = w;
183
184 // Prepare the popup
185 GtkWidget *dialog = gtk_dialog_new();
186#ifdef GDK_WINDOWING_QUARTZ
187// TODO: On MacOS (at least on version 13) the dialog windows doesn't behave as expected. The dialog
188// needs to have a parent window. "set_parent_window" wasn't working, so set_transient_for is
189// the way to go. Still the window manager isn't dealing with the dialog properly, when the dialog
190// is shifted outside its parent. The dialog isn't visible any longer but still listed as a window
191// of the app.
193 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER_ON_PARENT);
194#endif
195 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
196 gtk_window_set_modal(GTK_WINDOW(dialog), FALSE);
197 gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(dt_gui_main_window()));
198 gtk_window_set_title(GTK_WINDOW(dialog), _("Ansel - Export images"));
199 g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(_close_export_popup), NULL);
200
201 // Ensure the module is expanded
203 dt_gui_add_help_link(w, dt_get_help_url(module->plugin_name));
204 gtk_widget_set_size_request(w, DT_PIXEL_APPLY_DPI(450), -1);
205
206 // Populate popup and fire everything
207 GtkWidget *content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
208 gtk_box_pack_start(GTK_BOX(content), w, TRUE, TRUE, 0);
209 gtk_widget_set_visible(w, TRUE);
210 gtk_widget_show_all(dialog);
211
212 // Save the ref to the window. We don't reuse its content, we just need to know if it exists.
214
215 return TRUE;
216}
217
218
224
225// On screen and in the clipboard, both modes emit a single line ready to paste
226// as script arguments: IDs comma-separated (--ids 1,2,3), filenames
227// space-separated and shell-quoted (paths can contain spaces and
228// metacharacters; POSIX single-quote escaping also suits PowerShell — only
229// cmd.exe differs). Saved files instead get one raw item per line, unquoted:
230// files are read verbatim, not parsed by a shell.
231static gchar *_export_list_build(GList *imgids, const int mode, const gboolean one_per_line)
232{
233 GString *str = g_string_new(NULL);
234 for(GList *l = imgids; l; l = g_list_next(l))
235 {
236 const int32_t imgid = GPOINTER_TO_INT(l->data);
237 if(mode == DT_EXPORT_LIST_FILENAMES)
238 {
239 char path[DT_PATH_MAX] = { 0 };
240 gboolean from_cache = FALSE;
241 dt_image_full_path(imgid, path, sizeof(path), &from_cache, __FUNCTION__);
242 if(one_per_line)
243 g_string_append(str, path);
244 else
245 {
246 gchar *quoted = g_shell_quote(path);
247 g_string_append(str, quoted);
248 g_free(quoted);
249 }
250 }
251 else
252 g_string_append_printf(str, "%i", imgid);
253
254 if(one_per_line)
255 g_string_append_c(str, '\n');
256 else if(l->next)
257 g_string_append_c(str, mode == DT_EXPORT_LIST_FILENAMES ? ' ' : ',');
258 }
259 return g_string_free(str, FALSE);
260}
261
262static void _export_list_fill(GtkComboBox *combo, gpointer user_data)
263{
264 GtkTextBuffer *buffer = GTK_TEXT_BUFFER(user_data);
265 GList *imgids = (GList *)g_object_get_data(G_OBJECT(buffer), "imgids");
266 gchar *text = _export_list_build(imgids, gtk_combo_box_get_active(combo), FALSE);
267 gtk_text_buffer_set_text(buffer, text, -1);
268 g_free(text);
269}
270
271static void _export_list_save(GtkWidget *dialog, GtkTextBuffer *buffer)
272{
273 GList *imgids = (GList *)g_object_get_data(G_OBJECT(buffer), "imgids");
274 GtkComboBox *combo = GTK_COMBO_BOX(g_object_get_data(G_OBJECT(buffer), "combo"));
275 const int mode = gtk_combo_box_get_active(combo);
276
277 // GtkFileChooserNative uses the platform-native save dialog on Windows and macOS
278 GtkFileChooserNative *chooser
279 = gtk_file_chooser_native_new(_("Ansel - Save image list"), GTK_WINDOW(dialog),
280 GTK_FILE_CHOOSER_ACTION_SAVE, _("_Save"), _("_Cancel"));
281 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(chooser), TRUE);
282 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(chooser),
283 mode == DT_EXPORT_LIST_FILENAMES ? "ansel-image-files.txt"
284 : "ansel-image-ids.txt");
285 if(gtk_native_dialog_run(GTK_NATIVE_DIALOG(chooser)) == GTK_RESPONSE_ACCEPT)
286 {
287 gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(chooser));
288 gchar *text = _export_list_build(imgids, mode, TRUE);
289 GError *error = NULL;
290 if(!g_file_set_contents(filename, text, -1, &error))
291 {
292 gchar *item = g_strdup_printf("%s: %s", filename, error->message);
293 dt_control_alert(_("Could not save"), _("could not save the image list"), item);
294 dt_free(item);
295 g_error_free(error);
296 }
297 g_free(text);
298 g_free(filename);
299 }
300 g_object_unref(chooser);
301}
302
303static void _export_list_response(GtkWidget *dialog, gint response_id, gpointer user_data)
304{
305 if(response_id == GTK_RESPONSE_OK)
306 _export_list_save(dialog, GTK_TEXT_BUFFER(user_data));
307 else if(response_id == GTK_RESPONSE_APPLY)
308 {
309 // GDK_SELECTION_CLIPBOARD is the explicit-copy clipboard on all backends
310 // (X11, Wayland, Windows, macOS) — as opposed to the X11-only PRIMARY selection.
311 GtkTextBuffer *buffer = GTK_TEXT_BUFFER(user_data);
312 GtkTextIter start, end;
313 gtk_text_buffer_get_bounds(buffer, &start, &end);
314 gchar *text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
315 GtkClipboard *clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
316 gtk_clipboard_set_text(clipboard, text, -1);
317 gtk_clipboard_store(clipboard);
318 g_free(text);
319 }
320 else
321 gtk_widget_destroy(dialog);
322}
323
324static gboolean export_image_list_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
325{
326 static GtkWidget *dialog = NULL;
327
328 // Rebuild from scratch on each call so the content always reflects the current selection
329 if(dialog) gtk_widget_destroy(dialog);
330
332 if(IS_NULL_PTR(imgids)) return TRUE;
333
334 dialog = gtk_dialog_new_with_buttons(_("Ansel - Export image list"),
335 GTK_WINDOW(dt_gui_main_window()),
336 GTK_DIALOG_DESTROY_WITH_PARENT,
337 _("Copy to clipboard"), GTK_RESPONSE_APPLY,
338 _("Save as file..."), GTK_RESPONSE_OK,
339 _("Close"), GTK_RESPONSE_CLOSE, NULL);
340#ifdef GDK_WINDOWING_QUARTZ
342 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER_ON_PARENT);
343#endif
344 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CLOSE);
345 gtk_window_set_modal(GTK_WINDOW(dialog), FALSE);
346 g_signal_connect(G_OBJECT(dialog), "destroy", G_CALLBACK(gtk_widget_destroyed), &dialog);
347
348 GtkWidget *combo = gtk_combo_box_text_new();
349 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Image ID"));
350 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Image filename"));
351
352 GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_PIXEL_APPLY_DPI(8));
353 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_("Export:")), FALSE, FALSE, 0);
354 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
355
356 GtkWidget *view = gtk_text_view_new();
357 gtk_text_view_set_editable(GTK_TEXT_VIEW(view), FALSE);
358 gtk_text_view_set_monospace(GTK_TEXT_VIEW(view), TRUE);
359 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(view), GTK_WRAP_WORD_CHAR);
360 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
361 g_object_set_data_full(G_OBJECT(buffer), "imgids", imgids, (GDestroyNotify)g_list_free);
362 g_object_set_data(G_OBJECT(buffer), "combo", combo);
363
364 GtkWidget *scroll = gtk_scrolled_window_new(NULL, NULL);
365 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
366 gtk_scrolled_window_set_min_content_width(GTK_SCROLLED_WINDOW(scroll), DT_PIXEL_APPLY_DPI(450));
367 gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scroll), DT_PIXEL_APPLY_DPI(300));
368 gtk_container_add(GTK_CONTAINER(scroll), view);
369
370 GtkWidget *content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
371 gtk_box_pack_start(GTK_BOX(content), hbox, FALSE, FALSE, DT_PIXEL_APPLY_DPI(4));
372 gtk_box_pack_start(GTK_BOX(content), scroll, TRUE, TRUE, 0);
373
374 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(_export_list_fill), buffer);
375 g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(_export_list_response), buffer);
376
377 // Setting the active entry fires "changed", which fills the buffer
378 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), DT_EXPORT_LIST_IDS);
379
380 gtk_widget_show_all(dialog);
381 return TRUE;
382}
383
384
394
395void append_file(GtkWidget **menus, GList **lists, const dt_menus_t index)
396{
397 add_sub_menu_entry(menus, lists, _("Import..."), index, NULL, GET_ACCEL_WRAPPER(dt_images_import), NULL, NULL,
398 NULL, GDK_KEY_i, DT_PRIMARY_MASK | GDK_SHIFT_MASK);
399
400 add_sub_menu_entry(menus, lists, _("Export..."), index, NULL, export_files_callback, NULL, NULL,
401 NULL, GDK_KEY_e, DT_PRIMARY_MASK | GDK_SHIFT_MASK);
402
403 add_sub_menu_entry(menus, lists, _("Export image list..."), index, NULL, export_image_list_callback, NULL, NULL,
404 has_selection, 0, 0);
405
406 add_menu_separator(menus[index]);
407
408 add_top_submenu_entry(menus, lists, _("Recent collections"), index);
409 GtkWidget *parent = get_last_widget(lists);
410
411 for(int i = 0; i < NUM_LAST_COLLECTIONS; i++)
412 {
413 // Pass the position of current menuitem in list as custom-data pointer
414 gchar *item_label = g_strdup_printf(_("Most recent collection #%i"), i);
415 add_sub_sub_menu_entry(menus, parent, lists, item_label, index, GINT_TO_POINTER(i), update_collection_callback, NULL, NULL, NULL, 0, 0);
416 dt_free(item_label);
417
418 // Call init directly just this once
419 GtkWidget *this = get_last_widget(lists);
421
422 // Connect init to collection_changed signal for future updates
424 G_CALLBACK(init_collection_line), (gpointer)this);
425 }
426
427 add_menu_separator(menus[index]);
428
429 add_sub_menu_entry(menus, lists, _("Copy files on disk..."), index, NULL, GET_ACCEL_WRAPPER(dt_control_copy_images), NULL, NULL,
430 has_active_images, 0, 0);
431
432 add_sub_menu_entry(menus, lists, _("Move files on disk..."), index, NULL, GET_ACCEL_WRAPPER(dt_control_move_images), NULL, NULL,
433 has_active_images, 0, 0);
434
435 add_sub_menu_entry(menus, lists, _("Create a blended HDR"), index, NULL, GET_ACCEL_WRAPPER(dt_control_merge_hdr), NULL, NULL,
436 has_active_images, 0, 0);
437
438 add_menu_separator(menus[index]);
439
440 add_sub_menu_entry(menus, lists, _("Copy distant images locally"), index, NULL, GET_ACCEL_WRAPPER(dt_control_set_local_copy_images), NULL, NULL,
441 has_active_images, 0, 0);
442
443 add_sub_menu_entry(menus, lists, _("Resynchronize distant images"), index, NULL, GET_ACCEL_WRAPPER(dt_control_reset_local_copy_images), NULL, NULL,
444 has_active_images, 0, 0);
445
446 add_menu_separator(menus[index]);
447
448 add_no_accel_sub_menu_entry(menus, lists, _("Remove from library"), index, NULL, GET_ACCEL_WRAPPER(dt_control_remove_images), NULL, NULL,
449 has_active_image_in_lighttable, GDK_KEY_Delete, 0);
450
451 add_sub_menu_entry(menus, lists, _("Delete from disk"), index, NULL, GET_ACCEL_WRAPPER(dt_control_delete_images), NULL, NULL,
452 has_active_image_in_lighttable, GDK_KEY_Delete, GDK_SHIFT_MASK);
453
454 add_menu_separator(menus[index]);
455
456 add_sub_menu_entry(menus, lists, _("Quit"), index, NULL, GET_ACCEL_WRAPPER(dt_gui_closing_quit), NULL, NULL, NULL, GDK_KEY_q, DT_PRIMARY_MASK);
457}
Handle default and user-set shortcuts (accelerators)
#define DT_PRIMARY_MASK
GtkWidget * dt_gui_main_window(void)
void dt_gui_add_help_link(GtkWidget *widget, char *link)
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
void dt_gui_closing_quit(void)
Definition closing.c:384
@ DT_LIB_COLLECT_MODE_OR
Definition collect.h:39
@ DT_LIB_COLLECT_MODE_AND
Definition collect.h:38
const char * dt_collection_name(dt_collection_properties_t prop)
Definition collection.c:390
void dt_collection_deserialize(const char *buf)
Definition collection.c:763
dt_collection_properties_t
Definition collection.h:120
@ DT_COLLECTION_PROP_LAST
Definition collection.h:153
@ DT_COLLECTION_PROP_UNDEF
Definition collection.h:155
dt_collection_change_t
Definition collection.h:160
@ DT_COLLECTION_CHANGE_NONE
Definition collection.h:161
#define NUM_LAST_COLLECTIONS
Definition collection.h:54
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
const dt_colormatrix_t dt_aligned_pixel_t out
const char * dt_conf_get_string_const(const char *name)
Borrow the stored string for name without copying it.
const char * dt_image_film_roll_name(const char *path)
void dt_image_full_path(const int32_t imgid, char *pathname, size_t pathname_len, gboolean *from_cache, const char *calling_func)
Get the full path of an image out of the database.
void dt_control_alert(const char *title, const char *message, const char *item)
Tell the user something that must not go unseen: what it says stays true after a toast would have fad...
Definition control.c:883
void dt_control_delete_images()
void dt_control_move_images()
void dt_control_reset_local_copy_images()
void dt_control_set_local_copy_images()
gboolean dt_control_remove_images()
void dt_control_merge_hdr()
void dt_control_copy_images()
struct dt_gui_gtk_t * dt_gui_get_global(void)
Definition darktable.c:526
GtkWidget * view
the tree view
dt_export_list_mode_t
Definition file.c:220
@ DT_EXPORT_LIST_IDS
Definition file.c:221
@ DT_EXPORT_LIST_FILENAMES
Definition file.c:222
void init_collection_line(gpointer instance, dt_collection_change_t query_change, dt_collection_properties_t changed_property, gpointer imgs, int next, gpointer user_data)
Definition file.c:118
static gboolean export_files_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition file.c:162
static gboolean update_collection_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition file.c:99
static gchar * _export_list_build(GList *imgids, const int mode, const gboolean one_per_line)
Definition file.c:231
static gboolean export_image_list_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition file.c:324
static void _export_list_response(GtkWidget *dialog, gint response_id, gpointer user_data)
Definition file.c:303
void _close_export_popup(GtkWidget *dialog, gint response_id, gpointer data)
Definition file.c:151
static void _export_list_save(GtkWidget *dialog, GtkTextBuffer *buffer)
Definition file.c:271
static void pretty_print_collection(const char *buf, char *out, size_t outsize)
Definition file.c:46
static void _export_list_fill(GtkComboBox *combo, gpointer user_data)
Definition file.c:262
void append_file(GtkWidget **menus, GList **lists, const dt_menus_t index)
Definition file.c:395
void dt_images_import()
Definition import.c:1636
void dt_lib_gui_set_expanded(dt_lib_module_t *module, gboolean expanded)
Definition lib.c:852
GtkWidget * dt_lib_gui_get_expander(dt_lib_module_t *module)
Definition lib.c:1050
float *const restrict const size_t k
#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_active_image_in_lighttable()
Definition menu.c:666
gboolean has_selection()
Definition menu.c:643
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:597
void add_no_accel_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:566
GtkWidget * get_last_widget(GList **list)
Definition menu.c:631
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:555
void * get_custom_data(GtkWidget *widget)
Definition menu.c:625
void add_top_submenu_entry(GtkWidget **menus, GList **lists, const gchar *label, const dt_menus_t index)
Definition menu.c:528
void add_menu_separator(GtkWidget *menu)
Definition menu.c:611
gboolean has_active_images()
Definition menu.c:649
#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
struct dt_selection_t * dt_selection_get_global(void)
Definition selection.c:80
GList * dt_selection_get_list(struct dt_selection_t *selection)
Definition selection.c:185
struct dt_control_signal_t * dt_control_signal_get_global(void)
Definition darktable.c:619
@ DT_SIGNAL_COLLECTION_CHANGED
This signal is raised when collection changed. To avoid leaking the list, dt_collection_t is connecte...
Definition signal.h:126
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
Definition signal.h:400
struct dt_gui_gtk_t::@34 export_popup
GtkWidget * window
char * dt_get_help_url(char *name)
#define DT_PIXEL_APPLY_DPI(value)
void dt_capitalize_label(gchar *text)