changeset 7894:b259507d839f

[SCENEBUILDER] Going on fixing DTL-6783: Rationalize job implementation between Hierarchy and Content Panels => Adapt GridPane jobs
author slions
date Thu, 28 Aug 2014 09:03:45 +0200
parents f43dd33a649f
children 9bcafd4e5543
files apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/AddColumnConstraintsJob.java apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/AddColumnJob.java apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/AddRowConstraintsJob.java apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/AddRowJob.java apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/DeleteColumnJob.java apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/DeleteRowJob.java apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/GridPaneJobUtils.java apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/MoveColumnJob.java apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/MoveRowJob.java apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/RemoveColumnConstraintsJob.java apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/RemoveColumnContentJob.java apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/RemoveRowConstraintsJob.java apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/RemoveRowContentJob.java
diffstat 13 files changed, 372 insertions(+), 884 deletions(-) [+]
line wrap: on
line diff
--- a/apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/AddColumnConstraintsJob.java	Thu Aug 28 08:55:45 2014 +0200
+++ b/apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/AddColumnConstraintsJob.java	Thu Aug 28 09:03:45 2014 +0200
@@ -32,7 +32,7 @@
 package com.oracle.javafx.scenebuilder.kit.editor.job.gridpane;
 
 import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
-import com.oracle.javafx.scenebuilder.kit.editor.job.BatchJob;
+import com.oracle.javafx.scenebuilder.kit.editor.job.BatchDocumentJob;
 import com.oracle.javafx.scenebuilder.kit.editor.job.Job;
 import com.oracle.javafx.scenebuilder.kit.editor.job.JobUtils;
 import com.oracle.javafx.scenebuilder.kit.editor.job.gridpane.GridPaneJobUtils.Position;
@@ -45,19 +45,21 @@
 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMPropertyC;
 import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;
 import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
+import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import javafx.scene.layout.ColumnConstraints;
 
 /**
  * Job invoked when adding column constraints.
  */
-public class AddColumnConstraintsJob extends Job {
+public class AddColumnConstraintsJob extends BatchDocumentJob {
 
-    private BatchJob subJob;
     // Key = target GridPane instance
     // Value = list of target column indexes for this GridPane
-    private final Map<FXOMObject, List<Integer>> targetGridPanes;
+    private final Map<FXOMObject, Set<Integer>> targetGridPanes;
     private final Position position;
     // If the selected column is associated to an existing constraints, 
     // we duplicate the existing constraints.
@@ -68,68 +70,38 @@
     public AddColumnConstraintsJob(
             final EditorController editorController,
             final Position position,
-            final Map<FXOMObject, List<Integer>> targetGridPanes) {
+            final Map<FXOMObject, Set<Integer>> targetGridPanes) {
         super(editorController);
         this.position = position;
         this.targetGridPanes = targetGridPanes;
-        buildSubJobs();
     }
 
     @Override
-    public boolean isExecutable() {
-        return subJob != null && subJob.isExecutable();
-    }
+    protected List<Job> makeSubJobs() {
 
-    @Override
-    public void execute() {
-        assert isExecutable();
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-        fxomDocument.beginUpdate();
-        subJob.execute();
-        fxomDocument.endUpdate();
-    }
-
-    @Override
-    public void undo() {
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-        fxomDocument.beginUpdate();
-        subJob.undo();
-        fxomDocument.endUpdate();
-    }
-
-    @Override
-    public void redo() {
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-        fxomDocument.beginUpdate();
-        subJob.redo();
-        fxomDocument.endUpdate();
-    }
-
-    @Override
-    public String getDescription() {
-        return "Add Column Constraints"; //NOI18N
-    }
-
-    private void buildSubJobs() {
-
-        // Create sub job
-        subJob = new BatchJob(getEditorController(),
-                true /* shouldRefreshSceneGraph */, null);
+        final List<Job> result = new ArrayList<>();
 
         // Add column constraints job
         assert targetGridPanes.isEmpty() == false;
         for (FXOMObject targetGridPane : targetGridPanes.keySet()) {
             assert targetGridPane instanceof FXOMInstance;
-            final List<Integer> targetIndexes = targetGridPanes.get(targetGridPane);
-            addColumnConstraints((FXOMInstance) targetGridPane, targetIndexes);
+            final Set<Integer> targetIndexes = targetGridPanes.get(targetGridPane);
+            result.addAll(addColumnConstraints((FXOMInstance) targetGridPane, targetIndexes));
         }
+        
+        return result;
     }
+    
+    @Override
+    protected String makeDescription() {
+        return "Add Column Constraints"; //NOI18N
+    }
+    
+    private Set<Job> addColumnConstraints(
+            final FXOMInstance targetGridPane,
+            final Set<Integer> targetIndexes) {
 
-    private void addColumnConstraints(
-            final FXOMInstance targetGridPane,
-            final List<Integer> targetIndexes) {
-
-        assert subJob != null;
+        final Set<Job> result = new HashSet<>();
         final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
 
         // Retrieve the constraints property for the specified target GridPane
@@ -166,7 +138,7 @@
                         addedConstraints,
                         (FXOMPropertyC) constraintsProperty,
                         addedIndex, getEditorController());
-                subJob.addSubJob(addValueJob);
+                result.add(addValueJob);
             } //
             // The target index is not associated to an existing constraints value :
             // - we add new empty constraints from the last existing one to the added index (excluded)
@@ -179,7 +151,7 @@
                             addedConstraints,
                             (FXOMPropertyC) constraintsProperty,
                             index, getEditorController());
-                    subJob.addSubJob(addValueJob);
+                    result.add(addValueJob);
                 }
                 // Create new constraints with default values for the new added column
                 final FXOMInstance addedConstraints = makeColumnConstraintsInstance();
@@ -189,7 +161,7 @@
                         addedConstraints,
                         (FXOMPropertyC) constraintsProperty,
                         addedIndex, getEditorController());
-                subJob.addSubJob(addValueJob);
+                result.add(addValueJob);
                 constraintsSize = addedIndex + 1;
             }
             shiftIndex++;
@@ -203,8 +175,10 @@
                     constraintsProperty,
                     targetGridPane,
                     -1, getEditorController());
-            subJob.addSubJob(addPropertyJob);
+            result.add(addPropertyJob);
         }
