diff --git a/app/actions/file-actions.c b/app/actions/file-actions.c index f5ee9fdc31..7d934f9772 100644 --- a/app/actions/file-actions.c +++ b/app/actions/file-actions.c @@ -82,6 +82,12 @@ static const GimpActionEntry file_actions[] = file_open_as_layers_cmd_callback, GIMP_HELP_FILE_OPEN_AS_LAYER }, + { "file-open-as-link-layers", GIMP_ICON_LAYER, + NC_("file-action", "Op_en as Link Layer..."), NULL, { "O", NULL }, + NC_("file-action", "Open an image file as Link layer"), + file_open_as_link_layer_cmd_callback, + GIMP_HELP_FILE_OPEN_AS_LAYER }, + { "file-open-location", GIMP_ICON_WEB, NC_("file-action", "Open _Location..."), NULL, { NULL }, NC_("file-action", "Open an image file from a specified location"), diff --git a/app/actions/file-commands.c b/app/actions/file-commands.c index 8236dc8a95..f78813c75c 100644 --- a/app/actions/file-commands.c +++ b/app/actions/file-commands.c @@ -73,7 +73,8 @@ static void file_open_dialog_show (Gimp *gimp, const gchar *title, GimpImage *image, GFile *file, - gboolean open_as_layers); + gboolean open_as_layers, + gboolean open_as_link); static GtkWidget * file_save_dialog_show (Gimp *gimp, GimpImage *image, GtkWidget *parent, @@ -118,7 +119,7 @@ file_open_cmd_callback (GimpAction *action, file_open_dialog_show (gimp, widget, _("Open Image"), - image, NULL, FALSE); + image, NULL, FALSE, FALSE); } void @@ -140,7 +141,29 @@ file_open_as_layers_cmd_callback (GimpAction *action, file_open_dialog_show (gimp, widget, _("Open Image as Layers"), - image, NULL, TRUE); + image, NULL, TRUE, FALSE); +} + +void +file_open_as_link_layer_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + Gimp *gimp; + GtkWidget *widget; + GimpDisplay *display; + GimpImage *image = NULL; + return_if_no_gimp (gimp, data); + return_if_no_widget (widget, data); + + display = action_data_get_display (data); + + if (display) + image = gimp_display_get_image (display); + + file_open_dialog_show (gimp, widget, + _("Open Image as Link Layer"), + image, NULL, TRUE, TRUE); } void @@ -577,7 +600,7 @@ file_file_open_dialog (Gimp *gimp, { file_open_dialog_show (gimp, parent, _("Open Image"), - NULL, file, FALSE); + NULL, file, FALSE, FALSE); } @@ -589,7 +612,8 @@ file_open_dialog_show (Gimp *gimp, const gchar *title, GimpImage *image, GFile *file, - gboolean open_as_layers) + gboolean open_as_layers, + gboolean open_as_link) { GtkWidget *dialog; @@ -621,7 +645,7 @@ file_open_dialog_show (Gimp *gimp, gtk_window_set_title (GTK_WINDOW (dialog), title); gimp_open_dialog_set_image (GIMP_OPEN_DIALOG (dialog), - image, open_as_layers); + image, open_as_layers, open_as_link); gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (gtk_widget_get_toplevel (parent))); diff --git a/app/actions/file-commands.h b/app/actions/file-commands.h index 22c48bb208..b1e4d29882 100644 --- a/app/actions/file-commands.h +++ b/app/actions/file-commands.h @@ -24,6 +24,9 @@ void file_open_cmd_callback (GimpAction *action, void file_open_as_layers_cmd_callback (GimpAction *action, GVariant *value, gpointer data); +void file_open_as_link_layer_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); void file_open_location_cmd_callback (GimpAction *action, GVariant *value, gpointer data); diff --git a/app/dialogs/file-open-dialog.c b/app/dialogs/file-open-dialog.c index 16e08f9fe8..f1b6bc6f67 100644 --- a/app/dialogs/file-open-dialog.c +++ b/app/dialogs/file-open-dialog.c @@ -55,11 +55,13 @@ static void file_open_dialog_response (GtkWidget *dialog, static GimpImage *file_open_dialog_open_image (GtkWidget *dialog, Gimp *gimp, GFile *file, - GimpPlugInProcedure *load_proc); + GimpPlugInProcedure *load_proc, + gboolean as_link); static gboolean file_open_dialog_open_layers (GtkWidget *dialog, GimpImage *image, GFile *file, - GimpPlugInProcedure *load_proc); + GimpPlugInProcedure *load_proc, + gboolean as_link); /* public functions */ @@ -147,13 +149,14 @@ file_open_dialog_response (GtkWidget *dialog, { if (! file_dialog->image) { - gimp_open_dialog_set_image ( - open_dialog, - file_open_dialog_open_image (dialog, - gimp, - file, - file_dialog->file_proc), - TRUE); + gimp_open_dialog_set_image (open_dialog, + file_open_dialog_open_image (dialog, + gimp, + file, + file_dialog->file_proc, + open_dialog->open_as_link), + TRUE, + open_dialog->open_as_link); if (file_dialog->image) { @@ -170,7 +173,8 @@ file_open_dialog_response (GtkWidget *dialog, else if (file_open_dialog_open_layers (dialog, file_dialog->image, file, - file_dialog->file_proc)) + file_dialog->file_proc, + open_dialog->open_as_link)) { success = TRUE; } @@ -180,7 +184,8 @@ file_open_dialog_response (GtkWidget *dialog, if (file_open_dialog_open_image (dialog, gimp, file, - file_dialog->file_proc)) + file_dialog->file_proc, + open_dialog->open_as_link)) { success = TRUE; @@ -227,7 +232,8 @@ static GimpImage * file_open_dialog_open_image (GtkWidget *dialog, Gimp *gimp, GFile *file, - GimpPlugInProcedure *load_proc) + GimpPlugInProcedure *load_proc, + gboolean as_link) { GimpImage *image; GimpPDBStatusType status; @@ -236,7 +242,7 @@ file_open_dialog_open_image (GtkWidget *dialog, image = file_open_with_proc_and_display (gimp, gimp_get_user_context (gimp), GIMP_PROGRESS (dialog), - file, FALSE, + file, FALSE, as_link, load_proc, G_OBJECT (gimp_widget_get_monitor (dialog)), &status, &error); @@ -256,7 +262,8 @@ static gboolean file_open_dialog_open_layers (GtkWidget *dialog, GimpImage *image, GFile *file, - GimpPlugInProcedure *load_proc) + GimpPlugInProcedure *load_proc, + gboolean as_link) { GList *new_layers; GimpPDBStatusType status; @@ -265,7 +272,7 @@ file_open_dialog_open_layers (GtkWidget *dialog, new_layers = file_open_layers (image->gimp, gimp_get_user_context (image->gimp), GIMP_PROGRESS (dialog), - image, FALSE, + image, FALSE, as_link, file, GIMP_RUN_INTERACTIVE, load_proc, &status, &error); diff --git a/app/dialogs/file-open-location-dialog.c b/app/dialogs/file-open-location-dialog.c index d5c710e8ac..57eae58eae 100644 --- a/app/dialogs/file-open-location-dialog.c +++ b/app/dialogs/file-open-location-dialog.c @@ -204,7 +204,7 @@ file_open_location_response (GtkDialog *dialog, image = file_open_with_proc_and_display (gimp, gimp_get_user_context (gimp), GIMP_PROGRESS (box), - file, FALSE, NULL, + file, FALSE, FALSE, NULL, G_OBJECT (gimp_widget_get_monitor (entry)), &status, &error); diff --git a/app/display/gimpdisplayshell-dnd.c b/app/display/gimpdisplayshell-dnd.c index 30e011a111..e5a9c438fd 100644 --- a/app/display/gimpdisplayshell-dnd.c +++ b/app/display/gimpdisplayshell-dnd.c @@ -571,7 +571,7 @@ gimp_display_shell_drop_uri_list (GtkWidget *widget, new_layers = file_open_layers (shell->display->gimp, context, GIMP_PROGRESS (shell->display), - image, FALSE, + image, FALSE, FALSE, file, GIMP_RUN_INTERACTIVE, NULL, &status, &error); diff --git a/app/file/file-open.c b/app/file/file-open.c index 2d84149cfd..357f526d41 100644 --- a/app/file/file-open.c +++ b/app/file/file-open.c @@ -55,7 +55,7 @@ #include "gimp-intl.h" -static GimpImage * file_open_or_link_image (Gimp *gimp, +static GimpImage * file_open_link_image (Gimp *gimp, GimpContext *context, GimpProgress *progress, GFile *file, @@ -78,6 +78,8 @@ static GList * file_open_get_layers (GimpImage *image, gboolean merge_visible, gint *n_visible); static gboolean file_open_file_proc_is_import (GimpPlugInProcedure *file_proc); +static gboolean file_open_valid_permissions (GFile *file, + GError **error); /* public functions */ @@ -135,46 +137,8 @@ file_open_image (Gimp *gimp, } } - /* FIXME enable these tests for remote files again, needs testing */ - if (g_file_is_native (file)) - { - GFileInfo *info; - - info = g_file_query_info (file, - G_FILE_ATTRIBUTE_STANDARD_TYPE "," - G_FILE_ATTRIBUTE_ACCESS_CAN_READ, - G_FILE_QUERY_INFO_NONE, - NULL, error); - - if (info != NULL) - { - 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); - return NULL; - } - - if (! g_file_info_get_attribute_boolean (info, - G_FILE_ATTRIBUTE_ACCESS_CAN_READ)) - { - g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, - _("Permission denied")); - g_object_unref (info); - return NULL; - } - - g_object_unref (info); - } - else - { - /* File likely does not exists. error will already have a more - * accurate reason. - */ - return NULL; - } - } + if (! file_open_valid_permissions (file, error)) + return NULL; if (! file_proc) file_proc = gimp_plug_in_manager_file_procedure_find (gimp->plug_in_manager, @@ -519,9 +483,8 @@ file_open_with_display (Gimp *gimp, GError **error) { return file_open_with_proc_and_display (gimp, context, progress, - file, as_new, NULL, - monitor, - status, error); + file, as_new, FALSE, NULL, + monitor, status, error); } GimpImage * @@ -530,6 +493,7 @@ file_open_with_proc_and_display (Gimp *gimp, GimpProgress *progress, GFile *file, gboolean as_new, + gboolean as_link, GimpPlugInProcedure *file_proc, GObject *monitor, GimpPDBStatusType *status, @@ -549,15 +513,26 @@ file_open_with_proc_and_display (Gimp *gimp, if (gimp->no_interface) run_mode = GIMP_RUN_NONINTERACTIVE; - image = file_open_or_link_image (gimp, context, progress, - file, 0, 0, - as_new, - file_proc, - run_mode, - NULL, - status, - &mime_type, - error); + if (as_link) + image = file_open_link_image (gimp, context, progress, + file, 0, 0, + as_new, + file_proc, + run_mode, + NULL, + status, + &mime_type, + error); + else + image = file_open_image (gimp, context, progress, + file, 0, 0, + as_new, + file_proc, + run_mode, + NULL, + status, + &mime_type, + error); if (image) { @@ -636,6 +611,7 @@ file_open_layers (Gimp *gimp, GimpProgress *progress, GimpImage *dest_image, gboolean merge_visible, + gboolean as_link, GFile *file, GimpRunMode run_mode, GimpPlugInProcedure *file_proc, @@ -654,14 +630,24 @@ file_open_layers (Gimp *gimp, g_return_val_if_fail (status != NULL, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); - new_image = file_open_or_link_image (gimp, context, progress, - file, - gimp_image_get_width (dest_image), - gimp_image_get_height (dest_image), - FALSE, - file_proc, - run_mode, - NULL, status, &mime_type, error); + if (as_link) + new_image = file_open_link_image (gimp, context, progress, + file, + gimp_image_get_width (dest_image), + gimp_image_get_height (dest_image), + FALSE, + file_proc, + run_mode, + NULL, status, &mime_type, error); + else + new_image = file_open_image (gimp, context, progress, + file, + gimp_image_get_width (dest_image), + gimp_image_get_height (dest_image), + FALSE, + file_proc, + run_mode, + NULL, status, &mime_type, error); if (new_image) { @@ -769,30 +755,38 @@ file_open_from_command_line (Gimp *gimp, /* private functions */ static GimpImage * -file_open_or_link_image (Gimp *gimp, - GimpContext *context, - GimpProgress *progress, - GFile *file, - gint vector_width, - gint vector_height, - gboolean as_new, - GimpPlugInProcedure *file_proc, - GimpRunMode run_mode, - gboolean *file_proc_handles_vector, - GimpPDBStatusType *status, - const gchar **mime_type, - GError **error) +file_open_link_image (Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + GFile *file, + gint vector_width, + gint vector_height, + gboolean as_new, + GimpPlugInProcedure *file_proc, + GimpRunMode run_mode, + gboolean *file_proc_handles_vector, + GimpPDBStatusType *status, + const gchar **mime_type, + GError **error) { GimpImage *image = NULL; + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL); + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (status != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + if (! file_proc) file_proc = gimp_plug_in_manager_file_procedure_find (gimp->plug_in_manager, GIMP_FILE_PROCEDURE_GROUP_OPEN, file, error); - if (g_file_is_native (file) && - file_proc != NULL && - file_open_file_proc_is_import (file_proc)) + if (! file_open_valid_permissions (file, error)) + return NULL; + + if (g_file_is_native (file) && file_proc != NULL) { GimpLink *link = gimp_link_new (gimp, file, progress, error); @@ -825,18 +819,16 @@ file_open_or_link_image (Gimp *gimp, g_clear_object (&link); } - else + else if (! g_file_is_native (file)) { - image = file_open_image (gimp, context, progress, - file, - vector_width, vector_height, - as_new, - file_proc, - run_mode, - file_proc_handles_vector, - status, - mime_type, - error); + gchar *uri = g_file_get_uri (file); + + if (error && ! *error) + g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, + _("Only platform-native file paths are supported: '%s' cannot be opened as link."), + uri); + + g_free (uri); } return image; @@ -954,3 +946,53 @@ file_open_file_proc_is_import (GimpPlugInProcedure *file_proc) g_strcmp0 (proc_name, "file-bz2-load") != 0 && g_strcmp0 (proc_name, "file-xz-load") != 0); } + +static gboolean +file_open_valid_permissions (GFile *file, + GError **error) +{ + /* FIXME enable these tests for remote files again, needs testing */ + if (g_file_is_native (file)) + { + GFileInfo *info; + + info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_TYPE "," + G_FILE_ATTRIBUTE_ACCESS_CAN_READ, + G_FILE_QUERY_INFO_NONE, + NULL, error); + + if (info != NULL) + { + 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); + return FALSE; + } + + if (! g_file_info_get_attribute_boolean (info, + G_FILE_ATTRIBUTE_ACCESS_CAN_READ)) + { + g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, + _("Permission denied")); + g_object_unref (info); + return FALSE; + } + + g_object_unref (info); + } + else + { + /* File likely does not exists. error will already have a more + * accurate reason. + */ + return FALSE; + } + + return TRUE; + } + + return FALSE; +} diff --git a/app/file/file-open.h b/app/file/file-open.h index d0cc1b406c..6f23603118 100644 --- a/app/file/file-open.h +++ b/app/file/file-open.h @@ -59,6 +59,7 @@ GimpImage * file_open_with_proc_and_display (Gimp *gimp, GimpProgress *progress, GFile *file, gboolean as_new, + gboolean as_link, GimpPlugInProcedure *file_proc, GObject *monitor, GimpPDBStatusType *status, @@ -69,6 +70,7 @@ GList * file_open_layers (Gimp *gimp, GimpProgress *progress, GimpImage *dest_image, gboolean merge_visible, + gboolean as_link, GFile *file, GimpRunMode run_mode, GimpPlugInProcedure *file_proc, diff --git a/app/pdb/file-cmds.c b/app/pdb/file-cmds.c index 38d261ac8b..aef4cda9e3 100644 --- a/app/pdb/file-cmds.c +++ b/app/pdb/file-cmds.c @@ -135,7 +135,7 @@ file_load_layer_invoker (GimpProcedure *procedure, GimpPDBStatusType status; layers = file_open_layers (gimp, context, progress, - image, FALSE, + image, FALSE, FALSE, file, run_mode, NULL, &status, error); if (layers) @@ -181,7 +181,7 @@ file_load_layers_invoker (GimpProcedure *procedure, GimpPDBStatusType status; layer_list = file_open_layers (gimp, context, progress, - image, FALSE, + image, FALSE, FALSE, file, run_mode, NULL, &status, error); if (layer_list) diff --git a/app/widgets/gimplayertreeview.c b/app/widgets/gimplayertreeview.c index a3679e9db9..aa8118942b 100644 --- a/app/widgets/gimplayertreeview.c +++ b/app/widgets/gimplayertreeview.c @@ -774,7 +774,7 @@ gimp_layer_tree_view_drop_uri_list (GimpContainerTreeView *view, new_layers = file_open_layers (image->gimp, gimp_container_view_get_context (cont_view), NULL, - image, FALSE, + image, FALSE, FALSE, file, GIMP_RUN_INTERACTIVE, NULL, &status, &error); diff --git a/app/widgets/gimpopendialog.c b/app/widgets/gimpopendialog.c index 22d0a07cc0..32fd4a8658 100644 --- a/app/widgets/gimpopendialog.c +++ b/app/widgets/gimpopendialog.c @@ -66,7 +66,7 @@ gimp_open_dialog_init (GimpOpenDialog *dialog) static void gimp_open_dialog_dispose (GObject *object) { - gimp_open_dialog_set_image (GIMP_OPEN_DIALOG (object), NULL, FALSE); + gimp_open_dialog_set_image (GIMP_OPEN_DIALOG (object), NULL, FALSE, FALSE); G_OBJECT_CLASS (parent_class)->dispose (object); } @@ -99,16 +99,19 @@ gimp_open_dialog_new (Gimp *gimp) void gimp_open_dialog_set_image (GimpOpenDialog *dialog, GimpImage *image, - gboolean open_as_layers) + gboolean open_as_layers, + gboolean open_as_link) { GimpFileDialog *file_dialog; g_return_if_fail (GIMP_IS_OPEN_DIALOG (dialog)); g_return_if_fail (image == NULL || GIMP_IS_IMAGE (image)); + g_return_if_fail (! open_as_link || open_as_layers); file_dialog = GIMP_FILE_DIALOG (dialog); g_set_weak_pointer (&file_dialog->image, image); dialog->open_as_layers = open_as_layers; + dialog->open_as_link = open_as_link; } diff --git a/app/widgets/gimpopendialog.h b/app/widgets/gimpopendialog.h index b9451ec8bf..117e7d170a 100644 --- a/app/widgets/gimpopendialog.h +++ b/app/widgets/gimpopendialog.h @@ -38,6 +38,7 @@ struct _GimpOpenDialog GimpFileDialog parent_instance; gboolean open_as_layers; + gboolean open_as_link; }; struct _GimpOpenDialogClass @@ -52,4 +53,5 @@ GtkWidget * gimp_open_dialog_new (Gimp *gimp); void gimp_open_dialog_set_image (GimpOpenDialog *dialog, GimpImage *image, - gboolean open_as_layers); + gboolean open_as_layers, + gboolean open_as_link); diff --git a/menus/image-menu.ui.in.in b/menus/image-menu.ui.in.in index 31c2a57ad8..6bc6de1943 100644 --- a/menus/image-menu.ui.in.in +++ b/menus/image-menu.ui.in.in @@ -14,6 +14,7 @@ app.file-open app.file-open-as-layers + app.file-open-as-link-layers app.file-open-location Open _Recent diff --git a/pdb/groups/file.pdb b/pdb/groups/file.pdb index a67ea016f5..01254c4fd6 100644 --- a/pdb/groups/file.pdb +++ b/pdb/groups/file.pdb @@ -138,7 +138,7 @@ HELP GimpPDBStatusType status; layers = file_open_layers (gimp, context, progress, - image, FALSE, + image, FALSE, FALSE, file, run_mode, NULL, &status, error); if (layers) @@ -186,7 +186,7 @@ HELP GimpPDBStatusType status; layer_list = file_open_layers (gimp, context, progress, - image, FALSE, + image, FALSE, FALSE, file, run_mode, NULL, &status, error); if (layer_list)