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