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