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.ColumnConstraints; 54 55 /** 56 * Job invoked when adding column constraints. 57 */ 58 public class AddColumnConstraintsJob extends BatchDocumentJob { 59 60 // Key = target GridPane instance 61 // Value = list of target column indexes for this GridPane 62 private final Map<FXOMObject, Set<Integer>> targetGridPanes; 63 private final Position position; 64 // If the selected column 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 defaultMinWidth = 10.0; 68 private static final double defaultPrefWidth = 100.0; 69 70 public AddColumnConstraintsJob( 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(addColumnConstraints((FXOMInstance) targetGridPane, targetIndexes)); 90 } 91 92 return result; 93 } 94 95 @Override 96 protected String makeDescription() { 97 return "Add Column Constraints"; //NOI18N 98 } 99 100 private Set<Job> addColumnConstraints( 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("columnConstraints"); //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.getColumnsConstraintsSize(); 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.AFTER) { 124 addedIndex++; 125 } 126 127 final FXOMObject targetConstraints 128 = mask.getColumnConstraintsAtIndex(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 = makeColumnConstraintsInstance( 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 columns 149 final FXOMInstance addedConstraints = makeColumnConstraintsInstance(); 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 column 157 final FXOMInstance addedConstraints = makeColumnConstraintsInstance(); 158 JobUtils.setMinWidth(addedConstraints, ColumnConstraints.class, defaultMinWidth); 159 JobUtils.setPrefWidth(addedConstraints, ColumnConstraints.class, defaultPrefWidth); 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 makeColumnConstraintsInstance() { 185 186 // Create new constraints instance 187 final FXOMDocument newDocument = new FXOMDocument(); 188 final FXOMInstance result 189 = new FXOMInstance(newDocument, ColumnConstraints.class); 190 newDocument.setFxomRoot(result); 191 result.moveToFxomDocument(getEditorController().getFxomDocument()); 192 193 return result; 194 } 195 196 private FXOMInstance makeColumnConstraintsInstance(final FXOMInstance constraints) { 197 198 assert constraints != null; 199 assert constraints.getDeclaredClass() == ColumnConstraints.class; 200 201 // Create new constraints instance 202 final FXOMInstance result = makeColumnConstraintsInstance(); 203 204 // Set the new column constraints values with the values of the specified instance 205 final boolean fillWidth = JobUtils.getFillWidth(constraints, ColumnConstraints.class); 206 final double maxWidth = JobUtils.getMaxWidth(constraints, ColumnConstraints.class); 207 final double minWidth = JobUtils.getMinWidth(constraints, ColumnConstraints.class); 208 final double percentWidth = JobUtils.getPercentWidth(constraints, ColumnConstraints.class); 209 final double prefWidth = JobUtils.getPrefWidth(constraints, ColumnConstraints.class); 210 final String halignment = JobUtils.getHAlignment(constraints, ColumnConstraints.class); 211 final String hgrow = JobUtils.getHGrow(constraints, ColumnConstraints.class); 212 213 JobUtils.setFillWidth(result, ColumnConstraints.class, fillWidth); 214 JobUtils.setMaxWidth(result, ColumnConstraints.class, maxWidth); 215 // If the existing constraints minWidth is too small, we use the default one 216 JobUtils.setMinWidth(result, ColumnConstraints.class, Math.max(minWidth, defaultMinWidth)); 217 JobUtils.setPercentWidth(result, ColumnConstraints.class, percentWidth); 218 // If the existing constraints prefWidth is too small, we use the default one 219 JobUtils.setPrefWidth(result, ColumnConstraints.class, Math.max(prefWidth, defaultPrefWidth)); 220 JobUtils.setHAlignment(result, ColumnConstraints.class, halignment); 221 JobUtils.setHGrow(result, ColumnConstraints.class, hgrow); 222 223 return result; 224 } 225 }