001 /*
002 * Copyright 1997-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
026 package javax.swing.plaf.basic;
027
028 import sun.swing.SwingUtilities2;
029 import java.awt.*;
030 import java.awt.event.*;
031 import java.io.Serializable;
032 import javax.swing.*;
033 import javax.swing.border.*;
034 import java.awt.*;
035 import java.awt.event.*;
036 import javax.swing.plaf.ButtonUI;
037 import javax.swing.plaf.UIResource;
038 import javax.swing.plaf.ComponentUI;
039 import javax.swing.text.View;
040
041 /**
042 * BasicButton implementation
043 *
044 * @version 1.124 05/05/07
045 * @author Jeff Dinkins
046 */
047 public class BasicButtonUI extends ButtonUI {
048 // Shared UI object
049 private final static BasicButtonUI buttonUI = new BasicButtonUI();
050
051 // Visual constants
052 // NOTE: This is not used or set any where. Were we allowed to remove
053 // fields, this would be removed.
054 protected int defaultTextIconGap;
055
056 // Amount to offset text, the value of this comes from
057 // defaultTextShiftOffset once setTextShiftOffset has been invoked.
058 private int shiftOffset = 0;
059 // Value that is set in shiftOffset once setTextShiftOffset has been
060 // invoked. The value of this comes from the defaults table.
061 protected int defaultTextShiftOffset;
062
063 private final static String propertyPrefix = "Button" + ".";
064
065 // ********************************
066 // Create PLAF
067 // ********************************
068 public static ComponentUI createUI(JComponent c) {
069 return buttonUI;
070 }
071
072 protected String getPropertyPrefix() {
073 return propertyPrefix;
074 }
075
076 // ********************************
077 // Install PLAF
078 // ********************************
079 public void installUI(JComponent c) {
080 installDefaults((AbstractButton) c);
081 installListeners((AbstractButton) c);
082 installKeyboardActions((AbstractButton) c);
083 BasicHTML.updateRenderer(c, ((AbstractButton) c).getText());
084 }
085
086 protected void installDefaults(AbstractButton b) {
087 // load shared instance defaults
088 String pp = getPropertyPrefix();
089
090 defaultTextShiftOffset = UIManager.getInt(pp
091 + "textShiftOffset");
092
093 // set the following defaults on the button
094 if (b.isContentAreaFilled()) {
095 LookAndFeel.installProperty(b, "opaque", Boolean.TRUE);
096 } else {
097 LookAndFeel.installProperty(b, "opaque", Boolean.FALSE);
098 }
099
100 if (b.getMargin() == null
101 || (b.getMargin() instanceof UIResource)) {
102 b.setMargin(UIManager.getInsets(pp + "margin"));
103 }
104
105 LookAndFeel.installColorsAndFont(b, pp + "background", pp
106 + "foreground", pp + "font");
107 LookAndFeel.installBorder(b, pp + "border");
108
109 Object rollover = UIManager.get(pp + "rollover");
110 if (rollover != null) {
111 LookAndFeel.installProperty(b, "rolloverEnabled", rollover);
112 }
113
114 LookAndFeel.installProperty(b, "iconTextGap", new Integer(4));
115 }
116
117 protected void installListeners(AbstractButton b) {
118 BasicButtonListener listener = createButtonListener(b);
119 if (listener != null) {
120 b.addMouseListener(listener);
121 b.addMouseMotionListener(listener);
122 b.addFocusListener(listener);
123 b.addPropertyChangeListener(listener);
124 b.addChangeListener(listener);
125 }
126 }
127
128 protected void installKeyboardActions(AbstractButton b) {
129 BasicButtonListener listener = getButtonListener(b);
130
131 if (listener != null) {
132 listener.installKeyboardActions(b);
133 }
134 }
135
136 // ********************************
137 // Uninstall PLAF
138 // ********************************
139 public void uninstallUI(JComponent c) {
140 uninstallKeyboardActions((AbstractButton) c);
141 uninstallListeners((AbstractButton) c);
142 uninstallDefaults((AbstractButton) c);
143 BasicHTML.updateRenderer(c, "");
144 }
145
146 protected void uninstallKeyboardActions(AbstractButton b) {
147 BasicButtonListener listener = getButtonListener(b);
148 if (listener != null) {
149 listener.uninstallKeyboardActions(b);
150 }
151 }
152
153 protected void uninstallListeners(AbstractButton b) {
154 BasicButtonListener listener = getButtonListener(b);
155 if (listener != null) {
156 b.removeMouseListener(listener);
157 b.removeMouseMotionListener(listener);
158 b.removeFocusListener(listener);
159 b.removeChangeListener(listener);
160 b.removePropertyChangeListener(listener);
161 }
162 }
163
164 protected void uninstallDefaults(AbstractButton b) {
165 LookAndFeel.uninstallBorder(b);
166 }
167
168 // ********************************
169 // Create Listeners
170 // ********************************
171 protected BasicButtonListener createButtonListener(AbstractButton b) {
172 return new BasicButtonListener(b);
173 }
174
175 public int getDefaultTextIconGap(AbstractButton b) {
176 return defaultTextIconGap;
177 }
178
179 /* These rectangles/insets are allocated once for all
180 * ButtonUI.paint() calls. Re-using rectangles rather than
181 * allocating them in each paint call substantially reduced the time
182 * it took paint to run. Obviously, this method can't be re-entered.
183 */
184 private static Rectangle viewRect = new Rectangle();
185 private static Rectangle textRect = new Rectangle();
186 private static Rectangle iconRect = new Rectangle();
187
188 // ********************************
189 // Paint Methods
190 // ********************************
191
192 public void paint(Graphics g, JComponent c) {
193 AbstractButton b = (AbstractButton) c;
194 ButtonModel model = b.getModel();
195
196 String text = layout(b, SwingUtilities2.getFontMetrics(b, g), b
197 .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,
227 Rectangle iconRect) {
228 AbstractButton b = (AbstractButton) c;
229 ButtonModel model = b.getModel();
230 Icon icon = b.getIcon();
231 Icon tmpIcon = null;
232
233 if (icon == null) {
234 return;
235 }
236
237 Icon selectedIcon = null;
238
239 /* the fallback icon should be based on the selected state */
240 if (model.isSelected()) {
241 selectedIcon = (Icon) b.getSelectedIcon();
242 if (selectedIcon != null) {
243 icon = selectedIcon;
244 }
245 }
246
247 if (!model.isEnabled()) {
248 if (model.isSelected()) {
249 tmpIcon = (Icon) b.getDisabledSelectedIcon();
250 if (tmpIcon == null) {
251 tmpIcon = selectedIcon;
252 }
253 }
254
255 if (tmpIcon == null) {
256 tmpIcon = (Icon) b.getDisabledIcon();
257 }
258 } else if (model.isPressed() && model.isArmed()) {
259 tmpIcon = (Icon) b.getPressedIcon();
260 if (tmpIcon != null) {
261 // revert back to 0 offset
262 clearTextShiftOffset();
263 }
264 } else if (b.isRolloverEnabled() && model.isRollover()) {
265 if (model.isSelected()) {
266 tmpIcon = (Icon) b.getRolloverSelectedIcon();
267 if (tmpIcon == null) {
268 tmpIcon = selectedIcon;
269 }
270 }
271
272 if (tmpIcon == null) {
273 tmpIcon = (Icon) b.getRolloverIcon();
274 }
275 }
276
277 if (tmpIcon != null) {
278 icon = tmpIcon;
279 }
280
281 if (model.isPressed() && model.isArmed()) {
282 icon.paintIcon(c, g, iconRect.x + getTextShiftOffset(),
283 iconRect.y + getTextShiftOffset());
284 } else {
285 icon.paintIcon(c, g, iconRect.x, iconRect.y);
286 }
287
288 }
289
290 /**
291 * As of Java 2 platform v 1.4 this method should not be used or overriden.
292 * Use the paintText method which takes the AbstractButton argument.
293 */
294 protected void paintText(Graphics g, JComponent c,
295 Rectangle textRect, String text) {
296 AbstractButton b = (AbstractButton) c;
297 ButtonModel model = b.getModel();
298 FontMetrics fm = SwingUtilities2.getFontMetrics(c, g);
299 int mnemonicIndex = b.getDisplayedMnemonicIndex();
300
301 /* Draw the Text */
302 if (model.isEnabled()) {
303 /*** paint the text normally */
304 g.setColor(b.getForeground());
305 SwingUtilities2.drawStringUnderlineCharAt(c, g, text,
306 mnemonicIndex, textRect.x + getTextShiftOffset(),
307 textRect.y + fm.getAscent() + getTextShiftOffset());
308 } else {
309 /*** paint the text disabled ***/
310 g.setColor(b.getBackground().brighter());
311 SwingUtilities2.drawStringUnderlineCharAt(c, g, text,
312 mnemonicIndex, textRect.x, textRect.y
313 + fm.getAscent());
314 g.setColor(b.getBackground().darker());
315 SwingUtilities2.drawStringUnderlineCharAt(c, g, text,
316 mnemonicIndex, textRect.x - 1, textRect.y
317 + fm.getAscent() - 1);
318 }
319 }
320
321 /**
322 * Method which renders the text of the current button.
323 * <p>
324 * @param g Graphics context
325 * @param b Current button to render
326 * @param textRect Bounding rectangle to render the text.
327 * @param text String to render
328 * @since 1.4
329 */
330 protected void paintText(Graphics g, AbstractButton b,
331 Rectangle textRect, String text) {
332 paintText(g, (JComponent) b, textRect, text);
333 }
334
335 // Method signature defined here overriden in subclasses.
336 // Perhaps this class should be abstract?
337 protected void paintFocus(Graphics g, AbstractButton b,
338 Rectangle viewRect, Rectangle textRect, Rectangle iconRect) {
339 }
340
341 protected void paintButtonPressed(Graphics g, AbstractButton b) {
342 }
343
344 protected void clearTextShiftOffset() {
345 this .shiftOffset = 0;
346 }
347
348 protected void setTextShiftOffset() {
349 this .shiftOffset = defaultTextShiftOffset;
350 }
351
352 protected int getTextShiftOffset() {
353 return shiftOffset;
354 }
355
356 // ********************************
357 // Layout Methods
358 // ********************************
359 public Dimension getMinimumSize(JComponent c) {
360 Dimension d = getPreferredSize(c);
361 View v = (View) c.getClientProperty(BasicHTML.propertyKey);
362 if (v != null) {
363 d.width -= v.getPreferredSpan(View.X_AXIS)
364 - v.getMinimumSpan(View.X_AXIS);
365 }
366 return d;
367 }
368
369 public Dimension getPreferredSize(JComponent c) {
370 AbstractButton b = (AbstractButton) c;
371 return BasicGraphicsUtils.getPreferredButtonSize(b, b
372 .getIconTextGap());
373 }
374
375 public Dimension getMaximumSize(JComponent c) {
376 Dimension d = getPreferredSize(c);
377 View v = (View) c.getClientProperty(BasicHTML.propertyKey);
378 if (v != null) {
379 d.width += v.getMaximumSpan(View.X_AXIS)
380 - v.getPreferredSpan(View.X_AXIS);
381 }
382 return d;
383 }
384
385 /**
386 * Returns the baseline.
387 *
388 * @throws NullPointerException {@inheritDoc}
389 * @throws IllegalArgumentException {@inheritDoc}
390 * @see javax.swing.JComponent#getBaseline(int, int)
391 * @since 1.6
392 */
393 public int getBaseline(JComponent c, int width, int height) {
394 super .getBaseline(c, width, height);
395 AbstractButton b = (AbstractButton) c;
396 String text = b.getText();
397 if (text == null || "".equals(text)) {
398 return -1;
399 }
400 FontMetrics fm = b.getFontMetrics(b.getFont());
401 layout(b, fm, width, height);
402 return BasicHTML.getBaseline(b, textRect.y, fm.getAscent(),
403 textRect.width, textRect.height);
404 }
405
406 /**
407 * Returns an enum indicating how the baseline of the component
408 * changes as the size changes.
409 *
410 * @throws NullPointerException {@inheritDoc}
411 * @see javax.swing.JComponent#getBaseline(int, int)
412 * @since 1.6
413 */
414 public Component.BaselineResizeBehavior getBaselineResizeBehavior(
415 JComponent c) {
416 super .getBaselineResizeBehavior(c);
417 if (c.getClientProperty(BasicHTML.propertyKey) != null) {
418 return Component.BaselineResizeBehavior.OTHER;
419 }
420 switch (((AbstractButton) c).getVerticalAlignment()) {
421 case AbstractButton.TOP:
422 return Component.BaselineResizeBehavior.CONSTANT_ASCENT;
423 case AbstractButton.BOTTOM:
424 return Component.BaselineResizeBehavior.CONSTANT_DESCENT;
425 case AbstractButton.CENTER:
426 return Component.BaselineResizeBehavior.CENTER_OFFSET;
427 }
428 return Component.BaselineResizeBehavior.OTHER;
429 }
430
431 private String layout(AbstractButton b, FontMetrics fm, int width,
432 int height) {
433 Insets i = b.getInsets();
434 viewRect.x = i.left;
435 viewRect.y = i.top;
436 viewRect.width = width - (i.right + viewRect.x);
437 viewRect.height = height - (i.bottom + viewRect.y);
438
439 textRect.x = textRect.y = textRect.width = textRect.height = 0;
440 iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0;
441
442 // layout the text and icon
443 return SwingUtilities.layoutCompoundLabel(b, fm, b.getText(), b
444 .getIcon(), b.getVerticalAlignment(), b
445 .getHorizontalAlignment(), b.getVerticalTextPosition(),
446 b.getHorizontalTextPosition(), viewRect, iconRect,
447 textRect, b.getText() == null ? 0 : b.getIconTextGap());
448 }
449
450 /**
451 * Returns the ButtonListener for the passed in Button, or null if one
452 * could not be found.
453 */
454 private BasicButtonListener getButtonListener(AbstractButton b) {
455 MouseMotionListener[] listeners = b.getMouseMotionListeners();
456
457 if (listeners != null) {
458 for (int counter = 0; counter < listeners.length; counter++) {
459 if (listeners[counter] instanceof BasicButtonListener) {
460 return (BasicButtonListener) listeners[counter];
461 }
462 }
463 }
464 return null;
465 }
466
467 }
|