1
2
3
4
5
6
7
8
9
10
11
12
13 package org.gscg.common.gui.components.interactivespinner;
14
15 import java.awt.Component;
16 import java.awt.Container;
17 import java.awt.Dimension;
18 import java.awt.Insets;
19 import java.awt.LayoutManager;
20 import java.awt.event.ActionEvent;
21 import java.beans.PropertyChangeEvent;
22 import java.beans.PropertyChangeListener;
23
24 import javax.swing.Action;
25 import javax.swing.ActionMap;
26 import javax.swing.JPanel;
27 import javax.swing.event.ChangeEvent;
28 import javax.swing.event.ChangeListener;
29
30
31 /***
32 * <p>
33 * (c) copyright 2005 <a href="http://www.simulation.tudelft.nl">Delft
34 * University of Technology </a>, the Netherlands. <br>
35 * See for project information <a
36 * href="http://www.simulation.tudelft.nl">www.simulation.tudelft.nl </a> <br>
37 *
38 * Copyright (c) 2003-2005 Delft University of Technology, Jaffalaan 5, 2628 BX
39 * Delft, the Netherlands. All rights reserved.
40 *
41 * See for project information <a href="http://www.simulation.tudelft.nl/">
42 * www.simulation.tudelft.nl </a>.
43 *
44 * The source code and binary code of this software are proprietary information
45 * of Delft University of Technology.
46 *
47 * @author <a
48 * href="http://www.tbm.tudelft.nl/webstaf/stijnh/index.htm">Stijn-Pieter
49 * van Houten </a>
50 * @version $Revision: 1.1 $ $Date: 2005/06/16 12:33:56 $
51 * @since 1.1.3
52 */
53 /***
54 * A simple base class for more specialized editors that displays a read-only
55 * view of the model's current value with a
56 * <code>JFormattedTextField<code>. Subclasses
57 * can configure the <code>JFormattedTextField<code> to create
58 * an editor that's appropriate for the type of model they
59 * support and they may want to override
60 * the <code>stateChanged</code> and <code>propertyChanged</code>
61 * methods, which keep the model and the text field in sync.
62 * <p>
63 * This class defines a <code>dismiss</code> method that removes the
64 * editors <code>ChangeListener</code> from the <code>JSpinner</code>
65 * that it's part of. The <code>setEditor</code> method knows about
66 * <code>DefaultEditor.dismiss</code>, so if the developer
67 * replaces an editor that's derived from <code>JSpinner.DefaultEditor</code>
68 * its <code>ChangeListener</code> connection back to the
69 * <code>JSpinner</code> will be removed. However after that,
70 * it's up to the developer to manage their editor listeners.
71 * Similarly, if a subclass overrides <code>createEditor</code>,
72 * it's up to the subclasser to deal with their editor
73 * subsequently being replaced (with <code>setEditor</code>).
74 * We expect that in most cases, and in editor installed
75 * with <code>setEditor</code> or created by a <code>createEditor</code>
76 * override, will not be replaced anyway.
77 * <p>
78 * This class is the <code>LayoutManager<code> for it's single
79 * <code>JFormattedTextField</code> child. By default the
80 * child is just centered with the parents insets.
81 */
82 public class DefaultEditor extends JPanel implements ChangeListener,
83 PropertyChangeListener, LayoutManager
84 {
85 /*** the default disable action for the ftf */
86 private static final Action DISABLED_ACTION = new DisabledAction();
87
88 /***
89 * Constructs an editor component for the specified <code>JSpinner</code>.
90 * This <code>DefaultEditor</code> is it's own layout manager and it is
91 * added to the spinner's <code>ChangeListener</code> list. The
92 * constructor creates a single <code>JFormattedTextField<code> child,
93 * initializes it's value to be the spinner model's current value
94 * and adds it to <code>this</code> <code>DefaultEditor</code>.
95 *
96 * @param spinner the spinner whose model <code>this</code> editor will monitor
97 * @see #getTextField
98 */
99 public DefaultEditor(final JInteractiveSpinner spinner)
100 {
101 super(null);
102
103 JFormattedTextField ftf = new JFormattedTextField();
104 ftf.setValue(spinner.getValue());
105 ftf.addPropertyChangeListener(this);
106
107 add(ftf);
108
109 setLayout(this);
110 spinner.addChangeListener(this);
111
112
113
114
115
116
117 ActionMap ftfMap = ftf.getActionMap();
118
119 if (ftfMap != null)
120 {
121 ftfMap.put("increment", DISABLED_ACTION);
122 ftfMap.put("decrement", DISABLED_ACTION);
123 }
124 }
125
126 /***
127 * Disconnect <code>this</code> editor from the specified
128 * <code>JSpinner</code>. By default, this method removes itself from the
129 * spinners <code>ChangeListener</code> list.
130 *
131 * @param spinner the <code>JSpinner</code> to disconnect this editor
132 * from; the same spinner as was passed to the constructor.
133 */
134 public void dismiss(final JInteractiveSpinner spinner)
135 {
136 spinner.removeChangeListener(this);
137 }
138
139
140 /***
141 * Returns the <code>JSpinner</code> ancestor of this editor or null.
142 * Typically the editor's parent is a <code>JSpinner</code> however
143 * subclasses of <codeJSpinner</code> may override the the <code>
144 * createEditor</code> method and insert one or more containers between the
145 * <code>JSpinner</code> and it's editor.
146 *
147 * @return <code>JSpinner</code> ancestor
148 */
149 public JInteractiveSpinner getSpinner()
150 {
151 for (Component c = this; c != null; c = c.getParent())
152 {
153 if (c instanceof JInteractiveSpinner)
154 {
155 return (JInteractiveSpinner) c;
156 }
157 }
158 return null;
159 }
160
161
162 /***
163 * Returns the <code>JFormattedTextField</code> child of this editor. By
164 * default the text field is the first and only child of editor.
165 *
166 * @return the <code>JFormattedTextField</code> that gives the user access
167 * to the <code>SpinnerDateModel's</code> value.
168 * @see #getSpinner
169 */
170 public JFormattedTextField getTextField()
171 {
172 return (JFormattedTextField) getComponent(0);
173 }
174
175
176 /***
177 * This method is called when the spinner's model's state changes. It sets
178 * the <code>value</code> of the text field to the current value of the
179 * spinners model.
180 *
181 * @param e not used
182 * @see #getTextField
183 */
184 public void stateChanged(final ChangeEvent e)
185 {
186 JInteractiveSpinner spinner = (JInteractiveSpinner) (e.getSource());
187 getTextField().setValue(spinner.getValue());
188 }
189
190
191 /***
192 * Called by the <code>JFormattedTextField</code>
193 * <code>PropertyChangeListener</code>.
194 * When the <code>"value"</code> property changes, which implies that the
195 * user has typed a new number, we set the value of the spinners model.
196 * <p>
197 * This class ignores <code>PropertyChangeEvents</code> whose source is
198 * not the <code>JFormattedTextField</code>, so subclasses may safely
199 * make <code>this</code> <code>DefaultEditor</code> a
200 * <code>PropertyChangeListener</code> on other objects.
201 *
202 * @param e the <code>PropertyChangeEvent</code> whose source is the
203 * <code>JFormattedTextField</code> created by this class.
204 * @see #getTextField
205 */
206 public void propertyChange(final PropertyChangeEvent e)
207 {
208 JInteractiveSpinner spinner = getSpinner();
209 if (spinner == null)
210 {
211
212 return;
213 }
214
215 Object source = e.getSource();
216 String name = e.getPropertyName();
217 if ((source instanceof JFormattedTextField) && "value".equals(name))
218 {
219 spinner.setValue(getTextField().getValue());
220 }
221 }
222
223
224 /***
225 * This <code>LayoutManager</code> method does nothing. We're only
226 * managing a single child and there's no support for layout constraints.
227 *
228 * @param name ignored
229 * @param child ignored
230 */
231 public void addLayoutComponent(final String name, final Component child)
232 {
233
234 }
235
236
237 /***
238 * This <code>LayoutManager</code> method does nothing. There isn't any
239 * per-child state.
240 *
241 * @param child ignored
242 */
243 public void removeLayoutComponent(final Component child)
244 {
245
246 }
247
248
249 /***
250 * Returns the size of the parents insets.
251 *
252 * @param parent the parent
253 * @return the size as a Dimension
254 */
255 private Dimension insetSize(final Container parent)
256 {
257 Insets insets = parent.getInsets();
258 int w = insets.left + insets.right;
259 int h = insets.top + insets.bottom;
260 return new Dimension(w, h);
261 }
262
263
264 /***
265 * Returns the preferred size of first (and only) child plus the size of the
266 * parents insets.
267 *
268 * @param parent the Container that's managing the layout
269 * @return the preferred dimensions to lay out the subcomponents of the
270 * specified container.
271 */
272 public Dimension preferredLayoutSize(final Container parent)
273 {
274 Dimension preferredSize = insetSize(parent);
275 if (parent.getComponentCount() > 0)
276 {
277 Dimension childSize = getComponent(0).getPreferredSize();
278 preferredSize.width += childSize.width;
279 preferredSize.height += childSize.height;
280 }
281 return preferredSize;
282 }
283
284
285 /***
286 * Returns the minimum size of first (and only) child plus the size of the
287 * parents insets.
288 *
289 * @param parent the Container that's managing the layout
290 * @return the minimum dimensions needed to lay out the subcomponents of the
291 * specified container.
292 */
293 public Dimension minimumLayoutSize(final Container parent)
294 {
295 Dimension minimumSize = insetSize(parent);
296 if (parent.getComponentCount() > 0)
297 {
298 Dimension childSize = getComponent(0).getMinimumSize();
299 minimumSize.width += childSize.width;
300 minimumSize.height += childSize.height;
301 }
302 return minimumSize;
303 }
304
305
306 /***
307 * @see java.awt.LayoutManager#layoutContainer(java.awt.Container)
308 */
309 public void layoutContainer(final Container parent)
310 {
311 if (parent.getComponentCount() > 0)
312 {
313 Insets insets = parent.getInsets();
314 int w = parent.getWidth() - (insets.left + insets.right);
315 int h = parent.getHeight() - (insets.top + insets.bottom);
316 getComponent(0).setBounds(insets.left, insets.top, w, h);
317 }
318 }
319
320 /***
321 * An Action implementation that is always disabled.
322 */
323 private static class DisabledAction implements Action
324 {
325 /***
326 * @see javax.swing.Action#getValue(java.lang.String)
327 */
328 public Object getValue(final String key)
329 {
330 return null;
331 }
332
333 /***
334 * @see javax.swing.Action#putValue(java.lang.String, java.lang.Object)
335 */
336 public void putValue(final String key, final Object value)
337 {
338
339 }
340
341 /***
342 * @see javax.swing.Action#setEnabled(boolean)
343 */
344 public void setEnabled(final boolean b)
345 {
346
347 }
348
349 /***
350 * @see javax.swing.Action#isEnabled()
351 */
352 public boolean isEnabled()
353 {
354 return false;
355 }
356
357 /***
358 * @see javax.swing.Action#addPropertyChangeListener(java.beans.PropertyChangeListener)
359 */
360 public void addPropertyChangeListener(final PropertyChangeListener l)
361 {
362
363 }
364
365 /***
366 * @see javax.swing.Action#removePropertyChangeListener(java.beans.PropertyChangeListener)
367 */
368 public void removePropertyChangeListener(final PropertyChangeListener l)
369 {
370
371 }
372
373 /***
374 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
375 */
376 public void actionPerformed(final ActionEvent ae)
377 {
378
379 }
380 }
381 }