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.uml;
29  
30  import y.base.Node;
31  import y.base.NodeList;
32  import y.geom.YPoint;
33  import y.view.Graph2D;
34  import y.view.HitInfo;
35  import y.view.NodeLabel;
36  import y.view.NodeRealizer;
37  import y.view.ViewMode;
38  
39  import java.beans.PropertyChangeEvent;
40  import java.beans.PropertyChangeListener;
41  
42  /**
43   * Custom {@link ViewMode} for special label handling of the UML class nodes:
44   * <ul>
45   *   <li>The labels for the name, the attributes and the operations open a label editor when double-clicked.</li>
46   *   <li>The labels for attribute and operation caption are not editable.</li>
47   *   <li>An {@link UmlClassLabelEditMode.LabelChangeHandler label change handler} changes the text of the label after
48   *   editing.</li>
49   * </ul>
50   */
51  class UmlClassLabelEditMode extends ViewMode {
52  
53    /**
54     * Reacts to double-clicks on labels for the name, the attributes and the operations by presenting a label editor.
55     * Reacts to single-clicks on labels for attributes or operations to select the label.
56     *
57     * @param x the x-coordinate of the mouse event in world coordinates.
58     * @param y the y-coordinate of the mouse event in world coordinates.
59     */
60    public void mouseClicked(double x, double y) {
61      final HitInfo hitInfo = getHitInfo(x, y);
62      final NodeRealizer singleClickedNode = getSingleClickedNode(hitInfo);
63      if (singleClickedNode != null) {
64        if (UmlClassLabelSupport.selectListItemAt(singleClickedNode, x, y)) {
65          view.updateView();
66        }
67      }
68  
69      final NodeLabel doubleClickedLabel = getDoubleClickedLabel(hitInfo, x, y);
70      if (doubleClickedLabel != null && !UmlClassLabelSupport.isCaptionLabel(doubleClickedLabel)) {
71        // Caption labels are not editable.
72        editLabel(doubleClickedLabel);
73      }
74    }
75  
76    /**
77     * Returns the label at the given location if it has been double clicked; null otherwise.
78     */
79    private NodeLabel getDoubleClickedLabel(final HitInfo hitInfo, final double x, final double y) {
80      if (lastClickEvent != null && lastClickEvent.getClickCount() == 2) {
81        if (hitInfo.hasHitNodeLabels()) {
82          return hitInfo.getHitNodeLabel();
83        } else if (hitInfo.hasHitNodes()) {
84          final NodeRealizer realizer = view.getGraph2D().getRealizer(hitInfo.getHitNode());
85          for (int i = realizer.labelCount(); i-- > 0; ) {
86            final NodeLabel label = realizer.getLabel(i);
87            if (label.contains(x, y)) {
88              return label;
89            }
90          }
91        }
92      }
93      return null;
94    }
95  
96    /**
97     * Returns the node realizer at the given position if it has been single clicked; null otherwise.
98     */
99    private NodeRealizer getSingleClickedNode(final HitInfo hitInfo) {
100     if (hitInfo.hasHitNodeLabels()) {
101       return hitInfo.getHitNodeLabel().getRealizer();
102     } else if (hitInfo.hasHitNodes()) {
103       return view.getGraph2D().getRealizer(hitInfo.getHitNode());
104     } else {
105       return null;
106     }
107   }
108 
109   /**
110    * Opens the given label with an label editor
111    */
112   private void editLabel(final NodeLabel label) {
113     if (!UmlClassLabelSupport.isCaptionLabel(label)) {
114       final YPoint location = label.getTextLocation();
115       view.openLabelEditor(label, location.getX(), location.getY(), new LabelChangeHandler(), true, false);
116     }
117   }
118 
119   /**
120    * This handler listens for label changes and adjusts the node size to
121    * the label size.
122    */
123   private class LabelChangeHandler implements PropertyChangeListener {
124     public void propertyChange(PropertyChangeEvent e) {
125       final Object source = e.getSource();
126       if (source instanceof NodeLabel) {
127         final Graph2D graph = getGraph2D();
128         final NodeLabel label = (NodeLabel) source;
129         final Node node = label.getNode();
130         final NodeRealizer realizer = graph.getRealizer(node);
131 
132         // Do not allow an empty name label.
133         graph.firePreEvent();
134         graph.backupRealizers(new NodeList(node).nodes());
135         graph.backupRealizers(node.edges());
136         try {
137           if ("".equals(e.getNewValue())) {
138             // Remove empty label.
139             label.setText(" ");
140             final UmlClassAnimation animation = new UmlClassRemoveItemButton.RemoveItemAnimation(view, realizer, true);
141             animation.play();
142           } else {
143             // Set the text of the label.
144             label.setText((String) e.getNewValue());
145             UmlClassLabelSupport.updateLabelText(realizer, label);
146             UmlClassLabelSupport.selectLabel(realizer, label);
147             UmlClassLabelSupport.updateRealizerSize(realizer);
148           }
149         } finally {
150           graph.firePostEvent();
151         }
152       }
153     }
154   }
155 }