001 /*
002 * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025 package javax.swing.plaf.synth;
026
027 import javax.swing.plaf.basic.BasicHTML;
028 import java.awt.*;
029 import java.awt.event.*;
030 import java.beans.PropertyChangeEvent;
031 import java.beans.PropertyChangeListener;
032
033 import javax.swing.*;
034 import javax.swing.event.*;
035 import javax.swing.border.*;
036 import javax.swing.plaf.*;
037 import javax.swing.plaf.basic.*;
038 import javax.swing.text.View;
039 import sun.swing.plaf.synth.*;
040 import sun.swing.SwingUtilities2;
041
042 /**
043 * Synth's MenuItemUI.
044 *
045 * @version 1.25, 01/25/05
046 * @author Georges Saab
047 * @author David Karlton
048 * @author Arnaud Weber
049 * @author Fredrik Lagerblad
050 */
051 class SynthMenuItemUI extends BasicMenuItemUI implements
052 PropertyChangeListener, SynthUI {
053 private SynthStyle style;
054 private SynthStyle accStyle;
055
056 private String acceleratorDelimiter;
057
058 public static ComponentUI createUI(JComponent c) {
059 return new SynthMenuItemUI();
060 }
061
062 //
063 // The next handful of static methods are used by both SynthMenuUI
064 // and SynthMenuItemUI. This is necessitated by SynthMenuUI not
065 // extending SynthMenuItemUI.
066 //
067
068 /*
069 * All JMenuItems (and JMenus) include enough space for the insets
070 * plus one or more elements. When we say "icon(s)" below, we mean
071 * "check/radio indicator and/or user icon." If both are defined for
072 * a given menu item, then in a LTR orientation the check/radio indicator
073 * is on the left side followed by the user icon to the right; it is
074 * just the opposite in a RTL orientation.
075 *
076 * Cases to consider for SynthMenuItemUI (visualized here in a
077 * LTR orientation; the RTL case would be reversed):
078 * text
079 * icon(s) + text
080 * icon(s) + text + accelerator
081 * text + accelerator
082 *
083 * Cases to consider for SynthMenuUI (again visualized here in a
084 * LTR orientation):
085 * text + arrow
086 * (user)icon + text + arrow
087 *
088 * Note that in the above scenarios, accelerator and arrow icon are
089 * mutually exclusive. This means that if a popup menu contains a mix
090 * of JMenus and JMenuItems, we only need to allow enough space for
091 * max(maxAccelerator, maxArrow), and both accelerators and arrow icons
092 * can occupy the same "column" of space in the menu.
093 *
094 * A quick note about how preferred sizes are calculated... Generally
095 * speaking, SynthPopupMenuUI will run through the list of its children
096 * (from top to bottom) and ask each for its preferred size. Each menu
097 * item will add up the max width of each element (icons, text,
098 * accelerator spacing, accelerator text or arrow icon) encountered thus
099 * far, so by the time all menu items have been calculated, we will
100 * know the maximum (preferred) menu item size for that popup menu.
101 * Later when it comes time to paint each menu item, we can use those
102 * same accumulated max element sizes in order to layout the item.
103 */
104 static Dimension getPreferredMenuItemSize(SynthContext context,
105 SynthContext accContext, JComponent c, Icon checkIcon,
106 Icon arrowIcon, int defaultTextIconGap,
107 String acceleratorDelimiter) {
108 JMenuItem b = (JMenuItem) c;
109 Icon icon = (Icon) b.getIcon();
110 String text = b.getText();
111 KeyStroke accelerator = b.getAccelerator();
112 String acceleratorText = "";
113
114 if (accelerator != null) {
115 int modifiers = accelerator.getModifiers();
116 if (modifiers > 0) {
117 acceleratorText = KeyEvent
118 .getKeyModifiersText(modifiers);
119 acceleratorText += acceleratorDelimiter;
120 }
121 int keyCode = accelerator.getKeyCode();
122 if (keyCode != 0) {
123 acceleratorText += KeyEvent.getKeyText(keyCode);
124 } else {
125 acceleratorText += accelerator.getKeyChar();
126 }
127 }
128
129 Font font = context.getStyle().getFont(context);
130 FontMetrics fm = b.getFontMetrics(font);
131 FontMetrics fmAccel = b.getFontMetrics(accContext.getStyle()
132 .getFont(accContext));
133
134 resetRects();
135
136 layoutMenuItem(context, fm, accContext, text, fmAccel,
137 acceleratorText, icon, checkIcon, arrowIcon, b
138 .getVerticalAlignment(), b
139 .getHorizontalAlignment(), b
140 .getVerticalTextPosition(), b
141 .getHorizontalTextPosition(), viewRect,
142 iconRect, textRect, acceleratorRect, checkIconRect,
143 arrowIconRect, text == null ? 0 : defaultTextIconGap,
144 defaultTextIconGap);
145
146 r.setBounds(textRect);
147
148 int totalIconWidth = 0;
149 int maxIconHeight = 0;
150 if (icon != null) {
151 // Add in the user icon
152 totalIconWidth += iconRect.width;
153 if (textRect.width > 0) {
154 // Allow for some room between the user icon and the text
155 totalIconWidth += defaultTextIconGap;
156 }
157 maxIconHeight = Math.max(iconRect.height, maxIconHeight);
158 }
159 if (checkIcon != null) {
160 // Add in the checkIcon
161 totalIconWidth += checkIconRect.width;
162 if (textRect.width > 0 || icon != null) {
163 // Allow for some room between the check/radio indicator
164 // and the text (or user icon, if both are specified)
165 totalIconWidth += defaultTextIconGap;
166 }
167 maxIconHeight = Math.max(checkIconRect.height,
168 maxIconHeight);
169 }
170
171 int arrowWidth = 0;
172 if (arrowIcon != null) {
173 // Add in the arrowIcon
174 arrowWidth += defaultTextIconGap;
175 arrowWidth += arrowIconRect.width;
176 maxIconHeight = Math.max(arrowIconRect.height,
177 maxIconHeight);
178 }
179
180 int accelSpacing = 0;
181 if (acceleratorRect.width > 0) {
182 // Allow for some room between the text and the accelerator
183 accelSpacing += 4 * defaultTextIconGap;
184 }
185
186 // Take text and all icons into account when determining height
187 r.height = Math.max(r.height, maxIconHeight);
188
189 // To make the accelerator texts appear in a column,
190 // find the widest MenuItem text and the widest accelerator text.
191
192 // Get the parent, which stores the information.
193 Container parent = b.getParent();
194
195 if (parent instanceof JPopupMenu) {
196 SynthPopupMenuUI popupUI = (SynthPopupMenuUI) SynthLookAndFeel
197 .getUIOfType(((JPopupMenu) parent).getUI(),
198 SynthPopupMenuUI.class);
199
200 if (popupUI != null) {
201 // This gives us the widest MenuItem text encountered thus
202 // far in the parent JPopupMenu
203 r.width = popupUI.adjustTextWidth(r.width);
204
205 // Add in the widest icon (includes both user and
206 // check/radio icons) encountered thus far
207 r.width += popupUI.adjustIconWidth(totalIconWidth);
208
209 // Add in the widest text/accelerator spacing
210 // encountered thus far
211 r.width += popupUI
212 .adjustAccelSpacingWidth(accelSpacing);
213
214 // Add in the widest accelerator text (or arrow)
215 // encountered thus far (at least one of these values
216 // will always be zero, so we combine them here to
217 // avoid double counting)
218 int totalAccelOrArrow = acceleratorRect.width
219 + arrowWidth;
220 r.width += popupUI
221 .adjustAcceleratorWidth(totalAccelOrArrow);
222 }
223 } else if (parent != null
224 && !(b instanceof JMenu && ((JMenu) b).isTopLevelMenu())) {
225 r.width += totalIconWidth + accelSpacing
226 + acceleratorRect.width + arrowWidth;
227 }
228
229 Insets insets = b.getInsets();
230 if (insets != null) {
231 r.width += insets.left + insets.right;
232 r.height += insets.top + insets.bottom;
233 }
234
235 // if the width is even, bump it up one. This is critical
236 // for the focus dash line to draw properly
237 if (r.width % 2 == 0) {
238 r.width++;
239 }
240
241 // if the height is even, bump it up one. This is critical
242 // for the text to center properly
243 if (r.height % 2 == 0) {
244 r.height++;
245 }
246 return r.getSize();
247 }
248
249 static void paint(SynthContext context, SynthContext accContext,
250 Graphics g, Icon checkIcon, Icon arrowIcon,
251 String acceleratorDelimiter, int defaultTextIconGap) {
252 JComponent c = context.getComponent();
253 JMenuItem b = (JMenuItem) c;
254 ButtonModel model = b.getModel();
255 Insets i = b.getInsets();
256
257 resetRects();
258
259 viewRect.setBounds(0, 0, b.getWidth(), b.getHeight());
260
261 viewRect.x += i.left;
262 viewRect.y += i.top;
263 viewRect.width -= (i.right + viewRect.x);
264 viewRect.height -= (i.bottom + viewRect.y);
265
266 SynthStyle style = context.getStyle();
267 Font f = style.getFont(context);
268 g.setFont(f);
269 FontMetrics fm = SwingUtilities2.getFontMetrics(c, g, f);
270 FontMetrics accFM = SwingUtilities2.getFontMetrics(c, g,
271 accContext.getStyle().getFont(accContext));
272
273 // get Accelerator text
274 KeyStroke accelerator = b.getAccelerator();
275 String acceleratorText = "";
276 if (accelerator != null) {
277 int modifiers = accelerator.getModifiers();
278 if (modifiers > 0) {
279 acceleratorText = KeyEvent
280 .getKeyModifiersText(modifiers);
281 acceleratorText += acceleratorDelimiter;
282 }
283
284 int keyCode = accelerator.getKeyCode();
285 if (keyCode != 0) {
286 acceleratorText += KeyEvent.getKeyText(keyCode);
287 } else {
288 acceleratorText += accelerator.getKeyChar();
289 }
290 }
291
292 // Layout the text and icon
293 String text = layoutMenuItem(context, fm, accContext, b
294 .getText(), accFM, acceleratorText, b.getIcon(),
295 checkIcon, arrowIcon, b.getVerticalAlignment(), b
296 .getHorizontalAlignment(), b
297 .getVerticalTextPosition(), b
298 .getHorizontalTextPosition(), viewRect,
299 iconRect, textRect, acceleratorRect, checkIconRect,
300 arrowIconRect, b.getText() == null ? 0
301 : defaultTextIconGap, defaultTextIconGap);
302
303 // Paint the Check
304 if (checkIcon != null) {
305 SynthIcon.paintIcon(checkIcon, context, g, checkIconRect.x,
306 checkIconRect.y, checkIconRect.width,
307 checkIconRect.height);
308 }
309
310 // Paint the Icon
311 if (b.getIcon() != null) {
312 Icon icon;
313 if (!model.isEnabled()) {
314 icon = (Icon) b.getDisabledIcon();
315 } else if (model.isPressed() && model.isArmed()) {
316 icon = (Icon) b.getPressedIcon();
317 if (icon == null) {
318 // Use default icon
319 icon = (Icon) b.getIcon();
320 }
321 } else {
322 icon = (Icon) b.getIcon();
323 }
324
325 if (icon != null) {
326 SynthIcon.paintIcon(icon, context, g, iconRect.x,
327 iconRect.y, iconRect.width, iconRect.height);
328 }
329 }
330
331 // Draw the Text
332 if (text != null) {
333 View v = (View) c.getClientProperty(BasicHTML.propertyKey);
334 if (v != null) {
335 v.paint(g, textRect);
336 } else {
337 g.setColor(style.getColor(context,
338 ColorType.TEXT_FOREGROUND));
339 g.setFont(style.getFont(context));
340 style.getGraphicsUtils(context).paintText(context, g,
341 text, textRect.x, textRect.y,
342 b.getDisplayedMnemonicIndex());
343 }
344 }
345
346 // Draw the Accelerator Text
347 if (acceleratorText != null && !acceleratorText.equals("")) {
348 // Get the maxAccWidth from the parent to calculate the offset.
349 int accOffset = 0;
350 Container parent = b.getParent();
351 if (parent != null && parent instanceof JPopupMenu) {
352 SynthPopupMenuUI popupUI = (SynthPopupMenuUI) ((JPopupMenu) parent)
353 .getUI();
354
355 // Note that we can only get here for SynthMenuItemUI
356 // (not SynthMenuUI) since acceleratorText is defined,
357 // so this cast should be safe
358 SynthMenuItemUI miUI = (SynthMenuItemUI) SynthLookAndFeel
359 .getUIOfType(b.getUI(), SynthMenuItemUI.class);
360
361 if (popupUI != null && miUI != null) {
362 String prop = miUI.getPropertyPrefix()
363 + ".alignAcceleratorText";
364 boolean align = style.getBoolean(context, prop,
365 true);
366
367 // Calculate the offset, with which the accelerator texts
368 // will be drawn.
369 if (align) {
370 // When align==true and we're in the LTR case,
371 // we add an offset here so that all accelerators
372 // will be left-justified in their own column.
373 int max = popupUI.getMaxAcceleratorWidth();
374 if (max > 0) {
375 accOffset = max - acceleratorRect.width;
376 if (!SynthLookAndFeel.isLeftToRight(c)) {
377 // In the RTL, flip the sign so that all
378 // accelerators will be right-justified.
379 accOffset = -accOffset;
380 }
381 }
382 } //else {
383 // Don't need to do anything special here; in the
384 // LTR case, the accelerator is already justified
385 // against the right edge of the menu (and against
386 // the left edge in the RTL case).
387 //}
388 }
389 }
390
391 SynthStyle accStyle = accContext.getStyle();
392
393 g.setColor(accStyle.getColor(accContext,
394 ColorType.TEXT_FOREGROUND));
395 g.setFont(accStyle.getFont(accContext));
396 accStyle.getGraphicsUtils(accContext).paintText(accContext,
397 g, acceleratorText, acceleratorRect.x - accOffset,
398 acceleratorRect.y, -1);
399 }
400
401 // Paint the Arrow
402 if (arrowIcon != null) {
403 SynthIcon.paintIcon(arrowIcon, context, g, arrowIconRect.x,
404 arrowIconRect.y, arrowIconRect.width,
405 arrowIconRect.height);
406 }
407 }
408
409 /**
410 * Compute and return the location of the icons origin, the
411 * location of origin of the text baseline, and a possibly clipped
412 * version of the compound labels string. Locations are computed
413 * relative to the viewRect rectangle.
414 */
415
416 private static String layoutMenuItem(SynthContext context,
417 FontMetrics fm, SynthContext accContext, String text,
418 FontMetrics fmAccel, String acceleratorText, Icon icon,
419 Icon checkIcon, Icon arrowIcon, int verticalAlignment,
420 int horizontalAlignment, int verticalTextPosition,
421 int horizontalTextPosition, Rectangle viewRect,
422 Rectangle iconRect, Rectangle textRect,
423 Rectangle acceleratorRect, Rectangle checkIconRect,
424 Rectangle arrowIconRect, int textIconGap, int menuItemGap) {
425 // If parent is JPopupMenu, get and store it's UI
426 SynthPopupMenuUI popupUI = null;
427 JComponent b = context.getComponent();
428 Container parent = b.getParent();
429 if (parent instanceof JPopupMenu) {
430 popupUI = (SynthPopupMenuUI) SynthLookAndFeel.getUIOfType(
431 ((JPopupMenu) parent).getUI(),
432 SynthPopupMenuUI.class);
433 }
434
435 context.getStyle().getGraphicsUtils(context).layoutText(
436 context, fm, text, icon, horizontalAlignment,
437 verticalAlignment, horizontalTextPosition,
438 verticalTextPosition, viewRect, iconRect, textRect,
439 textIconGap);
440
441 /* Initialize the acceleratorText bounds rectangle textRect. If a null
442 * or and empty String was specified we substitute "" here
443 * and use 0,0,0,0 for acceleratorTextRect.
444 */
445 if ((acceleratorText == null) || acceleratorText.equals("")) {
446 acceleratorRect.width = acceleratorRect.height = 0;
447 acceleratorText = "";
448 } else {
449 SynthStyle style = accContext.getStyle();
450 acceleratorRect.width = style.getGraphicsUtils(accContext)
451 .computeStringWidth(accContext, fmAccel.getFont(),
452 fmAccel, acceleratorText);
453 acceleratorRect.height = fmAccel.getHeight();
454 }
455
456 // Initialize the checkIcon bounds rectangle width & height.
457 if (checkIcon != null) {
458 checkIconRect.width = SynthIcon.getIconWidth(checkIcon,
459 context);
460 checkIconRect.height = SynthIcon.getIconHeight(checkIcon,
461 context);
462 } else {
463 checkIconRect.width = checkIconRect.height = 0;
464 }
465
466 // Initialize the arrowIcon bounds rectangle width & height.
467 if (arrowIcon != null) {
468 arrowIconRect.width = SynthIcon.getIconWidth(arrowIcon,
469 context);
470 arrowIconRect.height = SynthIcon.getIconHeight(arrowIcon,
471 context);
472 } else {
473 arrowIconRect.width = arrowIconRect.height = 0;
474 }
475
476 // Note: layoutText() has already left room for
477 // the user icon, so no need to adjust textRect below
478 // to account for the user icon. However, we do have to
479 // reposition textRect when the check icon is visible.
480
481 Rectangle labelRect = iconRect.union(textRect);
482 if (SynthLookAndFeel.isLeftToRight(context.getComponent())) {
483 // Position the check and user icons
484 iconRect.x = viewRect.x;
485 if (checkIcon != null) {
486 checkIconRect.x = viewRect.x;
487 iconRect.x += menuItemGap + checkIconRect.width;
488 textRect.x += menuItemGap + checkIconRect.width;
489 }
490
491 // Position the arrow icon
492 arrowIconRect.x = viewRect.x + viewRect.width
493 - arrowIconRect.width;
494
495 // Position the accelerator text rect
496 acceleratorRect.x = viewRect.x + viewRect.width
497 - acceleratorRect.width;
498
499 /* Align icons and text horizontally */
500 if (popupUI != null) {
501 int this TextOffset = popupUI
502 .adjustTextOffset(textRect.x - viewRect.x);
503 textRect.x = this TextOffset + viewRect.x;
504
505 if (icon != null) {
506 // REMIND: The following code currently assumes the
507 // default (TRAILING) horizontalTextPosition, which means
508 // it will always place the icon to the left of the text.
509 // Other values of horizontalTextPosition aren't very
510 // useful for menu items, so we ignore them for now, but
511 // someday we might want to fix this situation.
512 int this IconOffset = popupUI
513 .adjustIconOffset(iconRect.x - viewRect.x);
514 iconRect.x = this IconOffset + viewRect.x;
515 }
516 }
517 } else {
518 // Position the accelerator text rect
519 acceleratorRect.x = viewRect.x;
520
521 // Position the arrow icon
522 arrowIconRect.x = viewRect.x;
523
524 // Position the check and user icons
525 iconRect.x = viewRect.x + viewRect.width - iconRect.width;
526 if (checkIcon != null) {
527 checkIconRect.x = viewRect.x + viewRect.width
528 - checkIconRect.width;
529 textRect.x -= menuItemGap + checkIconRect.width;
530 iconRect.x -= menuItemGap + checkIconRect.width;
531 }
532
533 /* Align icons and text horizontally */
534 if (popupUI != null) {
535 int this TextOffset = viewRect.x + viewRect.width
536 - textRect.x - textRect.width;
537 this TextOffset = popupUI
538 .adjustTextOffset(this TextOffset);
539 textRect.x = viewRect.x + viewRect.width
540 - this TextOffset - textRect.width;
541 if (icon != null) {
542 // REMIND: The following code currently assumes the
543 // default (TRAILING) horizontalTextPosition, which means
544 // it will always place the icon to the right of the text.
545 // Other values of horizontalTextPosition aren't very
546 // useful for menu items, so we ignore them for now, but
547 // someday we might want to fix this situation.
548 int this IconOffset = viewRect.x + viewRect.width
549 - iconRect.x - iconRect.width;
550 this IconOffset = popupUI
551 .adjustIconOffset(this IconOffset);
552 iconRect.x = viewRect.x + viewRect.width
553 - this IconOffset - iconRect.width;
554 }
555 }
556 }
557
558 // Align the accelerator text and all icons vertically
559 // with the center of the label rect.
560 int midY = labelRect.y + (labelRect.height / 2);
561 iconRect.y = midY - (iconRect.height / 2);
562 acceleratorRect.y = midY - (acceleratorRect.height / 2);
563 arrowIconRect.y = midY - (arrowIconRect.height / 2);
564 checkIconRect.y = midY - (checkIconRect.height / 2);
565
566 return text;
567 }
568
569 // these rects are used for painting and preferredsize calculations.
570 // they used to be regenerated constantly. Now they are reused.
571 static Rectangle iconRect = new Rectangle();
572 static Rectangle textRect = new Rectangle();
573 static Rectangle acceleratorRect = new Rectangle();
574 static Rectangle checkIconRect = new Rectangle();
575 static Rectangle arrowIconRect = new Rectangle();
576 static Rectangle viewRect = new Rectangle(Short.MAX_VALUE,
577 Short.MAX_VALUE);
578 static Rectangle r = new Rectangle();
579
580 private static void resetRects() {
581 iconRect.setBounds(0, 0, 0, 0);
582 textRect.setBounds(0, 0, 0, 0);
583 acceleratorRect.setBounds(0, 0, 0, 0);
584 checkIconRect.setBounds(0, 0, 0, 0);
585 arrowIconRect.setBounds(0, 0, 0, 0);
586 viewRect.setBounds(0, 0, Short.MAX_VALUE, Short.MAX_VALUE);
587 r.setBounds(0, 0, 0, 0);
588 }
589
590 protected void installDefaults() {
591 updateStyle(menuItem);
592 }
593
594 protected void installListeners() {
595 super .installListeners();
596 menuItem.addPropertyChangeListener(this );
597 }
598
599 private void updateStyle(JMenuItem mi) {
600 SynthContext context = getContext(mi, ENABLED);
601 SynthStyle oldStyle = style;
602
603 style = SynthLookAndFeel.updateStyle(context, this );
604 if (oldStyle != style) {
605 String prefix = getPropertyPrefix();
606
607 Object value = style.get(context, prefix + ".textIconGap");
608 if (value != null) {
609 LookAndFeel.installProperty(mi, "iconTextGap", value);
610 }
611 defaultTextIconGap = mi.getIconTextGap();
612
613 if (menuItem.getMargin() == null
614 || (menuItem.getMargin() instanceof UIResource)) {
615 Insets insets = (Insets) style.get(context, prefix
616 + ".margin");
617
618 if (insets == null) {
619 // Some places assume margins are non-null.
620 insets = SynthLookAndFeel.EMPTY_UIRESOURCE_INSETS;
621 }
622 menuItem.setMargin(insets);
623 }
624 acceleratorDelimiter = style.getString(context, prefix
625 + ".acceleratorDelimiter", "+");
626
627 arrowIcon = style.getIcon(context, prefix + ".arrowIcon");
628
629 checkIcon = style.getIcon(context, prefix + ".checkIcon");
630 if (oldStyle != null) {
631 uninstallKeyboardActions();
632 installKeyboardActions();
633 }
634 }
635 context.dispose();
636
637 SynthContext accContext = getContext(mi,
638 Region.MENU_ITEM_ACCELERATOR, ENABLED);
639
640 accStyle = SynthLookAndFeel.updateStyle(accContext, this );
641 accContext.dispose();
642 }
643
644 protected void uninstallDefaults() {
645 SynthContext context = getContext(menuItem, ENABLED);
646 style.uninstallDefaults(context);
647 context.dispose();
648 style = null;
649
650 SynthContext accContext = getContext(menuItem,
651 Region.MENU_ITEM_ACCELERATOR, ENABLED);
652 accStyle.uninstallDefaults(accContext);
653 accContext.dispose();
654 accStyle = null;
655
656 super .uninstallDefaults();
657 }
658
659 protected void uninstallListeners() {
660 super .uninstallListeners();
661 menuItem.removePropertyChangeListener(this );
662 }
663
664 public SynthContext getContext(JComponent c) {
665 return getContext(c, getComponentState(c));
666 }
667
668 SynthContext getContext(JComponent c, int state) {
669 return SynthContext.getContext(SynthContext.class, c,
670 SynthLookAndFeel.getRegion(c), style, state);
671 }
672
673 public SynthContext getContext(JComponent c, Region region) {
674 return getContext(c, region, getComponentState(c, region));
675 }
676
677 private SynthContext getContext(JComponent c, Region region,
678 int state) {
679 return SynthContext.getContext(SynthContext.class, c, region,
680 accStyle, state);
681 }
682
683 private Region getRegion(JComponent c) {
684 return SynthLookAndFeel.getRegion(c);
685 }
686
687 private int getComponentState(JComponent c) {
688 int state;
689
690 if (!c.isEnabled()) {
691 state = DISABLED;
692 } else if (menuItem.isArmed()) {
693 state = MOUSE_OVER;
694 } else {
695 state = SynthLookAndFeel.getComponentState(c);
696 }
697 if (menuItem.isSelected()) {
698 state |= SELECTED;
699 }
700 return state;
701 }
702
703 private int getComponentState(JComponent c, Region region) {
704 return getComponentState(c);
705 }
706
707 protected Dimension getPreferredMenuItemSize(JComponent c,
708 Icon checkIcon, Icon arrowIcon, int defaultTextIconGap) {
709 SynthContext context = getContext(c);
710 SynthContext accContext = getContext(c,
711 Region.MENU_ITEM_ACCELERATOR);
712 Dimension value = getPreferredMenuItemSize(context, accContext,
713 c, checkIcon, arrowIcon, defaultTextIconGap,
714 acceleratorDelimiter);
715 context.dispose();
716 accContext.dispose();
717 return value;
718 }
719
720 public void update(Graphics g, JComponent c) {
721 SynthContext context = getContext(c);
722
723 SynthLookAndFeel.update(context, g);
724 paintBackground(context, g, c);
725 paint(context, g);
726 context.dispose();
727 }
728
729 public void paint(Graphics g, JComponent c) {
730 SynthContext context = getContext(c);
731
732 paint(context, g);
733 context.dispose();
734 }
735
736 protected void paint(SynthContext context, Graphics g) {
737 SynthContext accContext = getContext(menuItem,
738 Region.MENU_ITEM_ACCELERATOR);
739
740 // Refetch the appropriate check indicator for the current state
741 String prefix = getPropertyPrefix();
742 Icon checkIcon = style.getIcon(context, prefix + ".checkIcon");
743 Icon arrowIcon = style.getIcon(context, prefix + ".arrowIcon");
744 paint(context, accContext, g, checkIcon, arrowIcon,
745 acceleratorDelimiter, defaultTextIconGap);
746 accContext.dispose();
747 }
748
749 void paintBackground(SynthContext context, Graphics g, JComponent c) {
750 context.getPainter().paintMenuItemBackground(context, g, 0, 0,
751 c.getWidth(), c.getHeight());
752 }
753
754 public void paintBorder(SynthContext context, Graphics g, int x,
755 int y, int w, int h) {
756 context.getPainter()
757 .paintMenuItemBorder(context, g, x, y, w, h);
758 }
759
760 public void propertyChange(PropertyChangeEvent e) {
761 if (SynthLookAndFeel.shouldUpdateStyle(e)) {
762 updateStyle((JMenuItem) e.getSource());
763 }
764 }
765 }
|