/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Pix
*
* Copyright (C) 2010 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 "gth-change-date-task.h"
struct _GthChangeDateTaskPrivate {
GFile *location;
GList *files; /* GFile */
GList *file_list; /* GthFileData */
GthChangeFields fields;
GthChangeType change_type;
GthDateTime *date_time;
int time_offset;
int n_files;
int n_current;
GList *current;
};
G_DEFINE_TYPE_WITH_CODE (GthChangeDateTask,
gth_change_date_task,
GTH_TYPE_TASK,
G_ADD_PRIVATE (GthChangeDateTask))
static void
gth_change_date_task_finalize (GObject *object)
{
GthChangeDateTask *self;
self = GTH_CHANGE_DATE_TASK (object);
_g_object_unref (self->priv->location);
_g_object_list_unref (self->priv->file_list);
_g_object_list_unref (self->priv->files);
gth_datetime_free (self->priv->date_time);
G_OBJECT_CLASS (gth_change_date_task_parent_class)->finalize (object);
}
static void
set_date_time_from_change_type (GthChangeDateTask *self,
GthDateTime *date_time,
GthChangeType change_type,
GthFileData *file_data)
{
if (change_type == GTH_CHANGE_TO_FOLLOWING_DATE) {
gth_datetime_copy (self->priv->date_time, date_time);
}
else if (change_type == GTH_CHANGE_TO_FILE_MODIFIED_DATE) {
gth_datetime_from_timeval (date_time, gth_file_data_get_modification_time (file_data));
}
else if (change_type == GTH_CHANGE_TO_FILE_CREATION_DATE) {
gth_datetime_from_timeval (date_time, gth_file_data_get_creation_time (file_data));
}
else if (change_type == GTH_CHANGE_TO_PHOTO_ORIGINAL_DATE) {
GTimeVal time_val;
if (gth_file_data_get_digitalization_time (file_data, &time_val))
gth_datetime_from_timeval (date_time, &time_val);
}
}
static void
update_modification_time (GthChangeDateTask *self)
{
GList *scan;
GError *error = NULL;
GthMonitor *monitor;
GList *files;
if ((self->priv->fields & GTH_CHANGE_LAST_MODIFIED_DATE) == GTH_CHANGE_LAST_MODIFIED_DATE) {
GthDateTime *date_time;
date_time = gth_datetime_new ();
for (scan = self->priv->file_list; scan; scan = scan->next) {
GthFileData *file_data = scan->data;
GTimeVal timeval;
gth_datetime_clear (date_time);
if (self->priv->change_type == GTH_CHANGE_ADJUST_TIME)
set_date_time_from_change_type (self, date_time, GTH_CHANGE_TO_FILE_MODIFIED_DATE, file_data);
else
set_date_time_from_change_type (self, date_time, self->priv->change_type, file_data);
/* Read the time from the attribute if it's not valid in
* date_time. */
if (! gth_time_valid (date_time->time)) {
GTimeVal *original_modification_time;
GthDateTime *original_date_time;
original_modification_time = gth_file_data_get_modification_time (file_data);
original_date_time = gth_datetime_new ();
gth_datetime_from_timeval (original_date_time, original_modification_time);
*date_time->time = *original_date_time->time;
gth_datetime_free (original_date_time);
}
if (! gth_datetime_valid (date_time))
continue;
if (gth_datetime_to_timeval (date_time, &timeval)) {
if (self->priv->change_type == GTH_CHANGE_ADJUST_TIME)
timeval.tv_sec += self->priv->time_offset;
if (! _g_file_set_modification_time (file_data->file,
&timeval,
gth_task_get_cancellable (GTH_TASK (self)),
&error))
{
break;
}
}
}
gth_datetime_free (date_time);
}
monitor = gth_main_get_default_monitor ();
files = gth_file_data_list_to_file_list (self->priv->file_list);
gth_monitor_folder_changed (monitor,
self->priv->location,
files,
GTH_MONITOR_EVENT_CHANGED);
gth_task_completed (GTH_TASK (self), error);
_g_object_list_unref (files);
}
static void
write_metadata_ready_cb (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
GthChangeDateTask *self = user_data;
GError *error = NULL;
if (! _g_write_metadata_finish (result, &error)) {
gth_task_completed (GTH_TASK (self), error);
return;
}
if (g_cancellable_set_error_if_cancelled (gth_task_get_cancellable (GTH_TASK (self)), &error)) {
gth_task_completed (GTH_TASK (self), error);
return;
}
update_modification_time (self);
}
static void
set_date_metadata (GthFileData *file_data,
const char *attribute,
GthDateTime *date_time,
int time_offset)
{
GthDateTime *new_date_time;
new_date_time = gth_datetime_new ();
gth_datetime_copy (date_time, new_date_time);
if (time_offset != 0) {
GTimeVal timeval;
gth_datetime_to_timeval (new_date_time, &timeval);
timeval.tv_sec += time_offset;
gth_datetime_from_timeval (new_date_time, &timeval);
}
else {
/* Read the time from the attribute if it's not valid in
* date_time. */
if (! gth_time_valid (new_date_time->time)) {
GthMetadata *original_date;
original_date = (GthMetadata *) g_file_info_get_attribute_object (file_data->info, attribute);
if (original_date != NULL) {
GthDateTime *original_date_time;
original_date_time = gth_datetime_new ();
gth_datetime_from_exif_date (original_date_time, gth_metadata_get_raw (original_date));
*new_date_time->time = *original_date_time->time;
gth_datetime_free (original_date_time);
}
}
}
if (gth_datetime_valid (new_date_time)) {
char *raw;
char *formatted;
GObject *metadata;
raw = gth_datetime_to_exif_date (new_date_time);
formatted = gth_datetime_strftime (new_date_time, "%x");
metadata = (GObject *) gth_metadata_new ();
g_object_set (metadata,
"id", attribute,
"raw", raw,
"formatted", formatted,
NULL);
g_file_info_set_attribute_object (file_data->info, attribute, metadata);
g_object_unref (metadata);
g_free (formatted);
g_free (raw);
}
gth_datetime_free (new_date_time);
}
static void
set_date_time_from_field (GthChangeDateTask *self,
GthDateTime *date_time,
GthChangeFields field,
GthFileData *file_data)
{
if (field & GTH_CHANGE_LAST_MODIFIED_DATE) {
gth_datetime_from_timeval (date_time, gth_file_data_get_modification_time (file_data));
}
else if (field & GTH_CHANGE_COMMENT_DATE) {
GthMetadata *m;
GTimeVal time_val;
m = (GthMetadata *) g_file_info_get_attribute_object (file_data->info, "general::datetime");
if ((m != NULL) && _g_time_val_from_exif_date (gth_metadata_get_raw (m), &time_val))
gth_datetime_from_timeval (date_time, &time_val);
}
}
static void
info_ready_cb (GList *files,
GError *error,
gpointer user_data)
{
GthChangeDateTask *self = user_data;
GthDateTime *date_time;
GList *scan;
GPtrArray *attribute_v;
if (error != NULL) {
gth_task_completed (GTH_TASK (self), error);
return;
}
if (g_cancellable_set_error_if_cancelled (gth_task_get_cancellable (GTH_TASK (self)), &error)) {
gth_task_completed (GTH_TASK (self), error);
return;
}
date_time = gth_datetime_new ();
self->priv->file_list = _g_object_list_ref (files);
for (scan = self->priv->file_list; scan; scan = scan->next) {
GthFileData *file_data = scan->data;
if (self->priv->change_type == GTH_CHANGE_ADJUST_TIME) {
if (self->priv->fields & GTH_CHANGE_COMMENT_DATE) {
gth_datetime_clear (date_time);
set_date_time_from_field (self, date_time, GTH_CHANGE_COMMENT_DATE, file_data);
if (gth_datetime_valid (date_time))
set_date_metadata (file_data, "general::datetime", date_time, self->priv->time_offset);
}
}
else {
gth_datetime_clear (date_time);
set_date_time_from_change_type (self, date_time, self->priv->change_type, file_data);
if (g_date_valid (date_time->date)) {
if (self->priv->fields & GTH_CHANGE_COMMENT_DATE) {
set_date_metadata (file_data, "general::datetime", date_time, 0);
}
}
}
}
attribute_v = g_ptr_array_new ();
if (self->priv->fields & GTH_CHANGE_COMMENT_DATE)
g_ptr_array_add (attribute_v, "general::datetime");
if (attribute_v->len > 0) {
char *attributes;
attributes = _g_string_array_join (attribute_v, ",");
_g_write_metadata_async (self->priv->file_list,
GTH_METADATA_WRITE_DEFAULT,
attributes,
gth_task_get_cancellable (GTH_TASK (self)),
write_metadata_ready_cb,
self);
g_free (attributes);
}
else
update_modification_time (self);
g_ptr_array_unref (attribute_v);
gth_datetime_free (date_time);
}
static void
gth_change_date_task_exec (GthTask *task)
{
GthChangeDateTask *self = GTH_CHANGE_DATE_TASK (task);
_g_query_all_metadata_async (self->priv->files,
GTH_LIST_DEFAULT,
"*",
gth_task_get_cancellable (task),
info_ready_cb,
self);
}
static void
gth_change_date_task_class_init (GthChangeDateTaskClass *klass)
{
GObjectClass *object_class;
GthTaskClass *task_class;
object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gth_change_date_task_finalize;
task_class = GTH_TASK_CLASS (klass);
task_class->exec = gth_change_date_task_exec;
}
static void
gth_change_date_task_init (GthChangeDateTask *self)
{
self->priv = gth_change_date_task_get_instance_private (self);
self->priv->date_time = gth_datetime_new ();
}
GthTask *
gth_change_date_task_new (GFile *location,
GList *files, /* GthFileData */
GthChangeFields fields,
GthChangeType change_type,
GthDateTime *date_time,
int time_offset)
{
GthChangeDateTask *self;
self = GTH_CHANGE_DATE_TASK (g_object_new (GTH_TYPE_CHANGE_DATE_TASK, NULL));
self->priv->location = g_file_dup (location);
self->priv->files = gth_file_data_list_to_file_list (files);
self->priv->fields = fields;
self->priv->change_type = change_type;
if (date_time != NULL)
gth_datetime_copy (date_time, self->priv->date_time);
self->priv->time_offset = time_offset;
return (GthTask *) self;
}