/* LIBGIMP - The GIMP Library * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball * * gimpprocedureconfig.c * Copyright (C) 2019 Michael Natterer * * This library is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . */ #include "config.h" #include "gimp.h" #include "gimpimagemetadata.h" #include "gimpprocedureconfig-private.h" /** * SECTION: gimpprocedureconfig * @title: GimpProcedureConfig * @short_description: Config object for procedure arguments * * #GimpProcedureConfig is the base class for #GimpProcedure specific * config objects and the main interface to manage aspects of * #GimpProcedure's arguments such as persistency of the last used * arguments across GIMP sessions. * * A #GimpProcedureConfig is created by a #GimpProcedure using * gimp_procedure_create_config() and its properties match the * procedure's arguments and auxiliary arguments in number, order and * type. * * It implements the #GimpConfig interface and therefore has all its * serialization and deserialization features. * * Since: 3.0 **/ enum { PROP_0, PROP_PROCEDURE, N_PROPS }; struct _GimpProcedureConfigPrivate { GimpProcedure *procedure; GimpImage *image; GimpRunMode run_mode; GimpMetadata *metadata; gchar *mime_type; GimpMetadataSaveFlags metadata_flags; gboolean metadata_saved; }; static void gimp_procedure_config_constructed (GObject *object); static void gimp_procedure_config_dispose (GObject *object); static void gimp_procedure_config_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void gimp_procedure_config_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GimpProcedureConfig, gimp_procedure_config, G_TYPE_OBJECT) #define parent_class gimp_procedure_config_parent_class static GParamSpec *props[N_PROPS] = { NULL, }; static const struct { const gchar *name; GimpMetadataSaveFlags flag; } metadata_properties[] = { { "save-exif", GIMP_METADATA_SAVE_EXIF }, { "save-xmp", GIMP_METADATA_SAVE_XMP }, { "save-iptc", GIMP_METADATA_SAVE_IPTC }, { "save-thumbnail", GIMP_METADATA_SAVE_THUMBNAIL }, { "save-color-profile", GIMP_METADATA_SAVE_COLOR_PROFILE }, { "save-comment", GIMP_METADATA_SAVE_COMMENT } }; static void gimp_procedure_config_class_init (GimpProcedureConfigClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructed = gimp_procedure_config_constructed; object_class->dispose = gimp_procedure_config_dispose; object_class->set_property = gimp_procedure_config_set_property; object_class->get_property = gimp_procedure_config_get_property; props[PROP_PROCEDURE] = g_param_spec_object ("procedure", "Procedure", "The procedure this config object is used for", GIMP_TYPE_PROCEDURE, GIMP_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_properties (object_class, N_PROPS, props); } static void gimp_procedure_config_init (GimpProcedureConfig *config) { config->priv = gimp_procedure_config_get_instance_private (config); config->priv->run_mode = -1; } static void gimp_procedure_config_constructed (GObject *object) { GimpProcedureConfig *config = GIMP_PROCEDURE_CONFIG (object); G_OBJECT_CLASS (parent_class)->constructed (object); g_assert (GIMP_IS_PROCEDURE (config->priv->procedure)); } static void gimp_procedure_config_dispose (GObject *object) { GimpProcedureConfig *config = GIMP_PROCEDURE_CONFIG (object); g_clear_object (&config->priv->procedure); g_clear_object (&config->priv->metadata); g_clear_pointer (&config->priv->mime_type, g_free); G_OBJECT_CLASS (parent_class)->dispose (object); } static void gimp_procedure_config_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GimpProcedureConfig *config = GIMP_PROCEDURE_CONFIG (object); switch (property_id) { case PROP_PROCEDURE: config->priv->procedure = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gimp_procedure_config_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GimpProcedureConfig *config = GIMP_PROCEDURE_CONFIG (object); switch (property_id) { case PROP_PROCEDURE: g_value_set_object (value, config->priv->procedure); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } /* public functions */ /** * gimp_procedure_config_get_procedure: * @config: a #GimpProcedureConfig * * This function returns the #GimpProcedure which created @config, see * gimp_procedure_create_config(). * * Returns: (transfer none): The #GimpProcedure which created @config. * * Since: 3.0 **/ GimpProcedure * gimp_procedure_config_get_procedure (GimpProcedureConfig *config) { g_return_val_if_fail (GIMP_IS_PROCEDURE_CONFIG (config), NULL); return config->priv->procedure; } /** * gimp_procedure_config_set_values: * @config: a #GimpProcedureConfig * @values: a #GimpValueArray * * Sets the values from @values on @config's properties. * * The number, order and types of values in @values must match the * number, order and types of @config's properties. * * This function is meant to be used on @values which are passed as * arguments to the run() function of the #GimpProcedure which created * this @config. See gimp_procedure_create_config(). * * Since: 3.0 **/ void gimp_procedure_config_set_values (GimpProcedureConfig *config, const GimpValueArray *values) { GParamSpec **pspecs; guint n_pspecs; gint n_aux_args; gint n_values; gint i; g_return_if_fail (GIMP_IS_PROCEDURE_CONFIG (config)); g_return_if_fail (values != NULL); pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (config), &n_pspecs); gimp_procedure_get_aux_arguments (config->priv->procedure, &n_aux_args); n_values = gimp_value_array_length (values); /* The first property is the procedure, all others are arguments. */ g_return_if_fail (n_pspecs == n_values + n_aux_args + 1); for (i = 0; i < n_values; i++) { GParamSpec *pspec = pspecs[i + 1]; GValue *value = gimp_value_array_index (values, i); g_object_set_property (G_OBJECT (config), pspec->name, value); } g_free (pspecs); } /** * gimp_procedure_config_get_values: * @config: a #GimpProcedureConfig * @values: a #GimpValueArray * * Gets the values from @config's properties and stores them in * @values. * * See gimp_procedure_config_set_values(). * * Since: 3.0 **/ void gimp_procedure_config_get_values (GimpProcedureConfig *config, GimpValueArray *values) { GParamSpec **pspecs; guint n_pspecs; gint n_aux_args; gint n_values; gint i; g_return_if_fail (GIMP_IS_PROCEDURE_CONFIG (config)); g_return_if_fail (values != NULL); pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (config), &n_pspecs); gimp_procedure_get_aux_arguments (config->priv->procedure, &n_aux_args); n_values = gimp_value_array_length (values); g_return_if_fail (n_pspecs == n_values + n_aux_args); for (i = 0; i < n_values; i++) { GParamSpec *pspec = pspecs[i]; GValue *value = gimp_value_array_index (values, i); g_object_get_property (G_OBJECT (config), pspec->name, value); } g_free (pspecs); } static void gimp_procedure_config_get_parasite (GimpProcedureConfig *config, GParamSpec *pspec) { GimpParasite *parasite; gchar *value = NULL; /* for now we only support strings */ if (! config->priv->image || ! G_IS_PARAM_SPEC_STRING (pspec)) return; parasite = gimp_image_get_parasite (config->priv->image, pspec->name); if (parasite) { value = g_strndup (gimp_parasite_data (parasite), gimp_parasite_data_size (parasite)); gimp_parasite_free (parasite); if (value && ! strlen (value)) g_clear_pointer (&value, g_free); } if (! value) { /* special case "gimp-comment" here, yes this is bad hack */ if (! strcmp (pspec->name, "gimp-comment")) { value = gimp_get_default_comment (); } } if (value && strlen (value)) g_object_set (config, pspec->name, value, NULL); g_free (value); } static void gimp_procedure_config_set_parasite (GimpProcedureConfig *config, GParamSpec *pspec) { GimpParasite *parasite; gchar *value; /* for now we only support strings */ if (! config->priv->image || ! G_IS_PARAM_SPEC_STRING (pspec)) return; g_object_get (config, pspec->name, &value, NULL); parasite = gimp_image_get_parasite (config->priv->image, pspec->name); if (parasite) { /* it there is a parasite, always override it if its value was * changed */ gchar *image_value; image_value = g_strndup (gimp_parasite_data (parasite), gimp_parasite_data_size (parasite)); gimp_parasite_free (parasite); if (g_strcmp0 (value, image_value)) { if (value && strlen (value)) { parasite = gimp_parasite_new (pspec->name, GIMP_PARASITE_PERSISTENT, strlen (value) + 1, value); gimp_image_attach_parasite (config->priv->image, parasite); gimp_parasite_free (parasite); } else { gimp_image_detach_parasite (config->priv->image, pspec->name); } } g_free (image_value); } else { /* otherwise, set the parasite if the value was changed from * the default value */ gchar *default_value = NULL; /* special case "gimp-comment" here, yes this is bad hack */ if (! strcmp (pspec->name, "gimp-comment")) { default_value = gimp_get_default_comment (); } if (g_strcmp0 (value, default_value) && value && strlen (value)) { parasite = gimp_parasite_new (pspec->name, GIMP_PARASITE_PERSISTENT, strlen (value) + 1, value); gimp_image_attach_parasite (config->priv->image, parasite); gimp_parasite_free (parasite); } g_free (default_value); } g_free (value); } /** * gimp_procedure_config_begin_run: * @config: a #GimpProcedureConfig * @image: (nullable): a #GimpImage or %NULL * @run_mode: the #GimpRunMode passed to a #GimpProcedure's run() * @args: the #GimpValueArray passed to a #GimpProcedure's run() * * Populates @config with values for a #GimpProcedure's run(), * depending on @run_mode. * * If @run_mode is %GIMP_RUN_INTERACTIVE or %GIMP_RUN_WITH_LAST_VALS, * the saved values from the procedure's last run() are loaded and set * on @config. If @image is not %NULL, the last used values for this * image are tried first, and if no image-specific values are found * the globally saved last used values are used. If no saved last used * values are found, the procedure's default argument values are used. * * If @run_mode is %GIMP_RUN_NONINTERACTIVE, the contents of @args are * set on @config using gimp_procedure_config_set_values(). * * After setting @config's properties like described above, arguments * and auxiliary arguments are automatically synced from image * parasites of the same name if they were set to * %GIMP_ARGUMENT_SYNC_PARASITE with * gimp_procedure_set_argument_sync(): * * String properties are set to the value of the image parasite if * @run_mode is %GIMP_RUN_INTERACTIVE or %GIMP_RUN_WITH_LAST_VALS, or * if the corresponding argument is an auxiliary argument. As a * special case, a property named "gimp-comment" will default to * gimp_get_default_comment() if there is no "gimp-comment" parasite. * * After calling this function, the @args passed to run() should be * left alone and @config be treated as the procedure's arguments. * * It is possible to get @config's resulting values back into @args by * calling gimp_procedure_config_get_values(), as long as modified * @args are written back to @config using * gimp_procedure_config_set_values() before the call to * gimp_procedure_config_end_run(). * * This function should be used at the beginning of a procedure's * run() and be paired with a call to gimp_procedure_config_end_run() * at the end of run(). * * Since: 3.0 **/ void gimp_procedure_config_begin_run (GimpProcedureConfig *config, GimpImage *image, GimpRunMode run_mode, const GimpValueArray *args) { GParamSpec **pspecs; guint n_pspecs; gint i; gboolean loaded = FALSE; GError *error = NULL; g_return_if_fail (GIMP_IS_PROCEDURE_CONFIG (config)); g_return_if_fail (image == NULL || GIMP_IS_IMAGE (image)); g_return_if_fail (args != NULL); config->priv->image = image; config->priv->run_mode = run_mode; switch (run_mode) { case GIMP_RUN_INTERACTIVE : case GIMP_RUN_WITH_LAST_VALS: if (image) { loaded = gimp_procedure_config_load_parasite (config, image, &error); if (! loaded && error) { g_printerr ("Loading last used values from parasite failed: %s\n", error->message); g_clear_error (&error); } } if (! loaded && ! gimp_procedure_config_load_last (config, &error) && error) { g_printerr ("Loading last used values from disk failed: %s\n", error->message); g_clear_error (&error); } break; case GIMP_RUN_NONINTERACTIVE: gimp_procedure_config_set_values (config, args); break; } pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (config), &n_pspecs); for (i = 0; i < n_pspecs; i++) { GParamSpec *pspec = pspecs[i]; /* skip our own properties */ if (pspec->owner_type == GIMP_TYPE_PROCEDURE_CONFIG) continue; switch (gimp_procedure_get_argument_sync (config->priv->procedure, pspec->name)) { case GIMP_ARGUMENT_SYNC_PARASITE: /* we sync the property from the image parasite if it is an * aux argument, or if we run interactively, because the * parasite should be global to the image and not depend on * whatever parasite another image had when last using this * procedure */ if (gimp_procedure_find_aux_argument (config->priv->procedure, pspec->name) || (run_mode != GIMP_RUN_NONINTERACTIVE)) { gimp_procedure_config_get_parasite (config, pspec); } break; default: break; } } g_free (pspecs); } /** * gimp_procedure_config_end_run: * @config: a #GimpProcedureConfig * @status: the return status of the #GimpProcedure's run() * * This function is the counterpart of * gimp_procedure_config_begin_run() and must always be called in * pairs in a procedure's run(), before returning return values. * * If the @run_mode passed to gimp_procedure_config_end_run() was * %GIMP_RUN_INTERACTIVE, @config is saved as last used values to be * used when the procedure runs again. Additionally, if the #GimpImage * passed to gimp_procedure_config_begin_run() was not %NULL, @config is * attached to @image as last used values for this image using a * #GimpParasite and gimp_image_attach_parasite(). * * If @run_mode was not %GIMP_RUN_NONINTERACTIVE, this function also * conveniently calls gimp_displays_flush(), which is what most * procedures want and doesn't do any harm if called redundantly. * * After a %GIMP_RUN_INTERACTIVE run, %GIMP_ARGUMENT_SYNC_PARASITE * values that have been changed are written back to their * corresponding image parasite. * * See gimp_procedure_config_begin_run(). * * Since: 3.0 **/ void gimp_procedure_config_end_run (GimpProcedureConfig *config, GimpPDBStatusType status) { g_return_if_fail (GIMP_IS_PROCEDURE_CONFIG (config)); if (config->priv->run_mode != GIMP_RUN_NONINTERACTIVE) gimp_displays_flush (); if (status == GIMP_PDB_SUCCESS && config->priv->run_mode == GIMP_RUN_INTERACTIVE) { GParamSpec **pspecs; guint n_pspecs; gint i; GError *error = NULL; if (config->priv->image) gimp_procedure_config_save_parasite (config, config->priv->image, NULL); if (! gimp_procedure_config_save_last (config, &error)) { g_printerr ("Saving last used values to disk failed: %s\n", error->message); g_clear_error (&error); } pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (config), &n_pspecs); for (i = 0; i < n_pspecs; i++) { GParamSpec *pspec = pspecs[i]; /* skip our own properties */ if (pspec->owner_type == GIMP_TYPE_PROCEDURE_CONFIG) continue; switch (gimp_procedure_get_argument_sync (config->priv->procedure, pspec->name)) { case GIMP_ARGUMENT_SYNC_PARASITE: gimp_procedure_config_set_parasite (config, pspec); break; default: break; } } g_free (pspecs); } config->priv->image = NULL; config->priv->run_mode = -1; } /** * gimp_procedure_config_begin_export: * @config: a #GimpProcedureConfig * @original_image: the #GimpImage passed to run() * @run_mode: the #GimpRunMode passed to a #GimpProcedure's run() * @args: the #GimpValueArray passed to a #GimpProcedure's run() * @mime_type: (nullable): exported file format's mime type, or %NULL. * * This is a variant of gimp_procedure_config_begin_run() to be used * by file export procedures using #GimpSaveProcedure. It must be * paired with a call to gimp_procedure_config_end_export() at the end * of run(). * * It does everything gimp_procedure_config_begin_run() does but * provides additional features to automate file export: * * If @mime_type is non-%NULL, exporting metadata is handled * automatically, by calling gimp_image_metadata_save_prepare() and * syncing its returned #GimpMetadataSaveFlags with @config's * properties. (The corresponding gimp_image_metadata_save_finish() * will be called by gimp_procedure_config_end_export()). * * The following boolean arguments of the used #GimpSaveProcedure are * synced. The procedure can but must not provide these arguments. * * "save-exif" for %GIMP_METADATA_SAVE_EXIF. * * "save-xmp" for %GIMP_METADATA_SAVE_XMP. * * "save-iptc" for %GIMP_METADATA_SAVE_IPTC. * * "save-thumbnail" for %GIMP_METADATA_SAVE_THUMBNAIL. * * "save-color-profile" for %GIMP_METADATA_SAVE_COLOR_PROFILE. * * "save-comment" for %GIMP_METADATA_SAVE_COMMENT. * * The values from the #GimpMetadataSaveFlags will only ever be used * to set these properties to %FALSE, overriding the user's saved * default values for the procedure, but NOT overriding the last used * values from exporting @original_image or the last used values from * exporting any other image using this procedure. * * If @mime_type is %NULL, #GimpMetadata handling is skipped. The * procedure can still have all of the above listed boolean arguments, * but must take care of their default values itself. The easiest way * to do this is by simply using gimp_export_comment(), * gimp_export_exif() etc. as default values for these arguments when * adding them using GIMP_PROC_ARG_BOOLEAN() or * GIMP_PROC_AUX_ARG_BOOLEAN(). * * Returns: (transfer none) (nullable): The #GimpMetadata to be used * for this export, or %NULL if @original_image doesn't have * metadata. * * Since: 3.0 **/ GimpMetadata * gimp_procedure_config_begin_export (GimpProcedureConfig *config, GimpImage *original_image, GimpRunMode run_mode, const GimpValueArray *args, const gchar *mime_type) { GObjectClass *object_class; g_return_val_if_fail (GIMP_IS_PROCEDURE_CONFIG (config), NULL); g_return_val_if_fail (GIMP_IS_IMAGE (original_image), NULL); g_return_val_if_fail (args != NULL, NULL); object_class = G_OBJECT_GET_CLASS (config); if (mime_type) { GimpMetadataSaveFlags metadata_flags; gint i; config->priv->metadata = gimp_image_metadata_save_prepare (original_image, mime_type, &metadata_flags); if (config->priv->metadata) { config->priv->mime_type = g_strdup (mime_type); config->priv->metadata_flags = metadata_flags; } for (i = 0; i < G_N_ELEMENTS (metadata_properties); i++) { /* we only disable properties based on metadata flags here * and never enable them, so we don't override the user's * saved default values that are passed to us via "args" */ if (! (metadata_flags & metadata_properties[i].flag)) { const gchar *prop_name = metadata_properties[i].name; GParamSpec *pspec; pspec = g_object_class_find_property (object_class, prop_name); if (pspec) g_object_set (config, prop_name, FALSE, NULL); } } } gimp_procedure_config_begin_run (config, original_image, run_mode, args); return config->priv->metadata; } /** * gimp_procedure_config_end_export: * @config: a #GimpProcedureConfig * @exported_image: the #GimpImage that was actually exported * @file: the #GFile @exported_image was written to * @status: the return status of the #GimpProcedure's run() * * This is a variant of gimp_procedure_config_end_run() to be used by * file export procedures using #GimpSaveProcedure. It must be paired * with a call to gimp_procedure_config_begin_export() at the * beginning of run(). * * It does everything gimp_procedure_config_begin_run() does but * provides additional features to automate file export: * * If @status is %GIMP_PDB_SUCCESS, and * gimp_procedure_config_begin_export() returned a #GimpMetadata, this * function calls gimp_procedure_config_save_metadata(), which syncs * back @config's export properties to the metadata's * #GimpMetadataSaveFlags and writes metadata to @file using * gimp_image_metadata_save_finish(). * * Since: 3.0 **/ void gimp_procedure_config_end_export (GimpProcedureConfig *config, GimpImage *exported_image, GFile *file, GimpPDBStatusType status) { g_return_if_fail (GIMP_IS_PROCEDURE_CONFIG (config)); g_return_if_fail (GIMP_IS_IMAGE (exported_image)); g_return_if_fail (G_IS_FILE (file)); if (status == GIMP_PDB_SUCCESS) { gimp_procedure_config_save_metadata (config, exported_image, file); } g_clear_object (&config->priv->metadata); g_clear_pointer (&config->priv->mime_type, g_free); config->priv->metadata_flags = 0; config->priv->metadata_saved = FALSE; gimp_procedure_config_end_run (config, status); } /** * gimp_procedure_config_save_metadata: * @config: a #GimpProcedureConfig * @exported_image: the #GimpImage that was actually exported * @file: the #GFile @exported_image was written to * * Note: There is normally no need to call this function because it's * already called from gimp_procedure_config_end_export(). * * Only use this function if the #GimpMetadata returned by * gimp_procedure_config_begin_run() needs to be written at a specific * point of the export, other than its end. * * This function syncs back @config's export properties to the * metadata's #GimpMetadataSaveFlags and writes the metadata to @file * using gimp_image_metadata_save_finish(). * * The metadata is only ever written once. If this function has been * called explicitly, it will do nothing when called a second time * from gimp_procedure_config_end_export(). * * Since: 3.0 **/ void gimp_procedure_config_save_metadata (GimpProcedureConfig *config, GimpImage *exported_image, GFile *file) { g_return_if_fail (GIMP_IS_PROCEDURE_CONFIG (config)); g_return_if_fail (GIMP_IS_IMAGE (exported_image)); g_return_if_fail (G_IS_FILE (file)); if (config->priv->metadata && ! config->priv->metadata_saved) { GObjectClass *object_class = G_OBJECT_GET_CLASS (config); gint i; for (i = 0; i < G_N_ELEMENTS (metadata_properties); i++) { const gchar *prop_name = metadata_properties[i].name; GimpMetadataSaveFlags prop_flag = metadata_properties[i].flag; GParamSpec *pspec; gboolean value; pspec = g_object_class_find_property (object_class, prop_name); if (pspec) { g_object_get (config, prop_name, &value, NULL); if (value) config->priv->metadata_flags |= prop_flag; else config->priv->metadata_flags &= ~prop_flag; } } gimp_image_metadata_save_finish (exported_image, config->priv->mime_type, config->priv->metadata, config->priv->metadata_flags, file, NULL); config->priv->metadata_saved = TRUE; } } /* private functions */ static GFile * gimp_procedure_config_get_file (GimpProcedureConfig *config, const gchar *extension) { GFile *file; gchar *basename; basename = g_strconcat (G_OBJECT_TYPE_NAME (config), extension, NULL); file = gimp_directory_file ("plug-in-settings", basename, NULL); g_free (basename); return file; } gboolean gimp_procedure_config_load_default (GimpProcedureConfig *config, GError **error) { GFile *file; gboolean success; g_return_val_if_fail (GIMP_IS_PROCEDURE_CONFIG (config), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); file = gimp_procedure_config_get_file (config, ".default"); success = gimp_config_deserialize_file (GIMP_CONFIG (config), file, NULL, error); if (! success && error && (*error)->code == GIMP_CONFIG_ERROR_OPEN_ENOENT) { g_clear_error (error); } g_object_unref (file); return success; } gboolean gimp_procedure_config_save_default (GimpProcedureConfig *config, GError **error) { GFile *file; gboolean success; g_return_val_if_fail (GIMP_IS_PROCEDURE_CONFIG (config), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); file = gimp_procedure_config_get_file (config, ".default"); success = gimp_config_serialize_to_file (GIMP_CONFIG (config), file, "settings", "end of settings", NULL, error); g_object_unref (file); return success; } gboolean gimp_procedure_config_load_last (GimpProcedureConfig *config, GError **error) { GFile *file; gboolean success; g_return_val_if_fail (GIMP_IS_PROCEDURE_CONFIG (config), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); file = gimp_procedure_config_get_file (config, ".last"); success = gimp_config_deserialize_file (GIMP_CONFIG (config), file, NULL, error); if (! success && (*error)->code == GIMP_CONFIG_ERROR_OPEN_ENOENT) { g_clear_error (error); } g_object_unref (file); return success; } gboolean gimp_procedure_config_save_last (GimpProcedureConfig *config, GError **error) { GFile *file; gboolean success; g_return_val_if_fail (GIMP_IS_PROCEDURE_CONFIG (config), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); file = gimp_procedure_config_get_file (config, ".last"); success = gimp_config_serialize_to_file (GIMP_CONFIG (config), file, "settings", "end of settings", NULL, error); g_object_unref (file); return success; } static gchar * gimp_procedure_config_parasite_name (GimpProcedureConfig *config, const gchar *suffix) { return g_strconcat (G_OBJECT_TYPE_NAME (config), suffix, NULL); } gboolean gimp_procedure_config_load_parasite (GimpProcedureConfig *config, GimpImage *image, GError **error) { gchar *name; GimpParasite *parasite; gboolean success; g_return_val_if_fail (GIMP_IS_PROCEDURE_CONFIG (config), FALSE); g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); name = gimp_procedure_config_parasite_name (config, "-last"); parasite = gimp_image_get_parasite (image, name); g_free (name); if (! parasite) return FALSE; success = gimp_config_deserialize_parasite (GIMP_CONFIG (config), parasite, NULL, error); gimp_parasite_free (parasite); return success; } gboolean gimp_procedure_config_save_parasite (GimpProcedureConfig *config, GimpImage *image, GError **error) { gchar *name; GimpParasite *parasite; g_return_val_if_fail (GIMP_IS_PROCEDURE_CONFIG (config), FALSE); g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); name = gimp_procedure_config_parasite_name (config, "-last"); parasite = gimp_config_serialize_to_parasite (GIMP_CONFIG (config), name, GIMP_PARASITE_PERSISTENT, NULL); g_free (name); if (! parasite) return FALSE; gimp_image_attach_parasite (image, parasite); gimp_parasite_free (parasite); return TRUE; }