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