2
* Copyright (c) 2005-2010 Substance Kirill Grouchnikov. All Rights Reserved.
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions are met:
7
* o Redistributions of source code must retain the above copyright notice,
8
* this list of conditions and the following disclaimer.
10
* o Redistributions in binary form must reproduce the above copyright notice,
11
* this list of conditions and the following disclaimer in the documentation
12
* and/or other materials provided with the distribution.
14
* o Neither the name of Substance Kirill Grouchnikov nor the names of
15
* its contributors may be used to endorse or promote products derived
16
* from this software without specific prior written permission.
18
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
22
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
package org.pushingpixels.substance.internal.ui;
33
import java.awt.event.MouseEvent;
34
import java.awt.image.BufferedImage;
35
import java.beans.PropertyChangeEvent;
36
import java.beans.PropertyChangeListener;
40
import javax.swing.border.Border;
41
import javax.swing.plaf.ComponentUI;
42
import javax.swing.plaf.UIResource;
43
import javax.swing.plaf.basic.*;
44
import javax.swing.text.View;
46
import org.pushingpixels.substance.api.*;
47
import org.pushingpixels.substance.api.painter.border.SubstanceBorderPainter;
48
import org.pushingpixels.substance.api.painter.fill.SubstanceFillPainter;
49
import org.pushingpixels.substance.internal.animation.StateTransitionTracker;
50
import org.pushingpixels.substance.internal.animation.TransitionAwareUI;
51
import org.pushingpixels.substance.internal.painter.BackgroundPaintingUtils;
52
import org.pushingpixels.substance.internal.utils.*;
55
* UI for radio buttons in <b>Substance </b> look and feel.
57
* @author Kirill Grouchnikov
59
public class SubstanceRadioButtonUI extends BasicRadioButtonUI implements
62
* Property change listener. Listens on changes to
63
* {@link AbstractButton#MODEL_CHANGED_PROPERTY} property.
65
protected PropertyChangeListener substancePropertyListener;
68
* Associated toggle button.
70
protected JToggleButton button;
73
* Icons for all component states
75
private static LazyResettableHashMap<Icon> icons = new LazyResettableHashMap<Icon>(
76
"SubstanceRadioButtonUI");
78
protected StateTransitionTracker stateTransitionTracker;
80
private Rectangle viewRect = new Rectangle();
82
private Rectangle iconRect = new Rectangle();
84
private Rectangle textRect = new Rectangle();
89
* @seejavax.swing.plaf.basic.BasicButtonUI#installListeners(javax.swing.
93
protected void installListeners(final AbstractButton b) {
94
super.installListeners(b);
96
this.stateTransitionTracker.registerModelListeners();
97
this.stateTransitionTracker.registerFocusListeners();
99
this.substancePropertyListener = new PropertyChangeListener() {
101
public void propertyChange(PropertyChangeEvent evt) {
102
if (AbstractButton.MODEL_CHANGED_PROPERTY.equals(evt
103
.getPropertyName())) {
104
stateTransitionTracker.setModel((ButtonModel) evt
107
if ("font".equals(evt.getPropertyName())) {
108
SwingUtilities.invokeLater(new Runnable() {
117
b.addPropertyChangeListener(substancePropertyListener);
124
* javax.swing.plaf.basic.BasicRadioButtonUI#installDefaults(javax.swing
128
protected void installDefaults(AbstractButton b) {
129
super.installDefaults(b);
130
Border border = b.getBorder();
131
if (border == null || border instanceof UIResource) {
132
b.setBorder(SubstanceSizeUtils.getRadioButtonBorder(
133
SubstanceSizeUtils.getComponentFontSize(b), b
134
.getComponentOrientation().isLeftToRight()));
137
button.setRolloverEnabled(true);
139
LookAndFeel.installProperty(b, "iconTextGap", SubstanceSizeUtils
140
.getTextIconGap(SubstanceSizeUtils.getComponentFontSize(b)));
146
* @seejavax.swing.plaf.basic.BasicButtonUI#uninstallListeners(javax.swing.
150
protected void uninstallListeners(AbstractButton b) {
151
b.removePropertyChangeListener(substancePropertyListener);
152
substancePropertyListener = null;
154
this.stateTransitionTracker.unregisterModelListeners();
155
this.stateTransitionTracker.unregisterFocusListeners();
157
super.uninstallListeners(b);
161
* Returns the icon that matches the current and previous states of the
165
* Button (should be {@link JRadioButton}).
166
* @param stateTransitionTracker
167
* state of the checkbox.
168
* @return Matching icon.
170
private static Icon getIcon(JToggleButton button,
171
StateTransitionTracker stateTransitionTracker) {
172
StateTransitionTracker.ModelStateInfo modelStateInfo = stateTransitionTracker
173
.getModelStateInfo();
174
Map<ComponentState, StateTransitionTracker.StateContributionInfo> activeStates = modelStateInfo
175
.getStateContributionMap();
177
int fontSize = SubstanceSizeUtils.getComponentFontSize(button);
178
int checkMarkSize = SubstanceSizeUtils.getRadioButtonMarkSize(fontSize);
180
SubstanceFillPainter fillPainter = SubstanceCoreUtilities
181
.getFillPainter(button);
182
SubstanceBorderPainter borderPainter = SubstanceCoreUtilities
183
.getBorderPainter(button);
184
ComponentState currState = modelStateInfo.getCurrModelState();
186
SubstanceColorScheme baseFillColorScheme = SubstanceColorSchemeUtilities
187
.getColorScheme(button, ColorSchemeAssociationKind.FILL,
189
SubstanceColorScheme baseMarkColorScheme = SubstanceColorSchemeUtilities
190
.getColorScheme(button, ColorSchemeAssociationKind.MARK,
192
SubstanceColorScheme baseBorderColorScheme = SubstanceColorSchemeUtilities
193
.getColorScheme(button, ColorSchemeAssociationKind.BORDER,
195
float visibility = stateTransitionTracker
196
.getFacetStrength(ComponentStateFacet.SELECTION);
198
HashMapKey keyBase = SubstanceCoreUtilities.getHashKey(fontSize,
199
checkMarkSize, fillPainter.getDisplayName(), borderPainter
201
baseFillColorScheme.getDisplayName(), baseMarkColorScheme
202
.getDisplayName(), baseBorderColorScheme
203
.getDisplayName(), visibility);
204
Icon iconBase = icons.get(keyBase);
205
if (iconBase == null) {
206
iconBase = new ImageIcon(SubstanceImageCreator.getRadioButton(
207
button, fillPainter, borderPainter, checkMarkSize,
208
currState, 0, baseFillColorScheme, baseMarkColorScheme,
209
baseBorderColorScheme, visibility));
210
icons.put(keyBase, iconBase);
212
if (currState.isDisabled() || (activeStates.size() == 1)) {
216
BufferedImage result = SubstanceCoreUtilities.getBlankImage(iconBase
217
.getIconWidth(), iconBase.getIconHeight());
218
Graphics2D g2d = result.createGraphics();
219
// draw the base layer
220
iconBase.paintIcon(button, g2d, 0, 0);
222
// draw other active layers
223
for (Map.Entry<ComponentState, StateTransitionTracker.StateContributionInfo> activeEntry : activeStates
225
ComponentState activeState = activeEntry.getKey();
226
// System.out.println("Painting state " + activeState + "[curr is "
227
// + currState + "] with " + activeEntry.getValue());
228
if (activeState == currState)
231
float stateContribution = activeEntry.getValue().getContribution();
232
if (stateContribution > 0.0f) {
233
g2d.setComposite(AlphaComposite.SrcOver
234
.derive(stateContribution));
235
SubstanceColorScheme fillColorScheme = SubstanceColorSchemeUtilities
236
.getColorScheme(button,
237
ColorSchemeAssociationKind.FILL, activeState);
238
SubstanceColorScheme markColorScheme = SubstanceColorSchemeUtilities
239
.getColorScheme(button,
240
ColorSchemeAssociationKind.MARK, activeState);
241
SubstanceColorScheme borderColorScheme = SubstanceColorSchemeUtilities
242
.getColorScheme(button,
243
ColorSchemeAssociationKind.BORDER, activeState);
245
HashMapKey keyLayer = SubstanceCoreUtilities.getHashKey(
246
fontSize, checkMarkSize, fillPainter.getDisplayName(),
247
borderPainter.getDisplayName(), fillColorScheme
248
.getDisplayName(), markColorScheme
249
.getDisplayName(), borderColorScheme
250
.getDisplayName(), visibility);
251
Icon iconLayer = icons.get(keyLayer);
252
if (iconLayer == null) {
253
iconLayer = new ImageIcon(SubstanceImageCreator
254
.getRadioButton(button, fillPainter, borderPainter,
255
checkMarkSize, currState, 0,
256
fillColorScheme, markColorScheme,
257
borderColorScheme, visibility));
258
icons.put(keyLayer, iconLayer);
261
iconLayer.paintIcon(button, g2d, 0, 0);
266
return new ImageIcon(result);
272
* @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent)
274
public static ComponentUI createUI(JComponent comp) {
275
SubstanceCoreUtilities.testComponentCreationThreadingViolation(comp);
276
return new SubstanceRadioButtonUI((JToggleButton) comp);
280
* Simple constructor.
283
* Associated radio button.
285
public SubstanceRadioButtonUI(JToggleButton button) {
286
this.button = button;
287
button.setRolloverEnabled(true);
288
this.stateTransitionTracker = new StateTransitionTracker(this.button,
289
this.button.getModel());
296
* javax.swing.plaf.basic.BasicButtonUI#createButtonListener(javax.swing
300
protected BasicButtonListener createButtonListener(AbstractButton b) {
301
return new RolloverButtonListener(b, this.stateTransitionTracker);
307
* @see javax.swing.plaf.basic.BasicRadioButtonUI#getDefaultIcon()
310
public Icon getDefaultIcon() {
311
return SubstanceRadioButtonUI.getIcon(button,
312
this.stateTransitionTracker);
316
public void paint(Graphics g, JComponent c) {
317
AbstractButton b = (AbstractButton) c;
319
// boolean isOpaque = b.isOpaque();
320
// b.putClientProperty(SubstanceButtonUI.LOCK_OPACITY, Boolean.TRUE);
321
// b.setOpaque(false);
323
if (SubstanceCoreUtilities.isOpaque(c)) {
324
BackgroundPaintingUtils.update(g, c, false);
327
// b.setOpaque(isOpaque);
329
// b.putClientProperty(SubstanceButtonUI.LOCK_OPACITY, null);
331
FontMetrics fm = g.getFontMetrics();
333
Insets i = b.getInsets();
337
viewRect.width = b.getWidth() - (i.right + viewRect.x);
338
viewRect.height = b.getHeight() - (i.bottom + viewRect.y);
340
textRect.x = textRect.y = textRect.width = textRect.height = 0;
341
iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0;
343
Font f = b.getFont();
346
Icon icon = SubstanceCoreUtilities.getOriginalIcon(b, getDefaultIcon());
348
// layout the text and icon
349
String text = SwingUtilities.layoutCompoundLabel(c, fm, b.getText(),
350
icon, b.getVerticalAlignment(), b.getHorizontalAlignment(), b
351
.getVerticalTextPosition(), b
352
.getHorizontalTextPosition(), viewRect, iconRect,
353
textRect, b.getText() == null ? 0 : b.getIconTextGap());
355
Graphics2D g2d = (Graphics2D) g.create();
356
if (text != null && !text.equals("")) {
357
final View v = (View) b.getClientProperty(BasicHTML.propertyKey);
359
v.paint(g2d, textRect);
361
this.paintButtonText(g2d, b, textRect, text);
367
icon.paintIcon(c, g2d, iconRect.x, iconRect.y);
370
if (b.isFocusPainted()) {
371
// make sure that the focus ring is not clipped
372
int focusRingPadding = SubstanceSizeUtils
373
.getFocusRingPadding(SubstanceSizeUtils
374
.getComponentFontSize(button)) / 2;
375
SubstanceCoreUtilities.paintFocus(g2d, button, button, this, null,
376
textRect, 1.0f, focusRingPadding);
378
// g2d.setColor(Color.red);
379
// g2d.draw(iconRect);
380
// g2d.draw(viewRect);
381
// g2d.draw(textRect);
382
// g2d.setColor(Color.blue);
383
// g2d.drawRect(0, 0, button.getWidth() - 1, button.getHeight() - 1);
389
* Returns memory usage string.
391
* @return Memory usage string.
393
public static String getMemoryUsage() {
394
StringBuffer sb = new StringBuffer();
395
sb.append("SubstanceRadioButtonUI: \n");
396
sb.append("\t" + SubstanceRadioButtonUI.icons.size() + " icons");
397
return sb.toString();
412
protected void paintButtonText(Graphics g, AbstractButton button,
413
Rectangle textRect, String text) {
414
SubstanceTextUtilities.paintText(g, button, textRect, text, button
415
.getDisplayedMnemonicIndex());
419
public boolean isInside(MouseEvent me) {
424
public StateTransitionTracker getTransitionTracker() {
425
return this.stateTransitionTracker;