1 /* 2 * Copyright (c) 2012, 2014, Oracle and/or its affiliates. 3 * All rights reserved. Use is subject to license terms. 4 * 5 * This file is available and licensed under the following license: 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * - Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * - Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the distribution. 16 * - Neither the name of Oracle Corporation nor the names of its 17 * contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 package com.oracle.javafx.scenebuilder.kit.editor.job.gridpane; 33 34 import com.oracle.javafx.scenebuilder.kit.editor.EditorController; 35 import com.oracle.javafx.scenebuilder.kit.editor.job.BatchDocumentJob; 36 import com.oracle.javafx.scenebuilder.kit.editor.job.Job; 37 import com.oracle.javafx.scenebuilder.kit.editor.job.JobUtils; 38 import com.oracle.javafx.scenebuilder.kit.editor.job.gridpane.GridPaneJobUtils.Position; 39 import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.AddPropertyJob; 40 import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.AddPropertyValueJob; 41 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument; 42 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance; 43 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject; 44 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMProperty; 45 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMPropertyC; 46 import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask; 47 import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName; 48 import java.util.ArrayList; 49 import java.util.LinkedHashSet; 50 import java.util.List; 51 import java.util.Map; 52 import java.util.Set; 53 import javafx.scene.layout.RowConstraints; 54 55 /** 56 * Job invoked when adding row constraints. 57 */ 58 public class AddRowConstraintsJob extends BatchDocumentJob { 59 60 // Key = target GridPane instance 61 // Value = list of target row indexes for this GridPane 62 private final Map<FXOMObject, Set<Integer>> targetGridPanes; 63 private final Position position; 64 // If the selected row is associated to an existing constraints, 65 // we duplicate the existing constraints. 66 // Otherwise, we use the default values below. 67 private static final double defaultMinHeight = 10.0; 68 private static final double defaultPrefHeight = 30.0; 69 70 public AddRowConstraintsJob( 71 final EditorController editorController, 72 final Position position, 73 final Map<FXOMObject, Set<Integer>> targetGridPanes) { 74 super(editorController); 75 this.position = position; 76 this.targetGridPanes = targetGridPanes; 77 } 78 79 @Override 80 protected List<Job> makeSubJobs() { 81 82 final List<Job> result = new ArrayList<>(); 83 84 // Add column constraints job 85 assert targetGridPanes.isEmpty() == false; 86 for (FXOMObject targetGridPane : targetGridPanes.keySet()) { 87 assert targetGridPane instanceof FXOMInstance; 88 final Set<Integer> targetIndexes = targetGridPanes.get(targetGridPane); 89 result.addAll(addRowConstraints((FXOMInstance) targetGridPane, targetIndexes)); 90 } 91 92 return result; 93 } 94 95 @Override 96 protected String makeDescription() { 97 return "Add Row Constraints"; //NOI18N 98 } 99 100 private Set<Job> addRowConstraints( 101 final FXOMInstance targetGridPane, 102 final Set<Integer> targetIndexes) { 103 104 final Set<Job> result = new LinkedHashSet<>(); 105 final FXOMDocument fxomDocument = getEditorController().getFxomDocument(); 106 107 // Retrieve the constraints property for the specified target GridPane 108 final PropertyName propertyName = new PropertyName("rowConstraints"); //NOI18N 109 FXOMProperty constraintsProperty = targetGridPane.getProperties().get(propertyName); 110 if (constraintsProperty == null) { 111 constraintsProperty = new FXOMPropertyC(fxomDocument, propertyName); 112 } 113 assert constraintsProperty instanceof FXOMPropertyC; 114 115 final DesignHierarchyMask mask = new DesignHierarchyMask(targetGridPane); 116 117 int shiftIndex = 0; 118 int constraintsSize = mask.getRowsConstraintsSize(); 119 for (int targetIndex : targetIndexes) { 120 121 // Retrieve the index for the new constraints to be added 122 int addedIndex = targetIndex + shiftIndex; 123 if (position == Position.BELOW) { 124 addedIndex++; 125 } 126 127 final FXOMObject targetConstraints 128 = mask.getRowConstraintsAtIndex(targetIndex); 129 // The target index is associated to an existing constraints value : 130 // we add a new constraints using the values of the existing one 131 if (targetConstraints != null) { 132 assert targetConstraints instanceof FXOMInstance; 133 // Create new constraints instance with same values as the target one 134 final FXOMInstance addedConstraints = makeRowConstraintsInstance( 135 (FXOMInstance) targetConstraints); 136 137 final Job addValueJob = new AddPropertyValueJob( 138 addedConstraints, 139 (FXOMPropertyC) constraintsProperty, 140 addedIndex, getEditorController()); 141 result.add(addValueJob); 142 } // 143 // The target index is not associated to an existing constraints value : 144 // - we add new empty constraints from the last existing one to the added index (excluded) 145 // - we add a new constraints with default values for the added index 146 else { 147 for (int index = constraintsSize; index < addedIndex; index++) { 148 // Create new empty constraints for the exisiting rows 149 final FXOMInstance addedConstraints = makeRowConstraintsInstance(); 150 final Job addValueJob = new AddPropertyValueJob( 151 addedConstraints, 152 (FXOMPropertyC) constraintsProperty, 153 index, getEditorController()); 154 result.add(addValueJob); 155 } 156 // Create new constraints with default values for the new added row 157 final FXOMInstance addedConstraints = makeRowConstraintsInstance(); 158 JobUtils.setMinHeight(addedConstraints, RowConstraints.class, defaultMinHeight); 159 JobUtils.setPrefHeight(addedConstraints, RowConstraints.class, defaultPrefHeight); 160 final Job addValueJob = new AddPropertyValueJob( 161 addedConstraints, 162 (FXOMPropertyC) constraintsProperty, 163 addedIndex, getEditorController()); 164 result.add(addValueJob); 165 constraintsSize = addedIndex + 1; 166 } 167 shiftIndex++; 168 } 169 170 // Add the constraints property to the target GridPane if not already there. 171 // IMPORTANT : 172 // Note that the AddPropertyJob must be called after the AddPropertyValueJob. 173 if (constraintsProperty.getParentInstance() == null) { 174 final Job addPropertyJob = new AddPropertyJob( 175 constraintsProperty, 176 targetGridPane, 177 -1, getEditorController()); 178 result.add(addPropertyJob); 179 } 180 181 return result; 182 } 183 184 private FXOMInstance makeRowConstraintsInstance() { 185 186 // Create new constraints instance 187 final FXOMDocument newDocument = new FXOMDocument(); 188 final FXOMInstance result 189 = new FXOMInstance(newDocument, RowConstraints.class); 190 newDocument.setFxomRoot(result); 191 result.moveToFxomDocument(getEditorController().getFxomDocument()); 192 193 return result; 194 } 195 196 private FXOMInstance makeRowConstraintsInstance(final FXOMInstance constraints) { 197 198 assert constraints != null; 199 assert constraints.getDeclaredClass() == RowConstraints.class; 200 201 // Create new constraints instance 202 final FXOMInstance result = makeRowConstraintsInstance(); 203 204 // Set the new row constraints values with the values of the specified instance 205 final boolean fillHeight = JobUtils.getFillHeight(constraints, RowConstraints.class); 206 final double maxHeight = JobUtils.getMaxHeight(constraints, RowConstraints.class); 207 final double minHeight = JobUtils.getMinHeight(constraints, RowConstraints.class); 208 final double percentHeight = JobUtils.getPercentHeight(constraints, RowConstraints.class); 209 final double prefHeight = JobUtils.getPrefHeight(constraints, RowConstraints.class); 210 final String valignment = JobUtils.getVAlignment(constraints, RowConstraints.class); 211 final String vgrow = JobUtils.getVGrow(constraints, RowConstraints.class); 212 213 JobUtils.setFillHeight(result, RowConstraints.class, fillHeight); 214 JobUtils.setMaxHeight(result, RowConstraints.class, maxHeight); 215 // If the existing constraints minHeight is too small, we use the default one 216 JobUtils.setMinHeight(result, RowConstraints.class, Math.max(minHeight, defaultMinHeight)); 217 JobUtils.setPercentHeight(result, RowConstraints.class, percentHeight); 218 // If the existing constraints prefHeight is too small, we use the default one 219 JobUtils.setPrefHeight(result, RowConstraints.class, Math.max(prefHeight, defaultPrefHeight)); 220 JobUtils.setVAlignment(result, RowConstraints.class, valignment); 221 JobUtils.setVGrow(result, RowConstraints.class, vgrow); 222 223 return result; 224 } 225 }