Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
import_jobs.c
Go to the documentation of this file.
1/*
2 This file is part of the Ansel project.
3 Copyright (C) 2025-2026 Aurélien PIERRE.
4
5 Ansel is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 Ansel is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with Ansel. If not, see <http://www.gnu.org/licenses/>.
17*/
18#include "common/darktable.h"
19#include "import_jobs.h"
20#include "common/collection.h"
21#include "common/datetime.h"
22#include "common/exif.h"
23#include "common/metadata.h"
24#include "control/control.h"
25#include "common/image.h"
27#include "gui/gtk.h"
28
29#ifndef _WIN32
30#include <glob.h>
31#endif
32#include <string.h>
33#ifdef __APPLE__
34#include "osx/osx.h"
35#endif
36#ifdef _WIN32
37#include "win/dtwin.h"
38#endif
39
40
48gboolean _create_dir(const char *path)
49{
50 if(g_mkdir_with_parents(path, 0755) == -1)
51 {
52 fprintf(stderr, "failed to create directory %s.\n", path);
53 dt_control_log(_("Impossible to create directory %s.\nThe target may be full or read-only.\n"), path);
54 return TRUE;
55 }
56 return FALSE;
57}
58
66gchar *_path_cleanup(gchar *path_in)
67{
68 gchar *clean = dt_cleanup_separators(path_in);
69 gchar *path_out = dt_util_remove_whitespace(clean);
70 dt_free(clean);
71 return path_out;
72}
73
74gchar *dt_build_filename_from_pattern(const char *const filename, const int index, dt_image_t *img, dt_control_import_t *data)
75{
78 params->filename = g_strdup(filename);
79 params->sequence = index;
80 params->jobcode = g_strdup(data->jobcode);
81 params->imgid = UNKNOWN_IMAGE;
82 params->img = img;
84
85 gchar *file_expand = dt_variables_expand(params, data->target_file_pattern, FALSE);
86 gchar *path_expand = dt_variables_expand(params, data->target_subfolder_pattern, FALSE);
87
88 // remove this if we decide to do the correction on user's settings directly
89 gchar *file = _path_cleanup(file_expand);
90 gchar *path = _path_cleanup(path_expand);
91 dt_free(file_expand);
92 dt_free(path_expand);
93
94 gchar *dir = g_build_path(G_DIR_SEPARATOR_S, data->base_folder, path, (char *) NULL);
96 gchar *res = g_build_path(G_DIR_SEPARATOR_S, data->target_dir, file, (char *) NULL);
97
98 dt_print(DT_DEBUG_PRINT, "[Import] Importing file to %s\n", res);
99
100 dt_free(file);
101 dt_free(path);
102 dt_free(dir);
104 return res;
105}
106
113gboolean _file_exist(const char *dest_file_path)
114{
115 return !IS_NULL_PTR(dest_file_path) && dest_file_path[0] && g_file_test(dest_file_path, G_FILE_TEST_EXISTS);
116}
117
125gboolean _copy_file(const char *filename, const char *dest_file_path)
126{
127 GFile *in = g_file_new_for_path(filename);
128 GFile *out = g_file_new_for_path(dest_file_path);
129
130 gboolean res = g_file_copy(in, out, G_FILE_COPY_NONE, 0, 0, 0, NULL);
131 if(!res) dt_print(DT_DEBUG_IMPORT, "[Import] Could not copy the file %s to %s\n", filename, dest_file_path);
132
133 g_object_unref(in);
134 g_object_unref(out);
135
136 return res;
137}
138
146const int32_t _import_job(dt_control_import_t *data, gchar *img_path_to_db)
147{
148 gchar *dirname = g_strdup(dt_util_path_get_dirname(img_path_to_db));
149
150 dt_film_t film;
151 const int32_t filmid = dt_film_new(&film, dirname);
152 const int32_t imgid = dt_image_import(filmid, img_path_to_db, FALSE);
153 dt_free(dirname);
154 return imgid;
155}
156
169void dt_import_duplicate_get_dest_name(char *xmp_dest_name, const char *dest_file_path, const int counter)
170{
171 char *norm_dest_file = dt_util_normalize_path(dest_file_path);
172 char *ext = norm_dest_file + safe_strlen(norm_dest_file);
173 while(*ext != '.' && ext > norm_dest_file) ext--;
174 const size_t name_len = safe_strlen(norm_dest_file) - safe_strlen(ext);
175
176 if(counter == 0)
177 g_snprintf(xmp_dest_name, PATH_MAX, "%s.xmp", norm_dest_file);
178 else
179 g_snprintf(xmp_dest_name, PATH_MAX, "%.*s_%.2d%s.xmp", (int)name_len, norm_dest_file, counter, ext);
180
181 dt_print(DT_DEBUG_IMPORT, "[Import] XMP destination name: %s\n", xmp_dest_name);
182
183 dt_free(norm_dest_file);
184}
185
193int _import_copy_xmp(const char *const filename, gchar *dest_file_path)
194{
195 int xmp_cntr = 0;
196
197 GList *xmp_files = dt_image_find_xmps(filename); // the first xmp will be the original
198 if(g_list_length(xmp_files) > 0)
199 {
200 for(GList *current_xmp = xmp_files; current_xmp; current_xmp = g_list_next(current_xmp))
201 {
202 char *xmp_source = g_strdup((char*) current_xmp->data);
203 gchar xmp_dest_name[PATH_MAX] = { 0 };
204 dt_import_duplicate_get_dest_name(xmp_dest_name, dest_file_path, xmp_cntr);
205
206 // folder already created and writable, just copy.
207 int success = _copy_file(xmp_source, xmp_dest_name);
208 dt_print(DT_DEBUG_IMPORT, "[Import] copying %s to %s %s\n", xmp_source, xmp_dest_name,
209 (success) ? "succeeded" : "failed");
210 if(success) xmp_cntr++;
211 dt_free(xmp_source);
212 }
213 }
214 g_list_free(xmp_files);
215 xmp_files = NULL;
216 return xmp_cntr;
217}
218
219int _import_copy_txt(const char *const filename, const char *dest_file_path)
220{
221 char *txt_source = dt_image_get_text_path_from_path(filename);
222 if(IS_NULL_PTR(txt_source)) return 0;
223
224 char *txt_dest = dt_image_build_text_path_from_path(dest_file_path);
225 int success = 0;
226
227 if(!IS_NULL_PTR(txt_dest))
228 {
229 success = _copy_file(txt_source, txt_dest);
230 dt_print(DT_DEBUG_IMPORT, "[Import] copying %s to %s %s\n", txt_source, txt_dest,
231 (success) ? "succeeded" : "failed");
232 }
233
234 dt_free(txt_dest);
235 dt_free(txt_source);
236 return success ? 1 : 0;
237}
238
249int _import_copy_file(const char *const filename, const int index, dt_control_import_t *data, gchar *img_path_to_db, size_t pathname_len, GList **discarded)
250{
251 dt_image_t *img = malloc(sizeof(dt_image_t));
252 dt_image_init(img);
253
254 // Generate file I/O only if the pattern is using EXIF variables.
255 // Otherwise, discard it since it's really expensive if the file is on external/remote storage.
256 // This is mandatory BEFORE expanding variables in pattern
257 if(strstr(data->target_file_pattern, "$(EXIF") != NULL
258 || strstr(data->target_subfolder_pattern, "$(EXIF") != NULL )
259 {
260 dt_print(DT_DEBUG_IMPORT, "[Import] EXIF will be read for %s because the pattern needs it (performance penalty)\n", filename);
261 dt_exif_read(img, filename);
262 }
263
264 gchar *dest_file_path = dt_build_filename_from_pattern(filename, index, img, data);
265 dt_print(DT_DEBUG_IMPORT, "[Import] Image %s will be copied into %s\n", filename, dest_file_path);
266 dt_free(img);
267
268 int process = TRUE;
269
270 if(!_file_exist(dest_file_path))
271 {
272 if(!dt_util_dir_exist(data->target_dir))
274 else
275 dt_print(DT_DEBUG_PRINT, "[Import] target folder %s already exists. Nothing to do.\n", data->target_dir);
276
277 if(process)
279 else
280 fprintf(stdout, "[Import] Unable to create the target folder %s.\n", data->target_dir);
281
282 if(process)
283 process = _copy_file(filename, dest_file_path);
284 else
285 fprintf(stdout, "[Import] Not allowed to write in the %s folder.\n", data->target_dir);
286
287 if(process)
288 {
289 _import_copy_xmp(filename, dest_file_path);
290 _import_copy_txt(filename, dest_file_path);
291 }
292
293 if(process)
294 g_strlcpy(img_path_to_db, dest_file_path, pathname_len);
295 else
296 fprintf(stderr, "[Import] Unable to copy the file %s to %s.\n", img_path_to_db, dest_file_path);
297 }
298 else
299 {
300 *discarded = g_list_prepend(*discarded, g_strdup(filename));
301 g_strlcpy(img_path_to_db, dest_file_path, pathname_len);
302 dt_print(DT_DEBUG_IMPORT, "[Import] File copy skipped, the target file %s already exists on the destination.\n", dest_file_path);
303 }
304
305 dt_free(dest_file_path);
306 return !process;
307}
308
309void _write_xmp_id(const char *filename, int32_t imgid)
310{
311 GList *res = dt_metadata_get(imgid, "Xmp.darktable.image_id", NULL);
312 if(!IS_NULL_PTR(res))
313 {
314 // Image ID is already set in metadata, don't overwrite it
315 g_list_free_full(res, dt_free_gpointer);
316 res = NULL;
317 return;
318 }
319 // else : init it
320 GError *error = NULL;
321 GFile *gfile = g_file_new_for_path(filename);
322 GFileInfo *info = g_file_query_info(gfile,
323 G_FILE_ATTRIBUTE_STANDARD_NAME ","
324 G_FILE_ATTRIBUTE_TIME_MODIFIED,
325 G_FILE_QUERY_INFO_NONE, NULL, &error);
326 const char *fn = g_file_info_get_name(info);
327
328 const time_t datetime = g_file_info_get_attribute_uint64(info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
329 char dt_txt[DT_DATETIME_EXIF_LENGTH];
330 dt_datetime_unix_to_exif(dt_txt, sizeof(dt_txt), &datetime);
331 const char *id = g_strconcat(fn, "-", dt_txt, NULL);
332 dt_metadata_set(imgid, "Xmp.darktable.image_id", id, FALSE);
333 g_object_unref(info);
334 g_object_unref(gfile);
335 g_clear_error(&error);
336}
337
346int32_t _import_image(const GList *img, dt_control_import_t *data, const int index, GList **discarded, int *xmps)
347{
348 const char *filename = (const char*) img->data;
349
350 gchar img_path_to_db[PATH_MAX] = { 0 };
351 gboolean process_error = FALSE;
352 int32_t imgid = UNKNOWN_IMAGE;
353
354 if(data->copy)
355 // Copy the file to destination folder, expanding variables internally
356 process_error = _import_copy_file(filename, index, data, img_path_to_db, sizeof(img_path_to_db), discarded);
357 else
358 // destination = origin, nothing to do
359 g_strlcpy(img_path_to_db, filename, sizeof(img_path_to_db));
360
361 if(process_error)
362 ;
363 else if(img_path_to_db[0] == 0)
364 fprintf(stderr, "[Import] Could not import file from disk: empty file path\n");
365 else
366 {
367 imgid = _import_job(data, img_path_to_db);
368
369 if(imgid == UNKNOWN_IMAGE)
370 {
371 dt_control_log(_("Error importing file in collection: %s"), img_path_to_db);
372 fprintf(stderr, "[Import] Error importing file in collection: %s", img_path_to_db);
373 }
374 else
375 {
376 // read all sidecar files (including the original one) and import them if not found in db.
377 *xmps = dt_image_read_duplicates(imgid, img_path_to_db, FALSE);
378 dt_print(DT_DEBUG_IMPORT, "[Import] Found and imported %i XMP for %s.\n", *xmps, img_path_to_db);
379 dt_print(DT_DEBUG_IMPORT, "[Import] successfully imported %s in DB at imgid %i\n", img_path_to_db, imgid);
380 }
381 }
382
383 return imgid;
384}
385
386void _refresh_progress_counter(dt_job_t *job, const int elements, const int index)
387{
388 gchar message[32] = { 0 };
389 double fraction = (double)index / (double)elements;
390 snprintf(message, sizeof(message), ngettext("importing %i/%i image", "importing %i/%i images", index), index, elements);
392 dt_control_job_set_progress(job, fraction);
393 g_usleep(100);
394}
395
397{
399 dt_control_import_t *data = params->data;
400
401 int index = 0;
402 int xmps = 0; // number of xmps imported in db.
403 int32_t imgid = UNKNOWN_IMAGE;
404
405 for(GList *img = g_list_first(data->imgs); img; img = g_list_next(img))
406 {
407 dt_print(DT_DEBUG_IMPORT, "[Import] starting import of image #%i...\n", index);
408
409 _refresh_progress_counter(job, data->elements, index);
410 imgid = _import_image(img, data, index, &data->discarded, &xmps);
411
412 if(imgid > UNKNOWN_IMAGE)
413 {
414 // On the first image, try to switch the current filmroll to the imported image's folder.
415 // dt_collection_load_filmroll() silently declines to do anything (no collection refresh)
416 // when it cannot switch folders, e.g. the collect module is not on the "Folders" tab. In
417 // that case a single imported image would never show up until the user reloads the
418 // collection by hand (issue #860). So always run a collection update afterwards: it
419 // re-runs the current query and makes newly-imported matching images appear.
420 if(index == 0)
422
424
425 index++;
426 }
427 }
428
429 if(index == 0)
430 {
431 dt_control_log(_("No image imported!"));
432 fprintf(stderr, "No image imported!\n\n");
433 }
434 // don't open picture in darkroom if more than 1 xmps (= duplicates) have been imported.
435 else if(index == 1 && xmps == 1)
436 {
438 }
439 else
440 {
441 dt_control_log(ngettext("imported %d image", "imported %d images", index), index);
442 fprintf(stdout, "%d files imported in database.\n\n", index);
443 }
444
445 dt_conf_set_int("ui_last/nb_imported", index);
446
447 return index >= 1 ? 0 : 1;
448}
449
451{
452 g_date_time_unref(data->datetime);
453 dt_free(data->jobcode);
454 dt_free(data->base_folder);
457 dt_free(data->target_dir);
458
459 // GList of pathes stored as *char. We need to free the list and the *char
460 if(data->discarded)
461 {
462 g_list_free_full(data->discarded, dt_free_gpointer);
463 data->discarded = NULL;
464 }
465 if(data->imgs)
466 {
467 g_list_free_full(data->imgs, dt_free_gpointer);
468 data->imgs = NULL;
469 }
470}
471
473{
474 dt_control_import_t *data = params->data;
475
476 // Create the window
477 GtkWidget *dialog = gtk_dialog_new_with_buttons("Message",
478 GTK_WINDOW(dt_ui_main_window(darktable.gui->ui)),
479 GTK_DIALOG_DESTROY_WITH_PARENT,
480 _("_OK"),
481 GTK_RESPONSE_NONE,
482 NULL);
483 gtk_window_set_title(GTK_WINDOW(dialog), _("Some files have not been copied"));
484 gtk_window_set_default_size(GTK_WINDOW(dialog), DT_PIXEL_APPLY_DPI(800), DT_PIXEL_APPLY_DPI(800));
485
486 // Create the label
487 GtkWidget *label = gtk_label_new(_("The following source files have not been copied "
488 "because similarly-named files already exist on the destination. "
489 "This may be because the files have already been imported "
490 "or the naming pattern leads to non-unique file names."));
491 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
492
493 // Create the scrolled window internal container
494 GtkWidget *scrolled_window = gtk_scrolled_window_new (NULL, NULL);
495 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), GTK_POLICY_AUTOMATIC,
496 GTK_POLICY_AUTOMATIC);
497 gtk_scrolled_window_set_propagate_natural_height(GTK_SCROLLED_WINDOW(scrolled_window), TRUE);
498
499 // Create the treeview model from the list of discarded file pathes
500 GtkListStore *store = gtk_list_store_new(1, G_TYPE_STRING);
501 GtkTreeIter iter;
502 for(GList *file = g_list_first(data->discarded); file; file = g_list_next(file))
503 {
504 if(file->data)
505 {
506 gtk_list_store_append(store, &iter);
507 gtk_list_store_set(store, &iter, 0, (char *)file->data, -1);
508 }
509 }
510
511 // Create the treeview view. Sooooo verbose... it's only a flat list.
512 GtkWidget *view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
513 GtkTreeViewColumn *col = gtk_tree_view_column_new();
514 gtk_tree_view_column_set_title(col, _("Origin path"));
515 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
516 gtk_tree_view_column_pack_start(col, renderer, TRUE);
517 gtk_tree_view_column_set_attributes(col, renderer, "text", 0, NULL);
518 gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
519 g_object_unref(store);
520
521 // Pack widgets to an unified box
522 GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
523 gtk_box_pack_start(GTK_BOX(box), label, TRUE, TRUE, 0);
524 gtk_box_pack_start(GTK_BOX(box), scrolled_window, TRUE, TRUE, 0);
525 dt_gui_add_class(scrolled_window, "dt_recessed_scroll");
526 gtk_container_add(GTK_CONTAINER(scrolled_window), view);
527
528 // Pack the box to the dialog internal container
529 GtkWidget *content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
530 gtk_container_add(GTK_CONTAINER(content_area), box);
531 gtk_widget_show_all(dialog);
532
533#ifdef GDK_WINDOWING_QUARTZ
535#endif
536
537 gtk_dialog_run(GTK_DIALOG(dialog));
538 gtk_widget_destroy(dialog);
539
541 dt_free(data);
543
544 return 0;
545}
546
548{
550 dt_control_import_t *data = params->data;
551
552 // Display a recap of files that weren't copied
553 if(g_list_length(data->discarded) > 0)
554 {
555 g_main_context_invoke(NULL, (GSourceFunc)_discarded_files_popup, (gpointer)params);
556 // we will free data and params from the function since it's run asynchronously
557 }
558 else
559 {
561 dt_free(data);
563 }
564}
565
567{
569 if(IS_NULL_PTR(params)) return NULL;
570
571 params->data = g_malloc0(sizeof(dt_control_import_t));
572 if(IS_NULL_PTR(params->data))
573 {
575 return NULL;
576 }
577 return params;
578}
579
581{
583 if(IS_NULL_PTR(job)) return NULL;
585 if(IS_NULL_PTR(params))
586 {
588 return NULL;
589 }
590 memcpy(params->data, &data, sizeof(dt_control_import_t));
591 params->index = NULL;
592 dt_control_job_add_progress(job, _("import"), FALSE);
594 return job;
595}
596
__DT_CLONE_TARGETS__ int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid)
Definition ashift.c:3153
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
void dt_collection_load_filmroll(dt_collection_t *collection, const int32_t imgid, gboolean open_single_image)
void dt_collection_update_query(const dt_collection_t *collection, dt_collection_change_t query_change, dt_collection_properties_t changed_property, GList *list)
@ DT_COLLECTION_PROP_UNDEF
Definition collection.h:142
@ DT_COLLECTION_CHANGE_NEW_QUERY
Definition collection.h:149
const dt_colormatrix_t dt_aligned_pixel_t out
int32_t dt_image_import(const int32_t film_id, const char *filename, gboolean raise_signals)
char * dt_image_get_text_path_from_path(const char *image_path)
void dt_image_init(dt_image_t *img)
GList * dt_image_find_xmps(const char *filename)
int dt_image_read_duplicates(const uint32_t id, const char *filename, const gboolean clear_selection)
char * dt_image_build_text_path_from_path(const char *image_path)
void dt_metadata_set(const int32_t imgid, const char *key, const char *value, const gboolean undo_on)
GList * dt_metadata_get(const int id, const char *key, uint32_t *count)
void dt_conf_set_int(const char *name, int val)
void dt_control_log(const char *msg,...)
Definition control.c:761
void * dt_control_image_enumerator_alloc()
void dt_control_image_enumerator_cleanup(void *p)
uint32_t view(const dt_view_t *self)
Definition darkroom.c:227
darktable_t darktable
Definition darktable.c:181
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1542
#define UNKNOWN_IMAGE
Definition darktable.h:182
@ DT_DEBUG_PRINT
Definition darktable.h:730
@ DT_DEBUG_IMPORT
Definition darktable.h:742
static void dt_free_gpointer(gpointer ptr)
Definition darktable.h:463
#define dt_free(ptr)
Definition darktable.h:456
#define PATH_MAX
Definition darktable.h:1062
#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_datetime_unix_to_exif(char *exif, const size_t exif_size, const time_t *unix)
Definition datetime.c:191
#define DT_DATETIME_EXIF_LENGTH
Definition datetime.h:38
int store(dt_imageio_module_storage_t *self, dt_imageio_module_data_t *sdata, const int32_t imgid, dt_imageio_module_format_t *format, dt_imageio_module_data_t *fdata, const int num, const int total, const gboolean high_quality, const gboolean export_masks, dt_colorspaces_color_profile_type_t icc_type, const gchar *icc_filename, dt_iop_color_intent_t icc_intent, dt_export_metadata_t *metadata)
Definition disk.c:252
int dt_exif_read(dt_image_t *img, const char *path)
Definition exif.cc:1753
int dt_film_new(dt_film_t *film, const char *directory)
Definition film.c:161
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_GUI_BOX_SPACING
Definition gtk.h:109
#define DT_PIXEL_APPLY_DPI(value)
Definition gtk.h:90
int _import_copy_file(const char *const filename, const int index, dt_control_import_t *data, gchar *img_path_to_db, size_t pathname_len, GList **discarded)
copy a file to a destination path after checking if everything is allright.
static void * _control_import_alloc()
int _import_copy_xmp(const char *const filename, gchar *dest_file_path)
Attempt to find all sidecar XMP files along an image file and import (copy) it to destination.
gboolean _create_dir(const char *path)
Creates folders from path. Returns TRUE if success.
Definition import_jobs.c:48
static void _control_import_job_cleanup(void *p)
gchar * dt_build_filename_from_pattern(const char *const filename, const int index, dt_image_t *img, dt_control_import_t *data)
Build a full path for a given image file, given a pattern.
Definition import_jobs.c:74
gboolean _copy_file(const char *filename, const char *dest_file_path)
Just copy a file. Returns 1 if success.
void dt_import_duplicate_get_dest_name(char *xmp_dest_name, const char *dest_file_path, const int counter)
Gets the computed xmp file name with apropriate number for import copy. It computes a duplicate name ...
static int _discarded_files_popup(dt_control_image_enumerator_t *params)
int32_t _import_image(const GList *img, dt_control_import_t *data, const int index, GList **discarded, int *xmps)
process to copy (or not) and import an image to database.
static int32_t _control_import_job_run(dt_job_t *job)
void _refresh_progress_counter(dt_job_t *job, const int elements, const int index)
void dt_control_import(dt_control_import_t data)
Process a list of images to import with or without copying the files on an arbitrary hard-drive.
static dt_job_t * _control_import_job_create(dt_control_import_t data)
gboolean _file_exist(const char *dest_file_path)
Tests if file exist. Returns 1 if so.
const int32_t _import_job(dt_control_import_t *data, gchar *img_path_to_db)
Add an image entry in the database and returns its imgID.
void _write_xmp_id(const char *filename, int32_t imgid)
gchar * _path_cleanup(gchar *path_in)
Replaces separator depending of the current OS and removes whitespaces.
Definition import_jobs.c:66
int _import_copy_txt(const char *const filename, const char *dest_file_path)
void dt_control_import_data_free(dt_control_import_t *data)
dt_job_t * dt_control_job_create(dt_job_execute_callback execute, const char *msg,...)
Definition jobs.c:135
int dt_control_add_job(dt_control_t *control, dt_job_queue_t queue_id, _dt_job_t *job)
Definition jobs.c:405
void * dt_control_job_get_params(const _dt_job_t *job)
Definition jobs.c:129
void dt_control_job_set_progress(dt_job_t *job, double value)
Definition jobs.c:626
void dt_control_job_add_progress(dt_job_t *job, const char *message, gboolean cancellable)
Definition jobs.c:612
void dt_control_job_set_progress_message(dt_job_t *job, const char *message)
Definition jobs.c:620
void dt_control_job_set_params(_dt_job_t *job, void *params, dt_job_destroy_callback callback)
Definition jobs.c:112
void dt_control_job_dispose(_dt_job_t *job)
Definition jobs.c:153
@ DT_JOB_QUEUE_USER_FG
Definition jobs.h:53
void dt_osx_disallow_fullscreen(GtkWidget *widget)
Definition osx.mm:104
struct _GtkWidget GtkWidget
Definition splash.h:29
char * dt_variables_expand(dt_variables_params_t *params, gchar *source, gboolean iterate)
void dt_variables_params_destroy(dt_variables_params_t *params)
void dt_variables_params_init(dt_variables_params_t **params)
void dt_variables_set_datetime(dt_variables_params_t *params, GDateTime *datetime)
struct dt_gui_gtk_t * gui
Definition darktable.h:775
struct dt_collection_t * collection
Definition darktable.h:781
struct dt_control_t * control
Definition darktable.h:773
GDateTime * datetime
Definition import_jobs.h:32
char * target_subfolder_pattern
Definition import_jobs.h:43
dt_ui_t * ui
Definition gtk.h:164
typedef double((*spd)(unsigned long int wavelength, double TempK))
gchar * dt_cleanup_separators(gchar *string)
Definition utility.c:1048
gboolean dt_util_dir_exist(const char *dir)
Definition utility.c:367
gchar * dt_util_path_get_dirname(const gchar *filename)
Definition utility.c:774
gchar * dt_util_normalize_path(const gchar *_input)
Definition utility.c:680
size_t safe_strlen(const char *str)
check if the string is empty or NULL before calling strlen()
Definition utility.c:90
gboolean dt_util_test_writable_dir(const char *path)
Definition utility.c:344
gchar * dt_util_remove_whitespace(const gchar *path)
Definition utility.c:1059