1. /*
  2. * @(#)SynthMenuItemUI.java 1.18 03/04/10
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package com.sun.java.swing.plaf.gtk;
  8. import javax.swing.plaf.basic.BasicHTML;
  9. import java.awt.*;
  10. import java.awt.event.*;
  11. import java.beans.PropertyChangeEvent;
  12. import java.beans.PropertyChangeListener;
  13. import javax.swing.*;
  14. import javax.swing.event.*;
  15. import javax.swing.border.*;
  16. import javax.swing.plaf.*;
  17. import javax.swing.text.View;
  18. /**
  19. * BasicMenuItem implementation
  20. *
  21. * @version 1.18, 04/10/03 (based on BasicMenuItemUI v 1.116)
  22. * @author Georges Saab
  23. * @author David Karlton
  24. * @author Arnaud Weber
  25. * @author Fredrik Lagerblad
  26. */
  27. class SynthMenuItemUI extends MenuItemUI implements SynthUI {
  28. static final String MAX_TEXT_WIDTH = "maxTextWidth";
  29. static final String MAX_ACC_WIDTH = "maxAccWidth";
  30. private SynthStyle style;
  31. private SynthStyle accStyle;
  32. protected JMenuItem menuItem = null;
  33. private String acceleratorDelimiter;
  34. protected int defaultTextIconGap;
  35. protected MouseInputListener mouseInputListener;
  36. protected MenuDragMouseListener menuDragMouseListener;
  37. protected MenuKeyListener menuKeyListener;
  38. private PropertyChangeListener propertyChangeListener;
  39. protected Icon arrowIcon = null;
  40. protected Icon checkIcon = null;
  41. /** Used for accelerator binding, lazily created. */
  42. InputMap windowInputMap;
  43. /* diagnostic aids -- should be false for production builds. */
  44. private static final boolean TRACE = false; // trace creates and disposes
  45. private static final boolean VERBOSE = false; // show reuse hits/misses
  46. private static final boolean DEBUG = false; // show bad params, misc.
  47. public static ComponentUI createUI(JComponent c) {
  48. return new SynthMenuItemUI();
  49. }
  50. public static void loadActionMap(ActionMap map) {
  51. // NOTE: this needs to remain static. If you have a need to
  52. // have Actions that reference the UI in the ActionMap,
  53. // then you'll also need to change the registeration of the
  54. // ActionMap.
  55. map.put("doClick", new ClickAction());
  56. }
  57. public void installUI(JComponent c) {
  58. menuItem = (JMenuItem) c;
  59. installDefaults();
  60. installComponents(menuItem);
  61. installListeners();
  62. installKeyboardActions();
  63. }
  64. protected void installDefaults() {
  65. fetchStyle(menuItem);
  66. }
  67. private void fetchStyle(JMenuItem mi) {
  68. SynthContext context = getContext(mi, ENABLED);
  69. SynthStyle oldStyle = style;
  70. style = SynthLookAndFeel.updateStyle(context, this);
  71. if (oldStyle != style) {
  72. String prefix = getPropertyPrefix();
  73. defaultTextIconGap = style.getInt(
  74. context, prefix + ".textIconGap", 4);
  75. if (menuItem.getMargin() == null ||
  76. (menuItem.getMargin() instanceof UIResource)) {
  77. Insets insets = (Insets)style.get(context, prefix + ".margin");
  78. if (insets == null) {
  79. // Some places assume margins are non-null.
  80. insets = SynthLookAndFeel.EMPTY_UIRESOURCE_INSETS;
  81. }
  82. menuItem.setMargin(insets);
  83. }
  84. acceleratorDelimiter = style.getString(context, prefix +
  85. ".acceleratorDelimiter", "+");
  86. arrowIcon = style.getIcon(context, prefix + ".arrowIcon");
  87. checkIcon = style.getIcon(context, prefix + ".checkIcon");
  88. }
  89. context.dispose();
  90. SynthContext accContext = getContext(mi, Region.MENU_ITEM_ACCELERATOR,
  91. ENABLED);
  92. accStyle = SynthLookAndFeel.updateStyle(accContext, this);
  93. accContext.dispose();
  94. }
  95. /**
  96. * @since 1.3
  97. */
  98. protected void installComponents(JMenuItem menuItem){
  99. BasicHTML.updateRenderer(menuItem, menuItem.getText());
  100. }
  101. protected String getPropertyPrefix() {
  102. return "MenuItem";
  103. }
  104. protected void installListeners() {
  105. if ((mouseInputListener = createMouseInputListener(menuItem)) != null) {
  106. menuItem.addMouseListener(mouseInputListener);
  107. menuItem.addMouseMotionListener(mouseInputListener);
  108. }
  109. if ((menuDragMouseListener = createMenuDragMouseListener(menuItem)) != null) {
  110. menuItem.addMenuDragMouseListener(menuDragMouseListener);
  111. }
  112. if ((menuKeyListener = createMenuKeyListener(menuItem)) != null) {
  113. menuItem.addMenuKeyListener(menuKeyListener);
  114. }
  115. if ((propertyChangeListener = createPropertyChangeListener(menuItem)) != null) {
  116. menuItem.addPropertyChangeListener(propertyChangeListener);
  117. }
  118. }
  119. protected void installKeyboardActions() {
  120. registerActionMap();
  121. updateAcceleratorBinding();
  122. }
  123. public void uninstallUI(JComponent c) {
  124. menuItem = (JMenuItem)c;
  125. uninstallDefaults();
  126. uninstallComponents(menuItem);
  127. uninstallListeners();
  128. uninstallKeyboardActions();
  129. menuItem = null;
  130. }
  131. protected void uninstallDefaults() {
  132. if (menuItem.getMargin() instanceof UIResource)
  133. menuItem.setMargin(null);
  134. if (arrowIcon instanceof UIResource)
  135. arrowIcon = null;
  136. if (checkIcon instanceof UIResource)
  137. checkIcon = null;
  138. SynthContext context = getContext(menuItem, ENABLED);
  139. style.uninstallDefaults(context);
  140. context.dispose();
  141. style = null;
  142. SynthContext accContext = getContext(menuItem,
  143. Region.MENU_ITEM_ACCELERATOR, ENABLED);
  144. accStyle.uninstallDefaults(accContext);
  145. accContext.dispose();
  146. accStyle = null;
  147. }
  148. /**
  149. * @since 1.3
  150. */
  151. protected void uninstallComponents(JMenuItem menuItem){
  152. BasicHTML.updateRenderer(menuItem, "");
  153. }
  154. protected void uninstallListeners() {
  155. if (mouseInputListener != null) {
  156. menuItem.removeMouseListener(mouseInputListener);
  157. menuItem.removeMouseMotionListener(mouseInputListener);
  158. }
  159. if (menuDragMouseListener != null) {
  160. menuItem.removeMenuDragMouseListener(menuDragMouseListener);
  161. }
  162. if (menuKeyListener != null) {
  163. menuItem.removeMenuKeyListener(menuKeyListener);
  164. }
  165. if (propertyChangeListener != null) {
  166. menuItem.removePropertyChangeListener(propertyChangeListener);
  167. }
  168. mouseInputListener = null;
  169. menuDragMouseListener = null;
  170. menuKeyListener = null;
  171. propertyChangeListener = null;
  172. }
  173. protected void uninstallKeyboardActions() {
  174. SwingUtilities.replaceUIActionMap(menuItem, null);
  175. if (windowInputMap != null) {
  176. SwingUtilities.replaceUIInputMap(menuItem, JComponent.
  177. WHEN_IN_FOCUSED_WINDOW, null);
  178. windowInputMap = null;
  179. }
  180. }
  181. protected MouseInputListener createMouseInputListener(JComponent c) {
  182. return new MouseInputHandler();
  183. }
  184. protected MenuDragMouseListener createMenuDragMouseListener(JComponent c) {
  185. return new MenuDragMouseHandler();
  186. }
  187. protected MenuKeyListener createMenuKeyListener(JComponent c) {
  188. return new MenuKeyHandler();
  189. }
  190. private PropertyChangeListener createPropertyChangeListener(JComponent c) {
  191. return new PropertyChangeHandler();
  192. }
  193. public SynthContext getContext(JComponent c) {
  194. return getContext(c, getComponentState(c));
  195. }
  196. SynthContext getContext(JComponent c, int state) {
  197. return SynthContext.getContext(SynthContext.class, c,
  198. SynthLookAndFeel.getRegion(c), style, state);
  199. }
  200. public SynthContext getContext(JComponent c, Region region) {
  201. return getContext(c, region, getComponentState(c, region));
  202. }
  203. private SynthContext getContext(JComponent c, Region region, int state) {
  204. return SynthContext.getContext(SynthContext.class, c,
  205. region, accStyle, state);
  206. }
  207. private Region getRegion(JComponent c) {
  208. return SynthLookAndFeel.getRegion(c);
  209. }
  210. private int getComponentState(JComponent c) {
  211. int state;
  212. if (!c.isEnabled()) {
  213. return DISABLED;
  214. }
  215. if (menuItem.isArmed()) {
  216. state = MOUSE_OVER;
  217. }
  218. else {
  219. state = SynthLookAndFeel.getComponentState(c);
  220. }
  221. if (menuItem.isSelected()) {
  222. state |= SELECTED;
  223. }
  224. return state;
  225. }
  226. private int getComponentState(JComponent c, Region region) {
  227. return getComponentState(c);
  228. }
  229. void registerActionMap() {
  230. LazyActionMap.installLazyActionMap(menuItem, SynthMenuItemUI.class,
  231. "MenuItem.actionMap");
  232. }
  233. InputMap createInputMap(int condition) {
  234. if (condition == JComponent.WHEN_IN_FOCUSED_WINDOW) {
  235. return new ComponentInputMapUIResource(menuItem);
  236. }
  237. return null;
  238. }
  239. void updateAcceleratorBinding() {
  240. KeyStroke accelerator = menuItem.getAccelerator();
  241. if (windowInputMap != null) {
  242. windowInputMap.clear();
  243. }
  244. if (accelerator != null) {
  245. if (windowInputMap == null) {
  246. windowInputMap = createInputMap(JComponent.
  247. WHEN_IN_FOCUSED_WINDOW);
  248. SwingUtilities.replaceUIInputMap(menuItem,
  249. JComponent.WHEN_IN_FOCUSED_WINDOW, windowInputMap);
  250. }
  251. windowInputMap.put(accelerator, "doClick");
  252. }
  253. }
  254. public Dimension getMinimumSize(JComponent c) {
  255. Dimension d = null;
  256. View v = (View) c.getClientProperty(BasicHTML.propertyKey);
  257. if (v != null) {
  258. d = getPreferredSize(c);
  259. d.width -= v.getPreferredSpan(View.X_AXIS) - v.getMinimumSpan(View.X_AXIS);
  260. }
  261. return d;
  262. }
  263. public Dimension getPreferredSize(JComponent c) {
  264. return getPreferredMenuItemSize(c,
  265. checkIcon,
  266. arrowIcon,
  267. defaultTextIconGap);
  268. }
  269. public Dimension getMaximumSize(JComponent c) {
  270. Dimension d = null;
  271. View v = (View) c.getClientProperty(BasicHTML.propertyKey);
  272. if (v != null) {
  273. d = getPreferredSize(c);
  274. d.width += v.getMaximumSpan(View.X_AXIS) - v.getPreferredSpan(View.X_AXIS);
  275. }
  276. return d;
  277. }
  278. // these rects are used for painting and preferredsize calculations.
  279. // they used to be regenerated constantly. Now they are reused.
  280. static Rectangle iconRect = new Rectangle();
  281. static Rectangle textRect = new Rectangle();
  282. static Rectangle acceleratorRect = new Rectangle();
  283. static Rectangle checkIconRect = new Rectangle();
  284. static Rectangle arrowIconRect = new Rectangle();
  285. static Rectangle viewRect = new Rectangle(Short.MAX_VALUE, Short.MAX_VALUE);
  286. static Rectangle r = new Rectangle();
  287. private void resetRects() {
  288. iconRect.setBounds(0, 0, 0, 0);
  289. textRect.setBounds(0, 0, 0, 0);
  290. acceleratorRect.setBounds(0, 0, 0, 0);
  291. checkIconRect.setBounds(0, 0, 0, 0);
  292. arrowIconRect.setBounds(0, 0, 0, 0);
  293. viewRect.setBounds(0,0,Short.MAX_VALUE, Short.MAX_VALUE);
  294. r.setBounds(0, 0, 0, 0);
  295. }
  296. protected Dimension getPreferredMenuItemSize(JComponent c,
  297. Icon checkIcon,
  298. Icon arrowIcon,
  299. int defaultTextIconGap) {
  300. JMenuItem b = (JMenuItem) c;
  301. Icon icon = (Icon) b.getIcon();
  302. String text = b.getText();
  303. KeyStroke accelerator = b.getAccelerator();
  304. String acceleratorText = "";
  305. if (accelerator != null) {
  306. int modifiers = accelerator.getModifiers();
  307. if (modifiers > 0) {
  308. acceleratorText = KeyEvent.getKeyModifiersText(modifiers);
  309. //acceleratorText += "-";
  310. acceleratorText += acceleratorDelimiter;
  311. }
  312. int keyCode = accelerator.getKeyCode();
  313. if (keyCode != 0) {
  314. acceleratorText += KeyEvent.getKeyText(keyCode);
  315. } else {
  316. acceleratorText += accelerator.getKeyChar();
  317. }
  318. }
  319. SynthContext context = getContext(c);
  320. SynthContext accContext = getContext(c, Region.MENU_ITEM_ACCELERATOR);
  321. Font font = context.getStyle().getFont(context);
  322. FontMetrics fm = b.getToolkit().getFontMetrics(font);
  323. FontMetrics fmAccel = b.getToolkit().getFontMetrics(
  324. accContext.getStyle().getFont(accContext));
  325. resetRects();
  326. layoutMenuItem(
  327. context, fm, accContext, text, fmAccel, acceleratorText,
  328. icon, checkIcon, arrowIcon, b.getVerticalAlignment(),
  329. b.getHorizontalAlignment(), b.getVerticalTextPosition(),
  330. b.getHorizontalTextPosition(), viewRect, iconRect, textRect,
  331. acceleratorRect, checkIconRect, arrowIconRect,
  332. text == null ? 0 : defaultTextIconGap, defaultTextIconGap
  333. );
  334. // find the union of the icon and text rects
  335. r.setBounds(textRect);
  336. r = SwingUtilities.computeUnion(iconRect.x,
  337. iconRect.y,
  338. iconRect.width,
  339. iconRect.height,
  340. r);
  341. // To make the accelerator texts appear in a column,
  342. // find the widest MenuItem text and the widest accelerator text.
  343. // Get the parent, which stores the information.
  344. Container parent = menuItem.getParent();
  345. if (parent instanceof JPopupMenu) {
  346. JPopupMenu popup = (JPopupMenu)parent;
  347. ComponentUI ui = popup.getUI();
  348. if (ui instanceof SynthPopupMenuUI) {
  349. SynthPopupMenuUI popupUI = (SynthPopupMenuUI)ui;
  350. r.width = popupUI.adjustTextWidth(r.width);
  351. popupUI.adjustAcceleratorWidth(acceleratorRect.width);
  352. r.width += popupUI.getMaxAcceleratorWidth();
  353. }
  354. else {
  355. // We don't have a SynthPopupMenuUI, fallback to storing the
  356. // values in the client properties of the JPopupMenu.
  357. //Get widest text so far from parent, if no one exists null is returned.
  358. Integer maxTextWidth = (Integer)popup.getClientProperty(
  359. MAX_TEXT_WIDTH);
  360. Integer maxAccWidth = (Integer)popup.
  361. getClientProperty(MAX_ACC_WIDTH);
  362. int maxTextValue = maxTextWidth!=null ?
  363. maxTextWidth.intValue() : 0;
  364. int maxAccValue = maxAccWidth!=null ?
  365. maxAccWidth.intValue() : 0;
  366. // Compare the text widths, and adjust the r.width to the
  367. // widest.
  368. if (r.width < maxTextValue) {
  369. r.width = maxTextValue;
  370. } else {
  371. popup.putClientProperty(MAX_TEXT_WIDTH,
  372. new Integer(r.width));
  373. }
  374. // Compare the accelarator widths.
  375. if (acceleratorRect.width > maxAccValue) {
  376. maxAccValue = acceleratorRect.width;
  377. popup.putClientProperty(MAX_ACC_WIDTH, new Integer(
  378. acceleratorRect.width));
  379. }
  380. //Add on the widest accelerator
  381. r.width += maxAccValue;
  382. }
  383. }
  384. if( useCheckAndArrow() ) {
  385. // Add in the checkIcon
  386. r.width += checkIconRect.width;
  387. r.width += defaultTextIconGap;
  388. // Add in the arrowIcon
  389. r.width += defaultTextIconGap;
  390. r.width += arrowIconRect.width;
  391. }
  392. r.width += 2*defaultTextIconGap;
  393. Insets insets = b.getInsets();
  394. if(insets != null) {
  395. r.width += insets.left + insets.right;
  396. r.height += insets.top + insets.bottom;
  397. }
  398. // if the width is even, bump it up one. This is critical
  399. // for the focus dash line to draw properly
  400. if(r.width%2 == 0) {
  401. r.width++;
  402. }
  403. // if the height is even, bump it up one. This is critical
  404. // for the text to center properly
  405. if(r.height%2 == 0) {
  406. r.height++;
  407. }
  408. /*
  409. if(!(b instanceof JMenu && ((JMenu) b).isTopLevelMenu()) ) {
  410. // Container parent = menuItem.getParent();
  411. JComponent p = (JComponent) parent;
  412. System.out.println("MaxText: "+p.getClientProperty(BasicMenuItemUI.MAX_TEXT_WIDTH));
  413. System.out.println("MaxACC"+p.getClientProperty(BasicMenuItemUI.MAX_ACC_WIDTH));
  414. System.out.println("returning pref.width: " + r.width);
  415. System.out.println("Current getSize: " + b.getSize() + "\n");
  416. }*/
  417. context.dispose();
  418. accContext.dispose();
  419. return r.getSize();
  420. }
  421. public void update(Graphics g, JComponent c) {
  422. SynthContext context = getContext(c);
  423. SynthLookAndFeel.update(context, g);
  424. paint(context, g);
  425. context.dispose();
  426. }
  427. public void paint(Graphics g, JComponent c) {
  428. SynthContext context = getContext(c);
  429. paint(context, g);
  430. context.dispose();
  431. }
  432. protected void paint(SynthContext context, Graphics g) {
  433. JComponent c = context.getComponent();
  434. JMenuItem b = (JMenuItem)c;
  435. ButtonModel model = b.getModel();
  436. Insets i = b.getInsets();
  437. resetRects();
  438. viewRect.setBounds(0, 0, b.getWidth(), b.getHeight());
  439. viewRect.x += i.left;
  440. viewRect.y += i.top;
  441. viewRect.width -= (i.right + viewRect.x);
  442. viewRect.height -= (i.bottom + viewRect.y);
  443. SynthContext accContext = getContext(menuItem,
  444. Region.MENU_ITEM_ACCELERATOR);
  445. SynthStyle style = context.getStyle();
  446. Font f = style.getFont(context);
  447. g.setFont(f);
  448. FontMetrics fm = g.getFontMetrics(f);
  449. FontMetrics accFM = g.getFontMetrics(accContext.getStyle().
  450. getFont(accContext));
  451. // get Accelerator text
  452. KeyStroke accelerator = b.getAccelerator();
  453. String acceleratorText = "";
  454. if (accelerator != null) {
  455. int modifiers = accelerator.getModifiers();
  456. if (modifiers > 0) {
  457. acceleratorText = KeyEvent.getKeyModifiersText(modifiers);
  458. //acceleratorText += "-";
  459. acceleratorText += acceleratorDelimiter;
  460. }
  461. int keyCode = accelerator.getKeyCode();
  462. if (keyCode != 0) {
  463. acceleratorText += KeyEvent.getKeyText(keyCode);
  464. } else {
  465. acceleratorText += accelerator.getKeyChar();
  466. }
  467. }
  468. // layout the text and icon
  469. String text = layoutMenuItem(context, fm, accContext,
  470. b.getText(), accFM, acceleratorText, b.getIcon(),
  471. checkIcon, arrowIcon,
  472. b.getVerticalAlignment(), b.getHorizontalAlignment(),
  473. b.getVerticalTextPosition(), b.getHorizontalTextPosition(),
  474. viewRect, iconRect, textRect, acceleratorRect,
  475. checkIconRect, arrowIconRect,
  476. b.getText() == null ? 0 : defaultTextIconGap,
  477. defaultTextIconGap
  478. );
  479. // Paint the Check
  480. if (checkIcon != null && useCheckAndArrow() ) {
  481. SynthIcon.paintIcon(checkIcon, context, g, checkIconRect.x,
  482. checkIconRect.y, checkIconRect.width, checkIconRect.height);
  483. }
  484. // Paint the Icon
  485. if(b.getIcon() != null) {
  486. Icon icon;
  487. if(!model.isEnabled()) {
  488. icon = (Icon) b.getDisabledIcon();
  489. } else if(model.isPressed() && model.isArmed()) {
  490. icon = (Icon) b.getPressedIcon();
  491. if(icon == null) {
  492. // Use default icon
  493. icon = (Icon) b.getIcon();
  494. }
  495. } else {
  496. icon = (Icon) b.getIcon();
  497. }
  498. if (icon!=null) {
  499. SynthIcon.paintIcon(icon, context, g, iconRect.x,
  500. iconRect.y, iconRect.width, iconRect.height);
  501. }
  502. }
  503. // Draw the Text
  504. if(text != null) {
  505. View v = (View) c.getClientProperty(BasicHTML.propertyKey);
  506. if (v != null) {
  507. v.paint(g, textRect);
  508. } else {
  509. g.setColor(style.getColor(context, ColorType.TEXT_FOREGROUND));
  510. g.setFont(style.getFont(context));
  511. style.getSynthGraphics(context).paintText(context, g, text,
  512. textRect.x, textRect.y, menuItem.
  513. getDisplayedMnemonicIndex());
  514. }
  515. }
  516. // Draw the Accelerator Text
  517. if(acceleratorText != null && !acceleratorText.equals("")) {
  518. // Get the maxAccWidth from the parent to calculate the offset.
  519. int accOffset = 0;
  520. Container parent = menuItem.getParent();
  521. if (parent != null && parent instanceof JPopupMenu) {
  522. ComponentUI ui = ((JPopupMenu)parent).getUI();
  523. if (ui instanceof SynthPopupMenuUI) {
  524. SynthPopupMenuUI popupUI = (SynthPopupMenuUI)ui;
  525. // Calculate the offset, with which the accelerator texts
  526. // will be drawn with.
  527. int max = popupUI.getMaxAcceleratorWidth();
  528. if (max > 0) {
  529. accOffset = max - acceleratorRect.width;
  530. }
  531. }
  532. else {
  533. // We don't have a SynthPopupMenuUI, fallback to using the
  534. // values in the client properties of the JPopupMenu.
  535. Integer maxValueInt = (Integer)((JComponent)parent).
  536. getClientProperty(MAX_ACC_WIDTH);
  537. int maxValue = maxValueInt != null ?
  538. maxValueInt.intValue() : acceleratorRect.width;
  539. // Calculate the offset, with which the accelerator texts
  540. // will be drawn with.
  541. accOffset = maxValue - acceleratorRect.width;
  542. }
  543. }
  544. SynthStyle accStyle = accContext.getStyle();
  545. g.setColor(accStyle.getColor(accContext,
  546. ColorType.TEXT_FOREGROUND));
  547. g.setFont(accStyle.getFont(accContext));
  548. accStyle.getSynthGraphics(accContext).paintText(
  549. accContext, g, acceleratorText, acceleratorRect.x -
  550. accOffset, acceleratorRect.y, -1);
  551. }
  552. // Paint the Arrow
  553. if (arrowIcon != null && useCheckAndArrow()) {
  554. SynthIcon.paintIcon(arrowIcon, context, g, arrowIconRect.x,
  555. arrowIconRect.y, arrowIconRect.width, arrowIconRect.height);
  556. }
  557. accContext.dispose();
  558. }
  559. /**
  560. * Compute and return the location of the icons origin, the
  561. * location of origin of the text baseline, and a possibly clipped
  562. * version of the compound labels string. Locations are computed
  563. * relative to the viewRect rectangle.
  564. */
  565. private String layoutMenuItem(
  566. SynthContext context,
  567. FontMetrics fm,
  568. SynthContext accContext,
  569. String text,
  570. FontMetrics fmAccel,
  571. String acceleratorText,
  572. Icon icon,
  573. Icon checkIcon,
  574. Icon arrowIcon,
  575. int verticalAlignment,
  576. int horizontalAlignment,
  577. int verticalTextPosition,
  578. int horizontalTextPosition,
  579. Rectangle viewRect,
  580. Rectangle iconRect,
  581. Rectangle textRect,
  582. Rectangle acceleratorRect,
  583. Rectangle checkIconRect,
  584. Rectangle arrowIconRect,
  585. int textIconGap,
  586. int menuItemGap
  587. )
  588. {
  589. context.getStyle().getSynthGraphics(context).layoutText(
  590. context, fm, text, icon,horizontalAlignment, verticalAlignment,
  591. horizontalTextPosition, verticalTextPosition, viewRect,
  592. iconRect, textRect, textIconGap);
  593. /* Initialize the acceelratorText bounds rectangle textRect. If a null
  594. * or and empty String was specified we substitute "" here
  595. * and use 0,0,0,0 for acceleratorTextRect.
  596. */
  597. if( (acceleratorText == null) || acceleratorText.equals("") ) {
  598. acceleratorRect.width = acceleratorRect.height = 0;
  599. acceleratorText = "";
  600. }
  601. else {
  602. SynthStyle style = accContext.getStyle();
  603. acceleratorRect.width = style.getSynthGraphics(accContext).
  604. computeStringWidth(accContext, fmAccel.getFont(), fmAccel,
  605. acceleratorText);
  606. acceleratorRect.height = fmAccel.getHeight();
  607. }
  608. /* Initialize the checkIcon bounds rectangle's width & height.
  609. */
  610. if( useCheckAndArrow()) {
  611. if (checkIcon != null) {
  612. checkIconRect.width = SynthIcon.getIconWidth(checkIcon,
  613. context);
  614. checkIconRect.height = SynthIcon.getIconHeight(checkIcon,
  615. context);
  616. }
  617. else {
  618. checkIconRect.width = checkIconRect.height = 0;
  619. }
  620. /* Initialize the arrowIcon bounds rectangle width & height.
  621. */
  622. if (arrowIcon != null) {
  623. arrowIconRect.width = SynthIcon.getIconWidth(arrowIcon,
  624. context);
  625. arrowIconRect.height = SynthIcon.getIconHeight(arrowIcon,
  626. context);
  627. } else {
  628. arrowIconRect.width = arrowIconRect.height = 0;
  629. }
  630. }
  631. Rectangle labelRect = iconRect.union(textRect);
  632. if( SynthLookAndFeel.isLeftToRight(menuItem) ) {
  633. textRect.x += menuItemGap;
  634. iconRect.x += menuItemGap;
  635. // Position the Accelerator text rect
  636. acceleratorRect.x = viewRect.x + viewRect.width -
  637. arrowIconRect.width - menuItemGap - acceleratorRect.width;
  638. // Position the Check and Arrow Icons
  639. if (useCheckAndArrow()) {
  640. checkIconRect.x = viewRect.x + menuItemGap;
  641. textRect.x += menuItemGap + checkIconRect.width;
  642. iconRect.x += menuItemGap + checkIconRect.width;
  643. arrowIconRect.x = viewRect.x + viewRect.width - menuItemGap
  644. - arrowIconRect.width;
  645. }
  646. } else {
  647. textRect.x -= menuItemGap;
  648. iconRect.x -= menuItemGap;
  649. // Position the Accelerator text rect
  650. acceleratorRect.x = viewRect.x + arrowIconRect.width + menuItemGap;
  651. // Position the Check and Arrow Icons
  652. if (useCheckAndArrow()) {
  653. checkIconRect.x = viewRect.x + viewRect.width - menuItemGap
  654. - checkIconRect.width;
  655. textRect.x -= menuItemGap + checkIconRect.width;
  656. iconRect.x -= menuItemGap + checkIconRect.width;
  657. arrowIconRect.x = viewRect.x + menuItemGap;
  658. }
  659. }
  660. // Align the accelertor text and the check and arrow icons vertically
  661. // with the center of the label rect.
  662. acceleratorRect.y = labelRect.y + (labelRect.height2) - (acceleratorRect.height2);
  663. if( useCheckAndArrow() ) {
  664. arrowIconRect.y = labelRect.y + (labelRect.height2) - (arrowIconRect.height2);
  665. checkIconRect.y = labelRect.y + (labelRect.height2) - (checkIconRect.height2);
  666. }
  667. /*
  668. System.out.println("Layout: text="+menuItem.getText()+"\n\tv="
  669. +viewRect+"\n\tc="+checkIconRect+"\n\ti="
  670. +iconRect+"\n\tt="+textRect+"\n\tacc="
  671. +acceleratorRect+"\n\ta="+arrowIconRect+"\n");
  672. */
  673. return text;
  674. }
  675. /*
  676. * Returns false if the component is a JMenu and it is a top
  677. * level menu (on the menubar).
  678. */
  679. private boolean useCheckAndArrow(){
  680. boolean b = true;
  681. if((menuItem instanceof JMenu) &&
  682. (((JMenu)menuItem).isTopLevelMenu())) {
  683. b = false;
  684. }
  685. return b;
  686. }
  687. public MenuElement[] getPath() {
  688. MenuSelectionManager m = MenuSelectionManager.defaultManager();
  689. MenuElement oldPath[] = m.getSelectedPath();
  690. MenuElement newPath[];
  691. int i = oldPath.length;
  692. if (i == 0)
  693. return new MenuElement[0];
  694. Component parent = menuItem.getParent();
  695. if (oldPath[i-1].getComponent() == parent) {
  696. // The parent popup menu is the last so far
  697. newPath = new MenuElement[i+1];
  698. System.arraycopy(oldPath, 0, newPath, 0, i);
  699. newPath[i] = menuItem;
  700. } else {
  701. // A sibling menuitem is the current selection
  702. //
  703. // This probably needs to handle 'exit submenu into
  704. // a menu item. Search backwards along the current
  705. // selection until you find the parent popup menu,
  706. // then copy up to that and add yourself...
  707. int j;
  708. for (j = oldPath.length-1; j >= 0; j--) {
  709. if (oldPath[j].getComponent() == parent)
  710. break;
  711. }
  712. newPath = new MenuElement[j+2];
  713. System.arraycopy(oldPath, 0, newPath, 0, j+1);
  714. newPath[j+1] = menuItem;
  715. /*
  716. System.out.println("Sibling condition -- ");
  717. System.out.println("Old array : ");
  718. printMenuElementArray(oldPath, false);
  719. System.out.println("New array : ");
  720. printMenuElementArray(newPath, false);
  721. */
  722. }
  723. return newPath;
  724. }
  725. void printMenuElementArray(MenuElement path[], boolean dumpStack) {
  726. System.out.println("Path is(");
  727. int i, j;
  728. for(i=0,j=path.length; i<j ;i++){
  729. for (int k=0; k<=i; k++)
  730. System.out.print(" ");
  731. MenuElement me = (MenuElement) path[i];
  732. if(me instanceof JMenuItem)
  733. System.out.println(((JMenuItem)me).getText() + ", ");
  734. else if (me == null)
  735. System.out.println("NULL , ");
  736. else
  737. System.out.println("" + me + ", ");
  738. }
  739. System.out.println(")");
  740. if (dumpStack == true)
  741. Thread.dumpStack();
  742. }
  743. protected class MouseInputHandler implements MouseInputListener {
  744. public void mouseClicked(MouseEvent e) {}
  745. public void mousePressed(MouseEvent e) {
  746. }
  747. public void mouseReleased(MouseEvent e) {
  748. MenuSelectionManager manager =
  749. MenuSelectionManager.defaultManager();
  750. Point p = e.getPoint();
  751. if(p.x >= 0 && p.x < menuItem.getWidth() &&
  752. p.y >= 0 && p.y < menuItem.getHeight()) {
  753. doClick(manager);
  754. } else {
  755. manager.processMouseEvent(e);
  756. }
  757. }
  758. public void mouseEntered(MouseEvent e) {
  759. MenuSelectionManager manager = MenuSelectionManager.defaultManager();
  760. int modifiers = e.getModifiers();
  761. // 4188027: drag enter/exit added in JDK 1.1.7A, JDK1.2
  762. if ((modifiers & (InputEvent.BUTTON1_MASK |
  763. InputEvent.BUTTON2_MASK | InputEvent.BUTTON3_MASK)) !=0 ) {
  764. MenuSelectionManager.defaultManager().processMouseEvent(e);
  765. } else {
  766. manager.setSelectedPath(getPath());
  767. }
  768. }
  769. public void mouseExited(MouseEvent e) {
  770. MenuSelectionManager manager = MenuSelectionManager.defaultManager();
  771. int modifiers = e.getModifiers();
  772. // 4188027: drag enter/exit added in JDK 1.1.7A, JDK1.2
  773. if ((modifiers & (InputEvent.BUTTON1_MASK |
  774. InputEvent.BUTTON2_MASK | InputEvent.BUTTON3_MASK)) !=0 ) {
  775. MenuSelectionManager.defaultManager().processMouseEvent(e);
  776. } else {
  777. MenuElement path[] = manager.getSelectedPath();
  778. if (path.length > 1) {
  779. MenuElement newPath[] = new MenuElement[path.length-1];
  780. int i,c;
  781. for(i=0,c=path.length-1;i<c;i++)
  782. newPath[i] = path[i];
  783. manager.setSelectedPath(newPath);
  784. }
  785. }
  786. }
  787. public void mouseDragged(MouseEvent e) {
  788. MenuSelectionManager.defaultManager().processMouseEvent(e);
  789. }
  790. public void mouseMoved(MouseEvent e) {
  791. }
  792. }
  793. private class MenuDragMouseHandler implements MenuDragMouseListener {
  794. public void menuDragMouseEntered(MenuDragMouseEvent e) {
  795. MenuSelectionManager manager = e.getMenuSelectionManager();
  796. MenuElement path[] = e.getPath();
  797. manager.setSelectedPath(path);
  798. }
  799. public void menuDragMouseDragged(MenuDragMouseEvent e) {
  800. MenuSelectionManager manager = e.getMenuSelectionManager();
  801. MenuElement path[] = e.getPath();
  802. manager.setSelectedPath(path);
  803. }
  804. public void menuDragMouseExited(MenuDragMouseEvent e) {}
  805. public void menuDragMouseReleased(MenuDragMouseEvent e) {
  806. MenuSelectionManager manager = e.getMenuSelectionManager();
  807. MenuElement path[] = e.getPath();
  808. Point p = e.getPoint();
  809. if(p.x >= 0 && p.x < menuItem.getWidth() &&
  810. p.y >= 0 && p.y < menuItem.getHeight()) {
  811. doClick(manager);
  812. } else {
  813. manager.clearSelectedPath();
  814. }
  815. }
  816. }
  817. private class MenuKeyHandler implements MenuKeyListener {
  818. /**
  819. * Handles the mnemonic key typed in the MenuItem if this menuItem is in
  820. * a standalone popup menu. This invocation normally
  821. * handled in BasicMenuUI.MenuKeyHandler.menuKeyPressed. Ideally, the
  822. * MenuKeyHandlers for both BasicMenuItemUI and BasicMenuUI can be consolidated
  823. * into BasicPopupMenuUI but that would require an semantic change. This
  824. * would result in a performance win since we can shortcut a lot of the needless
  825. * processing from MenuSelectionManager.processKeyEvent(). See 4670831.
  826. */
  827. public void menuKeyTyped(MenuKeyEvent e) {
  828. if (DEBUG) {
  829. System.out.println("in BasicMenuItemUI.menuKeyTyped for " + menuItem.getText());
  830. }
  831. int key = menuItem.getMnemonic();
  832. if(key == 0 || e.getPath().length != 2) // Hack! Only proceed if in a JPopupMenu
  833. return;
  834. if(lower((char)key) == lower(e.getKeyChar())) {
  835. MenuSelectionManager manager =
  836. e.getMenuSelectionManager();
  837. doClick(manager);
  838. e.consume();
  839. }
  840. }
  841. public void menuKeyPressed(MenuKeyEvent e) {
  842. if (DEBUG) {
  843. System.out.println("in BasicMenuItemUI.menuKeyPressed for " + menuItem.getText());
  844. }
  845. }
  846. public void menuKeyReleased(MenuKeyEvent e) {}
  847. private char lower(char keyChar) {
  848. return Character.toLowerCase(keyChar);
  849. }
  850. }
  851. private class PropertyChangeHandler implements PropertyChangeListener {
  852. public void propertyChange(PropertyChangeEvent e) {
  853. String name = e.getPropertyName();
  854. if (SynthLookAndFeel.shouldUpdateStyle(e)) {
  855. fetchStyle((JMenuItem)e.getSource());
  856. }
  857. if (name.equals("labelFor") || name.equals("displayedMnemonic") ||
  858. name.equals("accelerator")) {
  859. updateAcceleratorBinding();
  860. } else if (name.equals("text") || "font".equals(name) ||
  861. "foreground".equals(name)) {
  862. // remove the old html view client property if one
  863. // existed, and install a new one if the text installed
  864. // into the JLabel is html source.
  865. JMenuItem lbl = ((JMenuItem) e.getSource());
  866. String text = lbl.getText();
  867. BasicHTML.updateRenderer(lbl, text);
  868. }
  869. }
  870. }
  871. private static class ClickAction extends AbstractAction {
  872. public void actionPerformed(ActionEvent e) {
  873. JMenuItem mi = (JMenuItem)e.getSource();
  874. MenuSelectionManager.defaultManager().clearSelectedPath();
  875. mi.doClick();
  876. }
  877. }
  878. /**
  879. * Call this method when a menu item is to be activated.
  880. * This method handles some of the details of menu item activation
  881. * such as clearing the selected path and messaging the
  882. * JMenuItem's doClick() method.
  883. *
  884. * @param msm A MenuSelectionManager. The visual feedback and
  885. * internal bookkeeping tasks are delegated to
  886. * this MenuSelectionManager. If <code>null</code> is
  887. * passed as this argument, the
  888. * <code>MenuSelectionManager.defaultManager</code> is
  889. * used.
  890. * @see MenuSelectionManager
  891. * @see JMenuItem#doClick(int)
  892. * @since 1.4
  893. */
  894. protected void doClick(MenuSelectionManager msm) {
  895. // Auditory cue
  896. if (! isInternalFrameSystemMenu()) {
  897. SynthLookAndFeel.playSound(menuItem, getPropertyPrefix() +
  898. ".commandSound");
  899. }
  900. // Visual feedback
  901. if (msm == null) {
  902. msm = MenuSelectionManager.defaultManager();
  903. }
  904. msm.clearSelectedPath();
  905. menuItem.doClick(0);
  906. }
  907. /**
  908. * This is to see if the menu item in question is part of the
  909. * system menu on an internal frame.
  910. * The Strings that are being checked can be found in
  911. * MetalInternalFrameTitlePaneUI.java,
  912. * WindowsInternalFrameTitlePaneUI.java, and
  913. * MotifInternalFrameTitlePaneUI.java.
  914. *
  915. * @since 1.4
  916. */
  917. private boolean isInternalFrameSystemMenu() {
  918. String actionCommand = menuItem.getActionCommand();
  919. if ((actionCommand == "Close") ||
  920. (actionCommand == "Minimize") ||
  921. (actionCommand == "Restore") ||
  922. (actionCommand == "Maximize")) {
  923. return true;
  924. } else {
  925. return false;
  926. }
  927. }
  928. }