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 }