1   /****************************************************************************
2    * This demo file is part of yFiles for Java 2.14.
3    * Copyright (c) 2000-2017 by yWorks GmbH, Vor dem Kreuzberg 28,
4    * 72070 Tuebingen, Germany. All rights reserved.
5    * 
6    * yFiles demo files exhibit yFiles for Java functionalities. Any redistribution
7    * of demo files in source code or binary form, with or without
8    * modification, is not permitted.
9    * 
10   * Owners of a valid software license for a yFiles for Java version that this
11   * demo is shipped with are allowed to use the demo source code as basis
12   * for their own yFiles for Java powered applications. Use of such programs is
13   * governed by the rights and conditions as set out in the yFiles for Java
14   * license agreement.
15   * 
16   * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED
17   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18   * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
19   * NO EVENT SHALL yWorks BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21   * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26   *
27   ***************************************************************************/
28  package demo.view.mindmap;
29  
30  import y.base.Edge;
31  import y.base.EdgeList;
32  import y.base.EdgeMap;
33  import y.base.Node;
34  import y.base.NodeMap;
35  import y.view.Graph2D;
36  
37  import java.util.Collection;
38  import java.util.Iterator;
39  import java.util.LinkedHashSet;
40  
41  /**
42   * Provides methods to determine the mind map root item,
43   * cross-reference connections, the collapsed state of items, as well as the
44   * location of items relative to the root item.
45   * Additionally, items that are temporarily removed from the mind map
46   * are stored here, too.
47   */
48  public class ViewModel {
49    /**
50     * Singleton instance of the view model.
51     */
52    public static final ViewModel instance = new ViewModel();
53  
54  
55    /**
56     * The data structure that represents the mind map.
57     */
58    public final Graph2D graph;
59  
60    /**
61     * Stores a mapping from collapsed node to the corresponding hidden,
62     * non-cross-reference edges.
63     * <p>
64     * Note, edges that are removed from a graph still reference their source and
65     * target nodes, which means that hidden nodes are stored implicitly as well. 
66     * </p>
67     */
68    private final NodeMap hiddenEdges;
69    /**
70     * Determines which nodes are placed to the left and to the right of the root
71     * node.
72     */
73    private final NodeMap leftOrRight;
74    /**
75     * Determines which edges represent cross-references.
76     */
77    private final EdgeMap crossReferences;
78    /**
79     * Stores hidden cross-reference edges.
80     */
81    private final Collection hiddenCrossReferences;
82  
83    /**
84     * The root node of the mind map.
85     */
86    private Node root;
87  
88    private ViewModel() {
89      graph = new Graph2D();
90      hiddenEdges = graph.createNodeMap();
91      leftOrRight = graph.createNodeMap();
92      LayoutUtil.addLeftRightMap(graph, leftOrRight);
93      crossReferences = graph.createEdgeMap();
94      LayoutUtil.addCrossReferencesMap(graph, crossReferences);
95      hiddenCrossReferences = new LinkedHashSet();
96  
97      LayoutUtil.addPlacersAndComparators(graph);
98    }
99  
100 
101   /**
102    * Determines if the specified node represents the root item of the mind map.
103    * @param node the node to be checked.
104    * @return <code>true</code> if the specified node represents the root item
105    * of the mind map; <code>false</code> otherwise.
106    */
107   public boolean isRoot( final Node node ) {
108     return node == root;
109   }
110 
111   /**
112    * Returns the root node of the mind map.
113    * @return the root node of the mind map.
114    */
115   public Node getRoot() {
116     return root;
117   }
118 
119   /**
120    * Sets the root node of the mind map.
121    * @param root the root node of the mind map.
122    */
123   public void setRoot( final Node root ) {
124     this.root = root;
125   }
126 
127   /**
128    * Determines if the specified node is currently collapsed in the mind map.
129    * @param node the node to be checked.
130    * @return <code>true</code> if the specified node is currently collapsed in
131    * the mind map; <code>false</code> otherwise.
132    */
133   public boolean isCollapsed( final Node node ) {
134     final EdgeList edges = getHiddenEdges(node);
135     return edges != null && !edges.isEmpty();
136   }
137 
138   /**
139    * Determines if the given node should be placed to the left or to the right
140    * of the root node.
141    * @param node the node whose relative placement is required.
142    * @return <code>true</code> if the node should be placed to the left of the
143    * root node; <code>false</code> otherwise.
144    */
145   public boolean isLeft( final Node node ) {
146     return leftOrRight.getBool(node);
147   }
148 
149   /**
150    * Specifies if the given node should be placed to the left or to the right
151    * of the root node. 
152    * @param node the node whose placement is specified.
153    * @param left if <code>true</code>, the node is placed to the left of the
154    * root node, otherwise it is placed to the right of the root node.
155    */
156   public void setLeft( final Node node, final boolean left ) {
157     leftOrRight.setBool(node, left);
158   }
159 
160   /**
161    * Retrieves and removes the non-cross-reference edges that were temporarily
162    * removed from the mind map when the specified node was collapsed.
163    * Note, the returned list implicitly specifies the subtree to re-insert
164    * into the mind map when expanding the specified node because removed edges
165    * still reference their original source and target nodes.
166    * @param node a currently collapsed node.
167    * @return an {@link EdgeList} with all non-cross-reference edges that were
168    * temporarily removed when the specified node was collapsed or
169    * <code>null</code> if there are no temporarily removed edges for the
170    * specified node.
171    * @see #setHiddenEdges(y.base.Node, y.base.EdgeList)
172    */
173   public EdgeList popHiddenEdges( final Node node ) {
174     final EdgeList edges = getHiddenEdges(node);
175     hiddenEdges.set(node, null);
176     return edges;
177   }
178 
179   /**
180    * Stores the non-cross-reference edges that were temporarily removed from
181    * the mind map when the specified node was collapsed.
182    * Note, the specified list has to include all edges in the subtree rooted at
183    * the specified node at the time of collapsing. 
184    * @param node the node that is collapsed.
185    * @param edges a list of temporarily removed non-cross-reference edges.
186    * @see #popHiddenEdges(y.base.Node)
187    */
188   public void setHiddenEdges( final Node node, final EdgeList edges ) {
189     hiddenEdges.set(node, edges);
190   }
191 
192   /**
193    * Returns the currently hidden, non-cross-reference edges that define
194    * the subtree rooted at the specified node.
195    * @param node a currently collapsed node.
196    * @return an {@link EdgeList} with all the non-cross-reference edges that
197    * define the subtree rooted at the specified node. 
198    */
199   private EdgeList getHiddenEdges( final Node node ) {
200     return (EdgeList) hiddenEdges.get(node);
201   }
202 
203 
204   /**
205    * Determines if the specified edge is a cross-reference in the mind map.
206    * @param edge the edge to check.
207    * @return <code>true</code> if the specified edge is a cross-reference in
208    * the min map; <code>false</code> otherwise.
209    * @see #setCrossReference(y.base.Edge)
210    */
211   public boolean isCrossReference( final Edge edge ) {
212     return crossReferences.getBool(edge);
213   }
214 
215   /**
216    * Marks the specified edge as a cross-reference in the mind map.
217    * @param edge the edge to mark as cross-reference.
218    * @see #isCrossReference(y.base.Edge)
219    */
220   public void setCrossReference( final Edge edge ) {
221     crossReferences.setBool(edge, true);
222   }
223 
224   /**
225    * Stores the specified, temporarily removed cross-reference edges for
226    * later reinsertion.
227    * @param edges a list of temporarily removed cross-reference edges.
228    * @see #hiddenCrossReferences()
229    * @see #clearHiddenCrossReferences()
230    */
231   public void addHiddenCrossReferences( final Collection edges ) {
232     hiddenCrossReferences.addAll(edges);
233   }
234 
235   /**
236    * Iterates over the temporarily removed cross-reference edges.
237    * @return an {@link Iterator} for the temporarily removed cross-reference
238    * edges.
239    */
240   public Iterator hiddenCrossReferences() {
241     return hiddenCrossReferences.iterator();
242   }
243 
244   /**
245    * Clears the cache of temporarily removed cross-reference edges.
246    * @see #addHiddenCrossReferences(java.util.Collection)
247    */
248   public void clearHiddenCrossReferences() {
249     hiddenCrossReferences.clear();
250   }
251 }
252