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.awt.event.*;
034: import java.beans.PropertyChangeEvent;
035: import java.beans.PropertyChangeListener;
036: import java.util.HashSet;
037: import java.util.Set;
038:
039: import javax.swing.*;
040: import javax.swing.plaf.ComponentUI;
041: import javax.swing.plaf.basic.BasicMenuUI;
042:
043: import org.jvnet.lafwidget.animation.FadeKind;
044: import org.jvnet.lafwidget.animation.FadeStateListener;
045: import org.jvnet.substance.utils.SubstanceCoreUtilities;
046: import org.jvnet.substance.utils.SubstanceSizeUtils;
047: import org.jvnet.substance.utils.icon.MenuArrowIcon;
048: import org.jvnet.substance.utils.menu.MenuUtilities;
049: import org.jvnet.substance.utils.menu.MenuUtilities.MenuPropertyListener;
050:
051: /**
052: * UI for menus in <b>Substance</b> look and feel.
053: *
054: * @author Kirill Grouchnikov
055: */
056: public class SubstanceMenuUI extends BasicMenuUI implements
057: SubstanceMenu {
058: /**
059: * For rollover effects - enhancement 93.
060: */
061: protected MouseListener substanceMouseListener;
062:
063: /**
064: * Listener for fade animations.
065: */
066: protected FadeStateListener substanceFadeStateListener;
067:
068: /**
069: * Listens on all changes to the underlying menu item.
070: */
071: protected MenuPropertyListener substanceMenuPropertyListener;
072:
073: /**
074: * Property change listener. Listens on changes to
075: * {@link AbstractButton#MODEL_CHANGED_PROPERTY} property.
076: */
077: protected PropertyChangeListener substancePropertyListener;
078:
079: /**
080: * For rollover effects - enhancement 93.
081: */
082: protected FocusListener substanceFocusListener;
083:
084: /*
085: * (non-Javadoc)
086: *
087: * @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent)
088: */
089: public static ComponentUI createUI(JComponent c) {
090: return new SubstanceMenuUI();
091: }
092:
093: @Override
094: protected void installDefaults() {
095: super .installDefaults();
096: this .menuItem.setRolloverEnabled(true);
097:
098: this .arrowIcon = new MenuArrowIcon(this .menuItem);
099:
100: this .defaultTextIconGap = SubstanceSizeUtils
101: .getMenuTextIconGap(SubstanceSizeUtils
102: .getComponentFontSize(this .menuItem));
103: }
104:
105: @Override
106: protected void installListeners() {
107: super .installListeners();
108:
109: // Improving performance on big menus.
110: this .substanceMenuPropertyListener = new MenuPropertyListener(
111: this .menuItem);
112: this .substanceMenuPropertyListener.install();
113:
114: // fix for enhancement 93 - rollover effects on menu items
115: this .substanceMouseListener = new MouseAdapter() {
116: // fix for defect 93 - no rollover effects on menu
117: // items that are not in the selected path
118: private boolean toRepaint() {
119: MenuElement[] selectedMenuPath = MenuSelectionManager
120: .defaultManager().getSelectedPath();
121: for (MenuElement elem : selectedMenuPath) {
122: if (elem == SubstanceMenuUI.this .menuItem) {
123: return true;
124: }
125: }
126: return (selectedMenuPath.length == 0);
127: }
128:
129: @Override
130: public void mouseEntered(MouseEvent e) {
131: if (this .toRepaint()) {
132: SubstanceMenuUI.this .menuItem.getModel()
133: .setRollover(true);
134: SubstanceMenuUI.this .menuItem.repaint();
135: }
136: }
137:
138: @Override
139: public void mouseExited(MouseEvent e) {
140: if (this .toRepaint()) {
141: SubstanceMenuUI.this .menuItem.getModel()
142: .setRollover(false);
143: SubstanceMenuUI.this .menuItem.repaint();
144: }
145: }
146: };
147: this .menuItem.addMouseListener(this .substanceMouseListener);
148: this .substanceFocusListener = new FocusAdapter() {
149: // fix for defect 93 - no rollover effects on menu
150: // items that are not in the selected path
151: private boolean toRepaint() {
152: MenuElement[] selectedMenuPath = MenuSelectionManager
153: .defaultManager().getSelectedPath();
154: for (MenuElement elem : selectedMenuPath) {
155: if (elem == SubstanceMenuUI.this .menuItem) {
156: return true;
157: }
158: }
159: return (selectedMenuPath.length == 0);
160: }
161:
162: @Override
163: public void focusLost(FocusEvent e) {
164: if (toRepaint()) {
165: SubstanceMenuUI.this .menuItem.getModel()
166: .setRollover(false);
167: SubstanceMenuUI.this .menuItem.repaint();
168: }
169: }
170: };
171: this .menuItem.addFocusListener(this .substanceFocusListener);
172:
173: final Set<FadeKind> toIgnore = new HashSet<FadeKind>();
174: // toIgnore.add(FadeKind.ROLLOVER);
175: this .substanceFadeStateListener = new FadeStateListener(
176: this .menuItem, this .menuItem.getModel(),
177: SubstanceCoreUtilities.getFadeCallback(this .menuItem,
178: this .menuItem.getModel(), false, false,
179: this .menuItem), toIgnore);
180: this .substanceFadeStateListener.registerListeners();
181:
182: // this.menuItem.getModel().addChangeListener(new ChangeListener() {
183: // public void stateChanged(ChangeEvent e) {
184: // ButtonModel bm = menuItem.getModel();
185: // System.out.println(menuItem.getText() + " e:" + bm.isEnabled()
186: // + ":a:" + bm.isArmed() + ":r:" + bm.isRollover()
187: // + ":p:" + bm.isPressed() + ":s:" + bm.isSelected());
188: // }
189: // });
190:
191: this .substancePropertyListener = new PropertyChangeListener() {
192: public void propertyChange(PropertyChangeEvent evt) {
193: if (AbstractButton.MODEL_CHANGED_PROPERTY.equals(evt
194: .getPropertyName())) {
195: if (substanceFadeStateListener != null)
196: substanceFadeStateListener
197: .unregisterListeners();
198: substanceFadeStateListener = new FadeStateListener(
199: menuItem, menuItem.getModel(),
200: SubstanceCoreUtilities.getFadeCallback(
201: menuItem, menuItem.getModel(),
202: false, false, menuItem), toIgnore);
203: substanceFadeStateListener.registerListeners();
204: }
205: if ("font".equals(evt.getPropertyName())) {
206: SwingUtilities.invokeLater(new Runnable() {
207: public void run() {
208: menuItem.updateUI();
209: }
210: });
211: }
212: }
213: };
214: this .menuItem
215: .addPropertyChangeListener(this .substancePropertyListener);
216: }
217:
218: /*
219: * (non-Javadoc)
220: *
221: * @see javax.swing.plaf.basic.BasicMenuUI#uninstallListeners()
222: */
223: @Override
224: protected void uninstallListeners() {
225: super .uninstallListeners();
226:
227: // Improving performance on big menus.
228: this .substanceMenuPropertyListener.uninstall();
229: this .substanceMenuPropertyListener = null;
230:
231: // fix for enhancement 93 - rollover effects on menu items
232: this .menuItem.removeMouseListener(this .substanceMouseListener);
233: this .substanceMouseListener = null;
234: this .menuItem.removeFocusListener(this .substanceFocusListener);
235: this .substanceFocusListener = null;
236:
237: this .menuItem
238: .removePropertyChangeListener(this .substancePropertyListener);
239: this .substancePropertyListener = null;
240:
241: this .substanceFadeStateListener.unregisterListeners();
242: this .substanceFadeStateListener = null;
243: }
244:
245: /*
246: * (non-Javadoc)
247: *
248: * @see org.jvnet.substance.SubstanceMenu#getAssociatedMenuItem()
249: */
250: public JMenuItem getAssociatedMenuItem() {
251: return this .menuItem;
252: }
253:
254: /*
255: * (non-Javadoc)
256: *
257: * @see org.jvnet.substance.SubstanceMenu#getAcceleratorFont()
258: */
259: public Font getAcceleratorFont() {
260: return this .acceleratorFont;
261: }
262:
263: /*
264: * (non-Javadoc)
265: *
266: * @see org.jvnet.substance.SubstanceMenu#getArrowIcon()
267: */
268: public Icon getArrowIcon() {
269: return this .arrowIcon;
270: }
271:
272: /*
273: * (non-Javadoc)
274: *
275: * @see org.jvnet.substance.SubstanceMenu#getCheckIcon()
276: */
277: public Icon getCheckIcon() {
278: return null;
279: }
280:
281: /*
282: * (non-Javadoc)
283: *
284: * @see org.jvnet.substance.SubstanceMenu#getDefaultTextIconGap()
285: */
286: public int getDefaultTextIconGap() {
287: return this .defaultTextIconGap;
288: }
289:
290: /*
291: * (non-Javadoc)
292: *
293: * @see javax.swing.plaf.basic.BasicMenuItemUI#getPreferredMenuItemSize(javax.swing.JComponent,
294: * javax.swing.Icon, javax.swing.Icon, int)
295: */
296: @Override
297: protected Dimension getPreferredMenuItemSize(JComponent c,
298: Icon checkIcon, Icon arrowIcon, int defaultTextIconGap) {
299: Dimension super Dim = super .getPreferredMenuItemSize(c,
300: checkIcon, arrowIcon, defaultTextIconGap);
301:
302: if (MenuUtilities.getPopupLayoutMetrics(menuItem) != null) {
303: return new Dimension(MenuUtilities
304: .getPreferredWidth(menuItem), super Dim.height);
305: }
306:
307: return super Dim;
308: }
309:
310: /*
311: * (non-Javadoc)
312: *
313: * @see javax.swing.plaf.basic.BasicMenuItemUI#paintMenuItem(java.awt.Graphics,
314: * javax.swing.JComponent, javax.swing.Icon, javax.swing.Icon,
315: * java.awt.Color, java.awt.Color, int)
316: */
317: @Override
318: protected void paintMenuItem(Graphics g, JComponent c,
319: Icon checkIcon, Icon arrowIcon, Color background,
320: Color foreground, int defaultTextIconGap) {
321: MenuUtilities.paintMenuItem(g, menuItem, checkIcon, arrowIcon,
322: defaultTextIconGap);
323: }
324: }
|