1 /* -*- Mode: C; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 3 -*- */
2
3 /*
4 * GImageView
5 * Copyright (C) 2001 Takuro Ashie
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 *
21 * $Id: gimv_comment.c,v 1.3 2004/03/07 11:53:30 makeinu Exp $
22 */
23
24 /*
25 * If use GTK+-2.0, internal character code for comment will be UTF-8.
26 * Else, it will be locale encode.
27 * System entry list will be ASCII only. Do not use any other character set.
28 */
29
30 #include "gimv_comment.h"
31
32 #include <stdio.h>
33 #include <string.h>
34 #include <time.h>
35
36 #include "charset.h"
37 #include "fileutil.h"
38 #include "gimv_image_info.h"
39 #include "gimv_mime_types.h"
40 #include "gtk2-compat.h"
41 #include "gtkutils.h"
42 #include "prefs.h"
43
44 #define GIMV_COMMENT_DIRECTORY ".gimv/comment"
45
46 typedef enum {
47 FILE_SAVED,
48 FILE_DELETED,
49 LAST_SIGNAL
50 } GimvCommentSignalType;
51
52
53 static void gimv_comment_init (GimvComment *comment);
54 static void gimv_comment_class_init (GimvCommentClass *klass);
55 static void gimv_comment_destroy (GtkObject *object);
56
57 static gchar *defval_time (GimvImageInfo *info, gpointer data);
58 static gchar *defval_file_url (GimvImageInfo *info, gpointer data);
59 static gchar *defval_file_path_in_archive (GimvImageInfo *info, gpointer data);
60 static gchar *defval_file_mtime (GimvImageInfo *info, gpointer data);
61 static gchar *defval_img_type (GimvImageInfo *info, gpointer data);
62 static gchar *defval_img_size (GimvImageInfo *info, gpointer data);
63 #if 0
64 static gchar *defval_img_depth (GimvImageInfo *info, gpointer data);
65 static gchar *defval_img_cspace (GimvImageInfo *info, gpointer data);
66 #endif
67
68
69 static GtkObjectClass *parent_class = NULL;
70 static gint gimv_comment_signals[LAST_SIGNAL] = {0};
71
72
73 GimvCommentDataEntry gimv_comment_data_entry[] = {
74 {"X-IMG-Subject", N_("Subject"), NULL, TRUE, FALSE, TRUE, FALSE, NULL},
75 {"X-IMG-Date", N_("Date"), NULL, TRUE, FALSE, TRUE, FALSE, NULL},
76 {"X-IMG-Location", N_("Location"), NULL, TRUE, FALSE, TRUE, FALSE, NULL},
77 {"X-IMG-Model", N_("Model"), NULL, TRUE, FALSE, TRUE, FALSE, NULL},
78 {"X-IMG-Comment-Time", N_("Comment Time"), NULL, TRUE, TRUE, FALSE, FALSE, defval_time},
79 {"X-IMG-File-URL", N_("URL"), NULL, TRUE, TRUE, TRUE, FALSE, defval_file_url},
80 {"X-IMG-File-Path-In-Archive", N_("Path in Archive"), NULL, TRUE, TRUE, TRUE, FALSE, defval_file_path_in_archive},
81 {"X-IMG-File-MTime", N_("Modification Time"), NULL, TRUE, TRUE, TRUE, FALSE, defval_file_mtime},
82 {"X-IMG-Image-Type", N_("Image Type"), NULL, TRUE, TRUE, TRUE, FALSE, defval_img_type},
83 {"X-IMG-Image-Size", N_("Image Size"), NULL, TRUE, TRUE, TRUE, FALSE, defval_img_size},
84 /*
85 {"X-IMG-Image-Depth", N_("Color Depth"), NULL, TRUE, TRUE, TRUE, FALSE, defval_img_depth},
86 {"X-IMG-Image-ColorSpace", N_("Color Space"), NULL, TRUE, TRUE, TRUE, FALSE, defval_img_cspace},
87 */
88 {NULL, NULL, NULL, FALSE, FALSE, FALSE, FALSE, NULL},
89 };
90
91 GList *gimv_comment_data_entry_list = NULL;
92
93
94
95 /******************************************************************************
96 *
97 * GimvComment class funcs
98 *
99 ******************************************************************************/
100 GtkType
gimv_comment_get_type()101 gimv_comment_get_type ()
102 {
103 static GtkType gimv_comment_type = 0;
104
105 if (!gimv_comment_type) {
106 static const GtkTypeInfo gimv_comment_info = {
107 "GimvComment",
108 sizeof (GimvComment),
109 sizeof (GimvCommentClass),
110 (GtkClassInitFunc) gimv_comment_class_init,
111 (GtkObjectInitFunc) gimv_comment_init,
112 NULL,
113 NULL,
114 (GtkClassInitFunc) NULL,
115 };
116
117 gimv_comment_type = gtk_type_unique (gtk_object_get_type (), &gimv_comment_info);
118 }
119
120 return gimv_comment_type;
121 }
122
123
124 static void
gimv_comment_class_init(GimvCommentClass * klass)125 gimv_comment_class_init (GimvCommentClass *klass)
126 {
127 GtkObjectClass *object_class;
128
129 object_class = (GtkObjectClass *) klass;
130 parent_class = gtk_type_class (gtk_object_get_type ());
131
132 gimv_comment_signals[FILE_SAVED]
133 = gtk_signal_new ("file_saved",
134 GTK_RUN_FIRST,
135 GTK_CLASS_TYPE (object_class),
136 GTK_SIGNAL_OFFSET (GimvCommentClass, file_saved),
137 gtk_signal_default_marshaller,
138 GTK_TYPE_NONE, 0);
139
140 gimv_comment_signals[FILE_DELETED]
141 = gtk_signal_new ("file_deleted",
142 GTK_RUN_FIRST,
143 GTK_CLASS_TYPE (object_class),
144 GTK_SIGNAL_OFFSET (GimvCommentClass, file_deleted),
145 gtk_signal_default_marshaller,
146 GTK_TYPE_NONE, 0);
147
148 gtk_object_class_add_signals (object_class, gimv_comment_signals, LAST_SIGNAL);
149
150 object_class->destroy = gimv_comment_destroy;
151
152 klass->file_saved = NULL;
153 klass->file_deleted = NULL;
154 }
155
156
157 static void
gimv_comment_init(GimvComment * comment)158 gimv_comment_init (GimvComment *comment)
159 {
160 comment->filename = NULL;
161 comment->info = NULL;
162
163 comment->data_list = NULL;
164
165 comment->note = NULL;
166
167 #ifdef USE_GTK2
168 gtk_object_ref (GTK_OBJECT (comment));
169 gtk_object_sink (GTK_OBJECT (comment));
170 #endif
171 }
172
173
174 static void
gimv_comment_destroy(GtkObject * object)175 gimv_comment_destroy (GtkObject *object)
176 {
177 GimvComment *comment = GIMV_COMMENT (object);
178 GList *node;
179
180 g_return_if_fail (comment);
181 g_return_if_fail (GIMV_IS_COMMENT (comment));
182
183 if (comment->filename) {
184 g_free (comment->filename);
185 comment->filename = NULL;
186 }
187
188 if (comment->info) {
189 gimv_image_info_set_comment (comment->info, NULL);
190 gimv_image_info_unref (comment->info);
191 comment->info = NULL;
192 }
193
194 node = comment->data_list;
195 while (node) {
196 GimvCommentDataEntry *entry = node->data;
197 node = g_list_next (node);
198 comment->data_list = g_list_remove (comment->data_list, entry);
199 gimv_comment_data_entry_delete (entry);
200 }
201
202 if (comment->note) {
203 g_free (comment->note);
204 comment->note = NULL;
205 }
206
207 if (GTK_OBJECT_CLASS (parent_class)->destroy)
208 (*GTK_OBJECT_CLASS (parent_class)->destroy) (object);
209 }
210
211
212 GimvComment *
gimv_comment_ref(GimvComment * comment)213 gimv_comment_ref (GimvComment *comment)
214 {
215 g_return_val_if_fail (comment, NULL);
216 g_return_val_if_fail (GIMV_IS_COMMENT (comment), NULL);
217
218 gtk_object_ref (GTK_OBJECT (comment));
219
220 return comment;
221 }
222
223
224 void
gimv_comment_unref(GimvComment * comment)225 gimv_comment_unref (GimvComment *comment)
226 {
227 g_return_if_fail (comment);
228 g_return_if_fail (GIMV_IS_COMMENT (comment));
229
230 gtk_object_unref (GTK_OBJECT (comment));
231 }
232
233
234 /******************************************************************************
235 *
236 * private functions.
237 *
238 ******************************************************************************/
239 static const gchar *
get_file_charset()240 get_file_charset ()
241 {
242 const gchar *lang;
243
244 if (conf.comment_charset && *conf.comment_charset
245 && g_strcasecmp (conf.comment_charset, "default"))
246 {
247 return conf.comment_charset;
248 }
249
250 lang = get_lang ();
251
252 if (!lang || !*lang)
253 return charset_get_internal ();
254
255 /* get default charset for each language */
256 if (!strncmp (lang, "ja", 2)) { /* japanese */
257 return CHARSET_JIS;
258 } else {
259 return charset_get_internal ();
260 }
261 }
262
263
264 static gchar *
defval_time(GimvImageInfo * info,gpointer data)265 defval_time (GimvImageInfo *info, gpointer data)
266 {
267 time_t current_time;
268 struct tm tm_buf;
269 gchar buf[256];
270
271 time(¤t_time);
272 tm_buf = *localtime (¤t_time);
273 strftime(buf, 256, "%Y-%m-%d %H:%M:%S %Z", &tm_buf);
274
275 return charset_locale_to_internal (buf);
276 }
277
278
279 static gchar *
defval_file_url(GimvImageInfo * info,gpointer data)280 defval_file_url (GimvImageInfo *info, gpointer data)
281 {
282 const gchar *filename;
283
284 g_return_val_if_fail (info, NULL);
285
286 if (gimv_image_info_is_in_archive (info)) {
287 filename = gimv_image_info_get_archive_path (info);
288 } else {
289 filename = gimv_image_info_get_path (info);
290 }
291
292 if (filename) {
293 return charset_to_internal (filename, conf.charset_filename,
294 conf.charset_auto_detect_fn,
295 conf.comment_charset_read_mode);
296 } else {
297 return NULL;
298 }
299 }
300
301
302 static gchar *
defval_file_path_in_archive(GimvImageInfo * info,gpointer data)303 defval_file_path_in_archive (GimvImageInfo *info, gpointer data)
304 {
305 const gchar *path;
306
307 g_return_val_if_fail (info, NULL);
308 if (!gimv_image_info_is_in_archive (info)) return NULL;
309
310 path = gimv_image_info_get_path (info);
311
312 if (path)
313 return charset_to_internal (path, conf.charset_filename,
314 conf.charset_auto_detect_fn,
315 conf.comment_charset_read_mode);
316 else
317 return NULL;
318 }
319
320
321 static gchar *
defval_file_mtime(GimvImageInfo * info,gpointer data)322 defval_file_mtime (GimvImageInfo *info, gpointer data)
323 {
324 struct tm tm_buf;
325 gchar buf[256];
326
327 g_return_val_if_fail (info, NULL);
328
329 tm_buf = *localtime (&info->st.st_mtime);
330 strftime(buf, 256, "%Y-%m-%d %H:%M:%S %Z", &tm_buf);
331
332 return charset_locale_to_internal (buf);
333 }
334
335
336 static gchar *
defval_img_type(GimvImageInfo * info,gpointer data)337 defval_img_type (GimvImageInfo *info, gpointer data)
338 {
339 const gchar *filename;
340 const gchar *type = NULL;
341
342 g_return_val_if_fail (info, NULL);
343
344 filename = gimv_image_info_get_path (info);
345 if (filename) {
346 const gchar *ext = gimv_mime_types_get_extension (filename);
347 type = gimv_mime_types_get_type_from_ext (ext);
348 }
349
350 if (type)
351 return g_strdup (type); /* FIXME: need convert? */
352 else
353 return NULL;
354 }
355
356
357 static gchar *
defval_img_size(GimvImageInfo * info,gpointer data)358 defval_img_size (GimvImageInfo *info, gpointer data)
359 {
360 gchar buf[256];
361
362 g_return_val_if_fail (info, NULL);
363
364 g_snprintf (buf, 256, "%dx%d", info->width, info->height);
365
366 return g_strdup (buf);
367 }
368
369
370 #if 0
371 static gchar *
372 defval_img_depth (GimvImageInfo *info, gpointer data)
373 {
374 g_return_val_if_fail (info, NULL);
375 return NULL;
376 }
377
378
379 static gchar *
380 defval_img_cspace (GimvImageInfo *info, gpointer data)
381 {
382 g_return_val_if_fail (info, NULL);
383 return NULL;
384 }
385 #endif
386
387
388 static GimvComment *
gimv_comment_new()389 gimv_comment_new ()
390 {
391 GimvComment *comment;
392
393 comment = GIMV_COMMENT (gtk_type_new (gimv_comment_get_type ()));
394 g_return_val_if_fail (comment, NULL);
395
396 return comment;
397 }
398
399
400 static void
parse_comment_file(GimvComment * comment)401 parse_comment_file (GimvComment *comment)
402 {
403 FILE *file;
404 gchar buf[BUF_SIZE];
405 gchar **pair, *tmpstr;
406 gint i;
407 gboolean is_note = FALSE;
408 gchar *key_internal, *value_internal = NULL;
409
410 g_return_if_fail (comment);
411 g_return_if_fail (comment->filename);
412
413 if (!file_exists (comment->filename)) return;
414
415 file = fopen (comment->filename, "r");
416 if (!file) {
417 g_warning (_("Can't open comment file for read."));
418 return;
419 }
420
421 while (fgets (buf, sizeof(buf), file)) {
422 gchar *key, *value = NULL;
423
424 if (is_note) {
425 if (comment->note) {
426 tmpstr = g_strconcat (comment->note, buf, NULL);
427 g_free (comment->note);
428 comment->note = tmpstr;
429 } else {
430 comment->note = g_strdup (buf);
431 }
432 continue;
433 }
434
435 if (buf[0] == '\n') {
436 is_note = TRUE;
437 continue;
438 }
439
440 pair = g_strsplit (buf, ":", -1);
441 if (!pair[0]) continue;
442 if (!*pair[0]) goto ERROR;
443 key = g_strdup (pair[0]);
444 g_strstrip (key);
445
446 if (pair[1]) {
447 value = g_strdup (pair[1]);
448 for (i = 2; pair[i]; i++) { /* concat all value to one string */
449 gchar *tmpstr = value;
450 value = g_strconcat (value, ":", pair[i], NULL);
451 g_free (tmpstr);
452 }
453 g_strstrip (value);
454 if (!*value) {
455 g_free (value);
456 value = NULL;
457 }
458 } else {
459 value = NULL;
460 }
461
462 key_internal = charset_to_internal (key, get_file_charset (),
463 conf.charset_auto_detect_fn,
464 conf.comment_charset_read_mode);
465 if (value && *value) {
466 value_internal = charset_to_internal (value, get_file_charset (),
467 conf.charset_auto_detect_fn,
468 conf.comment_charset_read_mode);
469 } else {
470 value_internal = NULL;
471 }
472
473 gimv_comment_append_data (comment, key_internal, value_internal);
474
475 g_free (key_internal);
476 g_free (value_internal);
477
478 g_free (key);
479 g_free (value);
480
481 ERROR:
482 g_strfreev (pair);
483 }
484
485 fclose (file);
486
487 if (comment->note && *comment->note) {
488 gchar *note_internal;
489
490 note_internal = charset_to_internal (comment->note, get_file_charset (),
491 conf.charset_auto_detect_fn,
492 conf.comment_charset_read_mode);
493
494 g_free (comment->note);
495 comment->note = note_internal;
496 }
497 }
498
499
500 static void
gimv_comment_set_default_value(GimvComment * comment)501 gimv_comment_set_default_value (GimvComment *comment)
502 {
503 GList *list = gimv_comment_get_data_entry_list ();
504
505 while (list) {
506 GimvCommentDataEntry *template = list->data, *entry;
507
508 list = g_list_next (list);
509
510 if (!template) continue;
511
512 entry = g_new0 (GimvCommentDataEntry, 1);
513
514 *entry = *template;
515 entry->key = g_strdup (template->key);
516 entry->display_name = g_strdup (template->display_name);
517
518 if (comment->info && template->def_val_fn) {
519 entry->value = entry->def_val_fn (comment->info, NULL);
520 } else {
521 entry->value = NULL;
522 }
523
524 comment->data_list = g_list_append (comment->data_list, entry);
525 }
526 }
527
528
529 /******************************************************************************
530 *
531 * public functions.
532 *
533 ******************************************************************************/
534 gchar *
gimv_comment_get_path(const gchar * img_path)535 gimv_comment_get_path (const gchar *img_path)
536 {
537 gchar buf[MAX_PATH_LEN];
538
539 g_return_val_if_fail (img_path && *img_path, NULL);
540 g_return_val_if_fail (img_path[0] == '/', NULL);
541
542 g_snprintf (buf, MAX_PATH_LEN, "%s/%s%s" ".txt",
543 g_getenv("HOME"), GIMV_COMMENT_DIRECTORY, img_path);
544
545 return g_strdup (buf);
546 }
547
548
549 gchar *
gimv_comment_find_file(const gchar * img_path)550 gimv_comment_find_file (const gchar *img_path)
551 {
552 gchar *path = gimv_comment_get_path (img_path);
553
554 if (!path) return NULL;
555
556 if (file_exists (path)) {
557 return path;
558 } else {
559 g_free (path);
560 return NULL;
561 }
562 }
563
564
565 GimvCommentDataEntry *
gimv_comment_data_entry_find_template_by_key(const gchar * key)566 gimv_comment_data_entry_find_template_by_key (const gchar *key)
567 {
568 GList *list;
569
570 g_return_val_if_fail (key && *key, NULL);
571
572 list = gimv_comment_get_data_entry_list ();
573 while (list) {
574 GimvCommentDataEntry *template = list->data;
575
576 list = g_list_next (list);
577
578 if (!template) continue;
579
580 if (template->key && *template->key && !strcmp (key, template->key))
581 return template;
582 }
583
584 return NULL;
585 }
586
587
588 GimvCommentDataEntry *
gimv_comment_find_data_entry_by_key(GimvComment * comment,const gchar * key)589 gimv_comment_find_data_entry_by_key (GimvComment *comment, const gchar *key)
590 {
591 GList *list;
592
593 g_return_val_if_fail (comment, NULL);
594 g_return_val_if_fail (key, NULL);
595
596 list = comment->data_list;
597 while (list) {
598 GimvCommentDataEntry *entry = list->data;
599
600 list = g_list_next (list);
601
602 if (!entry) continue;
603
604 if (entry->key && !strcmp (key, entry->key))
605 return entry;
606 }
607
608 return NULL;
609 }
610
611
612 /*
613 * gimv_comment_append_data:
614 * @ Append a key & value pair.
615 * @ Before enter this function, character set must be converted to internal
616 * code.
617 *
618 * comment :
619 * key :
620 * value :
621 * Return :
622 */
623 GimvCommentDataEntry *
gimv_comment_append_data(GimvComment * comment,const gchar * key,const gchar * value)624 gimv_comment_append_data (GimvComment *comment, const gchar *key, const gchar *value)
625 {
626 GimvCommentDataEntry *entry;
627
628 g_return_val_if_fail (comment, NULL);
629 g_return_val_if_fail (key, NULL);
630
631 entry = gimv_comment_find_data_entry_by_key (comment, key);
632
633 if (!entry) {
634 GimvCommentDataEntry *template;
635
636 entry = g_new0 (GimvCommentDataEntry, 1);
637 g_return_val_if_fail (entry, NULL);
638 comment->data_list = g_list_append (comment->data_list, entry);
639
640 /* find template */
641 template = gimv_comment_data_entry_find_template_by_key (key);
642 if (template) {
643 *entry = *template;
644 entry->key = g_strdup (template->key);
645 entry->display_name = g_strdup (template->display_name);
646 } else {
647 entry->key = g_strdup (key);
648 entry->display_name = g_strdup (key);
649 entry->value = NULL;
650 entry->enable = TRUE;
651 entry->auto_val = FALSE;
652 entry->display = TRUE;
653 entry->def_val_fn = NULL;
654 }
655 }
656
657 /* free old value */
658 if (entry->value) {
659 g_free (entry->value);
660 entry->value = NULL;
661 }
662
663 /* set new value */
664 if (entry->auto_val && entry->def_val_fn) {
665 entry->value = entry->def_val_fn (comment->info, NULL);
666 } else if (value) {
667 entry->value = g_strdup (value);
668 } else {
669 entry->value = NULL;
670 }
671
672 return entry;
673 }
674
675
676 /*
677 * gimv_comment_update_note:
678 * @ Apply "note" value.
679 * @ Before enter this function, character set must be converted to internal
680 * code.
681 *
682 * comment :
683 * note :
684 * Return :
685 */
686 gboolean
gimv_comment_update_note(GimvComment * comment,gchar * note)687 gimv_comment_update_note (GimvComment *comment, gchar *note)
688 {
689 g_return_val_if_fail (comment, FALSE);
690
691 if (comment->note)
692 g_free (comment->note);
693
694 comment->note = g_strdup (note);
695
696 return TRUE;
697 }
698
699
700 void
gimv_comment_data_entry_remove(GimvComment * comment,GimvCommentDataEntry * entry)701 gimv_comment_data_entry_remove (GimvComment *comment, GimvCommentDataEntry *entry)
702 {
703 GList *list;
704
705 g_return_if_fail (comment);
706 g_return_if_fail (entry);
707
708 list = g_list_find (comment->data_list, entry);
709 if (list) {
710 comment->data_list = g_list_remove (comment->data_list, entry);
711 gimv_comment_data_entry_delete (entry);
712 }
713 }
714
715
716 void
gimv_comment_data_entry_remove_by_key(GimvComment * comment,const gchar * key)717 gimv_comment_data_entry_remove_by_key (GimvComment *comment, const gchar *key)
718 {
719 GList *list;
720
721 g_return_if_fail (comment);
722 g_return_if_fail (key && *key);
723
724 list = comment->data_list;
725 while (list) {
726 GimvCommentDataEntry *entry = list->data;
727
728 list = g_list_next (list);
729
730 if (!entry) continue;
731
732 if (entry->key && !strcmp (key, entry->key)) {
733 comment->data_list = g_list_remove (comment->data_list, entry);
734 gimv_comment_data_entry_delete (entry);
735 }
736 }
737 }
738
739
740 GimvCommentDataEntry *
gimv_comment_data_entry_dup(GimvCommentDataEntry * src)741 gimv_comment_data_entry_dup (GimvCommentDataEntry *src)
742 {
743 GimvCommentDataEntry *dest;
744 g_return_val_if_fail (src, NULL);
745
746 dest = g_new0 (GimvCommentDataEntry, 1);
747 *dest = *src;
748 if (src->key)
749 dest->key = g_strdup (src->key);
750 if (src->display_name)
751 dest->display_name = g_strdup (src->display_name);
752
753 return dest;
754 }
755
756
757 void
gimv_comment_data_entry_delete(GimvCommentDataEntry * entry)758 gimv_comment_data_entry_delete (GimvCommentDataEntry *entry)
759 {
760 g_return_if_fail (entry);
761
762 g_free (entry->key);
763 entry->key = NULL;
764 g_free (entry->display_name);
765 entry->display_name = NULL;
766 if (entry->value)
767 g_free (entry->value);
768 entry->value = NULL;
769 g_free (entry);
770 }
771
772
773 gboolean
gimv_comment_save_file(GimvComment * comment)774 gimv_comment_save_file (GimvComment *comment)
775 {
776 GList *node;
777 FILE *file;
778 gboolean success;
779 gint n;
780
781 g_return_val_if_fail (comment, FALSE);
782 g_return_val_if_fail (comment->filename, FALSE);
783
784 g_print ("save comments to file: %s\n", comment->filename);
785
786 success = mkdirs (comment->filename);
787 if (!success) {
788 g_warning (_("cannot make dir\n"));
789 return FALSE;
790 }
791
792 file = fopen (comment->filename, "w");
793 if (!file) {
794 g_warning (_("Can't open comment file for write."));
795 return FALSE;
796 }
797
798 node = comment->data_list;
799 while (node) {
800 GimvCommentDataEntry *entry = node->data;
801
802 node = g_list_next (node);
803
804 if (!entry || !entry->key) continue;
805
806 { /********** convert charset **********/
807 gchar *tmpstr, buf[BUF_SIZE];
808
809 if (entry->value && *entry->value)
810 g_snprintf (buf, BUF_SIZE, "%s: %s\n", entry->key, entry->value);
811 else
812 g_snprintf (buf, BUF_SIZE, "%s:\n", entry->key);
813
814 tmpstr = charset_from_internal (buf, get_file_charset ());
815
816 n = fprintf (file, "%s", tmpstr);
817
818 g_free (tmpstr);
819 }
820
821 if (n < 0) goto ERROR;
822 }
823
824 /********** convert charset **********/
825 if (comment->note && *comment->note) {
826 gchar *tmpstr;
827
828 tmpstr = charset_from_internal (comment->note, get_file_charset ());
829
830 n = fprintf (file, "\n%s", tmpstr);
831 if (n < 0) goto ERROR;
832
833 g_free (tmpstr);
834 }
835
836 fclose (file);
837
838 gtk_signal_emit (GTK_OBJECT (comment), gimv_comment_signals[FILE_SAVED]);
839
840 return TRUE;
841
842 ERROR:
843 fclose (file);
844
845 gtk_signal_emit (GTK_OBJECT (comment), gimv_comment_signals[FILE_SAVED]);
846 return FALSE;
847 }
848
849
850 void
gimv_comment_delete_file(GimvComment * comment)851 gimv_comment_delete_file (GimvComment *comment)
852 {
853 g_return_if_fail (comment);
854 g_return_if_fail (comment->filename);
855
856 remove (comment->filename);
857 }
858
859
860 GimvComment *
gimv_comment_get_from_image_info(GimvImageInfo * info)861 gimv_comment_get_from_image_info (GimvImageInfo *info)
862 {
863 GimvComment *comment;
864 gchar *image_name;
865
866 g_return_val_if_fail (info, NULL);
867
868 comment = gimv_image_info_get_comment (info);
869 if (comment)
870 return gimv_comment_ref (comment);
871
872 comment = gimv_comment_new ();
873 comment->info = gimv_image_info_ref (info);
874 gimv_image_info_set_comment (info, comment);
875
876 /* get comment file path */
877 image_name = gimv_image_info_get_path_with_archive (info);
878
879 comment->filename = gimv_comment_get_path (image_name);
880
881 /* get comment */
882 if (file_exists (comment->filename)) {
883 parse_comment_file (comment);
884 } else {
885 gimv_comment_set_default_value (comment);
886 }
887
888 g_free (image_name);
889
890 return comment;
891 }
892
893
894 /******************************************************************************
895 *
896 * Key list related functions
897 *
898 ******************************************************************************/
899 static void
gimv_comment_prefs_set_default_key_list()900 gimv_comment_prefs_set_default_key_list ()
901 {
902 gchar *string, *tmp;
903 gint i;
904
905 if (conf.comment_key_list) return;
906
907 string = g_strdup ("");
908
909 for (i = 0; gimv_comment_data_entry[i].key; i++) {
910 GimvCommentDataEntry *entry = &gimv_comment_data_entry[i];
911
912 tmp = string;
913 if (*string) {
914 string = g_strconcat (string, "; ", NULL);
915 g_free (tmp);
916 tmp = string;
917 }
918 string = g_strconcat (string, entry->key, ",", entry->display_name,
919 ",", boolean_to_text (entry->enable),
920 ",", boolean_to_text (entry->auto_val),
921 ",", boolean_to_text (entry->display),
922 NULL);
923 g_free (tmp);
924 tmp = NULL;
925 }
926
927 conf.comment_key_list = string;
928 }
929
930
931 static GList *
gimv_comment_get_system_data_entry_list(void)932 gimv_comment_get_system_data_entry_list (void)
933 {
934 GList *list = NULL;
935 gint i;
936
937 for (i = 0; gimv_comment_data_entry[i].key; i++) {
938 GimvCommentDataEntry *entry = g_new0 (GimvCommentDataEntry, 1);
939
940 list = g_list_append (list, entry);
941
942 *entry = gimv_comment_data_entry[i];
943 entry->key = g_strdup (gimv_comment_data_entry[i].key);
944 entry->display_name = g_strdup (gimv_comment_data_entry[i].display_name);
945 if (entry->value && *entry->value)
946 entry->value = g_strdup (entry->value);
947 }
948
949 return list;
950 }
951
952
953 static void
free_data_entry_list(void)954 free_data_entry_list (void)
955 {
956 if (!gimv_comment_data_entry_list) return;
957
958 g_list_foreach (gimv_comment_data_entry_list,
959 (GFunc) gimv_comment_data_entry_delete,
960 NULL);
961 g_list_free (gimv_comment_data_entry_list);
962 gimv_comment_data_entry_list = NULL;
963 }
964
965
966 static GimvCommentDataEntry *
gimv_comment_data_entry_new(gchar * key,gchar * name,gboolean enable,gboolean auto_val,gboolean display)967 gimv_comment_data_entry_new (gchar *key, gchar *name,
968 gboolean enable,
969 gboolean auto_val,
970 gboolean display)
971 {
972 GimvCommentDataEntry *entry = NULL;
973 gint i, pos = -1;
974
975 g_return_val_if_fail (key && *key, NULL);
976 g_return_val_if_fail (name && *name, NULL);
977
978 for (i = 0; gimv_comment_data_entry[i].key; i++) {
979 if (!strcmp (key, gimv_comment_data_entry[i].key)) {
980 pos = i;
981 break;
982 }
983 }
984
985 entry = g_new0 (GimvCommentDataEntry, 1);
986 if (!entry) return NULL;
987
988 if (pos < 0) {
989 entry->key = g_strdup (key);
990 entry->display_name = g_strdup (name);
991 entry->value = NULL;
992 entry->def_val_fn = NULL;
993 entry->userdef = TRUE;
994 } else {
995 *entry = gimv_comment_data_entry[pos];
996 entry->key = g_strdup (gimv_comment_data_entry[pos].key);
997 entry->display_name = g_strdup (gimv_comment_data_entry[pos].display_name);
998 entry->userdef = FALSE;
999 }
1000
1001 entry->enable = enable;
1002 if (entry->def_val_fn)
1003 entry->auto_val = auto_val;
1004 else
1005 entry->auto_val = FALSE;
1006 entry->display = display;
1007
1008 return entry;
1009 }
1010
1011
1012 static gint
compare_entry_by_key(gconstpointer a,gconstpointer b)1013 compare_entry_by_key (gconstpointer a, gconstpointer b)
1014 {
1015 const GimvCommentDataEntry *entry = a;
1016 const gchar *string = b;
1017
1018 g_return_val_if_fail (entry, TRUE);
1019 g_return_val_if_fail (string, TRUE);
1020
1021 if (!entry->key || !*entry->key) return TRUE;
1022 if (!string || !*string) return TRUE;
1023
1024 if (!strcmp (entry->key, string))
1025 return FALSE;
1026
1027 return TRUE;
1028 }
1029
1030
1031 void
gimv_comment_update_data_entry_list()1032 gimv_comment_update_data_entry_list ()
1033 {
1034 GList *node = NULL, *list;
1035 gchar **sections, **values;
1036 gint i, j;
1037
1038 if (!conf.comment_key_list)
1039 gimv_comment_prefs_set_default_key_list ();
1040
1041 if (gimv_comment_data_entry_list)
1042 free_data_entry_list ();
1043
1044 sections = g_strsplit (conf.comment_key_list, ";", -1);
1045 if (!sections) return;
1046
1047 /* for undefined system entry in prefs */
1048 list = gimv_comment_get_system_data_entry_list ();
1049
1050 for (i = 0; sections[i]; i++) {
1051 GimvCommentDataEntry *entry;
1052 gchar *key, *name;
1053
1054 if (!*sections[i]) continue;
1055
1056 values = g_strsplit (sections[i], ",", 5);
1057 if (!values) continue;
1058
1059 /* strip space characters */
1060 for (j = 0; j < 5; j++) {
1061 if (values[j] && *values[j])
1062 g_strstrip (values[j]);
1063 }
1064 if (!*values[0] || !*values[1]) goto ERROR1;
1065
1066 key = values[0];
1067 name = charset_locale_to_internal (values[1]);
1068
1069 /* check duplicate */
1070 node = NULL;
1071 node = g_list_find_custom (gimv_comment_data_entry_list,
1072 key, compare_entry_by_key);
1073 if (node) {
1074 gimv_comment_data_entry_delete (node->data);
1075 list = g_list_remove (list, node->data);
1076 }
1077
1078 /* add entry */
1079 entry = gimv_comment_data_entry_new (key, name,
1080 text_to_boolean (values[2]),
1081 text_to_boolean (values[3]),
1082 text_to_boolean (values[4]));
1083 g_free (name);
1084
1085 if (!entry) goto ERROR1;
1086
1087 gimv_comment_data_entry_list
1088 = g_list_append (gimv_comment_data_entry_list, entry);
1089
1090 /* for undefined system entry in prefs */
1091 if (!entry->userdef) {
1092 node = NULL;
1093 if (list)
1094 node = g_list_find_custom (list, entry->key, compare_entry_by_key);
1095 if (node) { /* this entry is already defined in prefs */
1096 gimv_comment_data_entry_delete (node->data);
1097 list = g_list_remove (list, node->data);
1098 }
1099 }
1100
1101 ERROR1:
1102 g_strfreev (values);
1103 values = NULL;
1104 }
1105
1106 /* append system defined entry if it isn't exist in prefs */
1107 for (node = list; node; node = g_list_next (node)) {
1108 if (node && node->data)
1109 gimv_comment_data_entry_list
1110 = g_list_append (gimv_comment_data_entry_list, node->data);
1111 }
1112
1113 if (list) g_list_free (list);
1114 g_strfreev (sections);
1115 }
1116
1117
1118 GList *
gimv_comment_get_data_entry_list()1119 gimv_comment_get_data_entry_list ()
1120 {
1121 if (!gimv_comment_data_entry_list)
1122 gimv_comment_update_data_entry_list ();
1123
1124 return gimv_comment_data_entry_list;
1125 }
1126