History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

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

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

Unified · Split

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