Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
export_metadata.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2019 luzpaz.
4 Copyright (C) 2019 parafin.
5 Copyright (C) 2019-2020 Pascal Obry.
6 Copyright (C) 2019-2022 Philippe Weyland.
7 Copyright (C) 2019 Sam Smith.
8 Copyright (C) 2020 Marco.
9 Copyright (C) 2021 Hubert Kowalski.
10 Copyright (C) 2021 Ralf Brown.
11 Copyright (C) 2022 Aldric Renaudin.
12 Copyright (C) 2022 Martin Bařinka.
13 Copyright (C) 2022 Victor Forsiuk.
14 Copyright (C) 2025 Aurélien PIERRE.
15
16 darktable is free software: you can redistribute it and/or modify
17 it under the terms of the GNU General Public License as published by
18 the Free Software Foundation, either version 3 of the License, or
19 (at your option) any later version.
20
21 darktable is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 GNU General Public License for more details.
25
26 You should have received a copy of the GNU General Public License
27 along with darktable. If not, see <http://www.gnu.org/licenses/>.
28*/
29#include "widgets/bauhaus.h"
30#include "system/macros.h"
31#include "system/mem_alloc.h"
32#include "common/utility.h"
33#include "widgets/gdkkeys.h"
35#include "widgets/button.h"
36
37#include "gui/application.h"
38#include "widgets/gtkentry.h"
39#include "libs/lib.h"
40#include "libs/lib_api.h"
41#ifdef GDK_WINDOWING_QUARTZ
42#include "osx/osx.h"
43#endif
44#include <gdk/gdkkeysyms.h>
45#include <gtk/gtk.h>
46#include <stdlib.h>
48
57
69
70const GList *dt_exif_get_exiv2_taglist();
71
72// find a string on the list
73static gboolean _find_metadata_iter_per_text(GtkTreeModel *model, GtkTreeIter *iter, gint col, const char *text)
74{
75 if(IS_NULL_PTR(text)) return FALSE;
76 GtkTreeIter it;
77 gboolean valid = gtk_tree_model_get_iter_first(model, &it);
78 char *name;
79 while (valid)
80 {
81 gtk_tree_model_get(model, &it, col, &name, -1);
82 const gboolean found = g_strcmp0(text, name) == 0;
84 if(found)
85 {
86 if (iter) *iter = it;
87 return TRUE;
88 }
89 valid = gtk_tree_model_iter_next(model, &it);
90 }
91 return FALSE;
92}
93
94// add selected metadata tag to formula list
96{
97 GtkTreeIter iter;
98 GtkTreeModel *model = gtk_tree_view_get_model(view);
99 GtkTreeSelection *selection = gtk_tree_view_get_selection(view);
100 if(gtk_tree_selection_get_selected(selection, &model, &iter))
101 {
102 char *tagname;
103 gtk_tree_model_get(model, &iter, DT_LIB_EXPORT_METADATA_COL_XMP, &tagname, -1);
104 if (!_find_metadata_iter_per_text(GTK_TREE_MODEL(d->liststore), NULL, DT_LIB_EXPORT_METADATA_COL_XMP, tagname))
105 {
106 gtk_list_store_append(d->liststore, &iter);
107 gtk_list_store_set(d->liststore, &iter, DT_LIB_EXPORT_METADATA_COL_XMP, tagname,
109 selection = gtk_tree_view_get_selection(d->view);
110 gtk_tree_selection_select_iter(selection, &iter);
111 }
112 dt_free(tagname);
113 }
114}
115
116// choice of a metadata tag
117static gboolean _click_on_metadata_list(GtkWidget *view, GdkEventButton *event, dt_lib_export_metadata_t *d)
118{
119 if(event->type == GDK_2BUTTON_PRESS && event->button == 1)
120 {
121
122 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
123 GtkTreePath *path = NULL;
124 // Get tree path for row that was clicked
125 if(gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(view), (gint)event->x, (gint)event->y, &path, NULL, NULL, NULL))
126 {
127 gtk_tree_selection_select_path(selection, path);
128 if(event->type == GDK_2BUTTON_PRESS && event->button == 1)
129 {
130 _add_selected_metadata(GTK_TREE_VIEW(view), d);
131 gtk_tree_path_free(path);
132 return TRUE;
133 }
134 }
135 gtk_tree_path_free(path);
136 }
137 return FALSE;
138}
139
140// routine to set individual visibility flag
141static gboolean _set_matching_tag_visibility(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, dt_lib_export_metadata_t *d)
142{
143 gboolean visible;
144 gchar *tagname = NULL;
145 gtk_tree_model_get(model, iter, DT_LIB_EXPORT_METADATA_COL_XMP, &tagname, -1);
146 if (!d->sel_entry_text[0])
147 visible = TRUE;
148 else
149 {
150 gchar *haystack = g_utf8_strdown(tagname, -1);
151 gchar *needle = g_utf8_strdown(d->sel_entry_text, -1);
152 visible = (g_strrstr(haystack, needle) != NULL);
153 dt_free(haystack);
154 dt_free(needle);
155 }
156 gtk_list_store_set(GTK_LIST_STORE(model), iter, DT_LIB_EXPORT_METADATA_COL_VISIBLE, visible, -1);
157 dt_free(tagname);
158 return FALSE;
159}
160
161// set the metadata tag visibility aligned with filter
162static void _tag_name_changed(GtkEntry *entry, dt_lib_export_metadata_t *d)
163{
164 d->sel_entry_text = gtk_entry_get_text(GTK_ENTRY(d->sel_entry));
165 GtkTreeModel *model = gtk_tree_view_get_model(d->sel_view);
166 GtkTreeModel *store = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(model));
167 gtk_tree_model_foreach(store, (GtkTreeModelForeachFunc)_set_matching_tag_visibility, d);
168}
169
170// dialog to add metadata tag into the formula list
171static void _add_tag_button_clicked(GtkButton *button, dt_lib_export_metadata_t *d)
172{
173 GtkWidget *dialog = gtk_dialog_new_with_buttons(_("select tag"), GTK_WINDOW(d->dialog), GTK_DIALOG_DESTROY_WITH_PARENT,
174 _("add"), GTK_RESPONSE_ACCEPT, _("done"), GTK_RESPONSE_NONE, NULL);
175 g_signal_connect(dialog, "key-press-event", G_CALLBACK(dt_handle_dialog_enter), NULL);
176 gtk_window_set_default_size(GTK_WINDOW(dialog), 300, -1);
177 GtkWidget *area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
178 GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
179 gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
180 gtk_container_add(GTK_CONTAINER(area), vbox);
181
182 GtkWidget *entry = gtk_entry_new();
184 d->sel_entry = entry;
185 gtk_entry_set_text(GTK_ENTRY(entry), "");
186 gtk_widget_set_tooltip_text(entry, _("list filter"));
187 gtk_box_pack_start(GTK_BOX(vbox), entry, TRUE, TRUE, 0);
188 g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(_tag_name_changed), d);
189
190 GtkWidget *w = gtk_scrolled_window_new(NULL, NULL);
191 gtk_widget_set_size_request(w, DT_PIXEL_APPLY_DPI(500), DT_PIXEL_APPLY_DPI(300));
192 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
193 dt_gui_add_class(w, "dt_recessed_scroll");
194 gtk_box_pack_start(GTK_BOX(vbox), w, TRUE, TRUE, 0);
195 GtkTreeView *view = GTK_TREE_VIEW(gtk_tree_view_new());
196 d->sel_view = view;
197 gtk_container_add(GTK_CONTAINER(w), GTK_WIDGET(view));
198 gtk_widget_set_tooltip_text(GTK_WIDGET(view), _("list of available tags. click 'add' button or double-click on tag to add the selected one"));
199 gtk_tree_selection_set_mode(gtk_tree_view_get_selection(view), GTK_SELECTION_SINGLE);
200 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
201 GtkTreeViewColumn *col = gtk_tree_view_column_new_with_attributes(_("tag"), renderer, "text", 0, NULL);
202 gtk_tree_view_append_column(view, col);
203 renderer = gtk_cell_renderer_text_new();
204 col = gtk_tree_view_column_new_with_attributes(_("type"), renderer, "text", 1, NULL);
205 gtk_tree_view_append_column(view, col);
206 GtkListStore *liststore = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN);
207 GtkTreeModel *model = gtk_tree_model_filter_new(GTK_TREE_MODEL(liststore), NULL);
208 gtk_tree_model_filter_set_visible_column(GTK_TREE_MODEL_FILTER(model), DT_LIB_EXPORT_METADATA_COL_VISIBLE);
209
210 // populate the metadata tag list with exiv2 information
211 for(GList *tag = d->taglist; tag; tag = g_list_next(tag))
212 {
213 GtkTreeIter iter;
214 gtk_list_store_append(liststore, &iter);
215 const char *tagname = tag->data;
216 char *type = g_strstr_len(tagname, -1, ",");
217 if(type)
218 {
219 type[0] = '\0';
220 type++;
221 }
222 gtk_list_store_set(liststore, &iter, DT_LIB_EXPORT_METADATA_COL_XMP, tagname, DT_LIB_EXPORT_METADATA_COL_TYPE, type,
224 if(type)
225 {
226 type--;
227 type[0] = ',';
228 }
229 }
230
231 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(liststore), DT_LIB_EXPORT_METADATA_COL_XMP, GTK_SORT_ASCENDING);
232 gtk_tree_view_set_model(view, model);
233 g_object_unref(model);
234 g_signal_connect(G_OBJECT(view), "button-press-event", G_CALLBACK(_click_on_metadata_list), (gpointer)d);
235
236 #ifdef GDK_WINDOWING_QUARTZ
238 #endif
239 gtk_widget_show_all(dialog);
240 while (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
241 {
243 }
244 gtk_widget_destroy(dialog);
245}
246
248{
249 GtkTreeIter iter;
250 GtkTreeModel *model = GTK_TREE_MODEL(d->liststore);
251 GtkTreeSelection *selection = gtk_tree_view_get_selection(d->view);
252 if(gtk_tree_selection_get_selected(selection, &model, &iter))
253 {
254 gtk_list_store_remove(d->liststore, &iter);
255 }
256}
257
259{
261}
262
263static gboolean _key_press_on_list(GtkWidget *widget, GdkEventKey *event, dt_lib_export_metadata_t *d)
264{
265 guint key = dt_keys_mainpad_alternatives(event->keyval);
266
267 if(event->type == GDK_KEY_PRESS && (key == GDK_KEY_Delete) && !event->state)
268 {
270 return TRUE;
271 }
272 return FALSE;
273}
274
275static void _tags_toggled(GtkToggleButton *dttag, dt_lib_export_metadata_t *d)
276{
277 const gboolean tags = gtk_toggle_button_get_active(dttag);
278 gtk_widget_set_sensitive(d->private, tags);
279 gtk_widget_set_sensitive(d->synonyms, tags);
280 gtk_widget_set_sensitive(d->omithierarchy, tags);
281}
282
283static void _formula_edited(GtkCellRenderer *renderer, gchar *path, gchar *new_text, dt_lib_export_metadata_t *d)
284{
285 GtkTreeIter iter;
286 if (gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(d->liststore), &iter, path))
287 gtk_list_store_set(d->liststore, &iter, DT_LIB_EXPORT_METADATA_COL_FORMULA, new_text, -1);
288}
289
290static void _formula_editing_started(GtkCellRenderer *renderer, GtkCellEditable *editable,
291 char *path, dt_lib_export_metadata_t *d)
292{
294}
295
296char *dt_lib_export_metadata_configuration_dialog(char *metadata_presets, const gboolean ondisk)
297{
299
301 GtkWidget *dialog = gtk_dialog_new_with_buttons(_("edit metadata exportation"), GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT,
302 _("cancel"), GTK_RESPONSE_NONE, _("save"), GTK_RESPONSE_ACCEPT, NULL);
303 d->dialog = dialog;
304 g_signal_connect(dialog, "key-press-event", G_CALLBACK(dt_handle_dialog_enter), NULL);
305
306 gtk_window_set_default_size(GTK_WINDOW(dialog), 300, -1);
307 GtkWidget *area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
308
309 GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
310 gtk_container_add(GTK_CONTAINER(area), hbox);
311
312 // general info
313 GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
314 gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
315 gtk_container_add(GTK_CONTAINER(hbox), vbox);
316 GtkWidget *label = gtk_label_new(_("general settings"));
317 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
318 GtkWidget *vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
319 gtk_box_pack_start(GTK_BOX(vbox), vbox2, FALSE, TRUE, 0);
320
321 GtkWidget *exiftag = gtk_check_button_new_with_label(_("EXIF data"));
322 gtk_widget_set_tooltip_text(exiftag, _("export EXIF metadata"));
323 gtk_box_pack_start(GTK_BOX(vbox2), exiftag, FALSE, TRUE, 0);
324 GtkWidget *dtmetadata = gtk_check_button_new_with_label(_("metadata"));
325 gtk_widget_set_tooltip_text(dtmetadata, _("export dt xmp metadata (from metadata editor module)"));
326 gtk_box_pack_start(GTK_BOX(vbox2), dtmetadata, FALSE, TRUE, 0);
327
328 GtkWidget *calculated;
329 if (!ondisk)
330 {
331 GtkWidget *box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
332 gtk_box_pack_start(GTK_BOX(vbox2), box, FALSE, TRUE, 0);
333 GtkWidget *vbox3 = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
334 gtk_box_pack_start(GTK_BOX(box), vbox3, FALSE, TRUE, 10);
335 calculated = gtk_check_button_new_with_label(_("only embedded"));
336 gtk_widget_set_tooltip_text(calculated, _("per default the interface sends some (limited) metadata beside the image to remote storage.\n"
337 "to avoid this and let only image embedded dt xmp metadata, check this flag.\n"
338 "if remote storage doesn't understand dt xmp metadata, you can use calculated metadata instead"));
339 gtk_box_pack_start(GTK_BOX(vbox3), calculated, FALSE, TRUE, 0);
340 }
341
342 GtkWidget *geotag = gtk_check_button_new_with_label(_("geo tags"));
343 gtk_widget_set_tooltip_text(geotag, _("export geo tags"));
344 gtk_box_pack_start(GTK_BOX(vbox2), geotag, FALSE, TRUE, 0);
345 GtkWidget *dttag = gtk_check_button_new_with_label(_("tags"));
346 gtk_widget_set_tooltip_text(dttag, _("export tags (to Xmp.dc.Subject)"));
347 gtk_box_pack_start(GTK_BOX(vbox2), dttag, FALSE, TRUE, 0);
348 g_signal_connect(G_OBJECT(dttag), "clicked", G_CALLBACK(_tags_toggled), (gpointer)d);
349
350 GtkWidget *box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
351 gtk_box_pack_start(GTK_BOX(vbox2), box, FALSE, TRUE, 0);
352 GtkWidget *vbox3 = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
353 gtk_box_pack_start(GTK_BOX(box), vbox3, FALSE, TRUE, 10);
354 d->private = gtk_check_button_new_with_label(_("private tags"));
355 gtk_widget_set_tooltip_text(d->private, _("export private tags"));
356 gtk_box_pack_start(GTK_BOX(vbox3), d->private, FALSE, TRUE, 0);
357 d->synonyms = gtk_check_button_new_with_label(_("synonyms"));
358 gtk_widget_set_tooltip_text(d->synonyms, _("export tags synonyms"));
359 gtk_box_pack_start(GTK_BOX(vbox3), d->synonyms, FALSE, TRUE, 0);
360 d->omithierarchy = gtk_check_button_new_with_label(_("omit hierarchy"));
361 gtk_widget_set_tooltip_text(d->omithierarchy, _("only the last part of the hierarchical tags is included. can be useful if categories are not used"));
362 gtk_box_pack_start(GTK_BOX(vbox3), d->omithierarchy, FALSE, TRUE, 0);
363
364 GtkWidget *hierarchical = gtk_check_button_new_with_label(_("hierarchical tags"));
365 gtk_widget_set_tooltip_text(hierarchical, _("export hierarchical tags (to Xmp.lr.Hierarchical Subject)"));
366 gtk_box_pack_start(GTK_BOX(vbox2), hierarchical, FALSE, TRUE, 0);
367 GtkWidget *dthistory = gtk_check_button_new_with_label(_("develop history"));
368 gtk_widget_set_tooltip_text(dthistory, _("export dt development data (recovery purpose in case of loss of database or xmp file)"));
369 gtk_box_pack_start(GTK_BOX(vbox2), dthistory, FALSE, TRUE, 0);
370
371 // specific rules
372 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
373 gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
374 gtk_container_add(GTK_CONTAINER(hbox), vbox);
375 label = gtk_label_new(_("per metadata settings"));
376 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
377
378 GtkWidget *w = gtk_scrolled_window_new(NULL, NULL);
379 gtk_widget_set_size_request(w, DT_PIXEL_APPLY_DPI(450), DT_PIXEL_APPLY_DPI(100));
380 gtk_widget_set_hexpand(w, TRUE);
381 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
382 dt_gui_add_class(w, "dt_recessed_scroll");
383 gtk_box_pack_start(GTK_BOX(vbox), w, TRUE, TRUE, 0);
384 GtkTreeView *view = GTK_TREE_VIEW(gtk_tree_view_new());
385 d->view = view;
386 gtk_container_add(GTK_CONTAINER(w), GTK_WIDGET(view));
387 gtk_tree_selection_set_mode(gtk_tree_view_get_selection(view), GTK_SELECTION_SINGLE);
388 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
389 GtkTreeViewColumn *col = gtk_tree_view_column_new_with_attributes(_("redefined tag"), renderer, "text", 0, NULL);
390 gtk_tree_view_append_column(view, col);
391 renderer = gtk_cell_renderer_text_new();
392 g_object_set(renderer, "editable", TRUE, NULL);
393 g_signal_connect(G_OBJECT(renderer), "edited", G_CALLBACK(_formula_edited), (gpointer)d);
394 g_signal_connect(renderer, "editing-started" , G_CALLBACK(_formula_editing_started), (gpointer)d);
395 col = gtk_tree_view_column_new_with_attributes(_("formula"), renderer, "text", 2, NULL);
396 gtk_tree_view_append_column(view, col);
397 gtk_widget_set_tooltip_text(GTK_WIDGET(view),
398 _("list of calculated metadata\n"
399 "click on '+' button to select and add new metadata\n"
400 "if formula is empty, the corresponding metadata is removed from exported file,\n"
401 "if formula is \'=\', the EXIF metadata is exported even if EXIF data are disabled\n"
402 "otherwise the corresponding metadata is calculated and added to exported file\n"
403 "click on formula cell to edit\n"
404 "type '$(' to activate the completion and see the list of variables"));
405 g_signal_connect(G_OBJECT(view), "key_press_event", G_CALLBACK(_key_press_on_list), (gpointer)d);
406
407 GtkListStore *liststore = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
408 d->liststore = liststore;
409 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(liststore), DT_LIB_EXPORT_METADATA_COL_XMP, GTK_SORT_ASCENDING);
410 gtk_tree_view_set_model(view, GTK_TREE_MODEL(liststore));
411 g_object_unref(liststore);
412 d->taglist = (GList *)dt_exif_get_exiv2_taglist();
413 GList *list = dt_util_str_to_glist("\1", metadata_presets);
414 int32_t flags = 0;
415 if (list)
416 {
417 char *flags_hexa = list->data;
418 flags = strtol(flags_hexa, NULL, 16);
419 list = g_list_remove(list, flags_hexa);
420 dt_free(flags_hexa);
421 if (list)
422 {
423 for (GList *tags = list; tags; tags = g_list_next(tags))
424 {
425 GtkTreeIter iter;
426 const char *tagname = (char *)tags->data;
427 tags = g_list_next(tags);
428 if (IS_NULL_PTR(tags)) break;
429 const char *formula = (char *)tags->data;
430 gtk_list_store_append(d->liststore, &iter);
431 gtk_list_store_set(d->liststore, &iter, DT_LIB_EXPORT_METADATA_COL_XMP, tagname,
433 }
434 }
435 }
436 g_list_free_full(list, dt_free_gpointer);
437 list = NULL;
438
439 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(exiftag), flags & DT_META_EXIF);
440 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dtmetadata), flags & DT_META_METADATA);
441 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(geotag), flags & DT_META_GEOTAG);
442 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dttag), flags & DT_META_TAG);
443 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->private), flags & DT_META_PRIVATE_TAG);
444 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->synonyms), flags & DT_META_SYNONYMS_TAG);
445 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->omithierarchy), flags & DT_META_OMIT_HIERARCHY);
446 _tags_toggled(GTK_TOGGLE_BUTTON(dttag), d);
447 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(hierarchical), flags & DT_META_HIERARCHICAL_TAG);
448 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dthistory), flags & DT_META_DT_HISTORY);
449 if (!ondisk)
450 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(calculated), flags & DT_META_CALCULATED);
451
452 box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
453 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, TRUE, 0);
454
456 gtk_widget_set_tooltip_text(button, _("add an output metadata tag"));
457 gtk_box_pack_end(GTK_BOX(box), button, FALSE, TRUE, 0);
458 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(_add_tag_button_clicked), (gpointer)d);
459
461 gtk_widget_set_tooltip_text(button, _("delete metadata tag"));
462 gtk_box_pack_end(GTK_BOX(box), button, FALSE, TRUE, 0);
463 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(_delete_tag_button_clicked), (gpointer)d);
464
465#ifdef GDK_WINDOWING_QUARTZ
467#endif
468 gtk_widget_show_all(dialog);
469
470 char *newlist = metadata_presets;
471 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
472 {
473 const gint newflags = (
474 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(exiftag)) ? DT_META_EXIF : 0) |
475 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dtmetadata)) ? DT_META_METADATA : 0) |
476 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(geotag)) ? DT_META_GEOTAG : 0) |
477 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dttag)) ? DT_META_TAG : 0) |
478 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(d->private)) ? DT_META_PRIVATE_TAG : 0) |
479 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(d->synonyms)) ? DT_META_SYNONYMS_TAG : 0) |
480 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(d->omithierarchy)) ? DT_META_OMIT_HIERARCHY : 0) |
481 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hierarchical)) ? DT_META_HIERARCHICAL_TAG : 0) |
482 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dthistory)) ? DT_META_DT_HISTORY : 0) |
483 (!ondisk ? (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(calculated)) ? DT_META_CALCULATED : 0) : 0)
484 );
485
486 newlist = g_strdup_printf("%x", newflags);
487 GtkTreeIter iter;
488 gboolean valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(d->liststore), &iter);
489 while(valid)
490 {
491 char *tagname, *formula;
492 gtk_tree_model_get(GTK_TREE_MODEL(d->liststore), &iter, DT_LIB_EXPORT_METADATA_COL_XMP, &tagname,
494 // metadata presets are stored into a single string with '\1' as a separator
495 newlist = dt_util_dstrcat(newlist,"\1%s\1%s", tagname, formula);
496 dt_free(tagname);
497 dt_free(formula);
498 valid = gtk_tree_model_iter_next (GTK_TREE_MODEL(d->liststore), &iter);
499 }
500 dt_free(metadata_presets);
502 }
503 gtk_widget_destroy(dialog);
504 dt_free(d);
505 return newlist;
506}
507
508// clang-format off
509// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
510// vim: shiftwidth=2 expandtab tabstop=2 cindent
511// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
512// clang-format on
void dt_accels_disconnect_on_text_input(GtkWidget *widget)
Disconnect accels while a text or search entry has the focus, and reconnect them when it loses it....
GtkWidget * dt_gui_main_window(void)
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
GtkWidget * dtgtk_button_new(DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
Definition button.c:135
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
GtkTreeStore * store
its model, owned by the view
GtkWidget * view
the tree view
static void _add_tag_button_clicked(GtkButton *button, dt_lib_export_metadata_t *d)
static void _add_selected_metadata(GtkTreeView *view, dt_lib_export_metadata_t *d)
static gboolean _find_metadata_iter_per_text(GtkTreeModel *model, GtkTreeIter *iter, gint col, const char *text)
static gboolean _click_on_metadata_list(GtkWidget *view, GdkEventButton *event, dt_lib_export_metadata_t *d)
static void _remove_tag_from_list(dt_lib_export_metadata_t *d)
static void _delete_tag_button_clicked(GtkButton *button, dt_lib_export_metadata_t *d)
static void _formula_editing_started(GtkCellRenderer *renderer, GtkCellEditable *editable, char *path, dt_lib_export_metadata_t *d)
static gboolean _set_matching_tag_visibility(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, dt_lib_export_metadata_t *d)
static gboolean _key_press_on_list(GtkWidget *widget, GdkEventKey *event, dt_lib_export_metadata_t *d)
dt_lib_tagging_cols_t
@ DT_LIB_EXPORT_METADATA_COL_FORMULA
@ DT_LIB_EXPORT_METADATA_COL_TYPE
@ DT_LIB_EXPORT_METADATA_COL_VISIBLE
@ DT_LIB_EXPORT_METADATA_COL_XMP
@ DT_LIB_EXPORT_METADATA_NUM_COLS
char * dt_lib_export_metadata_configuration_dialog(char *metadata_presets, const gboolean ondisk)
static void _tag_name_changed(GtkEntry *entry, dt_lib_export_metadata_t *d)
static void _formula_edited(GtkCellRenderer *renderer, gchar *path, gchar *new_text, dt_lib_export_metadata_t *d)
const GList * dt_exif_get_exiv2_taglist()
Definition exif.cc:361
static void _tags_toggled(GtkToggleButton *dttag, dt_lib_export_metadata_t *d)
static guint dt_keys_mainpad_alternatives(const guint key_val)
Remap keypad keys to usual mainpad ones.
Definition gdkkeys.h:118
void dt_gtkentry_setup_completion(GtkEntry *entry, const dt_gtkentry_completion_spec *compl_list, const char *trigger_char)
Definition gtkentry.c:176
const dt_gtkentry_completion_spec * dt_gtkentry_get_default_path_compl_list()
Definition gtkentry.c:200
const char * model
gboolean dt_handle_dialog_enter(GtkWidget *widget, GdkEventKey *event, gpointer data)
Definition lib.c:1324
_lib_location_type_t type
Definition location.c:1
#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
void dt_lib_export_metadata_set_conf(const char *metadata_presets)
@ DT_META_OMIT_HIERARCHY
@ DT_META_HIERARCHICAL_TAG
@ DT_META_TAG
@ DT_META_CALCULATED
@ DT_META_EXIF
@ DT_META_METADATA
@ DT_META_DT_HISTORY
@ DT_META_PRIVATE_TAG
@ DT_META_GEOTAG
@ DT_META_SYNONYMS_TAG
dt_mipmap_buffer_dsc_flags flags
Definition mipmap_cache.c:4
void dt_osx_disallow_fullscreen(GtkWidget *widget)
Definition osx.mm:105
const char * name
Definition pdf.h:90
GList * dt_util_str_to_glist(const gchar *separator, const gchar *text)
Definition utility.c:833
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)
void dtgtk_cairo_paint_minus_simple(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_plus_simple(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)