blob: c794999ffbe84dd21b99883c907e91c570988328 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25package javax.swing.plaf.synth;
26
27import java.awt.*;
28import java.awt.event.*;
29import java.text.ParseException;
30
31import javax.swing.*;
32import javax.swing.event.*;
33import javax.swing.plaf.*;
34import javax.swing.plaf.basic.BasicSpinnerUI;
35import javax.swing.text.*;
36
37import java.beans.*;
38import java.text.*;
39import java.util.*;
40import sun.swing.plaf.synth.SynthUI;
41
42/**
43 * Synth's SpinnerUI.
44 *
45 * @author Hans Muller
46 * @author Joshua Outwater
47 */
48class SynthSpinnerUI extends BasicSpinnerUI implements PropertyChangeListener,
49 SynthUI {
50 private SynthStyle style;
51
52
53 /**
54 * Returns a new instance of SynthSpinnerUI.
55 *
56 * @param c the JSpinner (not used)
57 * @see ComponentUI#createUI
58 * @return a new SynthSpinnerUI object
59 */
60 public static ComponentUI createUI(JComponent c) {
61 return new SynthSpinnerUI();
62 }
63
64 protected void installListeners() {
65 super.installListeners();
66 spinner.addPropertyChangeListener(this);
67 }
68
69 /**
70 * Removes the <code>propertyChangeListener</code> added
71 * by installListeners.
72 * <p>
73 * This method is called by <code>uninstallUI</code>.
74 *
75 * @see #installListeners
76 */
77 protected void uninstallListeners() {
78 super.uninstallListeners();
79 spinner.removePropertyChangeListener(this);
80 }
81
82 /**
83 * Initialize the <code>JSpinner</code> <code>border</code>,
84 * <code>foreground</code>, and <code>background</code>, properties
85 * based on the corresponding "Spinner.*" properties from defaults table.
86 * The <code>JSpinners</code> layout is set to the value returned by
87 * <code>createLayout</code>. This method is called by <code>installUI</code>.
88 *
89 * @see #uninstallDefaults
90 * @see #installUI
91 * @see #createLayout
92 * @see LookAndFeel#installBorder
93 * @see LookAndFeel#installColors
94 */
95 protected void installDefaults() {
96 LayoutManager layout = spinner.getLayout();
97
98 if (layout == null || layout instanceof UIResource) {
99 spinner.setLayout(createLayout());
100 }
101 updateStyle(spinner);
102 }
103
104
105 private void updateStyle(JSpinner c) {
106 SynthContext context = getContext(c, ENABLED);
107 SynthStyle oldStyle = style;
108 style = SynthLookAndFeel.updateStyle(context, this);
109 if (style != oldStyle) {
110 if (oldStyle != null) {
111 // Only call installKeyboardActions as uninstall is not
112 // public.
113 installKeyboardActions();
114 }
115 }
116 context.dispose();
117 }
118
119
120 /**
121 * Sets the <code>JSpinner's</code> layout manager to null. This
122 * method is called by <code>uninstallUI</code>.
123 *
124 * @see #installDefaults
125 * @see #uninstallUI
126 */
127 protected void uninstallDefaults() {
128 if (spinner.getLayout() instanceof UIResource) {
129 spinner.setLayout(null);
130 }
131
132 SynthContext context = getContext(spinner, ENABLED);
133
134 style.uninstallDefaults(context);
135 context.dispose();
136 style = null;
137 }
138
139
140 protected LayoutManager createLayout() {
141 return new SpinnerLayout();
142 }
143
144
145 /**
146 * Create a component that will replace the spinner models value
147 * with the object returned by <code>spinner.getPreviousValue</code>.
148 * By default the <code>previousButton</code> is a JButton
149 * who's <code>ActionListener</code> updates it's <code>JSpinner</code>
150 * ancestors model. If a previousButton isn't needed (in a subclass)
151 * then override this method to return null.
152 *
153 * @return a component that will replace the spinners model with the
154 * next value in the sequence, or null
155 * @see #installUI
156 * @see #createNextButton
157 */
158 protected Component createPreviousButton() {
159 JButton b = new SynthArrowButton(SwingConstants.SOUTH);
160 b.setName("Spinner.previousButton");
161 installPreviousButtonListeners(b);
162 return b;
163 }
164
165
166 /**
167 * Create a component that will replace the spinner models value
168 * with the object returned by <code>spinner.getNextValue</code>.
169 * By default the <code>nextButton</code> is a JButton
170 * who's <code>ActionListener</code> updates it's <code>JSpinner</code>
171 * ancestors model. If a nextButton isn't needed (in a subclass)
172 * then override this method to return null.
173 *
174 * @return a component that will replace the spinners model with the
175 * next value in the sequence, or null
176 * @see #installUI
177 * @see #createPreviousButton
178 */
179 protected Component createNextButton() {
180 JButton b = new SynthArrowButton(SwingConstants.NORTH);
181 b.setName("Spinner.nextButton");
182 installNextButtonListeners(b);
183 return b;
184 }
185
186
187 /**
188 * This method is called by installUI to get the editor component
189 * of the <code>JSpinner</code>. By default it just returns
190 * <code>JSpinner.getEditor()</code>. Subclasses can override
191 * <code>createEditor</code> to return a component that contains
192 * the spinner's editor or null, if they're going to handle adding
193 * the editor to the <code>JSpinner</code> in an
194 * <code>installUI</code> override.
195 * <p>
196 * Typically this method would be overridden to wrap the editor
197 * with a container with a custom border, since one can't assume
198 * that the editors border can be set directly.
199 * <p>
200 * The <code>replaceEditor</code> method is called when the spinners
201 * editor is changed with <code>JSpinner.setEditor</code>. If you've
202 * overriden this method, then you'll probably want to override
203 * <code>replaceEditor</code> as well.
204 *
205 * @return the JSpinners editor JComponent, spinner.getEditor() by default
206 * @see #installUI
207 * @see #replaceEditor
208 * @see JSpinner#getEditor
209 */
210 protected JComponent createEditor() {
211 JComponent editor = spinner.getEditor();
212 editor.setName("Spinner.editor");
213 updateEditorAlignment(editor);
214 return editor;
215 }
216
217
218 /**
219 * Called by the <code>PropertyChangeListener</code> when the
220 * <code>JSpinner</code> editor property changes. It's the responsibility
221 * of this method to remove the old editor and add the new one. By
222 * default this operation is just:
223 * <pre>
224 * spinner.remove(oldEditor);
225 * spinner.add(newEditor, "Editor");
226 * </pre>
227 * The implementation of <code>replaceEditor</code> should be coordinated
228 * with the <code>createEditor</code> method.
229 *
230 * @see #createEditor
231 * @see #createPropertyChangeListener
232 */
233 protected void replaceEditor(JComponent oldEditor, JComponent newEditor) {
234 spinner.remove(oldEditor);
235 spinner.add(newEditor, "Editor");
236 }
237
238 private void updateEditorAlignment(JComponent editor) {
239 if (editor instanceof JSpinner.DefaultEditor) {
240 SynthContext context = getContext(spinner);
241 Integer alignment = (Integer)context.getStyle().get(
242 context, "Spinner.editorAlignment");
243 if (alignment != null) {
244 JTextField text = ((JSpinner.DefaultEditor)editor).getTextField();
245 text.setHorizontalAlignment(alignment);
246 }
247 }
248 }
249
250 public SynthContext getContext(JComponent c) {
251 return getContext(c, getComponentState(c));
252 }
253
254 private SynthContext getContext(JComponent c, int state) {
255 return SynthContext.getContext(SynthContext.class, c,
256 SynthLookAndFeel.getRegion(c), style, state);
257 }
258
259
260 private Region getRegion(JComponent c) {
261 return SynthLookAndFeel.getRegion(c);
262 }
263
264
265 private int getComponentState(JComponent c) {
266 return SynthLookAndFeel.getComponentState(c);
267 }
268
269
270 public void update(Graphics g, JComponent c) {
271 SynthContext context = getContext(c);
272
273 SynthLookAndFeel.update(context, g);
274 context.getPainter().paintSpinnerBackground(context,
275 g, 0, 0, c.getWidth(), c.getHeight());
276 paint(context, g);
277 context.dispose();
278 }
279
280
281 public void paint(Graphics g, JComponent c) {
282 SynthContext context = getContext(c);
283
284 paint(context, g);
285 context.dispose();
286 }
287
288
289 protected void paint(SynthContext context, Graphics g) {
290 }
291
292 public void paintBorder(SynthContext context, Graphics g, int x,
293 int y, int w, int h) {
294 context.getPainter().paintSpinnerBorder(context, g, x, y, w, h);
295 }
296
297 /**
298 * A simple layout manager for the editor and the next/previous buttons.
299 * See the SynthSpinnerUI javadoc for more information about exactly
300 * how the components are arranged.
301 */
302 private static class SpinnerLayout implements LayoutManager, UIResource
303 {
304 private Component nextButton = null;
305 private Component previousButton = null;
306 private Component editor = null;
307
308 public void addLayoutComponent(String name, Component c) {
309 if ("Next".equals(name)) {
310 nextButton = c;
311 }
312 else if ("Previous".equals(name)) {
313 previousButton = c;
314 }
315 else if ("Editor".equals(name)) {
316 editor = c;
317 }
318 }
319
320 public void removeLayoutComponent(Component c) {
321 if (c == nextButton) {
322 nextButton = null;
323 }
324 else if (c == previousButton) {
325 previousButton = null;
326 }
327 else if (c == editor) {
328 editor = null;
329 }
330 }
331
332 private Dimension preferredSize(Component c) {
333 return (c == null) ? new Dimension(0, 0) : c.getPreferredSize();
334 }
335
336 public Dimension preferredLayoutSize(Container parent) {
337 Dimension nextD = preferredSize(nextButton);
338 Dimension previousD = preferredSize(previousButton);
339 Dimension editorD = preferredSize(editor);
340
341 /* Force the editors height to be a multiple of 2
342 */
343 editorD.height = ((editorD.height + 1) / 2) * 2;
344
345 Dimension size = new Dimension(editorD.width, editorD.height);
346 size.width += Math.max(nextD.width, previousD.width);
347 Insets insets = parent.getInsets();
348 size.width += insets.left + insets.right;
349 size.height += insets.top + insets.bottom;
350 return size;
351 }
352
353 public Dimension minimumLayoutSize(Container parent) {
354 return preferredLayoutSize(parent);
355 }
356
357 private void setBounds(Component c, int x, int y, int width, int height) {
358 if (c != null) {
359 c.setBounds(x, y, width, height);
360 }
361 }
362
363 public void layoutContainer(Container parent) {
364 Insets insets = parent.getInsets();
365 int availWidth = parent.getWidth() - (insets.left + insets.right);
366 int availHeight = parent.getHeight() - (insets.top + insets.bottom);
367 Dimension nextD = preferredSize(nextButton);
368 Dimension previousD = preferredSize(previousButton);
369 int nextHeight = availHeight / 2;
370 int previousHeight = availHeight - nextHeight;
371 int buttonsWidth = Math.max(nextD.width, previousD.width);
372 int editorWidth = availWidth - buttonsWidth;
373
374 /* Deal with the spinners componentOrientation property.
375 */
376 int editorX, buttonsX;
377 if (parent.getComponentOrientation().isLeftToRight()) {
378 editorX = insets.left;
379 buttonsX = editorX + editorWidth;
380 }
381 else {
382 buttonsX = insets.left;
383 editorX = buttonsX + buttonsWidth;
384 }
385
386 int previousY = insets.top + nextHeight;
387 setBounds(editor, editorX, insets.top, editorWidth, availHeight);
388 setBounds(nextButton, buttonsX, insets.top, buttonsWidth, nextHeight);
389 setBounds(previousButton, buttonsX, previousY, buttonsWidth, previousHeight);
390 }
391 }
392
393
394 public void propertyChange(PropertyChangeEvent e) {
395 String propertyName = e.getPropertyName();
396 JSpinner spinner = (JSpinner)(e.getSource());
397 SpinnerUI spinnerUI = spinner.getUI();
398
399 if (spinnerUI instanceof SynthSpinnerUI) {
400 SynthSpinnerUI ui = (SynthSpinnerUI)spinnerUI;
401
402 if (SynthLookAndFeel.shouldUpdateStyle(e)) {
403 ui.updateStyle(spinner);
404 }
405 }
406 }
407}