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 
getAccText(String acceleratorDelimiter)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 
getIcon(String propertyPrefix)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 
getMinTextOffset(String propertyPrefix)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 
getAfterCheckIconGap(String propertyPrefix)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 
getLeadingGap(String propertyPrefix)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 
getCheckOffset(String propertyPrefix)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 
calcWidthsAndHeights()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 
calcMaxWidths()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 
calcMaxWidth(RectSize rs, Object key)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      */
calcMaxValue(Object propertyName, int value)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      */
getParentIntProperty(Object propertyName)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 
isColumnLayout(boolean isLeftToRight, JMenuItem mi)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      */
isColumnLayout(boolean isLeftToRight, int horizontalAlignment, int horizontalTextPosition, int verticalTextPosition)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      */
calcMaxTextOffset(Rectangle viewRect)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      */
layoutMenuItem()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 
createLayoutResult()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 
getLTRColumnAlignment()508     public ColumnAlignment getLTRColumnAlignment() {
509         return ColumnAlignment.LEFT_ALIGNMENT;
510     }
511 
getRTLColumnAlignment()512     public ColumnAlignment getRTLColumnAlignment() {
513         return ColumnAlignment.RIGHT_ALIGNMENT;
514     }
515 
prepareForLayout(LayoutResult lr)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      */
alignAccCheckAndArrowVertically(LayoutResult lr)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      */
fixVerticalAlignment(LayoutResult lr, Rectangle r)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 
doLTRColumnLayout(LayoutResult lr, ColumnAlignment alignment)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 
doLTRComplexLayout(LayoutResult lr, ColumnAlignment alignment)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 
doRTLColumnLayout(LayoutResult lr, ColumnAlignment alignment)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 
doRTLComplexLayout(LayoutResult lr, ColumnAlignment alignment)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 
alignRects(LayoutResult lr, ColumnAlignment alignment)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 
alignRect(Rectangle rect, int alignment, int origWidth)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 
layoutIconAndTextInLabelRect(LayoutResult lr)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 
calcXPositionsLTR(int startXPos, int leadingGap, int gap, Rectangle... rects)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 
calcXPositionsRTL(int startXPos, int leadingGap, int gap, Rectangle... rects)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      */
calcTextAndIconYPositions(LayoutResult lr)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      */
calcLabelYPosition(LayoutResult lr)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      */
getMenuItemParent(JMenuItem menuItem)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 
clearUsedParentClientProperties(JMenuItem menuItem)814     public static void clearUsedParentClientProperties(JMenuItem menuItem) {
815         clearUsedClientProperties(getMenuItemParent(menuItem));
816     }
817 
clearUsedClientProperties(JComponent c)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      */
max(int... values)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 
createMaxRect()845     public static Rectangle createMaxRect() {
846         return new Rectangle(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
847     }
848 
addMaxWidth(RectSize size, int gap, Dimension result)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 
addWidth(int width, int gap, Dimension result)855     public static void addWidth(int width, int gap, Dimension result) {
856         if (width > 0) {
857             result.width += width + gap;
858         }
859     }
860 
getMenuItem()861     public JMenuItem getMenuItem() {
862         return mi;
863     }
864 
getMenuItemParent()865     public JComponent getMenuItemParent() {
866         return miParent;
867     }
868 
getFont()869     public Font getFont() {
870         return font;
871     }
872 
getAccFont()873     public Font getAccFont() {
874         return accFont;
875     }
876 
getFontMetrics()877     public FontMetrics getFontMetrics() {
878         return fm;
879     }
880 
getAccFontMetrics()881     public FontMetrics getAccFontMetrics() {
882         return accFm;
883     }
884 
getIcon()885     public Icon getIcon() {
886         return icon;
887     }
888 
getCheckIcon()889     public Icon getCheckIcon() {
890         return checkIcon;
891     }
892 
getArrowIcon()893     public Icon getArrowIcon() {
894         return arrowIcon;
895     }
896 
getText()897     public String getText() {
898         return text;
899     }
900 
getAccText()901     public String getAccText() {
902         return accText;
903     }
904 
isColumnLayout()905     public boolean isColumnLayout() {
906         return isColumnLayout;
907     }
908 
useCheckAndArrow()909     public boolean useCheckAndArrow() {
910         return useCheckAndArrow;
911     }
912 
isLeftToRight()913     public boolean isLeftToRight() {
914         return isLeftToRight;
915     }
916 
isTopLevelMenu()917     public boolean isTopLevelMenu() {
918         return isTopLevelMenu;
919     }
920 
getHtmlView()921     public View getHtmlView() {
922         return htmlView;
923     }
924 
getVerticalAlignment()925     public int getVerticalAlignment() {
926         return verticalAlignment;
927     }
928 
getHorizontalAlignment()929     public int getHorizontalAlignment() {
930         return horizontalAlignment;
931     }
932 
getVerticalTextPosition()933     public int getVerticalTextPosition() {
934         return verticalTextPosition;
935     }
936 
getHorizontalTextPosition()937     public int getHorizontalTextPosition() {
938         return horizontalTextPosition;
939     }
940 
getGap()941     public int getGap() {
942         return gap;
943     }
944 
getLeadingGap()945     public int getLeadingGap() {
946         return leadingGap;
947     }
948 
getAfterCheckIconGap()949     public int getAfterCheckIconGap() {
950         return afterCheckIconGap;
951     }
952 
getMinTextOffset()953     public int getMinTextOffset() {
954         return minTextOffset;
955     }
956 
getViewRect()957     public Rectangle getViewRect() {
958         return viewRect;
959     }
960 
getIconSize()961     public RectSize getIconSize() {
962         return iconSize;
963     }
964 
getTextSize()965     public RectSize getTextSize() {
966         return textSize;
967     }
968 
getAccSize()969     public RectSize getAccSize() {
970         return accSize;
971     }
972 
getCheckSize()973     public RectSize getCheckSize() {
974         return checkSize;
975     }
976 
getArrowSize()977     public RectSize getArrowSize() {
978         return arrowSize;
979     }
980 
getLabelSize()981     public RectSize getLabelSize() {
982         return labelSize;
983     }
984 
setMenuItem(JMenuItem mi)985     protected void setMenuItem(JMenuItem mi) {
986         this.mi = mi;
987     }
988 
setMenuItemParent(JComponent miParent)989     protected void setMenuItemParent(JComponent miParent) {
990         this.miParent = miParent;
991     }
992 
setFont(Font font)993     protected void setFont(Font font) {
994         this.font = font;
995     }
996 
setAccFont(Font accFont)997     protected void setAccFont(Font accFont) {
998         this.accFont = accFont;
999     }
1000 
setFontMetrics(FontMetrics fm)1001     protected void setFontMetrics(FontMetrics fm) {
1002         this.fm = fm;
1003     }
1004 
setAccFontMetrics(FontMetrics accFm)1005     protected void setAccFontMetrics(FontMetrics accFm) {
1006         this.accFm = accFm;
1007     }
1008 
setIcon(Icon icon)1009     protected void setIcon(Icon icon) {
1010         this.icon = icon;
1011     }
1012 
setCheckIcon(Icon checkIcon)1013     protected void setCheckIcon(Icon checkIcon) {
1014         this.checkIcon = checkIcon;
1015     }
1016 
setArrowIcon(Icon arrowIcon)1017     protected void setArrowIcon(Icon arrowIcon) {
1018         this.arrowIcon = arrowIcon;
1019     }
1020 
setText(String text)1021     protected void setText(String text) {
1022         this.text = text;
1023     }
1024 
setAccText(String accText)1025     protected void setAccText(String accText) {
1026         this.accText = accText;
1027     }
1028 
setColumnLayout(boolean columnLayout)1029     protected void setColumnLayout(boolean columnLayout) {
1030         isColumnLayout = columnLayout;
1031     }
1032 
setUseCheckAndArrow(boolean useCheckAndArrow)1033     protected void setUseCheckAndArrow(boolean useCheckAndArrow) {
1034         this.useCheckAndArrow = useCheckAndArrow;
1035     }
1036 
setLeftToRight(boolean leftToRight)1037     protected void setLeftToRight(boolean leftToRight) {
1038         isLeftToRight = leftToRight;
1039     }
1040 
setTopLevelMenu(boolean topLevelMenu)1041     protected void setTopLevelMenu(boolean topLevelMenu) {
1042         isTopLevelMenu = topLevelMenu;
1043     }
1044 
setHtmlView(View htmlView)1045     protected void setHtmlView(View htmlView) {
1046         this.htmlView = htmlView;
1047     }
1048 
setVerticalAlignment(int verticalAlignment)1049     protected void setVerticalAlignment(int verticalAlignment) {
1050         this.verticalAlignment = verticalAlignment;
1051     }
1052 
setHorizontalAlignment(int horizontalAlignment)1053     protected void setHorizontalAlignment(int horizontalAlignment) {
1054         this.horizontalAlignment = horizontalAlignment;
1055     }
1056 
setVerticalTextPosition(int verticalTextPosition)1057     protected void setVerticalTextPosition(int verticalTextPosition) {
1058         this.verticalTextPosition = verticalTextPosition;
1059     }
1060 
setHorizontalTextPosition(int horizontalTextPosition)1061     protected void setHorizontalTextPosition(int horizontalTextPosition) {
1062         this.horizontalTextPosition = horizontalTextPosition;
1063     }
1064 
setGap(int gap)1065     protected void setGap(int gap) {
1066         this.gap = gap;
1067     }
1068 
setLeadingGap(int leadingGap)1069     protected void setLeadingGap(int leadingGap) {
1070         this.leadingGap = leadingGap;
1071     }
1072 
setAfterCheckIconGap(int afterCheckIconGap)1073     protected void setAfterCheckIconGap(int afterCheckIconGap) {
1074         this.afterCheckIconGap = afterCheckIconGap;
1075     }
1076 
setMinTextOffset(int minTextOffset)1077     protected void setMinTextOffset(int minTextOffset) {
1078         this.minTextOffset = minTextOffset;
1079     }
1080 
setViewRect(Rectangle viewRect)1081     protected void setViewRect(Rectangle viewRect) {
1082         this.viewRect = viewRect;
1083     }
1084 
setIconSize(RectSize iconSize)1085     protected void setIconSize(RectSize iconSize) {
1086         this.iconSize = iconSize;
1087     }
1088 
setTextSize(RectSize textSize)1089     protected void setTextSize(RectSize textSize) {
1090         this.textSize = textSize;
1091     }
1092 
setAccSize(RectSize accSize)1093     protected void setAccSize(RectSize accSize) {
1094         this.accSize = accSize;
1095     }
1096 
setCheckSize(RectSize checkSize)1097     protected void setCheckSize(RectSize checkSize) {
1098         this.checkSize = checkSize;
1099     }
1100 
setArrowSize(RectSize arrowSize)1101     protected void setArrowSize(RectSize arrowSize) {
1102         this.arrowSize = arrowSize;
1103     }
1104 
setLabelSize(RectSize labelSize)1105     protected void setLabelSize(RectSize labelSize) {
1106         this.labelSize = labelSize;
1107     }
1108 
getLeftTextExtraWidth()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      */
useCheckAndArrow(JMenuItem menuItem)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 
LayoutResult()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 
LayoutResult(Rectangle iconRect, Rectangle textRect, Rectangle accRect, Rectangle checkRect, Rectangle arrowRect, Rectangle labelRect)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 
getIconRect()1154         public Rectangle getIconRect() {
1155             return iconRect;
1156         }
1157 
setIconRect(Rectangle iconRect)1158         public void setIconRect(Rectangle iconRect) {
1159             this.iconRect = iconRect;
1160         }
1161 
getTextRect()1162         public Rectangle getTextRect() {
1163             return textRect;
1164         }
1165 
setTextRect(Rectangle textRect)1166         public void setTextRect(Rectangle textRect) {
1167             this.textRect = textRect;
1168         }
1169 
getAccRect()1170         public Rectangle getAccRect() {
1171             return accRect;
1172         }
1173 
setAccRect(Rectangle accRect)1174         public void setAccRect(Rectangle accRect) {
1175             this.accRect = accRect;
1176         }
1177 
getCheckRect()1178         public Rectangle getCheckRect() {
1179             return checkRect;
1180         }
1181 
setCheckRect(Rectangle checkRect)1182         public void setCheckRect(Rectangle checkRect) {
1183             this.checkRect = checkRect;
1184         }
1185 
getArrowRect()1186         public Rectangle getArrowRect() {
1187             return arrowRect;
1188         }
1189 
setArrowRect(Rectangle arrowRect)1190         public void setArrowRect(Rectangle arrowRect) {
1191             this.arrowRect = arrowRect;
1192         }
1193 
getLabelRect()1194         public Rectangle getLabelRect() {
1195             return labelRect;
1196         }
1197 
setLabelRect(Rectangle labelRect)1198         public void setLabelRect(Rectangle labelRect) {
1199             this.labelRect = labelRect;
1200         }
1201 
getAllRects()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 
ColumnAlignment(int checkAlignment, int iconAlignment, int textAlignment, int accAlignment, int arrowAlignment)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 
getCheckAlignment()1249         public int getCheckAlignment() {
1250             return checkAlignment;
1251         }
1252 
getIconAlignment()1253         public int getIconAlignment() {
1254             return iconAlignment;
1255         }
1256 
getTextAlignment()1257         public int getTextAlignment() {
1258             return textAlignment;
1259         }
1260 
getAccAlignment()1261         public int getAccAlignment() {
1262             return accAlignment;
1263         }
1264 
getArrowAlignment()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 
RectSize()1276         public RectSize() {
1277         }
1278 
RectSize(int width, int height, int origWidth, int maxWidth)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 
getWidth()1286         public int getWidth() {
1287             return width;
1288         }
1289 
getHeight()1290         public int getHeight() {
1291             return height;
1292         }
1293 
getOrigWidth()1294         public int getOrigWidth() {
1295             return origWidth;
1296         }
1297 
getMaxWidth()1298         public int getMaxWidth() {
1299             return maxWidth;
1300         }
1301 
setWidth(int width)1302         public void setWidth(int width) {
1303             this.width = width;
1304         }
1305 
setHeight(int height)1306         public void setHeight(int height) {
1307             this.height = height;
1308         }
1309 
setOrigWidth(int origWidth)1310         public void setOrigWidth(int origWidth) {
1311             this.origWidth = origWidth;
1312         }
1313 
setMaxWidth(int maxWidth)1314         public void setMaxWidth(int maxWidth) {
1315             this.maxWidth = maxWidth;
1316         }
1317 
toString()1318         public String toString() {
1319             return "[w=" + width + ",h=" + height + ",ow="
1320                     + origWidth + ",mw=" + maxWidth + "]";
1321         }
1322     }
1323 }
1324