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.*;
34
import java.beans.PropertyChangeEvent;
35
import java.beans.PropertyChangeListener;
38
import javax.swing.plaf.ComponentUI;
39
import javax.swing.plaf.basic.BasicMenuUI;
41
import org.pushingpixels.substance.api.SubstanceLookAndFeel;
42
import org.pushingpixels.substance.internal.animation.StateTransitionTracker;
43
import org.pushingpixels.substance.internal.animation.TransitionAwareUI;
44
import org.pushingpixels.substance.internal.utils.SubstanceCoreUtilities;
45
import org.pushingpixels.substance.internal.utils.SubstanceSizeUtils;
46
import org.pushingpixels.substance.internal.utils.icon.MenuArrowIcon;
47
import org.pushingpixels.substance.internal.utils.menu.MenuUtilities;
48
import org.pushingpixels.substance.internal.utils.menu.SubstanceMenu;
49
import org.pushingpixels.substance.internal.utils.menu.MenuUtilities.MenuPropertyListener;
52
* UI for menus in <b>Substance</b> look and feel.
54
* @author Kirill Grouchnikov
56
public class SubstanceMenuUI extends BasicMenuUI implements SubstanceMenu,
59
* For rollover effects - enhancement 93.
61
protected MouseListener substanceMouseListener;
63
protected StateTransitionTracker stateTransitionTracker;
66
* Listens on all changes to the underlying menu item.
68
protected MenuPropertyListener substanceMenuPropertyListener;
71
* Property change listener. Listens on changes to
72
* {@link AbstractButton#MODEL_CHANGED_PROPERTY} property.
74
protected PropertyChangeListener substancePropertyListener;
77
* For rollover effects - enhancement 93.
79
protected FocusListener substanceFocusListener;
84
* @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent)
86
public static ComponentUI createUI(JComponent comp) {
87
SubstanceCoreUtilities.testComponentCreationThreadingViolation(comp);
88
return new SubstanceMenuUI((JMenu) comp);
91
public SubstanceMenuUI(JMenu menuItem) {
92
this.stateTransitionTracker = new StateTransitionTracker(menuItem,
99
* @see javax.swing.plaf.basic.BasicMenuUI#installDefaults()
102
protected void installDefaults() {
103
super.installDefaults();
104
this.menuItem.setRolloverEnabled(true);
105
// this.menuItem.setOpaque(false);
107
this.arrowIcon = new MenuArrowIcon((JMenu) this.menuItem);
109
this.defaultTextIconGap = SubstanceSizeUtils
110
.getTextIconGap(SubstanceSizeUtils
111
.getComponentFontSize(this.menuItem));
113
this.menuItem.putClientProperty(SubstanceLookAndFeel.FLAT_PROPERTY,
116
LookAndFeel.installProperty(menuItem, "opaque", Boolean.FALSE);
122
* @see javax.swing.plaf.basic.BasicMenuUI#installListeners()
125
protected void installListeners() {
126
super.installListeners();
128
// Improving performance on big menus.
129
this.substanceMenuPropertyListener = new MenuPropertyListener(
131
this.substanceMenuPropertyListener.install();
133
this.stateTransitionTracker.registerModelListeners();
135
// fix for enhancement 93 - rollover effects on menu items
136
this.substanceMouseListener = new MouseAdapter() {
137
// fix for defect 93 - no rollover effects on menu
138
// items that are not in the selected path
139
private boolean toRepaint() {
140
MenuElement[] selectedMenuPath = MenuSelectionManager
141
.defaultManager().getSelectedPath();
142
for (MenuElement elem : selectedMenuPath) {
143
if (elem == SubstanceMenuUI.this.menuItem) {
147
return (selectedMenuPath.length == 0);
151
public void mouseEntered(MouseEvent e) {
152
if (this.toRepaint()) {
153
stateTransitionTracker.turnOffModelChangeTracking();
154
menuItem.getModel().setRollover(true);
155
stateTransitionTracker.onModelStateChanged();
156
// fix for issue 371 - repaint the menu bar since the
157
// menu is marked as flat
158
Rectangle bounds = menuItem.getBounds();
159
menuItem.getParent().repaint(bounds.x, bounds.y,
160
bounds.width, bounds.height);
165
public void mouseExited(MouseEvent e) {
166
if (this.toRepaint()) {
167
stateTransitionTracker.turnOffModelChangeTracking();
168
menuItem.getModel().setRollover(false);
169
stateTransitionTracker.onModelStateChanged();
170
// fix for issue 371 - repaint the menu bar since the
171
// menu is marked as flat
172
Rectangle bounds = menuItem.getBounds();
173
menuItem.getParent().repaint(bounds.x, bounds.y,
174
bounds.width, bounds.height);
178
this.menuItem.addMouseListener(this.substanceMouseListener);
179
this.substanceFocusListener = new FocusAdapter() {
180
// fix for defect 93 - no rollover effects on menu
181
// items that are not in the selected path
182
private boolean toRepaint() {
183
MenuElement[] selectedMenuPath = MenuSelectionManager
184
.defaultManager().getSelectedPath();
185
for (MenuElement elem : selectedMenuPath) {
186
if (elem == SubstanceMenuUI.this.menuItem) {
190
return (selectedMenuPath.length == 0);
194
public void focusLost(FocusEvent e) {
196
stateTransitionTracker.turnOffModelChangeTracking();
197
menuItem.getModel().setRollover(false);
198
stateTransitionTracker.onModelStateChanged();
199
// fix for issue 371 - repaint the menu bar since the
200
// menu is marked as flat
201
Rectangle bounds = menuItem.getBounds();
202
menuItem.getParent().repaint(bounds.x, bounds.y,
203
bounds.width, bounds.height);
207
this.menuItem.addFocusListener(this.substanceFocusListener);
209
this.substancePropertyListener = new PropertyChangeListener() {
211
public void propertyChange(PropertyChangeEvent evt) {
212
if (AbstractButton.MODEL_CHANGED_PROPERTY.equals(evt
213
.getPropertyName())) {
214
stateTransitionTracker.setModel((ButtonModel) evt
217
if ("font".equals(evt.getPropertyName())) {
218
SwingUtilities.invokeLater(new Runnable() {
221
if (menuItem != null) {
229
this.menuItem.addPropertyChangeListener(this.substancePropertyListener);
235
* @see javax.swing.plaf.basic.BasicMenuUI#uninstallListeners()
238
protected void uninstallListeners() {
239
super.uninstallListeners();
241
// Improving performance on big menus.
242
this.substanceMenuPropertyListener.uninstall();
243
this.substanceMenuPropertyListener = null;
245
// fix for enhancement 93 - rollover effects on menu items
246
this.menuItem.removeMouseListener(this.substanceMouseListener);
247
this.substanceMouseListener = null;
248
this.menuItem.removeFocusListener(this.substanceFocusListener);
249
this.substanceFocusListener = null;
252
.removePropertyChangeListener(this.substancePropertyListener);
253
this.substancePropertyListener = null;
255
this.stateTransitionTracker.unregisterModelListeners();
261
* @see org.pushingpixels.substance.SubstanceMenu#getAssociatedMenuItem()
264
public JMenuItem getAssociatedMenuItem() {
265
return this.menuItem;
271
* @see org.pushingpixels.substance.SubstanceMenu#getAcceleratorFont()
274
public Font getAcceleratorFont() {
275
return this.acceleratorFont;
281
* @see org.pushingpixels.substance.SubstanceMenu#getArrowIcon()
284
public Icon getArrowIcon() {
285
return this.arrowIcon;
291
* @see org.pushingpixels.substance.SubstanceMenu#getCheckIcon()
294
public Icon getCheckIcon() {
301
* @see org.pushingpixels.substance.SubstanceMenu#getDefaultTextIconGap()
304
public int getDefaultTextIconGap() {
305
return this.defaultTextIconGap;
312
* javax.swing.plaf.basic.BasicMenuItemUI#getPreferredMenuItemSize(javax
313
* .swing.JComponent, javax.swing.Icon, javax.swing.Icon, int)
316
protected Dimension getPreferredMenuItemSize(JComponent c, Icon checkIcon,
317
Icon arrowIcon, int defaultTextIconGap) {
318
Dimension superDim = super.getPreferredMenuItemSize(c, checkIcon,
319
arrowIcon, defaultTextIconGap);
321
if (MenuUtilities.getPopupLayoutMetrics(menuItem, false) != null) {
322
return new Dimension(MenuUtilities.getPreferredWidth(menuItem),
333
* javax.swing.plaf.basic.BasicMenuItemUI#paintMenuItem(java.awt.Graphics,
334
* javax.swing.JComponent, javax.swing.Icon, javax.swing.Icon,
335
* java.awt.Color, java.awt.Color, int)
338
protected void paintMenuItem(Graphics g, JComponent c, Icon checkIcon,
339
Icon arrowIcon, Color background, Color foreground,
340
int defaultTextIconGap) {
341
MenuUtilities.paintMenuItem(g, menuItem, checkIcon, arrowIcon,
346
public StateTransitionTracker getTransitionTracker() {
347
return this.stateTransitionTracker;
351
public boolean isInside(MouseEvent me) {