gimp/app/core/gimp-edit.c

1206 lines
40 KiB
C
Raw Normal View History

/* GIMP - The GNU Image Manipulation Program
1997-11-24 22:05:25 +00:00
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This program is free software: you can redistribute it and/or modify
1997-11-24 22:05:25 +00:00
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
1997-11-24 22:05:25 +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
* along with this program. If not, see <https://www.gnu.org/licenses/>.
1997-11-24 22:05:25 +00:00
*/
#include "config.h"
#include <cairo.h>
#include <gegl.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
Makefile.am configure.in added the new library below. 2001-01-24 Michael Natterer <mitch@gimp.org> * Makefile.am * configure.in * gimptool.in: added the new library below. * libgimpwidgets/Makefile.am * libgimpwidgets/gimpchainbutton.[ch] * libgimpwidgets/gimpcolorarea.[ch] * libgimpwidgets/gimpcolorbutton.[ch] * libgimpwidgets/gimpdialog.[ch] * libgimpwidgets/gimpfileselection.[ch] * libgimpwidgets/gimphelpui.[ch] * libgimpwidgets/gimppatheditor.[ch] * libgimpwidgets/gimppixmap.[ch] * libgimpwidgets/gimpquerybox.[ch] * libgimpwidgets/gimpsizeentry.[ch] * libgimpwidgets/gimpunitmenu.[ch] * libgimpwidgets/gimpwidgets.[ch] * libgimpwidgets/gimpwidgets.def * libgimpwidgets/gimpwidgetstypes.h: new shared library. Currently there are some ugly dependencies into libgimp. These will be removed and go to a "libgimpglue" library which will be a library for functions which share a common interface between plug-ins and the app but have different implementations. Include "libgimp/gimpunit.h" from "libgimpwidgets/gimpwidgetstypes.h" to simulate this upcoming separation. * libgimp/Makefile.am * libgimp/gimpchainbutton.[ch] * libgimp/gimpcolorarea.[ch] * libgimp/gimpcolorbutton.[ch] * libgimp/gimpdialog.[ch] * libgimp/gimpfileselection.[ch] * libgimp/gimphelpui.[ch] * libgimp/gimppatheditor.[ch] * libgimp/gimppixmap.[ch] * libgimp/gimpquerybox.[ch] * libgimp/gimpsizeentry.[ch] * libgimp/gimpunitmenu.[ch] * libgimp/gimpwidgets.[ch]: removed from here. * libgimp/gimpui.h * libgimp/gimpuitypes.h * libgimp/makefile.mingw.in * libgimp/makefile.msc: changed accordingly. * app/[all ui files] * app/pdb/palette_cmds.c * app/pdb/tools_cmds.c * tools/pdbgen/pdb/palette.pdb * tools/pdbgen/pdb/tools.pdb: #include "libgimpwidgets/gimpwidgets.h" and removed useless includes. * app/apptypes.h: #include "libgimpwidgets/gimpwidgetstypes.h" * app/Makefile.am * plug-ins/[all makefiles which link against libgimpui]: link against libgimpwidgets.la * po-libgimp/POTFILES.in: changed file locations.
2001-01-24 22:36:18 +00:00
#include "libgimpbase/gimpbase.h"
#include "libgimpcolor/gimpcolor.h"
app/core/Makefile.am app/core/core-types.h added an "application object" 2001-07-04 Michael Natterer <mitch@gimp.org> * app/core/Makefile.am * app/core/core-types.h * app/core/gimp.[ch]: added an "application object" called Gimp. Currently, it contains the image list, the clipboard, the data factories, the procedural hashtable and the tool info list. It's the toplevel object of the core object system. Finally, creating a Gimp object will return a standalone gimp core engine instance with no other global states/variables involved. * app/app_procs.[ch]: allocate a "Gimp" instance called "the_gimp" :) Removed stuff which is now done by the "Gimp" object. Merged gimp_init() into app_init() because gimp_init() is taken now. * app/context_manager.[ch]: removed stuff done by "Gimp". * app/batch.[ch] * app/gimage.[ch] * app/xcf/xcf-load.[ch] * app/xcf/xcf.[ch] * app/core/gimpedit.[ch] * app/tools/tool_manager.[ch]: pass around an additional "Gimp" argument. * app/pdb/procedural_db.[ch]: pass a "Gimp" pointer as first parameter to all internal procedures and to all procedural_db_* functions. * app/core/gimpcontext.[ch] * app/core/gimpimage.[ch]: added a "Gimp" pointer to the structs. * app/devices.c * app/errors.c * app/file-open.c * app/file-save.c * app/gimphelp.c * app/gimpunit.c * app/image_new.c * app/main.c * app/nav_window.c * app/plug_in.c * app/base/base.c * app/core/gimpdatafactory.c * app/core/gimpimage-duplicate.c * app/core/gimpimage-mask.c * app/core/gimptoolinfo.[ch] * app/gui/brush-select.c * app/gui/convert-dialog.c * app/gui/dialogs-constructors.c * app/gui/edit-commands.c * app/gui/file-open-dialog.c * app/gui/file-save-dialog.c * app/gui/gradient-editor.c * app/gui/gradient-select.c * app/gui/gui.c * app/gui/image-commands.c * app/gui/info-window.c * app/gui/menus.c * app/gui/palette-editor.c * app/gui/palette-import-dialog.c * app/gui/palette-select.c * app/gui/paths-dialog.c * app/gui/pattern-select.c * app/gui/preferences-dialog.c * app/gui/test-commands.c * app/gui/toolbox.c * app/gui/tools-commands.c * app/tools/gimpbezierselecttool.c * app/tools/gimpbucketfilltool.c * app/tools/gimppainttool.h * app/tools/gimptexttool.c * app/tools/gimptransformtool.h * app/widgets/gimpbufferview.c * app/widgets/gimpcontainerview-utils.c * app/widgets/gimpcursor.c * app/widgets/gimpdnd.c * app/widgets/gimpimagedock.c: changed accordingly. Cleaned up lots of includes. Many files still access the global "the_gimp" variable exported by app_procs.h. * tools/pdbgen/app.pl * tools/pdbgen/pdb/brush_select.pdb * tools/pdbgen/pdb/brushes.pdb * tools/pdbgen/pdb/convert.pdb * tools/pdbgen/pdb/edit.pdb * tools/pdbgen/pdb/fileops.pdb * tools/pdbgen/pdb/gradient_select.pdb * tools/pdbgen/pdb/gradients.pdb * tools/pdbgen/pdb/image.pdb * tools/pdbgen/pdb/palette.pdb * tools/pdbgen/pdb/pattern_select.pdb * tools/pdbgen/pdb/patterns.pdb * tools/pdbgen/pdb/procedural_db.pdb: changed accordingly. Don't use "the_gimp" here because all procedures get passed a "Gimp" pointer now. * app/pdb/*: regenerated.
2001-07-04 19:31:35 +00:00
#include "core-types.h"
app/core/Makefile.am app/core/core-types.h added an "application object" 2001-07-04 Michael Natterer <mitch@gimp.org> * app/core/Makefile.am * app/core/core-types.h * app/core/gimp.[ch]: added an "application object" called Gimp. Currently, it contains the image list, the clipboard, the data factories, the procedural hashtable and the tool info list. It's the toplevel object of the core object system. Finally, creating a Gimp object will return a standalone gimp core engine instance with no other global states/variables involved. * app/app_procs.[ch]: allocate a "Gimp" instance called "the_gimp" :) Removed stuff which is now done by the "Gimp" object. Merged gimp_init() into app_init() because gimp_init() is taken now. * app/context_manager.[ch]: removed stuff done by "Gimp". * app/batch.[ch] * app/gimage.[ch] * app/xcf/xcf-load.[ch] * app/xcf/xcf.[ch] * app/core/gimpedit.[ch] * app/tools/tool_manager.[ch]: pass around an additional "Gimp" argument. * app/pdb/procedural_db.[ch]: pass a "Gimp" pointer as first parameter to all internal procedures and to all procedural_db_* functions. * app/core/gimpcontext.[ch] * app/core/gimpimage.[ch]: added a "Gimp" pointer to the structs. * app/devices.c * app/errors.c * app/file-open.c * app/file-save.c * app/gimphelp.c * app/gimpunit.c * app/image_new.c * app/main.c * app/nav_window.c * app/plug_in.c * app/base/base.c * app/core/gimpdatafactory.c * app/core/gimpimage-duplicate.c * app/core/gimpimage-mask.c * app/core/gimptoolinfo.[ch] * app/gui/brush-select.c * app/gui/convert-dialog.c * app/gui/dialogs-constructors.c * app/gui/edit-commands.c * app/gui/file-open-dialog.c * app/gui/file-save-dialog.c * app/gui/gradient-editor.c * app/gui/gradient-select.c * app/gui/gui.c * app/gui/image-commands.c * app/gui/info-window.c * app/gui/menus.c * app/gui/palette-editor.c * app/gui/palette-import-dialog.c * app/gui/palette-select.c * app/gui/paths-dialog.c * app/gui/pattern-select.c * app/gui/preferences-dialog.c * app/gui/test-commands.c * app/gui/toolbox.c * app/gui/tools-commands.c * app/tools/gimpbezierselecttool.c * app/tools/gimpbucketfilltool.c * app/tools/gimppainttool.h * app/tools/gimptexttool.c * app/tools/gimptransformtool.h * app/widgets/gimpbufferview.c * app/widgets/gimpcontainerview-utils.c * app/widgets/gimpcursor.c * app/widgets/gimpdnd.c * app/widgets/gimpimagedock.c: changed accordingly. Cleaned up lots of includes. Many files still access the global "the_gimp" variable exported by app_procs.h. * tools/pdbgen/app.pl * tools/pdbgen/pdb/brush_select.pdb * tools/pdbgen/pdb/brushes.pdb * tools/pdbgen/pdb/convert.pdb * tools/pdbgen/pdb/edit.pdb * tools/pdbgen/pdb/fileops.pdb * tools/pdbgen/pdb/gradient_select.pdb * tools/pdbgen/pdb/gradients.pdb * tools/pdbgen/pdb/image.pdb * tools/pdbgen/pdb/palette.pdb * tools/pdbgen/pdb/pattern_select.pdb * tools/pdbgen/pdb/patterns.pdb * tools/pdbgen/pdb/procedural_db.pdb: changed accordingly. Don't use "the_gimp" here because all procedures get passed a "Gimp" pointer now. * app/pdb/*: regenerated.
2001-07-04 19:31:35 +00:00
#include "gimp.h"
#include "gimp-edit.h"
#include "gimpbuffer.h"
#include "gimpcontext.h"
#include "gimpdrawable-edit.h"
#include "gimpgrouplayer.h"
#include "gimpimage.h"
#include "gimpimage-duplicate.h"
#include "gimpimage-merge.h"
#include "gimpimage-new.h"
#include "gimpimage-resize.h"
#include "gimpimage-undo.h"
#include "gimplayer-floating-selection.h"
#include "gimplayer-new.h"
#include "gimplayermask.h"
#include "gimplist.h"
#include "gimppickable.h"
Treat changes to the selection like changes to any other drawable: 2003-10-06 Michael Natterer <mitch@gimp.org> Treat changes to the selection like changes to any other drawable: * app/core/gimpchannel.c * app/core/gimpchannel-combine.c: call gimp_drawable_update() after changing the channel. * app/core/gimpimage.[ch]: added struct GimpImageFlushAccumulator with one member "gboolean mask_changed". Connect to "update" of the selection and set accum.mask_changed to TRUE in the callback. Added default implementation for GimpImage::flush() and emit "mask_changed" there. Unrelated: * app/core/gimpimage.h: removed GimpGuide struct... * app/core/gimpimage-guides.h: ...and added it here. * app/core/gimpimage-undo-push.c (undo_pop_mask) (undo_pop_channel_mod): don't distinguish between selection and non-selection channels and just call gimp_drawable_update(). * app/core/gimpundo.h * app/core/gimpimage-undo.c: removed "gboolean mask_changed" from the GimpUndoAccumulator struct since we don't have to care about that signal explicitly any more. * app/display/gimpdisplay-foreach.[ch]: removed gimp_displays_flush(). * tools/pdbgen/pdb/display.pdb (displays_flush_invoker): call gimp_image_flush() on all images so the flush accumulator is honored. This generalization enables the removal of more special purpose code which was needed to treat the selection different: * app/core/gimpimage-mask-select.[ch]: removed... * app/core/gimpchannel-select.[ch]: ...and added under a new name because it's not selection specific any more. * app/core/gimpimage-mask.[ch]: removed... * app/core/gimpselection.[ch]: ...added the two remaining functions here. Removed all calls to gimp_image_mask_changed(). * app/core/Makefile.am * app/core/gimp-edit.c * app/core/gimpdrawable-transform.c * app/core/gimpimage-scale.c * app/core/gimpimage-snap.c * app/display/gimpdisplayshell.c * app/gui/channels-commands.c * app/gui/layers-commands.c * app/gui/select-commands.c * app/gui/vectors-commands.c * app/tools/gimpbycolorselecttool.c * app/tools/gimpeditselectiontool.c * app/tools/gimpellipseselecttool.c * app/tools/gimpfreeselecttool.c * app/tools/gimpfuzzyselecttool.c * app/tools/gimpiscissorstool.c * app/tools/gimprectselecttool.c * app/tools/gimptransformtool.c * app/widgets/gimpchanneltreeview.c * app/widgets/gimpselectioneditor.c * app/widgets/gimpvectorstreeview.c * app/xcf/xcf-save.c * tools/pdbgen/pdb/paths.pdb * tools/pdbgen/pdb/selection.pdb * tools/pdbgen/pdb/selection_tools.pdb: changed accordingly. * app/core/gimpdrawable-bucket-fill.c * app/core/gimpimage-colormap.c * app/core/gimplayer-floating-sel.c * app/core/gimplayer.c * app/gui/image-menu.c * app/paint/gimppaintcore.c * app/tools/gimpcroptool.c * app/tools/gimpinkoptions.c * app/tools/gimpvectortool.c: removed useless and/or obsolete #includes. * app/pdb/display_cmds.c * app/pdb/paths_cmds.c * app/pdb/selection_cmds.c * app/pdb/selection_tools_cmds.c: regenerated.
2003-10-06 12:17:11 +00:00
#include "gimpselection.h"
#include "gimp-intl.h"
/* local function protypes */
static GimpBuffer * gimp_edit_extract (GimpImage *image,
GList *pickables,
GimpContext *context,
gboolean cut_pixels,
GError **error);
static GimpDrawable * gimp_edit_paste_get_top_item (GList *drawables);
/* public functions */
GimpObject *
gimp_edit_cut (GimpImage *image,
GList *drawables,
GimpContext *context,
GError **error)
1997-11-24 22:05:25 +00:00
{
GList *iter;
gboolean layers_only = TRUE;
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
Get rid of the "current_context" which was in fact just a bunch of global 2004-04-15 Michael Natterer <mitch@gimp.org> Get rid of the "current_context" which was in fact just a bunch of global variables. Instead, pass the needed context all the way from the GUI and the PDB to the core. This is a prerequisite for macro recording and generally helps separating the various subsystems from each other. Work in progress... * app/core/gimp.[ch]: removed member "current_context" and gimp_[get|set]_current_context(). * app/core/gimp-edit.[ch] * app/core/gimpdrawable-blend.[ch] * app/core/gimpdrawable-bucket-fill.[ch] * app/core/gimpdrawable-offset.[ch] * app/core/gimpdrawable-transform.[ch] * app/core/gimpimage-crop.[ch] * app/core/gimpimage-flip.[ch] * app/core/gimpimage-merge.[ch] * app/core/gimpimage-resize.[ch] * app/core/gimpimage-rotate.[ch] * app/core/gimpimage.[ch] * app/core/gimpimagefile.[ch] * app/core/gimpitem-linked.[ch] * app/core/gimpitem.[ch] * app/core/gimplayer.[ch] * app/core/gimpselection.[ch] * app/core/gimptemplate.[ch] * app/file/file-open.[ch] * app/file/file-save.[ch] * app/pdb/procedural_db.[ch] * app/text/gimptext-compat.[ch] * app/text/gimptextlayer-transform.[ch] * app/gui/brush-select.[ch] * app/gui/font-select.[ch] * app/gui/gradient-select.[ch] * app/gui/palette-select.[ch] * app/gui/pattern-select.[ch]: added tons of "GimpContext *context" parameters and use the passed context instead of gimp_get_current_context(). * app/app_procs.c * app/batch.c * app/core/gimpchannel.c * app/core/gimpdrawable.c * app/paint/gimperaser.c * app/paint/gimppaintbrush.c * app/plug-in/plug-in-message.c * app/plug-in/plug-ins.c * app/text/gimptextlayer.c * app/tools/gimpblendtool.c * app/tools/gimpbucketfilltool.c * app/tools/gimpcroptool.c * app/tools/gimpeditselectiontool.c * app/tools/gimpfliptool.c * app/tools/gimpinktool.c * app/tools/gimptransformtool.c * app/vectors/gimpvectors.c * app/gui/convert-dialog.c * app/gui/drawable-commands.c * app/gui/edit-commands.c * app/gui/file-commands.c * app/gui/file-new-dialog.c * app/gui/file-open-dialog.c * app/gui/file-save-dialog.c * app/gui/image-commands.c * app/gui/layers-commands.c * app/gui/offset-dialog.c * app/gui/select-commands.c * app/gui/vectors-commands.c * app/widgets/gimpdnd.c * app/widgets/gimpdocumentview.c * app/widgets/gimphelp.c * app/widgets/gimpthumbbox.c: pass gimp_get_user_context() or GIMP_CONTEXT(tool_options) or whatever is the right context to the changed core functions. * tools/pdbgen/app.pl: pass "GimpContext *context" to all generated PDB invokers. * tools/pdbgen/pdb/brush_select.pdb * tools/pdbgen/pdb/brushes.pdb * tools/pdbgen/pdb/drawable.pdb * tools/pdbgen/pdb/edit.pdb * tools/pdbgen/pdb/font_select.pdb * tools/pdbgen/pdb/gradient_select.pdb * tools/pdbgen/pdb/gradients.pdb * tools/pdbgen/pdb/image.pdb * tools/pdbgen/pdb/layer.pdb * tools/pdbgen/pdb/paint_tools.pdb * tools/pdbgen/pdb/palette.pdb * tools/pdbgen/pdb/palette_select.pdb * tools/pdbgen/pdb/palettes.pdb * tools/pdbgen/pdb/paths.pdb * tools/pdbgen/pdb/pattern_select.pdb * tools/pdbgen/pdb/patterns.pdb * tools/pdbgen/pdb/selection.pdb * tools/pdbgen/pdb/text_tool.pdb * tools/pdbgen/pdb/transform_tools.pdb: pass the new context parameter to the changed core functions. * app/pdb/*_cmds.c: regenerated.
2004-04-14 23:37:34 +00:00
g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1997-11-24 22:05:25 +00:00
for (iter = drawables; iter; iter = iter->next)
if (! GIMP_IS_LAYER (iter->data))
{
layers_only = FALSE;
break;
}
if (layers_only)
{
gchar *undo_label;
undo_label = g_strdup_printf (ngettext ("Cut Layer", "Cut %d Layers",
g_list_length (drawables)),
g_list_length (drawables));
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_EDIT_CUT,
undo_label);
g_free (undo_label);
if (gimp_channel_is_empty (gimp_image_get_mask (image)))
{
GList *remove = NULL;
GimpImage *clip_image;
/* Let's work on a copy because we will edit the list to remove
* layers whose ancestor is also cut.
*/
drawables = g_list_copy (drawables);
for (iter = drawables; iter; iter = iter->next)
{
GList *iter2;
for (iter2 = drawables; iter2; iter2 = iter2->next)
{
if (iter2 == iter)
continue;
if (gimp_viewable_is_ancestor (iter2->data, iter->data))
{
/* When cutting a layer group, all its children come
* with anyway.
*/
remove = g_list_prepend (remove, iter);
break;
}
}
}
for (iter = remove; iter; iter = iter->next)
drawables = g_list_delete_link (drawables, iter->data);
g_list_free (remove);
/* Now copy all layers into the clipboard image. */
clip_image = gimp_image_new_from_drawables (image->gimp, drawables, FALSE, TRUE);
gimp_container_remove (image->gimp->images, GIMP_OBJECT (clip_image));
gimp_set_clipboard_image (image->gimp, clip_image);
g_object_unref (clip_image);
/* Remove layers from source image. */
for (iter = drawables; iter; iter = iter->next)
gimp_image_remove_layer (image, GIMP_LAYER (iter->data),
TRUE, NULL);
g_list_free (drawables);
}
else
{
/* With selection, a cut is similar to a copy followed by a clear. */
gimp_edit_copy (image, drawables, context, error);
for (iter = drawables; iter; iter = iter->next)
if (! GIMP_IS_GROUP_LAYER (iter->data))
gimp_drawable_edit_clear (GIMP_DRAWABLE (iter->data), context);
}
gimp_image_undo_group_end (image);
return GIMP_OBJECT (gimp_get_clipboard_image (image->gimp));
}
else
{
GimpBuffer *buffer;
buffer = gimp_edit_extract (image, drawables, context, TRUE, error);
if (buffer)
{
gimp_set_clipboard_buffer (image->gimp, buffer);
g_object_unref (buffer);
return GIMP_OBJECT (gimp_get_clipboard_buffer (image->gimp));
}
}
return NULL;
1997-11-24 22:05:25 +00:00
}
GimpObject *
gimp_edit_copy (GimpImage *image,
GList *drawables,
GimpContext *context,
GError **error)
1997-11-24 22:05:25 +00:00
{
GList *iter;
gboolean drawables_are_layers = TRUE;
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
g_return_val_if_fail (drawables != NULL, NULL);
Get rid of the "current_context" which was in fact just a bunch of global 2004-04-15 Michael Natterer <mitch@gimp.org> Get rid of the "current_context" which was in fact just a bunch of global variables. Instead, pass the needed context all the way from the GUI and the PDB to the core. This is a prerequisite for macro recording and generally helps separating the various subsystems from each other. Work in progress... * app/core/gimp.[ch]: removed member "current_context" and gimp_[get|set]_current_context(). * app/core/gimp-edit.[ch] * app/core/gimpdrawable-blend.[ch] * app/core/gimpdrawable-bucket-fill.[ch] * app/core/gimpdrawable-offset.[ch] * app/core/gimpdrawable-transform.[ch] * app/core/gimpimage-crop.[ch] * app/core/gimpimage-flip.[ch] * app/core/gimpimage-merge.[ch] * app/core/gimpimage-resize.[ch] * app/core/gimpimage-rotate.[ch] * app/core/gimpimage.[ch] * app/core/gimpimagefile.[ch] * app/core/gimpitem-linked.[ch] * app/core/gimpitem.[ch] * app/core/gimplayer.[ch] * app/core/gimpselection.[ch] * app/core/gimptemplate.[ch] * app/file/file-open.[ch] * app/file/file-save.[ch] * app/pdb/procedural_db.[ch] * app/text/gimptext-compat.[ch] * app/text/gimptextlayer-transform.[ch] * app/gui/brush-select.[ch] * app/gui/font-select.[ch] * app/gui/gradient-select.[ch] * app/gui/palette-select.[ch] * app/gui/pattern-select.[ch]: added tons of "GimpContext *context" parameters and use the passed context instead of gimp_get_current_context(). * app/app_procs.c * app/batch.c * app/core/gimpchannel.c * app/core/gimpdrawable.c * app/paint/gimperaser.c * app/paint/gimppaintbrush.c * app/plug-in/plug-in-message.c * app/plug-in/plug-ins.c * app/text/gimptextlayer.c * app/tools/gimpblendtool.c * app/tools/gimpbucketfilltool.c * app/tools/gimpcroptool.c * app/tools/gimpeditselectiontool.c * app/tools/gimpfliptool.c * app/tools/gimpinktool.c * app/tools/gimptransformtool.c * app/vectors/gimpvectors.c * app/gui/convert-dialog.c * app/gui/drawable-commands.c * app/gui/edit-commands.c * app/gui/file-commands.c * app/gui/file-new-dialog.c * app/gui/file-open-dialog.c * app/gui/file-save-dialog.c * app/gui/image-commands.c * app/gui/layers-commands.c * app/gui/offset-dialog.c * app/gui/select-commands.c * app/gui/vectors-commands.c * app/widgets/gimpdnd.c * app/widgets/gimpdocumentview.c * app/widgets/gimphelp.c * app/widgets/gimpthumbbox.c: pass gimp_get_user_context() or GIMP_CONTEXT(tool_options) or whatever is the right context to the changed core functions. * tools/pdbgen/app.pl: pass "GimpContext *context" to all generated PDB invokers. * tools/pdbgen/pdb/brush_select.pdb * tools/pdbgen/pdb/brushes.pdb * tools/pdbgen/pdb/drawable.pdb * tools/pdbgen/pdb/edit.pdb * tools/pdbgen/pdb/font_select.pdb * tools/pdbgen/pdb/gradient_select.pdb * tools/pdbgen/pdb/gradients.pdb * tools/pdbgen/pdb/image.pdb * tools/pdbgen/pdb/layer.pdb * tools/pdbgen/pdb/paint_tools.pdb * tools/pdbgen/pdb/palette.pdb * tools/pdbgen/pdb/palette_select.pdb * tools/pdbgen/pdb/palettes.pdb * tools/pdbgen/pdb/paths.pdb * tools/pdbgen/pdb/pattern_select.pdb * tools/pdbgen/pdb/patterns.pdb * tools/pdbgen/pdb/selection.pdb * tools/pdbgen/pdb/text_tool.pdb * tools/pdbgen/pdb/transform_tools.pdb: pass the new context parameter to the changed core functions. * app/pdb/*_cmds.c: regenerated.
2004-04-14 23:37:34 +00:00
g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1997-11-24 22:05:25 +00:00
for (iter = drawables; iter; iter = iter->next)
{
g_return_val_if_fail (GIMP_IS_DRAWABLE (iter->data), NULL);
g_return_val_if_fail (gimp_item_is_attached (iter->data), NULL);
if (! GIMP_IS_LAYER (iter->data))
drawables_are_layers = FALSE;
}
/* Only accept multiple drawables for layers. */
g_return_val_if_fail (g_list_length (drawables) == 1 || drawables_are_layers, NULL);
if (drawables_are_layers)
{
/* Special-casing the 1 layer with no selection case.
* It allows us to save the whole layer with all pixels as stored,
* not the rendered version of it.
*/
GimpImage *clip_image;
GimpChannel *clip_selection;
GeglRectangle selection_bounds;
gboolean has_selection = FALSE;
clip_image = gimp_image_new_from_drawables (image->gimp, drawables, TRUE, TRUE);
gimp_container_remove (image->gimp->images, GIMP_OBJECT (clip_image));
gimp_set_clipboard_image (image->gimp, clip_image);
g_object_unref (clip_image);
clip_selection = gimp_image_get_mask (clip_image);
if (! gimp_channel_is_empty (clip_selection))
{
gimp_item_bounds (GIMP_ITEM (clip_selection),
&selection_bounds.x, &selection_bounds.y,
&selection_bounds.width, &selection_bounds.height);
/* Invert the selection. */
gimp_channel_invert (clip_selection, FALSE);
/* Empty selection work the same as full selection for the
* gimp_drawable_edit_*() APIs. So as a special case, we check again
* after inverting the selection (if the inverted selection is empty,
* it meant we were in "all" selection case).
* Otherwise we were ending up clearing everything with
* gimp_drawable_edit_clear() on the inverted "all" selection.
*/
has_selection = (! gimp_channel_is_empty (clip_selection));
}
if (has_selection)
{
GList *all_items;
all_items = gimp_image_get_layer_list (clip_image);
for (iter = all_items; iter; iter = g_list_next (iter))
{
app: copying from selection creates layers the size of the selection. After further discussions with Aryeom, we had to make decisions about a few problems. The main problem was: what happens when we copy a selection of a layer whose bounds don't intersect with the selection? The silent treatment of discarding the layer was not acceptable, because e.g. it could happen on huge set of selected layers (like say you copy 100 layers with a selection: you expect 100 created layers and if you realize you don't have them all — e.g. you have 99! — after hours of work, trying to find the missing one can be a huge time loss). The status bar notification (or even error) did not feel right either because this can typically be missed easily. Also it doesn't give a lot of feedback (e.g. you might want hint to find the non-intersecting layers, in case it was not supposed to happen). An error box was even considered, possibly proposing to ignore the problematic layers, or even giving easy ways to find them. Finally what if we let the selection happen regardless the non-intersecting layers? What should the dimension and offset of said layers be? In the end, we went with the more consistent behavior of always creating new layers of the exact same size as the selection. It can be considered as a rule which would make the behavior predictable. For the non-intersecting layers, we'd just have new layers with the dimension/offset of the selection bounding box, and no contents. For other layers, they'd be also this same dimension, possibly increasing the dimension of the source layers (though any new pixel is fully transparent obviously). Aryeom wondered if some people might absolutely need for their workflow that the new layers stick to the origin bounding box. But we felt it was enough of a stretch that we'd try this way for now. Note: of course if some day we get infinite canvas/layers, this whole discussion could be less of a problem anyway! This was Aryeom's conclusion! Ahahah!
2022-11-10 23:49:04 +01:00
gint item_x;
gint item_y;
gimp_item_get_offset (GIMP_ITEM (iter->data), &item_x, &item_y);
/* Even if the original layer may not have an alpha channel, the
* selected data must always have one. First because a selection
* is in some way an alpha channel when we copy (we may copy part
* of a pixel, i.e. with transparency). Second because the
* selection is not necessary rectangular, unlike layers. So when
* we will clear, if we hadn't added an alpha channel, we'd end up
* with background color all over the place.
*/
gimp_layer_add_alpha (GIMP_LAYER (iter->data));
gimp_drawable_edit_clear (GIMP_DRAWABLE (iter->data), context);
app: copying from selection creates layers the size of the selection. After further discussions with Aryeom, we had to make decisions about a few problems. The main problem was: what happens when we copy a selection of a layer whose bounds don't intersect with the selection? The silent treatment of discarding the layer was not acceptable, because e.g. it could happen on huge set of selected layers (like say you copy 100 layers with a selection: you expect 100 created layers and if you realize you don't have them all — e.g. you have 99! — after hours of work, trying to find the missing one can be a huge time loss). The status bar notification (or even error) did not feel right either because this can typically be missed easily. Also it doesn't give a lot of feedback (e.g. you might want hint to find the non-intersecting layers, in case it was not supposed to happen). An error box was even considered, possibly proposing to ignore the problematic layers, or even giving easy ways to find them. Finally what if we let the selection happen regardless the non-intersecting layers? What should the dimension and offset of said layers be? In the end, we went with the more consistent behavior of always creating new layers of the exact same size as the selection. It can be considered as a rule which would make the behavior predictable. For the non-intersecting layers, we'd just have new layers with the dimension/offset of the selection bounding box, and no contents. For other layers, they'd be also this same dimension, possibly increasing the dimension of the source layers (though any new pixel is fully transparent obviously). Aryeom wondered if some people might absolutely need for their workflow that the new layers stick to the origin bounding box. But we felt it was enough of a stretch that we'd try this way for now. Note: of course if some day we get infinite canvas/layers, this whole discussion could be less of a problem anyway! This was Aryeom's conclusion! Ahahah!
2022-11-10 23:49:04 +01:00
/* Finally shrink the copied layer to selection bounds. */
gimp_item_resize (iter->data, context, GIMP_FILL_TRANSPARENT,
app: copying from selection creates layers the size of the selection. After further discussions with Aryeom, we had to make decisions about a few problems. The main problem was: what happens when we copy a selection of a layer whose bounds don't intersect with the selection? The silent treatment of discarding the layer was not acceptable, because e.g. it could happen on huge set of selected layers (like say you copy 100 layers with a selection: you expect 100 created layers and if you realize you don't have them all — e.g. you have 99! — after hours of work, trying to find the missing one can be a huge time loss). The status bar notification (or even error) did not feel right either because this can typically be missed easily. Also it doesn't give a lot of feedback (e.g. you might want hint to find the non-intersecting layers, in case it was not supposed to happen). An error box was even considered, possibly proposing to ignore the problematic layers, or even giving easy ways to find them. Finally what if we let the selection happen regardless the non-intersecting layers? What should the dimension and offset of said layers be? In the end, we went with the more consistent behavior of always creating new layers of the exact same size as the selection. It can be considered as a rule which would make the behavior predictable. For the non-intersecting layers, we'd just have new layers with the dimension/offset of the selection bounding box, and no contents. For other layers, they'd be also this same dimension, possibly increasing the dimension of the source layers (though any new pixel is fully transparent obviously). Aryeom wondered if some people might absolutely need for their workflow that the new layers stick to the origin bounding box. But we felt it was enough of a stretch that we'd try this way for now. Note: of course if some day we get infinite canvas/layers, this whole discussion could be less of a problem anyway! This was Aryeom's conclusion! Ahahah!
2022-11-10 23:49:04 +01:00
selection_bounds.width, selection_bounds.height,
item_x - selection_bounds.x, item_y - selection_bounds.y);
}
g_list_free (all_items);
/* We need to keep the image size as-is, because even after cropping
* layers to selection, their offset stay important for in-place paste
* variants. Yet we also need to store the dimensions where we'd have
* cropped the image for the "Paste as New Image" action.
*/
g_object_set_data (G_OBJECT (clip_image),
"gimp-edit-new-image-x",
GINT_TO_POINTER (selection_bounds.x));
g_object_set_data (G_OBJECT (clip_image),
"gimp-edit-new-image-y",
GINT_TO_POINTER (selection_bounds.y));
g_object_set_data (G_OBJECT (clip_image),
"gimp-edit-new-image-width",
GINT_TO_POINTER (selection_bounds.width));
g_object_set_data (G_OBJECT (clip_image),
"gimp-edit-new-image-height",
GINT_TO_POINTER (selection_bounds.height));
}
/* Remove selection from the clipboard image. */
gimp_channel_clear (clip_selection, NULL, FALSE);
return GIMP_OBJECT (gimp_get_clipboard_image (image->gimp));
}
else
{
GimpBuffer *buffer;
buffer = gimp_edit_extract (image, drawables, context, FALSE, error);
if (buffer)
{
gimp_set_clipboard_buffer (image->gimp, buffer);
g_object_unref (buffer);
return GIMP_OBJECT (gimp_get_clipboard_buffer (image->gimp));
}
}
return NULL;
1997-11-24 22:05:25 +00:00
}
GimpBuffer *
gimp_edit_copy_visible (GimpImage *image,
GimpContext *context,
GError **error)
{
GimpBuffer *buffer;
GList *pickables;
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
pickables = g_list_prepend (NULL, image);
buffer = gimp_edit_extract (image, pickables, context, FALSE, error);
g_list_free (pickables);
if (buffer)
{
gimp_set_clipboard_buffer (image->gimp, buffer);
g_object_unref (buffer);
return gimp_get_clipboard_buffer (image->gimp);
}
return NULL;
}
static gboolean
gimp_edit_paste_is_in_place (GimpPasteType paste_type)
{
switch (paste_type)
{
case GIMP_PASTE_TYPE_FLOATING:
case GIMP_PASTE_TYPE_FLOATING_INTO:
case GIMP_PASTE_TYPE_NEW_LAYER:
case GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING:
case GIMP_PASTE_TYPE_NEW_MERGED_LAYER_OR_FLOATING:
return FALSE;
case GIMP_PASTE_TYPE_FLOATING_IN_PLACE:
case GIMP_PASTE_TYPE_FLOATING_INTO_IN_PLACE:
case GIMP_PASTE_TYPE_NEW_LAYER_IN_PLACE:
case GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING_IN_PLACE:
case GIMP_PASTE_TYPE_NEW_MERGED_LAYER_OR_FLOATING_IN_PLACE:
return TRUE;
}
g_return_val_if_reached (FALSE);
}
static gboolean
gimp_edit_paste_is_floating (GimpPasteType paste_type,
GimpDrawable *drawable)
{
switch (paste_type)
{
case GIMP_PASTE_TYPE_FLOATING:
case GIMP_PASTE_TYPE_FLOATING_INTO:
case GIMP_PASTE_TYPE_FLOATING_IN_PLACE:
case GIMP_PASTE_TYPE_FLOATING_INTO_IN_PLACE:
return TRUE;
case GIMP_PASTE_TYPE_NEW_LAYER:
case GIMP_PASTE_TYPE_NEW_LAYER_IN_PLACE:
return FALSE;
case GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING:
case GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING_IN_PLACE:
case GIMP_PASTE_TYPE_NEW_MERGED_LAYER_OR_FLOATING:
case GIMP_PASTE_TYPE_NEW_MERGED_LAYER_OR_FLOATING_IN_PLACE:
if (GIMP_IS_LAYER_MASK (drawable))
return TRUE;
else
return FALSE;
}
g_return_val_if_reached (FALSE);
}
static GList *
gimp_edit_paste_get_tagged_layers (GimpImage *image,
GList *layers,
GList *returned_layers,
const Babl *floating_format,
GimpImageBaseType base_type,
GimpPrecision precision,
GimpPasteType paste_type)
{
GList *iter;
for (iter = layers; iter; iter = iter->next)
{
GimpLayer *layer;
GType layer_type;
gboolean copied = TRUE;
switch (paste_type)
{
case GIMP_PASTE_TYPE_FLOATING:
case GIMP_PASTE_TYPE_FLOATING_IN_PLACE:
case GIMP_PASTE_TYPE_FLOATING_INTO:
case GIMP_PASTE_TYPE_FLOATING_INTO_IN_PLACE:
/* when pasting as floating make sure gimp_item_convert()
* will turn group layers into normal layers, otherwise use
* the same layer type so e.g. text information gets
* preserved. See issue #2667.
*/
if (GIMP_IS_GROUP_LAYER (iter->data))
layer_type = GIMP_TYPE_LAYER;
else
layer_type = G_TYPE_FROM_INSTANCE (iter->data);
break;
case GIMP_PASTE_TYPE_NEW_LAYER:
case GIMP_PASTE_TYPE_NEW_LAYER_IN_PLACE:
layer_type = G_TYPE_FROM_INSTANCE (iter->data);
break;
default:
g_return_val_if_reached (NULL);
}
if (GIMP_IS_GROUP_LAYER (iter->data))
copied = (gboolean) GPOINTER_TO_INT (g_object_get_data (G_OBJECT (iter->data),
"gimp-image-copied-layer"));
if (copied)
{
layer = GIMP_LAYER (gimp_item_convert (GIMP_ITEM (iter->data),
image, layer_type));
returned_layers = g_list_prepend (returned_layers, layer);
switch (paste_type)
{
case GIMP_PASTE_TYPE_FLOATING:
case GIMP_PASTE_TYPE_FLOATING_IN_PLACE:
case GIMP_PASTE_TYPE_FLOATING_INTO:
case GIMP_PASTE_TYPE_FLOATING_INTO_IN_PLACE:
/* when pasting as floating selection, get rid of the layer mask,
* and make sure the layer has the right format
*/
if (gimp_layer_get_mask (iter->data))
gimp_layer_apply_mask (iter->data, GIMP_MASK_DISCARD, FALSE);
if (gimp_drawable_get_format (GIMP_DRAWABLE (iter->data)) !=
floating_format)
{
gimp_drawable_convert_type (GIMP_DRAWABLE (iter->data), image,
base_type, precision,
TRUE, NULL, NULL,
GEGL_DITHER_NONE, GEGL_DITHER_NONE,
FALSE, NULL);
}
break;
default:
break;
}
}
else
{
GimpContainer *container;
container = gimp_viewable_get_children (iter->data);
returned_layers = gimp_edit_paste_get_tagged_layers (image,
GIMP_LIST (container)->queue->head,
returned_layers,
floating_format,
base_type, precision, paste_type);
}
}
return returned_layers;
}
static GList *
gimp_edit_paste_get_layers (GimpImage *image,
GList *drawables,
GimpObject *paste,
GimpPasteType *paste_type)
{
GList *layers = NULL;
const Babl *floating_format;
/* change paste type to NEW_LAYER for cases where we can't attach a
* floating selection
*/
if (g_list_length (drawables) != 1 ||
gimp_viewable_get_children (drawables->data) ||
gimp_item_is_content_locked (drawables->data, NULL))
{
if (gimp_edit_paste_is_in_place (*paste_type))
*paste_type = GIMP_PASTE_TYPE_NEW_LAYER_IN_PLACE;
else
*paste_type = GIMP_PASTE_TYPE_NEW_LAYER;
}
/* floating pastes always have the pasted-to drawable's format with
* alpha; if drawable == NULL, user is pasting into an empty image
*/
if (drawables && gimp_edit_paste_is_floating (*paste_type, drawables->data))
floating_format = gimp_drawable_get_format_with_alpha (drawables->data);
else
floating_format = gimp_image_get_layer_format (image, TRUE);
if (GIMP_IS_IMAGE (paste))
{
layers = gimp_image_get_layer_iter (GIMP_IMAGE (paste));
if (g_list_length (layers) > 1)
{
if (gimp_edit_paste_is_in_place (*paste_type))
*paste_type = GIMP_PASTE_TYPE_NEW_LAYER_IN_PLACE;
else
*paste_type = GIMP_PASTE_TYPE_NEW_LAYER;
}
layers = gimp_edit_paste_get_tagged_layers (image, layers, NULL, floating_format,
gimp_drawable_get_base_type (drawables->data),
gimp_drawable_get_precision (drawables->data),
*paste_type);
layers = g_list_reverse (layers);
}
else if (GIMP_IS_BUFFER (paste))
{
GimpLayer *layer;
layer = gimp_layer_new_from_buffer (GIMP_BUFFER (paste), image,
floating_format,
_("Pasted Layer"),
GIMP_OPACITY_OPAQUE,
gimp_image_get_default_new_layer_mode (image));
layers = g_list_prepend (layers, layer);
}
return layers;
}
static void
gimp_edit_paste_get_viewport_offset (GimpImage *image,
GList *drawables,
GList *pasted_layers,
gint viewport_x,
gint viewport_y,
gint viewport_width,
gint viewport_height,
gint *pasted_bbox_x,
gint *pasted_bbox_y,
gint *offset_x,
gint *offset_y)
1997-11-24 22:05:25 +00:00
{
GList *iter;
gint image_width;
gint image_height;
/* Source: pasted layers */
gint source_width;
gint source_height;
gint x1 = G_MAXINT;
gint y1 = G_MAXINT;
gint x2 = G_MININT;
gint y2 = G_MININT;
gboolean clamp_to_image = TRUE;
g_return_if_fail (GIMP_IS_IMAGE (image));
g_return_if_fail (pasted_layers != NULL);
g_return_if_fail (offset_x != NULL);
g_return_if_fail (offset_y != NULL);
1997-11-24 22:05:25 +00:00
image_width = gimp_image_get_width (image);
image_height = gimp_image_get_height (image);
for (iter = pasted_layers; iter; iter = iter->next)
{
gint layer_off_x;
gint layer_off_y;
gint layer_width;
gint layer_height;
g_return_if_fail (GIMP_IS_VIEWABLE (iter->data));
gimp_item_get_offset (GIMP_ITEM (iter->data), &layer_off_x, &layer_off_y);
gimp_viewable_get_size (iter->data, &layer_width, &layer_height);
x1 = MIN (layer_off_x, x1);
y1 = MIN (layer_off_y, y1);
x2 = MAX (layer_off_x + layer_width, x2);
y2 = MAX (layer_off_y + layer_height, y2);
}
/* Source offset and dimensions: this is the bounding box for all layers which
* we want to paste together, keeping their relative position to each others.
*/
*pasted_bbox_x = x1;
*pasted_bbox_y = y1;
source_width = x2 - x1;
source_height = y2 - y1;
if (viewport_width == image_width &&
viewport_height == image_height)
{
/* if the whole image is visible, act as if there was no viewport */
viewport_x = 0;
viewport_y = 0;
viewport_width = 0;
viewport_height = 0;
}
if (drawables)
app/Makefile.am removed. 2001-06-26 Michael Natterer <mitch@gimp.org> * app/Makefile.am * app/color_transfer.[ch]: removed. * app/tools/Makefile.am * app/tools/gimpcolorbalancetool-transfer.[ch]: added. * app/tools/gimpcolorbalancetool.c: changed accordingly. * app/base/Makefile.am * app/base/tile-manager-crop.[ch]: formerly known as crop_buffer(). * app/tools/gimptexttool.c: changed accordingly. * app/context_manager.[ch]: added the global clipboard and the named buffer list here. * app/app_procs.c: don't call color_transfer_init() and don't free the buffer stuff (done by the context manager now). * app/errorconsole.c: don't #include "gui/commands.h" * app/global_edit.[ch]: removed lots of stuff which is now done by gui/edit-commands.* or the new GimpBuffer object. The "paste named" dialog will go away and this file will be moved to core/ soon. * app/image_new.c: no need to declare the global_buffer extern any more. * app/qmask.c: don't #include "global_edit.h" * app/core/Makefile.am * app/core/core-types.h * app/core/gimpbuffer.[ch]: new object (aka named buffer) * app/core/gimpcontext.[ch]: added a GimpBuffer attribute. * app/core/gimpimage.[ch]: one s/int/gboolean/. * app/core/gimppattern.c: hmm... * app/gui/commands.[ch]: split up in small files: * app/gui/Makefile.am * app/gui/edit-commands.[ch] * app/gui/file-commands.[ch] * app/gui/image-commands.[ch] * app/gui/select-commands.[ch] * app/gui/view-commands.[ch]: new files. * app/gui/dialogs-constructors.[ch] * app/gui/dialogs.c: added the named buffer list & grid. * app/gui/file-new-dialog.[ch] * app/gui/menus.c * app/gui/palette-editor.c * app/gui/test-commands.c: changed accordingly. * app/pdb/edit_cmds.c * tools/pdbgen/pdb/edit.pdb: changed for the global_edit stuff. * app/widgets/Makefile.am * app/widgets/gimpbufferpreview.[ch] * app/widgets/gimpbufferview.[ch] * app/widgets/gimpcontainereditor.[ch]: new widgets. * app/widgets/gimpcontainerview-utils.c * app/widgets/gimpdatafactoryview.[ch] * app/widgets/gimpdnd.[ch] * app/widgets/gimpdrawablepreview.c * app/widgets/gimplayerlistview.c * app/widgets/gimppreview.c * app/widgets/widgets-types.h: changed accordingly for the new GimpBuffer object and it's views, misc. cleanups. * pixmaps/Makefile.am * pixmaps/paste-as-new.xpm * pixmaps/paste-into.xpm * pixmaps/paste.xpm: new pixmaps (they all look the same... Tigert? ;-) * po/POTFILES.in: added the new files.
2001-06-26 12:09:43 +00:00
{
/* if pasting while 1 or more drawables are selected */
gboolean empty_target = TRUE;
gint target_x;
gint target_y;
gint target_width;
gint target_height;
gint paste_x, paste_y;
gint paste_width;
gint paste_height;
gboolean have_mask;
have_mask = ! gimp_channel_is_empty (gimp_image_get_mask (image));
for (iter = drawables; iter; iter = iter->next)
{
GimpContainer *children;
children = gimp_viewable_get_children (iter->data);
if (! children || gimp_container_get_n_children (children) > 0)
{
empty_target = FALSE;
break;
}
}
if (empty_target)
{
/* treat empty layer groups as image-sized, use the selection
* as target
*/
gimp_item_bounds (GIMP_ITEM (gimp_image_get_mask (image)),
&target_x, &target_y,
&target_width, &target_height);
}
else
{
gint x1 = G_MAXINT;
gint y1 = G_MAXINT;
gint x2 = G_MININT;
gint y2 = G_MININT;
for (iter = drawables; iter; iter = iter->next)
{
gint layer_off_x;
gint layer_off_y;
gint mask_off_x;
gint mask_off_y;
gint mask_width;
gint mask_height;
gimp_item_get_offset (GIMP_ITEM (iter->data), &layer_off_x, &layer_off_y);
/* This is the bounds relatively to the drawable. */
gimp_item_mask_intersect (GIMP_ITEM (iter->data),
&mask_off_x, &mask_off_y,
&mask_width, &mask_height);
/* The bounds relatively to the image. */
x1 = MIN (layer_off_x + mask_off_x, x1);
y1 = MIN (layer_off_y + mask_off_y, y1);
x2 = MAX (layer_off_x + mask_off_x + mask_width, x2);
y2 = MAX (layer_off_y + mask_off_y + mask_height, y2);
}
target_x = x1;
target_y = y1;
target_width = x2 - x1;
target_height = y2 - y1;
}
Fixed bug #78732 (don't paste off screen): 2004-01-15 Michael Natterer <mitch@gimp.org> Fixed bug #78732 (don't paste off screen): * app/display/gimpdisplayshell-transform.[ch]: added new function gimp_display_shell_untransform_viewport() which returns the visible rectangle of the image in image coordinates. * app/core/gimp-edit.[ch] (gimp_edit_paste): added viewport parameters and changed positioning of the pasted layer as follows: - if there is a selection, center on the selection (just as before). - if there is no viewport, center on the active drawable. - if the viewport intersects with the active drawable, center on the intersection. - if the viewport does *not* intersect with the active drawable, center on the active drawable (off-screen, but better than pasting something that will be invisible due to floating selection clipping). - if there is no active drawable, center on the viewport. - if there is no active drawable and no viewport, center on the image. * app/widgets/gimpbufferview.c (gimp_buffer_view_paste_clicked) (gimp_buffer_view_paste_into_clicked) * app/display/gimpdisplayshell-dnd.c (gimp_display_shell_drop_buffer) * app/gui/edit-commands.c (edit_paste_cmd_callback) (edit_paste_into_cmd_callback): ask the shell for the viewport and pass it to gimp_edit_paste(). * app/display/gimpdisplayshell-dnd.c (gimp_display_shell_drop_drawable): center the created layer on the viewport. * app/tools/gimpmovetool.c (gimp_move_tool_button_release): use gimp_display_shell_untransform_viewport() (its code was taken from here). * tools/pdbgen/pdb/edit.pdb: pass "no viewport" to gimp_edit_paste(). * app/pdb/edit_cmds.c: regenerated.
2004-01-15 14:36:43 +00:00
if (! have_mask && /* if we have no mask */
viewport_width > 0 && /* and we have a viewport */
Fixed bug #78732 (don't paste off screen): 2004-01-15 Michael Natterer <mitch@gimp.org> Fixed bug #78732 (don't paste off screen): * app/display/gimpdisplayshell-transform.[ch]: added new function gimp_display_shell_untransform_viewport() which returns the visible rectangle of the image in image coordinates. * app/core/gimp-edit.[ch] (gimp_edit_paste): added viewport parameters and changed positioning of the pasted layer as follows: - if there is a selection, center on the selection (just as before). - if there is no viewport, center on the active drawable. - if the viewport intersects with the active drawable, center on the intersection. - if the viewport does *not* intersect with the active drawable, center on the active drawable (off-screen, but better than pasting something that will be invisible due to floating selection clipping). - if there is no active drawable, center on the viewport. - if there is no active drawable and no viewport, center on the image. * app/widgets/gimpbufferview.c (gimp_buffer_view_paste_clicked) (gimp_buffer_view_paste_into_clicked) * app/display/gimpdisplayshell-dnd.c (gimp_display_shell_drop_buffer) * app/gui/edit-commands.c (edit_paste_cmd_callback) (edit_paste_into_cmd_callback): ask the shell for the viewport and pass it to gimp_edit_paste(). * app/display/gimpdisplayshell-dnd.c (gimp_display_shell_drop_drawable): center the created layer on the viewport. * app/tools/gimpmovetool.c (gimp_move_tool_button_release): use gimp_display_shell_untransform_viewport() (its code was taken from here). * tools/pdbgen/pdb/edit.pdb: pass "no viewport" to gimp_edit_paste(). * app/pdb/edit_cmds.c: regenerated.
2004-01-15 14:36:43 +00:00
viewport_height > 0 &&
(source_width < target_width || /* and the paste is smaller than the target */
source_height < target_height) &&
/* and the viewport intersects with the target */
Fixed bug #78732 (don't paste off screen): 2004-01-15 Michael Natterer <mitch@gimp.org> Fixed bug #78732 (don't paste off screen): * app/display/gimpdisplayshell-transform.[ch]: added new function gimp_display_shell_untransform_viewport() which returns the visible rectangle of the image in image coordinates. * app/core/gimp-edit.[ch] (gimp_edit_paste): added viewport parameters and changed positioning of the pasted layer as follows: - if there is a selection, center on the selection (just as before). - if there is no viewport, center on the active drawable. - if the viewport intersects with the active drawable, center on the intersection. - if the viewport does *not* intersect with the active drawable, center on the active drawable (off-screen, but better than pasting something that will be invisible due to floating selection clipping). - if there is no active drawable, center on the viewport. - if there is no active drawable and no viewport, center on the image. * app/widgets/gimpbufferview.c (gimp_buffer_view_paste_clicked) (gimp_buffer_view_paste_into_clicked) * app/display/gimpdisplayshell-dnd.c (gimp_display_shell_drop_buffer) * app/gui/edit-commands.c (edit_paste_cmd_callback) (edit_paste_into_cmd_callback): ask the shell for the viewport and pass it to gimp_edit_paste(). * app/display/gimpdisplayshell-dnd.c (gimp_display_shell_drop_drawable): center the created layer on the viewport. * app/tools/gimpmovetool.c (gimp_move_tool_button_release): use gimp_display_shell_untransform_viewport() (its code was taken from here). * tools/pdbgen/pdb/edit.pdb: pass "no viewport" to gimp_edit_paste(). * app/pdb/edit_cmds.c: regenerated.
2004-01-15 14:36:43 +00:00
gimp_rectangle_intersect (viewport_x, viewport_y,
viewport_width, viewport_height,
target_x, target_x,
target_width, target_height,
Fixed bug #78732 (don't paste off screen): 2004-01-15 Michael Natterer <mitch@gimp.org> Fixed bug #78732 (don't paste off screen): * app/display/gimpdisplayshell-transform.[ch]: added new function gimp_display_shell_untransform_viewport() which returns the visible rectangle of the image in image coordinates. * app/core/gimp-edit.[ch] (gimp_edit_paste): added viewport parameters and changed positioning of the pasted layer as follows: - if there is a selection, center on the selection (just as before). - if there is no viewport, center on the active drawable. - if the viewport intersects with the active drawable, center on the intersection. - if the viewport does *not* intersect with the active drawable, center on the active drawable (off-screen, but better than pasting something that will be invisible due to floating selection clipping). - if there is no active drawable, center on the viewport. - if there is no active drawable and no viewport, center on the image. * app/widgets/gimpbufferview.c (gimp_buffer_view_paste_clicked) (gimp_buffer_view_paste_into_clicked) * app/display/gimpdisplayshell-dnd.c (gimp_display_shell_drop_buffer) * app/gui/edit-commands.c (edit_paste_cmd_callback) (edit_paste_into_cmd_callback): ask the shell for the viewport and pass it to gimp_edit_paste(). * app/display/gimpdisplayshell-dnd.c (gimp_display_shell_drop_drawable): center the created layer on the viewport. * app/tools/gimpmovetool.c (gimp_move_tool_button_release): use gimp_display_shell_untransform_viewport() (its code was taken from here). * tools/pdbgen/pdb/edit.pdb: pass "no viewport" to gimp_edit_paste(). * app/pdb/edit_cmds.c: regenerated.
2004-01-15 14:36:43 +00:00
&paste_x, &paste_y,
&paste_width, &paste_height))
{
/* center on the viewport */
*offset_x = viewport_x + (viewport_width - source_width) / 2;
*offset_y = viewport_y + (viewport_height- source_height) / 2;
Fixed bug #78732 (don't paste off screen): 2004-01-15 Michael Natterer <mitch@gimp.org> Fixed bug #78732 (don't paste off screen): * app/display/gimpdisplayshell-transform.[ch]: added new function gimp_display_shell_untransform_viewport() which returns the visible rectangle of the image in image coordinates. * app/core/gimp-edit.[ch] (gimp_edit_paste): added viewport parameters and changed positioning of the pasted layer as follows: - if there is a selection, center on the selection (just as before). - if there is no viewport, center on the active drawable. - if the viewport intersects with the active drawable, center on the intersection. - if the viewport does *not* intersect with the active drawable, center on the active drawable (off-screen, but better than pasting something that will be invisible due to floating selection clipping). - if there is no active drawable, center on the viewport. - if there is no active drawable and no viewport, center on the image. * app/widgets/gimpbufferview.c (gimp_buffer_view_paste_clicked) (gimp_buffer_view_paste_into_clicked) * app/display/gimpdisplayshell-dnd.c (gimp_display_shell_drop_buffer) * app/gui/edit-commands.c (edit_paste_cmd_callback) (edit_paste_into_cmd_callback): ask the shell for the viewport and pass it to gimp_edit_paste(). * app/display/gimpdisplayshell-dnd.c (gimp_display_shell_drop_drawable): center the created layer on the viewport. * app/tools/gimpmovetool.c (gimp_move_tool_button_release): use gimp_display_shell_untransform_viewport() (its code was taken from here). * tools/pdbgen/pdb/edit.pdb: pass "no viewport" to gimp_edit_paste(). * app/pdb/edit_cmds.c: regenerated.
2004-01-15 14:36:43 +00:00
}
else
{
/* otherwise center on the target */
*offset_x = target_x + (target_width - source_width) / 2;
*offset_y = target_y + (target_height - source_height) / 2;
/* and keep it that way */
clamp_to_image = FALSE;
Fixed bug #78732 (don't paste off screen): 2004-01-15 Michael Natterer <mitch@gimp.org> Fixed bug #78732 (don't paste off screen): * app/display/gimpdisplayshell-transform.[ch]: added new function gimp_display_shell_untransform_viewport() which returns the visible rectangle of the image in image coordinates. * app/core/gimp-edit.[ch] (gimp_edit_paste): added viewport parameters and changed positioning of the pasted layer as follows: - if there is a selection, center on the selection (just as before). - if there is no viewport, center on the active drawable. - if the viewport intersects with the active drawable, center on the intersection. - if the viewport does *not* intersect with the active drawable, center on the active drawable (off-screen, but better than pasting something that will be invisible due to floating selection clipping). - if there is no active drawable, center on the viewport. - if there is no active drawable and no viewport, center on the image. * app/widgets/gimpbufferview.c (gimp_buffer_view_paste_clicked) (gimp_buffer_view_paste_into_clicked) * app/display/gimpdisplayshell-dnd.c (gimp_display_shell_drop_buffer) * app/gui/edit-commands.c (edit_paste_cmd_callback) (edit_paste_into_cmd_callback): ask the shell for the viewport and pass it to gimp_edit_paste(). * app/display/gimpdisplayshell-dnd.c (gimp_display_shell_drop_drawable): center the created layer on the viewport. * app/tools/gimpmovetool.c (gimp_move_tool_button_release): use gimp_display_shell_untransform_viewport() (its code was taken from here). * tools/pdbgen/pdb/edit.pdb: pass "no viewport" to gimp_edit_paste(). * app/pdb/edit_cmds.c: regenerated.
2004-01-15 14:36:43 +00:00
}
}
else if (viewport_width > 0 && /* if we have a viewport */
viewport_height > 0 &&
(source_width < image_width || /* and the paste is */
source_height < image_height)) /* smaller than the image */
Fixed bug #78732 (don't paste off screen): 2004-01-15 Michael Natterer <mitch@gimp.org> Fixed bug #78732 (don't paste off screen): * app/display/gimpdisplayshell-transform.[ch]: added new function gimp_display_shell_untransform_viewport() which returns the visible rectangle of the image in image coordinates. * app/core/gimp-edit.[ch] (gimp_edit_paste): added viewport parameters and changed positioning of the pasted layer as follows: - if there is a selection, center on the selection (just as before). - if there is no viewport, center on the active drawable. - if the viewport intersects with the active drawable, center on the intersection. - if the viewport does *not* intersect with the active drawable, center on the active drawable (off-screen, but better than pasting something that will be invisible due to floating selection clipping). - if there is no active drawable, center on the viewport. - if there is no active drawable and no viewport, center on the image. * app/widgets/gimpbufferview.c (gimp_buffer_view_paste_clicked) (gimp_buffer_view_paste_into_clicked) * app/display/gimpdisplayshell-dnd.c (gimp_display_shell_drop_buffer) * app/gui/edit-commands.c (edit_paste_cmd_callback) (edit_paste_into_cmd_callback): ask the shell for the viewport and pass it to gimp_edit_paste(). * app/display/gimpdisplayshell-dnd.c (gimp_display_shell_drop_drawable): center the created layer on the viewport. * app/tools/gimpmovetool.c (gimp_move_tool_button_release): use gimp_display_shell_untransform_viewport() (its code was taken from here). * tools/pdbgen/pdb/edit.pdb: pass "no viewport" to gimp_edit_paste(). * app/pdb/edit_cmds.c: regenerated.
2004-01-15 14:36:43 +00:00
{
/* center on the viewport */
Fixed bug #78732 (don't paste off screen): 2004-01-15 Michael Natterer <mitch@gimp.org> Fixed bug #78732 (don't paste off screen): * app/display/gimpdisplayshell-transform.[ch]: added new function gimp_display_shell_untransform_viewport() which returns the visible rectangle of the image in image coordinates. * app/core/gimp-edit.[ch] (gimp_edit_paste): added viewport parameters and changed positioning of the pasted layer as follows: - if there is a selection, center on the selection (just as before). - if there is no viewport, center on the active drawable. - if the viewport intersects with the active drawable, center on the intersection. - if the viewport does *not* intersect with the active drawable, center on the active drawable (off-screen, but better than pasting something that will be invisible due to floating selection clipping). - if there is no active drawable, center on the viewport. - if there is no active drawable and no viewport, center on the image. * app/widgets/gimpbufferview.c (gimp_buffer_view_paste_clicked) (gimp_buffer_view_paste_into_clicked) * app/display/gimpdisplayshell-dnd.c (gimp_display_shell_drop_buffer) * app/gui/edit-commands.c (edit_paste_cmd_callback) (edit_paste_into_cmd_callback): ask the shell for the viewport and pass it to gimp_edit_paste(). * app/display/gimpdisplayshell-dnd.c (gimp_display_shell_drop_drawable): center the created layer on the viewport. * app/tools/gimpmovetool.c (gimp_move_tool_button_release): use gimp_display_shell_untransform_viewport() (its code was taken from here). * tools/pdbgen/pdb/edit.pdb: pass "no viewport" to gimp_edit_paste(). * app/pdb/edit_cmds.c: regenerated.
2004-01-15 14:36:43 +00:00
*offset_x = viewport_x + (viewport_width - source_width) / 2;
*offset_y = viewport_y + (viewport_height - source_height) / 2;
app/Makefile.am removed. 2001-06-26 Michael Natterer <mitch@gimp.org> * app/Makefile.am * app/color_transfer.[ch]: removed. * app/tools/Makefile.am * app/tools/gimpcolorbalancetool-transfer.[ch]: added. * app/tools/gimpcolorbalancetool.c: changed accordingly. * app/base/Makefile.am * app/base/tile-manager-crop.[ch]: formerly known as crop_buffer(). * app/tools/gimptexttool.c: changed accordingly. * app/context_manager.[ch]: added the global clipboard and the named buffer list here. * app/app_procs.c: don't call color_transfer_init() and don't free the buffer stuff (done by the context manager now). * app/errorconsole.c: don't #include "gui/commands.h" * app/global_edit.[ch]: removed lots of stuff which is now done by gui/edit-commands.* or the new GimpBuffer object. The "paste named" dialog will go away and this file will be moved to core/ soon. * app/image_new.c: no need to declare the global_buffer extern any more. * app/qmask.c: don't #include "global_edit.h" * app/core/Makefile.am * app/core/core-types.h * app/core/gimpbuffer.[ch]: new object (aka named buffer) * app/core/gimpcontext.[ch]: added a GimpBuffer attribute. * app/core/gimpimage.[ch]: one s/int/gboolean/. * app/core/gimppattern.c: hmm... * app/gui/commands.[ch]: split up in small files: * app/gui/Makefile.am * app/gui/edit-commands.[ch] * app/gui/file-commands.[ch] * app/gui/image-commands.[ch] * app/gui/select-commands.[ch] * app/gui/view-commands.[ch]: new files. * app/gui/dialogs-constructors.[ch] * app/gui/dialogs.c: added the named buffer list & grid. * app/gui/file-new-dialog.[ch] * app/gui/menus.c * app/gui/palette-editor.c * app/gui/test-commands.c: changed accordingly. * app/pdb/edit_cmds.c * tools/pdbgen/pdb/edit.pdb: changed for the global_edit stuff. * app/widgets/Makefile.am * app/widgets/gimpbufferpreview.[ch] * app/widgets/gimpbufferview.[ch] * app/widgets/gimpcontainereditor.[ch]: new widgets. * app/widgets/gimpcontainerview-utils.c * app/widgets/gimpdatafactoryview.[ch] * app/widgets/gimpdnd.[ch] * app/widgets/gimpdrawablepreview.c * app/widgets/gimplayerlistview.c * app/widgets/gimppreview.c * app/widgets/widgets-types.h: changed accordingly for the new GimpBuffer object and it's views, misc. cleanups. * pixmaps/Makefile.am * pixmaps/paste-as-new.xpm * pixmaps/paste-into.xpm * pixmaps/paste.xpm: new pixmaps (they all look the same... Tigert? ;-) * po/POTFILES.in: added the new files.
2001-06-26 12:09:43 +00:00
}
else
{
/* otherwise center on the image */
1997-11-24 22:05:25 +00:00
*offset_x = (image_width - source_width) / 2;
*offset_y = (image_height - source_height) / 2;
/* and keep it that way */
clamp_to_image = FALSE;
}
if (clamp_to_image)
{
/* Ensure that the pasted layer is always within the image, if it
* fits and aligned at top left if it doesn't. (See bug #142944).
*/
*offset_x = MIN (*offset_x, image_width - source_width);
*offset_y = MIN (*offset_y, image_height - source_height);
*offset_x = MAX (*offset_x, 0);
*offset_y = MAX (*offset_y, 0);
}
}
static GList *
gimp_edit_paste_paste (GimpImage *image,
GList *drawables,
GList *layers,
GimpPasteType paste_type,
gboolean use_offset,
gint layers_bbox_x,
gint layers_bbox_y,
gint offset_x,
gint offset_y)
{
GList *iter;
g_return_val_if_fail (paste_type > GIMP_PASTE_TYPE_FLOATING_INTO_IN_PLACE ||
g_list_length (drawables) == 1, NULL);
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_EDIT_PASTE,
C_("undo-type", "Paste"));
for (iter = layers; iter; iter = iter->next)
{
/* Layers for in-place paste are already translated. */
if (use_offset)
gimp_item_translate (GIMP_ITEM (iter->data),
offset_x - layers_bbox_x,
offset_y - layers_bbox_y, FALSE);
1997-11-24 22:05:25 +00:00
switch (paste_type)
{
case GIMP_PASTE_TYPE_FLOATING:
case GIMP_PASTE_TYPE_FLOATING_IN_PLACE:
/* if there is a selection mask clear it - this might not
* always be desired, but in general, it seems like the correct
* behavior
*/
if (! gimp_channel_is_empty (gimp_image_get_mask (image)))
gimp_channel_clear (gimp_image_get_mask (image), NULL, TRUE);
/* fall thru */
case GIMP_PASTE_TYPE_FLOATING_INTO:
case GIMP_PASTE_TYPE_FLOATING_INTO_IN_PLACE:
floating_sel_attach (iter->data, drawables->data);
break;
case GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING:
case GIMP_PASTE_TYPE_NEW_LAYER_OR_FLOATING_IN_PLACE:
if (g_list_length (drawables) == 1 && GIMP_IS_LAYER_MASK (drawables->data))
{
if (! gimp_channel_is_empty (gimp_image_get_mask (image)))
gimp_channel_clear (gimp_image_get_mask (image), NULL, TRUE);
floating_sel_attach (iter->data, drawables->data);
break;
}
/* fall thru if not a layer mask */
case GIMP_PASTE_TYPE_NEW_LAYER:
case GIMP_PASTE_TYPE_NEW_LAYER_IN_PLACE:
{
GimpLayer *parent = NULL;
gint position = 0;
/* always add on top of a passed layer, where we would attach
* a floating selection
*/
if (g_list_length (drawables) > 0 && GIMP_IS_LAYER (drawables->data))
{
GimpDrawable *top_drawable;
top_drawable = gimp_edit_paste_get_top_item (drawables);
parent = gimp_layer_get_parent (GIMP_LAYER (top_drawable));
position = gimp_item_get_index (GIMP_ITEM (top_drawable));
}
gimp_image_add_layer (image, iter->data, parent, position, TRUE);
}
break;
case GIMP_PASTE_TYPE_NEW_MERGED_LAYER_OR_FLOATING:
case GIMP_PASTE_TYPE_NEW_MERGED_LAYER_OR_FLOATING_IN_PLACE:
g_return_val_if_reached (NULL);
}
}
gimp_image_undo_group_end (image);
app/Makefile.am removed. 2001-06-26 Michael Natterer <mitch@gimp.org> * app/Makefile.am * app/color_transfer.[ch]: removed. * app/tools/Makefile.am * app/tools/gimpcolorbalancetool-transfer.[ch]: added. * app/tools/gimpcolorbalancetool.c: changed accordingly. * app/base/Makefile.am * app/base/tile-manager-crop.[ch]: formerly known as crop_buffer(). * app/tools/gimptexttool.c: changed accordingly. * app/context_manager.[ch]: added the global clipboard and the named buffer list here. * app/app_procs.c: don't call color_transfer_init() and don't free the buffer stuff (done by the context manager now). * app/errorconsole.c: don't #include "gui/commands.h" * app/global_edit.[ch]: removed lots of stuff which is now done by gui/edit-commands.* or the new GimpBuffer object. The "paste named" dialog will go away and this file will be moved to core/ soon. * app/image_new.c: no need to declare the global_buffer extern any more. * app/qmask.c: don't #include "global_edit.h" * app/core/Makefile.am * app/core/core-types.h * app/core/gimpbuffer.[ch]: new object (aka named buffer) * app/core/gimpcontext.[ch]: added a GimpBuffer attribute. * app/core/gimpimage.[ch]: one s/int/gboolean/. * app/core/gimppattern.c: hmm... * app/gui/commands.[ch]: split up in small files: * app/gui/Makefile.am * app/gui/edit-commands.[ch] * app/gui/file-commands.[ch] * app/gui/image-commands.[ch] * app/gui/select-commands.[ch] * app/gui/view-commands.[ch]: new files. * app/gui/dialogs-constructors.[ch] * app/gui/dialogs.c: added the named buffer list & grid. * app/gui/file-new-dialog.[ch] * app/gui/menus.c * app/gui/palette-editor.c * app/gui/test-commands.c: changed accordingly. * app/pdb/edit_cmds.c * tools/pdbgen/pdb/edit.pdb: changed for the global_edit stuff. * app/widgets/Makefile.am * app/widgets/gimpbufferpreview.[ch] * app/widgets/gimpbufferview.[ch] * app/widgets/gimpcontainereditor.[ch]: new widgets. * app/widgets/gimpcontainerview-utils.c * app/widgets/gimpdatafactoryview.[ch] * app/widgets/gimpdnd.[ch] * app/widgets/gimpdrawablepreview.c * app/widgets/gimplayerlistview.c * app/widgets/gimppreview.c * app/widgets/widgets-types.h: changed accordingly for the new GimpBuffer object and it's views, misc. cleanups. * pixmaps/Makefile.am * pixmaps/paste-as-new.xpm * pixmaps/paste-into.xpm * pixmaps/paste.xpm: new pixmaps (they all look the same... Tigert? ;-) * po/POTFILES.in: added the new files.
2001-06-26 12:09:43 +00:00
return layers;
1997-11-24 22:05:25 +00:00
}
GList *
gimp_edit_paste (GimpImage *image,
GList *drawables,
GimpObject *paste,
GimpPasteType paste_type,
GimpContext *context,
gboolean merged,
gint viewport_x,
gint viewport_y,
gint viewport_width,
gint viewport_height)
{
GList *layers;
gboolean use_offset = FALSE;
gint layers_bbox_x = 0;
gint layers_bbox_y = 0;
gint offset_y = 0;
gint offset_x = 0;
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
g_return_val_if_fail (GIMP_IS_IMAGE (paste) || GIMP_IS_BUFFER (paste), NULL);
for (GList *iter = drawables; iter; iter = iter->next)
{
g_return_val_if_fail (GIMP_IS_DRAWABLE (iter->data), NULL);
g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (iter->data)), NULL);
}
if (merged && GIMP_IS_IMAGE (paste))
{
GimpImage *tmp_image;
tmp_image = gimp_image_duplicate (GIMP_IMAGE (paste));
gimp_container_remove (image->gimp->images, GIMP_OBJECT (tmp_image));
gimp_image_merge_visible_layers (tmp_image, context, GIMP_EXPAND_AS_NECESSARY,
FALSE, FALSE, NULL);
layers = g_list_copy (gimp_image_get_layer_iter (tmp_image));
/* The merge process should ensure that we get a single non-group and
* no-mask layer.
*/
g_return_val_if_fail (g_list_length (layers) == 1, NULL);
layers->data = gimp_item_convert (GIMP_ITEM (layers->data), image,
G_TYPE_FROM_INSTANCE (layers->data));
switch (paste_type)
{
case GIMP_PASTE_TYPE_FLOATING:
case GIMP_PASTE_TYPE_FLOATING_IN_PLACE:
case GIMP_PASTE_TYPE_FLOATING_INTO:
case GIMP_PASTE_TYPE_FLOATING_INTO_IN_PLACE:
if (gimp_drawable_get_format (GIMP_DRAWABLE (layers->data)) !=
gimp_drawable_get_format_with_alpha (GIMP_DRAWABLE (layers->data)))
{
gimp_drawable_convert_type (GIMP_DRAWABLE (layers->data), image,
gimp_drawable_get_base_type (layers->data),
gimp_drawable_get_precision (layers->data),
TRUE, NULL, NULL,
GEGL_DITHER_NONE, GEGL_DITHER_NONE,
FALSE, NULL);
}
break;
default:
break;
}
g_object_unref (tmp_image);
}
else
{
layers = gimp_edit_paste_get_layers (image, drawables, paste, &paste_type);
}
if (! layers)
return NULL;
if (gimp_edit_paste_is_in_place (paste_type))
{
if (GIMP_IS_BUFFER (paste))
{
GimpBuffer *buffer = GIMP_BUFFER (paste);
use_offset = TRUE;
offset_x = buffer->offset_x;
offset_y = buffer->offset_y;
}
}
else
{
use_offset = TRUE;
gimp_edit_paste_get_viewport_offset (image, drawables, layers,
viewport_x,
viewport_y,
viewport_width,
viewport_height,
&layers_bbox_x,
&layers_bbox_y,
&offset_x,
&offset_y);
}
return gimp_edit_paste_paste (image, drawables, layers, paste_type,
use_offset, layers_bbox_x, layers_bbox_y,
offset_x, offset_y);
}
GimpImage *
gimp_edit_paste_as_new_image (Gimp *gimp,
GimpObject *paste,
GimpContext *context)
{
GimpImage *image = NULL;
g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
g_return_val_if_fail (GIMP_IS_IMAGE (paste) || GIMP_IS_BUFFER (paste), NULL);
if (GIMP_IS_IMAGE (paste))
{
gint offset_x;
gint offset_y;
gint new_width;
gint new_height;
offset_x = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (paste),
"gimp-edit-new-image-x"));
offset_y = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (paste),
"gimp-edit-new-image-y"));
new_width = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (paste),
"gimp-edit-new-image-width"));
new_height = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (paste),
"gimp-edit-new-image-height"));
image = gimp_image_duplicate (GIMP_IMAGE (paste));
if (new_width > 0 && new_height > 0)
{
gimp_image_undo_disable (image);
gimp_image_resize (image, context,
new_width, new_height,
-offset_x, -offset_y,
NULL);
gimp_image_undo_enable (image);
}
}
else if (GIMP_IS_BUFFER (paste))
{
image = gimp_image_new_from_buffer (gimp, GIMP_BUFFER (paste));
}
return image;
}
const gchar *
gimp_edit_named_cut (GimpImage *image,
const gchar *name,
GList *drawables,
GimpContext *context,
GError **error)
{
GimpBuffer *buffer;
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
g_return_val_if_fail (name != NULL, NULL);
g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
buffer = gimp_edit_extract (image, drawables, context, TRUE, error);
if (buffer)
{
gimp_object_set_name (GIMP_OBJECT (buffer), name);
gimp_container_add (image->gimp->named_buffers, GIMP_OBJECT (buffer));
g_object_unref (buffer);
return gimp_object_get_name (buffer);
}
return NULL;
}
const gchar *
gimp_edit_named_copy (GimpImage *image,
const gchar *name,
GList *drawables,
GimpContext *context,
GError **error)
{
GimpBuffer *buffer;
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
g_return_val_if_fail (name != NULL, NULL);
g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
buffer = gimp_edit_extract (image, drawables, context, FALSE, error);
if (buffer)
{
gimp_object_set_name (GIMP_OBJECT (buffer), name);
gimp_container_add (image->gimp->named_buffers, GIMP_OBJECT (buffer));
g_object_unref (buffer);
return gimp_object_get_name (buffer);
}
return NULL;
}
const gchar *
gimp_edit_named_copy_visible (GimpImage *image,
const gchar *name,
GimpContext *context,
GError **error)
{
GimpBuffer *buffer;
GList *pickables;
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
g_return_val_if_fail (name != NULL, NULL);
g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
pickables = g_list_prepend (NULL, image);
buffer = gimp_edit_extract (image, pickables, context, FALSE, error);
g_list_free (pickables);
if (buffer)
{
gimp_object_set_name (GIMP_OBJECT (buffer), name);
gimp_container_add (image->gimp->named_buffers, GIMP_OBJECT (buffer));
g_object_unref (buffer);
return gimp_object_get_name (buffer);
}
return NULL;
}
/* private functions */
/**
* gimp_edit_extract:
* @image:
* @pickables:
* @context:
* @cut_pixels:
* @error:
*
* Extracts the selected part of @image from the list of @pickables.
* If @cut_pixels is %TRUE, and there is only one pickable input, and if
* this pickable is a #GimpDrawable, then the selected pixels will be
* effectively erased from the input pickable.
* Otherwise @cut_pixels has no additional effect.
* Note that all @pickables must belong to the same @image.
*
* Returns: a #GimpBuffer of the selected part of @image as if only the
* selected @pickables were present (composited according to their
* properties, unless there is only one pickable, in which case direct
* pixel information is used without composition).
*/
static GimpBuffer *
gimp_edit_extract (GimpImage *image,
GList *pickables,
GimpContext *context,
gboolean cut_pixels,
GError **error)
{
GeglBuffer *buffer;
gint offset_x;
gint offset_y;
g_return_val_if_fail (g_list_length (pickables) > 0, NULL);
if (g_list_length (pickables) > 1 ||
! GIMP_IS_DRAWABLE (pickables->data))
cut_pixels = FALSE;
if (cut_pixels)
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_EDIT_CUT,
C_("undo-type", "Cut"));
/* Cut/copy the mask portion from the image */
buffer = gimp_selection_extract (GIMP_SELECTION (gimp_image_get_mask (image)),
pickables, context,
cut_pixels, FALSE, FALSE,
&offset_x, &offset_y, error);
if (cut_pixels)
gimp_image_undo_group_end (image);
if (buffer)
{
GimpBuffer *gimp_buffer;
gdouble res_x;
gdouble res_y;
gimp_buffer = gimp_buffer_new (buffer, _("Global Buffer"),
offset_x, offset_y, FALSE);
g_object_unref (buffer);
gimp_image_get_resolution (image, &res_x, &res_y);
gimp_buffer_set_resolution (gimp_buffer, res_x, res_y);
gimp_buffer_set_unit (gimp_buffer, gimp_image_get_unit (image));
if (GIMP_IS_COLOR_MANAGED (pickables->data))
{
GimpColorProfile *profile =
gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (pickables->data));
if (profile)
gimp_buffer_set_color_profile (gimp_buffer, profile);
}
return gimp_buffer;
}
return NULL;
}
/* Return the visually top item. */
static GimpDrawable *
gimp_edit_paste_get_top_item (GList *drawables)
{
GList *iter;
GimpDrawable *top = NULL;
GList *top_path = NULL;
for (iter = drawables; iter; iter = iter->next)
{
GList *path = gimp_item_get_path (iter->data);
if (top == NULL)
{
top = iter->data;
top_path = path;
path = NULL;
}
else
{
GList *p_iter;
GList *tp_iter;
for (p_iter = path, tp_iter = top_path; p_iter || tp_iter; p_iter = p_iter->next, tp_iter = tp_iter->next)
{
if (tp_iter == NULL)
{
break;
}
else if (p_iter == NULL ||
GPOINTER_TO_UINT (p_iter->data) < GPOINTER_TO_UINT (tp_iter->data))
{
g_list_free (top_path);
top_path = path;
path = NULL;
top = iter->data;
break;
}
else if (GPOINTER_TO_UINT (p_iter->data) > GPOINTER_TO_UINT (tp_iter->data))
{
break;
}
}
}
g_list_free (path);
}
g_list_free (top_path);
return top;
}