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