diff --git a/app/actions/file-commands.c b/app/actions/file-commands.c
index c5b939f397..8236dc8a95 100644
--- a/app/actions/file-commands.c
+++ b/app/actions/file-commands.c
@@ -843,7 +843,7 @@ file_revert_confirm_response (GtkWidget *dialog,
GIMP_PROGRESS (display),
file, 0, 0, FALSE, NULL,
GIMP_RUN_INTERACTIVE,
- &status, NULL, &error);
+ NULL, &status, NULL, &error);
if (new_image)
{
diff --git a/app/actions/layers-actions.c b/app/actions/layers-actions.c
index 1153737a2d..ddaa66a204 100644
--- a/app/actions/layers-actions.c
+++ b/app/actions/layers-actions.c
@@ -30,6 +30,7 @@
#include "core/gimpimage.h"
#include "core/gimplayer.h"
#include "core/gimplayer-floating-selection.h"
+#include "core/gimplinklayer.h"
#include "text/gimptextlayer.h"
@@ -173,6 +174,18 @@ static const GimpActionEntry layers_actions[] =
image_flatten_image_cmd_callback,
GIMP_HELP_IMAGE_FLATTEN },
+ { "layers-link-discard", GIMP_ICON_TOOL_TEXT,
+ NC_("layers-action", "_Discard Link Information"), NULL, { NULL },
+ NC_("layers-action", "Turn this link layer into a normal layer"),
+ layers_link_discard_cmd_callback,
+ GIMP_HELP_LAYER_TEXT_DISCARD },
+
+ { "layers-link-monitor", GIMP_ICON_TOOL_TEXT,
+ NC_("layers-action", "_Monitor Linked Image"), NULL, { NULL },
+ NC_("layers-action", "Discard any transformation and monitor the linked file again"),
+ layers_link_monitor_cmd_callback,
+ GIMP_HELP_LAYER_TEXT_DISCARD },
+
{ "layers-text-discard", GIMP_ICON_TOOL_TEXT,
NC_("layers-action", "_Discard Text Information"), NULL, { NULL },
NC_("layers-action", "Turn these text layers into normal layers"),
@@ -757,6 +770,7 @@ layers_actions_update (GimpActionGroup *group,
gboolean lock_alpha = TRUE;
gboolean can_lock_alpha = FALSE;
gboolean text_layer = FALSE;
+ gboolean link_layer = FALSE;
gboolean bs_mutable = FALSE; /* At least 1 selected layers' blend space is mutable. */
gboolean cs_mutable = FALSE; /* At least 1 selected layers' composite space is mutable. */
gboolean cm_mutable = FALSE; /* At least 1 selected layers' composite mode is mutable. */
@@ -978,6 +992,7 @@ layers_actions_update (GimpActionGroup *group,
gimp_action_group_set_action_active (group, action, TRUE);
text_layer = gimp_item_is_text_layer (GIMP_ITEM (layer));
+ link_layer = gimp_item_is_link_layer (GIMP_ITEM (layer));
}
}
@@ -1038,6 +1053,9 @@ layers_actions_update (GimpActionGroup *group,
SET_SENSITIVE ("layers-merge-layers", n_selected_layers > 0 && !fs && !ac);
SET_SENSITIVE ("layers-flatten-image", !fs && !ac);
+ SET_VISIBLE ("layers-link-discard", link_layer && !ac);
+ SET_VISIBLE ("layers-link-monitor", GIMP_IS_LINK_LAYER (layer) && ! link_layer && !ac);
+
SET_VISIBLE ("layers-text-discard", n_text_layers > 0 && !ac);
SET_VISIBLE ("layers-text-to-vectors", n_text_layers > 0 && !ac);
SET_VISIBLE ("layers-text-along-vectors", text_layer && !ac);
diff --git a/app/actions/layers-commands.c b/app/actions/layers-commands.c
index bde74e484a..9ce64224c1 100644
--- a/app/actions/layers-commands.c
+++ b/app/actions/layers-commands.c
@@ -48,6 +48,8 @@
#include "core/gimplayerpropundo.h"
#include "core/gimplayer-floating-selection.h"
#include "core/gimplayer-new.h"
+#include "core/gimplink.h"
+#include "core/gimplinklayer.h"
#include "core/gimplist.h"
#include "core/gimppickable.h"
#include "core/gimppickable-auto-shrink.h"
@@ -66,6 +68,7 @@
#include "widgets/gimpaction.h"
#include "widgets/gimpdock.h"
#include "widgets/gimphelp-ids.h"
+#include "widgets/gimpopendialog.h"
#include "widgets/gimpprogressdialog.h"
#include "display/gimpdisplay.h"
@@ -101,6 +104,7 @@ static void layers_new_callback (GtkWidget *dialog,
GimpLayerCompositeMode layer_composite_mode,
gdouble layer_opacity,
GimpFillType layer_fill_type,
+ GimpLink *link,
gint layer_width,
gint layer_height,
gint layer_offset_x,
@@ -124,6 +128,7 @@ static void layers_edit_attributes_callback (GtkWidget *dialog,
GimpLayerCompositeMode layer_composite_mode,
gdouble layer_opacity,
GimpFillType layer_fill_type,
+ GimpLink *link,
gint layer_width,
gint layer_height,
gint layer_offset_x,
@@ -172,6 +177,9 @@ static gint layers_mode_index (GimpLayerMode layer_mode
const GimpLayerMode *modes,
gint n_modes);
+static void layers_link_dialog_response (GtkDialog *dialog,
+ gint response_id,
+ GimpImage *image);
/* private variables */
@@ -427,6 +435,22 @@ layers_new_last_vals_cmd_callback (GimpAction *action,
return;
}
+ if (config->layer_new_fill_type == GIMP_FILL_LINK)
+ {
+ GtkWidget *dialog;
+
+ dialog = gimp_open_dialog_new (image->gimp);
+ gtk_window_set_title (GTK_WINDOW (dialog), _("Select Linked Image"));
+ gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), FALSE);
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (layers_link_dialog_response),
+ image);
+ gtk_widget_show (dialog);
+
+ return;
+ }
+
layer_mode = config->layer_new_mode;
if (layer_mode == GIMP_LAYER_MODE_NORMAL ||
@@ -1045,6 +1069,42 @@ layers_delete_cmd_callback (GimpAction *action,
gimp_image_flush (image);
}
+void
+layers_link_discard_cmd_callback (GimpAction *action,
+ GVariant *value,
+ gpointer data)
+{
+ GimpImage *image;
+ GList *layers;
+ GList *iter;
+ return_if_no_layers (image, layers, data);
+
+ gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_PROPERTIES,
+ _("Discard Links"));
+ for (iter = layers; iter; iter = iter->next)
+ if (GIMP_IS_LINK_LAYER (iter->data))
+ gimp_link_layer_discard (GIMP_LINK_LAYER (iter->data));
+ gimp_image_undo_group_end (image);
+}
+
+void
+layers_link_monitor_cmd_callback (GimpAction *action,
+ GVariant *value,
+ gpointer data)
+{
+ GimpImage *image;
+ GList *layers;
+ GList *iter;
+ return_if_no_layers (image, layers, data);
+
+ gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_PROPERTIES,
+ _("Monitor Links"));
+ for (iter = layers; iter; iter = iter->next)
+ if (GIMP_IS_LINK_LAYER (iter->data))
+ gimp_link_layer_monitor (GIMP_LINK_LAYER (iter->data));
+ gimp_image_undo_group_end (image);
+}
+
void
layers_text_discard_cmd_callback (GimpAction *action,
GVariant *value,
@@ -2263,6 +2323,7 @@ layers_new_callback (GtkWidget *dialog,
GimpLayerCompositeMode layer_composite_mode,
gdouble layer_opacity,
GimpFillType layer_fill_type,
+ GimpLink *link,
gint layer_width,
gint layer_height,
gint layer_offset_x,
@@ -2324,17 +2385,32 @@ layers_new_callback (GtkWidget *dialog,
position = 0;
}
- layer = gimp_layer_new (image, layer_width, layer_height,
- gimp_image_get_layer_format (image, TRUE),
- config->layer_new_name,
- config->layer_new_opacity,
- config->layer_new_mode);
+ if (layer_fill_type == GIMP_FILL_LINK)
+ {
+ if (link)
+ {
+ gimp_link_set_size (link, layer_width, layer_height);
+ layer = gimp_link_layer_new (image, link);
+
+ if (layer_mode != gimp_layer_get_mode (layer))
+ gimp_layer_set_mode (layer, layer_mode, TRUE);
+ }
+ }
+ else
+ {
+ layer = gimp_layer_new (image, layer_width, layer_height,
+ gimp_image_get_layer_format (image, TRUE),
+ config->layer_new_name,
+ config->layer_new_opacity,
+ config->layer_new_mode);
+ }
if (layer)
{
gimp_item_set_offset (GIMP_ITEM (layer), layer_offset_x, layer_offset_y);
- gimp_drawable_fill (GIMP_DRAWABLE (layer), context,
- config->layer_new_fill_type);
+ if (layer_fill_type != GIMP_FILL_LINK)
+ gimp_drawable_fill (GIMP_DRAWABLE (layer), context,
+ config->layer_new_fill_type);
gimp_item_set_visible (GIMP_ITEM (layer), layer_visible, FALSE);
gimp_item_set_color_tag (GIMP_ITEM (layer), layer_color_tag, FALSE);
gimp_item_set_lock_content (GIMP_ITEM (layer), layer_lock_pixels,
@@ -2353,6 +2429,16 @@ layers_new_callback (GtkWidget *dialog,
new_layers = g_list_prepend (new_layers, layer);
}
+ else if (layer_fill_type == GIMP_FILL_LINK)
+ {
+ /* We special-case the link layers because chances of failure are
+ * higher and especially source of failure may be user choices.
+ * For such case, we don't want to generate WARNINGS (which are
+ * for code bugs), but an error dialog instead.
+ */
+ gimp_message_literal (image->gimp, NULL, GIMP_MESSAGE_WARNING,
+ _("Invalid image file"));
+ }
else
{
g_warning ("%s: could not allocate new layer", G_STRFUNC);
@@ -2379,6 +2465,7 @@ layers_edit_attributes_callback (GtkWidget *dialog,
GimpLayerCompositeMode layer_composite_mode,
gdouble layer_opacity,
GimpFillType unused1,
+ GimpLink *link,
gint unused2,
gint unused3,
gint layer_offset_x,
@@ -2407,7 +2494,8 @@ layers_edit_attributes_callback (GtkWidget *dialog,
layer_lock_pixels != gimp_item_get_lock_content (item) ||
layer_lock_position != gimp_item_get_lock_position (item) ||
layer_lock_visibility != gimp_item_get_lock_visibility (item) ||
- layer_lock_alpha != gimp_layer_get_lock_alpha (layer))
+ layer_lock_alpha != gimp_layer_get_lock_alpha (layer) ||
+ link)
{
gimp_image_undo_group_start (image,
GIMP_UNDO_GROUP_ITEM_PROPERTIES,
@@ -2468,6 +2556,9 @@ layers_edit_attributes_callback (GtkWidget *dialog,
if (layer_lock_alpha != gimp_layer_get_lock_alpha (layer))
gimp_layer_set_lock_alpha (layer, layer_lock_alpha, TRUE);
+ if (GIMP_IS_LINK_LAYER (layer) && link)
+ gimp_link_layer_set_link (GIMP_LINK_LAYER (layer), link, TRUE);
+
gimp_image_undo_group_end (image);
gimp_image_flush (image);
}
@@ -2649,3 +2740,45 @@ layers_mode_index (GimpLayerMode layer_mode,
return i;
}
+
+static void
+layers_link_dialog_response (GtkDialog *dialog,
+ gint response_id,
+ GimpImage *image)
+{
+ if (response_id == GTK_RESPONSE_OK)
+ {
+ GFile *file;
+
+ file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
+ if (file)
+ {
+ GimpLayer *layer;
+ GimpLink *link;
+
+ link = gimp_link_new (image->gimp, file);
+ layer = gimp_link_layer_new (image, link);
+
+ if (layer)
+ {
+ GimpDialogConfig *config;
+
+ config = GIMP_DIALOG_CONFIG (image->gimp->config);
+ gimp_layer_set_blend_space (layer,
+ config->layer_new_blend_space, FALSE);
+ gimp_layer_set_composite_space (layer,
+ config->layer_new_composite_space, FALSE);
+ gimp_layer_set_composite_mode (layer,
+ config->layer_new_composite_mode, FALSE);
+
+ gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_LAYER_ADD,
+ _("Add Link Layer"));
+ gimp_image_add_layer (image, layer, GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE);
+ gimp_image_undo_group_end (image);
+ gimp_image_flush (image);
+ }
+ }
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+}
diff --git a/app/actions/layers-commands.h b/app/actions/layers-commands.h
index 6e28d641e5..283d186eda 100644
--- a/app/actions/layers-commands.h
+++ b/app/actions/layers-commands.h
@@ -75,6 +75,12 @@ void layers_merge_group_cmd_callback (GimpAction *action,
void layers_delete_cmd_callback (GimpAction *action,
GVariant *value,
gpointer data);
+void layers_link_discard_cmd_callback (GimpAction *action,
+ GVariant *value,
+ gpointer data);
+void layers_link_monitor_cmd_callback (GimpAction *action,
+ GVariant *value,
+ gpointer data);
void layers_text_discard_cmd_callback (GimpAction *action,
GVariant *value,
gpointer data);
diff --git a/app/core/core-enums.c b/app/core/core-enums.c
index 827f0b5f11..794e0d347a 100644
--- a/app/core/core-enums.c
+++ b/app/core/core-enums.c
@@ -1257,6 +1257,7 @@ gimp_undo_type_get_type (void)
{ GIMP_UNDO_LAYER_MODE, "GIMP_UNDO_LAYER_MODE", "layer-mode" },
{ GIMP_UNDO_LAYER_OPACITY, "GIMP_UNDO_LAYER_OPACITY", "layer-opacity" },
{ GIMP_UNDO_LAYER_LOCK_ALPHA, "GIMP_UNDO_LAYER_LOCK_ALPHA", "layer-lock-alpha" },
+ { GIMP_UNDO_LINK_LAYER, "GIMP_UNDO_LINK_LAYER", "link-layer" },
{ GIMP_UNDO_GROUP_LAYER_SUSPEND_RESIZE, "GIMP_UNDO_GROUP_LAYER_SUSPEND_RESIZE", "group-layer-suspend-resize" },
{ GIMP_UNDO_GROUP_LAYER_RESUME_RESIZE, "GIMP_UNDO_GROUP_LAYER_RESUME_RESIZE", "group-layer-resume-resize" },
{ GIMP_UNDO_GROUP_LAYER_SUSPEND_MASK, "GIMP_UNDO_GROUP_LAYER_SUSPEND_MASK", "group-layer-suspend-mask" },
@@ -1370,6 +1371,7 @@ gimp_undo_type_get_type (void)
{ GIMP_UNDO_LAYER_MODE, NC_("undo-type", "Set layer mode"), NULL },
{ GIMP_UNDO_LAYER_OPACITY, NC_("undo-type", "Set layer opacity"), NULL },
{ GIMP_UNDO_LAYER_LOCK_ALPHA, NC_("undo-type", "Lock/Unlock alpha channel"), NULL },
+ { GIMP_UNDO_LINK_LAYER, NC_("undo-type", "Link layer"), NULL },
{ GIMP_UNDO_GROUP_LAYER_SUSPEND_RESIZE, NC_("undo-type", "Suspend group layer resize"), NULL },
{ GIMP_UNDO_GROUP_LAYER_RESUME_RESIZE, NC_("undo-type", "Resume group layer resize"), NULL },
{ GIMP_UNDO_GROUP_LAYER_SUSPEND_MASK, NC_("undo-type", "Suspend group layer mask"), NULL },
diff --git a/app/core/core-enums.h b/app/core/core-enums.h
index 5db3dfd541..70bdd260c4 100644
--- a/app/core/core-enums.h
+++ b/app/core/core-enums.h
@@ -611,6 +611,7 @@ typedef enum /*< pdb-skip >*/
GIMP_UNDO_LAYER_MODE, /*< desc="Set layer mode" >*/
GIMP_UNDO_LAYER_OPACITY, /*< desc="Set layer opacity" >*/
GIMP_UNDO_LAYER_LOCK_ALPHA, /*< desc="Lock/Unlock alpha channel" >*/
+ GIMP_UNDO_LINK_LAYER, /*< desc="Link layer" >*/
GIMP_UNDO_GROUP_LAYER_SUSPEND_RESIZE, /*< desc="Suspend group layer resize" >*/
GIMP_UNDO_GROUP_LAYER_RESUME_RESIZE, /*< desc="Resume group layer resize" >*/
GIMP_UNDO_GROUP_LAYER_SUSPEND_MASK, /*< desc="Suspend group layer mask" >*/
diff --git a/app/core/core-types.h b/app/core/core-types.h
index 93cb8958c5..adbf8dc56d 100644
--- a/app/core/core-types.h
+++ b/app/core/core-types.h
@@ -92,6 +92,7 @@ typedef struct _GimpViewable GimpViewable;
typedef struct _GimpFilter GimpFilter;
typedef struct _GimpItem GimpItem;
typedef struct _GimpAuxItem GimpAuxItem;
+typedef struct _GimpLink GimpLink;
typedef struct _Gimp Gimp;
typedef struct _GimpImage GimpImage;
@@ -169,6 +170,7 @@ typedef struct _GimpLayerMask GimpLayerMask;
typedef struct _GimpSelection GimpSelection;
typedef struct _GimpLayer GimpLayer;
typedef struct _GimpGroupLayer GimpGroupLayer;
+typedef struct _GimpLinkLayer GimpLinkLayer;
/* auxiliary image items */
diff --git a/app/core/gimpimage-undo-push.c b/app/core/gimpimage-undo-push.c
index 468146e13a..0f13cc3141 100644
--- a/app/core/gimpimage-undo-push.c
+++ b/app/core/gimpimage-undo-push.c
@@ -48,6 +48,8 @@
#include "gimplayermaskundo.h"
#include "gimplayerpropundo.h"
#include "gimplayerundo.h"
+#include "gimplinklayer.h"
+#include "gimplinklayerundo.h"
#include "gimpmaskundo.h"
#include "gimpsamplepoint.h"
#include "gimpsamplepointundo.h"
@@ -876,6 +878,25 @@ gimp_image_undo_push_text_layer_convert (GimpImage *image,
NULL);
}
+/**********************/
+/* Link Layer Undos */
+/**********************/
+
+GimpUndo *
+gimp_image_undo_push_link_layer (GimpImage *image,
+ const gchar *undo_desc,
+ GimpLinkLayer *layer)
+{
+ g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
+ g_return_val_if_fail (GIMP_IS_LINK_LAYER (layer), NULL);
+ g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (layer)), NULL);
+
+ return gimp_image_undo_push (image, GIMP_TYPE_LINK_LAYER_UNDO,
+ GIMP_UNDO_LINK_LAYER, undo_desc,
+ GIMP_DIRTY_ITEM | GIMP_DIRTY_DRAWABLE,
+ "item", layer,
+ NULL);
+}
/**********************/
/* Layer Mask Undos */
diff --git a/app/core/gimpimage-undo-push.h b/app/core/gimpimage-undo-push.h
index c13850ce98..3e8e09fda7 100644
--- a/app/core/gimpimage-undo-push.h
+++ b/app/core/gimpimage-undo-push.h
@@ -215,6 +215,11 @@ GimpUndo * gimp_image_undo_push_text_layer_convert (GimpImage *image,
const gchar *undo_desc,
GimpTextLayer *layer);
+/* link layer undos */
+
+GimpUndo * gimp_image_undo_push_link_layer (GimpImage *image,
+ const gchar *undo_desc,
+ GimpLinkLayer *layer);
/* layer mask undos */
diff --git a/app/core/gimpimage.c b/app/core/gimpimage.c
index 803dc47ee8..ec84d35149 100644
--- a/app/core/gimpimage.c
+++ b/app/core/gimpimage.c
@@ -71,6 +71,7 @@
#include "gimplayer-floating-selection.h"
#include "gimplayermask.h"
#include "gimplayerstack.h"
+#include "gimplinklayer.h"
#include "gimpmarshal.h"
#include "gimppalette.h"
#include "gimpparasitelist.h"
@@ -3013,6 +3014,14 @@ gimp_image_get_xcf_version (GimpImage *image,
*/
version = MAX (23, version);
}
+
+ /* Need version 24 TODO for link layers. */
+ if (GIMP_IS_LINK_LAYER (layer))
+ {
+ ADD_REASON (g_strdup_printf (_("Link layers were added in %s"),
+ "GIMP TODO"));
+ version = MAX (14, version);
+ }
}
g_list_free (items);
diff --git a/app/core/gimpimagefile.c b/app/core/gimpimagefile.c
index d1128d1a0f..01156677aa 100644
--- a/app/core/gimpimagefile.c
+++ b/app/core/gimpimagefile.c
@@ -520,7 +520,7 @@ gimp_imagefile_create_thumbnail (GimpImagefile *imagefile,
private->file, size, size,
FALSE, NULL,
GIMP_RUN_NONINTERACTIVE,
- &status, &mime_type, error);
+ NULL, &status, &mime_type, error);
if (image)
gimp_thumbnail_set_info_from_image (private->thumbnail,
diff --git a/app/core/gimplink.c b/app/core/gimplink.c
new file mode 100644
index 0000000000..0a079c4839
--- /dev/null
+++ b/app/core/gimplink.c
@@ -0,0 +1,403 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpLink
+ * Copyright (C) 2019 Jehan
+ *
+ * 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
+#include
+
+#include "libgimpbase/gimpbase.h"
+
+#include "core-types.h"
+
+#include "gimp.h"
+#include "gimpimage.h"
+#include "gimplink.h"
+#include "gimpmarshal.h"
+#include "gimppickable.h"
+#include "gimpprojection.h"
+
+#include "file/file-open.h"
+
+#include "gimp-intl.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_GIMP,
+ PROP_FILE
+};
+
+enum
+{
+ CHANGED,
+ LAST_SIGNAL
+};
+
+struct _GimpLinkPrivate
+{
+ Gimp *gimp;
+ GFile *file;
+ GFileMonitor *monitor;
+
+ gboolean broken;
+ guint idle_changed_source;
+
+ gboolean is_vector;
+ gint width;
+ gint height;
+};
+
+static void gimp_link_finalize (GObject *object);
+static void gimp_link_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_link_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_link_file_changed (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ GimpLink *link);
+static gboolean gimp_link_emit_changed (gpointer data);
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpLink, gimp_link, GIMP_TYPE_OBJECT)
+
+#define parent_class gimp_link_parent_class
+
+static guint link_signals[LAST_SIGNAL] = { 0 };
+
+static void
+gimp_link_class_init (GimpLinkClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ link_signals[CHANGED] =
+ g_signal_new ("changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpLinkClass, changed),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
+ object_class->finalize = gimp_link_finalize;
+ object_class->get_property = gimp_link_get_property;
+ object_class->set_property = gimp_link_set_property;
+
+ g_object_class_install_property (object_class, PROP_GIMP,
+ g_param_spec_object ("gimp", NULL, NULL,
+ GIMP_TYPE_GIMP,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (object_class, PROP_FILE,
+ g_param_spec_object ("file", NULL, NULL,
+ G_TYPE_FILE,
+ GIMP_PARAM_READWRITE));
+}
+
+static void
+gimp_link_init (GimpLink *link)
+{
+ link->p = gimp_link_get_instance_private (link);
+ link->p->gimp = NULL;
+ link->p->file = NULL;
+ link->p->monitor = NULL;
+ link->p->broken = TRUE;
+ link->p->width = 0;
+ link->p->height = 0;
+
+ link->p->idle_changed_source = 0;
+}
+
+static void
+gimp_link_finalize (GObject *object)
+{
+ GimpLink *link = GIMP_LINK (object);
+
+ g_clear_object (&link->p->file);
+ g_clear_object (&link->p->monitor);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_link_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpLink *link = GIMP_LINK (object);
+
+ switch (property_id)
+ {
+ case PROP_GIMP:
+ g_value_set_object (value, link->p->gimp);
+ break;
+ case PROP_FILE:
+ g_value_set_object (value, link->p->file);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_link_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpLink *link = GIMP_LINK (object);
+
+ switch (property_id)
+ {
+ case PROP_GIMP:
+ link->p->gimp = g_value_get_object (value);
+ break;
+ case PROP_FILE:
+ if (link->p->file)
+ g_object_unref (link->p->file);
+ if (link->p->monitor)
+ g_object_unref (link->p->monitor);
+
+ link->p->is_vector = FALSE;
+ link->p->file = g_value_dup_object (value);
+
+ if (link->p->file)
+ {
+ gchar *basename;
+
+ basename = g_file_get_basename (link->p->file);
+ link->p->monitor = g_file_monitor_file (link->p->file, G_FILE_MONITOR_NONE, NULL, NULL);
+ g_signal_connect (link->p->monitor, "changed",
+ G_CALLBACK (gimp_link_file_changed),
+ link);
+ gimp_object_set_name_safe (GIMP_OBJECT (object), basename);
+ g_free (basename);
+ }
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_link_file_changed (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ GimpLink *link)
+{
+ switch (event_type)
+ {
+ case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
+ if (link->p->idle_changed_source == 0)
+ link->p->idle_changed_source = g_idle_add_full (G_PRIORITY_LOW,
+ gimp_link_emit_changed,
+ link, NULL);
+ break;
+ case G_FILE_MONITOR_EVENT_CREATED:
+ g_signal_emit (link, link_signals[CHANGED], 0);
+ break;
+ case G_FILE_MONITOR_EVENT_DELETED:
+ link->p->broken = TRUE;
+ break;
+
+ default:
+ /* No need to signal for changes where nothing can be done anyway.
+ * In particular a file deletion, the link is broken, yet we don't
+ * want to re-render.
+ * Don't emit either on G_FILE_MONITOR_EVENT_CHANGED because too
+ * many such events may be emitted for a single file writing.
+ */
+ break;
+ }
+
+}
+
+static gboolean
+gimp_link_emit_changed (gpointer data)
+{
+ GimpLink *link = GIMP_LINK (data);
+
+ g_signal_emit (link, link_signals[CHANGED], 0);
+ link->p->idle_changed_source = 0;
+
+ return G_SOURCE_REMOVE;
+}
+
+/* public functions */
+
+/**
+ * gimp_link_new:
+ * @gimp: #Gimp object.
+ * @file: a #GFile object.
+ *
+ * Creates a new text layer.
+ *
+ * Return value: a new #GimpLink or %NULL in case of a problem
+ **/
+GimpLink *
+gimp_link_new (Gimp *gimp,
+ GFile *file)
+{
+ GimpObject *link;
+
+ g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+ link = g_object_new (GIMP_TYPE_LINK,
+ "gimp", gimp,
+ "file", file,
+ NULL);
+
+ return GIMP_LINK (link);
+}
+
+GFile *
+gimp_link_get_file (GimpLink *link)
+{
+ g_return_val_if_fail (GIMP_IS_LINK (link), NULL);
+
+ return link->p->file;
+}
+
+void
+gimp_link_set_file (GimpLink *link,
+ GFile *file)
+{
+ g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (link)));
+ g_return_if_fail (G_IS_FILE (file) || file == NULL);
+
+ if (file && g_file_equal (file, link->p->file))
+ return;
+
+ g_object_set (link,
+ "file", file,
+ NULL);
+}
+
+gboolean
+gimp_link_is_broken (GimpLink *link,
+ gboolean recheck)
+{
+ GeglBuffer *buffer;
+
+ if (recheck)
+ {
+ buffer = gimp_link_get_buffer (link, NULL, NULL);
+ g_clear_object (&buffer);
+ }
+
+ return link->p->broken;
+}
+
+GimpLink *
+gimp_link_duplicate (GimpLink *link)
+{
+ g_return_val_if_fail (GIMP_IS_LINK (link), NULL);
+
+ return gimp_link_new (link->p->gimp, link->p->file);
+}
+
+void
+gimp_link_set_size (GimpLink *link,
+ gint width,
+ gint height)
+{
+ link->p->width = width;
+ link->p->height = height;
+}
+
+void
+gimp_link_get_size (GimpLink *link,
+ gint *width,
+ gint *height)
+{
+ *width = link->p->width;
+ *height = link->p->height;
+}
+
+gboolean
+gimp_link_is_vector (GimpLink *link)
+{
+ return link->p->is_vector;
+}
+
+GeglBuffer *
+gimp_link_get_buffer (GimpLink *link,
+ GimpProgress *progress,
+ GError **error)
+{
+ GeglBuffer *buffer = NULL;
+
+ if (link->p->file)
+ {
+ GimpImage *image;
+ GimpPDBStatusType status;
+ const gchar *mime_type = NULL;
+
+ image = file_open_image (link->p->gimp,
+ gimp_get_user_context (link->p->gimp),
+ progress,
+ link->p->file,
+ link->p->width, link->p->height,
+ FALSE, NULL,
+ /* XXX We might want interactive opening
+ * for a first opening (when done through
+ * GUI), but not for every re-render.
+ */
+ GIMP_RUN_NONINTERACTIVE,
+ &link->p->is_vector,
+ &status, &mime_type, error);
+
+ if (image && status == GIMP_PDB_SUCCESS)
+ {
+ /* If we don't flush the projection first, the buffer may be empty.
+ * I do wonder if the flushing and updating of the link could
+ * not be multi-threaded with gimp_projection_flush() instead,
+ * then notifying the update through signals. For very heavy
+ * images, would it be a better UX? XXX
+ */
+ gimp_projection_flush_now (gimp_image_get_projection (image), TRUE);
+ buffer = gimp_pickable_get_buffer (GIMP_PICKABLE (image));
+ g_object_ref (buffer);
+ }
+
+ /* Only keep the buffer, free the rest. */
+ g_clear_object (&image);
+ }
+
+ link->p->broken = (buffer == NULL);
+
+ return buffer;
+}
diff --git a/app/core/gimplink.h b/app/core/gimplink.h
new file mode 100644
index 0000000000..c62d3c557c
--- /dev/null
+++ b/app/core/gimplink.h
@@ -0,0 +1,79 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpLink
+ * Copyright (C) 2019 Jehan
+ *
+ * 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 .
+ */
+
+#ifndef __GIMP_LINK_H__
+#define __GIMP_LINK_H__
+
+
+#include "gimpitem.h"
+
+
+#define GIMP_TYPE_LINK (gimp_link_get_type ())
+#define GIMP_LINK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_LINK, GimpLink))
+#define GIMP_LINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_LINK, GimpLinkClass))
+#define GIMP_IS_LINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_LINK))
+#define GIMP_IS_LINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_LINK))
+#define GIMP_LINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_LINK, GimpLinkClass))
+
+
+typedef struct _GimpLinkClass GimpLinkClass;
+typedef struct _GimpLinkPrivate GimpLinkPrivate;
+
+struct _GimpLink
+{
+ GimpObject parent_instance;
+
+ GimpLinkPrivate *p;
+};
+
+struct _GimpLinkClass
+{
+ GimpObjectClass parent_class;
+
+ void (* changed) (GimpLink *link);
+};
+
+
+GType gimp_link_get_type (void) G_GNUC_CONST;
+
+GimpLink * gimp_link_new (Gimp *gimp,
+ GFile *file);
+
+GFile * gimp_link_get_file (GimpLink *link);
+void gimp_link_set_file (GimpLink *layer,
+ GFile *file);
+
+gboolean gimp_link_is_broken (GimpLink *link,
+ gboolean recheck);
+GimpLink * gimp_link_duplicate (GimpLink *link);
+
+void gimp_link_set_size (GimpLink *link,
+ gint width,
+ gint height);
+void gimp_link_get_size (GimpLink *link,
+ gint *width,
+ gint *height);
+gboolean gimp_link_is_vector (GimpLink *link);
+
+GeglBuffer * gimp_link_get_buffer (GimpLink *link,
+ GimpProgress *progress,
+ GError **error);
+
+#endif /* __GIMP_LINK_H__ */
diff --git a/app/core/gimplinklayer.c b/app/core/gimplinklayer.c
new file mode 100644
index 0000000000..e38653c013
--- /dev/null
+++ b/app/core/gimplinklayer.c
@@ -0,0 +1,830 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpLinkLayer
+ * Copyright (C) 2019 Jehan
+ *
+ * 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
+#include
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpconfig/gimpconfig.h"
+
+#include "core-types.h"
+
+#include "gegl/gimp-gegl-loops.h"
+#include "gegl/gimp-gegl-utils.h"
+
+#include "gimp.h"
+#include "gimpcontext.h"
+#include "gimpimage.h"
+#include "gimpimage-color-profile.h"
+#include "gimpimage-undo.h"
+#include "gimpimage-undo-push.h"
+#include "gimpitemtree.h"
+#include "gimpobjectqueue.h"
+#include "gimpprogress.h"
+
+#include "gimplink.h"
+#include "gimplinklayer.h"
+
+#include "gimp-intl.h"
+
+
+enum
+{
+ LINK_LAYER_XCF_NONE = 0,
+ LINK_LAYER_XCF_DONT_AUTO_RENAME = 1 << 0,
+ LINK_LAYER_XCF_MODIFIED = 1 << 1
+};
+
+enum
+{
+ PROP_0,
+ PROP_LINK,
+ PROP_AUTO_RENAME,
+ PROP_MODIFIED,
+ PROP_SCALED_ONLY
+};
+
+struct _GimpLinkLayerPrivate
+{
+ GimpLink *link;
+ gboolean modified;
+ gboolean scaled_only;
+ gboolean auto_rename;
+};
+
+static void gimp_link_layer_finalize (GObject *object);
+static void gimp_link_layer_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_link_layer_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static gint64 gimp_link_layer_get_memsize (GimpObject *object,
+ gint64 *gui_size);
+
+static GimpItem * gimp_link_layer_duplicate (GimpItem *item,
+ GType new_type);
+static gboolean gimp_link_layer_rename (GimpItem *item,
+ const gchar *new_name,
+ const gchar *undo_desc,
+ GError **error);
+static void gimp_link_layer_scale (GimpItem *item,
+ gint new_width,
+ gint new_height,
+ gint new_offset_x,
+ gint new_offset_y,
+ GimpInterpolationType interpolation_type,
+ GimpProgress *progress);
+
+static void gimp_link_layer_set_buffer (GimpDrawable *drawable,
+ gboolean push_undo,
+ const gchar *undo_desc,
+ GeglBuffer *buffer,
+ const GeglRectangle *bounds);
+static void gimp_link_layer_push_undo (GimpDrawable *drawable,
+ const gchar *undo_desc,
+ GeglBuffer *buffer,
+ gint x,
+ gint y,
+ gint width,
+ gint height);
+
+static void gimp_link_layer_convert_type (GimpLayer *layer,
+ GimpImage *dest_image,
+ const Babl *new_format,
+ GimpColorProfile *src_profile,
+ GimpColorProfile *dest_profile,
+ GeglDitherMethod layer_dither_type,
+ GeglDitherMethod mask_dither_type,
+ gboolean push_undo,
+ GimpProgress *progress);
+
+static void gimp_link_layer_link_changed (GimpLinkLayer *layer);
+static gboolean gimp_link_layer_render (GimpLinkLayer *layer);
+
+static void gimp_link_layer_set_xcf_flags (GimpLinkLayer *layer,
+ guint32 flags);
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpLinkLayer, gimp_link_layer, GIMP_TYPE_LAYER)
+
+#define parent_class gimp_link_layer_parent_class
+
+
+static void
+gimp_link_layer_class_init (GimpLinkLayerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
+ GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass);
+ GimpItemClass *item_class = GIMP_ITEM_CLASS (klass);
+ GimpDrawableClass *drawable_class = GIMP_DRAWABLE_CLASS (klass);
+ GimpLayerClass *layer_class = GIMP_LAYER_CLASS (klass);
+
+ object_class->finalize = gimp_link_layer_finalize;
+ object_class->get_property = gimp_link_layer_get_property;
+ object_class->set_property = gimp_link_layer_set_property;
+
+ gimp_object_class->get_memsize = gimp_link_layer_get_memsize;
+
+ viewable_class->default_icon_name = "emblem-symbolic-link";
+
+ item_class->duplicate = gimp_link_layer_duplicate;
+ item_class->rename = gimp_link_layer_rename;
+ item_class->scale = gimp_link_layer_scale;
+
+ item_class->default_name = _("Link Layer");
+ item_class->rename_desc = _("Rename Link Layer");
+ item_class->translate_desc = _("Move Link Layer");
+ item_class->scale_desc = _("Scale Link Layer");
+ item_class->resize_desc = _("Resize Link Layer");
+ item_class->flip_desc = _("Flip Link Layer");
+ item_class->rotate_desc = _("Rotate Link Layer");
+ item_class->transform_desc = _("Transform Link Layer");
+
+ drawable_class->set_buffer = gimp_link_layer_set_buffer;
+ drawable_class->push_undo = gimp_link_layer_push_undo;
+
+ layer_class->convert_type = gimp_link_layer_convert_type;
+
+ g_object_class_install_property (object_class, PROP_LINK,
+ g_param_spec_object ("link",
+ NULL, NULL,
+ GIMP_TYPE_LINK,
+ GIMP_PARAM_READWRITE));
+
+ GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_AUTO_RENAME,
+ "auto-rename",
+ NULL, NULL,
+ TRUE,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_MODIFIED,
+ "modified",
+ NULL, NULL,
+ FALSE,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SCALED_ONLY,
+ "scaled-only",
+ NULL, NULL,
+ FALSE,
+ GIMP_PARAM_STATIC_STRINGS);
+}
+
+static void
+gimp_link_layer_init (GimpLinkLayer *layer)
+{
+ layer->p = gimp_link_layer_get_instance_private (layer);
+ layer->p->link = NULL;
+}
+
+static void
+gimp_link_layer_finalize (GObject *object)
+{
+ GimpLinkLayer *layer = GIMP_LINK_LAYER (object);
+
+ g_clear_object (&layer->p->link);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_link_layer_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpLinkLayer *layer = GIMP_LINK_LAYER (object);
+
+ switch (property_id)
+ {
+ case PROP_LINK:
+ g_value_set_object (value, layer->p->link);
+ break;
+ case PROP_AUTO_RENAME:
+ g_value_set_boolean (value, layer->p->auto_rename);
+ break;
+ case PROP_MODIFIED:
+ g_value_set_boolean (value, layer->p->modified);
+ break;
+ case PROP_SCALED_ONLY:
+ g_value_set_boolean (value, layer->p->scaled_only);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_link_layer_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpLinkLayer *layer = GIMP_LINK_LAYER (object);
+
+ switch (property_id)
+ {
+ case PROP_LINK:
+ gimp_link_layer_set_link (layer, g_value_get_object (value), TRUE);
+ break;
+ case PROP_AUTO_RENAME:
+ layer->p->auto_rename = g_value_get_boolean (value);
+ break;
+ case PROP_MODIFIED:
+ layer->p->modified = g_value_get_boolean (value);
+ break;
+ case PROP_SCALED_ONLY:
+ layer->p->scaled_only = g_value_get_boolean (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static gint64
+gimp_link_layer_get_memsize (GimpObject *object,
+ gint64 *gui_size)
+{
+ GimpLinkLayer *layer = GIMP_LINK_LAYER (object);
+ gint64 memsize = 0;
+
+ memsize += gimp_object_get_memsize (GIMP_OBJECT (layer->p->link),
+ gui_size);
+
+ return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
+ gui_size);
+}
+
+static GimpItem *
+gimp_link_layer_duplicate (GimpItem *item,
+ GType new_type)
+{
+ GimpItem *new_item;
+
+ g_return_val_if_fail (g_type_is_a (new_type, GIMP_TYPE_DRAWABLE), NULL);
+
+ new_item = GIMP_ITEM_CLASS (parent_class)->duplicate (item, new_type);
+
+ if (GIMP_IS_LINK_LAYER (new_item))
+ {
+ GimpLinkLayer *layer = GIMP_LINK_LAYER (item);
+ GimpLinkLayer *new_layer = GIMP_LINK_LAYER (new_item);
+
+ gimp_config_sync (G_OBJECT (layer), G_OBJECT (new_layer), 0);
+
+ if (layer->p->link)
+ {
+ GimpLink *link = gimp_link_duplicate (layer->p->link);
+
+ gimp_link_layer_set_link (new_layer, link, FALSE);
+ }
+ }
+
+ return new_item;
+}
+
+static gboolean
+gimp_link_layer_rename (GimpItem *item,
+ const gchar *new_name,
+ const gchar *undo_desc,
+ GError **error)
+{
+ if (GIMP_ITEM_CLASS (parent_class)->rename (item, new_name, undo_desc, error))
+ {
+ g_object_set (item, "auto-rename", FALSE, NULL);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+gimp_link_layer_scale (GimpItem *item,
+ gint new_width,
+ gint new_height,
+ gint new_offset_x,
+ gint new_offset_y,
+ GimpInterpolationType interpolation_type,
+ GimpProgress *progress)
+{
+ GimpLinkLayer *link_layer = GIMP_LINK_LAYER (item);
+ GimpLayer *layer = GIMP_LAYER (item);
+ GimpObjectQueue *queue = NULL;
+
+ if (progress && layer->mask)
+ {
+ GimpLayerMask *mask;
+
+ queue = gimp_object_queue_new (progress);
+ progress = GIMP_PROGRESS (queue);
+
+ /* temporarily set layer->mask to NULL, so that its size won't be counted
+ * when pushing the layer to the queue.
+ */
+ mask = layer->mask;
+ layer->mask = NULL;
+
+ gimp_object_queue_push (queue, layer);
+ gimp_object_queue_push (queue, mask);
+
+ layer->mask = mask;
+ }
+
+ if (queue)
+ gimp_object_queue_pop (queue);
+
+ if (gimp_link_is_vector (link_layer->p->link) && ! link_layer->p->modified)
+ {
+ /* Non-modified vector images are always recomputed from the
+ * source file and therefore are always sharp.
+ */
+ gimp_link_set_size (link_layer->p->link, new_width, new_height);
+ gimp_link_layer_render (link_layer);
+ }
+ else
+ {
+ gboolean scaled_only = FALSE;
+
+ if (! link_layer->p->modified || link_layer->p->scaled_only)
+ {
+ /* Raster images whose only modification are previous scaling
+ * are scaled back from the source file. Though they are still
+ * considered "modified" after a scaling (unlike vector
+ * images) and therefore are demoted to work like normal
+ * layers, scaling is still special-cased for better image
+ * quality.
+ */
+ gimp_link_layer_render (link_layer);
+ scaled_only = TRUE;
+ }
+
+ GIMP_ITEM_CLASS (parent_class)->scale (GIMP_ITEM (layer),
+ new_width, new_height,
+ new_offset_x, new_offset_y,
+ interpolation_type, progress);
+ g_object_set (layer, "scaled-only", scaled_only, NULL);
+ }
+
+ if (layer->mask)
+ {
+ if (queue)
+ gimp_object_queue_pop (queue);
+
+ gimp_item_scale (GIMP_ITEM (layer->mask),
+ new_width, new_height,
+ new_offset_x, new_offset_y,
+ interpolation_type, progress);
+ }
+
+ g_clear_object (&queue);
+}
+
+static void
+gimp_link_layer_set_buffer (GimpDrawable *drawable,
+ gboolean push_undo,
+ const gchar *undo_desc,
+ GeglBuffer *buffer,
+ const GeglRectangle *bounds)
+{
+ GimpLinkLayer *layer = GIMP_LINK_LAYER (drawable);
+ GimpImage *image = gimp_item_get_image (GIMP_ITEM (layer));
+
+ if (push_undo && ! layer->p->modified)
+ gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_DRAWABLE_MOD,
+ undo_desc);
+
+ GIMP_DRAWABLE_CLASS (parent_class)->set_buffer (drawable,
+ push_undo, undo_desc,
+ buffer,
+ bounds);
+
+ if (push_undo && ! layer->p->modified)
+ {
+ gimp_image_undo_push_link_layer (image, NULL, layer);
+
+ g_object_set (drawable, "modified", TRUE, NULL);
+ g_object_set (drawable, "scaled-only", FALSE, NULL);
+
+ gimp_image_undo_group_end (image);
+ }
+}
+
+static void
+gimp_link_layer_push_undo (GimpDrawable *drawable,
+ const gchar *undo_desc,
+ GeglBuffer *buffer,
+ gint x,
+ gint y,
+ gint width,
+ gint height)
+{
+ GimpLinkLayer *layer = GIMP_LINK_LAYER (drawable);
+ GimpImage *image = gimp_item_get_image (GIMP_ITEM (layer));
+
+ if (! layer->p->modified)
+ gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_DRAWABLE, undo_desc);
+
+ GIMP_DRAWABLE_CLASS (parent_class)->push_undo (drawable, undo_desc,
+ buffer,
+ x, y, width, height);
+
+ if (! layer->p->modified)
+ {
+ gimp_image_undo_push_link_layer (image, NULL, layer);
+
+ g_object_set (drawable, "modified", TRUE, NULL);
+
+ gimp_image_undo_group_end (image);
+ }
+}
+
+static void
+gimp_link_layer_convert_type (GimpLayer *layer,
+ GimpImage *dest_image,
+ const Babl *new_format,
+ GimpColorProfile *src_profile,
+ GimpColorProfile *dest_profile,
+ GeglDitherMethod layer_dither_type,
+ GeglDitherMethod mask_dither_type,
+ gboolean push_undo,
+ GimpProgress *progress)
+{
+ GimpLinkLayer *link_layer = GIMP_LINK_LAYER (layer);
+ GimpImage *image = gimp_item_get_image (GIMP_ITEM (link_layer));
+
+ if (! link_layer->p->link ||
+ link_layer->p->modified ||
+ layer_dither_type != GEGL_DITHER_NONE)
+ {
+ GIMP_LAYER_CLASS (parent_class)->convert_type (layer, dest_image,
+ new_format,
+ src_profile,
+ dest_profile,
+ layer_dither_type,
+ mask_dither_type,
+ push_undo,
+ progress);
+ }
+ else
+ {
+ if (push_undo)
+ gimp_image_undo_push_link_layer (image, NULL, link_layer);
+
+ gimp_link_layer_render (link_layer);
+ }
+}
+
+
+/* public functions */
+
+/**
+ * gimp_link_layer_new:
+ * @image: the #GimpImage the layer should belong to
+ * @text: a #GimpText object
+ *
+ * Creates a new link layer.
+ *
+ * Return value: a new #GimpLinkLayer or %NULL in case of a problem
+ **/
+GimpLayer *
+gimp_link_layer_new (GimpImage *image,
+ GimpLink *link)
+{
+ GimpLinkLayer *layer;
+
+ g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
+
+ layer =
+ GIMP_LINK_LAYER (gimp_drawable_new (GIMP_TYPE_LINK_LAYER,
+ image, NULL,
+ 0, 0, 1, 1,
+ gimp_image_get_layer_format (image,
+ TRUE)));
+ gimp_layer_set_mode (GIMP_LAYER (layer),
+ gimp_image_get_default_new_layer_mode (image),
+ FALSE);
+ if (! gimp_link_layer_set_link (layer, link, FALSE))
+ {
+ g_object_unref (layer);
+ return NULL;
+ }
+
+ return GIMP_LAYER (layer);
+}
+
+GimpLink *
+gimp_link_layer_get_link (GimpLinkLayer *layer)
+{
+ g_return_val_if_fail (GIMP_IS_LINK_LAYER (layer), NULL);
+
+ return layer->p->link;
+}
+
+gboolean
+gimp_link_layer_set_link (GimpLinkLayer *layer,
+ GimpLink *link,
+ gboolean push_undo)
+{
+ gboolean rendered = FALSE;
+
+ g_return_val_if_fail (GIMP_IS_LINK_LAYER (layer), FALSE);
+ g_return_val_if_fail (GIMP_IS_LINK (link), FALSE);
+
+ /* TODO: look deeper into the link paths. */
+ if (layer->p->link == link)
+ return ! gimp_link_is_broken (link, FALSE);
+
+ if (gimp_item_is_attached (GIMP_ITEM (layer)) && push_undo)
+ gimp_image_undo_push_link_layer (gimp_item_get_image (GIMP_ITEM (layer)),
+ _("Set layer link"), layer);
+
+ if (layer->p->link)
+ {
+ g_signal_handlers_disconnect_by_func (layer->p->link,
+ G_CALLBACK (gimp_link_layer_link_changed),
+ layer);
+
+ g_clear_object (&layer->p->link);
+ }
+
+ if (link)
+ {
+ layer->p->link = g_object_ref (link);
+
+ g_signal_connect_object (link, "changed",
+ G_CALLBACK (gimp_link_layer_link_changed),
+ layer, G_CONNECT_SWAPPED);
+
+ g_object_set (layer, "modified", FALSE, NULL);
+ rendered = gimp_link_layer_render (layer);
+ }
+
+ g_object_notify (G_OBJECT (layer), "link");
+ gimp_viewable_invalidate_preview (GIMP_VIEWABLE (layer));
+
+ return rendered;
+}
+
+/**
+ * gimp_link_layer_discard:
+ * @layer: a #GimpLinkLayer
+ *
+ * Discards the link. This makes @layer behave like a
+ * normal layer.
+ */
+void
+gimp_link_layer_discard (GimpLinkLayer *layer)
+{
+ g_return_if_fail (GIMP_IS_LINK_LAYER (layer));
+ g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (layer)));
+
+ gimp_image_undo_push_link_layer (gimp_item_get_image (GIMP_ITEM (layer)),
+ _("Discard Link"), layer);
+
+ layer->p->modified = TRUE;
+}
+
+void
+gimp_link_layer_monitor (GimpLinkLayer *layer)
+{
+ g_return_if_fail (GIMP_IS_LINK_LAYER (layer));
+ g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (layer)));
+
+ gimp_image_undo_push_link_layer (gimp_item_get_image (GIMP_ITEM (layer)),
+ _("Monitor Link"), layer);
+
+ layer->p->modified = FALSE;
+ gimp_link_layer_render (layer);
+}
+
+gboolean
+gimp_item_is_link_layer (GimpItem *item)
+{
+ return (GIMP_IS_LINK_LAYER (item) &&
+ ! GIMP_LINK_LAYER (item)->p->modified);
+}
+
+guint32
+gimp_link_layer_get_xcf_flags (GimpLinkLayer *link_layer)
+{
+ guint flags = 0;
+
+ g_return_val_if_fail (GIMP_IS_LINK_LAYER (link_layer), 0);
+
+ if (! link_layer->p->auto_rename)
+ flags |= LINK_LAYER_XCF_DONT_AUTO_RENAME;
+
+ if (link_layer->p->modified)
+ flags |= LINK_LAYER_XCF_MODIFIED;
+
+ return flags;
+}
+
+/**
+ * gimp_link_layer_from_layer:
+ * @layer: a #GimpLayer object
+ * @link: a #GimpLink object
+ * @flags: flags as retrieved from the XCF file.
+ *
+ * Converts a standard #GimpLayer into a #GimpLinkLayer.
+ * The new link layer takes ownership of the @link.
+ * The old @layer object is freed and replaced in-place by the new
+ * #GimpLinkLayer.
+ *
+ * This is a hack similar to the one used to load text layers from XCF,
+ * since at first they are loaded as normal layers, and only later
+ * promoted to link layers when the corresponding property is read from
+ * the file.
+ **/
+void
+gimp_link_layer_from_layer (GimpLayer **layer,
+ GimpLink *link,
+ guint32 flags)
+{
+ GimpLinkLayer *link_layer;
+ GimpDrawable *drawable;
+
+ g_return_if_fail (GIMP_IS_LAYER (*layer));
+ g_return_if_fail (GIMP_IS_LINK (link));
+
+ link_layer = g_object_new (GIMP_TYPE_LINK_LAYER,
+ "image", gimp_item_get_image (GIMP_ITEM (*layer)),
+ NULL);
+
+ gimp_item_replace_item (GIMP_ITEM (link_layer), GIMP_ITEM (*layer));
+
+ drawable = GIMP_DRAWABLE (link_layer);
+ gimp_drawable_steal_buffer (drawable, GIMP_DRAWABLE (*layer));
+
+ gimp_layer_set_opacity (GIMP_LAYER (link_layer),
+ gimp_layer_get_opacity (*layer), FALSE);
+ gimp_layer_set_mode (GIMP_LAYER (link_layer),
+ gimp_layer_get_mode (*layer), FALSE);
+ gimp_layer_set_blend_space (GIMP_LAYER (link_layer),
+ gimp_layer_get_blend_space (*layer), FALSE);
+ gimp_layer_set_composite_space (GIMP_LAYER (link_layer),
+ gimp_layer_get_composite_space (*layer), FALSE);
+ gimp_layer_set_composite_mode (GIMP_LAYER (link_layer),
+ gimp_layer_get_composite_mode (*layer), FALSE);
+ gimp_layer_set_lock_alpha (GIMP_LAYER (link_layer),
+ gimp_layer_get_lock_alpha (*layer), FALSE);
+
+ gimp_link_layer_set_link (link_layer, link, FALSE);
+ gimp_link_layer_set_xcf_flags (link_layer, flags);
+
+ g_object_unref (link);
+ g_object_unref (*layer);
+
+ *layer = GIMP_LAYER (link_layer);
+}
+
+/* private functions */
+
+static void
+gimp_link_layer_link_changed (GimpLinkLayer *layer)
+{
+ if (! layer->p->modified)
+ gimp_link_layer_render (layer);
+}
+
+static gboolean
+gimp_link_layer_render (GimpLinkLayer *layer)
+{
+ GimpDrawable *drawable;
+ GimpItem *item;
+ GimpImage *image;
+ GeglBuffer *buffer;
+ gdouble xres;
+ gdouble yres;
+ gint width;
+ gint height;
+
+ if (! layer->p->link)
+ return FALSE;
+
+ drawable = GIMP_DRAWABLE (layer);
+ item = GIMP_ITEM (layer);
+ image = gimp_item_get_image (item);
+
+ gimp_image_get_resolution (image, &xres, &yres);
+ /* TODO: I could imagine a GimpBusyBox (to be made as GimpProgress) in
+ * the later list showing layer update.
+ */
+ buffer = gimp_link_get_buffer (layer->p->link, NULL, NULL);
+
+ if (! buffer)
+ return FALSE;
+
+ width = gegl_buffer_get_width (buffer);
+ height = gegl_buffer_get_height (buffer);
+
+ g_object_freeze_notify (G_OBJECT (drawable));
+
+ if ((width != gimp_item_get_width (item) ||
+ height != gimp_item_get_height (item)))
+ {
+ GeglBuffer *new_buffer;
+
+ new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, width, height),
+ gimp_drawable_get_format (drawable));
+ gimp_drawable_set_buffer (drawable, FALSE, NULL, new_buffer);
+ g_object_unref (new_buffer);
+
+ if (gimp_layer_get_mask (GIMP_LAYER (layer)))
+ {
+ GimpLayerMask *mask = gimp_layer_get_mask (GIMP_LAYER (layer));
+
+ static GimpContext *unused_eek = NULL;
+
+ if (! unused_eek)
+ unused_eek = gimp_context_new (image->gimp, "eek", NULL);
+
+ gimp_item_resize (GIMP_ITEM (mask),
+ unused_eek, GIMP_FILL_TRANSPARENT,
+ width, height, 0, 0);
+ }
+ }
+
+ if (layer->p->auto_rename)
+ {
+ GimpItem *item = GIMP_ITEM (layer);
+ gchar *name = NULL;
+
+ if (layer->p->link)
+ {
+ name = g_strdup (gimp_object_get_name (layer->p->link));
+ }
+
+ if (! name || ! name[0])
+ {
+ g_free (name);
+ name = g_strdup (_("Link Layer"));
+ }
+
+ if (gimp_item_is_attached (item))
+ {
+ gimp_item_tree_rename_item (gimp_item_get_tree (item), item,
+ name, FALSE, NULL);
+ g_free (name);
+ }
+ else
+ {
+ gimp_object_take_name (GIMP_OBJECT (layer), name);
+ }
+ }
+
+ gimp_gegl_buffer_copy (buffer, NULL, GEGL_ABYSS_NONE,
+ gimp_drawable_get_buffer (drawable), NULL);
+ g_object_unref (buffer);
+
+ g_object_thaw_notify (G_OBJECT (drawable));
+
+ gimp_drawable_update (drawable, 0, 0, width, height);
+ gimp_image_flush (image);
+
+ return (width > 0 && height > 0);
+}
+
+static void
+gimp_link_layer_set_xcf_flags (GimpLinkLayer *layer,
+ guint32 flags)
+{
+ g_return_if_fail (GIMP_IS_LINK_LAYER (layer));
+
+ g_object_set (layer,
+ "auto-rename", (flags & LINK_LAYER_XCF_DONT_AUTO_RENAME) == 0,
+ "modified", (flags & LINK_LAYER_XCF_MODIFIED) != 0,
+ NULL);
+}
diff --git a/app/core/gimplinklayer.h b/app/core/gimplinklayer.h
new file mode 100644
index 0000000000..379d7959c0
--- /dev/null
+++ b/app/core/gimplinklayer.h
@@ -0,0 +1,74 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpLinkLayer
+ * Copyright (C) 2019 Jehan
+ *
+ * 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 .
+ */
+
+#ifndef __GIMP_LINK_LAYER_H__
+#define __GIMP_LINK_LAYER_H__
+
+
+#include "gimplayer.h"
+
+
+#define GIMP_TYPE_LINK_LAYER (gimp_link_layer_get_type ())
+#define GIMP_LINK_LAYER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_LINK_LAYER, GimpLinkLayer))
+#define GIMP_LINK_LAYER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_LINK_LAYER, GimpLinkLayerClass))
+#define GIMP_IS_LINK_LAYER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_LINK_LAYER))
+#define GIMP_IS_LINK_LAYER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_LINK_LAYER))
+#define GIMP_LINK_LAYER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_LINK_LAYER, GimpLinkLayerClass))
+
+
+typedef struct _GimpLinkLayerClass GimpLinkLayerClass;
+typedef struct _GimpLinkLayerPrivate GimpLinkLayerPrivate;
+
+struct _GimpLinkLayer
+{
+ GimpLayer layer;
+
+ GimpLinkLayerPrivate *p;
+};
+
+struct _GimpLinkLayerClass
+{
+ GimpLayerClass parent_class;
+};
+
+
+GType gimp_link_layer_get_type (void) G_GNUC_CONST;
+
+GimpLayer * gimp_link_layer_new (GimpImage *image,
+ GimpLink *link);
+
+GimpLink * gimp_link_layer_get_link (GimpLinkLayer *layer);
+gboolean gimp_link_layer_set_link (GimpLinkLayer *layer,
+ GimpLink *link,
+ gboolean push_undo);
+
+void gimp_link_layer_discard (GimpLinkLayer *layer);
+void gimp_link_layer_monitor (GimpLinkLayer *layer);
+gboolean gimp_item_is_link_layer (GimpItem *item);
+
+
+/* Only to be used for XCF loading/saving. */
+
+guint32 gimp_link_layer_get_xcf_flags (GimpLinkLayer *layer);
+void gimp_link_layer_from_layer (GimpLayer **layer,
+ GimpLink *link,
+ guint32 flags);
+
+#endif /* __GIMP_LINK_LAYER_H__ */
diff --git a/app/core/gimplinklayerundo.c b/app/core/gimplinklayerundo.c
new file mode 100644
index 0000000000..52e828ddbd
--- /dev/null
+++ b/app/core/gimplinklayerundo.c
@@ -0,0 +1,198 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Copyright (C) 2019 Jehan
+ *
+ * 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 "libgimpbase/gimpbase.h"
+
+#include "core-types.h"
+
+#include "gimpimage.h"
+#include "gimplayer.h"
+#include "gimplink.h"
+#include "gimplinklayer.h"
+#include "gimplinklayerundo.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_PREV_MODIFIED,
+ PROP_PREV_LINK
+};
+
+
+static void gimp_link_layer_undo_constructed (GObject *object);
+static void gimp_link_layer_undo_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_link_layer_undo_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static gint64 gimp_link_layer_undo_get_memsize (GimpObject *object,
+ gint64 *gui_size);
+
+static void gimp_link_layer_undo_pop (GimpUndo *undo,
+ GimpUndoMode undo_mode,
+ GimpUndoAccumulator *accum);
+
+
+G_DEFINE_TYPE (GimpLinkLayerUndo, gimp_link_layer_undo, GIMP_TYPE_ITEM_UNDO)
+
+#define parent_class gimp_link_layer_undo_parent_class
+
+
+static void
+gimp_link_layer_undo_class_init (GimpLinkLayerUndoClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
+ GimpUndoClass *undo_class = GIMP_UNDO_CLASS (klass);
+
+ object_class->constructed = gimp_link_layer_undo_constructed;
+ object_class->set_property = gimp_link_layer_undo_set_property;
+ object_class->get_property = gimp_link_layer_undo_get_property;
+
+ gimp_object_class->get_memsize = gimp_link_layer_undo_get_memsize;
+
+ undo_class->pop = gimp_link_layer_undo_pop;
+
+ g_object_class_install_property (object_class, PROP_PREV_MODIFIED,
+ g_param_spec_boolean ("prev-modified", NULL, NULL,
+ TRUE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (object_class, PROP_PREV_LINK,
+ g_param_spec_object ("prev-link",
+ NULL, NULL,
+ GIMP_TYPE_LINK,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+gimp_link_layer_undo_init (GimpLinkLayerUndo *undo)
+{
+}
+
+static void
+gimp_link_layer_undo_constructed (GObject *object)
+{
+ GimpLinkLayerUndo *undo = GIMP_LINK_LAYER_UNDO (object);
+ GimpLinkLayer *layer;
+
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ gimp_assert (GIMP_IS_LINK_LAYER (GIMP_ITEM_UNDO (object)->item));
+
+ layer = GIMP_LINK_LAYER (GIMP_ITEM_UNDO (undo)->item);
+
+ undo->link = gimp_link_layer_get_link (layer);
+ undo->modified = ! gimp_item_is_link_layer (GIMP_ITEM (layer));
+}
+
+static void
+gimp_link_layer_undo_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpLinkLayerUndo *undo = GIMP_LINK_LAYER_UNDO (object);
+
+ switch (property_id)
+ {
+ case PROP_PREV_MODIFIED:
+ undo->modified = g_value_get_boolean (value);
+ break;
+ case PROP_PREV_LINK:
+ undo->link = g_value_get_object (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_link_layer_undo_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpLinkLayerUndo *undo = GIMP_LINK_LAYER_UNDO (object);
+
+ switch (property_id)
+ {
+ case PROP_PREV_MODIFIED:
+ g_value_set_boolean (value, undo->modified);
+ break;
+ case PROP_PREV_LINK:
+ g_value_set_object (value, undo->link);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static gint64
+gimp_link_layer_undo_get_memsize (GimpObject *object,
+ gint64 *gui_size)
+{
+ GimpItemUndo *item_undo = GIMP_ITEM_UNDO (object);
+ gint64 memsize = 0;
+
+ if (! gimp_item_is_attached (item_undo->item))
+ memsize += gimp_object_get_memsize (GIMP_OBJECT (item_undo->item),
+ gui_size);
+
+ return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
+ gui_size);
+}
+
+static void
+gimp_link_layer_undo_pop (GimpUndo *undo,
+ GimpUndoMode undo_mode,
+ GimpUndoAccumulator *accum)
+{
+ GimpLinkLayerUndo *layer_undo = GIMP_LINK_LAYER_UNDO (undo);
+ GimpLinkLayer *layer = GIMP_LINK_LAYER (GIMP_ITEM_UNDO (undo)->item);
+ GimpLink *link;
+ gboolean modified;
+
+ GIMP_UNDO_CLASS (parent_class)->pop (undo, undo_mode, accum);
+
+ modified = ! gimp_item_is_link_layer (GIMP_ITEM (layer));
+ link = gimp_link_layer_get_link (layer);
+
+ gimp_link_layer_set_link (layer, layer_undo->link, FALSE);
+ g_object_set (layer, "modified", layer_undo->modified, NULL);
+
+ layer_undo->modified = modified;
+ layer_undo->link = link;
+}
diff --git a/app/core/gimplinklayerundo.h b/app/core/gimplinklayerundo.h
new file mode 100644
index 0000000000..7428bce3bf
--- /dev/null
+++ b/app/core/gimplinklayerundo.h
@@ -0,0 +1,56 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Copyright (C) 2019 Jehan
+ *
+ * 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 .
+ */
+
+#ifndef __GIMP_LINK_LAYER_UNDO_H__
+#define __GIMP_LINK_LAYER_UNDO_H__
+
+
+#include "gimpitemundo.h"
+
+
+#define GIMP_TYPE_LINK_LAYER_UNDO (gimp_link_layer_undo_get_type ())
+#define GIMP_LINK_LAYER_UNDO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_LINK_LAYER_UNDO, GimpLinkLayerUndo))
+#define GIMP_LINK_LAYER_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_LINK_LAYER_UNDO, GimpLinkLayerUndoClass))
+#define GIMP_IS_LINK_LAYER_UNDO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_LINK_LAYER_UNDO))
+#define GIMP_IS_LINK_LAYER_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_LINK_LAYER_UNDO))
+#define GIMP_LINK_LAYER_UNDO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_LINK_LAYER_UNDO, GimpLinkLayerUndoClass))
+
+
+typedef struct _GimpLinkLayerUndo GimpLinkLayerUndo;
+typedef struct _GimpLinkLayerUndoClass GimpLinkLayerUndoClass;
+
+struct _GimpLinkLayerUndo
+{
+ GimpItemUndo parent_instance;
+
+ gboolean modified;
+ GimpLink *link;
+};
+
+struct _GimpLinkLayerUndoClass
+{
+ GimpItemUndoClass parent_class;
+};
+
+
+GType gimp_link_layer_undo_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_LINK_LAYER_UNDO_H__ */
+
diff --git a/app/core/meson.build b/app/core/meson.build
index a7430dafc0..eefb789df1 100644
--- a/app/core/meson.build
+++ b/app/core/meson.build
@@ -193,6 +193,9 @@ libappcore_sources = [
'gimplayerstack.c',
'gimplayerundo.c',
'gimplineart.c',
+ 'gimplink.c',
+ 'gimplinklayer.c',
+ 'gimplinklayerundo.c',
'gimplist.c',
'gimpmaskundo.c',
'gimpmybrush-load.c',
diff --git a/app/dialogs/item-options-dialog.c b/app/dialogs/item-options-dialog.c
index 3413d7920d..f6d7f05b04 100644
--- a/app/dialogs/item-options-dialog.c
+++ b/app/dialogs/item-options-dialog.c
@@ -322,6 +322,21 @@ item_options_dialog_get_vbox (GtkWidget *dialog)
return private->left_vbox;
}
+GtkWidget *
+item_options_dialog_get_right_vbox (GtkWidget *dialog)
+{
+ ItemOptionsDialog *private;
+
+ g_return_val_if_fail (GIMP_IS_VIEWABLE_DIALOG (dialog), NULL);
+
+ private = g_object_get_data (G_OBJECT (dialog),
+ "item-options-dialog-private");
+
+ g_return_val_if_fail (private != NULL, NULL);
+
+ return private->right_vbox;
+}
+
GtkWidget *
item_options_dialog_get_grid (GtkWidget *dialog,
gint *next_row)
diff --git a/app/dialogs/item-options-dialog.h b/app/dialogs/item-options-dialog.h
index 15d7b2e205..1b56aee329 100644
--- a/app/dialogs/item-options-dialog.h
+++ b/app/dialogs/item-options-dialog.h
@@ -56,6 +56,7 @@ GtkWidget * item_options_dialog_new (GimpImage *image,
gpointer user_data);
GtkWidget * item_options_dialog_get_vbox (GtkWidget *dialog);
+GtkWidget * item_options_dialog_get_right_vbox (GtkWidget *dialog);
GtkWidget * item_options_dialog_get_grid (GtkWidget *dialog,
gint *next_row);
GtkWidget * item_options_dialog_get_name_entry (GtkWidget *dialog);
diff --git a/app/dialogs/layer-options-dialog.c b/app/dialogs/layer-options-dialog.c
index afcd354537..caf524baf1 100644
--- a/app/dialogs/layer-options-dialog.c
+++ b/app/dialogs/layer-options-dialog.c
@@ -32,12 +32,15 @@
#include "core/gimpdrawable-filters.h"
#include "core/gimpimage.h"
#include "core/gimplayer.h"
+#include "core/gimplink.h"
+#include "core/gimplinklayer.h"
#include "text/gimptext.h"
#include "text/gimptextlayer.h"
#include "widgets/gimpcontainertreeview.h"
#include "widgets/gimplayermodebox.h"
+#include "widgets/gimpopendialog.h"
#include "widgets/gimpviewabledialog.h"
#include "item-options-dialog.h"
@@ -50,6 +53,7 @@ typedef struct _LayerOptionsDialog LayerOptionsDialog;
struct _LayerOptionsDialog
{
+ Gimp *gimp;
GimpLayer *layer;
GimpLayerMode mode;
GimpLayerColorSpace blend_space;
@@ -68,6 +72,8 @@ struct _LayerOptionsDialog
GtkWidget *composite_mode_combo;
GtkWidget *size_se;
GtkWidget *offset_se;
+
+ GimpLink *link;
};
@@ -94,6 +100,11 @@ static void layer_options_dialog_rename_toggled (GtkWidget *widget,
LayerOptionsDialog *private);
+static void layer_options_fill_changed (GtkWidget *combo,
+ GtkWidget *file_select);
+static void layer_options_file_set (GtkFileChooserButton *widget,
+ LayerOptionsDialog *private);
+
/* public functions */
GtkWidget *
@@ -127,6 +138,7 @@ layer_options_dialog_new (GimpImage *image,
GtkWidget *grid;
GtkListStore *space_model;
GtkWidget *combo;
+ GtkWidget *file_select;
GtkWidget *scale;
GtkWidget *label;
GtkAdjustment *adjustment;
@@ -144,6 +156,7 @@ layer_options_dialog_new (GimpImage *image,
private = g_slice_new0 (LayerOptionsDialog);
+ private->gimp = image->gimp;
private->layer = layer;
private->mode = layer_mode;
private->blend_space = layer_blend_space;
@@ -374,15 +387,35 @@ layer_options_dialog_new (GimpImage *image,
if (! layer)
{
+ GtkWidget *right_vbox = item_options_dialog_get_right_vbox (dialog);
+ GtkWidget *open_dialog;
+
/* The fill type */
combo = gimp_enum_combo_box_new (GIMP_TYPE_FILL_TYPE);
- gimp_grid_attach_aligned (GTK_GRID (grid), 0, row++,
+ gimp_grid_attach_aligned (GTK_GRID (grid), 0, row,
_("_Fill with:"), 0.0, 0.5,
combo, 1);
+
+ /* File chooser dialog. */
+ open_dialog = gimp_open_dialog_new (private->gimp);
+ gtk_window_set_title (GTK_WINDOW (open_dialog),
+ _("Select Linked Image"));
+
+ /* File chooser button. */
+ file_select = gtk_file_chooser_button_new_with_dialog (open_dialog);
+ gtk_box_pack_end (GTK_BOX (right_vbox), file_select, FALSE, FALSE, 1);
+
gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
private->fill_type,
G_CALLBACK (gimp_int_combo_box_get_active),
&private->fill_type, NULL);
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (layer_options_fill_changed),
+ file_select);
+ g_signal_connect (file_select, "file-set",
+ G_CALLBACK (layer_options_file_set),
+ private);
+ layer_options_fill_changed (combo, file_select);
}
if (layer)
@@ -402,6 +435,31 @@ layer_options_dialog_new (GimpImage *image,
GIMP_VIEW_SIZE_SMALL, 0);
gtk_container_add (GTK_CONTAINER (frame), view);
gtk_widget_show (view);
+
+ if (GIMP_IS_LINK_LAYER (layer))
+ {
+ GtkWidget *open_dialog;
+ GimpLink *link;
+
+ /* File chooser dialog. */
+ open_dialog = gimp_open_dialog_new (private->gimp);
+ gtk_window_set_title (GTK_WINDOW (open_dialog),
+ _("Select Linked Image"));
+
+ /* File chooser button. */
+ file_select = gtk_file_chooser_button_new_with_dialog (open_dialog);
+ link = gimp_link_layer_get_link (GIMP_LINK_LAYER (layer));
+ gtk_file_chooser_set_file (GTK_FILE_CHOOSER (file_select),
+ gimp_link_get_file (link), NULL);
+ gtk_widget_show (file_select);
+ gimp_grid_attach_aligned (GTK_GRID (grid), 0, row++,
+ _("_Linked image:"), 0.0, 0.5,
+ file_select, 1);
+
+ g_signal_connect (file_select, "file-set",
+ G_CALLBACK (layer_options_file_set),
+ private);
+ }
}
button = item_options_dialog_get_lock_position (dialog);
@@ -502,6 +560,7 @@ layer_options_dialog_callback (GtkWidget *dialog,
private->composite_mode,
private->opacity / 100.0,
private->fill_type,
+ private->link,
width,
height,
offset_x,
@@ -574,3 +633,44 @@ layer_options_dialog_rename_toggled (GtkWidget *widget,
}
}
}
+
+static void
+layer_options_fill_changed (GtkWidget *combo,
+ GtkWidget *file_select)
+{
+ gint value = GIMP_FILL_FOREGROUND;
+
+ g_return_if_fail (GIMP_IS_ENUM_COMBO_BOX (combo));
+ g_return_if_fail (GTK_IS_WIDGET (file_select));
+
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (combo), &value);
+ gtk_widget_set_visible (file_select, (value == GIMP_FILL_LINK));
+}
+
+static void
+layer_options_file_set (GtkFileChooserButton *widget,
+ LayerOptionsDialog *private)
+{
+ GimpLink *link = NULL;
+ GFile *file;
+
+ file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (widget));
+ if (file)
+ {
+ link = gimp_link_new (private->gimp, file);
+ if (gimp_link_is_broken (link, TRUE))
+ {
+ g_clear_object (&link);
+ g_signal_handlers_block_by_func (widget,
+ G_CALLBACK (layer_options_file_set),
+ private);
+ gtk_file_chooser_unselect_file (GTK_FILE_CHOOSER (widget), file);
+ g_signal_handlers_unblock_by_func (widget,
+ G_CALLBACK (layer_options_file_set),
+ private);
+ }
+ }
+ g_clear_object (&file);
+
+ private->link = link;
+}
diff --git a/app/dialogs/layer-options-dialog.h b/app/dialogs/layer-options-dialog.h
index 0cac5e8013..5fbf9a2b84 100644
--- a/app/dialogs/layer-options-dialog.h
+++ b/app/dialogs/layer-options-dialog.h
@@ -30,6 +30,7 @@ typedef void (* GimpLayerOptionsCallback) (GtkWidget *dialog,
GimpLayerCompositeMode layer_composite_mode,
gdouble layer_opacity,
GimpFillType layer_fill_type,
+ GimpLink *link,
gint layer_width,
gint layer_height,
gint layer_offset_x,
diff --git a/app/file/file-open.c b/app/file/file-open.c
index c6dd4eb05f..2137612c66 100644
--- a/app/file/file-open.c
+++ b/app/file/file-open.c
@@ -76,6 +76,7 @@ file_open_image (Gimp *gimp,
gboolean as_new,
GimpPlugInProcedure *file_proc,
GimpRunMode run_mode,
+ gboolean *file_proc_handles_vector,
GimpPDBStatusType *status,
const gchar **mime_type,
GError **error)
@@ -210,6 +211,9 @@ file_open_image (Gimp *gimp,
return NULL;
}
+ if (file_proc_handles_vector)
+ *file_proc_handles_vector = file_proc->handles_vector;
+
if (progress)
g_object_add_weak_pointer (G_OBJECT (progress), (gpointer) &progress);
@@ -534,6 +538,7 @@ file_open_with_proc_and_display (Gimp *gimp,
as_new,
file_proc,
run_mode,
+ NULL,
status,
&mime_type,
error);
@@ -636,7 +641,7 @@ file_open_layers (Gimp *gimp,
FALSE,
file_proc,
run_mode,
- status, &mime_type, error);
+ NULL, status, &mime_type, error);
if (new_image)
{
diff --git a/app/file/file-open.h b/app/file/file-open.h
index 600af8b58a..2c50571fa6 100644
--- a/app/file/file-open.h
+++ b/app/file/file-open.h
@@ -30,6 +30,7 @@ GimpImage * file_open_image (Gimp *gimp,
gboolean as_new,
GimpPlugInProcedure *file_proc,
GimpRunMode run_mode,
+ gboolean *file_proc_handles_vector,
GimpPDBStatusType *status,
const gchar **mime_type,
GError **error);
diff --git a/app/tests/test-save-and-export.c b/app/tests/test-save-and-export.c
index 73e1d36193..ffaf325252 100644
--- a/app/tests/test-save-and-export.c
+++ b/app/tests/test-save-and-export.c
@@ -122,6 +122,7 @@ opened_xcf_file_files (gconstpointer data)
FALSE /*as_new*/,
NULL /*file_proc*/,
GIMP_RUN_NONINTERACTIVE,
+ NULL, /* file_proc_handles_vector */
&status,
NULL /*mime_type*/,
NULL /*error*/);
@@ -163,6 +164,7 @@ imported_file_files (gconstpointer data)
FALSE /*as_new*/,
NULL /*file_proc*/,
GIMP_RUN_NONINTERACTIVE,
+ NULL, /* file_proc_handles_vector */
&status,
NULL /*mime_type*/,
NULL /*error*/);
@@ -212,6 +214,7 @@ saved_imported_file_files (gconstpointer data)
FALSE /*as_new*/,
NULL /*file_proc*/,
GIMP_RUN_NONINTERACTIVE,
+ NULL, /* file_proc_handles_vector */
&status,
NULL /*mime_type*/,
NULL /*error*/);
@@ -320,6 +323,7 @@ clear_import_file_after_export (gconstpointer data)
FALSE /*as_new*/,
NULL /*file_proc*/,
GIMP_RUN_NONINTERACTIVE,
+ NULL, /* file_proc_handles_vector */
&status,
NULL /*mime_type*/,
NULL /*error*/);
diff --git a/app/tests/test-xcf.c b/app/tests/test-xcf.c
index c4138bf583..161bd6caa3 100644
--- a/app/tests/test-xcf.c
+++ b/app/tests/test-xcf.c
@@ -282,6 +282,7 @@ gimp_test_load_image (Gimp *gimp,
FALSE /*as_new*/,
proc,
GIMP_RUN_NONINTERACTIVE,
+ NULL, /* file_proc_handles_vector */
&unused /*status*/,
NULL /*mime_type*/,
NULL /*error*/);
diff --git a/app/widgets/gimpviewrendererlayer.c b/app/widgets/gimpviewrendererlayer.c
index 6c4c6cb5ee..f5266bf0fe 100644
--- a/app/widgets/gimpviewrendererlayer.c
+++ b/app/widgets/gimpviewrendererlayer.c
@@ -32,6 +32,7 @@
#include "core/gimp.h"
#include "core/gimpcontainer.h"
#include "core/gimpimage.h"
+#include "core/gimplinklayer.h"
#include "text/gimptextlayer.h"
@@ -71,7 +72,8 @@ gimp_view_renderer_layer_render (GimpViewRenderer *renderer,
{
icon_name = GIMP_ICON_LAYER_FLOATING_SELECTION;
}
- else if (gimp_item_is_text_layer (GIMP_ITEM (renderer->viewable)))
+ else if (gimp_item_is_text_layer (GIMP_ITEM (renderer->viewable)) ||
+ gimp_item_is_link_layer (GIMP_ITEM (renderer->viewable)))
{
icon_name = gimp_viewable_get_icon_name (renderer->viewable);
}
diff --git a/app/xcf/xcf-load.c b/app/xcf/xcf-load.c
index a530db281c..f880459358 100644
--- a/app/xcf/xcf-load.c
+++ b/app/xcf/xcf-load.c
@@ -59,6 +59,8 @@
#include "core/gimplayer-floating-selection.h"
#include "core/gimplayer-new.h"
#include "core/gimplayermask.h"
+#include "core/gimplink.h"
+#include "core/gimplinklayer.h"
#include "core/gimpparasitelist.h"
#include "core/gimpprogress.h"
#include "core/gimpselection.h"
@@ -131,7 +133,8 @@ static gboolean xcf_load_layer_props (XcfInfo *info,
static gboolean xcf_check_layer_props (XcfInfo *info,
GList **item_path,
gboolean *is_group_layer,
- gboolean *is_text_layer);
+ gboolean *is_text_layer,
+ gboolean *is_link_layer);
static gboolean xcf_load_channel_props (XcfInfo *info,
GimpImage *image,
GimpChannel **channel,
@@ -1961,6 +1964,30 @@ xcf_load_layer_props (XcfInfo *info,
xcf_read_int32 (info, text_layer_flags, 1);
break;
+ case PROP_LINK_LAYER_DATA:
+ {
+ GimpLink *link;
+ gchar *path;
+ guint32 flags;
+ gboolean is_selected_layer;
+
+ xcf_read_int32 (info, &flags, 1);
+ xcf_read_string (info, &path, 1);
+
+ link = gimp_link_new (info->gimp, g_file_new_for_path (path));
+ g_free (path);
+
+ is_selected_layer = (g_list_find (info->selected_layers, *layer ) != NULL);
+ if (is_selected_layer)
+ info->selected_layers = g_list_remove (info->selected_layers, *layer);
+
+ gimp_link_layer_from_layer (layer, link, flags);
+
+ if (is_selected_layer)
+ info->selected_layers = g_list_prepend (info->selected_layers, *layer);
+ }
+ break;
+
case PROP_GROUP_ITEM:
{
GimpLayer *group;
@@ -2062,13 +2089,15 @@ static gboolean
xcf_check_layer_props (XcfInfo *info,
GList **item_path,
gboolean *is_group_layer,
- gboolean *is_text_layer)
+ gboolean *is_text_layer,
+ gboolean *is_link_layer)
{
PropType prop_type;
guint32 prop_size;
g_return_val_if_fail (*is_group_layer == FALSE, FALSE);
g_return_val_if_fail (*is_text_layer == FALSE, FALSE);
+ g_return_val_if_fail (*is_link_layer == TRUE, FALSE);
while (TRUE)
{
@@ -2087,6 +2116,13 @@ xcf_check_layer_props (XcfInfo *info,
return FALSE;
break;
+ case PROP_LINK_LAYER_DATA:
+ *is_link_layer = TRUE;
+
+ if (! xcf_skip_unknown_prop (info, prop_size))
+ return FALSE;
+ break;
+
case PROP_GROUP_ITEM:
case PROP_GROUP_ITEM_FLAGS:
*is_group_layer = TRUE;
@@ -3095,16 +3131,18 @@ xcf_load_layer (XcfInfo *info,
{
gboolean is_group_layer = FALSE;
gboolean is_text_layer = FALSE;
+ gboolean is_link_layer = FALSE;
goffset saved_pos;
saved_pos = info->cp;
/* Load item path and check if this is a group or text layer. */
- xcf_check_layer_props (info, item_path, &is_group_layer, &is_text_layer);
- if ((is_text_layer || is_group_layer) &&
+ xcf_check_layer_props (info, item_path, &is_group_layer,
+ &is_text_layer, &is_link_layer);
+ if ((is_text_layer || is_group_layer || is_link_layer) &&
xcf_seek_pos (info, saved_pos, NULL))
{
/* Something is wrong, but leave a chance to the layer because
- * anyway group and text layer depends on their contents.
+ * anyway group, text and link layer depends on their contents.
*/
width = height = 1;
g_clear_pointer (item_path, g_list_free);
diff --git a/app/xcf/xcf-private.h b/app/xcf/xcf-private.h
index 944b0388c9..a65435207e 100644
--- a/app/xcf/xcf-private.h
+++ b/app/xcf/xcf-private.h
@@ -72,7 +72,8 @@ typedef enum
PROP_SELECTED_PATH = 43,
PROP_FILTER_REGION = 44,
PROP_FILTER_ARGUMENT = 45,
- PROP_FILTER_CLIP = 46
+ PROP_FILTER_CLIP = 46,
+ PROP_LINK_LAYER_DATA = 47,
} PropType;
typedef enum
diff --git a/app/xcf/xcf-save.c b/app/xcf/xcf-save.c
index 39b162b7a6..80c3841e52 100644
--- a/app/xcf/xcf-save.c
+++ b/app/xcf/xcf-save.c
@@ -54,6 +54,8 @@
#include "core/gimpitemlist.h"
#include "core/gimplayer.h"
#include "core/gimplayermask.h"
+#include "core/gimplink.h"
+#include "core/gimplinklayer.h"
#include "core/gimplist.h"
#include "core/gimpparasitelist.h"
#include "core/gimpprogress.h"
@@ -721,6 +723,9 @@ xcf_save_layer_props (XcfInfo *info,
flags), ;);
}
+ if (GIMP_IS_LINK_LAYER (layer))
+ xcf_check_error (xcf_save_prop (info, image, PROP_LINK_LAYER_DATA, error, layer), ;);
+
if (gimp_viewable_get_children (GIMP_VIEWABLE (layer)))
{
gint32 flags = 0;
@@ -1664,6 +1669,27 @@ xcf_save_prop (XcfInfo *info,
}
break;
+ case PROP_LINK_LAYER_DATA:
+ {
+ GimpLinkLayer *layer = va_arg (args, GimpLinkLayer *);
+ GFile *file;
+ const gchar *path;
+ guint32 flags;
+
+ flags = gimp_link_layer_get_xcf_flags (layer);
+ file = gimp_link_get_file (gimp_link_layer_get_link (layer));
+ path = g_file_peek_path (file);
+
+ size = 4 + strlen (path) ? strlen (path) + 5 : 4;
+
+ xcf_write_prop_type_check_error (info, prop_type, va_end (args));
+ xcf_write_int32_check_error (info, &size, 1, va_end (args));
+
+ xcf_write_int32_check_error (info, &flags, 1, va_end (args));
+ xcf_write_string_check_error (info, (gchar **) &path, 1, va_end (args));
+ }
+ break;
+
case PROP_ITEM_PATH:
{
GList *path = va_arg (args, GList *);
diff --git a/libgimpbase/gimpbaseenums.c b/libgimpbase/gimpbaseenums.c
index eee17911a9..b7e5c0ef86 100644
--- a/libgimpbase/gimpbaseenums.c
+++ b/libgimpbase/gimpbaseenums.c
@@ -511,6 +511,7 @@ gimp_fill_type_get_type (void)
{ GIMP_FILL_WHITE, "GIMP_FILL_WHITE", "white" },
{ GIMP_FILL_TRANSPARENT, "GIMP_FILL_TRANSPARENT", "transparent" },
{ GIMP_FILL_PATTERN, "GIMP_FILL_PATTERN", "pattern" },
+ { GIMP_FILL_LINK, "GIMP_FILL_LINK", "link" },
{ 0, NULL, NULL }
};
@@ -522,6 +523,7 @@ gimp_fill_type_get_type (void)
{ GIMP_FILL_WHITE, NC_("fill-type", "White"), NULL },
{ GIMP_FILL_TRANSPARENT, NC_("fill-type", "Transparency"), NULL },
{ GIMP_FILL_PATTERN, NC_("fill-type", "Pattern"), NULL },
+ { GIMP_FILL_LINK, NC_("fill-type", "Image link"), NULL },
{ 0, NULL, NULL }
};
diff --git a/libgimpbase/gimpbaseenums.h b/libgimpbase/gimpbaseenums.h
index 5960533e9f..72cc5529ae 100644
--- a/libgimpbase/gimpbaseenums.h
+++ b/libgimpbase/gimpbaseenums.h
@@ -368,6 +368,7 @@ typedef enum
* @GIMP_FILL_WHITE: White
* @GIMP_FILL_TRANSPARENT: Transparency
* @GIMP_FILL_PATTERN: Pattern
+ * @GIMP_FILL_LINK: Image link
*
* Types of filling.
**/
@@ -382,7 +383,8 @@ typedef enum
GIMP_FILL_CIELAB_MIDDLE_GRAY, /*< desc="Middle Gray (CIELAB)" >*/
GIMP_FILL_WHITE, /*< desc="White" >*/
GIMP_FILL_TRANSPARENT, /*< desc="Transparency" >*/
- GIMP_FILL_PATTERN /*< desc="Pattern" >*/
+ GIMP_FILL_PATTERN, /*< desc="Pattern" >*/
+ GIMP_FILL_LINK /*< desc="Image link" >*/
} GimpFillType;
diff --git a/menus/layers-menu.ui b/menus/layers-menu.ui
index e780d64387..8027f58002 100644
--- a/menus/layers-menu.ui
+++ b/menus/layers-menu.ui
@@ -58,6 +58,10 @@
- app.layers-merge-group
- app.layers-delete
+
+ - app.layers-link-discard
+ - app.layers-link-monitor
+
- app.layers-text-discard
- app.layers-text-to-vectors
diff --git a/pdb/enums.pl b/pdb/enums.pl
index e34077c828..c9be03859f 100644
--- a/pdb/enums.pl
+++ b/pdb/enums.pl
@@ -168,13 +168,15 @@ package Gimp::CodeGen::enums;
header => 'libgimpbase/gimpbaseenums.h',
symbols => [ qw(GIMP_FILL_FOREGROUND GIMP_FILL_BACKGROUND
GIMP_FILL_CIELAB_MIDDLE_GRAY GIMP_FILL_WHITE
- GIMP_FILL_TRANSPARENT GIMP_FILL_PATTERN) ],
+ GIMP_FILL_TRANSPARENT GIMP_FILL_PATTERN
+ GIMP_FILL_LINK) ],
mapping => { GIMP_FILL_FOREGROUND => '0',
GIMP_FILL_BACKGROUND => '1',
GIMP_FILL_CIELAB_MIDDLE_GRAY => '2',
GIMP_FILL_WHITE => '3',
GIMP_FILL_TRANSPARENT => '4',
- GIMP_FILL_PATTERN => '5' }
+ GIMP_FILL_PATTERN => '5',
+ GIMP_FILL_LINK => '6' }
},
GimpForegroundExtractMode =>
{ contig => 1,