/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Pix
*
* Copyright (C) 2009 The Free Software Foundation, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include "gth-comment.h"
#define COMMENT_VERSION "3.0"
struct _GthCommentPrivate { /* All strings in utf8 format. */
char *caption;
char *note;
char *place;
int rating;
GPtrArray *categories;
GDate *date;
GthTime *time_of_day;
gboolean changed;
gboolean utf8;
};
static void gth_comment_gth_duplicable_interface_init (GthDuplicableInterface *iface);
static void gth_comment_dom_domizable_interface_init (DomDomizableInterface *iface);
G_DEFINE_TYPE_WITH_CODE (GthComment,
gth_comment,
G_TYPE_OBJECT,
G_ADD_PRIVATE (GthComment)
G_IMPLEMENT_INTERFACE (GTH_TYPE_DUPLICABLE,
gth_comment_gth_duplicable_interface_init)
G_IMPLEMENT_INTERFACE (DOM_TYPE_DOMIZABLE,
gth_comment_dom_domizable_interface_init))
static void
gth_comment_free_data (GthComment *self)
{
if (self->priv->place != NULL) {
g_free (self->priv->place);
self->priv->place = NULL;
}
if (self->priv->note != NULL) {
g_free (self->priv->note);
self->priv->note = NULL;
}
if (self->priv->caption != NULL) {
g_free (self->priv->caption);
self->priv->caption = NULL;
}
}
static void
gth_comment_finalize (GObject *obj)
{
GthComment *self = GTH_COMMENT (obj);
gth_comment_free_data (self);
gth_comment_clear_categories (self);
g_ptr_array_unref (self->priv->categories);
g_date_free (self->priv->date);
gth_time_free (self->priv->time_of_day);
G_OBJECT_CLASS (gth_comment_parent_class)->finalize (obj);
}
static void
gth_comment_class_init (GthCommentClass *klass)
{
G_OBJECT_CLASS (klass)->finalize = gth_comment_finalize;
}
static void
gth_comment_init (GthComment *self)
{
self->priv = gth_comment_get_instance_private (self);
self->priv->caption = NULL;
self->priv->note = NULL;
self->priv->place = NULL;
self->priv->rating = 0;
self->priv->categories = g_ptr_array_new ();
self->priv->date = g_date_new ();
self->priv->time_of_day = gth_time_new ();
}
static GObject *
gth_comment_real_duplicate (GthDuplicable *base)
{
return (GObject *) gth_comment_dup ((GthComment*) base);
}
static void
gth_comment_gth_duplicable_interface_init (GthDuplicableInterface *iface)
{
iface->duplicate = gth_comment_real_duplicate;
}
static DomElement*
gth_comment_real_create_element (DomDomizable *base,
DomDocument *doc)
{
GthComment *self;
DomElement *element;
char *value;
GPtrArray *categories;
DomElement *categories_element;
int i;
g_return_val_if_fail (DOM_IS_DOCUMENT (doc), NULL);
self = GTH_COMMENT (base);
element = dom_document_create_element (doc, "comment",
"version", COMMENT_VERSION,
NULL);
dom_element_append_child (element, dom_document_create_element_with_text (doc, self->priv->caption, "caption", NULL));
dom_element_append_child (element, dom_document_create_element_with_text (doc, self->priv->note, "note", NULL));
dom_element_append_child (element, dom_document_create_element_with_text (doc, self->priv->place, "place", NULL));
if (self->priv->rating > 0) {
value = g_strdup_printf ("%d", self->priv->rating);
dom_element_append_child (element, dom_document_create_element (doc, "rating", "value", value, NULL));
g_free (value);
}
value = gth_comment_get_time_as_exif_format (self);
if (value != NULL) {
dom_element_append_child (element, dom_document_create_element (doc, "time", "value", value, NULL));
g_free (value);
}
categories = gth_comment_get_categories (self);
categories_element = dom_document_create_element (doc, "categories", NULL);
dom_element_append_child (element, categories_element);
for (i = 0; i < categories->len; i++)
dom_element_append_child (categories_element, dom_document_create_element (doc, "category", "value", g_ptr_array_index (categories, i), NULL));
return element;
}
static void
gth_comment_real_load_from_element (DomDomizable *base,
DomElement *element)
{
GthComment *self;
DomElement *node;
g_return_if_fail (DOM_IS_ELEMENT (element));
self = GTH_COMMENT (base);
gth_comment_reset (self);
if (g_strcmp0 (dom_element_get_attribute (element, "format"), "2.0") == 0) {
for (node = element->first_child; node; node = node->next_sibling) {
if (g_strcmp0 (node->tag_name, "Note") == 0)
gth_comment_set_note (self, dom_element_get_inner_text (node));
else if (g_strcmp0 (node->tag_name, "Place") == 0)
gth_comment_set_place (self, dom_element_get_inner_text (node));
else if (g_strcmp0 (node->tag_name, "Time") == 0)
gth_comment_set_time_from_time_t (self, atol (dom_element_get_inner_text (node)));
else if (g_strcmp0 (node->tag_name, "Keywords") == 0) {
const char *text;
text = dom_element_get_inner_text (node);
if (text != NULL) {
char **categories;
int i;
categories = g_strsplit (text, ",", -1);
for (i = 0; categories[i] != NULL; i++)
gth_comment_add_category (self, categories[i]);
g_strfreev (categories);
}
}
}
}
else if (g_strcmp0 (dom_element_get_attribute (element, "version"), "3.0") == 0) {
for (node = element->first_child; node; node = node->next_sibling) {
if (g_strcmp0 (node->tag_name, "caption") == 0)
gth_comment_set_caption (self, dom_element_get_inner_text (node));
else if (g_strcmp0 (node->tag_name, "note") == 0)
gth_comment_set_note (self, dom_element_get_inner_text (node));
else if (g_strcmp0 (node->tag_name, "place") == 0)
gth_comment_set_place (self, dom_element_get_inner_text (node));
else if (g_strcmp0 (node->tag_name, "time") == 0)
gth_comment_set_time_from_exif_format (self, dom_element_get_attribute (node, "value"));
else if (g_strcmp0 (node->tag_name, "rating") == 0) {
int v;
sscanf (dom_element_get_attribute (node, "value"), "%d", &v);
gth_comment_set_rating (self, v);
}
else if (g_strcmp0 (node->tag_name, "categories") == 0) {
DomElement *child;
for (child = node->first_child; child != NULL; child = child->next_sibling)
if (strcmp (child->tag_name, "category") == 0)
gth_comment_add_category (self, dom_element_get_attribute (child, "value"));
}
}
}
}
static void
gth_comment_dom_domizable_interface_init (DomDomizableInterface *iface)
{
iface->create_element = gth_comment_real_create_element;
iface->load_from_element = gth_comment_real_load_from_element;
}
GthComment *
gth_comment_new (void)
{
return g_object_new (GTH_TYPE_COMMENT, NULL);
}
GFile *
gth_comment_get_comment_file (GFile *file)
{
GFile *parent;
char *basename;
char *comment_basename;
GFile *comment_file;
parent = g_file_get_parent (file);
if (parent == NULL)
return NULL;
basename = g_file_get_basename (file);
comment_basename = g_strconcat (basename, ".xml", NULL);
comment_file = _g_file_get_child (parent, ".comments", comment_basename, NULL);
g_free (comment_basename);
g_free (basename);
g_object_unref (parent);
return comment_file;
}
GthComment *
gth_comment_new_for_file (GFile *file,
GCancellable *cancellable,
GError **error)
{
GFile *comment_file;
GthComment *comment;
void *zipped_buffer;
gsize zipped_size;
void *buffer;
gsize size;
DomDocument *doc;
comment_file = gth_comment_get_comment_file (file);
if (comment_file == NULL)
return NULL;
if (! _g_file_load_in_buffer (comment_file, &zipped_buffer, &zipped_size, cancellable, error)) {
g_object_unref (comment_file);
return NULL;
}
g_object_unref (comment_file);
if ((zipped_buffer != NULL) && (((char *) zipped_buffer)[0] != '<')) {
if (! zlib_decompress_buffer (zipped_buffer, zipped_size, &buffer, &size))
return NULL;
}
else {
buffer = zipped_buffer;
size = zipped_size;
zipped_buffer = NULL;
}
comment = gth_comment_new ();
doc = dom_document_new ();
if (dom_document_load (doc, buffer, size, error)) {
dom_domizable_load_from_element (DOM_DOMIZABLE (comment), DOM_ELEMENT (doc)->first_child);
}
else {
buffer = NULL;
g_object_unref (comment);
comment = NULL;
}
g_object_unref (doc);
g_free (buffer);
g_free (zipped_buffer);
return comment;
}
char *
gth_comment_to_data (GthComment *comment,
gsize *length)
{
DomDocument *doc;
char *data;
doc = dom_document_new ();
dom_element_append_child (DOM_ELEMENT (doc), dom_domizable_create_element (DOM_DOMIZABLE (comment), doc));
data = dom_document_dump (doc, length);
g_object_unref (doc);
return data;
}
GthComment *
gth_comment_dup (GthComment *self)
{
GthComment *comment;
char *time;
int i;
if (self == NULL)
return NULL;
comment = gth_comment_new ();
gth_comment_set_caption (comment, gth_comment_get_caption (self));
gth_comment_set_note (comment, gth_comment_get_note (self));
gth_comment_set_place (comment, gth_comment_get_place (self));
gth_comment_set_rating (comment, gth_comment_get_rating (self));
time = gth_comment_get_time_as_exif_format (self);
gth_comment_set_time_from_exif_format (comment, time);
for (i = 0; i < self->priv->categories->len; i++)
gth_comment_add_category (comment, g_ptr_array_index (self->priv->categories, i));
g_free (time);
return comment;
}
void
gth_comment_reset (GthComment *self)
{
gth_comment_free_data (self);
gth_comment_clear_categories (self);
gth_comment_reset_time (self);
}
void
gth_comment_set_caption (GthComment *comment,
const char *value)
{
g_free (comment->priv->caption);
comment->priv->caption = NULL;
if ((value != NULL) && (strcmp (value, "") != 0))
comment->priv->caption = g_strdup (value);
}
void
gth_comment_set_note (GthComment *comment,
const char *value)
{
g_free (comment->priv->note);
comment->priv->note = NULL;
if ((value != NULL) && (strcmp (value, "") != 0))
comment->priv->note = g_strdup (value);
}
void
gth_comment_set_place (GthComment *comment,
const char *value)
{
g_free (comment->priv->place);
comment->priv->place = NULL;
if ((value != NULL) && (strcmp (value, "") != 0))
comment->priv->place = g_strdup (value);
}
void
gth_comment_set_rating (GthComment *comment,
int value)
{
comment->priv->rating = value;
}
void
gth_comment_clear_categories (GthComment *self)
{
g_ptr_array_foreach (self->priv->categories, (GFunc) g_free, NULL);
g_ptr_array_unref (self->priv->categories);
self->priv->categories = g_ptr_array_new ();
}
void
gth_comment_add_category (GthComment *comment,
const char *value)
{
g_return_if_fail (value != NULL);
g_ptr_array_add (comment->priv->categories, g_strdup (value));
}
void
gth_comment_reset_time (GthComment *self)
{
g_date_clear (self->priv->date, 1);
gth_time_clear (self->priv->time_of_day);
}
void
gth_comment_set_time_from_exif_format (GthComment *comment,
const char *value)
{
unsigned int y, m, d, hh, mm, ss;
gth_comment_reset_time (comment);
if ((value == NULL) || (*value == '\0'))
return;
if (sscanf (value, "%u:%u:%u %u:%u:%u", &y, &m, &d, &hh, &mm, &ss) != 6) {
g_warning ("invalid time format: %s", value);
return;
}
if (g_date_valid_dmy (d, m, y)) {
g_date_set_dmy (comment->priv->date, d, m, y);
gth_time_set_hms (comment->priv->time_of_day, hh, mm, ss, 0);
}
}
void
gth_comment_set_time_from_time_t (GthComment *comment,
time_t value)
{
struct tm *tm;
if (value == 0)
return;
tm = localtime (&value);
g_date_set_dmy (comment->priv->date, tm->tm_mday, tm->tm_mon + 1, 1900 + tm->tm_year);
gth_time_set_hms (comment->priv->time_of_day, tm->tm_hour, tm->tm_min, tm->tm_sec, 0);
}
const char *
gth_comment_get_caption (GthComment *comment)
{
return comment->priv->caption;
}
const char *
gth_comment_get_note (GthComment *comment)
{
return comment->priv->note;
}
const char *
gth_comment_get_place (GthComment *comment)
{
return comment->priv->place;
}
int
gth_comment_get_rating (GthComment *comment)
{
return comment->priv->rating;
}
GPtrArray *
gth_comment_get_categories (GthComment *comment)
{
return comment->priv->categories;
}
GDate *
gth_comment_get_date (GthComment *comment)
{
return comment->priv->date;
}
GthTime *
gth_comment_get_time_of_day (GthComment *comment)
{
return comment->priv->time_of_day;
}
char *
gth_comment_get_time_as_exif_format (GthComment *comment)
{
char *s;
if (! g_date_valid (comment->priv->date))
return NULL;
s = g_strdup_printf ("%04u:%02u:%02u %02u:%02u:%02u",
g_date_get_year (comment->priv->date),
g_date_get_month (comment->priv->date),
g_date_get_day (comment->priv->date),
comment->priv->time_of_day->hour,
comment->priv->time_of_day->min,
comment->priv->time_of_day->sec);
return s;
}
void
gth_comment_update_general_attributes (GthFileData *file_data)
{
const char *value;
value = g_file_info_get_attribute_string (file_data->info, "comment::note");
if (value != NULL)
set_attribute_from_string (file_data->info,
"general::description",
value,
NULL);
value = g_file_info_get_attribute_string (file_data->info, "comment::caption");
if (value != NULL)
set_attribute_from_string (file_data->info,
"general::title",
value,
NULL);
value = g_file_info_get_attribute_string (file_data->info, "comment::place");
if (value != NULL)
set_attribute_from_string (file_data->info,
"general::location",
value,
NULL);
if (g_file_info_has_attribute (file_data->info, "comment::rating")) {
char *v;
v = g_strdup_printf ("%d", g_file_info_get_attribute_int32 (file_data->info, "comment::rating"));
set_attribute_from_string (file_data->info, "general::rating", v, NULL);
g_free (v);
}
if (g_file_info_has_attribute (file_data->info, "comment::categories"))
g_file_info_set_attribute_object (file_data->info,
"general::tags",
g_file_info_get_attribute_object (file_data->info, "comment::categories"));
if (g_file_info_has_attribute (file_data->info, "comment::time"))
g_file_info_set_attribute_object (file_data->info,
"general::datetime",
g_file_info_get_attribute_object (file_data->info, "comment::time"));
}
void
gth_comment_update_from_general_attributes (GthFileData *file_data)
{
gboolean write_comment;
GthMetadata *metadata;
GthStringList *comment_categories;
GList *scan;
const char *text;
GthComment *comment;
GthStringList *categories;
write_comment = FALSE;
comment = gth_comment_new ();
gth_comment_set_note (comment, g_file_info_get_attribute_string (file_data->info, "comment::note"));
gth_comment_set_caption (comment, g_file_info_get_attribute_string (file_data->info, "comment::caption"));
gth_comment_set_place (comment, g_file_info_get_attribute_string (file_data->info, "comment::place"));
metadata = (GthMetadata *) g_file_info_get_attribute_object (file_data->info, "comment::time");
if (metadata != NULL)
gth_comment_set_time_from_exif_format (comment, gth_metadata_get_raw (metadata));
metadata = (GthMetadata *) g_file_info_get_attribute_object (file_data->info, "comment::categories");
comment_categories = gth_metadata_get_string_list (metadata);
if (comment_categories != NULL)
for (scan = gth_string_list_get_list (comment_categories); scan; scan = scan->next)
gth_comment_add_category (comment, (char *) scan->data);
gth_comment_set_rating (comment, g_file_info_get_attribute_int32 (file_data->info, "comment::rating"));
/* sync embedded data and .comment data if required */
metadata = (GthMetadata *) g_file_info_get_attribute_object (file_data->info, "general::description");
if (metadata != NULL) {
text = g_file_info_get_attribute_string (file_data->info, "comment::note");
if (! dom_str_equal (gth_metadata_get_formatted (metadata), text)) {
char *value = _g_utf8_try_from_any (gth_metadata_get_formatted (metadata));
if (value != NULL) {
gth_comment_set_note (comment, value);
g_free (value);
write_comment = TRUE;
}
}
}
metadata = (GthMetadata *) g_file_info_get_attribute_object (file_data->info, "general::title");
if (metadata != NULL) {
text = g_file_info_get_attribute_string (file_data->info, "comment::caption");
if (! dom_str_equal (gth_metadata_get_formatted (metadata), text)) {
char *value = _g_utf8_try_from_any (gth_metadata_get_formatted (metadata));
if (value != NULL) {
gth_comment_set_caption (comment, value);
g_free (value);
write_comment = TRUE;
}
}
}
metadata = (GthMetadata *) g_file_info_get_attribute_object (file_data->info, "general::location");
if (metadata != NULL) {
text = g_file_info_get_attribute_string (file_data->info, "comment::place");
if (! dom_str_equal (gth_metadata_get_formatted (metadata), text)) {
char *value = _g_utf8_try_from_any (gth_metadata_get_formatted (metadata));
if (value != NULL) {
gth_comment_set_place (comment, value);
g_free (value);
write_comment = TRUE;
}
}
}
metadata = (GthMetadata *) g_file_info_get_attribute_object (file_data->info, "general::datetime");
if (metadata != NULL) {
GthMetadata *comment_time;
text = gth_metadata_get_raw (metadata);
comment_time = (GthMetadata *) g_file_info_get_attribute_object (file_data->info, "comment::time");
if (comment_time != NULL) {
if (! dom_str_equal (gth_metadata_get_raw (comment_time), text)) {
gth_comment_set_time_from_exif_format (comment, gth_metadata_get_raw (metadata));
write_comment = TRUE;
}
}
}
metadata = (GthMetadata *) g_file_info_get_attribute_object (file_data->info, "general::tags");
categories = gth_metadata_get_string_list (metadata);
if (categories != NULL) {
metadata = (GthMetadata *) g_file_info_get_attribute_object (file_data->info, "comment::categories");
comment_categories = gth_metadata_get_string_list (metadata);
if (! gth_string_list_equal_custom (categories, comment_categories, (GCompareFunc) dom_str_find)) {
GList *scan;
gth_comment_clear_categories (comment);
for (scan = gth_string_list_get_list (categories); scan; scan = scan->next) {
char *value = _g_utf8_try_from_any (scan->data);
if (value != NULL) {
gth_comment_add_category (comment, value);
g_free (value);
}
}
write_comment = TRUE;
}
}
if (write_comment) {
GFile *comment_file;
GFile *comment_directory;
char *buffer;
gsize size;
comment_file = gth_comment_get_comment_file (file_data->file);
comment_directory = g_file_get_parent (comment_file);
if (! g_file_query_exists (comment_directory, NULL))
g_file_make_directory (comment_directory, NULL, NULL);
buffer = gth_comment_to_data (comment, &size);
if (_g_file_write (comment_file,
FALSE,
G_FILE_CREATE_NONE,
buffer,
size,
NULL,
NULL))
{
GFile *parent;
GList *list;
parent = g_file_get_parent (file_data->file);
list = g_list_prepend (NULL, file_data->file);
gth_monitor_folder_changed (gth_main_get_default_monitor (),
parent,
list,
GTH_MONITOR_EVENT_CHANGED);
g_list_free (list);
g_object_unref (parent);
}
g_free (buffer);
g_object_unref (comment_directory);
g_object_unref (comment_file);
}
g_object_unref (comment);
}