mirror of
https://gitlab.gnome.org/GNOME/gimp.git
synced 2025-07-03 17:33:25 +00:00
Merge branch 'wip/Jehan/link-layers-multi' into 'master'
Draft: Link layer See merge request GNOME/gimp!823
This commit is contained in:
commit
e008b9f9f3
34 changed files with 2067 additions and 22 deletions
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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" >*/
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
403
app/core/gimplink.c
Normal 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
79
app/core/gimplink.h
Normal 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
830
app/core/gimplinklayer.c
Normal 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
74
app/core/gimplinklayer.h
Normal 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__ */
|
198
app/core/gimplinklayerundo.c
Normal file
198
app/core/gimplinklayerundo.c
Normal 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;
|
||||
}
|
56
app/core/gimplinklayerundo.h
Normal file
56
app/core/gimplinklayerundo.h
Normal 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__ */
|
||||
|
|
@ -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',
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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*/);
|
||||
|
|
|
@ -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*/);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -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 }
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue