1 /*
2  * Copyright (c) 2011, 2013, 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.apple.laf;
27 
28 import java.awt.*;
29 import java.awt.event.*;
30 
31 import javax.swing.*;
32 import javax.swing.border.Border;
33 import javax.swing.plaf.basic.BasicHTML;
34 import javax.swing.text.View;
35 
36 import sun.swing.SwingUtilities2;
37 
38 import apple.laf.JRSUIConstants.*;
39 
40 import com.apple.laf.AquaIcon.InvertableIcon;
41 import com.apple.laf.AquaUtils.RecyclableSingleton;
42 import com.apple.laf.AquaUtils.RecyclableSingletonFromDefaultConstructor;
43 
44 /**
45  * AquaMenuPainter, implements paintMenuItem to avoid code duplication
46  *
47  * BasicMenuItemUI didn't factor out the various parts of the Menu, and
48  * we subclass it and its subclasses BasicMenuUI
49  * Our classes need an implementation of paintMenuItem
50  * that allows them to paint their own backgrounds
51  */
52 
53 public class AquaMenuPainter {
54     // Glyph statics:
55     // ASCII character codes
56     static final byte
57         kShiftGlyph = 0x05,
58         kOptionGlyph = 0x07,
59         kControlGlyph = 0x06,
60         kPencilGlyph = 0x0F,
61         kCommandMark = 0x11;
62 
63     // Unicode character codes
64     static final char
65         kUBlackDiamond = 0x25C6,
66         kUCheckMark = 0x2713,
67         kUControlGlyph = 0x2303,
68         kUOptionGlyph = 0x2325,
69         kUEnterGlyph = 0x2324,
70         kUCommandGlyph = 0x2318,
71         kULeftDeleteGlyph = 0x232B,
72         kURightDeleteGlyph = 0x2326,
73         kUShiftGlyph = 0x21E7,
74         kUCapsLockGlyph = 0x21EA;
75 
76     static final int ALT_GRAPH_MASK = 1 << 5; // New to Java2
77     static final int sUnsupportedModifiersMask = ~(InputEvent.CTRL_MASK | InputEvent.ALT_MASK | InputEvent.SHIFT_MASK | InputEvent.META_MASK | ALT_GRAPH_MASK);
78 
79     interface Client {
paintBackground(Graphics g, JComponent c, int menuWidth, int menuHeight)80         public void paintBackground(Graphics g, JComponent c, int menuWidth, int menuHeight);
81     }
82 
83     // Return a string with the proper modifier glyphs
getKeyModifiersText(final int modifiers, final boolean isLeftToRight)84     static String getKeyModifiersText(final int modifiers, final boolean isLeftToRight) {
85         return getKeyModifiersUnicode(modifiers, isLeftToRight);
86     }
87 
88     // Return a string with the proper modifier glyphs
getKeyModifiersUnicode(final int modifiers, final boolean isLeftToRight)89     private static String getKeyModifiersUnicode(final int modifiers, final boolean isLeftToRight) {
90         final StringBuilder buf = new StringBuilder(2);
91         // Order (from StandardMenuDef.c): control, option(alt), shift, cmd
92         // reverse for right-to-left
93         //$ check for substitute key glyphs for localization
94         if (isLeftToRight) {
95             if ((modifiers & InputEvent.CTRL_MASK) != 0) {
96                 buf.append(kUControlGlyph);
97             }
98             if ((modifiers & (InputEvent.ALT_MASK | ALT_GRAPH_MASK)) != 0) {
99                 buf.append(kUOptionGlyph);
100             }
101             if ((modifiers & InputEvent.SHIFT_MASK) != 0) {
102                 buf.append(kUShiftGlyph);
103             }
104             if ((modifiers & InputEvent.META_MASK) != 0) {
105                 buf.append(kUCommandGlyph);
106             }
107         } else {
108             if ((modifiers & InputEvent.META_MASK) != 0) {
109                 buf.append(kUCommandGlyph);
110             }
111             if ((modifiers & InputEvent.SHIFT_MASK) != 0) {
112                 buf.append(kUShiftGlyph);
113             }
114             if ((modifiers & (InputEvent.ALT_MASK | ALT_GRAPH_MASK)) != 0) {
115                 buf.append(kUOptionGlyph);
116             }
117             if ((modifiers & InputEvent.CTRL_MASK) != 0) {
118                 buf.append(kUControlGlyph);
119             }
120         }
121         return buf.toString();
122     }
123 
124     static final RecyclableSingleton<AquaMenuPainter> sPainter = new RecyclableSingletonFromDefaultConstructor<AquaMenuPainter>(AquaMenuPainter.class);
instance()125     static AquaMenuPainter instance() {
126         return sPainter.get();
127     }
128 
129     static final int defaultMenuItemGap = 2;
130     static final int kAcceleratorArrowSpace = 16; // Accel space doesn't overlap arrow space, even though items can't have both
131 
132     static class RecyclableBorder extends RecyclableSingleton<Border> {
133         final String borderName;
RecyclableBorder(final String borderName)134         RecyclableBorder(final String borderName) { this.borderName = borderName; }
getInstance()135         protected Border getInstance() { return UIManager.getBorder(borderName); }
136     }
137 
138     protected final RecyclableBorder menuBarPainter = new RecyclableBorder("MenuBar.backgroundPainter");
139     protected final RecyclableBorder selectedMenuBarItemPainter = new RecyclableBorder("MenuBar.selectedBackgroundPainter");
140     protected final RecyclableBorder selectedMenuItemPainter = new RecyclableBorder("MenuItem.selectedBackgroundPainter");
141 
paintMenuBarBackground(final Graphics g, final int width, final int height, final JComponent c)142     public void paintMenuBarBackground(final Graphics g, final int width, final int height, final JComponent c) {
143         g.setColor(c == null ? Color.white : c.getBackground());
144         g.fillRect(0, 0, width, height);
145         menuBarPainter.get().paintBorder(null, g, 0, 0, width, height);
146     }
147 
paintSelectedMenuTitleBackground(final Graphics g, final int width, final int height)148     public void paintSelectedMenuTitleBackground(final Graphics g, final int width, final int height) {
149         selectedMenuBarItemPainter.get().paintBorder(null, g, -1, 0, width + 2, height);
150     }
151 
paintSelectedMenuItemBackground(final Graphics g, final int width, final int height)152     public void paintSelectedMenuItemBackground(final Graphics g, final int width, final int height) {
153         selectedMenuItemPainter.get().paintBorder(null, g, 0, 0, width, height);
154     }
155 
paintMenuItem(final Client client, final Graphics g, final JComponent c, final Icon checkIcon, final Icon arrowIcon, final Color background, final Color foreground, final Color disabledForeground, final Color selectionForeground, final int defaultTextIconGap, final Font acceleratorFont)156     protected void paintMenuItem(final Client client, final Graphics g, final JComponent c, final Icon checkIcon, final Icon arrowIcon, final Color background, final Color foreground, final Color disabledForeground, final Color selectionForeground, final int defaultTextIconGap, final Font acceleratorFont) {
157         final JMenuItem b = (JMenuItem)c;
158         final ButtonModel model = b.getModel();
159 
160 //        Dimension size = b.getSize();
161         final int menuWidth = b.getWidth();
162         final int menuHeight = b.getHeight();
163         final Insets i = c.getInsets();
164 
165         Rectangle viewRect = new Rectangle(0, 0, menuWidth, menuHeight);
166 
167         viewRect.x += i.left;
168         viewRect.y += i.top;
169         viewRect.width -= (i.right + viewRect.x);
170         viewRect.height -= (i.bottom + viewRect.y);
171 
172         final Font holdf = g.getFont();
173         final Color holdc = g.getColor();
174         final Font f = c.getFont();
175         g.setFont(f);
176         final FontMetrics fm = g.getFontMetrics(f);
177 
178         final FontMetrics fmAccel = g.getFontMetrics(acceleratorFont);
179 
180         // Paint background (doesn't touch the Graphics object's color)
181         if (c.isOpaque()) {
182             client.paintBackground(g, c, menuWidth, menuHeight);
183         }
184 
185         // get Accelerator text
186         final KeyStroke accelerator = b.getAccelerator();
187         String modifiersString = "", keyString = "";
188         final boolean leftToRight = AquaUtils.isLeftToRight(c);
189         if (accelerator != null) {
190             final int modifiers = accelerator.getModifiers();
191             if (modifiers > 0) {
192                 modifiersString = getKeyModifiersText(modifiers, leftToRight);
193             }
194             final int keyCode = accelerator.getKeyCode();
195             if (keyCode != 0) {
196                 keyString = KeyEvent.getKeyText(keyCode);
197             } else {
198                 keyString += accelerator.getKeyChar();
199             }
200         }
201 
202         Rectangle iconRect = new Rectangle();
203         Rectangle textRect = new Rectangle();
204         Rectangle acceleratorRect = new Rectangle();
205         Rectangle checkIconRect = new Rectangle();
206         Rectangle arrowIconRect = new Rectangle();
207 
208         // layout the text and icon
209         final String text = layoutMenuItem(b, fm, b.getText(), fmAccel, keyString, modifiersString, b.getIcon(), checkIcon, arrowIcon, b.getVerticalAlignment(), b.getHorizontalAlignment(), b.getVerticalTextPosition(), b.getHorizontalTextPosition(), viewRect, iconRect, textRect, acceleratorRect, checkIconRect, arrowIconRect, b.getText() == null ? 0 : defaultTextIconGap, defaultTextIconGap);
210 
211         // if this is in a AquaScreenMenuBar that's attached to a DialogPeer
212         // the native menu will be disabled, though the awt Menu won't know about it
213         // so the JPopupMenu will not have visibility set and the items should draw disabled
214         // If it's not on a JPopupMenu then it should just use the model's enable state
215         final Container parent = b.getParent();
216         final boolean parentIsMenuBar = parent instanceof JMenuBar;
217 
218         Container ancestor = parent;
219         while (ancestor != null && !(ancestor instanceof JPopupMenu)) ancestor = ancestor.getParent();
220 
221         boolean isEnabled = model.isEnabled() && (ancestor == null || ancestor.isVisible());
222 
223         // Set the accel/normal text color
224         boolean isSelected = false;
225         if (!isEnabled) {
226             // *** paint the text disabled
227             g.setColor(disabledForeground);
228         } else {
229             // *** paint the text normally
230             if (model.isArmed() || (c instanceof JMenu && model.isSelected())) {
231                 g.setColor(selectionForeground);
232                 isSelected = true;
233             } else {
234                 g.setColor(parentIsMenuBar ? parent.getForeground() : b.getForeground()); // Which is either MenuItem.foreground or the user's choice
235             }
236         }
237 
238         // We want to paint the icon after the text color is set since some icon painting depends on the correct
239         // graphics color being set
240         // See <rdar://problem/3792383> Menu icons missing in Java2D's Lines.Joins demo
241         // Paint the Icon
242         if (b.getIcon() != null) {
243             paintIcon(g, b, iconRect, isEnabled);
244         }
245 
246         // Paint the Check using the current text color
247         if (checkIcon != null) {
248             paintCheck(g, b, checkIcon, checkIconRect);
249         }
250 
251         // Draw the accelerator first in case the HTML renderer changes the color
252         if (keyString != null && !keyString.equals("")) {
253             final int yAccel = acceleratorRect.y + fm.getAscent();
254             if (modifiersString.equals("")) {
255                 // just draw the keyString
256                 SwingUtilities2.drawString(c, g, keyString, acceleratorRect.x, yAccel);
257             } else {
258                 final int modifiers = accelerator.getModifiers();
259                 int underlinedChar = 0;
260                 if ((modifiers & ALT_GRAPH_MASK) > 0) underlinedChar = kUOptionGlyph; // This is a Java2 thing, we won't be getting kOptionGlyph
261                 // The keyStrings should all line up, so always adjust the width by the same amount
262                 // (if they're multi-char, they won't line up but at least they won't be cut off)
263                 final int emWidth = Math.max(fm.charWidth('M'), SwingUtilities.computeStringWidth(fm, keyString));
264 
265                 if (leftToRight) {
266                     g.setFont(acceleratorFont);
267                     drawString(g, c, modifiersString, underlinedChar, acceleratorRect.x, yAccel, isEnabled, isSelected);
268                     g.setFont(f);
269                     SwingUtilities2.drawString(c, g, keyString, acceleratorRect.x + acceleratorRect.width - emWidth, yAccel);
270                 } else {
271                     final int xAccel = acceleratorRect.x + emWidth;
272                     g.setFont(acceleratorFont);
273                     drawString(g, c, modifiersString, underlinedChar, xAccel, yAccel, isEnabled, isSelected);
274                     g.setFont(f);
275                     SwingUtilities2.drawString(c, g, keyString, xAccel - fm.stringWidth(keyString), yAccel);
276                 }
277             }
278         }
279 
280         // Draw the Text
281         if (text != null && !text.equals("")) {
282             final View v = (View)c.getClientProperty(BasicHTML.propertyKey);
283             if (v != null) {
284                 v.paint(g, textRect);
285             } else {
286                 final int mnemonic = (AquaMnemonicHandler.isMnemonicHidden() ? -1 : model.getMnemonic());
287                 drawString(g, c, text, mnemonic, textRect.x, textRect.y + fm.getAscent(), isEnabled, isSelected);
288             }
289         }
290 
291         // Paint the Arrow
292         if (arrowIcon != null) {
293             paintArrow(g, b, model, arrowIcon, arrowIconRect);
294         }
295 
296         g.setColor(holdc);
297         g.setFont(holdf);
298     }
299 
300     // All this had to be copied from BasicMenuItemUI, just to get the right keyModifiersText fn
301     // and a few Mac tweaks
getPreferredMenuItemSize(final JComponent c, final Icon checkIcon, final Icon arrowIcon, final int defaultTextIconGap, final Font acceleratorFont)302     protected Dimension getPreferredMenuItemSize(final JComponent c, final Icon checkIcon, final Icon arrowIcon, final int defaultTextIconGap, final Font acceleratorFont) {
303         final JMenuItem b = (JMenuItem)c;
304         final Icon icon = b.getIcon();
305         final String text = b.getText();
306         final KeyStroke accelerator = b.getAccelerator();
307         String keyString = "", modifiersString = "";
308 
309         if (accelerator != null) {
310             final int modifiers = accelerator.getModifiers();
311             if (modifiers > 0) {
312                 modifiersString = getKeyModifiersText(modifiers, true); // doesn't matter, this is just for metrics
313             }
314             final int keyCode = accelerator.getKeyCode();
315             if (keyCode != 0) {
316                 keyString = KeyEvent.getKeyText(keyCode);
317             } else {
318                 keyString += accelerator.getKeyChar();
319             }
320         }
321 
322         final Font font = b.getFont();
323         final FontMetrics fm = b.getFontMetrics(font);
324         final FontMetrics fmAccel = b.getFontMetrics(acceleratorFont);
325 
326         Rectangle iconRect = new Rectangle();
327         Rectangle textRect = new Rectangle();
328         Rectangle acceleratorRect = new Rectangle();
329         Rectangle checkIconRect = new Rectangle();
330         Rectangle arrowIconRect = new Rectangle();
331         Rectangle viewRect = new Rectangle(Short.MAX_VALUE, Short.MAX_VALUE);
332 
333         layoutMenuItem(b, fm, text, fmAccel, keyString, modifiersString, icon, checkIcon, arrowIcon, b.getVerticalAlignment(), b.getHorizontalAlignment(), b.getVerticalTextPosition(), b.getHorizontalTextPosition(), viewRect, iconRect, textRect, acceleratorRect, checkIconRect, arrowIconRect, text == null ? 0 : defaultTextIconGap, defaultTextIconGap);
334         // find the union of the icon and text rects
335         Rectangle r = new Rectangle();
336         r.setBounds(textRect);
337         r = SwingUtilities.computeUnion(iconRect.x, iconRect.y, iconRect.width, iconRect.height, r);
338         //   r = iconRect.union(textRect);
339 
340         // Add in the accelerator
341         boolean acceleratorTextIsEmpty = (keyString == null) || keyString.equals("");
342 
343         if (!acceleratorTextIsEmpty) {
344             r.width += acceleratorRect.width;
345         }
346 
347         if (!isTopLevelMenu(b)) {
348             // Add in the checkIcon
349             r.width += checkIconRect.width;
350             r.width += defaultTextIconGap;
351 
352             // Add in the arrowIcon space
353             r.width += defaultTextIconGap;
354             r.width += arrowIconRect.width;
355         }
356 
357         final Insets insets = b.getInsets();
358         if (insets != null) {
359             r.width += insets.left + insets.right;
360             r.height += insets.top + insets.bottom;
361         }
362 
363         // Tweak for Mac
364         r.width += 4 + defaultTextIconGap;
365         r.height = Math.max(r.height, 18);
366 
367         return r.getSize();
368     }
369 
paintCheck(final Graphics g, final JMenuItem item, Icon checkIcon, Rectangle checkIconRect)370     protected void paintCheck(final Graphics g, final JMenuItem item, Icon checkIcon, Rectangle checkIconRect) {
371         if (isTopLevelMenu(item) || !item.isSelected()) return;
372 
373         if (item.isArmed() && checkIcon instanceof InvertableIcon) {
374             ((InvertableIcon)checkIcon).getInvertedIcon().paintIcon(item, g, checkIconRect.x, checkIconRect.y);
375         } else {
376             checkIcon.paintIcon(item, g, checkIconRect.x, checkIconRect.y);
377         }
378     }
379 
paintIcon(final Graphics g, final JMenuItem c, final Rectangle localIconRect, boolean isEnabled)380     protected void paintIcon(final Graphics g, final JMenuItem c, final Rectangle localIconRect, boolean isEnabled) {
381         final ButtonModel model = c.getModel();
382         Icon icon;
383         if (!isEnabled) {
384             icon = c.getDisabledIcon();
385         } else if (model.isPressed() && model.isArmed()) {
386             icon = c.getPressedIcon();
387             if (icon == null) {
388                 // Use default icon
389                 icon = c.getIcon();
390             }
391         } else {
392             icon = c.getIcon();
393         }
394 
395         if (icon != null) icon.paintIcon(c, g, localIconRect.x, localIconRect.y);
396     }
397 
paintArrow(Graphics g, JMenuItem c, ButtonModel model, Icon arrowIcon, Rectangle arrowIconRect)398     protected void paintArrow(Graphics g, JMenuItem c, ButtonModel model, Icon arrowIcon, Rectangle arrowIconRect) {
399         if (isTopLevelMenu(c)) return;
400 
401         if (c instanceof JMenu && (model.isArmed() || model.isSelected()) && arrowIcon instanceof InvertableIcon) {
402             ((InvertableIcon)arrowIcon).getInvertedIcon().paintIcon(c, g, arrowIconRect.x, arrowIconRect.y);
403         } else {
404             arrowIcon.paintIcon(c, g, arrowIconRect.x, arrowIconRect.y);
405         }
406     }
407 
408     /** Draw a string with the graphics g at location (x,y) just like g.drawString() would.
409      *  The first occurrence of underlineChar in text will be underlined. The matching is
410      *  not case sensitive.
411      */
drawString(final Graphics g, final JComponent c, final String text, final int underlinedChar, final int x, final int y, final boolean isEnabled, final boolean isSelected)412     public void drawString(final Graphics g, final JComponent c, final String text, final int underlinedChar, final int x, final int y, final boolean isEnabled, final boolean isSelected) {
413         char lc, uc;
414         int index = -1, lci, uci;
415 
416         if (underlinedChar != '\0') {
417             uc = Character.toUpperCase((char)underlinedChar);
418             lc = Character.toLowerCase((char)underlinedChar);
419 
420             uci = text.indexOf(uc);
421             lci = text.indexOf(lc);
422 
423             if (uci == -1) index = lci;
424             else if (lci == -1) index = uci;
425             else index = (lci < uci) ? lci : uci;
426         }
427 
428         SwingUtilities2.drawStringUnderlineCharAt(c, g, text, index, x, y);
429     }
430 
431     /*
432      * Returns false if the component is a JMenu and it is a top
433      * level menu (on the menubar).
434      */
isTopLevelMenu(final JMenuItem menuItem)435     private static boolean isTopLevelMenu(final JMenuItem menuItem) {
436         return (menuItem instanceof JMenu) && (((JMenu)menuItem).isTopLevelMenu());
437     }
438 
layoutMenuItem(final JMenuItem menuItem, final FontMetrics fm, final String text, final FontMetrics fmAccel, String keyString, final String modifiersString, final Icon icon, final Icon checkIcon, final Icon arrowIcon, final int verticalAlignment, final int horizontalAlignment, final int verticalTextPosition, final int horizontalTextPosition, final Rectangle viewR, final Rectangle iconR, final Rectangle textR, final Rectangle acceleratorR, final Rectangle checkIconR, final Rectangle arrowIconR, final int textIconGap, final int menuItemGap)439     private String layoutMenuItem(final JMenuItem menuItem, final FontMetrics fm, final String text, final FontMetrics fmAccel, String keyString, final String modifiersString, final Icon icon, final Icon checkIcon, final Icon arrowIcon, final int verticalAlignment, final int horizontalAlignment, final int verticalTextPosition, final int horizontalTextPosition, final Rectangle viewR, final Rectangle iconR, final Rectangle textR, final Rectangle acceleratorR, final Rectangle checkIconR, final Rectangle arrowIconR, final int textIconGap, final int menuItemGap) {
440         // Force it to do "LEFT", then flip the rects if we're right-to-left
441         SwingUtilities.layoutCompoundLabel(menuItem, fm, text, icon, verticalAlignment, SwingConstants.LEFT, verticalTextPosition, horizontalTextPosition, viewR, iconR, textR, textIconGap);
442 
443         final boolean acceleratorTextIsEmpty = (keyString == null) || keyString.equals("");
444 
445         if (acceleratorTextIsEmpty) {
446             acceleratorR.width = acceleratorR.height = 0;
447             keyString = "";
448         } else {
449             // Accel space doesn't overlap arrow space, even though items can't have both
450             acceleratorR.width = SwingUtilities.computeStringWidth(fmAccel, modifiersString);
451             // The keyStrings should all line up, so always adjust the width by the same amount
452             // (if they're multi-char, they won't line up but at least they won't be cut off)
453             acceleratorR.width += Math.max(fm.charWidth('M'), SwingUtilities.computeStringWidth(fm, keyString));
454             acceleratorR.height = fmAccel.getHeight();
455         }
456 
457         /* Initialize the checkIcon bounds rectangle checkIconR.
458          */
459 
460         final boolean isTopLevelMenu = isTopLevelMenu(menuItem);
461         if (!isTopLevelMenu) {
462             if (checkIcon != null) {
463                 checkIconR.width = checkIcon.getIconWidth();
464                 checkIconR.height = checkIcon.getIconHeight();
465             } else {
466                 checkIconR.width = checkIconR.height = 16;
467             }
468 
469             /* Initialize the arrowIcon bounds rectangle arrowIconR.
470              */
471 
472             if (arrowIcon != null) {
473                 arrowIconR.width = arrowIcon.getIconWidth();
474                 arrowIconR.height = arrowIcon.getIconHeight();
475             } else {
476                 arrowIconR.width = arrowIconR.height = 16;
477             }
478 
479             textR.x += 12;
480             iconR.x += 12;
481         }
482 
483         final Rectangle labelR = iconR.union(textR);
484 
485         // Position the Accelerator text rect
486         // Menu shortcut text *ought* to have the letters left-justified - look at a menu with an "M" in it
487         acceleratorR.x += (viewR.width - arrowIconR.width - acceleratorR.width);
488         acceleratorR.y = viewR.y + (viewR.height / 2) - (acceleratorR.height / 2);
489 
490         if (!isTopLevelMenu) {
491             //    if ( GetSysDirection() < 0 ) hierRect.right = hierRect.left + w + 4;
492             //    else hierRect.left = hierRect.right - w - 4;
493             arrowIconR.x = (viewR.width - arrowIconR.width) + 1;
494             arrowIconR.y = viewR.y + (labelR.height / 2) - (arrowIconR.height / 2) + 1;
495 
496             checkIconR.y = viewR.y + (labelR.height / 2) - (checkIconR.height / 2);
497             checkIconR.x = 5;
498 
499             textR.width += 8;
500         }
501 
502         /*System.out.println("Layout: " +horizontalAlignment+ " v=" +viewR+"  c="+checkIconR+" i="+
503          iconR+" t="+textR+" acc="+acceleratorR+" a="+arrowIconR);*/
504 
505         if (!AquaUtils.isLeftToRight(menuItem)) {
506             // Flip the rectangles so that instead of [check][icon][text][accel/arrow] it's [accel/arrow][text][icon][check]
507             final int w = viewR.width;
508             checkIconR.x = w - (checkIconR.x + checkIconR.width);
509             iconR.x = w - (iconR.x + iconR.width);
510             textR.x = w - (textR.x + textR.width);
511             acceleratorR.x = w - (acceleratorR.x + acceleratorR.width);
512             arrowIconR.x = w - (arrowIconR.x + arrowIconR.width);
513         }
514         textR.x += menuItemGap;
515         iconR.x += menuItemGap;
516 
517         return text;
518     }
519 
getMenuBarPainter()520     public static Border getMenuBarPainter() {
521         final AquaBorder border = new AquaBorder.Default();
522         border.painter.state.set(Widget.MENU_BAR);
523         return border;
524     }
525 
getSelectedMenuBarItemPainter()526     public static Border getSelectedMenuBarItemPainter() {
527         final AquaBorder border = new AquaBorder.Default();
528         border.painter.state.set(Widget.MENU_TITLE);
529         border.painter.state.set(State.PRESSED);
530         return border;
531     }
532 
getSelectedMenuItemPainter()533     public static Border getSelectedMenuItemPainter() {
534         final AquaBorder border = new AquaBorder.Default();
535         border.painter.state.set(Widget.MENU_ITEM);
536         border.painter.state.set(State.PRESSED);
537         return border;
538     }
539 }
540