1 /*
   2  * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.java.swing.plaf.windows;
  27 
  28 import java.awt.*;
  29 import java.awt.event.MouseEvent;
  30 
  31 import javax.swing.plaf.ComponentUI;
  32 import javax.swing.plaf.basic.BasicMenuUI;
  33 import javax.swing.event.MouseInputListener;
  34 import javax.swing.*;
  35 
  36 import com.sun.java.swing.plaf.windows.TMSchema.Part;
  37 import com.sun.java.swing.plaf.windows.TMSchema.State;
  38 
  39 /**
  40  * Windows rendition of the component.
  41  * <p>
  42  * <strong>Warning:</strong>
  43  * Serialized objects of this class will not be compatible with
  44  * future Swing releases.  The current serialization support is appropriate
  45  * for short term storage or RMI between applications running the same
  46  * version of Swing.  A future release of Swing will provide support for
  47  * long term persistence.
  48  */
  49 public class WindowsMenuUI extends BasicMenuUI {
  50     protected Integer menuBarHeight;
  51     protected boolean hotTrackingOn;
  52 
  53     final WindowsMenuItemUIAccessor accessor =
  54         new WindowsMenuItemUIAccessor() {
  55 
  56             public JMenuItem getMenuItem() {
  57                 return menuItem;
  58             }
  59 
  60             public State getState(JMenuItem menu) {
  61                 State state = menu.isEnabled() ? State.NORMAL
  62                         : State.DISABLED;
  63                 ButtonModel model = menu.getModel();
  64                 if (model.isArmed() || model.isSelected()) {
  65                     state = (menu.isEnabled()) ? State.PUSHED
  66                             : State.DISABLEDPUSHED;
  67                 } else if (model.isRollover()
  68                            && ((JMenu) menu).isTopLevelMenu()) {
  69                     /*
  70                      * Only paint rollover if no other menu on menubar is
  71                      * selected
  72                      */
  73                     State stateTmp = state;
  74                     state = (menu.isEnabled()) ? State.HOT
  75                             : State.DISABLEDHOT;
  76                     for (MenuElement menuElement :
  77                         ((JMenuBar) menu.getParent()).getSubElements()) {
  78                         if (((JMenuItem) menuElement).isSelected()) {
  79                             state = stateTmp;
  80                             break;
  81                         }
  82                     }
  83                 }
  84 
  85                 //non top level menus have HOT state instead of PUSHED
  86                 if (!((JMenu) menu).isTopLevelMenu()) {
  87                     if (state == State.PUSHED) {
  88                         state = State.HOT;
  89                     } else if (state == State.DISABLEDPUSHED) {
  90                         state = State.DISABLEDHOT;
  91                     }
  92                 }
  93 
  94                 /*
  95                  * on Vista top level menu for non active frame looks disabled
  96                  */
  97                 if (((JMenu) menu).isTopLevelMenu() && WindowsMenuItemUI.isVistaPainting()) {
  98                     if (! WindowsMenuBarUI.isActive(menu)) {
  99                         state = State.DISABLED;
 100                     }
 101                 }
 102                 return state;
 103             }
 104 
 105             public Part getPart(JMenuItem menuItem) {
 106                 return ((JMenu) menuItem).isTopLevelMenu() ? Part.MP_BARITEM
 107                         : Part.MP_POPUPITEM;
 108             }
 109     };
 110     public static ComponentUI createUI(JComponent x) {
 111         return new WindowsMenuUI();
 112     }
 113 
 114     protected void installDefaults() {
 115         super.installDefaults();
 116         if (!WindowsLookAndFeel.isClassicWindows()) {
 117             menuItem.setRolloverEnabled(true);
 118         }
 119 
 120         menuBarHeight = (Integer)UIManager.getInt("MenuBar.height");
 121 
 122         Object obj      = UIManager.get("MenuBar.rolloverEnabled");
 123         hotTrackingOn = (obj instanceof Boolean) ? (Boolean)obj : true;
 124     }
 125 
 126     /**
 127      * Draws the background of the menu.
 128      * @since 1.4
 129      */
 130     protected void paintBackground(Graphics g, JMenuItem menuItem, Color bgColor) {
 131         if (WindowsMenuItemUI.isVistaPainting()) {
 132             WindowsMenuItemUI.paintBackground(accessor, g, menuItem, bgColor);
 133             return;
 134         }
 135 
 136         JMenu menu = (JMenu)menuItem;
 137         ButtonModel model = menu.getModel();
 138 
 139         // Use superclass method for the old Windows LAF,
 140         // for submenus, and for XP toplevel if selected or pressed
 141         if (WindowsLookAndFeel.isClassicWindows() ||
 142             !menu.isTopLevelMenu() ||
 143             (XPStyle.getXP() != null && (model.isArmed() || model.isSelected()))) {
 144 
 145             super.paintBackground(g, menu, bgColor);
 146             return;
 147         }
 148 
 149         Color oldColor = g.getColor();
 150         int menuWidth = menu.getWidth();
 151         int menuHeight = menu.getHeight();
 152 
 153         UIDefaults table = UIManager.getLookAndFeelDefaults();
 154         Color highlight = table.getColor("controlLtHighlight");
 155         Color shadow = table.getColor("controlShadow");
 156 
 157         g.setColor(menu.getBackground());
 158         g.fillRect(0,0, menuWidth, menuHeight);
 159 
 160         if (menu.isOpaque()) {
 161             if (model.isArmed() || model.isSelected()) {
 162                 // Draw a lowered bevel border
 163                 g.setColor(shadow);
 164                 g.drawLine(0,0, menuWidth - 1,0);
 165                 g.drawLine(0,0, 0,menuHeight - 2);
 166 
 167                 g.setColor(highlight);
 168                 g.drawLine(menuWidth - 1,0, menuWidth - 1,menuHeight - 2);
 169                 g.drawLine(0,menuHeight - 2, menuWidth - 1,menuHeight - 2);
 170             } else if (model.isRollover() && model.isEnabled()) {
 171                 // Only paint rollover if no other menu on menubar is selected
 172                 boolean otherMenuSelected = false;
 173                 MenuElement[] menus = ((JMenuBar)menu.getParent()).getSubElements();
 174                 for (int i = 0; i < menus.length; i++) {
 175                     if (((JMenuItem)menus[i]).isSelected()) {
 176                         otherMenuSelected = true;
 177                         break;
 178                     }
 179                 }
 180                 if (!otherMenuSelected) {
 181                     if (XPStyle.getXP() != null) {
 182                         g.setColor(selectionBackground); // Uses protected field.
 183                         g.fillRect(0, 0, menuWidth, menuHeight);
 184                     } else {
 185                         // Draw a raised bevel border
 186                         g.setColor(highlight);
 187                         g.drawLine(0,0, menuWidth - 1,0);
 188                         g.drawLine(0,0, 0,menuHeight - 2);
 189 
 190                         g.setColor(shadow);
 191                         g.drawLine(menuWidth - 1,0, menuWidth - 1,menuHeight - 2);
 192                         g.drawLine(0,menuHeight - 2, menuWidth - 1,menuHeight - 2);
 193                     }
 194                 }
 195             }
 196         }
 197         g.setColor(oldColor);
 198     }
 199 
 200     /**
 201      * Method which renders the text of the current menu item.
 202      *
 203      * @param g Graphics context
 204      * @param menuItem Current menu item to render
 205      * @param textRect Bounding rectangle to render the text.
 206      * @param text String to render
 207      * @since 1.4
 208      */
 209     protected void paintText(Graphics g, JMenuItem menuItem,
 210                              Rectangle textRect, String text) {
 211         if (WindowsMenuItemUI.isVistaPainting()) {
 212             WindowsMenuItemUI.paintText(accessor, g, menuItem, textRect, text);
 213             return;
 214         }
 215         JMenu menu = (JMenu)menuItem;
 216         ButtonModel model = menuItem.getModel();
 217         Color oldColor = g.getColor();
 218 
 219         // Only paint rollover if no other menu on menubar is selected
 220         boolean paintRollover = model.isRollover();
 221         if (paintRollover && menu.isTopLevelMenu()) {
 222             MenuElement[] menus = ((JMenuBar)menu.getParent()).getSubElements();
 223             for (int i = 0; i < menus.length; i++) {
 224                 if (((JMenuItem)menus[i]).isSelected()) {
 225                     paintRollover = false;
 226                     break;
 227                 }
 228             }
 229         }
 230 
 231         if ((model.isSelected() && (WindowsLookAndFeel.isClassicWindows() ||
 232                                     !menu.isTopLevelMenu())) ||
 233             (XPStyle.getXP() != null && (paintRollover ||
 234                                          model.isArmed() ||
 235                                          model.isSelected()))) {
 236             g.setColor(selectionForeground); // Uses protected field.
 237         }
 238 
 239         WindowsGraphicsUtils.paintText(g, menuItem, textRect, text, 0);
 240 
 241         g.setColor(oldColor);
 242     }
 243 
 244     protected MouseInputListener createMouseInputListener(JComponent c) {
 245         return new WindowsMouseInputHandler();
 246     }
 247 
 248     /**
 249      * This class implements a mouse handler that sets the rollover flag to
 250      * true when the mouse enters the menu and false when it exits.
 251      * @since 1.4
 252      */
 253     protected class WindowsMouseInputHandler extends BasicMenuUI.MouseInputHandler {
 254         public void mouseEntered(MouseEvent evt) {
 255             super.mouseEntered(evt);
 256 
 257             JMenu menu = (JMenu)evt.getSource();
 258             if (hotTrackingOn && menu.isTopLevelMenu() && menu.isRolloverEnabled()) {
 259                 menu.getModel().setRollover(true);
 260                 menuItem.repaint();
 261             }
 262         }
 263 
 264         public void mouseExited(MouseEvent evt) {
 265             super.mouseExited(evt);
 266 
 267             JMenu menu = (JMenu)evt.getSource();
 268             ButtonModel model = menu.getModel();
 269             if (menu.isRolloverEnabled()) {
 270                 model.setRollover(false);
 271                 menuItem.repaint();
 272             }
 273         }
 274     }
 275 
 276     protected Dimension getPreferredMenuItemSize(JComponent c,
 277                                                      Icon checkIcon,
 278                                                      Icon arrowIcon,
 279                                                      int defaultTextIconGap) {
 280 
 281         Dimension d = super.getPreferredMenuItemSize(c, checkIcon, arrowIcon,
 282                                                      defaultTextIconGap);
 283 
 284         // Note: When toolbar containers (rebars) are implemented, only do
 285         // this if the JMenuBar is not in a rebar (i.e. ignore the desktop
 286         // property win.menu.height if in a rebar.)
 287         if (c instanceof JMenu && ((JMenu)c).isTopLevelMenu() &&
 288             menuBarHeight != null && d.height < menuBarHeight) {
 289 
 290             d.height = menuBarHeight;
 291         }
 292 
 293         return d;
 294     }
 295 }