Merge branch 'wip/Jehan/link-layers-multi' into 'master'

Draft: Link layer

See merge request GNOME/gimp!823
This commit is contained in:
Jehan 2025-07-02 21:24:25 +00:00
commit e008b9f9f3
34 changed files with 2067 additions and 22 deletions

View file

@ -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)
{

View file

@ -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);

View file

@ -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));
}

View file

@ -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);

View file

@ -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 },

View file

@ -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" >*/

View file

@ -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 */

View file

@ -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 */

View file

@ -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 */

View file

@ -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);

View file

@ -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,

403
app/core/gimplink.c Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <cairo.h>
#include <gegl.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#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;
}

79
app/core/gimplink.h Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
*/
#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__ */

830
app/core/gimplinklayer.c Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <cairo.h>
#include <gegl.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#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);
}

74
app/core/gimplinklayer.h Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
*/
#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__ */

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
#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;
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
#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__ */

View file

@ -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',

View file

@ -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)

View file

@ -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);

View file

@ -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;
}

View file

@ -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,

View file

@ -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)
{

View file

@ -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);

View file

@ -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*/);

View file

@ -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*/);

View file

@ -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);
}

View file

@ -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);

View file

@ -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

View file

@ -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 *);

View file

@ -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 }
};

View file

@ -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;

View file

@ -58,6 +58,10 @@
<item><attribute name="action">app.layers-merge-group</attribute></item>
<item><attribute name="action">app.layers-delete</attribute></item>
</section>
<section>
<item><attribute name="action">app.layers-link-discard</attribute></item>
<item><attribute name="action">app.layers-link-monitor</attribute></item>
</section>
<section>
<item><attribute name="action">app.layers-text-discard</attribute></item>
<item><attribute name="action">app.layers-text-to-vectors</attribute></item>

View file

@ -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,