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