[gimp] Bug 674160 - Redesign of "Lock panel"



commit d4933b30526903624340ed98f97ed094b3a80b4d
Author: Michael Natterer <mitch gimp org>
Date:   Fri Nov 9 11:17:25 2012 +0100

    Bug 674160 - Redesign of "Lock panel"
    
    Apply and heavily modify patch from remyDev which adds "lock position"
    to GimpItem, similar to "lock content". Lock position disables all
    sorts of translation and transform, from the GUI and the PDB.
    
    Cleaned up some aspects of the lock content code as well because a
    second instance of similar code always shows what went wrong the first
    time.

 app/actions/drawable-actions.c          |   66 ++++++++++------
 app/actions/drawable-commands.c         |   30 +++++++
 app/actions/drawable-commands.h         |    2 +
 app/actions/layers-actions.c            |   22 +++---
 app/actions/vectors-actions.c           |   61 ++++++++------
 app/actions/vectors-commands.c          |   27 ++++++
 app/actions/vectors-commands.h          |    2 +
 app/core/core-enums.c                   |    4 +
 app/core/core-enums.h                   |    2 +
 app/core/gimpgrouplayer.c               |   21 +++++
 app/core/gimpimage-undo-push.c          |   32 ++++++++
 app/core/gimpimage-undo-push.h          |    6 ++
 app/core/gimpitem-linked.c              |   33 ++++++++
 app/core/gimpitem-linked.h              |   48 ++++++------
 app/core/gimpitem.c                     |  133 ++++++++++++++++++++++++++-----
 app/core/gimpitem.h                     |   17 +++-
 app/core/gimpitempropundo.c             |   28 +++++++
 app/core/gimpitempropundo.h             |    6 +-
 app/core/gimplayermask.c                |   64 +++++++++------
 app/pdb/drawable-transform-cmds.c       |   54 +++++++++----
 app/pdb/gimppdb-utils.c                 |   10 +++
 app/pdb/internal-procs.c                |    2 +-
 app/pdb/item-cmds.c                     |  114 ++++++++++++++++++++++++++
 app/pdb/item-transform-cmds.c           |   27 ++++--
 app/pdb/layer-cmds.c                    |   62 +++++++++------
 app/pdb/pdb-types.h                     |    3 +-
 app/pdb/transform-tools-cmds.c          |   18 +++--
 app/pdb/vectors-cmds.c                  |   22 ++++--
 app/tools/gimpmovetool.c                |  130 +++++++++++++++++++++++--------
 app/tools/gimptransformtool.c           |   53 +++++++++---
 app/tools/gimpvectortool.c              |    3 +-
 app/widgets/gimpchanneltreeview.c       |   24 +++---
 app/widgets/gimpdrawabletreeview.c      |    6 +-
 app/widgets/gimphelp-ids.h              |    7 ++-
 app/widgets/gimpitemtreeview.c          |  121 +++++++++++++++++++++++++++-
 app/widgets/gimpitemtreeview.h          |    5 +
 app/widgets/gimplayertreeview.c         |   29 ++++---
 app/widgets/gimpvectorstreeview.c       |   31 ++++---
 app/xcf/xcf-load.c                      |   22 +++++
 app/xcf/xcf-private.h                   |    3 +-
 app/xcf/xcf-save.c                      |   17 ++++
 libgimp/gimp.def                        |    2 +
 libgimp/gimpitem_pdb.c                  |   66 +++++++++++++++
 libgimp/gimpitem_pdb.h                  |    3 +
 tools/pdbgen/pdb/drawable_transform.pdb |   19 +++--
 tools/pdbgen/pdb/item.pdb               |   53 ++++++++++++
 tools/pdbgen/pdb/item_transform.pdb     |   12 ++-
 tools/pdbgen/pdb/layer.pdb              |   62 +++++++++------
 tools/pdbgen/pdb/transform_tools.pdb    |   18 +++--
 tools/pdbgen/pdb/vectors.pdb            |   22 ++++--
 50 files changed, 1291 insertions(+), 333 deletions(-)
---
diff --git a/app/actions/drawable-actions.c b/app/actions/drawable-actions.c
index 0871c1a..4b63374 100644
--- a/app/actions/drawable-actions.c
+++ b/app/actions/drawable-actions.c
@@ -95,7 +95,15 @@ static const GimpToggleActionEntry drawable_toggle_actions[] =
         "Keep the pixels on this drawable from being modified"),
     G_CALLBACK (drawable_lock_content_cmd_callback),
     FALSE,
-    NULL, /* GIMP_HELP_LAYER_LOCK_PIXELS */ }
+    GIMP_HELP_LAYER_LOCK_PIXELS },
+
+  { "drawable-lock-position", GIMP_STOCK_TOOL_MOVE,
+    NC_("drawable-action", "L_ock position of channel"), NULL,
+    NC_("drawable-action",
+        "Keep the position on this drawable from being modified"),
+    G_CALLBACK (drawable_lock_position_cmd_callback),
+    FALSE,
+    GIMP_HELP_LAYER_LOCK_POSITION },
 };
 
 static const GimpEnumActionEntry drawable_flip_actions[] =