+        
+        return result;
     }
 
     private FXOMInstance makeColumnConstraintsInstance() {
--- a/apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/AddColumnJob.java	Thu Aug 28 08:55:45 2014 +0200
+++ b/apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/AddColumnJob.java	Thu Aug 28 09:03:45 2014 +0200
@@ -32,22 +32,24 @@
 package com.oracle.javafx.scenebuilder.kit.editor.job.gridpane;
 
 import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
-import com.oracle.javafx.scenebuilder.kit.editor.job.BatchJob;
+import com.oracle.javafx.scenebuilder.kit.editor.job.BatchSelectionJob;
 import com.oracle.javafx.scenebuilder.kit.editor.job.Job;
 import com.oracle.javafx.scenebuilder.kit.editor.job.gridpane.GridPaneJobUtils.Position;
 import com.oracle.javafx.scenebuilder.kit.editor.selection.AbstractSelectionGroup;
 import com.oracle.javafx.scenebuilder.kit.editor.selection.GridSelectionGroup;
 import com.oracle.javafx.scenebuilder.kit.editor.selection.GridSelectionGroup.Type;
+import com.oracle.javafx.scenebuilder.kit.editor.selection.ObjectSelectionGroup;
 import com.oracle.javafx.scenebuilder.kit.editor.selection.Selection;
-import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
 import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Job invoked when adding columns.
@@ -60,98 +62,31 @@
  * We add new columns for each selected column, either before or after.
  *
  */
-public class AddColumnJob extends Job {
+public class AddColumnJob extends BatchSelectionJob {
 
-    private BatchJob subJob;
-    private AbstractSelectionGroup selectionSnapshot;
     // Key = target GridPane instance
-    // Value = list of target column indexes for this GridPane
-    private final Map<FXOMObject, List<Integer>> targetGridPanes = new HashMap<>();
+    // Value = set of target column indexes for this GridPane
+    private final Map<FXOMObject, Set<Integer>> targetGridPanes = new HashMap<>();
     private final Position position;
 
     public AddColumnJob(EditorController editorController, Position position) {
         super(editorController);
         assert position == Position.BEFORE || position == Position.AFTER;
         this.position = position;
-        buildSubJobs();
     }
-
+    
     @Override
-    public boolean isExecutable() {
-        return subJob != null && subJob.isExecutable();
-    }
-
-    @Override
-    public void execute() {
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-        final Selection selection = getEditorController().getSelection();
-
-        assert isExecutable(); // (1)
-        assert targetGridPanes.isEmpty() == false; // Because of (1)
-
-        try {
-            selectionSnapshot = selection.getGroup().clone();
-        } catch (CloneNotSupportedException x) {
-            // Emergency code
-            throw new RuntimeException(x);
-        }
-        selection.clear();
-        selection.beginUpdate();
-        fxomDocument.beginUpdate();
-        subJob.execute();
-        fxomDocument.endUpdate();
-        updateSelection();
-        selection.endUpdate();
-    }
-
-    @Override
-    public void undo() {
-        assert subJob != null;
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-        final Selection selection = getEditorController().getSelection();
-
-        selection.beginUpdate();
-        fxomDocument.beginUpdate();
-        subJob.undo();
-        fxomDocument.endUpdate();
-        selection.select(selectionSnapshot);
-        selection.endUpdate();
-    }
-
-    @Override
-    public void redo() {
-        assert subJob != null;
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-        final Selection selection = getEditorController().getSelection();
-
-        selection.clear();
-        selection.beginUpdate();
-        fxomDocument.beginUpdate();
-        subJob.redo();
-        fxomDocument.endUpdate();
-        updateSelection();
-        selection.endUpdate();
-    }
-
-    @Override
-    public String getDescription() {
-        return "Add Column " + position.name(); //NOI18N
-    }
-
-    private void buildSubJobs() {
+    protected List<Job> makeSubJobs() {
+        final List<Job> result = new ArrayList<>();
 
         if (GridPaneJobUtils.canPerformAdd(getEditorController())) {
 
-            // Create sub job
-            subJob = new BatchJob(getEditorController(),
-                    true /* shouldUpdateSceneGraph */, null);
-
             // Populate the target GridPane map
             assert targetGridPanes.isEmpty() == true;
             final List<FXOMObject> objectList
                     = GridPaneJobUtils.getTargetGridPanes(getEditorController());
             for (FXOMObject object : objectList) {
-                final List<Integer> indexList
+                final Set<Integer> indexList
                         = getTargetColumnIndexes(getEditorController(), object);
                 targetGridPanes.put(object, indexList);
             }
@@ -160,19 +95,48 @@
             // First add the new column constraints
             final Job addConstraints = new AddColumnConstraintsJob(
                     getEditorController(), position, targetGridPanes);
-            subJob.addSubJob(addConstraints);
+            result.add(addConstraints);
             // Then move the column content
-            moveColumnContent();
+            result.addAll(moveColumnContent());
         }
+        return result;
     }
 
-    private void moveColumnContent() {
+    @Override
+    protected String makeDescription() {
+        return "Add Column " + position.name(); //NOI18N
+    }
 
-        assert subJob != null;
+    @Override
+    protected AbstractSelectionGroup getNewSelectionGroup() {
+        final AbstractSelectionGroup asg;
+        // Update new selection :
+        // - if there is more than 1 GridPane, we select the GridPane instances
+        // - if there is a single GridPane, we select the added columns
+        if (targetGridPanes.size() > 1) {
+            Set<FXOMObject> objects = targetGridPanes.keySet();
+            asg = new ObjectSelectionGroup(objects, objects.iterator().next(), null);
+        } else {
+            assert targetGridPanes.size() == 1;
+            final FXOMInstance targetGridPane
+                    = (FXOMInstance) targetGridPanes.keySet().iterator().next();
+            final Set<Integer> targetIndexes = targetGridPanes.get(targetGridPane);
+            assert targetIndexes.size() >= 1;
+            final Set<Integer> addedIndexes
+                    = GridPaneJobUtils.getAddedIndexes(targetIndexes, position);
+            
+            asg = new GridSelectionGroup(targetGridPane, Type.COLUMN, addedIndexes);
+        }
+        return asg;
+    }
+
+    private List<Job> moveColumnContent() {
+
+        final List<Job> result = new ArrayList<>();
 
         for (FXOMObject targetGridPane : targetGridPanes.keySet()) {
 
-            final List<Integer> targetIndexes = targetGridPanes.get(targetGridPane);
+            final Set<Integer> targetIndexes = targetGridPanes.get(targetGridPane);
 
             final DesignHierarchyMask mask = new DesignHierarchyMask(targetGridPane);
             final int columnsSize = mask.getColumnsSize();
@@ -213,7 +177,7 @@
                         break;
                     default:
                         assert false;
-                        return;
+                        return result;
                 }
 
                 // If fromIndex >= columnsSize, we are below the last existing column 
@@ -224,39 +188,13 @@
                             = GridPaneJobUtils.getIndexes(fromIndex, toIndex);
                     final ReIndexColumnContentJob reIndexJob = new ReIndexColumnContentJob(
                             getEditorController(), offset, targetGridPane, indexes);
-                    subJob.addSubJob(reIndexJob);
+                    result.add(reIndexJob);
                 }
 
                 shiftIndex++;
             }
         }
-    }
-
-    private void updateSelection() {
-        final Selection selection = getEditorController().getSelection();
-        // Update new selection :
-        // - if there is more than 1 GridPane, we select the GridPane instances
-        // - if there is a single GridPane, we select the added columns
-        if (targetGridPanes.size() > 1) {
-            selection.select(targetGridPanes.keySet());
-        } else {
-            assert targetGridPanes.size() == 1;
-            final FXOMInstance targetGridPane
-                    = (FXOMInstance) targetGridPanes.keySet().iterator().next();
-            final List<Integer> targetIndexes = targetGridPanes.get(targetGridPane);
-            assert targetIndexes.size() >= 1;
-            final List<Integer> addedIndexes
-                    = GridPaneJobUtils.getAddedIndexes(targetIndexes, position);
-
-            // Selection has been cleared at execution time
-            assert selection.isEmpty();
-            // Select added columns
-            for (int addedIndex : addedIndexes) {
-                // Selection is empty => just toggle selection
-                selection.toggleSelection(targetGridPane,
-                        GridSelectionGroup.Type.COLUMN, addedIndex);
-            }
-        }
+        return result;
     }
 
     /**
@@ -265,14 +203,14 @@
      *
      * @return the list of target indexes
      */
-    private List<Integer> getTargetColumnIndexes(
+    private Set<Integer> getTargetColumnIndexes(
             final EditorController editorController,
             final FXOMObject targetGridPane) {
 
         final Selection selection = editorController.getSelection();
         final AbstractSelectionGroup asg = selection.getGroup();
 
-        final List<Integer> result = new ArrayList<>();
+        final Set<Integer> result = new HashSet<>();
 
         // Selection == GridPane columns
         // => return the list of selected columns
--- a/apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/AddRowConstraintsJob.java	Thu Aug 28 08:55:45 2014 +0200
+++ b/apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/AddRowConstraintsJob.java	Thu Aug 28 09:03:45 2014 +0200
@@ -32,7 +32,7 @@
 package com.oracle.javafx.scenebuilder.kit.editor.job.gridpane;
 
 import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
-import com.oracle.javafx.scenebuilder.kit.editor.job.BatchJob;
+import com.oracle.javafx.scenebuilder.kit.editor.job.BatchDocumentJob;
 import com.oracle.javafx.scenebuilder.kit.editor.job.Job;
 import com.oracle.javafx.scenebuilder.kit.editor.job.JobUtils;
 import com.oracle.javafx.scenebuilder.kit.editor.job.gridpane.GridPaneJobUtils.Position;
@@ -45,19 +45,21 @@
 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMPropertyC;
 import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;
 import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
+import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import javafx.scene.layout.RowConstraints;
 
 /**
  * Job invoked when adding row constraints.
  */
-public class AddRowConstraintsJob extends Job {
+public class AddRowConstraintsJob extends BatchDocumentJob {
 
-    private BatchJob subJob;
     // Key = target GridPane instance
     // Value = list of target row indexes for this GridPane
-    private final Map<FXOMObject, List<Integer>> targetGridPanes;
+    private final Map<FXOMObject, Set<Integer>> targetGridPanes;
     private final Position position;
     // If the selected row is associated to an existing constraints, 
     // we duplicate the existing constraints.
@@ -68,68 +70,38 @@
     public AddRowConstraintsJob(
             final EditorController editorController,
             final Position position,
-            final Map<FXOMObject, List<Integer>> targetGridPanes) {
+            final Map<FXOMObject, Set<Integer>> targetGridPanes) {
         super(editorController);
         this.position = position;
         this.targetGridPanes = targetGridPanes;
-        buildSubJobs();
     }
 
     @Override
-    public boolean isExecutable() {
-        return subJob != null && subJob.isExecutable();
+    protected List<Job> makeSubJobs() {
+
+        final List<Job> result = new ArrayList<>();
+
+        // Add column constraints job
+        assert targetGridPanes.isEmpty() == false;
+        for (FXOMObject targetGridPane : targetGridPanes.keySet()) {
+            assert targetGridPane instanceof FXOMInstance;
+            final Set<Integer> targetIndexes = targetGridPanes.get(targetGridPane);
+            result.addAll(addRowConstraints((FXOMInstance) targetGridPane, targetIndexes));
+        }
+        
+        return result;
     }
-
+    
     @Override
-    public void execute() {
-        assert isExecutable();
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-        fxomDocument.beginUpdate();
-        subJob.execute();
-        fxomDocument.endUpdate();
-    }
-
-    @Override
-    public void undo() {
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-        fxomDocument.beginUpdate();
-        subJob.undo();
-        fxomDocument.endUpdate();
-    }
-
-    @Override
-    public void redo() {
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-        fxomDocument.beginUpdate();
-        subJob.redo();
-        fxomDocument.endUpdate();
-    }
-
-    @Override
-    public String getDescription() {
+    protected String makeDescription() {
         return "Add Row Constraints"; //NOI18N
     }
 
-    private void buildSubJobs() {
+    private Set<Job> addRowConstraints(
+            final FXOMInstance targetGridPane,
+            final Set<Integer> targetIndexes) {
 
-        // Create sub job
-        subJob = new BatchJob(getEditorController(),
-                true /* shouldRefreshSceneGraph */, null);
-
-        // Add row constraints job
-        assert targetGridPanes.isEmpty() == false;
-        for (FXOMObject targetGridPane : targetGridPanes.keySet()) {
-            assert targetGridPane instanceof FXOMInstance;
-            final List<Integer> targetIndexes = targetGridPanes.get(targetGridPane);
-            addRowConstraints((FXOMInstance) targetGridPane, targetIndexes);
-        }
-    }
-
-    private void addRowConstraints(
-            final FXOMInstance targetGridPane,
-            final List<Integer> targetIndexes) {
-
-        assert subJob != null;
+        final Set<Job> result = new HashSet<>();
         final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
 
         // Retrieve the constraints property for the specified target GridPane
@@ -166,7 +138,7 @@
                         addedConstraints,
                         (FXOMPropertyC) constraintsProperty,
                         addedIndex, getEditorController());
-                subJob.addSubJob(addValueJob);
+                result.add(addValueJob);
             } //
             // The target index is not associated to an existing constraints value :
             // - we add new empty constraints from the last existing one to the added index (excluded)
@@ -179,7 +151,7 @@
                             addedConstraints,
                             (FXOMPropertyC) constraintsProperty,
                             index, getEditorController());
-                    subJob.addSubJob(addValueJob);
+                    result.add(addValueJob);
                 }
                 // Create new constraints with default values for the new added row
                 final FXOMInstance addedConstraints = makeRowConstraintsInstance();
@@ -189,7 +161,7 @@
                         addedConstraints,
                         (FXOMPropertyC) constraintsProperty,
                         addedIndex, getEditorController());
-                subJob.addSubJob(addValueJob);
+                result.add(addValueJob);
                 constraintsSize = addedIndex + 1;
             }
             shiftIndex++;
@@ -203,8 +175,10 @@
                     constraintsProperty,
                     targetGridPane,
                     -1, getEditorController());
-            subJob.addSubJob(addPropertyJob);
+            result.add(addPropertyJob);
         }
+        
+        return result;
     }
 
     private FXOMInstance makeRowConstraintsInstance() {
--- a/apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/AddRowJob.java	Thu Aug 28 08:55:45 2014 +0200
+++ b/apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/AddRowJob.java	Thu Aug 28 09:03:45 2014 +0200
@@ -32,22 +32,24 @@
 package com.oracle.javafx.scenebuilder.kit.editor.job.gridpane;
 
 import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
-import com.oracle.javafx.scenebuilder.kit.editor.job.BatchJob;
+import com.oracle.javafx.scenebuilder.kit.editor.job.BatchSelectionJob;
 import com.oracle.javafx.scenebuilder.kit.editor.job.Job;
 import com.oracle.javafx.scenebuilder.kit.editor.job.gridpane.GridPaneJobUtils.Position;
 import com.oracle.javafx.scenebuilder.kit.editor.selection.AbstractSelectionGroup;
 import com.oracle.javafx.scenebuilder.kit.editor.selection.GridSelectionGroup;
 import com.oracle.javafx.scenebuilder.kit.editor.selection.GridSelectionGroup.Type;
+import com.oracle.javafx.scenebuilder.kit.editor.selection.ObjectSelectionGroup;
 import com.oracle.javafx.scenebuilder.kit.editor.selection.Selection;
-import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
 import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Job invoked when adding rows.
@@ -60,98 +62,32 @@
  * We add new rows for each selected row, either above or below.
  *
  */
-public class AddRowJob extends Job {
+public class AddRowJob extends BatchSelectionJob {
 
-    private BatchJob subJob;
-    private AbstractSelectionGroup selectionSnapshot;
     // Key = target GridPane instance
-    // Value = list of target row indexes for this GridPane
-    private final Map<FXOMObject, List<Integer>> targetGridPanes = new HashMap<>();
+    // Value = set of target row indexes for this GridPane
+    private final Map<FXOMObject, Set<Integer>> targetGridPanes = new HashMap<>();
     private final Position position;
 
     public AddRowJob(final EditorController editorController, final Position position) {
         super(editorController);
         assert position == Position.ABOVE || position == Position.BELOW;
         this.position = position;
-        buildSubJobs();
     }
 
-    @Override
-    public boolean isExecutable() {
-        return subJob != null && subJob.isExecutable();
-    }
 
     @Override
-    public void execute() {
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-        final Selection selection = getEditorController().getSelection();
-
-        assert isExecutable(); // (1)
-        assert targetGridPanes.isEmpty() == false; // Because of (1)
-
-        try {
-            selectionSnapshot = selection.getGroup().clone();
-        } catch (CloneNotSupportedException x) {
-            // Emergency code
-            throw new RuntimeException(x);
-        }
-        selection.clear();
-        selection.beginUpdate();
-        fxomDocument.beginUpdate();
-        subJob.execute();
-        fxomDocument.endUpdate();
-        updateSelection();
-        selection.endUpdate();
-    }
-
-    @Override
-    public void undo() {
-        assert subJob != null;
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-        final Selection selection = getEditorController().getSelection();
-
-        selection.beginUpdate();
-        fxomDocument.beginUpdate();
-        subJob.undo();
-        fxomDocument.endUpdate();
-        selection.select(selectionSnapshot);
-        selection.endUpdate();
-    }
-
-    @Override
-    public void redo() {
-        assert subJob != null;
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-        final Selection selection = getEditorController().getSelection();
-
-        selection.clear();
-        selection.beginUpdate();
-        fxomDocument.beginUpdate();
-        subJob.redo();
-        fxomDocument.endUpdate();
-        updateSelection();
-        selection.endUpdate();
-    }
-
-    @Override
-    public String getDescription() {
-        return "Add Row " + position.name(); //NOI18N
-    }
-
-    private void buildSubJobs() {
+    protected List<Job> makeSubJobs() {
+        final List<Job> result = new ArrayList<>();
 
         if (GridPaneJobUtils.canPerformAdd(getEditorController())) {
 
-            // Create sub job
-            subJob = new BatchJob(getEditorController(),
-                    true /* shouldUpdateSceneGraph */, null);
-
             // Populate the target GridPane map
             assert targetGridPanes.isEmpty() == true;
             final List<FXOMObject> objectList
                     = GridPaneJobUtils.getTargetGridPanes(getEditorController());
             for (FXOMObject object : objectList) {
-                final List<Integer> indexList
+                final Set<Integer> indexList
                         = getTargetIndexes(getEditorController(), object);
                 targetGridPanes.put(object, indexList);
             }
@@ -160,19 +96,48 @@
             // First add the new row constraints
             final Job addConstraints = new AddRowConstraintsJob(
                     getEditorController(), position, targetGridPanes);
-            subJob.addSubJob(addConstraints);
+            result.add(addConstraints);
             // Then move the row content
-            moveRowContent();
+            result.addAll(moveRowContent());
         }
+        return result;
     }
 
-    private void moveRowContent() {
+    @Override
+    protected String makeDescription() {
+        return "Add Row " + position.name(); //NOI18N
+    }
 
-        assert subJob != null;
+    @Override
+    protected AbstractSelectionGroup getNewSelectionGroup() {
+        final AbstractSelectionGroup asg;
+        // Update new selection :
+        // - if there is more than 1 GridPane, we select the GridPane instances
+        // - if there is a single GridPane, we select the added rows
+        if (targetGridPanes.size() > 1) {
+            Set<FXOMObject> objects = targetGridPanes.keySet();
+            asg = new ObjectSelectionGroup(objects, objects.iterator().next(), null);
+        } else {
+            assert targetGridPanes.size() == 1;
+            final FXOMInstance targetGridPane
+                    = (FXOMInstance) targetGridPanes.keySet().iterator().next();
+            final Set<Integer> targetIndexes = targetGridPanes.get(targetGridPane);
+            assert targetIndexes.size() >= 1;
+            final Set<Integer> addedIndexes
+                    = GridPaneJobUtils.getAddedIndexes(targetIndexes, position);
+
+            asg = new GridSelectionGroup(targetGridPane, Type.ROW, addedIndexes);
+        }
+        return asg;
+    }
+
+    private List<Job> moveRowContent() {
+
+        final List<Job> result = new ArrayList<>();
 
         for (FXOMObject targetGridPane : targetGridPanes.keySet()) {
 
-            final List<Integer> targetIndexes = targetGridPanes.get(targetGridPane);
+            final Set<Integer> targetIndexes = targetGridPanes.get(targetGridPane);
 
             final DesignHierarchyMask mask = new DesignHierarchyMask(targetGridPane);
             final int rowsSize = mask.getRowsSize();
@@ -213,7 +178,7 @@
                         break;
                     default:
                         assert false;
-                        return;
+                        return result;
                 }
 
                 // If fromIndex >= rowsSize, we are below the last existing row 
@@ -224,39 +189,13 @@
                             = GridPaneJobUtils.getIndexes(fromIndex, toIndex);
                     final ReIndexRowContentJob reIndexJob = new ReIndexRowContentJob(
                             getEditorController(), offset, targetGridPane, indexes);
-                    subJob.addSubJob(reIndexJob);
+                    result.add(reIndexJob);
                 }
 
                 shiftIndex++;
             }
         }
-    }
-
-    private void updateSelection() {
-        final Selection selection = getEditorController().getSelection();
-        // Update new selection :
-        // - if there is more than 1 GridPane, we select the GridPane instances
-        // - if there is a single GridPane, we select the added rows
-        if (targetGridPanes.size() > 1) {
-            selection.select(targetGridPanes.keySet());
-        } else {
-            assert targetGridPanes.size() == 1;
-            final FXOMInstance targetGridPane
-                    = (FXOMInstance) targetGridPanes.keySet().iterator().next();
-            final List<Integer> targetIndexes = targetGridPanes.get(targetGridPane);
-            assert targetIndexes.size() >= 1;
-            final List<Integer> addedIndexes
-                    = GridPaneJobUtils.getAddedIndexes(targetIndexes, position);
-
-            // Selection has been cleared at execution time
-            assert selection.isEmpty();
-            // Select added rows
-            for (int addedIndex : addedIndexes) {
-                // Selection is empty => just toggle selection
-                selection.toggleSelection(targetGridPane,
-                        GridSelectionGroup.Type.ROW, addedIndex);
-            }
-        }
+        return result;
     }
 
     /**
@@ -265,14 +204,14 @@
      *
      * @return the list of target indexes
      */
-    private List<Integer> getTargetIndexes(
+    private Set<Integer> getTargetIndexes(
             final EditorController editorController,
             final FXOMObject targetGridPane) {
 
         final Selection selection = editorController.getSelection();
         final AbstractSelectionGroup asg = selection.getGroup();
 
-        final List<Integer> result = new ArrayList<>();
+        final Set<Integer> result = new HashSet<>();
 
         // Selection == GridPane rows
         // => return the list of selected rows
--- a/apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/DeleteColumnJob.java	Thu Aug 28 08:55:45 2014 +0200
+++ b/apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/DeleteColumnJob.java	Thu Aug 28 09:03:45 2014 +0200
@@ -32,13 +32,12 @@
 package com.oracle.javafx.scenebuilder.kit.editor.job.gridpane;
 
 import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
-import com.oracle.javafx.scenebuilder.kit.editor.job.BatchJob;
+import com.oracle.javafx.scenebuilder.kit.editor.job.BatchSelectionJob;
 import com.oracle.javafx.scenebuilder.kit.editor.job.Job;
 import com.oracle.javafx.scenebuilder.kit.editor.job.togglegroup.AdjustAllToggleGroupJob;
 import com.oracle.javafx.scenebuilder.kit.editor.selection.AbstractSelectionGroup;
 import com.oracle.javafx.scenebuilder.kit.editor.selection.GridSelectionGroup;
 import com.oracle.javafx.scenebuilder.kit.editor.selection.Selection;
-import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
 import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;
 import java.util.ArrayList;
@@ -48,72 +47,22 @@
 /**
  * Job invoked when removing columns.
  */
-public class DeleteColumnJob extends Job {
+public class DeleteColumnJob extends BatchSelectionJob {
 
-    private BatchJob subJob;
     private FXOMObject targetGridPane;
     private final List<Integer> targetIndexes = new ArrayList<>();
-    private String description; // Final but constructed lazily
 
     public DeleteColumnJob(EditorController editorController) {
         super(editorController);
-        buildSubJobs();
     }
 
     @Override
-    public boolean isExecutable() {
-        return subJob != null && subJob.isExecutable();
-    }
+    protected List<Job> makeSubJobs() {
 
-    @Override
-    public void execute() {
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-
-        assert isExecutable(); // (1)
-        assert targetIndexes.isEmpty() == false; // Because of (1)
-
-        fxomDocument.beginUpdate();
-        subJob.execute();
-        fxomDocument.endUpdate();
-    }
-
-    @Override
-    public void undo() {
-        assert subJob != null;
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-
-        fxomDocument.beginUpdate();
-        subJob.undo();
-        fxomDocument.endUpdate();
-    }
-
-    @Override
-    public void redo() {
-        assert subJob != null;
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-
-        fxomDocument.beginUpdate();
-        subJob.redo();
-        fxomDocument.endUpdate();
-    }
-
-    @Override
-    public String getDescription() {
-        if (description == null) {
-            buildDescription();
-        }
-
-        return description;
-    }
-
-    private void buildSubJobs() {
+        final List<Job> result = new ArrayList<>();
 
         if (GridPaneJobUtils.canPerformRemove(getEditorController())) { // (1)
 
-            // Create sub job
-            subJob = new BatchJob(getEditorController(),
-                    true /* shouldUpdateSceneGraph */, null);
-
             // Retrieve the target GridPane
             final Selection selection = getEditorController().getSelection();
             final AbstractSelectionGroup asg = selection.getGroup();
@@ -127,20 +76,44 @@
             // First remove the column constraints
             final Job removeConstraints = new RemoveColumnConstraintsJob(
                     getEditorController(), targetGridPane, targetIndexes);
-            subJob.addSubJob(removeConstraints);
+            result.add(removeConstraints);
             // Then remove the column content
             final Job removeContent = new RemoveColumnContentJob(
                     getEditorController(), targetGridPane, targetIndexes);
-            subJob.addSubJob(removeContent);
-            subJob.addSubJob(new AdjustAllToggleGroupJob(getEditorController()));
+            result.add(removeContent);
+            result.add(new AdjustAllToggleGroupJob(getEditorController()));
             // Finally shift the column content
-            moveColumnContent();
+            result.addAll(moveColumnContent());
         }
+        return result;
     }
 
-    private void moveColumnContent() {
+    @Override
+    protected String makeDescription() {
+        String result;
+        switch (targetIndexes.size()) {
+            case 0:
+                result = "Unexecutable Delete"; //NO18N
+                break;
+            case 1:
+                result = "Delete Column"; //NO18N
+                break;
+            default:
+                result = makeMultipleSelectionDescription();
+                break;
+        }
+        return result;
+    }
 
-        assert subJob != null;
+    @Override
+    protected AbstractSelectionGroup getNewSelectionGroup() {
+        // Selection emptied
+        return null;
+    }
+
+    private List<Job> moveColumnContent() {
+
+        final List<Job> result = new ArrayList<>();
 
         final DesignHierarchyMask targetGridPaneMask
                 = new DesignHierarchyMask(targetGridPane);
@@ -178,26 +151,13 @@
                         = GridPaneJobUtils.getIndexes(fromIndex, toIndex);
                 final ReIndexColumnContentJob reIndexJob = new ReIndexColumnContentJob(
                         getEditorController(), offset, targetGridPane, indexes);
-                subJob.addSubJob(reIndexJob);
+                result.add(reIndexJob);
             }
 
             targetIndex = nextTargetIndex;
             shiftIndex--;
         }
-    }
-
-    private void buildDescription() {
-        switch (targetIndexes.size()) {
-            case 0:
-                description = "Unexecutable Delete"; //NO18N
-                break;
-            case 1:
-                description = "Delete Column"; //NO18N
-                break;
-            default:
-                description = makeMultipleSelectionDescription();
-                break;
-        }
+        return result;
     }
 
     private String makeMultipleSelectionDescription() {
--- a/apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/DeleteRowJob.java	Thu Aug 28 08:55:45 2014 +0200
+++ b/apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/DeleteRowJob.java	Thu Aug 28 09:03:45 2014 +0200
@@ -32,13 +32,12 @@
 package com.oracle.javafx.scenebuilder.kit.editor.job.gridpane;
 
 import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
-import com.oracle.javafx.scenebuilder.kit.editor.job.BatchJob;
+import com.oracle.javafx.scenebuilder.kit.editor.job.BatchSelectionJob;
 import com.oracle.javafx.scenebuilder.kit.editor.job.Job;
 import com.oracle.javafx.scenebuilder.kit.editor.job.togglegroup.AdjustAllToggleGroupJob;
 import com.oracle.javafx.scenebuilder.kit.editor.selection.AbstractSelectionGroup;
 import com.oracle.javafx.scenebuilder.kit.editor.selection.GridSelectionGroup;
 import com.oracle.javafx.scenebuilder.kit.editor.selection.Selection;
-import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
 import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;
 import java.util.ArrayList;
@@ -48,72 +47,22 @@
 /**
  * Job invoked when removing rows.
  */
-public class DeleteRowJob extends Job {
+public class DeleteRowJob extends BatchSelectionJob {
 
-    private BatchJob subJob;
     private FXOMObject targetGridPane;
     private final List<Integer> targetIndexes = new ArrayList<>();
-    private String description; // Final but constructed lazily
 
     public DeleteRowJob(EditorController editorController) {
         super(editorController);
-        buildSubJobs();
     }
 
     @Override
-    public boolean isExecutable() {
-        return subJob != null && subJob.isExecutable();
-    }
+    protected List<Job> makeSubJobs() {
 
-    @Override
-    public void execute() {
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-
-        assert isExecutable(); // (1)
-        assert targetIndexes.isEmpty() == false; // Because of (1)
-
-        fxomDocument.beginUpdate();
-        subJob.execute();
-        fxomDocument.endUpdate();
-    }
-
-    @Override
-    public void undo() {
-        assert subJob != null;
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-
-        fxomDocument.beginUpdate();
-        subJob.undo();
-        fxomDocument.endUpdate();
-    }
-
-    @Override
-    public void redo() {
-        assert subJob != null;
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-
-        fxomDocument.beginUpdate();
-        subJob.redo();
-        fxomDocument.endUpdate();
-    }
-
-    @Override
-    public String getDescription() {
-        if (description == null) {
-            buildDescription();
-        }
-
-        return description;
-    }
-
-    private void buildSubJobs() {
+        final List<Job> result = new ArrayList<>();
 
         if (GridPaneJobUtils.canPerformRemove(getEditorController())) { // (1)
 
-            // Create sub job
-            subJob = new BatchJob(getEditorController(),
-                    true /* shouldUpdateSceneGraph */, null);
-
             // Retrieve the target GridPane
             final Selection selection = getEditorController().getSelection();
             final AbstractSelectionGroup asg = selection.getGroup();
@@ -127,20 +76,44 @@
             // First remove the row constraints
             final Job removeConstraints = new RemoveRowConstraintsJob(
                     getEditorController(), targetGridPane, targetIndexes);
-            subJob.addSubJob(removeConstraints);
+            result.add(removeConstraints);
             // Then remove the row content
             final Job removeContent = new RemoveRowContentJob(
                     getEditorController(), targetGridPane, targetIndexes);
-            subJob.addSubJob(removeContent);
-            subJob.addSubJob(new AdjustAllToggleGroupJob(getEditorController()));
+            result.add(removeContent);
+            result.add(new AdjustAllToggleGroupJob(getEditorController()));
             // Finally shift the row content
-            moveRowContent();
+            result.addAll(moveRowContent());
         }
+        return result;
     }
 
-    private void moveRowContent() {
+    @Override
+    protected String makeDescription() {
+        String result;
+        switch (targetIndexes.size()) {
+            case 0:
+                result = "Unexecutable Delete"; //NO18N
+                break;
+            case 1:
+                result = "Delete Row"; //NO18N
+                break;
+            default:
+                result = makeMultipleSelectionDescription();
+                break;
+        }
+        return result;
+    }
 
-        assert subJob != null;
+    @Override
+    protected AbstractSelectionGroup getNewSelectionGroup() {
+        // Selection emptied
+        return null;
+    }
+
+    private List<Job> moveRowContent() {
+
+        final List<Job> result = new ArrayList<>();
 
         final DesignHierarchyMask targetGridPaneMask
                 = new DesignHierarchyMask(targetGridPane);
@@ -178,26 +151,13 @@
                         = GridPaneJobUtils.getIndexes(fromIndex, toIndex);
                 final ReIndexRowContentJob reIndexJob = new ReIndexRowContentJob(
                         getEditorController(), offset, targetGridPane, indexes);
-                subJob.addSubJob(reIndexJob);
+                result.add(reIndexJob);
             }
 
             targetIndex = nextTargetIndex;
             shiftIndex--;
         }
-    }
-
-    private void buildDescription() {
-        switch (targetIndexes.size()) {
-            case 0:
-                description = "Unexecutable Delete"; //NO18N
-                break;
-            case 1:
-                description = "Delete Row"; //NO18N
-                break;
-            default:
-                description = makeMultipleSelectionDescription();
-                break;
-        }
+        return result;
     }
 
     private String makeMultipleSelectionDescription() {
--- a/apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/GridPaneJobUtils.java	Thu Aug 28 08:55:45 2014 +0200
+++ b/apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/GridPaneJobUtils.java	Thu Aug 28 09:03:45 2014 +0200
@@ -40,7 +40,9 @@
 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
 import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import javafx.scene.layout.GridPane;
 
 /**
@@ -108,10 +110,10 @@
      *
      * @return the list of target indexes
      */
-    static List<Integer> getAddedIndexes(
-            final List<Integer> targetIndexes,
+    static Set<Integer> getAddedIndexes(
+            final Set<Integer> targetIndexes,
             final Position position) {
-        final List<Integer> result = new ArrayList<>();
+        final Set<Integer> result = new HashSet<>();
         int shiftIndex = 0;
         for (int targetIndex : targetIndexes) {
             int addedIndex = targetIndex + shiftIndex++;
--- a/apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/MoveColumnJob.java	Thu Aug 28 08:55:45 2014 +0200
+++ b/apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/MoveColumnJob.java	Thu Aug 28 09:03:45 2014 +0200
@@ -32,7 +32,7 @@
 package com.oracle.javafx.scenebuilder.kit.editor.job.gridpane;
 
 import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
-import com.oracle.javafx.scenebuilder.kit.editor.job.BatchJob;
+import com.oracle.javafx.scenebuilder.kit.editor.job.BatchSelectionJob;
 import com.oracle.javafx.scenebuilder.kit.editor.job.DeleteObjectJob;
 import com.oracle.javafx.scenebuilder.kit.editor.job.Job;
 import com.oracle.javafx.scenebuilder.kit.editor.job.gridpane.GridPaneJobUtils.Position;
@@ -48,16 +48,16 @@
 import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;
 import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import javafx.scene.layout.ColumnConstraints;
 
 /**
  * Job invoked when moving columns BEFORE or AFTER.
  */
-public class MoveColumnJob extends Job {
+public class MoveColumnJob extends BatchSelectionJob {
 
-    private BatchJob subJob;
-    private AbstractSelectionGroup selectionSnapshot;
     private FXOMObject targetGridPane;
     private final List<Integer> targetIndexes = new ArrayList<>();
     private final GridPaneJobUtils.Position position;
@@ -66,79 +66,15 @@
         super(editorController);
         assert position == Position.BEFORE || position == Position.AFTER;
         this.position = position;
-        buildSubJobs();
     }
 
     @Override
-    public boolean isExecutable() {
-        return subJob != null && subJob.isExecutable();
-    }
+    protected List<Job> makeSubJobs() {
 
-    @Override
-    public void execute() {
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-        final Selection selection = getEditorController().getSelection();
-
-        assert isExecutable(); // (1)
-        assert targetIndexes.isEmpty() == false; // Because of (1)
-
-        try {
-            selectionSnapshot = selection.getGroup().clone();
-        } catch (CloneNotSupportedException x) {
-            // Emergency code
-            throw new RuntimeException(x);
-        }
-        selection.clear();
-        selection.beginUpdate();
-        fxomDocument.beginUpdate();
-        subJob.execute();
-        fxomDocument.endUpdate();
-        updateSelection();
-        selection.endUpdate();
-    }
-
-    @Override
-    public void undo() {
-        assert subJob != null;
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-        final Selection selection = getEditorController().getSelection();
-
-        selection.beginUpdate();
-        fxomDocument.beginUpdate();
-        subJob.undo();
-        fxomDocument.endUpdate();
-        selection.select(selectionSnapshot);
-        selection.endUpdate();
-    }
-
-    @Override
-    public void redo() {
-        assert subJob != null;
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-        final Selection selection = getEditorController().getSelection();
-
-        selection.clear();
-        selection.beginUpdate();
-        fxomDocument.beginUpdate();
-        subJob.redo();
-        fxomDocument.endUpdate();
-        updateSelection();
-        selection.endUpdate();
-    }
-
-    @Override
-    public String getDescription() {
-        return "Move Column " + position.name(); //NOI18N
-    }
-
-    private void buildSubJobs() {
+        final List<Job> result = new ArrayList<>();
 
         if (GridPaneJobUtils.canPerformMove(getEditorController(), position)) {
 
-            // Create sub job
-            subJob = new BatchJob(getEditorController(),
-                    true /* shouldUpdateSceneGraph */, null);
-
             // Retrieve the target GridPane
             final Selection selection = getEditorController().getSelection();
             final AbstractSelectionGroup asg = selection.getGroup();
@@ -150,15 +86,32 @@
 
             // Add sub jobs
             // First move the column constraints
-            moveColumnConstraints();
+            result.addAll(moveColumnConstraints());
             // Then move the column content
-            moveColumnContent();
+            result.addAll(moveColumnContent());
         }
+        return result;
     }
 
-    private void moveColumnConstraints() {
+    @Override
+    protected String makeDescription() {
+        return "Move Column " + position.name(); //NOI18N
+    }
 
-        assert subJob != null;
+    @Override
+    protected AbstractSelectionGroup getNewSelectionGroup() {
+        final Set<Integer> movedIndexes = new HashSet<>();
+        for (int targetIndex : targetIndexes) {
+            int movedIndex = position == Position.BEFORE
+                    ? targetIndex - 1 : targetIndex + 1;
+            movedIndexes.add(movedIndex);
+        }
+        return new GridSelectionGroup(targetGridPane, GridSelectionGroup.Type.COLUMN, movedIndexes);
+    }
+
+    private List<Job> moveColumnConstraints() {
+
+        final List<Job> result = new ArrayList<>();
 
         // Retrieve the constraints property for the specified target GridPane
         final PropertyName propertyName = new PropertyName("columnConstraints"); //NOI18N
@@ -167,7 +120,7 @@
                 = ((FXOMInstance) targetGridPane).getProperties().get(propertyName);
         // GridPane has no constraints property => no constraints to move
         if (constraintsProperty == null) {
-            return;
+            return result;
         }
 
         final DesignHierarchyMask mask = new DesignHierarchyMask(targetGridPane);
@@ -183,7 +136,7 @@
                     break;
                 default:
                     assert false;
-                    return;
+                    return result;
             }
 
             // Retrieve the target constraints
@@ -199,14 +152,14 @@
                 final Job removeValueJob = new DeleteObjectJob(
                         targetConstraints,
                         getEditorController());
-                subJob.addSubJob(removeValueJob);
+                result.add(removeValueJob);
 
                 // Then add the target constraints at new positionIndex
                 final Job addValueJob = new AddPropertyValueJob(
                         targetConstraints,
                         (FXOMPropertyC) constraintsProperty,
                         positionIndex, getEditorController());
-                subJob.addSubJob(addValueJob);
+                result.add(addValueJob);
             }//
             // The target index is not associated to an existing constraints value :
             // we may need to move the constraints before the target one if any
@@ -224,22 +177,23 @@
                             addedConstraints,
                             (FXOMPropertyC) constraintsProperty,
                             positionIndex, getEditorController());
-                    subJob.addSubJob(addValueJob);
+                    result.add(addValueJob);
                 }
             }
         }
+        return result;
     }
 
-    private void moveColumnContent() {
+    private List<Job> moveColumnContent() {
 
-        assert subJob != null;
+        final List<Job> result = new ArrayList<>();
 
         for (int targetIndex : targetIndexes) {
 
             switch (position) {
                 case BEFORE:
                     // First move the target column content
-                    subJob.addSubJob(new ReIndexColumnContentJob(
+                    result.add(new ReIndexColumnContentJob(
                             getEditorController(),
                             -1, targetGridPane, targetIndex));
                     int beforeIndex = targetIndex - 1;
@@ -251,14 +205,14 @@
                         while (targetIndexes.contains(targetIndex + shiftIndex)) {
                             shiftIndex++;
                         }
-                        subJob.addSubJob(new ReIndexColumnContentJob(
+                        result.add(new ReIndexColumnContentJob(
                                 getEditorController(),
                                 shiftIndex, targetGridPane, beforeIndex));
                     }
                     break;
                 case AFTER:
                     // First move the target column content
-                    subJob.addSubJob(new ReIndexColumnContentJob(
+                    result.add(new ReIndexColumnContentJob(
                             getEditorController(),
                             +1, targetGridPane, targetIndex));
                     int afterIndex = targetIndex + 1;
@@ -270,16 +224,16 @@
                         while (targetIndexes.contains(targetIndex + shiftIndex)) {
                             shiftIndex--;
                         }
-                        subJob.addSubJob(new ReIndexColumnContentJob(
+                        result.add(new ReIndexColumnContentJob(
                                 getEditorController(),
                                 shiftIndex, targetGridPane, afterIndex));
                     }
                     break;
                 default:
                     assert false;
-                    return;
             }
         }
+        return result;
     }
 
     private FXOMInstance makeColumnConstraintsInstance() {
@@ -293,19 +247,4 @@
 
         return result;
     }
-
-    private void updateSelection() {
-        final Selection selection = getEditorController().getSelection();
-
-        // Selection has been cleared at execution time
-        assert selection.isEmpty();
-        for (int targetIndex : targetIndexes) {
-            int positionIndex = position == Position.BEFORE
-                    ? targetIndex - 1 : targetIndex + 1;
-
-            // Selection is empty => just toggle selection
-            selection.toggleSelection((FXOMInstance) targetGridPane,
-                    GridSelectionGroup.Type.COLUMN, positionIndex);
-        }
-    }
 }
--- a/apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/MoveRowJob.java	Thu Aug 28 08:55:45 2014 +0200
+++ b/apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/MoveRowJob.java	Thu Aug 28 09:03:45 2014 +0200
@@ -32,7 +32,7 @@
 package com.oracle.javafx.scenebuilder.kit.editor.job.gridpane;
 
 import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
-import com.oracle.javafx.scenebuilder.kit.editor.job.BatchJob;
+import com.oracle.javafx.scenebuilder.kit.editor.job.BatchSelectionJob;
 import com.oracle.javafx.scenebuilder.kit.editor.job.DeleteObjectJob;
 import com.oracle.javafx.scenebuilder.kit.editor.job.Job;
 import com.oracle.javafx.scenebuilder.kit.editor.job.gridpane.GridPaneJobUtils.Position;
@@ -48,16 +48,16 @@
 import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;
 import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import javafx.scene.layout.RowConstraints;
 
 /**
  * Job invoked when moving rows ABOVE or BELOW.
  */
-public class MoveRowJob extends Job {
+public class MoveRowJob extends BatchSelectionJob {
 
-    private BatchJob subJob;
-    private AbstractSelectionGroup selectionSnapshot;
     private FXOMObject targetGridPane;
     private final List<Integer> targetIndexes = new ArrayList<>();
     private final Position position;
@@ -66,79 +66,15 @@
         super(editorController);
         assert position == Position.ABOVE || position == Position.BELOW;
         this.position = position;
-        buildSubJobs();
     }
 
     @Override
-    public boolean isExecutable() {
-        return subJob != null && subJob.isExecutable();
-    }
+    protected List<Job> makeSubJobs() {
 
-    @Override
-    public void execute() {
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-        final Selection selection = getEditorController().getSelection();
-
-        assert isExecutable(); // (1)
-        assert targetIndexes.isEmpty() == false; // Because of (1)
-
-        try {
-            selectionSnapshot = selection.getGroup().clone();
-        } catch (CloneNotSupportedException x) {
-            // Emergency code
-            throw new RuntimeException(x);
-        }
-        selection.clear();
-        selection.beginUpdate();
-        fxomDocument.beginUpdate();
-        subJob.execute();
-        fxomDocument.endUpdate();
-        updateSelection();
-        selection.endUpdate();
-    }
-
-    @Override
-    public void undo() {
-        assert subJob != null;
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-        final Selection selection = getEditorController().getSelection();
-
-        selection.beginUpdate();
-        fxomDocument.beginUpdate();
-        subJob.undo();
-        fxomDocument.endUpdate();
-        selection.select(selectionSnapshot);
-        selection.endUpdate();
-    }
-
-    @Override
-    public void redo() {
-        assert subJob != null;
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-        final Selection selection = getEditorController().getSelection();
-
-        selection.clear();
-        selection.beginUpdate();
-        fxomDocument.beginUpdate();
-        subJob.redo();
-        fxomDocument.endUpdate();
-        updateSelection();
-        selection.endUpdate();
-    }
-
-    @Override
-    public String getDescription() {
-        return "Move Row " + position.name(); //NOI18N
-    }
-
-    private void buildSubJobs() {
+        final List<Job> result = new ArrayList<>();
 
         if (GridPaneJobUtils.canPerformMove(getEditorController(), position)) {
 
-            // Create sub job
-            subJob = new BatchJob(getEditorController(),
-                    true /* shouldUpdateSceneGraph */, null);
-
             // Retrieve the target GridPane
             final Selection selection = getEditorController().getSelection();
             final AbstractSelectionGroup asg = selection.getGroup();
@@ -150,15 +86,32 @@
 
             // Add sub jobs
             // First move the row constraints
-            moveRowConstraints();
+            result.addAll(moveRowConstraints());
             // Then move the row content
-            moveRowContent();
+            result.addAll(moveRowContent());
         }
+        return result;
     }
 
-    private void moveRowConstraints() {
+    @Override
+    protected String makeDescription() {
+        return "Move Row " + position.name(); //NOI18N
+    }
 
-        assert subJob != null;
+    @Override
+    protected AbstractSelectionGroup getNewSelectionGroup() {
+        final Set<Integer> movedIndexes = new HashSet<>();
+        for (int targetIndex : targetIndexes) {
+            int movedIndex = position == Position.ABOVE
+                    ? targetIndex - 1 : targetIndex + 1;
+            movedIndexes.add(movedIndex);
+        }
+        return new GridSelectionGroup(targetGridPane, GridSelectionGroup.Type.ROW, movedIndexes);
+    }
+
+    private List<Job> moveRowConstraints() {
+
+        final List<Job> result = new ArrayList<>();
 
         // Retrieve the constraints property for the specified target GridPane
         final PropertyName propertyName = new PropertyName("rowConstraints"); //NOI18N
@@ -167,7 +120,7 @@
                 = ((FXOMInstance) targetGridPane).getProperties().get(propertyName);
         // GridPane has no constraints property => no constraints to move
         if (constraintsProperty == null) {
-            return;
+            return result;
         }
 
         final DesignHierarchyMask mask = new DesignHierarchyMask(targetGridPane);
@@ -183,7 +136,7 @@
                     break;
                 default:
                     assert false;
-                    return;
+                    return result;
             }
 
             // Retrieve the target constraints
@@ -199,14 +152,14 @@
                 final Job removeValueJob = new DeleteObjectJob(
                         targetConstraints,
                         getEditorController());
-                subJob.addSubJob(removeValueJob);
+                result.add(removeValueJob);
 
                 // Then add the target constraints at new positionIndex
                 final Job addValueJob = new AddPropertyValueJob(
                         targetConstraints,
                         (FXOMPropertyC) constraintsProperty,
                         positionIndex, getEditorController());
-                subJob.addSubJob(addValueJob);
+                result.add(addValueJob);
             }//
             // The target index is not associated to an existing constraints value :
             // we may need to move the constraints above the target one if any
@@ -224,22 +177,23 @@
                             addedConstraints,
                             (FXOMPropertyC) constraintsProperty,
                             positionIndex, getEditorController());
-                    subJob.addSubJob(addValueJob);
+                    result.add(addValueJob);
                 }
             }
         }
+        return result;
     }
 
-    private void moveRowContent() {
+    private List<Job> moveRowContent() {
 
-        assert subJob != null;
+        final List<Job> result = new ArrayList<>();
 
         for (int targetIndex : targetIndexes) {
 
             switch (position) {
                 case ABOVE:
                     // First move the target row content
-                    subJob.addSubJob(new ReIndexRowContentJob(
+                    result.add(new ReIndexRowContentJob(
                             getEditorController(),
                             -1, targetGridPane, targetIndex));
                     int aboveIndex = targetIndex - 1;
@@ -251,14 +205,14 @@
                         while (targetIndexes.contains(targetIndex + shiftIndex)) {
                             shiftIndex++;
                         }
-                        subJob.addSubJob(new ReIndexRowContentJob(
+                        result.add(new ReIndexRowContentJob(
                                 getEditorController(),
                                 shiftIndex, targetGridPane, aboveIndex));
                     }
                     break;
                 case BELOW:
                     // First move the target row content
-                    subJob.addSubJob(new ReIndexRowContentJob(
+                    result.add(new ReIndexRowContentJob(
                             getEditorController(),
                             +1, targetGridPane, targetIndex));
                     int belowIndex = targetIndex + 1;
@@ -270,16 +224,16 @@
                         while (targetIndexes.contains(targetIndex + shiftIndex)) {
                             shiftIndex--;
                         }
-                        subJob.addSubJob(new ReIndexRowContentJob(
+                        result.add(new ReIndexRowContentJob(
                                 getEditorController(),
                                 shiftIndex, targetGridPane, belowIndex));
                     }
                     break;
                 default:
                     assert false;
-                    return;
             }
         }
+        return result;
     }
 
     private FXOMInstance makeRowConstraintsInstance() {
@@ -293,19 +247,4 @@
 
         return result;
     }
-
-    private void updateSelection() {
-        final Selection selection = getEditorController().getSelection();
-
-        // Selection has been cleared at execution time
-        assert selection.isEmpty();
-        for (int targetIndex : targetIndexes) {
-            int positionIndex = position == Position.ABOVE
-                    ? targetIndex - 1 : targetIndex + 1;
-
-            // Selection is empty => just toggle selection
-            selection.toggleSelection((FXOMInstance) targetGridPane,
-                    GridSelectionGroup.Type.ROW, positionIndex);
-        }
-    }
 }
--- a/apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/RemoveColumnConstraintsJob.java	Thu Aug 28 08:55:45 2014 +0200
+++ b/apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/RemoveColumnConstraintsJob.java	Thu Aug 28 09:03:45 2014 +0200
@@ -32,21 +32,20 @@
 package com.oracle.javafx.scenebuilder.kit.editor.job.gridpane;
 
 import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
-import com.oracle.javafx.scenebuilder.kit.editor.job.BatchJob;
+import com.oracle.javafx.scenebuilder.kit.editor.job.BatchDocumentJob;
 import com.oracle.javafx.scenebuilder.kit.editor.job.DeleteObjectJob;
 import com.oracle.javafx.scenebuilder.kit.editor.job.Job;
-import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
 import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;
+import java.util.ArrayList;
 import java.util.List;
 
 /**
  * Job invoked when removing column constraints.
  */
-public class RemoveColumnConstraintsJob extends Job {
+public class RemoveColumnConstraintsJob extends BatchDocumentJob {
 
-    private BatchJob subJob;
     private final FXOMObject targetGridPane;
     private final List<Integer> targetIndexes;
 
@@ -60,51 +59,12 @@
         assert targetIndexes != null;
         this.targetGridPane = targetGridPane;
         this.targetIndexes = targetIndexes;
-        buildSubJobs();
     }
 
     @Override
-    public boolean isExecutable() {
-        // Remove column constraints job may be empty 
-        // (when there is no constraints defined for the selected column).
-        return subJob != null;
-    }
+    protected List<Job> makeSubJobs() {
 
-    @Override
-    public void execute() {
-        assert isExecutable();
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-        fxomDocument.beginUpdate();
-        subJob.execute();
-        fxomDocument.endUpdate();
-    }
-
-    @Override
-    public void undo() {
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-        fxomDocument.beginUpdate();
-        subJob.undo();
-        fxomDocument.endUpdate();
-    }
-
-    @Override
-    public void redo() {
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-        fxomDocument.beginUpdate();
-        subJob.redo();
-        fxomDocument.endUpdate();
-    }
-
-    @Override
-    public String getDescription() {
-        return "Remove Column Constraints"; //NOI18N
-    }
-
-    private void buildSubJobs() {
-
-        // Create sub job
-        subJob = new BatchJob(getEditorController(),
-                true /* shouldRefreshSceneGraph */, null);
+        final List<Job> result = new ArrayList<>();
 
         // Remove column constraints job
         assert targetGridPane instanceof FXOMInstance;
@@ -112,7 +72,7 @@
 
         final DesignHierarchyMask mask = new DesignHierarchyMask(targetGridPane);
         for (int targetIndex : targetIndexes) {
-            final FXOMObject targetConstraints 
+            final FXOMObject targetConstraints
                     = mask.getColumnConstraintsAtIndex(targetIndex);
             // The target index is associated to an existing constraints value :
             // => we remove the constraints value
@@ -120,8 +80,14 @@
                 final Job removeValueJob = new DeleteObjectJob(
                         targetConstraints,
                         getEditorController());
-                subJob.addSubJob(removeValueJob);
+                result.add(removeValueJob);
             }
         }
+        return result;
+    }
+
+    @Override
+    protected String makeDescription() {
+        return "Remove Column Constraints"; //NOI18N
     }
 }
--- a/apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/RemoveColumnContentJob.java	Thu Aug 28 08:55:45 2014 +0200
+++ b/apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/RemoveColumnContentJob.java	Thu Aug 28 09:03:45 2014 +0200
@@ -32,22 +32,21 @@
 package com.oracle.javafx.scenebuilder.kit.editor.job.gridpane;
 
 import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
-import com.oracle.javafx.scenebuilder.kit.editor.job.BatchJob;
+import com.oracle.javafx.scenebuilder.kit.editor.job.BatchDocumentJob;
 import com.oracle.javafx.scenebuilder.kit.editor.job.DeleteObjectJob;
 import com.oracle.javafx.scenebuilder.kit.editor.job.Job;
 import com.oracle.javafx.scenebuilder.kit.editor.job.togglegroup.AdjustAllToggleGroupJob;
-import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
 import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;
+import java.util.ArrayList;
 import java.util.List;
 
 /**
  * Job invoked when removing column content.
  */
-public class RemoveColumnContentJob extends Job {
+public class RemoveColumnContentJob extends BatchDocumentJob {
 
-    private BatchJob subJob;
     private final FXOMObject targetGridPane;
     private final List<Integer> targetIndexes;
 
@@ -61,52 +60,12 @@
         assert targetIndexes != null;
         this.targetGridPane = targetGridPane;
         this.targetIndexes = targetIndexes;
-        buildSubJobs();
     }
 
     @Override
-    public boolean isExecutable() {
-        // Remove column content job may be empty 
-        // (when removing a column with no content).
-        // So we do not invoke subJob.isExecutable() here. 
-        return subJob != null;
-    }
+    protected List<Job> makeSubJobs() {
 
-    @Override
-    public void execute() {
-        assert isExecutable();
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-        fxomDocument.beginUpdate();
-        subJob.execute();
-        fxomDocument.endUpdate();
-    }
-
-    @Override
-    public void undo() {
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-        fxomDocument.beginUpdate();
-        subJob.undo();
-        fxomDocument.endUpdate();
-    }
-
-    @Override
-    public void redo() {
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-        fxomDocument.beginUpdate();
-        subJob.redo();
-        fxomDocument.endUpdate();
-    }
-
-    @Override
-    public String getDescription() {
-        return "Remove Column Content"; //NOI18N
-    }
-
-    private void buildSubJobs() {
-
-        // Create sub job
-        subJob = new BatchJob(getEditorController(),
-                true /* shouldRefreshSceneGraph */, null);
+        final List<Job> result = new ArrayList<>();
 
         assert targetGridPane instanceof FXOMInstance;
         assert targetIndexes.isEmpty() == false;
@@ -120,11 +79,17 @@
                 final Job removeChildJob = new DeleteObjectJob(
                         child,
                         getEditorController());
-                subJob.addSubJob(removeChildJob);
+                result.add(removeChildJob);
             }
         }
-        
+
         final Job adjustToggleGroups = new AdjustAllToggleGroupJob(getEditorController());
-        subJob.addSubJob(adjustToggleGroups);
+        result.add(adjustToggleGroups);
+        return result;
+    }
+
+    @Override
+    protected String makeDescription() {
+        return "Remove Column Content"; //NOI18N
     }
 }
--- a/apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/RemoveRowConstraintsJob.java	Thu Aug 28 08:55:45 2014 +0200
+++ b/apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/RemoveRowConstraintsJob.java	Thu Aug 28 09:03:45 2014 +0200
@@ -32,21 +32,20 @@
 package com.oracle.javafx.scenebuilder.kit.editor.job.gridpane;
 
 import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
-import com.oracle.javafx.scenebuilder.kit.editor.job.BatchJob;
+import com.oracle.javafx.scenebuilder.kit.editor.job.BatchDocumentJob;
 import com.oracle.javafx.scenebuilder.kit.editor.job.DeleteObjectJob;
 import com.oracle.javafx.scenebuilder.kit.editor.job.Job;
-import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
 import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;
+import java.util.ArrayList;
 import java.util.List;
 
 /**
  * Job invoked when removing row constraints.
  */
-public class RemoveRowConstraintsJob extends Job {
+public class RemoveRowConstraintsJob extends BatchDocumentJob {
 
-    private BatchJob subJob;
     private final FXOMObject targetGridPane;
     private final List<Integer> targetIndexes;
 
@@ -60,51 +59,12 @@
         assert targetIndexes != null;
         this.targetGridPane = targetGridPane;
         this.targetIndexes = targetIndexes;
-        buildSubJobs();
     }
 
     @Override
-    public boolean isExecutable() {
-        // Remove row constraints job may be empty 
-        // (when there is no constraints defined for the selected row).
-        return subJob != null;
-    }
+    protected List<Job> makeSubJobs() {
 
-    @Override
-    public void execute() {
-        assert isExecutable();
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-        fxomDocument.beginUpdate();
-        subJob.execute();
-        fxomDocument.endUpdate();
-    }
-
-    @Override
-    public void undo() {
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-        fxomDocument.beginUpdate();
-        subJob.undo();
-        fxomDocument.endUpdate();
-    }
-
-    @Override
-    public void redo() {
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-        fxomDocument.beginUpdate();
-        subJob.redo();
-        fxomDocument.endUpdate();
-    }
-
-    @Override
-    public String getDescription() {
-        return "Remove Row Constraints"; //NOI18N
-    }
-
-    private void buildSubJobs() {
-
-        // Create sub job
-        subJob = new BatchJob(getEditorController(),
-                true /* shouldRefreshSceneGraph */, null);
+        final List<Job> result = new ArrayList<>();
 
         // Remove row constraints job
         assert targetGridPane instanceof FXOMInstance;
@@ -112,7 +72,7 @@
 
         final DesignHierarchyMask mask = new DesignHierarchyMask(targetGridPane);
         for (int targetIndex : targetIndexes) {
-            final FXOMObject targetConstraints 
+            final FXOMObject targetConstraints
                     = mask.getRowConstraintsAtIndex(targetIndex);
             // The target index is associated to an existing constraints value :
             // => we remove the constraints value
@@ -120,8 +80,14 @@
                 final Job removeValueJob = new DeleteObjectJob(
                         targetConstraints,
                         getEditorController());
-                subJob.addSubJob(removeValueJob);
+                result.add(removeValueJob);
             }
         }
+        return result;
+    }
+
+    @Override
+    protected String makeDescription() {
+        return "Remove Row Constraints"; //NOI18N
     }
 }
--- a/apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/RemoveRowContentJob.java	Thu Aug 28 08:55:45 2014 +0200
+++ b/apps/scenebuilder/SceneBuilderKit/src/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/RemoveRowContentJob.java	Thu Aug 28 09:03:45 2014 +0200
@@ -32,22 +32,21 @@
 package com.oracle.javafx.scenebuilder.kit.editor.job.gridpane;
 
 import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
-import com.oracle.javafx.scenebuilder.kit.editor.job.BatchJob;
+import com.oracle.javafx.scenebuilder.kit.editor.job.BatchDocumentJob;
 import com.oracle.javafx.scenebuilder.kit.editor.job.DeleteObjectJob;
 import com.oracle.javafx.scenebuilder.kit.editor.job.Job;
 import com.oracle.javafx.scenebuilder.kit.editor.job.togglegroup.AdjustAllToggleGroupJob;
-import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
 import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;
+import java.util.ArrayList;
 import java.util.List;
 
 /**
  * Job invoked when removing row content.
  */
-public class RemoveRowContentJob extends Job {
+public class RemoveRowContentJob extends BatchDocumentJob {
 
-    private BatchJob subJob;
     private final FXOMObject targetGridPane;
     private final List<Integer> targetIndexes;
 
@@ -61,52 +60,12 @@
         assert targetIndexes != null;
         this.targetGridPane = targetGridPane;
         this.targetIndexes = targetIndexes;
-        buildSubJobs();
     }
 
     @Override
-    public boolean isExecutable() {
-        // Remove row content job may be empty 
-        // (when removing a row with no content).
-        // So we do not invoke subJob.isExecutable() here. 
-        return subJob != null;
-    }
+    protected List<Job> makeSubJobs() {
 
-    @Override
-    public void execute() {
-        assert isExecutable();
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-        fxomDocument.beginUpdate();
-        subJob.execute();
-        fxomDocument.endUpdate();
-    }
-
-    @Override
-    public void undo() {
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-        fxomDocument.beginUpdate();
-        subJob.undo();
-        fxomDocument.endUpdate();
-    }
-
-    @Override
-    public void redo() {
-        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
-        fxomDocument.beginUpdate();
-        subJob.redo();
-        fxomDocument.endUpdate();
-    }
-
-    @Override
-    public String getDescription() {
-        return "Remove Row Content"; //NOI18N
-    }
-
-    private void buildSubJobs() {
-
-        // Create sub job
-        subJob = new BatchJob(getEditorController(),
-                true /* shouldRefreshSceneGraph */, null);
+        final List<Job> result = new ArrayList<>();
 
         assert targetGridPane instanceof FXOMInstance;
         assert targetIndexes.isEmpty() == false;
@@ -120,11 +79,18 @@
                 final Job removeChildJob = new DeleteObjectJob(
                         child,
                         getEditorController());
-                subJob.addSubJob(removeChildJob);
+                result.add(removeChildJob);
             }
         }
-        
+
         final Job adjustToggleGroups = new AdjustAllToggleGroupJob(getEditorController());
-        subJob.addSubJob(adjustToggleGroups);
+        result.add(adjustToggleGroups);
+
+        return result;
+    }
+
+    @Override
+    protected String makeDescription() {
+        return "Remove Row Content"; //NOI18N
     }
 }