/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Pix
*
* Copyright (C) 2001-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 "dlg-add-to-catalog.h"
#include "gth-catalog.h"
#include "gth-file-source-catalogs.h"
#include "preferences.h"
#define GET_WIDGET(name) _gtk_builder_get_widget (data->builder, (name))
#define ADD_TO_CATALOG_DIALOG_NAME "add-to-catalog"
#define UPDATE_SELECTION_DELAY 50
typedef struct {
int ref;
GthBrowser *browser;
GtkWidget *parent_window;
GtkWidget *dialog;
GList *files;
gboolean view_destination;
gboolean close_after_adding;
GFile *catalog_file;
GthCatalog *catalog;
} AddData;
static AddData *
add_data_new (void)
{
AddData *add_data;
add_data = g_new0 (AddData, 1);
add_data->ref = 0;
add_data->view_destination = FALSE;
add_data->close_after_adding = TRUE;
return add_data;
}
static void
add_data_ref (AddData *add_data)
{
add_data->ref++;
}
static void
add_data_unref (AddData *add_data)
{
add_data->ref--;
if (add_data->ref > 0)
return;
_g_object_unref (add_data->catalog);
_g_object_list_unref (add_data->files);
_g_object_unref (add_data->catalog_file);
g_free (add_data);
}
typedef struct {
GthBrowser *browser;
GtkBuilder *builder;
GtkWidget *dialog;
GtkWidget *keep_open_checkbutton;
GtkWidget *source_tree;
GtkWidget *info;
AddData *add_data;
GthFileSource *catalog_source;
GthFileData *new_catalog;
GthFileData *new_library;
gulong file_selection_changed_event;
guint update_selection_event;
GSettings *settings;
} DialogData;
static void
destroy_cb (GtkWidget *widget,
DialogData *data)
{
gth_browser_set_dialog (data->browser, ADD_TO_CATALOG_DIALOG_NAME, NULL);
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_selection_event != 0) {
g_source_remove (data->update_selection_event);
data->update_selection_event = 0;
}
add_data_unref (data->add_data);
_g_object_unref (data->catalog_source);
_g_object_unref (data->new_catalog);
_g_object_unref (data->new_library);
_g_object_unref (data->settings);
g_object_unref (data->builder);
g_free (data);
}
static GFile *
get_selected_catalog (DialogData *data)
{
GthFileData *file_data;
GFile *file;
file_data = gth_folder_tree_get_selected_or_parent (GTH_FOLDER_TREE (data->source_tree));
if ((file_data != NULL) && ! g_file_info_get_attribute_boolean (file_data->info, "pix::no-child")) {
_g_object_unref (file_data);
file_data = NULL;
}
if (file_data != NULL)
file = g_object_ref (file_data->file);
else
file = NULL;
_g_object_unref (file_data);
return file;
}
static void
catalog_save_done_cb (void **buffer,
gsize count,
GError *error,
gpointer user_data)
{
AddData *add_data = user_data;
if (error != NULL) {
_gtk_error_dialog_from_gerror_show (GTK_WINDOW (add_data->parent_window), _("Could not add the files to the catalog"), error);
add_data_unref (add_data);
return;
}
gth_monitor_folder_changed (gth_main_get_default_monitor (),
add_data->catalog_file,
add_data->files,
GTH_MONITOR_EVENT_CREATED);
if (add_data->close_after_adding) {
if (add_data->view_destination)
gth_browser_go_to (add_data->browser, add_data->catalog_file, NULL);
if (add_data->dialog != NULL)
gtk_widget_destroy (add_data->dialog);
}
else
gth_browser_show_next_image (add_data->browser, FALSE, FALSE);
add_data_unref (add_data);
}
static void
catalog_ready_cb (GObject *catalog,
GError *error,
gpointer user_data)
{
AddData *add_data = user_data;
GList *scan;
char *buffer;
gsize length;
GFile *gio_file;
_g_object_unref (add_data->catalog);
if (catalog != NULL)
add_data->catalog = (GthCatalog *) catalog;
else
add_data->catalog = gth_catalog_new_for_file (add_data->catalog_file);
for (scan = add_data->files; scan; scan = scan->next)
gth_catalog_insert_file (add_data->catalog, (GFile *) scan->data, -1);
buffer = gth_catalog_to_data (add_data->catalog, &length);
gio_file = gth_catalog_file_to_gio_file (add_data->catalog_file);
_g_file_write_async (gio_file,
buffer,
length,
TRUE,
G_PRIORITY_DEFAULT,
NULL,
catalog_save_done_cb,
add_data);
g_object_unref (gio_file);
}
static void
add_data_exec (AddData *add_data)
{
add_data_ref (add_data);
gth_catalog_load_from_file_async (add_data->catalog_file,
NULL,
catalog_ready_cb,
add_data);
}
static void
add_selection_to_catalog (DialogData *data,
gboolean close_after_adding)
{
char *last_catalog;
GList *items;
GList *file_list;
_g_clear_object (&data->add_data->catalog_file);
data->add_data->catalog_file = get_selected_catalog (data);
if (data->add_data->catalog_file == NULL)
return;
last_catalog = g_file_get_uri (data->add_data->catalog_file);
g_settings_set_string (data->settings, PREF_CATALOGS_LAST_CATALOG, last_catalog);
g_free (last_catalog);
_g_object_list_unref (data->add_data->files);
data->add_data->files = NULL;
items = gth_file_selection_get_selected (GTH_FILE_SELECTION (gth_browser_get_file_list_view (data->browser)));
file_list = gth_file_list_get_files (GTH_FILE_LIST (gth_browser_get_file_list (data->browser)), items);
data->add_data->files = gth_file_data_list_to_file_list (file_list);
if (data->add_data->files != NULL) {
data->add_data->close_after_adding = close_after_adding;
data->add_data->view_destination = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("view_destination_checkbutton")));
add_data_exec (data->add_data);
}
_g_object_list_unref (file_list);
_gtk_tree_path_list_free (items);
}
static void
add_button_clicked_cb (GtkWidget *widget,
DialogData *data)
{
add_selection_to_catalog (data, ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->keep_open_checkbutton)));
}
static void
update_sensitivity (DialogData *data)
{
GFile *selected_catalog;
GList *items;
GList *file_data_list;
gboolean can_add;
selected_catalog = get_selected_catalog (data);
items = gth_file_selection_get_selected (GTH_FILE_SELECTION (gth_browser_get_file_list_view (data->browser)));
can_add = (items != NULL) && (selected_catalog != NULL);
gtk_dialog_set_response_sensitive (GTK_DIALOG (data->dialog), GTK_RESPONSE_OK, can_add);
gtk_toggle_button_set_inconsistent (GTK_TOGGLE_BUTTON (GET_WIDGET ("view_destination_checkbutton")), gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->keep_open_checkbutton)));
gtk_widget_set_sensitive (GET_WIDGET ("view_destination_checkbutton"), ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->keep_open_checkbutton)));
file_data_list = gth_file_list_get_files (GTH_FILE_LIST (gth_browser_get_file_list (data->browser)), items);
gth_file_selection_info_set_file_list (GTH_FILE_SELECTION_INFO (data->info), file_data_list);
_g_object_list_unref (file_data_list);
_gtk_tree_path_list_free (items);
_g_object_unref (selected_catalog);
}
static void
source_tree_changed_cb (GthVfsTree *folder_tree,
gpointer user_data)
{
update_sensitivity ((DialogData *) user_data);
}
static void
source_tree_selection_changed_cb (GtkTreeSelection *treeselection,
gpointer user_data)
{
update_sensitivity ((DialogData *) user_data);
}
static void
new_catalog_metadata_ready_cb (GObject *object,
GError *error,
gpointer user_data)
{
DialogData *data = user_data;
GFile *parent;
GList *file_list;
GtkTreePath *tree_path;
if (error != NULL) {
_gtk_error_dialog_from_gerror_show (GTK_WINDOW (data->dialog), _("Could not create the catalog"), error);
return;
}
/* add the new catalog to the tree and select it */
parent = g_file_get_parent (data->new_catalog->file);
file_list = g_list_append (NULL, g_object_ref (data->new_catalog));
gth_folder_tree_add_children (GTH_FOLDER_TREE (data->source_tree),
parent,
file_list);
tree_path = gth_folder_tree_get_path (GTH_FOLDER_TREE (data->source_tree), data->new_catalog->file);
if (tree_path != NULL) {
gth_folder_tree_select_path (GTH_FOLDER_TREE (data->source_tree), tree_path);
gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (data->source_tree), tree_path, NULL, TRUE, 0.5, 0.0);
gtk_tree_path_free (tree_path);
}
_g_object_list_unref (file_list);
/* notify the catalog creation */
file_list = g_list_prepend (NULL, g_object_ref (data->new_catalog->file));
gth_monitor_folder_changed (gth_main_get_default_monitor (),
parent,
file_list,
GTH_MONITOR_EVENT_CREATED);
_g_object_list_unref (file_list);
g_object_unref (parent);
}
static void
new_catalog_dialog_response_cb (GtkWidget *dialog,
int response_id,
gpointer user_data)
{
DialogData *data = user_data;
char *name;
GthFileData *selected_parent;
GFile *parent;
GFile *gio_parent;
char *display_name;
GError *error = NULL;
GFile *gio_file;
if (response_id != GTK_RESPONSE_OK) {
gtk_widget_destroy (dialog);
return;
}
name = gth_request_dialog_get_normalized_text (GTH_REQUEST_DIALOG (dialog));
if (_g_utf8_all_spaces (name)) {
g_free (name);
gth_request_dialog_set_info_text (GTH_REQUEST_DIALOG (dialog), GTK_MESSAGE_ERROR, _("No name specified"));
return;
}
if (g_regex_match_simple ("/", name, 0, 0)) {
char *message;
message = g_strdup_printf (_("Invalid name. The following characters are not allowed: %s"), "/");
gth_request_dialog_set_info_text (GTH_REQUEST_DIALOG (dialog), GTK_MESSAGE_ERROR, message);
g_free (message);
g_free (name);
return;
}
selected_parent = gth_folder_tree_get_selected_or_parent (GTH_FOLDER_TREE (data->source_tree));
if (selected_parent != NULL) {
GthFileSource *file_source;
GFileInfo *info;
file_source = gth_main_get_file_source (selected_parent->file);
info = gth_file_source_get_file_info (file_source, selected_parent->file, GFILE_BASIC_ATTRIBUTES);
if (g_file_info_get_attribute_boolean (info, "pix::no-child"))
parent = g_file_get_parent (selected_parent->file);
else
parent = g_file_dup (selected_parent->file);
g_object_unref (info);
g_object_unref (file_source);
}
else
parent = g_file_new_for_uri ("catalog:///");
_g_object_unref (data->catalog_source);
data->catalog_source = gth_main_get_file_source (parent);
gio_parent = gth_file_source_to_gio_file (data->catalog_source, parent);
display_name = g_strconcat (name, ".catalog", NULL);
gio_file = g_file_get_child_for_display_name (gio_parent, display_name, &error);
if (gio_file != NULL) {
GFileOutputStream *stream;
stream = g_file_create (gio_file, G_FILE_CREATE_NONE, NULL, &error);
if (stream != NULL) {
GFile *file;
_g_object_unref (data->new_catalog);
file = gth_catalog_file_from_gio_file (gio_file, NULL);
data->new_catalog = gth_file_data_new (file, NULL);
gth_file_source_read_metadata (data->catalog_source,
data->new_catalog,
"*",
new_catalog_metadata_ready_cb,
data);
g_object_unref (file);
g_object_unref (stream);
}
g_object_unref (gio_file);
}
if (error != NULL) {
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
gth_request_dialog_set_info_text (GTH_REQUEST_DIALOG (dialog), GTK_MESSAGE_ERROR, _("Name already used"));
else
gth_request_dialog_set_info_text (GTH_REQUEST_DIALOG (dialog), GTK_MESSAGE_ERROR, error->message);
g_clear_error (&error);
}
else
gtk_widget_destroy (dialog);
g_free (display_name);
g_object_unref (gio_parent);
}
static void
new_catalog_button_clicked_cb (GtkWidget *widget,
DialogData *data)
{
GtkWidget *dialog;
dialog = gth_request_dialog_new (GTK_WINDOW (data->dialog),
GTK_DIALOG_MODAL,
_("New Catalog"),
_("Enter the catalog name:"),
_GTK_LABEL_CANCEL,
_("C_reate"));
g_signal_connect (dialog,
"response",
G_CALLBACK (new_catalog_dialog_response_cb),
data);
gtk_widget_show (dialog);
}
static void
new_library_metadata_ready_cb (GObject *object,
GError *error,
gpointer user_data)
{
DialogData *data = user_data;
GFile *parent;
GList *file_list;
GtkTreePath *tree_path;
if (error != NULL)
return;
/* add the new library to the tree and select it */
parent = g_file_get_parent (data->new_library->file);
file_list = g_list_append (NULL, g_object_ref (data->new_library));
gth_folder_tree_add_children (GTH_FOLDER_TREE (data->source_tree),
parent,
file_list);
_g_object_list_unref (file_list);
/* select the new library */
tree_path = gth_folder_tree_get_path (GTH_FOLDER_TREE (data->source_tree), data->new_library->file);
if (tree_path != NULL) {
gth_folder_tree_select_path (GTH_FOLDER_TREE (data->source_tree), tree_path);
gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (data->source_tree), tree_path, NULL, TRUE, 0.5, 0.0);
gtk_tree_path_free (tree_path);
}
/* notify the library creation */
file_list = g_list_prepend (NULL, g_object_ref (data->new_library->file));
gth_monitor_folder_changed (gth_main_get_default_monitor (),
parent,
file_list,
GTH_MONITOR_EVENT_CREATED);
_g_object_list_unref (file_list);
g_object_unref (parent);
}
static void
new_library_dialog_response_cb (GtkWidget *dialog,
int response_id,
gpointer user_data)
{
DialogData *data = user_data;
char *name;
GthFileData *selected_parent;
GFile *parent;
GthFileSource *file_source;
GFile *gio_parent;
GError *error = NULL;
GFile *gio_file;
if (response_id != GTK_RESPONSE_OK) {
gtk_widget_destroy (dialog);
return;
}
name = gth_request_dialog_get_normalized_text (GTH_REQUEST_DIALOG (dialog));
if (_g_utf8_all_spaces (name)) {
g_free (name);
gth_request_dialog_set_info_text (GTH_REQUEST_DIALOG (dialog), GTK_MESSAGE_ERROR, _("No name specified"));
return;
}
if (g_regex_match_simple ("/", name, 0, 0)) {
char *message;
message = g_strdup_printf (_("Invalid name. The following characters are not allowed: %s"), "/");
gth_request_dialog_set_info_text (GTH_REQUEST_DIALOG (dialog), GTK_MESSAGE_ERROR, message);
g_free (message);
g_free (name);
return;
}
selected_parent = gth_folder_tree_get_selected_or_parent (GTH_FOLDER_TREE (data->source_tree));
if (selected_parent != NULL) {
GthFileSource *file_source;
GFileInfo *info;
file_source = gth_main_get_file_source (selected_parent->file);
info = gth_file_source_get_file_info (file_source, selected_parent->file, GFILE_BASIC_ATTRIBUTES);
if (g_file_info_get_attribute_boolean (info, "pix::no-child"))
parent = g_file_get_parent (selected_parent->file);
else
parent = g_file_dup (selected_parent->file);
g_object_unref (info);
g_object_unref (file_source);
}
else
parent = g_file_new_for_uri ("catalog:///");
file_source = gth_main_get_file_source (parent);
gio_parent = gth_file_source_to_gio_file (file_source, parent);
gio_file = g_file_get_child_for_display_name (gio_parent, name, &error);
if ((gio_file != NULL) && g_file_make_directory (gio_file, NULL, &error)) {
GFile *file;
data->catalog_source = gth_main_get_file_source (parent);
_g_object_unref (data->new_library);
file = gth_catalog_file_from_gio_file (gio_file, NULL);
data->new_library = gth_file_data_new (file, NULL);
gth_file_source_read_metadata (data->catalog_source,
data->new_library,
"*",
new_library_metadata_ready_cb,
data);
g_object_unref (file);
}
if (error != NULL) {
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
gth_request_dialog_set_info_text (GTH_REQUEST_DIALOG (dialog), GTK_MESSAGE_ERROR, _("Name already used"));
else
gth_request_dialog_set_info_text (GTH_REQUEST_DIALOG (dialog), GTK_MESSAGE_ERROR, error->message);
g_clear_error (&error);
}
else
gtk_widget_destroy (dialog);
g_object_unref (gio_file);
g_object_unref (gio_parent);
g_object_unref (file_source);
g_free (name);
}
static void
new_library_button_clicked_cb (GtkWidget *widget,
DialogData *data)
{
GtkWidget *dialog;
dialog = gth_request_dialog_new (GTK_WINDOW (data->dialog),
GTK_DIALOG_MODAL,
_("New Library"),
_("Enter the library name:"),
_GTK_LABEL_CANCEL,
_("C_reate"));
g_signal_connect (dialog,
"response",
G_CALLBACK (new_library_dialog_response_cb),
data);
gtk_widget_show (dialog);
}
static gboolean
update_sensitivity_cb (gpointer user_data)
{
DialogData *data = user_data;
if (data->update_selection_event != 0) {
g_source_remove (data->update_selection_event);
data->update_selection_event = 0;
}
update_sensitivity (data);
return FALSE;
}
static void
file_selection_changed_cb (GthFileSelection *self,
DialogData *data)
{
if (data->update_selection_event != 0)
g_source_remove (data->update_selection_event);
data->update_selection_event = g_timeout_add (UPDATE_SELECTION_DELAY, update_sensitivity_cb, 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));
update_sensitivity (data);
}
void
dlg_add_to_catalog (GthBrowser *browser)
{
DialogData *data;
GtkTreeSelection *selection;
char *last_catalog;
if (gth_browser_get_dialog (browser, ADD_TO_CATALOG_DIALOG_NAME)) {
gtk_window_present (GTK_WINDOW (gth_browser_get_dialog (browser, ADD_TO_CATALOG_DIALOG_NAME)));
return;
}
data = g_new0 (DialogData, 1);
data->browser = browser;
data->builder = _gtk_builder_new_from_file ("add-to-catalog.ui", "catalogs");
data->settings = g_settings_new (PIX_CATALOGS_SCHEMA);
data->dialog = g_object_new (GTK_TYPE_DIALOG,
"title", _("Add to Catalog"),
"transient-for", GTK_WINDOW (browser),
"modal", FALSE,
"use-header-bar", _gtk_settings_get_dialogs_use_header (),
NULL);
gtk_container_set_border_width (GTK_CONTAINER (data->dialog), 5);
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_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (data->dialog))),
GET_WIDGET ("dialog_content"),
TRUE,
TRUE,
0);
gtk_dialog_add_buttons (GTK_DIALOG (data->dialog),
_GTK_LABEL_CLOSE, GTK_RESPONSE_CANCEL,
_("_Add"), 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, ADD_TO_CATALOG_DIALOG_NAME, data->dialog);
data->add_data = add_data_new ();
data->add_data->browser = browser;
data->add_data->parent_window = data->add_data->dialog = data->dialog;
add_data_ref (data->add_data);
last_catalog = g_settings_get_string (data->settings, PREF_CATALOGS_LAST_CATALOG);
data->source_tree = gth_vfs_tree_new ("catalog:///", last_catalog);
gtk_widget_show (data->source_tree);
gtk_container_add (GTK_CONTAINER (GET_WIDGET ("catalog_list_scrolled_window")), data->source_tree);
gtk_label_set_mnemonic_widget (GTK_LABEL (GET_WIDGET ("catalogs_label")), data->source_tree);
update_sensitivity (data);
g_free (last_catalog);
/* Set the signals handlers. */
g_signal_connect (G_OBJECT (data->dialog),
"destroy",
G_CALLBACK (destroy_cb),
data);
g_signal_connect_swapped (gtk_dialog_get_widget_for_response (GTK_DIALOG (data->dialog), GTK_RESPONSE_CANCEL),
"clicked",
G_CALLBACK (gtk_widget_destroy),
G_OBJECT (data->dialog));
g_signal_connect (G_OBJECT (data->source_tree),
"changed",
G_CALLBACK (source_tree_changed_cb),
data);
g_signal_connect (gtk_dialog_get_widget_for_response (GTK_DIALOG (data->dialog), GTK_RESPONSE_OK),
"clicked",
G_CALLBACK (add_button_clicked_cb),
data);
g_signal_connect (G_OBJECT (GET_WIDGET ("new_catalog_button")),
"clicked",
G_CALLBACK (new_catalog_button_clicked_cb),
data);
g_signal_connect (G_OBJECT (GET_WIDGET ("new_library_button")),
"clicked",
G_CALLBACK (new_library_button_clicked_cb),
data);
g_signal_connect (data->keep_open_checkbutton,
"toggled",
G_CALLBACK (keep_open_button_toggled_cb),
data);
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (data->source_tree));
g_signal_connect (selection,
"changed",
G_CALLBACK (source_tree_selection_changed_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);
/* run dialog. */
gtk_window_set_transient_for (GTK_WINDOW (data->dialog), GTK_WINDOW (browser));
gtk_window_set_modal (GTK_WINDOW (data->dialog), FALSE);
gtk_widget_show (data->dialog);
}
void
add_to_catalog (GthBrowser *browser,
GFile *catalog,
GList *list)
{
AddData *add_data;
add_data = add_data_new ();
add_data->browser = browser;
add_data->parent_window = (GtkWidget *) browser;
add_data->catalog_file = g_object_ref (catalog);
add_data->files = _g_object_list_ref (list);
add_data_exec (add_data);
}