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
38 darktable is free software: you can redistribute it and/or modify
39 it under the terms of the GNU General Public License as published by
40 the Free Software Foundation, either version 3 of the License, or
41 (at your option) any later version.
42
43 darktable is distributed in the hope that it will be useful,
44 but WITHOUT ANY WARRANTY; without even the implied warranty of
45 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
46 GNU General Public License for more details.
47
48 You should have received a copy of the GNU General Public License
49 along with darktable. If not, see <http://www.gnu.org/licenses/>.
50*/
51
52#include "system/macros.h"
53#include "system/mem_alloc.h"
55#include "common/paths.h"
56#include <glib/gstdio.h>
57#include "common/image.h"
60#include "metadata/metadata.h"
61#include "common/utility.h"
62#include "common/variables.h"
63#include "common/conf.h"
65#include "widgets/button.h"
66#include "widgets/paint.h"
67#include "gui/application.h"
68#include "widgets/gtkentry.h"
70#ifdef GDK_WINDOWING_QUARTZ
71#endif
72#include <stdio.h>
73#include <stdlib.h>
74#include "widgets/label.h"
76
77DT_MODULE(2)
78
79// gui data
80typedef struct gallery_t
81{
82 GtkEntry *entry;
83 GtkEntry *title_entry;
85
86// saved params
88{
90 char title[1024];
91 char cached_dirname[DT_MAX_PATH_FOR_PARAMS]; // expanded during first img store, not stored in param struct.
93 GList *l;
95
96// sorted list of all images
97typedef struct pair_t
98{
99 char line[4096];
100 char item[4096];
101 int pos;
103
104
105const char *name(const struct dt_imageio_module_storage_t *self)
106{
107 return _("Static HTML gallery");
108}
109
110void *legacy_params(dt_imageio_module_storage_t *self, const void *const old_params,
111 const size_t old_params_size, const int old_version, const int new_version,
112 size_t *new_size)
113{
114 if(old_version == 1 && new_version == 2)
115 {
116 typedef struct dt_imageio_gallery_v1_t
117 {
118 char filename[1024];
119 char title[1024];
120 char cached_dirname[1024]; // expanded during first img store, not stored in param struct.
122 GList *l;
123 } dt_imageio_gallery_v1_t;
124
126 dt_imageio_gallery_v1_t *o = (dt_imageio_gallery_v1_t *)old_params;
127
128 g_strlcpy(n->filename, o->filename, sizeof(n->filename));
129 g_strlcpy(n->title, o->title, sizeof(n->title));
130 g_strlcpy(n->cached_dirname, o->cached_dirname, sizeof(n->cached_dirname));
131
132 *new_size = self->params_size(self);
133 return n;
134 }
135 return NULL;
136}
137
139{
140 gallery_t *d = (gallery_t *)self->gui_data;
142 GtkFileChooserNative *filechooser = gtk_file_chooser_native_new(
143 _("select directory"), GTK_WINDOW(win), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
144 _("_select as output destination"), _("_cancel"));
145
146 gchar *old = g_strdup(gtk_entry_get_text(d->entry));
147 char *c = g_strstr_len(old, -1, "$");
148 if(c) *c = '\0';
149 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(filechooser), old);
150 dt_free(old);
151 if(gtk_native_dialog_run(GTK_NATIVE_DIALOG(filechooser)) == GTK_RESPONSE_ACCEPT)
152 {
153 gchar *dir = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filechooser));
154 char *composed = g_build_filename(dir, "$(FILE_NAME)", NULL);
155
156 // composed can now contain '\': on Windows it's the path separator,
157 // on other platforms it can be part of a regular folder name.
158 // This would later clash with variable substitution, so we have to escape them
159 gchar *escaped = dt_util_str_replace(composed, "\\", "\\\\");
160
161 gtk_entry_set_text(GTK_ENTRY(d->entry), escaped); // the signal handler will write this to conf
162 dt_free(dir);
163 dt_free(composed);
164 dt_free(escaped);
165 }
166 g_object_unref(filechooser);
167}
168
169static void entry_changed_callback(GtkEntry *entry, gpointer user_data)
170{
171 dt_conf_set_string("plugins/imageio/storage/gallery/file_directory", gtk_entry_get_text(entry));
172}
173
174static void title_changed_callback(GtkEntry *entry, gpointer user_data)
175{
176 dt_conf_set_string("plugins/imageio/storage/gallery/title", gtk_entry_get_text(entry));
177}
178
180{
181 gallery_t *d = (gallery_t *)malloc(sizeof(gallery_t));
182 self->gui_data = (void *)d;
183 self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
184 GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
185 gtk_box_pack_start(GTK_BOX(self->widget), hbox, TRUE, TRUE, 0);
186 GtkWidget *widget;
187
188 widget = gtk_entry_new();
190 gtk_entry_set_width_chars(GTK_ENTRY(widget), 0);
191 gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
192 const char *dir = dt_conf_get_string_const("plugins/imageio/storage/gallery/file_directory");
193 if(dir)
194 {
195 gtk_entry_set_text(GTK_ENTRY(widget), dir);
196 }
197 d->entry = GTK_ENTRY(widget);
198
200
201 gtk_widget_set_tooltip_text(widget,
202 _("enter the path where to put exported images\nvariables support bash like string manipulation\n"
203 "type '$(' to activate the completion and see the list of variables"));
204 g_signal_connect(G_OBJECT(widget), "changed", G_CALLBACK(entry_changed_callback), self);
205
207 gtk_widget_set_tooltip_text(widget, _("select directory"));
208 gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, FALSE, 0);
209 g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(button_clicked), self);
210
211 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
212 gtk_box_pack_start(GTK_BOX(self->widget), hbox, TRUE, TRUE, 0);
213 gtk_box_pack_start(GTK_BOX(hbox), dt_ui_label_new(_("title")), FALSE, FALSE, 0);
214 d->title_entry = GTK_ENTRY(gtk_entry_new());
215 dt_accels_disconnect_on_text_input(GTK_WIDGET(d->title_entry));
216 gtk_entry_set_width_chars(d->title_entry, 0);
217 gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(d->title_entry), TRUE, TRUE, 0);
218 gtk_widget_set_tooltip_text(GTK_WIDGET(d->title_entry), _("enter the title of the website"));
219 dir = dt_conf_get_string_const("plugins/imageio/storage/gallery/title");
220 if(dir)
221 {
222 gtk_entry_set_text(GTK_ENTRY(d->title_entry), dir);
223 }
224 g_signal_connect(G_OBJECT(d->title_entry), "changed", G_CALLBACK(title_changed_callback), self);
225}
226
228{
229 dt_free(self->gui_data);
230}
231
233{
234 gallery_t *d = (gallery_t *)self->gui_data;
235 gtk_entry_set_text(d->entry, dt_confgen_get("plugins/imageio/storage/gallery/file_directory", DT_DEFAULT));
236 gtk_entry_set_text(d->title_entry, dt_confgen_get("plugins/imageio/storage/gallery/title", DT_DEFAULT));
237 dt_conf_set_string("plugins/imageio/storage/gallery/file_directory", gtk_entry_get_text(d->entry));
238 dt_conf_set_string("plugins/imageio/storage/gallery/title", gtk_entry_get_text(d->title_entry));
239}
240
241static gint sort_pos(pair_t *a, pair_t *b)
242{
243 return a->pos - b->pos;
244}
245
246int store(dt_imageio_module_storage_t *self, dt_imageio_module_data_t *sdata, const int32_t imgid,
247 dt_imageio_module_format_t *format, dt_imageio_module_data_t *fdata, const int num, const int total,
248 const gboolean high_quality, const gboolean export_masks,
249 dt_colorspaces_color_profile_type_t icc_type, const gchar *icc_filename, dt_iop_color_intent_t icc_intent,
250 dt_export_metadata_t *metadata)
251{
253
254 char filename[DT_PATH_MAX] = { 0 };
255 char dirname[DT_PATH_MAX] = { 0 };
256 gboolean from_cache = FALSE;
257 dt_image_full_path(imgid, dirname, sizeof(dirname), &from_cache, __FUNCTION__);
258
259 char tmp_dir[DT_PATH_MAX] = { 0 };
260
261 // set variable values to expand them afterwards in darktable variables
263
264 d->vp->filename = dirname;
265 d->vp->jobcode = "export";
266 d->vp->imgid = imgid;
267 d->vp->sequence = num;
268
269 gchar *result_tmp_dir = dt_variables_expand(d->vp, d->filename, TRUE);
270 g_strlcpy(tmp_dir, result_tmp_dir, sizeof(tmp_dir));
271 dt_free(result_tmp_dir);
272
273 // if filenamepattern is a directory just let att ${FILE_NAME} as default..
274 if(g_file_test(tmp_dir, G_FILE_TEST_IS_DIR)
275 || ((d->filename + strlen(d->filename) - 1)[0] == '/'
276 || (d->filename + strlen(d->filename) - 1)[0] == '\\'))
277 snprintf(d->filename + strlen(d->filename), sizeof(d->filename) - strlen(d->filename), "/$(FILE_NAME)");
278
279 // avoid braindead export which is bound to overwrite at random:
280 if(total > 1 && !g_strrstr(d->filename, "$"))
281 {
282 snprintf(d->filename + strlen(d->filename), sizeof(d->filename) - strlen(d->filename), "_$(SEQUENCE)");
283 }
284
285 gchar *fixed_path = dt_util_fix_path(d->filename);
286 g_strlcpy(d->filename, fixed_path, sizeof(d->filename));
287 dt_free(fixed_path);
288
289 gchar *result_filename = dt_variables_expand(d->vp, d->filename, TRUE);
290 g_strlcpy(filename, result_filename, sizeof(filename));
291 dt_free(result_filename);
292
293 g_strlcpy(dirname, filename, sizeof(dirname));
294
295 const char *ext = format->extension(fdata);
296 char *c = dirname + strlen(dirname);
297 for(; c > dirname && *c != '/'; c--)
298 ;
299 if(*c == '/') *c = '\0';
300 if(g_mkdir_with_parents(dirname, 0755))
301 {
302 fprintf(stderr, "[imageio_storage_gallery] could not create directory: `%s'!\n", dirname);
303 dt_control_log(_("could not create directory `%s'!"), dirname);
304 return 1;
305 }
306
307 // store away dir.
308 g_strlcpy(d->cached_dirname, dirname, sizeof(d->cached_dirname));
309
310 c = filename + strlen(filename);
311 for(; c > filename && *c != '.' && *c != '/'; c--)
312 ;
313 if(c <= filename || *c == '/') c = filename + strlen(filename);
314
315 sprintf(c, ".%s", ext);
316
317 // save image to list, in order:
318 pair_t *pair = malloc(sizeof(pair_t));
319
320 char *title = NULL, *description = NULL;
321 GList *res_title = NULL, *res_desc = NULL;
322
323 if ((metadata->flags & DT_META_METADATA) && !(metadata->flags & DT_META_CALCULATED))
324 {
325 res_title = dt_metadata_get(imgid, "Xmp.dc.title", NULL);
326 if(res_title)
327 {
328 title = res_title->data;
329 }
330
331 res_desc = dt_metadata_get(imgid, "Xmp.dc.description", NULL);
332 if(res_desc)
333 {
334 description = res_desc->data;
335 }
336 }
337
338 char relfilename[DT_PATH_MAX] = { 0 }, relthumbfilename[DT_PATH_MAX] = { 0 };
339 c = filename + strlen(filename);
340 for(; c > filename && *c != '/'; c--)
341 ;
342 if(*c == '/') c++;
343 if(c <= filename) c = filename;
344 g_strlcpy(relfilename, c, sizeof(relfilename));
345 g_strlcpy(relthumbfilename, relfilename, sizeof(relthumbfilename));
346 c = relthumbfilename + strlen(relthumbfilename);
347 for(; c > relthumbfilename && *c != '.'; c--)
348 ;
349 if(c <= relthumbfilename) c = relthumbfilename + strlen(relthumbfilename);
350 sprintf(c, "-thumb.%s", ext);
351
352 char subfilename[DT_PATH_MAX] = { 0 }, relsubfilename[DT_PATH_MAX] = { 0 };
353 g_strlcpy(subfilename, d->cached_dirname, sizeof(subfilename));
354 char *sc = subfilename + strlen(subfilename);
355 sprintf(sc, "/img_%d.html", num);
356 snprintf(relsubfilename, sizeof(relsubfilename), "img_%d.html", num);
357
358 // escape special character and especially " which is used in <img> and below in src and msrc
359
360 gchar *esc_relfilename = g_strescape(relfilename, NULL);
361 gchar *esc_relthumbfilename = g_strescape(relthumbfilename, NULL);
362
363 snprintf(pair->line, sizeof(pair->line),
364 "\n"
365 " <div><div class=\"dia\">\n"
366 " <img src=\"%s\" alt=\"img%d\" class=\"img\" onclick=\"openSwipe(%d)\"/></div>\n"
367 " <h1>%s</h1>\n"
368 " %s</div>\n",
369 esc_relthumbfilename,
370 num, num-1, title ? title : "&nbsp;", description ? description : "&nbsp;");
371
372 if(res_title)
373 {
374 g_list_free_full(res_title, dt_free_gpointer);
375 res_title = NULL;
376 }
377 if(res_desc)
378 {
379 g_list_free_full(res_desc, dt_free_gpointer);
380 res_desc = NULL;
381 }
382
383 // export image to file. need this to be able to access meaningful
384 // fdata->width and height below.
385 if(dt_imageio_export(imgid, filename, format, fdata, TRUE, TRUE, export_masks, icc_type,
386 icc_filename, icc_intent, self, sdata, num, total, metadata) != 0)
387 {
388 fprintf(stderr, "[imageio_storage_gallery] could not export to file: `%s'!\n", filename);
389 dt_control_log(_("could not export to file `%s'!"), 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 if(dt_imageio_export(imgid, filename, format, fdata, FALSE, FALSE, export_masks, icc_type, icc_filename,
426 icc_intent, self, sdata, num, total, NULL) != 0)
427 {
428 fprintf(stderr, "[imageio_storage_gallery] could not export to file: `%s'!\n", filename);
429 dt_control_log(_("could not export to file `%s'!"), 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
611{
612 if(IS_NULL_PTR(params)) return;
615 dt_free(params);
616}
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.
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.
@ DT_DEFAULT
Definition conf.h:135
const char darktable_package_string[]
void dt_control_log(const char *msg,...)
Definition control.c:824
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:174
void gui_cleanup(dt_imageio_module_storage_t *self)
Definition gallery.c:227
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:179
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:232
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:110
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:138
static void entry_changed_callback(GtkEntry *entry, gpointer user_data)
Definition gallery.c:169
static gint sort_pos(pair_t *a, pair_t *b)
Definition gallery.c:241
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 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
#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 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:82
GtkEntry * title_entry
Definition gallery.c:83
int pos
Definition gallery.c:101
char item[4096]
Definition gallery.c:100
char line[4096]
Definition gallery.c:99
Telling the user something happened.
gchar * dt_util_str_replace(const gchar *string, const gchar *pattern, const gchar *substitute)
Definition utility.c:140
void dt_copy_resource_file(const char *src, const char *dst)
Definition utility.c:952
gchar * dt_util_fix_path(const gchar *path)
Definition utility.c:226
#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