1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /*
4 * GThumb
5 *
6 * Copyright (C) 2003-2010 Free Software Foundation, Inc.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include <config.h>
23 #include <string.h>
24 #include <stdio.h>
25 #include <sys/types.h>
26 #include <unistd.h>
27 #include <time.h>
28 #include <locale.h>
29 #include <ctype.h>
30 #include <glib/gi18n.h>
31 #include <gtk/gtk.h>
32 #include <gthumb.h>
33 #include <extensions/image_rotation/rotation-utils.h>
34 #include "gth-web-exporter.h"
35 #include "albumtheme-private.h"
36 #include "preferences.h"
37
38 #define DEFAULT_DATE_FORMAT ("%x, %X")
39 #define DEFAULT_THUMB_SIZE 100
40 #define DEFAULT_INDEX_FILE "index.html"
41 #define SAVING_TIMEOUT 5
42
43
44 typedef enum {
45 GTH_TEMPLATE_TYPE_INDEX,
46 GTH_TEMPLATE_TYPE_IMAGE,
47 GTH_TEMPLATE_TYPE_THUMBNAIL,
48 GTH_TEMPLATE_TYPE_FRAGMENT
49 } GthTemplateType;
50
51 typedef enum {
52 GTH_VISIBILITY_ALWAYS = 0,
53 GTH_VISIBILITY_INDEX,
54 GTH_VISIBILITY_IMAGE
55 } GthTagVisibility;
56
57 typedef enum {
58 GTH_IMAGE_TYPE_IMAGE = 0,
59 GTH_IMAGE_TYPE_THUMBNAIL,
60 GTH_IMAGE_TYPE_PREVIEW
61 } GthAttrImageType;
62
63
64 extern GFileInputStream *yy_istream;
65
66
67 typedef struct {
68 GthFileData *file_data;
69 char *dest_filename;
70 GthImage *image;
71 int image_width, image_height;
72 GthImage *thumb;
73 int thumb_width, thumb_height;
74 GthImage *preview;
75 int preview_width, preview_height;
76 gboolean caption_set;
77 gboolean no_preview;
78 } ImageData;
79
80
81 #define IMAGE_DATA(x) ((ImageData*)(x))
82
83
84 typedef struct {
85 char *previews;
86 char *thumbnails;
87 char *images;
88 char *html_images;
89 char *html_indexes;
90 char *theme_files;
91 } AlbumDirs;
92
93
94 typedef struct {
95 int ref;
96 gboolean first_item;
97 gboolean last_item;
98 gboolean item_is_empty;
99 int item_index;
100 GthFileData *item;
101 char *attribute;
102 char *iterator;
103 int iterator_value;
104 } LoopInfo;
105
106
107 struct _GthWebExporterPrivate {
108 GthBrowser *browser;
109 GList *gfile_list; /* GFile list */
110
111 /* options */
112
113 char *header;
114 char *footer;
115 char *image_page_header;
116 char *image_page_footer;
117 GFile *style_dir;
118 GFile *target_dir; /* Save files in this location. */
119 gboolean use_subfolders;
120 AlbumDirs directories;
121 char *index_file;
122 gboolean copy_images;
123 gboolean resize_images;
124 int resize_max_width;
125 int resize_max_height;
126 GthFileDataSort *sort_type;
127 gboolean sort_inverse;
128 int images_per_index;
129 gboolean single_index;
130 int columns_per_page;
131 int rows_per_page;
132 gboolean adapt_to_width;
133 gboolean squared_thumbnails;
134 int thumb_width;
135 int thumb_height;
136 int preview_max_width;
137 int preview_max_height;
138 int preview_min_width;
139 int preview_min_height;
140 gboolean image_description_enabled;
141 char *image_attributes;
142 char *thumbnail_caption;
143
144 /* private date */
145
146 GList *file_list; /* ImageData list */
147 GFile *tmp_dir;
148 GthImageLoader *iloader;
149 GList *current_file; /* Next file to be loaded. */
150 int n_images; /* Used for the progress signal. */
151 int n_pages;
152 int image;
153 int page;
154 GList *index_template;
155 GList *thumbnail_template;
156 GList *image_template;
157 guint saving_timeout;
158 ImageData *eval_image;
159 LoopInfo *loop_info;
160 GError *error;
161 gboolean interrupted;
162 };
163
164
G_DEFINE_TYPE_WITH_CODE(GthWebExporter,gth_web_exporter,GTH_TYPE_TASK,G_ADD_PRIVATE (GthWebExporter))165 G_DEFINE_TYPE_WITH_CODE (GthWebExporter,
166 gth_web_exporter,
167 GTH_TYPE_TASK,
168 G_ADD_PRIVATE (GthWebExporter))
169
170
171 static LoopInfo *
172 loop_info_new (void)
173 {
174 LoopInfo *info;
175
176 info = g_new0 (LoopInfo, 1);
177 info->ref = 1;
178 info->first_item = FALSE;
179 info->last_item = FALSE;
180 info->item = NULL;
181 info->attribute = NULL;
182 info->iterator = NULL;
183
184 return info;
185 }
186
187
188 G_GNUC_UNUSED
189 static void
loop_info_ref(LoopInfo * info)190 loop_info_ref (LoopInfo *info)
191 {
192 info->ref++;
193 }
194
195
196 static void
loop_info_unref(LoopInfo * info)197 loop_info_unref (LoopInfo *info)
198 {
199 info->ref--;
200 if (info->ref > 0)
201 return;
202 _g_object_unref (info->item);
203 g_free (info->attribute);
204 g_free (info->iterator);
205 g_free (info);
206 }
207
208
209 static ImageData *
image_data_new(GthFileData * file_data,int file_idx)210 image_data_new (GthFileData *file_data,
211 int file_idx)
212 {
213 ImageData *idata;
214
215 idata = g_new0 (ImageData, 1);
216 idata->file_data = g_object_ref (file_data);
217 idata->dest_filename = g_strdup_printf ("%03d-%s", file_idx, g_file_info_get_name (file_data->info));
218
219 idata->image = NULL;
220 idata->image_width = 0;
221 idata->image_height = 0;
222
223 idata->thumb = NULL;
224 idata->thumb_width = 0;
225 idata->thumb_height = 0;
226
227 idata->preview = NULL;
228 idata->preview_width = 0;
229 idata->preview_height = 0;
230
231 idata->caption_set = FALSE;
232 idata->no_preview = FALSE;
233
234 return idata;
235 }
236
237
238 static void
image_data_free(ImageData * idata)239 image_data_free (ImageData *idata)
240 {
241 _g_object_unref (idata->preview);
242 _g_object_unref (idata->thumb);
243 _g_object_unref (idata->image);
244 g_free (idata->dest_filename);
245 _g_object_unref (idata->file_data);
246 g_free (idata);
247 }
248
249
250 static void
free_parsed_docs(GthWebExporter * self)251 free_parsed_docs (GthWebExporter *self)
252 {
253 if (self->priv->index_template != NULL) {
254 gth_parsed_doc_free (self->priv->index_template);
255 self->priv->index_template = NULL;
256 }
257
258 if (self->priv->thumbnail_template != NULL) {
259 gth_parsed_doc_free (self->priv->thumbnail_template);
260 self->priv->thumbnail_template = NULL;
261 }
262
263 if (self->priv->image_template != NULL) {
264 gth_parsed_doc_free (self->priv->image_template);
265 self->priv->image_template = NULL;
266 }
267 }
268
269
270 static GFile *
get_style_dir(GthWebExporter * self,const char * style_name)271 get_style_dir (GthWebExporter *self,
272 const char *style_name)
273 {
274 GFile *style_dir;
275 GFile *data_dir;
276
277 if (style_name == NULL)
278 return NULL;
279
280 /* search in local themes */
281
282 style_dir = gth_user_dir_get_file_for_read (GTH_DIR_DATA, GTHUMB_DIR, "albumthemes", style_name, NULL);
283 if (g_file_query_exists (style_dir, NULL))
284 return style_dir;
285
286 g_object_unref (style_dir);
287
288 /* search in system themes */
289
290 data_dir = g_file_new_for_path (WEBALBUM_DATADIR);
291 style_dir = _g_file_get_child (data_dir, "albumthemes", style_name, NULL);
292 g_object_unref (data_dir);
293 if (g_file_query_exists (style_dir, NULL))
294 return style_dir;
295
296 g_object_unref (style_dir);
297
298 return NULL;
299 }
300
301
302 #define IMAGE_FIELD(image, field) ((image != NULL) ? image->field : 0)
303
304
305 static int
get_var_value(GthExpr * expr,int * index,const char * var_name,gpointer data)306 get_var_value (GthExpr *expr,
307 int *index,
308 const char *var_name,
309 gpointer data)
310 {
311 GthWebExporter *self = data;
312
313 if (strcmp (var_name, "image_idx") == 0)
314 return self->priv->image + 1;
315 else if (strcmp (var_name, "images") == 0)
316 return self->priv->n_images;
317 else if (strcmp (var_name, "page_idx") == 0)
318 return self->priv->page + 1;
319 else if (strcmp (var_name, "page_rows") == 0)
320 return self->priv->rows_per_page;
321 else if (strcmp (var_name, "page_cols") == 0)
322 return self->priv->columns_per_page;
323 else if (strcmp (var_name, "pages") == 0)
324 return self->priv->n_pages;
325 else if (strcmp (var_name, "preview_min_width") == 0)
326 return self->priv->preview_min_width;
327 else if (strcmp (var_name, "preview_min_height") == 0)
328 return self->priv->preview_min_height;
329 else if (strcmp (var_name, "index") == 0)
330 return GTH_VISIBILITY_INDEX;
331 else if (strcmp (var_name, "image") == 0)
332 return GTH_VISIBILITY_IMAGE;
333 else if (strcmp (var_name, "always") == 0)
334 return GTH_VISIBILITY_ALWAYS;
335
336 else if (strcmp (var_name, "image_width") == 0)
337 return IMAGE_FIELD (self->priv->eval_image, image_width);
338 else if (strcmp (var_name, "image_height") == 0)
339 return IMAGE_FIELD (self->priv->eval_image, image_height);
340 else if (strcmp (var_name, "preview_width") == 0)
341 return IMAGE_FIELD (self->priv->eval_image, preview_width);
342 else if (strcmp (var_name, "preview_height") == 0)
343 return IMAGE_FIELD (self->priv->eval_image, preview_height);
344 else if (strcmp (var_name, "thumb_width") == 0)
345 return IMAGE_FIELD (self->priv->eval_image, thumb_width);
346 else if (strcmp (var_name, "thumb_height") == 0)
347 return IMAGE_FIELD (self->priv->eval_image, thumb_height);
348
349 else if (g_str_equal (var_name, "first_item"))
350 return (self->priv->loop_info != NULL) ? self->priv->loop_info->first_item : FALSE;
351 else if (g_str_equal (var_name, "last_item"))
352 return (self->priv->loop_info != NULL) ? self->priv->loop_info->last_item : FALSE;
353 else if (g_str_equal (var_name, "item_is_empty"))
354 return (self->priv->loop_info != NULL) ? self->priv->loop_info->item_is_empty : TRUE;
355
356 else if (g_str_equal (var_name, "image_attribute_available")) {
357 GthCell *cell;
358
359 cell = gth_expr_get_pos (expr, (*index) + 1);
360 if ((cell != NULL) && (cell->type == GTH_CELL_TYPE_STRING)) {
361 const char *attribute_id;
362 char *value;
363 int result;
364
365 attribute_id = cell->value.string->str;
366 value = gth_file_data_get_attribute_as_string (self->priv->eval_image->file_data, attribute_id);
367 result = (value != NULL);
368 *index += 1;
369
370 g_free (value);
371
372 return result;
373 }
374 else
375 return 0;
376 }
377 else if (strcmp (var_name, "copy_originals") == 0) {
378 return self->priv->copy_images;
379 }
380 else if (g_str_equal (var_name, "image_description_enabled")) {
381 return self->priv->image_description_enabled;
382 }
383 else if (strcmp (var_name, "image_attributes_enabled") == 0) {
384 return ! g_str_equal (self->priv->image_attributes, "");
385 }
386 else if (g_str_equal (var_name, "image_attribute_enabled")) {
387 GthCell *cell;
388
389 cell = gth_expr_get_pos (expr, (*index) + 1);
390 if ((cell != NULL) && (cell->type == GTH_CELL_TYPE_STRING)) {
391 const char *attribute_id;
392 int result;
393
394 attribute_id = cell->value.string->str;
395 result = _g_file_attributes_matches_any (attribute_id, self->priv->image_attributes);
396 *index += 1;
397
398 return result;
399 }
400 else
401 return 0;
402 }
403
404 else if ((self->priv->loop_info != NULL) && g_str_equal (var_name, self->priv->loop_info->iterator))
405 return self->priv->loop_info->iterator_value;
406
407 g_warning ("[GetVarValue] Unknown variable name: %s", var_name);
408
409 return 0;
410 }
411
412
413 static int
expression_value(GthWebExporter * self,GthExpr * expr)414 expression_value (GthWebExporter *self,
415 GthExpr *expr)
416 {
417 gth_expr_set_get_var_value_func (expr, get_var_value, self);
418 return gth_expr_eval (expr);
419 }
420
421
422 static int
gth_tag_get_idx(GthTag * tag,GthWebExporter * self,int default_value,int max_value)423 gth_tag_get_idx (GthTag *tag,
424 GthWebExporter *self,
425 int default_value,
426 int max_value)
427 {
428 GList *scan;
429 int retval = default_value;
430
431 if ((tag->type == GTH_TAG_HTML)
432 || (tag->type == GTH_TAG_IF)
433 || (tag->type == GTH_TAG_FOR_EACH_THUMBNAIL_CAPTION)
434 || (tag->type == GTH_TAG_FOR_EACH_IMAGE_CAPTION)
435 || (tag->type == GTH_TAG_FOR_EACH_IN_RANGE)
436 || (tag->type == GTH_TAG_INVALID))
437 {
438 return 0;
439 }
440
441 for (scan = tag->value.attributes; scan; scan = scan->next) {
442 GthAttribute *attribute = scan->data;
443
444 if (strcmp (attribute->name, "idx_relative") == 0) {
445 retval = default_value + expression_value (self, attribute->value.expr);
446 break;
447
448 } else if (strcmp (attribute->name, "idx") == 0) {
449 retval = expression_value (self, attribute->value.expr) - 1;
450 break;
451 }
452 }
453
454 retval = MIN (retval, max_value);
455 retval = MAX (retval, 0);
456
457 return retval;
458 }
459
460
461 static int
get_image_idx(GthTag * tag,GthWebExporter * self)462 get_image_idx (GthTag *tag,
463 GthWebExporter *self)
464 {
465 return gth_tag_get_idx (tag, self, self->priv->image, self->priv->n_images - 1);
466 }
467
468
469 static int
get_page_idx(GthTag * tag,GthWebExporter * self)470 get_page_idx (GthTag *tag,
471 GthWebExporter *self)
472 {
473 return gth_tag_get_idx (tag, self, self->priv->page, self->priv->n_pages - 1);
474 }
475
476
477 static int
gth_tag_has_attribute(GthWebExporter * self,GthTag * tag,const char * attribute_name)478 gth_tag_has_attribute (GthWebExporter *self,
479 GthTag *tag,
480 const char *attribute_name)
481 {
482 GList *scan;
483
484 for (scan = tag->value.attributes; scan; scan = scan->next) {
485 GthAttribute *attribute = scan->data;
486 if (strcmp (attribute->name, attribute_name) == 0)
487 return TRUE;
488 }
489
490 return FALSE;
491 }
492
493
494 static int
gth_tag_get_attribute_int(GthWebExporter * self,GthTag * tag,const char * attribute_name)495 gth_tag_get_attribute_int (GthWebExporter *self,
496 GthTag *tag,
497 const char *attribute_name)
498 {
499 GList *scan;
500
501 for (scan = tag->value.attributes; scan; scan = scan->next) {
502 GthAttribute *attribute = scan->data;
503 if (strcmp (attribute->name, attribute_name) == 0)
504 return expression_value (self, attribute->value.expr);
505 }
506
507 return 0;
508 }
509
510
511 static const char *
gth_tag_get_attribute_string(GthWebExporter * self,GthTag * tag,const char * attribute_name)512 gth_tag_get_attribute_string (GthWebExporter *self,
513 GthTag *tag,
514 const char *attribute_name)
515 {
516 GList *scan;
517
518 for (scan = tag->value.attributes; scan; scan = scan->next) {
519 GthAttribute *attribute = scan->data;
520
521 if (strcmp (attribute->name, attribute_name) == 0) {
522 if (attribute->type == GTH_ATTRIBUTE_EXPR) {
523 GthCell *cell;
524
525 cell = gth_expr_get (attribute->value.expr);
526 if (cell->type == GTH_CELL_TYPE_VAR)
527 return cell->value.var;
528 }
529 else if (attribute->type == GTH_ATTRIBUTE_STRING)
530 return attribute->value.string;
531 else
532 return NULL;
533 }
534 }
535
536 return NULL;
537 }
538
539
540 /* -- gth_tag_translate_get_string -- */
541
542
543 typedef struct {
544 GthWebExporter *self;
545 GthTag *tag;
546 GList *attribute_p;
547 GError **error;
548 } TranslateData;
549
550
551 static gboolean
translate_eval_cb(const GMatchInfo * info,GString * res,gpointer data)552 translate_eval_cb (const GMatchInfo *info,
553 GString *res,
554 gpointer data)
555 {
556 TranslateData *translate_data = data;
557 GthAttribute *attribute;
558 char *match;
559
560 if (translate_data->attribute_p == NULL) {
561 *translate_data->error = g_error_new_literal (GTH_TASK_ERROR, GTH_TASK_ERROR_FAILED, _("Malformed command"));
562 return TRUE;
563 }
564
565 attribute = translate_data->attribute_p->data;
566 match = g_match_info_fetch (info, 0);
567 if (strcmp (match, "%s") == 0) {
568 if (attribute->type == GTH_ATTRIBUTE_STRING) {
569 g_string_append (res, attribute->value.string);
570 translate_data->attribute_p = translate_data->attribute_p->next;
571 }
572 else
573 *translate_data->error = g_error_new_literal (GTH_TASK_ERROR, GTH_TASK_ERROR_FAILED, _("Malformed command"));
574 }
575 else if (strcmp (match, "%d") == 0) {
576 if (attribute->type == GTH_ATTRIBUTE_EXPR) {
577 g_string_append_printf (res, "%d", expression_value (translate_data->self, attribute->value.expr));
578 translate_data->attribute_p = translate_data->attribute_p->next;
579 }
580 else
581 *translate_data->error = g_error_new_literal (GTH_TASK_ERROR, GTH_TASK_ERROR_FAILED, _("Malformed command"));
582 }
583
584 g_free (match);
585
586 return (*translate_data->error != NULL);
587 }
588
589
590 static char *
gth_tag_translate_get_string(GthWebExporter * self,GthTag * tag)591 gth_tag_translate_get_string (GthWebExporter *self,
592 GthTag *tag)
593 {
594 TranslateData *translate_data;
595 GRegex *re;
596 GError *error = NULL;
597 char *result;
598
599 if (tag->value.attributes == NULL)
600 return NULL;
601
602 if (tag->value.attributes->next == NULL)
603 return g_strdup (_(gth_tag_get_attribute_string (self, tag, "text")));
604
605 translate_data = g_new0 (TranslateData, 1);
606 translate_data->self = self;
607 translate_data->tag = tag;
608 translate_data->attribute_p = tag->value.attributes->next;
609 translate_data->error = &error;
610
611 re = g_regex_new ("%d|%s", 0, 0, NULL);
612 result = g_regex_replace_eval (re, _(gth_tag_get_attribute_string (self, tag, "text")), -1, 0, 0, translate_eval_cb, translate_data, &error);
613 if (error != NULL) {
614 result = g_strdup (error->message);
615 g_clear_error (&error);
616 }
617
618 g_regex_unref (re);
619 g_free (translate_data);
620
621 return result;
622 }
623
624
625 static int
get_page_idx_from_image_idx(GthWebExporter * self,int image_idx)626 get_page_idx_from_image_idx (GthWebExporter *self,
627 int image_idx)
628 {
629 if (self->priv->single_index)
630 return 0;
631 else
632 return image_idx / self->priv->images_per_index;
633 }
634
635
636 static gboolean
line_is_void(const char * line)637 line_is_void (const char *line)
638 {
639 const char *scan;
640
641 if (line == NULL)
642 return TRUE;
643
644 for (scan = line; *scan != '\0'; scan++)
645 if ((*scan != ' ') && (*scan != '\t') && (*scan != '\n'))
646 return FALSE;
647
648 return TRUE;
649 }
650
651
652 /* write a line when no error is pending */
653 static void
_write_line(GFileOutputStream * ostream,const char * line,GError ** error)654 _write_line (GFileOutputStream *ostream,
655 const char *line,
656 GError **error)
657 {
658 if ((error != NULL) && (*error != NULL))
659 return;
660
661 g_output_stream_write_all (G_OUTPUT_STREAM (ostream),
662 line,
663 strlen (line),
664 NULL,
665 NULL,
666 error);
667 }
668
669
670 static void
_write_locale_line(GFileOutputStream * ostream,const char * line,GError ** error)671 _write_locale_line (GFileOutputStream *ostream,
672 const char *line,
673 GError **error)
674 {
675 char *utf8_line;
676
677 utf8_line = g_locale_to_utf8 (line, -1, 0, 0, error);
678 _write_line (ostream, utf8_line, error);
679
680 g_free (utf8_line);
681 }
682
683
684 static void
write_line(GFileOutputStream * ostream,const char * line,GError ** error)685 write_line (GFileOutputStream *ostream,
686 const char *line,
687 GError **error)
688 {
689 if (! line_is_void (line))
690 _write_line (ostream, line, error);
691 }
692
693
694 static void
write_markup_escape_line(GFileOutputStream * ostream,const char * line,GError ** error)695 write_markup_escape_line (GFileOutputStream *ostream,
696 const char *line,
697 GError **error)
698 {
699 char *e_line;
700
701 if (line_is_void (line))
702 return;
703
704 e_line = _g_utf8_text_escape_xml (line);
705 _write_line (ostream, e_line, error);
706
707 g_free (e_line);
708 }
709
710
711 static void
write_markup_escape_locale_line(GFileOutputStream * ostream,const char * line,GError ** error)712 write_markup_escape_locale_line (GFileOutputStream *ostream,
713 const char *line,
714 GError **error)
715 {
716 char *e_line;
717
718 if ((line == NULL) || (*line == 0))
719 return;
720
721 e_line = _g_utf8_text_escape_xml (line);
722 _write_locale_line (ostream, e_line, error);
723
724 g_free (e_line);
725 }
726
727
728 static char *
get_image_attribute(GthWebExporter * self,GthTag * tag,const char * attribute,ImageData * image_data)729 get_image_attribute (GthWebExporter *self,
730 GthTag *tag,
731 const char *attribute,
732 ImageData *image_data)
733 {
734 char *value;
735 int max_length;
736 char *line = NULL;
737
738 value = gth_file_data_get_attribute_as_string (image_data->file_data, attribute);
739 if (value == NULL)
740 return NULL;
741
742 max_length = gth_tag_get_attribute_int (self, tag, "max_length");
743 if (max_length > 0) {
744 char *truncated;
745
746 truncated = g_strndup (value, max_length);
747 if (strlen (truncated) < strlen (value))
748 line = g_strconcat (truncated, "…", NULL);
749 else
750 line = g_strdup (truncated);
751
752 g_free (truncated);
753 }
754 else
755 line = g_strdup (value);
756
757 g_free (value);
758
759 return line;
760 }
761
762
763 /* GFile to string */
764
765
766 static char *
gfile_get_relative_path(GFile * file,GFile * base)767 gfile_get_relative_path (GFile *file,
768 GFile *base)
769 {
770 char *uri;
771 char *base_uri;
772 char *result;
773
774 uri = g_file_get_uri (file);
775 base_uri = g_file_get_uri (base);
776 result = _g_uri_get_relative_path (uri, base_uri);
777
778 g_free (base_uri);
779 g_free (uri);
780
781 return result;
782 }
783
784
785 /* construct a GFile for a GthWebExporter */
786
787
788 static GFile *
get_album_file(GthWebExporter * self,GFile * target_dir,const char * subdir,const char * filename)789 get_album_file (GthWebExporter *self,
790 GFile *target_dir,
791 const char *subdir,
792 const char *filename)
793 {
794 return _g_file_get_child (target_dir,
795 (self->priv->use_subfolders ? subdir : filename),
796 (self->priv->use_subfolders ? filename : NULL),
797 NULL);
798 }
799
800
801 static GFile *
get_html_index_dir(GthWebExporter * self,const int page,GFile * target_dir)802 get_html_index_dir (GthWebExporter *self,
803 const int page,
804 GFile *target_dir)
805 {
806 if (page == 0)
807 return g_file_dup (target_dir);
808 else
809 return get_album_file (self, target_dir, self->priv->directories.html_indexes, NULL);
810 }
811
812
813 static GFile *
get_html_image_dir(GthWebExporter * self,GFile * target_dir)814 get_html_image_dir (GthWebExporter *self,
815 GFile *target_dir)
816 {
817 return get_album_file (self, target_dir, self->priv->directories.html_images, NULL);
818 }
819
820
821 static GFile *
get_theme_file(GthWebExporter * self,GFile * target_dir,const char * filename)822 get_theme_file (GthWebExporter *self,
823 GFile *target_dir,
824 const char *filename)
825 {
826 return get_album_file (self, target_dir, self->priv->directories.theme_files, filename);
827 }
828
829
830 static GFile *
get_html_index_file(GthWebExporter * self,const int page,GFile * target_dir)831 get_html_index_file (GthWebExporter *self,
832 const int page,
833 GFile *target_dir)
834 {
835 char *filename;
836 GFile *dir;
837 GFile *result;
838
839 if (page == 0)
840 filename = g_strdup (self->priv->index_file);
841 else
842 filename = g_strdup_printf ("page%03d.html", page + 1);
843 dir = get_html_index_dir (self, page, target_dir);
844 result = g_file_get_child (dir, filename);
845
846 g_object_unref (dir);
847 g_free (filename);
848
849 return result;
850 }
851
852
853 static GFile *
get_html_image_file(GthWebExporter * self,ImageData * image_data,GFile * target_dir)854 get_html_image_file (GthWebExporter *self,
855 ImageData *image_data,
856 GFile *target_dir)
857 {
858 char *filename;
859 GFile *result;
860
861 filename = g_strconcat (image_data->dest_filename, ".html", NULL);
862 result = get_album_file (self, target_dir, self->priv->directories.html_images, filename);
863
864 g_free (filename);
865
866 return result;
867 }
868
869
870 static GFile *
get_thumbnail_file(GthWebExporter * self,ImageData * image_data,GFile * target_dir)871 get_thumbnail_file (GthWebExporter *self,
872 ImageData *image_data,
873 GFile *target_dir)
874 {
875 char *filename;
876 GFile *result;
877
878 filename = g_strconcat (image_data->dest_filename, ".small", ".jpeg", NULL);
879 result = get_album_file (self, target_dir, self->priv->directories.thumbnails, filename);
880
881 g_free (filename);
882
883 return result;
884 }
885
886
887 static GFile *
get_image_file(GthWebExporter * self,ImageData * image_data,GFile * target_dir)888 get_image_file (GthWebExporter *self,
889 ImageData *image_data,
890 GFile *target_dir)
891 {
892 GFile *result;
893
894 if (self->priv->copy_images)
895 result = get_album_file (self, target_dir, self->priv->directories.images, image_data->dest_filename);
896 else
897 result = g_file_dup (image_data->file_data->file);
898
899 return result;
900 }
901
902
903 static GFile *
get_preview_file(GthWebExporter * self,ImageData * image_data,GFile * target_dir)904 get_preview_file (GthWebExporter *self,
905 ImageData *image_data,
906 GFile *target_dir)
907 {
908 GFile *result;
909
910 if (image_data->no_preview) {
911 result = get_image_file (self, image_data, target_dir);
912 }
913 else {
914 char *filename;
915
916 filename = g_strconcat (image_data->dest_filename, ".medium", ".jpeg", NULL);
917 result = get_album_file (self, target_dir, self->priv->directories.previews, filename);
918
919 g_free (filename);
920 }
921
922 return result;
923 }
924
925
926 static char *
get_current_date(const char * format)927 get_current_date (const char *format)
928 {
929 GTimeVal timeval;
930 char *s;
931 char *u;
932
933 g_get_current_time (&timeval);
934 s = _g_time_val_strftime (&timeval, format);
935 u = g_locale_to_utf8 (s, -1, 0, 0, 0);
936
937 g_free (s);
938
939 return u;
940 }
941
942
943 static int
is_alpha_string(char * s,size_t maxlen)944 is_alpha_string (char *s,
945 size_t maxlen)
946 {
947 if (s == NULL)
948 return 0;
949
950 while ((maxlen > 0) && (*s != '\0') && isalpha (*s)) {
951 maxlen--;
952 s++;
953 }
954
955 return ((maxlen == 0) || (*s == '\0'));
956 }
957
958
959 static char*
get_current_language(void)960 get_current_language (void)
961 {
962 char *language = NULL;
963 char *tmp_locale;
964 char *locale;
965 char *underline;
966 size_t len;
967
968 tmp_locale = setlocale (LC_ALL, NULL);
969 if (tmp_locale == NULL)
970 return NULL;
971 locale = g_strdup (tmp_locale);
972
973 /* FIXME: complete LC_ALL -> RFC 3066 */
974
975 underline = strchr (locale, '_');
976 if (underline != NULL)
977 *underline = '\0';
978
979 len = strlen (locale);
980 if (((len == 2) || (len == 3)) && is_alpha_string (locale, len))
981 language = g_locale_to_utf8 (locale, -1, 0, 0, 0);
982
983 g_free (locale);
984
985 return language;
986 }
987
988
989 static gboolean
header_footer_eval_cb(const GMatchInfo * match_info,GString * result,gpointer user_data)990 header_footer_eval_cb (const GMatchInfo *match_info,
991 GString *result,
992 gpointer user_data)
993 {
994 GthWebExporter *self = user_data;
995 char *r = NULL;
996 char *match;
997
998 match = g_match_info_fetch (match_info, 0);
999 if (strcmp (match, "%p") == 0) {
1000 r = g_strdup_printf ("%d", self->priv->page + 1);
1001 }
1002 else if (strcmp (match, "%P") == 0) {
1003 r = g_strdup_printf ("%d", self->priv->n_pages);
1004 }
1005 else if (strcmp (match, "%i") == 0) {
1006 r = g_strdup_printf ("%d", self->priv->image + 1);
1007 }
1008 else if (strcmp (match, "%I") == 0) {
1009 r = g_strdup_printf ("%d", self->priv->n_images);
1010 }
1011 else if (strncmp (match, "%D", 2) == 0) {
1012 GTimeVal timeval;
1013 GRegex *re;
1014 char **a;
1015 char *format = NULL;
1016
1017 g_get_current_time (&timeval);
1018
1019 /* Get the date format */
1020
1021 re = g_regex_new ("%[A-Z]\\{([^}]+)\\}", 0, 0, NULL);
1022 a = g_regex_split (re, match, 0);
1023 if (g_strv_length (a) >= 2)
1024 format = g_strstrip (a[1]);
1025 else
1026 format = DEFAULT_DATE_FORMAT;
1027 r = _g_time_val_strftime (&timeval, format);
1028
1029 g_strfreev (a);
1030 g_regex_unref (re);
1031 }
1032 else if (strcmp (match, "%F") == 0) {
1033 GList *link;
1034
1035 link = g_list_nth (self->priv->file_list, self->priv->image);
1036 if (link != NULL) {
1037 ImageData *idata = link->data;
1038 r = g_strdup (g_file_info_get_display_name (idata->file_data->info));
1039 }
1040 }
1041 else if (strcmp (match, "%C") == 0) {
1042 GList *link;
1043
1044 link = g_list_nth (self->priv->file_list, self->priv->image);
1045 if (link != NULL) {
1046 ImageData *idata = link->data;
1047 r = gth_file_data_get_attribute_as_string (idata->file_data, "general::description");
1048 }
1049 }
1050
1051 if (r != NULL)
1052 g_string_append (result, r);
1053
1054 g_free (r);
1055 g_free (match);
1056
1057 return FALSE;
1058 }
1059
1060
1061 static char *
get_header_footer_text(GthWebExporter * self,const char * utf8_text)1062 get_header_footer_text (GthWebExporter *self,
1063 const char *utf8_text)
1064 {
1065 GRegex *re;
1066 char *new_text;
1067
1068 if (utf8_text == NULL)
1069 return NULL;
1070
1071 if (g_utf8_strchr (utf8_text, -1, '%') == NULL)
1072 return g_strdup (utf8_text);
1073
1074 re = g_regex_new ("%[pPiIDFC](\\{[^}]+\\})?", 0, 0, NULL);
1075 new_text = g_regex_replace_eval (re, utf8_text, -1, 0, 0, header_footer_eval_cb, self, NULL);
1076 g_regex_unref (re);
1077
1078 return new_text;
1079 }
1080
1081
1082 static GthAttrImageType
get_attr_image_type_from_tag(GthWebExporter * self,GthTag * tag)1083 get_attr_image_type_from_tag (GthWebExporter *self,
1084 GthTag *tag)
1085 {
1086 if (gth_tag_get_attribute_int (self, tag, "thumbnail") != 0)
1087 return GTH_IMAGE_TYPE_THUMBNAIL;
1088
1089 if (gth_tag_get_attribute_int (self, tag, "preview") != 0)
1090 return GTH_IMAGE_TYPE_PREVIEW;
1091
1092 return GTH_IMAGE_TYPE_IMAGE;
1093 }
1094
1095
1096 static void
gth_parsed_doc_print(GthWebExporter * self,GList * document,GthTemplateType template_type,LoopInfo * loop_info,GFile * relative_to,GFileOutputStream * ostream,GError ** error)1097 gth_parsed_doc_print (GthWebExporter *self,
1098 GList *document,
1099 GthTemplateType template_type,
1100 LoopInfo *loop_info,
1101 GFile *relative_to,
1102 GFileOutputStream *ostream,
1103 GError **error)
1104 {
1105 GList *scan;
1106
1107 self->priv->loop_info = loop_info;
1108
1109 for (scan = document; scan; scan = scan->next) {
1110 GthTag *tag = scan->data;
1111 ImageData *idata;
1112 GFile *file = NULL;
1113 GFile *dir;
1114 char *line = NULL;
1115 char *image_src = NULL;
1116 char *unescaped_path = NULL;
1117 int idx;
1118 int image_width;
1119 int image_height;
1120 int max_length;
1121 int r, c;
1122 int value;
1123 const char *src;
1124 char *src_attr;
1125 const char *class = NULL;
1126 char *class_attr = NULL;
1127 const char *alt = NULL;
1128 char *alt_attr = NULL;
1129 const char *id = NULL;
1130 char *id_attr = NULL;
1131 gboolean relative;
1132 GList *scan;
1133
1134 if ((error != NULL) && (*error != NULL))
1135 return;
1136
1137 switch (tag->type) {
1138 case GTH_TAG_HEADER:
1139 if (template_type == GTH_TEMPLATE_TYPE_INDEX)
1140 line = get_header_footer_text (self, self->priv->header);
1141 else if (template_type == GTH_TEMPLATE_TYPE_IMAGE)
1142 line = get_header_footer_text (self, self->priv->image_page_header ? self->priv->image_page_header : self->priv->header);
1143 write_markup_escape_line (ostream, line, error);
1144 break;
1145
1146 case GTH_TAG_FOOTER:
1147 if (template_type == GTH_TEMPLATE_TYPE_INDEX)
1148 line = get_header_footer_text (self, self->priv->footer);
1149 else if (template_type == GTH_TEMPLATE_TYPE_IMAGE)
1150 line = get_header_footer_text (self, self->priv->image_page_footer ? self->priv->image_page_footer : self->priv->footer);
1151 if (line != NULL)
1152 write_markup_escape_line (ostream, line, error);
1153 break;
1154
1155 case GTH_TAG_LANGUAGE:
1156 line = get_current_language ();
1157 write_markup_escape_line (ostream, line, error);
1158 break;
1159
1160 case GTH_TAG_THEME_LINK:
1161 src = gth_tag_get_attribute_string (self, tag, "src");
1162 if (src == NULL)
1163 break;
1164 file = get_theme_file (self, self->priv->target_dir, src);
1165 line = gfile_get_relative_path (file, relative_to);
1166 write_markup_escape_line (ostream, line, error);
1167 g_object_unref (file);
1168 break;
1169
1170 case GTH_TAG_IMAGE:
1171 idx = get_image_idx (tag, self);
1172 idata = g_list_nth (self->priv->file_list, idx)->data;
1173 self->priv->eval_image = idata;
1174
1175 switch (get_attr_image_type_from_tag (self, tag)) {
1176 case GTH_IMAGE_TYPE_THUMBNAIL:
1177 file = get_thumbnail_file (self, idata, self->priv->target_dir);
1178 image_width = idata->thumb_width;
1179 image_height = idata->thumb_height;
1180 break;
1181
1182 case GTH_IMAGE_TYPE_PREVIEW:
1183 file = get_preview_file (self, idata, self->priv->target_dir);
1184 image_width = idata->preview_width;
1185 image_height = idata->preview_height;
1186 break;
1187
1188 case GTH_IMAGE_TYPE_IMAGE:
1189 file = get_image_file (self, idata, self->priv->target_dir);
1190 image_width = idata->image_width;
1191 image_height = idata->image_height;
1192 break;
1193 }
1194
1195 image_src = gfile_get_relative_path (file, relative_to);
1196 src_attr = _g_utf8_escape_xml (image_src);
1197
1198 class = gth_tag_get_attribute_string (self, tag, "class");
1199 if (class)
1200 class_attr = g_strdup_printf (" class=\"%s\"", class);
1201 else
1202 class_attr = g_strdup ("");
1203
1204 max_length = gth_tag_get_attribute_int (self, tag, "max_size");
1205 if (max_length > 0)
1206 scale_keeping_ratio (&image_width,
1207 &image_height,
1208 max_length,
1209 max_length,
1210 FALSE);
1211
1212 alt = gth_tag_get_attribute_string (self, tag, "alt");
1213 if (alt != NULL) {
1214 alt_attr = g_strdup (alt);
1215 }
1216 else {
1217 char *unescaped_path;
1218
1219 unescaped_path = g_uri_unescape_string (image_src, NULL);
1220 alt_attr = _g_utf8_escape_xml (unescaped_path);
1221 g_free (unescaped_path);
1222 }
1223
1224 id = gth_tag_get_attribute_string (self, tag, "id");
1225 if (id != NULL)
1226 id_attr = g_strdup_printf (" id=\"%s\"", id);
1227 else
1228 id_attr = g_strdup ("");
1229
1230 line = g_strdup_printf ("<img src=\"%s\" alt=\"%s\" width=\"%d\" height=\"%d\"%s%s />",
1231 src_attr,
1232 alt_attr,
1233 image_width,
1234 image_height,
1235 id_attr,
1236 class_attr);
1237 write_line (ostream, line, error);
1238
1239 g_free (src_attr);
1240 g_free (id_attr);
1241 g_free (alt_attr);
1242 g_free (class_attr);
1243 g_free (image_src);
1244 g_object_unref (file);
1245 break;
1246
1247 case GTH_TAG_IMAGE_LINK:
1248 idx = get_image_idx (tag, self);
1249 idata = g_list_nth (self->priv->file_list, idx)->data;
1250 file = get_html_image_file (self, idata, self->priv->target_dir);
1251 line = gfile_get_relative_path (file, relative_to);
1252 write_markup_escape_line (ostream, line, error);
1253 g_object_unref (file);
1254 break;
1255
1256 case GTH_TAG_IMAGE_IDX:
1257 line = g_strdup_printf ("%d", get_image_idx (tag, self) + 1);
1258 write_line (ostream, line, error);
1259 break;
1260
1261 case GTH_TAG_IMAGE_DIM:
1262 idx = get_image_idx (tag, self);
1263 idata = g_list_nth (self->priv->file_list, idx)->data;
1264 line = g_strdup_printf ("%d×%d", idata->image_width, idata->image_height);
1265 write_line (ostream, line, error);
1266 break;
1267
1268 case GTH_TAG_IMAGE_ATTRIBUTE:
1269 idx = get_image_idx (tag, self);
1270 idata = g_list_nth (self->priv->file_list, idx)->data;
1271 id = gth_tag_get_attribute_string (self, tag, "id");
1272 if (id != NULL) {
1273 line = get_image_attribute (self, tag, id, idata);
1274 write_line (ostream, line, error);
1275 }
1276 break;
1277
1278 case GTH_TAG_IMAGES:
1279 line = g_strdup_printf ("%d", self->priv->n_images);
1280 write_line (ostream, line, error);
1281 break;
1282
1283 case GTH_TAG_FILE_NAME:
1284 idx = get_image_idx (tag, self);
1285 idata = g_list_nth (self->priv->file_list, idx)->data;
1286 self->priv->eval_image = idata;
1287
1288 switch (get_attr_image_type_from_tag (self, tag)) {
1289 case GTH_IMAGE_TYPE_THUMBNAIL:
1290 file = get_thumbnail_file (self, idata, self->priv->target_dir);
1291 break;
1292
1293 case GTH_IMAGE_TYPE_PREVIEW:
1294 file = get_preview_file (self, idata, self->priv->target_dir);
1295 break;
1296
1297 case GTH_IMAGE_TYPE_IMAGE:
1298 file = get_image_file (self, idata, self->priv->target_dir);
1299 break;
1300 }
1301
1302 relative = (gth_tag_get_attribute_int (self, tag, "with_relative_path") != 0);
1303
1304 if (relative)
1305 unescaped_path = gfile_get_relative_path (file, relative_to);
1306 else
1307 unescaped_path = g_file_get_path (file);
1308
1309 if (relative || (gth_tag_get_attribute_int (self, tag, "with_path") != 0)) {
1310 line = unescaped_path;
1311 }
1312 else {
1313 line = _g_uri_get_basename (unescaped_path);
1314 g_free (unescaped_path);
1315 }
1316
1317 if (gth_tag_get_attribute_int (self, tag, "utf8") != 0)
1318 write_markup_escape_locale_line (ostream, line, error);
1319 else
1320 write_markup_escape_line (ostream, line, error);
1321
1322 g_object_unref (file);
1323 break;
1324
1325 case GTH_TAG_FILE_PATH:
1326 idx = get_image_idx (tag, self);
1327 idata = g_list_nth (self->priv->file_list, idx)->data;
1328 self->priv->eval_image = idata;
1329
1330 switch (get_attr_image_type_from_tag (self, tag)) {
1331 case GTH_IMAGE_TYPE_THUMBNAIL:
1332 file = get_thumbnail_file (self,
1333 idata,
1334 self->priv->target_dir);
1335 break;
1336
1337 case GTH_IMAGE_TYPE_PREVIEW:
1338 file = get_preview_file (self,
1339 idata,
1340 self->priv->target_dir);
1341 break;
1342
1343 case GTH_IMAGE_TYPE_IMAGE:
1344 file = get_image_file (self,
1345 idata,
1346 self->priv->target_dir);
1347 break;
1348 }
1349
1350 dir = g_file_get_parent (file);
1351
1352 relative = (gth_tag_get_attribute_int (self, tag, "relative_path") != 0);
1353
1354 if (relative)
1355 line = gfile_get_relative_path (dir, relative_to);
1356 else
1357 line = g_file_get_path (dir);
1358
1359 if (gth_tag_get_attribute_int (self, tag, "utf8") != 0)
1360 write_markup_escape_locale_line (ostream, line, error);
1361 else
1362 write_markup_escape_line (ostream, line, error);
1363
1364 g_object_unref (dir);
1365 g_object_unref (file);
1366 break;
1367
1368 case GTH_TAG_FILE_SIZE:
1369 idx = get_image_idx (tag, self);
1370 idata = g_list_nth (self->priv->file_list, idx)->data;
1371 line = g_format_size (g_file_info_get_size (idata->file_data->info));
1372 write_markup_escape_line (ostream, line, error);
1373 break;
1374
1375 case GTH_TAG_PAGE_LINK:
1376 if (gth_tag_get_attribute_int (self, tag, "image_idx") != 0) {
1377 int image_idx;
1378 image_idx = get_image_idx (tag, self);
1379 idx = get_page_idx_from_image_idx (self, image_idx);
1380 }
1381 else
1382 idx = get_page_idx (tag, self);
1383
1384 file = get_html_index_file (self, idx, self->priv->target_dir);
1385 line = gfile_get_relative_path (file, relative_to);
1386 write_markup_escape_line (ostream, line, error);
1387
1388 g_object_unref (file);
1389 break;
1390
1391 case GTH_TAG_PAGE_IDX:
1392 line = g_strdup_printf ("%d", get_page_idx (tag, self) + 1);
1393 write_line (ostream, line, error);
1394 break;
1395
1396 case GTH_TAG_PAGE_ROWS:
1397 line = g_strdup_printf ("%d", self->priv->rows_per_page);
1398 write_line (ostream, line, error);
1399 break;
1400
1401 case GTH_TAG_PAGE_COLS:
1402 line = g_strdup_printf ("%d", self->priv->columns_per_page);
1403 write_line (ostream, line, error);
1404 break;
1405
1406 case GTH_TAG_PAGES:
1407 line = g_strdup_printf ("%d", self->priv->n_pages);
1408 write_line (ostream, line, error);
1409 break;
1410
1411 case GTH_TAG_THUMBNAILS:
1412 if (template_type != GTH_TEMPLATE_TYPE_INDEX)
1413 break;
1414
1415 if (self->priv->adapt_to_width) {
1416 for (r = 0; r < (self->priv->single_index ? self->priv->n_images : self->priv->images_per_index); r++) {
1417 if (self->priv->image >= self->priv->n_images)
1418 break;
1419 gth_parsed_doc_print (self,
1420 self->priv->thumbnail_template,
1421 GTH_TEMPLATE_TYPE_THUMBNAIL,
1422 loop_info,
1423 relative_to,
1424 ostream,
1425 error);
1426 self->priv->image++;
1427 }
1428 }
1429 else {
1430 write_line (ostream, "<table id=\"thumbnail-grid\">\n", error);
1431 for (r = 0; r < self->priv->rows_per_page; r++) {
1432 if (self->priv->image < self->priv->n_images)
1433 write_line (ostream, " <tr class=\"tr_index\">\n", error);
1434 else
1435 write_line (ostream, " <tr class=\"tr_empty_index\">\n", error);
1436 for (c = 0; c < self->priv->columns_per_page; c++) {
1437 if (self->priv->image < self->priv->n_images) {
1438 write_line (ostream, " <td class=\"td_index\">\n", error);
1439 gth_parsed_doc_print (self,
1440 self->priv->thumbnail_template,
1441 GTH_TEMPLATE_TYPE_THUMBNAIL,
1442 loop_info,
1443 relative_to,
1444 ostream,
1445 error);
1446 write_line (ostream, " </td>\n", error);
1447 self->priv->image++;
1448 }
1449 else {
1450 write_line (ostream, " <td class=\"td_empty_index\">\n", error);
1451 write_line (ostream, " \n", error);
1452 write_line (ostream, " </td>\n", error);
1453 }
1454 }
1455 write_line (ostream, " </tr>\n", error);
1456 }
1457 write_line (ostream, "</table>\n", error);
1458 }
1459 break;
1460
1461 case GTH_TAG_TIMESTAMP:
1462 {
1463 const char *format;
1464
1465 format = gth_tag_get_attribute_string (self, tag, "format");
1466 if (format == NULL)
1467 format = DEFAULT_DATE_FORMAT;
1468 line = get_current_date (format);
1469 write_markup_escape_line (ostream, line, error);
1470 }
1471 break;
1472
1473 case GTH_TAG_HTML:
1474 write_line (ostream, tag->value.html, error);
1475 break;
1476
1477 case GTH_TAG_EVAL:
1478 idx = get_image_idx (tag, self);
1479 idata = g_list_nth (self->priv->file_list, idx)->data;
1480 self->priv->eval_image = idata;
1481
1482 value = gth_tag_get_attribute_int (self, tag, "expr");
1483 line = g_strdup_printf ("%d", value);
1484 write_line (ostream, line, error);
1485 break;
1486
1487 case GTH_TAG_IF:
1488 idx = MIN (self->priv->image, self->priv->n_images - 1);
1489 idata = g_list_nth (self->priv->file_list, idx)->data;
1490 self->priv->eval_image = idata;
1491
1492 for (scan = tag->value.cond_list; scan; scan = scan->next) {
1493 GthCondition *cond = scan->data;
1494 if (expression_value (self, cond->expr) != 0) {
1495 gth_parsed_doc_print (self,
1496 cond->document,
1497 template_type,
1498 loop_info,
1499 relative_to,
1500 ostream,
1501 error);
1502 break;
1503 }
1504 }
1505 break;
1506
1507 case GTH_TAG_FOR_EACH_THUMBNAIL_CAPTION:
1508 {
1509 LoopInfo *inner_loop_info;
1510 char **attributes;
1511 int i;
1512 int n_attributes;
1513 int first_non_empty;
1514 int last_non_empty;
1515 int n;
1516
1517 idx = MIN (self->priv->image, self->priv->n_images - 1);
1518 idata = g_list_nth (self->priv->file_list, idx)->data;
1519 self->priv->eval_image = idata;
1520
1521 attributes = g_strsplit (self->priv->thumbnail_caption, ",", -1);
1522 n_attributes = g_strv_length (attributes);
1523 first_non_empty = -1;
1524 last_non_empty = -1;
1525 for (i = 0; attributes[i] != NULL; i++) {
1526 char *value;
1527
1528 value = gth_file_data_get_attribute_as_string (idata->file_data, attributes[i]);
1529 if ((value != NULL) && ! g_str_equal (value, "")) {
1530 if (first_non_empty == -1)
1531 first_non_empty = i;
1532 last_non_empty = i;
1533 }
1534
1535 g_free (value);
1536 }
1537
1538 n = 0;
1539 inner_loop_info = loop_info_new ();
1540
1541 for (i = 0; attributes[i] != NULL; i++) {
1542 char *value;
1543
1544 value = gth_file_data_get_attribute_as_string (idata->file_data, attributes[i]);
1545 if ((value != NULL) && ! g_str_equal (value, "")) {
1546 inner_loop_info->first_item = (n == 0);
1547 inner_loop_info->last_item = (n == n_attributes - 1);
1548 inner_loop_info->item_index = n;
1549 inner_loop_info->item = g_object_ref (idata->file_data);
1550 inner_loop_info->attribute = g_strdup (attributes[i]);
1551 inner_loop_info->item_is_empty = FALSE;
1552
1553 gth_parsed_doc_print (self,
1554 tag->value.loop->document,
1555 GTH_TEMPLATE_TYPE_FRAGMENT,
1556 inner_loop_info,
1557 relative_to,
1558 ostream,
1559 error);
1560 n++;
1561 }
1562
1563 g_free (value);
1564 }
1565
1566 for (i = 0; attributes[i] != NULL; i++) {
1567 if ((i < first_non_empty) || (i > last_non_empty)) {
1568 inner_loop_info->first_item = (n == 0);
1569 inner_loop_info->last_item = (n == n_attributes - 1);
1570 inner_loop_info->item_index = n;
1571 inner_loop_info->item = g_object_ref (idata->file_data);
1572 inner_loop_info->attribute = g_strdup (attributes[i]);
1573 inner_loop_info->item_is_empty = TRUE;
1574
1575 gth_parsed_doc_print (self,
1576 tag->value.loop->document,
1577 GTH_TEMPLATE_TYPE_FRAGMENT,
1578 inner_loop_info,
1579 relative_to,
1580 ostream,
1581 error);
1582 n++;
1583 }
1584 }
1585
1586 g_strfreev (attributes);
1587 loop_info_unref (inner_loop_info);
1588 }
1589 break;
1590
1591 case GTH_TAG_FOR_EACH_IMAGE_CAPTION:
1592 {
1593 LoopInfo *inner_loop_info;
1594 char **attributes;
1595 int i;
1596 int first_non_empty;
1597 int last_non_empty;
1598 int n;
1599
1600 idx = MIN (self->priv->image, self->priv->n_images - 1);
1601 idata = g_list_nth (self->priv->file_list, idx)->data;
1602 self->priv->eval_image = idata;
1603
1604 attributes = g_strsplit (self->priv->image_attributes, ",", -1);
1605 first_non_empty = -1;
1606 last_non_empty = -1;
1607 for (i = 0; attributes[i] != NULL; i++) {
1608 char *value;
1609
1610 value = gth_file_data_get_attribute_as_string (idata->file_data, attributes[i]);
1611 if ((value != NULL) && ! g_str_equal (value, "")) {
1612 if (first_non_empty == -1)
1613 first_non_empty = i;
1614 last_non_empty = i;
1615 }
1616
1617 g_free (value);
1618 }
1619
1620 n = 0;
1621 inner_loop_info = loop_info_new ();
1622
1623 for (i = 0; attributes[i] != NULL; i++) {
1624 char *value;
1625
1626 value = gth_file_data_get_attribute_as_string (idata->file_data, attributes[i]);
1627 if ((value != NULL) && ! g_str_equal (value, "")) {
1628 inner_loop_info->first_item = (i == first_non_empty);
1629 inner_loop_info->last_item = (i == last_non_empty);
1630 inner_loop_info->item_index = n;
1631 inner_loop_info->item = g_object_ref (idata->file_data);
1632 inner_loop_info->attribute = g_strdup (attributes[i]);
1633 inner_loop_info->item_is_empty = FALSE;
1634
1635 gth_parsed_doc_print (self,
1636 tag->value.loop->document,
1637 GTH_TEMPLATE_TYPE_FRAGMENT,
1638 inner_loop_info,
1639 relative_to,
1640 ostream,
1641 error);
1642 n++;
1643 }
1644
1645 g_free (value);
1646 }
1647
1648 g_strfreev (attributes);
1649 loop_info_unref (inner_loop_info);
1650 }
1651 break;
1652
1653 case GTH_TAG_FOR_EACH_IN_RANGE:
1654 {
1655 LoopInfo *inner_loop_info;
1656 int i;
1657 int first_value;
1658 int last_value;
1659
1660 first_value = expression_value (self, tag->value.range_loop->first_value);
1661 last_value = expression_value (self, tag->value.range_loop->last_value);
1662 inner_loop_info = loop_info_new ();
1663 inner_loop_info->iterator = g_strdup (tag->value.range_loop->iterator);
1664 for (i = first_value; i <= last_value; i++) {
1665 inner_loop_info->first_item = (i == first_value);
1666 inner_loop_info->last_item = (i == last_value);
1667 inner_loop_info->iterator_value = i;
1668
1669 gth_parsed_doc_print (self,
1670 tag->value.loop->document,
1671 GTH_TEMPLATE_TYPE_FRAGMENT,
1672 inner_loop_info,
1673 relative_to,
1674 ostream,
1675 error);
1676 }
1677
1678 loop_info_unref (inner_loop_info);
1679 }
1680 break;
1681
1682 case GTH_TAG_ITEM_ATTRIBUTE:
1683 if ((loop_info != NULL) && (loop_info->item != NULL)) {
1684 GthMetadataInfo *metadata_info;
1685
1686 metadata_info = gth_main_get_metadata_info (loop_info->attribute);
1687 if (metadata_info != NULL) {
1688 if (gth_tag_get_attribute_int (self, tag, "id") != 0) {
1689 line = g_strdup (metadata_info->id);
1690 }
1691 else if (gth_tag_get_attribute_int (self, tag, "display_name") != 0) {
1692 line = g_strdup (metadata_info->display_name);
1693 }
1694 else if (gth_tag_get_attribute_int (self, tag, "value") != 0) {
1695 line = gth_file_data_get_attribute_as_string (loop_info->item, loop_info->attribute);
1696 }
1697 else if (gth_tag_get_attribute_int (self, tag, "index") != 0) {
1698 line = g_strdup_printf ("%d", loop_info->item_index);
1699 }
1700 }
1701
1702 if ((line == NULL) || (g_strcmp0 (line, "") == 0)) {
1703 /* Always print a non-void value to
1704 * make the caption of the same height
1705 * for all the images, this fixes a
1706 * layout issue when the "adapt columns
1707 * to the window width" option is
1708 * enabled. */
1709 _write_line (ostream, " ", error);
1710 }
1711 else
1712 write_markup_escape_line (ostream, line, error);
1713 }
1714 break;
1715
1716 case GTH_TAG_TRANSLATE:
1717 line = gth_tag_translate_get_string (self, tag);
1718 write_markup_escape_line (ostream, line, error);
1719 break;
1720
1721 default:
1722 break;
1723 }
1724
1725 g_free (line);
1726 }
1727 }
1728
1729
1730 static void
save_template(GthWebExporter * self,GList * document,GthTemplateType template_type,GFile * file,GFile * relative_to,GError ** error)1731 save_template (GthWebExporter *self,
1732 GList *document,
1733 GthTemplateType template_type,
1734 GFile *file,
1735 GFile *relative_to,
1736 GError **error)
1737 {
1738 GFileOutputStream *ostream;
1739
1740 ostream = g_file_replace (file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, error);
1741 if (ostream != NULL) {
1742 gth_parsed_doc_print (self,
1743 document,
1744 template_type,
1745 NULL,
1746 relative_to,
1747 ostream,
1748 error);
1749 g_object_unref (ostream);
1750 }
1751 }
1752
1753
1754 enum {
1755 _OPEN_IN_BROWSER_RESPONSE = 1,
1756 _OPEN_FOLDER_RESPONSE
1757 };
1758
1759
1760 static void
success_dialog_response_cb(GtkDialog * dialog,int response_id,gpointer user_data)1761 success_dialog_response_cb (GtkDialog *dialog,
1762 int response_id,
1763 gpointer user_data)
1764 {
1765 GthWebExporter *self = user_data;
1766
1767 gtk_widget_destroy (GTK_WIDGET (dialog));
1768
1769 switch (response_id) {
1770 case _OPEN_IN_BROWSER_RESPONSE:
1771 case _OPEN_FOLDER_RESPONSE:
1772 {
1773 GFile *file;
1774 char *url = NULL;
1775 GError *error = NULL;
1776
1777 if (response_id == _OPEN_FOLDER_RESPONSE)
1778 file = g_object_ref (self->priv->target_dir);
1779 else if (response_id == _OPEN_IN_BROWSER_RESPONSE)
1780 file = get_html_index_file (self, 0, self->priv->target_dir);
1781 else
1782 break;
1783
1784 url = g_file_get_uri (file);
1785 if ((url != NULL) && ! gtk_show_uri_on_window (GTK_WINDOW (self->priv->browser), url, GDK_CURRENT_TIME, &error)) {
1786 gth_task_dialog (GTH_TASK (self), TRUE, NULL);
1787 _gtk_error_dialog_from_gerror_run (GTK_WINDOW (self->priv->browser), _("Could not show the destination"), error);
1788 g_clear_error (&error);
1789 }
1790
1791 g_free (url);
1792 g_object_unref (file);
1793 }
1794 break;
1795
1796 default:
1797 break;
1798 }
1799
1800 gth_task_dialog (GTH_TASK (self), FALSE, NULL);
1801 gth_task_completed (GTH_TASK (self), self->priv->error);
1802 }
1803
1804
1805 static void
delete_temp_dir_ready_cb(GError * error,gpointer user_data)1806 delete_temp_dir_ready_cb (GError *error,
1807 gpointer user_data)
1808 {
1809 GthWebExporter *self = user_data;
1810 GtkWidget *dialog;
1811
1812 if ((self->priv->error == NULL) && (error != NULL))
1813 self->priv->error = g_error_copy (error);
1814
1815 if (self->priv->error != NULL) {
1816 gth_task_completed (GTH_TASK (self), self->priv->error);
1817 return;
1818 }
1819
1820 dialog = _gtk_message_dialog_new (GTK_WINDOW (self->priv->browser),
1821 GTK_DIALOG_MODAL,
1822 _GTK_ICON_NAME_DIALOG_INFO,
1823 _("The album has been created successfully."),
1824 NULL,
1825 _GTK_LABEL_CLOSE, GTK_RESPONSE_CLOSE,
1826 _("_Open in the Browser"), _OPEN_IN_BROWSER_RESPONSE,
1827 _("_View the destination"), _OPEN_FOLDER_RESPONSE,
1828 NULL);
1829 g_signal_connect (dialog,
1830 "response",
1831 G_CALLBACK (success_dialog_response_cb),
1832 self);
1833 gth_task_dialog (GTH_TASK (self), TRUE, dialog);
1834 gtk_window_present (GTK_WINDOW (dialog));
1835 }
1836
1837
1838 static void
cleanup_and_terminate(GthWebExporter * self,GError * error)1839 cleanup_and_terminate (GthWebExporter *self,
1840 GError *error)
1841 {
1842 if (error != NULL)
1843 self->priv->error = g_error_copy (error);
1844
1845 if (self->priv->file_list != NULL) {
1846 g_list_foreach (self->priv->file_list, (GFunc) image_data_free, NULL);
1847 g_list_free (self->priv->file_list);
1848 self->priv->file_list = NULL;
1849 }
1850
1851 if (self->priv->tmp_dir != NULL) {
1852 GList *file_list;
1853
1854 file_list = g_list_append (NULL, self->priv->tmp_dir);
1855 _g_file_list_delete_async (file_list,
1856 TRUE,
1857 TRUE,
1858 NULL,
1859 NULL,
1860 delete_temp_dir_ready_cb,
1861 self);
1862
1863 g_list_free (file_list);
1864 }
1865 else
1866 delete_temp_dir_ready_cb (NULL, self);
1867 }
1868
1869
1870 static void
copy_to_destination_ready_cb(GError * error,gpointer user_data)1871 copy_to_destination_ready_cb (GError *error,
1872 gpointer user_data)
1873 {
1874 cleanup_and_terminate (GTH_WEB_EXPORTER (user_data), error);
1875 }
1876
1877
1878 static void
save_files_progress_cb(GObject * object,const char * description,const char * details,gboolean pulse,double fraction,gpointer user_data)1879 save_files_progress_cb (GObject *object,
1880 const char *description,
1881 const char *details,
1882 gboolean pulse,
1883 double fraction,
1884 gpointer user_data)
1885 {
1886 GthWebExporter *self = user_data;
1887
1888 gth_task_progress (GTH_TASK (self),
1889 description,
1890 details,
1891 pulse,
1892 fraction);
1893 }
1894
1895
1896 static void
save_files_dialog_cb(gboolean opened,GtkWidget * dialog,gpointer user_data)1897 save_files_dialog_cb (gboolean opened,
1898 GtkWidget *dialog,
1899 gpointer user_data)
1900 {
1901 gth_task_dialog (GTH_TASK (user_data), opened, dialog);
1902 }
1903
1904
1905 static void
save_other_files_ready_cb(GError * error,gpointer user_data)1906 save_other_files_ready_cb (GError *error,
1907 gpointer user_data)
1908 {
1909 GthWebExporter *self = user_data;
1910 GFileEnumerator *enumerator;
1911 GFileInfo *info;
1912 GList *files;
1913
1914 if (error != NULL) {
1915 cleanup_and_terminate (self, error);
1916 return;
1917 }
1918
1919 enumerator = g_file_enumerate_children (self->priv->tmp_dir,
1920 G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_TYPE,
1921 0,
1922 gth_task_get_cancellable (GTH_TASK (self)),
1923 &error);
1924
1925 if (error != NULL) {
1926 cleanup_and_terminate (self, error);
1927 return;
1928 }
1929
1930 files = NULL;
1931 while ((error == NULL) && ((info = g_file_enumerator_next_file (enumerator, NULL, &error)) != NULL)) {
1932 files = g_list_prepend (files, g_file_get_child (self->priv->tmp_dir, g_file_info_get_name (info)));
1933 g_object_unref (info);
1934 }
1935
1936 g_object_unref (enumerator);
1937
1938 if (error == NULL)
1939 _g_file_list_copy_async (files,
1940 self->priv->target_dir,
1941 FALSE,
1942 GTH_FILE_COPY_DEFAULT,
1943 GTH_OVERWRITE_RESPONSE_UNSPECIFIED,
1944 G_PRIORITY_DEFAULT,
1945 gth_task_get_cancellable (GTH_TASK (self)),
1946 save_files_progress_cb,
1947 self,
1948 save_files_dialog_cb,
1949 self,
1950 copy_to_destination_ready_cb,
1951 self);
1952 else
1953 cleanup_and_terminate (self, error);
1954
1955 _g_object_list_unref (files);
1956 }
1957
1958
1959 static void
save_other_files(GthWebExporter * self)1960 save_other_files (GthWebExporter *self)
1961 {
1962 GFileEnumerator *enumerator;
1963 GError *error = NULL;
1964 GFileInfo *info;
1965 GList *files;
1966
1967 enumerator = g_file_enumerate_children (self->priv->style_dir,
1968 G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_TYPE,
1969 0,
1970 gth_task_get_cancellable (GTH_TASK (self)),
1971 &error);
1972
1973 if (error != NULL) {
1974 cleanup_and_terminate (self, error);
1975 return;
1976 }
1977
1978 files = NULL;
1979 while ((error == NULL) && ((info = g_file_enumerator_next_file (enumerator, NULL, &error)) != NULL)) {
1980 const char *name;
1981 GFile *source;
1982
1983 if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
1984 g_object_unref (info);
1985 continue;
1986 }
1987
1988 name = g_file_info_get_name (info);
1989
1990 if ((strcmp (name, "index.gthtml") == 0)
1991 || (strcmp (name, "thumbnail.gthtml") == 0)
1992 || (strcmp (name, "image.gthtml") == 0)
1993 || (strcmp (name, "Makefile.am") == 0)
1994 || (strcmp (name, "Makefile.in") == 0)
1995 || (strcmp (name, "preview.png") == 0))
1996 {
1997 g_object_unref (info);
1998 continue;
1999 }
2000
2001 source = g_file_get_child (self->priv->style_dir, name);
2002 files = g_list_prepend (files, g_object_ref (source));
2003
2004 g_object_unref (source);
2005 g_object_unref (info);
2006 }
2007
2008 g_object_unref (enumerator);
2009
2010 if (error == NULL) {
2011 GFile *theme_dir;
2012
2013 theme_dir = get_theme_file (self, self->priv->tmp_dir, NULL);
2014 _g_file_list_copy_async (files,
2015 theme_dir,
2016 FALSE,
2017 GTH_FILE_COPY_DEFAULT,
2018 GTH_OVERWRITE_RESPONSE_UNSPECIFIED,
2019 G_PRIORITY_DEFAULT,
2020 gth_task_get_cancellable (GTH_TASK (self)),
2021 save_files_progress_cb,
2022 self,
2023 save_files_dialog_cb,
2024 self,
2025 save_other_files_ready_cb,
2026 self);
2027
2028 g_object_unref (theme_dir);
2029 }
2030 else
2031 cleanup_and_terminate (self, error);
2032
2033 _g_object_list_unref (files);
2034 }
2035
2036
2037 static gboolean save_thumbnail (gpointer data);
2038
2039
2040 static void
save_next_thumbnail(GthWebExporter * self)2041 save_next_thumbnail (GthWebExporter *self)
2042 {
2043 self->priv->current_file = self->priv->current_file->next;
2044 self->priv->image++;
2045 self->priv->saving_timeout = g_idle_add (save_thumbnail, self);
2046 }
2047
2048
2049 static void
save_thumbnail_ready_cb(GthFileData * file_data,GError * error,gpointer data)2050 save_thumbnail_ready_cb (GthFileData *file_data,
2051 GError *error,
2052 gpointer data)
2053 {
2054 GthWebExporter *self = data;
2055 ImageData *image_data;
2056
2057 if (error != NULL) {
2058 cleanup_and_terminate (self, error);
2059 return;
2060 }
2061
2062 image_data = self->priv->current_file->data;
2063 g_object_unref (image_data->thumb);
2064 image_data->thumb = NULL;
2065
2066 save_next_thumbnail (self);
2067 }
2068
2069
2070 static gboolean
save_thumbnail(gpointer data)2071 save_thumbnail (gpointer data)
2072 {
2073 GthWebExporter *self = data;
2074 ImageData *image_data;
2075
2076 if (self->priv->saving_timeout != 0) {
2077 g_source_remove (self->priv->saving_timeout);
2078 self->priv->saving_timeout = 0;
2079 }
2080
2081 if (self->priv->current_file == NULL) {
2082 save_other_files (self);
2083 return FALSE;
2084 }
2085
2086 image_data = self->priv->current_file->data;
2087 if (image_data->thumb != NULL) {
2088 GFile *destination;
2089 GthFileData *file_data;
2090
2091 gth_task_progress (GTH_TASK (self),
2092 _("Saving thumbnails"),
2093 NULL,
2094 FALSE,
2095 (double) (self->priv->image + 1) / (self->priv->n_images + 1));
2096
2097 destination = get_thumbnail_file (self, image_data, self->priv->tmp_dir);
2098 file_data = gth_file_data_new (destination, NULL);
2099 gth_image_save_to_file (image_data->thumb,
2100 "image/jpeg",
2101 file_data,
2102 TRUE,
2103 gth_task_get_cancellable (GTH_TASK (self)),
2104 save_thumbnail_ready_cb,
2105 self);
2106
2107 g_object_unref (file_data);
2108 g_object_unref (destination);
2109 }
2110 else
2111 save_next_thumbnail (self);
2112
2113 return FALSE;
2114 }
2115
2116
2117 static void
save_thumbnails(GthWebExporter * self)2118 save_thumbnails (GthWebExporter *self)
2119 {
2120 gth_task_progress (GTH_TASK (self), _("Saving thumbnails"), NULL, TRUE, 0);
2121
2122 self->priv->image = 0;
2123 self->priv->current_file = self->priv->file_list;
2124 self->priv->saving_timeout = g_idle_add (save_thumbnail, self);
2125 }
2126
2127
2128 static gboolean
save_html_image(gpointer data)2129 save_html_image (gpointer data)
2130 {
2131 GthWebExporter *self = data;
2132 ImageData *image_data;
2133 GFile *file;
2134 GFile *relative_to;
2135 GError *error = NULL;
2136
2137 if (self->priv->saving_timeout != 0) {
2138 g_source_remove (self->priv->saving_timeout);
2139 self->priv->saving_timeout = 0;
2140 }
2141
2142 if (self->priv->current_file == NULL) {
2143 save_thumbnails (self);
2144 return FALSE;
2145 }
2146
2147 gth_task_progress (GTH_TASK (self),
2148 _("Saving HTML pages: Images"),
2149 NULL,
2150 FALSE,
2151 (double) (self->priv->image + 1) / (self->priv->n_images + 1));
2152
2153 image_data = self->priv->current_file->data;
2154 file = get_html_image_file (self, image_data, self->priv->tmp_dir);
2155 relative_to = get_html_image_dir (self, self->priv->target_dir);
2156 save_template (self, self->priv->image_template, GTH_TEMPLATE_TYPE_IMAGE, file, relative_to, &error);
2157
2158 g_object_unref (file);
2159 g_object_unref (relative_to);
2160
2161 /**/
2162
2163 if (error != NULL) {
2164 cleanup_and_terminate (self, error);
2165 return FALSE;
2166 }
2167
2168 self->priv->current_file = self->priv->current_file->next;
2169 self->priv->image++;
2170 self->priv->saving_timeout = g_idle_add (save_html_image, data);
2171
2172 return FALSE;
2173 }
2174
2175
2176 static void
save_html_images(GthWebExporter * self)2177 save_html_images (GthWebExporter *self)
2178 {
2179 self->priv->image = 0;
2180 self->priv->current_file = self->priv->file_list;
2181 self->priv->saving_timeout = g_idle_add (save_html_image, self);
2182 }
2183
2184
2185 static gboolean
save_html_index(gpointer data)2186 save_html_index (gpointer data)
2187 {
2188 GthWebExporter *self = data;
2189 GFile *file;
2190 GFile *relative_to;
2191 GError *error = NULL;
2192
2193 if (self->priv->saving_timeout != 0) {
2194 g_source_remove (self->priv->saving_timeout);
2195 self->priv->saving_timeout = 0;
2196 }
2197
2198 if (self->priv->page >= self->priv->n_pages) {
2199 save_html_images (self);
2200 return FALSE;
2201 }
2202
2203 /* write index.html and pageXXX.html */
2204
2205 gth_task_progress (GTH_TASK (self),
2206 _("Saving HTML pages: Indexes"),
2207 NULL,
2208 FALSE,
2209 (double) (self->priv->page + 1) / (self->priv->n_pages + 1));
2210
2211 file = get_html_index_file (self, self->priv->page, self->priv->tmp_dir);
2212 relative_to = get_html_index_dir (self, self->priv->page, self->priv->target_dir);
2213 save_template (self, self->priv->index_template, GTH_TEMPLATE_TYPE_INDEX, file, relative_to, &error);
2214
2215 g_object_unref (file);
2216 g_object_unref (relative_to);
2217
2218 /**/
2219
2220 if (error != NULL) {
2221 cleanup_and_terminate (self, error);
2222 return FALSE;
2223 }
2224
2225 self->priv->page++;
2226 self->priv->saving_timeout = g_idle_add (save_html_index, data);
2227
2228 return FALSE;
2229 }
2230
2231
2232 static void
save_html_files(GthWebExporter * self)2233 save_html_files (GthWebExporter *self)
2234 {
2235 self->priv->page = 0;
2236 self->priv->image = 0;
2237 self->priv->saving_timeout = g_idle_add (save_html_index, self);
2238 }
2239
2240
2241 static void load_current_file (GthWebExporter *self);
2242
2243
2244 static void
load_next_file(GthWebExporter * self)2245 load_next_file (GthWebExporter *self)
2246 {
2247 if (self->priv->interrupted) {
2248 GError *error;
2249
2250 error = g_error_new_literal (GTH_TASK_ERROR, GTH_TASK_ERROR_CANCELLED, "");
2251 cleanup_and_terminate (self, error);
2252 g_error_free (error);
2253
2254 return;
2255 }
2256
2257 if (self->priv->current_file != NULL) {
2258 ImageData *image_data = self->priv->current_file->data;
2259
2260 if (image_data->preview != NULL) {
2261 g_object_unref (image_data->preview);
2262 image_data->preview = NULL;
2263 }
2264
2265 if (image_data->image != NULL) {
2266 g_object_unref (image_data->image);
2267 image_data->image = NULL;
2268 }
2269 }
2270
2271 self->priv->image++;
2272 self->priv->current_file = self->priv->current_file->next;
2273 load_current_file (self);
2274 }
2275
2276
2277 static gboolean
load_next_file_cb(gpointer data)2278 load_next_file_cb (gpointer data)
2279 {
2280 GthWebExporter *self = data;
2281
2282 if (self->priv->saving_timeout != 0) {
2283 g_source_remove (self->priv->saving_timeout);
2284 self->priv->saving_timeout = 0;
2285 }
2286
2287 load_next_file (self);
2288
2289 return FALSE;
2290 }
2291
2292
2293 static void
save_image_preview_ready_cb(GthFileData * file_data,GError * error,gpointer data)2294 save_image_preview_ready_cb (GthFileData *file_data,
2295 GError *error,
2296 gpointer data)
2297 {
2298 GthWebExporter *self = data;
2299
2300 if (error != NULL) {
2301 cleanup_and_terminate (self, error);
2302 return;
2303 }
2304
2305 self->priv->saving_timeout = g_idle_add (load_next_file_cb, self);
2306 }
2307
2308
2309 static gboolean
save_image_preview(gpointer data)2310 save_image_preview (gpointer data)
2311 {
2312 GthWebExporter *self = data;
2313 ImageData *image_data;
2314
2315 if (self->priv->saving_timeout != 0) {
2316 g_source_remove (self->priv->saving_timeout);
2317 self->priv->saving_timeout = 0;
2318 }
2319
2320 image_data = self->priv->current_file->data;
2321 if (! image_data->no_preview && (image_data->preview != NULL)) {
2322 GFile *destination;
2323 GthFileData *file_data;
2324
2325 gth_task_progress (GTH_TASK (self),
2326 _("Saving images"),
2327 g_file_info_get_display_name (image_data->file_data->info),
2328 FALSE,
2329 (double) (self->priv->image + 1) / (self->priv->n_images + 1));
2330
2331 destination = get_preview_file (self, image_data, self->priv->tmp_dir);
2332 file_data = gth_file_data_new (destination, NULL);
2333 gth_image_save_to_file (image_data->preview,
2334 "image/jpeg",
2335 file_data,
2336 TRUE,
2337 gth_task_get_cancellable (GTH_TASK (self)),
2338 save_image_preview_ready_cb,
2339 self);
2340
2341 g_object_unref (file_data);
2342 g_object_unref (destination);
2343 }
2344 else
2345 self->priv->saving_timeout = g_idle_add (load_next_file_cb, self);
2346
2347 return FALSE;
2348 }
2349
2350
2351 static void
save_resized_image_ready_cd(GthFileData * file_data,GError * error,gpointer data)2352 save_resized_image_ready_cd (GthFileData *file_data,
2353 GError *error,
2354 gpointer data)
2355 {
2356 GthWebExporter *self = data;
2357
2358 if (error != NULL) {
2359 cleanup_and_terminate (self, error);
2360 return;
2361 }
2362
2363 self->priv->saving_timeout = g_idle_add (save_image_preview, self);
2364 }
2365
2366
2367 static const char *
get_format_description(const char * mime_type)2368 get_format_description (const char *mime_type)
2369 {
2370 const char *description = NULL;
2371 GSList *formats;
2372 GSList *scan;
2373
2374 formats = gdk_pixbuf_get_formats ();
2375 for (scan = formats; ! description && scan; scan = scan->next) {
2376 GdkPixbufFormat *format = scan->data;
2377 char **mime_types;
2378 int i;
2379
2380 mime_types = gdk_pixbuf_format_get_mime_types (format);
2381 for (i = 0; ! description && mime_types[i] != NULL; i++)
2382 if (g_strcmp0 (mime_types[i], mime_type) == 0)
2383 description = gdk_pixbuf_format_get_description (format);
2384 }
2385
2386 g_slist_free (formats);
2387
2388 return description;
2389 }
2390
2391
2392 static gboolean
save_resized_image(gpointer data)2393 save_resized_image (gpointer data)
2394 {
2395 GthWebExporter *self = data;
2396 ImageData *image_data;
2397
2398 if (self->priv->saving_timeout != 0) {
2399 g_source_remove (self->priv->saving_timeout);
2400 self->priv->saving_timeout = 0;
2401 }
2402
2403 image_data = self->priv->current_file->data;
2404 if (self->priv->copy_images && (image_data->image != NULL)) {
2405 char *filename_no_ext;
2406 char *size;
2407 GFile *destination;
2408 GthFileData *file_data;
2409
2410 gth_task_progress (GTH_TASK (self),
2411 _("Saving images"),
2412 g_file_info_get_display_name (image_data->file_data->info),
2413 FALSE,
2414 (double) (self->priv->image + 1) / (self->priv->n_images + 1));
2415
2416 /* change the file extension to jpeg */
2417
2418 filename_no_ext = _g_path_remove_extension (image_data->dest_filename);
2419 g_free (image_data->dest_filename);
2420 image_data->dest_filename = g_strconcat(filename_no_ext, ".jpeg", NULL);
2421 g_free (filename_no_ext);
2422
2423 /* change the file type */
2424
2425 gth_file_data_set_mime_type (image_data->file_data, "image/jpeg");
2426 g_file_info_set_attribute_string (image_data->file_data->info, "general::format", get_format_description ("image/jpeg"));
2427
2428 /* change the image dimensions info */
2429
2430 g_file_info_set_attribute_int32 (image_data->file_data->info, "image::width", image_data->image_width);
2431 g_file_info_set_attribute_int32 (image_data->file_data->info, "image::height", image_data->image_height);
2432 g_file_info_set_attribute_int32 (image_data->file_data->info, "frame::width", image_data->image_width);
2433 g_file_info_set_attribute_int32 (image_data->file_data->info, "frame::height", image_data->image_height);
2434 size = g_strdup_printf (_("%d × %d"), image_data->image_width, image_data->image_height);
2435 g_file_info_set_attribute_string (image_data->file_data->info, "general::dimensions", size);
2436
2437 /* save the pixbuf */
2438
2439 destination = get_image_file (self, image_data, self->priv->tmp_dir);
2440 file_data = gth_file_data_new (destination, NULL);
2441 gth_image_save_to_file (image_data->image,
2442 "image/jpeg",
2443 file_data,
2444 TRUE,
2445 gth_task_get_cancellable (GTH_TASK (self)),
2446 save_resized_image_ready_cd,
2447 self);
2448
2449 g_object_unref (file_data);
2450 g_object_unref (destination);
2451 }
2452 else
2453 self->priv->saving_timeout = g_idle_add (save_image_preview, self);
2454
2455 return FALSE;
2456 }
2457
2458
2459 static void
transformation_ready_cb(GError * error,gpointer user_data)2460 transformation_ready_cb (GError *error,
2461 gpointer user_data)
2462 {
2463 GthWebExporter *self = user_data;
2464
2465 if (error != NULL) {
2466 cleanup_and_terminate (self, error);
2467 return;
2468 }
2469
2470 self->priv->saving_timeout = g_idle_add (save_image_preview, self);
2471 }
2472
2473
2474 static gboolean
copy_current_file(GthWebExporter * self)2475 copy_current_file (GthWebExporter *self)
2476 {
2477 ImageData *image_data;
2478 GFile *destination;
2479 GError *error = NULL;
2480
2481 /* This function is used when "Copy originals to destination" is
2482 enabled, and resizing is NOT enabled. This allows us to use a
2483 lossless copy (and rotate). When resizing is enabled, a lossy
2484 save has to be used. */
2485
2486 if (self->priv->saving_timeout != 0) {
2487 g_source_remove (self->priv->saving_timeout);
2488 self->priv->saving_timeout = 0;
2489 }
2490
2491 gth_task_progress (GTH_TASK (self), _("Copying original images"), NULL, TRUE, 0);
2492
2493 image_data = self->priv->current_file->data;
2494 destination = get_image_file (self, image_data, self->priv->tmp_dir);
2495 if (g_file_copy (image_data->file_data->file,
2496 destination,
2497 G_FILE_COPY_NONE,
2498 gth_task_get_cancellable (GTH_TASK (self)),
2499 NULL,
2500 NULL,
2501 &error))
2502 {
2503 gboolean appling_tranformation = FALSE;
2504
2505 if (gth_main_extension_is_active ("image_rotation")) {
2506 GthFileData *file_data;
2507
2508 file_data = gth_file_data_new (destination, image_data->file_data->info);
2509 apply_transformation_async (file_data,
2510 GTH_TRANSFORM_NONE,
2511 JPEG_MCU_ACTION_TRIM,
2512 gth_task_get_cancellable (GTH_TASK (self)),
2513 transformation_ready_cb,
2514 self);
2515 appling_tranformation = TRUE;
2516
2517 g_object_unref (file_data);
2518 }
2519
2520 if (! appling_tranformation)
2521 self->priv->saving_timeout = g_idle_add (save_image_preview, self);
2522 }
2523 else
2524 cleanup_and_terminate (self, error);
2525
2526 g_object_unref (destination);
2527
2528 return FALSE;
2529 }
2530
2531
2532 static int
image_data_cmp(gconstpointer a,gconstpointer b,gpointer user_data)2533 image_data_cmp (gconstpointer a,
2534 gconstpointer b,
2535 gpointer user_data)
2536 {
2537 GthWebExporter *self = user_data;
2538 ImageData *idata_a = (ImageData *) a;
2539 ImageData *idata_b = (ImageData *) b;
2540
2541 return self->priv->sort_type->cmp_func (idata_a->file_data, idata_b->file_data);
2542 }
2543
2544
2545 static void
image_loader_ready_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)2546 image_loader_ready_cb (GObject *source_object,
2547 GAsyncResult *result,
2548 gpointer user_data)
2549 {
2550 GthWebExporter *self = user_data;
2551 ImageData *idata;
2552 GthImage *image = NULL;
2553 cairo_surface_t *surface;
2554
2555 if (! gth_image_loader_load_finish (GTH_IMAGE_LOADER (source_object),
2556 result,
2557 &image,
2558 NULL,
2559 NULL,
2560 NULL,
2561 NULL))
2562 {
2563 load_next_file (self);
2564 return;
2565 }
2566
2567 idata = (ImageData *) self->priv->current_file->data;
2568 surface = gth_image_get_cairo_surface (image);
2569
2570 /* image */
2571
2572 idata->image = g_object_ref (image);
2573 idata->image_width = cairo_image_surface_get_width (surface);
2574 idata->image_height = cairo_image_surface_get_width (surface);
2575
2576 if (self->priv->copy_images && self->priv->resize_images) {
2577 int w = cairo_image_surface_get_width (surface);
2578 int h = cairo_image_surface_get_height (surface);
2579
2580 if (scale_keeping_ratio (&w, &h,
2581 self->priv->resize_max_width,
2582 self->priv->resize_max_height,
2583 FALSE))
2584 {
2585 cairo_surface_t *scaled;
2586
2587 scaled = _cairo_image_surface_scale (surface, w, h, SCALE_FILTER_BEST, NULL);
2588 if (scaled != NULL) {
2589 g_object_unref (idata->image);
2590
2591 idata->image = gth_image_new_for_surface (scaled);
2592 idata->image_width = cairo_image_surface_get_width (scaled);
2593 idata->image_height = cairo_image_surface_get_height (scaled);
2594
2595 cairo_surface_destroy (scaled);
2596 }
2597 }
2598 }
2599
2600 /* preview */
2601
2602 idata->preview = g_object_ref (image);
2603 idata->preview_width = cairo_image_surface_get_width (surface);
2604 idata->preview_height = cairo_image_surface_get_width (surface);
2605
2606 if ((self->priv->preview_max_width > 0) && (self->priv->preview_max_height > 0)) {
2607 int w = cairo_image_surface_get_width (surface);
2608 int h = cairo_image_surface_get_height (surface);
2609
2610 if (scale_keeping_ratio_min (&w, &h,
2611 self->priv->preview_min_width,
2612 self->priv->preview_min_height,
2613 self->priv->preview_max_width,
2614 self->priv->preview_max_height,
2615 FALSE))
2616 {
2617 cairo_surface_t *scaled;
2618
2619 scaled = _cairo_image_surface_scale (surface, w, h, SCALE_FILTER_BEST, NULL);
2620 if (scaled != NULL) {
2621 g_object_unref (idata->preview);
2622
2623 idata->preview = gth_image_new_for_surface (scaled);
2624 idata->preview_width = cairo_image_surface_get_width (scaled);
2625 idata->preview_height = cairo_image_surface_get_height (scaled);
2626
2627 cairo_surface_destroy (scaled);
2628 }
2629 }
2630 }
2631
2632 idata->no_preview = ((idata->preview_width == idata->image_width)
2633 && (idata->preview_height == idata->image_height));
2634
2635 if (idata->no_preview && (idata->preview != NULL)) {
2636 g_object_unref (idata->preview);
2637 idata->preview = NULL;
2638 }
2639
2640 /* thumbnail. */
2641
2642 idata->thumb = g_object_ref (image);
2643 idata->thumb_width = cairo_image_surface_get_width (surface);
2644 idata->thumb_height = cairo_image_surface_get_width (surface);
2645
2646 if ((self->priv->thumb_width > 0) && (self->priv->thumb_height > 0)) {
2647 int w = cairo_image_surface_get_width (surface);
2648 int h = cairo_image_surface_get_height (surface);
2649
2650 if (self->priv->squared_thumbnails) {
2651 cairo_surface_t *scaled;
2652
2653 g_object_unref (idata->thumb);
2654
2655 scaled = _cairo_image_surface_scale_squared (surface, self->priv->thumb_width, SCALE_FILTER_BEST, NULL);
2656 idata->thumb = gth_image_new_for_surface (scaled);
2657 idata->thumb_width = cairo_image_surface_get_width (scaled);
2658 idata->thumb_height = cairo_image_surface_get_height (scaled);
2659
2660 cairo_surface_destroy (scaled);
2661 }
2662 else if (scale_keeping_ratio (&w, &h,
2663 self->priv->thumb_width,
2664 self->priv->thumb_height,
2665 FALSE))
2666 {
2667 cairo_surface_t *scaled;
2668
2669 scaled = _cairo_image_surface_scale (surface, w, h, SCALE_FILTER_BEST, NULL);
2670 if (scaled != NULL) {
2671 g_object_unref (idata->thumb);
2672
2673 idata->thumb = gth_image_new_for_surface (scaled);
2674 idata->thumb_width = cairo_image_surface_get_width (scaled);
2675 idata->thumb_height = cairo_image_surface_get_height (scaled);
2676
2677 cairo_surface_destroy (scaled);
2678 }
2679 }
2680 }
2681
2682 /* save the image */
2683
2684 if (self->priv->copy_images) {
2685 if (self->priv->resize_images)
2686 self->priv->saving_timeout = g_idle_add (save_resized_image, self);
2687 else
2688 copy_current_file (self);
2689 }
2690 else
2691 self->priv->saving_timeout = g_idle_add (save_image_preview, self);
2692
2693 cairo_surface_destroy (surface);
2694 g_object_unref (image);
2695 }
2696
2697
2698 static void
load_current_file(GthWebExporter * self)2699 load_current_file (GthWebExporter *self)
2700 {
2701 GthFileData *file_data;
2702
2703 if (self->priv->current_file == NULL) {
2704 if ((self->priv->sort_type != NULL) && (self->priv->sort_type->cmp_func != NULL))
2705 self->priv->file_list = g_list_sort_with_data (self->priv->file_list, image_data_cmp, self);
2706 if (self->priv->sort_inverse)
2707 self->priv->file_list = g_list_reverse (self->priv->file_list);
2708 save_html_files (self);
2709 return;
2710 }
2711
2712 file_data = IMAGE_DATA (self->priv->current_file->data)->file_data;
2713 gth_task_progress (GTH_TASK (self),
2714 _("Loading images"),
2715 g_file_info_get_display_name (file_data->info),
2716 FALSE,
2717 (double) (self->priv->image + 1) / (self->priv->n_images + 1));
2718
2719 gth_image_loader_load (self->priv->iloader,
2720 file_data,
2721 -1,
2722 G_PRIORITY_DEFAULT,
2723 gth_task_get_cancellable (GTH_TASK (self)),
2724 image_loader_ready_cb,
2725 self);
2726 }
2727
2728
2729 static void
file_list_info_ready_cb(GList * files,GError * error,gpointer user_data)2730 file_list_info_ready_cb (GList *files,
2731 GError *error,
2732 gpointer user_data)
2733 {
2734 GthWebExporter *self = user_data;
2735 GList *scan;
2736 int file_idx;
2737
2738 if (error != NULL) {
2739 cleanup_and_terminate (self, error);
2740 return;
2741 }
2742
2743 file_idx = 0;
2744 for (scan = files; scan; scan = scan->next) {
2745 GthFileData *file_data = scan->data;
2746 self->priv->file_list = g_list_prepend (self->priv->file_list, image_data_new (file_data, file_idx++));
2747 }
2748 self->priv->file_list = g_list_reverse (self->priv->file_list);
2749
2750 /* load the thumbnails */
2751
2752 self->priv->image = 0;
2753 self->priv->current_file = self->priv->file_list;
2754 load_current_file (self);
2755 }
2756
2757
2758 static GList *
parse_template(GFile * file)2759 parse_template (GFile *file)
2760 {
2761 GList *result = NULL;
2762 GError *error = NULL;
2763
2764 yy_parsed_doc = NULL;
2765 yy_istream = g_file_read (file, NULL, &error);
2766 if (error == NULL) {
2767 if (gth_albumtheme_yyparse () == 0)
2768 result = yy_parsed_doc;
2769 else
2770 debug (DEBUG_INFO, "<<syntax error>>");
2771
2772 g_input_stream_close (G_INPUT_STREAM (yy_istream), NULL, &error);
2773 g_object_unref (yy_istream);
2774 }
2775 else {
2776 g_warning ("%s", error->message);
2777 g_clear_error (&error);
2778 }
2779
2780 return result;
2781 }
2782
2783
2784 static void
parse_theme_files(GthWebExporter * self)2785 parse_theme_files (GthWebExporter *self)
2786 {
2787 GFile *template;
2788 GList *scan;
2789
2790 free_parsed_docs (self);
2791
2792 self->priv->image = 0;
2793
2794 /* read and parse index.gthtml */
2795
2796 template = g_file_get_child (self->priv->style_dir, "index.gthtml");
2797 self->priv->index_template = parse_template (template);
2798 if (self->priv->index_template == NULL) {
2799 GthTag *tag = gth_tag_new (GTH_TAG_THUMBNAILS, NULL);
2800 self->priv->index_template = g_list_prepend (NULL, tag);
2801 }
2802 g_object_unref (template);
2803
2804 /* read and parse thumbnail.gthtml */
2805
2806 template = g_file_get_child (self->priv->style_dir, "thumbnail.gthtml");
2807 self->priv->thumbnail_template = parse_template (template);
2808 if (self->priv->thumbnail_template == NULL) {
2809 GList *attributes = NULL;
2810 GthExpr *expr;
2811 GthAttribute *attribute;
2812 GthTag *tag;
2813
2814 expr = gth_expr_new ();
2815 gth_expr_push_integer (expr, 0);
2816 attribute = gth_attribute_new_expression ("idx_relative", expr);
2817 attributes = g_list_prepend (attributes, attribute);
2818 gth_expr_unref (expr);
2819
2820 expr = gth_expr_new ();
2821 gth_expr_push_integer (expr, 1);
2822 attribute = gth_attribute_new_expression ("thumbnail", expr);
2823 attributes = g_list_prepend (attributes, attribute);
2824 gth_expr_unref (expr);
2825
2826 tag = gth_tag_new (GTH_TAG_IMAGE, attributes);
2827 self->priv->thumbnail_template = g_list_prepend (NULL, tag);
2828 }
2829 g_object_unref (template);
2830
2831 /* Read and parse image.gthtml */
2832
2833 template = g_file_get_child (self->priv->style_dir, "image.gthtml");
2834 self->priv->image_template = parse_template (template);
2835 if (self->priv->image_template == NULL) {
2836 GList *attributes = NULL;
2837 GthExpr *expr;
2838 GthAttribute *attribute;
2839 GthTag *tag;
2840
2841 expr = gth_expr_new ();
2842 gth_expr_push_integer (expr, 0);
2843 attribute = gth_attribute_new_expression ("idx_relative", expr);
2844 attributes = g_list_prepend (attributes, attribute);
2845 gth_expr_unref (expr);
2846
2847 expr = gth_expr_new ();
2848 gth_expr_push_integer (expr, 0);
2849 attribute = gth_attribute_new_expression ("thumbnail", expr);
2850 attributes = g_list_prepend (attributes, attribute);
2851 gth_expr_unref (expr);
2852
2853 tag = gth_tag_new (GTH_TAG_IMAGE, attributes);
2854 self->priv->image_template = g_list_prepend (NULL, tag);
2855 }
2856 g_object_unref (template);
2857
2858 /* read index.html and set variables. */
2859
2860 for (scan = self->priv->index_template; scan; scan = scan->next) {
2861 GthTag *tag = scan->data;
2862
2863 if (tag->type == GTH_TAG_SET_VAR) {
2864 int width;
2865 int height;
2866
2867 if (gth_tag_has_attribute (self, tag, "if")) {
2868 if (! gth_tag_get_attribute_int (self, tag, "if"))
2869 continue;
2870 }
2871 else if (gth_tag_has_attribute (self, tag, "unless")) {
2872 if (gth_tag_get_attribute_int (self, tag, "unless"))
2873 continue;
2874 }
2875
2876 width = gth_tag_get_attribute_int (self, tag, "thumbnail_width");
2877 height = gth_tag_get_attribute_int (self, tag, "thumbnail_height");
2878 if ((width != 0) && (height != 0)) {
2879 debug (DEBUG_INFO, "thumbnail --> %dx%d", width, height);
2880 gth_web_exporter_set_thumb_size (self,
2881 gth_tag_get_attribute_int (self, tag, "squared"),
2882 width,
2883 height);
2884 continue;
2885 }
2886
2887 width = gth_tag_get_attribute_int (self, tag, "preview_width");
2888 height = gth_tag_get_attribute_int (self, tag, "preview_height");
2889 if ((width != 0) && (height != 0)) {
2890 debug (DEBUG_INFO, "preview --> %dx%d", width, height);
2891 gth_web_exporter_set_preview_size (self, width, height);
2892 continue;
2893 }
2894
2895 width = gth_tag_get_attribute_int (self, tag, "preview_min_width");
2896 height = gth_tag_get_attribute_int (self, tag, "preview_min_height");
2897 if ((width != 0) && (height != 0)) {
2898 debug (DEBUG_INFO, "preview min --> %dx%d", width, height);
2899 gth_web_exporter_set_preview_min_size (self, width, height);
2900 continue;
2901 }
2902 }
2903 }
2904
2905 if (self->priv->copy_images
2906 && self->priv->resize_images
2907 && (self->priv->resize_max_width > 0)
2908 && (self->priv->resize_max_height > 0))
2909 {
2910 if (self->priv->preview_max_width > self->priv->resize_max_width)
2911 self->priv->preview_max_width = self->priv->resize_max_width;
2912 if (self->priv->preview_max_height > self->priv->resize_max_height)
2913 self->priv->preview_max_height = self->priv->resize_max_height;
2914 }
2915 }
2916
2917
2918 static gboolean
make_album_dir(GFile * parent,const char * child_name,GError ** error)2919 make_album_dir (GFile *parent,
2920 const char *child_name,
2921 GError **error)
2922 {
2923 GFile *child;
2924 gboolean result;
2925
2926 child = g_file_get_child (parent, child_name);
2927 result = g_file_make_directory (child, NULL, error);
2928
2929 g_object_unref (child);
2930
2931 return result;
2932 }
2933
2934
2935 static void
gth_web_exporter_exec(GthTask * task)2936 gth_web_exporter_exec (GthTask *task)
2937 {
2938 GthWebExporter *self;
2939 GError *error = NULL;
2940 GSettings *settings;
2941 GString *required_attributes;
2942
2943 g_return_if_fail (GTH_IS_WEB_EXPORTER (task));
2944
2945 self = GTH_WEB_EXPORTER (task);
2946
2947 if (self->priv->gfile_list == NULL) {
2948 cleanup_and_terminate (self, NULL);
2949 return;
2950 }
2951
2952 /*
2953 * check that the style directory is not NULL. A NULL indicates that
2954 * the folder of the selected style has been deleted or renamed
2955 * before the user started the export. It is unlikely.
2956 */
2957 if (self->priv->style_dir == NULL) {
2958 error = g_error_new_literal (GTH_ERROR, GTH_ERROR_GENERIC, _("Could not find the style folder"));
2959 cleanup_and_terminate (self, error);
2960 return;
2961 }
2962
2963 self->priv->n_images = g_list_length (self->priv->gfile_list);
2964 if (! self->priv->single_index) {
2965 self->priv->n_pages = self->priv->n_images / self->priv->images_per_index;
2966 if (self->priv->n_images % self->priv->images_per_index > 0)
2967 self->priv->n_pages++;
2968 }
2969 else {
2970 self->priv->n_pages = 1;
2971 self->priv->images_per_index = self->priv->n_images;
2972 }
2973 self->priv->rows_per_page = self->priv->images_per_index / self->priv->columns_per_page;
2974 if (self->priv->images_per_index % self->priv->columns_per_page > 0)
2975 self->priv->rows_per_page++;
2976
2977 /* get index file name and sub-directories (hidden preferences) */
2978
2979 settings = g_settings_new (GTHUMB_WEBALBUMS_SCHEMA);
2980 self->priv->index_file = g_settings_get_string (settings, PREF_WEBALBUMS_INDEX_FILE);
2981 g_object_unref (settings);
2982
2983 settings = g_settings_new (GTHUMB_WEBALBUMS_DIRECTORIES_SCHEMA);
2984 self->priv->directories.previews = g_settings_get_string (settings, PREF_WEBALBUMS_DIR_PREVIEWS);
2985 self->priv->directories.thumbnails = g_settings_get_string (settings, PREF_WEBALBUMS_DIR_THUMBNAILS);
2986 self->priv->directories.images = g_settings_get_string (settings, PREF_WEBALBUMS_DIR_IMAGES);
2987 self->priv->directories.html_images = g_settings_get_string (settings, PREF_WEBALBUMS_DIR_HTML_IMAGES);
2988 self->priv->directories.html_indexes = g_settings_get_string (settings, PREF_WEBALBUMS_DIR_HTML_INDEXES);
2989 self->priv->directories.theme_files = g_settings_get_string (settings, PREF_WEBALBUMS_DIR_THEME_FILES);
2990 g_object_unref (settings);
2991
2992 /* create a tmp dir */
2993
2994 self->priv->tmp_dir = _g_directory_create_tmp ();
2995 if (self->priv->tmp_dir == NULL) {
2996 error = g_error_new_literal (GTH_ERROR, GTH_ERROR_GENERIC, _("Could not create a temporary folder"));
2997 cleanup_and_terminate (self, error);
2998 return;
2999 }
3000
3001 if (self->priv->use_subfolders) {
3002 if (! make_album_dir (self->priv->tmp_dir, self->priv->directories.previews, &error)) {
3003 if (! g_error_matches(error, G_IO_ERROR, G_IO_ERROR_EXISTS)) {
3004 cleanup_and_terminate (self, error);
3005 return;
3006 }
3007 else
3008 g_clear_error (&error);
3009 }
3010 if (! make_album_dir (self->priv->tmp_dir, self->priv->directories.thumbnails, &error)) {
3011 if (! g_error_matches(error, G_IO_ERROR, G_IO_ERROR_EXISTS)) {
3012 cleanup_and_terminate (self, error);
3013 return;
3014 }
3015 else
3016 g_clear_error (&error);
3017 }
3018 if (self->priv->copy_images && ! make_album_dir (self->priv->tmp_dir, self->priv->directories.images, &error)) {
3019 if (! g_error_matches(error, G_IO_ERROR, G_IO_ERROR_EXISTS)) {
3020 cleanup_and_terminate (self, error);
3021 return;
3022 }
3023 else
3024 g_clear_error (&error);
3025 }
3026 if (! make_album_dir (self->priv->tmp_dir, self->priv->directories.html_images, &error)) {
3027 if (! g_error_matches(error, G_IO_ERROR, G_IO_ERROR_EXISTS)) {
3028 cleanup_and_terminate (self, error);
3029 return;
3030 }
3031 else
3032 g_clear_error (&error);
3033 }
3034 if ((self->priv->n_pages > 1) && ! make_album_dir (self->priv->tmp_dir, self->priv->directories.html_indexes, &error)) {
3035 if (! g_error_matches(error, G_IO_ERROR, G_IO_ERROR_EXISTS)) {
3036 cleanup_and_terminate (self, error);
3037 return;
3038 }
3039 else
3040 g_clear_error (&error);
3041 }
3042 if (! make_album_dir (self->priv->tmp_dir, self->priv->directories.theme_files, &error)) {
3043 if (! g_error_matches(error, G_IO_ERROR, G_IO_ERROR_EXISTS)) {
3044 cleanup_and_terminate (self, error);
3045 return;
3046 }
3047 else
3048 g_clear_error (&error);
3049 }
3050 }
3051
3052 parse_theme_files (self);
3053
3054 required_attributes = g_string_new (GFILE_STANDARD_ATTRIBUTES_WITH_CONTENT_TYPE);
3055 if (gth_main_extension_is_active ("image_rotation"))
3056 g_string_append (required_attributes, ",Embedded::Image::Orientation"); /* required to rotate jpeg images */
3057 if (self->priv->image_attributes != NULL) {
3058 g_string_append (required_attributes, ",");
3059 g_string_append (required_attributes, self->priv->image_attributes);
3060 }
3061 if (self->priv->image_description_enabled) {
3062 g_string_append (required_attributes, ",general::description");
3063 g_string_append (required_attributes, ",general::title");
3064 }
3065 if (self->priv->thumbnail_caption != NULL) {
3066 g_string_append (required_attributes, ",");
3067 g_string_append (required_attributes, self->priv->thumbnail_caption);
3068 }
3069 if ((self->priv->sort_type != NULL) && (self->priv->sort_type->required_attributes != NULL)) {
3070 g_string_append (required_attributes, ",");
3071 g_string_append (required_attributes, self->priv->sort_type->required_attributes);
3072 }
3073 _g_query_all_metadata_async (self->priv->gfile_list,
3074 GTH_LIST_DEFAULT,
3075 required_attributes->str,
3076 gth_task_get_cancellable (GTH_TASK (self)),
3077 file_list_info_ready_cb,
3078 self);
3079
3080 g_string_free (required_attributes, TRUE);
3081 }
3082
3083
3084 static void
gth_web_exporter_cancelled(GthTask * task)3085 gth_web_exporter_cancelled (GthTask *task)
3086 {
3087 GthWebExporter *self;
3088
3089 g_return_if_fail (GTH_IS_WEB_EXPORTER (task));
3090
3091 self = GTH_WEB_EXPORTER (task);
3092 self->priv->interrupted = TRUE;
3093 }
3094
3095
3096 static void
gth_web_exporter_finalize(GObject * object)3097 gth_web_exporter_finalize (GObject *object)
3098 {
3099 GthWebExporter *self;
3100
3101 g_return_if_fail (GTH_IS_WEB_EXPORTER (object));
3102
3103 self = GTH_WEB_EXPORTER (object);
3104 g_free (self->priv->header);
3105 g_free (self->priv->footer);
3106 g_free (self->priv->image_page_header);
3107 g_free (self->priv->image_page_footer);
3108 _g_object_unref (self->priv->style_dir);
3109 _g_object_unref (self->priv->target_dir);
3110 _g_object_unref (self->priv->tmp_dir);
3111 g_free (self->priv->directories.previews);
3112 g_free (self->priv->directories.thumbnails);
3113 g_free (self->priv->directories.images);
3114 g_free (self->priv->directories.html_images);
3115 g_free (self->priv->directories.html_indexes);
3116 g_free (self->priv->directories.theme_files);
3117 g_free (self->priv->index_file);
3118 _g_object_unref (self->priv->iloader);
3119 g_free (self->priv->thumbnail_caption);
3120 g_free (self->priv->image_attributes);
3121 free_parsed_docs (self);
3122 if (self->priv->file_list != NULL) {
3123 g_list_foreach (self->priv->file_list, (GFunc) image_data_free, NULL);
3124 g_list_free (self->priv->file_list);
3125 }
3126 _g_object_list_unref (self->priv->gfile_list);
3127
3128 G_OBJECT_CLASS (gth_web_exporter_parent_class)->finalize (object);
3129 }
3130
3131
3132 static void
gth_web_exporter_class_init(GthWebExporterClass * klass)3133 gth_web_exporter_class_init (GthWebExporterClass *klass)
3134 {
3135 GObjectClass *object_class;
3136 GthTaskClass *task_class;
3137
3138 object_class = G_OBJECT_CLASS (klass);
3139 object_class->finalize = gth_web_exporter_finalize;
3140
3141 task_class = GTH_TASK_CLASS (klass);
3142 task_class->exec = gth_web_exporter_exec;
3143 task_class->cancelled = gth_web_exporter_cancelled;
3144 }
3145
3146
3147 static void
gth_web_exporter_init(GthWebExporter * self)3148 gth_web_exporter_init (GthWebExporter *self)
3149 {
3150 self->priv = gth_web_exporter_get_instance_private (self);
3151 self->priv->header = NULL;
3152 self->priv->footer = NULL;
3153 self->priv->image_page_header = NULL;
3154 self->priv->image_page_footer = NULL;
3155 self->priv->style_dir = NULL;
3156 self->priv->target_dir = NULL;
3157 self->priv->use_subfolders = TRUE;
3158 self->priv->directories.previews = NULL;
3159 self->priv->directories.thumbnails = NULL;
3160 self->priv->directories.images = NULL;
3161 self->priv->directories.html_images = NULL;
3162 self->priv->directories.html_indexes = NULL;
3163 self->priv->directories.theme_files = NULL;
3164 self->priv->copy_images = FALSE;
3165 self->priv->resize_images = FALSE;
3166 self->priv->resize_max_width = 0;
3167 self->priv->resize_max_height = 0;
3168 self->priv->sort_type = NULL;
3169 self->priv->sort_inverse = FALSE;
3170 self->priv->images_per_index = 0;
3171 self->priv->columns_per_page = 0;
3172 self->priv->rows_per_page = 0;
3173 self->priv->single_index = FALSE;
3174 self->priv->thumb_width = DEFAULT_THUMB_SIZE;
3175 self->priv->thumb_height = DEFAULT_THUMB_SIZE;
3176 self->priv->preview_max_width = 0;
3177 self->priv->preview_max_height = 0;
3178 self->priv->preview_min_width = 0;
3179 self->priv->preview_min_height = 0;
3180 self->priv->thumbnail_caption = NULL;
3181 self->priv->image_attributes = NULL;
3182 self->priv->index_file = g_strdup (DEFAULT_INDEX_FILE);
3183 self->priv->file_list = NULL;
3184 self->priv->tmp_dir = NULL;
3185 self->priv->interrupted = FALSE;
3186 self->priv->iloader = gth_image_loader_new (NULL, NULL);
3187 self->priv->error = NULL;
3188 }
3189
3190
3191 GthTask *
gth_web_exporter_new(GthBrowser * browser,GList * file_list)3192 gth_web_exporter_new (GthBrowser *browser,
3193 GList *file_list)
3194 {
3195 GthWebExporter *self;
3196
3197 g_return_val_if_fail (browser != NULL, NULL);
3198
3199 self = (GthWebExporter *) g_object_new (GTH_TYPE_WEB_EXPORTER, NULL);
3200 self->priv->browser = browser;
3201 self->priv->gfile_list = _g_object_list_ref (file_list);
3202
3203 return (GthTask *) self;
3204 }
3205
3206
3207 void
gth_web_exporter_set_header(GthWebExporter * self,const char * value)3208 gth_web_exporter_set_header (GthWebExporter *self,
3209 const char *value)
3210 {
3211 g_return_if_fail (GTH_IS_WEB_EXPORTER (self));
3212
3213 g_free (self->priv->header);
3214 self->priv->header = g_strdup (value);
3215 }
3216
3217
3218 void
gth_web_exporter_set_footer(GthWebExporter * self,const char * value)3219 gth_web_exporter_set_footer (GthWebExporter *self,
3220 const char *value)
3221 {
3222 g_return_if_fail (GTH_IS_WEB_EXPORTER (self));
3223
3224 g_free (self->priv->footer);
3225 self->priv->footer = g_strdup (value);
3226 }
3227
3228
3229 void
gth_web_exporter_set_image_page_header(GthWebExporter * self,const char * value)3230 gth_web_exporter_set_image_page_header (GthWebExporter *self,
3231 const char *value)
3232 {
3233 g_return_if_fail (GTH_IS_WEB_EXPORTER (self));
3234
3235 g_free (self->priv->image_page_header);
3236 if ((value != NULL) && (*value != '\0'))
3237 self->priv->image_page_header = g_strdup (value);
3238 else
3239 self->priv->image_page_header = NULL;
3240 }
3241
3242
3243 void
gth_web_exporter_set_image_page_footer(GthWebExporter * self,const char * value)3244 gth_web_exporter_set_image_page_footer (GthWebExporter *self,
3245 const char *value)
3246 {
3247 g_return_if_fail (GTH_IS_WEB_EXPORTER (self));
3248
3249 g_free (self->priv->image_page_footer);
3250 if ((value != NULL) && (*value != '\0'))
3251 self->priv->image_page_footer = g_strdup (value);
3252 else
3253 self->priv->image_page_footer = NULL;
3254 }
3255
3256
3257 void
gth_web_exporter_set_style(GthWebExporter * self,const char * style_name)3258 gth_web_exporter_set_style (GthWebExporter *self,
3259 const char *style_name)
3260 {
3261 g_return_if_fail (GTH_IS_WEB_EXPORTER (self));
3262
3263 _g_object_unref (self->priv->style_dir);
3264 self->priv->style_dir = get_style_dir (self, style_name);
3265 }
3266
3267
3268 void
gth_web_exporter_set_destination(GthWebExporter * self,GFile * destination)3269 gth_web_exporter_set_destination (GthWebExporter *self,
3270 GFile *destination)
3271 {
3272 g_return_if_fail (GTH_IS_WEB_EXPORTER (self));
3273
3274 _g_object_unref (self->priv->target_dir);
3275 self->priv->target_dir = _g_object_ref (destination);
3276 }
3277
3278
3279 void
gth_web_exporter_set_use_subfolders(GthWebExporter * self,gboolean use_subfolders)3280 gth_web_exporter_set_use_subfolders (GthWebExporter *self,
3281 gboolean use_subfolders)
3282 {
3283 g_return_if_fail (GTH_IS_WEB_EXPORTER (self));
3284
3285 self->priv->use_subfolders = use_subfolders;
3286 }
3287
3288
3289 void
gth_web_exporter_set_copy_images(GthWebExporter * self,gboolean copy)3290 gth_web_exporter_set_copy_images (GthWebExporter *self,
3291 gboolean copy)
3292 {
3293 g_return_if_fail (GTH_IS_WEB_EXPORTER (self));
3294
3295 self->priv->copy_images = copy;
3296 }
3297
3298
3299 void
gth_web_exporter_set_resize_images(GthWebExporter * self,gboolean resize,int max_width,int max_height)3300 gth_web_exporter_set_resize_images (GthWebExporter *self,
3301 gboolean resize,
3302 int max_width,
3303 int max_height)
3304 {
3305 g_return_if_fail (GTH_IS_WEB_EXPORTER (self));
3306
3307 self->priv->resize_images = resize;
3308 if (resize) {
3309 self->priv->resize_max_width = max_width;
3310 self->priv->resize_max_height = max_height;
3311 }
3312 else {
3313 self->priv->resize_max_width = 0;
3314 self->priv->resize_max_height = 0;
3315 }
3316 }
3317
3318
3319 void
gth_web_exporter_set_sort_order(GthWebExporter * self,GthFileDataSort * sort_type,gboolean sort_inverse)3320 gth_web_exporter_set_sort_order (GthWebExporter *self,
3321 GthFileDataSort *sort_type,
3322 gboolean sort_inverse)
3323 {
3324 g_return_if_fail (GTH_IS_WEB_EXPORTER (self));
3325
3326 self->priv->sort_type = sort_type;
3327 self->priv->sort_inverse = sort_inverse;
3328 }
3329
3330
3331 void
gth_web_exporter_set_images_per_index(GthWebExporter * self,int value)3332 gth_web_exporter_set_images_per_index (GthWebExporter *self,
3333 int value)
3334 {
3335 g_return_if_fail (GTH_IS_WEB_EXPORTER (self));
3336
3337 self->priv->images_per_index = value;
3338 }
3339
3340
3341 void
gth_web_exporter_set_single_index(GthWebExporter * self,gboolean value)3342 gth_web_exporter_set_single_index (GthWebExporter *self,
3343 gboolean value)
3344 {
3345 g_return_if_fail (GTH_IS_WEB_EXPORTER (self));
3346
3347 self->priv->single_index = value;
3348 }
3349
3350
3351 void
gth_web_exporter_set_columns(GthWebExporter * self,int cols)3352 gth_web_exporter_set_columns (GthWebExporter *self,
3353 int cols)
3354 {
3355 g_return_if_fail (GTH_IS_WEB_EXPORTER (self));
3356
3357 self->priv->columns_per_page = cols;
3358 }
3359
3360
3361 void
gth_web_exporter_set_adapt_to_width(GthWebExporter * self,gboolean value)3362 gth_web_exporter_set_adapt_to_width (GthWebExporter *self,
3363 gboolean value)
3364 {
3365 g_return_if_fail (GTH_IS_WEB_EXPORTER (self));
3366
3367 self->priv->adapt_to_width = value;
3368 }
3369
3370
3371 void
gth_web_exporter_set_thumb_size(GthWebExporter * self,gboolean squared,int width,int height)3372 gth_web_exporter_set_thumb_size (GthWebExporter *self,
3373 gboolean squared,
3374 int width,
3375 int height)
3376 {
3377 g_return_if_fail (GTH_IS_WEB_EXPORTER (self));
3378
3379 self->priv->squared_thumbnails = squared;
3380 self->priv->thumb_width = width;
3381 self->priv->thumb_height = height;
3382 }
3383
3384
3385 void
gth_web_exporter_set_preview_size(GthWebExporter * self,int width,int height)3386 gth_web_exporter_set_preview_size (GthWebExporter *self,
3387 int width,
3388 int height)
3389 {
3390 g_return_if_fail (GTH_IS_WEB_EXPORTER (self));
3391
3392 self->priv->preview_max_width = width;
3393 self->priv->preview_max_height = height;
3394 }
3395
3396
3397 void
gth_web_exporter_set_preview_min_size(GthWebExporter * self,int width,int height)3398 gth_web_exporter_set_preview_min_size (GthWebExporter *self,
3399 int width,
3400 int height)
3401 {
3402 g_return_if_fail (GTH_IS_WEB_EXPORTER (self));
3403
3404 self->priv->preview_min_width = width;
3405 self->priv->preview_min_height = height;
3406 }
3407
3408
3409 void
gth_web_exporter_set_image_attributes(GthWebExporter * self,gboolean image_description_enabled,const char * caption)3410 gth_web_exporter_set_image_attributes (GthWebExporter *self,
3411 gboolean image_description_enabled,
3412 const char *caption)
3413 {
3414 g_return_if_fail (GTH_IS_WEB_EXPORTER (self));
3415
3416 self->priv->image_description_enabled = image_description_enabled;
3417
3418 g_free (self->priv->image_attributes);
3419 self->priv->image_attributes = g_strdup (caption);
3420 }
3421
3422
3423 void
gth_web_exporter_set_thumbnail_caption(GthWebExporter * self,const char * caption)3424 gth_web_exporter_set_thumbnail_caption (GthWebExporter *self,
3425 const char *caption)
3426 {
3427 g_return_if_fail (GTH_IS_WEB_EXPORTER (self));
3428
3429 g_free (self->priv->thumbnail_caption);
3430 self->priv->thumbnail_caption = g_strdup (caption);
3431 }
3432