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, "    &nbsp;\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, "&nbsp;", 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