001: /*
002: * Copyright (c) 2005-2008 Substance Kirill Grouchnikov. All Rights Reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * o Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * o Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * o Neither the name of Substance Kirill Grouchnikov nor the names of
015: * its contributors may be used to endorse or promote products derived
016: * from this software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
021: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
022: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
025: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
026: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
027: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030: package org.jvnet.substance;
031:
032: import java.awt.*;
033: import java.beans.PropertyChangeEvent;
034: import java.beans.PropertyChangeListener;
035:
036: import javax.swing.*;
037: import javax.swing.border.Border;
038: import javax.swing.plaf.ComponentUI;
039: import javax.swing.plaf.basic.*;
040: import javax.swing.text.View;
041:
042: import org.jvnet.lafwidget.animation.*;
043: import org.jvnet.lafwidget.layout.TransitionLayout;
044: import org.jvnet.substance.button.SubstanceButtonShaper;
045: import org.jvnet.substance.painter.text.SubstanceTextPainter;
046: import org.jvnet.substance.utils.*;
047: import org.jvnet.substance.utils.icon.GlowingIcon;
048:
049: /**
050: * UI for toggle buttons in <b>Substance</b> look and feel.
051: *
052: * @author Kirill Grouchnikov
053: */
054: public class SubstanceToggleButtonUI extends BasicToggleButtonUI {
055: /**
056: * Painting delegate.
057: */
058: private ButtonBackgroundDelegate delegate;
059:
060: /**
061: * The rollover button listener.
062: */
063: private RolloverButtonListener substanceButtonListener;
064:
065: /**
066: * The matching glowing icon. Is used only when
067: * {@link FadeConfigurationManager#fadeAllowed(FadeKind, Component)} returns
068: * true on {@link FadeKind#ICON_GLOW}.
069: */
070: protected GlowingIcon glowingIcon;
071:
072: /**
073: * Property change listener. Listens on changes to the
074: * {@link SubstanceLookAndFeel#BUTTON_SHAPER_PROPERTY} property and
075: * {@link AbstractButton#MODEL_CHANGED_PROPERTY} property.
076: */
077: protected PropertyChangeListener substancePropertyListener;
078:
079: /**
080: * Listener for fade animations.
081: */
082: protected FadeStateListener substanceFadeStateListener;
083:
084: /*
085: * (non-Javadoc)
086: *
087: * @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent)
088: */
089: public static ComponentUI createUI(JComponent b) {
090: ((AbstractButton) b).setRolloverEnabled(true);
091: return new SubstanceToggleButtonUI();
092: }
093:
094: /**
095: * Simple constructor.
096: */
097: public SubstanceToggleButtonUI() {
098: this .delegate = new ButtonBackgroundDelegate();
099: }
100:
101: /*
102: * (non-Javadoc)
103: *
104: * @see javax.swing.plaf.basic.BasicButtonUI#installDefaults(javax.swing.AbstractButton)
105: */
106: @Override
107: public void installDefaults(AbstractButton b) {
108: super .installDefaults(b);
109: if (b.getClientProperty(SubstanceButtonUI.BORDER_ORIGINAL) == null)
110: b.putClientProperty(SubstanceButtonUI.BORDER_ORIGINAL, b
111: .getBorder());
112:
113: if (b.getClientProperty(SubstanceButtonUI.BORDER_ORIGINAL) == null)
114: b.putClientProperty(SubstanceButtonUI.BORDER_ORIGINAL, b
115: .getBorder());
116:
117: trackGlowingIcon(b);
118:
119: SubstanceButtonShaper shaper = SubstanceCoreUtilities
120: .getButtonShaper(b);
121:
122: if (b.getClientProperty(SubstanceButtonUI.BORDER_COMPUTED) == null) {
123: b.setBorder(shaper.getButtonBorder(b));
124: } else {
125: Border currBorder = b.getBorder();
126: if (!(currBorder instanceof SubstanceButtonBorder)) {
127: b.setBorder(shaper.getButtonBorder(b));
128: } else {
129: SubstanceButtonBorder sbCurrBorder = (SubstanceButtonBorder) currBorder;
130: if (shaper.getClass() != sbCurrBorder
131: .getButtonShaperClass())
132: b.setBorder(shaper.getButtonBorder(b));
133: }
134: }
135: b.putClientProperty(SubstanceButtonUI.OPACITY_ORIGINAL, b
136: .isOpaque());
137: // fix for defect 140
138: b.setOpaque(false);
139: }
140:
141: /*
142: * (non-Javadoc)
143: *
144: * @see javax.swing.plaf.basic.BasicButtonUI#uninstallDefaults(javax.swing.AbstractButton)
145: */
146: @Override
147: public void uninstallDefaults(AbstractButton b) {
148: super .uninstallDefaults(b);
149:
150: b.setBorder((Border) b
151: .getClientProperty(SubstanceButtonUI.BORDER_ORIGINAL));
152: b.setOpaque((Boolean) b
153: .getClientProperty(SubstanceButtonUI.OPACITY_ORIGINAL));
154: b.putClientProperty(SubstanceButtonUI.OPACITY_ORIGINAL, null);
155: }
156:
157: /*
158: * (non-Javadoc)
159: *
160: * @see javax.swing.plaf.basic.BasicButtonUI#createButtonListener(javax.swing.AbstractButton)
161: */
162: @Override
163: protected BasicButtonListener createButtonListener(AbstractButton b) {
164: return null;
165: }
166:
167: /*
168: * (non-Javadoc)
169: *
170: * @see javax.swing.plaf.basic.BasicButtonUI#installListeners(javax.swing.AbstractButton)
171: */
172: @Override
173: protected void installListeners(final AbstractButton b) {
174: super .installListeners(b);
175:
176: this .substanceButtonListener = new RolloverButtonListener(b);
177: b.addMouseListener(this .substanceButtonListener);
178: b.addMouseMotionListener(this .substanceButtonListener);
179: b.addFocusListener(this .substanceButtonListener);
180: b.addPropertyChangeListener(this .substanceButtonListener);
181: b.addChangeListener(this .substanceButtonListener);
182:
183: this .substancePropertyListener = new PropertyChangeListener() {
184: public void propertyChange(PropertyChangeEvent evt) {
185: if (SubstanceLookAndFeel.BUTTON_SHAPER_PROPERTY
186: .equals(evt.getPropertyName())) {
187: SwingUtilities.invokeLater(new Runnable() {
188: public void run() {
189: SwingUtilities.updateComponentTreeUI(b);
190: }
191: });
192: }
193:
194: if (AbstractButton.MODEL_CHANGED_PROPERTY.equals(evt
195: .getPropertyName())) {
196: if (substanceFadeStateListener != null)
197: substanceFadeStateListener
198: .unregisterListeners();
199: substanceFadeStateListener = new FadeStateListener(
200: b, b.getModel(), SubstanceCoreUtilities
201: .getFadeCallback(b, false));
202: substanceFadeStateListener.registerListeners();
203: }
204: if (AbstractButton.ICON_CHANGED_PROPERTY.equals(evt
205: .getPropertyName())) {
206: trackGlowingIcon(b);
207: }
208: }
209: };
210: b.addPropertyChangeListener(this .substancePropertyListener);
211:
212: this .substanceFadeStateListener = new FadeStateListener(b, b
213: .getModel(), SubstanceCoreUtilities.getFadeCallback(b,
214: false));
215: this .substanceFadeStateListener.registerListeners();
216: }
217:
218: /*
219: * (non-Javadoc)
220: *
221: * @see javax.swing.plaf.basic.BasicButtonUI#uninstallListeners(javax.swing.AbstractButton)
222: */
223: @Override
224: protected void uninstallListeners(AbstractButton b) {
225: b.removeMouseListener(this .substanceButtonListener);
226: b.removeMouseMotionListener(this .substanceButtonListener);
227: b.removeFocusListener(this .substanceButtonListener);
228: b.removePropertyChangeListener(this .substanceButtonListener);
229: b.removeChangeListener(this .substanceButtonListener);
230: this .substanceButtonListener = null;
231:
232: b.removePropertyChangeListener(this .substancePropertyListener);
233: this .substancePropertyListener = null;
234:
235: this .substanceFadeStateListener.unregisterListeners();
236: this .substanceFadeStateListener = null;
237:
238: super .uninstallListeners(b);
239: }
240:
241: /*
242: * (non-Javadoc)
243: *
244: * @see javax.swing.plaf.basic.BasicToggleButtonUI#paint(java.awt.Graphics,
245: * javax.swing.JComponent)
246: */
247: @Override
248: public void paint(Graphics g, JComponent c) {
249: // final AbstractButton b = (AbstractButton) c;
250: //
251: // SubstanceTextPainter textPainter = SubstanceLookAndFeel
252: // .getCurrentTextPainter();
253: // textPainter.init(b, null, false);
254: // if (textPainter.needsBackgroundImage()) {
255: // textPainter.setBackgroundFill(b, b.getParent().getBackground(),
256: // true, 0, 0);
257: // textPainter
258: // .attachCallback(new SubstanceTextPainter.BackgroundPaintingCallback() {
259: // public void paintBackground(Graphics g) {
260: // delegate.updateBackground(g, b);
261: // };
262: // });
263: // } else {
264: // this.delegate.updateBackground(g, b);
265: // }
266: //
267: // FontMetrics fm = g.getFontMetrics();
268: //
269: // Insets i = c.getInsets();
270: //
271: // Rectangle viewRect = new Rectangle();
272: // Rectangle iconRect = new Rectangle();
273: // Rectangle textRect = new Rectangle();
274: //
275: // viewRect.x = i.left;
276: // viewRect.y = i.top;
277: // viewRect.width = b.getWidth() - (i.right + viewRect.x);
278: // viewRect.height = b.getHeight() - (i.bottom + viewRect.y);
279: //
280: // textRect.x = textRect.y = textRect.width = textRect.height = 0;
281: // iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0;
282: //
283: // Font f = c.getFont();
284: // g.setFont(f);
285: //
286: // // layout the text and icon
287: // String text = SwingUtilities.layoutCompoundLabel(c, fm, b.getText(), b
288: // .getIcon(), b.getVerticalAlignment(), b
289: // .getHorizontalAlignment(), b.getVerticalTextPosition(), b
290: // .getHorizontalTextPosition(), viewRect, iconRect, textRect, b
291: // .getText() == null ? 0 : b.getIconTextGap());
292: //
293: // View v = (View) c.getClientProperty(BasicHTML.propertyKey);
294: // if (v != null) {
295: // v.paint(g, textRect);
296: // } else {
297: // float alpha = this.paintButtonText(g, b, textRect, text);
298: // Graphics2D g2d = (Graphics2D) g.create();
299: // g2d.setComposite(TransitionLayout.getAlphaComposite(b, alpha, g));
300: // textPainter.renderSurface(g2d);
301: // g2d.dispose();
302: // }
303: //
304: // // Paint the Icon
305: // if (b.getIcon() != null) {
306: // paintIcon(g, c, iconRect);
307: // }
308: //
309: // if (b.isFocusPainted()) {
310: // if (b.hasFocus()
311: // || FadeTracker.getInstance().isTracked(c, FadeKind.FOCUS)) {
312: // this.paintFocus(g, b, viewRect, textRect, iconRect);
313: // }
314: // }
315: final AbstractButton b = (AbstractButton) c;
316:
317: if (b instanceof JButton) {
318: JButton jb = (JButton) b;
319: if (PulseTracker.isPulsating(jb)) {
320: PulseTracker.update(jb);
321: } else {
322: }
323: }
324:
325: FontMetrics fm = g.getFontMetrics();
326:
327: Insets i = c.getInsets();
328:
329: Rectangle viewRect = new Rectangle();
330: Rectangle iconRect = new Rectangle();
331: final Rectangle textRect = new Rectangle();
332:
333: viewRect.x = i.left;
334: viewRect.y = i.top;
335: viewRect.width = b.getWidth() - (i.right + viewRect.x);
336: viewRect.height = b.getHeight() - (i.bottom + viewRect.y);
337:
338: textRect.x = textRect.y = textRect.width = textRect.height = 0;
339: iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0;
340:
341: Font f = c.getFont();
342:
343: // layout the text and icon
344: String text = SwingUtilities.layoutCompoundLabel(c, fm, b
345: .getText(), b.getIcon(), b.getVerticalAlignment(), b
346: .getHorizontalAlignment(), b.getVerticalTextPosition(),
347: b.getHorizontalTextPosition(), viewRect, iconRect,
348: textRect, b.getText() == null ? 0 : b.getIconTextGap());
349:
350: Graphics2D g2d = (Graphics2D) g.create();
351: boolean hasEmptyText = (text == null) || (text.length() == 0);
352:
353: final View v = (View) c
354: .getClientProperty(BasicHTML.propertyKey);
355: SubstanceTextPainter textPainter = SubstanceLookAndFeel
356: .getCurrentTextPainter();
357: textPainter.init(b, null, v != null);
358: g2d.setFont(f);
359:
360: if (!hasEmptyText && textPainter.needsBackgroundImage()) {
361: textPainter.setBackgroundFill(b, b.getParent()
362: .getBackground(), true, 0, 0);
363: textPainter
364: .attachCallback(new SubstanceTextPainter.BackgroundPaintingCallback() {
365: public void paintBackground(Graphics g) {
366: delegate.updateBackground(g, b);
367: };
368: });
369: } else {
370: this .delegate.updateBackground(g2d, b);
371: }
372: float textAlpha = 1.0f;
373: if (v != null) {
374: if (textPainter.needsBackgroundImage()) {
375: textPainter
376: .attachCallback(new SubstanceTextPainter.BackgroundPaintingCallback() {
377: public void paintBackground(Graphics g) {
378: v.paint(g, textRect);
379: }
380: });
381: } else {
382: v.paint(g2d, textRect);
383: }
384: } else {
385: textAlpha = this .paintButtonText(g, b, textRect, text);
386: }
387: g2d.setComposite(TransitionLayout.getAlphaComposite(b,
388: textAlpha, g));
389: textPainter.renderSurface(g2d);
390: g2d
391: .setComposite(TransitionLayout.getAlphaComposite(b,
392: 1.0f, g));
393:
394: // Paint the Icon
395: if (b.getIcon() != null) {
396: paintIcon(g2d, c, iconRect);
397: }
398:
399: if (b.isFocusPainted()) {
400: if (b.hasFocus()
401: || FadeTracker.getInstance().isTracked(c,
402: FadeKind.FOCUS)) {
403: this .paintFocus(g2d, b, viewRect, textRect, iconRect);
404: }
405: }
406: }
407:
408: /*
409: * (non-Javadoc)
410: *
411: * @see javax.swing.plaf.ComponentUI#getPreferredSize(javax.swing.JComponent)
412: */
413: @Override
414: public Dimension getPreferredSize(JComponent c) {
415: AbstractButton button = (AbstractButton) c;
416: SubstanceButtonShaper shaper = SubstanceCoreUtilities
417: .getButtonShaper(button);
418:
419: // fix for defect 263
420: Dimension super Pref = super .getPreferredSize(button);
421: if (super Pref == null)
422: return null;
423:
424: return shaper.getPreferredSize(button, super Pref);
425: }
426:
427: /*
428: * (non-Javadoc)
429: *
430: * @see javax.swing.plaf.ComponentUI#contains(javax.swing.JComponent, int,
431: * int)
432: */
433: @Override
434: public boolean contains(JComponent c, int x, int y) {
435: return ButtonBackgroundDelegate.contains((JToggleButton) c, x,
436: y);
437: }
438:
439: /*
440: * (non-Javadoc)
441: *
442: * @see javax.swing.plaf.basic.BasicButtonUI#paintFocus(java.awt.Graphics,
443: * javax.swing.AbstractButton, java.awt.Rectangle, java.awt.Rectangle,
444: * java.awt.Rectangle)
445: */
446: @Override
447: protected void paintFocus(Graphics g, AbstractButton b,
448: Rectangle viewRect, Rectangle textRect, Rectangle iconRect) {
449: if (!b.isFocusPainted())
450: return;
451:
452: SubstanceCoreUtilities.paintFocus(g, b, b, null, textRect,
453: 1.0f, 2 + SubstanceSizeUtils
454: .getExtraPadding(SubstanceSizeUtils
455: .getComponentFontSize(b)));
456: }
457:
458: /*
459: * (non-Javadoc)
460: *
461: * @see javax.swing.plaf.basic.BasicToggleButtonUI#paintIcon(java.awt.Graphics,
462: * javax.swing.AbstractButton, java.awt.Rectangle)
463: */
464: @Override
465: protected void paintIcon(Graphics g, AbstractButton b,
466: Rectangle iconRect) {
467: Graphics2D graphics = (Graphics2D) g.create();
468: FadeTracker fadeTracker = FadeTracker.getInstance();
469: Icon icon = SubstanceCoreUtilities.getIcon(b, null,
470: this .glowingIcon, false);
471:
472: graphics.setComposite(TransitionLayout.getAlphaComposite(b, g));
473: if (fadeTracker.isTracked(b, FadeKind.ROLLOVER)) {
474: ComponentState state = ComponentState.getState(
475: b.getModel(), b);
476: // System.out.println(state.name() + ":" + state.isRollover());
477: if (state.isKindActive(FadeKind.ROLLOVER)) {// ==
478: // ComponentState.ROLLOVER_UNSELECTED) {
479: // Came from default state
480: SubstanceCoreUtilities.getIcon(b, null,
481: this .glowingIcon, true).paintIcon(b, graphics,
482: iconRect.x, iconRect.y);
483: graphics.setComposite(TransitionLayout
484: .getAlphaComposite(b, fadeTracker.getFade10(b,
485: FadeKind.ROLLOVER) / 10.0f, g));
486: icon.paintIcon(b, graphics, iconRect.x, iconRect.y);
487: } else {
488: // if (state == ComponentState.DEFAULT) {
489: // Came from rollover state
490: icon.paintIcon(b, graphics, iconRect.x, iconRect.y);
491: graphics.setComposite(TransitionLayout
492: .getAlphaComposite(b, fadeTracker.getFade10(b,
493: FadeKind.ROLLOVER) / 10.0f, g));
494: b.getIcon().paintIcon(b, graphics, iconRect.x,
495: iconRect.y);
496: }
497: } else {
498: icon.paintIcon(b, graphics, iconRect.x, iconRect.y);
499: }
500:
501: graphics.dispose();
502: }
503:
504: /**
505: * Paints the text.
506: *
507: * @param g
508: * Graphic context
509: * @param button
510: * Button
511: * @param textRect
512: * Text rectangle
513: * @param text
514: * Text to paint
515: * @return Text alpha channel.
516: */
517: protected float paintButtonText(Graphics g, AbstractButton button,
518: Rectangle textRect, String text) {
519: return SubstanceCoreUtilities.paintText(button, textRect, text,
520: (button).getDisplayedMnemonicIndex());
521: }
522:
523: /**
524: * Tracks possible usage of glowing icon.
525: *
526: * @param b
527: * Button.
528: */
529: protected void trackGlowingIcon(AbstractButton b) {
530: Icon currIcon = b.getIcon();
531: if (currIcon instanceof GlowingIcon)
532: return;
533: if (currIcon == null)
534: return;
535: this .glowingIcon = new GlowingIcon(currIcon, b);
536: }
537:
538: /*
539: * (non-Javadoc)
540: *
541: * @see javax.swing.plaf.ComponentUI#update(java.awt.Graphics,
542: * javax.swing.JComponent)
543: */
544: @Override
545: public void update(Graphics g, JComponent c) {
546: this.paint(g, c);
547: }
548: }
|