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.selection; 33 34 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument; 35 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMNodes; 36 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject; 37 import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyPath; 38 import java.util.Collection; 39 import java.util.Collections; 40 import java.util.HashSet; 41 import java.util.List; 42 import java.util.Objects; 43 import java.util.Set; 44 import javafx.scene.Node; 45 46 /** 47 * 48 * 49 */ 50 public class ObjectSelectionGroup extends AbstractSelectionGroup { 51 52 private final Set<FXOMObject> items = new HashSet<>(); 53 private final FXOMObject hitItem; 54 private final Object hitSceneGraphObject; 55 private final Node hitNode; 56 57 ObjectSelectionGroup(FXOMObject fxomObject, Node hitNode) { 58 assert fxomObject != null; 59 this.items.add(fxomObject); 60 this.hitItem = fxomObject; 61 this.hitSceneGraphObject = fxomObject.getSceneGraphObject(); 62 this.hitNode = hitNode; 63 } 64 65 public ObjectSelectionGroup(Collection<FXOMObject> fxomObjects, FXOMObject hitItem, Node hitNode) { 66 assert fxomObjects != null; 67 assert hitItem != null; 68 assert fxomObjects.contains(hitItem); 69 this.items.addAll(fxomObjects); 70 this.hitItem = hitItem; 71 this.hitSceneGraphObject = this.hitItem.getSceneGraphObject(); 72 this.hitNode = hitNode; 73 } 74 75 public Set<FXOMObject> getItems() { 76 return Collections.unmodifiableSet(items); 77 } 78 79 public FXOMObject getHitItem() { 80 return hitItem; 81 } 82 83 public Node getHitNode() { 84 return hitNode; 85 } 86 87 public boolean isExpired() { 88 return hitItem.getSceneGraphObject() != hitSceneGraphObject; 89 } 90 91 public Node getCheckedHitNode() { 92 final Node result; 93 94 if ((hitNode == null) || isExpired()) { 95 result = getFallbackHitNode(); 96 } else { 97 result = hitNode; 98 } 99 100 return result; 101 } 102 103 public Node getFallbackHitNode() { 104 final Node result; 105 106 if (hitItem.isNode()) { 107 result = (Node) hitItem.getSceneGraphObject(); 108 } else { 109 final FXOMObject closestNodeObject = hitItem.getClosestNode(); 110 if (closestNodeObject != null) { 111 result = (Node) closestNodeObject.getSceneGraphObject(); 112 } else { 113 result = null; 114 } 115 } 116 117 return result; 118 } 119 120 public Set<FXOMObject> getFlattenItems() { 121 return FXOMNodes.flatten(items); 122 } 123 124 public List<FXOMObject> getSortedItems() { 125 return FXOMNodes.sort(items); 126 } 127 128 public boolean hasSingleParent() { 129 final boolean result; 130 131 if (items.size() == 1) { 132 result = true; 133 } else { 134 final Set<FXOMObject> parents = new HashSet<>(); 135 for (FXOMObject i : items) { 136 parents.add(i.getParentObject()); 137 } 138 result = parents.size() == 1; 139 } 140 141 return result; 142 } 143 144 /* 145 * AbstractSelectionGroup 146 */ 147 148 @Override 149 public FXOMObject getAncestor() { 150 final FXOMObject result; 151 152 assert items.isEmpty() == false; 153 154 switch(items.size()) { 155 156 case 0: 157 result = null; 158 break; 159 160 case 1: 161 result = items.iterator().next().getParentObject(); 162 break; 163 164 default: 165 DesignHierarchyPath commonPath = null; 166 for (FXOMObject i : items) { 167 final FXOMObject parent = i.getParentObject(); 168 if (parent != null) { 169 final DesignHierarchyPath dph = new DesignHierarchyPath(parent); 170 if (commonPath == null) { 171 commonPath = dph; 172 } else { 173 commonPath = commonPath.getCommonPathWith(dph); 174 } 175 } 176 } 177 assert commonPath != null; // Else it would mean root is selected twice 178 result = commonPath.getLeaf(); 179 break; 180 } 181 182 return result; 183 } 184 185 @Override 186 public boolean isValid(FXOMDocument fxomDocument) { 187 assert fxomDocument != null; 188 189 boolean result; 190 final FXOMObject fxomRoot = fxomDocument.getFxomRoot(); 191 if (fxomRoot == null) { 192 result = false; 193 } else { 194 result = true; 195 for (FXOMObject i : items) { 196 final boolean ok = (i == fxomRoot) || i.isDescendantOf(fxomRoot); 197 if (ok == false) { 198 result = false; 199 break; 200 } 201 } 202 } 203 204 return result; 205 } 206 207 208 /* 209 * Cloneable 210 */ 211 @Override 212 public ObjectSelectionGroup clone() throws CloneNotSupportedException { 213 return (ObjectSelectionGroup)super.clone(); 214 } 215 216 217 /* 218 * Object 219 */ 220 @Override 221 public int hashCode() { 222 int hash = 3; 223 hash = 41 * hash + Objects.hashCode(this.items); 224 hash = 41 * hash + Objects.hashCode(this.hitItem); 225 hash = 41 * hash + Objects.hashCode(this.hitNode); 226 return hash; 227 } 228 229 @Override 230 public boolean equals(Object obj) { 231 if (obj == null) { 232 return false; 233 } 234 if (getClass() != obj.getClass()) { 235 return false; 236 } 237 final ObjectSelectionGroup other = (ObjectSelectionGroup) obj; 238 if (!Objects.equals(this.items, other.items)) { 239 return false; 240 } 241 if (!Objects.equals(this.hitItem, other.hitItem)) { 242 return false; 243 } 244 if (this.hitNode != other.hitNode) { 245 return false; 246 } 247 return true; 248 } 249 }