Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
metadata_view.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2011-2012 Henrik Andersson.
4 Copyright (C) 2011, 2013-2014 johannes hanika.
5 Copyright (C) 2011 Robert Bieber.
6 Copyright (C) 2012 James C. McPherson.
7 Copyright (C) 2012 Richard Wonka.
8 Copyright (C) 2012-2018 Tobias Ellinghaus.
9 Copyright (C) 2012-2014 Ulrich Pegelow.
10 Copyright (C) 2013 Jan Kundrát.
11 Copyright (C) 2013 José Carlos García Sogo.
12 Copyright (C) 2013, 2019-2021 Pascal Obry.
13 Copyright (C) 2013-2016 Roman Lebedev.
14 Copyright (C) 2013 Simon Spannagel.
15 Copyright (C) 2013 Thomas Pryds.
16 Copyright (C) 2015 Jean-Sébastien Pédron.
17 Copyright (C) 2015-2016 Jérémy Rosen.
18 Copyright (C) 2015 Pedro Côrte-Real.
19 Copyright (C) 2015 Steven Fosdick.
20 Copyright (C) 2018 Maurizio Paglia.
21 Copyright (C) 2018 rawfiner.
22 Copyright (C) 2019 Andreas Schneider.
23 Copyright (C) 2019 luzpaz.
24 Copyright (C) 2019-2022 Philippe Weyland.
25 Copyright (C) 2020 a.
26 Copyright (C) 2020-2022 Diederik Ter Rahe.
27 Copyright (C) 2020 Hanno Schwalm.
28 Copyright (C) 2020 hatsunearu.
29 Copyright (C) 2020-2021 Hubert Kowalski.
30 Copyright (C) 2020 JP Verrue.
31 Copyright (C) 2021-2022 Aldric Renaudin.
32 Copyright (C) 2021 Bill Ferguson.
33 Copyright (C) 2021 Daniel Vogelbacher.
34 Copyright (C) 2021 Ralf Brown.
35 Copyright (C) 2021 Vincent THOMAS.
36 Copyright (C) 2021 Wolfgang Goetz.
37 Copyright (C) 2022-2023, 2025-2026 Aurélien PIERRE.
38 Copyright (C) 2022 Martin Bařinka.
39 Copyright (C) 2023 Ricky Moon.
40 Copyright (C) 2026 Guillaume Stutin.
41
42 darktable is free software: you can redistribute it and/or modify
43 it under the terms of the GNU General Public License as published by
44 the Free Software Foundation, either version 3 of the License, or
45 (at your option) any later version.
46
47 darktable is distributed in the hope that it will be useful,
48 but WITHOUT ANY WARRANTY; without even the implied warranty of
49 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
50 GNU General Public License for more details.
51
52 You should have received a copy of the GNU General Public License
53 along with darktable. If not, see <http://www.gnu.org/licenses/>.
54*/
55
56#include "common/collection.h"
57#include "common/paths.h" // DT_PATH_MAX
58#include "common/act_on.h"
59#include "system/macros.h"
60#include "system/mem_alloc.h"
62#include "common/image.h"
63#include "control/signal.h"
64#include "common/utility.h"
68#include "caches/image_cache.h"
69#include "metadata/metadata.h"
70#include "metadata/tags.h"
71#include "common/datetime.h"
72#include "common/selection.h"
73#include "common/conf.h"
74#include "control/control.h"
75#include "develop/develop.h"
76
77#include "gui/application.h"
78#include "libs/lib.h"
79#include "libs/lib_api.h"
80
81#include <gdk/gdkkeysyms.h>
82#include "widgets/scroll_wrap.h"
85#ifdef GDK_WINDOWING_QUARTZ
86#include "osx/osx.h"
87#endif
88
89DT_MODULE(1)
90
98
100{
102 GList *metadata;
104
105 // currently-displayed imgid
106 int32_t imgid;
108
110{
111 int index; // md_xx value or index inserted by lua
112 int order; // display order
113 char *name; // metadata name
114 char *value; // metadata value
115 char *tooltip; // tooltip
116 gboolean visible;
118
119enum
120{
121 /* internal */
135
136 /* exif */
149
150 /* size of final image */
153
154 /* xmp */
156
157 /* geotagging */
161
162 /* tags */
165
166 /* entries, do not touch! */
167 md_size
169
170static const char *_labels[] = {
171 /* internal */
172 N_("filmroll"),
173 N_("filmroll id"),
174 N_("image id"),
175 N_("group id"),
176 N_("filename"),
177 N_("version"),
178 N_("full path"),
179 N_("local copy"),
180 N_("import timestamp"),
181 N_("change timestamp"),
182 N_("export timestamp"),
183 N_("print timestamp"),
184 N_("flags"),
185
186 /* exif */
187 N_("model"),
188 N_("maker"),
189 N_("lens"),
190 N_("aperture"),
191 N_("exposure"),
192 N_("exposure bias"),
193 N_("focal length"),
194 N_("focus distance"),
195 N_("ISO"),
196 N_("datetime"),
197 N_("width"),
198 N_("height"),
199 N_("export width"),
200 N_("export height"),
201
202 /* xmp */
203 //FIXME: reserve DT_METADATA_NUMBER places
204 "","","","","","","","",
205
206 /* geotagging */
207 N_("latitude"),
208 N_("longitude"),
209 N_("elevation"),
210
211 /* tags */
212 N_("tags"),
213 N_("categories"),
214};
215
216static gboolean _dndactive = FALSE;
217
218const char *name(struct dt_lib_module_t *self)
219{
220 return _("EXIF and IPTC");
221}
222
223const char **views(dt_lib_module_t *self)
224{
225 static const char *v[] = {"darkroom", "lighttable", "map", "print", "slideshow", "studio_capture", NULL};
226 return v;
227}
228
230{
232}
233
235{
236 return 1;
237}
238
239static gboolean _is_metadata_ui(const int i)
240{
241 // internal metadata are not to be shown on the ui
243 {
246 }
247 else return TRUE;
248}
249
250static const char *_get_label(const int i)
251{
253 {
255 return(dt_metadata_get_name(keyid));
256 }
257 else return _labels[i];
258}
259
260#define NODATA_STRING "-"
261
262// initialize the metadata queue
264{
266 d->metadata = NULL;
267 for(int i = md_size - 1; i >= 0; i--)
268 {
269 dt_lib_metadata_info_t *m = g_malloc0(sizeof(dt_lib_metadata_info_t));
270 m->name = (char *)_get_label(i);
271 m->value = g_strdup(NODATA_STRING);
272 m->index = m->order = i;
273 m->visible = _is_metadata_ui(i);
274 d->metadata = g_list_prepend(d->metadata, m);
275 }
276}
277
278// helper which eliminates non-printable characters from a string
279// strings which are already in valid UTF-8 are retained.
280static void _filter_non_printable(char *string, size_t length)
281{
282 /* explicitly tell the validator to ignore the trailing nulls, otherwise this fails */
283 if(g_utf8_validate(string, -1, 0)) return;
284
285 unsigned char *str = (unsigned char *)string;
286 int n = 0;
287
288 while(*str != '\000' && n < length)
289 {
290 if((*str < 0x20) || (*str >= 0x7f)) *str = '.';
291
292 str++;
293 n++;
294 }
295}
296
298{
300 for(GList *meta = d->metadata; meta; meta = g_list_next(meta))
301 {
303 if(m->index == index)
304 {
305 return m;
306 }
307 }
308 return NULL;
309}
310
311static void _metadata_update_markup(const gint32 i, const char *const format, dt_lib_module_t *self)
312{
315
316 GtkLabel *label = GTK_LABEL(gtk_grid_get_child_at(GTK_GRID(d->grid), 1, m->order));
317 char *markup = g_markup_printf_escaped(format, gtk_label_get_text(label));
318 gtk_label_set_markup(label, markup);
319 dt_free(markup);
320}
321
328static void _value_label_set_text(GtkWidget *label, const char *value)
329{
330 gchar *line = g_strdelimit(g_strdup(value), "\r\n", ' ');
331 gtk_label_set_text(GTK_LABEL(label), line);
332 dt_free(line);
333}
334
335/* helper function for updating a metadata value */
336static void _metadata_update_value(const int i, const char *value, dt_lib_module_t *self)
337{
339 gboolean validated = g_utf8_validate(value, -1, NULL);
340 const gchar *str = validated ? value : NODATA_STRING;
342 if(m)
343 {
344 if(m->value)
345 {
346 dt_free(m->value);
347 }
348 m->value = g_strdup(str);
349 GtkWidget *w_value = gtk_grid_get_child_at(GTK_GRID(d->grid), 1, m->order);
350 _value_label_set_text(w_value, str);
351 const char *tooltip = m->tooltip ? m->tooltip : m->value;
352 gtk_widget_set_tooltip_text(GTK_WIDGET(w_value), tooltip);
353 }
354}
355
356static void _metadata_update_tooltip(const int i, const char *tooltip, dt_lib_module_t *self)
357{
359 if(m)
360 {
361 if(m->tooltip)
362 {
363 dt_free(m->tooltip);
364 }
365 m->tooltip = g_strdup(tooltip);
366 }
367}
368
369static void _metadata_update_timestamp(const int i, const GTimeSpan gts, dt_lib_module_t *self)
370{
371 char datetime[200];
372 const gboolean valid = gts ? dt_datetime_gtimespan_to_local(datetime, sizeof(datetime), gts, FALSE, TRUE) : FALSE;
373 _metadata_update_value(i, valid ? datetime : NODATA_STRING, self);
374}
375
376static gint _lib_metadata_sort_order(gconstpointer a, gconstpointer b)
377{
380 return ma->order - mb->order;
381}
382
383static gint _lib_metadata_sort_index(gconstpointer a, gconstpointer b)
384{
387 return ma->index - mb->index;
388}
389
390static void _metadata_get_flags(const dt_image_t *const img, char *const text, char *const tooltip, const size_t tooltip_size)
391{
392#define EMPTY_FIELD '.'
393#define FALSE_FIELD '.'
394#define TRUE_FIELD '!'
395
396#define FLAG_NB 15
397
398 char *flags_tooltip = NULL;
399 char *flag_descriptions[] = { N_("unused"),
400 N_("unused/deprecated"),
401 N_("ldr"),
402 N_("raw"),
403 N_("hdr"),
404 N_("marked for deletion"),
405 N_("auto-applying presets applied"),
406 N_("legacy flag. set for all new images"),
407 N_("local copy"),
408 N_("has .txt"),
409 N_("has .wav"),
410 N_("monochrome")
411 };
412 char *tooltip_parts[FLAG_NB] = { 0 };
413 int next_tooltip_part = 0;
414
415 gchar *value = g_strnfill(FLAG_NB, EMPTY_FIELD);
416
417 int stars = img->flags & 0x7;
418 char *star_string = NULL;
419 if(stars == 6)
420 {
421 value[0] = 'x';
422 tooltip_parts[next_tooltip_part++] = _("image rejected");
423 }
424 else
425 {
426 value[0] = '0' + stars;
427 tooltip_parts[next_tooltip_part++] = star_string = g_strdup_printf(ngettext("image has %d star", "image has %d stars", stars), stars);
428 }
429
430
431 if(img->flags & 8)
432 {
433 value[1] = TRUE_FIELD;
434 tooltip_parts[next_tooltip_part++] = _(flag_descriptions[0]);
435 }
436 else
437 value[1] = FALSE_FIELD;
438
440 {
441 value[2] = TRUE_FIELD;
442 tooltip_parts[next_tooltip_part++] = _(flag_descriptions[1]);
443 }
444 else
445 value[2] = FALSE_FIELD;
446
447 if(dt_image_is_ldr(img))
448 {
449 value[3] = 'l';
450 tooltip_parts[next_tooltip_part++] = _(flag_descriptions[2]);
451 }
452
453 if(dt_image_is_raw(img))
454 {
455 value[4] = 'r';
456 tooltip_parts[next_tooltip_part++] = _(flag_descriptions[3]);
457 }
458
459 if(dt_image_is_hdr(img))
460 {
461 value[5] = 'h';
462 tooltip_parts[next_tooltip_part++] = _(flag_descriptions[4]);
463 }
464
465 if(img->flags & DT_IMAGE_REMOVE)
466 {
467 value[6] = 'd';
468 tooltip_parts[next_tooltip_part++] = _(flag_descriptions[5]);
469 }
470
472 {
473 value[7] = 'a';
474 tooltip_parts[next_tooltip_part++] = _(flag_descriptions[6]);
475 }
476
478 {
479 value[8] = 'p';
480 tooltip_parts[next_tooltip_part++] = _(flag_descriptions[7]);
481 }
482
483 if(img->flags & DT_IMAGE_LOCAL_COPY)
484 {
485 value[9] = 'c';
486 tooltip_parts[next_tooltip_part++] = _(flag_descriptions[8]);
487 }
488
489 if(img->flags & DT_IMAGE_HAS_TXT)
490 {
491 value[10] = 't';
492 tooltip_parts[next_tooltip_part++] = _(flag_descriptions[9]);
493 }
494
495 if(img->flags & DT_IMAGE_HAS_WAV)
496 {
497 value[11] = 'w';
498 tooltip_parts[next_tooltip_part++] = _(flag_descriptions[10]);
499 }
500
502 {
503 value[12] = 'm';
504 tooltip_parts[next_tooltip_part++] = _(flag_descriptions[11]);
505 }
506
507 const int loader = (unsigned int)img->loader < LOADER_COUNT ? img->loader : 0;
508 value[13] = loaders_info[loader].flag;
509 char *loader_tooltip = g_strdup_printf(_("loader: %s"), _(loaders_info[loader].tooltip));
510 tooltip_parts[next_tooltip_part++] = loader_tooltip;
511
512 value[14] = '\0';
513
514 flags_tooltip = g_strjoinv("\n", tooltip_parts);
515 dt_free(loader_tooltip);
516
517 (void)g_strlcpy(text, value, FLAG_NB);
518 (void)g_strlcpy(tooltip, flags_tooltip, tooltip_size);
519
520 dt_free(value);
521 dt_free(star_string);
522 dt_free(flags_tooltip);
523
524#undef EMPTY_FIELD
525#undef FALSE_FIELD
526#undef TRUE_FIELD
527#undef FLAG_NB
528}
529
530static gboolean _metadata_view_get_thumb_info(const int32_t imgid, dt_image_t *info)
531{
532 if(imgid <= 0 || IS_NULL_PTR(info) || IS_NULL_PTR(dt_gui_get_global()) || IS_NULL_PTR(dt_gui_get_ui())) return FALSE;
533
534 if(dt_thumbtable_get_thumbnail_info(dt_gui_get_ui()->thumbtable_lighttable, imgid, info))
535 return TRUE;
536 if(dt_thumbtable_get_thumbnail_info(dt_gui_get_ui()->thumbtable_filmstrip, imgid, info))
537 return TRUE;
538
539 return FALSE;
540}
541
542static void _concatenate_multiple_images(gboolean skip[md_size])
543{
545
546 // Which fields do the selected images disagree on? The repository reports one count per
547 // dt_image_field_t, in that enumeration's order, which is this panel's own row order up to
548 // md_tag_names -- checked here rather than assumed.
549 int *counts = dt_image_repository_count_distinct_fields(imgids);
550 if(counts)
551 {
552 for(int32_t md = 0; md < md_tag_names && md < DT_IMAGE_FIELD_COUNT; md++)
553 skip[md] = (counts[md] > 1);
554 dt_free(counts);
555 }
556
557 // Tags and categories are their own two rows: shared only when every image carries them.
558 gboolean same_tags = TRUE;
559 gboolean same_categories = TRUE;
560 dt_tag_repository_get_agreement(imgids, &same_tags, &same_categories);
561
562 skip[md_tag_names] = !same_tags;
563 skip[md_categories] = !same_categories;
564
565 g_list_free(imgids);
566}
567
568
569/* update all values to reflect mouse over image id or no data at all */
571{
573
574 int32_t count = 0;
575 gboolean skip[md_size] = {FALSE};
576
577 int32_t img_id = dt_control_get_mouse_over_id();
578 if(img_id > -1)
579 {
580 count = 1;
581 }
583 {
586 }
587 else if(dt_act_on_get_first_image() > -1)
588 {
589 count = 1;
590 img_id = dt_act_on_get_first_image();
591 }
592
593 if(count > 1) _concatenate_multiple_images(skip);
594
595 dt_image_t tinfo = {0};
596 const gboolean have_thumb_info = _metadata_view_get_thumb_info(img_id, &tinfo);
597 if(!have_thumb_info) return;
598
599 if(img_id == d->imgid) return; // nothing to update, spare the SQL queries
600 d->imgid = img_id;
601
602 if(tinfo.film_id == -1)
603 {
604 goto fill_minuses;
605 }
606
607 // Update the metadata values
608 for(int32_t md = 0; md < md_size; md++)
609 {
610 if(skip[md] == TRUE)
611 {
612 if(md == md_internal_flags)
613 _metadata_update_tooltip(md, NULL, self);
614
615 if(md == md_internal_filmroll)
616 _metadata_update_tooltip(md, NULL, self);
617
618 _metadata_update_value(md, _("<various values>"), self);
619 _metadata_update_markup(md, "<span style=\"italic\">%s</span>", self);
620 continue;
621 }
622
623 char text[DT_PATH_MAX] = {0};
624
625 switch(md)
626 {
628 {
629 char tooltip_filmroll[300] = {0};
630 g_strlcpy(text, tinfo.filmroll, sizeof(text));
631 snprintf(tooltip_filmroll, sizeof(tooltip_filmroll), _("double-click to jump to film roll\n%s"), text);
632 _metadata_update_tooltip(md_internal_filmroll, tooltip_filmroll, self);
634 }
635 break;
636
638 (void)g_snprintf(text, sizeof(text), "%d", tinfo.film_id);
640 break;
641
643 (void)g_snprintf(text, sizeof(text), "%d", tinfo.id);
645 break;
646
648 (void)g_snprintf(text, sizeof(text), "%d", tinfo.group_id);
650 break;
651
654 break;
655
657 (void)g_snprintf(text, sizeof(text), "%d", tinfo.version);
659 break;
660
662 {
663 g_strlcpy(text, tinfo.fullpath, sizeof(text));
665 }
666 break;
667
669 (void)g_strlcpy(text, (tinfo.flags & DT_IMAGE_LOCAL_COPY) ? _("yes") : _("no"),
670 sizeof(text));
672 break;
673
676 break;
677
680 break;
681
684 break;
685
688 break;
689
691 {
692 char tooltip_flags[300] = {0};
693 dt_image_t tmp = {0};
694 tmp.flags = tinfo.flags;
695 tmp.loader = tinfo.loader;
696 _metadata_get_flags(&tmp, text, tooltip_flags, sizeof(tooltip_flags));
697 _metadata_update_tooltip(md_internal_flags, tooltip_flags, self);
699 }
700 break;
701
702 case md_exif_model:
704 break;
705
706 case md_exif_maker:
708 break;
709
710 case md_exif_lens:
712 break;
713
714 case md_exif_aperture:
715 (void)g_snprintf(text, sizeof(text), "f/%.1f", (double)tinfo.exif_aperture);
717 break;
718
719 case md_exif_exposure:
720 {
721 gchar *const str = dt_util_format_exposure(tinfo.exif_exposure);
723 dt_free(str);
724 }
725 break;
726
728 {
729 g_strlcpy(text, NODATA_STRING, sizeof(text));
730 if(!(isnan(tinfo.exif_exposure_bias)))
731 {
732 (void)g_snprintf(text, sizeof(text), _("%+.2f EV"), tinfo.exif_exposure_bias);
733 }
735 break;
736 }
737
739 (void)g_snprintf(text, sizeof(text), "%.0f mm", (double)tinfo.exif_focal_length);
741 break;
742
744 {
745 (void)g_strlcpy(text, NODATA_STRING, sizeof(text));
746 if(!(isnan(tinfo.exif_focus_distance) || (fpclassify(tinfo.exif_focus_distance) == FP_ZERO) ))
747 {
748 (void)g_snprintf(text, sizeof(text), _("%.2f m"), tinfo.exif_focus_distance);
749 }
751 break;
752 }
753
754 case md_exif_iso:
755 (void)g_snprintf(text, sizeof(text), "%.0f", (double)tinfo.exif_iso);
757 break;
758
759 case md_exif_datetime:
760 {
761 char datetime[200];
762 const gboolean milliseconds = dt_conf_get_bool("lighttable/ui/milliseconds");
763 const gboolean valid = dt_datetime_gtimespan_to_local(datetime, sizeof(datetime),
764 tinfo.exif_datetime_taken, milliseconds, FALSE);
765 _metadata_update_value(md_exif_datetime, valid ? datetime : NODATA_STRING, self);
766 }
767 break;
768
769 case md_exif_width:
770 if(tinfo.p_width && (tinfo.p_width != tinfo.width))
771 {
772 (void)g_snprintf(text, sizeof(text), "%d (%d)", tinfo.p_width, tinfo.width);
774 }
775 else
776 {
777 (void)g_snprintf(text, sizeof(text), "%d", tinfo.width);
779 }
780 break;
781
782 case md_exif_height:
783 if(tinfo.p_height && (tinfo.p_height != tinfo.height))
784 {
785 (void)g_snprintf(text, sizeof(text), "%d (%d)", tinfo.p_height, tinfo.height);
787 }
788 else
789 {
790 (void)g_snprintf(text, sizeof(text), "%d", tinfo.height);
792 }
793 break;
794
795// case md_xmp_metadata: //managed below the switch()
796// break;
797
799 if(isnan(tinfo.geoloc.latitude))
800 {
802 }
803 else
804 {
805 if(dt_conf_get_bool("plugins/lighttable/metadata_view/pretty_location"))
806 {
807 gchar *latitude = dt_util_latitude_str((float)tinfo.geoloc.latitude);
809 dt_free(latitude);
810 }
811 else
812 {
813 const gchar NS = tinfo.geoloc.latitude < 0 ? 'S' : 'N';
814 (void)g_snprintf(text, sizeof(text), "%c %09.6f", NS, fabs(tinfo.geoloc.latitude));
816 }
817 }
818 break;
819
821 if(isnan(tinfo.geoloc.longitude))
822 {
824 }
825 else
826 {
827 if(dt_conf_get_bool("plugins/lighttable/metadata_view/pretty_location"))
828 {
829 gchar *longitude = dt_util_longitude_str((float)tinfo.geoloc.longitude);
831 dt_free(longitude);
832 }
833 else
834 {
835 const gchar EW = tinfo.geoloc.longitude < 0 ? 'W' : 'E';
836 (void)g_snprintf(text, sizeof(text), "%c %010.6f", EW, fabs(tinfo.geoloc.longitude));
838 }
839 }
840 break;
841
843 if(isnan(tinfo.geoloc.elevation))
844 {
846 }
847 else
848 {
849 if(dt_conf_get_bool("plugins/lighttable/metadata_view/pretty_location"))
850 {
851 gchar *elevation = dt_util_elevation_str((float)tinfo.geoloc.elevation);
853 dt_free(elevation);
854 }
855 else
856 {
857 (void)g_snprintf(text, sizeof(text), "%.2f %s", tinfo.geoloc.elevation, _("m"));
859 }
860 }
861 break;
862
863 case md_tag_names:
864 case md_categories:
865 {
866 GList *tags = NULL;
867 char *tagstring = NULL;
868 char *categoriesstring = NULL;
869 if(dt_tag_get_attached(img_id, &tags, TRUE))
870 {
871 for(GList *taglist = tags; taglist; taglist = g_list_next(taglist))
872 {
873 const char *tagname = ((dt_tag_t *)taglist->data)->leave;
874 if(!(((dt_tag_t *)taglist->data)->flags & DT_TF_CATEGORY))
875 {
876 // tags - just keywords
877 tagstring = dt_util_dstrcat(tagstring, !IS_NULL_PTR(tagstring) ? ", %s" : "%s", tagname);
878 }
879 else
880 {
881 // categories - needs parent category to make sense
882 char *category = g_strdup(((dt_tag_t *)taglist->data)->tag);
883 char *catend = g_strrstr(category, "|");
884 if(catend)
885 {
886 catend[0] = '\0';
887 char *catstart = g_strrstr(category, "|");
888 catstart = catstart ? catstart + 1 : category;
889 categoriesstring = dt_util_dstrcat(categoriesstring,
890 !IS_NULL_PTR(categoriesstring) ? ", %s: %s" : "%s: %s",
891 catstart, ((dt_tag_t *)taglist->data)->leave);
892 }
893 else
894 categoriesstring = dt_util_dstrcat(categoriesstring,
895 !IS_NULL_PTR(categoriesstring) ? ", %s" : "%s",
896 ((dt_tag_t *)taglist->data)->leave);
897 dt_free(category);
898 }
899 }
900 }
901
902 if (md == md_tag_names)
903 _metadata_update_value(md_tag_names, tagstring ? tagstring : NODATA_STRING, self);
904 else
905 _metadata_update_value(md_categories, categoriesstring ? categoriesstring : NODATA_STRING, self);
906
907 dt_free(tagstring);
908 dt_free(categoriesstring);
909 dt_tag_free_result(&tags);
910 }
911 break;
912
913 default:
914 break;
915 }
916
917 //cases not handled by switch
919 {
920 g_strlcpy(text, NODATA_STRING, sizeof(text));
921
922 const uint32_t keyid = dt_metadata_get_keyid_by_display_order((uint32_t)(md - md_xmp_metadata));
923 const gboolean hidden = dt_metadata_get_type(keyid) == DT_METADATA_TYPE_INTERNAL;
924 if(! hidden)
925 {
926 const gchar *const key = dt_metadata_get_key(keyid);
927 GList *res = dt_metadata_get(img_id, key, NULL);
928 if(res)
929 {
930 g_strlcpy(text, (char *)res->data, sizeof(text));
931 _filter_non_printable(text, sizeof(text));
932 g_list_free_full(res, dt_free_gpointer);
933 res = NULL;
934 }
935 _metadata_update_value(md, text, self);
936 }
937 }
938 }
939
940 return;
941
942/* reset */
943fill_minuses:
944 for(int k = 0; k < md_size; k++) _metadata_update_value(k, NODATA_STRING, self);
945}
946
947static void _jump_to()
948{
949 int32_t imgid = dt_control_get_mouse_over_id();
951 if(imgid != UNKNOWN_IMAGE)
952 {
953 char path[512];
954 const dt_image_t *img = dt_image_cache_get(imgid, 'r');
955 if(IS_NULL_PTR(img)) return;
956 dt_image_film_roll_directory(img, path, sizeof(path));
958 char collect[1024];
959 snprintf(collect, sizeof(collect), "1:0:0:%s$", path);
961 }
962}
963
964static gboolean _filmroll_clicked(GtkWidget *widget, GdkEventButton *event, gpointer null)
965{
966 if(event->type != GDK_2BUTTON_PRESS) return FALSE;
967 _jump_to();
968 return TRUE;
969}
970
971/* callback for the mouse over image change signal */
972static void _mouse_over_image_callback(gpointer instance, gpointer user_data)
973{
974 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
975 //if(dt_lib_gui_get_expanded(self))
977}
978
980{
982 char *pref = NULL;
983
984 d->metadata = g_list_sort(d->metadata, _lib_metadata_sort_order);
985 for(GList *meta = d->metadata; meta; meta= g_list_next(meta))
986 {
988 if(_is_metadata_ui(m->index))
989 pref = dt_util_dstrcat(pref, "%s%s,", m->visible ? "" : "|", m->name);
990 }
991 if(pref)
992 {
993 pref[strlen(pref) - 1] = '\0';
994 }
995 return pref;
996}
997
999{
1001 d->metadata = g_list_sort(d->metadata, _lib_metadata_sort_order);
1002
1003 int j = 0;
1004 // initialize the grid with metadata queue content
1005 for(GList *meta = d->metadata; meta; meta = g_list_next(meta))
1006 {
1008 m->order = j;
1009 GtkWidget *w_name = gtk_grid_get_child_at(GTK_GRID(d->grid), 0, j);
1010 gtk_label_set_text(GTK_LABEL(w_name), _(m->name));
1011 gtk_widget_set_tooltip_text(w_name, _(m->name));
1012 GtkWidget *w_value = gtk_grid_get_child_at(GTK_GRID(d->grid), 1, j);
1013 _value_label_set_text(w_value, m->value);
1014 const char *tooltip = m->tooltip ? m->tooltip : m->value;
1015 gtk_widget_set_tooltip_text(w_value, tooltip);
1016
1017 const int i = m->index;
1018 gtk_label_set_ellipsize(GTK_LABEL(w_value),
1020 ? PANGO_ELLIPSIZE_END : PANGO_ELLIPSIZE_MIDDLE);
1021 if(i == md_internal_filmroll)
1022 {
1023 // film roll jump to:
1024 if(d->filmroll_event && GTK_IS_WIDGET(d->filmroll_event))
1025 g_signal_handlers_disconnect_by_func(d->filmroll_event, G_CALLBACK(_filmroll_clicked), NULL);
1026 g_signal_connect(G_OBJECT(w_value), "button-press-event", G_CALLBACK(_filmroll_clicked), NULL);
1027 d->filmroll_event = G_OBJECT(w_value);
1028 }
1029
1030 gtk_widget_set_visible(w_name, m->visible);
1031 gtk_widget_set_visible(w_value, m->visible);
1032 j++;
1033 }
1034}
1035
1037{
1039
1040 int j = 0;
1041 // initialize the grid with metadata queue content
1042 for(GList *meta = d->metadata; meta; meta = g_list_next(meta))
1043 {
1045 GtkWidget *w_name = gtk_label_new(_(m->name));
1046 gtk_widget_set_halign(w_name, GTK_ALIGN_START);
1047 gtk_label_set_xalign(GTK_LABEL(w_name), 0.0f);
1048 gtk_label_set_ellipsize(GTK_LABEL(w_name), PANGO_ELLIPSIZE_END);
1049 gtk_widget_set_tooltip_text(w_name, _(m->name));
1050
1051 GtkWidget *w_value= gtk_label_new(m->value);
1052 gtk_widget_set_name(w_value, "brightbg");
1053 gtk_label_set_selectable(GTK_LABEL(w_value), TRUE);
1054 gtk_widget_set_halign(w_value, GTK_ALIGN_FILL);
1055 gtk_label_set_xalign(GTK_LABEL(w_value), 0.0f);
1056
1057 gtk_grid_attach(GTK_GRID(d->grid), w_name, 0, j, 1, 1);
1058 gtk_grid_attach(GTK_GRID(d->grid), w_value, 1, j, 1, 1);
1059 j++;
1060 }
1061}
1062
1063static void _apply_preferences(const char *prefs_list, dt_lib_module_t *self)
1064{
1065 if(!prefs_list || !prefs_list[0]) return;
1067
1068 GList *prefs = dt_util_str_to_glist(",", prefs_list);
1069 int k = 0;
1070 for(GList *pref = prefs; pref; pref = g_list_next(pref))
1071 {
1072 const char *name = (char *)pref->data;
1073 gboolean visible = TRUE;
1074 if(name)
1075 {
1076 if(name[0] == '|')
1077 {
1078 name++;
1079 visible = FALSE;
1080 }
1081 for(GList *meta = d->metadata; meta; meta= g_list_next(meta))
1082 {
1084 if(name && !g_strcmp0(name, m->name))
1085 {
1086 m->order = k;
1087 m->visible = visible;
1088 break;
1089 }
1090 }
1091 }
1092 else continue;
1093 k++;
1094 }
1095 g_list_free_full(prefs, dt_free_gpointer);
1096 prefs = NULL;
1097
1099}
1100
1102{
1103 char *pref = _get_current_configuration(self);
1104 dt_conf_set_string("plugins/lighttable/metadata_view/visible", pref);
1105 dt_free(pref);
1106}
1107
1108static void _select_toggled_callback(GtkCellRendererToggle *cell_renderer, gchar *path_str, gpointer user_data)
1109{
1110 GtkListStore *store = (GtkListStore *)user_data;
1111 GtkTreeIter iter;
1112 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1113 gboolean selected;
1114
1115 gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path);
1116 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, DT_METADATA_PREF_COL_VISIBLE, &selected, -1);
1117 gtk_list_store_set(store, &iter, DT_METADATA_PREF_COL_VISIBLE, !selected, -1);
1118
1119 gtk_tree_path_free(path);
1120}
1121
1122static void _drag_data_inserted(GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter, gpointer user_data)
1123{
1124 _dndactive = TRUE;
1125}
1126
1127void _menuitem_preferences(GtkMenuItem *menuitem, dt_lib_module_t *self)
1128{
1130
1132 GtkWidget *dialog = gtk_dialog_new_with_buttons(_("metadata settings"), GTK_WINDOW(win),
1133 GTK_DIALOG_DESTROY_WITH_PARENT, _("default"), GTK_RESPONSE_YES,
1134 _("cancel"), GTK_RESPONSE_NONE, _("save"), GTK_RESPONSE_ACCEPT, NULL);
1135 g_signal_connect(dialog, "key-press-event", G_CALLBACK(dt_handle_dialog_enter), NULL);
1136 GtkWidget *area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1137
1138 GtkWidget *w = gtk_scrolled_window_new(NULL, NULL);
1139 gtk_widget_set_size_request(w, -1, DT_PIXEL_APPLY_DPI(600));
1140 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1141 gtk_scrolled_window_set_overlay_scrolling(GTK_SCROLLED_WINDOW(w), FALSE);
1142 dt_gui_add_class(w, "dt_recessed_scroll");
1143 gtk_box_pack_start(GTK_BOX(area), w, TRUE, TRUE, 0);
1144
1145 GtkListStore *store = gtk_list_store_new(DT_METADATA_PREF_NUM_COLS,
1146 G_TYPE_INT, G_TYPE_STRING, G_TYPE_BOOLEAN);
1147 GtkTreeModel *model = GTK_TREE_MODEL(store);
1148
1149 GtkTreeIter iter;
1150 d->metadata = g_list_sort(d->metadata, _lib_metadata_sort_order);
1151 for(GList *meta = d->metadata; meta; meta = g_list_next(meta))
1152 {
1154 if(!_is_metadata_ui(m->index))
1155 continue;
1156 gtk_list_store_append(store, &iter);
1157 gtk_list_store_set(store, &iter,
1161 -1);
1162 }
1163
1164 GtkWidget *view = gtk_tree_view_new_with_model(model);
1165 g_object_unref(model);
1166 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
1167 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(_("metadata"), renderer,
1168 "text", DT_METADATA_PREF_COL_NAME_L, NULL);
1169 gtk_tree_view_column_set_expand(column, TRUE);
1170 gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
1171 GtkWidget *header = gtk_tree_view_column_get_button(column);
1172 gtk_widget_set_tooltip_text(header,
1173 _("drag and drop one row at a time until you get the desired order"
1174 "\nuntick to hide metadata which are not of interest for you"
1175 "\nif different settings are needed, use presets"));
1176 renderer = gtk_cell_renderer_toggle_new();
1177 g_signal_connect(renderer, "toggled", G_CALLBACK(_select_toggled_callback), store);
1178 column = gtk_tree_view_column_new_with_attributes(_("visible"), renderer,
1179 "active", DT_METADATA_PREF_COL_VISIBLE, NULL);
1180 gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
1181
1182 // drag & drop
1183 gtk_tree_view_set_reorderable(GTK_TREE_VIEW(view), TRUE);
1184 g_signal_connect(G_OBJECT(model), "row-inserted", G_CALLBACK(_drag_data_inserted), NULL);
1185
1186 gtk_container_add(GTK_CONTAINER(w), view);
1187
1188#ifdef GDK_WINDOWING_QUARTZ
1190#endif
1191 gtk_widget_show_all(dialog);
1192
1193 int res = gtk_dialog_run(GTK_DIALOG(dialog));
1194 while(res == GTK_RESPONSE_YES)
1195 {
1196 gtk_tree_model_get_iter_first(model, &iter);
1197 d->metadata = g_list_sort(d->metadata, _lib_metadata_sort_index);
1198 for(GList *meta = d->metadata; meta; meta= g_list_next(meta))
1199 {
1201 if(!_is_metadata_ui(m->index))
1202 continue;
1203 gtk_list_store_set(store, &iter,
1207 -1);
1208 gtk_tree_model_iter_next(model, &iter);
1209 }
1210 res = gtk_dialog_run(GTK_DIALOG(dialog));
1211 }
1212
1213 int i = 0;
1214 if(res == GTK_RESPONSE_ACCEPT)
1215 {
1216 gboolean valid = gtk_tree_model_get_iter_first(model, &iter);
1217 while(valid)
1218 {
1219 gboolean visible;
1220 uint32_t index;
1221 gtk_tree_model_get(model, &iter,
1224 -1);
1225 for(GList *meta = d->metadata; meta; meta= g_list_next(meta))
1226 {
1228 if(m->index == index)
1229 {
1230 m->order = i;
1231 m->visible = visible;
1232 break;
1233 }
1234 }
1235 i++;
1236 valid = gtk_tree_model_iter_next(model, &iter);
1237 }
1238
1240 _save_preferences(self);
1241 }
1242 gtk_widget_destroy(dialog);
1243}
1244
1245void set_preferences(void *menu, dt_lib_module_t *self)
1246{
1247 GtkWidget *mi = gtk_menu_item_new_with_label(_("preferences..."));
1248 g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(_menuitem_preferences), self);
1249 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
1250}
1251
1253{
1254 *size = 0;
1255 char *params = _get_current_configuration(self);
1256 if(params)
1257 *size =strlen(params) + 1;
1258 return params;
1259}
1260
1261int set_params(dt_lib_module_t *self, const void *params, int size)
1262{
1263 if(IS_NULL_PTR(params)) return 1;
1264
1266 _save_preferences(self);
1267 return 0;
1268}
1269
1271{
1273
1274 for(GList *meta = d->metadata; meta; meta= g_list_next(meta))
1275 {
1277 m->order = m->index;
1278 m->visible = _is_metadata_ui(m->index);
1279 }
1281}
1282
1284{
1285 /* initialize ui */
1287 self->data = (void *)d;
1288 d->imgid = -1;
1289
1291
1292 GtkWidget *child_grid_window = gtk_grid_new();
1293 d->grid = child_grid_window;
1294 gtk_grid_set_column_spacing(GTK_GRID(child_grid_window), DT_GUI_BOX_SPACING);
1295
1296 // Fitted to the visible rows: every value is one line, so hovering another image never resizes it.
1297 self->widget = dt_ui_scroll_wrap(child_grid_window, 1, "plugins/lighttable/metadata_view/windowheight",
1299
1300 gtk_widget_show_all(d->grid);
1301 gtk_widget_set_no_show_all(d->grid, TRUE);
1303 const char *pref = dt_conf_get_string_const("plugins/lighttable/metadata_view/visible");
1304 if(!strlen(pref))
1305 _display_default(self);
1306 _apply_preferences(pref, self);
1307
1308 /* lets signup for mouse over image change signals */
1310 G_CALLBACK(_mouse_over_image_callback), self);
1311
1313 G_CALLBACK(_mouse_over_image_callback), self);
1314}
1315
1317{
1318 if(m->value)
1319 {
1320 dt_free(m->value);
1321 }
1322 if(m->tooltip)
1323 {
1324 dt_free(m->tooltip);
1325 }
1326 dt_free(m);
1327}
1328
1330{
1332 if(IS_NULL_PTR(self->data)) return;
1334 g_list_free_full(d->metadata, (GDestroyNotify)_free_metadata_queue);
1335 d->metadata = NULL;
1336 dt_free(self->data);
1337}
1338
1340{
1342
1343 for(GList *meta = d->metadata; meta; meta= g_list_next(meta))
1344 {
1346 m->order = m->index;
1347 m->visible = _is_metadata_ui(m->index);
1348 }
1350 _save_preferences(self);
1351}
1352
1353// clang-format off
1354// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1355// vim: shiftwidth=2 expandtab tabstop=2 cindent
1356// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1357// clang-format on
int32_t dt_act_on_get_first_image()
Definition act_on.c:67
GtkWidget * dt_gui_main_window(void)
struct dt_ui_t * dt_gui_get_ui(void)
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
#define m
Definition basecurve.c:283
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
void dt_collection_deserialize(const char *buf)
Definition collection.c:763
const float v
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
int dt_conf_get_bool(const char *name)
void dt_conf_set_string(const char *name, const char *val)
const char * dt_conf_get_string_const(const char *name)
Borrow the stored string for name without copying it.
gboolean dt_image_is_raw(const dt_image_t *img)
int dt_image_monochrome_flags(const dt_image_t *img)
gboolean dt_image_is_hdr(const dt_image_t *img)
gboolean dt_image_is_ldr(const dt_image_t *img)
void dt_image_film_roll_directory(const dt_image_t *img, char *pathname, size_t pathname_len)
int32_t dt_control_get_mouse_over_id()
Definition control.c:1032
struct dt_gui_gtk_t * dt_gui_get_global(void)
Definition darktable.c:526
gboolean dt_datetime_gtimespan_to_local(char *local, const size_t local_size, const GTimeSpan gts, const gboolean msec, const gboolean tz)
Definition datetime.c:178
GtkTreeStore * store
its model, owned by the view
GHashTable * selected
set of checked row labels, mirrored to conf on every change
GtkWidget * view
the tree view
const int res
Definition dtpthread.h:351
static const struct @4 loaders_info[LOADER_COUNT]
#define UNKNOWN_IMAGE
Definition image.h:78
const char * tooltip
Definition image.h:319
@ DT_IMAGE_NO_LEGACY_PRESETS
Definition image.h:141
@ DT_IMAGE_HAS_WAV
Definition image.h:147
@ DT_IMAGE_AUTO_PRESETS_APPLIED
Definition image.h:139
@ DT_IMAGE_REMOVE
Definition image.h:137
@ DT_IMAGE_LOCAL_COPY
Definition image.h:143
@ DT_IMAGE_THUMBNAIL_DEPRECATED
Definition image.h:128
@ DT_IMAGE_HAS_TXT
Definition image.h:145
@ LOADER_COUNT
Definition image.h:306
dt_image_t * dt_image_cache_get(const int32_t imgid, char mode)
void dt_image_cache_read_release(const dt_image_t *img)
int * dt_image_repository_count_distinct_fields(GList *imgids)
How many DISTINCT values each dt_image_field_t takes over imgids.
Reading and writing one dt_image_t to and from the library database. The SQL half of what used to be ...
@ DT_IMAGE_FIELD_COUNT
static CameraMetaData * meta
const char * model
static const dt_iop_drawlayer_params_t * params
Definition layers.h:80
gboolean dt_handle_dialog_enter(GtkWidget *widget, GdkEventKey *event, gpointer data)
Definition lib.c:1335
dt_metadata_pref_cols_t
float *const restrict const size_t k
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
static void dt_free_gpointer(gpointer ptr)
g_free() one pointer, with the signature GDestroyNotify wants.
Definition mem_alloc.h:184
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
char * key
const char * dt_metadata_get_key(const uint32_t keyid)
const char * dt_metadata_get_name(const uint32_t keyid)
GList * dt_metadata_get(const int id, const char *key, uint32_t *count)
int dt_metadata_get_type(const uint32_t keyid)
dt_metadata_t dt_metadata_get_keyid_by_display_order(const uint32_t order)
@ DT_METADATA_NUMBER
Definition metadata.h:57
@ DT_METADATA_TYPE_INTERNAL
Definition metadata.h:65
void gui_reset(dt_lib_module_t *self)
void _menuitem_preferences(GtkMenuItem *menuitem, dt_lib_module_t *self)
int set_params(dt_lib_module_t *self, const void *params, int size)
static void _save_preferences(dt_lib_module_t *self)
void * get_params(dt_lib_module_t *self, int *size)
static void _value_label_set_text(GtkWidget *label, const char *value)
Show value in label on one line, line breaks turned into spaces.
#define FLAG_NB
void set_preferences(void *menu, dt_lib_module_t *self)
#define EMPTY_FIELD
static gboolean _metadata_view_get_thumb_info(const int32_t imgid, dt_image_t *info)
static void _free_metadata_queue(dt_lib_metadata_info_t *m)
#define NODATA_STRING
static void _metadata_view_update_values(dt_lib_module_t *self)
static void _jump_to()
static char * _get_current_configuration(dt_lib_module_t *self)
static void _metadata_get_flags(const dt_image_t *const img, char *const text, char *const tooltip, const size_t tooltip_size)
static gboolean _filmroll_clicked(GtkWidget *widget, GdkEventButton *event, gpointer null)
void gui_cleanup(dt_lib_module_t *self)
static const char * _labels[]
dt_metadata_pref_cols_t
@ DT_METADATA_PREF_COL_INDEX
@ DT_METADATA_PREF_NUM_COLS
@ DT_METADATA_PREF_COL_NAME_L
@ DT_METADATA_PREF_COL_VISIBLE
static void _lib_metadata_setup_grid(dt_lib_module_t *self)
static gboolean _dndactive
static gboolean _is_metadata_ui(const int i)
@ md_exif_width
@ md_geotagging_ele
@ md_internal_fullpath
@ md_exif_exposure
@ md_internal_flags
@ md_height
@ md_xmp_metadata
@ md_categories
@ md_geotagging_lon
@ md_internal_filmroll_id
@ md_exif_model
@ md_exif_exposure_bias
@ md_internal_version
@ md_exif_lens
@ md_exif_focus_distance
@ md_tag_names
@ md_internal_imgid
@ md_internal_import_timestamp
@ md_exif_datetime
@ md_internal_groupid
@ md_internal_export_timestamp
@ md_size
@ md_exif_aperture
@ md_exif_iso
@ md_internal_print_timestamp
@ md_exif_height
@ md_exif_focal_length
@ md_internal_local_copy
@ md_internal_change_timestamp
@ md_internal_filmroll
@ md_internal_filename
@ md_width
@ md_exif_maker
@ md_geotagging_lat
static dt_lib_metadata_info_t * _get_metadata_per_index(const int index, dt_lib_module_t *self)
static void _lib_metadata_refill_grid(dt_lib_module_t *self)
static void _lib_metadata_init_queue(dt_lib_module_t *self)
static void _filter_non_printable(char *string, size_t length)
static void _metadata_update_markup(const gint32 i, const char *const format, dt_lib_module_t *self)
static void _apply_preferences(const char *prefs_list, dt_lib_module_t *self)
static void _select_toggled_callback(GtkCellRendererToggle *cell_renderer, gchar *path_str, gpointer user_data)
static void _mouse_over_image_callback(gpointer instance, gpointer user_data)
static gint _lib_metadata_sort_order(gconstpointer a, gconstpointer b)
uint32_t container(dt_lib_module_t *self)
static void _metadata_update_value(const int i, const char *value, dt_lib_module_t *self)
static void _drag_data_inserted(GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter, gpointer user_data)
void gui_init(dt_lib_module_t *self)
int position()
const char ** views(dt_lib_module_t *self)
static void _metadata_update_timestamp(const int i, const GTimeSpan gts, dt_lib_module_t *self)
static void _display_default(dt_lib_module_t *self)
static gint _lib_metadata_sort_index(gconstpointer a, gconstpointer b)
static void _metadata_update_tooltip(const int i, const char *tooltip, dt_lib_module_t *self)
static const char * _get_label(const int i)
static void _concatenate_multiple_images(gboolean skip[md_size])
#define TRUE_FIELD
#define FALSE_FIELD
size_t size
Definition mipmap_cache.c:3
dt_mipmap_buffer_dsc_flags flags
Definition mipmap_cache.c:4
#define DT_MODULE(MODVER)
Instantiate the version handshake every module must export. Use once, at file scope,...
void dt_osx_disallow_fullscreen(GtkWidget *widget)
Definition osx.mm:105
#define DT_PATH_MAX
Buffer size for a filesystem path anywhere in Ansel.
Definition paths.h:57
const char * name
Definition pdf.h:90
GtkWidget * dt_ui_scroll_wrap(GtkWidget *w, gint min_size, const char *config_str, dt_ui_resize_mode_t mode)
Wrap a scrollable content widget in a recessed, vertically resizable scrolled window.
@ DT_UI_RESIZE_DYNAMIC
Definition scroll_wrap.h:43
int32_t dt_selection_get_first_id(struct dt_selection_t *selection)
Definition selection.c:103
struct dt_selection_t * dt_selection_get_global(void)
Definition selection.c:80
int dt_selection_get_length(struct dt_selection_t *selection)
Definition selection.c:193
GList * dt_selection_get_list(struct dt_selection_t *selection)
Definition selection.c:185
int32_t dt_selection_repository_get_lowest_id(void)
The lowest selected image id, or UNKNOWN_IMAGE when nothing is selected.
main.selected_images, and the memory.selected_backup it is pushed onto.
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
Definition signal.h:411
struct dt_control_signal_t * dt_control_signal_get_global(void)
Definition darktable.c:619
@ DT_SIGNAL_MOUSE_OVER_IMAGE_CHANGE
This signal is raised when mouse hovers over image thumbs both on lighttable and in the filmstrip....
Definition signal.h:63
@ DT_SIGNAL_SELECTION_CHANGED
This signal is raised when the selection is changed no param, no returned value.
Definition signal.h:131
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
Definition signal.h:400
static const dt_aligned_pixel_simd_t value
Definition simd.h:144
double latitude
Definition image.h:341
double elevation
Definition image.h:341
double longitude
Definition image.h:341
float exif_exposure
Definition image.h:376
int32_t height
Definition image.h:406
GTimeSpan export_timestamp
Definition image.h:424
float exif_focus_distance
Definition image.h:381
GTimeSpan import_timestamp
Definition image.h:424
int32_t group_id
Definition image.h:410
float exif_exposure_bias
Definition image.h:377
float exif_iso
Definition image.h:379
dt_image_loader_t loader
Definition image.h:426
float exif_aperture
Definition image.h:378
int32_t version
Definition image.h:410
dt_image_geoloc_t geoloc
Definition image.h:443
int32_t flags
Definition image.h:410
int32_t width
Definition image.h:406
float exif_focal_length
Definition image.h:380
char exif_maker[64]
Definition image.h:383
GTimeSpan change_timestamp
Definition image.h:424
char exif_lens[128]
Definition image.h:385
GTimeSpan print_timestamp
Definition image.h:424
int32_t film_id
Definition image.h:410
GTimeSpan exif_datetime_taken
Definition image.h:386
int32_t p_height
Definition image.h:406
int32_t p_width
Definition image.h:406
char filmroll[DT_PATH_MAX]
Definition image.h:400
char exif_model[64]
Definition image.h:384
char filename[DT_MAX_FILENAME_LEN]
Definition image.h:395
int32_t id
Definition image.h:410
char fullpath[DT_PATH_MAX]
Definition image.h:396
GModule *void * data
Definition lib.h:79
GtkWidget * widget
Definition lib.h:83
void dt_tag_repository_get_agreement(GList *imgids, gboolean *same_tags, gboolean *same_categories)
Do all of imgids carry exactly the same tags? The same categories?
data.tags and main.tagged_images.
uint32_t dt_tag_get_attached(const int32_t imgid, GList **result, const gboolean ignore_dt_tags)
Definition tags.c:515
void dt_tag_free_result(GList **result)
Definition tags.c:1003
@ DT_TF_CATEGORY
Definition tags.h:64
typedef double((*spd)(unsigned long int wavelength, double TempK))
gboolean dt_thumbtable_get_thumbnail_info(dt_thumbtable_t *table, int32_t imgid, dt_image_t *out)
Definition thumbtable.c:607
gchar * dt_util_longitude_str(float longitude)
Definition utility.c:595
gchar * dt_util_elevation_str(float elevation)
Definition utility.c:613
GList * dt_util_str_to_glist(const gchar *separator, const gchar *text)
Definition utility.c:856
char * dt_util_format_exposure(const float exposuretime)
Definition utility.c:891
gchar * dt_util_latitude_str(float latitude)
Definition utility.c:577
gchar * dt_util_dstrcat(gchar *str, const gchar *format,...)
Definition utility.c:122
#define DT_GUI_BOX_SPACING
#define DT_PIXEL_APPLY_DPI(value)
void dt_gui_add_class(GtkWidget *widget, const gchar *class_name)
@ DT_UI_CONTAINER_PANEL_LEFT_CENTER