/* PIKA - Photo and Image Kooker Application * a rebranding of The GNU Image Manipulation Program (created with heckimp) * A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio * * Original copyright, applying to most contents (license remains unchanged): * Copyright (C) 1995, 1996, 1997 Spencer Kimball and Peter Mattis * Copyright (C) 1997 Josh MacDonald * * file-save.c * * 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 3 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 "config.h" #include #include #include "libpikabase/pikabase.h" #include "core/core-types.h" #include "config/pikacoreconfig.h" #include "core/pika.h" #include "core/pikacontext.h" #include "core/pikadocumentlist.h" #include "core/pikadrawable.h" #include "core/pikaimage.h" #include "core/pikaimagefile.h" #include "core/pikaparamspecs.h" #include "core/pikaprogress.h" #include "pdb/pikapdb.h" #include "plug-in/pikapluginprocedure.h" #include "file-remote.h" #include "file-save.h" #include "pika-file.h" #include "pika-intl.h" /* public functions */ PikaPDBStatusType file_save (Pika *pika, PikaImage *image, PikaProgress *progress, GFile *file, PikaPlugInProcedure *file_proc, PikaRunMode run_mode, gboolean change_saved_state, gboolean export_backward, gboolean export_forward, GError **error) { PikaValueArray *return_vals; GFile *orig_file; PikaPDBStatusType status = PIKA_PDB_EXECUTION_ERROR; GFile *local_file = NULL; gboolean mounted = TRUE; GError *my_error = NULL; GList *drawables_list; PikaDrawable **drawables = NULL; gint n_drawables; g_return_val_if_fail (PIKA_IS_PIKA (pika), PIKA_PDB_CALLING_ERROR); g_return_val_if_fail (PIKA_IS_IMAGE (image), PIKA_PDB_CALLING_ERROR); g_return_val_if_fail (progress == NULL || PIKA_IS_PROGRESS (progress), PIKA_PDB_CALLING_ERROR); g_return_val_if_fail (G_IS_FILE (file), PIKA_PDB_CALLING_ERROR); g_return_val_if_fail (PIKA_IS_PLUG_IN_PROCEDURE (file_proc), PIKA_PDB_CALLING_ERROR); g_return_val_if_fail ((export_backward && export_forward) == FALSE, PIKA_PDB_CALLING_ERROR); g_return_val_if_fail (error == NULL || *error == NULL, PIKA_PDB_CALLING_ERROR); orig_file = file; /* ref image and file, so they can't get deleted during save */ g_object_ref (image); g_object_ref (orig_file); pika_image_saving (image); drawables_list = pika_image_get_selected_drawables (image); if (drawables_list) { GList *iter; gint i; n_drawables = g_list_length (drawables_list); drawables = g_new (PikaDrawable *, n_drawables); for (iter = drawables_list, i = 0; iter; iter = iter->next, i++) drawables[i] = iter->data; g_list_free (drawables_list); } else { g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, _("There is no active layer to save")); goto out; } /* FIXME enable these tests for remote files again, needs testing */ if (g_file_is_native (file) && g_file_query_exists (file, NULL)) { GFileInfo *info; info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_TYPE "," G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, G_FILE_QUERY_INFO_NONE, NULL, error); if (! info) { /* extra paranoia */ if (error && ! *error) g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, _("Failed to get file information")); goto out; } if (g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_STANDARD_TYPE) != G_FILE_TYPE_REGULAR) { g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, _("Not a regular file")); g_object_unref (info); goto out; } if (! g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE)) { g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, _("Permission denied")); g_object_unref (info); goto out; } g_object_unref (info); } if (! g_file_is_native (file) && ! file_remote_mount_file (pika, file, progress, &my_error)) { if (my_error) { g_printerr ("%s: mounting remote volume failed, trying to upload " "the file: %s\n", G_STRFUNC, my_error->message); g_clear_error (&my_error); mounted = FALSE; } else { status = PIKA_PDB_CANCEL; goto out; } } if (! file_proc->handles_remote || ! mounted) { gchar *my_path = g_file_get_path (file); if (! my_path) { local_file = file_remote_upload_image_prepare (pika, file, progress, &my_error); if (! local_file) { if (my_error) g_propagate_error (error, my_error); else status = PIKA_PDB_CANCEL; goto out; } file = local_file; } g_free (my_path); } return_vals = pika_pdb_execute_procedure_by_name (image->pika->pdb, pika_get_user_context (pika), progress, error, pika_object_get_name (file_proc), PIKA_TYPE_RUN_MODE, run_mode, PIKA_TYPE_IMAGE, image, G_TYPE_INT, n_drawables, PIKA_TYPE_OBJECT_ARRAY, drawables, G_TYPE_FILE, file, G_TYPE_NONE); status = g_value_get_enum (pika_value_array_index (return_vals, 0)); pika_value_array_unref (return_vals); g_clear_pointer (&drawables, g_free); if (local_file) { if (status == PIKA_PDB_SUCCESS) { GError *my_error = NULL; if (! file_remote_upload_image_finish (pika, orig_file, local_file, progress, &my_error)) { status = PIKA_PDB_EXECUTION_ERROR; if (my_error) g_propagate_error (error, my_error); else status = PIKA_PDB_CANCEL; } } g_file_delete (local_file, NULL, NULL); g_object_unref (local_file); } if (status == PIKA_PDB_SUCCESS) { PikaDocumentList *documents; PikaImagefile *imagefile; if (change_saved_state) { pika_image_set_file (image, orig_file); pika_image_set_save_proc (image, file_proc); /* Forget the import source when we save. We interpret a * save as that the user is not interested in being able * to quickly export back to the original any longer */ pika_image_set_imported_file (image, NULL); pika_image_clean_all (image); } else if (export_backward) { /* We exported the image back to its imported source, * change nothing about export/import flags, only set * the export state to clean */ pika_image_export_clean_all (image); } else if (export_forward) { /* Remember the last entered Export URI for the image. We * only need to do this explicitly when exporting. It * happens implicitly when saving since the PikaObject name * of a PikaImage is the last-save URI */ pika_image_set_exported_file (image, orig_file); pika_image_set_export_proc (image, file_proc); /* An image can not be considered both exported and imported * at the same time, so stop consider it as imported now * that we consider it exported. */ pika_image_set_imported_file (image, NULL); pika_image_export_clean_all (image); } if (export_backward || export_forward) pika_image_exported (image, orig_file); else pika_image_saved (image, orig_file); documents = PIKA_DOCUMENT_LIST (image->pika->documents); imagefile = pika_document_list_add_file (documents, orig_file, g_slist_nth_data (file_proc->mime_types_list, 0)); /* only save a thumbnail if we are saving as XCF, see bug #25272 */ if (PIKA_PROCEDURE (file_proc)->proc_type == PIKA_PDB_PROC_TYPE_INTERNAL) pika_imagefile_save_thumbnail (imagefile, g_slist_nth_data (file_proc->mime_types_list, 0), image, NULL); } else if (status != PIKA_PDB_CANCEL) { if (error && *error == NULL) { g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, _("%s plug-in could not save image"), pika_procedure_get_label (PIKA_PROCEDURE (file_proc))); } } pika_image_flush (image); out: g_object_unref (orig_file); g_object_unref (image); return status; }