Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
gallery.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2010 Andrea Purracchio.
4 Copyright (C) 2010-2011, 2013 Henrik Andersson.
5 Copyright (C) 2010-2013, 2016 johannes hanika.
6 Copyright (C) 2010 Stuart Henderson.
7 Copyright (C) 2011 Moritz Lipp.
8 Copyright (C) 2011 Olivier Tribout.
9 Copyright (C) 2011 Robert Bieber.
10 Copyright (C) 2011-2018, 2020 Tobias Ellinghaus.
11 Copyright (C) 2012 Christian Tellefsen.
12 Copyright (C) 2012-2014 Jérémy Rosen.
13 Copyright (C) 2012 maigl.
14 Copyright (C) 2012 Richard Wonka.
15 Copyright (C) 2012 Valentin Saussois.
16 Copyright (C) 2013, 2018, 2020-2021 Pascal Obry.
17 Copyright (C) 2013 Thomas Pryds.
18 Copyright (C) 2014 Jesper Pedersen.
19 Copyright (C) 2014-2016 Roman Lebedev.
20 Copyright (C) 2015 Pascal de Bruijn.
21 Copyright (C) 2017 parafin.
22 Copyright (C) 2019 Andreas Schneider.
23 Copyright (C) 2019, 2023, 2025 Aurélien PIERRE.
24 Copyright (C) 2019 Denis Dyakov.
25 Copyright (C) 2019 Heiko Bauke.
26 Copyright (C) 2019 jakubfi.
27 Copyright (C) 2019, 2022 Philippe Weyland.
28 Copyright (C) 2020 Marco.
29 Copyright (C) 2020-2021 Ralf Brown.
30 Copyright (C) 2021 Diederik Ter Rahe.
31 Copyright (C) 2021 Hubert Kowalski.
32 Copyright (C) 2021 Marco Carrarini.
33 Copyright (C) 2021 Mark-64.
34 Copyright (C) 2022 Martin Bařinka.
35 Copyright (C) 2022 Miloš Komarčević.
36 Copyright (C) 2023 Ricky Moon.
37 Copyright (C) 2026 Guillaume Stutin.
38
39 darktable is free software: you can redistribute it and/or modify
40 it under the terms of the GNU General Public License as published by
41 the Free Software Foundation, either version 3 of the License, or
42 (at your option) any later version.
43
44 darktable is distributed in the hope that it will be useful,
45 but WITHOUT ANY WARRANTY; without even the implied warranty of
46 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
47 GNU General Public License for more details.
48
49 You should have received a copy of the GNU General Public License
50 along with darktable. If not, see <http://www.gnu.org/licenses/>.
51*/
52
53#include "system/macros.h"
54#include "system/mem_alloc.h"
56#include "common/paths.h"
57#include <glib/gstdio.h>
58#include "common/image.h"
61#include "metadata/metadata.h"
62#include "common/utility.h"
63#include "common/variables.h"
64#include "common/conf.h"
66#include "widgets/button.h"
67#include "widgets/paint.h"
68#include "gui/application.h"
69#include "widgets/gtkentry.h"
71#ifdef GDK_WINDOWING_QUARTZ
72#endif
73#include <stdio.h>
74#include <stdlib.h>
75#include "widgets/label.h"
77
78DT_MODULE(2)
79
80// gui data
81typedef struct gallery_t
82{
83 GtkEntry *entry;
84 GtkEntry *title_entry;
86
87// saved params
89{
91 char title[1024];
92 char cached_dirname[DT_MAX_PATH_FOR_PARAMS]; // expanded during first img store, not stored in param struct.
94 GList *l;
96
97// sorted list of all images
98typedef struct pair_t
99{
100 char line[4096];
101 char item[4096];
102 int pos;
104
105
106const char *name(const struct dt_imageio_module_storage_t *self)
107{
108 return _("Static HTML gallery");
109}
110
111void *legacy_params(dt_imageio_module_storage_t *self, const void *const old_params,
112 const size_t old_params_size, const int old_version, const int new_version,
113 size_t *new_size)
114{
115 if(old_version == 1 && new_version == 2)
116 {
117 typedef struct dt_imageio_gallery_v1_t
118 {
119 char filename[1024];
120 char title[1024];
121 char cached_dirname[1024]; // expanded during first img store, not stored in param struct.
123 GList *l;
124 } dt_imageio_gallery_v1_t;
125
127 dt_imageio_gallery_v1_t *o = (dt_imageio_gallery_v1_t *)old_params;
128
129 g_strlcpy(n->filename, o->filename, sizeof(n->filename));
130 g_strlcpy(n->title, o->title, sizeof(n->title));
131 g_strlcpy(n->cached_dirname, o->cached_dirname, sizeof(n->cached_dirname));
132
133 *new_size = self->params_size(self);
134 return n;
135 }
136 return NULL;
137}
138
140{
141 gallery_t *d = (gallery_t *)self->gui_data;
143 GtkFileChooserNative *filechooser = gtk_file_chooser_native_new(
144 _("select directory"), GTK_WINDOW(win), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
145 _("_select as output destination"), _("_cancel"));
146
147 gchar *old = g_strdup(gtk_entry_get_text(d->entry));
148 char *c = g_strstr_len(old, -1, "$");
149 if(c) *c = '\0';
150 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(filechooser), old);
151 dt_free(old);
152 if(gtk_native_dialog_run(GTK_NATIVE_DIALOG(filechooser)) == GTK_RESPONSE_ACCEPT)
153 {
154 gchar *dir = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filechooser));
155 char *composed = g_build_filename(dir, "$(FILE_NAME)", NULL);
156
157 // composed can now contain '\': on Windows it's the path separator,
158 // on other platforms it can be part of a regular folder name.
159 // This would later clash with variable substitution, so we have to escape them
160 gchar *escaped = dt_util_str_replace(composed, "\\", "\\\\");
161
162 gtk_entry_set_text(GTK_ENTRY(d->entry), escaped); // the signal handler will write this to conf
163 dt_free(dir);
164 dt_free(composed);
165 dt_free(escaped);
166 }
167 g_object_unref(filechooser);
168}
169
170static void entry_changed_callback(GtkEntry *entry, gpointer user_data)
171{
172 dt_conf_set_string("plugins/imageio/storage/gallery/file_directory", gtk_entry_get_text(entry));
173}
174
175static void title_changed_callback(GtkEntry *entry, gpointer user_data)
176{
177 dt_conf_set_string("plugins/imageio/storage/gallery/title", gtk_entry_get_text(entry));
178}
179
181{
182 gallery_t *d = (gallery_t *)malloc(sizeof(gallery_t));
183 self->gui_data = (void *)d;
184 self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
185 GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
186 gtk_box_pack_start(GTK_BOX(self->widget), hbox, TRUE, TRUE, 0);
187 GtkWidget *widget;
188
189 widget = gtk_entry_new();
191 gtk_entry_set_width_chars(GTK_ENTRY(widget), 0);
192 gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
193 const char *dir = dt_conf_get_string_const("plugins/imageio/storage/gallery/file_directory");
194 if(dir)
195 {
196 gtk_entry_set_text(GTK_ENTRY(widget), dir);
197 }
198 d->entry = GTK_ENTRY(widget);
199
201
202 gtk_widget_set_tooltip_text(widget,
203 _("enter the path where to put exported images\nvariables support bash like string manipulation\n"
204 "type '$(' to activate the completion and see the list of variables"));
205 g_signal_connect(G_OBJECT(widget), "changed", G_CALLBACK(entry_changed_callback), self);
206
208 gtk_widget_set_tooltip_text(widget, _("select directory"));
209 gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, FALSE, 0);
210 g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(button_clicked), self);
211
212 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
213 gtk_box_pack_start(GTK_BOX(self->widget), hbox, TRUE, TRUE, 0);
214 gtk_box_pack_start(GTK_BOX(hbox), dt_ui_label_new(_("title")), FALSE, FALSE, 0);
215 d->title_entry = GTK_ENTRY(gtk_entry_new());
216 dt_accels_disconnect_on_text_input(GTK_WIDGET(d->title_entry));
217 gtk_entry_set_width_chars(d->title_entry, 0);
218 gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(d->title_entry), TRUE, TRUE, 0);
219 gtk_widget_set_tooltip_text(GTK_WIDGET(d->title_entry), _("enter the title of the website"));
220 dir = dt_conf_get_string_const("plugins/imageio/storage/gallery/title");
221 if(dir)
222 {
223 gtk_entry_set_text(GTK_ENTRY(d->title_entry), dir);
224 }
225 g_signal_connect(G_OBJECT(d->title_entry), "changed", G_CALLBACK(title_changed_callback), self);
226}
227
229{
230 dt_free(self->gui_data);
231}
232
234{
235 gallery_t *d = (gallery_t *)self->gui_data;
236 gtk_entry_set_text(d->entry, dt_confgen_get("plugins/imageio/storage/gallery/file_directory", DT_DEFAULT));
237 gtk_entry_set_text(d->title_entry, dt_confgen_get("plugins/imageio/storage/gallery/title", DT_DEFAULT));
238 dt_conf_set_string("plugins/imageio/storage/gallery/file_directory", gtk_entry_get_text(d->entry));
239 dt_conf_set_string("plugins/imageio/storage/gallery/title", gtk_entry_get_text(d->title_entry));
240}
241
242static gint sort_pos(pair_t *a, pair_t *b)
243{
244 return a->pos - b->pos;
245}
246
247int store(dt_imageio_module_storage_t *self, dt_imageio_module_data_t *sdata, const int32_t imgid,
248 dt_imageio_module_format_t *format, dt_imageio_module_data_t *fdata, const double scale_factor,
249 const int num, const int total,
250 const gboolean high_quality, const gboolean export_masks,
251 dt_colorspaces_color_profile_type_t icc_type, const gchar *icc_filename, dt_iop_color_intent_t icc_intent,
252 dt_export_metadata_t *metadata)
253{
255
256 char filename[DT_PATH_MAX] = { 0 };
257 char dirname[DT_PATH_MAX] = { 0 };
258 gboolean from_cache = FALSE;
259 dt_image_full_path(imgid, dirname, sizeof(dirname), &from_cache, __FUNCTION__);
260
261 char tmp_dir[DT_PATH_MAX] = { 0 };
262
263 // set variable values to expand them afterwards in darktable variables
265
266 d->vp->filename = dirname;
267 d->vp->jobcode = "export";
268 d->vp->imgid = imgid;
269 d->vp->sequence = num;
270
271 gchar *result_tmp_dir = dt_variables_expand(d->vp, d->filename, TRUE);
272 g_strlcpy(tmp_dir, result_tmp_dir, sizeof(tmp_dir));
273 dt_free(result_tmp_dir);
274
275 // if filenamepattern is a directory just let att ${FILE_NAME} as default..
276 if(g_file_test(tmp_dir, G_FILE_TEST_IS_DIR)
277 || ((d->filename + strlen(d->filename) - 1)[0] == '/'
278 || (d->filename + strlen(d->filename) - 1)[0] == '\\'))
279 snprintf(d->filename + strlen(d->filename), sizeof(d->filename) - strlen(d->filename), "/$(FILE_NAME)");
280
281 // avoid braindead export which is bound to overwrite at random:
282 if(total > 1 && !g_strrstr(d->filename, "$"))
283 {
284 snprintf(d->filename + strlen(d->filename), sizeof(d->filename) - strlen(d->filename), "_$(SEQUENCE)");
285 }
286
287 gchar *fixed_path = dt_util_fix_path(d->filename);
288 g_strlcpy(d->filename, fixed_path, sizeof(d->filename));
289 dt_free(fixed_path);
290
291 gchar *result_filename = dt_variables_expand(d->vp, d->filename, TRUE);
292 g_strlcpy(filename, result_filename, sizeof(filename));
293 dt_free(result_filename);
294
295 g_strlcpy(dirname, filename, sizeof(dirname));
296
297 const char *ext = format->extension(fdata);
298 char *c = dirname + strlen(dirname);
299 for(; c > dirname && *c != '/'; c--)
300 ;
301 if(*c == '/') *c = '\0';
302 if(g_mkdir_with_parents(dirname, 0755))
303 {
304 dt_control_alert(_("Export failed"), _("could not create directory!"), dirname);
305 return 1;
306 }
307
308 // store away dir.
309 g_strlcpy(d->cached_dirname, dirname, sizeof(d->cached_dirname));
310
311 c = filename + strlen(filename);
312 for(; c > filename && *c != '.' && *c != '/'; c--)
313 ;
314 if(c <= filename || *c == '/') c = filename + strlen(filename);
315
316 sprintf(c, ".%s", ext);
317
318 // save image to list, in order:
319 pair_t *pair = malloc(sizeof(pair_t));
320
321 char *title = NULL, *description = NULL;
322 GList *res_title = NULL, *res_desc = NULL;
323
324 if ((metadata->flags & DT_META_METADATA) && !(metadata->flags & DT_META_CALCULATED))
325 {
326 res_title = dt_metadata_get(imgid, "Xmp.dc.title", NULL);
327 if(res_title)
328 {
329 title = res_title->data;
330 }
331
332 res_desc = dt_metadata_get(imgid, "Xmp.dc.description", NULL);
333 if(res_desc)
334 {
335 description = res_desc->data;
336 }
337 }
338
339 char relfilename[DT_PATH_MAX] = { 0 }, relthumbfilename[DT_PATH_MAX] = { 0 };
340 c = filename + strlen(filename);
341 for(; c > filename && *c != '/'; c--)
342 ;
343 if(*c == '/') c++;
344 if(c <= filename) c = filename;
345 g_strlcpy(relfilename, c, sizeof(relfilename));
346 g_strlcpy(relthumbfilename, relfilename, sizeof(relthumbfilename));
347 c = relthumbfilename + strlen(relthumbfilename);
348 for(; c > relthumbfilename && *c != '.'; c--)
349 ;
350 if(c <= relthumbfilename) c = relthumbfilename + strlen(relthumbfilename);
351 sprintf(c, "-thumb.%s", ext);
352
353 char subfilename[DT_PATH_MAX] = { 0 }, relsubfilename[DT_PATH_MAX] = { 0 };
354 g_strlcpy(subfilename, d->cached_dirname, sizeof(subfilename));
355 char *sc = subfilename + strlen(subfilename);
356 sprintf(sc, "/img_%d.html", num);
357 snprintf(relsubfilename, sizeof(relsubfilename), "img_%d.html", num);
358
359 // escape special character and especially " which is used in <img> and below in src and msrc
360
361 gchar *esc_relfilename = g_strescape(relfilename, NULL);
362 gchar *esc_relthumbfilename = g_strescape(relthumbfilename, NULL);
363
364 snprintf(pair->line, sizeof(pair->line),
365 "\n"
366 " <div><div class=\"dia\">\n"
367 " <img src=\"%s\" alt=\"img%d\" class=\"img\" onclick=\"openSwipe(%d)\"/></div>\n"
368 " <h1>%s</h1>\n"
369 " %s</div>\n",
370 esc_relthumbfilename,
371 num, num-1, title ? title : "&nbsp;", description ? description : "&nbsp;");
372
373 if(res_title)
374 {
375 g_list_free_full(res_title, dt_free_gpointer);
376 res_title = NULL;
377 }
378 if(res_desc)
379 {
380 g_list_free_full(res_desc, dt_free_gpointer);
381 res_desc = NULL;
382 }
383
384 // export image to file. need this to be able to access meaningful
385 // fdata->width and height below.
386 if(dt_imageio_export(imgid, filename, format, fdata, scale_factor, TRUE, TRUE, export_masks, icc_type,
387 icc_filename, icc_intent, self, sdata, num, total, metadata) != 0)
388 {
389 dt_control_alert(_("Export failed"), _("could not export to file!"), filename);
390 dt_free(pair);
391 dt_free(esc_relfilename);
392 dt_free(esc_relthumbfilename);
393 return 1;
394 }
395
396 snprintf(pair->item, sizeof(pair->item),
397 "{\n"
398 "src: \"%s\",\n"
399 "w: %d,\n"
400 "h: %d,\n"
401 "msrc: \"%s\",\n"
402 "},\n",
403 esc_relfilename, fdata->width, fdata->height, esc_relthumbfilename);
404
405 dt_free(esc_relfilename);
406 dt_free(esc_relthumbfilename);
407
408 pair->pos = num;
409 d->l = g_list_insert_sorted(d->l, pair, (GCompareFunc)sort_pos);
410 dt_free(pair);
411
412 /* also export thumbnail: */
413 // write with reduced resolution:
414 const int save_max_width = fdata->max_width;
415 const int save_max_height = fdata->max_height;
416 fdata->max_width = 200;
417 fdata->max_height = 200;
418 // alter filename with -thumb:
419 c = filename + strlen(filename);
420 for(; c > filename && *c != '.' && *c != '/'; c--)
421 ;
422 if(c <= filename || *c == '/') c = filename + strlen(filename);
423 ext = format->extension(fdata);
424 sprintf(c, "-thumb.%s", ext);
425 // the thumbnail fits its own 200 px box, whatever factor the image was reduced by
426 if(dt_imageio_export(imgid, filename, format, fdata, 0.0, FALSE, FALSE, export_masks, icc_type, icc_filename,
427 icc_intent, self, sdata, num, total, NULL) != 0)
428 {
429 dt_control_alert(_("Export failed"), _("could not export to file!"), filename);
430 return 1;
431 }
432 // restore for next image:
433 fdata->max_width = save_max_width;
434 fdata->max_height = save_max_height;
435
436 printf("[export_job] exported to `%s'\n", filename);
437 dt_control_log(ngettext("%d/%d exported to `%s'", "%d/%d exported to `%s'", num),
438 num, total, filename);
439 return 0;
440}
441
443{
445 char filename[DT_PATH_MAX] = { 0 };
446 g_strlcpy(filename, d->cached_dirname, sizeof(filename));
447 char *c = filename + strlen(filename);
448
449 // also create style/ subdir:
450 sprintf(c, "/style");
451 g_mkdir_with_parents(filename, 0755);
452 sprintf(c, "/style/style.css");
453 dt_copy_resource_file("/style/style.css", filename);
454 sprintf(c, "/style/favicon.ico");
455 dt_copy_resource_file("/style/favicon.ico", filename);
456
457 // create subdir pswp for photoswipe scripts
458 sprintf(c, "/pswp/default-skin/");
459 g_mkdir_with_parents(filename, 0755);
460 sprintf(c, "/pswp/photoswipe.js");
461 dt_copy_resource_file("/pswp/photoswipe.js", filename);
462 sprintf(c, "/pswp/photoswipe.min.js");
463 dt_copy_resource_file("/pswp/photoswipe.min.js", filename);
464 sprintf(c, "/pswp/photoswipe-ui-default.js");
465 dt_copy_resource_file("/pswp/photoswipe-ui-default.js", filename);
466 sprintf(c, "/pswp/photoswipe.css");
467 dt_copy_resource_file("/pswp/photoswipe.css", filename);
468 sprintf(c, "/pswp/photoswipe-ui-default.min.js");
469 dt_copy_resource_file("/pswp/photoswipe-ui-default.min.js", filename);
470 sprintf(c, "/pswp/default-skin/default-skin.css");
471 dt_copy_resource_file("/pswp/default-skin/default-skin.css", filename);
472 sprintf(c, "/pswp/default-skin/default-skin.png");
473 dt_copy_resource_file("/pswp/default-skin/default-skin.png", filename);
474 sprintf(c, "/pswp/default-skin/default-skin.svg");
475 dt_copy_resource_file("/pswp/default-skin/default-skin.svg", filename);
476 sprintf(c, "/pswp/default-skin/preloader.gif");
477 dt_copy_resource_file("/pswp/default-skin/preloader.gif", filename);
478
479 sprintf(c, "/index.html");
480
481 const char *title = d->title;
482
483 FILE *f = g_fopen(filename, "wb");
484 if(IS_NULL_PTR(f)) return;
485 fprintf(f,
486 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" "
487 "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
488 "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
489 " <head>\n"
490 " <meta http-equiv=\"Content-type\" content=\"text/html;charset=UTF-8\" />\n"
491 " <link rel=\"shortcut icon\" href=\"style/favicon.ico\" />\n"
492 " <link rel=\"stylesheet\" href=\"style/style.css\" type=\"text/css\" />\n"
493 " <link rel=\"stylesheet\" href=\"pswp/photoswipe.css\">\n"
494 " <link rel=\"stylesheet\" href=\"pswp/default-skin/default-skin.css\">\n"
495 " <script src=\"pswp/photoswipe.min.js\"></script>\n"
496 " <script src=\"pswp/photoswipe-ui-default.min.js\"></script>\n"
497 " <title>%s</title>\n"
498 " </head>\n"
499 " <body>\n"
500 " <div class=\"title\">%s</div>\n"
501 " <div class=\"page\">\n",
502 title, title);
503
504 size_t count = 0;
505 for(GList *tmp = d->l; tmp; tmp = g_list_next(tmp))
506 {
507 pair_t *p = (pair_t *)tmp->data;
508 fprintf(f, "%s", p->line);
509 count++;
510 }
511
512 fprintf(f, " <p style=\"clear:both;\"></p>\n"
513 " </div>\n"
514 " <div class=\"footer\">\n"
515 " <script language=\"JavaScript\" type=\"text/javascript\">\n"
516 " document.write(\"download all: <em>curl -O# \" + document.documentURI.replace( /\\\\/g, '/' ).replace( /\\/[^\\/]*$/, '' ) + \"/img_[0000-%04zu].jpg</em>\")\n"
517 " </script><br />\n"
518 " created with %s\n"
519 " </div>\n"
520 " <div class=\"pswp\" tabindex=\"-1\" role=\"dialog\" aria-hidden=\"true\">\n"
521 " <div class=\"pswp__bg\"></div>\n"
522 " <div class=\"pswp__scroll-wrap\">\n"
523 " <div class=\"pswp__container\">\n"
524 " <div class=\"pswp__item\"></div>\n"
525 " <div class=\"pswp__item\"></div>\n"
526 " <div class=\"pswp__item\"></div>\n"
527 " </div>\n"
528 " <div class=\"pswp__ui pswp__ui--hidden\">\n"
529 " <div class=\"pswp__top-bar\">\n"
530 " <div class=\"pswp__counter\"></div>\n"
531 " <button class=\"pswp__button pswp__button--close\" title=\"Close (Esc)\"></button>\n"
532 " <button class=\"pswp__button pswp__button--share\" title=\"Share\"></button>\n"
533 " <button class=\"pswp__button pswp__button--fs\" title=\"Toggle fullscreen\"></button>\n"
534 " <button class=\"pswp__button pswp__button--zoom\" title=\"Zoom in/out\"></button>\n"
535 " <div class=\"pswp__preloader\">\n"
536 " <div class=\"pswp__preloader__icn\">\n"
537 " <div class=\"pswp__preloader__cut\">\n"
538 " <div class=\"pswp__preloader__donut\"></div>\n"
539 " </div>\n"
540 " </div>\n"
541 " </div>\n"
542 " </div>\n"
543 " <div class=\"pswp__share-modal pswp__share-modal--hidden pswp__single-tap\">\n"
544 " <div class=\"pswp__share-tooltip\"></div>\n"
545 " </div>\n"
546 " <button class=\"pswp__button pswp__button--arrow--left\" title=\"Previous (arrow left)\">\n"
547 " </button>\n"
548 " <button class=\"pswp__button pswp__button--arrow--right\" title=\"Next (arrow right)\">\n"
549 " </button>\n"
550 " <div class=\"pswp__caption\">\n"
551 " <div class=\"pswp__caption__center\"></div>\n"
552 " </div>\n"
553 " </div>\n"
554 " </div>\n"
555 " </div>\n"
556 " </body>\n"
557 "<script>\n"
558 "var pswpElement = document.querySelectorAll('.pswp')[0];\n"
559 "var items = [\n",
560 count,
562 while(d->l)
563 {
564 pair_t *p = (pair_t *)d->l->data;
565 fprintf(f, "%s", p->item);
566 dt_free(p);
567 d->l = g_list_delete_link(d->l, d->l);
568 }
569 fprintf(f, "];\n"
570 "function openSwipe(img)\n"
571 "{\n"
572 " // define options (if needed)\n"
573 " var options = {\n"
574 " // optionName: 'option value'\n"
575 " index: img // start at first slide\n"
576 " };\n"
577 " var gallery = new PhotoSwipe( pswpElement, PhotoSwipeUI_Default, items, options);\n"
578 " gallery.init();\n"
579 "}\n"
580 "</script>\n"
581 "</html>\n");
582 fclose(f);
583}
584
586{
587 return sizeof(dt_imageio_gallery_t) - 2 * sizeof(void *) - DT_MAX_PATH_FOR_PARAMS;
588}
589
591{
592}
593
595{
597 d->vp = NULL;
598 d->l = NULL;
600
601 const char *text = dt_conf_get_string_const("plugins/imageio/storage/gallery/file_directory");
602 g_strlcpy(d->filename, text, sizeof(d->filename));
603
604 text = dt_conf_get_string_const("plugins/imageio/storage/gallery/title");
605 g_strlcpy(d->title, text, sizeof(d->title));
606
607 return d;
608}
609
617
618int set_params(dt_imageio_module_storage_t *self, const void *params, const int size)
619{
620 if(size != self->params_size(self)) return 1;
622 gallery_t *g = (gallery_t *)self->gui_data;
623 gtk_entry_set_text(GTK_ENTRY(g->entry), d->filename);
624 dt_conf_set_string("plugins/imageio/storage/gallery/file_directory", d->filename);
625 gtk_entry_set_text(GTK_ENTRY(g->title_entry), d->title);
626 dt_conf_set_string("plugins/imageio/storage/gallery/title", d->title);
627 return 0;
628}
629
631{
632 const char *mime = format->mime(NULL);
633 if(strcmp(mime, "image/jpeg") == 0)
634 return 1;
635 if(strcmp(mime, "image/png") == 0)
636 return 1;
637 if(strcmp(mime, "image/webp") == 0)
638 return 1;
639 if (strcmp(mime, "image/avif") == 0) return 1;
640
641 return 0;
642}
643
644// clang-format off
645// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
646// vim: shiftwidth=2 expandtab tabstop=2 cindent
647// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
648// clang-format on
void dt_accels_disconnect_on_text_input(GtkWidget *widget)
Disconnect accels while a text or search entry has the focus, and reconnect them when it loses it....
GtkWidget * dt_gui_main_window(void)
const char ** description(struct dt_iop_module_t *self)
Definition ashift.c:168
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
const char * mime(dt_imageio_module_data_t *data)
Definition avif.c:646
GtkWidget * dtgtk_button_new(DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
Definition button.c:135
const float f
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
void dt_conf_set_string(const char *name, const char *val)
const char * dt_conf_get_string_const(const char *name)
Borrow the stored string for name without copying it.
const char * dt_confgen_get(const char *name, dt_confgen_value_kind_t kind)
One declared attribute of name from the generated XML, as text.
@ DT_DEFAULT
void dt_image_full_path(const int32_t imgid, char *pathname, size_t pathname_len, gboolean *from_cache, const char *calling_func)
Get the full path of an image out of the database.
const char darktable_package_string[]
void dt_control_alert(const char *title, const char *message, const char *item)
Tell the user something that must not go unseen: what it says stays true after a toast would have fad...
Definition control.c:883
void dt_control_log(const char *msg,...)
Definition control.c:839
GtkTreeStore * store
its model, owned by the view
void * get_params(dt_imageio_module_storage_t *self)
Definition gallery.c:594
int supported(dt_imageio_module_storage_t *storage, dt_imageio_module_format_t *format)
Definition gallery.c:630
static void title_changed_callback(GtkEntry *entry, gpointer user_data)
Definition gallery.c:175
void gui_cleanup(dt_imageio_module_storage_t *self)
Definition gallery.c:228
void free_params(dt_imageio_module_storage_t *self, dt_imageio_module_data_t *params)
Definition gallery.c:610
size_t params_size(dt_imageio_module_storage_t *self)
Definition gallery.c:585
void gui_init(dt_imageio_module_storage_t *self)
Definition gallery.c:180
void finalize_store(dt_imageio_module_storage_t *self, dt_imageio_module_data_t *dd)
Definition gallery.c:442
void init(dt_imageio_module_storage_t *self)
Definition gallery.c:590
void gui_reset(dt_imageio_module_storage_t *self)
Definition gallery.c:233
void * legacy_params(dt_imageio_module_storage_t *self, const void *const old_params, const size_t old_params_size, const int old_version, const int new_version, size_t *new_size)
Definition gallery.c:111
int set_params(dt_imageio_module_storage_t *self, const void *params, const int size)
Definition gallery.c:618
static void button_clicked(GtkWidget *widget, dt_imageio_module_storage_t *self)
Definition gallery.c:139
static void entry_changed_callback(GtkEntry *entry, gpointer user_data)
Definition gallery.c:170
static gint sort_pos(pair_t *a, pair_t *b)
Definition gallery.c:242
void dt_gtkentry_setup_completion(GtkEntry *entry, const dt_gtkentry_completion_spec *compl_list, const char *trigger_char)
Definition gtkentry.c:176
const dt_gtkentry_completion_spec * dt_gtkentry_get_default_path_compl_list()
Definition gtkentry.c:200
int dt_imageio_export(const int32_t imgid, const char *filename, dt_imageio_module_format_t *format, dt_imageio_module_data_t *format_params, const double scale_factor, const gboolean high_quality, const gboolean copy_metadata, const gboolean export_masks, dt_colorspaces_color_profile_type_t icc_type, const gchar *icc_filename, dt_iop_color_intent_t icc_intent, dt_imageio_module_storage_t *storage, dt_imageio_module_data_t *storage_params, int num, int total, dt_export_metadata_t *metadata)
GtkWidget * dt_ui_label_new(const gchar *str)
Definition label.c:125
static const dt_iop_drawlayer_params_t * params
Definition layers.h:80
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
static void dt_free_gpointer(gpointer ptr)
g_free() one pointer, with the signature GDestroyNotify wants.
Definition mem_alloc.h:184
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
GList * dt_metadata_get(const int id, const char *key, uint32_t *count)
@ DT_META_CALCULATED
@ DT_META_METADATA
size_t size
Definition mipmap_cache.c:3
#define o
#define DT_MODULE(MODVER)
Instantiate the version handshake every module must export. Use once, at file scope,...
#define DT_PATH_MAX
Buffer size for a filesystem path anywhere in Ansel.
Definition paths.h:57
#define DT_MAX_PATH_FOR_PARAMS
Definition paths.h:65
const char * name
Definition pdf.h:90
dt_iop_color_intent_t
ICC rendering intent, as stored in iop params and in conf.
dt_colorspaces_color_profile_type_t
char * dt_variables_expand(dt_variables_params_t *params, gchar *source, gboolean iterate)
void dt_variables_params_destroy(dt_variables_params_t *params)
void dt_variables_set_max_width_height(dt_variables_params_t *params, int max_width, int max_height)
void dt_variables_params_init(dt_variables_params_t **params)
GModule *GtkWidget * widget
GtkEntry * entry
Definition gallery.c:83
GtkEntry * title_entry
Definition gallery.c:84
int pos
Definition gallery.c:102
char item[4096]
Definition gallery.c:101
char line[4096]
Definition gallery.c:100
Telling the user something happened.
gchar * dt_util_str_replace(const gchar *string, const gchar *pattern, const gchar *substitute)
Definition utility.c:163
void dt_copy_resource_file(const char *src, const char *dst)
Definition utility.c:975
gchar * dt_util_fix_path(const gchar *path)
Definition utility.c:249
#define DT_GUI_BOX_SPACING
void dtgtk_cairo_paint_directory(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
@ CPF_NONE