001 /*
002 * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025 package javax.swing.plaf.synth;
026
027 import java.awt.*;
028 import java.awt.event.*;
029 import java.text.ParseException;
030
031 import javax.swing.*;
032 import javax.swing.event.*;
033 import javax.swing.plaf.*;
034 import javax.swing.plaf.basic.BasicSpinnerUI;
035 import javax.swing.text.*;
036
037 import java.beans.*;
038 import java.text.*;
039 import java.util.*;
040 import sun.swing.plaf.synth.SynthUI;
041
042 /**
043 * Synth's SpinnerUI.
044 *
045 * @version 1.21, 05/09/07
046 * @author Hans Muller
047 * @author Joshua Outwater
048 */
049 class SynthSpinnerUI extends BasicSpinnerUI implements
050 PropertyChangeListener, SynthUI {
051 private SynthStyle style;
052
053 /**
054 * Returns a new instance of SynthSpinnerUI.
055 *
056 * @param c the JSpinner (not used)
057 * @see ComponentUI#createUI
058 * @return a new SynthSpinnerUI object
059 */
060 public static ComponentUI createUI(JComponent c) {
061 return new SynthSpinnerUI();
062 }
063
064 protected void installListeners() {
065 super .installListeners();
066 spinner.addPropertyChangeListener(this );
067 }
068
069 /**
070 * Removes the <code>propertyChangeListener</code> added
071 * by installListeners.
072 * <p>
073 * This method is called by <code>uninstallUI</code>.
074 *
075 * @see #installListeners
076 */
077 protected void uninstallListeners() {
078 super .uninstallListeners();
079 spinner.removePropertyChangeListener(this );
080 }
081
082 /**
083 * Initialize the <code>JSpinner</code> <code>border</code>,
084 * <code>foreground</code>, and <code>background</code>, properties
085 * based on the corresponding "Spinner.*" properties from defaults table.
086 * The <code>JSpinners</code> layout is set to the value returned by
087 * <code>createLayout</code>. This method is called by <code>installUI</code>.
088 *
089 * @see #uninstallDefaults
090 * @see #installUI
091 * @see #createLayout
092 * @see LookAndFeel#installBorder
093 * @see LookAndFeel#installColors
094 */
095 protected void installDefaults() {
096 LayoutManager layout = spinner.getLayout();
097
098 if (layout == null || layout instanceof UIResource) {
099 spinner.setLayout(createLayout());
100 }
101 updateStyle(spinner);
102 }
103
104 private void updateStyle(JSpinner c) {
105 SynthContext context = getContext(c, ENABLED);
106 SynthStyle oldStyle = style;
107 style = SynthLookAndFeel.updateStyle(context, this );
108 if (style != oldStyle) {
109 if (oldStyle != null) {
110 // Only call installKeyboardActions as uninstall is not
111 // public.
112 installKeyboardActions();
113 }
114 }
115 context.dispose();
116 }
117
118 /**
119 * Sets the <code>JSpinner's</code> layout manager to null. This
120 * method is called by <code>uninstallUI</code>.
121 *
122 * @see #installDefaults
123 * @see #uninstallUI
124 */
125 protected void uninstallDefaults() {
126 if (spinner.getLayout() instanceof UIResource) {
127 spinner.setLayout(null);
128 }
129
130 SynthContext context = getContext(spinner, ENABLED);
131
132 style.uninstallDefaults(context);
133 context.dispose();
134 style = null;
135 }
136
137 protected LayoutManager createLayout() {
138 return new SpinnerLayout();
139 }
140
141 /**
142 * Create a component that will replace the spinner models value
143 * with the object returned by <code>spinner.getPreviousValue</code>.
144 * By default the <code>previousButton</code> is a JButton
145 * who's <code>ActionListener</code> updates it's <code>JSpinner</code>
146 * ancestors model. If a previousButton isn't needed (in a subclass)
147 * then override this method to return null.
148 *
149 * @return a component that will replace the spinners model with the
150 * next value in the sequence, or null
151 * @see #installUI
152 * @see #createNextButton
153 */
154 protected Component createPreviousButton() {
155 JButton b = new SynthArrowButton(SwingConstants.SOUTH);
156 b.setName("Spinner.previousButton");
157 installPreviousButtonListeners(b);
158 return b;
159 }
160
161 /**
162 * Create a component that will replace the spinner models value
163 * with the object returned by <code>spinner.getNextValue</code>.
164 * By default the <code>nextButton</code> is a JButton
165 * who's <code>ActionListener</code> updates it's <code>JSpinner</code>
166 * ancestors model. If a nextButton isn't needed (in a subclass)
167 * then override this method to return null.
168 *
169 * @return a component that will replace the spinners model with the
170 * next value in the sequence, or null
171 * @see #installUI
172 * @see #createPreviousButton
173 */
174 protected Component createNextButton() {
175 JButton b = new SynthArrowButton(SwingConstants.NORTH);
176 b.setName("Spinner.nextButton");
177 installNextButtonListeners(b);
178 return b;
179 }
180
181 /**
182 * This method is called by installUI to get the editor component
183 * of the <code>JSpinner</code>. By default it just returns
184 * <code>JSpinner.getEditor()</code>. Subclasses can override
185 * <code>createEditor</code> to return a component that contains
186 * the spinner's editor or null, if they're going to handle adding
187 * the editor to the <code>JSpinner</code> in an
188 * <code>installUI</code> override.
189 * <p>
190 * Typically this method would be overridden to wrap the editor
191 * with a container with a custom border, since one can't assume
192 * that the editors border can be set directly.
193 * <p>
194 * The <code>replaceEditor</code> method is called when the spinners
195 * editor is changed with <code>JSpinner.setEditor</code>. If you've
196 * overriden this method, then you'll probably want to override
197 * <code>replaceEditor</code> as well.
198 *
199 * @return the JSpinners editor JComponent, spinner.getEditor() by default
200 * @see #installUI
201 * @see #replaceEditor
202 * @see JSpinner#getEditor
203 */
204 protected JComponent createEditor() {
205 JComponent editor = spinner.getEditor();
206 editor.setName("Spinner.editor");
207 updateEditorAlignment(editor);
208 return editor;
209 }
210
211 /**
212 * Called by the <code>PropertyChangeListener</code> when the
213 * <code>JSpinner</code> editor property changes. It's the responsibility
214 * of this method to remove the old editor and add the new one. By
215 * default this operation is just:
216 * <pre>
217 * spinner.remove(oldEditor);
218 * spinner.add(newEditor, "Editor");
219 * </pre>
220 * The implementation of <code>replaceEditor</code> should be coordinated
221 * with the <code>createEditor</code> method.
222 *
223 * @see #createEditor
224 * @see #createPropertyChangeListener
225 */
226 protected void replaceEditor(JComponent oldEditor,
227 JComponent newEditor) {
228 spinner.remove(oldEditor);
229 updateEditorAlignment(newEditor);
230 spinner.add(newEditor, "Editor");
231 }
232
233 private void updateEditorAlignment(JComponent editor) {
234 if (editor instanceof JSpinner.DefaultEditor) {
235 SynthContext context = getContext(spinner);
236 Integer alignment = (Integer) context.getStyle().get(
237 context, "Spinner.editorAlignment");
238 if (alignment != null) {
239 JTextField text = ((JSpinner.DefaultEditor) editor)
240 .getTextField();
241 text.setHorizontalAlignment(alignment);
242 }
243 }
244 }
245
246 public SynthContext getContext(JComponent c) {
247 return getContext(c, getComponentState(c));
248 }
249
250 private SynthContext getContext(JComponent c, int state) {
251 return SynthContext.getContext(SynthContext.class, c,
252 SynthLookAndFeel.getRegion(c), style, state);
253 }
254
255 private Region getRegion(JComponent c) {
256 return SynthLookAndFeel.getRegion(c);
257 }
258
259 private int getComponentState(JComponent c) {
260 return SynthLookAndFeel.getComponentState(c);
261 }
262
263 public void update(Graphics g, JComponent c) {
264 SynthContext context = getContext(c);
265
266 SynthLookAndFeel.update(context, g);
267 context.getPainter().paintSpinnerBackground(context, g, 0, 0,
268 c.getWidth(), c.getHeight());
269 paint(context, g);
270 context.dispose();
271 }
272
273 public void paint(Graphics g, JComponent c) {
274 SynthContext context = getContext(c);
275
276 paint(context, g);
277 context.dispose();
278 }
279
280 protected void paint(SynthContext context, Graphics g) {
281 }
282
283 public void paintBorder(SynthContext context, Graphics g, int x,
284 int y, int w, int h) {
285 context.getPainter().paintSpinnerBorder(context, g, x, y, w, h);
286 }
287
288 /**
289 * A simple layout manager for the editor and the next/previous buttons.
290 * See the SynthSpinnerUI javadoc for more information about exactly
291 * how the components are arranged.
292 */
293 private static class SpinnerLayout implements LayoutManager,
294 UIResource {
295 private Component nextButton = null;
296 private Component previousButton = null;
297 private Component editor = null;
298
299 public void addLayoutComponent(String name, Component c) {
300 if ("Next".equals(name)) {
301 nextButton = c;
302 } else if ("Previous".equals(name)) {
303 previousButton = c;
304 } else if ("Editor".equals(name)) {
305 editor = c;
306 }
307 }
308
309 public void removeLayoutComponent(Component c) {
310 if (c == nextButton) {
311 nextButton = null;
312 } else if (c == previousButton) {
313 previousButton = null;
314 } else if (c == editor) {
315 editor = null;
316 }
317 }
318
319 private Dimension preferredSize(Component c) {
320 return (c == null) ? new Dimension(0, 0) : c
321 .getPreferredSize();
322 }
323
324 public Dimension preferredLayoutSize(Container parent) {
325 Dimension nextD = preferredSize(nextButton);
326 Dimension previousD = preferredSize(previousButton);
327 Dimension editorD = preferredSize(editor);
328
329 /* Force the editors height to be a multiple of 2
330 */
331 editorD.height = ((editorD.height + 1) / 2) * 2;
332
333 Dimension size = new Dimension(editorD.width,
334 editorD.height);
335 size.width += Math.max(nextD.width, previousD.width);
336 Insets insets = parent.getInsets();
337 size.width += insets.left + insets.right;
338 size.height += insets.top + insets.bottom;
339 return size;
340 }
341
342 public Dimension minimumLayoutSize(Container parent) {
343 return preferredLayoutSize(parent);
344 }
345
346 private void setBounds(Component c, int x, int y, int width,
347 int height) {
348 if (c != null) {
349 c.setBounds(x, y, width, height);
350 }
351 }
352
353 public void layoutContainer(Container parent) {
354 Insets insets = parent.getInsets();
355 int availWidth = parent.getWidth()
356 - (insets.left + insets.right);
357 int availHeight = parent.getHeight()
358 - (insets.top + insets.bottom);
359 Dimension nextD = preferredSize(nextButton);
360 Dimension previousD = preferredSize(previousButton);
361 int nextHeight = availHeight / 2;
362 int previousHeight = availHeight - nextHeight;
363 int buttonsWidth = Math.max(nextD.width, previousD.width);
364 int editorWidth = availWidth - buttonsWidth;
365
366 /* Deal with the spinners componentOrientation property.
367 */
368 int editorX, buttonsX;
369 if (parent.getComponentOrientation().isLeftToRight()) {
370 editorX = insets.left;
371 buttonsX = editorX + editorWidth;
372 } else {
373 buttonsX = insets.left;
374 editorX = buttonsX + buttonsWidth;
375 }
376
377 int previousY = insets.top + nextHeight;
378 setBounds(editor, editorX, insets.top, editorWidth,
379 availHeight);
380 setBounds(nextButton, buttonsX, insets.top, buttonsWidth,
381 nextHeight);
382 setBounds(previousButton, buttonsX, previousY,
383 buttonsWidth, previousHeight);
384 }
385 }
386
387 public void propertyChange(PropertyChangeEvent e) {
388 String propertyName = e.getPropertyName();
389 JSpinner spinner = (JSpinner) (e.getSource());
390 SpinnerUI spinnerUI = spinner.getUI();
391
392 if (spinnerUI instanceof SynthSpinnerUI) {
393 SynthSpinnerUI ui = (SynthSpinnerUI) spinnerUI;
394
395 if (SynthLookAndFeel.shouldUpdateStyle(e)) {
396 ui.updateStyle(spinner);
397 }
398 }
399 }
400 }
|