History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/swing/MenuItemLayoutHelper.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/swing/MenuItemLayoutHelper.java+1323−0
@@ -0,0 +1,1323 @@
1+/*
2+ * Copyright (c) 2002, 2008, 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 sun.swing;
27+
28+import static sun.swing.SwingUtilities2.BASICMENUITEMUI_MAX_TEXT_OFFSET;
29+
30+import javax.swing.*;
31+import javax.swing.plaf.basic.BasicHTML;
32+import javax.swing.text.View;
33+import java.awt.*;
34+import java.awt.event.KeyEvent;
35+import java.util.Map;
36+import java.util.HashMap;
37+
38+/**
39+ * Calculates preferred size and layouts menu items.
40+ */
41+public class MenuItemLayoutHelper {
42+
43+ /* Client Property keys for calculation of maximal widths */
44+ public static final StringUIClientPropertyKey MAX_ARROW_WIDTH =
45+ new StringUIClientPropertyKey("maxArrowWidth");
46+ public static final StringUIClientPropertyKey MAX_CHECK_WIDTH =
47+ new StringUIClientPropertyKey("maxCheckWidth");
48+ public static final StringUIClientPropertyKey MAX_ICON_WIDTH =
49+ new StringUIClientPropertyKey("maxIconWidth");
50+ public static final StringUIClientPropertyKey MAX_TEXT_WIDTH =
51+ new StringUIClientPropertyKey("maxTextWidth");
52+ public static final StringUIClientPropertyKey MAX_ACC_WIDTH =
53+ new StringUIClientPropertyKey("maxAccWidth");
54+ public static final StringUIClientPropertyKey MAX_LABEL_WIDTH =
55+ new StringUIClientPropertyKey("maxLabelWidth");
56+
57+ private JMenuItem mi;
58+ private JComponent miParent;
59+
60+ private Font font;
61+ private Font accFont;
62+ private FontMetrics fm;
63+ private FontMetrics accFm;
64+
65+ private Icon icon;
66+ private Icon checkIcon;
67+ private Icon arrowIcon;
68+ private String text;
69+ private String accText;
70+
71+ private boolean isColumnLayout;
72+ private boolean useCheckAndArrow;
73+ private boolean isLeftToRight;
74+ private boolean isTopLevelMenu;
75+ private View htmlView;
76+
77+ private int verticalAlignment;
78+ private int horizontalAlignment;
79+ private int verticalTextPosition;
80+ private int horizontalTextPosition;
81+ private int gap;
82+ private int leadingGap;
83+ private int afterCheckIconGap;
84+ private int minTextOffset;
85+
86+ private int leftTextExtraWidth;
87+
88+ private Rectangle viewRect;
89+
90+ private RectSize iconSize;
91+ private RectSize textSize;
92+ private RectSize accSize;
93+ private RectSize checkSize;
94+ private RectSize arrowSize;
95+ private RectSize labelSize;
96+
97+ /**
98+ * The empty protected constructor is necessary for derived classes.
99+ */
100+ protected MenuItemLayoutHelper() {
101+ }
102+
103+ public MenuItemLayoutHelper(JMenuItem mi, Icon checkIcon, Icon arrowIcon,
104+ Rectangle viewRect, int gap, String accDelimiter,
105+ boolean isLeftToRight, Font font, Font accFont,
106+ boolean useCheckAndArrow, String propertyPrefix) {
107+ reset(mi, checkIcon, arrowIcon, viewRect, gap, accDelimiter,
108+ isLeftToRight, font, accFont, useCheckAndArrow, propertyPrefix);
109+ }
110+
111+ protected void reset(JMenuItem mi, Icon checkIcon, Icon arrowIcon,
112+ Rectangle viewRect, int gap, String accDelimiter,
113+ boolean isLeftToRight, Font font, Font accFont,
114+ boolean useCheckAndArrow, String propertyPrefix) {
115+ this.mi = mi;
116+ this.miParent = getMenuItemParent(mi);
117+ this.accText = getAccText(accDelimiter);
118+ this.verticalAlignment = mi.getVerticalAlignment();
119+ this.horizontalAlignment = mi.getHorizontalAlignment();
120+ this.verticalTextPosition = mi.getVerticalTextPosition();
121+ this.horizontalTextPosition = mi.getHorizontalTextPosition();
122+ this.useCheckAndArrow = useCheckAndArrow;
123+ this.font = font;
124+ this.accFont = accFont;
125+ this.fm = mi.getFontMetrics(font);
126+ this.accFm = mi.getFontMetrics(accFont);
127+ this.isLeftToRight = isLeftToRight;
128+ this.isColumnLayout = isColumnLayout(isLeftToRight,
129+ horizontalAlignment, horizontalTextPosition,
130+ verticalTextPosition);
131+ this.isTopLevelMenu = (this.miParent == null) ? true : false;
132+ this.checkIcon = checkIcon;
133+ this.icon = getIcon(propertyPrefix);
134+ this.arrowIcon = arrowIcon;
135+ this.text = mi.getText();
136+ this.gap = gap;
137+ this.afterCheckIconGap = getAfterCheckIconGap(propertyPrefix);
138+ this.minTextOffset = getMinTextOffset(propertyPrefix);
139+ this.htmlView = (View) mi.getClientProperty(BasicHTML.propertyKey);
140+ this.viewRect = viewRect;
141+
142+ this.iconSize = new RectSize();
143+ this.textSize = new RectSize();
144+ this.accSize = new RectSize();
145+ this.checkSize = new RectSize();
146+ this.arrowSize = new RectSize();
147+ this.labelSize = new RectSize();
148+ calcExtraWidths();
149+ calcWidthsAndHeights();
150+ setOriginalWidths();
151+ calcMaxWidths();
152+
153+ this.leadingGap = getLeadingGap(propertyPrefix);
154+ calcMaxTextOffset(viewRect);
155+ }
156+
157+ private void calcExtraWidths() {
158+ leftTextExtraWidth = getLeftExtraWidth(text);
159+ }
160+
161+ private int getLeftExtraWidth(String str) {
162+ int lsb = SwingUtilities2.getLeftSideBearing(mi, fm, str);
163+ if (lsb < 0) {
164+ return -lsb;
165+ } else {
166+ return 0;
167+ }
168+ }
169+
170+ private void setOriginalWidths() {
171+ iconSize.origWidth = iconSize.width;
172+ textSize.origWidth = textSize.width;
173+ accSize.origWidth = accSize.width;
174+ checkSize.origWidth = checkSize.width;
175+ arrowSize.origWidth = arrowSize.width;
176+ }
177+
178+ private String getAccText(String acceleratorDelimiter) {
179+ String accText = "";
180+ KeyStroke accelerator = mi.getAccelerator();
181+ if (accelerator != null) {
182+ int modifiers = accelerator.getModifiers();
183+ if (modifiers > 0) {
184+ accText = KeyEvent.getKeyModifiersText(modifiers);
185+ accText += acceleratorDelimiter;
186+ }
187+ int keyCode = accelerator.getKeyCode();
188+ if (keyCode != 0) {
189+ accText += KeyEvent.getKeyText(keyCode);
190+ } else {
191+ accText += accelerator.getKeyChar();
192+ }
193+ }
194+ return accText;
195+ }
196+
197+ private Icon getIcon(String propertyPrefix) {
198+ // In case of column layout, .checkIconFactory is defined for this UI,
199+ // the icon is compatible with it and useCheckAndArrow() is true,
200+ // then the icon is handled by the checkIcon.
201+ Icon icon = null;
202+ MenuItemCheckIconFactory iconFactory =
203+ (MenuItemCheckIconFactory) UIManager.get(propertyPrefix
204+ + ".checkIconFactory");
205+ if (!isColumnLayout || !useCheckAndArrow || iconFactory == null
206+ || !iconFactory.isCompatible(checkIcon, propertyPrefix)) {
207+ icon = mi.getIcon();
208+ }
209+ return icon;
210+ }
211+
212+ private int getMinTextOffset(String propertyPrefix) {
213+ int minimumTextOffset = 0;
214+ Object minimumTextOffsetObject =
215+ UIManager.get(propertyPrefix + ".minimumTextOffset");
216+ if (minimumTextOffsetObject instanceof Integer) {
217+ minimumTextOffset = (Integer) minimumTextOffsetObject;
218+ }
219+ return minimumTextOffset;
220+ }
221+
222+ private int getAfterCheckIconGap(String propertyPrefix) {
223+ int afterCheckIconGap = gap;
224+ Object afterCheckIconGapObject =
225+ UIManager.get(propertyPrefix + ".afterCheckIconGap");
226+ if (afterCheckIconGapObject instanceof Integer) {
227+ afterCheckIconGap = (Integer) afterCheckIconGapObject;
228+ }
229+ return afterCheckIconGap;
230+ }
231+
232+ private int getLeadingGap(String propertyPrefix) {
233+ if (checkSize.getMaxWidth() > 0) {
234+ return getCheckOffset(propertyPrefix);
235+ } else {
236+ return gap; // There is no any check icon
237+ }
238+ }
239+
240+ private int getCheckOffset(String propertyPrefix) {
241+ int checkIconOffset = gap;
242+ Object checkIconOffsetObject =
243+ UIManager.get(propertyPrefix + ".checkIconOffset");
244+ if (checkIconOffsetObject instanceof Integer) {
245+ checkIconOffset = (Integer) checkIconOffsetObject;
246+ }
247+ return checkIconOffset;
248+ }
249+
250+ protected void calcWidthsAndHeights() {
251+ // iconRect
252+ if (icon != null) {
253+ iconSize.width = icon.getIconWidth();
254+ iconSize.height = icon.getIconHeight();
255+ }
256+
257+ // accRect
258+ if (!accText.equals("")) {
259+ accSize.width = SwingUtilities2.stringWidth(mi, accFm, accText);
260+ accSize.height = accFm.getHeight();
261+ }
262+
263+ // textRect
264+ if (text == null) {
265+ text = "";
266+ } else if (!text.equals("")) {
267+ if (htmlView != null) {
268+ // Text is HTML
269+ textSize.width =
270+ (int) htmlView.getPreferredSpan(View.X_AXIS);
271+ textSize.height =
272+ (int) htmlView.getPreferredSpan(View.Y_AXIS);
273+ } else {
274+ // Text isn't HTML
275+ textSize.width = SwingUtilities2.stringWidth(mi, fm, text);
276+ textSize.height = fm.getHeight();
277+ }
278+ }
279+
280+ if (useCheckAndArrow) {
281+ // checkIcon
282+ if (checkIcon != null) {
283+ checkSize.width = checkIcon.getIconWidth();
284+ checkSize.height = checkIcon.getIconHeight();
285+ }
286+ // arrowRect
287+ if (arrowIcon != null) {
288+ arrowSize.width = arrowIcon.getIconWidth();
289+ arrowSize.height = arrowIcon.getIconHeight();
290+ }
291+ }
292+
293+ // labelRect
294+ if (isColumnLayout) {
295+ labelSize.width = iconSize.width + textSize.width + gap;
296+ labelSize.height = max(checkSize.height, iconSize.height,
297+ textSize.height, accSize.height, arrowSize.height);
298+ } else {
299+ Rectangle textRect = new Rectangle();
300+ Rectangle iconRect = new Rectangle();
301+ SwingUtilities.layoutCompoundLabel(mi, fm, text, icon,
302+ verticalAlignment, horizontalAlignment,
303+ verticalTextPosition, horizontalTextPosition,
304+ viewRect, iconRect, textRect, gap);
305+ textRect.width += leftTextExtraWidth;
306+ Rectangle labelRect = iconRect.union(textRect);
307+ labelSize.height = labelRect.height;
308+ labelSize.width = labelRect.width;
309+ }
310+ }
311+
312+ protected void calcMaxWidths() {
313+ calcMaxWidth(checkSize, MAX_CHECK_WIDTH);
314+ calcMaxWidth(arrowSize, MAX_ARROW_WIDTH);
315+ calcMaxWidth(accSize, MAX_ACC_WIDTH);
316+
317+ if (isColumnLayout) {
318+ calcMaxWidth(iconSize, MAX_ICON_WIDTH);
319+ calcMaxWidth(textSize, MAX_TEXT_WIDTH);
320+ int curGap = gap;
321+ if ((iconSize.getMaxWidth() == 0)
322+ || (textSize.getMaxWidth() == 0)) {
323+ curGap = 0;
324+ }
325+ labelSize.maxWidth =
326+ calcMaxValue(MAX_LABEL_WIDTH, iconSize.maxWidth
327+ + textSize.maxWidth + curGap);
328+ } else {
329+ // We shouldn't use current icon and text widths
330+ // in maximal widths calculation for complex layout.
331+ iconSize.maxWidth = getParentIntProperty(MAX_ICON_WIDTH);
332+ calcMaxWidth(labelSize, MAX_LABEL_WIDTH);
333+ // If maxLabelWidth is wider
334+ // than the widest icon + the widest text + gap,
335+ // we should update the maximal text witdh
336+ int candidateTextWidth = labelSize.maxWidth - iconSize.maxWidth;
337+ if (iconSize.maxWidth > 0) {
338+ candidateTextWidth -= gap;
339+ }
340+ textSize.maxWidth = calcMaxValue(MAX_TEXT_WIDTH, candidateTextWidth);
341+ }
342+ }
343+
344+ protected void calcMaxWidth(RectSize rs, Object key) {
345+ rs.maxWidth = calcMaxValue(key, rs.width);
346+ }
347+
348+ /**
349+ * Calculates and returns maximal value through specified parent component
350+ * client property.
351+ *
352+ * @param propertyName name of the property, which stores the maximal value.
353+ * @param value a value which pretends to be maximal
354+ * @return maximal value among the parent property and the value.
355+ */
356+ protected int calcMaxValue(Object propertyName, int value) {
357+ // Get maximal value from parent client property
358+ int maxValue = getParentIntProperty(propertyName);
359+ // Store new maximal width in parent client property
360+ if (value > maxValue) {
361+ if (miParent != null) {
362+ miParent.putClientProperty(propertyName, value);
363+ }
364+ return value;
365+ } else {
366+ return maxValue;
367+ }
368+ }
369+
370+ /**
371+ * Returns parent client property as int.
372+ * @param propertyName name of the parent property.
373+ * @return value of the property as int.
374+ */
375+ protected int getParentIntProperty(Object propertyName) {
376+ Object value = null;
377+ if (miParent != null) {
378+ value = miParent.getClientProperty(propertyName);
379+ }
380+ if ((value == null) || !(value instanceof Integer)) {
381+ value = 0;
382+ }
383+ return (Integer) value;
384+ }
385+
386+ public static boolean isColumnLayout(boolean isLeftToRight,
387+ JMenuItem mi) {
388+ assert(mi != null);
389+ return isColumnLayout(isLeftToRight, mi.getHorizontalAlignment(),
390+ mi.getHorizontalTextPosition(), mi.getVerticalTextPosition());
391+ }
392+
393+ /**
394+ * Answers should we do column layout for a menu item or not.
395+ * We do it when a user doesn't set any alignments
396+ * and text positions manually, except the vertical alignment.
397+ */
398+ public static boolean isColumnLayout(boolean isLeftToRight,
399+ int horizontalAlignment,
400+ int horizontalTextPosition,
401+ int verticalTextPosition) {
402+ if (verticalTextPosition != SwingConstants.CENTER) {
403+ return false;
404+ }
405+ if (isLeftToRight) {
406+ if (horizontalAlignment != SwingConstants.LEADING
407+ && horizontalAlignment != SwingConstants.LEFT) {
408+ return false;
409+ }
410+ if (horizontalTextPosition != SwingConstants.TRAILING
411+ && horizontalTextPosition != SwingConstants.RIGHT) {
412+ return false;
413+ }
414+ } else {
415+ if (horizontalAlignment != SwingConstants.LEADING
416+ && horizontalAlignment != SwingConstants.RIGHT) {
417+ return false;
418+ }
419+ if (horizontalTextPosition != SwingConstants.TRAILING
420+ && horizontalTextPosition != SwingConstants.LEFT) {
421+ return false;
422+ }
423+ }
424+ return true;
425+ }
426+
427+ /**
428+ * Calculates maximal text offset.
429+ * It is required for some L&Fs (ex: Vista L&F).
430+ * The offset is meaningful only for L2R column layout.
431+ *
432+ * @param viewRect the rectangle, the maximal text offset
433+ * will be calculated for.
434+ */
435+ private void calcMaxTextOffset(Rectangle viewRect) {
436+ if (!isColumnLayout || !isLeftToRight) {
437+ return;
438+ }
439+
440+ // Calculate the current text offset
441+ int offset = viewRect.x + leadingGap + checkSize.maxWidth
442+ + afterCheckIconGap + iconSize.maxWidth + gap;
443+ if (checkSize.maxWidth == 0) {
444+ offset -= afterCheckIconGap;
445+ }
446+ if (iconSize.maxWidth == 0) {
447+ offset -= gap;
448+ }
449+
450+ // maximal text offset shouldn't be less than minimal text offset;
451+ if (offset < minTextOffset) {
452+ offset = minTextOffset;
453+ }
454+
455+ // Calculate and store the maximal text offset
456+ calcMaxValue(SwingUtilities2.BASICMENUITEMUI_MAX_TEXT_OFFSET, offset);
457+ }
458+
459+ /**
460+ * Layout icon, text, check icon, accelerator text and arrow icon
461+ * in the viewRect and return their positions.
462+ *
463+ * If horizontalAlignment, verticalTextPosition and horizontalTextPosition
464+ * are default (user doesn't set any manually) the layouting algorithm is:
465+ * Elements are layouted in the five columns:
466+ * check icon + icon + text + accelerator text + arrow icon
467+ *
468+ * In the other case elements are layouted in the four columns:
469+ * check icon + label + accelerator text + arrow icon
470+ * Label is union of icon and text.
471+ *
472+ * The order of columns can be reversed.
473+ * It depends on the menu item orientation.
474+ */
475+ public LayoutResult layoutMenuItem() {
476+ LayoutResult lr = createLayoutResult();
477+ prepareForLayout(lr);
478+
479+ if (isColumnLayout()) {
480+ if (isLeftToRight()) {
481+ doLTRColumnLayout(lr, getLTRColumnAlignment());
482+ } else {
483+ doRTLColumnLayout(lr, getRTLColumnAlignment());
484+ }
485+ } else {
486+ if (isLeftToRight()) {
487+ doLTRComplexLayout(lr, getLTRColumnAlignment());
488+ } else {
489+ doRTLComplexLayout(lr, getRTLColumnAlignment());
490+ }
491+ }
492+
493+ alignAccCheckAndArrowVertically(lr);
494+ return lr;
495+ }
496+
497+ private LayoutResult createLayoutResult() {
498+ return new LayoutResult(
499+ new Rectangle(iconSize.width, iconSize.height),
500+ new Rectangle(textSize.width, textSize.height),
501+ new Rectangle(accSize.width, accSize.height),
502+ new Rectangle(checkSize.width, checkSize.height),
503+ new Rectangle(arrowSize.width, arrowSize.height),
504+ new Rectangle(labelSize.width, labelSize.height)
505+ );
506+ }
507+
508+ public ColumnAlignment getLTRColumnAlignment() {
509+ return ColumnAlignment.LEFT_ALIGNMENT;
510+ }
511+
512+ public ColumnAlignment getRTLColumnAlignment() {
513+ return ColumnAlignment.RIGHT_ALIGNMENT;
514+ }
515+
516+ protected void prepareForLayout(LayoutResult lr) {
517+ lr.checkRect.width = checkSize.maxWidth;
518+ lr.accRect.width = accSize.maxWidth;
519+ lr.arrowRect.width = arrowSize.maxWidth;
520+ }
521+
522+ /**
523+ * Aligns the accelertor text and the check and arrow icons vertically
524+ * with the center of the label rect.
525+ */
526+ private void alignAccCheckAndArrowVertically(LayoutResult lr) {
527+ lr.accRect.y = (int)(lr.labelRect.y
528+ + (float)lr.labelRect.height/2
529+ - (float)lr.accRect.height/2);
530+ fixVerticalAlignment(lr, lr.accRect);
531+ if (useCheckAndArrow) {
532+ lr.arrowRect.y = (int)(lr.labelRect.y
533+ + (float)lr.labelRect.height/2
534+ - (float)lr.arrowRect.height/2);
535+ lr.checkRect.y = (int)(lr.labelRect.y
536+ + (float)lr.labelRect.height/2
537+ - (float)lr.checkRect.height/2);
538+ fixVerticalAlignment(lr, lr.arrowRect);
539+ fixVerticalAlignment(lr, lr.checkRect);
540+ }
541+ }
542+
543+ /**
544+ * Fixes vertical alignment of all menu item elements if rect.y
545+ * or (rect.y + rect.height) is out of viewRect bounds
546+ */
547+ private void fixVerticalAlignment(LayoutResult lr, Rectangle r) {
548+ int delta = 0;
549+ if (r.y < viewRect.y) {
550+ delta = viewRect.y - r.y;
551+ } else if (r.y + r.height > viewRect.y + viewRect.height) {
552+ delta = viewRect.y + viewRect.height - r.y - r.height;
553+ }
554+ if (delta != 0) {
555+ lr.checkRect.y += delta;
556+ lr.iconRect.y += delta;
557+ lr.textRect.y += delta;
558+ lr.accRect.y += delta;
559+ lr.arrowRect.y += delta;
560+ lr.labelRect.y += delta;
561+ }
562+ }
563+
564+ private void doLTRColumnLayout(LayoutResult lr, ColumnAlignment alignment) {
565+ // Set maximal width for all the five basic rects
566+ // (three other ones are already maximal)
567+ lr.iconRect.width = iconSize.maxWidth;
568+ lr.textRect.width = textSize.maxWidth;
569+
570+ // Set X coordinates
571+ // All rects will be aligned at the left side
572+ calcXPositionsLTR(viewRect.x, leadingGap, gap, lr.checkRect,
573+ lr.iconRect, lr.textRect);
574+
575+ // Tune afterCheckIconGap
576+ if (lr.checkRect.width > 0) { // there is the afterCheckIconGap
577+ lr.iconRect.x += afterCheckIconGap - gap;
578+ lr.textRect.x += afterCheckIconGap - gap;
579+ }
580+
581+ calcXPositionsRTL(viewRect.x + viewRect.width, leadingGap, gap,
582+ lr.arrowRect, lr.accRect);
583+
584+ // Take into account minimal text offset
585+ int textOffset = lr.textRect.x - viewRect.x;
586+ if (!isTopLevelMenu && (textOffset < minTextOffset)) {
587+ lr.textRect.x += minTextOffset - textOffset;
588+ }
589+
590+ alignRects(lr, alignment);
591+
592+ // Set Y coordinate for text and icon.
593+ // Y coordinates for other rects
594+ // will be calculated later in layoutMenuItem.
595+ calcTextAndIconYPositions(lr);
596+
597+ // Calculate valid X and Y coordinates for labelRect
598+ lr.setLabelRect(lr.textRect.union(lr.iconRect));
599+ }
600+
601+ private void doLTRComplexLayout(LayoutResult lr, ColumnAlignment alignment) {
602+ lr.labelRect.width = labelSize.maxWidth;
603+
604+ // Set X coordinates
605+ calcXPositionsLTR(viewRect.x, leadingGap, gap, lr.checkRect,
606+ lr.labelRect);
607+
608+ // Tune afterCheckIconGap
609+ if (lr.checkRect.width > 0) { // there is the afterCheckIconGap
610+ lr.labelRect.x += afterCheckIconGap - gap;
611+ }
612+
613+ calcXPositionsRTL(viewRect.x + viewRect.width,
614+ leadingGap, gap, lr.arrowRect, lr.accRect);
615+
616+ // Take into account minimal text offset
617+ int labelOffset = lr.labelRect.x - viewRect.x;
618+ if (!isTopLevelMenu && (labelOffset < minTextOffset)) {
619+ lr.labelRect.x += minTextOffset - labelOffset;
620+ }
621+
622+ alignRects(lr, alignment);
623+
624+ // Center labelRect vertically
625+ calcLabelYPosition(lr);
626+
627+ layoutIconAndTextInLabelRect(lr);
628+ }
629+
630+ private void doRTLColumnLayout(LayoutResult lr, ColumnAlignment alignment) {
631+ // Set maximal width for all the five basic rects
632+ // (three other ones are already maximal)
633+ lr.iconRect.width = iconSize.maxWidth;
634+ lr.textRect.width = textSize.maxWidth;
635+
636+ // Set X coordinates
637+ calcXPositionsRTL(viewRect.x + viewRect.width, leadingGap, gap,
638+ lr.checkRect, lr.iconRect, lr.textRect);
639+
640+ // Tune the gap after check icon
641+ if (lr.checkRect.width > 0) { // there is the gap after check icon
642+ lr.iconRect.x -= afterCheckIconGap - gap;
643+ lr.textRect.x -= afterCheckIconGap - gap;
644+ }
645+
646+ calcXPositionsLTR(viewRect.x, leadingGap, gap, lr.arrowRect,
647+ lr.accRect);
648+
649+ // Take into account minimal text offset
650+ int textOffset = (viewRect.x + viewRect.width)
651+ - (lr.textRect.x + lr.textRect.width);
652+ if (!isTopLevelMenu && (textOffset < minTextOffset)) {
653+ lr.textRect.x -= minTextOffset - textOffset;
654+ }
655+
656+ alignRects(lr, alignment);
657+
658+ // Set Y coordinates for text and icon.
659+ // Y coordinates for other rects
660+ // will be calculated later in layoutMenuItem.
661+ calcTextAndIconYPositions(lr);
662+
663+ // Calculate valid X and Y coordinate for labelRect
664+ lr.setLabelRect(lr.textRect.union(lr.iconRect));
665+ }
666+
667+ private void doRTLComplexLayout(LayoutResult lr, ColumnAlignment alignment) {
668+ lr.labelRect.width = labelSize.maxWidth;
669+
670+ // Set X coordinates
671+ calcXPositionsRTL(viewRect.x + viewRect.width, leadingGap, gap,
672+ lr.checkRect, lr.labelRect);
673+
674+ // Tune the gap after check icon
675+ if (lr.checkRect.width > 0) { // there is the gap after check icon
676+ lr.labelRect.x -= afterCheckIconGap - gap;
677+ }
678+
679+ calcXPositionsLTR(viewRect.x, leadingGap, gap, lr.arrowRect, lr.accRect);
680+
681+ // Take into account minimal text offset
682+ int labelOffset = (viewRect.x + viewRect.width)
683+ - (lr.labelRect.x + lr.labelRect.width);
684+ if (!isTopLevelMenu && (labelOffset < minTextOffset)) {
685+ lr.labelRect.x -= minTextOffset - labelOffset;
686+ }
687+
688+ alignRects(lr, alignment);
689+
690+ // Center labelRect vertically
691+ calcLabelYPosition(lr);
692+
693+ layoutIconAndTextInLabelRect(lr);
694+ }
695+
696+ private void alignRects(LayoutResult lr, ColumnAlignment alignment) {
697+ alignRect(lr.checkRect, alignment.getCheckAlignment(),
698+ checkSize.getOrigWidth());
699+ alignRect(lr.iconRect, alignment.getIconAlignment(),
700+ iconSize.getOrigWidth());
701+ alignRect(lr.textRect, alignment.getTextAlignment(),
702+ textSize.getOrigWidth());
703+ alignRect(lr.accRect, alignment.getAccAlignment(),
704+ accSize.getOrigWidth());
705+ alignRect(lr.arrowRect, alignment.getArrowAlignment(),
706+ arrowSize.getOrigWidth());
707+ }
708+
709+ private void alignRect(Rectangle rect, int alignment, int origWidth) {
710+ if (alignment == SwingConstants.RIGHT) {
711+ rect.x = rect.x + rect.width - origWidth;
712+ }
713+ rect.width = origWidth;
714+ }
715+
716+ protected void layoutIconAndTextInLabelRect(LayoutResult lr) {
717+ lr.setTextRect(new Rectangle());
718+ lr.setIconRect(new Rectangle());
719+ SwingUtilities.layoutCompoundLabel(
720+ mi, fm, text,icon, verticalAlignment, horizontalAlignment,
721+ verticalTextPosition, horizontalTextPosition, lr.labelRect,
722+ lr.iconRect, lr.textRect, gap);
723+ }
724+
725+ private void calcXPositionsLTR(int startXPos, int leadingGap,
726+ int gap, Rectangle... rects) {
727+ int curXPos = startXPos + leadingGap;
728+ for (Rectangle rect : rects) {
729+ rect.x = curXPos;
730+ if (rect.width > 0) {
731+ curXPos += rect.width + gap;
732+ }
733+ }
734+ }
735+
736+ private void calcXPositionsRTL(int startXPos, int leadingGap,
737+ int gap, Rectangle... rects) {
738+ int curXPos = startXPos - leadingGap;
739+ for (Rectangle rect : rects) {
740+ rect.x = curXPos - rect.width;
741+ if (rect.width > 0) {
742+ curXPos -= rect.width + gap;
743+ }
744+ }
745+ }
746+
747+ /**
748+ * Sets Y coordinates of text and icon
749+ * taking into account the vertical alignment
750+ */
751+ private void calcTextAndIconYPositions(LayoutResult lr) {
752+ if (verticalAlignment == SwingUtilities.TOP) {
753+ lr.textRect.y = (int)(viewRect.y
754+ + (float)lr.labelRect.height/2
755+ - (float)lr.textRect.height/2);
756+ lr.iconRect.y = (int)(viewRect.y
757+ + (float)lr.labelRect.height/2
758+ - (float)lr.iconRect.height/2);
759+ } else if (verticalAlignment == SwingUtilities.CENTER) {
760+ lr.textRect.y = (int)(viewRect.y
761+ + (float)viewRect.height/2
762+ - (float)lr.textRect.height/2);
763+ lr.iconRect.y = (int)(viewRect.y
764+ + (float)viewRect.height/2
765+ - (float)lr.iconRect.height/2);
766+ }
767+ else if (verticalAlignment == SwingUtilities.BOTTOM) {
768+ lr.textRect.y = (int)(viewRect.y
769+ + viewRect.height
770+ - (float)lr.labelRect.height/2
771+ - (float)lr.textRect.height/2);
772+ lr.iconRect.y = (int)(viewRect.y
773+ + viewRect.height
774+ - (float)lr.labelRect.height/2
775+ - (float)lr.iconRect.height/2);
776+ }
777+ }
778+
779+ /**
780+ * Sets labelRect Y coordinate
781+ * taking into account the vertical alignment
782+ */
783+ private void calcLabelYPosition(LayoutResult lr) {
784+ if (verticalAlignment == SwingUtilities.TOP) {
785+ lr.labelRect.y = viewRect.y;
786+ } else if (verticalAlignment == SwingUtilities.CENTER) {
787+ lr.labelRect.y = (int)(viewRect.y
788+ + (float)viewRect.height/2
789+ - (float)lr.labelRect.height/2);
790+ } else if (verticalAlignment == SwingUtilities.BOTTOM) {
791+ lr.labelRect.y = viewRect.y + viewRect.height
792+ - lr.labelRect.height;
793+ }
794+ }
795+
796+ /**
797+ * Returns parent of this component if it is not a top-level menu
798+ * Otherwise returns null.
799+ * @param menuItem the menu item whose parent will be returned.
800+ * @return parent of this component if it is not a top-level menu
801+ * Otherwise returns null.
802+ */
803+ public static JComponent getMenuItemParent(JMenuItem menuItem) {
804+ Container parent = menuItem.getParent();
805+ if ((parent instanceof JComponent) &&
806+ (!(menuItem instanceof JMenu) ||
807+ !((JMenu)menuItem).isTopLevelMenu())) {
808+ return (JComponent) parent;
809+ } else {
810+ return null;
811+ }
812+ }
813+
814+ public static void clearUsedParentClientProperties(JMenuItem menuItem) {
815+ clearUsedClientProperties(getMenuItemParent(menuItem));
816+ }
817+
818+ public static void clearUsedClientProperties(JComponent c) {
819+ if (c != null) {
820+ c.putClientProperty(MAX_ARROW_WIDTH, null);
821+ c.putClientProperty(MAX_CHECK_WIDTH, null);
822+ c.putClientProperty(MAX_ACC_WIDTH, null);
823+ c.putClientProperty(MAX_TEXT_WIDTH, null);
824+ c.putClientProperty(MAX_ICON_WIDTH, null);
825+ c.putClientProperty(MAX_LABEL_WIDTH, null);
826+ c.putClientProperty(BASICMENUITEMUI_MAX_TEXT_OFFSET, null);
827+ }
828+ }
829+
830+ /**
831+ * Finds and returns maximal integer value in the given array.
832+ * @param values array where the search will be performed.
833+ * @return maximal vaule.
834+ */
835+ public static int max(int... values) {
836+ int maxValue = Integer.MIN_VALUE;
837+ for (int i : values) {
838+ if (i > maxValue) {
839+ maxValue = i;
840+ }
841+ }
842+ return maxValue;
843+ }
844+
845+ public static Rectangle createMaxRect() {
846+ return new Rectangle(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
847+ }
848+
849+ public static void addMaxWidth(RectSize size, int gap, Dimension result) {
850+ if (size.maxWidth > 0) {
851+ result.width += size.maxWidth + gap;
852+ }
853+ }
854+
855+ public static void addWidth(int width, int gap, Dimension result) {
856+ if (width > 0) {
857+ result.width += width + gap;
858+ }
859+ }
860+
861+ public JMenuItem getMenuItem() {
862+ return mi;
863+ }
864+
865+ public JComponent getMenuItemParent() {
866+ return miParent;
867+ }
868+
869+ public Font getFont() {
870+ return font;
871+ }
872+
873+ public Font getAccFont() {
874+ return accFont;
875+ }
876+
877+ public FontMetrics getFontMetrics() {
878+ return fm;
879+ }
880+
881+ public FontMetrics getAccFontMetrics() {
882+ return accFm;
883+ }
884+
885+ public Icon getIcon() {
886+ return icon;
887+ }
888+
889+ public Icon getCheckIcon() {
890+ return checkIcon;
891+ }
892+
893+ public Icon getArrowIcon() {
894+ return arrowIcon;
895+ }
896+
897+ public String getText() {
898+ return text;
899+ }
900+
901+ public String getAccText() {
902+ return accText;
903+ }
904+
905+ public boolean isColumnLayout() {
906+ return isColumnLayout;
907+ }
908+
909+ public boolean useCheckAndArrow() {
910+ return useCheckAndArrow;
911+ }
912+
913+ public boolean isLeftToRight() {
914+ return isLeftToRight;
915+ }
916+
917+ public boolean isTopLevelMenu() {
918+ return isTopLevelMenu;
919+ }
920+
921+ public View getHtmlView() {
922+ return htmlView;
923+ }
924+
925+ public int getVerticalAlignment() {
926+ return verticalAlignment;
927+ }
928+
929+ public int getHorizontalAlignment() {
930+ return horizontalAlignment;
931+ }
932+
933+ public int getVerticalTextPosition() {
934+ return verticalTextPosition;
935+ }
936+
937+ public int getHorizontalTextPosition() {
938+ return horizontalTextPosition;
939+ }
940+
941+ public int getGap() {
942+ return gap;
943+ }
944+
945+ public int getLeadingGap() {
946+ return leadingGap;
947+ }
948+
949+ public int getAfterCheckIconGap() {
950+ return afterCheckIconGap;
951+ }
952+
953+ public int getMinTextOffset() {
954+ return minTextOffset;
955+ }
956+
957+ public Rectangle getViewRect() {
958+ return viewRect;
959+ }
960+
961+ public RectSize getIconSize() {
962+ return iconSize;
963+ }
964+
965+ public RectSize getTextSize() {
966+ return textSize;
967+ }
968+
969+ public RectSize getAccSize() {
970+ return accSize;
971+ }
972+
973+ public RectSize getCheckSize() {
974+ return checkSize;
975+ }
976+
977+ public RectSize getArrowSize() {
978+ return arrowSize;
979+ }
980+
981+ public RectSize getLabelSize() {
982+ return labelSize;
983+ }
984+
985+ protected void setMenuItem(JMenuItem mi) {
986+ this.mi = mi;
987+ }
988+
989+ protected void setMenuItemParent(JComponent miParent) {
990+ this.miParent = miParent;
991+ }
992+
993+ protected void setFont(Font font) {
994+ this.font = font;
995+ }
996+
997+ protected void setAccFont(Font accFont) {
998+ this.accFont = accFont;
999+ }
1000+
1001+ protected void setFontMetrics(FontMetrics fm) {
1002+ this.fm = fm;
1003+ }
1004+
1005+ protected void setAccFontMetrics(FontMetrics accFm) {
1006+ this.accFm = accFm;
1007+ }
1008+
1009+ protected void setIcon(Icon icon) {
1010+ this.icon = icon;
1011+ }
1012+
1013+ protected void setCheckIcon(Icon checkIcon) {
1014+ this.checkIcon = checkIcon;
1015+ }
1016+
1017+ protected void setArrowIcon(Icon arrowIcon) {
1018+ this.arrowIcon = arrowIcon;
1019+ }
1020+
1021+ protected void setText(String text) {
1022+ this.text = text;
1023+ }
1024+
1025+ protected void setAccText(String accText) {
1026+ this.accText = accText;
1027+ }
1028+
1029+ protected void setColumnLayout(boolean columnLayout) {
1030+ isColumnLayout = columnLayout;
1031+ }
1032+
1033+ protected void setUseCheckAndArrow(boolean useCheckAndArrow) {
1034+ this.useCheckAndArrow = useCheckAndArrow;
1035+ }
1036+
1037+ protected void setLeftToRight(boolean leftToRight) {
1038+ isLeftToRight = leftToRight;
1039+ }
1040+
1041+ protected void setTopLevelMenu(boolean topLevelMenu) {
1042+ isTopLevelMenu = topLevelMenu;
1043+ }
1044+
1045+ protected void setHtmlView(View htmlView) {
1046+ this.htmlView = htmlView;
1047+ }
1048+
1049+ protected void setVerticalAlignment(int verticalAlignment) {
1050+ this.verticalAlignment = verticalAlignment;
1051+ }
1052+
1053+ protected void setHorizontalAlignment(int horizontalAlignment) {
1054+ this.horizontalAlignment = horizontalAlignment;
1055+ }
1056+
1057+ protected void setVerticalTextPosition(int verticalTextPosition) {
1058+ this.verticalTextPosition = verticalTextPosition;
1059+ }
1060+
1061+ protected void setHorizontalTextPosition(int horizontalTextPosition) {
1062+ this.horizontalTextPosition = horizontalTextPosition;
1063+ }
1064+
1065+ protected void setGap(int gap) {
1066+ this.gap = gap;
1067+ }
1068+
1069+ protected void setLeadingGap(int leadingGap) {
1070+ this.leadingGap = leadingGap;
1071+ }
1072+
1073+ protected void setAfterCheckIconGap(int afterCheckIconGap) {
1074+ this.afterCheckIconGap = afterCheckIconGap;
1075+ }
1076+
1077+ protected void setMinTextOffset(int minTextOffset) {
1078+ this.minTextOffset = minTextOffset;
1079+ }
1080+
1081+ protected void setViewRect(Rectangle viewRect) {
1082+ this.viewRect = viewRect;
1083+ }
1084+
1085+ protected void setIconSize(RectSize iconSize) {
1086+ this.iconSize = iconSize;
1087+ }
1088+
1089+ protected void setTextSize(RectSize textSize) {
1090+ this.textSize = textSize;
1091+ }
1092+
1093+ protected void setAccSize(RectSize accSize) {
1094+ this.accSize = accSize;
1095+ }
1096+
1097+ protected void setCheckSize(RectSize checkSize) {
1098+ this.checkSize = checkSize;
1099+ }
1100+
1101+ protected void setArrowSize(RectSize arrowSize) {
1102+ this.arrowSize = arrowSize;
1103+ }
1104+
1105+ protected void setLabelSize(RectSize labelSize) {
1106+ this.labelSize = labelSize;
1107+ }
1108+
1109+ public int getLeftTextExtraWidth() {
1110+ return leftTextExtraWidth;
1111+ }
1112+
1113+ /**
1114+ * Returns false if the component is a JMenu and it is a top
1115+ * level menu (on the menubar).
1116+ */
1117+ public static boolean useCheckAndArrow(JMenuItem menuItem) {
1118+ boolean b = true;
1119+ if ((menuItem instanceof JMenu) &&
1120+ (((JMenu) menuItem).isTopLevelMenu())) {
1121+ b = false;
1122+ }
1123+ return b;
1124+ }
1125+
1126+ public static class LayoutResult {
1127+ private Rectangle iconRect;
1128+ private Rectangle textRect;
1129+ private Rectangle accRect;
1130+ private Rectangle checkRect;
1131+ private Rectangle arrowRect;
1132+ private Rectangle labelRect;
1133+
1134+ public LayoutResult() {
1135+ iconRect = new Rectangle();
1136+ textRect = new Rectangle();
1137+ accRect = new Rectangle();
1138+ checkRect = new Rectangle();
1139+ arrowRect = new Rectangle();
1140+ labelRect = new Rectangle();
1141+ }
1142+
1143+ public LayoutResult(Rectangle iconRect, Rectangle textRect,
1144+ Rectangle accRect, Rectangle checkRect,
1145+ Rectangle arrowRect, Rectangle labelRect) {
1146+ this.iconRect = iconRect;
1147+ this.textRect = textRect;
1148+ this.accRect = accRect;
1149+ this.checkRect = checkRect;
1150+ this.arrowRect = arrowRect;
1151+ this.labelRect = labelRect;
1152+ }
1153+
1154+ public Rectangle getIconRect() {
1155+ return iconRect;
1156+ }
1157+
1158+ public void setIconRect(Rectangle iconRect) {
1159+ this.iconRect = iconRect;
1160+ }
1161+
1162+ public Rectangle getTextRect() {
1163+ return textRect;
1164+ }
1165+
1166+ public void setTextRect(Rectangle textRect) {
1167+ this.textRect = textRect;
1168+ }
1169+
1170+ public Rectangle getAccRect() {
1171+ return accRect;
1172+ }
1173+
1174+ public void setAccRect(Rectangle accRect) {
1175+ this.accRect = accRect;
1176+ }
1177+
1178+ public Rectangle getCheckRect() {
1179+ return checkRect;
1180+ }
1181+
1182+ public void setCheckRect(Rectangle checkRect) {
1183+ this.checkRect = checkRect;
1184+ }
1185+
1186+ public Rectangle getArrowRect() {
1187+ return arrowRect;
1188+ }
1189+
1190+ public void setArrowRect(Rectangle arrowRect) {
1191+ this.arrowRect = arrowRect;
1192+ }
1193+
1194+ public Rectangle getLabelRect() {
1195+ return labelRect;
1196+ }
1197+
1198+ public void setLabelRect(Rectangle labelRect) {
1199+ this.labelRect = labelRect;
1200+ }
1201+
1202+ public Map<String, Rectangle> getAllRects() {
1203+ Map<String, Rectangle> result = new HashMap<String, Rectangle>();
1204+ result.put("checkRect", checkRect);
1205+ result.put("iconRect", iconRect);
1206+ result.put("textRect", textRect);
1207+ result.put("accRect", accRect);
1208+ result.put("arrowRect", arrowRect);
1209+ result.put("labelRect", labelRect);
1210+ return result;
1211+ }
1212+ }
1213+
1214+ public static class ColumnAlignment {
1215+ private int checkAlignment;
1216+ private int iconAlignment;
1217+ private int textAlignment;
1218+ private int accAlignment;
1219+ private int arrowAlignment;
1220+
1221+ public static final ColumnAlignment LEFT_ALIGNMENT =
1222+ new ColumnAlignment(
1223+ SwingConstants.LEFT,
1224+ SwingConstants.LEFT,
1225+ SwingConstants.LEFT,
1226+ SwingConstants.LEFT,
1227+ SwingConstants.LEFT
1228+ );
1229+
1230+ public static final ColumnAlignment RIGHT_ALIGNMENT =
1231+ new ColumnAlignment(
1232+ SwingConstants.RIGHT,
1233+ SwingConstants.RIGHT,
1234+ SwingConstants.RIGHT,
1235+ SwingConstants.RIGHT,
1236+ SwingConstants.RIGHT
1237+ );
1238+
1239+ public ColumnAlignment(int checkAlignment, int iconAlignment,
1240+ int textAlignment, int accAlignment,
1241+ int arrowAlignment) {
1242+ this.checkAlignment = checkAlignment;
1243+ this.iconAlignment = iconAlignment;
1244+ this.textAlignment = textAlignment;
1245+ this.accAlignment = accAlignment;
1246+ this.arrowAlignment = arrowAlignment;
1247+ }
1248+
1249+ public int getCheckAlignment() {
1250+ return checkAlignment;
1251+ }
1252+
1253+ public int getIconAlignment() {
1254+ return iconAlignment;
1255+ }
1256+
1257+ public int getTextAlignment() {
1258+ return textAlignment;
1259+ }
1260+
1261+ public int getAccAlignment() {
1262+ return accAlignment;
1263+ }
1264+
1265+ public int getArrowAlignment() {
1266+ return arrowAlignment;
1267+ }
1268+ }
1269+
1270+ public static class RectSize {
1271+ private int width;
1272+ private int height;
1273+ private int origWidth;
1274+ private int maxWidth;
1275+
1276+ public RectSize() {
1277+ }
1278+
1279+ public RectSize(int width, int height, int origWidth, int maxWidth) {
1280+ this.width = width;
1281+ this.height = height;
1282+ this.origWidth = origWidth;
1283+ this.maxWidth = maxWidth;
1284+ }
1285+
1286+ public int getWidth() {
1287+ return width;
1288+ }
1289+
1290+ public int getHeight() {
1291+ return height;
1292+ }
1293+
1294+ public int getOrigWidth() {
1295+ return origWidth;
1296+ }
1297+
1298+ public int getMaxWidth() {
1299+ return maxWidth;
1300+ }
1301+
1302+ public void setWidth(int width) {
1303+ this.width = width;
1304+ }
1305+
1306+ public void setHeight(int height) {
1307+ this.height = height;
1308+ }
1309+
1310+ public void setOrigWidth(int origWidth) {
1311+ this.origWidth = origWidth;
1312+ }
1313+
1314+ public void setMaxWidth(int maxWidth) {
1315+ this.maxWidth = maxWidth;
1316+ }
1317+
1318+ public String toString() {
1319+ return "[w=" + width + ",h=" + height + ",ow="
1320+ + origWidth + ",mw=" + maxWidth + "]";
1321+ }
1322+ }
1323+}