2006-12-09 21:33:38 +00:00
|
|
|
/* GIMP - The GNU Image Manipulation Program
|
2001-04-20 16:27:44 +00:00
|
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
|
|
*
|
2009-01-17 22:28:01 +00:00
|
|
|
* This program is free software: you can redistribute it and/or modify
|
2001-04-20 16:27:44 +00:00
|
|
|
* it under the terms of the GNU General Public License as published by
|
2009-01-17 22:28:01 +00:00
|
|
|
* the Free Software Foundation; either version 3 of the License, or
|
2001-04-20 16:27:44 +00:00
|
|
|
* (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
|
2018-07-11 23:27:07 +02:00
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
2001-04-20 16:27:44 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
2003-08-31 18:10:15 +00:00
|
|
|
#include <string.h>
|
2001-04-20 16:27:44 +00:00
|
|
|
|
2008-10-09 20:24:04 +00:00
|
|
|
#include <gegl.h>
|
2001-04-20 16:27:44 +00:00
|
|
|
#include <gtk/gtk.h>
|
|
|
|
|
|
|
|
#include "libgimpmath/gimpmath.h"
|
2001-05-21 20:30:16 +00:00
|
|
|
#include "libgimpbase/gimpbase.h"
|
2016-05-04 00:00:28 +02:00
|
|
|
#include "libgimpcolor/gimpcolor.h"
|
2001-04-20 16:27:44 +00:00
|
|
|
#include "libgimpwidgets/gimpwidgets.h"
|
|
|
|
|
2004-04-20 13:25:55 +00:00
|
|
|
#include "actions-types.h"
|
2001-04-20 16:27:44 +00:00
|
|
|
|
2016-08-23 19:18:20 +02:00
|
|
|
#include "config/gimpdialogconfig.h"
|
2004-10-12 14:59:14 +00:00
|
|
|
|
2017-02-20 23:51:32 +01:00
|
|
|
#include "operations/layer-modes/gimp-layer-modes.h"
|
|
|
|
|
2001-07-07 12:17:23 +00:00
|
|
|
#include "core/gimp.h"
|
2012-03-17 18:30:13 +01:00
|
|
|
#include "core/gimpchannel.h"
|
2020-05-20 18:16:30 +02:00
|
|
|
#include "core/gimpchannel-combine.h"
|
2016-08-23 19:18:20 +02:00
|
|
|
#include "core/gimpcontainer.h"
|
2001-12-03 17:59:48 +00:00
|
|
|
#include "core/gimpcontext.h"
|
2016-03-08 01:38:05 +01:00
|
|
|
#include "core/gimpdrawable-fill.h"
|
2023-06-19 14:54:21 +00:00
|
|
|
#include "core/gimpdrawable-filters.h"
|
|
|
|
#include "core/gimpdrawablefilter.h"
|
2009-08-06 18:37:54 +02:00
|
|
|
#include "core/gimpgrouplayer.h"
|
2001-05-09 02:32:03 +00:00
|
|
|
#include "core/gimpimage.h"
|
include the new "paint-funcs/paint-funcs-types.h".
2001-11-28 Michael Natterer <mitch@gimp.org>
* app/base/base-types.h: include the new
"paint-funcs/paint-funcs-types.h".
* app/paint-funcs/Makefile.am
* app/paint-funcs/paint-funcs-types.h: new file. Includes
"base/base-types.h".
* app/paint-funcs/paint-funcs.[ch]: removed the enums here,
include "paint-funcs-types.h".
* app/widgets/widgets-types.h: include "display/display-types.h"
* app/display/display-types.h: include "widgets/widgets-types.h".
* app/tools/tools-types.h: include "display/display-types.h"
* app/gui/gui-types.h: include "tools/tools-types.h".
The order of namespaces/dependencies should be (but is not):
(base, paint-funcs) -> (core, file, xcf, pdb) ->
(widgets, display) -> tools -> gui
* app/path.c: include "tools/tools-types.h".
* app/core/Makefile.am
* app/core/gimpimage-guides.[ch]
* app/core/gimpimage-merge.[ch]
* app/core/gimpimage-resize.[ch]
* app/core/gimpimage-scale.[ch]: new files.
* app/core/gimpimage.[ch]: removed the stuff which is in the new
files. Reordered all functions in both the .h and .c files,
commented the groups of functions.
* app/core/gimpcontainer.c: create the handler_id using a counter,
not the address of a pointer, because the address *may* be the
same twice, added debugging output.
* app/core/gimpviewable.[ch]: added primitive support for getting
a preview GdkPixbuf.
* app/nav_window.c
* app/undo.c
* app/undo_history.c
* app/core/gimpimage-duplicate.c
* app/core/gimpimage-mask.[ch]
* app/display/gimpdisplay.c
* app/display/gimpdisplayshell-callbacks.c
* app/display/gimpdisplayshell-dnd.c
* app/display/gimpdisplayshell-render.c
* app/display/gimpdisplayshell-scale.c
* app/display/gimpdisplayshell-scroll.c
* app/gui/image-commands.c
* app/gui/info-window.c
* app/gui/layers-commands.c
* app/gui/palette-import-dialog.c
* app/tools/gimpbycolorselecttool.c
* app/tools/gimpeditselectiontool.c
* app/tools/gimpmeasuretool.c
* app/tools/gimpmovetool.c
* app/widgets/gimpcontainerview-utils.c
* app/xcf/xcf-load.c: changed accordingly, some cleanup.
* tools/pdbgen/pdb/guides.pdb
* tools/pdbgen/pdb/image.pdb: changed accordingly, reordered functions.
* app/plug_in.c: set the labels of the "Repeat" and "Re-Show" menu
items to the name of the last plug-in (Fixes #50986).
* app/display/gimpdisplayshell.[ch]: set the labels of "Undo" and
"Redo" to the resp. undo names. Much simplified the WM icon stuff
by removing most code and using gimp_viewable_get_new_preview_pixbuf().
* app/widgets/gimpbrushfactoryview.c: forgot to assign the GQuark
returned by gimp_container_add_handler().
* app/pdb/guides_cmds.c
* app/pdb/image_cmds.c
* libgimp/gimpimage_pdb.[ch]: regenerated.
2001-11-28 17:51:06 +00:00
|
|
|
#include "core/gimpimage-merge.h"
|
2003-02-13 11:23:50 +00:00
|
|
|
#include "core/gimpimage-undo.h"
|
2005-03-24 17:10:03 +00:00
|
|
|
#include "core/gimpimage-undo-push.h"
|
2004-06-23 00:23:25 +00:00
|
|
|
#include "core/gimpitemundo.h"
|
2017-02-02 00:38:25 +01:00
|
|
|
#include "core/gimplayerpropundo.h"
|
2016-05-20 16:46:26 +02:00
|
|
|
#include "core/gimplayer-floating-selection.h"
|
2015-06-17 13:21:01 +02:00
|
|
|
#include "core/gimplayer-new.h"
|
2023-06-19 14:54:21 +00:00
|
|
|
#include "core/gimplist.h"
|
2008-11-04 12:07:17 +00:00
|
|
|
#include "core/gimppickable.h"
|
2012-09-22 20:35:36 +02:00
|
|
|
#include "core/gimppickable-auto-shrink.h"
|
2004-01-13 19:08:16 +00:00
|
|
|
#include "core/gimptoolinfo.h"
|
2004-06-23 00:23:25 +00:00
|
|
|
#include "core/gimpundostack.h"
|
2004-08-10 18:47:21 +00:00
|
|
|
#include "core/gimpprogress.h"
|
2001-05-09 02:32:03 +00:00
|
|
|
|
2004-03-18 18:00:38 +00:00
|
|
|
#include "text/gimptext.h"
|
2024-07-13 05:07:57 +00:00
|
|
|
#include "text/gimptext-path.h"
|
2004-01-05 00:28:12 +00:00
|
|
|
#include "text/gimptextlayer.h"
|
|
|
|
|
2024-07-12 06:16:25 +00:00
|
|
|
#include "vectors/gimppath.h"
|
2024-07-13 05:07:57 +00:00
|
|
|
#include "vectors/gimppath-warp.h"
|
2018-11-13 11:30:49 +09:00
|
|
|
#include "vectors/gimpstroke.h"
|
2006-05-13 19:48:18 +00:00
|
|
|
|
2004-10-16 15:48:23 +00:00
|
|
|
#include "widgets/gimpaction.h"
|
2004-01-31 20:23:53 +00:00
|
|
|
#include "widgets/gimpdock.h"
|
2003-08-21 15:54:47 +00:00
|
|
|
#include "widgets/gimphelp-ids.h"
|
2004-10-12 14:59:14 +00:00
|
|
|
#include "widgets/gimpprogressdialog.h"
|
2001-05-25 16:04:54 +00:00
|
|
|
|
2002-12-10 16:38:16 +00:00
|
|
|
#include "display/gimpdisplay.h"
|
2004-06-01 22:04:20 +00:00
|
|
|
#include "display/gimpdisplayshell.h"
|
2010-06-06 16:44:36 +02:00
|
|
|
#include "display/gimpimagewindow.h"
|
2002-12-10 16:38:16 +00:00
|
|
|
|
2004-01-13 19:08:16 +00:00
|
|
|
#include "tools/gimptexttool.h"
|
|
|
|
#include "tools/tool_manager.h"
|
|
|
|
|
2016-09-12 00:03:22 +02:00
|
|
|
#include "dialogs/dialogs.h"
|
2004-10-23 14:02:53 +00:00
|
|
|
#include "dialogs/layer-add-mask-dialog.h"
|
2004-10-22 22:28:45 +00:00
|
|
|
#include "dialogs/layer-options-dialog.h"
|
2004-09-13 15:15:23 +00:00
|
|
|
#include "dialogs/resize-dialog.h"
|
2004-10-12 14:59:14 +00:00
|
|
|
#include "dialogs/scale-dialog.h"
|
2004-04-20 13:25:55 +00:00
|
|
|
|
2004-05-03 14:03:51 +00:00
|
|
|
#include "actions.h"
|
2016-10-29 16:50:13 +02:00
|
|
|
#include "items-commands.h"
|
2001-04-20 16:27:44 +00:00
|
|
|
#include "layers-commands.h"
|
|
|
|
|
2003-03-25 16:38:19 +00:00
|
|
|
#include "gimp-intl.h"
|
2001-04-20 16:27:44 +00:00
|
|
|
|
|
|
|
|
2001-11-30 14:41:56 +00:00
|
|
|
/* local function prototypes */
|
|
|
|
|
2016-09-24 21:10:07 +02:00
|
|
|
static void layers_new_callback (GtkWidget *dialog,
|
|
|
|
GimpImage *image,
|
|
|
|
GimpLayer *layer,
|
|
|
|
GimpContext *context,
|
|
|
|
const gchar *layer_name,
|
2017-01-08 23:00:19 +01:00
|
|
|
GimpLayerMode layer_mode,
|
2017-02-12 23:49:26 +01:00
|
|
|
GimpLayerColorSpace layer_blend_space,
|
|
|
|
GimpLayerColorSpace layer_composite_space,
|
|
|
|
GimpLayerCompositeMode layer_composite_mode,
|
2016-10-21 22:54:10 +02:00
|
|
|
gdouble layer_opacity,
|
2016-09-24 21:10:07 +02:00
|
|
|
GimpFillType layer_fill_type,
|
|
|
|
gint layer_width,
|
|
|
|
gint layer_height,
|
2016-10-21 22:54:10 +02:00
|
|
|
gint layer_offset_x,
|
|
|
|
gint layer_offset_y,
|
2016-10-23 22:45:08 +02:00
|
|
|
gboolean layer_visible,
|
2016-10-29 16:50:13 +02:00
|
|
|
GimpColorTag layer_color_tag,
|
2016-10-23 22:45:08 +02:00
|
|
|
gboolean layer_lock_pixels,
|
|
|
|
gboolean layer_lock_position,
|
2023-05-26 02:46:53 +02:00
|
|
|
gboolean layer_lock_visibility,
|
2016-10-23 22:45:08 +02:00
|
|
|
gboolean layer_lock_alpha,
|
2016-09-24 21:10:07 +02:00
|
|
|
gboolean rename_text_layer,
|
|
|
|
gpointer user_data);
|
|
|
|
static void layers_edit_attributes_callback (GtkWidget *dialog,
|
|
|
|
GimpImage *image,
|
|
|
|
GimpLayer *layer,
|
|
|
|
GimpContext *context,
|
|
|
|
const gchar *layer_name,
|
2017-01-08 23:00:19 +01:00
|
|
|
GimpLayerMode layer_mode,
|
2017-02-12 23:49:26 +01:00
|
|
|
GimpLayerColorSpace layer_blend_space,
|
|
|
|
GimpLayerColorSpace layer_composite_space,
|
|
|
|
GimpLayerCompositeMode layer_composite_mode,
|
2016-10-21 22:54:10 +02:00
|
|
|
gdouble layer_opacity,
|
2016-09-24 21:10:07 +02:00
|
|
|
GimpFillType layer_fill_type,
|
|
|
|
gint layer_width,
|
|
|
|
gint layer_height,
|
2016-10-21 22:54:10 +02:00
|
|
|
gint layer_offset_x,
|
|
|
|
gint layer_offset_y,
|
2016-10-23 22:45:08 +02:00
|
|
|
gboolean layer_visible,
|
2016-10-29 16:50:13 +02:00
|
|
|
GimpColorTag layer_color_tag,
|
2016-10-23 22:45:08 +02:00
|
|
|
gboolean layer_lock_pixels,
|
|
|
|
gboolean layer_lock_position,
|
2023-05-26 02:46:53 +02:00
|
|
|
gboolean layer_lock_visibility,
|
2016-10-23 22:45:08 +02:00
|
|
|
gboolean layer_lock_alpha,
|
2016-09-24 21:10:07 +02:00
|
|
|
gboolean rename_text_layer,
|
|
|
|
gpointer user_data);
|
|
|
|
static void layers_add_mask_callback (GtkWidget *dialog,
|
2020-05-02 01:42:04 +02:00
|
|
|
GList *layers,
|
2016-09-24 21:10:07 +02:00
|
|
|
GimpAddMaskType add_mask_type,
|
|
|
|
GimpChannel *channel,
|
|
|
|
gboolean invert,
|
|
|
|
gpointer user_data);
|
2016-09-25 22:18:37 +02:00
|
|
|
static void layers_scale_callback (GtkWidget *dialog,
|
2016-09-24 21:10:07 +02:00
|
|
|
GimpViewable *viewable,
|
|
|
|
gint width,
|
|
|
|
gint height,
|
Issue #8900 and #9923: reimplementing GimpUnit as a proper class.
This fixes all our GObject Introspection issues with GimpUnit which was
both an enum and an int-derived type of user-defined units *completing*
the enum values. GIR clearly didn't like this!
Now GimpUnit is a proper class and units are unique objects, allowing to
compare them with an identity test (i.e. `unit == gimp_unit_pixel ()`
tells us if unit is the pixel unit or not), which makes it easy to use,
just like with int, yet adding also methods, making for nicer
introspected API.
As an aside, this also fixes #10738, by having all the built-in units
retrievable even if libgimpbase had not been properly initialized with
gimp_base_init().
I haven't checked in details how GIR works to introspect, but it looks
like it loads the library to inspect and runs functions, hence
triggering some CRITICALS because virtual methods (supposed to be
initialized with gimp_base_init() run by libgimp) are not set. This new
code won't trigger any critical because the vtable method are now not
necessary, at least for all built-in units.
Note that GimpUnit is still in libgimpbase. It could have been moved to
libgimp in order to avoid any virtual method table (since we need to
keep core and libgimp side's units in sync, PDB is required), but too
many libgimpwidgets widgets were already using GimpUnit. And technically
most of GimpUnit logic doesn't require PDB (only the creation/sync
part). This is one of the reasons why user-created GimpUnit list is
handled and stored differently from other types of objects.
Globally this simplifies the code a lot too and we don't need separate
implementations of various utils for core and libgimp, which means less
prone to errors.
2024-07-25 20:55:21 +02:00
|
|
|
GimpUnit *unit,
|
2016-09-24 21:10:07 +02:00
|
|
|
GimpInterpolationType interpolation,
|
|
|
|
gdouble xresolution,
|
|
|
|
gdouble yresolution,
|
Issue #8900 and #9923: reimplementing GimpUnit as a proper class.
This fixes all our GObject Introspection issues with GimpUnit which was
both an enum and an int-derived type of user-defined units *completing*
the enum values. GIR clearly didn't like this!
Now GimpUnit is a proper class and units are unique objects, allowing to
compare them with an identity test (i.e. `unit == gimp_unit_pixel ()`
tells us if unit is the pixel unit or not), which makes it easy to use,
just like with int, yet adding also methods, making for nicer
introspected API.
As an aside, this also fixes #10738, by having all the built-in units
retrievable even if libgimpbase had not been properly initialized with
gimp_base_init().
I haven't checked in details how GIR works to introspect, but it looks
like it loads the library to inspect and runs functions, hence
triggering some CRITICALS because virtual methods (supposed to be
initialized with gimp_base_init() run by libgimp) are not set. This new
code won't trigger any critical because the vtable method are now not
necessary, at least for all built-in units.
Note that GimpUnit is still in libgimpbase. It could have been moved to
libgimp in order to avoid any virtual method table (since we need to
keep core and libgimp side's units in sync, PDB is required), but too
many libgimpwidgets widgets were already using GimpUnit. And technically
most of GimpUnit logic doesn't require PDB (only the creation/sync
part). This is one of the reasons why user-created GimpUnit list is
handled and stored differently from other types of objects.
Globally this simplifies the code a lot too and we don't need separate
implementations of various utils for core and libgimp, which means less
prone to errors.
2024-07-25 20:55:21 +02:00
|
|
|
GimpUnit *resolution_unit,
|
2016-09-24 21:10:07 +02:00
|
|
|
gpointer user_data);
|
2016-09-25 22:18:37 +02:00
|
|
|
static void layers_resize_callback (GtkWidget *dialog,
|
2016-09-24 21:10:07 +02:00
|
|
|
GimpViewable *viewable,
|
|
|
|
GimpContext *context,
|
|
|
|
gint width,
|
|
|
|
gint height,
|
Issue #8900 and #9923: reimplementing GimpUnit as a proper class.
This fixes all our GObject Introspection issues with GimpUnit which was
both an enum and an int-derived type of user-defined units *completing*
the enum values. GIR clearly didn't like this!
Now GimpUnit is a proper class and units are unique objects, allowing to
compare them with an identity test (i.e. `unit == gimp_unit_pixel ()`
tells us if unit is the pixel unit or not), which makes it easy to use,
just like with int, yet adding also methods, making for nicer
introspected API.
As an aside, this also fixes #10738, by having all the built-in units
retrievable even if libgimpbase had not been properly initialized with
gimp_base_init().
I haven't checked in details how GIR works to introspect, but it looks
like it loads the library to inspect and runs functions, hence
triggering some CRITICALS because virtual methods (supposed to be
initialized with gimp_base_init() run by libgimp) are not set. This new
code won't trigger any critical because the vtable method are now not
necessary, at least for all built-in units.
Note that GimpUnit is still in libgimpbase. It could have been moved to
libgimp in order to avoid any virtual method table (since we need to
keep core and libgimp side's units in sync, PDB is required), but too
many libgimpwidgets widgets were already using GimpUnit. And technically
most of GimpUnit logic doesn't require PDB (only the creation/sync
part). This is one of the reasons why user-created GimpUnit list is
handled and stored differently from other types of objects.
Globally this simplifies the code a lot too and we don't need separate
implementations of various utils for core and libgimp, which means less
prone to errors.
2024-07-25 20:55:21 +02:00
|
|
|
GimpUnit *unit,
|
2016-09-24 21:10:07 +02:00
|
|
|
gint offset_x,
|
|
|
|
gint offset_y,
|
2021-02-07 22:06:41 +06:00
|
|
|
gdouble unused0,
|
|
|
|
gdouble unused1,
|
Issue #8900 and #9923: reimplementing GimpUnit as a proper class.
This fixes all our GObject Introspection issues with GimpUnit which was
both an enum and an int-derived type of user-defined units *completing*
the enum values. GIR clearly didn't like this!
Now GimpUnit is a proper class and units are unique objects, allowing to
compare them with an identity test (i.e. `unit == gimp_unit_pixel ()`
tells us if unit is the pixel unit or not), which makes it easy to use,
just like with int, yet adding also methods, making for nicer
introspected API.
As an aside, this also fixes #10738, by having all the built-in units
retrievable even if libgimpbase had not been properly initialized with
gimp_base_init().
I haven't checked in details how GIR works to introspect, but it looks
like it loads the library to inspect and runs functions, hence
triggering some CRITICALS because virtual methods (supposed to be
initialized with gimp_base_init() run by libgimp) are not set. This new
code won't trigger any critical because the vtable method are now not
necessary, at least for all built-in units.
Note that GimpUnit is still in libgimpbase. It could have been moved to
libgimp in order to avoid any virtual method table (since we need to
keep core and libgimp side's units in sync, PDB is required), but too
many libgimpwidgets widgets were already using GimpUnit. And technically
most of GimpUnit logic doesn't require PDB (only the creation/sync
part). This is one of the reasons why user-created GimpUnit list is
handled and stored differently from other types of objects.
Globally this simplifies the code a lot too and we don't need separate
implementations of various utils for core and libgimp, which means less
prone to errors.
2024-07-25 20:55:21 +02:00
|
|
|
GimpUnit *unused2,
|
2016-10-20 00:30:10 +02:00
|
|
|
GimpFillType fill_type,
|
2021-02-07 22:06:41 +06:00
|
|
|
GimpItemSet unused3,
|
|
|
|
gboolean unused4,
|
2016-09-24 21:10:07 +02:00
|
|
|
gpointer data);
|
|
|
|
|
2017-02-20 23:51:32 +01:00
|
|
|
static gint layers_mode_index (GimpLayerMode layer_mode,
|
|
|
|
const GimpLayerMode *modes,
|
|
|
|
gint n_modes);
|
2001-04-20 16:27:44 +00:00
|
|
|
|
|
|
|
|
2004-09-19 22:26:41 +00:00
|
|
|
/* private variables */
|
|
|
|
|
Issue #8900 and #9923: reimplementing GimpUnit as a proper class.
This fixes all our GObject Introspection issues with GimpUnit which was
both an enum and an int-derived type of user-defined units *completing*
the enum values. GIR clearly didn't like this!
Now GimpUnit is a proper class and units are unique objects, allowing to
compare them with an identity test (i.e. `unit == gimp_unit_pixel ()`
tells us if unit is the pixel unit or not), which makes it easy to use,
just like with int, yet adding also methods, making for nicer
introspected API.
As an aside, this also fixes #10738, by having all the built-in units
retrievable even if libgimpbase had not been properly initialized with
gimp_base_init().
I haven't checked in details how GIR works to introspect, but it looks
like it loads the library to inspect and runs functions, hence
triggering some CRITICALS because virtual methods (supposed to be
initialized with gimp_base_init() run by libgimp) are not set. This new
code won't trigger any critical because the vtable method are now not
necessary, at least for all built-in units.
Note that GimpUnit is still in libgimpbase. It could have been moved to
libgimp in order to avoid any virtual method table (since we need to
keep core and libgimp side's units in sync, PDB is required), but too
many libgimpwidgets widgets were already using GimpUnit. And technically
most of GimpUnit logic doesn't require PDB (only the creation/sync
part). This is one of the reasons why user-created GimpUnit list is
handled and stored differently from other types of objects.
Globally this simplifies the code a lot too and we don't need separate
implementations of various utils for core and libgimp, which means less
prone to errors.
2024-07-25 20:55:21 +02:00
|
|
|
static GimpUnit *layer_resize_unit = NULL;
|
|
|
|
static GimpUnit *layer_scale_unit = NULL;
|
2006-10-14 16:51:30 +00:00
|
|
|
static GimpInterpolationType layer_scale_interp = -1;
|
2004-09-19 22:26:41 +00:00
|
|
|
|
|
|
|
|
2001-11-30 14:41:56 +00:00
|
|
|
/* public functions */
|
|
|
|
|
2004-05-25 14:37:02 +00:00
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_edit_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2017-05-13 00:15:05 +02:00
|
|
|
{
|
|
|
|
GimpImage *image;
|
2023-01-10 20:12:15 +01:00
|
|
|
GList *layers;
|
2017-05-13 00:15:05 +02:00
|
|
|
GtkWidget *widget;
|
2023-01-10 20:12:15 +01:00
|
|
|
return_if_no_layers (image, layers, data);
|
2017-05-13 00:15:05 +02:00
|
|
|
return_if_no_widget (widget, data);
|
|
|
|
|
2023-01-10 20:12:15 +01:00
|
|
|
if (g_list_length (layers) != 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (gimp_item_is_text_layer (GIMP_ITEM (layers->data)))
|
2017-05-13 00:15:05 +02:00
|
|
|
{
|
2019-07-04 01:11:48 +02:00
|
|
|
layers_edit_text_cmd_callback (action, value, data);
|
2017-05-13 00:15:05 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-07-04 01:11:48 +02:00
|
|
|
layers_edit_attributes_cmd_callback (action, value, data);
|
2017-05-13 00:15:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_edit_text_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2004-05-25 14:37:02 +00:00
|
|
|
{
|
2006-03-28 17:08:36 +00:00
|
|
|
GimpImage *image;
|
2004-05-25 14:37:02 +00:00
|
|
|
GimpLayer *layer;
|
2023-01-10 20:12:15 +01:00
|
|
|
GList *layers;
|
2004-05-25 14:37:02 +00:00
|
|
|
GtkWidget *widget;
|
2004-10-16 15:48:23 +00:00
|
|
|
GimpTool *active_tool;
|
2023-01-10 20:12:15 +01:00
|
|
|
return_if_no_layers (image, layers, data);
|
2004-05-25 14:37:02 +00:00
|
|
|
return_if_no_widget (widget, data);
|
|
|
|
|
2023-01-10 20:12:15 +01:00
|
|
|
if (g_list_length (layers) != 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
layer = layers->data;
|
2017-05-13 00:01:56 +02:00
|
|
|
g_return_if_fail (gimp_item_is_text_layer (GIMP_ITEM (layer)));
|
2004-10-16 15:48:23 +00:00
|
|
|
|
2006-03-28 17:08:36 +00:00
|
|
|
active_tool = tool_manager_get_active (image->gimp);
|
2004-10-16 15:48:23 +00:00
|
|
|
|
|
|
|
if (! GIMP_IS_TEXT_TOOL (active_tool))
|
|
|
|
{
|
2006-09-08 13:42:00 +00:00
|
|
|
GimpToolInfo *tool_info = gimp_get_tool_info (image->gimp,
|
|
|
|
"gimp-text-tool");
|
2004-10-16 15:48:23 +00:00
|
|
|
|
|
|
|
if (GIMP_IS_TOOL_INFO (tool_info))
|
|
|
|
{
|
|
|
|
gimp_context_set_tool (action_data_get_context (data), tool_info);
|
2006-03-28 17:08:36 +00:00
|
|
|
active_tool = tool_manager_get_active (image->gimp);
|
2004-10-16 15:48:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (GIMP_IS_TEXT_TOOL (active_tool))
|
2017-05-13 00:55:43 +02:00
|
|
|
{
|
2018-05-29 10:19:50 -04:00
|
|
|
if (gimp_text_tool_set_layer (GIMP_TEXT_TOOL (active_tool), layer))
|
|
|
|
{
|
|
|
|
GimpDisplayShell *shell;
|
2017-05-13 00:55:43 +02:00
|
|
|
|
2018-05-29 10:19:50 -04:00
|
|
|
shell = gimp_display_get_shell (active_tool->display);
|
|
|
|
gtk_widget_grab_focus (shell->canvas);
|
|
|
|
}
|
2017-05-13 00:55:43 +02:00
|
|
|
}
|
2004-05-25 14:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_edit_attributes_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2004-05-25 14:37:02 +00:00
|
|
|
{
|
2016-09-24 21:10:07 +02:00
|
|
|
GimpImage *image;
|
|
|
|
GimpLayer *layer;
|
2023-01-10 20:12:15 +01:00
|
|
|
GList *layers;
|
2016-09-24 21:10:07 +02:00
|
|
|
GtkWidget *widget;
|
|
|
|
GtkWidget *dialog;
|
2023-01-10 20:12:15 +01:00
|
|
|
return_if_no_layers (image, layers, data);
|
2004-05-25 14:37:02 +00:00
|
|
|
return_if_no_widget (widget, data);
|
|
|
|
|
2023-01-10 20:12:15 +01:00
|
|
|
if (g_list_length (layers) != 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
layer = layers->data;
|
|
|
|
|
2025-04-27 16:44:34 +00:00
|
|
|
if (gimp_layer_is_floating_sel (layer))
|
|
|
|
return;
|
|
|
|
|
2016-09-24 21:10:07 +02:00
|
|
|
#define EDIT_DIALOG_KEY "gimp-layer-edit-attributes-dialog"
|
|
|
|
|
|
|
|
dialog = dialogs_get_dialog (G_OBJECT (layer), EDIT_DIALOG_KEY);
|
|
|
|
|
|
|
|
if (! dialog)
|
|
|
|
{
|
2016-10-26 16:51:56 +02:00
|
|
|
GimpItem *item = GIMP_ITEM (layer);
|
|
|
|
|
2016-09-24 21:10:07 +02:00
|
|
|
dialog = layer_options_dialog_new (gimp_item_get_image (GIMP_ITEM (layer)),
|
|
|
|
layer,
|
|
|
|
action_data_get_context (data),
|
|
|
|
widget,
|
|
|
|
_("Layer Attributes"),
|
|
|
|
"gimp-layer-edit",
|
2017-03-05 16:01:59 +01:00
|
|
|
GIMP_ICON_EDIT,
|
2016-09-24 21:10:07 +02:00
|
|
|
_("Edit Layer Attributes"),
|
|
|
|
GIMP_HELP_LAYER_EDIT,
|
|
|
|
gimp_object_get_name (layer),
|
2016-10-21 22:54:10 +02:00
|
|
|
gimp_layer_get_mode (layer),
|
2017-02-12 23:49:26 +01:00
|
|
|
gimp_layer_get_blend_space (layer),
|
|
|
|
gimp_layer_get_composite_space (layer),
|
|
|
|
gimp_layer_get_composite_mode (layer),
|
2016-10-21 22:54:10 +02:00
|
|
|
gimp_layer_get_opacity (layer),
|
2016-09-24 21:10:07 +02:00
|
|
|
0 /* unused */,
|
2016-10-26 16:51:56 +02:00
|
|
|
gimp_item_get_visible (item),
|
2016-10-29 16:50:13 +02:00
|
|
|
gimp_item_get_color_tag (item),
|
2016-10-26 16:51:56 +02:00
|
|
|
gimp_item_get_lock_content (item),
|
|
|
|
gimp_item_get_lock_position (item),
|
2023-05-26 02:46:53 +02:00
|
|
|
gimp_item_get_lock_visibility (item),
|
2016-10-26 16:51:56 +02:00
|
|
|
gimp_layer_get_lock_alpha (layer),
|
2016-09-24 21:10:07 +02:00
|
|
|
layers_edit_attributes_callback,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
dialogs_attach_dialog (G_OBJECT (layer), EDIT_DIALOG_KEY, dialog);
|
|
|
|
}
|
2016-08-24 12:33:56 +02:00
|
|
|
|
2016-09-24 21:10:07 +02:00
|
|
|
gtk_window_present (GTK_WINDOW (dialog));
|
2004-05-25 14:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_new_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2004-05-25 14:37:02 +00:00
|
|
|
{
|
2016-09-24 21:10:07 +02:00
|
|
|
GimpImage *image;
|
|
|
|
GtkWidget *widget;
|
|
|
|
GimpLayer *floating_sel;
|
|
|
|
GtkWidget *dialog;
|
2006-03-28 17:08:36 +00:00
|
|
|
return_if_no_image (image, data);
|
2004-05-25 14:37:02 +00:00
|
|
|
return_if_no_widget (widget, data);
|
|
|
|
|
2004-10-16 15:48:23 +00:00
|
|
|
/* If there is a floating selection, the new command transforms
|
|
|
|
* the current fs into a new layer
|
|
|
|
*/
|
2008-11-14 15:01:44 +00:00
|
|
|
if ((floating_sel = gimp_image_get_floating_selection (image)))
|
2004-10-16 15:48:23 +00:00
|
|
|
{
|
2007-12-06 18:00:10 +00:00
|
|
|
GError *error = NULL;
|
|
|
|
|
|
|
|
if (! floating_sel_to_layer (floating_sel, &error))
|
|
|
|
{
|
2008-11-04 12:33:09 +00:00
|
|
|
gimp_message_literal (image->gimp,
|
2013-09-15 04:59:20 +12:00
|
|
|
G_OBJECT (widget), GIMP_MESSAGE_WARNING,
|
|
|
|
error->message);
|
2007-12-06 18:00:10 +00:00
|
|
|
g_clear_error (&error);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2006-03-28 17:08:36 +00:00
|
|
|
gimp_image_flush (image);
|
2004-10-16 15:48:23 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-09-24 21:10:07 +02:00
|
|
|
#define NEW_DIALOG_KEY "gimp-layer-new-dialog"
|
|
|
|
|
|
|
|
dialog = dialogs_get_dialog (G_OBJECT (image), NEW_DIALOG_KEY);
|
|
|
|
|
|
|
|
if (! dialog)
|
|
|
|
{
|
2018-04-24 13:58:30 +02:00
|
|
|
GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config);
|
2020-05-03 00:28:13 +02:00
|
|
|
const gchar *title;
|
|
|
|
gchar *desc;
|
|
|
|
gint n_layers;
|
2018-04-24 13:58:30 +02:00
|
|
|
GimpLayerMode layer_mode = config->layer_new_mode;
|
|
|
|
|
2020-05-03 00:28:13 +02:00
|
|
|
n_layers = g_list_length (gimp_image_get_selected_layers (image));
|
|
|
|
title = ngettext ("New Layer", "New Layers", n_layers > 0 ? n_layers : 1);
|
|
|
|
desc = ngettext ("Create a New Layer", "Create %d New Layers", n_layers > 0 ? n_layers : 1);
|
|
|
|
desc = g_strdup_printf (desc, n_layers > 0 ? n_layers : 1);
|
|
|
|
|
2018-04-24 13:58:30 +02:00
|
|
|
if (layer_mode == GIMP_LAYER_MODE_NORMAL ||
|
|
|
|
layer_mode == GIMP_LAYER_MODE_NORMAL_LEGACY)
|
|
|
|
{
|
|
|
|
layer_mode = gimp_image_get_default_new_layer_mode (image);
|
|
|
|
}
|
2016-09-24 21:10:07 +02:00
|
|
|
|
|
|
|
dialog = layer_options_dialog_new (image, NULL,
|
|
|
|
action_data_get_context (data),
|
|
|
|
widget,
|
2020-05-03 00:28:13 +02:00
|
|
|
title,
|
2016-09-24 21:10:07 +02:00
|
|
|
"gimp-layer-new",
|
2017-03-05 16:01:59 +01:00
|
|
|
GIMP_ICON_LAYER,
|
2020-05-03 00:28:13 +02:00
|
|
|
desc,
|
2016-09-24 21:10:07 +02:00
|
|
|
GIMP_HELP_LAYER_NEW,
|
|
|
|
config->layer_new_name,
|
2018-04-24 13:58:30 +02:00
|
|
|
layer_mode,
|
2017-02-12 23:49:26 +01:00
|
|
|
config->layer_new_blend_space,
|
|
|
|
config->layer_new_composite_space,
|
2017-02-02 00:38:25 +01:00
|
|
|
config->layer_new_composite_mode,
|
2016-10-21 22:54:10 +02:00
|
|
|
config->layer_new_opacity,
|
2016-09-24 21:10:07 +02:00
|
|
|
config->layer_new_fill_type,
|
2016-10-26 16:51:56 +02:00
|
|
|
TRUE,
|
2016-10-29 16:50:13 +02:00
|
|
|
GIMP_COLOR_TAG_NONE,
|
2016-10-26 16:51:56 +02:00
|
|
|
FALSE,
|
|
|
|
FALSE,
|
|
|
|
FALSE,
|
2023-05-26 02:46:53 +02:00
|
|
|
FALSE,
|
2016-09-24 21:10:07 +02:00
|
|
|
layers_new_callback,
|
|
|
|
NULL);
|
2020-05-03 00:28:13 +02:00
|
|
|
g_free (desc);
|
2016-09-24 21:10:07 +02:00
|
|
|
|
|
|
|
dialogs_attach_dialog (G_OBJECT (image), NEW_DIALOG_KEY, dialog);
|
|
|
|
}
|
|
|
|
|
|
|
|
gtk_window_present (GTK_WINDOW (dialog));
|
2004-10-16 15:48:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_new_last_vals_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2004-10-16 15:48:23 +00:00
|
|
|
{
|
2017-01-08 23:00:19 +01:00
|
|
|
GimpImage *image;
|
|
|
|
GtkWidget *widget;
|
2018-03-25 14:08:50 +02:00
|
|
|
GimpLayer *layer;
|
2017-01-08 23:00:19 +01:00
|
|
|
GimpDialogConfig *config;
|
2020-04-20 01:56:23 +02:00
|
|
|
GList *layers;
|
|
|
|
GList *new_layers = NULL;
|
|
|
|
GList *iter;
|
2018-04-24 13:58:30 +02:00
|
|
|
GimpLayerMode layer_mode;
|
2020-05-03 00:28:13 +02:00
|
|
|
gint n_layers;
|
|
|
|
gboolean run_once;
|
2018-03-23 18:43:22 +01:00
|
|
|
|
2006-03-28 17:08:36 +00:00
|
|
|
return_if_no_image (image, data);
|
2007-12-06 18:00:10 +00:00
|
|
|
return_if_no_widget (widget, data);
|
2004-10-16 15:48:23 +00:00
|
|
|
|
2016-08-24 12:33:56 +02:00
|
|
|
config = GIMP_DIALOG_CONFIG (image->gimp->config);
|
|
|
|
|
2004-10-16 15:48:23 +00:00
|
|
|
/* If there is a floating selection, the new command transforms
|
|
|
|
* the current fs into a new layer
|
|
|
|
*/
|
2018-03-25 14:08:50 +02:00
|
|
|
if (gimp_image_get_floating_selection (image))
|
2004-10-16 15:48:23 +00:00
|
|
|
{
|
2019-07-04 01:11:48 +02:00
|
|
|
layers_new_cmd_callback (action, value, data);
|
2004-10-16 15:48:23 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-04-24 13:58:30 +02:00
|
|
|
layer_mode = config->layer_new_mode;
|
|
|
|
|
|
|
|
if (layer_mode == GIMP_LAYER_MODE_NORMAL ||
|
|
|
|
layer_mode == GIMP_LAYER_MODE_NORMAL_LEGACY)
|
|
|
|
{
|
|
|
|
layer_mode = gimp_image_get_default_new_layer_mode (image);
|
|
|
|
}
|
|
|
|
|
2020-05-03 00:28:13 +02:00
|
|
|
layers = gimp_image_get_selected_layers (image);
|
|
|
|
layers = g_list_copy (layers);
|
|
|
|
n_layers = g_list_length (layers);
|
|
|
|
run_once = (n_layers == 0);
|
|
|
|
|
|
|
|
gimp_image_undo_group_start (image,
|
|
|
|
GIMP_UNDO_GROUP_LAYER_ADD,
|
|
|
|
ngettext ("New layer",
|
|
|
|
"New layers",
|
|
|
|
n_layers > 0 ? n_layers : 1));
|
|
|
|
for (iter = layers; iter || run_once ; iter = iter ? iter->next : NULL)
|
2020-04-20 01:56:23 +02:00
|
|
|
{
|
|
|
|
GimpLayer *parent;
|
|
|
|
gint position;
|
|
|
|
|
2020-05-03 00:28:13 +02:00
|
|
|
run_once = FALSE;
|
|
|
|
if (iter)
|
2020-04-20 01:56:23 +02:00
|
|
|
{
|
2020-05-03 00:28:13 +02:00
|
|
|
if (gimp_viewable_get_children (GIMP_VIEWABLE (iter->data)))
|
|
|
|
{
|
|
|
|
parent = iter->data;
|
|
|
|
position = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
parent = GIMP_LAYER (gimp_item_get_parent (iter->data));
|
|
|
|
position = gimp_item_get_index (iter->data);
|
|
|
|
}
|
2020-04-20 01:56:23 +02:00
|
|
|
}
|
2020-05-03 00:28:13 +02:00
|
|
|
else /* run_once */
|
2020-04-20 01:56:23 +02:00
|
|
|
{
|
2020-05-03 00:28:13 +02:00
|
|
|
parent = NULL;
|
|
|
|
position = -1;
|
2020-04-20 01:56:23 +02:00
|
|
|
}
|
|
|
|
layer = gimp_layer_new (image,
|
|
|
|
gimp_image_get_width (image),
|
|
|
|
gimp_image_get_height (image),
|
|
|
|
gimp_image_get_layer_format (image, TRUE),
|
|
|
|
config->layer_new_name,
|
|
|
|
config->layer_new_opacity,
|
|
|
|
layer_mode);
|
|
|
|
|
|
|
|
gimp_drawable_fill (GIMP_DRAWABLE (layer),
|
|
|
|
action_data_get_context (data),
|
|
|
|
config->layer_new_fill_type);
|
|
|
|
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_add_layer (image, layer, parent, position, TRUE);
|
|
|
|
new_layers = g_list_prepend (new_layers, layer);
|
|
|
|
}
|
2020-05-03 00:28:13 +02:00
|
|
|
gimp_image_set_selected_layers (image, new_layers);
|
|
|
|
gimp_image_undo_group_end (image);
|
|
|
|
|
2020-04-20 01:56:23 +02:00
|
|
|
g_list_free (layers);
|
|
|
|
g_list_free (new_layers);
|
2006-03-28 17:08:36 +00:00
|
|
|
gimp_image_flush (image);
|
2004-05-25 14:37:02 +00:00
|
|
|
}
|
|
|
|
|
2008-08-07 15:53:15 +00:00
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_new_from_visible_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2008-08-07 15:53:15 +00:00
|
|
|
{
|
2016-05-04 00:00:28 +02:00
|
|
|
GimpImage *image;
|
2019-09-19 20:00:00 +03:00
|
|
|
GimpDisplayShell *shell;
|
2016-05-04 00:00:28 +02:00
|
|
|
GimpLayer *layer;
|
|
|
|
GimpPickable *pickable;
|
|
|
|
GimpColorProfile *profile;
|
2008-08-07 15:53:15 +00:00
|
|
|
return_if_no_image (image, data);
|
2019-09-19 20:00:00 +03:00
|
|
|
return_if_no_shell (shell, data);
|
2008-08-07 15:53:15 +00:00
|
|
|
|
2019-09-19 20:00:00 +03:00
|
|
|
pickable = gimp_display_shell_get_canvas_pickable (shell);
|
2008-08-07 15:53:15 +00:00
|
|
|
|
2009-12-14 14:00:01 +01:00
|
|
|
gimp_pickable_flush (pickable);
|
|
|
|
|
2016-05-04 00:00:28 +02:00
|
|
|
profile = gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (image));
|
|
|
|
|
2015-06-20 00:02:11 +02:00
|
|
|
layer = gimp_layer_new_from_gegl_buffer (gimp_pickable_get_buffer (pickable),
|
|
|
|
image,
|
|
|
|
gimp_image_get_layer_format (image,
|
|
|
|
TRUE),
|
|
|
|
_("Visible"),
|
|
|
|
GIMP_OPACITY_OPAQUE,
|
2017-08-21 20:04:25 +02:00
|
|
|
gimp_image_get_default_new_layer_mode (image),
|
2016-05-04 00:00:28 +02:00
|
|
|
profile);
|
2009-08-03 22:30:36 +02:00
|
|
|
|
2016-09-25 22:18:37 +02:00
|
|
|
gimp_image_add_layer (image, layer, GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE);
|
2008-08-07 15:53:15 +00:00
|
|
|
gimp_image_flush (image);
|
|
|
|
}
|
|
|
|
|
2009-08-06 18:37:54 +02:00
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_new_group_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2009-08-06 18:37:54 +02:00
|
|
|
{
|
|
|
|
GimpImage *image;
|
2020-05-03 00:28:13 +02:00
|
|
|
GList *new_layers = NULL;
|
|
|
|
GList *layers;
|
|
|
|
GList *iter;
|
|
|
|
gint n_layers;
|
|
|
|
gboolean run_once;
|
|
|
|
|
2009-08-06 18:37:54 +02:00
|
|
|
return_if_no_image (image, data);
|
|
|
|
|
2020-05-03 00:28:13 +02:00
|
|
|
layers = gimp_image_get_selected_layers (image);
|
|
|
|
layers = g_list_copy (layers);
|
|
|
|
n_layers = g_list_length (layers);
|
|
|
|
run_once = (n_layers == 0);
|
|
|
|
gimp_image_undo_group_start (image,
|
|
|
|
GIMP_UNDO_GROUP_LAYER_ADD,
|
|
|
|
ngettext ("New layer group",
|
|
|
|
"New layer groups",
|
|
|
|
n_layers > 0 ? n_layers : 1));
|
2009-08-06 18:37:54 +02:00
|
|
|
|
2020-05-03 00:28:13 +02:00
|
|
|
for (iter = layers; iter || run_once ; iter = iter ? iter->next : NULL)
|
|
|
|
{
|
|
|
|
GimpLayer *layer;
|
|
|
|
GimpLayer *parent;
|
|
|
|
gint position;
|
|
|
|
|
|
|
|
run_once = FALSE;
|
|
|
|
if (iter)
|
|
|
|
{
|
|
|
|
if (gimp_viewable_get_children (GIMP_VIEWABLE (iter->data)))
|
|
|
|
{
|
|
|
|
parent = iter->data;
|
|
|
|
position = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
parent = GIMP_LAYER (gimp_item_get_parent (iter->data));
|
|
|
|
position = gimp_item_get_index (iter->data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else /* run_once */
|
|
|
|
{
|
|
|
|
parent = NULL;
|
|
|
|
position = -1;
|
|
|
|
}
|
|
|
|
layer = gimp_group_layer_new (image);
|
|
|
|
|
|
|
|
gimp_image_add_layer (image, layer, parent, position, TRUE);
|
|
|
|
new_layers = g_list_prepend (new_layers, layer);
|
|
|
|
}
|
|
|
|
|
|
|
|
gimp_image_set_selected_layers (image, new_layers);
|
|
|
|
gimp_image_undo_group_end (image);
|
2009-08-06 18:37:54 +02:00
|
|
|
gimp_image_flush (image);
|
2020-05-03 00:28:13 +02:00
|
|
|
|
|
|
|
g_list_free (layers);
|
|
|
|
g_list_free (new_layers);
|
2009-08-06 18:37:54 +02:00
|
|
|
}
|
|
|
|
|
2001-04-20 16:27:44 +00:00
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_select_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2001-04-20 16:27:44 +00:00
|
|
|
{
|
2019-07-04 01:11:48 +02:00
|
|
|
GimpImage *image;
|
2020-05-05 11:53:07 +02:00
|
|
|
GList *new_layers = NULL;
|
|
|
|
GList *layers;
|
|
|
|
GList *iter;
|
2019-07-04 01:11:48 +02:00
|
|
|
GimpActionSelectType select_type;
|
2020-05-05 11:53:07 +02:00
|
|
|
gboolean run_once;
|
2006-03-28 17:08:36 +00:00
|
|
|
return_if_no_image (image, data);
|
2001-04-20 16:27:44 +00:00
|
|
|
|
2019-07-04 01:11:48 +02:00
|
|
|
select_type = (GimpActionSelectType) g_variant_get_int32 (value);
|
|
|
|
|
2024-08-13 12:37:26 +02:00
|
|
|
layers = gimp_image_get_selected_layers (image);
|
2020-05-05 11:53:07 +02:00
|
|
|
run_once = (g_list_length (layers) == 0);
|
2003-06-06 10:30:14 +00:00
|
|
|
|
2020-05-05 11:53:07 +02:00
|
|
|
for (iter = layers; iter || run_once; iter = iter ? iter->next : NULL)
|
|
|
|
{
|
2021-06-20 15:38:10 +02:00
|
|
|
GimpLayer *new_layer;
|
|
|
|
GimpContainer *container;
|
|
|
|
|
2020-05-05 11:53:07 +02:00
|
|
|
if (iter)
|
2021-06-20 15:38:10 +02:00
|
|
|
{
|
|
|
|
container = gimp_item_get_container (GIMP_ITEM (iter->data));
|
|
|
|
}
|
2020-05-05 11:53:07 +02:00
|
|
|
else /* run_once */
|
2021-06-20 15:38:10 +02:00
|
|
|
{
|
|
|
|
container = gimp_image_get_layers (image);
|
|
|
|
run_once = FALSE;
|
|
|
|
}
|
2020-05-05 11:53:07 +02:00
|
|
|
new_layer = (GimpLayer *) action_select_object (select_type,
|
|
|
|
container,
|
2021-06-20 15:38:10 +02:00
|
|
|
iter ? iter->data : NULL);
|
2020-05-05 11:53:07 +02:00
|
|
|
if (new_layer)
|
|
|
|
new_layers = g_list_prepend (new_layers, new_layer);
|
|
|
|
}
|
2003-06-06 10:30:14 +00:00
|
|
|
|
2020-05-05 11:53:07 +02:00
|
|
|
if (new_layers)
|
2003-06-06 10:30:14 +00:00
|
|
|
{
|
2020-05-05 11:53:07 +02:00
|
|
|
gimp_image_set_selected_layers (image, new_layers);
|
2006-03-28 17:08:36 +00:00
|
|
|
gimp_image_flush (image);
|
2003-06-06 10:30:14 +00:00
|
|
|
}
|
2020-05-05 11:53:07 +02:00
|
|
|
|
|
|
|
g_list_free (new_layers);
|
2003-06-06 10:30:14 +00:00
|
|
|
}
|
|
|
|
|
2001-04-20 16:27:44 +00:00
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_raise_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2001-04-20 16:27:44 +00:00
|
|
|
{
|
2006-03-28 17:08:36 +00:00
|
|
|
GimpImage *image;
|
2020-05-04 17:11:14 +02:00
|
|
|
GList *layers;
|
|
|
|
GList *iter;
|
|
|
|
GList *raised_layers = NULL;
|
|
|
|
return_if_no_layers (image, layers, data);
|
|
|
|
|
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
gint index;
|
|
|
|
|
|
|
|
index = gimp_item_get_index (iter->data);
|
|
|
|
if (index > 0)
|
2024-07-14 19:21:53 +00:00
|
|
|
{
|
|
|
|
raised_layers = g_list_prepend (raised_layers, iter->data);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gimp_image_flush (image);
|
|
|
|
g_list_free (raised_layers);
|
|
|
|
return;
|
|
|
|
}
|
2020-05-04 17:11:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
gimp_image_undo_group_start (image,
|
|
|
|
GIMP_UNDO_GROUP_ITEM_DISPLACE,
|
|
|
|
ngettext ("Raise Layer",
|
|
|
|
"Raise Layers",
|
|
|
|
g_list_length (raised_layers)));
|
2024-07-14 19:21:53 +00:00
|
|
|
|
|
|
|
raised_layers = g_list_reverse (raised_layers);
|
2020-05-04 17:11:14 +02:00
|
|
|
for (iter = raised_layers; iter; iter = iter->next)
|
|
|
|
gimp_image_raise_item (image, iter->data, NULL);
|
2001-04-20 16:27:44 +00:00
|
|
|
|
2006-03-28 17:08:36 +00:00
|
|
|
gimp_image_flush (image);
|
2020-05-04 17:11:14 +02:00
|
|
|
gimp_image_undo_group_end (image);
|
|
|
|
|
|
|
|
g_list_free (raised_layers);
|
2001-04-20 16:27:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_raise_to_top_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2001-04-20 16:27:44 +00:00
|
|
|
{
|
2006-03-28 17:08:36 +00:00
|
|
|
GimpImage *image;
|
2020-05-04 17:11:14 +02:00
|
|
|
GList *layers;
|
|
|
|
GList *iter;
|
|
|
|
GList *raised_layers = NULL;
|
|
|
|
return_if_no_layers (image, layers, data);
|
|
|
|
|
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
gint index;
|
|
|
|
|
|
|
|
index = gimp_item_get_index (iter->data);
|
|
|
|
if (index > 0)
|
|
|
|
raised_layers = g_list_prepend (raised_layers, iter->data);
|
|
|
|
}
|
|
|
|
|
|
|
|
gimp_image_undo_group_start (image,
|
|
|
|
GIMP_UNDO_GROUP_ITEM_DISPLACE,
|
|
|
|
ngettext ("Raise Layer to Top",
|
|
|
|
"Raise Layers to Top",
|
|
|
|
g_list_length (raised_layers)));
|
|
|
|
|
|
|
|
for (iter = raised_layers; iter; iter = iter->next)
|
|
|
|
gimp_image_raise_item_to_top (image, iter->data);
|
2001-04-20 16:27:44 +00:00
|
|
|
|
2006-03-28 17:08:36 +00:00
|
|
|
gimp_image_flush (image);
|
2020-05-04 17:11:14 +02:00
|
|
|
gimp_image_undo_group_end (image);
|
|
|
|
|
|
|
|
g_list_free (raised_layers);
|
2001-04-20 16:27:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_lower_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2001-04-20 16:27:44 +00:00
|
|
|
{
|
2006-03-28 17:08:36 +00:00
|
|
|
GimpImage *image;
|
2020-05-04 17:11:14 +02:00
|
|
|
GList *layers;
|
|
|
|
GList *iter;
|
|
|
|
GList *lowered_layers = NULL;
|
|
|
|
return_if_no_layers (image, layers, data);
|
|
|
|
|
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
GList *layer_list;
|
|
|
|
gint index;
|
|
|
|
|
|
|
|
layer_list = gimp_item_get_container_iter (GIMP_ITEM (iter->data));
|
|
|
|
index = gimp_item_get_index (iter->data);
|
|
|
|
if (index < g_list_length (layer_list) - 1)
|
2024-07-14 19:21:53 +00:00
|
|
|
{
|
|
|
|
lowered_layers = g_list_prepend (lowered_layers, iter->data);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gimp_image_flush (image);
|
|
|
|
g_list_free (lowered_layers);
|
|
|
|
return;
|
|
|
|
}
|
2020-05-04 17:11:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
gimp_image_undo_group_start (image,
|
|
|
|
GIMP_UNDO_GROUP_ITEM_DISPLACE,
|
|
|
|
ngettext ("Lower Layer",
|
|
|
|
"Lower Layers",
|
|
|
|
g_list_length (lowered_layers)));
|
|
|
|
|
|
|
|
for (iter = lowered_layers; iter; iter = iter->next)
|
|
|
|
gimp_image_lower_item (image, iter->data, NULL);
|
2001-04-20 16:27:44 +00:00
|
|
|
|
2006-03-28 17:08:36 +00:00
|
|
|
gimp_image_flush (image);
|
2020-05-04 17:11:14 +02:00
|
|
|
gimp_image_undo_group_end (image);
|
|
|
|
|
|
|
|
g_list_free (lowered_layers);
|
2001-04-20 16:27:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_lower_to_bottom_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2001-04-20 16:27:44 +00:00
|
|
|
{
|
2006-03-28 17:08:36 +00:00
|
|
|
GimpImage *image;
|
2020-05-04 17:11:14 +02:00
|
|
|
GList *layers;
|
|
|
|
GList *iter;
|
|
|
|
GList *lowered_layers = NULL;
|
|
|
|
return_if_no_layers (image, layers, data);
|
|
|
|
|
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
GList *layer_list;
|
|
|
|
gint index;
|
|
|
|
|
|
|
|
layer_list = gimp_item_get_container_iter (GIMP_ITEM (iter->data));
|
|
|
|
index = gimp_item_get_index (iter->data);
|
|
|
|
if (index < g_list_length (layer_list) - 1)
|
|
|
|
lowered_layers = g_list_prepend (lowered_layers, iter->data);
|
|
|
|
}
|
|
|
|
|
|
|
|
gimp_image_undo_group_start (image,
|
|
|
|
GIMP_UNDO_GROUP_ITEM_DISPLACE,
|
|
|
|
ngettext ("Lower Layer to Bottom",
|
|
|
|
"Lower Layers to Bottom",
|
|
|
|
g_list_length (lowered_layers)));
|
|
|
|
|
|
|
|
for (iter = lowered_layers; iter; iter = iter->next)
|
|
|
|
gimp_image_lower_item_to_bottom (image, iter->data);
|
2001-04-20 16:27:44 +00:00
|
|
|
|
2006-03-28 17:08:36 +00:00
|
|
|
gimp_image_flush (image);
|
2020-05-04 17:11:14 +02:00
|
|
|
gimp_image_undo_group_end (image);
|
|
|
|
|
|
|
|
g_list_free (lowered_layers);
|
2001-04-20 16:27:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_duplicate_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2001-04-20 16:27:44 +00:00
|
|
|
{
|
2006-03-28 17:08:36 +00:00
|
|
|
GimpImage *image;
|
2020-04-20 19:42:58 +02:00
|
|
|
GList *layers;
|
|
|
|
GList *new_layers = NULL;
|
|
|
|
GList *iter;
|
|
|
|
return_if_no_layers (image, layers, data);
|
|
|
|
|
|
|
|
layers = g_list_copy (layers);
|
|
|
|
gimp_image_undo_group_start (image,
|
|
|
|
GIMP_UNDO_GROUP_LAYER_ADD,
|
|
|
|
_("Duplicate layers"));
|
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
{
|
2024-10-07 13:49:44 +00:00
|
|
|
GimpLayer *new_layer;
|
|
|
|
GimpContainer *filters;
|
2020-04-20 19:42:58 +02:00
|
|
|
|
|
|
|
new_layer = GIMP_LAYER (gimp_item_duplicate (GIMP_ITEM (iter->data),
|
|
|
|
G_TYPE_FROM_INSTANCE (iter->data)));
|
|
|
|
|
|
|
|
/* use the actual parent here, not GIMP_IMAGE_ACTIVE_PARENT because
|
|
|
|
* the latter would add a duplicated group inside itself instead of
|
|
|
|
* above it
|
|
|
|
*/
|
|
|
|
gimp_image_add_layer (image, new_layer,
|
|
|
|
gimp_layer_get_parent (iter->data),
|
|
|
|
gimp_item_get_index (iter->data),
|
|
|
|
TRUE);
|
2025-04-12 14:27:51 +00:00
|
|
|
gimp_drawable_enable_resize_undo (GIMP_DRAWABLE (new_layer));
|
2020-04-20 19:42:58 +02:00
|
|
|
new_layers = g_list_prepend (new_layers, new_layer);
|
2023-06-19 14:54:21 +00:00
|
|
|
|
|
|
|
/* Import any attached layer effects */
|
2024-10-07 13:49:44 +00:00
|
|
|
filters = gimp_drawable_get_filters (GIMP_DRAWABLE (iter->data));
|
|
|
|
if (gimp_container_get_n_children (filters) > 0)
|
2023-06-19 14:54:21 +00:00
|
|
|
{
|
|
|
|
GList *filter_list;
|
|
|
|
GimpContainer *filters;
|
|
|
|
|
|
|
|
filters = gimp_drawable_get_filters (GIMP_DRAWABLE (iter->data));
|
|
|
|
|
|
|
|
for (filter_list = GIMP_LIST (filters)->queue->tail; filter_list;
|
|
|
|
filter_list = g_list_previous (filter_list))
|
|
|
|
{
|
|
|
|
if (GIMP_IS_DRAWABLE_FILTER (filter_list->data))
|
|
|
|
{
|
|
|
|
GimpDrawableFilter *old_filter = filter_list->data;
|
|
|
|
GimpDrawableFilter *filter;
|
|
|
|
|
|
|
|
filter =
|
|
|
|
gimp_drawable_filter_duplicate (GIMP_DRAWABLE (new_layer),
|
|
|
|
old_filter);
|
|
|
|
|
2024-05-14 04:05:06 +00:00
|
|
|
if (filter != NULL)
|
|
|
|
{
|
|
|
|
gimp_drawable_filter_apply (filter, NULL);
|
|
|
|
gimp_drawable_filter_commit (filter, TRUE, NULL, FALSE);
|
|
|
|
|
|
|
|
gimp_drawable_filter_layer_mask_freeze (filter);
|
|
|
|
g_object_unref (filter);
|
|
|
|
}
|
2023-06-19 14:54:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-04-20 19:42:58 +02:00
|
|
|
}
|
2001-04-20 16:27:44 +00:00
|
|
|
|
2020-04-20 19:42:58 +02:00
|
|
|
gimp_image_set_selected_layers (image, new_layers);
|
|
|
|
g_list_free (layers);
|
|
|
|
g_list_free (new_layers);
|
2009-08-03 22:30:36 +02:00
|
|
|
|
2020-04-20 19:42:58 +02:00
|
|
|
gimp_image_undo_group_end (image);
|
2006-03-28 17:08:36 +00:00
|
|
|
gimp_image_flush (image);
|
2001-04-20 16:27:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_anchor_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2001-04-20 16:27:44 +00:00
|
|
|
{
|
2006-03-28 17:08:36 +00:00
|
|
|
GimpImage *image;
|
2020-05-21 15:06:48 +02:00
|
|
|
GList *layers;
|
|
|
|
return_if_no_layers (image, layers, data);
|
2001-04-20 16:27:44 +00:00
|
|
|
|
2020-05-21 15:06:48 +02:00
|
|
|
if (g_list_length (layers) == 1 &&
|
|
|
|
gimp_layer_is_floating_sel (layers->data))
|
2003-02-17 13:33:29 +00:00
|
|
|
{
|
2020-05-21 15:06:48 +02:00
|
|
|
floating_sel_anchor (layers->data);
|
2006-03-28 17:08:36 +00:00
|
|
|
gimp_image_flush (image);
|
2003-02-17 13:33:29 +00:00
|
|
|
}
|
2001-12-03 17:59:48 +00:00
|
|
|
}
|
2001-04-20 16:27:44 +00:00
|
|
|
|
2002-02-13 14:41:35 +00:00
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_merge_down_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2002-02-13 14:41:35 +00:00
|
|
|
{
|
app: merge layers in chunks, and show progress
In gimp_image_merge_layers() -- the internal function used by the
various layer-merging/flattenning functions -- process the merged-
layer graph in chunks, using gimp_gegl_apply_operation(), instead
of in one go, using gegl_node_blit_buffer(). Processing in chunks
better utilizes the cache, since it reduces the size of
intermediate buffers, reducing the chances of hitting the swap when
merging large images (see, for example, issue #3012.)
Additionally, this allows us to show progress indication. Have the
relevant gimpimage-merge functions take a GimpProgress, and pass it
down to gimp_image_merge_layers(). Adapt all callers.
2019-02-25 04:49:04 -05:00
|
|
|
GimpImage *image;
|
2020-06-30 23:29:05 +02:00
|
|
|
GList *layers;
|
app: merge layers in chunks, and show progress
In gimp_image_merge_layers() -- the internal function used by the
various layer-merging/flattenning functions -- process the merged-
layer graph in chunks, using gimp_gegl_apply_operation(), instead
of in one go, using gegl_node_blit_buffer(). Processing in chunks
better utilizes the cache, since it reduces the size of
intermediate buffers, reducing the chances of hitting the swap when
merging large images (see, for example, issue #3012.)
Additionally, this allows us to show progress indication. Have the
relevant gimpimage-merge functions take a GimpProgress, and pass it
down to gimp_image_merge_layers(). Adapt all callers.
2019-02-25 04:49:04 -05:00
|
|
|
GimpDisplay *display;
|
2020-06-30 23:29:05 +02:00
|
|
|
GError *error = NULL;
|
|
|
|
|
|
|
|
return_if_no_layers (image, layers, data);
|
app: merge layers in chunks, and show progress
In gimp_image_merge_layers() -- the internal function used by the
various layer-merging/flattenning functions -- process the merged-
layer graph in chunks, using gimp_gegl_apply_operation(), instead
of in one go, using gegl_node_blit_buffer(). Processing in chunks
better utilizes the cache, since it reduces the size of
intermediate buffers, reducing the chances of hitting the swap when
merging large images (see, for example, issue #3012.)
Additionally, this allows us to show progress indication. Have the
relevant gimpimage-merge functions take a GimpProgress, and pass it
down to gimp_image_merge_layers(). Adapt all callers.
2019-02-25 04:49:04 -05:00
|
|
|
return_if_no_display (display, data);
|
2002-02-13 14:41:35 +00:00
|
|
|
|
2020-06-30 23:29:05 +02:00
|
|
|
layers = gimp_image_merge_down (image, layers, action_data_get_context (data),
|
|
|
|
GIMP_EXPAND_AS_NECESSARY,
|
|
|
|
GIMP_PROGRESS (display), &error);
|
|
|
|
|
|
|
|
if (error)
|
|
|
|
{
|
|
|
|
gimp_message_literal (image->gimp,
|
|
|
|
G_OBJECT (display), GIMP_MESSAGE_WARNING,
|
|
|
|
error->message);
|
|
|
|
g_clear_error (&error);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
gimp_image_set_selected_layers (image, layers);
|
|
|
|
g_list_free (layers);
|
|
|
|
|
2006-03-28 17:08:36 +00:00
|
|
|
gimp_image_flush (image);
|
2002-02-13 14:41:35 +00:00
|
|
|
}
|
|
|
|
|
2009-09-21 19:17:02 +02:00
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_merge_group_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2009-09-21 19:17:02 +02:00
|
|
|
{
|
|
|
|
GimpImage *image;
|
2020-05-21 15:06:48 +02:00
|
|
|
GList *layers;
|
|
|
|
GList *merge_layers = NULL;
|
|
|
|
GList *iter;
|
|
|
|
return_if_no_layers (image, layers, data);
|
|
|
|
|
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
if (gimp_viewable_get_children (GIMP_VIEWABLE (iter->data)))
|
|
|
|
{
|
|
|
|
GList *iter2;
|
|
|
|
|
|
|
|
for (iter2 = layers; iter2; iter2 = iter2->next)
|
|
|
|
{
|
2025-03-11 12:11:10 +01:00
|
|
|
if (iter->data == iter2->data)
|
|
|
|
continue;
|
|
|
|
|
2020-05-21 15:06:48 +02:00
|
|
|
/* Do not merge a layer when we already merge one of its
|
|
|
|
* ancestors.
|
|
|
|
*/
|
2020-10-22 12:43:51 +02:00
|
|
|
if (gimp_viewable_is_ancestor (iter2->data, iter->data))
|
2020-05-21 15:06:48 +02:00
|
|
|
break;
|
2025-03-11 12:11:10 +01:00
|
|
|
|
|
|
|
/* Do not merge a layer which has a little sister (same
|
|
|
|
* parent and smaller index) or a little cousin (one of
|
|
|
|
* its ancestors is a little sister) of a pass-through
|
|
|
|
* group layer.
|
|
|
|
* These will be rendered and merged through the
|
|
|
|
* pass-through by definition.
|
|
|
|
*/
|
|
|
|
if (gimp_viewable_get_children (GIMP_VIEWABLE (iter2->data)) &&
|
|
|
|
gimp_layer_get_mode (iter2->data) == GIMP_LAYER_MODE_PASS_THROUGH)
|
|
|
|
{
|
|
|
|
GimpLayer *pass_through_parent = gimp_layer_get_parent (iter2->data);
|
|
|
|
GimpLayer *cousin = iter->data;
|
|
|
|
gboolean ignore = FALSE;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
GimpLayer *cousin_parent = gimp_layer_get_parent (cousin);
|
|
|
|
|
|
|
|
if (pass_through_parent == cousin_parent &&
|
|
|
|
gimp_item_get_index (GIMP_ITEM (iter2->data)) < gimp_item_get_index (GIMP_ITEM (cousin)))
|
|
|
|
{
|
|
|
|
ignore = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
cousin = cousin_parent;
|
|
|
|
}
|
|
|
|
while (cousin != NULL);
|
|
|
|
|
|
|
|
if (ignore)
|
|
|
|
break;
|
|
|
|
}
|
2020-05-21 15:06:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (iter2 == NULL)
|
|
|
|
merge_layers = g_list_prepend (merge_layers, iter->data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_list_length (merge_layers) > 1)
|
|
|
|
{
|
|
|
|
gchar *undo_name;
|
|
|
|
|
|
|
|
undo_name = g_strdup_printf (C_("undo-type", "Merge %d Layer Groups"),
|
|
|
|
g_list_length (merge_layers));
|
|
|
|
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_LAYERS_MERGE,
|
|
|
|
undo_name);
|
|
|
|
g_free (undo_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (iter = merge_layers; iter; iter = iter->next)
|
|
|
|
gimp_image_merge_group_layer (image, GIMP_GROUP_LAYER (iter->data));
|
|
|
|
|
|
|
|
if (g_list_length (merge_layers) > 1)
|
|
|
|
gimp_image_undo_group_end (image);
|
2009-09-21 19:17:02 +02:00
|
|
|
|
2020-05-21 15:06:48 +02:00
|
|
|
g_list_free (merge_layers);
|
2009-09-21 19:17:02 +02:00
|
|
|
gimp_image_flush (image);
|
|
|
|
}
|
|
|
|
|
2001-12-03 17:59:48 +00:00
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_delete_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2001-12-03 17:59:48 +00:00
|
|
|
{
|
2006-03-28 17:08:36 +00:00
|
|
|
GimpImage *image;
|
2020-03-21 14:22:58 +01:00
|
|
|
GList *layers;
|
|
|
|
GList *removed_layers;
|
2020-03-31 18:00:23 +02:00
|
|
|
GList *iter;
|
|
|
|
GList *iter2;
|
2020-03-21 14:22:58 +01:00
|
|
|
|
|
|
|
return_if_no_image (image, data);
|
|
|
|
|
|
|
|
layers = gimp_image_get_selected_layers (image);
|
|
|
|
|
|
|
|
/*TODO: we should have a failsafe to determine when we are going to
|
|
|
|
* delete all layers (i.e. all layers of first level at least) and
|
|
|
|
* forbid it. */
|
|
|
|
|
|
|
|
/* Copy of the original selection. */
|
|
|
|
removed_layers = g_list_copy (layers);
|
|
|
|
|
2020-03-31 18:00:23 +02:00
|
|
|
/* Removing children layers (they will be removed anyway by removing
|
2025-02-27 18:18:54 +01:00
|
|
|
* the parent).
|
2020-03-31 18:00:23 +02:00
|
|
|
*/
|
|
|
|
for (iter = removed_layers; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
for (iter2 = removed_layers; iter2; iter2 = iter2->next)
|
|
|
|
{
|
|
|
|
if (iter->data != iter2->data &&
|
2020-10-22 12:43:51 +02:00
|
|
|
gimp_viewable_is_ancestor (iter2->data, iter->data))
|
2020-03-31 18:00:23 +02:00
|
|
|
{
|
|
|
|
removed_layers = g_list_delete_link (removed_layers, iter);
|
|
|
|
iter = removed_layers;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-21 14:22:58 +01:00
|
|
|
if (g_list_length (removed_layers) > 1)
|
|
|
|
{
|
|
|
|
gchar *undo_name;
|
|
|
|
|
|
|
|
undo_name = g_strdup_printf (C_("undo-type", "Remove %d Layers"),
|
|
|
|
g_list_length (removed_layers));
|
|
|
|
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_ITEM_REMOVE,
|
|
|
|
undo_name);
|
|
|
|
}
|
|
|
|
|
2020-03-31 18:00:23 +02:00
|
|
|
for (iter = removed_layers; iter; iter = iter->next)
|
|
|
|
gimp_image_remove_layer (image, iter->data, TRUE, NULL);
|
2020-03-21 14:22:58 +01:00
|
|
|
|
|
|
|
if (g_list_length (removed_layers) > 1)
|
|
|
|
gimp_image_undo_group_end (image);
|
2001-04-20 16:27:44 +00:00
|
|
|
|
2020-03-21 14:22:58 +01:00
|
|
|
g_list_free (removed_layers);
|
2006-03-28 17:08:36 +00:00
|
|
|
gimp_image_flush (image);
|
2001-12-03 17:59:48 +00:00
|
|
|
}
|
|
|
|
|
2004-01-05 00:28:12 +00:00
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_text_discard_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2004-01-05 00:28:12 +00:00
|
|
|
{
|
2006-03-28 17:08:36 +00:00
|
|
|
GimpImage *image;
|
2023-01-10 23:13:49 +01:00
|
|
|
GList *layers;
|
|
|
|
GList *iter;
|
|
|
|
return_if_no_layers (image, layers, data);
|
2004-01-05 00:28:12 +00:00
|
|
|
|
2023-01-10 23:13:49 +01:00
|
|
|
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TEXT,
|
|
|
|
_("Discard Text Information"));
|
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
if (GIMP_IS_TEXT_LAYER (iter->data))
|
|
|
|
gimp_text_layer_discard (GIMP_TEXT_LAYER (iter->data));
|
|
|
|
gimp_image_undo_group_end (image);
|
2004-01-05 00:28:12 +00:00
|
|
|
}
|
|
|
|
|
2006-05-13 19:48:18 +00:00
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_text_to_vectors_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2006-05-13 19:48:18 +00:00
|
|
|
{
|
|
|
|
GimpImage *image;
|
2023-01-10 23:13:49 +01:00
|
|
|
GList *layers;
|
|
|
|
GList *iter;
|
|
|
|
return_if_no_layers (image, layers, data);
|
2006-05-13 19:48:18 +00:00
|
|
|
|
2023-01-10 23:13:49 +01:00
|
|
|
/* TODO: have the proper undo group. */
|
2024-07-13 05:07:57 +00:00
|
|
|
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_PATHS_IMPORT,
|
2023-01-10 23:13:49 +01:00
|
|
|
_("Add Paths"));
|
|
|
|
for (iter = layers; iter; iter = iter->next)
|
2006-05-13 19:48:18 +00:00
|
|
|
{
|
2023-01-10 23:13:49 +01:00
|
|
|
GimpLayer *layer = iter->data;
|
|
|
|
|
|
|
|
if (GIMP_IS_TEXT_LAYER (layer))
|
|
|
|
{
|
2024-07-12 06:16:25 +00:00
|
|
|
GimpPath *path;
|
|
|
|
gint x, y;
|
2006-05-13 19:48:18 +00:00
|
|
|
|
2024-07-13 05:07:57 +00:00
|
|
|
path = gimp_text_path_new (image, GIMP_TEXT_LAYER (layer)->text);
|
2006-05-13 19:48:18 +00:00
|
|
|
|
2023-01-10 23:13:49 +01:00
|
|
|
gimp_item_get_offset (GIMP_ITEM (layer), &x, &y);
|
2024-07-11 00:07:44 +00:00
|
|
|
gimp_item_translate (GIMP_ITEM (path), x, y, FALSE);
|
2006-05-13 19:48:18 +00:00
|
|
|
|
2024-07-11 00:07:44 +00:00
|
|
|
gimp_image_add_path (image, path,
|
|
|
|
GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE);
|
2023-01-10 23:13:49 +01:00
|
|
|
gimp_image_flush (image);
|
|
|
|
}
|
2006-05-13 19:48:18 +00:00
|
|
|
}
|
2023-01-10 23:13:49 +01:00
|
|
|
gimp_image_undo_group_end (image);
|
2006-05-13 19:48:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_text_along_vectors_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2006-05-13 19:48:18 +00:00
|
|
|
{
|
|
|
|
GimpImage *image;
|
2023-01-10 23:13:49 +01:00
|
|
|
GList *layers;
|
|
|
|
GList *paths;
|
2006-05-13 19:48:18 +00:00
|
|
|
GimpLayer *layer;
|
2024-07-13 05:07:57 +00:00
|
|
|
GimpPath *path;
|
2023-01-10 23:13:49 +01:00
|
|
|
return_if_no_layers (image, layers, data);
|
2024-07-17 23:25:48 +00:00
|
|
|
return_if_no_paths (image, paths, data);
|
2023-01-10 23:13:49 +01:00
|
|
|
|
|
|
|
if (g_list_length (layers) != 1 || g_list_length (paths) != 1)
|
|
|
|
return;
|
2006-05-13 19:48:18 +00:00
|
|
|
|
2024-07-13 05:07:57 +00:00
|
|
|
layer = layers->data;
|
|
|
|
path = paths->data;
|
2006-05-13 19:48:18 +00:00
|
|
|
if (GIMP_IS_TEXT_LAYER (layer))
|
|
|
|
{
|
2024-07-12 06:16:25 +00:00
|
|
|
gdouble box_width;
|
|
|
|
gdouble box_height;
|
2024-07-13 05:07:57 +00:00
|
|
|
GimpPath *new_path;
|
2024-07-12 06:16:25 +00:00
|
|
|
gdouble offset;
|
2018-11-13 11:30:49 +09:00
|
|
|
|
|
|
|
box_width = gimp_item_get_width (GIMP_ITEM (layer));
|
|
|
|
box_height = gimp_item_get_height (GIMP_ITEM (layer));
|
2006-05-13 19:48:18 +00:00
|
|
|
|
2024-07-13 05:07:57 +00:00
|
|
|
new_path = gimp_text_path_new (image, GIMP_TEXT_LAYER (layer)->text);
|
2006-05-13 19:48:18 +00:00
|
|
|
|
2018-11-13 11:30:49 +09:00
|
|
|
offset = 0;
|
|
|
|
switch (GIMP_TEXT_LAYER (layer)->text->base_dir)
|
|
|
|
{
|
|
|
|
case GIMP_TEXT_DIRECTION_LTR:
|
|
|
|
case GIMP_TEXT_DIRECTION_RTL:
|
|
|
|
offset = 0.5 * box_height;
|
|
|
|
break;
|
|
|
|
case GIMP_TEXT_DIRECTION_TTB_RTL:
|
|
|
|
case GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT:
|
|
|
|
case GIMP_TEXT_DIRECTION_TTB_LTR:
|
|
|
|
case GIMP_TEXT_DIRECTION_TTB_LTR_UPRIGHT:
|
|
|
|
{
|
|
|
|
GimpStroke *stroke = NULL;
|
|
|
|
|
2024-07-13 05:07:57 +00:00
|
|
|
while ((stroke = gimp_path_stroke_get_next (new_path, stroke)))
|
2018-11-13 11:30:49 +09:00
|
|
|
{
|
|
|
|
gimp_stroke_rotate (stroke, 0, 0, 270);
|
|
|
|
gimp_stroke_translate (stroke, 0, box_width);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
offset = 0.5 * box_width;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2024-07-13 05:07:57 +00:00
|
|
|
gimp_path_warp_path (path, new_path, offset);
|
2018-11-13 11:30:49 +09:00
|
|
|
|
2024-07-13 05:07:57 +00:00
|
|
|
gimp_item_set_visible (GIMP_ITEM (new_path), TRUE, FALSE);
|
2006-05-13 19:48:18 +00:00
|
|
|
|
2024-07-13 05:07:57 +00:00
|
|
|
gimp_image_add_path (image, new_path,
|
2024-07-11 00:07:44 +00:00
|
|
|
GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE);
|
2006-05-13 19:48:18 +00:00
|
|
|
gimp_image_flush (image);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2001-12-03 17:59:48 +00:00
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_resize_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2001-12-03 17:59:48 +00:00
|
|
|
{
|
2016-09-12 00:03:22 +02:00
|
|
|
GimpImage *image;
|
|
|
|
GimpLayer *layer;
|
2020-07-30 21:26:13 +02:00
|
|
|
GList *layers;
|
2016-09-12 00:03:22 +02:00
|
|
|
GtkWidget *widget;
|
|
|
|
GtkWidget *dialog;
|
2020-07-30 21:26:13 +02:00
|
|
|
return_if_no_layers (image, layers, data);
|
2004-04-29 12:52:29 +00:00
|
|
|
return_if_no_widget (widget, data);
|
2001-04-20 16:27:44 +00:00
|
|
|
|
2016-09-12 00:03:22 +02:00
|
|
|
#define RESIZE_DIALOG_KEY "gimp-resize-dialog"
|
2010-06-06 16:44:36 +02:00
|
|
|
|
2020-07-30 21:26:13 +02:00
|
|
|
g_return_if_fail (g_list_length (layers) == 1);
|
|
|
|
|
|
|
|
layer = layers->data;
|
|
|
|
|
2016-09-12 00:03:22 +02:00
|
|
|
dialog = dialogs_get_dialog (G_OBJECT (layer), RESIZE_DIALOG_KEY);
|
2005-08-24 17:22:07 +00:00
|
|
|
|
2016-09-12 00:03:22 +02:00
|
|
|
if (! dialog)
|
|
|
|
{
|
2016-10-20 00:30:10 +02:00
|
|
|
GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config);
|
|
|
|
GimpDisplay *display = NULL;
|
2016-09-12 00:03:22 +02:00
|
|
|
|
|
|
|
if (GIMP_IS_IMAGE_WINDOW (data))
|
|
|
|
display = action_data_get_display (data);
|
|
|
|
|
Issue #8900 and #9923: reimplementing GimpUnit as a proper class.
This fixes all our GObject Introspection issues with GimpUnit which was
both an enum and an int-derived type of user-defined units *completing*
the enum values. GIR clearly didn't like this!
Now GimpUnit is a proper class and units are unique objects, allowing to
compare them with an identity test (i.e. `unit == gimp_unit_pixel ()`
tells us if unit is the pixel unit or not), which makes it easy to use,
just like with int, yet adding also methods, making for nicer
introspected API.
As an aside, this also fixes #10738, by having all the built-in units
retrievable even if libgimpbase had not been properly initialized with
gimp_base_init().
I haven't checked in details how GIR works to introspect, but it looks
like it loads the library to inspect and runs functions, hence
triggering some CRITICALS because virtual methods (supposed to be
initialized with gimp_base_init() run by libgimp) are not set. This new
code won't trigger any critical because the vtable method are now not
necessary, at least for all built-in units.
Note that GimpUnit is still in libgimpbase. It could have been moved to
libgimp in order to avoid any virtual method table (since we need to
keep core and libgimp side's units in sync, PDB is required), but too
many libgimpwidgets widgets were already using GimpUnit. And technically
most of GimpUnit logic doesn't require PDB (only the creation/sync
part). This is one of the reasons why user-created GimpUnit list is
handled and stored differently from other types of objects.
Globally this simplifies the code a lot too and we don't need separate
implementations of various utils for core and libgimp, which means less
prone to errors.
2024-07-25 20:55:21 +02:00
|
|
|
if (layer_resize_unit == NULL)
|
|
|
|
layer_resize_unit = gimp_unit_pixel ();
|
|
|
|
|
|
|
|
if (layer_resize_unit != gimp_unit_percent () && display)
|
2016-09-12 00:03:22 +02:00
|
|
|
layer_resize_unit = gimp_display_get_shell (display)->unit;
|
|
|
|
|
|
|
|
dialog = resize_dialog_new (GIMP_VIEWABLE (layer),
|
|
|
|
action_data_get_context (data),
|
|
|
|
_("Set Layer Boundary Size"),
|
|
|
|
"gimp-layer-resize",
|
|
|
|
widget,
|
|
|
|
gimp_standard_help_func,
|
|
|
|
GIMP_HELP_LAYER_RESIZE,
|
|
|
|
layer_resize_unit,
|
2016-10-20 00:30:10 +02:00
|
|
|
config->layer_resize_fill_type,
|
|
|
|
GIMP_ITEM_SET_NONE,
|
|
|
|
FALSE,
|
2016-09-25 22:18:37 +02:00
|
|
|
layers_resize_callback,
|
2016-09-24 15:07:04 +02:00
|
|
|
NULL);
|
2016-09-12 00:03:22 +02:00
|
|
|
|
|
|
|
dialogs_attach_dialog (G_OBJECT (layer), RESIZE_DIALOG_KEY, dialog);
|
|
|
|
}
|
2004-10-27 10:33:08 +00:00
|
|
|
|
2016-09-12 00:03:22 +02:00
|
|
|
gtk_window_present (GTK_WINDOW (dialog));
|
2001-04-20 16:27:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_resize_to_image_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2001-04-20 16:27:44 +00:00
|
|
|
{
|
2006-03-28 17:08:36 +00:00
|
|
|
GimpImage *image;
|
2020-07-01 01:00:48 +02:00
|
|
|
GList *layers;
|
|
|
|
GList *iter;
|
|
|
|
return_if_no_layers (image, layers, data);
|
|
|
|
|
|
|
|
if (g_list_length (layers) > 1)
|
|
|
|
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_RESIZE,
|
|
|
|
_("Layers to Image Size"));
|
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
gimp_layer_resize_to_image (iter->data,
|
|
|
|
action_data_get_context (data),
|
|
|
|
GIMP_FILL_TRANSPARENT);
|
|
|
|
|
|
|
|
if (g_list_length (layers) > 1)
|
|
|
|
gimp_image_undo_group_end (image);
|
2003-08-31 18:10:15 +00:00
|
|
|
|
2006-03-28 17:08:36 +00:00
|
|
|
gimp_image_flush (image);
|
2002-04-12 12:29:51 +00:00
|
|
|
}
|
2001-04-20 16:27:44 +00:00
|
|
|
|
2002-04-12 12:29:51 +00:00
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_scale_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2002-04-12 12:29:51 +00:00
|
|
|
{
|
2016-09-24 14:14:43 +02:00
|
|
|
GimpImage *image;
|
2020-07-30 21:26:13 +02:00
|
|
|
GList *layers;
|
2016-09-24 14:14:43 +02:00
|
|
|
GimpLayer *layer;
|
|
|
|
GtkWidget *widget;
|
|
|
|
GtkWidget *dialog;
|
2020-07-30 21:26:13 +02:00
|
|
|
return_if_no_layers (image, layers, data);
|
2004-04-29 12:52:29 +00:00
|
|
|
return_if_no_widget (widget, data);
|
2002-04-12 12:29:51 +00:00
|
|
|
|
2016-09-24 14:14:43 +02:00
|
|
|
#define SCALE_DIALOG_KEY "gimp-scale-dialog"
|
2010-06-06 16:44:36 +02:00
|
|
|
|
2020-07-30 21:26:13 +02:00
|
|
|
g_return_if_fail (g_list_length (layers) == 1);
|
|
|
|
|
|
|
|
layer = layers->data;
|
|
|
|
|
2016-09-24 14:14:43 +02:00
|
|
|
dialog = dialogs_get_dialog (G_OBJECT (layer), SCALE_DIALOG_KEY);
|
2004-10-18 11:29:58 +00:00
|
|
|
|
2016-09-24 14:14:43 +02:00
|
|
|
if (! dialog)
|
|
|
|
{
|
|
|
|
GimpDisplay *display = NULL;
|
2004-10-18 11:29:58 +00:00
|
|
|
|
2016-09-24 14:14:43 +02:00
|
|
|
if (GIMP_IS_IMAGE_WINDOW (data))
|
|
|
|
display = action_data_get_display (data);
|
|
|
|
|
Issue #8900 and #9923: reimplementing GimpUnit as a proper class.
This fixes all our GObject Introspection issues with GimpUnit which was
both an enum and an int-derived type of user-defined units *completing*
the enum values. GIR clearly didn't like this!
Now GimpUnit is a proper class and units are unique objects, allowing to
compare them with an identity test (i.e. `unit == gimp_unit_pixel ()`
tells us if unit is the pixel unit or not), which makes it easy to use,
just like with int, yet adding also methods, making for nicer
introspected API.
As an aside, this also fixes #10738, by having all the built-in units
retrievable even if libgimpbase had not been properly initialized with
gimp_base_init().
I haven't checked in details how GIR works to introspect, but it looks
like it loads the library to inspect and runs functions, hence
triggering some CRITICALS because virtual methods (supposed to be
initialized with gimp_base_init() run by libgimp) are not set. This new
code won't trigger any critical because the vtable method are now not
necessary, at least for all built-in units.
Note that GimpUnit is still in libgimpbase. It could have been moved to
libgimp in order to avoid any virtual method table (since we need to
keep core and libgimp side's units in sync, PDB is required), but too
many libgimpwidgets widgets were already using GimpUnit. And technically
most of GimpUnit logic doesn't require PDB (only the creation/sync
part). This is one of the reasons why user-created GimpUnit list is
handled and stored differently from other types of objects.
Globally this simplifies the code a lot too and we don't need separate
implementations of various utils for core and libgimp, which means less
prone to errors.
2024-07-25 20:55:21 +02:00
|
|
|
if (layer_scale_unit == NULL)
|
|
|
|
layer_scale_unit = gimp_unit_pixel ();;
|
|
|
|
|
|
|
|
if (layer_scale_unit != gimp_unit_percent () && display)
|
2016-09-24 14:14:43 +02:00
|
|
|
layer_scale_unit = gimp_display_get_shell (display)->unit;
|
|
|
|
|
|
|
|
if (layer_scale_interp == -1)
|
|
|
|
layer_scale_interp = image->gimp->config->interpolation_type;
|
2004-10-18 11:29:58 +00:00
|
|
|
|
2016-09-24 14:14:43 +02:00
|
|
|
dialog = scale_dialog_new (GIMP_VIEWABLE (layer),
|
|
|
|
action_data_get_context (data),
|
|
|
|
_("Scale Layer"), "gimp-layer-scale",
|
|
|
|
widget,
|
|
|
|
gimp_standard_help_func, GIMP_HELP_LAYER_SCALE,
|
|
|
|
layer_scale_unit,
|
|
|
|
layer_scale_interp,
|
2016-09-25 22:18:37 +02:00
|
|
|
layers_scale_callback,
|
2016-09-24 14:14:43 +02:00
|
|
|
display);
|
|
|
|
|
|
|
|
dialogs_attach_dialog (G_OBJECT (layer), SCALE_DIALOG_KEY, dialog);
|
|
|
|
}
|
|
|
|
|
|
|
|
gtk_window_present (GTK_WINDOW (dialog));
|
2001-04-20 16:27:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_crop_to_selection_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2001-04-20 16:27:44 +00:00
|
|
|
{
|
2006-03-28 17:08:36 +00:00
|
|
|
GimpImage *image;
|
2020-07-30 18:35:34 +02:00
|
|
|
GList *layers;
|
|
|
|
GList *iter;
|
2006-10-09 18:49:15 +00:00
|
|
|
GtkWidget *widget;
|
2020-07-30 18:35:34 +02:00
|
|
|
gchar *desc;
|
2015-07-01 15:19:03 +02:00
|
|
|
gint x, y;
|
|
|
|
gint width, height;
|
2020-07-30 18:35:34 +02:00
|
|
|
return_if_no_layers (image, layers, data);
|
2006-10-09 18:49:15 +00:00
|
|
|
return_if_no_widget (widget, data);
|
2002-04-12 12:29:51 +00:00
|
|
|
|
2015-07-01 15:19:03 +02:00
|
|
|
if (! gimp_item_bounds (GIMP_ITEM (gimp_image_get_mask (image)),
|
|
|
|
&x, &y, &width, &height))
|
2002-04-12 12:29:51 +00:00
|
|
|
{
|
2008-11-04 12:33:09 +00:00
|
|
|
gimp_message_literal (image->gimp,
|
2013-09-15 04:59:20 +12:00
|
|
|
G_OBJECT (widget), GIMP_MESSAGE_WARNING,
|
2016-09-25 22:40:28 +02:00
|
|
|
_("Cannot crop because the current selection "
|
|
|
|
"is empty."));
|
2002-04-12 12:29:51 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-07-30 18:35:34 +02:00
|
|
|
desc = g_strdup_printf (ngettext ("Crop Layer to Selection",
|
|
|
|
"Crop %d Layers to Selection",
|
|
|
|
g_list_length (layers)),
|
|
|
|
g_list_length (layers));
|
|
|
|
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_RESIZE, desc);
|
|
|
|
g_free (desc);
|
2002-04-12 12:29:51 +00:00
|
|
|
|
2020-07-30 18:35:34 +02:00
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
gint off_x, off_y;
|
|
|
|
|
|
|
|
gimp_item_get_offset (GIMP_ITEM (iter->data), &off_x, &off_y);
|
|
|
|
off_x -= x;
|
|
|
|
off_y -= y;
|
2002-04-12 12:29:51 +00:00
|
|
|
|
2020-07-30 18:35:34 +02:00
|
|
|
gimp_item_resize (GIMP_ITEM (iter->data),
|
|
|
|
action_data_get_context (data), GIMP_FILL_TRANSPARENT,
|
|
|
|
width, height, off_x, off_y);
|
|
|
|
}
|
2002-04-12 12:29:51 +00:00
|
|
|
|
2006-03-28 17:08:36 +00:00
|
|
|
gimp_image_undo_group_end (image);
|
|
|
|
gimp_image_flush (image);
|
2001-04-20 16:27:44 +00:00
|
|
|
}
|
|
|
|
|
2012-09-22 20:35:36 +02:00
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_crop_to_content_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2012-09-22 20:35:36 +02:00
|
|
|
{
|
|
|
|
GimpImage *image;
|
2020-07-30 18:35:34 +02:00
|
|
|
GList *layers;
|
|
|
|
GList *iter;
|
2012-09-22 20:35:36 +02:00
|
|
|
GtkWidget *widget;
|
2020-07-30 18:35:34 +02:00
|
|
|
gchar *desc;
|
2016-09-25 22:40:28 +02:00
|
|
|
gint x, y;
|
|
|
|
gint width, height;
|
2020-07-30 18:35:34 +02:00
|
|
|
gint n_croppable = 0;
|
|
|
|
return_if_no_layers (image, layers, data);
|
2012-09-22 20:35:36 +02:00
|
|
|
return_if_no_widget (widget, data);
|
|
|
|
|
2020-07-30 18:35:34 +02:00
|
|
|
for (iter = layers; iter; iter = iter->next)
|
2012-09-22 20:35:36 +02:00
|
|
|
{
|
2020-07-30 18:35:34 +02:00
|
|
|
switch (gimp_pickable_auto_shrink (GIMP_PICKABLE (iter->data),
|
|
|
|
0, 0,
|
|
|
|
gimp_item_get_width (iter->data),
|
|
|
|
gimp_item_get_height (iter->data),
|
|
|
|
&x, &y, &width, &height))
|
|
|
|
{
|
|
|
|
case GIMP_AUTO_SHRINK_SHRINK:
|
|
|
|
n_croppable++;
|
|
|
|
break;
|
2012-09-22 20:35:36 +02:00
|
|
|
|
2020-07-30 18:35:34 +02:00
|
|
|
case GIMP_AUTO_SHRINK_EMPTY:
|
|
|
|
/* Cannot crop because the layer has no content. */
|
|
|
|
case GIMP_AUTO_SHRINK_UNSHRINKABLE:
|
|
|
|
/* Cannot crop because the active layer is already cropped to
|
|
|
|
* its content. */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2012-09-22 20:35:36 +02:00
|
|
|
|
2020-07-30 18:35:34 +02:00
|
|
|
if (n_croppable == 0)
|
|
|
|
{
|
2014-08-31 17:38:49 +02:00
|
|
|
gimp_message_literal (image->gimp,
|
|
|
|
G_OBJECT (widget), GIMP_MESSAGE_INFO,
|
2020-07-30 18:35:34 +02:00
|
|
|
_("Cannot crop because none of the selected"
|
|
|
|
" layers have content or they are already"
|
|
|
|
" cropped to their content."));
|
|
|
|
return;
|
|
|
|
}
|
2012-09-22 20:35:36 +02:00
|
|
|
|
2020-07-30 18:35:34 +02:00
|
|
|
desc = g_strdup_printf (ngettext ("Crop Layer to Content",
|
|
|
|
"Crop %d Layers to Content",
|
|
|
|
n_croppable),
|
|
|
|
n_croppable);
|
|
|
|
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_RESIZE, desc);
|
|
|
|
g_free (desc);
|
|
|
|
|
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
switch (gimp_pickable_auto_shrink (GIMP_PICKABLE (iter->data),
|
|
|
|
0, 0,
|
|
|
|
gimp_item_get_width (iter->data),
|
|
|
|
gimp_item_get_height (iter->data),
|
|
|
|
&x, &y, &width, &height))
|
|
|
|
{
|
|
|
|
case GIMP_AUTO_SHRINK_SHRINK:
|
|
|
|
gimp_item_resize (iter->data,
|
|
|
|
action_data_get_context (data), GIMP_FILL_TRANSPARENT,
|
|
|
|
width, height, -x, -y);
|
|
|
|
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2014-08-31 17:38:49 +02:00
|
|
|
}
|
2020-07-30 18:35:34 +02:00
|
|
|
gimp_image_flush (image);
|
|
|
|
gimp_image_undo_group_end (image);
|
2012-09-22 20:35:36 +02:00
|
|
|
}
|
|
|
|
|
2001-04-20 16:27:44 +00:00
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_mask_add_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2001-04-20 16:27:44 +00:00
|
|
|
{
|
2016-09-23 14:35:14 +02:00
|
|
|
GimpImage *image;
|
2020-05-02 01:42:04 +02:00
|
|
|
GList *layers;
|
|
|
|
GList *iter;
|
2016-09-23 14:35:14 +02:00
|
|
|
GtkWidget *widget;
|
|
|
|
GtkWidget *dialog;
|
2020-05-02 01:42:04 +02:00
|
|
|
GList *update_layers = NULL;
|
|
|
|
gint n_channels = 0;
|
|
|
|
return_if_no_layers (image, layers, data);
|
2004-04-29 12:52:29 +00:00
|
|
|
return_if_no_widget (widget, data);
|
2001-04-20 16:27:44 +00:00
|
|
|
|
2020-05-02 01:42:04 +02:00
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
g_return_if_fail (GIMP_IS_LAYER (iter->data));
|
|
|
|
|
|
|
|
if (! gimp_layer_get_mask (iter->data))
|
|
|
|
{
|
|
|
|
update_layers = g_list_prepend (update_layers, iter->data);
|
|
|
|
n_channels++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (n_channels == 0)
|
|
|
|
/* No layers or they all have masks already. */
|
2016-08-23 19:18:20 +02:00
|
|
|
return;
|
|
|
|
|
2016-09-23 14:35:14 +02:00
|
|
|
#define ADD_MASK_DIALOG_KEY "gimp-add-mask-dialog"
|
2016-08-23 19:18:20 +02:00
|
|
|
|
2020-05-02 01:42:04 +02:00
|
|
|
for (iter = update_layers; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
dialog = dialogs_get_dialog (G_OBJECT (iter->data), ADD_MASK_DIALOG_KEY);
|
|
|
|
if (dialog)
|
|
|
|
break;
|
|
|
|
}
|
2004-10-18 11:29:58 +00:00
|
|
|
|
2016-09-23 14:35:14 +02:00
|
|
|
if (! dialog)
|
|
|
|
{
|
|
|
|
GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config);
|
2004-10-18 11:29:58 +00:00
|
|
|
|
2020-05-02 01:42:04 +02:00
|
|
|
dialog = layer_add_mask_dialog_new (update_layers, action_data_get_context (data),
|
2016-09-23 14:35:14 +02:00
|
|
|
widget,
|
|
|
|
config->layer_add_mask_type,
|
|
|
|
config->layer_add_mask_invert,
|
|
|
|
layers_add_mask_callback,
|
|
|
|
NULL);
|
|
|
|
|
2020-05-02 01:42:04 +02:00
|
|
|
for (iter = update_layers; iter; iter = iter->next)
|
|
|
|
dialogs_attach_dialog (G_OBJECT (iter->data), ADD_MASK_DIALOG_KEY, dialog);
|
2016-09-23 14:35:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
gtk_window_present (GTK_WINDOW (dialog));
|
2001-04-20 16:27:44 +00:00
|
|
|
}
|
|
|
|
|
2016-08-23 19:18:20 +02:00
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_mask_add_last_vals_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2016-08-23 19:18:20 +02:00
|
|
|
{
|
|
|
|
GimpImage *image;
|
2020-04-22 00:05:31 +02:00
|
|
|
GList *layers;
|
|
|
|
GList *iter;
|
2016-08-23 19:18:20 +02:00
|
|
|
GtkWidget *widget;
|
|
|
|
GimpDialogConfig *config;
|
|
|
|
GimpChannel *channel = NULL;
|
|
|
|
GimpLayerMask *mask;
|
2020-04-22 00:05:31 +02:00
|
|
|
return_if_no_layers (image, layers, data);
|
2016-08-23 19:18:20 +02:00
|
|
|
return_if_no_widget (widget, data);
|
|
|
|
|
|
|
|
config = GIMP_DIALOG_CONFIG (image->gimp->config);
|
|
|
|
|
|
|
|
if (config->layer_add_mask_type == GIMP_ADD_MASK_CHANNEL)
|
|
|
|
{
|
2021-06-20 00:16:38 +02:00
|
|
|
GList *selected_channels;
|
2016-08-23 19:18:20 +02:00
|
|
|
|
2021-06-20 00:16:38 +02:00
|
|
|
selected_channels = gimp_image_get_selected_channels (image);
|
|
|
|
|
|
|
|
if (selected_channels)
|
|
|
|
{
|
|
|
|
channel = selected_channels->data;
|
|
|
|
}
|
|
|
|
else
|
2016-08-23 19:18:20 +02:00
|
|
|
{
|
|
|
|
GimpContainer *channels = gimp_image_get_channels (image);
|
|
|
|
|
|
|
|
channel = GIMP_CHANNEL (gimp_container_get_first_child (channels));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (! channel)
|
|
|
|
{
|
2019-07-04 01:11:48 +02:00
|
|
|
layers_mask_add_cmd_callback (action, value, data);
|
2016-08-23 19:18:20 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-22 00:05:31 +02:00
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
if (! gimp_layer_get_mask (iter->data))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (iter == NULL)
|
|
|
|
/* No layers or they all have masks already. */
|
|
|
|
return;
|
|
|
|
|
|
|
|
gimp_image_undo_group_start (image,
|
|
|
|
GIMP_UNDO_GROUP_LAYER_ADD,
|
|
|
|
_("Add Layer Masks"));
|
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
if (gimp_layer_get_mask (iter->data))
|
|
|
|
continue;
|
2016-08-23 19:18:20 +02:00
|
|
|
|
2020-04-22 00:05:31 +02:00
|
|
|
mask = gimp_layer_create_mask (iter->data,
|
|
|
|
config->layer_add_mask_type,
|
|
|
|
channel);
|
|
|
|
|
|
|
|
if (config->layer_add_mask_invert)
|
|
|
|
gimp_channel_invert (GIMP_CHANNEL (mask), FALSE);
|
|
|
|
|
|
|
|
gimp_layer_add_mask (iter->data, mask, TRUE, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
gimp_image_undo_group_end (image);
|
2016-08-23 19:18:20 +02:00
|
|
|
|
|
|
|
gimp_image_flush (image);
|
|
|
|
}
|
|
|
|
|
2001-04-20 16:27:44 +00:00
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_mask_apply_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2001-04-20 16:27:44 +00:00
|
|
|
{
|
2020-04-24 18:23:40 +02:00
|
|
|
GimpMaskApplyMode mode;
|
|
|
|
GimpImage *image;
|
|
|
|
GList *layers;
|
|
|
|
GList *iter;
|
|
|
|
gchar *undo_text = NULL;
|
|
|
|
GimpUndoType undo_type = GIMP_UNDO_GROUP_NONE;
|
2001-04-20 16:27:44 +00:00
|
|
|
|
2020-04-24 18:23:40 +02:00
|
|
|
return_if_no_layers (image, layers, data);
|
|
|
|
|
|
|
|
mode = (GimpMaskApplyMode) g_variant_get_int32 (value);
|
|
|
|
for (iter = layers; iter; iter = iter->next)
|
2001-04-20 16:27:44 +00:00
|
|
|
{
|
2020-04-24 18:23:40 +02:00
|
|
|
if (gimp_layer_get_mask (iter->data) &&
|
|
|
|
(mode != GIMP_MASK_APPLY ||
|
|
|
|
(! gimp_viewable_get_children (GIMP_VIEWABLE (iter->data)) &&
|
2022-02-15 15:42:44 +01:00
|
|
|
! gimp_item_is_content_locked (GIMP_ITEM (iter->data), NULL))))
|
2020-04-24 18:23:40 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (iter == NULL)
|
2020-08-08 05:58:47 +00:00
|
|
|
/* No layers or none have applicable masks. */
|
2020-04-24 18:23:40 +02:00
|
|
|
return;
|
2016-08-23 19:18:20 +02:00
|
|
|
|
2020-04-24 18:23:40 +02:00
|
|
|
switch (mode)
|
|
|
|
{
|
|
|
|
case GIMP_MASK_APPLY:
|
|
|
|
undo_type = GIMP_UNDO_GROUP_MASK;
|
|
|
|
undo_text = _("Apply Layer Masks");
|
|
|
|
break;
|
|
|
|
case GIMP_MASK_DISCARD:
|
|
|
|
undo_type = GIMP_UNDO_GROUP_MASK;
|
|
|
|
undo_text = _("Delete Layer Masks");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
g_warning ("%s: unhandled GimpMaskApplyMode %d\n",
|
|
|
|
G_STRFUNC, mode);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (undo_type != GIMP_UNDO_GROUP_NONE)
|
|
|
|
gimp_image_undo_group_start (image, undo_type, undo_text);
|
|
|
|
|
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
if (gimp_layer_get_mask (iter->data))
|
|
|
|
{
|
|
|
|
if (mode == GIMP_MASK_APPLY &&
|
|
|
|
(gimp_viewable_get_children (GIMP_VIEWABLE (iter->data)) ||
|
2022-02-15 15:42:44 +01:00
|
|
|
gimp_item_is_content_locked (GIMP_ITEM (iter->data), NULL)))
|
2020-04-24 18:23:40 +02:00
|
|
|
/* Layer groups cannot apply masks. Neither can
|
|
|
|
* content-locked layers.
|
|
|
|
*/
|
|
|
|
continue;
|
|
|
|
|
|
|
|
gimp_layer_apply_mask (iter->data, mode, TRUE);
|
|
|
|
}
|
2001-04-20 16:27:44 +00:00
|
|
|
}
|
2020-04-24 18:23:40 +02:00
|
|
|
|
|
|
|
if (undo_type != GIMP_UNDO_GROUP_NONE)
|
|
|
|
gimp_image_undo_group_end (image);
|
|
|
|
|
|
|
|
gimp_image_flush (image);
|
2001-04-20 16:27:44 +00:00
|
|
|
}
|
|
|
|
|
2004-08-20 22:32:14 +00:00
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_mask_edit_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2004-08-20 22:32:14 +00:00
|
|
|
{
|
2016-08-23 19:18:20 +02:00
|
|
|
GimpImage *image;
|
2020-07-30 22:30:17 +02:00
|
|
|
GList *layers;
|
|
|
|
GList *iter;
|
|
|
|
gboolean active = g_variant_get_boolean (value);
|
|
|
|
return_if_no_layers (image, layers, data);
|
2004-08-20 22:32:14 +00:00
|
|
|
|
2020-07-30 22:30:17 +02:00
|
|
|
/* Multiple-layer selection cannot edit masks. */
|
|
|
|
active = active && (g_list_length (layers) == 1);
|
2004-08-20 22:32:14 +00:00
|
|
|
|
2020-07-30 22:30:17 +02:00
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
if (gimp_layer_get_mask (iter->data))
|
|
|
|
gimp_layer_set_edit_mask (iter->data, active);
|
|
|
|
|
|
|
|
gimp_image_flush (image);
|
2004-08-20 22:32:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_mask_show_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2004-08-20 22:32:14 +00:00
|
|
|
{
|
2016-08-23 19:18:20 +02:00
|
|
|
GimpImage *image;
|
2020-04-28 17:04:47 +02:00
|
|
|
GList *layers;
|
|
|
|
GList *iter;
|
|
|
|
gboolean active = g_variant_get_boolean (value);
|
|
|
|
gboolean have_masks = FALSE;
|
|
|
|
return_if_no_layers (image, layers, data);
|
2004-08-20 22:32:14 +00:00
|
|
|
|
2020-04-28 17:04:47 +02:00
|
|
|
for (iter = layers; iter; iter = iter->next)
|
2004-08-20 22:32:14 +00:00
|
|
|
{
|
2020-04-28 17:04:47 +02:00
|
|
|
if (gimp_layer_get_mask (iter->data))
|
|
|
|
{
|
|
|
|
have_masks = TRUE;
|
|
|
|
/* A bit of tricky to handle multiple and diverse layers with
|
|
|
|
* a toggle action (with only binary state).
|
|
|
|
* In non-active state, we will consider sets of both shown
|
|
|
|
* and hidden masks as ok and exits. This allows us to switch
|
|
|
|
* the action "active" state without actually changing
|
|
|
|
* individual masks state without explicit user request.
|
|
|
|
*/
|
|
|
|
if (! active && ! gimp_layer_get_show_mask (iter->data))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (! have_masks)
|
|
|
|
return;
|
2004-08-20 22:32:14 +00:00
|
|
|
|
2020-04-28 17:04:47 +02:00
|
|
|
gimp_image_undo_group_start (image,
|
|
|
|
GIMP_UNDO_GROUP_LAYER_ADD,
|
|
|
|
_("Show Layer Masks"));
|
|
|
|
|
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
if (gimp_layer_get_mask (iter->data))
|
|
|
|
{
|
|
|
|
gimp_layer_set_show_mask (iter->data, active, TRUE);
|
|
|
|
}
|
2004-08-20 22:32:14 +00:00
|
|
|
}
|
2020-04-28 17:04:47 +02:00
|
|
|
|
|
|
|
gimp_image_flush (image);
|
|
|
|
gimp_image_undo_group_end (image);
|
2004-08-20 22:32:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_mask_disable_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2004-08-20 22:32:14 +00:00
|
|
|
{
|
2016-08-23 19:18:20 +02:00
|
|
|
GimpImage *image;
|
2020-04-28 17:04:47 +02:00
|
|
|
GList *layers;
|
|
|
|
GList *iter;
|
|
|
|
gboolean active = g_variant_get_boolean (value);
|
|
|
|
gboolean have_masks = FALSE;
|
|
|
|
return_if_no_layers (image, layers, data);
|
2004-08-20 22:32:14 +00:00
|
|
|
|
2020-04-28 17:04:47 +02:00
|
|
|
for (iter = layers; iter; iter = iter->next)
|
2004-08-20 22:32:14 +00:00
|
|
|
{
|
2020-04-28 17:04:47 +02:00
|
|
|
if (gimp_layer_get_mask (iter->data))
|
|
|
|
{
|
|
|
|
have_masks = TRUE;
|
|
|
|
/* A bit of tricky to handle multiple and diverse layers with
|
|
|
|
* a toggle action (with only binary state).
|
|
|
|
* In non-active state, we will consider sets of both enabled
|
|
|
|
* and disabled masks as ok and exits. This allows us to
|
|
|
|
* switch the action "active" state without actually changing
|
|
|
|
* individual masks state without explicit user request.
|
|
|
|
*/
|
|
|
|
if (! active && gimp_layer_get_apply_mask (iter->data))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (! have_masks)
|
|
|
|
return;
|
2004-08-20 22:32:14 +00:00
|
|
|
|
2020-04-28 17:04:47 +02:00
|
|
|
gimp_image_undo_group_start (image,
|
|
|
|
GIMP_UNDO_GROUP_LAYER_ADD,
|
|
|
|
_("Disable Layer Masks"));
|
|
|
|
|
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
if (gimp_layer_get_mask (iter->data))
|
|
|
|
{
|
|
|
|
gimp_layer_set_apply_mask (iter->data, ! active, TRUE);
|
|
|
|
}
|
2004-08-20 22:32:14 +00:00
|
|
|
}
|
2020-04-28 17:04:47 +02:00
|
|
|
|
|
|
|
gimp_image_flush (image);
|
|
|
|
gimp_image_undo_group_end (image);
|
2004-08-20 22:32:14 +00:00
|
|
|
}
|
|
|
|
|
2003-08-27 00:52:00 +00:00
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_mask_to_selection_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2003-08-27 00:52:00 +00:00
|
|
|
{
|
2010-07-20 23:09:19 +02:00
|
|
|
GimpImage *image;
|
2020-05-20 22:59:34 +02:00
|
|
|
GList *layers;
|
|
|
|
GList *iter;
|
|
|
|
GList *masks = NULL;
|
|
|
|
return_if_no_layers (image, layers, data);
|
2001-04-20 16:27:44 +00:00
|
|
|
|
2020-05-20 22:59:34 +02:00
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
if (gimp_layer_get_mask (iter->data))
|
|
|
|
masks = g_list_prepend (masks, gimp_layer_get_mask (iter->data));
|
|
|
|
}
|
2003-08-21 15:54:47 +00:00
|
|
|
|
2020-05-20 22:59:34 +02:00
|
|
|
if (masks)
|
2002-02-26 16:30:14 +00:00
|
|
|
{
|
2019-07-04 01:11:48 +02:00
|
|
|
GimpChannelOps operation = (GimpChannelOps) g_variant_get_int32 (value);
|
|
|
|
|
2020-05-20 22:59:34 +02:00
|
|
|
switch (operation)
|
|
|
|
{
|
|
|
|
case GIMP_CHANNEL_OP_REPLACE:
|
|
|
|
gimp_channel_push_undo (gimp_image_get_mask (image),
|
|
|
|
C_("undo-type", "Masks to Selection"));
|
|
|
|
break;
|
|
|
|
case GIMP_CHANNEL_OP_ADD:
|
|
|
|
gimp_channel_push_undo (gimp_image_get_mask (image),
|
|
|
|
C_("undo-type", "Add Masks to Selection"));
|
|
|
|
break;
|
|
|
|
case GIMP_CHANNEL_OP_SUBTRACT:
|
|
|
|
gimp_channel_push_undo (gimp_image_get_mask (image),
|
|
|
|
C_("undo-type", "Subtract Masks from Selection"));
|
|
|
|
break;
|
|
|
|
case GIMP_CHANNEL_OP_INTERSECT:
|
|
|
|
gimp_channel_push_undo (gimp_image_get_mask (image),
|
|
|
|
C_("undo-type", "Intersect Masks with Selection"));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
gimp_channel_combine_items (gimp_image_get_mask (image),
|
|
|
|
masks, operation);
|
2006-03-28 17:08:36 +00:00
|
|
|
gimp_image_flush (image);
|
2020-05-20 22:59:34 +02:00
|
|
|
g_list_free (masks);
|
2002-02-26 16:30:14 +00:00
|
|
|
}
|
2001-04-20 16:27:44 +00:00
|
|
|
}
|
|
|
|
|
2003-08-21 15:54:47 +00:00
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_alpha_add_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2001-04-20 16:27:44 +00:00
|
|
|
{
|
2006-03-28 17:08:36 +00:00
|
|
|
GimpImage *image;
|
2020-07-30 21:01:10 +02:00
|
|
|
GList *layers;
|
|
|
|
GList *iter;
|
|
|
|
return_if_no_layers (image, layers, data);
|
2001-04-20 16:27:44 +00:00
|
|
|
|
2020-07-30 21:01:10 +02:00
|
|
|
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_LAYER_ADD_ALPHA,
|
|
|
|
_("Add Alpha Channel"));
|
|
|
|
|
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
if (! gimp_drawable_has_alpha (iter->data))
|
|
|
|
gimp_layer_add_alpha (iter->data);
|
|
|
|
|
|
|
|
gimp_image_undo_group_end (image);
|
|
|
|
gimp_image_flush (image);
|
2001-04-20 16:27:44 +00:00
|
|
|
}
|
|
|
|
|
2005-08-07 16:38:35 +00:00
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_alpha_remove_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2005-08-07 16:38:35 +00:00
|
|
|
{
|
2006-03-28 17:08:36 +00:00
|
|
|
GimpImage *image;
|
2020-07-30 21:01:10 +02:00
|
|
|
GList *layers;
|
|
|
|
GList *iter;
|
|
|
|
return_if_no_layers (image, layers, data);
|
2005-08-07 16:38:35 +00:00
|
|
|
|
2020-07-30 21:01:10 +02:00
|
|
|
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_LAYER_ADD_ALPHA,
|
|
|
|
_("Remove Alpha Channel"));
|
|
|
|
|
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
if (gimp_drawable_has_alpha (iter->data))
|
|
|
|
gimp_layer_remove_alpha (iter->data, action_data_get_context (data));
|
|
|
|
|
|
|
|
gimp_image_undo_group_end (image);
|
|
|
|
gimp_image_flush (image);
|
2005-08-07 16:38:35 +00:00
|
|
|
}
|
|
|
|
|
2003-08-27 00:52:00 +00:00
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_alpha_to_selection_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2003-08-21 15:54:47 +00:00
|
|
|
{
|
2019-07-04 01:11:48 +02:00
|
|
|
GimpImage *image;
|
2020-05-25 17:40:57 +02:00
|
|
|
GimpDisplay *display;
|
2020-05-18 12:22:08 +02:00
|
|
|
GList *layers;
|
2019-07-04 01:11:48 +02:00
|
|
|
GimpChannelOps operation;
|
2020-05-18 12:22:08 +02:00
|
|
|
return_if_no_layers (image, layers, data);
|
2020-05-25 17:40:57 +02:00
|
|
|
return_if_no_display (display, data);
|
2003-08-21 15:54:47 +00:00
|
|
|
|
2019-07-04 01:11:48 +02:00
|
|
|
operation = (GimpChannelOps) g_variant_get_int32 (value);
|
|
|
|
|
2020-05-20 21:18:20 +02:00
|
|
|
switch (operation)
|
|
|
|
{
|
|
|
|
case GIMP_CHANNEL_OP_REPLACE:
|
|
|
|
gimp_channel_push_undo (gimp_image_get_mask (image),
|
|
|
|
C_("undo-type", "Alpha to Selection"));
|
|
|
|
break;
|
|
|
|
case GIMP_CHANNEL_OP_ADD:
|
|
|
|
gimp_channel_push_undo (gimp_image_get_mask (image),
|
|
|
|
C_("undo-type", "Add Alpha to Selection"));
|
|
|
|
break;
|
|
|
|
case GIMP_CHANNEL_OP_SUBTRACT:
|
|
|
|
gimp_channel_push_undo (gimp_image_get_mask (image),
|
|
|
|
C_("undo-type", "Subtract Alpha from Selection"));
|
|
|
|
break;
|
|
|
|
case GIMP_CHANNEL_OP_INTERSECT:
|
|
|
|
gimp_channel_push_undo (gimp_image_get_mask (image),
|
|
|
|
C_("undo-type", "Intersect Alpha with Selection"));
|
|
|
|
break;
|
|
|
|
}
|
2020-05-20 18:16:30 +02:00
|
|
|
gimp_channel_combine_items (gimp_image_get_mask (image),
|
|
|
|
layers, operation);
|
2006-03-28 17:08:36 +00:00
|
|
|
gimp_image_flush (image);
|
2020-05-25 17:40:57 +02:00
|
|
|
|
|
|
|
if (gimp_channel_is_empty (gimp_image_get_mask (image)))
|
|
|
|
{
|
|
|
|
gimp_message_literal (image->gimp, G_OBJECT (display),
|
|
|
|
GIMP_MESSAGE_WARNING,
|
|
|
|
_("Empty Selection"));
|
|
|
|
}
|
2003-08-21 15:54:47 +00:00
|
|
|
}
|
|
|
|
|
2004-06-23 00:23:25 +00:00
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_opacity_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2004-06-23 00:23:25 +00:00
|
|
|
{
|
2020-04-29 13:30:35 +02:00
|
|
|
GimpImage *image;
|
2020-04-29 02:12:00 +02:00
|
|
|
GList *layers;
|
|
|
|
GList *iter;
|
2020-04-29 13:30:35 +02:00
|
|
|
gdouble opacity;
|
|
|
|
GimpUndo *undo;
|
|
|
|
GimpActionSelectType select_type;
|
2020-04-29 02:12:00 +02:00
|
|
|
gboolean push_undo = TRUE;
|
|
|
|
return_if_no_layers (image, layers, data);
|
2004-06-23 00:23:25 +00:00
|
|
|
|
2020-04-29 13:30:35 +02:00
|
|
|
select_type = (GimpActionSelectType) g_variant_get_int32 (value);
|
2020-04-29 02:12:00 +02:00
|
|
|
if (g_list_length (layers) == 1)
|
|
|
|
{
|
|
|
|
undo = gimp_image_undo_can_compress (image, GIMP_TYPE_ITEM_UNDO,
|
|
|
|
GIMP_UNDO_LAYER_OPACITY);
|
2020-04-29 13:30:35 +02:00
|
|
|
|
2020-04-29 02:12:00 +02:00
|
|
|
if (undo && GIMP_ITEM_UNDO (undo)->item == GIMP_ITEM (layers->data))
|
|
|
|
push_undo = FALSE;
|
|
|
|
}
|
2004-08-03 14:09:49 +00:00
|
|
|
|
2020-04-29 02:12:00 +02:00
|
|
|
if (g_list_length (layers) > 1)
|
|
|
|
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_LAYER_OPACITY,
|
|
|
|
_("Set layers opacity"));
|
|
|
|
|
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
opacity = action_select_value (select_type,
|
|
|
|
gimp_layer_get_opacity (iter->data),
|
|
|
|
0.0, 1.0, 1.0,
|
2022-04-02 23:30:03 +02:00
|
|
|
1.0 / 255.0, 0.01, 0.1, 0.0, FALSE);
|
2020-04-29 02:12:00 +02:00
|
|
|
gimp_layer_set_opacity (iter->data, opacity, push_undo);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_list_length (layers) > 1)
|
|
|
|
gimp_image_undo_group_end (image);
|
2004-06-23 00:23:25 +00:00
|
|
|
|
2006-03-28 17:08:36 +00:00
|
|
|
gimp_image_flush (image);
|
2004-06-23 00:23:25 +00:00
|
|
|
}
|
|
|
|
|
2004-09-15 13:24:45 +00:00
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_mode_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2004-09-15 13:24:45 +00:00
|
|
|
{
|
2020-04-29 13:30:35 +02:00
|
|
|
GimpImage *image;
|
2020-04-29 02:12:00 +02:00
|
|
|
GList *layers;
|
|
|
|
GList *iter;
|
2020-04-29 13:30:35 +02:00
|
|
|
GimpActionSelectType select_type;
|
|
|
|
gboolean push_undo = TRUE;
|
2020-04-29 02:12:00 +02:00
|
|
|
return_if_no_layers (image, layers, data);
|
2004-09-15 13:24:45 +00:00
|
|
|
|
2020-04-29 13:30:35 +02:00
|
|
|
select_type = (GimpActionSelectType) g_variant_get_int32 (value);
|
|
|
|
|
2020-04-29 02:12:00 +02:00
|
|
|
if (g_list_length (layers) == 1)
|
|
|
|
{
|
|
|
|
GimpUndo *undo;
|
|
|
|
|
|
|
|
undo = gimp_image_undo_can_compress (image, GIMP_TYPE_ITEM_UNDO,
|
|
|
|
GIMP_UNDO_LAYER_MODE);
|
2004-09-15 13:24:45 +00:00
|
|
|
|
2020-04-29 02:12:00 +02:00
|
|
|
if (undo && GIMP_ITEM_UNDO (undo)->item == GIMP_ITEM (layers->data))
|
|
|
|
push_undo = FALSE;
|
|
|
|
}
|
2004-09-15 13:24:45 +00:00
|
|
|
|
2020-04-29 02:12:00 +02:00
|
|
|
if (g_list_length (layers) > 1)
|
|
|
|
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_LAYER_OPACITY,
|
|
|
|
_("Set layers opacity"));
|
2004-09-15 13:24:45 +00:00
|
|
|
|
2020-04-29 02:12:00 +02:00
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
GimpLayerMode *modes;
|
|
|
|
gint n_modes;
|
|
|
|
GimpLayerMode layer_mode;
|
|
|
|
gint index;
|
|
|
|
|
|
|
|
layer_mode = gimp_layer_get_mode (iter->data);
|
|
|
|
|
|
|
|
modes = gimp_layer_mode_get_context_array (layer_mode,
|
|
|
|
GIMP_LAYER_MODE_CONTEXT_LAYER,
|
|
|
|
&n_modes);
|
|
|
|
index = layers_mode_index (layer_mode, modes, n_modes);
|
|
|
|
index = action_select_value (select_type,
|
|
|
|
index, 0, n_modes - 1, 0,
|
2022-04-02 23:30:03 +02:00
|
|
|
0.0, 1.0, 1.0, 0.0, FALSE);
|
2020-04-29 02:12:00 +02:00
|
|
|
layer_mode = modes[index];
|
|
|
|
g_free (modes);
|
|
|
|
|
|
|
|
gimp_layer_set_mode (iter->data, layer_mode, push_undo);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_list_length (layers) > 1)
|
|
|
|
gimp_image_undo_group_end (image);
|
2017-02-20 23:51:32 +01:00
|
|
|
|
2006-03-28 17:08:36 +00:00
|
|
|
gimp_image_flush (image);
|
2004-09-15 13:24:45 +00:00
|
|
|
}
|
|
|
|
|
2017-02-02 00:38:25 +01:00
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_blend_space_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2017-02-12 23:49:26 +01:00
|
|
|
{
|
|
|
|
GimpImage *image;
|
2020-05-20 23:37:42 +02:00
|
|
|
GList *layers;
|
|
|
|
GList *update_layers = NULL;
|
|
|
|
GList *iter;
|
2017-02-12 23:49:26 +01:00
|
|
|
GimpLayerColorSpace blend_space;
|
2020-05-20 23:37:42 +02:00
|
|
|
gboolean push_undo = TRUE;
|
|
|
|
return_if_no_layers (image, layers, data);
|
2017-02-12 23:49:26 +01:00
|
|
|
|
2019-07-04 01:11:48 +02:00
|
|
|
blend_space = (GimpLayerColorSpace) g_variant_get_int32 (value);
|
2017-02-12 23:49:26 +01:00
|
|
|
|
2020-05-20 23:37:42 +02:00
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
GimpLayerMode mode;
|
|
|
|
|
|
|
|
mode = gimp_layer_get_mode (iter->data);
|
|
|
|
if (gimp_layer_mode_is_blend_space_mutable (mode) &&
|
|
|
|
blend_space != gimp_layer_get_blend_space (iter->data))
|
|
|
|
update_layers = g_list_prepend (update_layers, iter->data);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_list_length (update_layers) == 1)
|
2017-02-12 23:49:26 +01:00
|
|
|
{
|
|
|
|
GimpUndo *undo;
|
|
|
|
|
|
|
|
undo = gimp_image_undo_can_compress (image, GIMP_TYPE_LAYER_PROP_UNDO,
|
|
|
|
GIMP_UNDO_LAYER_MODE);
|
|
|
|
|
2020-05-20 23:37:42 +02:00
|
|
|
if (undo && GIMP_ITEM_UNDO (undo)->item == GIMP_ITEM (update_layers->data))
|
2017-02-12 23:49:26 +01:00
|
|
|
push_undo = FALSE;
|
2020-05-20 23:37:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (update_layers)
|
|
|
|
{
|
|
|
|
if (g_list_length (update_layers) > 1)
|
|
|
|
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_LAYER_MODE,
|
|
|
|
_("Set layers' blend space"));
|
2017-02-12 23:49:26 +01:00
|
|
|
|
2020-05-20 23:37:42 +02:00
|
|
|
for (iter = update_layers; iter; iter = iter->next)
|
|
|
|
gimp_layer_set_blend_space (iter->data, blend_space, push_undo);
|
|
|
|
|
|
|
|
if (g_list_length (update_layers) > 1)
|
|
|
|
gimp_image_undo_group_end (image);
|
|
|
|
|
|
|
|
g_list_free (update_layers);
|
2017-02-12 23:49:26 +01:00
|
|
|
gimp_image_flush (image);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_composite_space_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2017-02-12 23:49:26 +01:00
|
|
|
{
|
|
|
|
GimpImage *image;
|
2020-05-20 23:37:42 +02:00
|
|
|
GList *layers;
|
|
|
|
GList *update_layers = NULL;
|
|
|
|
GList *iter;
|
2017-02-12 23:49:26 +01:00
|
|
|
GimpLayerColorSpace composite_space;
|
2020-05-20 23:37:42 +02:00
|
|
|
gboolean push_undo = TRUE;
|
|
|
|
return_if_no_layers (image, layers, data);
|
2017-02-12 23:49:26 +01:00
|
|
|
|
2019-07-04 01:11:48 +02:00
|
|
|
composite_space = (GimpLayerColorSpace) g_variant_get_int32 (value);
|
2017-02-12 23:49:26 +01:00
|
|
|
|
2020-05-20 23:37:42 +02:00
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
GimpLayerMode mode;
|
|
|
|
|
|
|
|
mode = gimp_layer_get_mode (iter->data);
|
|
|
|
if (gimp_layer_mode_is_composite_space_mutable (mode) &&
|
|
|
|
composite_space != gimp_layer_get_composite_space (iter->data))
|
|
|
|
update_layers = g_list_prepend (update_layers, iter->data);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_list_length (update_layers) == 1)
|
2017-02-12 23:49:26 +01:00
|
|
|
{
|
|
|
|
GimpUndo *undo;
|
|
|
|
|
|
|
|
undo = gimp_image_undo_can_compress (image, GIMP_TYPE_LAYER_PROP_UNDO,
|
|
|
|
GIMP_UNDO_LAYER_MODE);
|
|
|
|
|
2020-05-20 23:37:42 +02:00
|
|
|
if (undo && GIMP_ITEM_UNDO (undo)->item == GIMP_ITEM (update_layers->data))
|
2017-02-12 23:49:26 +01:00
|
|
|
push_undo = FALSE;
|
2020-05-20 23:37:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (update_layers)
|
|
|
|
{
|
|
|
|
if (g_list_length (update_layers) > 1)
|
|
|
|
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_LAYER_MODE,
|
|
|
|
_("Set layers' composite space"));
|
|
|
|
|
|
|
|
for (iter = update_layers; iter; iter = iter->next)
|
|
|
|
gimp_layer_set_composite_space (iter->data, composite_space, push_undo);
|
2017-02-12 23:49:26 +01:00
|
|
|
|
2020-05-20 23:37:42 +02:00
|
|
|
if (g_list_length (update_layers) > 1)
|
|
|
|
gimp_image_undo_group_end (image);
|
|
|
|
|
|
|
|
g_list_free (update_layers);
|
2017-02-12 23:49:26 +01:00
|
|
|
gimp_image_flush (image);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_composite_mode_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2017-02-02 00:38:25 +01:00
|
|
|
{
|
|
|
|
GimpImage *image;
|
2020-05-20 23:37:42 +02:00
|
|
|
GList *layers;
|
|
|
|
GList *update_layers = NULL;
|
|
|
|
GList *iter;
|
2017-02-12 23:49:26 +01:00
|
|
|
GimpLayerCompositeMode composite_mode;
|
2020-05-20 23:37:42 +02:00
|
|
|
gboolean push_undo = TRUE;
|
|
|
|
return_if_no_layers (image, layers, data);
|
2017-02-02 00:38:25 +01:00
|
|
|
|
2019-07-04 01:11:48 +02:00
|
|
|
composite_mode = (GimpLayerCompositeMode) g_variant_get_int32 (value);
|
2017-02-02 00:38:25 +01:00
|
|
|
|
2020-05-20 23:37:42 +02:00
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
GimpLayerMode mode;
|
|
|
|
|
|
|
|
mode = gimp_layer_get_mode (iter->data);
|
|
|
|
if (gimp_layer_mode_is_composite_mode_mutable (mode) &&
|
|
|
|
composite_mode != gimp_layer_get_composite_mode (iter->data))
|
|
|
|
update_layers = g_list_prepend (update_layers, iter->data);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_list_length (update_layers) == 1)
|
2017-02-02 00:38:25 +01:00
|
|
|
{
|
|
|
|
GimpUndo *undo;
|
|
|
|
|
|
|
|
undo = gimp_image_undo_can_compress (image, GIMP_TYPE_LAYER_PROP_UNDO,
|
|
|
|
GIMP_UNDO_LAYER_MODE);
|
|
|
|
|
2020-05-20 23:37:42 +02:00
|
|
|
if (undo && GIMP_ITEM_UNDO (undo)->item == GIMP_ITEM (update_layers->data))
|
2017-02-02 00:38:25 +01:00
|
|
|
push_undo = FALSE;
|
2020-05-20 23:37:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (update_layers)
|
|
|
|
{
|
|
|
|
if (g_list_length (update_layers) > 1)
|
|
|
|
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_LAYER_MODE,
|
|
|
|
_("Set layers' composite mode"));
|
|
|
|
|
|
|
|
for (iter = update_layers; iter; iter = iter->next)
|
|
|
|
gimp_layer_set_composite_mode (iter->data, composite_mode, push_undo);
|
|
|
|
|
|
|
|
if (g_list_length (update_layers) > 1)
|
|
|
|
gimp_image_undo_group_end (image);
|
2017-02-02 00:38:25 +01:00
|
|
|
|
2020-05-20 23:37:42 +02:00
|
|
|
g_list_free (update_layers);
|
2017-02-02 00:38:25 +01:00
|
|
|
gimp_image_flush (image);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-29 16:50:13 +02:00
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_visible_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2023-01-10 18:41:31 +01:00
|
|
|
gpointer data)
|
2016-10-29 16:50:13 +02:00
|
|
|
{
|
|
|
|
GimpImage *image;
|
2023-01-10 18:41:31 +01:00
|
|
|
GList *layers;
|
|
|
|
return_if_no_layers (image, layers, data);
|
2016-10-29 16:50:13 +02:00
|
|
|
|
2023-01-10 18:41:31 +01:00
|
|
|
items_visible_cmd_callback (action, value, image, layers);
|
2016-10-29 16:50:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_lock_content_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2016-10-29 16:50:13 +02:00
|
|
|
{
|
|
|
|
GimpImage *image;
|
2020-05-04 17:31:46 +02:00
|
|
|
GList *layers;
|
|
|
|
return_if_no_layers (image, layers, data);
|
|
|
|
|
2023-01-10 19:06:42 +01:00
|
|
|
items_lock_content_cmd_callback (action, value, image, layers);
|
2016-10-29 16:50:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_lock_position_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2016-10-29 16:50:13 +02:00
|
|
|
{
|
|
|
|
GimpImage *image;
|
2020-05-04 17:31:46 +02:00
|
|
|
GList *layers;
|
|
|
|
return_if_no_layers (image, layers, data);
|
|
|
|
|
2023-01-10 19:12:36 +01:00
|
|
|
items_lock_position_cmd_callback (action, value, image, layers);
|
2016-10-29 16:50:13 +02:00
|
|
|
}
|
|
|
|
|
2004-09-15 13:24:45 +00:00
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_lock_alpha_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2004-09-15 13:24:45 +00:00
|
|
|
{
|
2006-03-28 17:08:36 +00:00
|
|
|
GimpImage *image;
|
2020-04-29 02:12:00 +02:00
|
|
|
GList *layers;
|
|
|
|
GList *iter;
|
2005-07-10 21:17:22 +00:00
|
|
|
gboolean lock_alpha;
|
2020-04-29 02:12:00 +02:00
|
|
|
gboolean lock_change = FALSE;
|
|
|
|
return_if_no_layers (image, layers, data);
|
2004-09-15 13:24:45 +00:00
|
|
|
|
2019-07-04 01:11:48 +02:00
|
|
|
lock_alpha = g_variant_get_boolean (value);
|
2004-09-15 13:24:45 +00:00
|
|
|
|
2020-04-29 02:12:00 +02:00
|
|
|
for (iter = layers; iter; iter = iter->next)
|
2004-09-15 13:24:45 +00:00
|
|
|
{
|
2020-04-29 02:12:00 +02:00
|
|
|
if (gimp_layer_can_lock_alpha (iter->data))
|
|
|
|
{
|
|
|
|
/* Similar trick as in layers_mask_show_cmd_callback().
|
|
|
|
* When unlocking, we expect all selected layers to be locked,
|
2020-08-08 05:58:47 +00:00
|
|
|
* otherwise SET_ACTIVE() calls in layers-actions.c will
|
2020-04-29 02:12:00 +02:00
|
|
|
* trigger lock updates.
|
|
|
|
*/
|
|
|
|
if (! lock_alpha && ! gimp_layer_get_lock_alpha (iter->data))
|
|
|
|
return;
|
|
|
|
if (lock_alpha != gimp_layer_get_lock_alpha (iter->data))
|
|
|
|
lock_change = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (! lock_change)
|
|
|
|
/* No layer locks would be changed. */
|
|
|
|
return;
|
2004-09-15 13:24:45 +00:00
|
|
|
|
2020-04-29 02:12:00 +02:00
|
|
|
gimp_image_undo_group_start (image,
|
|
|
|
GIMP_UNDO_GROUP_LAYER_LOCK_ALPHA,
|
|
|
|
lock_alpha ? _("Lock alpha channels") : _("Unlock alpha channels"));
|
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
if (gimp_layer_can_lock_alpha (iter->data))
|
|
|
|
{
|
|
|
|
if (lock_alpha != gimp_layer_get_lock_alpha (iter->data))
|
|
|
|
gimp_layer_set_lock_alpha (iter->data, lock_alpha, TRUE);
|
|
|
|
}
|
2004-09-15 13:24:45 +00:00
|
|
|
}
|
2020-04-29 02:12:00 +02:00
|
|
|
gimp_image_undo_group_end (image);
|
|
|
|
gimp_image_flush (image);
|
2004-09-15 13:24:45 +00:00
|
|
|
}
|
|
|
|
|
2016-10-29 16:50:13 +02:00
|
|
|
void
|
2019-07-02 16:12:18 +02:00
|
|
|
layers_color_tag_cmd_callback (GimpAction *action,
|
2019-07-04 01:11:48 +02:00
|
|
|
GVariant *value,
|
2019-07-02 16:12:18 +02:00
|
|
|
gpointer data)
|
2016-10-29 16:50:13 +02:00
|
|
|
{
|
2019-07-04 01:11:48 +02:00
|
|
|
GimpImage *image;
|
2020-04-28 18:17:59 +02:00
|
|
|
GList *layers;
|
2019-07-04 01:11:48 +02:00
|
|
|
GimpColorTag color_tag;
|
2020-04-28 18:17:59 +02:00
|
|
|
return_if_no_layers (image, layers, data);
|
2016-10-29 16:50:13 +02:00
|
|
|
|
2019-07-04 01:11:48 +02:00
|
|
|
color_tag = (GimpColorTag) g_variant_get_int32 (value);
|
|
|
|
|
2023-01-10 19:32:14 +01:00
|
|
|
items_color_tag_cmd_callback (action, image, layers, color_tag);
|
2016-10-29 16:50:13 +02:00
|
|
|
}
|
|
|
|
|
2001-04-20 16:27:44 +00:00
|
|
|
|
2004-10-18 11:29:58 +00:00
|
|
|
/* private functions */
|
2001-04-20 16:27:44 +00:00
|
|
|
|
|
|
|
static void
|
2017-02-02 00:38:25 +01:00
|
|
|
layers_new_callback (GtkWidget *dialog,
|
|
|
|
GimpImage *image,
|
|
|
|
GimpLayer *layer,
|
|
|
|
GimpContext *context,
|
|
|
|
const gchar *layer_name,
|
|
|
|
GimpLayerMode layer_mode,
|
2017-02-12 23:49:26 +01:00
|
|
|
GimpLayerColorSpace layer_blend_space,
|
|
|
|
GimpLayerColorSpace layer_composite_space,
|
|
|
|
GimpLayerCompositeMode layer_composite_mode,
|
2017-02-02 00:38:25 +01:00
|
|
|
gdouble layer_opacity,
|
|
|
|
GimpFillType layer_fill_type,
|
|
|
|
gint layer_width,
|
|
|
|
gint layer_height,
|
|
|
|
gint layer_offset_x,
|
|
|
|
gint layer_offset_y,
|
|
|
|
gboolean layer_visible,
|
|
|
|
GimpColorTag layer_color_tag,
|
|
|
|
gboolean layer_lock_pixels,
|
|
|
|
gboolean layer_lock_position,
|
2023-05-26 02:46:53 +02:00
|
|
|
gboolean layer_lock_visibility,
|
2017-02-02 00:38:25 +01:00
|
|
|
gboolean layer_lock_alpha,
|
|
|
|
gboolean rename_text_layer, /* unused */
|
|
|
|
gpointer user_data)
|
2001-04-20 16:27:44 +00:00
|
|
|
{
|
2020-05-03 00:28:13 +02:00
|
|
|
GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config);
|
|
|
|
GList *layers = gimp_image_get_selected_layers (image);
|
|
|
|
GList *new_layers = NULL;
|
|
|
|
GList *iter;
|
|
|
|
gint n_layers = g_list_length (layers);
|
|
|
|
gboolean run_once = (n_layers == 0);
|
2002-02-26 00:04:55 +00:00
|
|
|
|
2016-09-24 21:10:07 +02:00
|
|
|
g_object_set (config,
|
2017-02-12 23:49:26 +01:00
|
|
|
"layer-new-name", layer_name,
|
|
|
|
"layer-new-mode", layer_mode,
|
|
|
|
"layer-new-blend-space", layer_blend_space,
|
|
|
|
"layer-new-composite-space", layer_composite_space,
|
|
|
|
"layer-new-composite-mode", layer_composite_mode,
|
|
|
|
"layer-new-opacity", layer_opacity,
|
|
|
|
"layer-new-fill-type", layer_fill_type,
|
2016-09-24 21:10:07 +02:00
|
|
|
NULL);
|
2004-10-18 11:29:58 +00:00
|
|
|
|
2020-05-03 00:28:13 +02:00
|
|
|
layers = g_list_copy (layers);
|
|
|
|
gimp_image_undo_group_start (image,
|
|
|
|
GIMP_UNDO_GROUP_LAYER_ADD,
|
|
|
|
ngettext ("New layer",
|
|
|
|
"New layers",
|
|
|
|
n_layers > 0 ? n_layers : 1));
|
|
|
|
for (iter = layers; iter || run_once; iter = iter ? iter->next : NULL)
|
2016-09-24 21:10:07 +02:00
|
|
|
{
|
2020-05-03 00:28:13 +02:00
|
|
|
GimpLayer *parent;
|
|
|
|
gint position;
|
|
|
|
|
|
|
|
run_once = FALSE;
|
|
|
|
if (iter)
|
|
|
|
{
|
|
|
|
if (gimp_viewable_get_children (GIMP_VIEWABLE (iter->data)))
|
|
|
|
{
|
|
|
|
parent = iter->data;
|
|
|
|
position = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
parent = GIMP_LAYER (gimp_item_get_parent (iter->data));
|
|
|
|
position = gimp_item_get_index (iter->data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else /* run_once */
|
|
|
|
{
|
|
|
|
parent = NULL;
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
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,
|
|
|
|
FALSE);
|
|
|
|
gimp_item_set_lock_position (GIMP_ITEM (layer), layer_lock_position,
|
|
|
|
FALSE);
|
2023-05-26 02:46:53 +02:00
|
|
|
gimp_item_set_lock_visibility (GIMP_ITEM (layer), layer_lock_visibility,
|
|
|
|
FALSE);
|
2020-05-03 00:28:13 +02:00
|
|
|
gimp_layer_set_lock_alpha (layer, layer_lock_alpha, FALSE);
|
|
|
|
gimp_layer_set_blend_space (layer, layer_blend_space, FALSE);
|
|
|
|
gimp_layer_set_composite_space (layer, layer_composite_space, FALSE);
|
|
|
|
gimp_layer_set_composite_mode (layer, layer_composite_mode, FALSE);
|
|
|
|
|
|
|
|
gimp_image_add_layer (image, layer, parent, position, TRUE);
|
|
|
|
gimp_image_flush (image);
|
|
|
|
|
|
|
|
new_layers = g_list_prepend (new_layers, layer);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
g_warning ("%s: could not allocate new layer", G_STRFUNC);
|
|
|
|
}
|
2001-04-20 16:27:44 +00:00
|
|
|
}
|
|
|
|
|
2020-05-03 00:28:13 +02:00
|
|
|
gimp_image_undo_group_end (image);
|
|
|
|
gimp_image_set_selected_layers (image, new_layers);
|
|
|
|
|
|
|
|
g_list_free (layers);
|
|
|
|
g_list_free (new_layers);
|
2016-09-24 21:10:07 +02:00
|
|
|
gtk_widget_destroy (dialog);
|
2001-04-20 16:27:44 +00:00
|
|
|
}
|
|
|
|
|
2004-03-18 18:00:38 +00:00
|
|
|
static void
|
2017-02-02 00:38:25 +01:00
|
|
|
layers_edit_attributes_callback (GtkWidget *dialog,
|
|
|
|
GimpImage *image,
|
|
|
|
GimpLayer *layer,
|
|
|
|
GimpContext *context,
|
|
|
|
const gchar *layer_name,
|
|
|
|
GimpLayerMode layer_mode,
|
2017-02-12 23:49:26 +01:00
|
|
|
GimpLayerColorSpace layer_blend_space,
|
|
|
|
GimpLayerColorSpace layer_composite_space,
|
|
|
|
GimpLayerCompositeMode layer_composite_mode,
|
2017-02-02 00:38:25 +01:00
|
|
|
gdouble layer_opacity,
|
|
|
|
GimpFillType unused1,
|
|
|
|
gint unused2,
|
|
|
|
gint unused3,
|
|
|
|
gint layer_offset_x,
|
|
|
|
gint layer_offset_y,
|
|
|
|
gboolean layer_visible,
|
|
|
|
GimpColorTag layer_color_tag,
|
|
|
|
gboolean layer_lock_pixels,
|
|
|
|
gboolean layer_lock_position,
|
2023-05-26 02:46:53 +02:00
|
|
|
gboolean layer_lock_visibility,
|
2017-02-02 00:38:25 +01:00
|
|
|
gboolean layer_lock_alpha,
|
|
|
|
gboolean rename_text_layer,
|
|
|
|
gpointer user_data)
|
2004-10-18 11:29:58 +00:00
|
|
|
{
|
2016-10-21 22:54:10 +02:00
|
|
|
GimpItem *item = GIMP_ITEM (layer);
|
|
|
|
|
2017-02-12 23:49:26 +01:00
|
|
|
if (strcmp (layer_name, gimp_object_get_name (layer)) ||
|
|
|
|
layer_mode != gimp_layer_get_mode (layer) ||
|
|
|
|
layer_blend_space != gimp_layer_get_blend_space (layer) ||
|
|
|
|
layer_composite_space != gimp_layer_get_composite_space (layer) ||
|
|
|
|
layer_composite_mode != gimp_layer_get_composite_mode (layer) ||
|
|
|
|
layer_opacity != gimp_layer_get_opacity (layer) ||
|
|
|
|
layer_offset_x != gimp_item_get_offset_x (item) ||
|
|
|
|
layer_offset_y != gimp_item_get_offset_y (item) ||
|
|
|
|
layer_visible != gimp_item_get_visible (item) ||
|
|
|
|
layer_color_tag != gimp_item_get_color_tag (item) ||
|
|
|
|
layer_lock_pixels != gimp_item_get_lock_content (item) ||
|
|
|
|
layer_lock_position != gimp_item_get_lock_position (item) ||
|
2023-05-26 02:46:53 +02:00
|
|
|
layer_lock_visibility != gimp_item_get_lock_visibility (item) ||
|
2017-02-12 23:49:26 +01:00
|
|
|
layer_lock_alpha != gimp_layer_get_lock_alpha (layer))
|
2004-10-18 11:29:58 +00:00
|
|
|
{
|
2016-10-21 22:54:10 +02:00
|
|
|
gimp_image_undo_group_start (image,
|
|
|
|
GIMP_UNDO_GROUP_ITEM_PROPERTIES,
|
|
|
|
_("Layer Attributes"));
|
2001-04-20 16:27:44 +00:00
|
|
|
|
2016-10-21 22:54:10 +02:00
|
|
|
if (strcmp (layer_name, gimp_object_get_name (layer)))
|
2004-10-18 11:29:58 +00:00
|
|
|
{
|
2016-10-21 22:54:10 +02:00
|
|
|
GError *error = NULL;
|
|
|
|
|
|
|
|
if (! gimp_item_rename (GIMP_ITEM (layer), layer_name, &error))
|
|
|
|
{
|
|
|
|
gimp_message_literal (image->gimp,
|
|
|
|
G_OBJECT (dialog), GIMP_MESSAGE_WARNING,
|
|
|
|
error->message);
|
|
|
|
g_clear_error (&error);
|
|
|
|
}
|
2004-10-18 11:29:58 +00:00
|
|
|
}
|
2016-09-24 21:10:07 +02:00
|
|
|
|
2016-10-21 22:54:10 +02:00
|
|
|
if (layer_mode != gimp_layer_get_mode (layer))
|
|
|
|
gimp_layer_set_mode (layer, layer_mode, TRUE);
|
|
|
|
|
2017-02-12 23:49:26 +01:00
|
|
|
if (layer_blend_space != gimp_layer_get_blend_space (layer))
|
|
|
|
gimp_layer_set_blend_space (layer, layer_blend_space, TRUE);
|
|
|
|
|
|
|
|
if (layer_composite_space != gimp_layer_get_composite_space (layer))
|
|
|
|
gimp_layer_set_composite_space (layer, layer_composite_space, TRUE);
|
|
|
|
|
|
|
|
if (layer_composite_mode != gimp_layer_get_composite_mode (layer))
|
|
|
|
gimp_layer_set_composite_mode (layer, layer_composite_mode, TRUE);
|
2017-02-02 00:38:25 +01:00
|
|
|
|
2016-10-21 22:54:10 +02:00
|
|
|
if (layer_opacity != gimp_layer_get_opacity (layer))
|
|
|
|
gimp_layer_set_opacity (layer, layer_opacity, TRUE);
|
|
|
|
|
|
|
|
if (layer_offset_x != gimp_item_get_offset_x (item) ||
|
|
|
|
layer_offset_y != gimp_item_get_offset_y (item))
|
|
|
|
{
|
|
|
|
gimp_item_translate (item,
|
|
|
|
layer_offset_x - gimp_item_get_offset_x (item),
|
|
|
|
layer_offset_y - gimp_item_get_offset_y (item),
|
|
|
|
TRUE);
|
2004-10-18 11:29:58 +00:00
|
|
|
}
|
2016-10-21 22:54:10 +02:00
|
|
|
|
2016-10-23 22:45:08 +02:00
|
|
|
if (layer_visible != gimp_item_get_visible (item))
|
|
|
|
gimp_item_set_visible (item, layer_visible, TRUE);
|
|
|
|
|
2016-10-29 16:50:13 +02:00
|
|
|
if (layer_color_tag != gimp_item_get_color_tag (item))
|
|
|
|
gimp_item_set_color_tag (item, layer_color_tag, TRUE);
|
|
|
|
|
2016-10-23 22:45:08 +02:00
|
|
|
if (layer_lock_pixels != gimp_item_get_lock_content (item))
|
|
|
|
gimp_item_set_lock_content (item, layer_lock_pixels, TRUE);
|
|
|
|
|
|
|
|
if (layer_lock_position != gimp_item_get_lock_position (item))
|
|
|
|
gimp_item_set_lock_position (item, layer_lock_position, TRUE);
|
|
|
|
|
2023-05-26 02:46:53 +02:00
|
|
|
if (layer_lock_visibility != gimp_item_get_lock_visibility (item))
|
|
|
|
gimp_item_set_lock_visibility (item, layer_lock_visibility, TRUE);
|
|
|
|
|
2016-10-23 22:45:08 +02:00
|
|
|
if (layer_lock_alpha != gimp_layer_get_lock_alpha (layer))
|
|
|
|
gimp_layer_set_lock_alpha (layer, layer_lock_alpha, TRUE);
|
|
|
|
|
2016-10-21 22:54:10 +02:00
|
|
|
gimp_image_undo_group_end (image);
|
|
|
|
|
|
|
|
gimp_image_flush (image);
|
2004-10-18 11:29:58 +00:00
|
|
|
}
|
|
|
|
|
2016-09-24 21:10:07 +02:00
|
|
|
if (gimp_item_is_text_layer (GIMP_ITEM (layer)))
|
|
|
|
{
|
|
|
|
g_object_set (layer,
|
|
|
|
"auto-rename", rename_text_layer,
|
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
gtk_widget_destroy (dialog);
|
2004-10-18 11:29:58 +00:00
|
|
|
}
|
2001-04-20 16:27:44 +00:00
|
|
|
|
|
|
|
static void
|
2016-09-23 14:35:14 +02:00
|
|
|
layers_add_mask_callback (GtkWidget *dialog,
|
2020-05-02 01:42:04 +02:00
|
|
|
GList *layers,
|
2016-09-23 14:35:14 +02:00
|
|
|
GimpAddMaskType add_mask_type,
|
|
|
|
GimpChannel *channel,
|
|
|
|
gboolean invert,
|
|
|
|
gpointer user_data)
|
2001-04-20 16:27:44 +00:00
|
|
|
{
|
2020-05-02 01:42:04 +02:00
|
|
|
GimpImage *image = gimp_item_get_image (GIMP_ITEM (layers->data));
|
2016-09-23 14:35:14 +02:00
|
|
|
GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config);
|
|
|
|
GimpLayerMask *mask;
|
2020-05-02 01:42:04 +02:00
|
|
|
GList *iter;
|
2016-09-23 14:35:14 +02:00
|
|
|
GError *error = NULL;
|
2003-11-26 15:48:50 +00:00
|
|
|
|
2016-09-23 14:35:14 +02:00
|
|
|
g_object_set (config,
|
|
|
|
"layer-add-mask-type", add_mask_type,
|
|
|
|
"layer-add-mask-invert", invert,
|
|
|
|
NULL);
|
2003-03-06 13:22:11 +00:00
|
|
|
|
2020-05-02 01:42:04 +02:00
|
|
|
gimp_image_undo_group_start (image,
|
|
|
|
GIMP_UNDO_GROUP_LAYER_ADD,
|
|
|
|
_("Add Layer Masks"));
|
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
mask = gimp_layer_create_mask (iter->data,
|
|
|
|
config->layer_add_mask_type,
|
|
|
|
channel);
|
2002-09-01 07:25:41 +00:00
|
|
|
|
2020-05-02 01:42:04 +02:00
|
|
|
if (config->layer_add_mask_invert)
|
|
|
|
gimp_channel_invert (GIMP_CHANNEL (mask), FALSE);
|
2003-11-26 15:48:50 +00:00
|
|
|
|
2020-05-02 01:42:04 +02:00
|
|
|
if (! gimp_layer_add_mask (iter->data, mask, TRUE, &error))
|
|
|
|
{
|
|
|
|
gimp_message_literal (image->gimp,
|
|
|
|
G_OBJECT (dialog), GIMP_MESSAGE_WARNING,
|
|
|
|
error->message);
|
|
|
|
g_object_unref (mask);
|
|
|
|
g_clear_error (&error);
|
|
|
|
return;
|
|
|
|
}
|
2001-04-20 16:27:44 +00:00
|
|
|
}
|
|
|
|
|
2020-05-02 01:42:04 +02:00
|
|
|
gimp_image_undo_group_end (image);
|
2016-09-23 14:35:14 +02:00
|
|
|
gimp_image_flush (image);
|
|
|
|
gtk_widget_destroy (dialog);
|
2001-04-20 16:27:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-09-25 22:18:37 +02:00
|
|
|
layers_scale_callback (GtkWidget *dialog,
|
|
|
|
GimpViewable *viewable,
|
|
|
|
gint width,
|
|
|
|
gint height,
|
Issue #8900 and #9923: reimplementing GimpUnit as a proper class.
This fixes all our GObject Introspection issues with GimpUnit which was
both an enum and an int-derived type of user-defined units *completing*
the enum values. GIR clearly didn't like this!
Now GimpUnit is a proper class and units are unique objects, allowing to
compare them with an identity test (i.e. `unit == gimp_unit_pixel ()`
tells us if unit is the pixel unit or not), which makes it easy to use,
just like with int, yet adding also methods, making for nicer
introspected API.
As an aside, this also fixes #10738, by having all the built-in units
retrievable even if libgimpbase had not been properly initialized with
gimp_base_init().
I haven't checked in details how GIR works to introspect, but it looks
like it loads the library to inspect and runs functions, hence
triggering some CRITICALS because virtual methods (supposed to be
initialized with gimp_base_init() run by libgimp) are not set. This new
code won't trigger any critical because the vtable method are now not
necessary, at least for all built-in units.
Note that GimpUnit is still in libgimpbase. It could have been moved to
libgimp in order to avoid any virtual method table (since we need to
keep core and libgimp side's units in sync, PDB is required), but too
many libgimpwidgets widgets were already using GimpUnit. And technically
most of GimpUnit logic doesn't require PDB (only the creation/sync
part). This is one of the reasons why user-created GimpUnit list is
handled and stored differently from other types of objects.
Globally this simplifies the code a lot too and we don't need separate
implementations of various utils for core and libgimp, which means less
prone to errors.
2024-07-25 20:55:21 +02:00
|
|
|
GimpUnit *unit,
|
2016-09-25 22:18:37 +02:00
|
|
|
GimpInterpolationType interpolation,
|
|
|
|
gdouble xresolution, /* unused */
|
|
|
|
gdouble yresolution, /* unused */
|
Issue #8900 and #9923: reimplementing GimpUnit as a proper class.
This fixes all our GObject Introspection issues with GimpUnit which was
both an enum and an int-derived type of user-defined units *completing*
the enum values. GIR clearly didn't like this!
Now GimpUnit is a proper class and units are unique objects, allowing to
compare them with an identity test (i.e. `unit == gimp_unit_pixel ()`
tells us if unit is the pixel unit or not), which makes it easy to use,
just like with int, yet adding also methods, making for nicer
introspected API.
As an aside, this also fixes #10738, by having all the built-in units
retrievable even if libgimpbase had not been properly initialized with
gimp_base_init().
I haven't checked in details how GIR works to introspect, but it looks
like it loads the library to inspect and runs functions, hence
triggering some CRITICALS because virtual methods (supposed to be
initialized with gimp_base_init() run by libgimp) are not set. This new
code won't trigger any critical because the vtable method are now not
necessary, at least for all built-in units.
Note that GimpUnit is still in libgimpbase. It could have been moved to
libgimp in order to avoid any virtual method table (since we need to
keep core and libgimp side's units in sync, PDB is required), but too
many libgimpwidgets widgets were already using GimpUnit. And technically
most of GimpUnit logic doesn't require PDB (only the creation/sync
part). This is one of the reasons why user-created GimpUnit list is
handled and stored differently from other types of objects.
Globally this simplifies the code a lot too and we don't need separate
implementations of various utils for core and libgimp, which means less
prone to errors.
2024-07-25 20:55:21 +02:00
|
|
|
GimpUnit *resolution_unit,/* unused */
|
2016-09-25 22:18:37 +02:00
|
|
|
gpointer user_data)
|
2001-04-20 16:27:44 +00:00
|
|
|
{
|
2016-09-24 15:07:04 +02:00
|
|
|
GimpDisplay *display = GIMP_DISPLAY (user_data);
|
2001-04-20 16:27:44 +00:00
|
|
|
|
2006-10-14 16:51:30 +00:00
|
|
|
layer_scale_unit = unit;
|
|
|
|
layer_scale_interp = interpolation;
|
|
|
|
|
2004-10-12 14:59:14 +00:00
|
|
|
if (width > 0 && height > 0)
|
2001-04-20 16:27:44 +00:00
|
|
|
{
|
2004-10-12 14:59:14 +00:00
|
|
|
GimpItem *item = GIMP_ITEM (viewable);
|
2004-04-13 13:54:54 +00:00
|
|
|
GimpProgress *progress;
|
2004-10-12 14:59:14 +00:00
|
|
|
GtkWidget *progress_dialog = NULL;
|
|
|
|
|
|
|
|
gtk_widget_destroy (dialog);
|
|
|
|
|
2008-11-03 00:09:01 +00:00
|
|
|
if (width == gimp_item_get_width (item) &&
|
|
|
|
height == gimp_item_get_height (item))
|
2004-10-12 14:59:14 +00:00
|
|
|
return;
|
2002-02-25 17:58:50 +00:00
|
|
|
|
2006-03-28 17:55:52 +00:00
|
|
|
if (display)
|
2004-10-12 14:59:14 +00:00
|
|
|
{
|
2006-03-28 17:55:52 +00:00
|
|
|
progress = GIMP_PROGRESS (display);
|
2004-10-12 14:59:14 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
progress_dialog = gimp_progress_dialog_new ();
|
|
|
|
progress = GIMP_PROGRESS (progress_dialog);
|
|
|
|
}
|
2001-04-20 16:27:44 +00:00
|
|
|
|
2014-07-12 23:45:20 +02:00
|
|
|
progress = gimp_progress_start (progress, FALSE, _("Scaling"));
|
2002-02-25 17:58:50 +00:00
|
|
|
|
2004-10-12 14:59:14 +00:00
|
|
|
gimp_item_scale_by_origin (item,
|
|
|
|
width, height, interpolation,
|
|
|
|
progress, TRUE);
|
2004-02-09 00:09:20 +00:00
|
|
|
|
2004-08-10 18:47:21 +00:00
|
|
|
if (progress)
|
|
|
|
gimp_progress_end (progress);
|
2003-05-07 11:09:00 +00:00
|
|
|
|
2004-10-12 14:59:14 +00:00
|
|
|
if (progress_dialog)
|
|
|
|
gtk_widget_destroy (progress_dialog);
|
2001-04-20 16:27:44 +00:00
|
|
|
|
2004-10-12 14:59:14 +00:00
|
|
|
gimp_image_flush (gimp_item_get_image (item));
|
2001-04-20 16:27:44 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-10-14 16:51:30 +00:00
|
|
|
g_warning ("Scale Error: "
|
|
|
|
"Both width and height must be greater than zero.");
|
2001-04-20 16:27:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-09-25 22:18:37 +02:00
|
|
|
layers_resize_callback (GtkWidget *dialog,
|
|
|
|
GimpViewable *viewable,
|
|
|
|
GimpContext *context,
|
|
|
|
gint width,
|
|
|
|
gint height,
|
Issue #8900 and #9923: reimplementing GimpUnit as a proper class.
This fixes all our GObject Introspection issues with GimpUnit which was
both an enum and an int-derived type of user-defined units *completing*
the enum values. GIR clearly didn't like this!
Now GimpUnit is a proper class and units are unique objects, allowing to
compare them with an identity test (i.e. `unit == gimp_unit_pixel ()`
tells us if unit is the pixel unit or not), which makes it easy to use,
just like with int, yet adding also methods, making for nicer
introspected API.
As an aside, this also fixes #10738, by having all the built-in units
retrievable even if libgimpbase had not been properly initialized with
gimp_base_init().
I haven't checked in details how GIR works to introspect, but it looks
like it loads the library to inspect and runs functions, hence
triggering some CRITICALS because virtual methods (supposed to be
initialized with gimp_base_init() run by libgimp) are not set. This new
code won't trigger any critical because the vtable method are now not
necessary, at least for all built-in units.
Note that GimpUnit is still in libgimpbase. It could have been moved to
libgimp in order to avoid any virtual method table (since we need to
keep core and libgimp side's units in sync, PDB is required), but too
many libgimpwidgets widgets were already using GimpUnit. And technically
most of GimpUnit logic doesn't require PDB (only the creation/sync
part). This is one of the reasons why user-created GimpUnit list is
handled and stored differently from other types of objects.
Globally this simplifies the code a lot too and we don't need separate
implementations of various utils for core and libgimp, which means less
prone to errors.
2024-07-25 20:55:21 +02:00
|
|
|
GimpUnit *unit,
|
2016-09-25 22:18:37 +02:00
|
|
|
gint offset_x,
|
|
|
|
gint offset_y,
|
2021-02-07 22:06:41 +06:00
|
|
|
gdouble unused0,
|
|
|
|
gdouble unused1,
|
Issue #8900 and #9923: reimplementing GimpUnit as a proper class.
This fixes all our GObject Introspection issues with GimpUnit which was
both an enum and an int-derived type of user-defined units *completing*
the enum values. GIR clearly didn't like this!
Now GimpUnit is a proper class and units are unique objects, allowing to
compare them with an identity test (i.e. `unit == gimp_unit_pixel ()`
tells us if unit is the pixel unit or not), which makes it easy to use,
just like with int, yet adding also methods, making for nicer
introspected API.
As an aside, this also fixes #10738, by having all the built-in units
retrievable even if libgimpbase had not been properly initialized with
gimp_base_init().
I haven't checked in details how GIR works to introspect, but it looks
like it loads the library to inspect and runs functions, hence
triggering some CRITICALS because virtual methods (supposed to be
initialized with gimp_base_init() run by libgimp) are not set. This new
code won't trigger any critical because the vtable method are now not
necessary, at least for all built-in units.
Note that GimpUnit is still in libgimpbase. It could have been moved to
libgimp in order to avoid any virtual method table (since we need to
keep core and libgimp side's units in sync, PDB is required), but too
many libgimpwidgets widgets were already using GimpUnit. And technically
most of GimpUnit logic doesn't require PDB (only the creation/sync
part). This is one of the reasons why user-created GimpUnit list is
handled and stored differently from other types of objects.
Globally this simplifies the code a lot too and we don't need separate
implementations of various utils for core and libgimp, which means less
prone to errors.
2024-07-25 20:55:21 +02:00
|
|
|
GimpUnit *unused2,
|
2016-10-20 00:30:10 +02:00
|
|
|
GimpFillType fill_type,
|
2021-02-07 22:06:41 +06:00
|
|
|
GimpItemSet unused3,
|
|
|
|
gboolean unused4,
|
2016-09-25 22:18:37 +02:00
|
|
|
gpointer user_data)
|
2001-04-20 16:27:44 +00:00
|
|
|
{
|
2006-10-14 16:51:30 +00:00
|
|
|
layer_resize_unit = unit;
|
|
|
|
|
2004-10-27 10:33:08 +00:00
|
|
|
if (width > 0 && height > 0)
|
2001-04-20 16:27:44 +00:00
|
|
|
{
|
2016-10-20 00:30:10 +02:00
|
|
|
GimpItem *item = GIMP_ITEM (viewable);
|
|
|
|
GimpImage *image = gimp_item_get_image (item);
|
|
|
|
GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config);
|
|
|
|
|
|
|
|
g_object_set (config,
|
|
|
|
"layer-resize-fill-type", fill_type,
|
|
|
|
NULL);
|
2002-02-25 17:58:50 +00:00
|
|
|
|
2004-10-27 10:33:08 +00:00
|
|
|
gtk_widget_destroy (dialog);
|
2001-04-20 16:27:44 +00:00
|
|
|
|
2008-11-03 00:09:01 +00:00
|
|
|
if (width == gimp_item_get_width (item) &&
|
|
|
|
height == gimp_item_get_height (item))
|
2004-10-27 10:33:08 +00:00
|
|
|
return;
|
2001-04-20 16:27:44 +00:00
|
|
|
|
2016-10-10 00:02:16 +02:00
|
|
|
gimp_item_resize (item, context, fill_type,
|
2004-10-27 10:33:08 +00:00
|
|
|
width, height, offset_x, offset_y);
|
|
|
|
gimp_image_flush (gimp_item_get_image (item));
|
2001-04-20 16:27:44 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-10-14 16:51:30 +00:00
|
|
|
g_warning ("Resize Error: "
|
|
|
|
"Both width and height must be greater than zero.");
|
2001-04-20 16:27:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-09-15 13:24:45 +00:00
|
|
|
static gint
|
2017-02-20 23:51:32 +01:00
|
|
|
layers_mode_index (GimpLayerMode layer_mode,
|
|
|
|
const GimpLayerMode *modes,
|
|
|
|
gint n_modes)
|
2004-09-15 13:24:45 +00:00
|
|
|
{
|
|
|
|
gint i = 0;
|
|
|
|
|
2017-02-20 23:51:32 +01:00
|
|
|
while (i < (n_modes - 1) && modes[i] != layer_mode)
|
2004-09-15 13:24:45 +00:00
|
|
|
i++;
|
|
|
|
|
|
|
|
return i;
|
|
|
|
}
|