001: /*
002: * @(#)EclipseJideSplitButtonUI.java
003: *
004: * Copyright 2002 - 2004 JIDE Software Inc. All rights reserved.
005: */
006:
007: package com.jidesoft.plaf.eclipse;
008:
009: import com.jidesoft.icons.IconsFactory;
010: import com.jidesoft.plaf.UIDefaultsLookup;
011: import com.jidesoft.plaf.basic.LazyActionMap;
012: import com.jidesoft.plaf.basic.ThemePainter;
013: import com.jidesoft.plaf.basic.UIAction;
014: import com.jidesoft.swing.JideSplitButton;
015: import com.jidesoft.swing.JideSwingUtilities;
016:
017: import javax.swing.*;
018: import javax.swing.event.MouseInputListener;
019: import javax.swing.plaf.ComponentUI;
020: import javax.swing.plaf.basic.BasicGraphicsUtils;
021: import javax.swing.plaf.basic.BasicHTML;
022: import javax.swing.text.View;
023: import java.awt.*;
024: import java.awt.event.ActionEvent;
025: import java.awt.event.FocusEvent;
026: import java.awt.event.FocusListener;
027: import java.awt.event.MouseEvent;
028: import java.util.ArrayList;
029:
030: /**
031: * EclipseJideSplitButtonUI implementation.
032: */
033: public class EclipseJideSplitButtonUI extends EclipseMenuUI {
034:
035: protected ThemePainter _painter;
036:
037: protected Color _shadowColor;
038: protected Color _darkShadowColor;
039: protected Color _highlight;
040: protected Color _lightHighlightColor;
041:
042: protected int _splitButtonMargin = 12;
043: protected int _splitButtonMarginOnMenu = 20;
044:
045: private FocusListener _focusListener;
046:
047: private final static String propertyPrefix = "JideSplitButton";
048:
049: @Override
050: protected String getPropertyPrefix() {
051: return propertyPrefix;
052: }
053:
054: public static ComponentUI createUI(JComponent c) {
055: return new EclipseJideSplitButtonUI();
056: }
057:
058: @Override
059: protected void installDefaults() {
060: _painter = (ThemePainter) UIDefaultsLookup.get("Theme.painter");
061: _shadowColor = UIDefaultsLookup.getColor("controlShadow");
062: _darkShadowColor = UIDefaultsLookup.getColor("controlDkShadow");
063: _highlight = UIDefaultsLookup.getColor("controlHighlight");
064: _lightHighlightColor = UIDefaultsLookup
065: .getColor("controlLtHighlight");
066:
067: super .installDefaults();
068: }
069:
070: @Override
071: protected void uninstallDefaults() {
072: _painter = null;
073: _shadowColor = null;
074: _highlight = null;
075: _lightHighlightColor = null;
076: _darkShadowColor = null;
077:
078: super .uninstallDefaults();
079: }
080:
081: @Override
082: protected void installListeners() {
083: super .installListeners();
084: if (_focusListener == null) {
085: _focusListener = new FocusListener() {
086: public void focusGained(FocusEvent e) {
087: menuItem.repaint();
088: }
089:
090: public void focusLost(FocusEvent e) {
091: menuItem.repaint();
092: }
093: };
094: }
095: menuItem.addFocusListener(_focusListener);
096: }
097:
098: @Override
099: protected void uninstallListeners() {
100: super .uninstallListeners();
101: if (_focusListener != null) {
102: menuItem.removeFocusListener(_focusListener);
103: }
104: }
105:
106: /**
107: * Returns the ui that is of type <code>klass</code>, or null if
108: * one can not be found.
109: */
110: static Object getUIOfType(ComponentUI ui, Class klass) {
111: if (klass.isInstance(ui)) {
112: return ui;
113: }
114: return null;
115: }
116:
117: /**
118: * Returns the InputMap for condition <code>condition</code>. Called as
119: * part of <code>installKeyboardActions</code>.
120: */
121: public InputMap getInputMap(int condition, JComponent c) {
122: if (condition == JComponent.WHEN_FOCUSED) {
123: EclipseJideSplitButtonUI ui = (EclipseJideSplitButtonUI) getUIOfType(
124: ((JideSplitButton) c).getUI(),
125: EclipseJideSplitButtonUI.class);
126: if (ui != null) {
127: return (InputMap) UIDefaultsLookup.get(ui
128: .getPropertyPrefix()
129: + ".focusInputMap");
130: }
131: }
132: return null;
133: }
134:
135: @Override
136: protected void installKeyboardActions() {
137: super .installKeyboardActions();
138: AbstractButton b = menuItem;
139:
140: LazyActionMap.installLazyActionMap(b,
141: EclipseJideSplitButtonUI.class,
142: "JideSplitButton.actionMap");
143:
144: InputMap km = getInputMap(JComponent.WHEN_FOCUSED, b);
145:
146: SwingUtilities
147: .replaceUIInputMap(b, JComponent.WHEN_FOCUSED, km);
148: }
149:
150: @Override
151: protected void uninstallKeyboardActions() {
152: AbstractButton b = menuItem;
153: SwingUtilities.replaceUIInputMap(b,
154: JComponent.WHEN_IN_FOCUSED_WINDOW, null);
155: SwingUtilities.replaceUIInputMap(b, JComponent.WHEN_FOCUSED,
156: null);
157: SwingUtilities.replaceUIActionMap(b, null);
158: super .uninstallKeyboardActions();
159: }
160:
161: @Override
162: protected MouseInputListener createMouseInputListener(JComponent c) {
163: return new MouseInputHandler();
164: }
165:
166: @Override
167: protected void paintBackground(Graphics g, JMenuItem menuItem,
168: Color bgColor) {
169: ButtonModel model = menuItem.getModel();
170: int menuWidth = 0;
171: int menuHeight = 0;
172: if (JideSwingUtilities.getOrientationOf(menuItem) == SwingConstants.HORIZONTAL) {
173: menuWidth = menuItem.getWidth();
174: menuHeight = menuItem.getHeight();
175: } else {
176: menuWidth = menuItem.getHeight();
177: menuHeight = menuItem.getWidth();
178: }
179:
180: if (!((JMenu) menuItem).isTopLevelMenu()) {
181: super .paintBackground(g, menuItem, bgColor);
182: Color oldColor = g.getColor();
183: if (menuItem.isEnabled()) {
184: if (model.isArmed() || model.isPressed()
185: || isMouseOver()) {
186: g.setColor(selectionForeground);
187: g.drawLine(menuWidth - _splitButtonMarginOnMenu, 0,
188: menuWidth - _splitButtonMarginOnMenu,
189: menuHeight - 2);
190: JideSwingUtilities.paintArrow(g,
191: selectionForeground, menuWidth
192: - _splitButtonMarginOnMenu / 2 - 2,
193: menuHeight / 2 - 3, 7,
194: SwingConstants.VERTICAL);
195: } else {
196: g.setColor(menuItem.getForeground());
197: g.drawLine(menuWidth - _splitButtonMarginOnMenu, 0,
198: menuWidth - _splitButtonMarginOnMenu,
199: menuHeight - 2);
200: JideSwingUtilities.paintArrow(g, menuItem
201: .getForeground(), menuWidth
202: - _splitButtonMarginOnMenu / 2 - 2,
203: menuHeight / 2 - 3, 7,
204: SwingConstants.VERTICAL);
205: }
206: } else {
207: g
208: .setColor(UIDefaultsLookup
209: .getColor("controlDkShadow"));
210: g.drawLine(menuWidth - _splitButtonMarginOnMenu, 0,
211: menuWidth - _splitButtonMarginOnMenu,
212: menuHeight - 2);
213: JideSwingUtilities.paintArrow(g, UIDefaultsLookup
214: .getColor("controlDkShadow"), menuWidth
215: - _splitButtonMarginOnMenu / 2 - 2,
216: menuHeight / 2 - 3, 7, SwingConstants.VERTICAL);
217: }
218: g.setColor(oldColor);
219: return;
220: }
221:
222: if (menuItem.isOpaque()) {
223: Color oldColor = g.getColor();
224: if (menuItem.getParent() != null) {
225: g.setColor(menuItem.getParent().getBackground());
226: } else {
227: g.setColor(menuItem.getBackground());
228: }
229: g.fillRect(0, 0, menuWidth, menuHeight);
230: g.setColor(oldColor);
231: }
232:
233: if ((menuItem instanceof JMenu && model.isSelected())) {
234: // Draw a dark shadow border without bottom
235: getPainter().paintSelectedMenu(menuItem, g,
236: new Rectangle(0, 0, menuWidth, menuHeight),
237: JideSwingUtilities.getOrientationOf(menuItem),
238: ThemePainter.STATE_SELECTED);
239: } else if (model.isArmed() || model.isPressed()) {
240: // if (JideSwingUtilities.getOrientationOf(menuItem) == SwingConstants.HORIZONTAL) {
241: Rectangle rect = new Rectangle(0, 0, menuWidth
242: - _splitButtonMargin, menuHeight);
243: getPainter().paintButtonBackground(menuItem, g, rect,
244: JideSwingUtilities.getOrientationOf(menuItem),
245: ThemePainter.STATE_PRESSED);
246: rect = new Rectangle(menuWidth - _splitButtonMargin - 1
247: + getOffset(), 0, _splitButtonMargin - getOffset(),
248: menuHeight);
249: getPainter().paintButtonBackground(menuItem, g, rect,
250: JideSwingUtilities.getOrientationOf(menuItem),
251: ThemePainter.STATE_ROLLOVER);
252: } else {
253: if (isMouseOver() && model.isEnabled()) {
254: // Draw a line border with background
255: // if (JideSwingUtilities.getOrientationOf(menuItem) == SwingConstants.HORIZONTAL) {
256: Rectangle rect = new Rectangle(0, 0, menuWidth
257: - _splitButtonMargin, menuHeight);
258: getPainter().paintButtonBackground(menuItem, g, rect,
259: JideSwingUtilities.getOrientationOf(menuItem),
260: ThemePainter.STATE_ROLLOVER);
261: rect = new Rectangle(menuWidth - _splitButtonMargin - 1
262: + getOffset(), 0, _splitButtonMargin
263: - getOffset(), menuHeight);
264: getPainter().paintButtonBackground(menuItem, g, rect,
265: JideSwingUtilities.getOrientationOf(menuItem),
266: ThemePainter.STATE_ROLLOVER);
267: }
268: }
269:
270: if (menuItem.isEnabled()) {
271: JideSwingUtilities.paintArrow(g, menuItem.getForeground(),
272: menuWidth - 10, menuHeight / 2 - 1, 5,
273: SwingConstants.HORIZONTAL);
274: } else {
275: JideSwingUtilities.paintArrow(g, UIDefaultsLookup
276: .getColor("controlDkShadow"), menuWidth - 10,
277: menuHeight / 2 - 1, 5, SwingConstants.HORIZONTAL);
278: }
279: }
280:
281: protected class MouseInputHandler implements MouseInputListener {
282: public void mouseClicked(MouseEvent e) {
283: cancelMenuIfNecessary(e);
284: }
285:
286: /**
287: * Invoked when the mouse has been clicked on the menu. This
288: * method clears or sets the selection path of the
289: * MenuSelectionManager.
290: *
291: * @param e the mouse event
292: */
293: public void mousePressed(MouseEvent e) {
294: JMenu menu = (JMenu) menuItem;
295: if (!menu.isEnabled())
296: return;
297:
298: setMouseOver(true);
299:
300: if (!SwingUtilities.isLeftMouseButton(e)) {
301: return;
302: }
303: if (isClickOnButton(e, menu)) {
304: if (((JideSplitButton) menuItem).isButtonEnabled()) {
305: // click button
306: menu.getModel().setArmed(true);
307: menu.getModel().setPressed(true);
308: }
309: if (!menu.hasFocus() && menu.isRequestFocusEnabled()) {
310: menu.requestFocus();
311: }
312: } else {
313: downButtonPressed(menu);
314: }
315: }
316:
317: private boolean isClickOnButton(MouseEvent e, JMenu menu) {
318: if (((JideSplitButton) menu).isAlwaysDropdown()) {
319: return false;
320: }
321:
322: boolean clickOnDropDown = false;
323: int size = ((JMenu) menuItem).isTopLevelMenu() ? _splitButtonMargin
324: : _splitButtonMarginOnMenu;
325: if (JideSwingUtilities.getOrientationOf(menuItem) == SwingConstants.HORIZONTAL) {
326: if (e.getPoint().getX() < menu.getWidth() - size) {
327: clickOnDropDown = true;
328: }
329: } else {
330: if (e.getPoint().getY() < menu.getHeight() - size) {
331: clickOnDropDown = true;
332: }
333: }
334: return clickOnDropDown;
335: }
336:
337: /**
338: * Invoked when the mouse has been released on the menu. Delegates the
339: * mouse event to the MenuSelectionManager.
340: *
341: * @param e the mouse event
342: */
343: public void mouseReleased(MouseEvent e) {
344: if (!isMouseOver()) {
345: // these two lines order matters. In this order, it would not trigger actionPerformed.
346: menuItem.getModel().setArmed(false);
347: menuItem.getModel().setPressed(false);
348: }
349: cancelMenuIfNecessary(e);
350: }
351:
352: private void cancelMenuIfNecessary(MouseEvent e) {
353: JMenu menu = (JMenu) menuItem;
354: if (!menu.isEnabled())
355: return;
356: if (isClickOnButton(e, menu)) {
357: if (((JideSplitButton) menuItem).isButtonEnabled()) {
358: // click button
359: // these two lines order matters. In this order, it would trigger actionPerformed.
360: if (SwingUtilities.isLeftMouseButton(e)) {
361: menu.getModel().setPressed(false);
362: menu.getModel().setArmed(false);
363: } else {
364: menu.getModel().setArmed(false);
365: menu.getModel().setPressed(false);
366: }
367:
368: MenuSelectionManager manager = MenuSelectionManager
369: .defaultManager();
370: MenuElement[] menuElements = manager
371: .getSelectedPath();
372: for (int i = menuElements.length - 1; i >= 0; i--) {
373: MenuElement menuElement = menuElements[i];
374: if (menuElement instanceof JPopupMenu
375: && ((JPopupMenu) menuElement)
376: .isAncestorOf(menu)) {
377: menu.getModel().setRollover(false);
378: setMouseOver(false);
379: manager.clearSelectedPath();
380: }
381: }
382: }
383: } else {
384: // MenuSelectionManager manager =
385: // MenuSelectionManager.defaultManager();
386: // manager.processMouseEvent(e);
387: // if (!e.isConsumed())
388: // manager.clearSelectedPath();
389: }
390: }
391:
392: /**
393: * Invoked when the cursor enters the menu. This method sets the selected
394: * path for the MenuSelectionManager and handles the case
395: * in which a menu item is used to pop up an additional menu, as in a
396: * hierarchical menu system.
397: *
398: * @param e the mouse event; not used
399: */
400: public void mouseEntered(MouseEvent e) {
401: JMenu menu = (JMenu) menuItem;
402: if (!menu.isEnabled())
403: return;
404:
405: MenuSelectionManager manager = MenuSelectionManager
406: .defaultManager();
407: MenuElement selectedPath[] = manager.getSelectedPath();
408: if (!menu.isTopLevelMenu()) {
409: if (!(selectedPath.length > 0 && selectedPath[selectedPath.length - 1] == menu
410: .getPopupMenu())) {
411: if (menu.getDelay() == 0) {
412: appendPath(getPath(), menu.getPopupMenu());
413: } else {
414: manager.setSelectedPath(getPath());
415: setupPostTimer(menu);
416: }
417: }
418: } else {
419: if (selectedPath.length > 0
420: && selectedPath[0] == menu.getParent()) {
421: MenuElement newPath[] = new MenuElement[3];
422: // A top level menu's parent is by definition
423: // a JMenuBar
424: newPath[0] = (MenuElement) menu.getParent();
425: newPath[1] = menu;
426: newPath[2] = menu.getPopupMenu();
427: manager.setSelectedPath(newPath);
428: }
429: }
430:
431: if (!SwingUtilities.isLeftMouseButton(e)) {
432: setMouseOver(true);
433: }
434: menuItem.repaint();
435: }
436:
437: public void mouseExited(MouseEvent e) {
438: setMouseOver(false);
439: menuItem.repaint();
440: }
441:
442: /**
443: * Invoked when a mouse button is pressed on the menu and then dragged.
444: * Delegates the mouse event to the MenuSelectionManager.
445: *
446: * @param e the mouse event
447: * @see java.awt.event.MouseMotionListener#mouseDragged
448: */
449: public void mouseDragged(MouseEvent e) {
450: JMenu menu = (JMenu) menuItem;
451: if (!menu.isEnabled())
452: return;
453: MenuSelectionManager.defaultManager().processMouseEvent(e);
454: }
455:
456: public void mouseMoved(MouseEvent e) {
457: }
458: }
459:
460: @Override
461: public Dimension getMinimumSize(JComponent c) {
462: if (!(c instanceof JMenu) || !((JMenu) c).isTopLevelMenu()) {
463: return super .getMinimumSize(c);
464: }
465:
466: Dimension d = getPreferredSize(c);
467: View v = (View) c.getClientProperty(BasicHTML.propertyKey);
468: if (v != null) {
469: if (JideSwingUtilities.getOrientationOf(c) == SwingConstants.HORIZONTAL)
470: d.width -= v.getPreferredSpan(View.X_AXIS)
471: - v.getMinimumSpan(View.X_AXIS);
472: else
473: // TODO: not sure if this is correct
474: d.height -= v.getPreferredSpan(View.X_AXIS)
475: - v.getMinimumSpan(View.X_AXIS);
476: }
477:
478: return d;
479: }
480:
481: @Override
482: public Dimension getMaximumSize(JComponent c) {
483: if (!(c instanceof JMenu) || !((JMenu) c).isTopLevelMenu()) {
484: return super .getMaximumSize(c);
485: }
486:
487: Dimension d = getPreferredSize(c);
488: View v = (View) c.getClientProperty(BasicHTML.propertyKey);
489: if (v != null) {
490: if (JideSwingUtilities.getOrientationOf(c) == SwingConstants.HORIZONTAL)
491: d.width += v.getMaximumSpan(View.X_AXIS)
492: - v.getPreferredSpan(View.X_AXIS);
493: else
494: // TODO: not sure if this is correct
495: d.height += v.getMaximumSpan(View.X_AXIS)
496: - v.getPreferredSpan(View.X_AXIS);
497: }
498:
499: return d;
500: }
501:
502: @Override
503: public Dimension getPreferredSize(JComponent c) {
504: if (!(c instanceof JMenu) || !((JMenu) c).isTopLevelMenu()) {
505: return super .getPreferredSize(c);
506: }
507:
508: AbstractButton b = (AbstractButton) c;
509:
510: boolean isHorizontal = true;
511: if (JideSwingUtilities.getOrientationOf(c) == SwingConstants.VERTICAL) {
512: isHorizontal = false;
513: }
514:
515: // JDK PORTING HINT
516: // JDK1.3: No getIconTextGap, use defaultTextIconGap
517: Dimension d = BasicGraphicsUtils.getPreferredButtonSize(b,
518: defaultTextIconGap);
519: // d.width += b.getMargin().left + b.getMargin().right;
520: // d.height += b.getMargin().bottom + b.getMargin().top;
521:
522: int size = ((JMenu) menuItem).isTopLevelMenu() ? _splitButtonMargin
523: : _splitButtonMarginOnMenu;
524: d.width += size;
525:
526: if (isHorizontal)
527: return d;
528: else
529: return new Dimension(d.height, d.width); // swap width and height
530: }
531:
532: protected void paintIcon(JMenuItem b, Graphics g) {
533: ButtonModel model = b.getModel();
534:
535: // Paint the Icon
536: if (b.getIcon() != null) {
537: // rotate back since we don't want to paint icon in a rotated way.
538: if (JideSwingUtilities.getOrientationOf(b) == SwingConstants.VERTICAL) {
539: ((Graphics2D) g).translate(0, b.getWidth() - 1);
540: ((Graphics2D) g).rotate(-Math.PI / 2);
541: }
542: Icon icon;
543: if (!model.isEnabled()) {
544: icon = b.getDisabledIcon();
545: if (icon == null) {
546: icon = b.getIcon();
547: if (icon instanceof ImageIcon) {
548: icon = IconsFactory
549: .createGrayImage(((ImageIcon) icon)
550: .getImage());
551: } else {
552: icon = IconsFactory.createGrayImage(b, icon);
553: }
554: }
555: } else if (model.isPressed() && model.isArmed()) {
556: icon = b.getPressedIcon();
557: if (icon == null) {
558: // Use default icon
559: icon = b.getIcon();
560: }
561: } else {
562: icon = b.getIcon();
563: }
564:
565: if (icon != null) {
566: icon.paintIcon(b, g, iconRect.x, iconRect.y);
567: // if (model.isRollover() && !model.isPressed() && !model.isSelected()) {
568: // icon.paintIcon(b, g, iconRect.x, iconRect.y);
569: // }
570: // else {
571: // icon.paintIcon(b, g, iconRect.x, iconRect.y);
572: // }
573: }
574:
575: if (JideSwingUtilities.getOrientationOf(b) == SwingConstants.VERTICAL) {
576: ((Graphics2D) g).rotate(Math.PI / 2);
577: ((Graphics2D) g).translate(0, -b.getHeight() + 1);
578: }
579: }
580: }
581:
582: /**
583: * Actions for Buttons. Two type of action are supported:
584: * pressed: Moves the button to a pressed state
585: * released: Disarms the button.
586: */
587: private static class Actions extends UIAction {
588: private static final String PRESS = "pressed";
589: private static final String RELEASE = "released";
590: private static final String DOWN_PRESS = "downPressed";
591: private static final String DOWN_RELEASE = "downReleased";
592:
593: Actions(String name) {
594: super (name);
595: }
596:
597: public void actionPerformed(ActionEvent e) {
598: AbstractButton b = (AbstractButton) e.getSource();
599: String key = getName();
600:
601: // if isAlwaysDropDown it true, treat PRESS as DOWN_PRESS
602: if (PRESS.equals(key)
603: && ((JideSplitButton) b).isAlwaysDropdown()) {
604: key = DOWN_PRESS;
605: }
606:
607: if (PRESS.equals(key)) {
608: ButtonModel model = b.getModel();
609: model.setArmed(true);
610: model.setPressed(true);
611: if (!b.hasFocus()) {
612: b.requestFocus();
613: }
614: } else if (RELEASE.equals(key)) {
615: ButtonModel model = b.getModel();
616: model.setPressed(false);
617: model.setArmed(false);
618: } else if (DOWN_PRESS.equals(key)) {
619: downButtonPressed((JMenu) b);
620: } else if (DOWN_RELEASE.equals(key)) {
621: }
622: }
623:
624: @Override
625: public boolean isEnabled(Object sender) {
626: if (sender != null
627: && (sender instanceof AbstractButton)
628: && !((AbstractButton) sender).getModel()
629: .isEnabled()) {
630: return false;
631: } else {
632: return true;
633: }
634: }
635: }
636:
637: protected int getOffset() {
638: return 1;
639: }
640:
641: /**
642: * Populates Buttons actions.
643: */
644: public static void loadActionMap(LazyActionMap map) {
645: map.put(new Actions(Actions.PRESS));
646: map.put(new Actions(Actions.RELEASE));
647: map.put(new Actions(Actions.DOWN_PRESS));
648: map.put(new Actions(Actions.DOWN_RELEASE));
649: }
650:
651: protected static void downButtonPressed(JMenu menu) {
652: MenuSelectionManager manager = MenuSelectionManager
653: .defaultManager();
654: if (menu.isTopLevelMenu()) {
655: if (menu.isSelected()) {
656: manager.clearSelectedPath();
657: } else {
658: Container cnt = menu.getParent();
659: if (cnt != null && cnt instanceof MenuElement) {
660: ArrayList parents = new ArrayList();
661: while (cnt instanceof MenuElement) {
662: parents.add(0, cnt);
663: cnt = cnt.getParent();
664: }
665:
666: MenuElement me[] = new MenuElement[parents.size() + 1];
667: for (int i = 0; i < parents.size(); i++) {
668: Container container = (Container) parents
669: .get(i);
670: me[i] = (MenuElement) container;
671: }
672: me[parents.size()] = menu;
673: manager.setSelectedPath(me);
674: } else {
675: MenuElement me[] = new MenuElement[1];
676: me[0] = menu;
677: manager.setSelectedPath(me);
678: }
679: }
680: }
681:
682: MenuElement selectedPath[] = manager.getSelectedPath();
683: if (selectedPath.length > 0
684: && selectedPath[selectedPath.length - 1] != menu
685: .getPopupMenu()) {
686: if (menu.isTopLevelMenu() || menu.getDelay() == 0) {
687: appendPath(selectedPath, menu.getPopupMenu());
688: } else {
689: setupPostTimer(menu);
690: }
691: }
692: }
693: }
|