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
236 isSelected = true;
237 } else {
238 g.setColor(parentIsMenuBar ? parent.getForeground() : b.getForeground()); // Which is either MenuItem.foreground or the user's choice
239 }
240 }
241
242 // We want to paint the icon after the text color is set since some icon painting depends on the correct
243 // graphics color being set
244 // See <rdar://problem/3792383> Menu icons missing in Java2D's Lines.Joins demo
245 // Paint the Icon
246 if (b.getIcon() != null) {
247 paintIcon(g, b, iconRect, isEnabled);
248 }
249
250 // Paint the Check using the current text color
251 if (checkIcon != null) {
252 paintCheck(g, b, checkIcon, checkIconRect);
253 }
254
255 // Draw the accelerator first in case the HTML renderer changes the color
256 if (keyString != null && !keyString.equals("")) {
257 final int yAccel = acceleratorRect.y + fm.getAscent();
258 if (modifiersString.equals("")) {
259 // just draw the keyString
260 SwingUtilities2.drawString(c, g, keyString, acceleratorRect.x, yAccel);
261 } else {
262 final int modifiers = accelerator.getModifiers();
263 int underlinedChar = 0;
264 if ((modifiers & ALT_GRAPH_MASK) > 0) underlinedChar = kUOptionGlyph; // This is a Java2 thing, we won't be getting kOptionGlyph
265 // The keyStrings should all line up, so always adjust the width by the same amount
266 // (if they're multi-char, they won't line up but at least they won't be cut off)
267 final int emWidth = Math.max(fm.charWidth('M'), SwingUtilities.computeStringWidth(fm, keyString));
268
269 if (leftToRight) {
270 g.setFont(acceleratorFont);
271 drawString(g, c, modifiersString, underlinedChar, acceleratorRect.x, yAccel, isEnabled, isSelected);
272 g.setFont(f);
273 SwingUtilities2.drawString(c, g, keyString, acceleratorRect.x + acceleratorRect.width - emWidth, yAccel);
274 } else {
275 final int xAccel = acceleratorRect.x + emWidth;
276 g.setFont(acceleratorFont);
277 drawString(g, c, modifiersString, underlinedChar, xAccel, yAccel, isEnabled, isSelected);
278 g.setFont(f);
279 SwingUtilities2.drawString(c, g, keyString, xAccel - fm.stringWidth(keyString), yAccel);
280 }
281 }
282 }
283
284 // Draw the Text
285 if (text != null && !text.equals("")) {
286 final View v = (View)c.getClientProperty(BasicHTML.propertyKey);
287 if (v != null) {
288 v.paint(g, textRect);
289 } else {
290 final int mnemonic = (AquaMnemonicHandler.isMnemonicHidden() ? -1 : model.getMnemonic());
291 drawString(g, c, text, mnemonic, textRect.x, textRect.y + fm.getAscent(), isEnabled, isSelected);
292 }
293 }
294
295 // Paint the Arrow
296 if (arrowIcon != null) {
297 paintArrow(g, b, model, arrowIcon, arrowIconRect);
298 }
299
300 g.setColor(holdc);
301 g.setFont(holdf);
302 }
303
304 // All this had to be copied from BasicMenuItemUI, just to get the right keyModifiersText fn
305 // and a few Mac tweaks
325
326 final Font font = b.getFont();
327 final FontMetrics fm = b.getFontMetrics(font);
328 final FontMetrics fmAccel = b.getFontMetrics(acceleratorFont);
329
330 Rectangle iconRect = new Rectangle();
331 Rectangle textRect = new Rectangle();
332 Rectangle acceleratorRect = new Rectangle();
333 Rectangle checkIconRect = new Rectangle();
334 Rectangle arrowIconRect = new Rectangle();
335 Rectangle viewRect = new Rectangle(Short.MAX_VALUE, Short.MAX_VALUE);
336
337 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);
338 // find the union of the icon and text rects
339 Rectangle r = new Rectangle();
340 r.setBounds(textRect);
341 r = SwingUtilities.computeUnion(iconRect.x, iconRect.y, iconRect.width, iconRect.height, r);
342 // r = iconRect.union(textRect);
343
344 // Add in the accelerator
345 boolean acceleratorTextIsEmpty = (keyString == null) || keyString.equals("");
346
347 if (!acceleratorTextIsEmpty) {
348 r.width += acceleratorRect.width;
349 }
350
351 if (!isTopLevelMenu(b)) {
352 // Add in the checkIcon
353 r.width += checkIconRect.width;
354 r.width += defaultTextIconGap;
355
356 // Add in the arrowIcon space
357 r.width += defaultTextIconGap;
358 r.width += arrowIconRect.width;
359 }
360
361 final Insets insets = b.getInsets();
362 if (insets != null) {
363 r.width += insets.left + insets.right;
364 r.height += insets.top + insets.bottom;
365 }
427 if (uci == -1) index = lci;
428 else if (lci == -1) index = uci;
429 else index = (lci < uci) ? lci : uci;
430 }
431
432 SwingUtilities2.drawStringUnderlineCharAt(c, g, text, index, x, y);
433 }
434
435 /*
436 * Returns false if the component is a JMenu and it is a top
437 * level menu (on the menubar).
438 */
439 private static boolean isTopLevelMenu(final JMenuItem menuItem) {
440 return (menuItem instanceof JMenu) && (((JMenu)menuItem).isTopLevelMenu());
441 }
442
443 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) {
444 // Force it to do "LEFT", then flip the rects if we're right-to-left
445 SwingUtilities.layoutCompoundLabel(menuItem, fm, text, icon, verticalAlignment, SwingConstants.LEFT, verticalTextPosition, horizontalTextPosition, viewR, iconR, textR, textIconGap);
446
447 final boolean acceleratorTextIsEmpty = (keyString == null) || keyString.equals("");
448
449 if (acceleratorTextIsEmpty) {
450 acceleratorR.width = acceleratorR.height = 0;
451 keyString = "";
452 } else {
453 // Accel space doesn't overlap arrow space, even though items can't have both
454 acceleratorR.width = SwingUtilities.computeStringWidth(fmAccel, modifiersString);
455 // The keyStrings should all line up, so always adjust the width by the same amount
456 // (if they're multi-char, they won't line up but at least they won't be cut off)
457 acceleratorR.width += Math.max(fm.charWidth('M'), SwingUtilities.computeStringWidth(fm, keyString));
458 acceleratorR.height = fmAccel.getHeight();
459 }
460
461 /* Initialize the checkIcon bounds rectangle checkIconR.
462 */
463
464 final boolean isTopLevelMenu = isTopLevelMenu(menuItem);
465 if (!isTopLevelMenu) {
466 if (checkIcon != null) {
467 checkIconR.width = checkIcon.getIconWidth();
|
1 /*
2 * Copyright (c) 2011, 2019, 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
236 isSelected = true;
237 } else {
238 g.setColor(parentIsMenuBar ? parent.getForeground() : b.getForeground()); // Which is either MenuItem.foreground or the user's choice
239 }
240 }
241
242 // We want to paint the icon after the text color is set since some icon painting depends on the correct
243 // graphics color being set
244 // See <rdar://problem/3792383> Menu icons missing in Java2D's Lines.Joins demo
245 // Paint the Icon
246 if (b.getIcon() != null) {
247 paintIcon(g, b, iconRect, isEnabled);
248 }
249
250 // Paint the Check using the current text color
251 if (checkIcon != null) {
252 paintCheck(g, b, checkIcon, checkIconRect);
253 }
254
255 // Draw the accelerator first in case the HTML renderer changes the color
256 if (keyString != null && !keyString.isEmpty()) {
257 final int yAccel = acceleratorRect.y + fm.getAscent();
258 if (modifiersString.isEmpty()) {
259 // just draw the keyString
260 SwingUtilities2.drawString(c, g, keyString, acceleratorRect.x, yAccel);
261 } else {
262 final int modifiers = accelerator.getModifiers();
263 int underlinedChar = 0;
264 if ((modifiers & ALT_GRAPH_MASK) > 0) underlinedChar = kUOptionGlyph; // This is a Java2 thing, we won't be getting kOptionGlyph
265 // The keyStrings should all line up, so always adjust the width by the same amount
266 // (if they're multi-char, they won't line up but at least they won't be cut off)
267 final int emWidth = Math.max(fm.charWidth('M'), SwingUtilities.computeStringWidth(fm, keyString));
268
269 if (leftToRight) {
270 g.setFont(acceleratorFont);
271 drawString(g, c, modifiersString, underlinedChar, acceleratorRect.x, yAccel, isEnabled, isSelected);
272 g.setFont(f);
273 SwingUtilities2.drawString(c, g, keyString, acceleratorRect.x + acceleratorRect.width - emWidth, yAccel);
274 } else {
275 final int xAccel = acceleratorRect.x + emWidth;
276 g.setFont(acceleratorFont);
277 drawString(g, c, modifiersString, underlinedChar, xAccel, yAccel, isEnabled, isSelected);
278 g.setFont(f);
279 SwingUtilities2.drawString(c, g, keyString, xAccel - fm.stringWidth(keyString), yAccel);
280 }
281 }
282 }
283
284 // Draw the Text
285 if (text != null && !text.isEmpty()) {
286 final View v = (View)c.getClientProperty(BasicHTML.propertyKey);
287 if (v != null) {
288 v.paint(g, textRect);
289 } else {
290 final int mnemonic = (AquaMnemonicHandler.isMnemonicHidden() ? -1 : model.getMnemonic());
291 drawString(g, c, text, mnemonic, textRect.x, textRect.y + fm.getAscent(), isEnabled, isSelected);
292 }
293 }
294
295 // Paint the Arrow
296 if (arrowIcon != null) {
297 paintArrow(g, b, model, arrowIcon, arrowIconRect);
298 }
299
300 g.setColor(holdc);
301 g.setFont(holdf);
302 }
303
304 // All this had to be copied from BasicMenuItemUI, just to get the right keyModifiersText fn
305 // and a few Mac tweaks
325
326 final Font font = b.getFont();
327 final FontMetrics fm = b.getFontMetrics(font);
328 final FontMetrics fmAccel = b.getFontMetrics(acceleratorFont);
329
330 Rectangle iconRect = new Rectangle();
331 Rectangle textRect = new Rectangle();
332 Rectangle acceleratorRect = new Rectangle();
333 Rectangle checkIconRect = new Rectangle();
334 Rectangle arrowIconRect = new Rectangle();
335 Rectangle viewRect = new Rectangle(Short.MAX_VALUE, Short.MAX_VALUE);
336
337 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);
338 // find the union of the icon and text rects
339 Rectangle r = new Rectangle();
340 r.setBounds(textRect);
341 r = SwingUtilities.computeUnion(iconRect.x, iconRect.y, iconRect.width, iconRect.height, r);
342 // r = iconRect.union(textRect);
343
344 // Add in the accelerator
345 boolean acceleratorTextIsEmpty = (keyString == null) || keyString.isEmpty();
346
347 if (!acceleratorTextIsEmpty) {
348 r.width += acceleratorRect.width;
349 }
350
351 if (!isTopLevelMenu(b)) {
352 // Add in the checkIcon
353 r.width += checkIconRect.width;
354 r.width += defaultTextIconGap;
355
356 // Add in the arrowIcon space
357 r.width += defaultTextIconGap;
358 r.width += arrowIconRect.width;
359 }
360
361 final Insets insets = b.getInsets();
362 if (insets != null) {
363 r.width += insets.left + insets.right;
364 r.height += insets.top + insets.bottom;
365 }
427 if (uci == -1) index = lci;
428 else if (lci == -1) index = uci;
429 else index = (lci < uci) ? lci : uci;
430 }
431
432 SwingUtilities2.drawStringUnderlineCharAt(c, g, text, index, x, y);
433 }
434
435 /*
436 * Returns false if the component is a JMenu and it is a top
437 * level menu (on the menubar).
438 */
439 private static boolean isTopLevelMenu(final JMenuItem menuItem) {
440 return (menuItem instanceof JMenu) && (((JMenu)menuItem).isTopLevelMenu());
441 }
442
443 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) {
444 // Force it to do "LEFT", then flip the rects if we're right-to-left
445 SwingUtilities.layoutCompoundLabel(menuItem, fm, text, icon, verticalAlignment, SwingConstants.LEFT, verticalTextPosition, horizontalTextPosition, viewR, iconR, textR, textIconGap);
446
447 final boolean acceleratorTextIsEmpty = (keyString == null) || keyString.isEmpty();
448
449 if (acceleratorTextIsEmpty) {
450 acceleratorR.width = acceleratorR.height = 0;
451 keyString = "";
452 } else {
453 // Accel space doesn't overlap arrow space, even though items can't have both
454 acceleratorR.width = SwingUtilities.computeStringWidth(fmAccel, modifiersString);
455 // The keyStrings should all line up, so always adjust the width by the same amount
456 // (if they're multi-char, they won't line up but at least they won't be cut off)
457 acceleratorR.width += Math.max(fm.charWidth('M'), SwingUtilities.computeStringWidth(fm, keyString));
458 acceleratorR.height = fmAccel.getHeight();
459 }
460
461 /* Initialize the checkIcon bounds rectangle checkIconR.
462 */
463
464 final boolean isTopLevelMenu = isTopLevelMenu(menuItem);
465 if (!isTopLevelMenu) {
466 if (checkIcon != null) {
467 checkIconR.width = checkIcon.getIconWidth();
|