/* -*- 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 "dlg-edit-metadata.h"
#include "gth-edit-metadata-dialog.h"
#define UPDATE_SELECTION_DELAY 50
typedef struct {
int ref;
GthBrowser *browser;
GtkWidget *dialog;
GtkWidget *keep_open_checkbutton;
GtkWidget *info;
char *dialog_name;
GList *file_list; /* GthFileData list */
GList *parents;
gboolean never_shown;
gboolean close_dialog;
GthTask *loader;
gulong file_selection_changed_event;
guint update_selectection_event;
} DialogData;
static DialogData *
dialog_data_ref (DialogData *data)
{
g_atomic_int_inc (&data->ref);
return data;
}
static void
cancel_file_list_loading (DialogData *data)
{
if (data->loader == NULL)
return;
gth_task_cancel (data->loader);
g_object_unref (data->loader);
data->loader = NULL;
}
static void
dialog_data_unref (DialogData *data)
{
if (! g_atomic_int_dec_and_test (&data->ref))
return;
if (data->file_selection_changed_event != 0) {
g_signal_handler_disconnect (gth_browser_get_file_list_view (data->browser),
data->file_selection_changed_event);
data->file_selection_changed_event = 0;
}
if (data->update_selectection_event != 0) {
g_source_remove (data->update_selectection_event);
data->update_selectection_event = 0;
}
cancel_file_list_loading (data);
gth_browser_set_dialog (data->browser, data->dialog_name, NULL);
gtk_widget_destroy (data->dialog);
g_free (data->dialog_name);
_g_object_list_unref (data->file_list);
_g_object_list_unref (data->parents);
g_free (data);
}
static void
close_dialog (DialogData *data)
{
if (data->file_selection_changed_event != 0) {
g_signal_handler_disconnect (gth_browser_get_file_list_view (data->browser),
data->file_selection_changed_event);
data->file_selection_changed_event = 0;
}
gtk_widget_hide (data->dialog);
dialog_data_unref (data);
}
static void
saver_completed_cb (GthTask *task,
GError *error,
gpointer user_data)
{
DialogData *data = user_data;
GthMonitor *monitor;
GList *scan;
monitor = gth_main_get_default_monitor ();
for (scan = data->parents; scan; scan = scan->next)
gth_monitor_resume (monitor, (GFile *) scan->data);
if (error != NULL) {
if (! g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
_gtk_error_dialog_from_gerror_show (GTK_WINDOW (data->dialog), _("Could not save the file metadata"), error);
}
else {
for (scan = data->file_list; scan; scan = scan->next) {
GthFileData *file_data = scan->data;
GFile *parent;
GList *files;
parent = g_file_get_parent (file_data->file);
files = g_list_prepend (NULL, g_object_ref (file_data->file));
gth_monitor_folder_changed (monitor, parent, files, GTH_MONITOR_EVENT_CHANGED);
gth_monitor_metadata_changed (monitor, file_data);
_g_object_list_unref (files);
}
}
if (data->close_dialog)
close_dialog (data);
else
gth_browser_show_next_image (data->browser, FALSE, FALSE);
dialog_data_unref (data);
_g_object_unref (task);
}
static void
edit_metadata_dialog__response_cb (GtkDialog *dialog,
int response,
gpointer user_data)
{
DialogData *data = user_data;
GthMonitor *monitor;
GHashTable *parents;
GList *scan;
GthTask *task;
if (response != GTK_RESPONSE_OK) {
cancel_file_list_loading (data);
close_dialog (data);
return;
}
if (data->file_list == NULL)
return;
data->close_dialog = ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->keep_open_checkbutton));
/* get the parents list */
parents = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, g_object_unref, NULL);
for (scan = data->file_list; scan; scan = scan->next) {
GthFileData *file_data = scan->data;
GFile *parent;
parent = g_file_get_parent (file_data->file);
if (G_LIKELY (parent != NULL)) {
if (g_hash_table_lookup (parents, parent) == NULL)
g_hash_table_insert (parents, g_object_ref (parent), GINT_TO_POINTER (1));
g_object_unref (parent);
}
}
_g_object_list_unref (data->parents);
data->parents = g_hash_table_get_keys (parents);
g_list_foreach (data->parents, (GFunc) g_object_ref, NULL);
g_hash_table_unref (parents);
/* ignore changes to all the parents */
monitor = gth_main_get_default_monitor ();
for (scan = data->parents; scan; scan = scan->next)
gth_monitor_pause (monitor, (GFile *) scan->data);
gth_edit_metadata_dialog_update_info (GTH_EDIT_METADATA_DIALOG (data->dialog), data->file_list);
dialog_data_ref (data);
task = gth_save_file_data_task_new (data->file_list, "*");
g_signal_connect (task,
"completed",
G_CALLBACK (saver_completed_cb),
data);
gth_browser_exec_task (data->browser, task, GTH_TASK_FLAGS_IGNORE_ERROR);
}
typedef struct {
DialogData *data;
GList *files;
} LoaderData;
static void
loader_data_free (LoaderData *loader_data)
{
dialog_data_unref (loader_data->data);
_g_object_list_unref (loader_data->files);
g_free (loader_data);
}
static void
loader_completed_cb (GthTask *task,
GError *error,
gpointer user_data)
{
LoaderData *loader_data = user_data;
DialogData *data = loader_data->data;
if (error != NULL) {
if (! g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
_gtk_error_dialog_from_gerror_show (GTK_WINDOW (data->browser), _("Cannot read file information"), error);
loader_data_free (loader_data);
if (data->never_shown)
close_dialog (data);
return;
}
_g_object_list_unref (data->file_list);
data->file_list = _g_object_list_ref (gth_load_file_data_task_get_result (GTH_LOAD_FILE_DATA_TASK (task)));
gth_file_selection_info_set_file_list (GTH_FILE_SELECTION_INFO (data->info), data->file_list);
gth_edit_metadata_dialog_set_file_list (GTH_EDIT_METADATA_DIALOG (data->dialog), data->file_list);
gtk_window_set_transient_for (GTK_WINDOW (data->dialog), GTK_WINDOW (data->browser));
gtk_window_set_modal (GTK_WINDOW (data->dialog), FALSE);
gtk_window_present (GTK_WINDOW (data->dialog));
data->never_shown = FALSE;
loader_data_free (loader_data);
}
static gboolean
update_file_list (gpointer user_data)
{
DialogData *data = user_data;
LoaderData *loader_data;
GList *items;
GList *file_data_list;
if (data->update_selectection_event != 0) {
g_source_remove (data->update_selectection_event);
data->update_selectection_event = 0;
}
cancel_file_list_loading (data);
loader_data = g_new0 (LoaderData, 1);
loader_data->data = dialog_data_ref (data);
items = gth_file_selection_get_selected (GTH_FILE_SELECTION (gth_browser_get_file_list_view (data->browser)));
file_data_list = gth_file_list_get_files (GTH_FILE_LIST (gth_browser_get_file_list (data->browser)), items);
loader_data->files = gth_file_data_list_to_file_list (file_data_list);
gtk_dialog_set_response_sensitive (GTK_DIALOG (data->dialog), GTK_RESPONSE_OK, loader_data->files != NULL);
data->loader = gth_load_file_data_task_new (loader_data->files, "*");
g_signal_connect (data->loader,
"completed",
G_CALLBACK (loader_completed_cb),
loader_data);
gth_browser_exec_task (data->browser, data->loader, GTH_TASK_FLAGS_IGNORE_ERROR);
_g_object_list_unref (file_data_list);
_gtk_tree_path_list_free (items);
return FALSE;
}
static void
file_selection_changed_cb (GthFileSelection *self,
DialogData *data)
{
if (data->update_selectection_event != 0)
g_source_remove (data->update_selectection_event);
data->update_selectection_event = g_timeout_add (UPDATE_SELECTION_DELAY, update_file_list, data);
}
static void
keep_open_button_toggled_cb (GtkToggleButton *button,
DialogData *data)
{
gth_file_selection_info_set_visible (GTH_FILE_SELECTION_INFO (data->info),
gtk_toggle_button_get_active (button));
}
void
dlg_edit_metadata (GthBrowser *browser,
GType dialog_type,
const char *dialog_name)
{
DialogData *data;
if (gth_browser_get_dialog (browser, dialog_name)) {
gtk_window_present (GTK_WINDOW (gth_browser_get_dialog (browser, dialog_name)));
return;
}
data = g_new0 (DialogData, 1);
data->ref = 1;
data->browser = browser;
data->dialog = g_object_new (dialog_type,
"transient-for", GTK_WINDOW (browser),
"modal", FALSE,
"use-header-bar", _gtk_settings_get_dialogs_use_header (),
NULL);
data->dialog_name = g_strdup (dialog_name);
data->never_shown = TRUE;
data->info = gth_file_selection_info_new ();
gtk_widget_show (data->info);
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (data->dialog))), data->info, FALSE, FALSE, 0);
gtk_dialog_add_buttons (GTK_DIALOG (data->dialog),
_GTK_LABEL_CLOSE, GTK_RESPONSE_CANCEL,
_GTK_LABEL_SAVE, GTK_RESPONSE_OK,
NULL);
data->keep_open_checkbutton = _gtk_toggle_image_button_new_for_header_bar ("pinned-symbolic");
gtk_widget_set_tooltip_text (data->keep_open_checkbutton, _("Keep the dialog open"));
gtk_widget_show (data->keep_open_checkbutton);
_gtk_dialog_add_action_widget (GTK_DIALOG (data->dialog), data->keep_open_checkbutton);
_gtk_dialog_add_class_to_response (GTK_DIALOG (data->dialog), GTK_RESPONSE_OK, GTK_STYLE_CLASS_SUGGESTED_ACTION);
gth_browser_set_dialog (browser, data->dialog_name, data->dialog);
g_signal_connect (G_OBJECT (data->dialog),
"delete-event",
G_CALLBACK (gtk_true),
NULL);
g_signal_connect (data->dialog,
"response",
G_CALLBACK (edit_metadata_dialog__response_cb),
data);
g_signal_connect (data->keep_open_checkbutton,
"toggled",
G_CALLBACK (keep_open_button_toggled_cb),
data);
data->file_selection_changed_event =
g_signal_connect (gth_browser_get_file_list_view (data->browser),
"file-selection-changed",
G_CALLBACK (file_selection_changed_cb),
data);
update_file_list (data);
}