1040 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1040 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* 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 Spencer Kimball and Peter Mattis
 | |
|  *
 | |
|  * 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 <https://www.gnu.org/licenses/>.
 | |
|  */
 | |
| 
 | |
| #include "config.h"
 | |
| 
 | |
| #include <string.h>
 | |
| 
 | |
| #include <gegl.h>
 | |
| #include <gtk/gtk.h>
 | |
| 
 | |
| #include "libpikabase/pikabase.h"
 | |
| #include "libpikaconfig/pikaconfig.h"
 | |
| #include "libpikawidgets/pikawidgets.h"
 | |
| 
 | |
| #include "actions-types.h"
 | |
| 
 | |
| #include "config/pikadialogconfig.h"
 | |
| 
 | |
| #include "core/pika.h"
 | |
| #include "core/pika-utils.h"
 | |
| #include "core/pikachannel.h"
 | |
| #include "core/pikacontext.h"
 | |
| #include "core/pikaimage.h"
 | |
| #include "core/pikaimage-merge.h"
 | |
| #include "core/pikaimage-undo.h"
 | |
| #include "core/pikaparamspecs.h"
 | |
| #include "core/pikaprogress.h"
 | |
| #include "core/pikatoolinfo.h"
 | |
| 
 | |
| #include "pdb/pikapdb.h"
 | |
| #include "pdb/pikaprocedure.h"
 | |
| 
 | |
| #include "vectors/pikavectors.h"
 | |
| #include "vectors/pikavectors-export.h"
 | |
| #include "vectors/pikavectors-import.h"
 | |
| 
 | |
| #include "widgets/pikaaction.h"
 | |
| #include "widgets/pikaclipboard.h"
 | |
| #include "widgets/pikahelp-ids.h"
 | |
| 
 | |
| #include "display/pikadisplay.h"
 | |
| 
 | |
| #include "tools/pikavectortool.h"
 | |
| #include "tools/tool_manager.h"
 | |
| 
 | |
| #include "dialogs/dialogs.h"
 | |
| #include "dialogs/vectors-export-dialog.h"
 | |
| #include "dialogs/vectors-import-dialog.h"
 | |
| #include "dialogs/vectors-options-dialog.h"
 | |
| 
 | |
| #include "actions.h"
 | |
| #include "items-commands.h"
 | |
| #include "vectors-commands.h"
 | |
| 
 | |
| #include "pika-intl.h"
 | |
| 
 | |
| 
 | |
| /*  local function prototypes  */
 | |
| 
 | |
| static void   vectors_new_callback             (GtkWidget    *dialog,
 | |
|                                                 PikaImage    *image,
 | |
|                                                 PikaVectors  *vectors,
 | |
|                                                 PikaContext  *context,
 | |
|                                                 const gchar  *vectors_name,
 | |
|                                                 gboolean      vectors_visible,
 | |
|                                                 PikaColorTag  vectors_color_tag,
 | |
|                                                 gboolean      vectors_lock_content,
 | |
|                                                 gboolean      vectors_lock_position,
 | |
|                                                 gboolean      vectors_lock_visibility,
 | |
|                                                 gpointer      user_data);
 | |
| static void   vectors_edit_attributes_callback (GtkWidget    *dialog,
 | |
|                                                 PikaImage    *image,
 | |
|                                                 PikaVectors  *vectors,
 | |
|                                                 PikaContext  *context,
 | |
|                                                 const gchar  *vectors_name,
 | |
|                                                 gboolean      vectors_visible,
 | |
|                                                 PikaColorTag  vectors_color_tag,
 | |
|                                                 gboolean      vectors_lock_content,
 | |
|                                                 gboolean      vectors_lock_position,
 | |
|                                                 gboolean      vectors_lock_visibility,
 | |
|                                                 gpointer      user_data);
 | |
| static void   vectors_import_callback          (GtkWidget    *dialog,
 | |
|                                                 PikaImage    *image,
 | |
|                                                 GFile        *file,
 | |
|                                                 GFile        *import_folder,
 | |
|                                                 gboolean      merge_vectors,
 | |
|                                                 gboolean      scale_vectors,
 | |
|                                                 gpointer      user_data);
 | |
| static void   vectors_export_callback          (GtkWidget    *dialog,
 | |
|                                                 PikaImage    *image,
 | |
|                                                 GFile        *file,
 | |
|                                                 GFile        *export_folder,
 | |
|                                                 gboolean      active_only,
 | |
|                                                 gpointer      user_data);
 | |
| 
 | |
| 
 | |
| /*  public functions  */
 | |
| 
 | |
| void
 | |
| vectors_edit_cmd_callback (PikaAction *action,
 | |
|                            GVariant   *value,
 | |
|                            gpointer    data)
 | |
