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(&current_time);
272    tm_buf = *localtime (&current_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