Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
control_jobs.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2010-2012 Henrik Andersson.
4 Copyright (C) 2010-2014, 2016 johannes hanika.
5 Copyright (C) 2010 kibooz.
6 Copyright (C) 2010-2017, 2019-2020 Tobias Ellinghaus.
7 Copyright (C) 2011 Antony Dovgal.
8 Copyright (C) 2011 José Carlos García Sogo.
9 Copyright (C) 2011 Karl Mikaelsson.
10 Copyright (C) 2011 Robert Bieber.
11 Copyright (C) 2012 Christian Tellefsen.
12 Copyright (C) 2012 Frédéric Grollier.
13 Copyright (C) 2012 James C. McPherson.
14 Copyright (C) 2012 Jens Fendler.
15 Copyright (C) 2012 Joao Trindade.
16 Copyright (C) 2012 marcel.
17 Copyright (C) 2012 Michal Babej.
18 Copyright (C) 2012 Pascal de Bruijn.
19 Copyright (C) 2012-2015, 2018-2022 Pascal Obry.
20 Copyright (C) 2012 Richard Wonka.
21 Copyright (C) 2012-2013 Simon Spannagel.
22 Copyright (C) 2013, 2015 Jérémy Rosen.
23 Copyright (C) 2013 Pierre Le Magourou.
24 Copyright (C) 2013-2016 Roman Lebedev.
25 Copyright (C) 2013 Thomas Pryds.
26 Copyright (C) 2013 Ulrich Pegelow.
27 Copyright (C) 2014 Dan Torop.
28 Copyright (C) 2015 Bruce Guenter.
29 Copyright (C) 2015 Guillaume Benny.
30 Copyright (C) 2015 Nikolai Ugelvik.
31 Copyright (C) 2016 Matthieu Volat.
32 Copyright (C) 2016-2018 parafin.
33 Copyright (C) 2017 pgkos.
34 Copyright (C) 2018 Peter Budai.
35 Copyright (C) 2019 Andreas Schneider.
36 Copyright (C) 2019 August Schwerdfeger.
37 Copyright (C) 2019 Denis Dyakov.
38 Copyright (C) 2019 Edgardo Hoszowski.
39 Copyright (C) 2019-2020 Heiko Bauke.
40 Copyright (C) 2019-2022 Philippe Weyland.
41 Copyright (C) 2019 Sam Smith.
42 Copyright (C) 2020-2022 Aldric Renaudin.
43 Copyright (C) 2020 Hanno Schwalm.
44 Copyright (C) 2020-2021 Hubert Kowalski.
45 Copyright (C) 2020 JP Verrue.
46 Copyright (C) 2020 Marco.
47 Copyright (C) 2020-2021 Ralf Brown.
48 Copyright (C) 2020 Robert Bridge.
49 Copyright (C) 2020 U-DESKTOP-HQME86J\marco.
50 Copyright (C) 2021 Bill Ferguson.
51 Copyright (C) 2021-2022 Chris Elston.
52 Copyright (C) 2021 Cobert0.
53 Copyright (C) 2021 Diederik Ter Rahe.
54 Copyright (C) 2021 Felipe Contreras.
55 Copyright (C) 2021 Marco Carrarini.
56 Copyright (C) 2021 quovadit.
57 Copyright (C) 2022-2025 Aurélien PIERRE.
58 Copyright (C) 2022 Martin Bařinka.
59 Copyright (C) 2022 Paolo Benvenuto.
60 Copyright (C) 2022 Victor Forsiuk.
61 Copyright (C) 2023-2024 Alynx Zhou.
62 Copyright (C) 2023 Ricky Moon.
63 Copyright (C) 2024-2025 Guillaume Stutin.
64 Copyright (C) 2024 Miguel Moquillon.
65
66 darktable is free software: you can redistribute it and/or modify
67 it under the terms of the GNU General Public License as published by
68 the Free Software Foundation, either version 3 of the License, or
69 (at your option) any later version.
70
71 darktable is distributed in the hope that it will be useful,
72 but WITHOUT ANY WARRANTY; without even the implied warranty of
73 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
74 GNU General Public License for more details.
75
76 You should have received a copy of the GNU General Public License
77 along with darktable. If not, see <http://www.gnu.org/licenses/>.
78*/
79
81#include "common/collection.h"
82#include "common/darktable.h"
83#include "common/debug.h"
84#include "common/exif.h"
85#include "common/film.h"
86#include "common/gpx.h"
87#include "common/history.h"
89#include "common/image.h"
90#include "common/image_cache.h"
91#include "common/imageio.h"
92#include "common/imageio_dng.h"
94#include "common/mipmap_cache.h"
95#include "common/tags.h"
96#include "common/undo.h"
97#include "common/grouping.h"
98#include "common/utility.h"
99#include "common/datetime.h"
100#include "control/conf.h"
101#include "develop/imageop_math.h"
102#include "develop/develop.h"
103
104#include "common/selection.h"
105
106#include "gui/gtk.h"
107
108#include <gio/gio.h>
109#include <glib.h>
110#include <glib/gstdio.h>
111#ifndef _WIN32
112#include <glob.h>
113#endif
114#ifdef __APPLE__
115#include "osx/osx.h"
116#endif
117#ifdef _WIN32
118#include "win/dtwin.h"
119#endif
120
121// Control of the collection updates during an import. Start with a short interval to feel responsive,
122// but use fairly infrequent updates for large imports to minimize overall time.
123#define INIT_UPDATE_INTERVAL 2 //seconds
124#define MAX_UPDATE_INTERVAL 3.0 //seconds
125// How long (in seconds) between updates of the "importing N/M" progress indicator? Should be relatively
126// short to avoid the impression that the import has gotten stuck. Setting this too low will impact the
127// overall time for a large import.
128#define PROGRESS_UPDATE_INTERVAL 1
129
135
141
143{
145 dt_imageio_module_data_t *sdata; // needed since the gui thread resets things like overwrite once the export
146 // is dispatched, but we have to keep that information
147 gboolean export_masks;
148 char style[128];
154
155
156/* enumerator of images from filmroll */
158{
159 sqlite3_stmt *stmt;
160 /* get a list of images in filmroll */
161 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "SELECT id FROM main.images WHERE film_id = ?1", -1,
162 &stmt, NULL);
163 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, filmid);
164
165 while(sqlite3_step(stmt) == SQLITE_ROW)
166 {
167 const int32_t imgid = sqlite3_column_int(stmt, 0);
168 t->index = g_list_append(t->index, GINT_TO_POINTER(imgid));
169 }
170 sqlite3_finalize(stmt);
171}
172
174 int32_t (*fileop_callback)(const int32_t,
175 const int32_t),
176 const char *desc, const char *desc_pl)
177{
179 GList *t = params->index;
180 const guint total = g_list_length(t);
181 char message[512] = { 0 };
182 double fraction = 0;
183 gchar *newdir = (gchar *)params->data;
184
185 g_snprintf(message, sizeof(message), ngettext(desc, desc_pl, total), total);
187
188 // create new film roll for the destination directory
189 dt_film_t new_film;
190 const int32_t film_id = dt_film_new(&new_film, newdir);
191 dt_free(newdir);
192
193 if(film_id <= 0)
194 {
195 dt_control_log(_("failed to create film roll for destination directory, aborting move.."));
196 return -1;
197 }
198
199 gboolean completeSuccess = TRUE;
201 {
202 completeSuccess &= (fileop_callback(GPOINTER_TO_INT(t->data), film_id) != -1);
203 t = g_list_next(t);
204 fraction += 1.0 / total;
205 dt_control_job_set_progress(job, fraction);
206 }
207
208 if(completeSuccess)
209 {
210 char collect[1024];
211 snprintf(collect, sizeof(collect), "1:0:0:%s$", new_film.dirname);
213 }
217 g_list_copy(params->index));
219 return 0;
220}
221
223{
225 if(IS_NULL_PTR(params)) return NULL;
226 return params;
227}
228
230{
232
233 if(params->index)
234 {
235 g_list_free(params->index);
236 params->index = NULL;
237 }
238 params->index = NULL;
239
240 dt_free(params);
241}
242
244
246 int flag, gpointer data, progress_type_t progress_type,
247 gboolean only_visible)
248{
249 dt_job_t *job = dt_control_job_create(execute, "%s", message);
250 if(IS_NULL_PTR(job)) return NULL;
252 if(IS_NULL_PTR(params))
253 {
255 return NULL;
256 }
257 if(progress_type != PROGRESS_NONE)
258 dt_control_job_add_progress(job, _(message), progress_type == PROGRESS_CANCELLABLE);
259 params->index = dt_act_on_get_images();
260
262
263 params->flag = flag;
264 params->data = data;
265 return job;
266}
267
269 int flag, gpointer data, progress_type_t progress_type,
270 int32_t imgid)
271{
272 dt_job_t *job = dt_control_job_create(execute, "%s", message);
273 if(IS_NULL_PTR(job)) return NULL;
275 if(IS_NULL_PTR(params))
276 {
278 return NULL;
279 }
280 if(progress_type != PROGRESS_NONE)
281 dt_control_job_add_progress(job, _(message), progress_type == PROGRESS_CANCELLABLE);
282
283 params->index = g_list_append(NULL, GINT_TO_POINTER(imgid));
284
286
287 params->flag = flag;
288 params->data = data;
289 return job;
290}
291
292
294{
295 if(!dt_image_get_xmp_mode()) return 0;
296
298
299 for(GList *t = params->index; t; t = g_list_next(t))
300 {
301 const int32_t imgid = GPOINTER_TO_INT(t->data);
303 fprintf(stdout,
304 "cannot write XMP file for image %i. The target storage may be unavailable or read-only.\n",
305 imgid);
306 }
307 return 0;
308}
309
311{
312 GList *imgs = dt_act_on_get_images();
313 if(IS_NULL_PTR(imgs)) return;
315 g_list_free(imgs);
316 imgs = NULL;
317}
318
320{
321 uint32_t first_imgid;
322 uint32_t first_filter;
323 uint8_t first_xtrans[6][6];
324
325 float *pixels, *weight;
326
327 int wd;
328 int ht;
330
332 float epsw;
334 float adobe_XYZ_to_CAM[4][3];
336
337 // 0 - ok; 1 - errors, abort
338 gboolean abort;
340
346
348{
349 return 32;
350}
351
356
358{
359 return "memory";
360}
361
362static float envelope(const float xx)
363{
364 const float x = CLAMPS(xx, 0.0f, 1.0f);
365 // const float alpha = 2.0f;
366 const float beta = 0.5f;
367 if(x < beta)
368 {
369 // return 1.0f-fabsf(x/beta-1.0f)^2
370 const float tmp = fabsf(x / beta - 1.0f);
371 return 1.0f - tmp * tmp;
372 }
373 else
374 {
375 const float tmp1 = (1.0f - x) / (1.0f - beta);
376 const float tmp2 = tmp1 * tmp1;
377 const float tmp3 = tmp2 * tmp1;
378 return 3.0f * tmp2 - 2.0f * tmp3;
379 }
380}
381
382static int dt_control_merge_hdr_process(dt_imageio_module_data_t *datai, const char *filename,
383 const void *const ivoid,
384 dt_colorspaces_color_profile_type_t over_type, const char *over_filename,
385 void *exif, int exif_len, int32_t imgid, int num, int total,
386 dt_dev_pixelpipe_t *pipe, const gboolean export_masks)
387{
389 dt_control_merge_hdr_t *d = data->d;
390
391 // just take a copy. also do it after blocking read, so filters will make sense.
392 const dt_image_t *img = dt_image_cache_get(darktable.image_cache, imgid, 'r');
393 const dt_image_t image = *img;
395
396 if(IS_NULL_PTR(d->pixels))
397 {
398 d->first_imgid = imgid;
399 d->first_filter = image.dsc.filters;
400 // sensor layout is just passed on to be written to dng.
401 // we offset it to the crop of the image here, so we don't
402 // need to load in the FCxtrans dependency into the dng writer.
403 // for some stupid reason the dng needs this layout wrt cropped
404 // offsets, not globally.
405 dt_iop_roi_t roi = {0};
406 roi.x = image.crop_x;
407 roi.y = image.crop_y;
408 for(int j=0;j<6;j++)
409 for(int i = 0; i < 6; i++) d->first_xtrans[j][i] = FCxtrans(j, i, &roi, image.dsc.xtrans);
410 d->pixels = calloc((size_t)datai->width * datai->height, sizeof(float));
411 d->weight = calloc((size_t)datai->width * datai->height, sizeof(float));
412 d->wd = datai->width;
413 d->ht = datai->height;
414 d->orientation = image.orientation;
415 for(int i = 0; i < 3; i++)
416 d->wb_coeffs[i] = image.wb_coeffs[i];
417 for(int k=0; k<4; k++)
418 for(int i=0; i<3; i++)
419 d->adobe_XYZ_to_CAM[k][i] = image.adobe_XYZ_to_CAM[k][i];
420 }
421
422 if(image.dsc.filters == 0u || image.dsc.channels != 1 || image.dsc.datatype != TYPE_UINT16)
423 {
424 dt_control_log(_("exposure bracketing only works on raw images."));
425 d->abort = TRUE;
426 return 1;
427 }
428 else if(datai->width != d->wd || datai->height != d->ht || d->first_filter != image.dsc.filters
429 || d->orientation != image.orientation)
430 {
431 dt_control_log(_("images have to be of same size and orientation!"));
432 d->abort = TRUE;
433 return 1;
434 }
435
436 // if no valid exif data can be found, assume peleng fisheye at f/16, 8mm, with half of the light lost in
437 // the system => f/22
438 const float eap = image.exif_aperture > 0.0f ? image.exif_aperture : 22.0f;
439 const float efl = image.exif_focal_length > 0.0f ? image.exif_focal_length : 8.0f;
440 const float rad = .5f * efl / eap;
441 const float aperture = M_PI * rad * rad;
442 const float iso = image.exif_iso > 0.0f ? image.exif_iso : 100.0f;
443 const float exp = image.exif_exposure > 0.0f ? image.exif_exposure : 1.0f;
444 const float cal = 100.0f / (aperture * exp * iso);
445 // about proportional to how many photons we can expect from this shot:
446 const float photoncnt = 100.0f * aperture * exp / iso;
447 float saturation = 1.0f;
448 d->whitelevel = fmaxf(d->whitelevel, saturation * cal);
449 __OMP_PARALLEL_FOR__(collapse(2))
450 for(int y = 0; y < d->ht; y++)
451 for(int x = 0; x < d->wd; x++)
452 {
453 // read unclamped raw value with subtracted black and rescaled to 1.0 saturation.
454 // this is the output of the rawprepare iop.
455 const float in = ((float *)ivoid)[x + d->wd * y];
456 // weights based on siggraph 12 poster
457 // zijian zhu, zhengguo li, susanto rahardja, pasi fraenti
458 // 2d denoising factor for high dynamic range imaging
459 float w = photoncnt;
460
461 // need some safety margin due to upsampling and 16-bit quantization + dithering?
462 float offset = 3000.0f / (float)UINT16_MAX;
463
464 // cannot do an envelope based on single pixel values here, need to get
465 // maximum value of all color channels. to find that, go through the
466 // pattern block (we conservatively do a 3x3 for bayer or xtrans):
467 int xx = x & ~1, yy = y & ~1;
468 float M = 0.0f, m = FLT_MAX;
469 if(xx < d->wd - 2 && yy < d->ht - 2)
470 {
471 for(int i = 0; i < 3; i++)
472 for(int j = 0; j < 3; j++)
473 {
474 M = MAX(M, ((float *)ivoid)[xx + i + d->wd * (yy + j)]);
475 m = MIN(m, ((float *)ivoid)[xx + i + d->wd * (yy + j)]);
476 }
477 // move envelope a little to allow non-zero weight even for clipped regions.
478 // this is because even if the 2x2 block is clipped somewhere, the other channels
479 // might still prove useful. we'll check for individual channel saturation below.
480 w *= d->epsw + envelope((M + offset) / saturation);
481 }
482
483 if(M + offset >= saturation)
484 {
485 if(d->weight[x + d->wd * y] <= 0.0f)
486 { // only consider saturated pixels in case we have nothing better:
487 if(d->weight[x + d->wd * y] == 0 || m < -d->weight[x + d->wd * y])
488 {
489 if(m + offset >= saturation)
490 d->pixels[x + d->wd * y] = 1.0f; // let's admit we were completely clipped, too
491 else
492 d->pixels[x + d->wd * y] = in * cal / d->whitelevel;
493 d->weight[x + d->wd * y]
494 = -m; // could use -cal here, but m is per pixel and safer for varying illumination conditions
495 }
496 }
497 // else silently ignore, others have filled in a better color here already
498 }
499 else
500 {
501 if(d->weight[x + d->wd * y] <= 0.0)
502 { // cleanup potentially blown highlights from earlier images
503 d->pixels[x + d->wd * y] = 0.0f;
504 d->weight[x + d->wd * y] = 0.0f;
505 }
506 d->pixels[x + d->wd * y] += w * in * cal;
507 d->weight[x + d->wd * y] += w;
508 }
509 }
510
511 return 0;
512}
513
515{
517 GList *t = params->index;
518 const guint total = g_list_length(t);
519 char message[512] = { 0 };
520 double fraction = 0;
521 snprintf(message, sizeof(message), ngettext("merging %d image", "merging %d images", total), total);
522
524
526
530 .write_image = dt_control_merge_hdr_process };
531
533
534 int num = 1;
535 while(t)
536 {
537 if(d.abort) goto end;
538
539 const int32_t imgid = GPOINTER_TO_INT(t->data);
540
541 const gboolean is_scaling =
542 dt_conf_is_equal("plugins/lighttable/export/resizing", "scaling");
543
544 dt_imageio_export_with_flags(imgid, "unused", &buf, (dt_imageio_module_data_t *)&dat, TRUE, FALSE, FALSE, is_scaling,
545 FALSE, "pre:rawprepare", FALSE, FALSE, DT_COLORSPACE_NONE, NULL, DT_INTENT_LAST, NULL,
546 NULL, num, total, NULL, NULL);
547
548 t = g_list_next(t);
549
550 /* update the progress bar */
551 fraction += 1.0 / (total + 1);
552 dt_control_job_set_progress(job, fraction);
553 num++;
554 }
555
556 if(d.abort) goto end;
557
558// normalize by white level to make clipping at 1.0 work as expected
560 for(size_t k = 0; k < (size_t)d.wd * d.ht; k++)
561 {
562 if(d.weight[k] > 0.0) d.pixels[k] = fmaxf(0.0f, d.pixels[k] / (d.whitelevel * d.weight[k]));
563 }
564
565 // output hdr as digital negative with exif data.
566 uint8_t *exif = NULL;
567 char pathname[PATH_MAX] = { 0 };
568 gboolean from_cache = TRUE;
569 dt_image_full_path(d.first_imgid, pathname, sizeof(pathname), &from_cache, __FUNCTION__);
570
571 // last param is dng mode
572 const int exif_len = dt_exif_read_blob(&exif, pathname, d.first_imgid, 0, d.wd, d.ht, 1);
573 char *c = pathname + safe_strlen(pathname);
574 while(*c != '.' && c > pathname) c--;
575 g_strlcpy(c, "-hdr.dng", sizeof(pathname) - (c - pathname));
576 dt_imageio_write_dng(pathname,
577 d.pixels,
578 d.wd,
579 d.ht,
580 exif,
581 exif_len,
582 d.first_filter,
583 (const uint8_t (*)[6])d.first_xtrans,
584 1.0f,
585 (const float (*))d.wb_coeffs,
586 d.adobe_XYZ_to_CAM);
587 dt_free(exif);
588
590
591 while(*c != '/' && c > pathname) c--;
592 dt_control_log(_("wrote merged HDR `%s'"), c + 1);
593
594 // import new image
595 gchar *directory = g_path_get_dirname((const gchar *)pathname);
596 dt_film_t film;
597 const int filmid = dt_film_new(&film, directory);
598 const uint32_t imageid = dt_image_import(filmid, pathname, TRUE);
599 dt_free(directory);
600
601 // refresh the thumbtable view
603 g_list_prepend(NULL, GINT_TO_POINTER(imageid)));
606
607end:
608 dt_free(d.pixels);
609 dt_free(d.weight);
610
611 return 0;
612}
613
615{
617 GList *t = params->index;
618 const guint total = g_list_length(t);
619 double fraction = 0.0f;
620 char message[512] = { 0 };
621
623
624 snprintf(message, sizeof(message), ngettext("duplicating %d image", "duplicating %d images", total), total);
626 while(t)
627 {
628 const int32_t imgid = GPOINTER_TO_INT(t->data);
629 const int newimgid = dt_image_duplicate(imgid);
630 if(newimgid != UNKNOWN_IMAGE)
631 {
632 if(GPOINTER_TO_INT(params->data))
634 else
636
638 }
639 t = g_list_next(t);
640 fraction += 1.0 / total;
641 dt_control_job_set_progress(job, fraction);
642 }
643
645
648 return 0;
649}
650
652{
654 const int cw = params->flag;
655 GList *t = params->index;
656 const guint total = g_list_length(t);
657 double fraction = 0.0f;
658 char message[512] = { 0 };
659
661
662 snprintf(message, sizeof(message), ngettext("flipping %d image", "flipping %d images", total), total);
664 while(t)
665 {
666 const int32_t imgid = GPOINTER_TO_INT(t->data);
667 dt_image_flip(imgid, cw);
668 t = g_list_next(t);
669 fraction += 1.0 / total;
670 dt_control_job_set_progress(job, fraction);
671 }
672
675 return 0;
676}
678{
680 const int32_t mode = params->flag;
681 GList *t = params->index;
682 const guint total = g_list_length(t);
683 char message[512] = { 0 };
684 double fraction = 0.0f;
685
687
688 if(mode == 0)
689 snprintf(message, sizeof(message), ngettext("set %d color image", "setting %d color images", total), total);
690 else
691 snprintf(message, sizeof(message), ngettext("set %d monochrome image", "setting %d monochrome images", total), total);
692
694 while(t)
695 {
696 const int32_t imgid = GPOINTER_TO_INT(t->data);
697
698 if(imgid >= 0)
699 {
700 dt_image_set_monochrome_flag(imgid, mode == 2);
701 }
702 else
703 fprintf(stderr,"[dt_control_monochrome_images_job_run] got illegal imgid %i\n", imgid);
704
705 t = g_list_next(t);
706 fraction += 1.0 / total;
707 dt_control_job_set_progress(job, fraction);
708 }
709
711
713 g_list_copy(params->index));
715 return 0;
716}
717
718static char *_get_image_list(GList *l)
719{
720 const guint size = g_list_length(l);
721 char num[8];
722 char *buffer = calloc(size, sizeof(num));
723 gboolean first = TRUE;
724
725 buffer[0] = '\0';
726
727 while(l)
728 {
729 const int32_t imgid = GPOINTER_TO_INT(l->data);
730 snprintf(num, sizeof(num), "%s%6d", first ? "" : ",", imgid);
731 g_strlcat(buffer, num, size * sizeof(num));
732 l = g_list_next(l);
733 first = FALSE;
734 }
735 return buffer;
736}
737
738static void _set_remove_flag(char *imgs)
739{
740 sqlite3_stmt *stmt = NULL;
742 "UPDATE main.images SET flags = (flags|?1) WHERE id IN (?2)", -1, &stmt, NULL);
744 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, imgs, -1, SQLITE_STATIC);
745 sqlite3_step(stmt);
746 sqlite3_finalize(stmt);
747}
748
749static GList *_get_full_pathname(char *imgs)
750{
751 sqlite3_stmt *stmt = NULL;
752 GList *list = NULL;
753 // clang-format off
755 "SELECT DISTINCT folder || '" G_DIR_SEPARATOR_S "' || filename FROM "
756 "main.images i, main.film_rolls f "
757 "ON i.film_id = f.id WHERE i.id IN (?1)",
758 -1, &stmt, NULL);
759 // clang-format on
760 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 1, imgs, -1, SQLITE_STATIC);
761 while(sqlite3_step(stmt) == SQLITE_ROW)
762 {
763 list = g_list_prepend(list, g_strdup((const gchar *)sqlite3_column_text(stmt, 0)));
764 }
765 sqlite3_finalize(stmt);
766 return g_list_reverse(list); // list was built in reverse order, so un-reverse it
767}
768
770{
772 GList *t = params->index;
773 char *imgs = _get_image_list(t);
774 const guint total = g_list_length(t);
775 char message[512] = { 0 };
776 snprintf(message, sizeof(message), ngettext("removing %d image", "removing %d images", total), total);
778 sqlite3_stmt *stmt = NULL;
779
780 // check that we can safely remove the image
781 gboolean remove_ok = TRUE;
783 "SELECT id FROM main.images WHERE id IN (?2) AND flags&?1=?1", -1, &stmt, NULL);
785 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, imgs, -1, SQLITE_STATIC);
786
787 while(sqlite3_step(stmt) == SQLITE_ROW)
788 {
789 const int32_t imgid = sqlite3_column_int(stmt, 0);
790 if(!dt_image_safe_remove(imgid))
791 {
792 remove_ok = FALSE;
793 break;
794 }
795 }
796 sqlite3_finalize(stmt);
797
798 if(!remove_ok)
799 {
800 dt_control_log(_("cannot remove local copy when the original file is not accessible."));
801 dt_free(imgs);
802 return 0;
803 }
804
805 // update remove status
806 _set_remove_flag(imgs);
807
809
810 // We need a list of files to regenerate .xmp files if there are duplicates
811 GList *list = _get_full_pathname(imgs);
812
813 dt_free(imgs);
814
815 double fraction = 0.0f;
816 while(t)
817 {
818 int32_t imgid = GPOINTER_TO_INT(t->data);
819 dt_image_remove(imgid);
820 t = g_list_next(t);
821 fraction += 1.0 / total;
822 dt_control_job_set_progress(job, fraction);
823 }
824
825 while(list)
826 {
827 char *imgname = (char *)list->data;
828 dt_image_synch_all_xmp(imgname);
829 list = g_list_delete_link(list, list);
830 }
833 g_list_copy(params->index));
836
837 return 0;
838}
839
841{
843 const char *filename;
844 const char *error_message;
845
847
848 dt_pthread_mutex_t mutex;
849 pthread_cond_t cond;
851
859
868
869static gboolean _dt_delete_dialog_main_thread(gpointer user_data)
870{
871 _dt_delete_modal_dialog_t* modal_dialog = (_dt_delete_modal_dialog_t*)user_data;
872 dt_pthread_mutex_lock(&modal_dialog->mutex);
873
874 GtkWidget *dialog = gtk_message_dialog_new(
875 GTK_WINDOW(dt_ui_main_window(darktable.gui->ui)),
876 GTK_DIALOG_DESTROY_WITH_PARENT,
877 GTK_MESSAGE_QUESTION,
878 GTK_BUTTONS_NONE,
879 modal_dialog->send_to_trash
880 ? _("could not send %s to trash.%s%s")
881 : _("could not physically delete %s.%s%s"),
882 modal_dialog->filename,
883 !IS_NULL_PTR(modal_dialog->error_message) ? "\n" : "",
884 !IS_NULL_PTR(modal_dialog->error_message) ? modal_dialog->error_message : "");
885#ifdef GDK_WINDOWING_QUARTZ
887#endif
888
889 if(modal_dialog->send_to_trash)
890 {
891 gtk_dialog_add_button(GTK_DIALOG(dialog), _("physically delete"), _DT_DELETE_DIALOG_CHOICE_DELETE);
892 gtk_dialog_add_button(GTK_DIALOG(dialog), _("physically delete all files"), _DT_DELETE_DIALOG_CHOICE_DELETE_ALL);
893 }
894 gtk_dialog_add_button(GTK_DIALOG(dialog), _("only remove from the image library"), _DT_DELETE_DIALOG_CHOICE_REMOVE);
895 gtk_dialog_add_button(GTK_DIALOG(dialog), _("skip to next file"), _DT_DELETE_DIALOG_CHOICE_CONTINUE);
896 gtk_dialog_add_button(GTK_DIALOG(dialog), _("stop process"), _DT_DELETE_DIALOG_CHOICE_STOP);
897
898 gtk_window_set_title(
899 GTK_WINDOW(dialog),
900 modal_dialog->send_to_trash
901 ? _("trashing error")
902 : _("deletion error"));
903 modal_dialog->dialog_result = gtk_dialog_run(GTK_DIALOG(dialog));
904 gtk_widget_destroy(dialog);
905
906 pthread_cond_signal(&modal_dialog->cond);
907
908 dt_pthread_mutex_unlock(&modal_dialog->mutex);
909
910 // Don't call again on next idle time
911 return FALSE;
912}
913
914static gint _dt_delete_file_display_modal_dialog(int send_to_trash, const char *filename, const char *error_message)
915{
916 _dt_delete_modal_dialog_t modal_dialog;
917 modal_dialog.send_to_trash = send_to_trash;
918 modal_dialog.filename = filename;
919 modal_dialog.error_message = error_message;
920
921 modal_dialog.dialog_result = GTK_RESPONSE_NONE;
922
923 dt_pthread_mutex_init(&modal_dialog.mutex, NULL);
924 pthread_cond_init(&modal_dialog.cond, NULL);
925
926 dt_pthread_mutex_lock(&modal_dialog.mutex);
927
928 gdk_threads_add_idle(_dt_delete_dialog_main_thread, &modal_dialog);
929 while (modal_dialog.dialog_result == GTK_RESPONSE_NONE)
930 dt_pthread_cond_wait(&modal_dialog.cond, &modal_dialog.mutex);
931
932 dt_pthread_mutex_unlock(&modal_dialog.mutex);
933 dt_pthread_mutex_destroy(&modal_dialog.mutex);
934 pthread_cond_destroy(&modal_dialog.cond);
935
936 return modal_dialog.dialog_result;
937}
938
939static enum _dt_delete_status delete_file_from_disk(const char *filename, gboolean *delete_on_trash_error)
940{
941 enum _dt_delete_status delete_status = _DT_DELETE_STATUS_UNKNOWN;
942
943 GFile *gfile = g_file_new_for_path(filename);
944 int send_to_trash = dt_conf_get_bool("send_to_trash");
945
946 while (delete_status == _DT_DELETE_STATUS_UNKNOWN)
947 {
948 gboolean delete_success = FALSE;
949 GError *gerror = NULL;
950 if(send_to_trash)
951 {
952#ifdef __APPLE__
953 delete_success = dt_osx_file_trash(filename, &gerror);
954#elif defined(_WIN32)
955 delete_success = dt_win_file_trash(gfile, NULL /*cancellable*/, &gerror);
956#else
957 delete_success = g_file_trash(gfile, NULL /*cancellable*/, &gerror);
958#endif
959 }
960 else
961 {
962 delete_success = g_file_delete(gfile, NULL /*cancellable*/, &gerror);
963 }
964
965 // Delete is a success or the file does not exists: OK to remove from darktable
966 if(delete_success
967 || g_error_matches(gerror, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
968 {
969 delete_status = _DT_DELETE_STATUS_OK_TO_REMOVE;
970 }
971 else if(send_to_trash && *delete_on_trash_error)
972 {
973 // Loop again, this time delete instead of trashing
974 delete_status = _DT_DELETE_STATUS_UNKNOWN;
975 send_to_trash = FALSE;
976 }
977 else
978 {
979 const char *filename_display = NULL;
980 GFileInfo *gfileinfo = g_file_query_info(
981 gfile,
982 G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
983 G_FILE_QUERY_INFO_NONE,
984 NULL /*cancellable*/,
985 NULL /*error*/);
986 if(!IS_NULL_PTR(gfileinfo))
987 filename_display = g_file_info_get_attribute_string(
988 gfileinfo,
989 G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME);
990
992 send_to_trash,
993 IS_NULL_PTR(filename_display) ? filename : filename_display,
994 IS_NULL_PTR(gerror) ? NULL : gerror->message);
995 g_object_unref(gfileinfo);
996 if(send_to_trash && res == _DT_DELETE_DIALOG_CHOICE_DELETE)
997 {
998 // Loop again, this time delete instead of trashing
999 delete_status = _DT_DELETE_STATUS_UNKNOWN;
1000 send_to_trash = FALSE;
1001 }
1002 else if(send_to_trash && res == _DT_DELETE_DIALOG_CHOICE_DELETE_ALL)
1003 {
1004 // Loop again, this time delete instead of trashing
1005 delete_status = _DT_DELETE_STATUS_UNKNOWN;
1006 send_to_trash = FALSE;
1007 *delete_on_trash_error = TRUE;
1008 }
1009 else if(res == _DT_DELETE_DIALOG_CHOICE_REMOVE)
1010 {
1011 delete_status = _DT_DELETE_STATUS_OK_TO_REMOVE;
1012 }
1013 else if(res == _DT_DELETE_DIALOG_CHOICE_CONTINUE)
1014 {
1015 delete_status = _DT_DELETE_STATUS_SKIP_FILE;
1016 }
1017 else
1018 {
1019 delete_status = _DT_DELETE_STATUS_STOP_PROCESSING;
1020 }
1021 }
1022 if(!IS_NULL_PTR(gerror))
1023 g_error_free(gerror);
1024 }
1025
1026 if(!IS_NULL_PTR(gfile))
1027 g_object_unref(gfile);
1028
1029 return delete_status;
1030}
1031
1032
1034{
1036 GList *t = params->index;
1037 char *imgs = _get_image_list(t);
1038 char imgidstr[25] = { 0 };
1039 const guint total = g_list_length(t);
1040 double fraction = 0.0f;
1041 char message[512] = { 0 };
1042 gboolean delete_on_trash_error = FALSE;
1043 if(dt_conf_get_bool("send_to_trash"))
1044 snprintf(message, sizeof(message), ngettext("trashing %d image", "trashing %d images", total), total);
1045 else
1046 snprintf(message, sizeof(message), ngettext("deleting %d image", "deleting %d images", total), total);
1048
1049 sqlite3_stmt *stmt;
1050
1052
1053 // We need a list of files to regenerate .xmp files if there are duplicates
1054 GList *list = _get_full_pathname(imgs);
1055
1056 dt_free(imgs);
1057
1059 "SELECT COUNT(*) FROM main.images WHERE filename IN (SELECT filename FROM "
1060 "main.images WHERE id = ?1) AND film_id IN (SELECT film_id FROM main.images WHERE "
1061 "id = ?1)", -1, &stmt, NULL);
1062 while(t)
1063 {
1064 enum _dt_delete_status delete_status = _DT_DELETE_STATUS_UNKNOWN;
1065 const int32_t imgid = GPOINTER_TO_INT(t->data);
1066 char filename[PATH_MAX] = { 0 };
1067 gboolean from_cache = FALSE;
1068 dt_image_full_path(imgid, filename, sizeof(filename), &from_cache, __FUNCTION__);
1069
1070#ifdef _WIN32
1071 char *dirname = g_path_get_dirname(filename);
1072#endif
1073
1074 int duplicates = 0;
1075 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
1076 if(sqlite3_step(stmt) == SQLITE_ROW) duplicates = sqlite3_column_int(stmt, 0);
1077 sqlite3_reset(stmt);
1078 sqlite3_clear_bindings(stmt);
1079
1080 // remove from disk:
1081 if(duplicates == 1)
1082 {
1083 // first check for local copies, never delete a file whose original file is not accessible
1084 if(dt_image_local_copy_reset(imgid))
1085 goto delete_next_file;
1086
1087 snprintf(imgidstr, sizeof(imgidstr), "%d", imgid);
1088 _set_remove_flag(imgidstr);
1089 dt_image_remove(imgid);
1090
1091 // there are no further duplicates so we can remove the source data file
1092 delete_status = delete_file_from_disk(filename, &delete_on_trash_error);
1093 if(delete_status != _DT_DELETE_STATUS_OK_TO_REMOVE)
1094 goto delete_next_file;
1095
1096 // all sidecar files - including left-overs - can be deleted;
1097 // left-overs can result when previously duplicates have been REMOVED;
1098 // no need to keep them as the source data file is gone.
1099
1100 GList *files = dt_image_find_xmps(filename);
1101
1102 for(GList *file_iter = files; file_iter; file_iter = g_list_next(file_iter))
1103 {
1104 delete_status = delete_file_from_disk(file_iter->data, &delete_on_trash_error);
1105 if(delete_status != _DT_DELETE_STATUS_OK_TO_REMOVE)
1106 break;
1107 }
1108
1109 g_list_free_full(files, dt_free_gpointer);
1110 files = NULL;
1111 }
1112 else
1113 {
1114 // don't remove the actual source data if there are further duplicates using it;
1115 // just delete the xmp file of the duplicate selected.
1116
1117 dt_image_path_append_version(imgid, filename, sizeof(filename));
1118 g_strlcat(filename, ".xmp", sizeof(filename));
1119
1120 // remove image from db first ...
1121 snprintf(imgidstr, sizeof(imgidstr), "%d", imgid);
1122 _set_remove_flag(imgidstr);
1123 dt_image_remove(imgid);
1124
1125 // ... and delete afterwards because removing will re-write the XMP
1126 delete_status = delete_file_from_disk(filename, &delete_on_trash_error);
1127 }
1128
1129delete_next_file:
1130#ifdef _WIN32
1131 dt_free(dirname);
1132#endif
1133 t = g_list_next(t);
1134 fraction += 1.0 / total;
1135 dt_control_job_set_progress(job, fraction);
1136 if(delete_status == _DT_DELETE_STATUS_STOP_PROCESSING)
1137 break;
1138 }
1139
1140 sqlite3_finalize(stmt);
1141
1142 while(list)
1143 {
1144 char *imgname = (char *)list->data;
1145 dt_image_synch_all_xmp(imgname);
1146 list = g_list_delete_link(list, list);
1147 }
1148 g_list_free(list);
1149 list = NULL;
1152 g_list_copy(params->index));
1155 return 0;
1156}
1157
1159{
1161 GList *t = params->index;
1162 struct dt_gpx_t *gpx = NULL;
1163 uint32_t cntr = 0;
1164 const dt_control_gpx_apply_t *d = params->data;
1165 const gchar *filename = d->filename;
1166 const gchar *tz = d->tz;
1167 /* do we have any selected images */
1168 if(IS_NULL_PTR(t)) goto bail_out;
1169
1170 /* try parse the gpx data */
1171 gpx = dt_gpx_new(filename);
1172 if(IS_NULL_PTR(gpx))
1173 {
1174 dt_control_log(_("failed to parse GPX file"));
1175 goto bail_out;
1176 }
1177
1178 GTimeZone *tz_camera = (IS_NULL_PTR(tz)) ? g_time_zone_new_utc() : g_time_zone_new(tz);
1179 if(IS_NULL_PTR(tz_camera)) goto bail_out;
1180
1181 GList *imgs = NULL;
1182 GArray *gloc = g_array_new(FALSE, FALSE, sizeof(dt_image_geoloc_t));
1183 /* go thru each selected image and lookup location in gpx */
1184 do
1185 {
1186 dt_image_geoloc_t geoloc;
1187 int32_t imgid = GPOINTER_TO_INT(t->data);
1188
1189 /* get image */
1190 const dt_image_t *cimg = dt_image_cache_get(darktable.image_cache, imgid, 'r');
1191 if(IS_NULL_PTR(cimg)) continue;
1192
1193 GDateTime *exif_time = dt_datetime_img_to_gdatetime(cimg, tz_camera);
1194
1195 /* release the lock */
1197 if(IS_NULL_PTR(exif_time)) continue;
1198 GDateTime *utc_time = g_date_time_to_timezone(exif_time, darktable.utc_tz);
1199 g_date_time_unref(exif_time);
1200 if(IS_NULL_PTR(utc_time)) continue;
1201
1202 /* only update image location if time is within gpx tack range */
1203 if(dt_gpx_get_location(gpx, utc_time, &geoloc))
1204 {
1205 // takes the option to include the grouped images
1206 GList *grps = dt_grouping_get_group_images(imgid);
1207 for(GList *grp = grps; grp; grp = g_list_next(grp))
1208 {
1209 imgs = g_list_prepend(imgs, grp->data);
1210 g_array_append_val(gloc, geoloc);
1211 cntr++;
1212 }
1213 g_list_free(grps);
1214 grps = NULL;
1215 }
1216 g_date_time_unref(utc_time);
1217 } while((t = g_list_next(t)) != NULL);
1218 imgs = g_list_reverse(imgs);
1219
1221
1222 dt_control_log(ngettext("applied matched GPX location onto %d image",
1223 "applied matched GPX location onto %d images", cntr), cntr);
1224
1225 g_time_zone_unref(tz_camera);
1226 dt_gpx_destroy(gpx);
1227 g_array_unref(gloc);
1229 return 0;
1230
1231bail_out:
1232 if(!IS_NULL_PTR(gpx)) dt_gpx_destroy(gpx);
1233
1234 return 1;
1235}
1236
1238{
1239 return _generic_dt_control_fileop_images_job_run(job, &dt_image_move, _("moving %d image"),
1240 _("moving %d images"));
1241}
1242
1244{
1245 return _generic_dt_control_fileop_images_job_run(job, &dt_image_copy, _("copying %d image"),
1246 _("copying %d images"));
1247}
1248
1250{
1252 GList *t = params->index;
1253 guint tagid = 0;
1254 const guint total = g_list_length(t);
1255 double fraction = 0;
1256 const gboolean is_copy = params->flag == 1;
1257 char message[512] = { 0 };
1258
1259 if(is_copy)
1260 snprintf(message, sizeof(message),
1261 ngettext("creating local copy of %d image", "creating local copies of %d images", total), total);
1262 else
1263 snprintf(message, sizeof(message),
1264 ngettext("removing local copy of %d image", "removing local copies of %d images", total), total);
1265
1266 dt_control_log("%s", message);
1268
1269 dt_tag_new("darktable|local-copy", &tagid);
1270
1271 gboolean tag_change = FALSE;
1273 {
1274 const int32_t imgid = GPOINTER_TO_INT(t->data);
1275 if(is_copy)
1276 {
1277 if(dt_image_local_copy_set(imgid) == 0)
1278 {
1279 if(dt_tag_attach(tagid, imgid, FALSE, FALSE)) tag_change = TRUE;
1280 }
1281 }
1282 else
1283 {
1284 if(dt_image_local_copy_reset(imgid) == 0)
1285 {
1286 if(dt_tag_detach(tagid, imgid, FALSE, FALSE)) tag_change = TRUE;
1287 }
1288 }
1289 t = g_list_next(t);
1290
1291 fraction += 1.0 / total;
1292 dt_control_job_set_progress(job, fraction);
1293 }
1294
1296 g_list_copy(params->index));
1300 return 0;
1301}
1302
1304{
1306 GList *t = params->index;
1307 const guint total = g_list_length(t);
1308 double fraction = 0.0f;
1309 char message[512] = { 0 };
1310 snprintf(message, sizeof(message), ngettext("refreshing info for %d image", "refreshing info for %d images", total), total);
1312 while(t)
1313 {
1314 const int32_t imgid = GPOINTER_TO_INT(t->data);
1315 if(imgid >= 0)
1316 {
1317 gboolean from_cache = TRUE;
1318 char sourcefile[PATH_MAX];
1319 dt_image_full_path(imgid, sourcefile, sizeof(sourcefile), &from_cache, __FUNCTION__);
1320
1322 if(!IS_NULL_PTR(img))
1323 {
1324 // Re-sync the file-derived classification flags (LDR/HDR/RAW/sRAW, mosaic, monochrome,
1325 // 4bayer, additional-DNG-tags, buffer-resolved) with the actual file, repairing entries
1326 // that were persisted with stale/corrupted flags in the DB. We must *clear* these bits
1327 // before dt_exif_read(), because it only re-seeds the LDR/HDR/RAW class when none is set
1328 // yet (see exif.cc): leaving a stale DT_IMAGE_HDR in place would suppress the re-read.
1329 // Everything else in img->flags is DB-only / user state that the file knows nothing about
1330 // (star rating & reject, local copy, audio/txt sidecar, preset bookkeeping, monochrome
1331 // workflow), so it is preserved verbatim.
1332 const uint32_t file_class_flags
1336 img->flags &= ~file_class_flags;
1337 dt_exif_read(img, sourcefile);
1339 }
1340 else
1341 fprintf(stderr,"[dt_control_refresh_exif_run] couldn't dt_image_cache_get for imgid %i\n", imgid);
1342
1344 }
1345 else
1346 fprintf(stderr,"[dt_control_refresh_exif_run] illegal imgid %i\n", imgid);
1347
1348 t = g_list_next(t);
1349 fraction += 1.0 / total;
1350 dt_control_job_set_progress(job, fraction);
1351 }
1353 g_list_copy(params->index));
1356 return 0;
1357}
1358
1359
1361{
1363 dt_control_export_t *settings = (dt_control_export_t *)params->data;
1364 GList *t = params->index;
1366 g_assert(mformat);
1368 g_assert(mstorage);
1369 dt_imageio_module_data_t *sdata = settings->sdata;
1370
1371 gboolean tag_change = FALSE;
1372
1373 // get a thread-safe fdata struct (one jpeg struct per thread etc):
1374 dt_imageio_module_data_t *fdata = mformat->get_params(mformat);
1375
1376 if(mstorage->initialize_store)
1377 {
1378 if(mstorage->initialize_store(mstorage, sdata, &mformat, &fdata, &t, TRUE))
1379 {
1380 // bail out, something went wrong
1381 goto end;
1382 }
1383 mformat->set_params(mformat, fdata, mformat->params_size(mformat));
1384 mstorage->set_params(mstorage, sdata, mstorage->params_size(mstorage));
1385 }
1386
1387 // Get max dimensions...
1388 uint32_t w, h, fw, fh, sw, sh;
1389 fw = fh = sw = sh = 0;
1390 mstorage->dimension(mstorage, sdata, &sw, &sh);
1391 mformat->dimension(mformat, fdata, &fw, &fh);
1392
1393 if(sw == 0 || fw == 0)
1394 w = sw > fw ? sw : fw;
1395 else
1396 w = sw < fw ? sw : fw;
1397
1398 if(sh == 0 || fh == 0)
1399 h = sh > fh ? sh : fh;
1400 else
1401 h = sh < fh ? sh : fh;
1402
1403 const guint total = g_list_length(t);
1404 if(total > 0)
1405 dt_control_log(ngettext("exporting %d image..", "exporting %d images..", total), total);
1406 else
1407 dt_control_log(_("no image to export"));
1408
1409 double fraction = 0;
1410
1411 // set up the fdata struct
1412 fdata->max_width = (settings->max_width != 0 && w != 0) ? MIN(w, settings->max_width) : MAX(w, settings->max_width);
1413 fdata->max_height = (settings->max_height != 0 && h != 0) ? MIN(h, settings->max_height) : MAX(h, settings->max_height);
1414 g_strlcpy(fdata->style, settings->style, sizeof(fdata->style));
1415 // Invariant: the tagid for 'darktable|changed' will not change while this function runs. Is this a
1416 // sensible assumption?
1417 guint tagid = 0, etagid = 0;
1418 dt_tag_new("darktable|exported", &etagid);
1419
1420 dt_export_metadata_t metadata;
1421 metadata.flags = 0;
1422 metadata.list = dt_util_str_to_glist("\1", settings->metadata_export);
1423 if(metadata.list)
1424 {
1425 metadata.flags = strtol(metadata.list->data, NULL, 16);
1426 metadata.list = g_list_remove(metadata.list, metadata.list->data);
1427 }
1428
1430 {
1431 const int32_t imgid = GPOINTER_TO_INT(t->data);
1432 t = g_list_next(t);
1433 const guint num = total - g_list_length(t);
1434
1435 // progress message
1436 char message[512] = { 0 };
1437 snprintf(message, sizeof(message), _("exporting %d / %d to %s"), num, total, mstorage->name(mstorage));
1438 // update the message. initialize_store() might have changed the number of images
1440
1441 // remove 'changed' tag from image
1442 if(dt_tag_detach(tagid, imgid, FALSE, FALSE)) tag_change = TRUE;
1443 // make sure the 'exported' tag is set on the image
1444 if(dt_tag_attach(etagid, imgid, FALSE, FALSE)) tag_change = TRUE;
1445
1446 /* register export timestamp in cache */
1448
1449 // check if image still exists:
1450 const dt_image_t *image = dt_image_cache_get(darktable.image_cache, (int32_t)imgid, 'r');
1451 if(image)
1452 {
1453 char imgfilename[PATH_MAX] = { 0 };
1454 gboolean from_cache = TRUE;
1455 dt_image_full_path(image->id, imgfilename, sizeof(imgfilename), &from_cache, __FUNCTION__);
1456 if(!g_file_test(imgfilename, G_FILE_TEST_IS_REGULAR))
1457 {
1458 dt_control_log(_("image `%s' is currently unavailable"), image->filename);
1459 fprintf(stderr, "image `%s' is currently unavailable\n", imgfilename);
1460 // dt_image_remove(imgid);
1462 }
1463 else
1464 {
1466 if(mstorage->store(mstorage, sdata, imgid, mformat, fdata, num, total, TRUE,
1467 settings->export_masks, settings->icc_type, settings->icc_filename, settings->icc_intent,
1468 &metadata) != 0)
1470 }
1471 }
1472
1473 fraction += 1.0 / total;
1474 if(fraction > 1.0) fraction = 1.0;
1475 dt_control_job_set_progress(job, fraction);
1476 }
1477 g_list_free_full(metadata.list, dt_free_gpointer);
1478 metadata.list = NULL;
1479
1480 if(mstorage->finalize_store) mstorage->finalize_store(mstorage, sdata);
1481
1482end:
1483 // all threads free their fdata
1484 mformat->free_params(mformat, fdata);
1485
1486 // notify the user via the window manager
1488
1490 return 0;
1491}
1492
1494{
1496 if(IS_NULL_PTR(params)) return NULL;
1497
1498 params->data = calloc(1, sizeof(dt_control_gpx_apply_t));
1499 if(IS_NULL_PTR(params->data))
1500 {
1502 return NULL;
1503 }
1504
1505 return params;
1506}
1507
1509{
1511
1512 dt_control_gpx_apply_t *data = params->data;
1513 params->data = NULL;
1514 dt_free(data->filename);
1515 dt_free(data->tz);
1516
1517 dt_free(data);
1518
1520}
1521
1522static dt_job_t *_control_gpx_apply_job_create(const gchar *filename, int32_t filmid,
1523 const gchar *tz, GList *imgs)
1524{
1526 if(IS_NULL_PTR(job)) return NULL;
1528 if(IS_NULL_PTR(params))
1529 {
1531 return NULL;
1532 }
1534
1535 if(filmid != -1)
1537 else if(!imgs)
1538 params->index = dt_act_on_get_images();
1539 else
1540 params->index = imgs;
1541 dt_control_gpx_apply_t *data = params->data;
1542 data->filename = g_strdup(filename);
1543 data->tz = g_strdup(tz);
1544
1545 return job;
1546}
1547
1548void dt_control_save_xmp(const int32_t imgid)
1549{
1552 N_("save history to XMP"),
1553 0, NULL, PROGRESS_NONE, imgid));
1554}
1555
1556void dt_control_save_xmps(const GList *imgids, const gboolean check_history)
1557{
1558 (void)check_history;
1559 if(IS_NULL_PTR(imgids)) return;
1560
1562 if(IS_NULL_PTR(job)) return;
1563
1565 if(IS_NULL_PTR(params))
1566 {
1568 return;
1569 }
1570
1571 params->index = g_list_copy((GList *)imgids);
1572 params->flag = 0;
1573
1576}
1577
1584
1585void dt_control_gpx_apply(const gchar *filename, int32_t filmid, const gchar *tz, GList *imgs)
1586{
1588 _control_gpx_apply_job_create(filename, filmid, tz, imgs));
1589}
1590
1591void dt_control_duplicate_images(gboolean virgin)
1592{
1595 N_("duplicate images"), 0, GINT_TO_POINTER(virgin), PROGRESS_SIMPLE, TRUE));
1596}
1597
1604
1611
1613{
1615 {
1616 dt_control_log(_("removing images from library is only possible in Lighttable view"));
1617 return FALSE;
1618 }
1619
1620 // get all selected images now, to avoid the set changing during ui interaction
1622 NULL, PROGRESS_SIMPLE, FALSE);
1623 if(dt_conf_get_bool("ask_before_remove"))
1624 {
1625 GtkWidget *dialog;
1627
1629 const int number = g_list_length(e->index);
1630 if(number == 0)
1631 {
1633 return TRUE;
1634 }
1635
1636 dialog = gtk_message_dialog_new(
1637 GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
1638 ngettext("Do you really want to remove %d image from Ansel library ?\nThe files will not be deleted on disk.",
1639 "Do you really want to remove %d images from Ansel library ?\nThe files will not be deletetd on disk.", number),
1640 number);
1641#ifdef GDK_WINDOWING_QUARTZ
1643#endif
1644
1645 gtk_window_set_title(GTK_WINDOW(dialog), ngettext(_("Remove image from library ?"), _("Remove images from library ?"), number));
1646 gint res = gtk_dialog_run(GTK_DIALOG(dialog));
1647 gtk_widget_destroy(dialog);
1648 if(res != GTK_RESPONSE_YES)
1649 {
1651 return FALSE;
1652 }
1653 }
1655 return TRUE;
1656}
1657
1659{
1661 {
1662 dt_control_log(_("Deleting images from library is only possible in Lighttable view"));
1663 return;
1664 }
1665
1666 // first get all selected images, to avoid the set changing during ui interaction
1668 NULL, PROGRESS_SIMPLE, FALSE);
1669 if(dt_conf_get_bool("ask_before_delete"))
1670 {
1671 GtkWidget *dialog;
1673
1675 const int number = g_list_length(e->index);
1676
1677 // Do not show the dialog if no image is selected:
1678 if(number == 0)
1679 {
1681 return;
1682 }
1683
1684 dialog = gtk_message_dialog_new(
1685 GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
1686 ngettext("Do you really want to physically delete %d image ?\nThe system trash bin will be used if possible.",
1687 "Do you really want to physically delete %d images ?\nThe system trash bin will be used if possible.", number),
1688 number);
1689#ifdef GDK_WINDOWING_QUARTZ
1691#endif
1692
1693 gtk_window_set_title(GTK_WINDOW(dialog), ngettext(_("Remove image from disk ?"), _("Remove images from disk ?"), number));
1694 gint res = gtk_dialog_run(GTK_DIALOG(dialog));
1695 gtk_widget_destroy(dialog);
1696 if(res != GTK_RESPONSE_YES)
1697 {
1699 return;
1700 }
1701 }
1703}
1704
1705void dt_control_delete_image(int32_t imgid)
1706{
1708 {
1709 dt_control_log(_("Deleting images from library is only possible in Lighttable view"));
1710 return;
1711 }
1712 // first get all selected images, to avoid the set changing during ui interaction
1714 NULL, PROGRESS_SIMPLE, imgid);
1715 int send_to_trash = dt_conf_get_bool("send_to_trash");
1716 if(dt_conf_get_bool("ask_before_delete"))
1717 {
1718 GtkWidget *dialog;
1720
1721 // Do not show the dialog if no valid image
1722 if(imgid < 1)
1723 {
1725 return;
1726 }
1727
1728 dialog = gtk_message_dialog_new(
1729 GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
1730 send_to_trash ? _("do you really want to physically delete selected image (using trash if possible)?")
1731 : _("do you really want to physically delete selected image from disk?"));
1732#ifdef GDK_WINDOWING_QUARTZ
1734#endif
1735
1736 gtk_window_set_title(GTK_WINDOW(dialog), _("delete image?"));
1737 gint res = gtk_dialog_run(GTK_DIALOG(dialog));
1738 gtk_widget_destroy(dialog);
1739 if(res != GTK_RESPONSE_YES)
1740 {
1742 return;
1743 }
1744 }
1746}
1747
1749{
1750 // Open file chooser dialog
1751 gchar *dir = NULL;
1753
1757 const int number = g_list_length(e->index);
1758 if(number == 0)
1759 {
1761 return;
1762 }
1763
1764 GtkFileChooserNative *filechooser = gtk_file_chooser_native_new(
1765 _("select directory"), GTK_WINDOW(win), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
1766 _("_select as destination"), _("_cancel"));
1767
1768 dt_conf_get_folder_to_file_chooser("ui_last/move_path", GTK_FILE_CHOOSER(filechooser));
1769 if(gtk_native_dialog_run(GTK_NATIVE_DIALOG(filechooser)) == GTK_RESPONSE_ACCEPT)
1770 {
1771 dir = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filechooser));
1772 dt_conf_set_folder_from_file_chooser("ui_last/move_path", GTK_FILE_CHOOSER(filechooser));
1773 }
1774 g_object_unref(filechooser);
1775
1776 if(IS_NULL_PTR(dir) || !g_file_test(dir, G_FILE_TEST_IS_DIR)) goto abort;
1777
1778 // ugly, but we need to set this after constructing the job:
1780 // the job's cleanup function is responsible for freeing dir, so we don't do that here
1781
1782 if(dt_conf_get_bool("ask_before_move"))
1783 {
1784 GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT,
1785 GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
1786 ngettext("do you really want to physically move %d image to %s?\n"
1787 "(all duplicates will be moved along)",
1788 "do you really want to physically move %d images to %s?\n"
1789 "(all duplicates will be moved along)",
1790 number),
1791 number, dir);
1792#ifdef GDK_WINDOWING_QUARTZ
1794#endif
1795 gtk_window_set_title(GTK_WINDOW(dialog), ngettext("move image?", "move images?", number));
1796
1797 gint res = gtk_dialog_run(GTK_DIALOG(dialog));
1798 gtk_widget_destroy(dialog);
1799
1800 if(res != GTK_RESPONSE_YES) goto abort;
1801 }
1802
1804 return;
1805
1806abort:
1807 dt_free(dir);
1809}
1810
1812{
1813 // Open file chooser dialog
1814 gchar *dir = NULL;
1819 const int number = g_list_length(e->index);
1820 if(number == 0)
1821 {
1823 return;
1824 }
1825
1826 GtkFileChooserNative *filechooser = gtk_file_chooser_native_new(
1827 _("select directory"), GTK_WINDOW(win), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
1828 _("_select as destination"), _("_cancel"));
1829
1830 dt_conf_get_folder_to_file_chooser("ui_last/copy_path", GTK_FILE_CHOOSER(filechooser));
1831 if(gtk_native_dialog_run(GTK_NATIVE_DIALOG(filechooser)) == GTK_RESPONSE_ACCEPT)
1832 {
1833 dir = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filechooser));
1834 dt_conf_set_folder_from_file_chooser("ui_last/copy_path", GTK_FILE_CHOOSER(filechooser));
1835 }
1836 g_object_unref(filechooser);
1837
1838 if(IS_NULL_PTR(dir) || !g_file_test(dir, G_FILE_TEST_IS_DIR)) goto abort;
1839
1840 // ugly, but we need to set this after constructing the job:
1842 // the job's cleanup function is responsible for freeing dir, so we don't do that here
1843
1844 if(dt_conf_get_bool("ask_before_copy"))
1845 {
1846 GtkWidget *dialog = gtk_message_dialog_new(
1847 GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
1848 ngettext("do you really want to physically copy %d image to %s?",
1849 "do you really want to physically copy %d images to %s?", number),
1850 number, dir);
1851#ifdef GDK_WINDOWING_QUARTZ
1853#endif
1854 gtk_window_set_title(GTK_WINDOW(dialog), ngettext("copy image?", "copy images?", number));
1855
1856 gint res = gtk_dialog_run(GTK_DIALOG(dialog));
1857 gtk_widget_destroy(dialog);
1858
1859 if(res != GTK_RESPONSE_YES) goto abort;
1860 }
1861
1863 return;
1864
1865abort:
1866 dt_free(dir);
1868}
1869
1877
1885
1892
1894{
1896 if(IS_NULL_PTR(params)) return NULL;
1897
1898 params->data = calloc(1, sizeof(dt_control_export_t));
1899 if(IS_NULL_PTR(params->data))
1900 {
1902 return NULL;
1903 }
1904
1905 return params;
1906}
1907
1909{
1911
1912 dt_control_export_t *settings = (dt_control_export_t *)params->data;
1914 dt_imageio_module_data_t *sdata = settings->sdata;
1915
1916 mstorage->free_params(mstorage, sdata);
1917
1918 dt_free(settings->icc_filename);
1919 dt_free(settings->metadata_export);
1920 dt_free(params->data);
1921
1923}
1924
1925void dt_control_export(GList *imgid_list, int max_width, int max_height, int format_index, int storage_index,
1926 gboolean high_quality, gboolean export_masks, char *style,
1927 dt_colorspaces_color_profile_type_t icc_type, const gchar *icc_filename,
1928 dt_iop_color_intent_t icc_intent, const gchar *metadata_export)
1929{
1931 if(IS_NULL_PTR(job)) return;
1933 if(IS_NULL_PTR(params))
1934 {
1936 return;
1937 }
1939
1940 params->index = imgid_list;
1941
1942 dt_control_export_t *data = params->data;
1943 data->max_width = max_width;
1944 data->max_height = max_height;
1945 data->format_index = format_index;
1946 data->storage_index = storage_index;
1948 g_assert(mstorage);
1949 // get shared storage param struct (global sequence counter, one picasa connection etc)
1950 dt_imageio_module_data_t *sdata = mstorage->get_params(mstorage);
1951 if(IS_NULL_PTR(sdata))
1952 {
1953 dt_control_log(_("failed to get parameters from storage module `%s', aborting export.."),
1954 mstorage->name(mstorage));
1956 return;
1957 }
1958 data->sdata = sdata;
1959 data->export_masks = export_masks;
1960 g_strlcpy(data->style, style, sizeof(data->style));
1961 data->icc_type = icc_type;
1962 data->icc_filename = g_strdup(icc_filename);
1963 data->icc_intent = icc_intent;
1964 data->metadata_export = g_strdup(metadata_export);
1965
1966 dt_control_job_add_progress(job, _("export images"), TRUE);
1968
1969 // tell the storage that we got its params for an export so it can reset itself to a safe state
1970 mstorage->export_dispatched(mstorage);
1971}
1972
1973static void _add_datetime_offset(const char *odt, const long int offset, char *ndt)
1974{
1975 // get the datetime_taken and calculate the new time
1976 GDateTime *datetime_original = dt_datetime_exif_to_gdatetime(odt, darktable.utc_tz);
1977 if(IS_NULL_PTR(datetime_original))
1978 return;
1979
1980 // let's add our offset
1981 GDateTime *datetime_new = g_date_time_add(datetime_original, offset);
1982 g_date_time_unref(datetime_original);
1983
1984 if(IS_NULL_PTR(datetime_new))
1985 return;
1986 gchar *datetime = g_date_time_format(datetime_new, "%Y:%m:%d %H:%M:%S,%f");
1987 datetime[DT_DATETIME_LENGTH - 1] = '\0'; // limit to milliseconds
1988 g_date_time_unref(datetime_new);
1989
1990 if(datetime)
1991 g_strlcpy(ndt, datetime, DT_DATETIME_LENGTH);
1992 dt_free(datetime);
1993}
1994
1996{
1998 uint32_t cntr = 0;
1999 GList *t = params->index;
2000 const GTimeSpan offset = ((dt_control_datetime_t *)params->data)->offset;
2001 const char *datetime = ((dt_control_datetime_t *)params->data)->datetime;
2002 char message[512] = { 0 };
2003
2004 /* do we have any selected images and is offset != 0 */
2005 if(IS_NULL_PTR(t) || (offset == 0 && !datetime[0]))
2006 {
2007 return 1;
2008 }
2009
2010 const guint total = g_list_length(t);
2011
2012 const char *mes11 = offset ? N_("adding time offset to %d image") : N_("setting date/time of %d image");
2013 const char *mes12 = offset ? N_("adding time offset to %d images") : N_("setting date/time of %d images");
2014 snprintf(message, sizeof(message), ngettext(mes11, mes12, total), total);
2016
2017 GList *imgs = NULL;
2018 if(offset)
2019 {
2020 GArray *dtime = g_array_new(FALSE, TRUE, DT_DATETIME_LENGTH);
2021
2022 for(GList *img = t; img; img = g_list_next(img))
2023 {
2024 char odt[DT_DATETIME_LENGTH] = {0};
2025 dt_image_get_datetime(GPOINTER_TO_INT(img->data), odt);
2026 if(!odt[0]) continue;
2027
2028 char ndt[DT_DATETIME_LENGTH] = {0};
2029 _add_datetime_offset(odt, offset, ndt);
2030 if(!ndt[0]) continue;
2031
2032 // takes the option to include the grouped images
2033 GList *grps = dt_grouping_get_group_images(GPOINTER_TO_INT(img->data));
2034 for(GList *grp = grps; grp; grp = g_list_next(grp))
2035 {
2036 imgs = g_list_prepend(imgs, grp->data);
2037 g_array_append_val(dtime, ndt);
2038 cntr++;
2039 }
2040 g_list_free(grps);
2041 grps = NULL;
2042 }
2043 imgs = g_list_reverse(imgs);
2044 dt_image_set_datetimes(imgs, dtime, TRUE);
2045 }
2046 else
2047 {
2048 imgs = g_list_copy(t);
2049 // takes the option to include the grouped images
2051 cntr = g_list_length(imgs);
2052 dt_image_set_datetime(imgs, datetime, TRUE);
2053 }
2054
2055 const char *mes21 = offset ? N_("added time offset to %d image") : N_("set date/time of %d image");
2056 const char *mes22 = offset ? N_("added time offset to %d images") : N_("set date/time of %d images");
2057 dt_control_log(ngettext(mes21, mes22, cntr), cntr);
2058 return 0;
2059}
2060
2062{
2064 if(IS_NULL_PTR(params)) return NULL;
2065
2066 params->data = calloc(1, sizeof(dt_control_datetime_t));
2067 if(IS_NULL_PTR(params->data))
2068 {
2070 return NULL;
2071 }
2072
2073 return params;
2074}
2075
2077{
2079
2080 dt_free(params->data);
2081
2083}
2084
2085static dt_job_t *dt_control_datetime_job_create(const GTimeSpan offset, const char *datetime, GList *imgs)
2086{
2088 if(IS_NULL_PTR(job)) return NULL;
2090 if(IS_NULL_PTR(params))
2091 {
2093 return NULL;
2094 }
2095 dt_control_job_add_progress(job, _("time offset"), FALSE);
2097
2098 if(imgs)
2099 params->index = imgs;
2100 else
2101 params->index = dt_act_on_get_images();
2102
2103 dt_control_datetime_t *data = params->data;
2104 data->offset = offset;
2105 if(!IS_NULL_PTR(datetime))
2106 memcpy(data->datetime, datetime, sizeof(data->datetime));
2107 else
2108 data->datetime[0] = '\0';
2109 params->data = data;
2110 return job;
2111}
2112
2113void dt_control_datetime(const GTimeSpan offset, const char *datetime, GList *imgs)
2114{
2116 dt_control_datetime_job_create(offset, datetime, imgs));
2117}
2118
2119// clang-format off
2120// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
2121// vim: shiftwidth=2 expandtab tabstop=2 cindent
2122// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
2123// clang-format on
GList * dt_act_on_get_images()
Definition act_on.c:39
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
#define m
Definition basecurve.c:278
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
void dt_collection_update_query(const dt_collection_t *collection, dt_collection_change_t query_change, dt_collection_properties_t changed_property, GList *list)
void dt_collection_deserialize(const char *buf)
int dt_collection_update(const dt_collection_t *collection)
Definition collection.c:268
@ DT_COLLECTION_PROP_LOCAL_COPY
Definition collection.h:131
@ DT_COLLECTION_PROP_UNDEF
Definition collection.h:142
@ DT_COLLECTION_CHANGE_RELOAD
Definition collection.h:151
dt_iop_color_intent_t
Definition colorspaces.h:63
@ DT_INTENT_LAST
Definition colorspaces.h:68
dt_colorspaces_color_profile_type_t
Definition colorspaces.h:81
@ DT_COLORSPACE_NONE
Definition colorspaces.h:82
static const dt_colormatrix_t M
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
void dt_history_delete_on_image(int32_t imgid)
int32_t dt_image_move(const int32_t imgid, const int32_t filmid)
int32_t dt_image_import(const int32_t film_id, const char *filename, gboolean raise_signals)
void dt_image_path_append_version(const int32_t imgid, char *pathname, size_t pathname_len)
int dt_image_write_sidecar_file(const int32_t imgid)
int32_t dt_image_copy(const int32_t imgid, const int32_t filmid)
void dt_image_get_datetime(const int32_t imgid, char *datetime)
void dt_image_synch_all_xmp(const gchar *pathname)
void dt_image_set_datetimes(const GList *imgs, const GArray *dtime, const gboolean undo_on)
GList * dt_image_find_xmps(const char *filename)
void dt_image_set_datetime(const GList *imgs, const char *datetime, const gboolean undo_on)
void dt_image_set_monochrome_flag(const int32_t imgid, gboolean monochrome)
void dt_image_set_images_locations(const GList *imgs, const GArray *gloc, const gboolean undo_on)
int dt_image_local_copy_set(const int32_t imgid)
void dt_image_remove(const int32_t imgid)
int dt_image_local_copy_reset(const int32_t imgid)
gboolean dt_image_get_xmp_mode()
gboolean dt_image_safe_remove(const int32_t imgid)
void dt_image_flip(const int32_t imgid, const int32_t cw)
int32_t dt_image_duplicate(const int32_t imgid)
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.
int dt_conf_get_bool(const char *name)
void dt_conf_set_folder_from_file_chooser(const char *name, GtkFileChooser *chooser)
gboolean dt_conf_is_equal(const char *name, const char *value)
gboolean dt_conf_get_folder_to_file_chooser(const char *name, GtkFileChooser *chooser)
void dt_control_log(const char *msg,...)
Definition control.c:761
void dt_control_queue_redraw_center()
request redraw of center window. This redraws the center view within a gdk critical section to preven...
Definition control.c:861
static dt_job_t * _control_gpx_apply_job_create(const gchar *filename, int32_t filmid, const gchar *tz, GList *imgs)
void dt_control_move_images()
static int32_t dt_control_remove_images_job_run(dt_job_t *job)
void dt_control_refresh_exif()
static gboolean _dt_delete_dialog_main_thread(gpointer user_data)
void dt_control_duplicate_images(gboolean virgin)
void dt_control_export(GList *imgid_list, int max_width, int max_height, int format_index, int storage_index, gboolean high_quality, gboolean export_masks, char *style, dt_colorspaces_color_profile_type_t icc_type, const gchar *icc_filename, dt_iop_color_intent_t icc_intent, const gchar *metadata_export)
static int32_t dt_control_refresh_exif_run(dt_job_t *job)
static dt_job_t * dt_control_datetime_job_create(const GTimeSpan offset, const char *datetime, GList *imgs)
progress_type_t
@ PROGRESS_NONE
@ PROGRESS_SIMPLE
@ PROGRESS_CANCELLABLE
static dt_job_t * dt_control_generic_image_job_create(dt_job_execute_callback execute, const char *message, int flag, gpointer data, progress_type_t progress_type, int32_t imgid)
static dt_control_image_enumerator_t * dt_control_export_alloc()
void dt_control_gpx_apply(const gchar *filename, int32_t filmid, const gchar *tz, GList *imgs)
void dt_control_monochrome_images(const int32_t mode)
void * dt_control_image_enumerator_alloc()
void dt_control_write_sidecar_files()
static int dt_control_merge_hdr_bpp(dt_imageio_module_data_t *data)
static int32_t dt_control_export_job_run(dt_job_t *job)
void dt_control_delete_image(int32_t imgid)
static const char * dt_control_merge_hdr_mime(dt_imageio_module_data_t *data)
static int32_t dt_control_delete_images_job_run(dt_job_t *job)
void dt_control_reset_local_copy_images()
static int32_t dt_control_duplicate_images_job_run(dt_job_t *job)
static int32_t dt_control_datetime_job_run(dt_job_t *job)
void dt_control_image_enumerator_cleanup(void *p)
static int32_t dt_control_merge_hdr_job_run(dt_job_t *job)
static int32_t dt_control_local_copy_images_job_run(dt_job_t *job)
static int32_t dt_control_monochrome_images_job_run(dt_job_t *job)
static gint _dt_delete_file_display_modal_dialog(int send_to_trash, const char *filename, const char *error_message)
void dt_control_set_local_copy_images()
void dt_control_datetime(const GTimeSpan offset, const char *datetime, GList *imgs)
static void _add_datetime_offset(const char *odt, const long int offset, char *ndt)
static char * _get_image_list(GList *l)
void dt_control_save_xmp(const int32_t imgid)
static int32_t dt_control_copy_images_job_run(dt_job_t *job)
static dt_job_t * dt_control_generic_images_job_create(dt_job_execute_callback execute, const char *message, int flag, gpointer data, progress_type_t progress_type, gboolean only_visible)
static GList * _get_full_pathname(char *imgs)
static int32_t dt_control_save_xmps_job_run(dt_job_t *job)
static dt_control_image_enumerator_t * dt_control_gpx_apply_alloc()
static void dt_control_gpx_apply_job_cleanup(void *p)
void dt_control_delete_images()
static int32_t dt_control_move_images_job_run(dt_job_t *job)
gboolean dt_control_remove_images()
static int dt_control_merge_hdr_process(dt_imageio_module_data_t *datai, const char *filename, const void *const ivoid, dt_colorspaces_color_profile_type_t over_type, const char *over_filename, void *exif, int exif_len, int32_t imgid, int num, int total, dt_dev_pixelpipe_t *pipe, const gboolean export_masks)
void dt_control_merge_hdr()
static float envelope(const float xx)
static int32_t dt_control_flip_images_job_run(dt_job_t *job)
static void _set_remove_flag(char *imgs)
_dt_delete_status
@ _DT_DELETE_STATUS_SKIP_FILE
@ _DT_DELETE_STATUS_STOP_PROCESSING
@ _DT_DELETE_STATUS_OK_TO_REMOVE
@ _DT_DELETE_STATUS_UNKNOWN
void dt_control_flip_images(const int32_t cw)
static void * dt_control_datetime_alloc()
void dt_control_copy_images()
static void dt_control_datetime_job_cleanup(void *p)
void dt_control_save_xmps(const GList *imgids, const gboolean check_history)
static enum _dt_delete_status delete_file_from_disk(const char *filename, gboolean *delete_on_trash_error)
_dt_delete_dialog_choice
@ _DT_DELETE_DIALOG_CHOICE_REMOVE
@ _DT_DELETE_DIALOG_CHOICE_CONTINUE
@ _DT_DELETE_DIALOG_CHOICE_DELETE_ALL
@ _DT_DELETE_DIALOG_CHOICE_STOP
@ _DT_DELETE_DIALOG_CHOICE_DELETE
static void dt_control_image_enumerator_job_film_init(dt_control_image_enumerator_t *t, int32_t filmid)
static int32_t dt_control_gpx_apply_job_run(dt_job_t *job)
static int32_t _generic_dt_control_fileop_images_job_run(dt_job_t *job, int32_t(*fileop_callback)(const int32_t, const int32_t), const char *desc, const char *desc_pl)
static void dt_control_export_cleanup(void *p)
static int dt_control_merge_hdr_levels(dt_imageio_module_data_t *data)
darktable_t darktable
Definition darktable.c:181
#define UNKNOWN_IMAGE
Definition darktable.h:182
static void dt_free_gpointer(gpointer ptr)
Definition darktable.h:463
#define dt_free(ptr)
Definition darktable.h:456
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:258
#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
static int FCxtrans(const int row, const int col, global const unsigned char(*const xtrans)[6])
sqlite3 * dt_database_get(const dt_database_t *db)
Definition database.c:3646
GDateTime * dt_datetime_exif_to_gdatetime(const char *exif, const GTimeZone *tz)
Definition datetime.c:239
GDateTime * dt_datetime_img_to_gdatetime(const dt_image_t *img, const GTimeZone *tz)
Definition datetime.c:281
#define DT_DATETIME_LENGTH
Definition datetime.h:37
#define DT_DEBUG_SQLITE3_PREPARE_V2(a, b, c, d, e)
Definition debug.h:107
#define DT_DEBUG_SQLITE3_BIND_TEXT(a, b, c, d, e)
Definition debug.h:118
#define DT_DEBUG_SQLITE3_BIND_INT(a, b, c)
Definition debug.h:115
static int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:374
static int dt_pthread_mutex_init(dt_pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
Definition dtpthread.h:359
static int dt_pthread_mutex_destroy(dt_pthread_mutex_t *mutex)
Definition dtpthread.h:379
static int dt_pthread_cond_wait(pthread_cond_t *cond, dt_pthread_mutex_t *mutex)
Definition dtpthread.h:384
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:364
boolean dt_win_file_trash(GFile *file, GCancellable *cancellable, GError **error)
Definition dtwin.c:355
static void weight(const float *c1, const float *c2, const float sharpen, dt_aligned_pixel_t weight)
Definition eaw.c:30
int dt_exif_read(dt_image_t *img, const char *path)
Definition exif.cc:1753
int dt_exif_read_blob(uint8_t **buf, const char *path, const int32_t imgid, const int sRGB, const int out_width, const int out_height, const int dng_mode)
Definition exif.cc:1890
int dt_film_new(dt_film_t *film, const char *directory)
Definition film.c:161
void dt_film_remove_empty()
Definition film.c:342
@ TYPE_UINT16
Definition format.h:47
void dt_gpx_destroy(struct dt_gpx_t *gpx)
Definition gpx.c:152
gboolean dt_gpx_get_location(struct dt_gpx_t *gpx, GDateTime *timestamp, dt_image_geoloc_t *geoloc)
Definition gpx.c:170
dt_gpx_t * dt_gpx_new(const gchar *filename)
Definition gpx.c:88
void dt_grouping_add_grouped_images(GList **images)
Definition grouping.c:170
GList * dt_grouping_get_group_images(const int32_t imgid)
Definition grouping.c:144
void dt_ui_notify_user()
draw user's attention
Definition gtk.c:1744
GtkWidget * dt_ui_main_window(dt_ui_t *ui)
get the main window widget
gboolean dt_history_copy_and_paste_on_image(const int32_t imgid, const int32_t dest_imgid, GList *ops, const gboolean copy_full, const dt_history_merge_strategy_t mode, const gboolean copy_iop_order, dt_hm_batch_state_t *batch)
@ DT_HISTORY_MERGE_REPLACE
dt_image_orientation_t
Definition image.h:203
const char flag
Definition image.h:252
@ DT_IMAGE_HAS_ADDITIONAL_DNG_TAGS
Definition image.h:132
@ DT_IMAGE_S_RAW
Definition image.h:134
@ DT_IMAGE_REMOVE
Definition image.h:115
@ DT_IMAGE_MOSAIC
Definition image.h:146
@ DT_IMAGE_MONOCHROME_BAYER
Definition image.h:138
@ DT_IMAGE_LOCAL_COPY
Definition image.h:121
@ DT_IMAGE_BUFFER_RESOLVED
Definition image.h:151
@ DT_IMAGE_RAW
Definition image.h:111
@ DT_IMAGE_4BAYER
Definition image.h:127
@ DT_IMAGE_MONOCHROME
Definition image.h:129
@ DT_IMAGE_HDR
Definition image.h:113
@ DT_IMAGE_LDR
Definition image.h:109
void dt_image_cache_read_release(dt_image_cache_t *cache, const dt_image_t *img)
dt_image_t * dt_image_cache_get(dt_image_cache_t *cache, const int32_t imgid, char mode)
void dt_image_cache_set_export_timestamp(dt_image_cache_t *cache, const int32_t imgid)
void dt_image_cache_write_release(dt_image_cache_t *cache, dt_image_t *img, dt_image_cache_write_mode_t mode)
@ DT_IMAGE_CACHE_SAFE
Definition image_cache.h:49
int dt_imageio_export_with_flags(const int32_t imgid, const char *filename, dt_imageio_module_format_t *format, dt_imageio_module_data_t *format_params, const gboolean ignore_exif, const gboolean display_byteorder, const gboolean high_quality, gboolean is_scaling, const gboolean thumbnail_export, const char *filter, const gboolean copy_metadata, const gboolean export_masks, dt_colorspaces_color_profile_type_t icc_type, const gchar *icc_filename, dt_iop_color_intent_t icc_intent, dt_imageio_module_storage_t *storage, dt_imageio_module_data_t *storage_params, int num, int total, dt_export_metadata_t *metadata, dt_atomic_int *shutdown)
Definition imageio.c:961
@ IMAGEIO_RGB
Definition imageio.h:70
@ IMAGEIO_FLOAT
Definition imageio.h:66
static void dt_imageio_write_dng(const char *filename, const float *const pixel, const int wd, const int ht, void *exif, const int exif_len, const uint32_t filter, const uint8_t xtrans[6][6], const float whitelevel, const dt_aligned_pixel_t wb_coeffs, const float adobe_XYZ_to_CAM[4][3])
dt_imageio_module_storage_t * dt_imageio_get_storage_by_index(int index)
dt_imageio_module_format_t * dt_imageio_get_format_by_index(int index)
static const float x
const int t
dt_job_state_t dt_control_job_get_state(_dt_job_t *job)
Definition jobs.c:103
void dt_control_job_cancel(_dt_job_t *job)
Definition jobs.c:180
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
@ DT_JOB_QUEUE_USER_EXPORT
Definition jobs.h:56
int32_t(* dt_job_execute_callback)(dt_job_t *)
Definition jobs.h:63
@ DT_JOB_STATE_CANCELLED
Definition jobs.h:46
float *const restrict const size_t k
#define CLAMPS(A, L, H)
Definition math.h:76
#define M_PI
Definition math.h:45
size_t size
Definition mipmap_cache.c:3
float dt_aligned_pixel_t[4]
gboolean dt_osx_file_trash(const char *filename, GError **error)
Definition osx.mm:114
void dt_osx_disallow_fullscreen(GtkWidget *widget)
Definition osx.mm:104
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
Definition signal.h:347
@ DT_SIGNAL_DEVELOP_IMAGE_CHANGED
This signal is raised when image is changed in darkroom.
Definition signal.h:221
@ DT_SIGNAL_FILMROLLS_CHANGED
This signal is raised when a filmroll is deleted/changed but not imported.
Definition signal.h:156
@ DT_SIGNAL_GEOTAG_CHANGED
This signal is raised when a geotag is added/deleted/changed
Definition signal.h:136
@ DT_SIGNAL_TAG_CHANGED
This signal is raised when a tag is added/deleted/changed
Definition signal.h:130
struct _GtkWidget GtkWidget
Definition splash.h:29
dt_pthread_mutex_t mutex
struct dt_undo_t * undo
Definition darktable.h:787
struct dt_gui_gtk_t * gui
Definition darktable.h:775
GTimeZone * utc_tz
Definition darktable.h:832
struct dt_collection_t * collection
Definition darktable.h:781
const struct dt_database_t * db
Definition darktable.h:779
struct dt_control_signal_t * signals
Definition darktable.h:774
struct dt_image_cache_t * image_cache
Definition darktable.h:777
struct dt_view_manager_t * view_manager
Definition darktable.h:772
struct dt_control_t * control
Definition darktable.h:773
char datetime[DT_DATETIME_LENGTH]
dt_iop_color_intent_t icc_intent
dt_imageio_module_data_t * sdata
dt_colorspaces_color_profile_type_t icc_type
dt_control_merge_hdr_t * d
dt_imageio_module_data_t parent
uint8_t first_xtrans[6][6]
dt_aligned_pixel_t wb_coeffs
dt_image_orientation_t orientation
float adobe_XYZ_to_CAM[4][3]
char dirname[512]
Definition film.h:46
Definition gpx.c:48
dt_ui_t * ui
Definition gtk.h:164
float exif_exposure
Definition image.h:285
float exif_iso
Definition image.h:288
float exif_aperture
Definition image.h:287
int32_t flags
Definition image.h:319
dt_image_orientation_t orientation
Definition image.h:284
float exif_focal_length
Definition image.h:289
int32_t crop_y
Definition image.h:316
int32_t crop_x
Definition image.h:316
dt_iop_buffer_dsc_t dsc
Definition image.h:337
float adobe_XYZ_to_CAM[4][3]
Definition image.h:362
char filename[DT_MAX_FILENAME_LEN]
Definition image.h:304
int32_t id
Definition image.h:319
dt_aligned_pixel_t wb_coeffs
Definition image.h:359
uint32_t filters
Definition format.h:60
unsigned int channels
Definition format.h:54
uint8_t xtrans[6][6]
Definition format.h:70
dt_iop_buffer_type_t datatype
Definition format.h:56
Region of interest passed through the pixelpipe.
Definition imageop.h:72
dt_view_t * current_view
Definition view.h:201
gboolean dt_tag_attach(const guint tagid, const int32_t imgid, const gboolean undo_on, const gboolean group_on)
Definition tags.c:485
gboolean dt_tag_detach(const guint tagid, const int32_t imgid, const gboolean undo_on, const gboolean group_on)
Definition tags.c:593
gboolean dt_tag_new(const char *name, guint *tagid)
Definition tags.c:179
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29
void dt_undo_end_group(dt_undo_t *self)
Definition undo.c:149
void dt_undo_start_group(dt_undo_t *self, dt_undo_type_t type)
Definition undo.c:134
@ DT_UNDO_LT_HISTORY
Definition undo.h:48
@ DT_UNDO_FLAGS
Definition undo.h:49
@ DT_UNDO_DUPLICATE
Definition undo.h:51
GList * dt_util_str_to_glist(const gchar *separator, const gchar *text)
Definition utility.c:830
size_t safe_strlen(const char *str)
check if the string is empty or NULL before calling strlen()
Definition utility.c:90
@ DT_VIEW_LIGHTTABLE
Definition view.h:77