1 /* 2 * Copyright (c) 1997, 2008, 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 javax.swing.plaf.basic; 27 28 import java.awt.*; 29 import java.awt.event.*; 30 import java.beans.PropertyChangeEvent; 31 import java.beans.PropertyChangeListener; 32 33 import javax.swing.*; 34 import javax.swing.event.*; 35 import javax.swing.border.*; 36 import javax.swing.plaf.*; 37 import javax.swing.text.View; 38 39 import sun.swing.*; 40 41 /** 42 * BasicMenuItem implementation 43 * 44 * @author Georges Saab 45 * @author David Karlton 46 * @author Arnaud Weber 47 * @author Fredrik Lagerblad 48 */ 49 public class BasicMenuItemUI extends MenuItemUI 50 { 51 protected JMenuItem menuItem = null; 52 protected Color selectionBackground; 53 protected Color selectionForeground; 54 protected Color disabledForeground; 55 protected Color acceleratorForeground; 56 protected Color acceleratorSelectionForeground; 57 private String acceleratorDelimiter; 58 59 protected int defaultTextIconGap; 60 protected Font acceleratorFont; 61 62 protected MouseInputListener mouseInputListener; 63 protected MenuDragMouseListener menuDragMouseListener; 64 protected MenuKeyListener menuKeyListener; 65 /** 66 * <code>PropertyChangeListener</code> returned from 67 * <code>createPropertyChangeListener</code>. You should not 68 * need to access this field, rather if you want to customize the 69 * <code>PropertyChangeListener</code> override 70 * <code>createPropertyChangeListener</code>. 71 * 72 * @since 1.6 73 * @see #createPropertyChangeListener 74 */ 75 protected PropertyChangeListener propertyChangeListener; 76 // BasicMenuUI also uses this. 77 Handler handler; 78 79 protected Icon arrowIcon = null; 80 protected Icon checkIcon = null; 81 82 protected boolean oldBorderPainted; 83 84 /* diagnostic aids -- should be false for production builds. */ 85 private static final boolean TRACE = false; // trace creates and disposes 86 87 private static final boolean VERBOSE = false; // show reuse hits/misses 88 private static final boolean DEBUG = false; // show bad params, misc. 89 90 static void loadActionMap(LazyActionMap map) { 91 // NOTE: BasicMenuUI also calls into this method. 92 map.put(new Actions(Actions.CLICK)); 93 BasicLookAndFeel.installAudioActionMap(map); 94 } 95 96 public static ComponentUI createUI(JComponent c) { 97 return new BasicMenuItemUI(); 98 } 99 100 public void installUI(JComponent c) { 101 menuItem = (JMenuItem) c; 102 103 installDefaults(); 104 installComponents(menuItem); 105 installListeners(); 106 installKeyboardActions(); 107 } 108 109 110 protected void installDefaults() { 111 String prefix = getPropertyPrefix(); 112 113 acceleratorFont = UIManager.getFont("MenuItem.acceleratorFont"); 114 // use default if missing so that BasicMenuItemUI can be used in other 115 // LAFs like Nimbus 116 if (acceleratorFont == null) { 117 acceleratorFont = UIManager.getFont("MenuItem.font"); 118 } 119 120 Object opaque = UIManager.get(getPropertyPrefix() + ".opaque"); 121 if (opaque != null) { 122 LookAndFeel.installProperty(menuItem, "opaque", opaque); 123 } 124 else { 125 LookAndFeel.installProperty(menuItem, "opaque", Boolean.TRUE); 126 } 127 if(menuItem.getMargin() == null || 128 (menuItem.getMargin() instanceof UIResource)) { 129 menuItem.setMargin(UIManager.getInsets(prefix + ".margin")); 130 } 131 132 LookAndFeel.installProperty(menuItem, "iconTextGap", new Integer(4)); 133 defaultTextIconGap = menuItem.getIconTextGap(); 134 135 LookAndFeel.installBorder(menuItem, prefix + ".border"); 136 oldBorderPainted = menuItem.isBorderPainted(); 137 LookAndFeel.installProperty(menuItem, "borderPainted", 138 UIManager.getBoolean(prefix + ".borderPainted")); 139 LookAndFeel.installColorsAndFont(menuItem, 140 prefix + ".background", 141 prefix + ".foreground", 142 prefix + ".font"); 143 144 // MenuItem specific defaults 145 if (selectionBackground == null || 146 selectionBackground instanceof UIResource) { 147 selectionBackground = 148 UIManager.getColor(prefix + ".selectionBackground"); 149 } 150 if (selectionForeground == null || 151 selectionForeground instanceof UIResource) { 152 selectionForeground = 153 UIManager.getColor(prefix + ".selectionForeground"); 154 } 155 if (disabledForeground == null || 156 disabledForeground instanceof UIResource) { 157 disabledForeground = 158 UIManager.getColor(prefix + ".disabledForeground"); 159 } 160 if (acceleratorForeground == null || 161 acceleratorForeground instanceof UIResource) { 162 acceleratorForeground = 163 UIManager.getColor(prefix + ".acceleratorForeground"); 164 } 165 if (acceleratorSelectionForeground == null || 166 acceleratorSelectionForeground instanceof UIResource) { 167 acceleratorSelectionForeground = 168 UIManager.getColor(prefix + ".acceleratorSelectionForeground"); 169 } 170 // Get accelerator delimiter 171 acceleratorDelimiter = 172 UIManager.getString("MenuItem.acceleratorDelimiter"); 173 if (acceleratorDelimiter == null) { acceleratorDelimiter = "+"; } 174 // Icons 175 if (arrowIcon == null || 176 arrowIcon instanceof UIResource) { 177 arrowIcon = UIManager.getIcon(prefix + ".arrowIcon"); 178 } 179 if (checkIcon == null || 180 checkIcon instanceof UIResource) { 181 checkIcon = UIManager.getIcon(prefix + ".checkIcon"); 182 //In case of column layout, .checkIconFactory is defined for this UI, 183 //the icon is compatible with it and useCheckAndArrow() is true, 184 //then the icon is handled by the checkIcon. 185 boolean isColumnLayout = MenuItemLayoutHelper.isColumnLayout( 186 BasicGraphicsUtils.isLeftToRight(menuItem), menuItem); 187 if (isColumnLayout) { 188 MenuItemCheckIconFactory iconFactory = 189 (MenuItemCheckIconFactory) UIManager.get(prefix 190 + ".checkIconFactory"); 191 if (iconFactory != null 192 && MenuItemLayoutHelper.useCheckAndArrow(menuItem) 193 && iconFactory.isCompatible(checkIcon, prefix)) { 194 checkIcon = iconFactory.getIcon(menuItem); 195 } 196 } 197 } 198 } 199 200 /** 201 * @since 1.3 202 */ 203 protected void installComponents(JMenuItem menuItem){ 204 BasicHTML.updateRenderer(menuItem, menuItem.getText()); 205 } 206 207 protected String getPropertyPrefix() { 208 return "MenuItem"; 209 } 210 211 protected void installListeners() { 212 if ((mouseInputListener = createMouseInputListener(menuItem)) != null) { 213 menuItem.addMouseListener(mouseInputListener); 214 menuItem.addMouseMotionListener(mouseInputListener); 215 } 216 if ((menuDragMouseListener = createMenuDragMouseListener(menuItem)) != null) { 217 menuItem.addMenuDragMouseListener(menuDragMouseListener); 218 } 219 if ((menuKeyListener = createMenuKeyListener(menuItem)) != null) { 220 menuItem.addMenuKeyListener(menuKeyListener); 221 } 222 if ((propertyChangeListener = createPropertyChangeListener(menuItem)) != null) { 223 menuItem.addPropertyChangeListener(propertyChangeListener); 224 } 225 } 226 227 protected void installKeyboardActions() { 228 installLazyActionMap(); 229 updateAcceleratorBinding(); 230 } 231 232 void installLazyActionMap() { 233 LazyActionMap.installLazyActionMap(menuItem, BasicMenuItemUI.class, 234 getPropertyPrefix() + ".actionMap"); 235 } 236 237 public void uninstallUI(JComponent c) { 238 menuItem = (JMenuItem)c; 239 uninstallDefaults(); 240 uninstallComponents(menuItem); 241 uninstallListeners(); 242 uninstallKeyboardActions(); 243 MenuItemLayoutHelper.clearUsedParentClientProperties(menuItem); 244 menuItem = null; 245 } 246 247 248 protected void uninstallDefaults() { 249 LookAndFeel.uninstallBorder(menuItem); 250 LookAndFeel.installProperty(menuItem, "borderPainted", oldBorderPainted); 251 if (menuItem.getMargin() instanceof UIResource) 252 menuItem.setMargin(null); 253 if (arrowIcon instanceof UIResource) 254 arrowIcon = null; 255 if (checkIcon instanceof UIResource) 256 checkIcon = null; 257 } 258 259 /** 260 * @since 1.3 261 */ 262 protected void uninstallComponents(JMenuItem menuItem){ 263 BasicHTML.updateRenderer(menuItem, ""); 264 } 265 266 protected void uninstallListeners() { 267 if (mouseInputListener != null) { 268 menuItem.removeMouseListener(mouseInputListener); 269 menuItem.removeMouseMotionListener(mouseInputListener); 270 } 271 if (menuDragMouseListener != null) { 272 menuItem.removeMenuDragMouseListener(menuDragMouseListener); 273 } 274 if (menuKeyListener != null) { 275 menuItem.removeMenuKeyListener(menuKeyListener); 276 } 277 if (propertyChangeListener != null) { 278 menuItem.removePropertyChangeListener(propertyChangeListener); 279 } 280 281 mouseInputListener = null; 282 menuDragMouseListener = null; 283 menuKeyListener = null; 284 propertyChangeListener = null; 285 handler = null; 286 } 287 288 protected void uninstallKeyboardActions() { 289 SwingUtilities.replaceUIActionMap(menuItem, null); 290 SwingUtilities.replaceUIInputMap(menuItem, JComponent. 291 WHEN_IN_FOCUSED_WINDOW, null); 292 } 293 294 protected MouseInputListener createMouseInputListener(JComponent c) { 295 return getHandler(); 296 } 297 298 protected MenuDragMouseListener createMenuDragMouseListener(JComponent c) { 299 return getHandler(); 300 } 301 302 protected MenuKeyListener createMenuKeyListener(JComponent c) { 303 return null; 304 } 305 306 /** 307 * Creates a <code>PropertyChangeListener</code> which will be added to 308 * the menu item. 309 * If this method returns null then it will not be added to the menu item. 310 * 311 * @return an instance of a <code>PropertyChangeListener</code> or null 312 * @since 1.6 313 */ 314 protected PropertyChangeListener 315 createPropertyChangeListener(JComponent c) { 316 return getHandler(); 317 } 318 319 Handler getHandler() { 320 if (handler == null) { 321 handler = new Handler(); 322 } 323 return handler; 324 } 325 326 InputMap createInputMap(int condition) { 327 if (condition == JComponent.WHEN_IN_FOCUSED_WINDOW) { 328 return new ComponentInputMapUIResource(menuItem); 329 } 330 return null; 331 } 332 333 void updateAcceleratorBinding() { 334 KeyStroke accelerator = menuItem.getAccelerator(); 335 InputMap windowInputMap = SwingUtilities.getUIInputMap( 336 menuItem, JComponent.WHEN_IN_FOCUSED_WINDOW); 337 338 if (windowInputMap != null) { 339 windowInputMap.clear(); 340 } 341 if (accelerator != null) { 342 if (windowInputMap == null) { 343 windowInputMap = createInputMap(JComponent. 344 WHEN_IN_FOCUSED_WINDOW); 345 SwingUtilities.replaceUIInputMap(menuItem, 346 JComponent.WHEN_IN_FOCUSED_WINDOW, windowInputMap); 347 } 348 windowInputMap.put(accelerator, "doClick"); 349 } 350 } 351 352 public Dimension getMinimumSize(JComponent c) { 353 Dimension d = null; 354 View v = (View) c.getClientProperty(BasicHTML.propertyKey); 355 if (v != null) { 356 d = getPreferredSize(c); 357 d.width -= v.getPreferredSpan(View.X_AXIS) - v.getMinimumSpan(View.X_AXIS); 358 } 359 return d; 360 } 361 362 public Dimension getPreferredSize(JComponent c) { 363 return getPreferredMenuItemSize(c, 364 checkIcon, 365 arrowIcon, 366 defaultTextIconGap); 367 } 368 369 public Dimension getMaximumSize(JComponent c) { 370 Dimension d = null; 371 View v = (View) c.getClientProperty(BasicHTML.propertyKey); 372 if (v != null) { 373 d = getPreferredSize(c); 374 d.width += v.getMaximumSpan(View.X_AXIS) - v.getPreferredSpan(View.X_AXIS); 375 } 376 return d; 377 } 378 379 protected Dimension getPreferredMenuItemSize(JComponent c, 380 Icon checkIcon, 381 Icon arrowIcon, 382 int defaultTextIconGap) { 383 384 // The method also determines the preferred width of the 385 // parent popup menu (through DefaultMenuLayout class). 386 // The menu width equals to the maximal width 387 // among child menu items. 388 389 // Menu item width will be a sum of the widest check icon, label, 390 // arrow icon and accelerator text among neighbor menu items. 391 // For the latest menu item we will know the maximal widths exactly. 392 // It will be the widest menu item and it will determine 393 // the width of the parent popup menu. 394 395 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 396 // There is a conceptual problem: if user sets preferred size manually 397 // for a menu item, this method won't be called for it 398 // (see JComponent.getPreferredSize()), 399 // maximal widths won't be calculated, other menu items won't be able 400 // to take them into account and will be layouted in such a way, 401 // as there is no the item with manual preferred size. 402 // But after the first paint() method call, all maximal widths 403 // will be correctly calculated and layout of some menu items 404 // can be changed. For example, it can cause a shift of 405 // the icon and text when user points a menu item by mouse. 406 407 JMenuItem mi = (JMenuItem) c; 408 MenuItemLayoutHelper lh = new MenuItemLayoutHelper(mi, checkIcon, 409 arrowIcon, MenuItemLayoutHelper.createMaxRect(), defaultTextIconGap, 410 acceleratorDelimiter, BasicGraphicsUtils.isLeftToRight(mi), 411 mi.getFont(), acceleratorFont, 412 MenuItemLayoutHelper.useCheckAndArrow(menuItem), 413 getPropertyPrefix()); 414 415 Dimension result = new Dimension(); 416 417 // Calculate the result width 418 result.width = lh.getLeadingGap(); 419 MenuItemLayoutHelper.addMaxWidth(lh.getCheckSize(), 420 lh.getAfterCheckIconGap(), result); 421 // Take into account mimimal text offset. 422 if ((!lh.isTopLevelMenu()) 423 && (lh.getMinTextOffset() > 0) 424 && (result.width < lh.getMinTextOffset())) { 425 result.width = lh.getMinTextOffset(); 426 } 427 MenuItemLayoutHelper.addMaxWidth(lh.getLabelSize(), lh.getGap(), result); 428 MenuItemLayoutHelper.addMaxWidth(lh.getAccSize(), lh.getGap(), result); 429 MenuItemLayoutHelper.addMaxWidth(lh.getArrowSize(), lh.getGap(), result); 430 431 // Calculate the result height 432 result.height = MenuItemLayoutHelper.max(lh.getCheckSize().getHeight(), 433 lh.getLabelSize().getHeight(), lh.getAccSize().getHeight(), 434 lh.getArrowSize().getHeight()); 435 436 // Take into account menu item insets 437 Insets insets = lh.getMenuItem().getInsets(); 438 if(insets != null) { 439 result.width += insets.left + insets.right; 440 result.height += insets.top + insets.bottom; 441 } 442 443 // if the width is even, bump it up one. This is critical 444 // for the focus dash line to draw properly 445 if(result.width%2 == 0) { 446 result.width++; 447 } 448 449 // if the height is even, bump it up one. This is critical 450 // for the text to center properly 451 if(result.height%2 == 0 452 && Boolean.TRUE != 453 UIManager.get(getPropertyPrefix() + ".evenHeight")) { 454 result.height++; 455 } 456 457 return result; 458 } 459 460 /** 461 * We draw the background in paintMenuItem() 462 * so override update (which fills the background of opaque 463 * components by default) to just call paint(). 464 * 465 */ 466 public void update(Graphics g, JComponent c) { 467 paint(g, c); 468 } 469 470 public void paint(Graphics g, JComponent c) { 471 paintMenuItem(g, c, checkIcon, arrowIcon, 472 selectionBackground, selectionForeground, 473 defaultTextIconGap); 474 } 475 476 protected void paintMenuItem(Graphics g, JComponent c, 477 Icon checkIcon, Icon arrowIcon, 478 Color background, Color foreground, 479 int defaultTextIconGap) { 480 // Save original graphics font and color 481 Font holdf = g.getFont(); 482 Color holdc = g.getColor(); 483 484 JMenuItem mi = (JMenuItem) c; 485 g.setFont(mi.getFont()); 486 487 Rectangle viewRect = new Rectangle(0, 0, mi.getWidth(), mi.getHeight()); 488 applyInsets(viewRect, mi.getInsets()); 489 490 MenuItemLayoutHelper lh = new MenuItemLayoutHelper(mi, checkIcon, 491 arrowIcon, viewRect, defaultTextIconGap, acceleratorDelimiter, 492 BasicGraphicsUtils.isLeftToRight(mi), mi.getFont(), 493 acceleratorFont, MenuItemLayoutHelper.useCheckAndArrow(menuItem), 494 getPropertyPrefix()); 495 MenuItemLayoutHelper.LayoutResult lr = lh.layoutMenuItem(); 496 497 paintBackground(g, mi, background); 498 paintCheckIcon(g, lh, lr, holdc, foreground); 499 paintIcon(g, lh, lr, holdc); 500 paintText(g, lh, lr); 501 paintAccText(g, lh, lr); 502 paintArrowIcon(g, lh, lr, foreground); 503 504 // Restore original graphics font and color 505 g.setColor(holdc); 506 g.setFont(holdf); 507 } 508 509 private void paintIcon(Graphics g, MenuItemLayoutHelper lh, 510 MenuItemLayoutHelper.LayoutResult lr, Color holdc) { 511 if (lh.getIcon() != null) { 512 Icon icon; 513 ButtonModel model = lh.getMenuItem().getModel(); 514 if (!model.isEnabled()) { 515 icon = lh.getMenuItem().getDisabledIcon(); 516 } else if (model.isPressed() && model.isArmed()) { 517 icon = lh.getMenuItem().getPressedIcon(); 518 if (icon == null) { 519 // Use default icon 520 icon = lh.getMenuItem().getIcon(); 521 } 522 } else { 523 icon = lh.getMenuItem().getIcon(); 524 } 525 526 if (icon != null) { 527 icon.paintIcon(lh.getMenuItem(), g, lr.getIconRect().x, 528 lr.getIconRect().y); 529 g.setColor(holdc); 530 } 531 } 532 } 533 534 private void paintCheckIcon(Graphics g, MenuItemLayoutHelper lh, 535 MenuItemLayoutHelper.LayoutResult lr, 536 Color holdc, Color foreground) { 537 if (lh.getCheckIcon() != null) { 538 ButtonModel model = lh.getMenuItem().getModel(); 539 if (model.isArmed() || (lh.getMenuItem() instanceof JMenu 540 && model.isSelected())) { 541 g.setColor(foreground); 542 } else { 543 g.setColor(holdc); 544 } 545 if (lh.useCheckAndArrow()) { 546 lh.getCheckIcon().paintIcon(lh.getMenuItem(), g, 547 lr.getCheckRect().x, lr.getCheckRect().y); 548 } 549 g.setColor(holdc); 550 } 551 } 552 553 private void paintAccText(Graphics g, MenuItemLayoutHelper lh, 554 MenuItemLayoutHelper.LayoutResult lr) { 555 if (!lh.getAccText().equals("")) { 556 ButtonModel model = lh.getMenuItem().getModel(); 557 g.setFont(lh.getAccFontMetrics().getFont()); 558 if (!model.isEnabled()) { 559 // *** paint the accText disabled 560 if (disabledForeground != null) { 561 g.setColor(disabledForeground); 562 SwingUtilities2.drawString(lh.getMenuItem(), g, 563 lh.getAccText(), lr.getAccRect().x, 564 lr.getAccRect().y + lh.getAccFontMetrics().getAscent()); 565 } else { 566 g.setColor(lh.getMenuItem().getBackground().brighter()); 567 SwingUtilities2.drawString(lh.getMenuItem(), g, 568 lh.getAccText(), lr.getAccRect().x, 569 lr.getAccRect().y + lh.getAccFontMetrics().getAscent()); 570 g.setColor(lh.getMenuItem().getBackground().darker()); 571 SwingUtilities2.drawString(lh.getMenuItem(), g, 572 lh.getAccText(), lr.getAccRect().x - 1, 573 lr.getAccRect().y + lh.getFontMetrics().getAscent() - 1); 574 } 575 } else { 576 // *** paint the accText normally 577 if (model.isArmed() 578 || (lh.getMenuItem() instanceof JMenu 579 && model.isSelected())) { 580 g.setColor(acceleratorSelectionForeground); 581 } else { 582 g.setColor(acceleratorForeground); 583 } 584 SwingUtilities2.drawString(lh.getMenuItem(), g, lh.getAccText(), 585 lr.getAccRect().x, lr.getAccRect().y + 586 lh.getAccFontMetrics().getAscent()); 587 } 588 } 589 } 590 591 private void paintText(Graphics g, MenuItemLayoutHelper lh, 592 MenuItemLayoutHelper.LayoutResult lr) { 593 if (!lh.getText().equals("")) { 594 if (lh.getHtmlView() != null) { 595 // Text is HTML 596 lh.getHtmlView().paint(g, lr.getTextRect()); 597 } else { 598 // Text isn't HTML 599 paintText(g, lh.getMenuItem(), lr.getTextRect(), lh.getText()); 600 } 601 } 602 } 603 604 private void paintArrowIcon(Graphics g, MenuItemLayoutHelper lh, 605 MenuItemLayoutHelper.LayoutResult lr, 606 Color foreground) { 607 if (lh.getArrowIcon() != null) { 608 ButtonModel model = lh.getMenuItem().getModel(); 609 if (model.isArmed() || (lh.getMenuItem() instanceof JMenu 610 && model.isSelected())) { 611 g.setColor(foreground); 612 } 613 if (lh.useCheckAndArrow()) { 614 lh.getArrowIcon().paintIcon(lh.getMenuItem(), g, 615 lr.getArrowRect().x, lr.getArrowRect().y); 616 } 617 } 618 } 619 620 private void applyInsets(Rectangle rect, Insets insets) { 621 if(insets != null) { 622 rect.x += insets.left; 623 rect.y += insets.top; 624 rect.width -= (insets.right + rect.x); 625 rect.height -= (insets.bottom + rect.y); 626 } 627 } 628 629 /** 630 * Draws the background of the menu item. 631 * 632 * @param g the paint graphics 633 * @param menuItem menu item to be painted 634 * @param bgColor selection background color 635 * @since 1.4 636 */ 637 protected void paintBackground(Graphics g, JMenuItem menuItem, Color bgColor) { 638 ButtonModel model = menuItem.getModel(); 639 Color oldColor = g.getColor(); 640 int menuWidth = menuItem.getWidth(); 641 int menuHeight = menuItem.getHeight(); 642 643 if(menuItem.isOpaque()) { 644 if (model.isArmed()|| (menuItem instanceof JMenu && model.isSelected())) { 645 g.setColor(bgColor); 646 g.fillRect(0,0, menuWidth, menuHeight); 647 } else { 648 g.setColor(menuItem.getBackground()); 649 g.fillRect(0,0, menuWidth, menuHeight); 650 } 651 g.setColor(oldColor); 652 } 653 else if (model.isArmed() || (menuItem instanceof JMenu && 654 model.isSelected())) { 655 g.setColor(bgColor); 656 g.fillRect(0,0, menuWidth, menuHeight); 657 g.setColor(oldColor); 658 } 659 } 660 661 /** 662 * Renders the text of the current menu item. 663 * <p> 664 * @param g graphics context 665 * @param menuItem menu item to render 666 * @param textRect bounding rectangle for rendering the text 667 * @param text string to render 668 * @since 1.4 669 */ 670 protected void paintText(Graphics g, JMenuItem menuItem, Rectangle textRect, String text) { 671 ButtonModel model = menuItem.getModel(); 672 FontMetrics fm = SwingUtilities2.getFontMetrics(menuItem, g); 673 int mnemIndex = menuItem.getDisplayedMnemonicIndex(); 674 675 if(!model.isEnabled()) { 676 // *** paint the text disabled 677 if ( UIManager.get("MenuItem.disabledForeground") instanceof Color ) { 678 g.setColor( UIManager.getColor("MenuItem.disabledForeground") ); 679 SwingUtilities2.drawStringUnderlineCharAt(menuItem, g,text, 680 mnemIndex, textRect.x, textRect.y + fm.getAscent()); 681 } else { 682 g.setColor(menuItem.getBackground().brighter()); 683 SwingUtilities2.drawStringUnderlineCharAt(menuItem, g, text, 684 mnemIndex, textRect.x, textRect.y + fm.getAscent()); 685 g.setColor(menuItem.getBackground().darker()); 686 SwingUtilities2.drawStringUnderlineCharAt(menuItem, g,text, 687 mnemIndex, textRect.x - 1, textRect.y + 688 fm.getAscent() - 1); 689 } 690 } else { 691 // *** paint the text normally 692 if (model.isArmed()|| (menuItem instanceof JMenu && model.isSelected())) { 693 g.setColor(selectionForeground); // Uses protected field. 694 } 695 SwingUtilities2.drawStringUnderlineCharAt(menuItem, g,text, 696 mnemIndex, textRect.x, textRect.y + fm.getAscent()); 697 } 698 } 699 700 public MenuElement[] getPath() { 701 MenuSelectionManager m = MenuSelectionManager.defaultManager(); 702 MenuElement oldPath[] = m.getSelectedPath(); 703 MenuElement newPath[]; 704 int i = oldPath.length; 705 if (i == 0) 706 return new MenuElement[0]; 707 Component parent = menuItem.getParent(); 708 if (oldPath[i-1].getComponent() == parent) { 709 // The parent popup menu is the last so far 710 newPath = new MenuElement[i+1]; 711 System.arraycopy(oldPath, 0, newPath, 0, i); 712 newPath[i] = menuItem; 713 } else { 714 // A sibling menuitem is the current selection 715 // 716 // This probably needs to handle 'exit submenu into 717 // a menu item. Search backwards along the current 718 // selection until you find the parent popup menu, 719 // then copy up to that and add yourself... 720 int j; 721 for (j = oldPath.length-1; j >= 0; j--) { 722 if (oldPath[j].getComponent() == parent) 723 break; 724 } 725 newPath = new MenuElement[j+2]; 726 System.arraycopy(oldPath, 0, newPath, 0, j+1); 727 newPath[j+1] = menuItem; 728 /* 729 System.out.println("Sibling condition -- "); 730 System.out.println("Old array : "); 731 printMenuElementArray(oldPath, false); 732 System.out.println("New array : "); 733 printMenuElementArray(newPath, false); 734 */ 735 } 736 return newPath; 737 } 738 739 void printMenuElementArray(MenuElement path[], boolean dumpStack) { 740 System.out.println("Path is("); 741 int i, j; 742 for(i=0,j=path.length; i<j ;i++){ 743 for (int k=0; k<=i; k++) 744 System.out.print(" "); 745 MenuElement me = (MenuElement) path[i]; 746 if(me instanceof JMenuItem) 747 System.out.println(((JMenuItem)me).getText() + ", "); 748 else if (me == null) 749 System.out.println("NULL , "); 750 else 751 System.out.println("" + me + ", "); 752 } 753 System.out.println(")"); 754 755 if (dumpStack == true) 756 Thread.dumpStack(); 757 } 758 protected class MouseInputHandler implements MouseInputListener { 759 // NOTE: This class exists only for backward compatability. All 760 // its functionality has been moved into Handler. If you need to add 761 // new functionality add it to the Handler, but make sure this 762 // class calls into the Handler. 763 764 public void mouseClicked(MouseEvent e) { 765 getHandler().mouseClicked(e); 766 } 767 public void mousePressed(MouseEvent e) { 768 getHandler().mousePressed(e); 769 } 770 public void mouseReleased(MouseEvent e) { 771 getHandler().mouseReleased(e); 772 } 773 public void mouseEntered(MouseEvent e) { 774 getHandler().mouseEntered(e); 775 } 776 public void mouseExited(MouseEvent e) { 777 getHandler().mouseExited(e); 778 } 779 public void mouseDragged(MouseEvent e) { 780 getHandler().mouseDragged(e); 781 } 782 public void mouseMoved(MouseEvent e) { 783 getHandler().mouseMoved(e); 784 } 785 } 786 787 788 private static class Actions extends UIAction { 789 private static final String CLICK = "doClick"; 790 791 Actions(String key) { 792 super(key); 793 } 794 795 public void actionPerformed(ActionEvent e) { 796 JMenuItem mi = (JMenuItem)e.getSource(); 797 MenuSelectionManager.defaultManager().clearSelectedPath(); 798 mi.doClick(); 799 } 800 } 801 802 /** 803 * Call this method when a menu item is to be activated. 804 * This method handles some of the details of menu item activation 805 * such as clearing the selected path and messaging the 806 * JMenuItem's doClick() method. 807 * 808 * @param msm A MenuSelectionManager. The visual feedback and 809 * internal bookkeeping tasks are delegated to 810 * this MenuSelectionManager. If <code>null</code> is 811 * passed as this argument, the 812 * <code>MenuSelectionManager.defaultManager</code> is 813 * used. 814 * @see MenuSelectionManager 815 * @see JMenuItem#doClick(int) 816 * @since 1.4 817 */ 818 protected void doClick(MenuSelectionManager msm) { 819 // Auditory cue 820 if (! isInternalFrameSystemMenu()) { 821 BasicLookAndFeel.playSound(menuItem, getPropertyPrefix() + 822 ".commandSound"); 823 } 824 // Visual feedback 825 if (msm == null) { 826 msm = MenuSelectionManager.defaultManager(); 827 } 828 msm.clearSelectedPath(); 829 menuItem.doClick(0); 830 } 831 832 /** 833 * This is to see if the menu item in question is part of the 834 * system menu on an internal frame. 835 * The Strings that are being checked can be found in 836 * MetalInternalFrameTitlePaneUI.java, 837 * WindowsInternalFrameTitlePaneUI.java, and 838 * MotifInternalFrameTitlePaneUI.java. 839 * 840 * @since 1.4 841 */ 842 private boolean isInternalFrameSystemMenu() { 843 String actionCommand = menuItem.getActionCommand(); 844 if ((actionCommand == "Close") || 845 (actionCommand == "Minimize") || 846 (actionCommand == "Restore") || 847 (actionCommand == "Maximize")) { 848 return true; 849 } else { 850 return false; 851 } 852 } 853 854 855 // BasicMenuUI subclasses this. 856 class Handler implements MenuDragMouseListener, 857 MouseInputListener, PropertyChangeListener { 858 // 859 // MouseInputListener 860 // 861 public void mouseClicked(MouseEvent e) {} 862 public void mousePressed(MouseEvent e) { 863 } 864 public void mouseReleased(MouseEvent e) { 865 if (!menuItem.isEnabled()) { 866 return; 867 } 868 MenuSelectionManager manager = 869 MenuSelectionManager.defaultManager(); 870 Point p = e.getPoint(); 871 if(p.x >= 0 && p.x < menuItem.getWidth() && 872 p.y >= 0 && p.y < menuItem.getHeight()) { 873 doClick(manager); 874 } else { 875 manager.processMouseEvent(e); 876 } 877 } 878 public void mouseEntered(MouseEvent e) { 879 MenuSelectionManager manager = MenuSelectionManager.defaultManager(); 880 int modifiers = e.getModifiers(); 881 // 4188027: drag enter/exit added in JDK 1.1.7A, JDK1.2 882 if ((modifiers & (InputEvent.BUTTON1_MASK | 883 InputEvent.BUTTON2_MASK | InputEvent.BUTTON3_MASK)) !=0 ) { 884 MenuSelectionManager.defaultManager().processMouseEvent(e); 885 } else { 886 manager.setSelectedPath(getPath()); 887 } 888 } 889 public void mouseExited(MouseEvent e) { 890 MenuSelectionManager manager = MenuSelectionManager.defaultManager(); 891 892 int modifiers = e.getModifiers(); 893 // 4188027: drag enter/exit added in JDK 1.1.7A, JDK1.2 894 if ((modifiers & (InputEvent.BUTTON1_MASK | 895 InputEvent.BUTTON2_MASK | InputEvent.BUTTON3_MASK)) !=0 ) { 896 MenuSelectionManager.defaultManager().processMouseEvent(e); 897 } else { 898 899 MenuElement path[] = manager.getSelectedPath(); 900 if (path.length > 1 && path[path.length-1] == menuItem) { 901 MenuElement newPath[] = new MenuElement[path.length-1]; 902 int i,c; 903 for(i=0,c=path.length-1;i<c;i++) 904 newPath[i] = path[i]; 905 manager.setSelectedPath(newPath); 906 } 907 } 908 } 909 910 public void mouseDragged(MouseEvent e) { 911 MenuSelectionManager.defaultManager().processMouseEvent(e); 912 } 913 public void mouseMoved(MouseEvent e) { 914 } 915 916 // 917 // MenuDragListener 918 // 919 public void menuDragMouseEntered(MenuDragMouseEvent e) { 920 MenuSelectionManager manager = e.getMenuSelectionManager(); 921 MenuElement path[] = e.getPath(); 922 manager.setSelectedPath(path); 923 } 924 public void menuDragMouseDragged(MenuDragMouseEvent e) { 925 MenuSelectionManager manager = e.getMenuSelectionManager(); 926 MenuElement path[] = e.getPath(); 927 manager.setSelectedPath(path); 928 } 929 public void menuDragMouseExited(MenuDragMouseEvent e) {} 930 public void menuDragMouseReleased(MenuDragMouseEvent e) { 931 if (!menuItem.isEnabled()) { 932 return; 933 } 934 MenuSelectionManager manager = e.getMenuSelectionManager(); 935 MenuElement path[] = e.getPath(); 936 Point p = e.getPoint(); 937 if (p.x >= 0 && p.x < menuItem.getWidth() && 938 p.y >= 0 && p.y < menuItem.getHeight()) { 939 doClick(manager); 940 } else { 941 manager.clearSelectedPath(); 942 } 943 } 944 945 946 // 947 // PropertyChangeListener 948 // 949 public void propertyChange(PropertyChangeEvent e) { 950 String name = e.getPropertyName(); 951 952 if (name == "labelFor" || name == "displayedMnemonic" || 953 name == "accelerator") { 954 updateAcceleratorBinding(); 955 } else if (name == "text" || "font" == name || 956 "foreground" == name) { 957 // remove the old html view client property if one 958 // existed, and install a new one if the text installed 959 // into the JLabel is html source. 960 JMenuItem lbl = ((JMenuItem) e.getSource()); 961 String text = lbl.getText(); 962 BasicHTML.updateRenderer(lbl, text); 963 } else if (name == "iconTextGap") { 964 defaultTextIconGap = ((Number)e.getNewValue()).intValue(); 965 } 966 } 967 } 968 }