| {
 | |
|   PikaImage *image;
 | |
|   GList     *vectors;
 | |
|   PikaTool  *active_tool;
 | |
|   return_if_no_vectors_list (image, vectors, data);
 | |
| 
 | |
|   if (g_list_length (vectors) != 1)
 | |
|     return;
 | |
| 
 | |
|   active_tool = tool_manager_get_active (image->pika);
 | |
| 
 | |
|   if (! PIKA_IS_VECTOR_TOOL (active_tool))
 | |
|     {
 | |
|       PikaToolInfo  *tool_info = pika_get_tool_info (image->pika,
 | |
|                                                      "pika-vector-tool");
 | |
| 
 | |
|       if (PIKA_IS_TOOL_INFO (tool_info))
 | |
|         {
 | |
|           pika_context_set_tool (action_data_get_context (data), tool_info);
 | |
|           active_tool = tool_manager_get_active (image->pika);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   if (PIKA_IS_VECTOR_TOOL (active_tool))
 | |
|     pika_vector_tool_set_vectors (PIKA_VECTOR_TOOL (active_tool), vectors->data);
 | |
| }
 | |
| 
 | |
| void
 | |
| vectors_edit_attributes_cmd_callback (PikaAction *action,
 | |
|                                       GVariant   *value,
 | |
|                                       gpointer    data)
 | |
| {
 | |
|   PikaImage   *image;
 | |
|   GList       *paths;
 | |
|   PikaVectors *vectors;
 | |
|   GtkWidget   *widget;
 | |
|   GtkWidget   *dialog;
 | |
|   return_if_no_vectors_list (image, paths, data);
 | |
|   return_if_no_widget (widget, data);
 | |
| 
 | |
|   if (g_list_length (paths) != 1)
 | |
|     return;
 | |
| 
 | |
|   vectors = paths->data;
 | |
| 
 | |
| #define EDIT_DIALOG_KEY "pika-vectors-edit-attributes-dialog"
 | |
| 
 | |
|   dialog = dialogs_get_dialog (G_OBJECT (vectors), EDIT_DIALOG_KEY);
 | |
| 
 | |
|   if (! dialog)
 | |
|     {
 | |
|       PikaItem *item = PIKA_ITEM (vectors);
 | |
| 
 | |
|       dialog = vectors_options_dialog_new (image, vectors,
 | |
|                                            action_data_get_context (data),
 | |
|                                            widget,
 | |
|                                            _("Path Attributes"),
 | |
|                                            "pika-vectors-edit",
 | |
|                                            PIKA_ICON_EDIT,
 | |
|                                            _("Edit Path Attributes"),
 | |
|                                            PIKA_HELP_PATH_EDIT,
 | |
|                                            pika_object_get_name (vectors),
 | |
|                                            pika_item_get_visible (item),
 | |
|                                            pika_item_get_color_tag (item),
 | |
|                                            pika_item_get_lock_content (item),
 | |
|                                            pika_item_get_lock_position (item),
 | |
|                                            pika_item_get_lock_visibility (item),
 | |
|                                            vectors_edit_attributes_callback,
 | |
|                                            NULL);
 | |
| 
 | |
|       dialogs_attach_dialog (G_OBJECT (vectors), EDIT_DIALOG_KEY, dialog);
 | |
|     }
 | |
| 
 | |
|   gtk_window_present (GTK_WINDOW (dialog));
 | |
| }
 | |
| 
 | |
| void
 | |
| vectors_new_cmd_callback (PikaAction *action,
 | |
|                           GVariant   *value,
 | |
|                           gpointer    data)
 | |
| {
 | |
|   PikaImage *image;
 | |
|   GtkWidget *widget;
 | |
|   GtkWidget *dialog;
 | |
|   return_if_no_image (image, data);
 | |
|   return_if_no_widget (widget, data);
 | |
| 
 | |
| #define NEW_DIALOG_KEY "pika-vectors-new-dialog"
 | |
| 
 | |
|   dialog = dialogs_get_dialog (G_OBJECT (image), NEW_DIALOG_KEY);
 | |
| 
 | |
|   if (! dialog)
 | |
|     {
 | |
|       PikaDialogConfig *config = PIKA_DIALOG_CONFIG (image->pika->config);
 | |
| 
 | |
|       dialog = vectors_options_dialog_new (image, NULL,
 | |
|                                            action_data_get_context (data),
 | |
|                                            widget,
 | |
|                                            _("New Path"),
 | |
|                                            "pika-vectors-new",
 | |
|                                            PIKA_ICON_PATH,
 | |
|                                            _("Create a New Path"),
 | |
|                                            PIKA_HELP_PATH_NEW,
 | |
|                                            config->vectors_new_name,
 | |
|                                            FALSE,
 | |
|                                            PIKA_COLOR_TAG_NONE,
 | |
|                                            FALSE,
 | |
|                                            FALSE,
 | |
|                                            FALSE,
 | |
|                                            vectors_new_callback,
 | |
|                                            NULL);
 | |
| 
 | |
|       dialogs_attach_dialog (G_OBJECT (image), NEW_DIALOG_KEY, dialog);
 | |
|     }
 | |
| 
 | |
|   gtk_window_present (GTK_WINDOW (dialog));
 | |
| }
 | |
| 
 | |
| void
 | |
| vectors_new_last_vals_cmd_callback (PikaAction *action,
 | |
|                                     GVariant   *value,
 | |
|                                     gpointer    data)
 | |
| {
 | |
|   PikaImage        *image;
 | |
|   PikaVectors      *vectors;
 | |
|   PikaDialogConfig *config;
 | |
|   return_if_no_image (image, data);
 | |
| 
 | |
|   config = PIKA_DIALOG_CONFIG (image->pika->config);
 | |
| 
 | |
|   vectors = pika_vectors_new (image, config->vectors_new_name);
 | |
|   pika_image_add_vectors (image, vectors,
 | |
|                           PIKA_IMAGE_ACTIVE_PARENT, -1, TRUE);
 | |
|   pika_image_flush (image);
 | |
| }
 | |
| 
 | |
| void
 | |
| vectors_raise_cmd_callback (PikaAction *action,
 | |
|                             GVariant   *value,
 | |
|                             gpointer    data)
 | |
| {
 | |
|   PikaImage *image;
 | |
|   GList     *list;
 | |
|   GList     *iter;
 | |
|   GList     *moved_list = NULL;
 | |
|   return_if_no_vectors_list (image, list, data);
 | |
| 
 | |
|   for (iter = list; iter; iter = iter->next)
 | |
|     {
 | |
|       gint index;
 | |
| 
 | |
|       index = pika_item_get_index (iter->data);
 | |
|       if (index > 0)
 | |
|         moved_list = g_list_prepend (moved_list, iter->data);
 | |
|     }
 | |
| 
 | |
|   if (moved_list)
 | |
|     {
 | |
|       pika_image_undo_group_start (image,
 | |
|                                    PIKA_UNDO_GROUP_ITEM_DISPLACE,
 | |
|                                    ngettext ("Raise Path",
 | |
|                                              "Raise Paths",
 | |
|                                              g_list_length (moved_list)));
 | |
|       for (iter = moved_list; iter; iter = iter->next)
 | |
|         pika_image_raise_item (image, PIKA_ITEM (iter->data), NULL);
 | |
| 
 | |
|       pika_image_flush (image);
 | |
|       pika_image_undo_group_end (image);
 | |
|       g_list_free (moved_list);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void
 | |
| vectors_raise_to_top_cmd_callback (PikaAction *action,
 | |
|                                    GVariant   *value,
 | |
|                                    gpointer    data)
 | |
| {
 | |
|   PikaImage *image;
 | |
|   GList     *list;
 | |
|   GList     *iter;
 | |
|   GList     *moved_list = NULL;
 | |
|   return_if_no_vectors_list (image, list, data);
 | |
| 
 | |
|   for (iter = list; iter; iter = iter->next)
 | |
|     {
 | |
|       gint index;
 | |
| 
 | |
|       index = pika_item_get_index (iter->data);
 | |
|       if (index > 0)
 | |
|         moved_list = g_list_prepend (moved_list, iter->data);
 | |
|     }
 | |
| 
 | |
|   if (moved_list)
 | |
|     {
 | |
|       pika_image_undo_group_start (image,
 | |
|                                    PIKA_UNDO_GROUP_ITEM_DISPLACE,
 | |
|                                    ngettext ("Raise Path to Top",
 | |
|                                              "Raise Paths to Top",
 | |
|                                              g_list_length (moved_list)));
 | |
| 
 | |
|       for (iter = moved_list; iter; iter = iter->next)
 | |
|         pika_image_raise_item_to_top (image, PIKA_ITEM (iter->data));
 | |
| 
 | |
|       pika_image_flush (image);
 | |
|       pika_image_undo_group_end (image);
 | |
|       g_list_free (moved_list);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void
 | |
| vectors_lower_cmd_callback (PikaAction *action,
 | |
|                             GVariant   *value,
 | |
|                             gpointer    data)
 | |
| {
 | |
|   PikaImage *image;
 | |
|   GList     *list;
 | |
|   GList     *iter;
 | |
|   GList     *moved_list = NULL;
 | |
|   return_if_no_vectors_list (image, list, data);
 | |
| 
 | |
|   for (iter = list; iter; iter = iter->next)
 | |
|     {
 | |
|       GList *vectors_list;
 | |
|       gint   index;
 | |
| 
 | |
|       vectors_list = pika_item_get_container_iter (PIKA_ITEM (iter->data));
 | |
|       index = pika_item_get_index (iter->data);
 | |
|       if (index < g_list_length (vectors_list) - 1)
 | |
|         moved_list = g_list_prepend (moved_list, iter->data);
 | |
|     }
 | |
| 
 | |
|   if (moved_list)
 | |
|     {
 | |
|       moved_list = g_list_reverse (moved_list);
 | |
|       pika_image_undo_group_start (image,
 | |
|                                    PIKA_UNDO_GROUP_ITEM_DISPLACE,
 | |
|                                    ngettext ("Lower Path",
 | |
|                                              "Lower Paths",
 | |
|                                              g_list_length (moved_list)));
 | |
| 
 | |
|       for (iter = moved_list; iter; iter = iter->next)
 | |
|         pika_image_lower_item (image, PIKA_ITEM (iter->data), NULL);
 | |
| 
 | |
|       pika_image_flush (image);
 | |
|       pika_image_undo_group_end (image);
 | |
|       g_list_free (moved_list);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void
 | |
| vectors_lower_to_bottom_cmd_callback (PikaAction *action,
 | |
|                                       GVariant   *value,
 | |
|                                       gpointer    data)
 | |
| {
 | |
|   PikaImage *image;
 | |
|   GList     *list;
 | |
|   GList     *iter;
 | |
|   GList     *moved_list = NULL;
 | |
|   return_if_no_vectors_list (image, list, data);
 | |
| 
 | |
|   for (iter = list; iter; iter = iter->next)
 | |
|     {
 | |
|       GList *vectors_list;
 | |
|       gint   index;
 | |
| 
 | |
|       vectors_list = pika_item_get_container_iter (PIKA_ITEM (iter->data));
 | |
|       index = pika_item_get_index (iter->data);
 | |
|       if (index < g_list_length (vectors_list) - 1)
 | |
|         moved_list = g_list_prepend (moved_list, iter->data);
 | |
|     }
 | |
| 
 | |
|   if (moved_list)
 | |
|     {
 | |
|       moved_list = g_list_reverse (moved_list);
 | |
|       pika_image_undo_group_start (image,
 | |
|                                    PIKA_UNDO_GROUP_ITEM_DISPLACE,
 | |
|                                    ngettext ("Lower Path to Bottom",
 | |
|                                              "Lower Paths to Bottom",
 | |
|                                              g_list_length (moved_list)));
 | |
| 
 | |
|       for (iter = moved_list; iter; iter = iter->next)
 | |
|         pika_image_lower_item_to_bottom (image, PIKA_ITEM (iter->data));
 | |
| 
 | |
|       pika_image_flush (image);
 | |
|       pika_image_undo_group_end (image);
 | |
|       g_list_free (moved_list);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void
 | |
| vectors_duplicate_cmd_callback (PikaAction *action,
 | |
|                                 GVariant   *value,
 | |
|                                 gpointer    data)
 | |
| {
 | |
|   PikaImage *image;
 | |
|   GList     *paths;
 | |
|   GList     *new_paths = NULL;
 | |
|   GList     *iter;
 | |
|   return_if_no_vectors_list (image, paths, data);
 | |
| 
 | |
|   paths = g_list_copy (paths);
 | |
|   paths = g_list_reverse (paths);
 | |
| 
 | |
|   /* TODO: proper undo group. */
 | |
|   pika_image_undo_group_start (image,
 | |
|                                PIKA_UNDO_GROUP_VECTORS_IMPORT,
 | |
|                                _("Duplicate Paths"));
 | |
|   for (iter = paths; iter; iter = iter->next)
 | |
|     {
 | |
|       PikaVectors *new_vectors;
 | |
| 
 | |
|       new_vectors = PIKA_VECTORS (pika_item_duplicate (iter->data,
 | |
|                                                        G_TYPE_FROM_INSTANCE (iter->data)));
 | |
|       /*  use the actual parent here, not PIKA_IMAGE_ACTIVE_PARENT because
 | |
|        *  the latter would add a duplicated group inside itself instead of
 | |
|        *  above it
 | |
|        */
 | |
|       pika_image_add_vectors (image, new_vectors,
 | |
|                               pika_vectors_get_parent (iter->data), -1,
 | |
|                               TRUE);
 | |
|       new_paths = g_list_prepend (new_paths, new_vectors);
 | |
|     }
 | |
|   if (new_paths)
 | |
|     {
 | |
|       pika_image_set_selected_vectors (image, new_paths);
 | |
|       pika_image_flush (image);
 | |
|     }
 | |
|   pika_image_undo_group_end (image);
 | |
|   g_list_free (paths);
 | |
| }
 | |
| 
 | |
| void
 | |
| vectors_delete_cmd_callback (PikaAction *action,
 | |
|                              GVariant   *value,
 | |
|                              gpointer    data)
 | |
| {
 | |
|   PikaImage *image;
 | |
|   GList     *paths;
 | |
|   return_if_no_vectors_list (image, paths, data);
 | |
| 
 | |
|   paths = g_list_copy (paths);
 | |
|   /* TODO: proper undo group. */
 | |
|   pika_image_undo_group_start (image,
 | |
|                                PIKA_UNDO_GROUP_VECTORS_IMPORT,
 | |
|                                _("Remove Paths"));
 | |
| 
 | |
|   for (GList *iter = paths; iter; iter = iter->next)
 | |
|     pika_image_remove_vectors (image, iter->data, TRUE, NULL);
 | |
| 
 | |
|   pika_image_undo_group_end (image);
 | |
|   pika_image_flush (image);
 | |
|   g_list_free (paths);
 | |
| }
 | |
| 
 | |
| void
 | |
| vectors_merge_visible_cmd_callback (PikaAction *action,
 | |
|                                     GVariant   *value,
 | |
|                                     gpointer    data)
 | |
| {
 | |
|   PikaImage   *image;
 | |
|   GList       *vectors;
 | |
|   GtkWidget   *widget;
 | |
|   GError      *error = NULL;
 | |
|   return_if_no_vectors_list (image, vectors, data);
 | |
|   return_if_no_widget (widget, data);
 | |
| 
 | |
|   if (! pika_image_merge_visible_vectors (image, &error))
 | |
|     {
 | |
|       pika_message_literal (image->pika,
 | |
|                             G_OBJECT (widget), PIKA_MESSAGE_WARNING,
 | |
|                             error->message);
 | |
|       g_clear_error (&error);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|   pika_image_flush (image);
 | |
| }
 | |
| 
 | |
| void
 | |
| vectors_to_selection_cmd_callback (PikaAction *action,
 | |
|                                    GVariant   *value,
 | |
|                                    gpointer    data)
 | |
| {
 | |
|   PikaImage      *image;
 | |
|   GList          *vectors;
 | |
|   GList          *iter;
 | |
|   PikaChannelOps  operation;
 | |
|   return_if_no_vectors_list (image, vectors, data);
 | |
| 
 | |
|   pika_image_undo_group_start (image,
 | |
|                                PIKA_UNDO_GROUP_DRAWABLE_MOD,
 | |
|                                _("Paths to selection"));
 | |
| 
 | |
|   operation = (PikaChannelOps) g_variant_get_int32 (value);
 | |
| 
 | |
|   for (iter = vectors; iter; iter = iter->next)
 | |
|     {
 | |
|       pika_item_to_selection (iter->data, operation, TRUE, FALSE, 0, 0);
 | |
| 
 | |
|       if (operation == PIKA_CHANNEL_OP_REPLACE && iter == vectors)
 | |
|         operation = PIKA_CHANNEL_OP_ADD;
 | |
|     }
 | |
| 
 | |
|   pika_image_undo_group_end (image);
 | |
|   pika_image_flush (image);
 | |
| }
 | |
| 
 | |
| void
 | |
| vectors_selection_to_vectors_cmd_callback (PikaAction *action,
 | |
|                                            GVariant   *value,
 | |
|                                            gpointer    data)
 | |
| {
 | |
|   PikaImage      *image;
 | |
|   GtkWidget      *widget;
 | |
|   PikaProcedure  *procedure;
 | |
|   PikaValueArray *args;
 | |
|   PikaDisplay    *display;
 | |
|   gboolean        advanced;
 | |
|   GError         *error = NULL;
 | |
|   return_if_no_image (image, data);
 | |
|   return_if_no_widget (widget, data);
 | |
| 
 | |
|   advanced = (gboolean) g_variant_get_int32 (value);
 | |
| 
 | |
|   procedure = pika_pdb_lookup_procedure (image->pika->pdb,
 | |
|                                          "plug-in-sel2path");
 | |
| 
 | |
|   if (! procedure)
 | |
|     {
 | |
|       pika_message_literal (image->pika,
 | |
|                             G_OBJECT (widget), PIKA_MESSAGE_ERROR,
 | |
|                             "Selection to path procedure lookup failed.");
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|   display = pika_context_get_display (action_data_get_context (data));
 | |
| 
 | |
|   args = pika_procedure_get_arguments (procedure);
 | |
| 
 | |
|   g_value_set_enum   (pika_value_array_index (args, 0),
 | |
|                       advanced ?
 | |
|                       PIKA_RUN_INTERACTIVE : PIKA_RUN_NONINTERACTIVE);
 | |
|   g_value_set_object (pika_value_array_index (args, 1),
 | |
|                       image);
 | |
| 
 | |
|   pika_procedure_execute_async (procedure, image->pika,
 | |
|                                 action_data_get_context (data),
 | |
|                                 PIKA_PROGRESS (display), args,
 | |
|                                 display, &error);
 | |
| 
 | |
|   pika_value_array_unref (args);
 | |
| 
 | |
|   if (error)
 | |
|     {
 | |
|       pika_message_literal (image->pika,
 | |
|                             G_OBJECT (widget), PIKA_MESSAGE_ERROR,
 | |
|                             error->message);
 | |
|       g_error_free (error);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void
 | |
| vectors_fill_cmd_callback (PikaAction *action,
 | |
|                            GVariant   *value,
 | |
|                            gpointer    data)
 | |
| {
 | |
|   PikaImage *image;
 | |
|   GList     *vectors;
 | |
|   return_if_no_vectors_list (image, vectors, data);
 | |
| 
 | |
|   items_fill_cmd_callback (action, image, vectors,
 | |
|                            _("Fill Path"),
 | |
|                            PIKA_ICON_TOOL_BUCKET_FILL,
 | |
|                            PIKA_HELP_PATH_FILL,
 | |
|                            data);
 | |
| }
 | |
| 
 | |
| void
 | |
| vectors_fill_last_vals_cmd_callback (PikaAction *action,
 | |
|                                      GVariant   *value,
 | |
|                                      gpointer    data)
 | |
| {
 | |
|   PikaImage *image;
 | |
|   GList     *vectors;
 | |
|   return_if_no_vectors_list (image, vectors, data);
 | |
| 
 | |
|   items_fill_last_vals_cmd_callback (action, image, vectors, data);
 | |
| }
 | |
| 
 | |
| void
 | |
| vectors_stroke_cmd_callback (PikaAction *action,
 | |
|                              GVariant   *value,
 | |
|                              gpointer    data)
 | |
| {
 | |
|   PikaImage *image;
 | |
|   GList     *vectors;
 | |
|   return_if_no_vectors_list (image, vectors, data);
 | |
| 
 | |
|   items_stroke_cmd_callback (action, image, vectors,
 | |
|                              _("Stroke Path"),
 | |
|                              PIKA_ICON_PATH_STROKE,
 | |
|                              PIKA_HELP_PATH_STROKE,
 | |
|                              data);
 | |
| }
 | |
| 
 | |
| void
 | |
| vectors_stroke_last_vals_cmd_callback (PikaAction *action,
 | |
|                                        GVariant   *value,
 | |
|                                        gpointer    data)
 | |
| {
 | |
|   PikaImage *image;
 | |
|   GList     *vectors;
 | |
|   return_if_no_vectors_list (image, vectors, data);
 | |
| 
 | |
|   items_stroke_last_vals_cmd_callback (action, image, vectors, data);
 | |
| }
 | |
| 
 | |
| void
 | |
| vectors_copy_cmd_callback (PikaAction *action,
 | |
|                            GVariant   *value,
 | |
|                            gpointer    data)
 | |
| {
 | |
|   PikaImage   *image;
 | |
|   GList       *vectors;
 | |
|   gchar       *svg;
 | |
|   return_if_no_vectors_list (image, vectors, data);
 | |
| 
 | |
|   svg = pika_vectors_export_string (image, vectors);
 | |
| 
 | |
|   if (svg)
 | |
|     {
 | |
|       pika_clipboard_set_svg (image->pika, svg);
 | |
|       g_free (svg);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void
 | |
| vectors_paste_cmd_callback (PikaAction *action,
 | |
|                             GVariant   *value,
 | |
|                             gpointer    data)
 | |
| {
 | |
|   PikaImage *image;
 | |
|   GtkWidget *widget;
 | |
|   gchar     *svg;
 | |
|   gsize      svg_size;
 | |
|   return_if_no_image (image, data);
 | |
|   return_if_no_widget (widget, data);
 | |
| 
 | |
|   svg = pika_clipboard_get_svg (image->pika, &svg_size);
 | |
| 
 | |
|   if (svg)
 | |
|     {
 | |
|       GError *error = NULL;
 | |
| 
 | |
|       if (! pika_vectors_import_buffer (image, svg, svg_size,
 | |
|                                         TRUE, FALSE,
 | |
|                                         PIKA_IMAGE_ACTIVE_PARENT, -1,
 | |
|                                         NULL, &error))
 | |
|         {
 | |
|           pika_message (image->pika, G_OBJECT (widget), PIKA_MESSAGE_ERROR,
 | |
|                         "%s", error->message);
 | |
|           g_clear_error (&error);
 | |
|         }
 | |
|       else
 | |
|         {
 | |
|           pika_image_flush (image);
 | |
|         }
 | |
| 
 | |
|       g_free (svg);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void
 | |
| vectors_export_cmd_callback (PikaAction *action,
 | |
|                              GVariant   *value,
 | |
|                              gpointer    data)
 | |
| {
 | |
|   PikaImage   *image;
 | |
|   GList       *vectors;
 | |
|   GtkWidget   *widget;
 | |
|   GtkWidget   *dialog;
 | |
|   return_if_no_vectors_list (image, vectors, data);
 | |
|   return_if_no_widget (widget, data);
 | |
| 
 | |
| #define EXPORT_DIALOG_KEY "pika-vectors-export-dialog"
 | |
| 
 | |
|   dialog = dialogs_get_dialog (G_OBJECT (image), EXPORT_DIALOG_KEY);
 | |
| 
 | |
|   if (! dialog)
 | |
|     {
 | |
|       PikaDialogConfig *config = PIKA_DIALOG_CONFIG (image->pika->config);
 | |
|       GFile            *folder = NULL;
 | |
| 
 | |
|       if (config->vectors_export_path)
 | |
|         folder = pika_file_new_for_config_path (config->vectors_export_path,
 | |
|                                                 NULL);
 | |
| 
 | |
|       dialog = vectors_export_dialog_new (image, widget,
 | |
|                                           folder,
 | |
|                                           config->vectors_export_active_only,
 | |
|                                           vectors_export_callback,
 | |
|                                           NULL);
 | |
| 
 | |
|       if (folder)
 | |
|         g_object_unref (folder);
 | |
| 
 | |
|       dialogs_attach_dialog (G_OBJECT (image), EXPORT_DIALOG_KEY, dialog);
 | |
|     }
 | |
| 
 | |
|   gtk_window_present (GTK_WINDOW (dialog));
 | |
| }
 | |
| 
 | |
| void
 | |
| vectors_import_cmd_callback (PikaAction *action,
 | |
|                              GVariant   *value,
 | |
|                              gpointer    data)
 | |
| {
 | |
|   PikaImage *image;
 | |
|   GtkWidget *widget;
 | |
|   GtkWidget *dialog;
 | |
|   return_if_no_image (image, data);
 | |
|   return_if_no_widget (widget, data);
 | |
| 
 | |
| #define IMPORT_DIALOG_KEY "pika-vectors-import-dialog"
 | |
| 
 | |
|   dialog = dialogs_get_dialog (G_OBJECT (image), IMPORT_DIALOG_KEY);
 | |
| 
 | |
|   if (! dialog)
 | |
|     {
 | |
|       PikaDialogConfig *config = PIKA_DIALOG_CONFIG (image->pika->config);
 | |
|       GFile            *folder = NULL;
 | |
| 
 | |
|       if (config->vectors_import_path)
 | |
|         folder = pika_file_new_for_config_path (config->vectors_import_path,
 | |
|                                                 NULL);
 | |
| 
 | |
|       dialog = vectors_import_dialog_new (image, widget,
 | |
|                                           folder,
 | |
|                                           config->vectors_import_merge,
 | |
|                                           config->vectors_import_scale,
 | |
|                                           vectors_import_callback,
 | |
|                                           NULL);
 | |
| 
 | |
|       dialogs_attach_dialog (G_OBJECT (image), IMPORT_DIALOG_KEY, dialog);
 | |
|     }
 | |
| 
 | |
|   gtk_window_present (GTK_WINDOW (dialog));
 | |
| }
 | |
| 
 | |
| void
 | |
| vectors_visible_cmd_callback (PikaAction *action,
 | |
|                               GVariant   *value,
 | |
|                               gpointer    data)
 | |
| {
 | |
|   PikaImage *image;
 | |
|   GList     *vectors;
 | |
|   return_if_no_vectors_list (image, vectors, data);
 | |
| 
 | |
|   items_visible_cmd_callback (action, value, image, vectors);
 | |
| }
 | |
| 
 | |
| void
 | |
| vectors_lock_content_cmd_callback (PikaAction *action,
 | |
|                                    GVariant   *value,
 | |
|                                    gpointer    data)
 | |
| {
 | |
|   PikaImage *image;
 | |
|   GList     *vectors;
 | |
|   return_if_no_vectors_list (image, vectors, data);
 | |
| 
 | |
|   items_lock_content_cmd_callback (action, value, image, vectors);
 | |
| }
 | |
| 
 | |
| void
 | |
| vectors_lock_position_cmd_callback (PikaAction *action,
 | |
|                                     GVariant   *value,
 | |
|                                     gpointer    data)
 | |
| {
 | |
|   PikaImage *image;
 | |
|   GList     *vectors;
 | |
|   return_if_no_vectors_list (image, vectors, data);
 | |
| 
 | |
|   items_lock_position_cmd_callback (action, value, image, vectors);
 | |
| }
 | |
| 
 | |
| void
 | |
| vectors_color_tag_cmd_callback (PikaAction *action,
 | |
|                                 GVariant   *value,
 | |
|                                 gpointer    data)
 | |
| {
 | |
|   PikaImage    *image;
 | |
|   GList        *vectors;
 | |
|   PikaColorTag  color_tag;
 | |
|   return_if_no_vectors_list (image, vectors, data);
 | |
| 
 | |
|   color_tag = (PikaColorTag) g_variant_get_int32 (value);
 | |
| 
 | |
|   items_color_tag_cmd_callback (action, image, vectors, color_tag);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*  private functions  */
 | |
| 
 | |
| static void
 | |
| vectors_new_callback (GtkWidget    *dialog,
 | |
|                       PikaImage    *image,
 | |
|                       PikaVectors  *vectors,
 | |
|                       PikaContext  *context,
 | |
|                       const gchar  *vectors_name,
 | |
|                       gboolean      vectors_visible,
 | |
|                       PikaColorTag  vectors_color_tag,
 | |
|                       gboolean      vectors_lock_content,
 | |
|                       gboolean      vectors_lock_position,
 | |
|                       gboolean      vectors_lock_visibility,
 | |
|                       gpointer      user_data)
 | |
| {
 | |
|   PikaDialogConfig *config = PIKA_DIALOG_CONFIG (image->pika->config);
 | |
| 
 | |
|   g_object_set (config,
 | |
|                 "path-new-name", vectors_name,
 | |
|                 NULL);
 | |
| 
 | |
|   vectors = pika_vectors_new (image, config->vectors_new_name);
 | |
|   pika_item_set_visible (PIKA_ITEM (vectors), vectors_visible, FALSE);
 | |
|   pika_item_set_color_tag (PIKA_ITEM (vectors), vectors_color_tag, FALSE);
 | |
|   pika_item_set_lock_content (PIKA_ITEM (vectors), vectors_lock_content, FALSE);
 | |
|   pika_item_set_lock_position (PIKA_ITEM (vectors), vectors_lock_position, FALSE);
 | |
|   pika_item_set_lock_visibility (PIKA_ITEM (vectors), vectors_lock_visibility, FALSE);
 | |
| 
 | |
|   pika_image_add_vectors (image, vectors,
 | |
|                           PIKA_IMAGE_ACTIVE_PARENT, -1, TRUE);
 | |
|   pika_image_flush (image);
 | |
| 
 | |
|   gtk_widget_destroy (dialog);
 | |
| }
 | |
| 
 | |
| static void
 | |
| vectors_edit_attributes_callback (GtkWidget    *dialog,
 | |
|                                   PikaImage    *image,
 | |
|                                   PikaVectors  *vectors,
 | |
|                                   PikaContext  *context,
 | |
|                                   const gchar  *vectors_name,
 | |
|                                   gboolean      vectors_visible,
 | |
|                                   PikaColorTag  vectors_color_tag,
 | |
|                                   gboolean      vectors_lock_content,
 | |
|                                   gboolean      vectors_lock_position,
 | |
|                                   gboolean      vectors_lock_visibility,
 | |
|                                   gpointer      user_data)
 | |
| {
 | |
|   PikaItem *item = PIKA_ITEM (vectors);
 | |
| 
 | |
|   if (strcmp (vectors_name, pika_object_get_name (vectors))         ||
 | |
|       vectors_visible         != pika_item_get_visible (item)       ||
 | |
|       vectors_color_tag       != pika_item_get_color_tag (item)     ||
 | |
|       vectors_lock_content    != pika_item_get_lock_content (item)  ||
 | |
|       vectors_lock_position   != pika_item_get_lock_position (item) ||
 | |
|       vectors_lock_visibility != pika_item_get_lock_visibility (item))
 | |
|     {
 | |
|       pika_image_undo_group_start (image,
 | |
|                                    PIKA_UNDO_GROUP_ITEM_PROPERTIES,
 | |
|                                    _("Path Attributes"));
 | |
| 
 | |
|       if (strcmp (vectors_name, pika_object_get_name (vectors)))
 | |
|         pika_item_rename (PIKA_ITEM (vectors), vectors_name, NULL);
 | |
| 
 | |
|       if (vectors_visible != pika_item_get_visible (item))
 | |
|         pika_item_set_visible (item, vectors_visible, TRUE);
 | |
| 
 | |
|       if (vectors_color_tag != pika_item_get_color_tag (item))
 | |
|         pika_item_set_color_tag (item, vectors_color_tag, TRUE);
 | |
| 
 | |
|       if (vectors_lock_content != pika_item_get_lock_content (item))
 | |
|         pika_item_set_lock_content (item, vectors_lock_content, TRUE);
 | |
| 
 | |
|       if (vectors_lock_position != pika_item_get_lock_position (item))
 | |
|         pika_item_set_lock_position (item, vectors_lock_position, TRUE);
 | |
| 
 | |
|       if (vectors_lock_visibility != pika_item_get_lock_visibility (item))
 | |
|         pika_item_set_lock_visibility (item, vectors_lock_visibility, TRUE);
 | |
| 
 | |
|       pika_image_undo_group_end (image);
 | |
| 
 | |
|       pika_image_flush (image);
 | |
|     }
 | |
| 
 | |
|   gtk_widget_destroy (dialog);
 | |
| }
 | |
| 
 | |
| static void
 | |
| vectors_import_callback (GtkWidget *dialog,
 | |
|                          PikaImage *image,
 | |
|                          GFile     *file,
 | |
|                          GFile     *import_folder,
 | |
|                          gboolean   merge_vectors,
 | |
|                          gboolean   scale_vectors,
 | |
|                          gpointer   user_data)
 | |
| {
 | |
|   PikaDialogConfig *config = PIKA_DIALOG_CONFIG (image->pika->config);
 | |
|   gchar            *path   = NULL;
 | |
|   GError           *error  = NULL;
 | |
| 
 | |
|   if (import_folder)
 | |
|     path = pika_file_get_config_path (import_folder, NULL);
 | |
| 
 | |
|   g_object_set (config,
 | |
|                 "path-import-path",  path,
 | |
|                 "path-import-merge", merge_vectors,
 | |
|                 "path-import-scale", scale_vectors,
 | |
|                 NULL);
 | |
| 
 | |
|   if (path)
 | |
|     g_free (path);
 | |
| 
 | |
|   if (pika_vectors_import_file (image, file,
 | |
|                                 config->vectors_import_merge,
 | |
|                                 config->vectors_import_scale,
 | |
|                                 PIKA_IMAGE_ACTIVE_PARENT, -1,
 | |
|                                 NULL, &error))
 | |
|     {
 | |
|       pika_image_flush (image);
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       pika_message (image->pika, G_OBJECT (dialog),
 | |
|                     PIKA_MESSAGE_ERROR,
 | |
|                     "%s", error->message);
 | |
|       g_error_free (error);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|   gtk_widget_destroy (dialog);
 | |
| }
 | |
| 
 | |
| static void
 | |
| vectors_export_callback (GtkWidget *dialog,
 | |
|                          PikaImage *image,
 | |
|                          GFile     *file,
 | |
|                          GFile     *export_folder,
 | |
|                          gboolean   active_only,
 | |
|                          gpointer   user_data)
 | |
| {
 | |
|   PikaDialogConfig *config  = PIKA_DIALOG_CONFIG (image->pika->config);
 | |
|   GList            *vectors = NULL;
 | |
|   gchar            *path    = NULL;
 | |
|   GError           *error   = NULL;
 | |
| 
 | |
|   if (export_folder)
 | |
|     path = pika_file_get_config_path (export_folder, NULL);
 | |
| 
 | |
|   g_object_set (config,
 | |
|                 "path-export-path",        path,
 | |
|                 "path-export-active-only", active_only,
 | |
|                 NULL);
 | |
| 
 | |
|   if (path)
 | |
|     g_free (path);
 | |
| 
 | |
|   if (config->vectors_export_active_only)
 | |
|     vectors = pika_image_get_selected_vectors (image);
 | |
| 
 | |
|   if (! pika_vectors_export_file (image, vectors, file, &error))
 | |
|     {
 | |
|       pika_message (image->pika, G_OBJECT (dialog),
 | |
|                     PIKA_MESSAGE_ERROR,
 | |
|                     "%s", error->message);
 | |
|       g_clear_error (&error);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|   gtk_widget_destroy (dialog);
 | |
| }
 | |
| 
 | |
| void
 | |
| vectors_select_cmd_callback (PikaAction *action,
 | |
|                              GVariant   *value,
 | |
|                              gpointer    data)
 | |
| {
 | |
|   PikaImage            *image;
 | |
|   GList                *new_vectors = NULL;
 | |
|   GList                *vectors;
 | |
|   GList                *iter;
 | |
|   PikaActionSelectType  select_type;
 | |
|   gboolean              run_once;
 | |
|   return_if_no_image (image, data);
 | |
| 
 | |
|   select_type = (PikaActionSelectType) g_variant_get_int32 (value);
 | |
| 
 | |
|   vectors = pika_image_get_selected_vectors (image);
 | |
|   run_once = (g_list_length (vectors) == 0);
 | |
| 
 | |
|   for (iter = vectors; iter || run_once; iter = iter ? iter->next : NULL)
 | |
|     {
 | |
|       PikaVectors   *new_vec;
 | |
|       PikaContainer *container;
 | |
| 
 | |
|       if (iter)
 | |
|         {
 | |
|           container = pika_item_get_container (PIKA_ITEM (iter->data));
 | |
|         }
 | |
|       else /* run_once */
 | |
|         {
 | |
|           container = pika_image_get_vectors (image);
 | |
|           run_once  = FALSE;
 | |
|         }
 | |
|       new_vec = (PikaVectors *) action_select_object (select_type,
 | |
|                                                       container,
 | |
|                                                       iter ? iter->data : NULL);
 | |
|       if (new_vec)
 | |
|         new_vectors = g_list_prepend (new_vectors, new_vec);
 | |
|     }
 | |
| 
 | |
|   if (new_vectors)
 | |
|     {
 | |
|       pika_image_set_selected_vectors (image, new_vectors);
 | |
|       pika_image_flush (image);
 | |
|     }
 | |
| 
 | |
|   g_list_free (new_vectors);
 | |
| }
 |