@@ -171,14 +179,17 @@ drawable_actions_update (GimpActionGroup *group,
                          gpointer         data)
 {
   GimpImage    *image;
-  GimpDrawable *drawable   = NULL;
-  gboolean      is_rgb     = FALSE;
-  gboolean      visible    = FALSE;
-  gboolean      linked     = FALSE;
-  gboolean      locked     = FALSE;
-  gboolean      can_lock   = FALSE;
-  gboolean      writable   = FALSE;
-  gboolean      children   = FALSE;
+  GimpDrawable *drawable     = NULL;
+  gboolean      is_rgb       = FALSE;
+  gboolean      visible      = FALSE;
+  gboolean      linked       = FALSE;
+  gboolean      locked       = FALSE;
+  gboolean      can_lock     = FALSE;
+  gboolean      locked_pos   = FALSE;
+  gboolean      can_lock_pos = FALSE;
+  gboolean      writable     = FALSE;
+  gboolean      movable      = FALSE;
+  gboolean      children     = FALSE;
 
   image = action_data_get_image (data);
 
@@ -197,11 +208,14 @@ drawable_actions_update (GimpActionGroup *group,
           else
             item = GIMP_ITEM (drawable);
 
-          visible  = gimp_item_get_visible (item);
-          linked   = gimp_item_get_linked (item);
-          locked   = gimp_item_get_lock_content (item);
-          can_lock = gimp_item_can_lock_content (item);
-          writable = ! gimp_item_is_content_locked (item);
+          visible       = gimp_item_get_visible (item);
+          linked        = gimp_item_get_linked (item);
+          locked        = gimp_item_get_lock_content (item);
+          can_lock      = gimp_item_can_lock_content (item);
+          writable      = ! gimp_item_is_content_locked (item);
+          locked_pos    = gimp_item_get_lock_position (item);
+          can_lock_pos  = gimp_item_can_lock_position (item);
+          movable       = ! gimp_item_is_position_locked (item);
 
           if (gimp_viewable_get_children (GIMP_VIEWABLE (drawable)))
             children = TRUE;
@@ -219,20 +233,22 @@ drawable_actions_update (GimpActionGroup *group,
   SET_SENSITIVE ("drawable-levels-stretch", writable && !children && is_rgb);
   SET_SENSITIVE ("drawable-offset",         writable && !children);
 
-  SET_SENSITIVE ("drawable-visible",      drawable);
-  SET_SENSITIVE ("drawable-linked",       drawable);
-  SET_SENSITIVE ("drawable-lock-content", can_lock);
+  SET_SENSITIVE ("drawable-visible",       drawable);
+  SET_SENSITIVE ("drawable-linked",        drawable);
+  SET_SENSITIVE ("drawable-lock-content",  can_lock);
+  SET_SENSITIVE ("drawable-lock-position", can_lock_pos);
 
-  SET_ACTIVE ("drawable-visible",      visible);
-  SET_ACTIVE ("drawable-linked",       linked);
-  SET_ACTIVE ("drawable-lock-content", locked);
+  SET_ACTIVE ("drawable-visible",       visible);
+  SET_ACTIVE ("drawable-linked",        linked);
+  SET_ACTIVE ("drawable-lock-content",  locked);
+  SET_ACTIVE ("drawable-lock-position", locked_pos);
 
-  SET_SENSITIVE ("drawable-flip-horizontal", writable);
-  SET_SENSITIVE ("drawable-flip-vertical",   writable);
+  SET_SENSITIVE ("drawable-flip-horizontal", writable && movable);
+  SET_SENSITIVE ("drawable-flip-vertical",   writable && movable);
 
-  SET_SENSITIVE ("drawable-rotate-90",  writable);
-  SET_SENSITIVE ("drawable-rotate-180", writable);
-  SET_SENSITIVE ("drawable-rotate-270", writable);
+  SET_SENSITIVE ("drawable-rotate-90",  writable && movable);
+  SET_SENSITIVE ("drawable-rotate-180", writable && movable);
+  SET_SENSITIVE ("drawable-rotate-270", writable && movable);
 
 #undef SET_SENSITIVE
 #undef SET_ACTIVE
diff --git a/app/actions/drawable-commands.c b/app/actions/drawable-commands.c
index 5ef5b9c..e697a86 100644
--- a/app/actions/drawable-commands.c
+++ b/app/actions/drawable-commands.c
@@ -227,6 +227,36 @@ drawable_lock_content_cmd_callback (GtkAction *action,
     }
 }
 
+void
+drawable_lock_position_cmd_callback (GtkAction *action,
+                                    gpointer   data)
+{
+  GimpImage    *image;
+  GimpDrawable *drawable;
+  gboolean      locked;
+  return_if_no_drawable (image, drawable, data);
+
+  locked = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
+
+  if (GIMP_IS_LAYER_MASK (drawable))
+    drawable =
+      GIMP_DRAWABLE (gimp_layer_mask_get_layer (GIMP_LAYER_MASK (drawable)));
+
+  if (locked != gimp_item_get_lock_position (GIMP_ITEM (drawable)))
+    {
+      GimpUndo *undo;
+      gboolean  push_undo = TRUE;
+
+      undo = gimp_image_undo_can_compress (image, GIMP_TYPE_ITEM_UNDO,
+                                           GIMP_UNDO_ITEM_LOCK_POSITION);
+
+      if (undo && GIMP_ITEM_UNDO (undo)->item == GIMP_ITEM (drawable))
+        push_undo = FALSE;
+
+      gimp_item_set_lock_position (GIMP_ITEM (drawable), locked, push_undo);
+      gimp_image_flush (image);
+    }
+}
 
 void
 drawable_flip_cmd_callback (GtkAction *action,
diff --git a/app/actions/drawable-commands.h b/app/actions/drawable-commands.h
index 188347e..e206e0e 100644
--- a/app/actions/drawable-commands.h
+++ b/app/actions/drawable-commands.h
@@ -36,6 +36,8 @@ void   drawable_visible_cmd_callback        (GtkAction *action,
                                              gpointer   data);
 void   drawable_lock_content_cmd_callback   (GtkAction *action,
                                              gpointer   data);
+void   drawable_lock_position_cmd_callback  (GtkAction *action,
+                                             gpointer   data);
 
 void   drawable_flip_cmd_callback           (GtkAction *action,
                                              gint       value,
diff --git a/app/actions/layers-actions.c b/app/actions/layers-actions.c
index e05a9f2..e68d310 100644
--- a/app/actions/layers-actions.c
+++ b/app/actions/layers-actions.c
@@ -535,6 +535,7 @@ layers_actions_update (GimpActionGroup *group,
   gboolean       can_lock_alpha = FALSE;
   gboolean       text_layer     = FALSE;
   gboolean       writable       = FALSE;
+  gboolean       movable        = FALSE;
   gboolean       children       = FALSE;
   GList         *next           = NULL;
   GList         *next_visible   = NULL;
@@ -559,6 +560,7 @@ layers_actions_update (GimpActionGroup *group,
           can_lock_alpha = gimp_layer_can_lock_alpha (layer);
           alpha          = gimp_drawable_has_alpha (GIMP_DRAWABLE (layer));
           writable       = ! gimp_item_is_content_locked (GIMP_ITEM (layer));
+          movable        = ! gimp_item_is_position_locked (GIMP_ITEM (layer));
 
           if (gimp_viewable_get_children (GIMP_VIEWABLE (layer)))
             children = TRUE;
@@ -641,19 +643,19 @@ layers_actions_update (GimpActionGroup *group,
   SET_SENSITIVE ("layers-merge-layers",     layer && !fs && !ac);
   SET_SENSITIVE ("layers-flatten-image",    layer && !fs && !ac);
 
-  SET_VISIBLE   ("layers-text-discard",             text_layer && !ac);
-  SET_VISIBLE   ("layers-text-to-vectors",          text_layer && !ac);
-  SET_VISIBLE   ("layers-text-along-vectors",       text_layer && !ac);
+  SET_VISIBLE   ("layers-text-discard",       text_layer && !ac);
+  SET_VISIBLE   ("layers-text-to-vectors",    text_layer && !ac);
+  SET_VISIBLE   ("layers-text-along-vectors", text_layer && !ac);
 
-  SET_SENSITIVE ("layers-resize",          writable && !ac);
-  SET_SENSITIVE ("layers-resize-to-image", writable && !ac);
-  SET_SENSITIVE ("layers-scale",           writable && !ac);
+  SET_SENSITIVE ("layers-resize",          writable && movable && !ac);
+  SET_SENSITIVE ("layers-resize-to-image", writable && movable && !ac);
+  SET_SENSITIVE ("layers-scale",           writable && movable && !ac);
 
-  SET_SENSITIVE ("layers-crop-to-selection", writable && sel);
-  SET_SENSITIVE ("layers-crop-to-content",   writable);
+  SET_SENSITIVE ("layers-crop-to-selection", writable && movable && sel);
+  SET_SENSITIVE ("layers-crop-to-content",   writable && movable);
 
-  SET_SENSITIVE ("layers-alpha-add",       writable && !children && !fs && !alpha);
-  SET_SENSITIVE ("layers-alpha-remove",    writable && !children && !fs &&  alpha);
+  SET_SENSITIVE ("layers-alpha-add",    writable && !children && !fs && !alpha);
+  SET_SENSITIVE ("layers-alpha-remove", writable && !children && !fs &&  alpha);
 
   SET_SENSITIVE ("layers-lock-alpha", can_lock_alpha);
   SET_ACTIVE    ("layers-lock-alpha", lock_alpha);
diff --git a/app/actions/vectors-actions.c b/app/actions/vectors-actions.c
index aad41ce..04c2a80 100644
--- a/app/actions/vectors-actions.c
+++ b/app/actions/vectors-actions.c
@@ -159,7 +159,13 @@ static const GimpToggleActionEntry vectors_toggle_actions[] =
     NC_("vectors-action", "L_ock strokes"), NULL, NULL,
     G_CALLBACK (vectors_lock_content_cmd_callback),
     FALSE,
-    NULL /* GIMP_HELP_PATH_LOCK_STROKES */ }
+    GIMP_HELP_PATH_LOCK_STROKES },
+
+  { "vectors-lock-position", GIMP_STOCK_TOOL_MOVE,
+    NC_("vectors-action", "L_ock position"), NULL, NULL,
+    G_CALLBACK (vectors_lock_position_cmd_callback),
+    FALSE,
+    GIMP_HELP_PATH_LOCK_POSITION }
 };
 
 static const GimpEnumActionEntry vectors_to_selection_actions[] =
@@ -243,19 +249,21 @@ void
 vectors_actions_update (GimpActionGroup *group,
                         gpointer         data)
 {
-  GimpImage    *image       = action_data_get_image (data);
-  GimpVectors  *vectors     = NULL;
-  GimpDrawable *drawable    = NULL;
-  gint          n_vectors   = 0;
-  gboolean      mask_empty  = TRUE;
-  gboolean      visible     = FALSE;
-  gboolean      linked      = FALSE;
-  gboolean      locked      = FALSE;
-  gboolean      can_lock    = FALSE;
-  gboolean      dr_writable = FALSE;
-  gboolean      dr_children = FALSE;
-  GList        *next        = NULL;
-  GList        *prev        = NULL;
+  GimpImage    *image        = action_data_get_image (data);
+  GimpVectors  *vectors      = NULL;
+  GimpDrawable *drawable     = NULL;
+  gint          n_vectors    = 0;
+  gboolean      mask_empty   = TRUE;
+  gboolean      visible      = FALSE;
+  gboolean      linked       = FALSE;
+  gboolean      locked       = FALSE;
+  gboolean      can_lock     = FALSE;
+  gboolean      locked_pos   = FALSE;
+  gboolean      can_lock_pos = FALSE;
+  gboolean      dr_writable  = FALSE;
+  gboolean      dr_children  = FALSE;
+  GList        *next         = NULL;
+  GList        *prev         = NULL;
 
   if (image)
     {
@@ -270,11 +278,12 @@ vectors_actions_update (GimpActionGroup *group,
           GList    *vectors_list;
           GList    *list;
 
-          visible  = gimp_item_get_visible (item);
-          linked   = gimp_item_get_linked (item);
-          locked   = gimp_item_get_lock_content (item);
-          can_lock = gimp_item_can_lock_content (item);
-
+          visible      = gimp_item_get_visible (item);
+          linked       = gimp_item_get_linked (item);
+          locked       = gimp_item_get_lock_content (item);
+          can_lock     = gimp_item_can_lock_content (item);
+          locked_pos   = gimp_item_get_lock_position (item);
+          can_lock_pos = gimp_item_can_lock_position (item);
           vectors_list = gimp_item_get_container_iter (item);
 
           list = g_list_find (vectors_list, vectors);
@@ -323,13 +332,15 @@ vectors_actions_update (GimpActionGroup *group,
   SET_SENSITIVE ("vectors-export", vectors);
   SET_SENSITIVE ("vectors-import", image);
 
-  SET_SENSITIVE ("vectors-visible",      vectors);
-  SET_SENSITIVE ("vectors-linked",       vectors);
-  SET_SENSITIVE ("vectors-lock-content", can_lock);
+  SET_SENSITIVE ("vectors-visible",       vectors);
+  SET_SENSITIVE ("vectors-linked",        vectors);
+  SET_SENSITIVE ("vectors-lock-content",  can_lock);
+  SET_SENSITIVE ("vectors-lock-position", can_lock_pos);
 
-  SET_ACTIVE ("vectors-visible",      visible);
-  SET_ACTIVE ("vectors-linked",       linked);
-  SET_ACTIVE ("vectors-lock-content", locked);
+  SET_ACTIVE ("vectors-visible",       visible);
+  SET_ACTIVE ("vectors-linked",        linked);
+  SET_ACTIVE ("vectors-lock-content",  locked);
+  SET_ACTIVE ("vectors-lock-position", locked_pos);
 
   SET_SENSITIVE ("vectors-selection-to-vectors",          image && !mask_empty);
   SET_SENSITIVE ("vectors-selection-to-vectors-short",    image && !mask_empty);
diff --git a/app/actions/vectors-commands.c b/app/actions/vectors-commands.c
index 81a85ab..1ffa2ec 100644
--- a/app/actions/vectors-commands.c
+++ b/app/actions/vectors-commands.c
@@ -641,6 +641,33 @@ vectors_lock_content_cmd_callback (GtkAction *action,
     }
 }
 
+void
+vectors_lock_position_cmd_callback (GtkAction *action,
+                                   gpointer   data)
+{
+  GimpImage   *image;
+  GimpVectors *vectors;
+  gboolean     locked;
+  return_if_no_vectors (image, vectors, data);
+
+  locked = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
+
+  if (locked != gimp_item_get_lock_position (GIMP_ITEM (vectors)))
+    {
+      GimpUndo *undo;
+      gboolean  push_undo = TRUE;
+
+      undo = gimp_image_undo_can_compress (image, GIMP_TYPE_ITEM_UNDO,
+                                           GIMP_UNDO_ITEM_LOCK_POSITION);
+
+      if (undo && GIMP_ITEM_UNDO (undo)->item == GIMP_ITEM (vectors))
+        push_undo = FALSE;
+
+
+      gimp_item_set_lock_position (GIMP_ITEM (vectors), locked, push_undo);
+      gimp_image_flush (image);
+    }
+}
 
 /*  private functions  */
 
diff --git a/app/actions/vectors-commands.h b/app/actions/vectors-commands.h
index 40118c0..af485c3 100644
--- a/app/actions/vectors-commands.h
+++ b/app/actions/vectors-commands.h
@@ -69,6 +69,8 @@ void   vectors_linked_cmd_callback               (GtkAction   *action,
                                                   gpointer     data);
 void   vectors_lock_content_cmd_callback         (GtkAction   *action,
                                                   gpointer     data);
+void   vectors_lock_position_cmd_callback        (GtkAction   *action,
+                                                  gpointer     data);
 
 
 #endif /* __VECTORS_COMMANDS_H__ */
diff --git a/app/core/core-enums.c b/app/core/core-enums.c
index 3dfbaac..fc0d650 100644
--- a/app/core/core-enums.c
+++ b/app/core/core-enums.c
@@ -1081,6 +1081,8 @@ gimp_undo_type_get_type (void)
     { GIMP_UNDO_ITEM_DISPLACE, "GIMP_UNDO_ITEM_DISPLACE", "item-displace" },
     { GIMP_UNDO_ITEM_VISIBILITY, "GIMP_UNDO_ITEM_VISIBILITY", "item-visibility" },
     { GIMP_UNDO_ITEM_LINKED, "GIMP_UNDO_ITEM_LINKED", "item-linked" },
+    { GIMP_UNDO_ITEM_LOCK_CONTENT, "GIMP_UNDO_ITEM_LOCK_CONTENT", "item-lock-content" },
+    { GIMP_UNDO_ITEM_LOCK_POSITION, "GIMP_UNDO_ITEM_LOCK_POSITION", "item-lock-position" },
     { GIMP_UNDO_LAYER_ADD, "GIMP_UNDO_LAYER_ADD", "layer-add" },
     { GIMP_UNDO_LAYER_REMOVE, "GIMP_UNDO_LAYER_REMOVE", "layer-remove" },
     { GIMP_UNDO_LAYER_MODE, "GIMP_UNDO_LAYER_MODE", "layer-mode" },
@@ -1168,6 +1170,8 @@ gimp_undo_type_get_type (void)
     { GIMP_UNDO_ITEM_DISPLACE, NC_("undo-type", "Move item"), NULL },
     { GIMP_UNDO_ITEM_VISIBILITY, NC_("undo-type", "Item visibility"), NULL },
     { GIMP_UNDO_ITEM_LINKED, NC_("undo-type", "Link/Unlink item"), NULL },
+    { GIMP_UNDO_ITEM_LOCK_CONTENT, NC_("undo-type", "Lock/Unlock content"), NULL },
+    { GIMP_UNDO_ITEM_LOCK_POSITION, NC_("undo-type", "Lock/Unlock position"), NULL },
     { GIMP_UNDO_LAYER_ADD, NC_("undo-type", "New layer"), NULL },
     { GIMP_UNDO_LAYER_REMOVE, NC_("undo-type", "Delete layer"), NULL },
     { GIMP_UNDO_LAYER_MODE, NC_("undo-type", "Set layer mode"), NULL },
diff --git a/app/core/core-enums.h b/app/core/core-enums.h
index a7e6f6a..8bbba37 100644
--- a/app/core/core-enums.h
+++ b/app/core/core-enums.h
@@ -522,6 +522,8 @@ typedef enum /*< pdb-skip >*/
   GIMP_UNDO_ITEM_DISPLACE,            /*< desc="Move item"                   >*/
   GIMP_UNDO_ITEM_VISIBILITY,          /*< desc="Item visibility"             >*/
   GIMP_UNDO_ITEM_LINKED,              /*< desc="Link/Unlink item"            >*/
+  GIMP_UNDO_ITEM_LOCK_CONTENT,        /*< desc="Lock/Unlock content"         >*/
+  GIMP_UNDO_ITEM_LOCK_POSITION,       /*< desc="Lock/Unlock position"        >*/
   GIMP_UNDO_LAYER_ADD,                /*< desc="New layer"                   >*/
   GIMP_UNDO_LAYER_REMOVE,             /*< desc="Delete layer"                >*/
   GIMP_UNDO_LAYER_MODE,               /*< desc="Set layer mode"              >*/
diff --git a/app/core/gimpgrouplayer.c b/app/core/gimpgrouplayer.c
index f9f25e7..303412f 100644
--- a/app/core/gimpgrouplayer.c
+++ b/app/core/gimpgrouplayer.c
@@ -87,6 +87,7 @@ static gboolean        gimp_group_layer_get_expanded (GimpViewable    *viewable)
 static void            gimp_group_layer_set_expanded (GimpViewable    *viewable,
                                                       gboolean         expanded);
 
+static gboolean  gimp_group_layer_is_position_locked (const GimpItem  *item);
 static GimpItem      * gimp_group_layer_duplicate    (GimpItem        *item,
                                                       GType            new_type);
 static void            gimp_group_layer_convert      (GimpItem        *item,
@@ -208,6 +209,7 @@ gimp_group_layer_class_init (GimpGroupLayerClass *klass)
   viewable_class->set_expanded     = gimp_group_layer_set_expanded;
   viewable_class->get_expanded     = gimp_group_layer_get_expanded;
 
+  item_class->is_position_locked   = gimp_group_layer_is_position_locked;
   item_class->duplicate            = gimp_group_layer_duplicate;
   item_class->convert              = gimp_group_layer_convert;
   item_class->translate            = gimp_group_layer_translate;
@@ -405,6 +407,25 @@ gimp_group_layer_set_expanded (GimpViewable *viewable,
   GET_PRIVATE (group)->expanded = expanded;
 }
 
+static gboolean
+gimp_group_layer_is_position_locked (const GimpItem *item)
+{
+  GimpGroupLayerPrivate *private = GET_PRIVATE (item);
+  GList                 *list;
+
+  for (list = gimp_item_stack_get_item_iter (GIMP_ITEM_STACK (private->children));
+       list;
+       list = g_list_next (list))
+    {
+      GimpItem *child = list->data;
+
+      if (gimp_item_is_position_locked (child))
+        return TRUE;
+    }
+
+  return GIMP_ITEM_CLASS (parent_class)->is_position_locked (item);
+}
+
 static GimpItem *
 gimp_group_layer_duplicate (GimpItem *item,
                             GType     new_type)
diff --git a/app/core/gimpimage-undo-push.c b/app/core/gimpimage-undo-push.c
index b99046c..c226924 100644
--- a/app/core/gimpimage-undo-push.c
+++ b/app/core/gimpimage-undo-push.c
@@ -393,6 +393,38 @@ gimp_image_undo_push_item_linked (GimpImage   *image,
 }
 
 GimpUndo *
+gimp_image_undo_push_item_lock_content (GimpImage   *image,
+                                        const gchar *undo_desc,
+                                        GimpItem    *item)
+{
+  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
+  g_return_val_if_fail (GIMP_IS_ITEM (item), NULL);
+  g_return_val_if_fail (gimp_item_is_attached (item), NULL);
+
+  return gimp_image_undo_push (image, GIMP_TYPE_ITEM_PROP_UNDO,
+                               GIMP_UNDO_ITEM_LOCK_CONTENT, undo_desc,
+                               GIMP_DIRTY_ITEM_META,
+                               "item", item,
+                               NULL);
+}
+
+GimpUndo *
+gimp_image_undo_push_item_lock_position (GimpImage   *image,
+                                         const gchar *undo_desc,
+                                         GimpItem    *item)
+{
+  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
+  g_return_val_if_fail (GIMP_IS_ITEM (item), NULL);
+  g_return_val_if_fail (gimp_item_is_attached (item), NULL);
+
+  return gimp_image_undo_push (image, GIMP_TYPE_ITEM_PROP_UNDO,
+                               GIMP_UNDO_ITEM_LOCK_POSITION, undo_desc,
+                               GIMP_DIRTY_ITEM_META,
+                               "item", item,
+                               NULL);
+}
+
+GimpUndo *
 gimp_image_undo_push_item_parasite (GimpImage          *image,
                                     const gchar        *undo_desc,
                                     GimpItem           *item,
diff --git a/app/core/gimpimage-undo-push.h b/app/core/gimpimage-undo-push.h
index d1c56ca..eb97208 100644
--- a/app/core/gimpimage-undo-push.h
+++ b/app/core/gimpimage-undo-push.h
@@ -97,6 +97,12 @@ GimpUndo * gimp_image_undo_push_item_visibility     (GimpImage     *image,
 GimpUndo * gimp_image_undo_push_item_linked         (GimpImage     *image,
                                                      const gchar   *undo_desc,
                                                      GimpItem      *item);
+GimpUndo * gimp_image_undo_push_item_lock_content   (GimpImage     *image,
+                                                     const gchar   *undo_desc,
+                                                     GimpItem      *item);
+GimpUndo * gimp_image_undo_push_item_lock_position  (GimpImage     *image,
+                                                     const gchar   *undo_desc,
+                                                     GimpItem      *item);
 GimpUndo * gimp_image_undo_push_item_parasite       (GimpImage     *image,
                                                      const gchar   *undo_desc,
                                                      GimpItem      *item,
diff --git a/app/core/gimpitem-linked.c b/app/core/gimpitem-linked.c
index aa41723..fe86e40 100644
--- a/app/core/gimpitem-linked.c
+++ b/app/core/gimpitem-linked.c
@@ -32,6 +32,39 @@
 
 /*  public functions  */
 
+gboolean
+gimp_item_linked_is_locked (const GimpItem *item)
+{
+  GList    *list;
+  GList    *l;
+  gboolean  locked = FALSE;
+
+  g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE);
+  g_return_val_if_fail (gimp_item_get_linked (item) == TRUE, FALSE);
+  g_return_val_if_fail (gimp_item_is_attached (item), FALSE);
+
+  list = gimp_image_item_list_get_list (gimp_item_get_image (item), item,
+                                        GIMP_ITEM_TYPE_ALL,
+                                        GIMP_ITEM_SET_LINKED);
+
+  list = gimp_image_item_list_filter (item, list, TRUE, FALSE);
+
+  for (l = list; l; l = g_list_next (l))
+    {
+      GimpItem *item = l->data;
+
+      if (gimp_item_is_position_locked (item))
+        {
+          locked = TRUE;
+          break;
+        }
+    }
+
+  g_list_free (list);
+
+  return locked;
+}
+
 void
 gimp_item_linked_translate (GimpItem *item,
                             gint      offset_x,
diff --git a/app/core/gimpitem-linked.h b/app/core/gimpitem-linked.h
index 759361b..974d0ad 100644
--- a/app/core/gimpitem-linked.h
+++ b/app/core/gimpitem-linked.h
@@ -19,29 +19,31 @@
 #define __GIMP_ITEM_LINKED_H__
 
 
-void   gimp_item_linked_translate (GimpItem               *item,
-                                   gint                    offset_x,
-                                   gint                    offset_y,
-                                   gboolean                push_undo);
-void   gimp_item_linked_flip      (GimpItem               *item,
-                                   GimpContext            *context,
-                                   GimpOrientationType     flip_type,
-                                   gdouble                 axis,
-                                   gboolean                clip_result);
-void   gimp_item_linked_rotate    (GimpItem               *item,
-                                   GimpContext            *context,
-                                   GimpRotationType        rotate_type,
-                                   gdouble                 center_x,
-                                   gdouble                 center_y,
-                                   gboolean                clip_result);
-void   gimp_item_linked_transform (GimpItem               *item,
-                                   GimpContext            *context,
-                                   const GimpMatrix3      *matrix,
-                                   GimpTransformDirection  direction,
-                                   GimpInterpolationType   interpolation_type,
-                                   gint                    recursion_level,
-                                   GimpTransformResize     clip_result,
-                                   GimpProgress           *progress);
+gboolean gimp_item_linked_is_locked (const GimpItem         *item);
+
+void     gimp_item_linked_translate (GimpItem               *item,
+                                     gint                    offset_x,
+                                     gint                    offset_y,
+                                     gboolean                push_undo);
+void     gimp_item_linked_flip      (GimpItem               *item,
+                                     GimpContext            *context,
+                                     GimpOrientationType     flip_type,
+                                     gdouble                 axis,
+                                     gboolean                clip_result);
+void     gimp_item_linked_rotate    (GimpItem               *item,
+                                     GimpContext            *context,
+                                     GimpRotationType        rotate_type,
+                                     gdouble                 center_x,
+                                     gdouble                 center_y,
+                                     gboolean                clip_result);
+void     gimp_item_linked_transform (GimpItem               *item,
+                                     GimpContext            *context,
+                                     const GimpMatrix3      *matrix,
+                                     GimpTransformDirection  direction,
+                                     GimpInterpolationType   interpolation_type,
+                                     gint                    recursion_level,
+                                     GimpTransformResize     clip_result,
+                                     GimpProgress           *progress);
 
 
 #endif /* __GIMP_ITEM_LINKED_H__ */
diff --git a/app/core/gimpitem.c b/app/core/gimpitem.c
index 468e4e6..5f633df 100644
--- a/app/core/gimpitem.c
+++ b/app/core/gimpitem.c
@@ -35,6 +35,7 @@
 #include "gimpimage-undo.h"
 #include "gimpimage-undo-push.h"
 #include "gimpitem.h"
+#include "gimpitem-linked.h"
 #include "gimpitem-preview.h"
 #include "gimpitemtree.h"
 #include "gimplist.h"
@@ -52,6 +53,7 @@ enum
   VISIBILITY_CHANGED,
   LINKED_CHANGED,
   LOCK_CONTENT_CHANGED,
+  LOCK_POSITION_CHANGED,
   LAST_SIGNAL
 };
 
@@ -66,7 +68,8 @@ enum
   PROP_OFFSET_Y,
   PROP_VISIBLE,
   PROP_LINKED,
-  PROP_LOCK_CONTENT
+  PROP_LOCK_CONTENT,
+  PROP_LOCK_POSITION
 };
 
 
@@ -84,9 +87,10 @@ struct _GimpItemPrivate
   gint              width, height;      /*  size in pixels           */
   gint              offset_x, offset_y; /*  pixel offset in image    */
 
-  guint             visible      : 1;   /*  control visibility       */
-  guint             linked       : 1;   /*  control linkage          */
-  guint             lock_content : 1;   /*  content editability      */
+  guint             visible       : 1;  /*  control visibility       */
+  guint             linked        : 1;  /*  control linkage          */
+  guint             lock_content  : 1;  /*  content editability      */
+  guint             lock_position : 1;  /*  content movability       */
 
   guint             removed : 1;        /*  removed from the image?  */
 
@@ -119,6 +123,7 @@ static gint64     gimp_item_get_memsize             (GimpObject     *object,
 static void       gimp_item_real_visibility_changed (GimpItem       *item);
 
 static gboolean   gimp_item_real_is_content_locked  (const GimpItem *item);
+static gboolean   gimp_item_real_is_position_locked (const GimpItem *item);
 static GimpItem * gimp_item_real_duplicate          (GimpItem       *item,
                                                      GType           new_type);
 static void       gimp_item_real_convert            (GimpItem       *item,
@@ -197,6 +202,15 @@ gimp_item_class_init (GimpItemClass *klass)
                   gimp_marshal_VOID__VOID,
                   G_TYPE_NONE, 0);
 
+  gimp_item_signals[LOCK_POSITION_CHANGED] =
+    g_signal_new ("lock-position-changed",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_FIRST,
+                  G_STRUCT_OFFSET (GimpItemClass, lock_position_changed),
+                  NULL, NULL,
+                  gimp_marshal_VOID__VOID,
+                  G_TYPE_NONE, 0);
+
   object_class->constructed        = gimp_item_constructed;
   object_class->finalize           = gimp_item_finalize;
   object_class->set_property       = gimp_item_set_property;
@@ -211,10 +225,12 @@ gimp_item_class_init (GimpItemClass *klass)
   klass->visibility_changed        = gimp_item_real_visibility_changed;
   klass->linked_changed            = NULL;
   klass->lock_content_changed      = NULL;
+  klass->lock_position_changed     = NULL;
 
   klass->unset_removed             = NULL;
   klass->is_attached               = NULL;
   klass->is_content_locked         = gimp_item_real_is_content_locked;
+  klass->is_position_locked        = gimp_item_real_is_position_locked;
   klass->get_tree                  = NULL;
   klass->duplicate                 = gimp_item_real_duplicate;
   klass->convert                   = gimp_item_real_convert;
@@ -286,6 +302,12 @@ gimp_item_class_init (GimpItemClass *klass)
                                                          FALSE,
                                                          GIMP_PARAM_READABLE));
 
+  g_object_class_install_property (object_class, PROP_LOCK_POSITION,
+                                   g_param_spec_boolean ("lock-position",
+                                                         NULL, NULL,
+                                                         FALSE,
+                                                         GIMP_PARAM_READABLE));
+
   g_type_class_add_private (klass, sizeof (GimpItemPrivate));
 }
 
@@ -296,18 +318,19 @@ gimp_item_init (GimpItem *item)
 
   g_object_force_floating (G_OBJECT (item));
 
-  private->ID           = 0;
-  private->tattoo       = 0;
-  private->image        = NULL;
-  private->parasites    = gimp_parasite_list_new ();
-  private->width        = 0;
-  private->height       = 0;
-  private->offset_x     = 0;
-  private->offset_y     = 0;
-  private->visible      = TRUE;
-  private->linked       = FALSE;
-  private->lock_content = FALSE;
-  private->removed      = FALSE;
+  private->ID            = 0;
+  private->tattoo        = 0;
+  private->image         = NULL;
+  private->parasites     = gimp_parasite_list_new ();
+  private->width         = 0;
+  private->height        = 0;
+  private->offset_x      = 0;
+  private->offset_y      = 0;
+  private->visible       = TRUE;
+  private->linked        = FALSE;
+  private->lock_content  = FALSE;
+  private->lock_position = FALSE;
+  private->removed       = FALSE;
 }
 
 static void
@@ -412,6 +435,9 @@ gimp_item_get_property (GObject    *object,
     case PROP_LOCK_CONTENT:
       g_value_set_boolean (value, private->lock_content);
       break;
+    case PROP_LOCK_POSITION:
+      g_value_set_boolean (value, private->lock_position);
+      break;
 
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -466,6 +492,16 @@ gimp_item_real_is_content_locked (const GimpItem *item)
   return GET_PRIVATE (item)->lock_content;
 }
 
+static gboolean
+gimp_item_real_is_position_locked (const GimpItem *item)
+{
+  if (gimp_item_get_linked (item))
+    if (gimp_item_linked_is_locked (item))
+      return TRUE;
+
+  return GET_PRIVATE (item)->lock_position;
+}
+
 static GimpItem *
 gimp_item_real_duplicate (GimpItem *item,
                           GType     new_type)
@@ -528,6 +564,10 @@ gimp_item_real_duplicate (GimpItem *item,
     gimp_item_set_lock_content (new_item, gimp_item_get_lock_content (item),
                                 FALSE);
 
+  if (gimp_item_can_lock_position (new_item))
+    gimp_item_set_lock_position (new_item, gimp_item_get_lock_position (item),
+                                 FALSE);
+
   return new_item;
 }
 
@@ -1746,9 +1786,10 @@ gimp_item_replace_item (GimpItem *item,
                       gimp_item_get_width  (replace),
                       gimp_item_get_height (replace));
 
-  gimp_item_set_visible      (item, gimp_item_get_visible (replace), FALSE);
-  gimp_item_set_linked       (item, gimp_item_get_linked (replace), FALSE);
-  gimp_item_set_lock_content (item, gimp_item_get_lock_content (replace), FALSE);
+  gimp_item_set_visible       (item, gimp_item_get_visible (replace), FALSE);
+  gimp_item_set_linked        (item, gimp_item_get_linked (replace), FALSE);
+  gimp_item_set_lock_content  (item, gimp_item_get_lock_content (replace), FALSE);
+  gimp_item_set_lock_position (item, gimp_item_get_lock_position (replace), FALSE);
 }
 
 /**
@@ -2086,6 +2127,60 @@ gimp_item_is_content_locked (const GimpItem *item)
   return GIMP_ITEM_GET_CLASS (item)->is_content_locked (item);
 }
 
+void
+gimp_item_set_lock_position (GimpItem *item,
+                             gboolean  lock_position,
+                             gboolean  push_undo)
+{
+  g_return_if_fail (GIMP_IS_ITEM (item));
+  g_return_if_fail (gimp_item_can_lock_position (item));
+
+  lock_position = lock_position ? TRUE : FALSE;
+
+  if (gimp_item_get_lock_position (item) != lock_position)
+    {
+      if (push_undo && gimp_item_is_attached (item))
+        {
+          GimpImage *image = gimp_item_get_image (item);
+
+          gimp_image_undo_push_item_lock_position (image, NULL, item);
+        }
+
+      GET_PRIVATE (item)->lock_position = lock_position;
+
+      g_signal_emit (item, gimp_item_signals[LOCK_POSITION_CHANGED], 0);
+
+      g_object_notify (G_OBJECT (item), "lock-position");
+    }
+}
+
+gboolean
+gimp_item_get_lock_position (const GimpItem *item)
+{
+  g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE);
+
+  return GET_PRIVATE (item)->lock_position;
+}
+
+gboolean
+gimp_item_can_lock_position (const GimpItem *item)
+{
+  g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE);
+
+  if (gimp_viewable_get_children (GIMP_VIEWABLE (item)))
+    return FALSE;
+
+  return TRUE;
+}
+
+gboolean
+gimp_item_is_position_locked (const GimpItem *item)
+{
+  g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE);
+
+  return GIMP_ITEM_GET_CLASS (item)->is_position_locked (item);
+}
+
 gboolean
 gimp_item_mask_bounds (GimpItem *item,
                        gint     *x1,
diff --git a/app/core/gimpitem.h b/app/core/gimpitem.h
index 22d854e..ca4f14c 100644
--- a/app/core/gimpitem.h
+++ b/app/core/gimpitem.h
@@ -42,15 +42,17 @@ struct _GimpItemClass
   GimpViewableClass  parent_class;
 
   /*  signals  */
-  void            (* removed)              (GimpItem             *item);
-  void            (* visibility_changed)   (GimpItem             *item);
-  void            (* linked_changed)       (GimpItem             *item);
-  void            (* lock_content_changed) (GimpItem             *item);
+  void            (* removed)               (GimpItem            *item);
+  void            (* visibility_changed)    (GimpItem            *item);
+  void            (* linked_changed)        (GimpItem            *item);
+  void            (* lock_content_changed)  (GimpItem            *item);
+  void            (* lock_position_changed) (GimpItem            *item);
 
   /*  virtual functions  */
   void            (* unset_removed)      (GimpItem               *item);
   gboolean        (* is_attached)        (const GimpItem         *item);
   gboolean        (* is_content_locked)  (const GimpItem         *item);
+  gboolean        (* is_position_locked) (const GimpItem         *item);
   GimpItemTree  * (* get_tree)           (GimpItem               *item);
   GimpItem      * (* duplicate)          (GimpItem               *item,
                                           GType                   new_type);
@@ -308,6 +310,13 @@ gboolean        gimp_item_get_lock_content   (const GimpItem     *item);
 gboolean        gimp_item_can_lock_content   (const GimpItem     *item);
 gboolean        gimp_item_is_content_locked  (const GimpItem     *item);
 
+void            gimp_item_set_lock_position  (GimpItem          *item,
+                                              gboolean           lock_position,
+                                              gboolean           push_undo);
+gboolean        gimp_item_get_lock_position  (const GimpItem    *item);
+gboolean        gimp_item_can_lock_position  (const GimpItem    *item);
+gboolean        gimp_item_is_position_locked (const GimpItem    *item);
+
 gboolean        gimp_item_mask_bounds        (GimpItem           *item,
                                               gint               *x1,
                                               gint               *y1,
diff --git a/app/core/gimpitempropundo.c b/app/core/gimpitempropundo.c
index 9413f3b..359bbce 100644
--- a/app/core/gimpitempropundo.c
+++ b/app/core/gimpitempropundo.c
@@ -127,6 +127,14 @@ gimp_item_prop_undo_constructed (GObject *object)
       item_prop_undo->linked  = gimp_item_get_linked (item);
       break;
 
+    case GIMP_UNDO_ITEM_LOCK_CONTENT:
+      item_prop_undo->lock_content = gimp_item_get_lock_content (item);
+      break;
+
+    case GIMP_UNDO_ITEM_LOCK_POSITION:
+      item_prop_undo->lock_position = gimp_item_get_lock_position (item);
+      break;
+
     case GIMP_UNDO_PARASITE_ATTACH:
     case GIMP_UNDO_PARASITE_REMOVE:
       g_assert (item_prop_undo->parasite_name != NULL);
@@ -277,6 +285,26 @@ gimp_item_prop_undo_pop (GimpUndo            *undo,
       }
       break;
 
+    case GIMP_UNDO_ITEM_LOCK_CONTENT:
+      {
+        gboolean lock_content;
+
+        lock_content = gimp_item_get_lock_content (item);
+        gimp_item_set_lock_content (item, item_prop_undo->lock_content, FALSE);
+        item_prop_undo->lock_content = lock_content;
+      }
+      break;
+
+    case GIMP_UNDO_ITEM_LOCK_POSITION:
+      {
+        gboolean lock_position;
+
+        lock_position = gimp_item_get_lock_position (item);
+        gimp_item_set_lock_position (item, item_prop_undo->lock_position, FALSE);
+        item_prop_undo->lock_position = lock_position;
+      }
+      break;
+
     case GIMP_UNDO_PARASITE_ATTACH:
     case GIMP_UNDO_PARASITE_REMOVE:
       {
diff --git a/app/core/gimpitempropundo.h b/app/core/gimpitempropundo.h
index e20eb13..c0c1c5f 100644
--- a/app/core/gimpitempropundo.h
+++ b/app/core/gimpitempropundo.h
@@ -41,8 +41,10 @@ struct _GimpItemPropUndo
   gchar        *name;
   gint          offset_x;
   gint          offset_y;
-  gboolean      visible;
-  gboolean      linked;
+  guint         visible       : 1;
+  guint         linked        : 1;
+  guint         lock_content  : 1;
+  guint         lock_position : 1;
   gchar        *parasite_name;
   GimpParasite *parasite;
 };
diff --git a/app/core/gimplayermask.c b/app/core/gimplayermask.c
index 4ebf07f..d54d9fb 100644
--- a/app/core/gimplayermask.c
+++ b/app/core/gimplayermask.c
@@ -37,24 +37,25 @@
 #include "gimp-intl.h"
 
 
-static gboolean        gimp_layer_mask_is_attached       (const GimpItem    *item);
-static gboolean        gimp_layer_mask_is_content_locked (const GimpItem    *item);
-static GimpItemTree  * gimp_layer_mask_get_tree          (GimpItem          *item);
-static GimpItem      * gimp_layer_mask_duplicate         (GimpItem          *item,
-                                                          GType              new_type);
-static gboolean        gimp_layer_mask_rename            (GimpItem          *item,
-                                                          const gchar       *new_name,
-                                                          const gchar       *undo_desc,
-                                                          GError           **error);
-
-static void            gimp_layer_mask_convert_type      (GimpDrawable      *drawable,
-                                                          GimpImage         *dest_image,
-                                                          const Babl        *new_format,
-                                                          GimpImageBaseType  new_base_type,
-                                                          GimpPrecision      new_precision,
-                                                          gint               layer_dither_type,
-                                                          gint               mask_dither_type,
-                                                          gboolean           push_undo);
+static gboolean        gimp_layer_mask_is_attached        (const GimpItem    *item);
+static gboolean        gimp_layer_mask_is_content_locked  (const GimpItem    *item);
+static gboolean        gimp_layer_mask_is_position_locked (const GimpItem    *item);
+static GimpItemTree  * gimp_layer_mask_get_tree           (GimpItem          *item);
+static GimpItem      * gimp_layer_mask_duplicate          (GimpItem          *item,
+                                                           GType              new_type);
+static gboolean        gimp_layer_mask_rename             (GimpItem          *item,
+                                                           const gchar       *new_name,
+                                                           const gchar       *undo_desc,
+                                                           GError           **error);
+
+static void            gimp_layer_mask_convert_type       (GimpDrawable      *drawable,
+                                                           GimpImage         *dest_image,
+                                                           const Babl        *new_format,
+                                                           GimpImageBaseType  new_base_type,
+                                                           GimpPrecision      new_precision,
+                                                           gint               layer_dither_type,
+                                                           gint               mask_dither_type,
+                                                           gboolean           push_undo);
 
 
 G_DEFINE_TYPE (GimpLayerMask, gimp_layer_mask, GIMP_TYPE_CHANNEL)
@@ -71,13 +72,14 @@ gimp_layer_mask_class_init (GimpLayerMaskClass *klass)
 
   viewable_class->default_stock_id = "gimp-layer-mask";
 
-  item_class->is_attached       = gimp_layer_mask_is_attached;
-  item_class->is_content_locked = gimp_layer_mask_is_content_locked;
-  item_class->get_tree          = gimp_layer_mask_get_tree;
-  item_class->duplicate         = gimp_layer_mask_duplicate;
-  item_class->rename            = gimp_layer_mask_rename;
-  item_class->translate_desc    = C_("undo-type", "Move Layer Mask");
-  item_class->to_selection_desc = C_("undo-type", "Layer Mask to Selection");
+  item_class->is_attached        = gimp_layer_mask_is_attached;
+  item_class->is_content_locked  = gimp_layer_mask_is_content_locked;
+  item_class->is_position_locked = gimp_layer_mask_is_position_locked;
+  item_class->get_tree           = gimp_layer_mask_get_tree;
+  item_class->duplicate          = gimp_layer_mask_duplicate;
+  item_class->rename             = gimp_layer_mask_rename;
+  item_class->translate_desc     = C_("undo-type", "Move Layer Mask");
+  item_class->to_selection_desc  = C_("undo-type", "Layer Mask to Selection");
 
   drawable_class->convert_type  = gimp_layer_mask_convert_type;
 }
@@ -101,6 +103,18 @@ gimp_layer_mask_is_content_locked (const GimpItem *item)
 }
 
 static gboolean
+gimp_layer_mask_is_position_locked (const GimpItem *item)
+{
+  GimpLayerMask *mask  = GIMP_LAYER_MASK (item);
+  GimpLayer     *layer = gimp_layer_mask_get_layer (mask);
+
+  if (layer)
+    return gimp_item_is_position_locked (GIMP_ITEM (layer));
+
+  return FALSE;
+}
+
+static gboolean
 gimp_layer_mask_is_attached (const GimpItem *item)
 {
   GimpLayerMask *mask  = GIMP_LAYER_MASK (item);
diff --git a/app/pdb/drawable-transform-cmds.c b/app/pdb/drawable-transform-cmds.c
index 0e66e9a..50141ff 100644
--- a/app/pdb/drawable-transform-cmds.c
+++ b/app/pdb/drawable-transform-cmds.c
@@ -74,7 +74,8 @@ drawable_transform_flip_simple_invoker (GimpProcedure         *procedure,
       gint x, y, width, height;
 
       success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                           GIMP_PDB_ITEM_CONTENT, error);
+                                           GIMP_PDB_ITEM_CONTENT |
+                                           GIMP_PDB_ITEM_POSITION, error);
 
       if (success &&
           gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
@@ -143,7 +144,9 @@ drawable_transform_flip_invoker (GimpProcedure         *procedure,
       gint x, y, width, height;
 
       success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                                        GIMP_PDB_ITEM_CONTENT, error);
+                                                        GIMP_PDB_ITEM_CONTENT |
+                                                        GIMP_PDB_ITEM_POSITION,
+                                                        error);
 
       if (success &&
           gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
@@ -228,7 +231,8 @@ drawable_transform_flip_default_invoker (GimpProcedure         *procedure,
       gint x, y, width, height;
 
       success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                                        GIMP_PDB_ITEM_CONTENT, error);
+                                                        GIMP_PDB_ITEM_CONTENT |
+                                                        GIMP_PDB_ITEM_POSITION, error);
 
       if (success &&
           gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
@@ -329,7 +333,9 @@ drawable_transform_perspective_invoker (GimpProcedure         *procedure,
       gint x, y, width, height;
 
       success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                                        GIMP_PDB_ITEM_CONTENT, error);
+                                                        GIMP_PDB_ITEM_CONTENT |
+                                                        GIMP_PDB_ITEM_POSITION,
+                                                        error);
 
       if (success &&
           gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
@@ -424,7 +430,8 @@ drawable_transform_perspective_default_invoker (GimpProcedure         *procedure
       gint x, y, width, height;
 
       success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                                        GIMP_PDB_ITEM_CONTENT, error);
+                                                        GIMP_PDB_ITEM_CONTENT |
+                                                        GIMP_PDB_ITEM_POSITION, error);
 
       if (success &&
           gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
@@ -513,7 +520,8 @@ drawable_transform_rotate_simple_invoker (GimpProcedure         *procedure,
       gint x, y, width, height;
 
       success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                           GIMP_PDB_ITEM_CONTENT, error);
+                                           GIMP_PDB_ITEM_CONTENT |
+                                           GIMP_PDB_ITEM_POSITION, error);
 
       if (success &&
           gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
@@ -587,7 +595,9 @@ drawable_transform_rotate_invoker (GimpProcedure         *procedure,
       gint x, y, width, height;
 
       success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                                        GIMP_PDB_ITEM_CONTENT, error);
+                                                        GIMP_PDB_ITEM_CONTENT |
+                                                        GIMP_PDB_ITEM_POSITION,
+                                                        error);
 
       if (success &&
           gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
@@ -676,7 +686,8 @@ drawable_transform_rotate_default_invoker (GimpProcedure         *procedure,
       gint x, y, width, height;
 
       success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                                        GIMP_PDB_ITEM_CONTENT, error);
+                                                        GIMP_PDB_ITEM_CONTENT |
+                                                        GIMP_PDB_ITEM_POSITION, error);
 
       if (success &&
           gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
@@ -773,7 +784,8 @@ drawable_transform_scale_invoker (GimpProcedure         *procedure,
       gint x, y, width, height;
 
       success = (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                                          GIMP_PDB_ITEM_CONTENT, error) && x0 < x1 && y0 < y1);
+                                                          GIMP_PDB_ITEM_CONTENT |
+                                                          GIMP_PDB_ITEM_POSITION, error) && x0 < x1 && y0 < y1);
 
       if (success &&
           gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
@@ -859,7 +871,8 @@ drawable_transform_scale_default_invoker (GimpProcedure         *procedure,
       gint x, y, width, height;
 
       success = (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                                          GIMP_PDB_ITEM_CONTENT, error) && x0 < x1 && y0 < y1);
+                                                          GIMP_PDB_ITEM_CONTENT |
+                                                          GIMP_PDB_ITEM_POSITION, error) && x0 < x1 && y0 < y1);
 
       if (success &&
           gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
@@ -949,7 +962,9 @@ drawable_transform_shear_invoker (GimpProcedure         *procedure,
       gint x, y, width, height;
 
       success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                                        GIMP_PDB_ITEM_CONTENT, error);
+                                                        GIMP_PDB_ITEM_CONTENT |
+                                                        GIMP_PDB_ITEM_POSITION,
+                                                        error);
 
       if (success &&
           gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
@@ -1031,7 +1046,8 @@ drawable_transform_shear_default_invoker (GimpProcedure         *procedure,
       gint x, y, width, height;
 
       success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                                        GIMP_PDB_ITEM_CONTENT, error);
+                                                        GIMP_PDB_ITEM_CONTENT |
+                                                        GIMP_PDB_ITEM_POSITION, error);
 
       if (success &&
           gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
@@ -1131,7 +1147,9 @@ drawable_transform_2d_invoker (GimpProcedure         *procedure,
       gint x, y, width, height;
 
       success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                                        GIMP_PDB_ITEM_CONTENT, error);
+                                                        GIMP_PDB_ITEM_CONTENT |
+                                                        GIMP_PDB_ITEM_POSITION,
+                                                        error);
 
       if (success &&
           gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
@@ -1224,7 +1242,8 @@ drawable_transform_2d_default_invoker (GimpProcedure         *procedure,
       gint x, y, width, height;
 
       success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                                        GIMP_PDB_ITEM_CONTENT, error);
+                                                        GIMP_PDB_ITEM_CONTENT |
+                                                        GIMP_PDB_ITEM_POSITION, error);
 
       if (success &&
           gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
@@ -1329,7 +1348,9 @@ drawable_transform_matrix_invoker (GimpProcedure         *procedure,
       gint x, y, width, height;
 
       success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                                        GIMP_PDB_ITEM_CONTENT, error);
+                                                        GIMP_PDB_ITEM_CONTENT |
+                                                        GIMP_PDB_ITEM_POSITION,
+                                                        error);
 
       if (success &&
           gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
@@ -1430,7 +1451,8 @@ drawable_transform_matrix_default_invoker (GimpProcedure         *procedure,
       gint x, y, width, height;
 
       success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                                        GIMP_PDB_ITEM_CONTENT, error);
+                                                        GIMP_PDB_ITEM_CONTENT |
+                                                        GIMP_PDB_ITEM_POSITION, error);
 
       if (success &&
           gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
diff --git a/app/pdb/gimppdb-utils.c b/app/pdb/gimppdb-utils.c
index f803b82..4c553f4 100644
--- a/app/pdb/gimppdb-utils.c
+++ b/app/pdb/gimppdb-utils.c
@@ -498,6 +498,16 @@ gimp_pdb_item_is_modifyable (GimpItem           *item,
       return FALSE;
     }
 
+  if ((modify & GIMP_PDB_ITEM_POSITION) && gimp_item_is_position_locked (item))
+    {
+      g_set_error (error, GIMP_PDB_ERROR, GIMP_PDB_ERROR_INVALID_ARGUMENT,
+                   _("Item '%s' (%d) cannot be modified because its "
+                     "position and size are locked"),
+                   gimp_object_get_name (item),
+                   gimp_item_get_ID (item));
+      return FALSE;
+    }
+
   return TRUE;
 }
 
diff --git a/app/pdb/internal-procs.c b/app/pdb/internal-procs.c
index 2d937eb..ff65aef 100644
--- a/app/pdb/internal-procs.c
+++ b/app/pdb/internal-procs.c
@@ -28,7 +28,7 @@
 #include "internal-procs.h"
 
 
-/* 678 procedures registered total */
+/* 680 procedures registered total */
 
 void
 internal_procs_init (GimpPDB *pdb)
diff --git a/app/pdb/item-cmds.c b/app/pdb/item-cmds.c
index 20a5358..0addf69 100644
--- a/app/pdb/item-cmds.c
+++ b/app/pdb/item-cmds.c
@@ -660,6 +660,62 @@ item_set_lock_content_invoker (GimpProcedure         *procedure,
 }
 
 static GimpValueArray *
+item_get_lock_position_invoker (GimpProcedure         *procedure,
+                                Gimp                  *gimp,
+                                GimpContext           *context,
+                                GimpProgress          *progress,
+                                const GimpValueArray  *args,
+                                GError               **error)
+{
+  gboolean success = TRUE;
+  GimpValueArray *return_vals;
+  GimpItem *item;
+  gboolean lock_position = FALSE;
+
+  item = gimp_value_get_item (gimp_value_array_index (args, 0), gimp);
+
+  if (success)
+    {
+      lock_position = gimp_item_get_lock_position (GIMP_ITEM (item));
+    }
+
+  return_vals = gimp_procedure_get_return_values (procedure, success,
+                                                  error ? *error : NULL);
+
+  if (success)
+    g_value_set_boolean (gimp_value_array_index (return_vals, 1), lock_position);
+
+  return return_vals;
+}
+
+static GimpValueArray *
+item_set_lock_position_invoker (GimpProcedure         *procedure,
+                                Gimp                  *gimp,
+                                GimpContext           *context,
+                                GimpProgress          *progress,
+                                const GimpValueArray  *args,
+                                GError               **error)
+{
+  gboolean success = TRUE;
+  GimpItem *item;
+  gboolean lock_position;
+
+  item = gimp_value_get_item (gimp_value_array_index (args, 0), gimp);
+  lock_position = g_value_get_boolean (gimp_value_array_index (args, 1));
+
+  if (success)
+    {
+      if (gimp_item_can_lock_position (GIMP_ITEM (item)))
+        gimp_item_set_lock_position (GIMP_ITEM (item), lock_position, TRUE);
+      else
+        success = FALSE;
+    }
+
+  return gimp_procedure_get_return_values (procedure, success,
+                                           error ? *error : NULL);
+}
+
+static GimpValueArray *
 item_get_tattoo_invoker (GimpProcedure         *procedure,
                          Gimp                  *gimp,
                          GimpContext           *context,
@@ -1443,6 +1499,64 @@ register_item_procs (GimpPDB *pdb)
   g_object_unref (procedure);
 
   /*
+   * gimp-item-get-lock-position
+   */
+  procedure = gimp_procedure_new (item_get_lock_position_invoker);
+  gimp_object_set_static_name (GIMP_OBJECT (procedure),
+                               "gimp-item-get-lock-position");
+  gimp_procedure_set_static_strings (procedure,
+                                     "gimp-item-get-lock-position",
+                                     "Get the 'lock position' state of the specified item.",
+                                     "This procedure returns the specified item's lock position state.",
+                                     "Michael Natterer <mitch gimp org>",
+                                     "Michael Natterer",
+                                     "2012",
+                                     NULL);
+  gimp_procedure_add_argument (procedure,
+                               gimp_param_spec_item_id ("item",
+                                                        "item",
+                                                        "The item",
+                                                        pdb->gimp, FALSE,
+                                                        GIMP_PARAM_READWRITE));
+  gimp_procedure_add_return_value (procedure,
+                                   g_param_spec_boolean ("lock-position",
+                                                         "lock position",
+                                                         "Whether the item's position is locked",
+                                                         FALSE,
+                                                         GIMP_PARAM_READWRITE));
+  gimp_pdb_register_procedure (pdb, procedure);
+  g_object_unref (procedure);
+
+  /*
+   * gimp-item-set-lock-position
+   */
+  procedure = gimp_procedure_new (item_set_lock_position_invoker);
+  gimp_object_set_static_name (GIMP_OBJECT (procedure),
+                               "gimp-item-set-lock-position");
+  gimp_procedure_set_static_strings (procedure,
+                                     "gimp-item-set-lock-position",
+                                     "Set the 'lock position' state of the specified item.",
+                                     "This procedure sets the specified item's lock position state.",
+                                     "Michael Natterer <mitch gimp org>",
+                                     "Michael Natterer",
+                                     "2009",
+                                     NULL);
+  gimp_procedure_add_argument (procedure,
+                               gimp_param_spec_item_id ("item",
+                                                        "item",
+                                                        "The item",
+                                                        pdb->gimp, FALSE,
+                                                        GIMP_PARAM_READWRITE));
+  gimp_procedure_add_argument (procedure,
+                               g_param_spec_boolean ("lock-position",
+                                                     "lock position",
+                                                     "The new item 'lock position' state",
+                                                     FALSE,
+                                                     GIMP_PARAM_READWRITE));
+  gimp_pdb_register_procedure (pdb, procedure);
+  g_object_unref (procedure);
+
+  /*
    * gimp-item-get-tattoo
    */
   procedure = gimp_procedure_new (item_get_tattoo_invoker);
diff --git a/app/pdb/item-transform-cmds.c b/app/pdb/item-transform-cmds.c
index 98c43d9..48f02a4 100644
--- a/app/pdb/item-transform-cmds.c
+++ b/app/pdb/item-transform-cmds.c
@@ -72,7 +72,8 @@ item_transform_flip_simple_invoker (GimpProcedure         *procedure,
       gint x, y, width, height;
 
       success = gimp_pdb_item_is_attached (item, NULL,
-                                           GIMP_PDB_ITEM_CONTENT, error);
+                                           GIMP_PDB_ITEM_CONTENT |
+                                           GIMP_PDB_ITEM_POSITION, error);
 
       if (success &&
           gimp_item_mask_intersect (item, &x, &y, &width, &height))
@@ -147,7 +148,8 @@ item_transform_flip_invoker (GimpProcedure         *procedure,
       gint x, y, width, height;
 
       success = gimp_pdb_item_is_attached (item, NULL,
-                                                        GIMP_PDB_ITEM_CONTENT, error);
+                                                        GIMP_PDB_ITEM_CONTENT |
+                                                        GIMP_PDB_ITEM_POSITION, error);
 
       if (success &&
           gimp_item_mask_intersect (item, &x, &y, &width, &height))
@@ -245,7 +247,8 @@ item_transform_perspective_invoker (GimpProcedure         *procedure,
       gint x, y, width, height;
 
       success = gimp_pdb_item_is_attached (item, NULL,
-                                                        GIMP_PDB_ITEM_CONTENT, error);
+                                                        GIMP_PDB_ITEM_CONTENT |
+                                                        GIMP_PDB_ITEM_POSITION, error);
 
       if (success &&
           gimp_item_mask_intersect (item, &x, &y, &width, &height))
@@ -338,7 +341,8 @@ item_transform_rotate_simple_invoker (GimpProcedure         *procedure,
       gint x, y, width, height;
 
       success = gimp_pdb_item_is_attached (item, NULL,
-                                           GIMP_PDB_ITEM_CONTENT, error);
+                                           GIMP_PDB_ITEM_CONTENT |
+                                           GIMP_PDB_ITEM_POSITION, error);
 
       if (success &&
           gimp_item_mask_intersect (item, &x, &y, &width, &height))
@@ -416,7 +420,8 @@ item_transform_rotate_invoker (GimpProcedure         *procedure,
       gint x, y, width, height;
 
       success = gimp_pdb_item_is_attached (item, NULL,
-                                                        GIMP_PDB_ITEM_CONTENT, error);
+                                                        GIMP_PDB_ITEM_CONTENT |
+                                                        GIMP_PDB_ITEM_POSITION, error);
 
       if (success &&
           gimp_item_mask_intersect (item, &x, &y, &width, &height))
@@ -511,7 +516,8 @@ item_transform_scale_invoker (GimpProcedure         *procedure,
       gint x, y, width, height;
 
       success = (gimp_pdb_item_is_attached (item, NULL,
-                                                          GIMP_PDB_ITEM_CONTENT, error) && x0 < x1 && y0 < y1);
+                                                          GIMP_PDB_ITEM_CONTENT |
+                                                          GIMP_PDB_ITEM_POSITION, error) && x0 < x1 && y0 < y1);
 
       if (success &&
           gimp_item_mask_intersect (item, &x, &y, &width, &height))
@@ -599,7 +605,8 @@ item_transform_shear_invoker (GimpProcedure         *procedure,
       gint x, y, width, height;
 
       success = gimp_pdb_item_is_attached (item, NULL,
-                                                        GIMP_PDB_ITEM_CONTENT, error);
+                                                        GIMP_PDB_ITEM_CONTENT |
+                                                        GIMP_PDB_ITEM_POSITION, error);
 
       if (success &&
           gimp_item_mask_intersect (item, &x, &y, &width, &height))
@@ -697,7 +704,8 @@ item_transform_2d_invoker (GimpProcedure         *procedure,
       gint x, y, width, height;
 
       success = gimp_pdb_item_is_attached (item, NULL,
-                                                        GIMP_PDB_ITEM_CONTENT, error);
+                                                        GIMP_PDB_ITEM_CONTENT |
+                                                        GIMP_PDB_ITEM_POSITION, error);
 
       if (success &&
           gimp_item_mask_intersect (item, &x, &y, &width, &height))
@@ -800,7 +808,8 @@ item_transform_matrix_invoker (GimpProcedure         *procedure,
       gint x, y, width, height;
 
       success = gimp_pdb_item_is_attached (item, NULL,
-                                                        GIMP_PDB_ITEM_CONTENT, error);
+                                                        GIMP_PDB_ITEM_CONTENT |
+                                                        GIMP_PDB_ITEM_POSITION, error);
 
       if (success &&
           gimp_item_mask_intersect (item, &x, &y, &width, &height))
diff --git a/app/pdb/layer-cmds.c b/app/pdb/layer-cmds.c
index adeec46..8031b9e 100644
--- a/app/pdb/layer-cmds.c
+++ b/app/pdb/layer-cmds.c
@@ -377,7 +377,8 @@ layer_scale_invoker (GimpProcedure         *procedure,
   if (success)
     {
       if (gimp_pdb_item_is_attached (GIMP_ITEM (layer), NULL,
-                                     GIMP_PDB_ITEM_CONTENT, error))
+                                     GIMP_PDB_ITEM_CONTENT | GIMP_PDB_ITEM_POSITION,
+                                     error))
         {
           GimpPDBContext *pdb_context = GIMP_PDB_CONTEXT (context);
 
@@ -425,7 +426,8 @@ layer_scale_full_invoker (GimpProcedure         *procedure,
   if (success)
     {
       if (gimp_pdb_item_is_attached (GIMP_ITEM (layer), NULL,
-                                     GIMP_PDB_ITEM_CONTENT, error))
+                                     GIMP_PDB_ITEM_CONTENT | GIMP_PDB_ITEM_POSITION,
+                                     error))
         {
           if (progress)
             gimp_progress_start (progress, _("Scaling"), FALSE);
@@ -471,7 +473,8 @@ layer_resize_invoker (GimpProcedure         *procedure,
   if (success)
     {
       if (gimp_pdb_item_is_attached (GIMP_ITEM (layer), NULL,
-                                     GIMP_PDB_ITEM_CONTENT, error))
+                                     GIMP_PDB_ITEM_CONTENT | GIMP_PDB_ITEM_POSITION,
+                                     error))
         gimp_item_resize (GIMP_ITEM (layer), context,
                           new_width, new_height, offx, offy);
       else
@@ -498,7 +501,8 @@ layer_resize_to_image_size_invoker (GimpProcedure         *procedure,
   if (success)
     {
       if (gimp_pdb_item_is_attached (GIMP_ITEM (layer), NULL,
-                                     GIMP_PDB_ITEM_CONTENT, error))
+                                     GIMP_PDB_ITEM_CONTENT | GIMP_PDB_ITEM_POSITION,
+                                     error))
         gimp_layer_resize_to_image (layer, context);
       else
         success = FALSE;
@@ -527,17 +531,23 @@ layer_translate_invoker (GimpProcedure         *procedure,
 
   if (success)
     {
-      GimpImage *image = gimp_item_get_image (GIMP_ITEM (layer));
+      if (gimp_pdb_item_is_modifyable (GIMP_ITEM (layer),
+                                       GIMP_PDB_ITEM_POSITION, error))
+        {
+          GimpImage *image = gimp_item_get_image (GIMP_ITEM (layer));
 
-      gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_DISPLACE,
-                                   _("Move Layer"));
+          gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_DISPLACE,
+                                       _("Move Layer"));
 
-      gimp_item_translate (GIMP_ITEM (layer), offx, offy, TRUE);
+          gimp_item_translate (GIMP_ITEM (layer), offx, offy, TRUE);
 
-      if (gimp_item_get_linked (GIMP_ITEM (layer)))
-        gimp_item_linked_translate (GIMP_ITEM (layer), offx, offy, TRUE);
+          if (gimp_item_get_linked (GIMP_ITEM (layer)))
+            gimp_item_linked_translate (GIMP_ITEM (layer), offx, offy, TRUE);
 
-      gimp_image_undo_group_end (image);
+          gimp_image_undo_group_end (image);
+        }
+      else
+        success = FALSE;
     }
 
   return gimp_procedure_get_return_values (procedure, success,
@@ -563,23 +573,29 @@ layer_set_offsets_invoker (GimpProcedure         *procedure,
 
   if (success)
     {
-      GimpImage *image = gimp_item_get_image (GIMP_ITEM (layer));
-      gint       offset_x;
-      gint       offset_y;
+      if (gimp_pdb_item_is_modifyable (GIMP_ITEM (layer),
+                                       GIMP_PDB_ITEM_POSITION, error))
+        {
+          GimpImage *image = gimp_item_get_image (GIMP_ITEM (layer));
+          gint       offset_x;
+          gint       offset_y;
 
-      gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_DISPLACE,
-                                   _("Move Layer"));
+          gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_DISPLACE,
+                                       _("Move Layer"));
 
-      gimp_item_get_offset (GIMP_ITEM (layer), &offset_x, &offset_y);
-      offx -= offset_x;
-      offy -= offset_y;
+          gimp_item_get_offset (GIMP_ITEM (layer), &offset_x, &offset_y);
+          offx -= offset_x;
+          offy -= offset_y;
 
-      gimp_item_translate (GIMP_ITEM (layer), offx, offy, TRUE);
+          gimp_item_translate (GIMP_ITEM (layer), offx, offy, TRUE);
 
-      if (gimp_item_get_linked (GIMP_ITEM (layer)))
-        gimp_item_linked_translate (GIMP_ITEM (layer), offx, offy, TRUE);
+          if (gimp_item_get_linked (GIMP_ITEM (layer)))
+            gimp_item_linked_translate (GIMP_ITEM (layer), offx, offy, TRUE);
 
-      gimp_image_undo_group_end (image);
+          gimp_image_undo_group_end (image);
+        }
+      else
+        success = FALSE;
     }
 
   return gimp_procedure_get_return_values (procedure, success,
diff --git a/app/pdb/pdb-types.h b/app/pdb/pdb-types.h
index e8d1271..e5a5529 100644
--- a/app/pdb/pdb-types.h
+++ b/app/pdb/pdb-types.h
@@ -38,7 +38,8 @@ typedef enum
 
 typedef enum
 {
-  GIMP_PDB_ITEM_CONTENT = 1 << 0
+  GIMP_PDB_ITEM_CONTENT  = 1 << 0,
+  GIMP_PDB_ITEM_POSITION = 1 << 1
 } GimpPDBItemModify;
 
 
diff --git a/app/pdb/transform-tools-cmds.c b/app/pdb/transform-tools-cmds.c
index 806f203..eb575d8 100644
--- a/app/pdb/transform-tools-cmds.c
+++ b/app/pdb/transform-tools-cmds.c
@@ -68,7 +68,8 @@ flip_invoker (GimpProcedure         *procedure,
       gint x, y, width, height;
 
       success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                           GIMP_PDB_ITEM_CONTENT, error);
+                                           GIMP_PDB_ITEM_CONTENT |
+                                           GIMP_PDB_ITEM_POSITION, error);
 
       if (success &&
           gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
@@ -141,7 +142,8 @@ perspective_invoker (GimpProcedure         *procedure,
       gint x, y, width, height;
 
       success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                           GIMP_PDB_ITEM_CONTENT, error);
+                                           GIMP_PDB_ITEM_CONTENT |
+                                           GIMP_PDB_ITEM_POSITION, error);
 
       if (success &&
           gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
@@ -225,7 +227,8 @@ rotate_invoker (GimpProcedure         *procedure,
       gint x, y, width, height;
 
       success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                           GIMP_PDB_ITEM_CONTENT, error);
+                                           GIMP_PDB_ITEM_CONTENT |
+                                           GIMP_PDB_ITEM_POSITION, error);
 
       if (success &&
           gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
@@ -314,7 +317,8 @@ scale_invoker (GimpProcedure         *procedure,
       gint x, y, width, height;
 
       success = (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                            GIMP_PDB_ITEM_CONTENT, error) &&
+                                            GIMP_PDB_ITEM_CONTENT |
+                                            GIMP_PDB_ITEM_POSITION, error) &&
                  x0 < x1 && y0 < y1);
 
       if (success &&
@@ -400,7 +404,8 @@ shear_invoker (GimpProcedure         *procedure,
       gint x, y, width, height;
 
       success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                           GIMP_PDB_ITEM_CONTENT, error);
+                                           GIMP_PDB_ITEM_CONTENT |
+                                           GIMP_PDB_ITEM_POSITION, error);
 
       if (success &&
           gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
@@ -495,7 +500,8 @@ transform_2d_invoker (GimpProcedure         *procedure,
       gint x, y, width, height;
 
       success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                           GIMP_PDB_ITEM_CONTENT, error);
+                                           GIMP_PDB_ITEM_CONTENT |
+                                           GIMP_PDB_ITEM_POSITION, error);
 
       if (success &&
           gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
diff --git a/app/pdb/vectors-cmds.c b/app/pdb/vectors-cmds.c
index 3a0dd01..5adf500 100644
--- a/app/pdb/vectors-cmds.c
+++ b/app/pdb/vectors-cmds.c
@@ -101,7 +101,7 @@ vectors_new_from_text_layer_invoker (GimpProcedure         *procedure,
 
   if (success)
     {
-      if (gimp_pdb_layer_is_text_layer (layer, FALSE, error))
+      if (gimp_pdb_layer_is_text_layer (layer, 0, error))
         {
           gint x, y;
 
@@ -397,7 +397,9 @@ vectors_stroke_translate_invoker (GimpProcedure         *procedure,
   if (success)
     {
       GimpStroke *stroke = gimp_pdb_get_vectors_stroke (vectors, stroke_id,
-                                                        GIMP_PDB_ITEM_CONTENT, error);
+                                                        GIMP_PDB_ITEM_CONTENT |
+                                                        GIMP_PDB_ITEM_POSITION,
+                                                        error);
 
       if (stroke)
         {
@@ -438,7 +440,9 @@ vectors_stroke_scale_invoker (GimpProcedure         *procedure,
   if (success)
     {
       GimpStroke *stroke = gimp_pdb_get_vectors_stroke (vectors, stroke_id,
-                                                        GIMP_PDB_ITEM_CONTENT, error);
+                                                        GIMP_PDB_ITEM_CONTENT |
+                                                        GIMP_PDB_ITEM_POSITION,
+                                                        error);
 
       if (stroke)
         {
@@ -481,7 +485,9 @@ vectors_stroke_rotate_invoker (GimpProcedure         *procedure,
   if (success)
     {
       GimpStroke *stroke = gimp_pdb_get_vectors_stroke (vectors, stroke_id,
-                                                        GIMP_PDB_ITEM_CONTENT, error);
+                                                        GIMP_PDB_ITEM_CONTENT |
+                                                        GIMP_PDB_ITEM_POSITION,
+                                                        error);
 
       if (stroke)
         {
@@ -522,7 +528,9 @@ vectors_stroke_flip_invoker (GimpProcedure         *procedure,
   if (success)
     {
       GimpStroke *stroke = gimp_pdb_get_vectors_stroke (vectors, stroke_id,
-                                                        GIMP_PDB_ITEM_CONTENT, error);
+                                                        GIMP_PDB_ITEM_CONTENT |
+                                                        GIMP_PDB_ITEM_POSITION,
+                                                        error);
 
       if (stroke)
         {
@@ -567,7 +575,9 @@ vectors_stroke_flip_free_invoker (GimpProcedure         *procedure,
   if (success)
     {
       GimpStroke *stroke = gimp_pdb_get_vectors_stroke (vectors, stroke_id,
-                                                        GIMP_PDB_ITEM_CONTENT, error);
+                                                        GIMP_PDB_ITEM_CONTENT |
+                                                        GIMP_PDB_ITEM_POSITION,
+                                                        error);
 
       if (stroke)
         {
diff --git a/app/tools/gimpmovetool.c b/app/tools/gimpmovetool.c
index 7ace036..e71daf0 100644
--- a/app/tools/gimpmovetool.c
+++ b/app/tools/gimpmovetool.c
@@ -182,10 +182,13 @@ gimp_move_tool_button_press (GimpTool            *tool,
                              GimpButtonPressType  press_type,
                              GimpDisplay         *display)
 {
-  GimpMoveTool     *move    = GIMP_MOVE_TOOL (tool);
-  GimpMoveOptions  *options = GIMP_MOVE_TOOL_GET_OPTIONS (tool);
-  GimpDisplayShell *shell   = gimp_display_get_shell (display);
-  GimpImage        *image   = gimp_display_get_image (display);
+  GimpMoveTool     *move           = GIMP_MOVE_TOOL (tool);
+  GimpMoveOptions  *options        = GIMP_MOVE_TOOL_GET_OPTIONS (tool);
+  GimpDisplayShell *shell          = gimp_display_get_shell (display);
+  GimpImage        *image          = gimp_display_get_image (display);
+  GimpItem         *active_item    = NULL;
+  const gchar      *null_message   = NULL;
+  const gchar      *locked_message = NULL;
 
   tool->display = display;
 
@@ -289,48 +292,103 @@ gimp_move_tool_button_press (GimpTool            *tool,
   switch (options->move_type)
     {
     case GIMP_TRANSFORM_TYPE_PATH:
-      if (gimp_image_get_active_vectors (image))
-        {
-          gimp_tool_control_activate (tool->control);
-          gimp_edit_selection_tool_start (tool, display, coords,
-                                          GIMP_TRANSLATE_MODE_VECTORS, TRUE);
-        }
+      {
+        active_item    = GIMP_ITEM (gimp_image_get_active_vectors (image));
+        null_message   = _("There is no path to move.");
+        locked_message = _("The active path's position is locked.");
+
+        if (active_item && ! gimp_item_is_position_locked (active_item))
+          {
+            gimp_tool_control_activate (tool->control);
+            gimp_edit_selection_tool_start (tool, display, coords,
+                                            GIMP_TRANSLATE_MODE_VECTORS,
+                                            TRUE);
+            return;
+          }
+      }
       break;
 
     case GIMP_TRANSFORM_TYPE_SELECTION:
-      if (! gimp_channel_is_empty (gimp_image_get_mask (image)))
-        {
-          gimp_tool_control_activate (tool->control);
-          gimp_edit_selection_tool_start (tool, display, coords,
-                                          GIMP_TRANSLATE_MODE_MASK, TRUE);
-        }
+      {
+        active_item    = GIMP_ITEM (gimp_image_get_mask (image));
+        /* cannot happen, so don't translate these messages */
+        null_message   = "There is no selection to move.";
+        locked_message = "The selection's position is locked.";
+
+        if (active_item && ! gimp_item_is_position_locked (active_item))
+          {
+            if (! gimp_channel_is_empty (gimp_image_get_mask (image)))
+              {
+                gimp_tool_control_activate (tool->control);
+                gimp_edit_selection_tool_start (tool, display, coords,
+                                                GIMP_TRANSLATE_MODE_MASK,
+                                                TRUE);
+                return;
+              }
+            else
+              locked_message = _("The selection is empty.");
+          }
+      }
       break;
 
     case GIMP_TRANSFORM_TYPE_LAYER:
       {
-        GimpDrawable *drawable = gimp_image_get_active_drawable (image);
+        active_item  = GIMP_ITEM (gimp_image_get_active_drawable (image));
+        null_message = _("There is no layer to move.");
 
-        if (GIMP_IS_LAYER_MASK (drawable))
+        if (GIMP_IS_LAYER_MASK (active_item))
           {
-            gimp_tool_control_activate (tool->control);
-            gimp_edit_selection_tool_start (tool, display, coords,
-                                            GIMP_TRANSLATE_MODE_LAYER_MASK, TRUE);
+            locked_message = _("The active layer's position is locked.");
+
+            if (! gimp_item_is_position_locked (active_item))
+              {
+                gimp_tool_control_activate (tool->control);
+                gimp_edit_selection_tool_start (tool, display, coords,
+                                                GIMP_TRANSLATE_MODE_LAYER_MASK,
+                                                TRUE);
+                return;
+              }
           }
-        else if (GIMP_IS_CHANNEL (drawable))
+        else if (GIMP_IS_CHANNEL (active_item))
           {
-            gimp_tool_control_activate (tool->control);
-            gimp_edit_selection_tool_start (tool, display, coords,
-                                            GIMP_TRANSLATE_MODE_CHANNEL, TRUE);
+            locked_message = _("The active channel's position is locked.");
+
+            if (! gimp_item_is_position_locked (active_item))
+              {
+                gimp_tool_control_activate (tool->control);
+                gimp_edit_selection_tool_start (tool, display, coords,
+                                                GIMP_TRANSLATE_MODE_CHANNEL,
+                                                TRUE);
+                return;
+              }
           }
-        else if (GIMP_IS_LAYER (drawable))
+        else if (GIMP_IS_LAYER (active_item))
           {
-            gimp_tool_control_activate (tool->control);
-            gimp_edit_selection_tool_start (tool, display, coords,
-                                            GIMP_TRANSLATE_MODE_LAYER, TRUE);
+            locked_message = _("The active layer's position is locked.");
+
+            if (! gimp_item_is_position_locked (active_item))
+              {
+                gimp_tool_control_activate (tool->control);
+                gimp_edit_selection_tool_start (tool, display, coords,
+                                                GIMP_TRANSLATE_MODE_LAYER,
+                                                TRUE);
+                return;
+              }
           }
       }
       break;
     }
+
+  if (! active_item)
+    {
+      gimp_tool_message_literal (tool, display, null_message);
+      gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display);
+    }
+  else
+    {
+      gimp_tool_message_literal (tool, display, locked_message);
+      gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display);
+    }
 }
 
 static void
@@ -693,7 +751,9 @@ gimp_move_tool_cursor_update (GimpTool         *tool,
 
       if (options->move_current)
         {
-          if (! gimp_image_get_active_vectors (image))
+          GimpItem *item = GIMP_ITEM (gimp_image_get_active_vectors (image));
+
+          if (! item || gimp_item_is_position_locked (item))
             modifier = GIMP_CURSOR_MODIFIER_BAD;
         }
       else
@@ -720,7 +780,9 @@ gimp_move_tool_cursor_update (GimpTool         *tool,
     }
   else if (options->move_current)
     {
-      if (! gimp_image_get_active_drawable (image))
+      GimpItem *item = GIMP_ITEM (gimp_image_get_active_drawable (image));
+
+      if (! item || gimp_item_is_position_locked (item))
         modifier = GIMP_CURSOR_MODIFIER_BAD;
     }
   else
@@ -746,10 +808,14 @@ gimp_move_tool_cursor_update (GimpTool         *tool,
               tool_cursor = GIMP_TOOL_CURSOR_MOVE;
               modifier    = GIMP_CURSOR_MODIFIER_ANCHOR;
             }
+          else if (gimp_item_is_position_locked (GIMP_ITEM (layer)))
+            {
+              modifier = GIMP_CURSOR_MODIFIER_BAD;
+            }
           else if (layer != gimp_image_get_active_layer (image))
             {
               tool_cursor = GIMP_TOOL_CURSOR_HAND;
-              modifier    = GIMP_CURSOR_MODIFIER_MOVE;
+	      modifier    = GIMP_CURSOR_MODIFIER_MOVE;
             }
         }
       else
diff --git a/app/tools/gimptransformtool.c b/app/tools/gimptransformtool.c
index 3664086..c6dad96 100644
--- a/app/tools/gimptransformtool.c
+++ b/app/tools/gimptransformtool.c
@@ -260,7 +260,14 @@ gimp_transform_tool_initialize (GimpTool     *tool,
   if (gimp_item_is_content_locked (GIMP_ITEM (drawable)))
     {
       g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
-			   _("The active layer's pixels are locked."));
+                           _("The active layer's pixels are locked."));
+      return FALSE;
+    }
+
+  if (gimp_item_is_position_locked (GIMP_ITEM (drawable)))
+    {
+      g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
+                           _("The active layer's position and size are locked."));
       return FALSE;
     }
 
@@ -729,11 +736,13 @@ gimp_transform_tool_cursor_update (GimpTool         *tool,
 
   switch (options->type)
     {
-      GimpDrawable *drawable;
+      GimpDrawable *drawable = NULL;
+      GimpVectors  *vectors  = NULL;
 
     case GIMP_TRANSFORM_TYPE_LAYER:
       drawable = gimp_image_get_active_drawable (image);
-      if (gimp_item_is_content_locked (GIMP_ITEM (drawable)))
+      if (gimp_item_is_content_locked (GIMP_ITEM (drawable)) ||
+          gimp_item_is_position_locked (GIMP_ITEM (drawable)))
         modifier = GIMP_CURSOR_MODIFIER_BAD;
       break;
 
@@ -741,7 +750,10 @@ gimp_transform_tool_cursor_update (GimpTool         *tool,
       break;
 
     case GIMP_TRANSFORM_TYPE_PATH:
-      if (! gimp_image_get_active_vectors (image))
+      vectors = gimp_image_get_active_vectors (image);
+      if (! vectors ||
+          gimp_item_is_content_locked (GIMP_ITEM (vectors)) ||
+          gimp_item_is_position_locked (GIMP_ITEM (vectors)))
         modifier = GIMP_CURSOR_MODIFIER_BAD;
       break;
     }
@@ -1218,22 +1230,34 @@ gimp_transform_tool_transform (GimpTransformTool *tr_tool,
   switch (options->type)
     {
     case GIMP_TRANSFORM_TYPE_LAYER:
-      active_item = GIMP_ITEM (gimp_image_get_active_drawable (image));
-      null_message   = _("There is no layer to transform.");
-      locked_message = _("The active layer's pixels are locked.");
+      active_item  = GIMP_ITEM (gimp_image_get_active_drawable (image));
+      null_message = _("There is no layer to transform.");
+
+      if (gimp_item_is_content_locked (active_item))
+        locked_message = _("The active layer's pixels are locked.");
+      else
+        locked_message = _("The active layer's position and size are locked.");
       break;
 
     case GIMP_TRANSFORM_TYPE_SELECTION:
-      active_item = GIMP_ITEM (gimp_image_get_mask (image));
+      active_item  = GIMP_ITEM (gimp_image_get_mask (image));
       /* cannot happen, so don't translate these messages */
-      null_message   = "There is no selection to transform.";
-      locked_message = "The selection's pixels are locked.";
+      null_message = "There is no selection to transform.";
+
+      if (gimp_item_is_content_locked (active_item))
+        locked_message = "The selection's pixels are locked.";
+      else
+        locked_message = "The selection's position and size are locked.";
       break;
 
     case GIMP_TRANSFORM_TYPE_PATH:
-      active_item = GIMP_ITEM (gimp_image_get_active_vectors (image));
-      null_message   = _("There is no path to transform.");
-      locked_message = _("The active path's strokes are locked.");
+      active_item  = GIMP_ITEM (gimp_image_get_active_vectors (image));
+      null_message = _("There is no path to transform.");
+
+      if (gimp_item_is_content_locked (active_item))
+        locked_message = _("The active path's strokes are locked.");
+      else
+        locked_message = _("The active path's position are locked.");
       break;
     }
 
@@ -1244,7 +1268,8 @@ gimp_transform_tool_transform (GimpTransformTool *tr_tool,
       return;
     }
 
-  if (gimp_item_is_content_locked (active_item))
+  if (gimp_item_is_content_locked (active_item) ||
+      gimp_item_is_position_locked (active_item))
     {
       gimp_tool_message_literal (tool, display, locked_message);
       gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display);
diff --git a/app/tools/gimpvectortool.c b/app/tools/gimpvectortool.c
index e18d07f..980b10e 100644
--- a/app/tools/gimpvectortool.c
+++ b/app/tools/gimpvectortool.c
@@ -243,7 +243,8 @@ gimp_vector_tool_control (GimpTool       *tool,
 static gboolean
 gimp_vector_tool_check_writable (GimpVectorTool *vector_tool)
 {
-  if (gimp_item_is_content_locked (GIMP_ITEM (vector_tool->vectors)))
+  if (gimp_item_is_content_locked (GIMP_ITEM (vector_tool->vectors)) ||
+      gimp_item_is_position_locked (GIMP_ITEM (vector_tool->vectors)))
     {
       gimp_tool_message_literal (GIMP_TOOL (vector_tool),
                                  GIMP_TOOL (vector_tool)->display,
diff --git a/app/widgets/gimpchanneltreeview.c b/app/widgets/gimpchanneltreeview.c
index 458c749..bc72dee 100644
--- a/app/widgets/gimpchanneltreeview.c
+++ b/app/widgets/gimpchanneltreeview.c
@@ -113,17 +113,19 @@ gimp_channel_tree_view_class_init (GimpChannelTreeViewClass *klass)
   iv_class->remove_item      = (GimpRemoveItemFunc) gimp_image_remove_channel;
   iv_class->new_item         = gimp_channel_tree_view_item_new;
 
-  iv_class->action_group        = "channels";
-  iv_class->activate_action     = "channels-edit-attributes";
-  iv_class->edit_action         = "channels-edit-attributes";
-  iv_class->new_action          = "channels-new";
-  iv_class->new_default_action  = "channels-new-last-values";
-  iv_class->raise_action        = "channels-raise";
-  iv_class->raise_top_action    = "channels-raise-to-top";
-  iv_class->lower_action        = "channels-lower";
-  iv_class->lower_bottom_action = "channels-lower-to-bottom";
-  iv_class->duplicate_action    = "channels-duplicate";
-  iv_class->delete_action       = "channels-delete";
+  iv_class->action_group          = "channels";
+  iv_class->activate_action       = "channels-edit-attributes";
+  iv_class->edit_action           = "channels-edit-attributes";
+  iv_class->new_action            = "channels-new";
+  iv_class->new_default_action    = "channels-new-last-values";
+  iv_class->raise_action          = "channels-raise";
+  iv_class->raise_top_action      = "channels-raise-to-top";
+  iv_class->lower_action          = "channels-lower";
+  iv_class->lower_bottom_action   = "channels-lower-to-bottom";
+  iv_class->duplicate_action      = "channels-duplicate";
+  iv_class->delete_action         = "channels-delete";
+  iv_class->lock_content_help_id  = GIMP_HELP_CHANNEL_LOCK_PIXELS;
+  iv_class->lock_position_help_id = GIMP_HELP_CHANNEL_LOCK_POSITION;
 
   g_type_class_add_private (klass, sizeof (GimpChannelTreeViewPriv));
 }
diff --git a/app/widgets/gimpdrawabletreeview.c b/app/widgets/gimpdrawabletreeview.c
index 2c1f60f..ab8d4c6 100644
--- a/app/widgets/gimpdrawabletreeview.c
+++ b/app/widgets/gimpdrawabletreeview.c
@@ -117,8 +117,10 @@ gimp_drawable_tree_view_class_init (GimpDrawableTreeViewClass *klass)
 
   item_view_class->set_image     = gimp_drawable_tree_view_set_image;
 
-  item_view_class->lock_content_stock_id = GIMP_STOCK_TOOL_PAINTBRUSH;
-  item_view_class->lock_content_tooltip  = _("Lock pixels");
+  item_view_class->lock_content_stock_id  = GIMP_STOCK_TOOL_PAINTBRUSH;
+  item_view_class->lock_content_tooltip   = _("Lock pixels");
+  item_view_class->lock_position_stock_id = GIMP_STOCK_TOOL_MOVE;
+  item_view_class->lock_position_tooltip  = _("Lock position and size");
 }
 
 static void
diff --git a/app/widgets/gimphelp-ids.h b/app/widgets/gimphelp-ids.h
index 77d43b4..981226a 100644
--- a/app/widgets/gimphelp-ids.h
+++ b/app/widgets/gimphelp-ids.h
@@ -143,7 +143,6 @@
 #define GIMP_HELP_LAYER_DIALOG                    "gimp-layer-dialog"
 #define GIMP_HELP_LAYER_DIALOG_PAINT_MODE_MENU    "gimp-layer-dialog-paint-mode-menu"
 #define GIMP_HELP_LAYER_DIALOG_OPACITY_SCALE      "gimp-layer-dialog-opacity-scale"
-#define GIMP_HELP_LAYER_DIALOG_LOCK_ALPHA_BUTTON  "gimp-layer-dialog-lock-alpha-button"
 
 #define GIMP_HELP_LAYER_NEW                       "gimp-layer-new"
 #define GIMP_HELP_LAYER_NEW_FROM_VISIBLE          "gimp-layer-new-from-visible"
@@ -171,6 +170,8 @@
 #define GIMP_HELP_LAYER_OPACITY                   "gimp-layer-opacity"
 #define GIMP_HELP_LAYER_MODE                      "gimp-layer-mode"
 #define GIMP_HELP_LAYER_LOCK_ALPHA                "gimp-layer-lock-alpha"
+#define GIMP_HELP_LAYER_LOCK_PIXELS               "gimp-layer-lock-pixels"
+#define GIMP_HELP_LAYER_LOCK_POSITION             "gimp-layer-lock-position"
 #define GIMP_HELP_LAYER_MASK_ADD                  "gimp-layer-mask-add"
 #define GIMP_HELP_LAYER_MASK_APPLY                "gimp-layer-mask-apply"
 #define GIMP_HELP_LAYER_MASK_DELETE               "gimp-layer-mask-delete"
@@ -207,6 +208,8 @@
 #define GIMP_HELP_CHANNEL_LOWER_TO_BOTTOM         "gimp-channel-lower-to-bottom"
 #define GIMP_HELP_CHANNEL_DUPLICATE               "gimp-channel-duplicate"
 #define GIMP_HELP_CHANNEL_DELETE                  "gimp-channel-delete"
+#define GIMP_HELP_CHANNEL_LOCK_PIXELS             "gimp-channel-lock-pixels"
+#define GIMP_HELP_CHANNEL_LOCK_POSITION           "gimp-channel-lock-position"
 #define GIMP_HELP_CHANNEL_SELECTION_REPLACE       "gimp-channel-selection-replace"
 #define GIMP_HELP_CHANNEL_SELECTION_ADD           "gimp-channel-selection-add"
 #define GIMP_HELP_CHANNEL_SELECTION_SUBTRACT      "gimp-channel-selection-subtract"
@@ -229,6 +232,8 @@
 #define GIMP_HELP_PATH_MERGE_VISIBLE              "gimp-path-merge-visible"
 #define GIMP_HELP_PATH_VISIBLE                    "gimp-path-visible"
 #define GIMP_HELP_PATH_LINKED                     "gimp-path-linked"
+#define GIMP_HELP_PATH_LOCK_STROKES               "gimp-path-lock-strokes"
+#define GIMP_HELP_PATH_LOCK_POSITION              "gimp-path-lock-position"
 #define GIMP_HELP_PATH_SELECTION_REPLACE          "gimp-path-selection-replace"
 #define GIMP_HELP_PATH_SELECTION_ADD              "gimp-path-selection-add"
 #define GIMP_HELP_PATH_SELECTION_SUBTRACT         "gimp-path-selection-subtract"
diff --git a/app/widgets/gimpitemtreeview.c b/app/widgets/gimpitemtreeview.c
index 4caa6c8..cd8d0bd 100644
--- a/app/widgets/gimpitemtreeview.c
+++ b/app/widgets/gimpitemtreeview.c
@@ -74,6 +74,7 @@ struct _GimpItemTreeViewPriv
   GtkWidget       *lock_box;
 
   GtkWidget       *lock_content_toggle;
+  GtkWidget       *lock_position_toggle;
 
   GtkWidget       *edit_button;
   GtkWidget       *new_button;
@@ -91,6 +92,7 @@ struct _GimpItemTreeViewPriv
   GimpTreeHandler *visible_changed_handler;
   GimpTreeHandler *linked_changed_handler;
   GimpTreeHandler *lock_content_changed_handler;
+  GimpTreeHandler *lock_position_changed_handler;
 
   gboolean         inserting_item; /* EEK */
 };
@@ -169,6 +171,8 @@ static void   gimp_item_tree_view_linked_changed       (GimpItem          *item,
                                                         GimpItemTreeView  *view);
 static void   gimp_item_tree_view_lock_content_changed (GimpItem          *item,
                                                         GimpItemTreeView  *view);
+static void   gimp_item_tree_view_lock_position_changed(GimpItem          *item,
+                                                        GimpItemTreeView  *view);
 
 static void   gimp_item_tree_view_eye_clicked       (GtkCellRendererToggle *toggle,
                                                      gchar             *path,
@@ -181,7 +185,9 @@ static void   gimp_item_tree_view_chain_clicked     (GtkCellRendererToggle *togg
 static void   gimp_item_tree_view_lock_content_toggled
                                                     (GtkWidget         *widget,
                                                      GimpItemTreeView  *view);
-
+static void   gimp_item_tree_view_lock_position_toggled
+                                                    (GtkWidget         *widget,
+                                                     GimpItemTreeView  *view);
 static void   gimp_item_tree_view_update_options    (GimpItemTreeView  *view,
                                                      GimpItem          *item);
 
@@ -271,6 +277,10 @@ gimp_item_tree_view_class_init (GimpItemTreeViewClass *klass)
   klass->lock_content_tooltip    = NULL;
   klass->lock_content_help_id    = NULL;
 
+  klass->lock_position_stock_id   = NULL;
+  klass->lock_position_tooltip    = NULL;
+  klass->lock_position_help_id    = NULL;
+
   g_type_class_add_private (klass, sizeof (GimpItemTreeViewPriv));
 }
 
@@ -451,11 +461,9 @@ gimp_item_tree_view_constructed (GObject *object)
                                   GTK_BUTTON (item_view->priv->delete_button),
                                   item_view_class->item_type);
 
-
-  /*  Lock content toggle  */
-
   hbox = gimp_item_tree_view_get_lock_box (item_view);
 
+  /*  Lock content toggle  */
   item_view->priv->lock_content_toggle = gtk_toggle_button_new ();
   gtk_box_pack_start (GTK_BOX (hbox), item_view->priv->lock_content_toggle,
                       FALSE, FALSE, 0);
@@ -480,6 +488,28 @@ gimp_item_tree_view_constructed (GObject *object)
   gtk_container_add (GTK_CONTAINER (item_view->priv->lock_content_toggle),
                      image);
   gtk_widget_show (image);
+
+  /*  Lock position toggle  */
+  item_view->priv->lock_position_toggle = gtk_toggle_button_new ();
+  gtk_box_pack_start (GTK_BOX (hbox), item_view->priv->lock_position_toggle,
+                      FALSE, FALSE, 0);
+  gtk_box_reorder_child (GTK_BOX (hbox),
+                         item_view->priv->lock_position_toggle, 1);
+  gtk_widget_show (item_view->priv->lock_position_toggle);
+
+  g_signal_connect (item_view->priv->lock_position_toggle, "toggled",
+                    G_CALLBACK (gimp_item_tree_view_lock_position_toggled),
+                    item_view);
+
+  gimp_help_set_help_data (item_view->priv->lock_position_toggle,
+                           item_view_class->lock_position_tooltip,
+                           item_view_class->lock_position_help_id);
+
+  image = gtk_image_new_from_stock (item_view_class->lock_position_stock_id,
+                                    icon_size);
+  gtk_container_add (GTK_CONTAINER (item_view->priv->lock_position_toggle),
+                     image);
+  gtk_widget_show (image);
 }
 
 static void
@@ -862,6 +892,9 @@ gimp_item_tree_view_set_container (GimpContainerView *view,
 
       gimp_tree_handler_disconnect (item_view->priv->lock_content_changed_handler);
       item_view->priv->lock_content_changed_handler = NULL;
+
+      gimp_tree_handler_disconnect (item_view->priv->lock_position_changed_handler);
+      item_view->priv->lock_position_changed_handler = NULL;
     }
 
   parent_view_iface->set_container (view, container);
@@ -882,6 +915,11 @@ gimp_item_tree_view_set_container (GimpContainerView *view,
         gimp_tree_handler_connect (container, "lock-content-changed",
                                    G_CALLBACK (gimp_item_tree_view_lock_content_changed),
                                    view);
+
+      item_view->priv->lock_position_changed_handler =
+        gimp_tree_handler_connect (container, "lock-position-changed",
+                                   G_CALLBACK (gimp_item_tree_view_lock_position_changed),
+                                   view);
     }
 }
 
@@ -1393,6 +1431,61 @@ gimp_item_tree_view_lock_content_toggled (GtkWidget         *widget,
     }
 }
 
+static void
+gimp_item_tree_view_lock_position_changed (GimpItem         *item,
+                                           GimpItemTreeView *view)
+{
+  GimpImage *image = view->priv->image;
+  GimpItem  *active_item;
+
+  active_item = GIMP_ITEM_TREE_VIEW_GET_CLASS (view)->get_active_item (image);
+
+  if (active_item == item)
+    gimp_item_tree_view_update_options (view, item);
+}
+
+static void
+gimp_item_tree_view_lock_position_toggled (GtkWidget         *widget,
+                                           GimpItemTreeView  *view)
+{
+  GimpImage *image = view->priv->image;
+  GimpItem  *item;
+
+  item = GIMP_ITEM_TREE_VIEW_GET_CLASS (view)->get_active_item (image);
+
+  if (item)
+    {
+      gboolean lock_position;
+
+      lock_position = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+
+      if (gimp_item_get_lock_position (item) != lock_position)
+        {
+          GimpUndo *undo;
+          gboolean  push_undo = TRUE;
+
+          /*  compress lock position undos  */
+          undo = gimp_image_undo_can_compress (image, GIMP_TYPE_ITEM_UNDO,
+                                               GIMP_UNDO_ITEM_LOCK_POSITION);
+
+          if (undo && GIMP_ITEM_UNDO (undo)->item == item)
+            push_undo = FALSE;
+
+          g_signal_handlers_block_by_func (item,
+                                           gimp_item_tree_view_lock_position_changed,
+                                           view);
+
+          gimp_item_set_lock_position (item, lock_position, push_undo);
+
+          g_signal_handlers_unblock_by_func (item,
+                                             gimp_item_tree_view_lock_position_changed,
+                                             view);
+
+          gimp_image_flush (image);
+        }
+    }
+}
+
 static gboolean
 gimp_item_tree_view_item_pre_clicked (GimpCellRendererViewable *cell,
                                       const gchar              *path_str,
@@ -1407,7 +1500,7 @@ gimp_item_tree_view_item_pre_clicked (GimpCellRendererViewable *cell,
   path = gtk_tree_path_new_from_string (path_str);
 
   if (gtk_tree_model_get_iter (tree_view->model, &iter, path) &&
-      state & GDK_MOD1_MASK)
+      (state & GDK_MOD1_MASK))
     {
       GimpImage        *image    = gimp_item_tree_view_get_image (item_view);
       GimpViewRenderer *renderer = NULL;
@@ -1456,8 +1549,26 @@ gimp_item_tree_view_update_options (GimpItemTreeView *view,
                                          view);
     }
 
+  if (gimp_item_get_lock_position (item) !=
+      gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (view->priv->lock_position_toggle)))
+    {
+      g_signal_handlers_block_by_func (view->priv->lock_position_toggle,
+                                       gimp_item_tree_view_lock_position_toggled,
+                                       view);
+
+      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (view->priv->lock_position_toggle),
+                                    gimp_item_get_lock_position (item));
+
+      g_signal_handlers_unblock_by_func (view->priv->lock_position_toggle,
+                                         gimp_item_tree_view_lock_position_toggled,
+                                         view);
+    }
+
   gtk_widget_set_sensitive (view->priv->lock_content_toggle,
                             gimp_item_can_lock_content (item));
+
+  gtk_widget_set_sensitive (view->priv->lock_position_toggle,
+                            gimp_item_can_lock_position (item));
 }
 
 
diff --git a/app/widgets/gimpitemtreeview.h b/app/widgets/gimpitemtreeview.h
index f9ab1b1..75d8e23 100644
--- a/app/widgets/gimpitemtreeview.h
+++ b/app/widgets/gimpitemtreeview.h
@@ -95,6 +95,11 @@ struct _GimpItemTreeViewClass
   const gchar          *lock_content_stock_id;
   const gchar          *lock_content_tooltip;
   const gchar          *lock_content_help_id;
+
+  /* lock position (translation and transformation) button appearance */
+  const gchar          *lock_position_stock_id;
+  const gchar          *lock_position_tooltip;
+  const gchar          *lock_position_help_id;
 };
 
 
diff --git a/app/widgets/gimplayertreeview.c b/app/widgets/gimplayertreeview.c
index 6946a32..d48108e 100644
--- a/app/widgets/gimplayertreeview.c
+++ b/app/widgets/gimplayertreeview.c
@@ -208,17 +208,19 @@ gimp_layer_tree_view_class_init (GimpLayerTreeViewClass *klass)
   item_view_class->remove_item     = (GimpRemoveItemFunc) gimp_image_remove_layer;
   item_view_class->new_item        = gimp_layer_tree_view_item_new;
 
-  item_view_class->action_group        = "layers";
-  item_view_class->activate_action     = "layers-text-tool";
-  item_view_class->edit_action         = "layers-edit-attributes";
-  item_view_class->new_action          = "layers-new";
-  item_view_class->new_default_action  = "layers-new-last-values";
-  item_view_class->raise_action        = "layers-raise";
-  item_view_class->raise_top_action    = "layers-raise-to-top";
-  item_view_class->lower_action        = "layers-lower";
-  item_view_class->lower_bottom_action = "layers-lower-to-bottom";
-  item_view_class->duplicate_action    = "layers-duplicate";
-  item_view_class->delete_action       = "layers-delete";
+  item_view_class->action_group          = "layers";
+  item_view_class->activate_action       = "layers-text-tool";
+  item_view_class->edit_action           = "layers-edit-attributes";
+  item_view_class->new_action            = "layers-new";
+  item_view_class->new_default_action    = "layers-new-last-values";
+  item_view_class->raise_action          = "layers-raise";
+  item_view_class->raise_top_action      = "layers-raise-to-top";
+  item_view_class->lower_action          = "layers-lower";
+  item_view_class->lower_bottom_action   = "layers-lower-to-bottom";
+  item_view_class->duplicate_action      = "layers-duplicate";
+  item_view_class->delete_action         = "layers-delete";
+  item_view_class->lock_content_help_id  = GIMP_HELP_LAYER_LOCK_PIXELS;
+  item_view_class->lock_position_help_id = GIMP_HELP_LAYER_LOCK_POSITION;
 
   g_type_class_add_private (klass, sizeof (GimpLayerTreeViewPriv));
 }
@@ -304,8 +306,9 @@ gimp_layer_tree_view_init (GimpLayerTreeView *view)
                     G_CALLBACK (gimp_layer_tree_view_lock_alpha_button_toggled),
                     view);
 
-  gimp_help_set_help_data (view->priv->lock_alpha_toggle, _("Lock alpha channel"),
-                           GIMP_HELP_LAYER_DIALOG_LOCK_ALPHA_BUTTON);
+  gimp_help_set_help_data (view->priv->lock_alpha_toggle,
+                           _("Lock alpha channel"),
+                           GIMP_HELP_LAYER_LOCK_ALPHA);
 
   gtk_widget_style_get (GTK_WIDGET (view),
                         "button-icon-size", &icon_size,
diff --git a/app/widgets/gimpvectorstreeview.c b/app/widgets/gimpvectorstreeview.c
index ae4da93..eb80093 100644
--- a/app/widgets/gimpvectorstreeview.c
+++ b/app/widgets/gimpvectorstreeview.c
@@ -41,6 +41,7 @@
 #include "gimpactiongroup.h"
 #include "gimpcontainerview.h"
 #include "gimpdnd.h"
+#include "gimphelp-ids.h"
 #include "gimpuimanager.h"
 #include "gimpvectorstreeview.h"
 #include "gimpwidgets-utils.h"
@@ -96,19 +97,23 @@ gimp_vectors_tree_view_class_init (GimpVectorsTreeViewClass *klass)
   iv_class->remove_item     = (GimpRemoveItemFunc) gimp_image_remove_vectors;
   iv_class->new_item        = gimp_vectors_tree_view_item_new;
 
-  iv_class->action_group          = "vectors";
-  iv_class->activate_action       = "vectors-path-tool";
-  iv_class->edit_action           = "vectors-edit-attributes";
-  iv_class->new_action            = "vectors-new";
-  iv_class->new_default_action    = "vectors-new-last-values";
-  iv_class->raise_action          = "vectors-raise";
-  iv_class->raise_top_action      = "vectors-raise-to-top";
-  iv_class->lower_action          = "vectors-lower";
-  iv_class->lower_bottom_action   = "vectors-lower-to-bottom";
-  iv_class->duplicate_action      = "vectors-duplicate";
-  iv_class->delete_action         = "vectors-delete";
-  iv_class->lock_content_stock_id = GIMP_STOCK_TOOL_PATH;
-  iv_class->lock_content_tooltip  = _("Lock path strokes");
+  iv_class->action_group           = "vectors";
+  iv_class->activate_action        = "vectors-path-tool";
+  iv_class->edit_action            = "vectors-edit-attributes";
+  iv_class->new_action             = "vectors-new";
+  iv_class->new_default_action     = "vectors-new-last-values";
+  iv_class->raise_action           = "vectors-raise";
+  iv_class->raise_top_action       = "vectors-raise-to-top";
+  iv_class->lower_action           = "vectors-lower";
+  iv_class->lower_bottom_action    = "vectors-lower-to-bottom";
+  iv_class->duplicate_action       = "vectors-duplicate";
+  iv_class->delete_action          = "vectors-delete";
+  iv_class->lock_content_stock_id  = GIMP_STOCK_TOOL_PATH;
+  iv_class->lock_content_tooltip   = _("Lock path strokes");
+  iv_class->lock_content_help_id   = GIMP_HELP_PATH_LOCK_STROKES;
+  iv_class->lock_position_stock_id = GIMP_STOCK_TOOL_MOVE;
+  iv_class->lock_position_tooltip  = _("Lock path position");
+  iv_class->lock_position_help_id  = GIMP_HELP_PATH_LOCK_POSITION;
 }
 
 static void
diff --git a/app/xcf/xcf-load.c b/app/xcf/xcf-load.c
index 5f12659..3b8d923 100644
--- a/app/xcf/xcf-load.c
+++ b/app/xcf/xcf-load.c
@@ -805,6 +805,18 @@ xcf_load_layer_props (XcfInfo    *info,
           }
           break;
 
+        case PROP_LOCK_POSITION:
+          {
+            gboolean lock_position;
+
+            info->cp += xcf_read_int32 (info->fp, (guint32 *) &lock_position, 1);
+
+            if (gimp_item_can_lock_position (GIMP_ITEM (*layer)))
+              gimp_item_set_lock_position (GIMP_ITEM (*layer),
+                                           lock_position, FALSE);
+          }
+          break;
+
         case PROP_APPLY_MASK:
           info->cp += xcf_read_int32 (info->fp, (guint32 *) apply_mask, 1);
           break;
@@ -1006,6 +1018,16 @@ xcf_load_channel_props (XcfInfo      *info,
           }
           break;
 
+        case PROP_LOCK_POSITION:
+          {
+            gboolean lock_position;
+
+            info->cp += xcf_read_int32 (info->fp, (guint32 *) &lock_position, 1);
+            gimp_item_set_lock_position (GIMP_ITEM (*channel),
+                                         lock_position ? TRUE : FALSE, FALSE);
+          }
+          break;
+
         case PROP_SHOW_MASKED:
           {
             gboolean show_masked;
diff --git a/app/xcf/xcf-private.h b/app/xcf/xcf-private.h
index 05f1492..eb05224 100644
--- a/app/xcf/xcf-private.h
+++ b/app/xcf/xcf-private.h
@@ -55,7 +55,8 @@ typedef enum
   PROP_LOCK_CONTENT       = 28,
   PROP_GROUP_ITEM         = 29,
   PROP_ITEM_PATH          = 30,
-  PROP_GROUP_ITEM_FLAGS   = 31
+  PROP_GROUP_ITEM_FLAGS   = 31,
+  PROP_LOCK_POSITION      = 32
 } PropType;
 
 typedef enum
diff --git a/app/xcf/xcf-save.c b/app/xcf/xcf-save.c
index b707fe3..266f2b8 100644
--- a/app/xcf/xcf-save.c
+++ b/app/xcf/xcf-save.c
@@ -506,6 +506,8 @@ xcf_save_layer_props (XcfInfo    *info,
                                   gimp_item_get_lock_content (GIMP_ITEM (layer))));
   xcf_check_error (xcf_save_prop (info, image, PROP_LOCK_ALPHA, error,
                                   gimp_layer_get_lock_alpha (layer)));
+  xcf_check_error (xcf_save_prop (info, image, PROP_LOCK_POSITION, error,
+                                  gimp_item_get_lock_position (GIMP_ITEM (layer))));
 
   if (gimp_layer_get_mask (layer))
     {
@@ -596,6 +598,8 @@ xcf_save_channel_props (XcfInfo      *info,
                                   gimp_item_get_linked (GIMP_ITEM (channel))));
   xcf_check_error (xcf_save_prop (info, image, PROP_LOCK_CONTENT, error,
                                   gimp_item_get_lock_content (GIMP_ITEM (channel))));
+  xcf_check_error (xcf_save_prop (info, image, PROP_LOCK_POSITION, error,
+                                  gimp_item_get_lock_position (GIMP_ITEM (channel))));
   xcf_check_error (xcf_save_prop (info, image, PROP_SHOW_MASKED, error,
                                   gimp_channel_get_show_masked (channel)));
 
@@ -763,6 +767,19 @@ xcf_save_prop (XcfInfo    *info,
       }
       break;
 
+    case PROP_LOCK_POSITION:
+      {
+        guint32 lock_position;
+
+        lock_position = va_arg (args, guint32);
+        size = 4;
+
+        xcf_write_prop_type_check_error (info, prop_type);
+        xcf_write_int32_check_error (info, &size, 1);
+        xcf_write_int32_check_error (info, &lock_position, 1);
+      }
+      break;
+
     case PROP_APPLY_MASK:
       {
         guint32 apply_mask;
diff --git a/libgimp/gimp.def b/libgimp/gimp.def
index 3bf5288..710a8b2 100644
--- a/libgimp/gimp.def
+++ b/libgimp/gimp.def
@@ -507,6 +507,7 @@ EXPORTS
 	gimp_item_get_image
 	gimp_item_get_linked
 	gimp_item_get_lock_content
+	gimp_item_get_lock_position
 	gimp_item_get_name
 	gimp_item_get_parasite
 	gimp_item_get_parasite_list
@@ -524,6 +525,7 @@ EXPORTS
 	gimp_item_is_vectors
 	gimp_item_set_linked
 	gimp_item_set_lock_content
+	gimp_item_set_lock_position
 	gimp_item_set_name
 	gimp_item_set_tattoo
 	gimp_item_set_visible
diff --git a/libgimp/gimpitem_pdb.c b/libgimp/gimpitem_pdb.c
index 6f8b00a..4858263 100644
--- a/libgimp/gimpitem_pdb.c
+++ b/libgimp/gimpitem_pdb.c
@@ -733,6 +733,72 @@ gimp_item_set_lock_content (gint32   item_ID,
 }
 
 /**
+ * gimp_item_get_lock_position:
+ * @item_ID: The item.
+ *
+ * Get the 'lock position' state of the specified item.
+ *
+ * This procedure returns the specified item's lock position state.
+ *
+ * Returns: Whether the item's position is locked.
+ *
+ * Since: GIMP 2.10
+ **/
+gboolean
+gimp_item_get_lock_position (gint32 item_ID)
+{
+  GimpParam *return_vals;
+  gint nreturn_vals;
+  gboolean lock_position = FALSE;
+
+  return_vals = gimp_run_procedure ("gimp-item-get-lock-position",
+                                    &nreturn_vals,
+                                    GIMP_PDB_ITEM, item_ID,
+                                    GIMP_PDB_END);
+
+  if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+    lock_position = return_vals[1].data.d_int32;
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return lock_position;
+}
+
+/**
+ * gimp_item_set_lock_position:
+ * @item_ID: The item.
+ * @lock_position: The new item 'lock position' state.
+ *
+ * Set the 'lock position' state of the specified item.
+ *
+ * This procedure sets the specified item's lock position state.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: GIMP 2.10
+ **/
+gboolean
+gimp_item_set_lock_position (gint32   item_ID,
+                             gboolean lock_position)
+{
+  GimpParam *return_vals;
+  gint nreturn_vals;
+  gboolean success = TRUE;
+
+  return_vals = gimp_run_procedure ("gimp-item-set-lock-position",
+                                    &nreturn_vals,
+                                    GIMP_PDB_ITEM, item_ID,
+                                    GIMP_PDB_INT32, lock_position,
+                                    GIMP_PDB_END);
+
+  success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return success;
+}
+
+/**
  * gimp_item_get_tattoo:
  * @item_ID: The item.
  *
diff --git a/libgimp/gimpitem_pdb.h b/libgimp/gimpitem_pdb.h
index 04788a4..c564f0f 100644
--- a/libgimp/gimpitem_pdb.h
+++ b/libgimp/gimpitem_pdb.h
@@ -58,6 +58,9 @@ gboolean      gimp_item_set_linked        (gint32              item_ID,
 gboolean      gimp_item_get_lock_content  (gint32              item_ID);
 gboolean      gimp_item_set_lock_content  (gint32              item_ID,
                                            gboolean            lock_content);
+gboolean      gimp_item_get_lock_position (gint32              item_ID);
+gboolean      gimp_item_set_lock_position (gint32              item_ID,
+                                           gboolean            lock_position);
 gint          gimp_item_get_tattoo        (gint32              item_ID);
 gboolean      gimp_item_set_tattoo        (gint32              item_ID,
                                            gint                tattoo);
diff --git a/tools/pdbgen/pdb/drawable_transform.pdb b/tools/pdbgen/pdb/drawable_transform.pdb
index 7cbf357..faf6144 100644
--- a/tools/pdbgen/pdb/drawable_transform.pdb
+++ b/tools/pdbgen/pdb/drawable_transform.pdb
@@ -25,11 +25,14 @@
 sub transform_invoke {
     my ($progress_text, $assemble_matrix, $check) = @_;
     my $success_check = 'gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                                    GIMP_PDB_ITEM_CONTENT, error);';
+                                                    GIMP_PDB_ITEM_CONTENT |
+                                                    GIMP_PDB_ITEM_POSITION,
+                                                    error);';
 
     if ($check) {
         $success_check = "(gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                                      GIMP_PDB_ITEM_CONTENT, error) && " . $check . ");";
+                                                      GIMP_PDB_ITEM_CONTENT |
+                                                      GIMP_PDB_ITEM_POSITION, error) && " . $check . ");";
     }
 
     %invoke = (
@@ -86,11 +89,13 @@ CODE
 sub transform_default_invoke {
     my ($progress_text, $assemble_matrix, $check) = @_;
     my $success_check = 'gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                                    GIMP_PDB_ITEM_CONTENT, error);';
+                                                    GIMP_PDB_ITEM_CONTENT |
+                                                    GIMP_PDB_ITEM_POSITION, error);';
 
     if ($check) {
         $success_check = "(gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                                      GIMP_PDB_ITEM_CONTENT, error) && " . $check . ");";
+                                                      GIMP_PDB_ITEM_CONTENT |
+                                                      GIMP_PDB_ITEM_POSITION, error) && " . $check . ");";
     }
 
     %invoke = (
@@ -180,7 +185,8 @@ sub drawable_transform_flip_simple {
   gint x, y, width, height;
 
   success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                       GIMP_PDB_ITEM_CONTENT, error);
+                                       GIMP_PDB_ITEM_CONTENT |
+                                       GIMP_PDB_ITEM_POSITION, error);
 
   if (success &&
       gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
@@ -423,7 +429,8 @@ sub drawable_transform_rotate_simple {
   gint x, y, width, height;
 
   success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                       GIMP_PDB_ITEM_CONTENT, error);
+                                       GIMP_PDB_ITEM_CONTENT |
+                                       GIMP_PDB_ITEM_POSITION, error);
 
   if (success &&
       gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
diff --git a/tools/pdbgen/pdb/item.pdb b/tools/pdbgen/pdb/item.pdb
index 2c7eb43..de868ac 100644
--- a/tools/pdbgen/pdb/item.pdb
+++ b/tools/pdbgen/pdb/item.pdb
@@ -623,6 +623,58 @@ CODE
     );
 }
 
+sub item_get_lock_position {
+    $blurb = "Get the 'lock position' state of the specified item.";
+
+    $help = "This procedure returns the specified item's lock position state.";
+
+    &mitch_pdb_misc('2012', '2.10');
+
+    @inargs = (
+	{ name => 'item', type => 'item',
+	  desc => 'The item' }
+    );
+
+    @outargs = (
+	{ name => 'lock_position', type => 'boolean',
+	  desc => "Whether the item's position is locked" }
+    );
+
+    %invoke = (
+	code => <<'CODE'
+{
+  lock_position = gimp_item_get_lock_position (GIMP_ITEM (item));
+}
+CODE
+    );
+}
+
+sub item_set_lock_position {
+    $blurb = "Set the 'lock position' state of the specified item.";
+
+    $help = "This procedure sets the specified item's lock position state.";
+
+    &mitch_pdb_misc('2009', '2.10');
+
+    @inargs = (
+	{ name => 'item', type => 'item',
+	  desc => 'The item' },
+	{ name => 'lock_position', type => 'boolean',
+	  desc => "The new item 'lock position' state" }
+    );
+
+    %invoke = (
+	code => <<'CODE'
+{
+  if (gimp_item_can_lock_position (GIMP_ITEM (item)))
+    gimp_item_set_lock_position (GIMP_ITEM (item), lock_position, TRUE);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
 sub item_get_tattoo {
     $blurb = "Get the tattoo of the specified item.";
 
@@ -817,6 +869,7 @@ CODE
             item_get_visible item_set_visible
             item_get_linked item_set_linked
             item_get_lock_content item_set_lock_content
+            item_get_lock_position item_set_lock_position
             item_get_tattoo item_set_tattoo
 	    item_attach_parasite item_detach_parasite
 	    item_get_parasite
diff --git a/tools/pdbgen/pdb/item_transform.pdb b/tools/pdbgen/pdb/item_transform.pdb
index 1b6dd7d..cee4b52 100644
--- a/tools/pdbgen/pdb/item_transform.pdb
+++ b/tools/pdbgen/pdb/item_transform.pdb
@@ -23,11 +23,13 @@
 sub transform_invoke {
     my ($progress_text, $assemble_matrix, $check) = @_;
     my $success_check = 'gimp_pdb_item_is_attached (item, NULL,
-                                                    GIMP_PDB_ITEM_CONTENT, error);';
+                                                    GIMP_PDB_ITEM_CONTENT |
+                                                    GIMP_PDB_ITEM_POSITION, error);';
 
     if ($check) {
         $success_check = "(gimp_pdb_item_is_attached (item, NULL,
-                                                      GIMP_PDB_ITEM_CONTENT, error) && " . $check . ");";
+                                                      GIMP_PDB_ITEM_CONTENT |
+                                                      GIMP_PDB_ITEM_POSITION, error) && " . $check . ");";
     }
 
     %invoke = (
@@ -138,7 +140,8 @@ HELP
   gint x, y, width, height;
 
   success = gimp_pdb_item_is_attached (item, NULL,
-                                       GIMP_PDB_ITEM_CONTENT, error);
+                                       GIMP_PDB_ITEM_CONTENT |
+                                       GIMP_PDB_ITEM_POSITION, error);
 
   if (success &&
       gimp_item_mask_intersect (item, &x, &y, &width, &height))
@@ -346,7 +349,8 @@ HELP
   gint x, y, width, height;
 
   success = gimp_pdb_item_is_attached (item, NULL,
-                                       GIMP_PDB_ITEM_CONTENT, error);
+                                       GIMP_PDB_ITEM_CONTENT |
+                                       GIMP_PDB_ITEM_POSITION, error);
 
   if (success &&
       gimp_item_mask_intersect (item, &x, &y, &width, &height))
diff --git a/tools/pdbgen/pdb/layer.pdb b/tools/pdbgen/pdb/layer.pdb
index df12ec4..b381f30 100644
--- a/tools/pdbgen/pdb/layer.pdb
+++ b/tools/pdbgen/pdb/layer.pdb
@@ -433,7 +433,8 @@ HELP
 	code => <<'CODE'
 {
   if (gimp_pdb_item_is_attached (GIMP_ITEM (layer), NULL,
-                                 GIMP_PDB_ITEM_CONTENT, error))
+                                 GIMP_PDB_ITEM_CONTENT | GIMP_PDB_ITEM_POSITION,
+                                 error))
     {
       GimpPDBContext *pdb_context = GIMP_PDB_CONTEXT (context);
 
@@ -477,7 +478,8 @@ sub layer_scale_full {
 	code => <<'CODE'
 {
   if (gimp_pdb_item_is_attached (GIMP_ITEM (layer), NULL,
-                                 GIMP_PDB_ITEM_CONTENT, error))
+                                 GIMP_PDB_ITEM_CONTENT | GIMP_PDB_ITEM_POSITION,
+                                 error))
     {
       if (progress)
         gimp_progress_start (progress, _("Scaling"), FALSE);
@@ -529,7 +531,8 @@ HELP
 	code => <<'CODE'
 {
   if (gimp_pdb_item_is_attached (GIMP_ITEM (layer), NULL,
-                                 GIMP_PDB_ITEM_CONTENT, error))
+                                 GIMP_PDB_ITEM_CONTENT | GIMP_PDB_ITEM_POSITION,
+                                 error))
     gimp_item_resize (GIMP_ITEM (layer), context,
                       new_width, new_height, offx, offy);
   else
@@ -558,7 +561,8 @@ HELP
 	code => <<'CODE'
 {
   if (gimp_pdb_item_is_attached (GIMP_ITEM (layer), NULL,
-                                 GIMP_PDB_ITEM_CONTENT, error))
+                                 GIMP_PDB_ITEM_CONTENT | GIMP_PDB_ITEM_POSITION,
+                                 error))
     gimp_layer_resize_to_image (layer, context);
   else
     success = FALSE;
@@ -592,17 +596,23 @@ HELP
     %invoke = (
 	code => <<'CODE'
 {
-  GimpImage *image = gimp_item_get_image (GIMP_ITEM (layer));
+  if (gimp_pdb_item_is_modifyable (GIMP_ITEM (layer),
+                                   GIMP_PDB_ITEM_POSITION, error))
+    {
+      GimpImage *image = gimp_item_get_image (GIMP_ITEM (layer));
 
-  gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_DISPLACE,
-                               _("Move Layer"));
+      gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_DISPLACE,
+                                   _("Move Layer"));
 
-  gimp_item_translate (GIMP_ITEM (layer), offx, offy, TRUE);
+      gimp_item_translate (GIMP_ITEM (layer), offx, offy, TRUE);
 
-  if (gimp_item_get_linked (GIMP_ITEM (layer)))
-    gimp_item_linked_translate (GIMP_ITEM (layer), offx, offy, TRUE);
+      if (gimp_item_get_linked (GIMP_ITEM (layer)))
+        gimp_item_linked_translate (GIMP_ITEM (layer), offx, offy, TRUE);
 
-  gimp_image_undo_group_end (image);
+      gimp_image_undo_group_end (image);
+    }
+  else
+    success = FALSE;
 }
 CODE
     );
@@ -698,23 +708,29 @@ HELP
     %invoke = (
 	code => <<'CODE'
 {
-  GimpImage *image = gimp_item_get_image (GIMP_ITEM (layer));
-  gint       offset_x;
-  gint       offset_y;
+  if (gimp_pdb_item_is_modifyable (GIMP_ITEM (layer),
+                                   GIMP_PDB_ITEM_POSITION, error))
+    {
+      GimpImage *image = gimp_item_get_image (GIMP_ITEM (layer));
+      gint       offset_x;
+      gint       offset_y;
 
-  gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_DISPLACE,
-                               _("Move Layer"));
+      gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_DISPLACE,
+                                   _("Move Layer"));
 
-  gimp_item_get_offset (GIMP_ITEM (layer), &offset_x, &offset_y);
-  offx -= offset_x;
-  offy -= offset_y;
+      gimp_item_get_offset (GIMP_ITEM (layer), &offset_x, &offset_y);
+      offx -= offset_x;
+      offy -= offset_y;
 
-  gimp_item_translate (GIMP_ITEM (layer), offx, offy, TRUE);
+      gimp_item_translate (GIMP_ITEM (layer), offx, offy, TRUE);
 
-  if (gimp_item_get_linked (GIMP_ITEM (layer)))
-    gimp_item_linked_translate (GIMP_ITEM (layer), offx, offy, TRUE);
+      if (gimp_item_get_linked (GIMP_ITEM (layer)))
+        gimp_item_linked_translate (GIMP_ITEM (layer), offx, offy, TRUE);
 
-  gimp_image_undo_group_end (image);
+      gimp_image_undo_group_end (image);
+    }
+  else
+    success = FALSE;
 }
 CODE
     );
diff --git a/tools/pdbgen/pdb/transform_tools.pdb b/tools/pdbgen/pdb/transform_tools.pdb
index bea3e26..1912be5 100644
--- a/tools/pdbgen/pdb/transform_tools.pdb
+++ b/tools/pdbgen/pdb/transform_tools.pdb
@@ -38,7 +38,8 @@ sub flip {
   gint x, y, width, height;
 
   success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                       GIMP_PDB_ITEM_CONTENT, error);
+                                       GIMP_PDB_ITEM_CONTENT |
+                                       GIMP_PDB_ITEM_POSITION, error);
 
   if (success &&
       gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
@@ -113,7 +114,8 @@ sub perspective {
   gint x, y, width, height;
 
   success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                       GIMP_PDB_ITEM_CONTENT, error);
+                                       GIMP_PDB_ITEM_CONTENT |
+                                       GIMP_PDB_ITEM_POSITION, error);
 
   if (success &&
       gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
@@ -191,7 +193,8 @@ sub rotate {
   gint x, y, width, height;
 
   success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                       GIMP_PDB_ITEM_CONTENT, error);
+                                       GIMP_PDB_ITEM_CONTENT |
+                                       GIMP_PDB_ITEM_POSITION, error);
 
   if (success &&
       gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
@@ -278,7 +281,8 @@ sub scale {
   gint x, y, width, height;
 
   success = (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                        GIMP_PDB_ITEM_CONTENT, error) &&
+                                        GIMP_PDB_ITEM_CONTENT |
+                                        GIMP_PDB_ITEM_POSITION, error) &&
              x0 < x1 && y0 < y1);
 
   if (success &&
@@ -359,7 +363,8 @@ sub shear {
   gint x, y, width, height;
 
   success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                       GIMP_PDB_ITEM_CONTENT, error);
+                                       GIMP_PDB_ITEM_CONTENT |
+                                       GIMP_PDB_ITEM_POSITION, error);
 
   if (success &&
       gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
@@ -448,7 +453,8 @@ sub transform_2d {
   gint x, y, width, height;
 
   success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
-                                       GIMP_PDB_ITEM_CONTENT, error);
+                                       GIMP_PDB_ITEM_CONTENT |
+                                       GIMP_PDB_ITEM_POSITION, error);
 
   if (success &&
       gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
diff --git a/tools/pdbgen/pdb/vectors.pdb b/tools/pdbgen/pdb/vectors.pdb
index 22087bd..e5cd964 100644
--- a/tools/pdbgen/pdb/vectors.pdb
+++ b/tools/pdbgen/pdb/vectors.pdb
@@ -71,7 +71,7 @@ HELP
     %invoke = (
         code => <<'CODE'
 {
-  if (gimp_pdb_layer_is_text_layer (layer, FALSE, error))
+  if (gimp_pdb_layer_is_text_layer (layer, 0, error))
     {
       gint x, y;
 
@@ -359,7 +359,9 @@ HELP
         code => <<"CODE"
 {
   GimpStroke *stroke = gimp_pdb_get_vectors_stroke (vectors, stroke_id,
-                                                    GIMP_PDB_ITEM_CONTENT, error);
+                                                    GIMP_PDB_ITEM_CONTENT |
+                                                    GIMP_PDB_ITEM_POSITION,
+                                                    error);
   
   if (stroke)
     {
@@ -401,7 +403,9 @@ HELP
         code => <<"CODE"
 {
   GimpStroke *stroke = gimp_pdb_get_vectors_stroke (vectors, stroke_id,
-                                                    GIMP_PDB_ITEM_CONTENT, error);
+                                                    GIMP_PDB_ITEM_CONTENT |
+                                                    GIMP_PDB_ITEM_POSITION,
+                                                    error);
   
   if (stroke)
     {
@@ -444,7 +448,9 @@ HELP
         code => <<"CODE"
 {
   GimpStroke *stroke = gimp_pdb_get_vectors_stroke (vectors, stroke_id,
-                                                    GIMP_PDB_ITEM_CONTENT, error);
+                                                    GIMP_PDB_ITEM_CONTENT |
+                                                    GIMP_PDB_ITEM_POSITION,
+                                                    error);
   
   if (stroke)
     {
@@ -486,7 +492,9 @@ HELP
         code => <<"CODE"
 {
   GimpStroke *stroke = gimp_pdb_get_vectors_stroke (vectors, stroke_id,
-                                                    GIMP_PDB_ITEM_CONTENT, error);
+                                                    GIMP_PDB_ITEM_CONTENT |
+                                                    GIMP_PDB_ITEM_POSITION,
+                                                    error);
   
   if (stroke)
     {
@@ -533,7 +541,9 @@ HELP
         code => <<"CODE"
 {
   GimpStroke *stroke = gimp_pdb_get_vectors_stroke (vectors, stroke_id,
-                                                    GIMP_PDB_ITEM_CONTENT, error);
+                                                    GIMP_PDB_ITEM_CONTENT |
+                                                    GIMP_PDB_ITEM_POSITION,
+                                                    error);
   
   if (stroke)
     {



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]