blob: fd3d84b28338f62d8440b22e77bb0bf85c1b9b26 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1997-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.basic;
27
28import sun.swing.SwingUtilities2;
29import java.awt.*;
30import java.awt.event.*;
31import java.io.Serializable;
32import javax.swing.*;
33import javax.swing.border.*;
34import java.awt.*;
35import java.awt.event.*;
36import javax.swing.plaf.ButtonUI;
37import javax.swing.plaf.UIResource;
38import javax.swing.plaf.ComponentUI;
39import javax.swing.text.View;
40
41/**
42 * BasicButton implementation
43 *
44 * @author Jeff Dinkins
45 */
46public class BasicButtonUI extends ButtonUI{
47 // Shared UI object
48 private final static BasicButtonUI buttonUI = new BasicButtonUI();
49
50 // Visual constants
51 // NOTE: This is not used or set any where. Were we allowed to remove
52 // fields, this would be removed.
53 protected int defaultTextIconGap;
54
55 // Amount to offset text, the value of this comes from
56 // defaultTextShiftOffset once setTextShiftOffset has been invoked.
57 private int shiftOffset = 0;
58 // Value that is set in shiftOffset once setTextShiftOffset has been
59 // invoked. The value of this comes from the defaults table.
60 protected int defaultTextShiftOffset;
61
62 private final static String propertyPrefix = "Button" + ".";
63
64 // ********************************
65 // Create PLAF
66 // ********************************
67 public static ComponentUI createUI(JComponent c) {
68 return buttonUI;
69 }
70
71 protected String getPropertyPrefix() {
72 return propertyPrefix;
73 }
74
75
76 // ********************************
77 // Install PLAF
78 // ********************************
79 public void installUI(JComponent c) {
80 installDefaults((AbstractButton) c);
81 installListeners((AbstractButton) c);
82 installKeyboardActions((AbstractButton) c);
83 BasicHTML.updateRenderer(c, ((AbstractButton) c).getText());
84 }
85
86 protected void installDefaults(AbstractButton b) {
87 // load shared instance defaults
88 String pp = getPropertyPrefix();
89
90 defaultTextShiftOffset = UIManager.getInt(pp + "textShiftOffset");
91
92 // set the following defaults on the button
93 if (b.isContentAreaFilled()) {
94 LookAndFeel.installProperty(b, "opaque", Boolean.TRUE);
95 } else {
96 LookAndFeel.installProperty(b, "opaque", Boolean.FALSE);
97 }
98
99 if(b.getMargin() == null || (b.getMargin() instanceof UIResource)) {
100 b.setMargin(UIManager.getInsets(pp + "margin"));
101 }
102
103 LookAndFeel.installColorsAndFont(b, pp + "background",
104 pp + "foreground", pp + "font");
105 LookAndFeel.installBorder(b, pp + "border");
106
107 Object rollover = UIManager.get(pp + "rollover");
108 if (rollover != null) {
109 LookAndFeel.installProperty(b, "rolloverEnabled", rollover);
110 }
111
112 LookAndFeel.installProperty(b, "iconTextGap", new Integer(4));
113 }
114
115 protected void installListeners(AbstractButton b) {
116 BasicButtonListener listener = createButtonListener(b);
117 if(listener != null) {
118 b.addMouseListener(listener);
119 b.addMouseMotionListener(listener);
120 b.addFocusListener(listener);
121 b.addPropertyChangeListener(listener);
122 b.addChangeListener(listener);
123 }
124 }
125
126 protected void installKeyboardActions(AbstractButton b){
127 BasicButtonListener listener = getButtonListener(b);
128
129 if(listener != null) {
130 listener.installKeyboardActions(b);
131 }
132 }
133
134
135 // ********************************
136 // Uninstall PLAF
137 // ********************************
138 public void uninstallUI(JComponent c) {
139 uninstallKeyboardActions((AbstractButton) c);
140 uninstallListeners((AbstractButton) c);
141 uninstallDefaults((AbstractButton) c);
142 BasicHTML.updateRenderer(c, "");
143 }
144
145 protected void uninstallKeyboardActions(AbstractButton b) {
146 BasicButtonListener listener = getButtonListener(b);
147 if(listener != null) {
148 listener.uninstallKeyboardActions(b);
149 }
150 }
151
152 protected void uninstallListeners(AbstractButton b) {
153 BasicButtonListener listener = getButtonListener(b);
154 if(listener != null) {
155 b.removeMouseListener(listener);
156 b.removeMouseMotionListener(listener);
157 b.removeFocusListener(listener);
158 b.removeChangeListener(listener);
159 b.removePropertyChangeListener(listener);
160 }
161 }
162
163 protected void uninstallDefaults(AbstractButton b) {
164 LookAndFeel.uninstallBorder(b);
165 }
166
167 // ********************************
168 // Create Listeners
169 // ********************************
170 protected BasicButtonListener createButtonListener(AbstractButton b) {
171 return new BasicButtonListener(b);
172 }
173
174 public int getDefaultTextIconGap(AbstractButton b) {
175 return defaultTextIconGap;
176 }
177
178 /* These rectangles/insets are allocated once for all
179 * ButtonUI.paint() calls. Re-using rectangles rather than
180 * allocating them in each paint call substantially reduced the time
181 * it took paint to run. Obviously, this method can't be re-entered.
182 */
183 private static Rectangle viewRect = new Rectangle();
184 private static Rectangle textRect = new Rectangle();
185 private static Rectangle iconRect = new Rectangle();
186
187 // ********************************
188 // Paint Methods
189 // ********************************
190
191 public void paint(Graphics g, JComponent c)
192 {
193 AbstractButton b = (AbstractButton) c;
194 ButtonModel model = b.getModel();
195
196 String text = layout(b, SwingUtilities2.getFontMetrics(b, g),
197 b.getWidth(), b.getHeight());
198
199 clearTextShiftOffset();
200
201 // perform UI specific press action, e.g. Windows L&F shifts text
202 if (model.isArmed() && model.isPressed()) {
203 paintButtonPressed(g,b);
204 }
205
206 // Paint the Icon
207 if(b.getIcon() != null) {
208 paintIcon(g,c,iconRect);
209 }
210
211 if (text != null && !text.equals("")){
212 View v = (View) c.getClientProperty(BasicHTML.propertyKey);
213 if (v != null) {
214 v.paint(g, textRect);
215 } else {
216 paintText(g, b, textRect, text);
217 }
218 }
219
220 if (b.isFocusPainted() && b.hasFocus()) {
221 // paint UI specific focus
222 paintFocus(g,b,viewRect,textRect,iconRect);
223 }
224 }
225
226 protected void paintIcon(Graphics g, JComponent c, Rectangle iconRect){
227 AbstractButton b = (AbstractButton) c;
228 ButtonModel model = b.getModel();
229 Icon icon = b.getIcon();
230 Icon tmpIcon = null;
231
232 if(icon == null) {
233 return;
234 }
235
236 Icon selectedIcon = null;
237
238 /* the fallback icon should be based on the selected state */
239 if (model.isSelected()) {
240 selectedIcon = (Icon) b.getSelectedIcon();
241 if (selectedIcon != null) {
242 icon = selectedIcon;
243 }
244 }
245
246 if(!model.isEnabled()) {
247 if(model.isSelected()) {
248 tmpIcon = (Icon) b.getDisabledSelectedIcon();
249 if (tmpIcon == null) {
250 tmpIcon = selectedIcon;
251 }
252 }
253
254 if (tmpIcon == null) {
255 tmpIcon = (Icon) b.getDisabledIcon();
256 }
257 } else if(model.isPressed() && model.isArmed()) {
258 tmpIcon = (Icon) b.getPressedIcon();
259 if(tmpIcon != null) {
260 // revert back to 0 offset
261 clearTextShiftOffset();
262 }
263 } else if(b.isRolloverEnabled() && model.isRollover()) {
264 if(model.isSelected()) {
265 tmpIcon = (Icon) b.getRolloverSelectedIcon();
266 if (tmpIcon == null) {
267 tmpIcon = selectedIcon;
268 }
269 }
270
271 if (tmpIcon == null) {
272 tmpIcon = (Icon) b.getRolloverIcon();
273 }
274 }
275
276 if(tmpIcon != null) {
277 icon = tmpIcon;
278 }
279
280 if(model.isPressed() && model.isArmed()) {
281 icon.paintIcon(c, g, iconRect.x + getTextShiftOffset(),
282 iconRect.y + getTextShiftOffset());
283 } else {
284 icon.paintIcon(c, g, iconRect.x, iconRect.y);
285 }
286
287 }
288
289 /**
290 * As of Java 2 platform v 1.4 this method should not be used or overriden.
291 * Use the paintText method which takes the AbstractButton argument.
292 */
293 protected void paintText(Graphics g, JComponent c, Rectangle textRect, String text) {
294 AbstractButton b = (AbstractButton) c;
295 ButtonModel model = b.getModel();
296 FontMetrics fm = SwingUtilities2.getFontMetrics(c, g);
297 int mnemonicIndex = b.getDisplayedMnemonicIndex();
298
299 /* Draw the Text */
300 if(model.isEnabled()) {
301 /*** paint the text normally */
302 g.setColor(b.getForeground());
303 SwingUtilities2.drawStringUnderlineCharAt(c, g,text, mnemonicIndex,
304 textRect.x + getTextShiftOffset(),
305 textRect.y + fm.getAscent() + getTextShiftOffset());
306 }
307 else {
308 /*** paint the text disabled ***/
309 g.setColor(b.getBackground().brighter());
310 SwingUtilities2.drawStringUnderlineCharAt(c, g,text, mnemonicIndex,
311 textRect.x, textRect.y + fm.getAscent());
312 g.setColor(b.getBackground().darker());
313 SwingUtilities2.drawStringUnderlineCharAt(c, g,text, mnemonicIndex,
314 textRect.x - 1, textRect.y + fm.getAscent() - 1);
315 }
316 }
317
318 /**
319 * Method which renders the text of the current button.
320 * <p>
321 * @param g Graphics context
322 * @param b Current button to render
323 * @param textRect Bounding rectangle to render the text.
324 * @param text String to render
325 * @since 1.4
326 */
327 protected void paintText(Graphics g, AbstractButton b, Rectangle textRect, String text) {
328 paintText(g, (JComponent)b, textRect, text);
329 }
330
331 // Method signature defined here overriden in subclasses.
332 // Perhaps this class should be abstract?
333 protected void paintFocus(Graphics g, AbstractButton b,
334 Rectangle viewRect, Rectangle textRect, Rectangle iconRect){
335 }
336
337
338
339 protected void paintButtonPressed(Graphics g, AbstractButton b){
340 }
341
342 protected void clearTextShiftOffset(){
343 this.shiftOffset = 0;
344 }
345
346 protected void setTextShiftOffset(){
347 this.shiftOffset = defaultTextShiftOffset;
348 }
349
350 protected int getTextShiftOffset() {
351 return shiftOffset;
352 }
353
354 // ********************************
355 // Layout Methods
356 // ********************************
357 public Dimension getMinimumSize(JComponent c) {
358 Dimension d = getPreferredSize(c);
359 View v = (View) c.getClientProperty(BasicHTML.propertyKey);
360 if (v != null) {
361 d.width -= v.getPreferredSpan(View.X_AXIS) - v.getMinimumSpan(View.X_AXIS);
362 }
363 return d;
364 }
365
366 public Dimension getPreferredSize(JComponent c) {
367 AbstractButton b = (AbstractButton)c;
368 return BasicGraphicsUtils.getPreferredButtonSize(b, b.getIconTextGap());
369 }
370
371 public Dimension getMaximumSize(JComponent c) {
372 Dimension d = getPreferredSize(c);
373 View v = (View) c.getClientProperty(BasicHTML.propertyKey);
374 if (v != null) {
375 d.width += v.getMaximumSpan(View.X_AXIS) - v.getPreferredSpan(View.X_AXIS);
376 }
377 return d;
378 }
379
380 /**
381 * Returns the baseline.
382 *
383 * @throws NullPointerException {@inheritDoc}
384 * @throws IllegalArgumentException {@inheritDoc}
385 * @see javax.swing.JComponent#getBaseline(int, int)
386 * @since 1.6
387 */
388 public int getBaseline(JComponent c, int width, int height) {
389 super.getBaseline(c, width, height);
390 AbstractButton b = (AbstractButton)c;
391 String text = b.getText();
392 if (text == null || "".equals(text)) {
393 return -1;
394 }
395 FontMetrics fm = b.getFontMetrics(b.getFont());
396 layout(b, fm, width, height);
397 return BasicHTML.getBaseline(b, textRect.y, fm.getAscent(),
398 textRect.width, textRect.height);
399 }
400
401 /**
402 * Returns an enum indicating how the baseline of the component
403 * changes as the size changes.
404 *
405 * @throws NullPointerException {@inheritDoc}
406 * @see javax.swing.JComponent#getBaseline(int, int)
407 * @since 1.6
408 */
409 public Component.BaselineResizeBehavior getBaselineResizeBehavior(
410 JComponent c) {
411 super.getBaselineResizeBehavior(c);
412 if (c.getClientProperty(BasicHTML.propertyKey) != null) {
413 return Component.BaselineResizeBehavior.OTHER;
414 }
415 switch(((AbstractButton)c).getVerticalAlignment()) {
416 case AbstractButton.TOP:
417 return Component.BaselineResizeBehavior.CONSTANT_ASCENT;
418 case AbstractButton.BOTTOM:
419 return Component.BaselineResizeBehavior.CONSTANT_DESCENT;
420 case AbstractButton.CENTER:
421 return Component.BaselineResizeBehavior.CENTER_OFFSET;
422 }
423 return Component.BaselineResizeBehavior.OTHER;
424 }
425
426 private String layout(AbstractButton b, FontMetrics fm,
427 int width, int height) {
428 Insets i = b.getInsets();
429 viewRect.x = i.left;
430 viewRect.y = i.top;
431 viewRect.width = width - (i.right + viewRect.x);
432 viewRect.height = height - (i.bottom + viewRect.y);
433
434 textRect.x = textRect.y = textRect.width = textRect.height = 0;
435 iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0;
436
437 // layout the text and icon
438 return SwingUtilities.layoutCompoundLabel(
439 b, fm, b.getText(), b.getIcon(),
440 b.getVerticalAlignment(), b.getHorizontalAlignment(),
441 b.getVerticalTextPosition(), b.getHorizontalTextPosition(),
442 viewRect, iconRect, textRect,
443 b.getText() == null ? 0 : b.getIconTextGap());
444 }
445
446 /**
447 * Returns the ButtonListener for the passed in Button, or null if one
448 * could not be found.
449 */
450 private BasicButtonListener getButtonListener(AbstractButton b) {
451 MouseMotionListener[] listeners = b.getMouseMotionListeners();
452
453 if (listeners != null) {
454 for (int counter = 0; counter < listeners.length; counter++) {
455 if (listeners[counter] instanceof BasicButtonListener) {
456 return (BasicButtonListener)listeners[counter];
457 }
458 }
459 }
460 return null;
461 }
462
463}