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
41 darktable is free software: you can redistribute it and/or modify
42 it under the terms of the GNU General Public License as published by
43 the Free Software Foundation, either version 3 of the License, or
44 (at your option) any later version.
45
46 darktable is distributed in the hope that it will be useful,
47 but WITHOUT ANY WARRANTY; without even the implied warranty of
48 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
49 GNU General Public License for more details.
50
51 You should have received a copy of the GNU General Public License
52 along with darktable. If not, see <http://www.gnu.org/licenses/>.
53*/
54
55#include "common/collection.h"
56#include "common/paths.h" // DT_PATH_MAX
57#include "common/act_on.h"
58#include "system/macros.h"
59#include "system/mem_alloc.h"
61#include "common/image.h"
62#include "control/signal.h"
63#include "common/utility.h"
67#include "caches/image_cache.h"
68#include "metadata/metadata.h"
69#include "metadata/tags.h"
70#include "common/datetime.h"
71#include "common/selection.h"
72#include "common/conf.h"
73#include "control/control.h"
74#include "develop/develop.h"
75
76#include "gui/application.h"
77#include "libs/lib.h"
78#include "libs/lib_api.h"
79
80#include <gdk/gdkkeysyms.h>
81#include "widgets/scroll_wrap.h"
84#ifdef GDK_WINDOWING_QUARTZ
85#include "osx/osx.h"
86#endif
87
88DT_MODULE(1)
89
97
99{
101 GList *metadata;
103
104 // currently-displayed imgid
105 int32_t imgid;
107
109{
110 int index; // md_xx value or index inserted by lua
111 int order; // display order
112 char *name; // metadata name
113 char *value; // metadata value
114 char *tooltip; // tooltip
115 gboolean visible;
117
118enum
119{
120 /* internal */
134
135 /* exif */
148
149 /* size of final image */
152
153 /* xmp */
155
156 /* geotagging */
160
161 /* tags */
164
165 /* entries, do not touch! */
166 md_size
168
169static const char *_labels[] = {
170 /* internal */
171 N_("filmroll"),
172 N_("filmroll id"),
173 N_("image id"),
174 N_("group id"),
175 N_("filename"),
176 N_("version"),
177 N_("full path"),
178 N_("local copy"),
179 N_("import timestamp"),
180 N_("change timestamp"),
181 N_("export timestamp"),
182 N_("print timestamp"),
183 N_("flags"),
184
185 /* exif */
186 N_("model"),
187 N_("maker"),
188 N_("lens"),
189 N_("aperture"),
190 N_("exposure"),
191 N_("exposure bias"),
192 N_("focal length"),
193 N_("focus distance"),
194 N_("ISO"),
195 N_("datetime"),
196 N_("width"),
197 N_("height"),
198 N_("export width"),
199 N_("export height"),
200
201 /* xmp */
202 //FIXME: reserve DT_METADATA_NUMBER places
203 "","","","","","","","",
204
205 /* geotagging */
206 N_("latitude"),
207 N_("longitude"),
208 N_("elevation"),
209
210 /* tags */
211 N_("tags"),
212 N_("categories"),
213};
214
215static gboolean _dndactive = FALSE;
216
217const char *name(struct dt_lib_module_t *self)
218{
219 return _("EXIF and IPTC");
220}
221
222const char **views(dt_lib_module_t *self)
223{
224 static const char *v[] = {"darkroom", "lighttable", "map", "print", "slideshow", "studio_capture", NULL};
225 return v;
226}
227
229{
231}
232
234{
235 return 1;
236}
237
238static gboolean _is_metadata_ui(const int i)
239{
240 // internal metadata are not to be shown on the ui
242 {
245 }
246 else return TRUE;
247}
248
249static const char *_get_label(const int i)
250{
252 {
254 return(dt_metadata_get_name(keyid));
255 }
256 else return _labels[i];
257}
258
259#define NODATA_STRING "-"
260
261// initialize the metadata queue
263{
265 d->metadata = NULL;
266 for(int i = md_size - 1; i >= 0; i--)
267 {
268 dt_lib_metadata_info_t *m = g_malloc0(sizeof(dt_lib_metadata_info_t));
269 m->name = (char *)_get_label(i);
270 m->value = g_strdup(NODATA_STRING);
271 m->index = m->order = i;
272 m->visible = _is_metadata_ui(i);
273 d->metadata = g_list_prepend(d->metadata, m);
274 }
275}
276
277// helper which eliminates non-printable characters from a string
278// strings which are already in valid UTF-8 are retained.
279static void _filter_non_printable(char *string, size_t length)
280{
281 /* explicitly tell the validator to ignore the trailing nulls, otherwise this fails */
282 if(g_utf8_validate(string, -1, 0)) return;
283
284 unsigned char *str = (unsigned char *)string;
285 int n = 0;
286
287 while(*str != '\000' && n < length)
288 {
289 if((*str < 0x20) || (*str >= 0x7f)) *str = '.';
290
291 str++;
292 n++;
293 }
294}
295
297{
299 for(GList *meta = d->metadata; meta; meta = g_list_next(meta))
300 {
302 if(m->index == index)
303 {
304 return m;
305 }
306 }
307 return NULL;
308}
309
310static void _metadata_update_markup(const gint32 i, const char *const format, dt_lib_module_t *self)
311{
314
315 GtkLabel *label = GTK_LABEL(gtk_grid_get_child_at(GTK_GRID(d->grid), 1, m->order));
316 char *markup = g_markup_printf_escaped(format, gtk_label_get_text(label));
317 gtk_label_set_markup(label, markup);
318 dt_free(markup);
319}
320
321/* helper function for updating a metadata value */
322static void _metadata_update_value(const int i, const char *value, dt_lib_module_t *self)
323{
325 gboolean validated = g_utf8_validate(value, -1, NULL);
326 const gchar *str = validated ? value : NODATA_STRING;
328 if(m)
329 {
330 if(m->value)
331 {
332 dt_free(m->value);
333 }
334 m->value = g_strdup(str);
335 GtkWidget *w_value = gtk_grid_get_child_at(GTK_GRID(d->grid), 1, m->order);
336 gtk_label_set_text(GTK_LABEL(w_value), str);
337 const char *tooltip = m->tooltip ? m->tooltip : m->value;
338 gtk_widget_set_tooltip_text(GTK_WIDGET(w_value), tooltip);
339 }
340}
341
342static void _metadata_update_tooltip(const int i, const char *tooltip, dt_lib_module_t *self)
343{
345 if(m)
346 {
347 if(m->tooltip)
348 {
349 dt_free(m->tooltip);
350 }
351 m->tooltip = g_strdup(tooltip);
352 }
353}
354
355static void _metadata_update_timestamp(const int i, const GTimeSpan gts, dt_lib_module_t *self)
356{
357 char datetime[200];
358 const gboolean valid = gts ? dt_datetime_gtimespan_to_local(datetime, sizeof(datetime), gts, FALSE, TRUE) : FALSE;
359 _metadata_update_value(i, valid ? datetime : NODATA_STRING, self);
360}
361
362static gint _lib_metadata_sort_order(gconstpointer a, gconstpointer b)
363{
366 return ma->order - mb->order;
367}
368
369static gint _lib_metadata_sort_index(gconstpointer a, gconstpointer b)
370{
373 return ma->index - mb->index;
374}
375
376static void _metadata_get_flags(const dt_image_t *const img, char *const text, char *const tooltip, const size_t tooltip_size)
377{
378#define EMPTY_FIELD '.'
379#define FALSE_FIELD '.'
380#define TRUE_FIELD '!'
381
382#define FLAG_NB 15
383
384 char *flags_tooltip = NULL;
385 char *flag_descriptions[] = { N_("unused"),
386 N_("unused/deprecated"),
387 N_("ldr"),
388 N_("raw"),
389 N_("hdr"),
390 N_("marked for deletion"),
391 N_("auto-applying presets applied"),
392 N_("legacy flag. set for all new images"),
393 N_("local copy"),
394 N_("has .txt"),
395 N_("has .wav"),
396 N_("monochrome")
397 };
398 char *tooltip_parts[FLAG_NB] = { 0 };
399 int next_tooltip_part = 0;
400
401 gchar *value = g_strnfill(FLAG_NB, EMPTY_FIELD);
402
403 int stars = img->flags & 0x7;
404 char *star_string = NULL;
405 if(stars == 6)
406 {
407 value[0] = 'x';
408 tooltip_parts[next_tooltip_part++] = _("image rejected");
409 }
410 else
411 {
412 value[0] = '0' + stars;
413 tooltip_parts[next_tooltip_part++] = star_string = g_strdup_printf(ngettext("image has %d star", "image has %d stars", stars), stars);
414 }
415
416
417 if(img->flags & 8)
418 {
419 value[1] = TRUE_FIELD;
420 tooltip_parts[next_tooltip_part++] = _(flag_descriptions[0]);
421 }
422 else
423 value[1] = FALSE_FIELD;
424
426 {
427 value[2] = TRUE_FIELD;
428 tooltip_parts[next_tooltip_part++] = _(flag_descriptions[1]);
429 }
430 else
431 value[2] = FALSE_FIELD;
432
433 if(dt_image_is_ldr(img))
434 {
435 value[3] = 'l';
436 tooltip_parts[next_tooltip_part++] = _(flag_descriptions[2]);
437 }
438
439 if(dt_image_is_raw(img))
440 {
441 value[4] = 'r';
442 tooltip_parts[next_tooltip_part++] = _(flag_descriptions[3]);
443 }
444
445 if(dt_image_is_hdr(img))
446 {
447 value[5] = 'h';
448 tooltip_parts[next_tooltip_part++] = _(flag_descriptions[4]);
449 }
450
451 if(img->flags & DT_IMAGE_REMOVE)
452 {
453 value[6] = 'd';
454 tooltip_parts[next_tooltip_part++] = _(flag_descriptions[5]);
455 }
456
458 {
459 value[7] = 'a';
460 tooltip_parts[next_tooltip_part++] = _(flag_descriptions[6]);
461 }
462
464 {
465 value[8] = 'p';
466 tooltip_parts[next_tooltip_part++] = _(flag_descriptions[7]);
467 }
468
469 if(img->flags & DT_IMAGE_LOCAL_COPY)
470 {
471 value[9] = 'c';
472 tooltip_parts[next_tooltip_part++] = _(flag_descriptions[8]);
473 }
474
475 if(img->flags & DT_IMAGE_HAS_TXT)
476 {
477 value[10] = 't';
478 tooltip_parts[next_tooltip_part++] = _(flag_descriptions[9]);
479 }
480
481 if(img->flags & DT_IMAGE_HAS_WAV)
482 {
483 value[11] = 'w';
484 tooltip_parts[next_tooltip_part++] = _(flag_descriptions[10]);
485 }
486
488 {
489 value[12] = 'm';
490 tooltip_parts[next_tooltip_part++] = _(flag_descriptions[11]);
491 }
492
493 const int loader = (unsigned int)img->loader < LOADER_COUNT ? img->loader : 0;
494 value[13] = loaders_info[loader].flag;
495 char *loader_tooltip = g_strdup_printf(_("loader: %s"), _(loaders_info[loader].tooltip));
496 tooltip_parts[next_tooltip_part++] = loader_tooltip;
497
498 value[14] = '\0';
499
500 flags_tooltip = g_strjoinv("\n", tooltip_parts);
501 dt_free(loader_tooltip);
502
503 (void)g_strlcpy(text, value, FLAG_NB);
504 (void)g_strlcpy(tooltip, flags_tooltip, tooltip_size);
505
506 dt_free(value);
507 dt_free(star_string);
508 dt_free(flags_tooltip);
509
510#undef EMPTY_FIELD
511#undef FALSE_FIELD
512#undef TRUE_FIELD
513#undef FLAG_NB
514}
515
516static gboolean _metadata_view_get_thumb_info(const int32_t imgid, dt_image_t *info)
517{
518 if(imgid <= 0 || IS_NULL_PTR(info) || IS_NULL_PTR(dt_gui_get_global()) || IS_NULL_PTR(dt_gui_get_ui())) return FALSE;
519
520 if(dt_thumbtable_get_thumbnail_info(dt_gui_get_ui()->thumbtable_lighttable, imgid, info))
521 return TRUE;
522 if(dt_thumbtable_get_thumbnail_info(dt_gui_get_ui()->thumbtable_filmstrip, imgid, info))
523 return TRUE;
524
525 return FALSE;
526}
527
528static void _concatenate_multiple_images(gboolean skip[md_size])
529{
531
532 // Which fields do the selected images disagree on? The repository reports one count per
533 // dt_image_field_t, in that enumeration's order, which is this panel's own row order up to
534 // md_tag_names -- checked here rather than assumed.
535 int *counts = dt_image_repository_count_distinct_fields(imgids);
536 if(counts)
537 {
538 for(int32_t md = 0; md < md_tag_names && md < DT_IMAGE_FIELD_COUNT; md++)
539 skip[md] = (counts[md] > 1);
540 dt_free(counts);
541 }
542
543 // Tags and categories are their own two rows: shared only when every image carries them.
544 gboolean same_tags = TRUE;
545 gboolean same_categories = TRUE;
546 dt_tag_repository_get_agreement(imgids, &same_tags, &same_categories);
547
548 skip[md_tag_names] = !same_tags;
549 skip[md_categories] = !same_categories;
550
551 g_list_free(imgids);
552}
553
554
555/* update all values to reflect mouse over image id or no data at all */
557{
559
560 int32_t count = 0;
561 gboolean skip[md_size] = {FALSE};
562
563 int32_t img_id = dt_control_get_mouse_over_id();
564 if(img_id > -1)
565 {
566 count = 1;
567 }
569 {
572 }
573 else if(dt_act_on_get_first_image() > -1)
574 {
575 count = 1;
576 img_id = dt_act_on_get_first_image();
577 }
578
579 if(count > 1) _concatenate_multiple_images(skip);
580
581 dt_image_t tinfo = {0};
582 const gboolean have_thumb_info = _metadata_view_get_thumb_info(img_id, &tinfo);
583 if(!have_thumb_info) return;
584
585 if(img_id == d->imgid) return; // nothing to update, spare the SQL queries
586 d->imgid = img_id;
587
588 if(tinfo.film_id == -1)
589 {
590 goto fill_minuses;
591 }
592
593 // Update the metadata values
594 for(int32_t md = 0; md < md_size; md++)
595 {
596 if(skip[md] == TRUE)
597 {
598 if(md == md_internal_flags)
599 _metadata_update_tooltip(md, NULL, self);
600
601 if(md == md_internal_filmroll)
602 _metadata_update_tooltip(md, NULL, self);
603
604 _metadata_update_value(md, _("<various values>"), self);
605 _metadata_update_markup(md, "<span style=\"italic\">%s</span>", self);
606 continue;
607 }
608
609 char text[DT_PATH_MAX] = {0};
610
611 switch(md)
612 {
614 {
615 char tooltip_filmroll[300] = {0};
616 g_strlcpy(text, tinfo.filmroll, sizeof(text));
617 snprintf(tooltip_filmroll, sizeof(tooltip_filmroll), _("double-click to jump to film roll\n%s"), text);
618 _metadata_update_tooltip(md_internal_filmroll, tooltip_filmroll, self);
620 }
621 break;
622
624 (void)g_snprintf(text, sizeof(text), "%d", tinfo.film_id);
626 break;
627
629 (void)g_snprintf(text, sizeof(text), "%d", tinfo.id);
631 break;
632
634 (void)g_snprintf(text, sizeof(text), "%d", tinfo.group_id);
636 break;
637
640 break;
641
643 (void)g_snprintf(text, sizeof(text), "%d", tinfo.version);
645 break;
646
648 {
649 g_strlcpy(text, tinfo.fullpath, sizeof(text));
651 }
652 break;
653
655 (void)g_strlcpy(text, (tinfo.flags & DT_IMAGE_LOCAL_COPY) ? _("yes") : _("no"),
656 sizeof(text));
658 break;
659
662 break;
663
666 break;
667
670 break;
671
674 break;
675
677 {
678 char tooltip_flags[300] = {0};
679 dt_image_t tmp = {0};
680 tmp.flags = tinfo.flags;
681 tmp.loader = tinfo.loader;
682 _metadata_get_flags(&tmp, text, tooltip_flags, sizeof(tooltip_flags));
683 _metadata_update_tooltip(md_internal_flags, tooltip_flags, self);
685 }
686 break;
687
688 case md_exif_model:
690 break;
691
692 case md_exif_maker:
694 break;
695
696 case md_exif_lens:
698 break;
699
700 case md_exif_aperture:
701 (void)g_snprintf(text, sizeof(text), "f/%.1f", (double)tinfo.exif_aperture);
703 break;
704
705 case md_exif_exposure:
706 {
707 gchar *const str = dt_util_format_exposure(tinfo.exif_exposure);
709 dt_free(str);
710 }
711 break;
712
714 {
715 g_strlcpy(text, NODATA_STRING, sizeof(text));
716 if(!(isnan(tinfo.exif_exposure_bias)))
717 {
718 (void)g_snprintf(text, sizeof(text), _("%+.2f EV"), tinfo.exif_exposure_bias);
719 }
721 break;
722 }
723
725 (void)g_snprintf(text, sizeof(text), "%.0f mm", (double)tinfo.exif_focal_length);
727 break;
728
730 {
731 (void)g_strlcpy(text, NODATA_STRING, sizeof(text));
732 if(!(isnan(tinfo.exif_focus_distance) || (fpclassify(tinfo.exif_focus_distance) == FP_ZERO) ))
733 {
734 (void)g_snprintf(text, sizeof(text), _("%.2f m"), tinfo.exif_focus_distance);
735 }
737 break;
738 }
739
740 case md_exif_iso:
741 (void)g_snprintf(text, sizeof(text), "%.0f", (double)tinfo.exif_iso);
743 break;
744
745 case md_exif_datetime:
746 {
747 char datetime[200];
748 const gboolean milliseconds = dt_conf_get_bool("lighttable/ui/milliseconds");
749 const gboolean valid = dt_datetime_gtimespan_to_local(datetime, sizeof(datetime),
750 tinfo.exif_datetime_taken, milliseconds, FALSE);
751 _metadata_update_value(md_exif_datetime, valid ? datetime : NODATA_STRING, self);
752 }
753 break;
754
755 case md_exif_width:
756 if(tinfo.p_width && (tinfo.p_width != tinfo.width))
757 {
758 (void)g_snprintf(text, sizeof(text), "%d (%d)", tinfo.p_width, tinfo.width);
760 }
761 else
762 {
763 (void)g_snprintf(text, sizeof(text), "%d", tinfo.width);
765 }
766 break;
767
768 case md_exif_height:
769 if(tinfo.p_height && (tinfo.p_height != tinfo.height))
770 {
771 (void)g_snprintf(text, sizeof(text), "%d (%d)", tinfo.p_height, tinfo.height);
773 }
774 else
775 {
776 (void)g_snprintf(text, sizeof(text), "%d", tinfo.height);
778 }
779 break;
780
781// case md_xmp_metadata: //managed below the switch()
782// break;
783
785 if(isnan(tinfo.geoloc.latitude))
786 {
788 }
789 else
790 {
791 if(dt_conf_get_bool("plugins/lighttable/metadata_view/pretty_location"))
792 {
793 gchar *latitude = dt_util_latitude_str((float)tinfo.geoloc.latitude);
795 dt_free(latitude);
796 }
797 else
798 {
799 const gchar NS = tinfo.geoloc.latitude < 0 ? 'S' : 'N';
800 (void)g_snprintf(text, sizeof(text), "%c %09.6f", NS, fabs(tinfo.geoloc.latitude));
802 }
803 }
804 break;
805
807 if(isnan(tinfo.geoloc.longitude))
808 {
810 }
811 else
812 {
813 if(dt_conf_get_bool("plugins/lighttable/metadata_view/pretty_location"))
814 {
815 gchar *longitude = dt_util_longitude_str((float)tinfo.geoloc.longitude);
817 dt_free(longitude);
818 }
819 else
820 {
821 const gchar EW = tinfo.geoloc.longitude < 0 ? 'W' : 'E';
822 (void)g_snprintf(text, sizeof(text), "%c %010.6f", EW, fabs(tinfo.geoloc.longitude));
824 }
825 }
826 break;
827
829 if(isnan(tinfo.geoloc.elevation))
830 {
832 }
833 else
834 {
835 if(dt_conf_get_bool("plugins/lighttable/metadata_view/pretty_location"))
836 {
837 gchar *elevation = dt_util_elevation_str((float)tinfo.geoloc.elevation);
839 dt_free(elevation);
840 }
841 else
842 {
843 (void)g_snprintf(text, sizeof(text), "%.2f %s", tinfo.geoloc.elevation, _("m"));
845 }
846 }
847 break;
848
849 case md_tag_names:
850 case md_categories:
851 {
852 GList *tags = NULL;
853 char *tagstring = NULL;
854 char *categoriesstring = NULL;
855 if(dt_tag_get_attached(img_id, &tags, TRUE))
856 {
857 uint64_t length = 0u;
858 for(GList *taglist = tags; taglist; taglist = g_list_next(taglist))
859 {
860 const char *tagname = ((dt_tag_t *)taglist->data)->leave;
861 if(!(((dt_tag_t *)taglist->data)->flags & DT_TF_CATEGORY))
862 {
863 // tags - just keywords
864 length = length + strlen(tagname) + 2u;
865 if(length < 45u)
866 tagstring = dt_util_dstrcat(tagstring, "%s, ", tagname);
867 else
868 {
869 tagstring = dt_util_dstrcat(tagstring, "\n%s, ", tagname);
870 length = strlen(tagname) + 2u;
871 }
872 }
873 else
874 {
875 // categories - needs parent category to make sense
876 char *category = g_strdup(((dt_tag_t *)taglist->data)->tag);
877 char *catend = g_strrstr(category, "|");
878 if(catend)
879 {
880 catend[0] = '\0';
881 char *catstart = g_strrstr(category, "|");
882 catstart = catstart ? catstart + 1 : category;
883 categoriesstring = dt_util_dstrcat(categoriesstring, categoriesstring ? "\n%s: %s " : "%s: %s ",
884 catstart, ((dt_tag_t *)taglist->data)->leave);
885 }
886 else
887 categoriesstring = dt_util_dstrcat(categoriesstring, categoriesstring ? "\n%s" : "%s",
888 ((dt_tag_t *)taglist->data)->leave);
889 dt_free(category);
890 }
891 }
892 if(tagstring) tagstring[strlen(tagstring)-2] = '\0';
893 }
894
895 if (md == md_tag_names)
896 _metadata_update_value(md_tag_names, tagstring ? tagstring : NODATA_STRING, self);
897 else
898 _metadata_update_value(md_categories, categoriesstring ? categoriesstring : NODATA_STRING, self);
899
900 dt_free(tagstring);
901 dt_free(categoriesstring);
902 dt_tag_free_result(&tags);
903 }
904 break;
905
906 default:
907 break;
908 }
909
910 //cases not handled by switch
912 {
913 g_strlcpy(text, NODATA_STRING, sizeof(text));
914
915 const uint32_t keyid = dt_metadata_get_keyid_by_display_order((uint32_t)(md - md_xmp_metadata));
916 const gboolean hidden = dt_metadata_get_type(keyid) == DT_METADATA_TYPE_INTERNAL;
917 if(! hidden)
918 {
919 const gchar *const key = dt_metadata_get_key(keyid);
920 GList *res = dt_metadata_get(img_id, key, NULL);
921 if(res)
922 {
923 g_strlcpy(text, (char *)res->data, sizeof(text));
924 _filter_non_printable(text, sizeof(text));
925 g_list_free_full(res, dt_free_gpointer);
926 res = NULL;
927 }
928 _metadata_update_value(md, text, self);
929 }
930 }
931 }
932
933 return;
934
935/* reset */
936fill_minuses:
937 for(int k = 0; k < md_size; k++) _metadata_update_value(k, NODATA_STRING, self);
938}
939
940static void _jump_to()
941{
942 int32_t imgid = dt_control_get_mouse_over_id();
944 if(imgid != UNKNOWN_IMAGE)
945 {
946 char path[512];
947 const dt_image_t *img = dt_image_cache_get(imgid, 'r');
948 dt_image_film_roll_directory(img, path, sizeof(path));
950 char collect[1024];
951 snprintf(collect, sizeof(collect), "1:0:0:%s$", path);
953 }
954}
955
956static gboolean _filmroll_clicked(GtkWidget *widget, GdkEventButton *event, gpointer null)
957{
958 if(event->type != GDK_2BUTTON_PRESS) return FALSE;
959 _jump_to();
960 return TRUE;
961}
962
963/* callback for the mouse over image change signal */
964static void _mouse_over_image_callback(gpointer instance, gpointer user_data)
965{
966 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
967 //if(dt_lib_gui_get_expanded(self))
969}
970
972{
974 char *pref = NULL;
975
976 d->metadata = g_list_sort(d->metadata, _lib_metadata_sort_order);
977 for(GList *meta = d->metadata; meta; meta= g_list_next(meta))
978 {
980 if(_is_metadata_ui(m->index))
981 pref = dt_util_dstrcat(pref, "%s%s,", m->visible ? "" : "|", m->name);
982 }
983 if(pref)
984 {
985 pref[strlen(pref) - 1] = '\0';
986 }
987 return pref;
988}
989
991{
993 d->metadata = g_list_sort(d->metadata, _lib_metadata_sort_order);
994
995 int j = 0;
996 // initialize the grid with metadata queue content
997 for(GList *meta = d->metadata; meta; meta = g_list_next(meta))
998 {
1000 m->order = j;
1001 GtkWidget *w_name = gtk_grid_get_child_at(GTK_GRID(d->grid), 0, j);
1002 gtk_label_set_text(GTK_LABEL(w_name), _(m->name));
1003 gtk_widget_set_tooltip_text(w_name, _(m->name));
1004 GtkWidget *w_value = gtk_grid_get_child_at(GTK_GRID(d->grid), 1, j);
1005 gtk_label_set_text(GTK_LABEL(w_value), m->value);
1006 const char *tooltip = m->tooltip ? m->tooltip : m->value;
1007 gtk_widget_set_tooltip_text(w_value, tooltip);
1008
1009 const int i = m->index;
1010 gtk_label_set_ellipsize(GTK_LABEL(w_value),
1012 ? PANGO_ELLIPSIZE_END : PANGO_ELLIPSIZE_MIDDLE);
1013 if(i == md_internal_filmroll)
1014 {
1015 // film roll jump to:
1016 if(d->filmroll_event && GTK_IS_WIDGET(d->filmroll_event))
1017 g_signal_handlers_disconnect_by_func(d->filmroll_event, G_CALLBACK(_filmroll_clicked), NULL);
1018 g_signal_connect(G_OBJECT(w_value), "button-press-event", G_CALLBACK(_filmroll_clicked), NULL);
1019 d->filmroll_event = G_OBJECT(w_value);
1020 }
1021
1022 gtk_widget_set_visible(w_name, m->visible);
1023 gtk_widget_set_visible(w_value, m->visible);
1024 j++;
1025 }
1026}
1027
1029{
1031
1032 int j = 0;
1033 // initialize the grid with metadata queue content
1034 for(GList *meta = d->metadata; meta; meta = g_list_next(meta))
1035 {
1037 GtkWidget *w_name = gtk_label_new(_(m->name));
1038 gtk_widget_set_halign(w_name, GTK_ALIGN_START);
1039 gtk_label_set_xalign(GTK_LABEL(w_name), 0.0f);
1040 gtk_label_set_ellipsize(GTK_LABEL(w_name), PANGO_ELLIPSIZE_END);
1041 gtk_widget_set_tooltip_text(w_name, _(m->name));
1042
1043 GtkWidget *w_value= gtk_label_new(m->value);
1044 gtk_widget_set_name(w_value, "brightbg");
1045 gtk_label_set_selectable(GTK_LABEL(w_value), TRUE);
1046 gtk_widget_set_halign(w_value, GTK_ALIGN_FILL);
1047 gtk_label_set_xalign(GTK_LABEL(w_value), 0.0f);
1048
1049 gtk_grid_attach(GTK_GRID(d->grid), w_name, 0, j, 1, 1);
1050 gtk_grid_attach(GTK_GRID(d->grid), w_value, 1, j, 1, 1);
1051 j++;
1052 }
1053}
1054
1055static void _apply_preferences(const char *prefs_list, dt_lib_module_t *self)
1056{
1057 if(!prefs_list || !prefs_list[0]) return;
1059
1060 GList *prefs = dt_util_str_to_glist(",", prefs_list);
1061 int k = 0;
1062 for(GList *pref = prefs; pref; pref = g_list_next(pref))
1063 {
1064 const char *name = (char *)pref->data;
1065 gboolean visible = TRUE;
1066 if(name)
1067 {
1068 if(name[0] == '|')
1069 {
1070 name++;
1071 visible = FALSE;
1072 }
1073 for(GList *meta = d->metadata; meta; meta= g_list_next(meta))
1074 {
1076 if(name && !g_strcmp0(name, m->name))
1077 {
1078 m->order = k;
1079 m->visible = visible;
1080 break;
1081 }
1082 }
1083 }
1084 else continue;
1085 k++;
1086 }
1087 g_list_free_full(prefs, dt_free_gpointer);
1088 prefs = NULL;
1089
1091}
1092
1094{
1095 char *pref = _get_current_configuration(self);
1096 dt_conf_set_string("plugins/lighttable/metadata_view/visible", pref);
1097 dt_free(pref);
1098}
1099
1100static void _select_toggled_callback(GtkCellRendererToggle *cell_renderer, gchar *path_str, gpointer user_data)
1101{
1102 GtkListStore *store = (GtkListStore *)user_data;
1103 GtkTreeIter iter;
1104 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1105 gboolean selected;
1106
1107 gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path);
1108 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, DT_METADATA_PREF_COL_VISIBLE, &selected, -1);
1109 gtk_list_store_set(store, &iter, DT_METADATA_PREF_COL_VISIBLE, !selected, -1);
1110
1111 gtk_tree_path_free(path);
1112}
1113
1114static void _drag_data_inserted(GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter, gpointer user_data)
1115{
1116 _dndactive = TRUE;
1117}
1118
1119void _menuitem_preferences(GtkMenuItem *menuitem, dt_lib_module_t *self)
1120{
1122
1124 GtkWidget *dialog = gtk_dialog_new_with_buttons(_("metadata settings"), GTK_WINDOW(win),
1125 GTK_DIALOG_DESTROY_WITH_PARENT, _("default"), GTK_RESPONSE_YES,
1126 _("cancel"), GTK_RESPONSE_NONE, _("save"), GTK_RESPONSE_ACCEPT, NULL);
1127 g_signal_connect(dialog, "key-press-event", G_CALLBACK(dt_handle_dialog_enter), NULL);
1128 GtkWidget *area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1129
1130 GtkWidget *w = gtk_scrolled_window_new(NULL, NULL);
1131 gtk_widget_set_size_request(w, -1, DT_PIXEL_APPLY_DPI(600));
1132 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1133 gtk_scrolled_window_set_overlay_scrolling(GTK_SCROLLED_WINDOW(w), FALSE);
1134 dt_gui_add_class(w, "dt_recessed_scroll");
1135 gtk_box_pack_start(GTK_BOX(area), w, TRUE, TRUE, 0);
1136
1137 GtkListStore *store = gtk_list_store_new(DT_METADATA_PREF_NUM_COLS,
1138 G_TYPE_INT, G_TYPE_STRING, G_TYPE_BOOLEAN);
1139 GtkTreeModel *model = GTK_TREE_MODEL(store);
1140
1141 GtkTreeIter iter;
1142 d->metadata = g_list_sort(d->metadata, _lib_metadata_sort_order);
1143 for(GList *meta = d->metadata; meta; meta = g_list_next(meta))
1144 {
1146 if(!_is_metadata_ui(m->index))
1147 continue;
1148 gtk_list_store_append(store, &iter);
1149 gtk_list_store_set(store, &iter,
1153 -1);
1154 }
1155
1156 GtkWidget *view = gtk_tree_view_new_with_model(model);
1157 g_object_unref(model);
1158 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
1159 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(_("metadata"), renderer,
1160 "text", DT_METADATA_PREF_COL_NAME_L, NULL);
1161 gtk_tree_view_column_set_expand(column, TRUE);
1162 gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
1163 GtkWidget *header = gtk_tree_view_column_get_button(column);
1164 gtk_widget_set_tooltip_text(header,
1165 _("drag and drop one row at a time until you get the desired order"
1166 "\nuntick to hide metadata which are not of interest for you"
1167 "\nif different settings are needed, use presets"));
1168 renderer = gtk_cell_renderer_toggle_new();
1169 g_signal_connect(renderer, "toggled", G_CALLBACK(_select_toggled_callback), store);
1170 column = gtk_tree_view_column_new_with_attributes(_("visible"), renderer,
1171 "active", DT_METADATA_PREF_COL_VISIBLE, NULL);
1172 gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
1173
1174 // drag & drop
1175 gtk_tree_view_set_reorderable(GTK_TREE_VIEW(view), TRUE);
1176 g_signal_connect(G_OBJECT(model), "row-inserted", G_CALLBACK(_drag_data_inserted), NULL);
1177
1178 gtk_container_add(GTK_CONTAINER(w), view);
1179
1180#ifdef GDK_WINDOWING_QUARTZ
1182#endif
1183 gtk_widget_show_all(dialog);
1184
1185 int res = gtk_dialog_run(GTK_DIALOG(dialog));
1186 while(res == GTK_RESPONSE_YES)
1187 {
1188 gtk_tree_model_get_iter_first(model, &iter);
1189 d->metadata = g_list_sort(d->metadata, _lib_metadata_sort_index);
1190 for(GList *meta = d->metadata; meta; meta= g_list_next(meta))
1191 {
1193 if(!_is_metadata_ui(m->index))
1194 continue;
1195 gtk_list_store_set(store, &iter,
1199 -1);
1200 gtk_tree_model_iter_next(model, &iter);
1201 }
1202 res = gtk_dialog_run(GTK_DIALOG(dialog));
1203 }
1204
1205 int i = 0;
1206 if(res == GTK_RESPONSE_ACCEPT)
1207 {
1208 gboolean valid = gtk_tree_model_get_iter_first(model, &iter);
1209 while(valid)
1210 {
1211 gboolean visible;
1212 uint32_t index;
1213 gtk_tree_model_get(model, &iter,
1216 -1);
1217 for(GList *meta = d->metadata; meta; meta= g_list_next(meta))
1218 {
1220 if(m->index == index)
1221 {
1222 m->order = i;
1223 m->visible = visible;
1224 break;
1225 }
1226 }
1227 i++;
1228 valid = gtk_tree_model_iter_next(model, &iter);
1229 }
1230
1232 _save_preferences(self);
1233 }
1234 gtk_widget_destroy(dialog);
1235}
1236
1237void set_preferences(void *menu, dt_lib_module_t *self)
1238{
1239 GtkWidget *mi = gtk_menu_item_new_with_label(_("preferences..."));
1240 g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(_menuitem_preferences), self);
1241 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
1242}
1243
1245{
1246 *size = 0;
1247 char *params = _get_current_configuration(self);
1248 if(params)
1249 *size =strlen(params) + 1;
1250 return params;
1251}
1252
1253int set_params(dt_lib_module_t *self, const void *params, int size)
1254{
1255 if(IS_NULL_PTR(params)) return 1;
1256
1257 _apply_preferences(params, self);
1258 _save_preferences(self);
1259 return 0;
1260}
1261
1263{
1265
1266 for(GList *meta = d->metadata; meta; meta= g_list_next(meta))
1267 {
1269 m->order = m->index;
1270 m->visible = _is_metadata_ui(m->index);
1271 }
1273}
1274
1276{
1277 /* initialize ui */
1279 self->data = (void *)d;
1280 d->imgid = -1;
1281
1283
1284 GtkWidget *child_grid_window = gtk_grid_new();
1285 d->grid = child_grid_window;
1286 gtk_grid_set_column_spacing(GTK_GRID(child_grid_window), DT_GUI_BOX_SPACING);
1287
1288 // Static: the metadata grid refreshes for the hovered/selected image, so keep a fixed height.
1289 self->widget = dt_ui_scroll_wrap(child_grid_window, 200, "plugins/lighttable/metadata_view/windowheight",
1291
1292 gtk_widget_show_all(d->grid);
1293 gtk_widget_set_no_show_all(d->grid, TRUE);
1295 const char *pref = dt_conf_get_string_const("plugins/lighttable/metadata_view/visible");
1296 if(!strlen(pref))
1297 _display_default(self);
1298 _apply_preferences(pref, self);
1299
1300 /* lets signup for mouse over image change signals */
1302 G_CALLBACK(_mouse_over_image_callback), self);
1303
1305 G_CALLBACK(_mouse_over_image_callback), self);
1306}
1307
1309{
1310 if(m->value)
1311 {
1312 dt_free(m->value);
1313 }
1314 if(m->tooltip)
1315 {
1316 dt_free(m->tooltip);
1317 }
1318 dt_free(m);
1319}
1320
1322{
1324 if(IS_NULL_PTR(self->data)) return;
1326 g_list_free_full(d->metadata, (GDestroyNotify)_free_metadata_queue);
1327 d->metadata = NULL;
1328 dt_free(self->data);
1329}
1330
1332{
1334
1335 for(GList *meta = d->metadata; meta; meta= g_list_next(meta))
1336 {
1338 m->order = m->index;
1339 m->visible = _is_metadata_ui(m->index);
1340 }
1342 _save_preferences(self);
1343}
1344
1345// clang-format off
1346// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1347// vim: shiftwidth=2 expandtab tabstop=2 cindent
1348// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1349// 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:986
struct dt_gui_gtk_t * dt_gui_get_global(void)
Definition darktable.c:523
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:310
@ DT_IMAGE_NO_LEGACY_PRESETS
Definition image.h:132
@ DT_IMAGE_HAS_WAV
Definition image.h:138
@ DT_IMAGE_AUTO_PRESETS_APPLIED
Definition image.h:130
@ DT_IMAGE_REMOVE
Definition image.h:128
@ DT_IMAGE_LOCAL_COPY
Definition image.h:134
@ DT_IMAGE_THUMBNAIL_DEPRECATED
Definition image.h:119
@ DT_IMAGE_HAS_TXT
Definition image.h:136
@ LOADER_COUNT
Definition image.h:297
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
gboolean dt_handle_dialog_enter(GtkWidget *widget, GdkEventKey *event, gpointer data)
Definition lib.c:1324
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)
#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)
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
@ 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
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_STATIC
Definition scroll_wrap.h:46
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:407
struct dt_control_signal_t * dt_control_signal_get_global(void)
Definition darktable.c:616
@ 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:62
@ DT_SIGNAL_SELECTION_CHANGED
This signal is raised when the selection is changed no param, no returned value.
Definition signal.h:130
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
Definition signal.h:396
static const dt_aligned_pixel_simd_t value
Definition simd.h:144
unsigned __int64 uint64_t
Definition strptime.c:75
double latitude
Definition image.h:332
double elevation
Definition image.h:332
double longitude
Definition image.h:332
float exif_exposure
Definition image.h:367
int32_t height
Definition image.h:397
GTimeSpan export_timestamp
Definition image.h:415
float exif_focus_distance
Definition image.h:372
GTimeSpan import_timestamp
Definition image.h:415
int32_t group_id
Definition image.h:401
float exif_exposure_bias
Definition image.h:368
float exif_iso
Definition image.h:370
dt_image_loader_t loader
Definition image.h:417
float exif_aperture
Definition image.h:369
int32_t version
Definition image.h:401
dt_image_geoloc_t geoloc
Definition image.h:434
int32_t flags
Definition image.h:401
int32_t width
Definition image.h:397
float exif_focal_length
Definition image.h:371
char exif_maker[64]
Definition image.h:374
GTimeSpan change_timestamp
Definition image.h:415
char exif_lens[128]
Definition image.h:376
GTimeSpan print_timestamp
Definition image.h:415
int32_t film_id
Definition image.h:401
GTimeSpan exif_datetime_taken
Definition image.h:377
int32_t p_height
Definition image.h:397
int32_t p_width
Definition image.h:397
char filmroll[DT_PATH_MAX]
Definition image.h:391
char exif_model[64]
Definition image.h:375
char filename[DT_MAX_FILENAME_LEN]
Definition image.h:386
int32_t id
Definition image.h:401
char fullpath[DT_PATH_MAX]
Definition image.h:387
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:606
gchar * dt_util_longitude_str(float longitude)
Definition utility.c:572
gchar * dt_util_elevation_str(float elevation)
Definition utility.c:590
GList * dt_util_str_to_glist(const gchar *separator, const gchar *text)
Definition utility.c:833
char * dt_util_format_exposure(const float exposuretime)
Definition utility.c:868
gchar * dt_util_latitude_str(float latitude)
Definition utility.c:554
gchar * dt_util_dstrcat(gchar *str, const gchar *format,...)
Definition utility.c:99
#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