blob: 78dd60bd3e337738002eeca87fbc78f25e3ea07d [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 */
25
26package javax.swing.plaf.synth;
27
28import java.awt.*;
29import java.awt.event.*;
30import java.io.Serializable;
31import javax.swing.*;
32import javax.swing.border.*;
33import java.awt.*;
34import java.awt.event.*;
35import java.beans.*;
36import javax.swing.plaf.*;
37import javax.swing.plaf.basic.BasicButtonUI;
38import javax.swing.plaf.basic.BasicHTML;
39import javax.swing.text.View;
40import sun.swing.plaf.synth.SynthUI;
41import sun.swing.plaf.synth.DefaultSynthStyle;
42
43/**
44 * Synth's ButtonUI implementation.
45 *
46 * @author Scott Violet
47 */
48class SynthButtonUI extends BasicButtonUI implements
49 PropertyChangeListener, SynthUI {
50 private SynthStyle style;
51
52 public static ComponentUI createUI(JComponent c) {
53 return new SynthButtonUI();
54 }
55
56 protected void installDefaults(AbstractButton b) {
57 updateStyle(b);
58
59 LookAndFeel.installProperty(b, "rolloverEnabled", Boolean.TRUE);
60 }
61
62 protected void installListeners(AbstractButton b) {
63 super.installListeners(b);
64 b.addPropertyChangeListener(this);
65 }
66
67 void updateStyle(AbstractButton b) {
68 SynthContext context = getContext(b, SynthConstants.ENABLED);
69 SynthStyle oldStyle = style;
70 style = SynthLookAndFeel.updateStyle(context, this);
71 if (style != oldStyle) {
72 if (b.getMargin() == null ||
73 (b.getMargin() instanceof UIResource)) {
74 Insets margin = (Insets)style.get(context,getPropertyPrefix() +
75 "margin");
76
77 if (margin == null) {
78 // Some places assume margins are non-null.
79 margin = SynthLookAndFeel.EMPTY_UIRESOURCE_INSETS;
80 }
81 b.setMargin(margin);
82 }
83
84 Object value = style.get(context, getPropertyPrefix() + "iconTextGap");
85 if (value != null) {
86 LookAndFeel.installProperty(b, "iconTextGap", value);
87 }
88
89 value = style.get(context, getPropertyPrefix() + "contentAreaFilled");
90 LookAndFeel.installProperty(b, "contentAreaFilled",
91 value != null? value : Boolean.TRUE);
92
93 if (oldStyle != null) {
94 uninstallKeyboardActions(b);
95 installKeyboardActions(b);
96 }
97
98 }
99 context.dispose();
100 }
101
102 protected void uninstallListeners(AbstractButton b) {
103 super.uninstallListeners(b);
104 b.removePropertyChangeListener(this);
105 }
106
107 protected void uninstallDefaults(AbstractButton b) {
108 SynthContext context = getContext(b, ENABLED);
109
110 style.uninstallDefaults(context);
111 context.dispose();
112 style = null;
113 }
114
115 public SynthContext getContext(JComponent c) {
116 return getContext(c, getComponentState(c));
117 }
118
119 SynthContext getContext(JComponent c, int state) {
120 Region region = getRegion(c);
121 return SynthContext.getContext(SynthContext.class, c, region,
122 style, state);
123 }
124
125 private Region getRegion(JComponent c) {
126 return SynthLookAndFeel.getRegion(c);
127 }
128
129 /**
130 * Returns the current state of the passed in <code>AbstractButton</code>.
131 */
132 private int getComponentState(JComponent c) {
133 int state = ENABLED;
134
135 if (!c.isEnabled()) {
136 state = DISABLED;
137 }
138 if (SynthLookAndFeel.selectedUI == this) {
139 return SynthLookAndFeel.selectedUIState | SynthConstants.ENABLED;
140 }
141 AbstractButton button = (AbstractButton) c;
142 ButtonModel model = button.getModel();
143
144 if (model.isPressed()) {
145 if (model.isArmed()) {
146 state = PRESSED;
147 }
148 else {
149 state = MOUSE_OVER;
150 }
151 }
152 if (model.isRollover()) {
153 state |= MOUSE_OVER;
154 }
155 if (model.isSelected()) {
156 state |= SELECTED;
157 }
158 if (c.isFocusOwner() && button.isFocusPainted()) {
159 state |= FOCUSED;
160 }
161 if ((c instanceof JButton) && ((JButton)c).isDefaultButton()) {
162 state |= DEFAULT;
163 }
164 return state;
165 }
166
167 public int getBaseline(JComponent c, int width, int height) {
168 if (c == null) {
169 throw new NullPointerException("Component must be non-null");
170 }
171 if (width < 0 || height < 0) {
172 throw new IllegalArgumentException(
173 "Width and height must be >= 0");
174 }
175 AbstractButton b = (AbstractButton)c;
176 String text = b.getText();
177 if (text == null || "".equals(text)) {
178 return -1;
179 }
180 Insets i = b.getInsets();
181 Rectangle viewRect = new Rectangle();
182 Rectangle textRect = new Rectangle();
183 Rectangle iconRect = new Rectangle();
184 viewRect.x = i.left;
185 viewRect.y = i.top;
186 viewRect.width = width - (i.right + viewRect.x);
187 viewRect.height = height - (i.bottom + viewRect.y);
188
189 // layout the text and icon
190 SynthContext context = getContext(b);
191 FontMetrics fm = context.getComponent().getFontMetrics(
192 context.getStyle().getFont(context));
193 context.getStyle().getGraphicsUtils(context).layoutText(
194 context, fm, b.getText(), b.getIcon(),
195 b.getHorizontalAlignment(), b.getVerticalAlignment(),
196 b.getHorizontalTextPosition(), b.getVerticalTextPosition(),
197 viewRect, iconRect, textRect, b.getIconTextGap());
198 View view = (View)b.getClientProperty(BasicHTML.propertyKey);
199 int baseline;
200 if (view != null) {
201 baseline = BasicHTML.getHTMLBaseline(view, textRect.width,
202 textRect.height);
203 if (baseline >= 0) {
204 baseline += textRect.y;
205 }
206 }
207 else {
208 baseline = textRect.y + fm.getAscent();
209 }
210 context.dispose();
211 return baseline;
212 }
213
214 // ********************************
215 // Paint Methods
216 // ********************************
217
218 public void update(Graphics g, JComponent c) {
219 SynthContext context = getContext(c);
220
221 SynthLookAndFeel.update(context, g);
222 paintBackground(context, g, c);
223 paint(context, g);
224 context.dispose();
225 }
226
227 public void paint(Graphics g, JComponent c) {
228 SynthContext context = getContext(c);
229
230 paint(context, g);
231 context.dispose();
232 }
233
234 protected void paint(SynthContext context, Graphics g) {
235 AbstractButton b = (AbstractButton)context.getComponent();
236
237 g.setColor(context.getStyle().getColor(context,
238 ColorType.TEXT_FOREGROUND));
239 g.setFont(style.getFont(context));
240 context.getStyle().getGraphicsUtils(context).paintText(
241 context, g, b.getText(), getIcon(b),
242 b.getHorizontalAlignment(), b.getVerticalAlignment(),
243 b.getHorizontalTextPosition(), b.getVerticalTextPosition(),
244 b.getIconTextGap(), b.getDisplayedMnemonicIndex(),
245 getTextShiftOffset(context));
246 }
247
248 void paintBackground(SynthContext context, Graphics g, JComponent c) {
249 if (((AbstractButton) c).isContentAreaFilled()) {
250 context.getPainter().paintButtonBackground(context, g, 0, 0,
251 c.getWidth(),
252 c.getHeight());
253 }
254 }
255
256 public void paintBorder(SynthContext context, Graphics g, int x,
257 int y, int w, int h) {
258 context.getPainter().paintButtonBorder(context, g, x, y, w, h);
259 }
260
261 /**
262 * Returns the default icon. This should NOT callback
263 * to the JComponent.
264 *
265 * @param b AbstractButton the iocn is associated with
266 * @return default icon
267 */
268
269 protected Icon getDefaultIcon(AbstractButton b) {
270 SynthContext context = getContext(b);
271 Icon icon = context.getStyle().getIcon(context, getPropertyPrefix() + "icon");
272 context.dispose();
273 return icon;
274 }
275
276 /**
277 * Returns the Icon to use in painting the button.
278 */
279 protected Icon getIcon(AbstractButton b) {
280 Icon icon = b.getIcon();
281 ButtonModel model = b.getModel();
282
283 if (!model.isEnabled()) {
284 icon = getSynthDisabledIcon(b, icon);
285 } else if (model.isPressed() && model.isArmed()) {
286 icon = getPressedIcon(b, getSelectedIcon(b, icon));
287 } else if (b.isRolloverEnabled() && model.isRollover()) {
288 icon = getRolloverIcon(b, getSelectedIcon(b, icon));
289 } else if (model.isSelected()) {
290 icon = getSelectedIcon(b, icon);
291 } else {
292 icon = getEnabledIcon(b, icon);
293 }
294 if(icon == null) {
295 return getDefaultIcon(b);
296 }
297 return icon;
298 }
299
300 /**
301 * This method will return the icon that should be used for a button. We
302 * only want to use the synth icon defined by the style if the specific
303 * icon has not been defined for the button state and the backup icon is a
304 * UIResource (we set it, not the developer).
305 *
306 * @param b button
307 * @param specificIcon icon returned from the button for the specific state
308 * @param defaultIcon fallback icon
309 * @param state the synth state of the button
310 */
311 private Icon getIcon(AbstractButton b, Icon specificIcon, Icon defaultIcon,
312 int state) {
313 Icon icon = specificIcon;
314 if (icon == null) {
315 if (defaultIcon instanceof UIResource) {
316 icon = getSynthIcon(b, state);
317 if (icon == null) {
318 icon = defaultIcon;
319 }
320 } else {
321 icon = defaultIcon;
322 }
323 }
324 return icon;
325 }
326
327 private Icon getSynthIcon(AbstractButton b, int synthConstant) {
328 return style.getIcon(getContext(b, synthConstant), getPropertyPrefix() + "icon");
329 }
330
331 private Icon getEnabledIcon(AbstractButton b, Icon defaultIcon) {
332 if (defaultIcon == null) {
333 defaultIcon = getSynthIcon(b, SynthConstants.ENABLED);
334 }
335 return defaultIcon;
336 }
337
338 private Icon getSelectedIcon(AbstractButton b, Icon defaultIcon) {
339 return getIcon(b, b.getSelectedIcon(), defaultIcon,
340 SynthConstants.SELECTED);
341 }
342
343 private Icon getRolloverIcon(AbstractButton b, Icon defaultIcon) {
344 ButtonModel model = b.getModel();
345 Icon icon;
346 if (model.isSelected()) {
347 icon = getIcon(b, b.getRolloverSelectedIcon(), defaultIcon,
348 SynthConstants.MOUSE_OVER | SynthConstants.SELECTED);
349 } else {
350 icon = getIcon(b, b.getRolloverIcon(), defaultIcon,
351 SynthConstants.MOUSE_OVER);
352 }
353 return icon;
354 }
355
356 private Icon getPressedIcon(AbstractButton b, Icon defaultIcon) {
357 return getIcon(b, b.getPressedIcon(), defaultIcon,
358 SynthConstants.PRESSED);
359 }
360
361 private Icon getSynthDisabledIcon(AbstractButton b, Icon defaultIcon) {
362 ButtonModel model = b.getModel();
363 Icon icon;
364 if (model.isSelected()) {
365 icon = getIcon(b, b.getDisabledSelectedIcon(), defaultIcon,
366 SynthConstants.DISABLED | SynthConstants.SELECTED);
367 } else {
368 icon = getIcon(b, b.getDisabledIcon(), defaultIcon,
369 SynthConstants.DISABLED);
370 }
371 return icon;
372 }
373
374 /**
375 * Returns the amount to shift the text/icon when painting.
376 */
377 protected int getTextShiftOffset(SynthContext state) {
378 AbstractButton button = (AbstractButton)state.getComponent();
379 ButtonModel model = button.getModel();
380
381 if (model.isArmed() && model.isPressed() &&
382 button.getPressedIcon() == null) {
383 return state.getStyle().getInt(state, getPropertyPrefix() +
384 "textShiftOffset", 0);
385 }
386 return 0;
387 }
388
389 // ********************************
390 // Layout Methods
391 // ********************************
392 public Dimension getMinimumSize(JComponent c) {
393 if (c.getComponentCount() > 0 && c.getLayout() != null) {
394 return null;
395 }
396 AbstractButton b = (AbstractButton)c;
397 SynthContext ss = getContext(c);
398 Dimension size = ss.getStyle().getGraphicsUtils(ss).getMinimumSize(
399 ss, ss.getStyle().getFont(ss), b.getText(), getSizingIcon(b),
400 b.getHorizontalAlignment(), b.getVerticalAlignment(),
401 b.getHorizontalTextPosition(),
402 b.getVerticalTextPosition(), b.getIconTextGap(),
403 b.getDisplayedMnemonicIndex());
404
405 ss.dispose();
406 return size;
407 }
408
409 public Dimension getPreferredSize(JComponent c) {
410 if (c.getComponentCount() > 0 && c.getLayout() != null) {
411 return null;
412 }
413 AbstractButton b = (AbstractButton)c;
414 SynthContext ss = getContext(c);
415 Dimension size = ss.getStyle().getGraphicsUtils(ss).getPreferredSize(
416 ss, ss.getStyle().getFont(ss), b.getText(), getSizingIcon(b),
417 b.getHorizontalAlignment(), b.getVerticalAlignment(),
418 b.getHorizontalTextPosition(),
419 b.getVerticalTextPosition(), b.getIconTextGap(),
420 b.getDisplayedMnemonicIndex());
421
422 ss.dispose();
423 return size;
424 }
425
426 public Dimension getMaximumSize(JComponent c) {
427 if (c.getComponentCount() > 0 && c.getLayout() != null) {
428 return null;
429 }
430
431 AbstractButton b = (AbstractButton)c;
432 SynthContext ss = getContext(c);
433 Dimension size = ss.getStyle().getGraphicsUtils(ss).getMaximumSize(
434 ss, ss.getStyle().getFont(ss), b.getText(), getSizingIcon(b),
435 b.getHorizontalAlignment(), b.getVerticalAlignment(),
436 b.getHorizontalTextPosition(),
437 b.getVerticalTextPosition(), b.getIconTextGap(),
438 b.getDisplayedMnemonicIndex());
439
440 ss.dispose();
441 return size;
442 }
443
444 /**
445 * Returns the Icon used in calculating the pref/min/max size.
446 */
447 protected Icon getSizingIcon(AbstractButton b) {
448 // NOTE: this is slightly different than BasicButtonUI, where it
449 // would just use getIcon, but this should be ok.
450 Icon icon = (b.isEnabled()) ? b.getIcon() : b.getDisabledIcon();
451 if (icon == null) {
452 icon = getDefaultIcon(b);
453 }
454 return icon;
455 }
456
457 public void propertyChange(PropertyChangeEvent e) {
458 if (SynthLookAndFeel.shouldUpdateStyle(e)) {
459 updateStyle((AbstractButton)e.getSource());
460 }
461 }
462}