View Javadoc

1   /*
2   
3       dsh-piccolo-identify  Piccolo2D nodes for identifiable beans.
4       Copyright (c) 2007-2013 held jointly by the individual authors.
5   
6       This library is free software; you can redistribute it and/or modify it
7       under the terms of the GNU Lesser General Public License as published
8       by the Free Software Foundation; either version 3 of the License, or (at
9       your option) any later version.
10  
11      This library is distributed in the hope that it will be useful, but WITHOUT
12      ANY WARRANTY; with out even the implied warranty of MERCHANTABILITY or
13      FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14      License for more details.
15  
16      You should have received a copy of the GNU Lesser General Public License
17      along with this library;  if not, write to the Free Software Foundation,
18      Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA.
19  
20      > http://www.fsf.org/licensing/licenses/lgpl.html
21      > http://www.opensource.org/licenses/lgpl-license.php
22  
23  */
24  package org.dishevelled.piccolo.identify;
25  
26  import java.awt.Color;
27  import java.awt.Shape;
28  
29  import java.awt.geom.Point2D;
30  import java.awt.geom.Rectangle2D;
31  
32  import java.beans.PropertyChangeEvent;
33  import java.beans.PropertyChangeListener;
34  
35  import javax.swing.UIManager;
36  
37  import org.piccolo2d.nodes.PPath;
38  
39  import org.piccolo2d.util.PBounds;
40  
41  import org.piccolo2d.extras.nodes.PShadow;
42  
43  import org.dishevelled.iconbundle.IconSize;
44  import org.dishevelled.iconbundle.IconState;
45  
46  /**
47   * Explorer-style id node.
48   *
49   * @author  Michael Heuer
50   * @version $Revision$ $Date$
51   */
52  public final class ExplorerIdNode
53      extends AbstractIdNode
54  {
55      /** Text selection node. */
56      private PPath textSelection;
57  
58      /** Text shadow node. */
59      private PShadow textShadow;
60  
61      /** Icon text gap. */
62      private final double iconTextGap = DEFAULT_ICON_TEXT_GAP;
63  
64      /** Text shadow blur radius, <code>4</code>. */
65      private static final int BLUR_RADIUS = 4;
66  
67      /** Text shadow offset, <code>0.5d</code> */
68      private static final double TEXT_SHADOW_OFFSET = 0.5d;
69  
70     /** Default icon text gap. */
71      private static final double DEFAULT_ICON_TEXT_GAP = 2.0d;
72  
73  
74      /**
75       * Create a new explorer-style id node.
76       */
77      public ExplorerIdNode()
78      {
79          this(null);
80      }
81  
82      /**
83       * Create a new explorer-style id node with the specified value.
84       *
85       * @param value value for this id node
86       */
87      public ExplorerIdNode(final Object value)
88      {
89          super(value);
90          createNodes();
91          resetStateMachine();
92      }
93  
94      /**
95       * Create a new explorer-style id node with the specified value and icon size.
96       *
97       * @param value value for this id node
98       * @param iconSize icon size for this id node, must not be null
99       */
100     public ExplorerIdNode(final Object value, final IconSize iconSize)
101     {
102         super(value);
103         setIconSize(iconSize);
104         createNodes();
105         resetStateMachine();
106     }
107 
108 
109     /**
110      * Create nodes.
111      */
112     private void createNodes()
113     {
114         textSelection = new PPath.Double();
115         textShadow = new PShadow(getNameTextNode().toImage(), Color.BLACK, BLUR_RADIUS);
116         addChild(textSelection);
117         addChild(textShadow);
118         addChild(getIconBundleImageNode());
119         addChild(getNameTextNode());
120 
121         // update text shadow on text node property changes
122         PropertyChangeListener listener = new PropertyChangeListener()
123             {
124                 @Override
125                 public void propertyChange(final PropertyChangeEvent event)
126                 {
127                     updateTextShadow();
128                 }
129             };
130         getNameTextNode().addPropertyChangeListener("font", listener);
131         getNameTextNode().addPropertyChangeListener("text", listener);
132     }
133 
134     /**
135      * Normal state.
136      */
137     private void normal()
138     {
139         setTransparency(1.0f);
140         setIconState(IconState.NORMAL);
141         getNameTextNode().setTextPaint(Color.BLACK);
142         textSelection.setVisible(false);
143         textShadow.setVisible(false);
144     }
145 
146     /**
147      * Reverse normal state.
148      */
149     private void reverseNormal()
150     {
151         setTransparency(1.0f);
152         setIconState(IconState.NORMAL);
153         getNameTextNode().setTextPaint(Color.WHITE);
154         textSelection.setVisible(false);
155         textShadow.setVisible(true);
156     }
157 
158     /**
159      * Selected state.
160      */
161     private void selected()
162     {
163         setTransparency(1.0f);
164         setIconState(IconState.SELECTED);
165         getNameTextNode().setTextPaint(Color.WHITE);
166         textSelection.setPaint(UIManager.getColor("List.selectionBackground"));
167         textSelection.setStrokePaint(UIManager.getColor("List.selectionBackground"));
168         textSelection.setVisible(true);
169         textShadow.setVisible(false);
170     }
171 
172     /**
173      * Selected mouseover state.
174      */
175     private void selectedMouseover()
176     {
177         setTransparency(1.0f);
178         setIconState(IconState.SELECTED);
179         getNameTextNode().setTextPaint(Color.WHITE);
180         textSelection.setPaint(UIManager.getColor("List.selectionBackground"));
181         textSelection.setStrokePaint(UIManager.getColor("List.selectionBackground"));
182         textSelection.setVisible(true);
183         textShadow.setVisible(false);
184     }
185 
186     /**
187      * Reverse selected state.
188      */
189     private void reverseSelected()
190     {
191         selected();
192     }
193 
194     /**
195      * Reverse selected mouseover state.
196      */
197     private void reverseSelectedMouseover()
198     {
199         selected();
200     }
201 
202     /**
203      * Dragging state.
204      */
205     private void dragging()
206     {
207         setTransparency(0.66f);
208     }
209 
210     /**
211      * Reverse dragging state.
212      */
213     private void reverseDragging()
214     {
215         setTransparency(0.66f);
216     }
217 
218     /**
219      * Update text shadow.
220      */
221     private void updateTextShadow()
222     {
223         removeChild(textShadow);
224         boolean visible = textShadow.getVisible();
225         textShadow = new PShadow(getNameTextNode().toImage(), Color.BLACK, BLUR_RADIUS);
226         textShadow.setVisible(visible);
227         addChild(0, textShadow);
228     }
229 
230     @Override
231     protected void layoutChildren()
232     {
233         PBounds iconBounds = getIconBundleImageNode().getBoundsReference();
234         Point2D iconCenter = iconBounds.getCenter2D();
235 
236         PBounds textBounds = getNameTextNode().getBoundsReference();
237         Point2D textCenter = textBounds.getCenter2D();
238 
239         double textSelectionWidthMargin = Math.min(0.2d * textBounds.getWidth(), 6.0d);
240         double textSelectionHeightMargin = Math.min(0.1d * textBounds.getHeight(), 0.5d);
241 
242         Shape textSelectionRect = new Rectangle2D.Double(0.0d, 0.0d,
243                                                          textBounds.getWidth() + 2 * textSelectionWidthMargin, textBounds.getHeight() + 2 * textSelectionHeightMargin);
244         textSelection.reset();
245         textSelection.append(textSelectionRect, false);
246 
247         PBounds textSelectionBounds = textSelection.getBoundsReference();
248         Point2D textSelectionCenter = textSelectionBounds.getCenter2D();
249 
250         getIconBundleImageNode().setOffset(-iconCenter.getX(), -iconCenter.getY());
251         textSelection.setOffset(-textSelectionCenter.getX(), iconCenter.getY() + iconTextGap);
252         getNameTextNode().setOffset(-textCenter.getX(), iconCenter.getY() + iconTextGap + textSelectionHeightMargin);
253         textShadow.setOffset(-textCenter.getX() - (2 * BLUR_RADIUS) + TEXT_SHADOW_OFFSET,
254                              iconCenter.getY() + iconTextGap + textSelectionHeightMargin - (2 * BLUR_RADIUS) + TEXT_SHADOW_OFFSET);
255     }
256 }