/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * Pix * * Copyright (C) 2008 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 #include #include "gth-catalog.h" #include "gth-file-source-catalogs.h" struct _GthFileSourceCatalogsPrivate { GList *files; GthCatalog *catalog; ListReady ready_func; gpointer ready_data; }; G_DEFINE_TYPE_WITH_CODE (GthFileSourceCatalogs, gth_file_source_catalogs, GTH_TYPE_FILE_SOURCE, G_ADD_PRIVATE (GthFileSourceCatalogs)) static GList * get_entry_points (GthFileSource *file_source) { GList *list = NULL; GFile *file; GFileInfo *info; file = g_file_new_for_uri ("catalog:///"); info = gth_file_source_get_file_info (file_source, file, GFILE_BASIC_ATTRIBUTES); list = g_list_append (list, gth_file_data_new (file, info)); g_object_unref (info); g_object_unref (file); return list; } static GFile * gth_file_source_catalogs_to_gio_file (GthFileSource *file_source, GFile *file) { return gth_catalog_file_to_gio_file (file); } static void update_file_info (GthFileSource *file_source, GFile *catalog_file, GFileInfo *info) { char *uri; GIcon *icon = NULL; uri = g_file_get_uri (catalog_file); if (g_str_has_suffix (uri, ".gqv") || g_str_has_suffix (uri, ".catalog")) { g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY); g_file_info_set_content_type (info, "pix/catalog"); icon = g_themed_icon_new ("file-catalog-symbolic"); g_file_info_set_symbolic_icon (info, icon); g_file_info_set_sort_order (info, 1); g_file_info_set_attribute_boolean (info, "pix::no-child", TRUE); gth_catalog_update_standard_attributes (catalog_file, info); } else if (g_str_has_suffix (uri, ".search")) { g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY); g_file_info_set_content_type (info, "pix/search"); icon = g_themed_icon_new ("file-search-symbolic"); g_file_info_set_symbolic_icon (info, icon); g_file_info_set_sort_order (info, 1); g_file_info_set_attribute_boolean (info, "pix::no-child", TRUE); gth_catalog_update_standard_attributes (catalog_file, info); } else { g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY); g_file_info_set_content_type (info, "pix/library"); icon = g_themed_icon_new ("file-library-symbolic"); g_file_info_set_symbolic_icon (info, icon); g_file_info_set_sort_order (info, 0); g_file_info_set_attribute_boolean (info, "pix::no-child", FALSE); gth_catalog_update_standard_attributes (catalog_file, info); } _g_object_unref (icon); g_free (uri); } static GFileInfo * gth_file_source_catalogs_get_file_info (GthFileSource *file_source, GFile *file, const char *attributes) { GFile *gio_file; GFileInfo *file_info; gio_file = gth_catalog_file_to_gio_file (file); file_info = g_file_query_info (gio_file, attributes, G_FILE_QUERY_INFO_NONE, NULL, NULL); if (file_info == NULL) file_info = g_file_info_new (); update_file_info (file_source, file, file_info); g_object_unref (gio_file); return file_info; } static GthFileData * gth_file_source_catalogs_get_file_data (GthFileSource *file_source, GFile *file, GFileInfo *info) { GthFileData *file_data = NULL; char *uri; GFile *catalog_file; uri = g_file_get_uri (file); switch (g_file_info_get_file_type (info)) { case G_FILE_TYPE_REGULAR: if (! g_str_has_suffix (uri, ".gqv") && ! g_str_has_suffix (uri, ".catalog") && ! g_str_has_suffix (uri, ".search")) { file_data = gth_file_data_new (file, info); break; } catalog_file = gth_catalog_file_from_gio_file (file, NULL); update_file_info (file_source, catalog_file, info); file_data = gth_file_data_new (catalog_file, info); g_object_unref (catalog_file); break; case G_FILE_TYPE_DIRECTORY: catalog_file = gth_catalog_file_from_gio_file (file, NULL); update_file_info (file_source, catalog_file, info); file_data = gth_file_data_new (catalog_file, info); g_object_unref (catalog_file); break; default: break; } g_free (uri); return file_data; } /* -- gth_file_source_catalogs_write_metadata -- */ typedef struct { GthFileSourceCatalogs *file_souce; GthFileData *file_data; char *attributes; ReadyCallback ready_callback; gpointer user_data; GFile *gio_file; } MetadataOpData; static void metadata_op_free (MetadataOpData *metadata_op) { gth_file_source_set_active (GTH_FILE_SOURCE (metadata_op->file_souce), FALSE); g_object_unref (metadata_op->file_data); g_free (metadata_op->attributes); g_object_unref (metadata_op->gio_file); g_object_unref (metadata_op->file_souce); g_free (metadata_op); } static void write_metadata_write_buffer_ready_cb (void **buffer, gsize count, GError *error, gpointer user_data) { MetadataOpData *metadata_op = user_data; GthFileSourceCatalogs *catalogs = metadata_op->file_souce; metadata_op->ready_callback (G_OBJECT (catalogs), error, metadata_op->user_data); metadata_op_free (metadata_op); } static void write_metadata_load_buffer_ready_cb (void **buffer, gsize count, GError *error, gpointer user_data) { MetadataOpData *metadata_op = user_data; GthCatalog *catalog; void *catalog_buffer; gsize catalog_size; if (error != NULL) { metadata_op->ready_callback (G_OBJECT (metadata_op->file_souce), error, metadata_op->user_data); metadata_op_free (metadata_op); return; } catalog = gth_catalog_new_from_data (*buffer, count, &error); if (catalog == NULL) { metadata_op->ready_callback (G_OBJECT (metadata_op->file_souce), error, metadata_op->user_data); metadata_op_free (metadata_op); return; } gth_catalog_set_file (catalog, metadata_op->gio_file); if (error != NULL) { metadata_op->ready_callback (G_OBJECT (metadata_op->file_souce), error, metadata_op->user_data); g_object_unref (catalog); metadata_op_free (metadata_op); return; } if (_g_file_attributes_matches_any (metadata_op->attributes, "sort::*")) gth_catalog_set_order (catalog, g_file_info_get_attribute_string (metadata_op->file_data->info, "sort::type"), g_file_info_get_attribute_boolean (metadata_op->file_data->info, "sort::inverse")); gth_hook_invoke ("gth-catalog-read-metadata", catalog, metadata_op->file_data); catalog_buffer = gth_catalog_to_data (catalog, &catalog_size); _g_file_write_async (metadata_op->gio_file, catalog_buffer, catalog_size, TRUE, G_PRIORITY_DEFAULT, gth_file_source_get_cancellable (GTH_FILE_SOURCE (metadata_op->file_souce)), write_metadata_write_buffer_ready_cb, metadata_op); g_object_unref (catalog); } static void gth_file_source_catalogs_write_metadata (GthFileSource *file_source, GthFileData *file_data, const char *attributes, ReadyCallback callback, gpointer user_data) { GthFileSourceCatalogs *catalogs = (GthFileSourceCatalogs *) file_source; char *uri; MetadataOpData *metadata_op; uri = g_file_get_uri (file_data->file); if (! g_str_has_suffix (uri, ".gqv") && ! g_str_has_suffix (uri, ".catalog") && ! g_str_has_suffix (uri, ".search")) { g_free (uri); object_ready_with_error (file_source, callback, user_data, NULL); return; } metadata_op = g_new0 (MetadataOpData, 1); metadata_op->file_souce = g_object_ref (catalogs); metadata_op->file_data = g_object_ref (file_data); metadata_op->attributes = g_strdup (attributes); metadata_op->ready_callback = callback; metadata_op->user_data = user_data; gth_file_source_set_active (GTH_FILE_SOURCE (catalogs), TRUE); g_cancellable_reset (gth_file_source_get_cancellable (file_source)); metadata_op->gio_file = gth_file_source_to_gio_file (file_source, file_data->file); _g_file_load_async (metadata_op->gio_file, G_PRIORITY_DEFAULT, gth_file_source_get_cancellable (file_source), write_metadata_load_buffer_ready_cb, metadata_op); g_free (uri); } /* -- gth_file_source_catalogs_read_metadata -- */ typedef struct { GthFileSource *file_source; GthFileData *file_data; char *attributes; ReadyCallback callback; gpointer data; } ReadMetadataOpData; static void read_metadata_free (ReadMetadataOpData *read_metadata) { g_object_unref (read_metadata->file_source); g_object_unref (read_metadata->file_data); g_free (read_metadata->attributes); g_free (read_metadata); } static void read_metadata_catalog_ready_cb (GObject *object, GError *error, gpointer user_data) { ReadMetadataOpData *read_metadata = user_data; if (error != NULL) { /* ignore errors */ g_clear_error (&error); } else { g_assert (object != NULL); gth_catalog_update_metadata (GTH_CATALOG (object), read_metadata->file_data); g_object_unref (object); } read_metadata->callback (G_OBJECT (read_metadata->file_source), error, read_metadata->data); read_metadata_free (read_metadata); } static void read_metadata_info_ready_cb (GList *files, GError *error, gpointer user_data) { ReadMetadataOpData *read_metadata = user_data; GthFileData *result; GFile *gio_file; if (error != NULL) { read_metadata->callback (G_OBJECT (read_metadata->file_source), error, read_metadata->data); read_metadata_free (read_metadata); return; } result = files->data; g_file_info_copy_into (result->info, read_metadata->file_data->info); update_file_info (read_metadata->file_source, read_metadata->file_data->file, read_metadata->file_data->info); gio_file = gth_catalog_file_to_gio_file (read_metadata->file_data->file); gth_catalog_load_from_file_async (gio_file, gth_file_source_get_cancellable (read_metadata->file_source), read_metadata_catalog_ready_cb, read_metadata); g_object_unref (gio_file); } static void gth_file_source_catalogs_read_metadata (GthFileSource *file_source, GthFileData *file_data, const char *attributes, ReadyCallback callback, gpointer data) { ReadMetadataOpData *read_metadata; GFile *gio_file; GList *files; read_metadata = g_new0 (ReadMetadataOpData, 1); read_metadata->file_source = g_object_ref (file_source); read_metadata->file_data = g_object_ref (file_data); read_metadata->attributes = g_strdup (attributes); read_metadata->callback = callback; read_metadata->data = data; gio_file = gth_catalog_file_to_gio_file (file_data->file); files = g_list_prepend (NULL, gio_file); _g_query_all_metadata_async (files, GTH_LIST_DEFAULT, attributes, gth_file_source_get_cancellable (file_source), read_metadata_info_ready_cb, read_metadata); _g_object_list_unref (files); } static void gth_file_source_catalogs_rename (GthFileSource *file_source, GFile *file, const char *edit_name, ReadyCallback callback, gpointer data) { GFile *parent; GFile *new_file; GthCatalog *catalog; GError *error = NULL; parent = g_file_get_parent (file); catalog = gth_catalog_load_from_file (file); if (catalog != NULL) { char *uri; char *clean_name; char *ext; char *name; GFile *gio_new_file; char *data; gsize size; GFileOutputStream *fstream; uri = g_file_get_uri (file); clean_name = _g_filename_clear_for_file (edit_name); ext = _g_uri_get_extension (uri); name = g_strconcat (clean_name, ext, NULL); new_file = g_file_get_child_for_display_name (parent, name, &error); gth_catalog_set_file (catalog, new_file); gth_catalog_set_name (catalog, edit_name); gio_new_file = gth_catalog_file_to_gio_file (new_file); data = gth_catalog_to_data (catalog, &size); fstream = g_file_create (gio_new_file, G_FILE_CREATE_NONE, gth_file_source_get_cancellable (file_source), &error); if (fstream != NULL) { if (g_output_stream_write_all (G_OUTPUT_STREAM (fstream), data, size, NULL, gth_file_source_get_cancellable (file_source), &error)) { GFile *gio_old_file; gio_old_file = gth_catalog_file_to_gio_file (file); if (g_file_delete (gio_old_file, gth_file_source_get_cancellable (file_source), &error)) gth_monitor_file_renamed (gth_main_get_default_monitor (), file, new_file); g_object_unref (gio_old_file); } g_object_unref (fstream); } g_free (data); g_object_unref (gio_new_file); g_free (clean_name); g_free (ext); g_free (name); g_free (uri); } else { new_file = g_file_get_child_for_display_name (parent, edit_name, &error); if (new_file != NULL) { GFile *gio_file; GFile *gio_new_file; gio_file = gth_file_source_to_gio_file (file_source, file); gio_new_file = gth_file_source_to_gio_file (file_source, new_file); if (g_file_move (gio_file, gio_new_file, 0, gth_file_source_get_cancellable (file_source), NULL, NULL, &error)) { gth_monitor_file_renamed (gth_main_get_default_monitor (), file, new_file); } g_object_unref (gio_new_file); g_object_unref (gio_file); } } if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS)) { g_clear_error (&error); error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_EXISTS, _("Name already used")); } object_ready_with_error (file_source, callback, data, error); _g_object_unref (new_file); _g_object_unref (catalog); } /* -- gth_file_source_catalogs_for_each_child -- */ typedef struct { GthFileSource *file_source; gboolean recursive; char *attributes; StartDirCallback start_dir_func; ForEachChildCallback for_each_file_func; ReadyCallback ready_func; gpointer user_data; GList *to_visit; } ForEachChildData; static void for_each_child_data_free (ForEachChildData *data) { _g_object_list_unref (data->to_visit); g_free (data->attributes); g_object_ref (data->file_source); } static void for_each_child_data_done (ForEachChildData *data, GError *error) { gth_file_source_set_active (data->file_source, FALSE); data->ready_func (G_OBJECT (data->file_source), error, data->user_data); for_each_child_data_free (data); } static void for_each_child__visit_file (ForEachChildData *data, GthFileData *library); static void for_each_child__continue (ForEachChildData *data) { GthFileData *file; GList *tmp; if (! data->recursive || (data->to_visit == NULL)) { for_each_child_data_done (data, NULL); return; } file = data->to_visit->data; tmp = data->to_visit; data->to_visit = g_list_remove_link (data->to_visit, tmp); g_list_free (tmp); for_each_child__visit_file (data, file); g_object_unref (file); } static void for_each_child__done_func (GError *error, gpointer user_data) { for_each_child__continue ((ForEachChildData *) user_data); } static void for_each_child__for_each_file_func (GFile *file, GFileInfo *info, gpointer user_data) { ForEachChildData *data = user_data; GthFileData *file_data; file_data = gth_file_source_get_file_data (data->file_source, file, info); if (file_data == NULL) return; data->for_each_file_func (file_data->file, file_data->info, data->user_data); if (data->recursive && (g_file_info_get_file_type (file_data->info) == G_FILE_TYPE_DIRECTORY)) data->to_visit = g_list_append (data->to_visit, g_object_ref (file_data)); g_object_unref (file_data); } static DirOp for_each_child__start_dir_func (GFile *directory, GFileInfo *info, GError **error, gpointer user_data) { if (g_file_info_get_is_hidden (info)) return DIR_OP_SKIP; else return DIR_OP_CONTINUE; } static void for_each_child__catalog_list_ready_cb (GthCatalog *catalog, GList *files, GError *error, gpointer user_data) { ForEachChildData *data = user_data; GList *scan; for (scan = files; scan; scan = scan->next) { GthFileData *file_data = scan->data; if (g_file_info_get_is_hidden (file_data->info)) continue; data->for_each_file_func (file_data->file, file_data->info, data->user_data); } for_each_child__continue (data); } static void for_each_child__visit_file (ForEachChildData *data, GthFileData *file_data) { GFile *gio_file; char *uri; if (data->start_dir_func != NULL) { GError *error = NULL; switch (data->start_dir_func (file_data->file, file_data->info, &error, data->user_data)) { case DIR_OP_CONTINUE: break; case DIR_OP_SKIP: for_each_child__continue (data); return; case DIR_OP_STOP: for_each_child_data_done (data, NULL); return; } } gio_file = gth_file_source_to_gio_file (data->file_source, file_data->file); uri = g_file_get_uri (file_data->file); if (g_str_has_suffix (uri, ".gqv") || g_str_has_suffix (uri, ".catalog") || g_str_has_suffix (uri, ".search")) { gth_catalog_list_async (gio_file, data->attributes, gth_file_source_get_cancellable (data->file_source), for_each_child__catalog_list_ready_cb, data); } else _g_directory_foreach_child (gio_file, FALSE, TRUE, GFILE_STANDARD_ATTRIBUTES_WITH_FAST_CONTENT_TYPE, gth_file_source_get_cancellable (data->file_source), for_each_child__start_dir_func, for_each_child__for_each_file_func, for_each_child__done_func, data); g_object_unref (gio_file); g_free (uri); } static void for_each_child__parent_info_ready_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { ForEachChildData *data = user_data; GFile *file; GFileInfo *info; GError *error = NULL; GthFileData *file_data; file = G_FILE (source_object); info = g_file_query_info_finish (file, result, &error); if (info == NULL) { for_each_child_data_done (data, error); return; } file_data = gth_file_source_get_file_data (data->file_source, file, info); for_each_child__visit_file (data, file_data); g_object_unref (file_data); } static void gth_file_source_catalogs_for_each_child (GthFileSource *file_source, GFile *parent, gboolean recursive, const char *attributes, StartDirCallback start_dir_func, ForEachChildCallback for_each_file_func, ReadyCallback ready_func, gpointer user_data) { ForEachChildData *data; GFile *gio_parent; data = g_new0 (ForEachChildData, 1); data->file_source = g_object_ref (file_source); data->recursive = recursive; data->attributes = g_strdup (attributes); data->start_dir_func = start_dir_func; data->for_each_file_func = for_each_file_func; data->ready_func = ready_func; data->user_data = user_data; gio_parent = gth_file_source_to_gio_file (file_source, parent); g_file_query_info_async (gio_parent, GFILE_STANDARD_ATTRIBUTES_WITH_FAST_CONTENT_TYPE, G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT, gth_file_source_get_cancellable (data->file_source), for_each_child__parent_info_ready_cb, data); g_object_unref (gio_parent); } /* -- gth_file_source_catalogs_copy -- */ typedef struct { GthFileSource *file_source; GthFileData *destination; GList *file_list; int destination_position; ProgressCallback progress_callback; DialogCallback dialog_callback; ReadyCallback ready_callback; gpointer user_data; GList *files; GthCatalog *catalog; } CopyOpData; static void copy_op_data_free (CopyOpData *cod) { _g_object_unref (cod->catalog); _g_object_list_unref (cod->files); _g_object_list_unref (cod->file_list); g_object_unref (cod->destination); g_object_unref (cod->file_source); g_free (cod); } static void copy__catalog_save_done_cb (void **buffer, gsize count, GError *error, gpointer user_data) { CopyOpData *cod = user_data; if (error == NULL) { gth_monitor_files_created_with_pos (gth_main_get_default_monitor (), cod->destination->file, cod->files, cod->destination_position); } cod->ready_callback (G_OBJECT (cod->file_source), error, cod->user_data); copy_op_data_free (cod); } static void catalog_ready_cb (GObject *catalog, GError *error, gpointer user_data) { CopyOpData *cod = user_data; int position; GList *scan; char *buffer; gsize size; GFile *gio_file; if (error != NULL) { cod->ready_callback (G_OBJECT (cod->file_source), error, cod->user_data); copy_op_data_free (cod); return; } g_assert (catalog != NULL); cod->catalog = (GthCatalog *) catalog; if (cod->destination_position >= 0) gth_catalog_set_order (cod->catalog, "general::unsorted", FALSE); position = cod->destination_position; for (scan = cod->files; scan; scan = scan->next) { gth_catalog_insert_file (cod->catalog, (GFile *) scan->data, position); if (cod->destination_position >= 0) /* always append to the end if destination_position is -1 */ position += 1; } buffer = gth_catalog_to_data (cod->catalog, &size); gio_file = gth_catalog_file_to_gio_file (cod->destination->file); _g_file_write_async (gio_file, buffer, size, TRUE, G_PRIORITY_DEFAULT, NULL, copy__catalog_save_done_cb, cod); g_object_unref (gio_file); } static void copy__file_list_info_ready_cb (GList *files, GError *error, gpointer user_data) { CopyOpData *cod = user_data; GList *scan; for (scan = files; scan; scan = scan->next) { GthFileData *file_data = scan->data; switch (g_file_info_get_file_type (file_data->info)) { case G_FILE_TYPE_REGULAR: case G_FILE_TYPE_SYMBOLIC_LINK: cod->files = g_list_prepend (cod->files, g_object_ref (file_data->file)); break; default: break; } } cod->files = g_list_reverse (cod->files); gth_catalog_load_from_file_async (cod->destination->file, gth_file_source_get_cancellable (cod->file_source), catalog_ready_cb, cod); } /* -- _gth_file_source_catalogs_copy_catalog -- */ typedef struct { GthFileSource *file_source; gboolean move; ProgressCallback progress_callback; DialogCallback dialog_callback; ReadyCallback ready_callback; gpointer user_data; GthFileData *destination; GList *file_list; } CopyCatalogData; static void copy_catalog_data_free (CopyCatalogData *ccd) { _g_object_list_unref (ccd->file_list); _g_object_unref (ccd->destination); _g_object_unref (ccd->file_source); g_free (ccd); } static void _gth_file_source_catalogs_copy_catalog (CopyCatalogData *ccd, GthOverwriteResponse default_response); static void copy_catalog_overwrite_dialog_response_cb (GtkDialog *dialog, int response, gpointer user_data) { CopyCatalogData *ccd = user_data; gtk_widget_destroy (GTK_WIDGET (dialog)); if (response == GTK_RESPONSE_OK) { _gth_file_source_catalogs_copy_catalog (ccd, GTH_OVERWRITE_RESPONSE_ALWAYS_YES); return; } ccd->ready_callback (G_OBJECT (ccd->file_source), NULL, ccd->user_data); copy_catalog_data_free (ccd); } static void copy_catalog_ready_cb (GError *error, gpointer user_data) { CopyCatalogData *ccd = user_data; GFile *first_file; GFile *parent; GList *new_file_list; GList *scan; first_file = ccd->file_list->data; if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS)) { char *uri; char *extension; char *msg; GtkWidget *d; uri = g_file_get_uri (first_file); extension = _g_uri_get_extension (uri); if ((g_strcmp0 (extension, ".catalog") == 0) || (g_strcmp0 (extension, ".search") == 0)) msg = g_strdup_printf (_("The catalog “%s” already exists, do you want to overwrite it?"), g_file_info_get_display_name (ccd->destination->info)); else msg = g_strdup_printf (_("The library “%s” already exists, do you want to overwrite it?"), g_file_info_get_display_name (ccd->destination->info)); d = _gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, _GTK_ICON_NAME_DIALOG_QUESTION, msg, NULL, _GTK_LABEL_CANCEL, GTK_RESPONSE_CANCEL, _("Over_write"), GTK_RESPONSE_OK, NULL); g_signal_connect (d, "response", G_CALLBACK (copy_catalog_overwrite_dialog_response_cb), ccd); ccd->dialog_callback (TRUE, d, ccd->user_data); gtk_widget_show (d); g_free (msg); g_free (extension); g_free (uri); return; } parent = g_file_get_parent (first_file); if (parent != NULL) { gth_monitor_folder_changed (gth_main_get_default_monitor (), parent, ccd->file_list, GTH_MONITOR_EVENT_DELETED); g_object_unref (parent); } new_file_list = NULL; for (scan = ccd->file_list; scan; scan = scan->next) { GFile *old_file = scan->data; char *basename; GFile *new_file; basename = g_file_get_basename (old_file); new_file = g_file_get_child (ccd->destination->file, basename); new_file_list = g_list_prepend (new_file_list, new_file); g_free (basename); } new_file_list = g_list_reverse (new_file_list); gth_monitor_folder_changed (gth_main_get_default_monitor (), ccd->destination->file, new_file_list, GTH_MONITOR_EVENT_CREATED); ccd->ready_callback (G_OBJECT (ccd->file_source), error, ccd->user_data); _g_object_list_unref (new_file_list); copy_catalog_data_free (ccd); } static void _gth_file_source_catalogs_copy_catalog (CopyCatalogData *ccd, GthOverwriteResponse default_response) { GList *gio_list; GFile *gio_destination; gio_list = gth_file_source_to_gio_file_list (ccd->file_source, ccd->file_list); gio_destination = gth_file_source_to_gio_file (ccd->file_source, ccd->destination->file); _g_file_list_copy_async (gio_list, gio_destination, ccd->move, GTH_FILE_COPY_DEFAULT, default_response, G_PRIORITY_DEFAULT, gth_file_source_get_cancellable (ccd->file_source), ccd->progress_callback, ccd->user_data, ccd->dialog_callback, ccd->user_data, copy_catalog_ready_cb, ccd); g_object_unref (gio_destination); _g_object_list_unref (gio_list); } static void copy_catalog_error_dialog_response_cb (GtkDialog *dialog, int response, gpointer user_data) { CopyCatalogData *ccd = user_data; gtk_widget_destroy (GTK_WIDGET (dialog)); ccd->dialog_callback (FALSE, NULL, ccd->user_data); ccd->ready_callback (G_OBJECT (ccd->file_source), NULL, ccd->user_data); copy_catalog_data_free (ccd); } static void gth_file_source_catalogs_copy (GthFileSource *file_source, GthFileData *destination, GList *file_list, /* GFile * list */ gboolean move, int destination_position, ProgressCallback progress_callback, DialogCallback dialog_callback, ReadyCallback ready_callback, gpointer data) { GFile *first_file; first_file = file_list->data; if (g_file_has_uri_scheme (first_file, "catalog")) { if (g_strcmp0 (g_file_info_get_content_type (destination->info), "pix/catalog") == 0) { CopyCatalogData *ccd; const char *msg; GtkWidget *d; ccd = g_new0 (CopyCatalogData, 1); ccd->file_source = g_object_ref (file_source); ccd->dialog_callback = dialog_callback; ccd->ready_callback = ready_callback; ccd->user_data = data; if (move) msg = _("Cannot move the files"); else msg = _("Cannot copy the files"); d = _gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, _GTK_ICON_NAME_DIALOG_ERROR, msg, _("Invalid destination."), _GTK_LABEL_CLOSE, GTK_RESPONSE_CLOSE, NULL); g_signal_connect (d, "response", G_CALLBACK (copy_catalog_error_dialog_response_cb), ccd); dialog_callback (TRUE, d, data); gtk_widget_show (d); return; } else { /* copy / move a catalog or library into another library */ CopyCatalogData *ccd; ccd = g_new0 (CopyCatalogData, 1); ccd->file_source = g_object_ref (file_source); ccd->destination = gth_file_data_dup (destination); ccd->file_list = _g_object_list_ref (file_list); ccd->move = move; ccd->progress_callback = progress_callback; ccd->dialog_callback = dialog_callback; ccd->ready_callback = ready_callback; ccd->user_data = data; _gth_file_source_catalogs_copy_catalog (ccd, GTH_OVERWRITE_RESPONSE_ALWAYS_NO); } } else { /* copy / move files to a catalog */ CopyOpData *cod; cod = g_new0 (CopyOpData, 1); cod->file_source = g_object_ref (file_source); cod->destination = g_object_ref (destination); cod->file_list = _g_object_list_ref (file_list); cod->destination_position = destination_position; cod->progress_callback = progress_callback; cod->dialog_callback = dialog_callback; cod->ready_callback = ready_callback; cod->user_data = data; if (cod->progress_callback != NULL) { char *message; message = g_strdup_printf (_("Copying files to “%s”"), g_file_info_get_display_name (destination->info)); (cod->progress_callback) (G_OBJECT (file_source), message, NULL, TRUE, 0.0, cod->user_data); g_free (message); } _g_file_list_query_info_async (cod->file_list, GTH_LIST_DEFAULT, GFILE_NAME_TYPE_ATTRIBUTES, gth_file_source_get_cancellable (file_source), copy__file_list_info_ready_cb, cod); } } static gboolean gth_file_source_catalogs_is_reorderable (GthFileSource *file_source) { return TRUE; } typedef struct { GthFileSource *file_source; GthFileData *destination; GList *visible_files; /* GFile list */ GList *files_to_move; /* GFile list */ int dest_pos; ReadyCallback callback; gpointer data; int *new_order; } ReorderData; static void reorder_data_free (ReorderData *reorder_data) { gth_file_source_set_active (reorder_data->file_source, FALSE); _g_object_list_unref (reorder_data->visible_files); _g_object_list_unref (reorder_data->files_to_move); _g_object_unref (reorder_data->destination); _g_object_unref (reorder_data->file_source); g_free (reorder_data->new_order); g_free (reorder_data); } static void reorder_buffer_ready_cb (void **buffer, gsize count, GError *error, gpointer user_data) { ReorderData *reorder_data = user_data; gth_monitor_order_changed (gth_main_get_default_monitor (), reorder_data->destination->file, reorder_data->new_order); reorder_data->callback (G_OBJECT (reorder_data->file_source), error, reorder_data->data); reorder_data_free (reorder_data); } static int * reorder_catalog_list (GthCatalog *catalog, GList *visible_files, GList *files_to_move, int dest_pos) { int *new_order; GList *new_file_list; _g_list_reorder (gth_catalog_get_file_list (catalog), visible_files, files_to_move, dest_pos, &new_order, &new_file_list); gth_catalog_set_file_list (catalog, new_file_list); _g_object_list_unref (new_file_list); return new_order; } static void reorder_catalog_ready_cb (GObject *object, GError *error, gpointer user_data) { ReorderData *reorder_data = user_data; GthCatalog *catalog; char *buffer; gsize size; GFile *gio_file; if (error != NULL) { reorder_data->callback (G_OBJECT (reorder_data->file_source), error, reorder_data->data); reorder_data_free (reorder_data); return; } g_assert (object != NULL); catalog = (GthCatalog *) object; reorder_data->new_order = reorder_catalog_list (catalog, reorder_data->visible_files, reorder_data->files_to_move, reorder_data->dest_pos); gth_catalog_set_order (catalog, "general::unsorted", FALSE); buffer = gth_catalog_to_data (catalog, &size); gio_file = gth_file_source_to_gio_file (reorder_data->file_source, reorder_data->destination->file); _g_file_write_async (gio_file, buffer, size, TRUE, G_PRIORITY_DEFAULT, gth_file_source_get_cancellable (reorder_data->file_source), reorder_buffer_ready_cb, reorder_data); g_object_unref (gio_file); } static void gth_file_source_catalogs_reorder (GthFileSource *file_source, GthFileData *destination, GList *visible_files, /* GFile list */ GList *files_to_move, /* GFile list */ int dest_pos, ReadyCallback callback, gpointer data) { GthFileSourceCatalogs *catalogs = (GthFileSourceCatalogs *) file_source; ReorderData *reorder_data; GFile *gio_file; gth_file_source_set_active (GTH_FILE_SOURCE (catalogs), TRUE); g_cancellable_reset (gth_file_source_get_cancellable (file_source)); reorder_data = g_new0 (ReorderData, 1); reorder_data->file_source = g_object_ref (file_source); reorder_data->destination = g_object_ref (destination); reorder_data->visible_files = _g_object_list_ref (visible_files); reorder_data->files_to_move = _g_object_list_ref (files_to_move); reorder_data->dest_pos = dest_pos; reorder_data->callback = callback; reorder_data->data = data; gio_file = gth_file_source_to_gio_file (file_source, destination->file); gth_catalog_load_from_file_async (gio_file, gth_file_source_get_cancellable (file_source), reorder_catalog_ready_cb, reorder_data); g_object_unref (gio_file); } /* -- gth_catalog_manager_remove_files -- */ typedef struct { GtkWindow *parent; GList *file_list; GFile *gio_file; GthCatalog *catalog; gboolean notify; } RemoveFromCatalogData; static void remove_from_catalog_end (GError *error, RemoveFromCatalogData *data) { if ((data->catalog != NULL) && (error != NULL)) _gtk_error_dialog_from_gerror_show (data->parent, _("Could not remove the files from the catalog"), error); _g_object_unref (data->catalog); _g_object_unref (data->gio_file); _g_object_list_unref (data->file_list); g_free (data); } static void remove_files__catalog_save_done_cb (void **buffer, gsize count, GError *error, gpointer user_data) { RemoveFromCatalogData *data = user_data; if ((error == NULL) && data->notify) { GFile *catalog_file; catalog_file = gth_catalog_file_from_gio_file (data->gio_file, NULL); gth_monitor_folder_changed (gth_main_get_default_monitor (), catalog_file, data->file_list, GTH_MONITOR_EVENT_REMOVED); g_object_unref (catalog_file); } remove_from_catalog_end (error, data); } static void catalog_buffer_ready_cb (void **buffer, gsize count, GError *error, gpointer user_data) { RemoveFromCatalogData *data = user_data; GList *scan; void *catalog_buffer; gsize catalog_size; if (error != NULL) { remove_from_catalog_end (error, data); return; } data->catalog = gth_catalog_new_from_data (*buffer, count, &error); if (data->catalog == NULL) { remove_from_catalog_end (error, data); return; } for (scan = data->file_list; scan; scan = scan->next) { GFile *file = scan->data; gth_catalog_remove_file (data->catalog, file); } catalog_buffer = gth_catalog_to_data (data->catalog, &catalog_size); if (error != NULL) { remove_from_catalog_end (error, data); return; } _g_file_write_async (data->gio_file, catalog_buffer, catalog_size, TRUE, G_PRIORITY_DEFAULT, NULL, remove_files__catalog_save_done_cb, data); } void gth_catalog_manager_remove_files (GtkWindow *parent, GthFileData *location, GList *file_list, gboolean notify) { RemoveFromCatalogData *data; data = g_new0 (RemoveFromCatalogData, 1); data->parent = parent; data->file_list = _g_file_list_dup (file_list); data->gio_file = gth_main_get_gio_file (location->file); data->notify = notify; data->catalog = NULL; _g_file_load_async (data->gio_file, G_PRIORITY_DEFAULT, NULL, catalog_buffer_ready_cb, data); } static void gth_file_source_catalogs_remove (GthFileSource *file_source, GthFileData *location, GList *file_data_list /* GthFileData list */, gboolean permanently, GtkWindow *parent) { GList *file_list; file_list = gth_file_data_list_to_file_list (file_data_list); gth_catalog_manager_remove_files (parent, location, file_list, TRUE); _g_object_list_unref (file_list); } static void gth_file_source_catalogs_deleted_from_disk (GthFileSource *file_source, GthFileData *location, GList *file_list) { gth_catalog_manager_remove_files (NULL, location, file_list, FALSE); } static GdkDragAction gth_file_source_catalogs_get_drop_actions (GthFileSource *file_source, GFile *destination, GFile *file) { GdkDragAction actions = 0; char *dest_uri; char *dest_scheme; char *dest_ext; gboolean dest_is_catalog; char *file_uri; char *file_scheme; char *file_ext; gboolean file_is_catalog; dest_uri = g_file_get_uri (destination); dest_scheme = gth_main_get_source_scheme (dest_uri); dest_ext = _g_uri_get_extension (dest_uri); dest_is_catalog = _g_str_equal (dest_ext, ".catalog") || _g_str_equal (dest_ext, ".search"); file_uri = g_file_get_uri (file); file_scheme = gth_main_get_source_scheme (file_uri); file_ext = _g_uri_get_extension (file_uri); file_is_catalog = _g_str_equal (file_ext, ".catalog") || _g_str_equal (file_ext, ".search"); if (_g_str_equal (dest_scheme, "catalog") && dest_is_catalog && _g_str_equal (file_scheme, "file")) { /* Copy files into a catalog. */ actions = GDK_ACTION_COPY; } else if (_g_str_equal (file_scheme, "catalog") && file_is_catalog && _g_str_equal (dest_scheme, "catalog") && ! dest_is_catalog) { /* Move a catalog into a library. */ actions = GDK_ACTION_MOVE; } else if (_g_str_equal (file_scheme, "catalog") && ! file_is_catalog && _g_str_equal (dest_scheme, "catalog") && ! dest_is_catalog) { /* Move a library into another library. */ actions = GDK_ACTION_MOVE; } g_free (file_ext); g_free (file_scheme); g_free (file_uri); g_free (dest_ext); g_free (dest_scheme); g_free (dest_uri); return actions; } static void gth_file_source_catalogs_finalize (GObject *object) { GthFileSourceCatalogs *catalogs = GTH_FILE_SOURCE_CATALOGS (object); g_object_unref (catalogs->priv->catalog); _g_object_list_unref (catalogs->priv->files); catalogs->priv->files = NULL; G_OBJECT_CLASS (gth_file_source_catalogs_parent_class)->finalize (object); } static void gth_file_source_catalogs_class_init (GthFileSourceCatalogsClass *class) { GObjectClass *object_class; GthFileSourceClass *file_source_class; object_class = (GObjectClass*) class; object_class->finalize = gth_file_source_catalogs_finalize; file_source_class = (GthFileSourceClass*) class; file_source_class->get_entry_points = get_entry_points; file_source_class->to_gio_file = gth_file_source_catalogs_to_gio_file; file_source_class->get_file_info = gth_file_source_catalogs_get_file_info; file_source_class->get_file_data = gth_file_source_catalogs_get_file_data; file_source_class->write_metadata = gth_file_source_catalogs_write_metadata; file_source_class->read_metadata = gth_file_source_catalogs_read_metadata; file_source_class->rename = gth_file_source_catalogs_rename; file_source_class->for_each_child = gth_file_source_catalogs_for_each_child; file_source_class->copy = gth_file_source_catalogs_copy; file_source_class->is_reorderable = gth_file_source_catalogs_is_reorderable; file_source_class->reorder = gth_file_source_catalogs_reorder; file_source_class->remove = gth_file_source_catalogs_remove; file_source_class->deleted_from_disk = gth_file_source_catalogs_deleted_from_disk; file_source_class->get_drop_actions = gth_file_source_catalogs_get_drop_actions; } static void gth_file_source_catalogs_init (GthFileSourceCatalogs *catalogs) { gth_file_source_add_scheme (GTH_FILE_SOURCE (catalogs), "catalog"); catalogs->priv = gth_file_source_catalogs_get_instance_private (catalogs); catalogs->priv->files = NULL; catalogs->priv->catalog = gth_catalog_new (); catalogs->priv->ready_func = NULL; catalogs->priv->ready_data = NULL; }