1 /*
2 This file is part of darktable,
3 Copyright (C) 2009-2021 darktable developers.
4
5 darktable is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 darktable is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with darktable. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "bauhaus/bauhaus.h"
20 #include "common/collection.h"
21 #include "common/colorspaces.h"
22 #include "common/darktable.h"
23 #include "common/debug.h"
24 #include "common/file_location.h"
25 #include "common/imageio_module.h"
26 #include "common/styles.h"
27 #include "control/conf.h"
28 #include "control/control.h"
29 #include "control/jobs.h"
30 #include "control/signal.h"
31 #include "dtgtk/button.h"
32 #include "gui/accelerators.h"
33 #include "gui/gtk.h"
34 #include "gui/presets.h"
35 #include "libs/lib.h"
36 #include "libs/lib_api.h"
37 #ifdef GDK_WINDOWING_QUARTZ
38 #include "osx/osx.h"
39 #endif
40 #include <gdk/gdkkeysyms.h>
41 #include <gtk/gtk.h>
42 #include <stdlib.h>
43 #include <ctype.h>
44
45 #include <glib.h>
46
47 DT_MODULE(7)
48
49 #define EXPORT_MAX_IMAGE_SIZE UINT16_MAX
50 #define CONFIG_PREFIX "plugins/lighttable/export/"
51
52 typedef struct dt_lib_export_t
53 {
54 GtkWidget *dimensions_type, *print_dpi, *print_height, *print_width;
55 GtkWidget *unit_label;
56 GtkWidget *width, *height;
57 GtkWidget *px_size, *print_size, *scale, *size_in_px;
58 GtkWidget *storage, *format;
59 int format_lut[128];
60 uint32_t max_allowed_width , max_allowed_height;
61 GtkWidget *upscale, *profile, *intent, *style, *style_mode;
62 GtkButton *export_button;
63 GtkWidget *storage_extra_container, *format_extra_container;
64 GtkWidget *high_quality;
65 GtkWidget *export_masks;
66 char *metadata_export;
67 } dt_lib_export_t;
68
69
70 typedef enum dt_dimensions_type_t
71 {
72 DT_DIMENSIONS_PIXELS = 0, // set dimensions exactly in pixels
73 DT_DIMENSIONS_CM = 1, // set dimensions from physical size in centimeters * DPI
74 DT_DIMENSIONS_INCH = 2, // set dimensions from physical size in inch
75 DT_DIMENSIONS_SCALE = 3 // set dimensions by scale
76 } dt_dimensions_type_t;
77
78 char *dt_lib_export_metadata_configuration_dialog(char *list, const gboolean ondisk);
79 /** Updates the combo box and shows only the supported formats of current selected storage module */
80 static void _update_formats_combobox(dt_lib_export_t *d);
81 /** Sets the max dimensions based upon what storage and format supports */
82 static void _update_dimensions(dt_lib_export_t *d);
83 /** get the max output dimension supported by combination of storage and format.. */
84 static void _get_max_output_dimension(dt_lib_export_t *d, uint32_t *width, uint32_t *height);
85 static void _resync_print_dimensions(dt_lib_export_t *self);
86 static void _resync_pixel_dimensions(dt_lib_export_t *self);
87
88 #define INCH_TO_CM (2.54f)
89
pixels2cm(dt_lib_export_t * self,const uint32_t pix)90 static inline float pixels2cm(dt_lib_export_t *self, const uint32_t pix)
91 {
92 const int dpi = atoi(gtk_entry_get_text(GTK_ENTRY(self->print_dpi)));
93 return ((float)pix * INCH_TO_CM) / (float)dpi;
94 }
95
pixels2inch(dt_lib_export_t * self,const uint32_t pix)96 static inline float pixels2inch(dt_lib_export_t *self, const uint32_t pix)
97 {
98 const int dpi = atoi(gtk_entry_get_text(GTK_ENTRY(self->print_dpi)));
99 return (float)pix / (float)dpi;
100 }
101
cm2pixels(dt_lib_export_t * self,const float cm)102 static inline uint32_t cm2pixels(dt_lib_export_t *self, const float cm)
103 {
104 const int dpi = atoi(gtk_entry_get_text(GTK_ENTRY(self->print_dpi)));
105 return ceilf((cm * (float)dpi) / INCH_TO_CM);
106 }
107
inch2pixels(dt_lib_export_t * self,const float inch)108 static inline uint32_t inch2pixels(dt_lib_export_t *self, const float inch)
109 {
110 const int dpi = atoi(gtk_entry_get_text(GTK_ENTRY(self->print_dpi)));
111 return ceilf(inch * (float)dpi);
112 }
113
print2pixels(dt_lib_export_t * self,const float value)114 static inline uint32_t print2pixels(dt_lib_export_t *self, const float value)
115 {
116 const dt_dimensions_type_t d_type = (dt_dimensions_type_t)dt_bauhaus_combobox_get(self->dimensions_type);
117 switch(d_type)
118 {
119 case(DT_DIMENSIONS_PIXELS):
120 return ceilf(value);
121 case(DT_DIMENSIONS_CM):
122 return cm2pixels(self, value);
123 case(DT_DIMENSIONS_INCH):
124 return inch2pixels(self, value);
125 case(DT_DIMENSIONS_SCALE):
126 ;
127 }
128
129 // should never run this
130 return ceilf(value);
131 }
132
pixels2print(dt_lib_export_t * self,const uint32_t pix)133 static inline float pixels2print(dt_lib_export_t *self, const uint32_t pix)
134 {
135 const dt_dimensions_type_t d_type = (dt_dimensions_type_t)dt_bauhaus_combobox_get(self->dimensions_type);
136 switch(d_type)
137 {
138 case(DT_DIMENSIONS_PIXELS):
139 return (float)pix;
140 case(DT_DIMENSIONS_CM):
141 return pixels2cm(self, pix);
142 case(DT_DIMENSIONS_INCH):
143 return pixels2inch(self, pix);
144 case(DT_DIMENSIONS_SCALE):
145 ;
146 }
147
148 // should never run this
149 return (float)pix;
150 }
151
name(dt_lib_module_t * self)152 const char *name(dt_lib_module_t *self)
153 {
154 return _("export");
155 }
156
views(dt_lib_module_t * self)157 const char **views(dt_lib_module_t *self)
158 {
159 static const char *v1[] = {"lighttable", "darkroom", NULL};
160 static const char *v2[] = {"lighttable", NULL};
161
162 if(dt_conf_get_bool("plugins/darkroom/export/visible"))
163 return v1;
164 else
165 return v2;
166 }
167
container(dt_lib_module_t * self)168 uint32_t container(dt_lib_module_t *self)
169 {
170 const dt_view_t *cv = dt_view_manager_get_current_view(darktable.view_manager);
171 if(cv->view((dt_view_t *)cv) == DT_VIEW_DARKROOM)
172 return DT_UI_CONTAINER_PANEL_LEFT_CENTER;
173 else
174 return DT_UI_CONTAINER_PANEL_RIGHT_CENTER;
175 }
176
_update(dt_lib_module_t * self)177 static void _update(dt_lib_module_t *self)
178 {
179 dt_lib_cancel_postponed_update(self);
180 const dt_lib_export_t *d = (dt_lib_export_t *)self->data;
181
182 const GList *imgs = dt_view_get_images_to_act_on(TRUE, FALSE, FALSE);
183 const gboolean has_act_on = imgs != NULL;
184
185 const char *format_name = dt_conf_get_string_const(CONFIG_PREFIX "format_name");
186 const char *storage_name = dt_conf_get_string_const(CONFIG_PREFIX "storage_name");
187 const int format_index = dt_imageio_get_index_of_format(dt_imageio_get_format_by_name(format_name));
188 const int storage_index = dt_imageio_get_index_of_storage(dt_imageio_get_storage_by_name(storage_name));
189
190 gtk_widget_set_sensitive(GTK_WIDGET(d->export_button), has_act_on && format_index != -1 && storage_index != -1);
191 }
192
_image_selection_changed_callback(gpointer instance,dt_lib_module_t * self)193 static void _image_selection_changed_callback(gpointer instance, dt_lib_module_t *self)
194 {
195 _update(self);
196 }
197
_collection_updated_callback(gpointer instance,dt_collection_change_t query_change,dt_collection_properties_t changed_property,gpointer imgs,int next,dt_lib_module_t * self)198 static void _collection_updated_callback(gpointer instance, dt_collection_change_t query_change,
199 dt_collection_properties_t changed_property, gpointer imgs, int next,
200 dt_lib_module_t *self)
201 {
202 _update(self);
203 }
204
_mouse_over_image_callback(gpointer instance,dt_lib_module_t * self)205 static void _mouse_over_image_callback(gpointer instance, dt_lib_module_t *self)
206 {
207 dt_lib_queue_postponed_update(self, _update);
208 }
209
_is_int(double value)210 gboolean _is_int(double value)
211 {
212 return (value == (int)value);
213 }
214
_scale_optim()215 static void _scale_optim()
216 {
217 double num = 1.0, denum = 1.0;
218 dt_imageio_resizing_factor_get_and_parsing(&num, &denum);
219 gchar *scale_str = dt_conf_get_string(CONFIG_PREFIX "resizing_factor");
220 gchar _str[6] = "";
221
222 gchar *pdiv = strchr(scale_str, '/');
223
224 gchar scale_buf[64] = "";
225 if (pdiv == NULL)
226 {
227 if (_is_int(num) && num > 0.0)
228 {
229 sprintf(_str, "%d", (int) num);
230 g_strlcat(scale_buf, _str, sizeof(scale_buf));
231 }
232 else
233 {
234 g_strlcat(scale_buf, scale_str, sizeof(scale_buf));
235 }
236 }
237 else if (pdiv-scale_str == 0)
238 {
239 if (_is_int(denum) && denum > 0.0)
240 {
241 sprintf(_str, "%d", (int) denum);
242 g_strlcat(scale_buf, _str, sizeof(scale_buf));
243 }
244 else
245 {
246 g_strlcat(scale_buf, "1/", sizeof(scale_buf));
247 g_strlcat(scale_buf, pdiv+1, sizeof(scale_buf));
248 }
249 }
250 else
251 {
252 if (_is_int(num) && num > 0.0)
253 {
254 sprintf(_str, "%d", (int) num);
255 g_strlcat(scale_buf, _str, sizeof(scale_buf));
256 }
257 else
258 {
259 g_strlcat(scale_buf, scale_str, sizeof(scale_buf));
260 }
261 g_strlcat(scale_buf, "/", sizeof(scale_buf));
262 if (_is_int(denum) && denum > 0.0)
263 {
264 sprintf(_str, "%d", (int) denum);
265 g_strlcat(scale_buf, _str, sizeof(scale_buf));
266 }
267 else
268 {
269 g_strlcat(scale_buf, pdiv+1, sizeof(scale_buf));
270 }
271 }
272 dt_conf_set_string(CONFIG_PREFIX "resizing_factor", scale_buf);
273
274 free(scale_str);
275 }
276
_export_button_clicked(GtkWidget * widget,dt_lib_export_t * d)277 static void _export_button_clicked(GtkWidget *widget, dt_lib_export_t *d)
278 {
279 /* write current history changes so nothing gets lost,
280 do that only in the darkroom as there is nothing to be saved
281 when in the lighttable (and it would write over current history stack) */
282 const dt_view_t *cv = dt_view_manager_get_current_view(darktable.view_manager);
283 if(cv->view(cv) == DT_VIEW_DARKROOM) dt_dev_write_history(darktable.develop);
284
285 char style[128] = { 0 };
286
287 // get the format_name and storage_name settings which are plug-ins name and not necessary what is displayed on the combobox.
288 // note that we cannot take directly the combobox entry index as depending on the storage some format are not listed.
289 const char *format_name = dt_conf_get_string_const(CONFIG_PREFIX "format_name");
290 const char *storage_name = dt_conf_get_string_const(CONFIG_PREFIX "storage_name");
291 const int format_index = dt_imageio_get_index_of_format(dt_imageio_get_format_by_name(format_name));
292 const int storage_index = dt_imageio_get_index_of_storage(dt_imageio_get_storage_by_name(storage_name));
293
294 if(format_index == -1)
295 {
296 dt_control_log("invalid format for export selected");
297 return;
298 }
299 if(storage_index == -1)
300 {
301 dt_control_log("invalid storage for export selected");
302 return;
303 }
304
305 char *confirm_message = NULL;
306 dt_imageio_module_storage_t *mstorage = dt_imageio_get_storage();
307 if(mstorage->ask_user_confirmation)
308 confirm_message = mstorage->ask_user_confirmation(mstorage);
309 if(confirm_message)
310 {
311 const GtkWidget *win = dt_ui_main_window(darktable.gui->ui);
312 GtkWidget *dialog = gtk_message_dialog_new(
313 GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
314 "%s", confirm_message);
315 #ifdef GDK_WINDOWING_QUARTZ
316 dt_osx_disallow_fullscreen(dialog);
317 #endif
318
319 gtk_window_set_title(GTK_WINDOW(dialog), _("export to disk"));
320 const gint res = gtk_dialog_run(GTK_DIALOG(dialog));
321 gtk_widget_destroy(dialog);
322 g_free(confirm_message);
323 confirm_message = NULL;
324
325 if(res != GTK_RESPONSE_YES)
326 {
327 return;
328 }
329 }
330
331 // Let's get the max dimension restriction if any...
332 uint32_t max_width = dt_conf_get_int(CONFIG_PREFIX "width");
333 uint32_t max_height = dt_conf_get_int(CONFIG_PREFIX "height");
334
335 const gboolean upscale = dt_conf_get_bool(CONFIG_PREFIX "upscale");
336 const gboolean high_quality = dt_conf_get_bool(CONFIG_PREFIX "high_quality_processing");
337 const gboolean export_masks = dt_conf_get_bool(CONFIG_PREFIX "export_masks");
338 const gboolean style_append = dt_conf_get_bool(CONFIG_PREFIX "style_append");
339 const char *tmp = dt_conf_get_string_const(CONFIG_PREFIX "style");
340 if(tmp)
341 {
342 g_strlcpy(style, tmp, sizeof(style));
343 }
344
345 // if upscale is activated and only one dimension is 0 we adjust it to ensure
346 // that the up-scaling will happen. The null dimension is set to MAX_ASPECT_RATIO
347 // time the other dimension, allowing for a ratio of max 1:100 exported images.
348
349 if(upscale)
350 {
351 const uint32_t MAX_ASPECT_RATIO = 100;
352
353 if(max_width == 0 && max_height != 0)
354 {
355 max_width = max_height * MAX_ASPECT_RATIO;
356 }
357 else if(max_height == 0 && max_width != 0)
358 {
359 max_height = max_width * MAX_ASPECT_RATIO;
360 }
361 }
362
363 const dt_colorspaces_color_profile_type_t icc_type = dt_conf_get_int(CONFIG_PREFIX "icctype");
364 gchar *icc_filename = dt_conf_get_string(CONFIG_PREFIX "iccprofile");
365 const dt_iop_color_intent_t icc_intent = dt_conf_get_int(CONFIG_PREFIX "iccintent");
366
367 GList *list = g_list_copy((GList *)dt_view_get_images_to_act_on(TRUE, TRUE, TRUE));
368 dt_control_export(list, max_width, max_height, format_index, storage_index, high_quality, upscale, export_masks,
369 style, style_append, icc_type, icc_filename, icc_intent, d->metadata_export);
370
371 g_free(icc_filename);
372
373 _scale_optim();
374 gtk_entry_set_text(GTK_ENTRY(d->scale), dt_conf_get_string_const(CONFIG_PREFIX "resizing_factor"));
375 }
376
_scale_changed(GtkEntry * spin,dt_lib_export_t * d)377 static void _scale_changed(GtkEntry *spin, dt_lib_export_t *d)
378 {
379 const char *validSign = ",.0123456789";
380 const gchar *value = gtk_entry_get_text(spin);
381
382 const int len = sizeof(value);
383 int i, j = 0, idec = 0, idiv = 0, pdiv = 0;
384 char new_value[30] = "";
385
386 for (i = 0; i < len; i++)
387 {
388 char *val = strchr(validSign, value[i]);
389 if (val == NULL)
390 {
391 if (idiv==0)
392 {
393 if (i == 0)
394 {
395 new_value[j++] = '1';
396 }
397 else
398 {
399 if (atof(value) == 0.0)
400 {
401 new_value[0] = '1';
402 }
403 idec = 0;
404 idiv = 1;
405 new_value[j++] = '/';
406 pdiv = j;
407 }
408 }
409 }
410 else if ((val[0] == '.') || (val[0] == ','))
411 {
412 if (idec == 0)
413 {
414 if ((i == 0) || (i == pdiv))
415 {
416 new_value[j++] = '0';
417 }
418 else
419 {
420 idec = 1;
421 new_value[j++] = value[i];
422 }
423 }
424 }
425 else if (value[i] == '\0')
426 {
427 break;
428 }
429 else
430 {
431 new_value[j++] = value[i];
432 }
433 }
434 dt_conf_set_string(CONFIG_PREFIX "resizing_factor", new_value);
435 gtk_entry_set_text(spin, new_value);
436 }
437
438 static void _width_changed(GtkEditable *entry, gpointer user_data);
439 static void _height_changed(GtkEditable *entry, gpointer user_data);
440
_scale_mdlclick(GtkEntry * spin,GdkEventButton * event,dt_lib_export_t * d)441 static gboolean _scale_mdlclick(GtkEntry *spin, GdkEventButton *event, dt_lib_export_t *d)
442 {
443 if (event->button == 2)
444 {
445 dt_conf_set_string(CONFIG_PREFIX "resizing_factor", "1");
446 g_signal_handlers_block_by_func(spin, _scale_changed, d);
447 gtk_entry_set_text(GTK_ENTRY(spin), "1");
448 g_signal_handlers_unblock_by_func(spin, _scale_changed, d);
449 }
450 else
451 {
452 _scale_changed(spin, d);
453 }
454 return FALSE;
455 }
456
_widht_mdlclick(GtkEntry * spin,GdkEventButton * event,gpointer user_data)457 static void _widht_mdlclick(GtkEntry *spin, GdkEventButton *event, gpointer user_data)
458 {
459 if (event->button == 2)
460 {
461 dt_conf_set_int(CONFIG_PREFIX "width", 0);
462 g_signal_handlers_block_by_func(spin, _width_changed, user_data);
463 gtk_entry_set_text(GTK_ENTRY(spin), "0");
464 g_signal_handlers_unblock_by_func(spin, _width_changed, user_data);
465 }
466 else
467 {
468 _width_changed(GTK_EDITABLE(spin), user_data);
469 }
470 }
471
_height_mdlclick(GtkEntry * spin,GdkEventButton * event,gpointer user_data)472 static void _height_mdlclick(GtkEntry *spin, GdkEventButton *event, gpointer user_data)
473 {
474 if (event->button == 2)
475 {
476 dt_conf_set_int(CONFIG_PREFIX "height", 0);
477 g_signal_handlers_block_by_func(spin, _height_changed, user_data);
478 gtk_entry_set_text(GTK_ENTRY(spin), "0");
479 g_signal_handlers_unblock_by_func(spin, _height_changed, user_data);
480 }
481 else
482 {
483 _height_changed(GTK_EDITABLE(spin), user_data);
484 }
485 }
486
_size_in_px_update(dt_lib_export_t * d)487 static void _size_in_px_update(dt_lib_export_t *d)
488 {
489 const dt_dimensions_type_t d_type = (dt_dimensions_type_t)dt_bauhaus_combobox_get(d->dimensions_type);
490
491 if ((d_type == DT_DIMENSIONS_SCALE) || (d_type == DT_DIMENSIONS_PIXELS))
492 {
493 gtk_widget_hide(d->size_in_px);
494 }
495 else
496 {
497 gtk_widget_show(d->size_in_px);
498 gchar size_in_px_txt[120];
499 snprintf(size_in_px_txt, sizeof(size_in_px_txt) / sizeof(size_in_px_txt[0]), _("which is equal to %s × %s px"),
500 gtk_entry_get_text(GTK_ENTRY(d->width)), gtk_entry_get_text(GTK_ENTRY(d->height)));
501 gtk_label_set_text(GTK_LABEL(d->size_in_px), size_in_px_txt);
502 }
503 }
504
_set_dimensions(dt_lib_export_t * d,uint32_t max_width,uint32_t max_height)505 void _set_dimensions(dt_lib_export_t *d, uint32_t max_width, uint32_t max_height)
506 {
507 gchar *max_width_char = g_strdup_printf("%d", max_width);
508 gchar *max_height_char = g_strdup_printf("%d", max_height);
509
510 ++darktable.gui->reset;
511 gtk_entry_set_text(GTK_ENTRY(d->width), max_width_char);
512 gtk_entry_set_text(GTK_ENTRY(d->height), max_height_char);
513 _size_in_px_update(d);
514 --darktable.gui->reset;
515
516 dt_conf_set_int(CONFIG_PREFIX "width", max_width);
517 dt_conf_set_int(CONFIG_PREFIX "height", max_height);
518
519 g_free(max_width_char);
520 g_free(max_height_char);
521 _resync_print_dimensions(d);
522 }
523
524
_print_size_update_display(dt_lib_export_t * self)525 void _print_size_update_display(dt_lib_export_t *self)
526 {
527 const dt_dimensions_type_t d_type = (dt_dimensions_type_t)dt_bauhaus_combobox_get(self->dimensions_type);
528
529 if(d_type == DT_DIMENSIONS_PIXELS)
530 {
531 gtk_widget_set_visible(GTK_WIDGET(self->print_size), FALSE);
532 gtk_widget_set_sensitive(GTK_WIDGET(self->width), TRUE);
533 gtk_widget_set_sensitive(GTK_WIDGET(self->height), TRUE);
534 }
535 else
536 {
537 const gboolean is_scaling = dt_conf_is_equal(CONFIG_PREFIX "resizing", "scaling");
538 if (!is_scaling)
539 {
540 // max size
541 gtk_widget_set_visible(GTK_WIDGET(self->print_size), TRUE);
542 }
543 gtk_widget_set_sensitive(GTK_WIDGET(self->width), FALSE);
544 gtk_widget_set_sensitive(GTK_WIDGET(self->height), FALSE);
545
546 if(d_type == DT_DIMENSIONS_CM)
547 gtk_label_set_text(GTK_LABEL(self->unit_label), _("cm"));
548 else // DT_DIMENSIONS_INCH
549 gtk_label_set_text(GTK_LABEL(self->unit_label), C_("unit", "in"));
550 }
551 }
552
gui_reset(dt_lib_module_t * self)553 void gui_reset(dt_lib_module_t *self)
554 {
555 // make sure we don't do anything useless:
556 if(!dt_control_running()) return;
557 dt_lib_export_t *d = (dt_lib_export_t *)self->data;
558 gtk_entry_set_text(GTK_ENTRY(d->width), dt_confgen_get(CONFIG_PREFIX "width", DT_DEFAULT));
559 gtk_entry_set_text(GTK_ENTRY(d->height), dt_confgen_get(CONFIG_PREFIX "height", DT_DEFAULT));
560 dt_bauhaus_combobox_set(d->dimensions_type, dt_confgen_get_int(CONFIG_PREFIX "dimensions_type", DT_DEFAULT));
561 _print_size_update_display(d);
562
563 // Set storage
564 const int storage_index = dt_imageio_get_index_of_storage(dt_imageio_get_storage_by_name(dt_confgen_get(CONFIG_PREFIX "storage_name", DT_DEFAULT)));
565 dt_bauhaus_combobox_set(d->storage, storage_index);
566
567 dt_bauhaus_combobox_set(d->upscale, dt_confgen_get_bool(CONFIG_PREFIX "upscale", DT_DEFAULT) ? 1 : 0);
568 dt_bauhaus_combobox_set(d->high_quality, dt_confgen_get_bool(CONFIG_PREFIX "high_quality_processing", DT_DEFAULT) ? 1 : 0);
569 dt_bauhaus_combobox_set(d->export_masks, dt_confgen_get_bool(CONFIG_PREFIX "export_masks", DT_DEFAULT) ? 1 : 0);
570
571 dt_bauhaus_combobox_set(d->intent, dt_confgen_get_int(CONFIG_PREFIX "iccintent", DT_DEFAULT) + 1);
572
573 // iccprofile
574 const int icctype = dt_confgen_get_int(CONFIG_PREFIX "icctype", DT_DEFAULT);
575 gchar *iccfilename = dt_conf_get_string(CONFIG_PREFIX "iccprofile");
576 dt_bauhaus_combobox_set(d->profile, 0);
577 if(icctype != DT_COLORSPACE_NONE)
578 {
579 for(GList *profiles = darktable.color_profiles->profiles; profiles; profiles = g_list_next(profiles))
580 {
581 const dt_colorspaces_color_profile_t *pp = (dt_colorspaces_color_profile_t *)profiles->data;
582 if(pp->out_pos > -1
583 && icctype == pp->type
584 && (icctype != DT_COLORSPACE_FILE || !strcmp(iccfilename, pp->filename)))
585 {
586 dt_bauhaus_combobox_set(d->profile, pp->out_pos + 1);
587 break;
588 }
589 }
590 }
591
592 g_free(iccfilename);
593
594 // style
595 // set it to none if the var is not set or the style doesn't exist anymore
596 gboolean rc = FALSE;
597 const char *style = dt_confgen_get(CONFIG_PREFIX "style", DT_DEFAULT);
598 if(style != NULL && strlen(style) > 0)
599 {
600 rc = dt_bauhaus_combobox_set_from_text(d->style, style);
601 if(rc == FALSE) dt_bauhaus_combobox_set(d->style, 0);
602 }
603 else
604 dt_bauhaus_combobox_set(d->style, 0);
605
606 // style mode to overwrite as it was the initial behavior
607 dt_bauhaus_combobox_set(d->style_mode, dt_confgen_get_bool(CONFIG_PREFIX "style_append", DT_DEFAULT));
608
609 gtk_widget_set_sensitive(GTK_WIDGET(d->style_mode), dt_bauhaus_combobox_get(d->style)==0?FALSE:TRUE);
610
611 // export metadata presets
612 g_free(d->metadata_export);
613 d->metadata_export = dt_lib_export_metadata_get_conf();
614
615 dt_imageio_module_format_t *mformat = dt_imageio_get_format();
616 if(mformat) mformat->gui_reset(mformat);
617 dt_imageio_module_storage_t *mstorage = dt_imageio_get_storage();
618 if(mstorage) mstorage->gui_reset(mstorage);
619
620 _update(self);
621 }
622
set_format_by_name(dt_lib_export_t * d,const char * name)623 static void set_format_by_name(dt_lib_export_t *d, const char *name)
624 {
625 // Find the selected format plugin among all existing plugins
626 dt_imageio_module_format_t *module = NULL;
627 for(GList *it = darktable.imageio->plugins_format; it; it = g_list_next(it))
628 {
629 if(g_strcmp0(((dt_imageio_module_format_t *)it->data)->name(), name) == 0
630 || g_strcmp0(((dt_imageio_module_format_t *)it->data)->plugin_name, name) == 0)
631 {
632 module = (dt_imageio_module_format_t *)it->data;
633 break;
634 }
635 }
636
637 if(!module)
638 {
639 gtk_widget_hide(d->format_extra_container);
640 return;
641 }
642 else if(module->widget)
643 {
644 gtk_widget_show_all(d->format_extra_container);
645 gtk_stack_set_visible_child(GTK_STACK(d->format_extra_container), module->widget);
646 }
647 else
648 {
649 gtk_widget_hide(d->format_extra_container);
650 }
651
652 // Store the new format
653 dt_conf_set_string(CONFIG_PREFIX "format_name", module->plugin_name);
654
655 if(dt_bauhaus_combobox_set_from_text(d->format, module->name()) == FALSE)
656 dt_bauhaus_combobox_set(d->format, 0);
657
658 // Let's also update combination of storage/format dimension restrictions
659 _update_dimensions(d);
660
661 // only some modules support export of masks
662 // set it to 0 when insensitive and restore when making it sensitive again. this doesn't survive dt restarts.
663 const gboolean support_layers = (module->flags(NULL) & FORMAT_FLAGS_SUPPORT_LAYERS) == FORMAT_FLAGS_SUPPORT_LAYERS;
664 const gboolean is_enabled = gtk_widget_get_sensitive(d->export_masks);
665 if(support_layers && !is_enabled)
666 {
667 // combobox was disabled and shall be enabled. we want to restore the old setting.
668 const gboolean export_masks = dt_conf_get_bool(CONFIG_PREFIX "export_masks");
669 gtk_widget_set_sensitive(d->export_masks, TRUE);
670 dt_bauhaus_combobox_set(d->export_masks, export_masks ? 1 : 0);
671 }
672 else if(!support_layers && is_enabled)
673 {
674 // combobox was enabled but shall be disabled. we want to save the current setting.
675 const int export_masks = dt_bauhaus_combobox_get(d->export_masks);
676 dt_bauhaus_combobox_set(d->export_masks, 0);
677 dt_conf_set_bool(CONFIG_PREFIX "export_masks", export_masks == 1);
678 gtk_widget_set_sensitive(d->export_masks, FALSE);
679 }
680 }
681
_format_changed(GtkWidget * widget,dt_lib_export_t * d)682 static void _format_changed(GtkWidget *widget, dt_lib_export_t *d)
683 {
684 const gchar *name = dt_bauhaus_combobox_get_text(d->format);
685 g_signal_handlers_block_by_func(widget, _format_changed, d);
686 set_format_by_name(d, name);
687 g_signal_handlers_unblock_by_func(widget, _format_changed, d);
688 }
689
_get_max_output_dimension(dt_lib_export_t * d,uint32_t * width,uint32_t * height)690 static void _get_max_output_dimension(dt_lib_export_t *d, uint32_t *width, uint32_t *height)
691 {
692 const char *storage_name = dt_conf_get_string_const(CONFIG_PREFIX "storage_name");
693 dt_imageio_module_storage_t *storage = dt_imageio_get_storage_by_name(storage_name);
694
695 const char *format_name = dt_conf_get_string_const(CONFIG_PREFIX "format_name");
696 dt_imageio_module_format_t *format = dt_imageio_get_format_by_name(format_name);
697
698 if(storage && format)
699 {
700 uint32_t fw, fh, sw, sh;
701 fw = fh = sw = sh = 0; // We are all equals!!!
702 storage->dimension(storage, NULL, &sw, &sh);
703 format->dimension(format, NULL, &fw, &fh);
704
705 if(sw == 0 || fw == 0)
706 *width = sw > fw ? sw : fw;
707 else
708 *width = sw < fw ? sw : fw;
709
710 if(sh == 0 || fh == 0)
711 *height = sh > fh ? sh : fh;
712 else
713 *height = sh < fh ? sh : fh;
714 }
715 }
716
_validate_dimensions(dt_lib_export_t * d)717 static void _validate_dimensions(dt_lib_export_t *d)
718 {
719 //reset dimensions to previously stored value if they exceed the maximum
720 uint32_t width = atoi(gtk_entry_get_text(GTK_ENTRY(d->width)));
721 uint32_t height = atoi(gtk_entry_get_text(GTK_ENTRY(d->height)));
722 if(width > d->max_allowed_width || height > d->max_allowed_height)
723 {
724 width = width > d->max_allowed_width ? dt_conf_get_int(CONFIG_PREFIX "width") : width;
725 height = height > d->max_allowed_height ? dt_conf_get_int(CONFIG_PREFIX "height") : height;
726 _set_dimensions(d, width, height);
727 }
728 }
729
_update_dimensions(dt_lib_export_t * d)730 static void _update_dimensions(dt_lib_export_t *d)
731 {
732 uint32_t max_w = 0, max_h = 0;
733 _get_max_output_dimension(d, &max_w, &max_h);
734 d->max_allowed_width = max_w > 0 ? max_w : EXPORT_MAX_IMAGE_SIZE;
735 d->max_allowed_height = max_h > 0 ? max_h : EXPORT_MAX_IMAGE_SIZE;
736 _validate_dimensions(d);
737 }
738
set_storage_by_name(dt_lib_export_t * d,const char * name)739 static void set_storage_by_name(dt_lib_export_t *d, const char *name)
740 {
741 int k = -1;
742 dt_imageio_module_storage_t *module = NULL;
743
744 for(const GList *it = darktable.imageio->plugins_storage; it; it = g_list_next(it))
745 {
746 dt_imageio_module_storage_t *storage = (dt_imageio_module_storage_t *)it->data;
747 k++;
748 if(strcmp(storage->name(storage), name) == 0 || strcmp(storage->plugin_name, name) == 0)
749 {
750 module = storage;
751 break;
752 }
753 }
754
755 if(!module)
756 {
757 gtk_widget_hide(d->storage_extra_container);
758 return;
759 }
760 else if(module->widget)
761 {
762 gtk_widget_show_all(d->storage_extra_container);
763 gtk_stack_set_visible_child(GTK_STACK(d->storage_extra_container),module->widget);
764 }
765 else
766 {
767 gtk_widget_hide(d->storage_extra_container);
768 }
769 dt_bauhaus_combobox_set(d->storage, k);
770 dt_conf_set_string(CONFIG_PREFIX "storage_name", module->plugin_name);
771
772
773 // Check if plugin recommends a max dimension and set
774 // if not implemented the stored conf values are used..
775 uint32_t w = 0, h = 0;
776 module->recommended_dimension(module, NULL, &w, &h);
777
778 const uint32_t cw = dt_conf_get_int(CONFIG_PREFIX "width");
779 const uint32_t ch = dt_conf_get_int(CONFIG_PREFIX "height");
780
781 // If user's selected value is below the max, select it
782 if(w > cw || w == 0) w = cw;
783 if(h > ch || h == 0) h = ch;
784
785 // Set the recommended dimension
786 _set_dimensions(d, w, h);
787
788 // Let's update formats combobox with supported formats of selected storage module...
789 _update_formats_combobox(d);
790
791 // Lets try to set selected format if fail select first in list..
792 const char *format_name = dt_conf_get_string_const(CONFIG_PREFIX "format_name");
793 const dt_imageio_module_format_t *format = dt_imageio_get_format_by_name(format_name);
794
795 if(format == NULL
796 || dt_bauhaus_combobox_set_from_text(d->format, format->name()) == FALSE)
797 dt_bauhaus_combobox_set(d->format, 0);
798 }
799
_storage_changed(GtkWidget * widget,dt_lib_export_t * d)800 static void _storage_changed(GtkWidget *widget, dt_lib_export_t *d)
801 {
802 const gchar *name = dt_bauhaus_combobox_get_text(d->storage);
803 g_signal_handlers_block_by_func(widget, _storage_changed, d);
804 if(name) set_storage_by_name(d, name);
805 g_signal_handlers_unblock_by_func(widget, _storage_changed, d);
806 }
807
_profile_changed(GtkWidget * widget,dt_lib_export_t * d)808 static void _profile_changed(GtkWidget *widget, dt_lib_export_t *d)
809 {
810 int pos = dt_bauhaus_combobox_get(widget);
811 if(pos > 0)
812 {
813 pos--;
814 for(GList *profiles = darktable.color_profiles->profiles; profiles; profiles = g_list_next(profiles))
815 {
816 const dt_colorspaces_color_profile_t *pp = (dt_colorspaces_color_profile_t *)profiles->data;
817 if(pp->out_pos == pos)
818 {
819 dt_conf_set_int(CONFIG_PREFIX "icctype", pp->type);
820 if(pp->type == DT_COLORSPACE_FILE)
821 dt_conf_set_string(CONFIG_PREFIX "iccprofile", pp->filename);
822 else
823 dt_conf_set_string(CONFIG_PREFIX "iccprofile", "");
824 return;
825 }
826 }
827 }
828 dt_conf_set_int(CONFIG_PREFIX "icctype", DT_COLORSPACE_NONE);
829 dt_conf_set_string(CONFIG_PREFIX "iccprofile", "");
830 }
831
_dimensions_type_changed(GtkWidget * widget,dt_lib_export_t * d)832 static void _dimensions_type_changed(GtkWidget *widget, dt_lib_export_t *d)
833 {
834 if(darktable.gui->reset) return;
835
836 const dt_dimensions_type_t d_type = (dt_dimensions_type_t)dt_bauhaus_combobox_get(widget);
837
838 dt_conf_set_int(CONFIG_PREFIX "dimensions_type", d_type);
839 if(d_type != DT_DIMENSIONS_SCALE)
840 {
841 if(d_type != DT_DIMENSIONS_PIXELS)
842 {
843 gtk_widget_hide(GTK_WIDGET(d->px_size));
844 gtk_widget_show(GTK_WIDGET(d->print_size));
845 gtk_widget_hide(GTK_WIDGET(d->scale));
846 _resync_print_dimensions(d);
847 }
848 else
849 {
850 gtk_widget_show(GTK_WIDGET(d->px_size));
851 gtk_widget_hide(GTK_WIDGET(d->print_size));
852 gtk_widget_hide(GTK_WIDGET(d->scale));
853 }
854 dt_conf_set_string(CONFIG_PREFIX "resizing", "max_size");
855 _print_size_update_display(d);
856 }
857 else
858 {
859 gtk_widget_show(GTK_WIDGET(d->scale));
860 gtk_widget_hide(GTK_WIDGET(d->px_size));
861 gtk_widget_hide(GTK_WIDGET(d->print_size));
862 dt_conf_set_string(CONFIG_PREFIX "resizing", "scaling");
863 }
864 if(d_type == DT_DIMENSIONS_CM || d_type == DT_DIMENSIONS_INCH)
865 {
866 // set dpi to user-set dpi
867 dt_conf_set_int("metadata/resolution", dt_conf_get_int(CONFIG_PREFIX "print_dpi"));
868 }
869 else
870 {
871 // reset export dpi to default value for scale/pixel specific export
872 dt_conf_set_int("metadata/resolution", dt_confgen_get_int("metadata/resolution", DT_DEFAULT));
873 }
874 _size_in_px_update(d);
875 }
876
_resync_print_dimensions(dt_lib_export_t * self)877 static void _resync_print_dimensions(dt_lib_export_t *self)
878 {
879 if(darktable.gui->reset) return;
880
881 const uint32_t width = dt_conf_get_int(CONFIG_PREFIX "width");
882 const uint32_t height = dt_conf_get_int(CONFIG_PREFIX "height");
883 const int dpi = atoi(gtk_entry_get_text(GTK_ENTRY(self->print_dpi)));
884
885 const float p_width = pixels2print(self, width);
886 const float p_height = pixels2print(self, height);
887
888 ++darktable.gui->reset;
889 gchar *pwidth = g_strdup_printf("%.2f", p_width);
890 gchar *pheight = g_strdup_printf("%.2f", p_height);
891 gchar *pdpi = g_strdup_printf("%d", dpi);
892 gtk_entry_set_text(GTK_ENTRY(self->print_width), pwidth);
893 gtk_entry_set_text(GTK_ENTRY(self->print_height), pheight);
894 gtk_entry_set_text(GTK_ENTRY(self->print_dpi), pdpi);
895 g_free(pwidth);
896 g_free(pheight);
897 g_free(pdpi);
898 --darktable.gui->reset;
899 }
900
_resync_pixel_dimensions(dt_lib_export_t * self)901 static void _resync_pixel_dimensions(dt_lib_export_t *self)
902 {
903 if(darktable.gui->reset) return;
904
905 const float p_width = atof(gtk_entry_get_text(GTK_ENTRY(self->print_width)));
906 const float p_height = atof(gtk_entry_get_text(GTK_ENTRY(self->print_height)));
907
908 const uint32_t width = print2pixels(self, p_width);
909 const uint32_t height = print2pixels(self, p_height);
910
911 dt_conf_set_int(CONFIG_PREFIX "width", width);
912 dt_conf_set_int(CONFIG_PREFIX "height", height);
913
914 ++darktable.gui->reset;
915 gchar *pwidth = g_strdup_printf("%d", width);
916 gchar *pheight = g_strdup_printf("%d", height);
917 gtk_entry_set_text(GTK_ENTRY(self->width), pwidth);
918 gtk_entry_set_text(GTK_ENTRY(self->height), pheight);
919 g_free(pwidth);
920 g_free(pheight);
921 --darktable.gui->reset;
922 }
923
_width_changed(GtkEditable * entry,gpointer user_data)924 static void _width_changed(GtkEditable *entry, gpointer user_data)
925 {
926 if(darktable.gui->reset) return;
927
928 const dt_lib_export_t *d = (dt_lib_export_t *)user_data;
929 const uint32_t width = atoi(gtk_entry_get_text(GTK_ENTRY(d->width)));
930 dt_conf_set_int(CONFIG_PREFIX "width", width);
931 }
932
_print_width_changed(GtkEditable * entry,gpointer user_data)933 static void _print_width_changed(GtkEditable *entry, gpointer user_data)
934 {
935 if(darktable.gui->reset) return;
936
937 dt_lib_export_t *d = (dt_lib_export_t *)user_data;
938
939 const float p_width = atof(gtk_entry_get_text(GTK_ENTRY(d->print_width)));
940 const uint32_t width = print2pixels(d, p_width);
941 dt_conf_set_int(CONFIG_PREFIX "width", width);
942
943 ++darktable.gui->reset;
944 gchar *pwidth = g_strdup_printf("%d", width);
945 gtk_entry_set_text(GTK_ENTRY(d->width), pwidth);
946 g_free(pwidth);
947 _size_in_px_update(d);
948 --darktable.gui->reset;
949 }
950
_height_changed(GtkEditable * entry,gpointer user_data)951 static void _height_changed(GtkEditable *entry, gpointer user_data)
952 {
953 if(darktable.gui->reset) return;
954
955 const dt_lib_export_t *d = (dt_lib_export_t *)user_data;
956 const uint32_t height = atoi(gtk_entry_get_text(GTK_ENTRY(d->height)));
957 dt_conf_set_int(CONFIG_PREFIX "height", height);
958 }
959
_print_height_changed(GtkEditable * entry,gpointer user_data)960 static void _print_height_changed(GtkEditable *entry, gpointer user_data)
961 {
962 if(darktable.gui->reset) return;
963
964 dt_lib_export_t *d = (dt_lib_export_t *)user_data;
965
966 const float p_height = atof(gtk_entry_get_text(GTK_ENTRY(d->print_height)));
967 const uint32_t height = print2pixels(d, p_height);
968 dt_conf_set_int(CONFIG_PREFIX "height", height);
969
970 ++darktable.gui->reset;
971 gchar *pheight = g_strdup_printf("%d", height);
972 gtk_entry_set_text(GTK_ENTRY(d->height), pheight);
973 g_free(pheight);
974 _size_in_px_update(d);
975 --darktable.gui->reset;
976 }
977
_print_dpi_changed(GtkWidget * widget,gpointer user_data)978 static void _print_dpi_changed(GtkWidget *widget, gpointer user_data)
979 {
980 if(darktable.gui->reset) return;
981
982 dt_lib_export_t *d = (dt_lib_export_t *)user_data;
983 const int dpi = atoi(gtk_entry_get_text(GTK_ENTRY(d->print_dpi)));
984
985 dt_conf_set_int(CONFIG_PREFIX "print_dpi", dpi);
986 dt_conf_set_int("metadata/resolution", dpi);
987
988 _resync_pixel_dimensions(d);
989 _size_in_px_update(d);
990 }
991
_callback_bool(GtkWidget * widget,gpointer user_data)992 static void _callback_bool(GtkWidget *widget, gpointer user_data)
993 {
994 const char *key = (const char *)user_data;
995 dt_conf_set_bool(key, dt_bauhaus_combobox_get(widget) == 1);
996 }
997
_intent_changed(GtkWidget * widget,dt_lib_export_t * d)998 static void _intent_changed(GtkWidget *widget, dt_lib_export_t *d)
999 {
1000 const int pos = dt_bauhaus_combobox_get(widget);
1001 dt_conf_set_int(CONFIG_PREFIX "iccintent", pos - 1);
1002 }
1003
_style_changed(GtkWidget * widget,dt_lib_export_t * d)1004 static void _style_changed(GtkWidget *widget, dt_lib_export_t *d)
1005 {
1006 if(dt_bauhaus_combobox_get(d->style) == 0)
1007 {
1008 dt_conf_set_string(CONFIG_PREFIX "style", "");
1009 gtk_widget_set_sensitive(GTK_WIDGET(d->style_mode), FALSE);
1010 }
1011 else
1012 {
1013 const gchar *style = dt_bauhaus_combobox_get_text(d->style);
1014 dt_conf_set_string(CONFIG_PREFIX "style", style);
1015 gtk_widget_set_sensitive(GTK_WIDGET(d->style_mode), TRUE);
1016 }
1017 }
1018
position()1019 int position()
1020 {
1021 return 0;
1022 }
1023
_update_formats_combobox(dt_lib_export_t * d)1024 static void _update_formats_combobox(dt_lib_export_t *d)
1025 {
1026 // Clear format combo box
1027 dt_bauhaus_combobox_clear(d->format);
1028
1029 // Get current selected storage
1030 const char *storage_name = dt_conf_get_string_const(CONFIG_PREFIX "storage_name");
1031 dt_imageio_module_storage_t *storage = dt_imageio_get_storage_by_name(storage_name);
1032
1033 // Add supported formats to combobox
1034 gboolean empty = TRUE;
1035 for(const GList *it = darktable.imageio->plugins_format; it; it = g_list_next(it))
1036 {
1037 dt_imageio_module_format_t *format = (dt_imageio_module_format_t *)it->data;
1038 if(storage->supported(storage, format))
1039 {
1040 dt_bauhaus_combobox_add(d->format, format->name());
1041 empty = FALSE;
1042 }
1043 }
1044
1045 gtk_widget_set_sensitive(d->format, !empty);
1046 }
1047
_on_storage_list_changed(gpointer instance,dt_lib_module_t * self)1048 static void _on_storage_list_changed(gpointer instance, dt_lib_module_t *self)
1049 {
1050 dt_lib_export_t *d = self->data;
1051 dt_imageio_module_storage_t *storage = dt_imageio_get_storage();
1052 dt_bauhaus_combobox_clear(d->storage);
1053
1054 dt_gui_container_remove_children(GTK_CONTAINER(d->storage_extra_container));
1055
1056 for(const GList *it = darktable.imageio->plugins_storage; it; it = g_list_next(it))
1057 {
1058 const dt_imageio_module_storage_t *module = (dt_imageio_module_storage_t *)it->data;
1059 dt_bauhaus_combobox_add(d->storage, module->name(module));
1060 if(module->widget)
1061 {
1062 gtk_container_add(GTK_CONTAINER(d->storage_extra_container), module->widget);
1063 }
1064 }
1065 dt_bauhaus_combobox_set(d->storage, dt_imageio_get_index_of_storage(storage));
1066 }
1067
_lib_export_styles_changed_callback(gpointer instance,gpointer user_data)1068 static void _lib_export_styles_changed_callback(gpointer instance, gpointer user_data)
1069 {
1070 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
1071 dt_lib_export_t *d = self->data;
1072
1073 dt_bauhaus_combobox_clear(d->style);
1074 dt_bauhaus_combobox_add(d->style, _("none"));
1075
1076 GList *styles = dt_styles_get_list("");
1077 for(const GList *st_iter = styles; st_iter; st_iter = g_list_next(st_iter))
1078 {
1079 const dt_style_t *style = (dt_style_t *)st_iter->data;
1080 dt_bauhaus_combobox_add(d->style, style->name);
1081 }
1082 dt_bauhaus_combobox_set(d->style, 0);
1083
1084 g_list_free_full(styles, dt_style_free);
1085 }
1086
_menuitem_preferences(GtkMenuItem * menuitem,dt_lib_module_t * self)1087 void _menuitem_preferences(GtkMenuItem *menuitem, dt_lib_module_t *self)
1088 {
1089 dt_lib_export_t *d = (dt_lib_export_t *)self->data;
1090 const gchar *name = dt_bauhaus_combobox_get_text(d->storage);
1091 const gboolean ondisk = name && !g_strcmp0(name, _("file on disk")); // FIXME: NO!!!!!one!
1092 d->metadata_export = dt_lib_export_metadata_configuration_dialog(d->metadata_export, ondisk);
1093 }
1094
set_preferences(void * menu,dt_lib_module_t * self)1095 void set_preferences(void *menu, dt_lib_module_t *self)
1096 {
1097 GtkWidget *mi = gtk_menu_item_new_with_label(_("preferences..."));
1098 g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(_menuitem_preferences), self);
1099 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
1100 }
1101
gui_init(dt_lib_module_t * self)1102 void gui_init(dt_lib_module_t *self)
1103 {
1104 dt_lib_export_t *d = (dt_lib_export_t *)malloc(sizeof(dt_lib_export_t));
1105 self->timeout_handle = 0;
1106 self->data = (void *)d;
1107 self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1108 dt_gui_add_help_link(self->widget, dt_get_help_url(self->plugin_name));
1109
1110 GtkWidget *label = dt_ui_section_label_new(_("storage options"));
1111 GtkStyleContext *context = gtk_widget_get_style_context(GTK_WIDGET(label));
1112 gtk_style_context_add_class(context, "section_label_top");
1113 gtk_box_pack_start(GTK_BOX(self->widget), label, FALSE, TRUE, 0);
1114 dt_gui_add_help_link(self->widget, dt_get_help_url("export"));
1115
1116 d->storage = dt_bauhaus_combobox_new_action(DT_ACTION(self));
1117 dt_bauhaus_widget_set_label(d->storage, NULL, N_("target storage"));
1118 gtk_box_pack_start(GTK_BOX(self->widget), d->storage, FALSE, TRUE, 0);
1119
1120 // add all storage widgets to the stack widget
1121 d->storage_extra_container = gtk_stack_new();
1122 gtk_stack_set_homogeneous(GTK_STACK(d->storage_extra_container),FALSE);
1123 gtk_box_pack_start(GTK_BOX(self->widget), d->storage_extra_container, FALSE, TRUE, 0);
1124 for(const GList *it = darktable.imageio->plugins_storage; it; it = g_list_next(it))
1125 {
1126 const dt_imageio_module_storage_t *module = (dt_imageio_module_storage_t *)it->data;
1127 dt_bauhaus_combobox_add(d->storage, module->name(module));
1128 if(module->widget)
1129 {
1130 gtk_container_add(GTK_CONTAINER(d->storage_extra_container), module->widget);
1131 }
1132 }
1133
1134 // postponed so we can do the two steps in one loop
1135 DT_DEBUG_CONTROL_SIGNAL_CONNECT(darktable.signals, DT_SIGNAL_IMAGEIO_STORAGE_CHANGE,
1136 G_CALLBACK(_on_storage_list_changed), self);
1137 g_signal_connect(G_OBJECT(d->storage), "value-changed", G_CALLBACK(_storage_changed), (gpointer)d);
1138
1139 label = dt_ui_section_label_new(_("format options"));
1140 gtk_box_pack_start(GTK_BOX(self->widget), label, FALSE, TRUE, 0);
1141 dt_gui_add_help_link(self->widget, dt_get_help_url("export"));
1142
1143 d->format = dt_bauhaus_combobox_new_action(DT_ACTION(self));
1144 dt_bauhaus_widget_set_label(d->format, NULL, N_("file format"));
1145 gtk_box_pack_start(GTK_BOX(self->widget), d->format, FALSE, TRUE, 0);
1146 g_signal_connect(G_OBJECT(d->format), "value-changed", G_CALLBACK(_format_changed), (gpointer)d);
1147
1148 // add all format widgets to the stack widget
1149 d->format_extra_container = gtk_stack_new();
1150 gtk_stack_set_homogeneous(GTK_STACK(d->format_extra_container),FALSE);
1151 gtk_box_pack_start(GTK_BOX(self->widget), d->format_extra_container, FALSE, TRUE, 0);
1152 for(const GList *it = darktable.imageio->plugins_format; it; it = g_list_next(it))
1153 {
1154 const dt_imageio_module_format_t *module = (dt_imageio_module_format_t *)it->data;
1155 if(module->widget)
1156 {
1157 gtk_container_add(GTK_CONTAINER(d->format_extra_container), module->widget);
1158 }
1159 }
1160
1161 label = dt_ui_section_label_new(_("global options"));
1162 gtk_box_pack_start(GTK_BOX(self->widget), label, FALSE, TRUE, 0);
1163 dt_gui_add_help_link(self->widget, dt_get_help_url("export"));
1164
1165 DT_BAUHAUS_COMBOBOX_NEW_FULL(d->dimensions_type, self, NULL, N_("set size"),
1166 _("choose a method for setting the output size"),
1167 dt_conf_get_int(CONFIG_PREFIX "dimensions_type"),
1168 (GtkCallback)_dimensions_type_changed, d,
1169 N_("in pixels (for file)"),
1170 N_("in cm (for print)"),
1171 N_("in inch (for print)"),
1172 N_("by scale (for file)"));
1173
1174 d->print_width = gtk_entry_new();
1175 gtk_widget_set_tooltip_text(d->print_width, _("maximum output width limit.\n"
1176 "click middle mouse button to reset to 0."));
1177 gtk_entry_set_width_chars(GTK_ENTRY(d->print_width), 5);
1178 d->print_height = gtk_entry_new();
1179 gtk_widget_set_tooltip_text(d->print_height, _("maximum output height limit.\n"
1180 "click middle mouse button to reset to 0."));
1181 gtk_entry_set_width_chars(GTK_ENTRY(d->print_height), 5);
1182 d->print_dpi = gtk_entry_new();
1183 gtk_widget_set_tooltip_text(d->print_dpi, _("resolution in dot per inch"));
1184 gtk_entry_set_width_chars(GTK_ENTRY(d->print_dpi), 4);
1185 const char *dpi = dt_conf_get_string_const(CONFIG_PREFIX "print_dpi");
1186 gtk_entry_set_text(GTK_ENTRY(d->print_dpi), dpi);
1187
1188
1189 d->width = gtk_entry_new();
1190 gtk_widget_set_tooltip_text(d->width, _("maximum output width limit.\n"
1191 "click middle mouse button to reset to 0."));
1192 gtk_entry_set_width_chars(GTK_ENTRY(d->width), 5);
1193 d->height = gtk_entry_new();
1194 gtk_widget_set_tooltip_text(d->height, _("maximum output height limit.\n"
1195 "click middle mouse button to reset to 0."));
1196 gtk_entry_set_width_chars(GTK_ENTRY(d->height), 5);
1197
1198 gtk_widget_add_events(d->width, GDK_BUTTON_PRESS_MASK);
1199 gtk_widget_add_events(d->height, GDK_BUTTON_PRESS_MASK);
1200
1201
1202 d->print_size = gtk_flow_box_new();
1203 gtk_flow_box_set_max_children_per_line(GTK_FLOW_BOX(d->print_size), 5);
1204 gtk_flow_box_set_column_spacing (GTK_FLOW_BOX(d->print_size), 3);
1205 gtk_container_add(GTK_CONTAINER(d->print_size), d->print_width);
1206 gtk_container_add(GTK_CONTAINER(d->print_size), gtk_label_new(_("x")));
1207 gtk_container_add(GTK_CONTAINER(d->print_size), d->print_height);
1208 d->unit_label = gtk_label_new(_("cm"));
1209 gtk_container_add(GTK_CONTAINER(d->print_size), d->unit_label);
1210 GtkBox *dpi_box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3));
1211 gtk_box_pack_start(dpi_box, gtk_label_new(_("@")), FALSE, FALSE, 0);
1212 gtk_box_pack_start(dpi_box, d->print_dpi, TRUE, TRUE, 0);
1213 gtk_box_pack_start(dpi_box, gtk_label_new(_("dpi")), FALSE, FALSE, 0);
1214 gtk_container_add(GTK_CONTAINER(d->print_size), GTK_WIDGET(dpi_box));
1215 gtk_container_foreach(GTK_CONTAINER(d->print_size), (GtkCallback)gtk_widget_set_can_focus, GINT_TO_POINTER(FALSE));
1216
1217 d->px_size = gtk_flow_box_new();
1218 gtk_flow_box_set_max_children_per_line(GTK_FLOW_BOX(d->px_size), 3);
1219 gtk_flow_box_set_column_spacing (GTK_FLOW_BOX(d->px_size), 3);
1220 gtk_container_add(GTK_CONTAINER(d->px_size), d->width);
1221 gtk_container_add(GTK_CONTAINER(d->px_size), gtk_label_new(_("x")));
1222 GtkBox *px_box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3));
1223 gtk_box_pack_start(px_box, d->height, TRUE, TRUE, 0);
1224 gtk_box_pack_start(px_box, gtk_label_new(_("px")), FALSE, FALSE, 0);
1225 gtk_container_add(GTK_CONTAINER(d->px_size), GTK_WIDGET(px_box));
1226 gtk_container_foreach(GTK_CONTAINER(d->px_size), (GtkCallback)gtk_widget_set_can_focus, GINT_TO_POINTER(FALSE));
1227
1228 d->scale = gtk_entry_new();
1229 gtk_entry_set_width_chars(GTK_ENTRY(d->scale), 5);
1230 gtk_entry_set_text (GTK_ENTRY(d->scale), dt_conf_get_string_const(CONFIG_PREFIX "resizing_factor"));
1231 gtk_widget_set_tooltip_text(d->scale, _("it can be an integer, decimal number or simple fraction.\n"
1232 "zero or empty values are equal to 1.\n"
1233 "click middle mouse button to reset to 1."));
1234 gtk_widget_set_halign(GTK_WIDGET(d->scale), GTK_ALIGN_END);
1235 gtk_widget_add_events(d->scale, GDK_BUTTON_PRESS_MASK);
1236
1237 d->size_in_px = gtk_label_new("");
1238 gtk_label_set_ellipsize(GTK_LABEL(d->size_in_px), PANGO_ELLIPSIZE_START);
1239 gtk_widget_set_sensitive(GTK_WIDGET(d->size_in_px), FALSE);
1240
1241 gtk_widget_set_halign(GTK_WIDGET(d->scale), GTK_ALIGN_FILL);
1242 gtk_widget_set_halign(GTK_WIDGET(d->size_in_px), GTK_ALIGN_END);
1243
1244 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(d->dimensions_type), FALSE, FALSE, 0);
1245 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(d->px_size), FALSE, FALSE, 0);
1246 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(d->print_size), FALSE, FALSE, 0);
1247 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(d->scale), FALSE, FALSE, 0);
1248 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(d->size_in_px), FALSE, FALSE, 0);
1249
1250 d->upscale = dt_bauhaus_combobox_new_action(DT_ACTION(self));
1251 dt_bauhaus_widget_set_label(d->upscale, NULL, N_("allow upscaling"));
1252 dt_bauhaus_combobox_add(d->upscale, _("no"));
1253 dt_bauhaus_combobox_add(d->upscale, _("yes"));
1254 gtk_box_pack_start(GTK_BOX(self->widget), d->upscale, FALSE, TRUE, 0);
1255
1256 d->high_quality = dt_bauhaus_combobox_new_action(DT_ACTION(self));
1257 dt_bauhaus_widget_set_label(d->high_quality, NULL, N_("high quality resampling"));
1258 dt_bauhaus_combobox_add(d->high_quality, _("no"));
1259 dt_bauhaus_combobox_add(d->high_quality, _("yes"));
1260 gtk_widget_set_tooltip_text(d->high_quality, _("do high quality resampling during export"));
1261 gtk_box_pack_start(GTK_BOX(self->widget), d->high_quality, FALSE, TRUE, 0);
1262
1263 d->export_masks = dt_bauhaus_combobox_new_action(DT_ACTION(self));
1264 dt_bauhaus_widget_set_label(d->export_masks, NULL, N_("store masks"));
1265 dt_bauhaus_combobox_add(d->export_masks, _("no"));
1266 dt_bauhaus_combobox_add(d->export_masks, _("yes"));
1267 gtk_widget_set_tooltip_text(d->export_masks, _("store masks as layers in exported images. only works for some formats."));
1268 gtk_box_pack_start(GTK_BOX(self->widget), d->export_masks, FALSE, TRUE, 0);
1269
1270 // Add profile combo
1271
1272 char datadir[PATH_MAX] = { 0 };
1273 char confdir[PATH_MAX] = { 0 };
1274 dt_loc_get_user_config_dir(confdir, sizeof(confdir));
1275 dt_loc_get_datadir(datadir, sizeof(datadir));
1276
1277 d->profile = dt_bauhaus_combobox_new_action(DT_ACTION(self));
1278 dt_bauhaus_widget_set_label(d->profile, NULL, N_("profile"));
1279 gtk_box_pack_start(GTK_BOX(self->widget), d->profile, FALSE, TRUE, 0);
1280 dt_bauhaus_combobox_add(d->profile, _("image settings"));
1281 for(GList *l = darktable.color_profiles->profiles; l; l = g_list_next(l))
1282 {
1283 const dt_colorspaces_color_profile_t *prof = (dt_colorspaces_color_profile_t *)l->data;
1284 if(prof->out_pos > -1)
1285 dt_bauhaus_combobox_add(d->profile, prof->name);
1286 }
1287
1288 dt_bauhaus_combobox_set(d->profile, 0);
1289
1290 char *system_profile_dir = g_build_filename(datadir, "color", "out", NULL);
1291 char *user_profile_dir = g_build_filename(confdir, "color", "out", NULL);
1292 char *tooltip = g_strdup_printf(_("output ICC profiles in %s or %s"), user_profile_dir, system_profile_dir);
1293 gtk_widget_set_tooltip_text(d->profile, tooltip);
1294 g_free(system_profile_dir);
1295 g_free(user_profile_dir);
1296 g_free(tooltip);
1297
1298 // Add intent combo
1299
1300 d->intent = dt_bauhaus_combobox_new_action(DT_ACTION(self));
1301 dt_bauhaus_widget_set_label(d->intent, NULL, N_("intent"));
1302 dt_bauhaus_combobox_add(d->intent, _("image settings"));
1303 dt_bauhaus_combobox_add(d->intent, _("perceptual"));
1304 dt_bauhaus_combobox_add(d->intent, _("relative colorimetric"));
1305 dt_bauhaus_combobox_add(d->intent, C_("rendering intent", "saturation"));
1306 dt_bauhaus_combobox_add(d->intent, _("absolute colorimetric"));
1307 gtk_box_pack_start(GTK_BOX(self->widget), d->intent, FALSE, TRUE, 0);
1308
1309 tooltip = g_strdup_printf(_("• perceptual : "
1310 "smoothly moves out-of-gamut colors into gamut,"
1311 "preserving gradations, but distorts in-gamut colors in the process."
1312 " note that perceptual is often a proprietary LUT that depends"
1313 " on the destination space."
1314 "\n\n"
1315
1316 "• relative colorimetric : "
1317 "keeps luminance while reducing as little as possible saturation"
1318 " until colors fit in gamut."
1319 "\n\n"
1320
1321 "• saturation : "
1322 "designed to present eye-catching business graphics"
1323 " by preserving the saturation. (not suited for photography)."
1324 "\n\n"
1325
1326 "• absolute colorimetric : "
1327 "adapt white point of the image to the white point of the"
1328 " destination medium and do nothing else. mainly used when"
1329 " proofing colors. (not suited for photography)."
1330 ""
1331 ));
1332 gtk_widget_set_tooltip_text(d->intent, tooltip);
1333 g_free(tooltip);
1334
1335 // Add style combo
1336
1337 d->style = dt_bauhaus_combobox_new_action(DT_ACTION(self));
1338 dt_bauhaus_widget_set_label(d->style, NULL, N_("style"));
1339 _lib_export_styles_changed_callback(NULL, self);
1340 gtk_box_pack_start(GTK_BOX(self->widget), d->style, FALSE, TRUE, 0);
1341 gtk_widget_set_tooltip_text(d->style, _("temporary style to use while exporting"));
1342
1343 // Add check to control whether the style is to replace or append the current module
1344
1345 d->style_mode = dt_bauhaus_combobox_new_action(DT_ACTION(self));
1346 dt_bauhaus_widget_set_label(d->style_mode, NULL, N_("mode"));
1347
1348 gtk_box_pack_start(GTK_BOX(self->widget), d->style_mode, FALSE, TRUE, 0);
1349
1350 dt_bauhaus_combobox_add(d->style_mode, _("replace history"));
1351 dt_bauhaus_combobox_add(d->style_mode, _("append history"));
1352
1353 dt_bauhaus_combobox_set(d->style_mode, 0);
1354
1355 gtk_widget_set_tooltip_text(d->style_mode,
1356 _("whether the style items are appended to the history or replacing the history"));
1357
1358 // Set callback signals
1359
1360 g_signal_connect(G_OBJECT(d->upscale), "value-changed", G_CALLBACK(_callback_bool),
1361 (gpointer)CONFIG_PREFIX "upscale");
1362 g_signal_connect(G_OBJECT(d->high_quality), "value-changed", G_CALLBACK(_callback_bool),
1363 (gpointer)CONFIG_PREFIX "high_quality_processing");
1364 g_signal_connect(G_OBJECT(d->export_masks), "value-changed", G_CALLBACK(_callback_bool),
1365 (gpointer)CONFIG_PREFIX "export_masks");
1366 g_signal_connect(G_OBJECT(d->intent), "value-changed", G_CALLBACK(_intent_changed), (gpointer)d);
1367 g_signal_connect(G_OBJECT(d->profile), "value-changed", G_CALLBACK(_profile_changed), (gpointer)d);
1368 g_signal_connect(G_OBJECT(d->style), "value-changed", G_CALLBACK(_style_changed), (gpointer)d);
1369 g_signal_connect(G_OBJECT(d->style_mode), "value-changed", G_CALLBACK(_callback_bool),
1370 (gpointer)CONFIG_PREFIX "style_append");
1371
1372 DT_DEBUG_CONTROL_SIGNAL_CONNECT(darktable.signals, DT_SIGNAL_STYLE_CHANGED,
1373 G_CALLBACK(_lib_export_styles_changed_callback), self);
1374
1375 GtkBox *hbox = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0));
1376 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(hbox), FALSE, TRUE, 0);
1377
1378 // Export button
1379 d->export_button = GTK_BUTTON(dt_ui_button_new(_("export"), _("export with current settings"), NULL));
1380 gtk_box_pack_start(hbox, GTK_WIDGET(d->export_button), TRUE, TRUE, 0);
1381
1382 g_signal_connect(G_OBJECT(d->export_button), "clicked", G_CALLBACK(_export_button_clicked), (gpointer)d);
1383 g_signal_connect(G_OBJECT(d->width), "changed", G_CALLBACK(_width_changed), (gpointer)d);
1384 g_signal_connect(G_OBJECT(d->height), "changed", G_CALLBACK(_height_changed), (gpointer)d);
1385 g_signal_connect(G_OBJECT(d->print_width), "changed", G_CALLBACK(_print_width_changed), (gpointer)d);
1386 g_signal_connect(G_OBJECT(d->print_height), "changed", G_CALLBACK(_print_height_changed), (gpointer)d);
1387 g_signal_connect(G_OBJECT(d->print_dpi), "changed", G_CALLBACK(_print_dpi_changed), (gpointer)d);
1388
1389 g_signal_connect(G_OBJECT(d->width), "changed", G_CALLBACK(_width_changed), (gpointer)d);
1390 g_signal_connect(G_OBJECT(d->height), "changed", G_CALLBACK(_height_changed), (gpointer)d);
1391
1392 g_signal_connect(G_OBJECT(d->width), "button-press-event", G_CALLBACK(_widht_mdlclick), (gpointer)d);
1393 g_signal_connect(G_OBJECT(d->height), "button-press-event", G_CALLBACK(_height_mdlclick), (gpointer)d);
1394 g_signal_connect(G_OBJECT(d->print_width), "button-press-event", G_CALLBACK(_widht_mdlclick), (gpointer)d);
1395 g_signal_connect(G_OBJECT(d->print_height), "button-press-event", G_CALLBACK(_height_mdlclick), (gpointer)d);
1396
1397 g_signal_connect(G_OBJECT(d->scale), "button-press-event", G_CALLBACK(_scale_mdlclick), (gpointer)d);
1398 g_signal_connect(G_OBJECT(d->scale), "changed", G_CALLBACK(_scale_changed), (gpointer)d);
1399
1400 // this takes care of keeping hidden widgets hidden
1401 gtk_widget_show_all(self->widget);
1402 gtk_widget_set_no_show_all(self->widget, TRUE);
1403
1404 const char* setting = dt_conf_get_string_const(CONFIG_PREFIX "width");
1405 gtk_entry_set_text(GTK_ENTRY(d->width), setting);
1406 setting = dt_conf_get_string_const(CONFIG_PREFIX "height");
1407 gtk_entry_set_text(GTK_ENTRY(d->height), setting);
1408 dt_bauhaus_combobox_set(d->dimensions_type, dt_conf_get_int(CONFIG_PREFIX "dimensions_type"));
1409
1410 const gboolean is_scaling = dt_conf_is_equal(CONFIG_PREFIX "resizing", "scaling");
1411 if (is_scaling)
1412 {
1413 // scaling
1414 gtk_widget_show(GTK_WIDGET(d->scale));
1415 gtk_widget_hide(GTK_WIDGET(d->px_size));
1416 gtk_widget_hide(GTK_WIDGET(d->print_size));
1417 }
1418 else
1419 {
1420 // max size
1421 gtk_widget_hide(GTK_WIDGET(d->scale));
1422 gtk_widget_show(GTK_WIDGET(d->px_size));
1423 gtk_widget_show(GTK_WIDGET(d->print_size));
1424 }
1425
1426 _print_size_update_display(d);
1427
1428 // Set storage
1429 setting = dt_conf_get_string_const(CONFIG_PREFIX "storage_name");
1430 const int storage_index = dt_imageio_get_index_of_storage(dt_imageio_get_storage_by_name(setting));
1431 dt_bauhaus_combobox_set(d->storage, storage_index);
1432
1433 dt_bauhaus_combobox_set(d->upscale, dt_conf_get_bool(CONFIG_PREFIX "upscale") ? 1 : 0);
1434 dt_bauhaus_combobox_set(d->high_quality, dt_conf_get_bool(CONFIG_PREFIX "high_quality_processing") ? 1 : 0);
1435 dt_bauhaus_combobox_set(d->export_masks, dt_conf_get_bool(CONFIG_PREFIX "export_masks") ? 1 : 0);
1436
1437 dt_bauhaus_combobox_set(d->intent, dt_conf_get_int(CONFIG_PREFIX "iccintent") + 1);
1438
1439 // iccprofile
1440 const int icctype = dt_conf_get_int(CONFIG_PREFIX "icctype");
1441 gchar *iccfilename = dt_conf_get_string(CONFIG_PREFIX "iccprofile");
1442 dt_bauhaus_combobox_set(d->profile, 0);
1443 if(icctype != DT_COLORSPACE_NONE)
1444 {
1445 for(GList *profiles = darktable.color_profiles->profiles; profiles; profiles = g_list_next(profiles))
1446 {
1447 const dt_colorspaces_color_profile_t *pp = (dt_colorspaces_color_profile_t *)profiles->data;
1448 if(pp->out_pos > -1
1449 && icctype == pp->type
1450 && (icctype != DT_COLORSPACE_FILE || !strcmp(iccfilename, pp->filename)))
1451 {
1452 dt_bauhaus_combobox_set(d->profile, pp->out_pos + 1);
1453 break;
1454 }
1455 }
1456 }
1457
1458 g_free(iccfilename);
1459
1460 // style
1461 // set it to none if the var is not set or the style doesn't exist anymore
1462 gboolean rc = FALSE;
1463 setting = dt_conf_get_string_const(CONFIG_PREFIX "style");
1464 if(setting != NULL && strlen(setting) > 0)
1465 {
1466 rc = dt_bauhaus_combobox_set_from_text(d->style, setting);
1467 if(rc == FALSE)
1468 dt_bauhaus_combobox_set(d->style, 0);
1469 }
1470 else
1471 dt_bauhaus_combobox_set(d->style, 0);
1472
1473 // style mode to overwrite as it was the initial behavior
1474 dt_bauhaus_combobox_set(d->style_mode, dt_conf_get_bool(CONFIG_PREFIX "style_append"));
1475
1476 gtk_widget_set_sensitive(GTK_WIDGET(d->style_mode), dt_bauhaus_combobox_get(d->style)==0?FALSE:TRUE);
1477
1478 // export metadata presets
1479 d->metadata_export = dt_lib_export_metadata_get_conf();
1480
1481 DT_DEBUG_CONTROL_SIGNAL_CONNECT(darktable.signals, DT_SIGNAL_SELECTION_CHANGED,
1482 G_CALLBACK(_image_selection_changed_callback), self);
1483 DT_DEBUG_CONTROL_SIGNAL_CONNECT(darktable.signals, DT_SIGNAL_MOUSE_OVER_IMAGE_CHANGE,
1484 G_CALLBACK(_mouse_over_image_callback), self);
1485 DT_DEBUG_CONTROL_SIGNAL_CONNECT(darktable.signals, DT_SIGNAL_COLLECTION_CHANGED,
1486 G_CALLBACK(_collection_updated_callback), self);
1487 }
1488
gui_cleanup(dt_lib_module_t * self)1489 void gui_cleanup(dt_lib_module_t *self)
1490 {
1491 dt_lib_cancel_postponed_update(self);
1492 dt_lib_export_t *d = (dt_lib_export_t *)self->data;
1493
1494 DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(darktable.signals, G_CALLBACK(_on_storage_list_changed), self);
1495 DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(darktable.signals, G_CALLBACK(_lib_export_styles_changed_callback), self);
1496
1497 DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(darktable.signals, G_CALLBACK(_image_selection_changed_callback), self);
1498 DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(darktable.signals, G_CALLBACK(_mouse_over_image_callback), self);
1499 DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(darktable.signals, G_CALLBACK(_collection_updated_callback), self);
1500
1501 for(const GList *it = darktable.imageio->plugins_storage; it; it = g_list_next(it))
1502 {
1503 dt_imageio_module_storage_t *module = (dt_imageio_module_storage_t *)it->data;
1504 if(module->widget) gtk_container_remove(GTK_CONTAINER(d->storage_extra_container), module->widget);
1505 }
1506
1507 for(const GList *it = darktable.imageio->plugins_format; it; it = g_list_next(it))
1508 {
1509 dt_imageio_module_format_t *module = (dt_imageio_module_format_t *)it->data;
1510 if(module->widget) gtk_container_remove(GTK_CONTAINER(d->format_extra_container), module->widget);
1511 }
1512
1513 g_free(d->metadata_export);
1514
1515 free(self->data);
1516 self->data = NULL;
1517 }
1518
init_presets(dt_lib_module_t * self)1519 void init_presets(dt_lib_module_t *self)
1520 {
1521 // TODO: store presets in db:
1522 // dt_lib_presets_add(const char *name, const char *plugin_name, const void *params, const int32_t
1523 // params_size)
1524
1525
1526 // I know that it is super ugly to have this inside a module, but then is export not your average module
1527 // since it handles the params blobs of imageio libs.
1528 // - get all existing presets for export from db,
1529 // - extract the versions of the embedded format/storage blob
1530 // - check if it's up to date
1531 // - if older than the module -> call its legacy_params and update the preset
1532 // - drop presets that cannot be updated
1533
1534 const int version = self->version();
1535
1536 sqlite3_stmt *stmt;
1537 DT_DEBUG_SQLITE3_PREPARE_V2(
1538 dt_database_get(darktable.db),
1539 "SELECT rowid, op_version, op_params, name FROM data.presets WHERE operation='export'", -1, &stmt, NULL);
1540 while(sqlite3_step(stmt) == SQLITE_ROW)
1541 {
1542 const int rowid = sqlite3_column_int(stmt, 0);
1543 const int op_version = sqlite3_column_int(stmt, 1);
1544 const void *op_params = (void *)sqlite3_column_blob(stmt, 2);
1545 const size_t op_params_size = sqlite3_column_bytes(stmt, 2);
1546 const char *name = (char *)sqlite3_column_text(stmt, 3);
1547
1548 if(op_version != version)
1549 {
1550 // shouldn't happen, we run legacy_params on the lib level before calling this
1551 fprintf(stderr, "[export_init_presets] found export preset '%s' with version %d, version %d was "
1552 "expected. dropping preset.\n",
1553 name, op_version, version);
1554 sqlite3_stmt *innerstmt;
1555 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
1556 "DELETE FROM data.presets WHERE rowid=?1", -1,
1557 &innerstmt, NULL);
1558 DT_DEBUG_SQLITE3_BIND_INT(innerstmt, 1, rowid);
1559 sqlite3_step(innerstmt);
1560 sqlite3_finalize(innerstmt);
1561 }
1562 else
1563 {
1564 // extract the interesting parts from the blob
1565 const char *buf = (const char *)op_params;
1566
1567 // skip 6*int32_t: max_width, max_height, upscale, high_quality and iccintent, icctype
1568 buf += 6 * sizeof(int32_t);
1569 // skip metadata presets string
1570 buf += strlen(buf) + 1;
1571 // next skip iccfilename
1572 buf += strlen(buf) + 1;
1573
1574 // parse both names to '\0'
1575 const char *fname = buf;
1576 buf += strlen(fname) + 1;
1577 const char *sname = buf;
1578 buf += strlen(sname) + 1;
1579
1580 // get module by name and skip if not there.
1581 dt_imageio_module_format_t *fmod = dt_imageio_get_format_by_name(fname);
1582 dt_imageio_module_storage_t *smod = dt_imageio_get_storage_by_name(sname);
1583 if(!fmod || !smod) continue;
1584
1585 // next we have fversion, sversion, fsize, ssize, fdata, sdata which is the stuff that might change
1586 size_t copy_over_part = (void *)buf - (void *)op_params;
1587
1588 const int fversion = *(const int *)buf;
1589 buf += sizeof(int32_t);
1590 const int sversion = *(const int *)buf;
1591 buf += sizeof(int32_t);
1592 const int fsize = *(const int *)buf;
1593 buf += sizeof(int32_t);
1594 const int ssize = *(const int *)buf;
1595 buf += sizeof(int32_t);
1596
1597 const void *fdata = buf;
1598 buf += fsize;
1599 const void *sdata = buf;
1600
1601 void *new_fdata = NULL, *new_sdata = NULL;
1602 size_t new_fsize = fsize, new_ssize = ssize;
1603 const int32_t new_fversion = fmod->version(), new_sversion = smod->version();
1604
1605 if(fversion < new_fversion)
1606 {
1607 if(!(fmod->legacy_params
1608 && (new_fdata = fmod->legacy_params(fmod, fdata, fsize, fversion, new_fversion, &new_fsize))
1609 != NULL))
1610 goto delete_preset;
1611 }
1612
1613 if(sversion < new_sversion)
1614 {
1615 if(!(smod->legacy_params
1616 && (new_sdata = smod->legacy_params(smod, sdata, ssize, sversion, new_sversion, &new_ssize))
1617 != NULL))
1618 goto delete_preset;
1619 }
1620
1621 if(new_fdata || new_sdata)
1622 {
1623 // we got an updated blob -> reassemble the parts and update the preset
1624 const size_t new_params_size = op_params_size - (fsize + ssize) + (new_fsize + new_ssize);
1625 void *new_params = malloc(new_params_size);
1626 memcpy(new_params, op_params, copy_over_part);
1627 // next we have fversion, sversion, fsize, ssize, fdata, sdata which is the stuff that might change
1628 size_t pos = copy_over_part;
1629 memcpy(new_params + pos, &new_fversion, sizeof(int32_t));
1630 pos += sizeof(int32_t);
1631 memcpy(new_params + pos, &new_sversion, sizeof(int32_t));
1632 pos += sizeof(int32_t);
1633 memcpy(new_params + pos, &new_fsize, sizeof(int32_t));
1634 pos += sizeof(int32_t);
1635 memcpy(new_params + pos, &new_ssize, sizeof(int32_t));
1636 pos += sizeof(int32_t);
1637 if(new_fdata)
1638 memcpy(new_params + pos, new_fdata, new_fsize);
1639 else
1640 memcpy(new_params + pos, fdata, fsize);
1641 pos += new_fsize;
1642 if(new_sdata)
1643 memcpy(new_params + pos, new_sdata, new_ssize);
1644 else
1645 memcpy(new_params + pos, sdata, ssize);
1646
1647 // write the updated preset back to db
1648 fprintf(stderr,
1649 "[export_init_presets] updating export preset '%s' from versions %d/%d to versions %d/%d\n",
1650 name, fversion, sversion, new_fversion, new_sversion);
1651 sqlite3_stmt *innerstmt;
1652 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
1653 "UPDATE data.presets SET op_params=?1 WHERE rowid=?2",
1654 -1, &innerstmt, NULL);
1655 DT_DEBUG_SQLITE3_BIND_BLOB(innerstmt, 1, new_params, new_params_size, SQLITE_TRANSIENT);
1656 DT_DEBUG_SQLITE3_BIND_INT(innerstmt, 2, rowid);
1657 sqlite3_step(innerstmt);
1658 sqlite3_finalize(innerstmt);
1659
1660 free(new_fdata);
1661 free(new_sdata);
1662 free(new_params);
1663 }
1664
1665 continue;
1666
1667 delete_preset:
1668 free(new_fdata);
1669 free(new_sdata);
1670 fprintf(stderr, "[export_init_presets] export preset '%s' can't be updated from versions %d/%d to "
1671 "versions %d/%d. dropping preset\n",
1672 name, fversion, sversion, new_fversion, new_sversion);
1673 sqlite3_stmt *innerstmt;
1674 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
1675 "DELETE FROM data.presets WHERE rowid=?1", -1,
1676 &innerstmt, NULL);
1677 DT_DEBUG_SQLITE3_BIND_INT(innerstmt, 1, rowid);
1678 sqlite3_step(innerstmt);
1679 sqlite3_finalize(innerstmt);
1680 }
1681 }
1682 sqlite3_finalize(stmt);
1683 }
1684
legacy_params(dt_lib_module_t * self,const void * const old_params,const size_t old_params_size,const int old_version,int * new_version,size_t * new_size)1685 void *legacy_params(dt_lib_module_t *self, const void *const old_params, const size_t old_params_size,
1686 const int old_version, int *new_version, size_t *new_size)
1687 {
1688 if(old_version == 1)
1689 {
1690 // add version of format & storage to params
1691 const size_t new_params_size = old_params_size + 2 * sizeof(int32_t);
1692 void *new_params = malloc(new_params_size);
1693
1694 const char *buf = (const char *)old_params;
1695
1696 // skip 3*int32_t: max_width, max_height and iccintent
1697 buf += 3 * sizeof(int32_t);
1698 // next skip iccprofile
1699 buf += strlen(buf) + 1;
1700
1701 // parse both names to '\0'
1702 const char *fname = buf;
1703 buf += strlen(fname) + 1;
1704 const char *sname = buf;
1705 buf += strlen(sname) + 1;
1706
1707 // get module by name and fail if not there.
1708 dt_imageio_module_format_t *fmod = dt_imageio_get_format_by_name(fname);
1709 dt_imageio_module_storage_t *smod = dt_imageio_get_storage_by_name(sname);
1710 if(!fmod || !smod)
1711 {
1712 free(new_params);
1713 return NULL;
1714 }
1715
1716 // now we are just behind the module/storage names and before their param sizes. this is the place where
1717 // we want their versions
1718 // copy everything until here to the new params
1719 size_t first_half = (void *)buf - (void *)old_params;
1720 memcpy(new_params, old_params, first_half);
1721 // add the versions. at the time this code was added all modules were at version 1, except of picasa which was at 2.
1722 // every newer version of the imageio modules should result in a preset that is not going through this code.
1723 int32_t fversion = 1;
1724 int32_t sversion = (strcmp(sname, "picasa") == 0 ? 2 : 1);
1725 memcpy(new_params + first_half, &fversion, sizeof(int32_t));
1726 memcpy(new_params + first_half + sizeof(int32_t), &sversion, sizeof(int32_t));
1727 // copy the rest of the old params over
1728 memcpy(new_params + first_half + sizeof(int32_t) * 2, buf, old_params_size - first_half);
1729
1730 *new_size = new_params_size;
1731 *new_version = 2;
1732 return new_params;
1733 }
1734 else if(old_version == 2)
1735 {
1736 // add upscale to params
1737 const size_t new_params_size = old_params_size + sizeof(int32_t);
1738 void *new_params = calloc(1, new_params_size);
1739
1740 memcpy(new_params, old_params, sizeof(int32_t) * 2);
1741 memcpy(new_params + sizeof(int32_t) * 3, old_params + sizeof(int32_t) * 2, old_params_size - sizeof(int32_t) * 2);
1742
1743 *new_size = new_params_size;
1744 *new_version = 3;
1745 return new_params;
1746 }
1747 else if(old_version == 3)
1748 {
1749 // replace iccprofile by type + filename
1750 // format of v3:
1751 // - 4 x int32_t (max_width, max_height, upscale, iccintent)
1752 // - char* (iccprofile)
1753 // - rest
1754 // format of v4:
1755 // - 5 x int32_t (max_width, max_height, upscale, iccintent, icctype)
1756 // - char* (iccfilename)
1757 // - old rest
1758
1759 const char *buf = (const char *)old_params;
1760
1761 // first get the old iccprofile to find out how big our new blob has to be
1762 const char *iccprofile = buf + 4 * sizeof(int32_t);
1763
1764 size_t new_params_size = old_params_size - strlen(iccprofile) + sizeof(int32_t);
1765 int icctype;
1766 const char *iccfilename = "";
1767
1768 if(!strcmp(iccprofile, "image"))
1769 icctype = DT_COLORSPACE_NONE;
1770 else if(!strcmp(iccprofile, "sRGB"))
1771 icctype = DT_COLORSPACE_SRGB;
1772 else if(!strcmp(iccprofile, "linear_rec709_rgb") || !strcmp(iccprofile, "linear_rgb"))
1773 icctype = DT_COLORSPACE_LIN_REC709;
1774 else if(!strcmp(iccprofile, "linear_rec2020_rgb"))
1775 icctype = DT_COLORSPACE_LIN_REC2020;
1776 else if(!strcmp(iccprofile, "adobergb"))
1777 icctype = DT_COLORSPACE_ADOBERGB;
1778 else
1779 {
1780 icctype = DT_COLORSPACE_FILE;
1781 iccfilename = iccprofile;
1782 new_params_size += strlen(iccfilename);
1783 }
1784
1785 void *new_params = calloc(1, new_params_size);
1786 size_t pos = 0;
1787 memcpy(new_params, old_params, sizeof(int32_t) * 4);
1788 pos += 4 * sizeof(int32_t);
1789 memcpy(new_params + pos, &icctype, sizeof(int32_t));
1790 pos += sizeof(int32_t);
1791 memcpy(new_params + pos, iccfilename, strlen(iccfilename) + 1);
1792 pos += strlen(iccfilename) + 1;
1793 size_t old_pos = 4 * sizeof(int32_t) + strlen(iccprofile) + 1;
1794 memcpy(new_params + pos, old_params + old_pos, old_params_size - old_pos);
1795
1796 *new_size = new_params_size;
1797 *new_version = 4;
1798 return new_params;
1799 }
1800 else if(old_version == 4)
1801 {
1802 // add high_quality to params
1803
1804 // format of v4:
1805 // - 5 x int32_t (max_width, max_height, upscale, iccintent, icctype)
1806 // - char* (iccfilename)
1807 // - old rest
1808 // format of v5:
1809 // - 6 x int32_t (max_width, max_height, upscale, high_quality, iccintent, icctype)
1810 // - char* (iccfilename)
1811 // - old rest
1812
1813 const size_t new_params_size = old_params_size + sizeof(int32_t);
1814 void *new_params = calloc(1, new_params_size);
1815
1816 size_t pos = 0;
1817 memcpy(new_params, old_params, sizeof(int32_t) * 3);
1818 pos += 4 * sizeof(int32_t);
1819 memcpy(new_params + pos, old_params + pos - sizeof(int32_t), old_params_size - sizeof(int32_t) * 3);
1820
1821 *new_size = new_params_size;
1822 *new_version = 5;
1823 return new_params;
1824 }
1825 else if(old_version == 5)
1826 {
1827 // add metadata preset string
1828
1829 // format of v5:
1830 // - 6 x int32_t (max_width, max_height, upscale, high_quality, iccintent, icctype)
1831 // - char* (iccfilename)
1832 // - old rest
1833 // format of v6:
1834 // - 6 x int32_t (max_width, max_height, upscale, high_quality, iccintent, icctype)
1835 // - char* (metadata_export)
1836 // - char* (iccfilename)
1837 // - old rest
1838
1839 const gboolean omit = dt_conf_get_bool("omit_tag_hierarchy");
1840 gchar *flags = g_strdup_printf("%x", dt_lib_export_metadata_default_flags() | (omit ? DT_META_OMIT_HIERARCHY : 0));
1841 const int flags_size = strlen(flags) + 1;
1842 const size_t new_params_size = old_params_size + flags_size;
1843 void *new_params = calloc(1, new_params_size);
1844 size_t pos = 0;
1845 memcpy(new_params, old_params, sizeof(int32_t) * 6);
1846 pos += 6 * sizeof(int32_t);
1847 memcpy(new_params + pos, flags, flags_size);
1848 pos += flags_size;
1849 memcpy(new_params + pos, old_params + pos - flags_size, old_params_size - sizeof(int32_t) * 6);
1850
1851 g_free(flags);
1852 *new_size = new_params_size;
1853 *new_version = 6;
1854 return new_params;
1855 }
1856 else if(old_version == 6)
1857 {
1858 // add export_masks
1859
1860 // format of v6:
1861 // - 6 x int32_t (max_width, max_height, upscale, high_quality, iccintent, icctype)
1862 // - old rest
1863 // format of v7:
1864 // - 7 x int32_t (max_width, max_height, upscale, high_quality, export_masks, iccintent, icctype)
1865 // - old rest
1866
1867 const size_t new_params_size = old_params_size + sizeof(int32_t);
1868 void *new_params = calloc(1, new_params_size);
1869
1870 size_t pos = 0;
1871 memcpy(new_params, old_params, sizeof(int32_t) * 4);
1872 pos += 5 * sizeof(int32_t);
1873 memcpy(new_params + pos, old_params + pos - sizeof(int32_t), old_params_size - sizeof(int32_t) * 4);
1874
1875 *new_size = new_params_size;
1876 *new_version = 7;
1877 return new_params;
1878 }
1879
1880 return NULL;
1881 }
1882
get_params(dt_lib_module_t * self,int * size)1883 void *get_params(dt_lib_module_t *self, int *size)
1884 {
1885 dt_lib_export_t *d = (dt_lib_export_t *)self->data;
1886 // concat storage and format, size is max + header
1887 dt_imageio_module_format_t *mformat = dt_imageio_get_format();
1888 dt_imageio_module_storage_t *mstorage = dt_imageio_get_storage();
1889 if(!mformat || !mstorage) return NULL;
1890
1891 // size will be only as large as needed to remove random pointers from params (stored at the end).
1892 size_t fsize = mformat->params_size(mformat);
1893 dt_imageio_module_data_t *fdata = mformat->get_params(mformat);
1894 size_t ssize = mstorage->params_size(mstorage);
1895 void *sdata = mstorage->get_params(mstorage);
1896 const int32_t fversion = mformat->version();
1897 const int32_t sversion = mstorage->version();
1898 // we allow null pointers (plugin not ready for export in current state), and just don't copy back the
1899 // settings later:
1900 if(!sdata) ssize = 0;
1901 if(!fdata) fsize = 0;
1902 if(fdata)
1903 {
1904 // clean up format global params (need to set all bytes to reliably detect which preset is active).
1905 // we happen to want to set it all to 0
1906 memset(fdata, 0, sizeof(dt_imageio_module_data_t));
1907 }
1908
1909 // FIXME: also the web preset has to be applied twice to be known as preset! (other dimension magic going on
1910 // here?)
1911 // TODO: get this stuff from gui and not from conf, so it will be sanity-checked (you can never delete an
1912 // insane preset)?
1913 // also store icc profile/intent here.
1914 const int32_t iccintent = dt_conf_get_int(CONFIG_PREFIX "iccintent");
1915 const int32_t icctype = dt_conf_get_int(CONFIG_PREFIX "icctype");
1916 const int32_t max_width = dt_conf_get_int(CONFIG_PREFIX "width");
1917 const int32_t max_height = dt_conf_get_int(CONFIG_PREFIX "height");
1918 const int32_t upscale = dt_conf_get_bool(CONFIG_PREFIX "upscale") ? 1 : 0;
1919 const int32_t high_quality = dt_conf_get_bool(CONFIG_PREFIX "high_quality_processing") ? 1 : 0;
1920 const int32_t export_masks = dt_conf_get_bool(CONFIG_PREFIX "export_masks") ? 1 : 0;
1921 gchar *iccfilename = dt_conf_get_string(CONFIG_PREFIX "iccprofile");
1922 gchar *style = dt_conf_get_string(CONFIG_PREFIX "style");
1923 const gboolean style_append = dt_conf_get_bool(CONFIG_PREFIX "style_append");
1924 const char *metadata_export = d->metadata_export;
1925
1926 if(fdata)
1927 {
1928 g_strlcpy(fdata->style, style, sizeof(fdata->style));
1929 fdata->style_append = style_append;
1930 }
1931
1932 if(icctype != DT_COLORSPACE_FILE)
1933 {
1934 g_free(iccfilename);
1935 iccfilename = NULL;
1936 }
1937
1938 if(!iccfilename) iccfilename = g_strdup("");
1939 if(!metadata_export) metadata_export = g_strdup("");
1940
1941 const char *fname = mformat->plugin_name;
1942 const char *sname = mstorage->plugin_name;
1943 const int32_t fname_len = strlen(fname);
1944 const int32_t sname_len = strlen(sname);
1945
1946 *size = fname_len + sname_len + 2 + 4 * sizeof(int32_t) + fsize + ssize + 7 * sizeof(int32_t)
1947 + strlen(iccfilename) + 1 + strlen(metadata_export) + 1;
1948
1949 char *params = (char *)calloc(1, *size);
1950 int pos = 0;
1951 memcpy(params + pos, &max_width, sizeof(int32_t));
1952 pos += sizeof(int32_t);
1953 memcpy(params + pos, &max_height, sizeof(int32_t));
1954 pos += sizeof(int32_t);
1955 memcpy(params + pos, &upscale, sizeof(int32_t));
1956 pos += sizeof(int32_t);
1957 memcpy(params + pos, &high_quality, sizeof(int32_t));
1958 pos += sizeof(int32_t);
1959 memcpy(params + pos, &export_masks, sizeof(int32_t));
1960 pos += sizeof(int32_t);
1961 memcpy(params + pos, &iccintent, sizeof(int32_t));
1962 pos += sizeof(int32_t);
1963 memcpy(params + pos, &icctype, sizeof(int32_t));
1964 pos += sizeof(int32_t);
1965 memcpy(params + pos, metadata_export, strlen(metadata_export) + 1);
1966 pos += strlen(metadata_export) + 1;
1967 memcpy(params + pos, iccfilename, strlen(iccfilename) + 1);
1968 pos += strlen(iccfilename) + 1;
1969 memcpy(params + pos, fname, fname_len + 1);
1970 pos += fname_len + 1;
1971 memcpy(params + pos, sname, sname_len + 1);
1972 pos += sname_len + 1;
1973 memcpy(params + pos, &fversion, sizeof(int32_t));
1974 pos += sizeof(int32_t);
1975 memcpy(params + pos, &sversion, sizeof(int32_t));
1976 pos += sizeof(int32_t);
1977 memcpy(params + pos, &fsize, sizeof(int32_t));
1978 pos += sizeof(int32_t);
1979 memcpy(params + pos, &ssize, sizeof(int32_t));
1980 pos += sizeof(int32_t);
1981 if(fdata != NULL) // otherwise fsize == 0, but clang doesn't like it ...
1982 {
1983 memcpy(params + pos, fdata, fsize);
1984 pos += fsize;
1985 }
1986 if(sdata != NULL) // see above
1987 {
1988 memcpy(params + pos, sdata, ssize);
1989 pos += ssize;
1990 }
1991 g_assert(pos == *size);
1992
1993 g_free(iccfilename);
1994 g_free(style);
1995
1996 if(fdata) mformat->free_params(mformat, fdata);
1997 if(sdata) mstorage->free_params(mstorage, sdata);
1998 return params;
1999 }
2000
set_params(dt_lib_module_t * self,const void * params,int size)2001 int set_params(dt_lib_module_t *self, const void *params, int size)
2002 {
2003 dt_lib_export_t *d = (dt_lib_export_t *)self->data;
2004 // apply these stored presets again (parse blob)
2005 const char *buf = (const char *)params;
2006
2007 const int max_width = *(const int *)buf;
2008 buf += sizeof(int32_t);
2009 const int max_height = *(const int *)buf;
2010 buf += sizeof(int32_t);
2011 const int upscale = *(const int *)buf;
2012 buf += sizeof(int32_t);
2013 const int high_quality = *(const int *)buf;
2014 buf += sizeof(int32_t);
2015 const int export_masks = *(const int *)buf;
2016 buf += sizeof(int32_t);
2017 const int iccintent = *(const int *)buf;
2018 buf += sizeof(int32_t);
2019 const int icctype = *(const int *)buf;
2020 buf += sizeof(int32_t);
2021 const char *metadata_export = buf;
2022 buf += strlen(metadata_export) + 1;
2023 g_free(d->metadata_export);
2024 d->metadata_export = g_strdup(metadata_export);
2025 dt_lib_export_metadata_set_conf(d->metadata_export);
2026 const char *iccfilename = buf;
2027 buf += strlen(iccfilename) + 1;
2028
2029 // reverse these by setting the gui, not the conf vars!
2030 dt_bauhaus_combobox_set(d->intent, iccintent + 1);
2031
2032 dt_bauhaus_combobox_set(d->profile, 0);
2033 if(icctype != DT_COLORSPACE_NONE)
2034 {
2035 for(GList *iter = darktable.color_profiles->profiles; iter; iter = g_list_next(iter))
2036 {
2037 const dt_colorspaces_color_profile_t *pp = (dt_colorspaces_color_profile_t *)iter->data;
2038 if(pp->out_pos > -1
2039 && icctype == pp->type
2040 && (icctype != DT_COLORSPACE_FILE || !strcmp(iccfilename, pp->filename)))
2041 {
2042 dt_bauhaus_combobox_set(d->profile, pp->out_pos + 1);
2043 break;
2044 }
2045 }
2046 }
2047
2048 // parse both names to '\0'
2049 const char *fname = buf;
2050 buf += strlen(fname) + 1;
2051 const char *sname = buf;
2052 buf += strlen(sname) + 1;
2053
2054 // get module by name and fail if not there.
2055 dt_imageio_module_format_t *fmod = dt_imageio_get_format_by_name(fname);
2056 dt_imageio_module_storage_t *smod = dt_imageio_get_storage_by_name(sname);
2057 if(!fmod || !smod) return 1;
2058
2059 const int32_t fversion = *(const int32_t *)buf;
2060 buf += sizeof(int32_t);
2061 const int32_t sversion = *(const int32_t *)buf;
2062 buf += sizeof(int32_t);
2063
2064 const int fsize = *(const int *)buf;
2065 buf += sizeof(int32_t);
2066 const int ssize = *(const int *)buf;
2067 buf += sizeof(int32_t);
2068
2069 if(size
2070 != strlen(fname) + strlen(sname) + 2 + 4 * sizeof(int32_t) + fsize + ssize + 7 * sizeof(int32_t)
2071 + strlen(iccfilename) + 1 + strlen(metadata_export) + 1)
2072 return 1;
2073 if(fversion != fmod->version() || sversion != smod->version()) return 1;
2074
2075 const dt_imageio_module_data_t *fdata = (const dt_imageio_module_data_t *)buf;
2076
2077 if(fdata->style[0] == '\0')
2078 dt_bauhaus_combobox_set(d->style, 0);
2079 else
2080 dt_bauhaus_combobox_set_from_text(d->style, fdata->style);
2081
2082 dt_bauhaus_combobox_set(d->style_mode, fdata->style_append ? 1 : 0);
2083
2084 buf += fsize;
2085 const void *sdata = buf;
2086
2087 // switch modules
2088 set_storage_by_name(d, sname);
2089 set_format_by_name(d, fname);
2090
2091 // set dimensions after switching, to have new range ready.
2092 _set_dimensions(d, max_width, max_height);
2093 dt_bauhaus_combobox_set(d->upscale, upscale ? 1 : 0);
2094 dt_bauhaus_combobox_set(d->high_quality, high_quality ? 1 : 0);
2095 dt_bauhaus_combobox_set(d->export_masks, export_masks ? 1 : 0);
2096
2097 // propagate to modules
2098 int res = 0;
2099 if(ssize) res += smod->set_params(smod, sdata, ssize);
2100 if(fsize) res += fmod->set_params(fmod, fdata, fsize);
2101 return res;
2102 }
2103
init_key_accels(dt_lib_module_t * self)2104 void init_key_accels(dt_lib_module_t *self)
2105 {
2106 dt_accel_register_lib(self, NC_("accel", "export"), GDK_KEY_e, GDK_CONTROL_MASK);
2107 }
2108
connect_key_accels(dt_lib_module_t * self)2109 void connect_key_accels(dt_lib_module_t *self)
2110 {
2111 const dt_lib_export_t *d = (dt_lib_export_t *)self->data;
2112
2113 dt_accel_connect_button_lib(self, "export", GTK_WIDGET(d->export_button));
2114 }
2115
2116 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
2117 // vim: shiftwidth=2 expandtab tabstop=2 cindent
2118 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
2119