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 javax.swing.plaf.synth;
27 
28 import sun.swing.StringUIClientPropertyKey;
29 import sun.swing.MenuItemLayoutHelper;
30 
31 import javax.swing.*;
32 import javax.swing.text.View;
33 import java.awt.*;
34 
35 /**
36  * Calculates preferred size and layouts synth menu items.
37  *
38  * All JMenuItems (and JMenus) include enough space for the insets
39  * plus one or more elements.  When we say "label" below, we mean
40  * "icon and/or text."
41  *
42  * Cases to consider for SynthMenuItemUI (visualized here in a
43  * LTR orientation; the RTL case would be reversed):
44  *                   label
45  *      check icon + label
46  *      check icon + label + accelerator
47  *                   label + accelerator
48  *
49  * Cases to consider for SynthMenuUI (again visualized here in a
50  * LTR orientation):
51  *                   label + arrow
52  *
53  * Note that in the above scenarios, accelerator and arrow icon are
54  * mutually exclusive.  This means that if a popup menu contains a mix
55  * of JMenus and JMenuItems, we only need to allow enough space for
56  * max(maxAccelerator, maxArrow), and both accelerators and arrow icons
57  * can occupy the same "column" of space in the menu.
58  */
59 class SynthMenuItemLayoutHelper extends MenuItemLayoutHelper {
60 
61     public static final StringUIClientPropertyKey MAX_ACC_OR_ARROW_WIDTH =
62             new StringUIClientPropertyKey("maxAccOrArrowWidth");
63 
64     public static final ColumnAlignment LTR_ALIGNMENT_1 =
65             new ColumnAlignment(
66                     SwingConstants.LEFT,
67                     SwingConstants.LEFT,
68                     SwingConstants.LEFT,
69                     SwingConstants.RIGHT,
70                     SwingConstants.RIGHT
71             );
72     public static final ColumnAlignment LTR_ALIGNMENT_2 =
73             new ColumnAlignment(
74                     SwingConstants.LEFT,
75                     SwingConstants.LEFT,
76                     SwingConstants.LEFT,
77                     SwingConstants.LEFT,
78                     SwingConstants.RIGHT
79             );
80     public static final ColumnAlignment RTL_ALIGNMENT_1 =
81             new ColumnAlignment(
82                     SwingConstants.RIGHT,
83                     SwingConstants.RIGHT,
84                     SwingConstants.RIGHT,
85                     SwingConstants.LEFT,
86                     SwingConstants.LEFT
87             );
88     public static final ColumnAlignment RTL_ALIGNMENT_2 =
89             new ColumnAlignment(
90                     SwingConstants.RIGHT,
91                     SwingConstants.RIGHT,
92                     SwingConstants.RIGHT,
93                     SwingConstants.RIGHT,
94                     SwingConstants.LEFT
95             );
96 
97     private SynthContext context;
98     private SynthContext accContext;
99     private SynthStyle style;
100     private SynthStyle accStyle;
101     private SynthGraphicsUtils gu;
102     private SynthGraphicsUtils accGu;
103     private boolean alignAcceleratorText;
104     private int maxAccOrArrowWidth;
105 
SynthMenuItemLayoutHelper(SynthContext context, SynthContext accContext, JMenuItem mi, Icon checkIcon, Icon arrowIcon, Rectangle viewRect, int gap, String accDelimiter, boolean isLeftToRight, boolean useCheckAndArrow, String propertyPrefix)106     public SynthMenuItemLayoutHelper(SynthContext context, SynthContext accContext,
107                                      JMenuItem mi, Icon checkIcon, Icon arrowIcon,
108                                      Rectangle viewRect, int gap, String accDelimiter,
109                                      boolean isLeftToRight, boolean useCheckAndArrow,
110                                      String propertyPrefix) {
111         this.context = context;
112         this.accContext = accContext;
113         this.style = context.getStyle();
114         this.accStyle = accContext.getStyle();
115         this.gu = style.getGraphicsUtils(context);
116         this.accGu = accStyle.getGraphicsUtils(accContext);
117         this.alignAcceleratorText = getAlignAcceleratorText(propertyPrefix);
118         reset(mi, checkIcon, arrowIcon, viewRect, gap, accDelimiter,
119               isLeftToRight, style.getFont(context), accStyle.getFont(accContext),
120               useCheckAndArrow, propertyPrefix);
121         setLeadingGap(0);
122     }
123 
getAlignAcceleratorText(String propertyPrefix)124     private boolean getAlignAcceleratorText(String propertyPrefix) {
125         return style.getBoolean(context,
126                 propertyPrefix + ".alignAcceleratorText", true);
127     }
128 
calcWidthsAndHeights()129     protected void calcWidthsAndHeights() {
130         // iconRect
131         if (getIcon() != null) {
132             getIconSize().setWidth(SynthGraphicsUtils.getIconWidth(getIcon(), context));
133             getIconSize().setHeight(SynthGraphicsUtils.getIconHeight(getIcon(), context));
134         }
135 
136         // accRect
137         if (!getAccText().equals("")) {
138              getAccSize().setWidth(accGu.computeStringWidth(getAccContext(),
139                     getAccFontMetrics().getFont(), getAccFontMetrics(),
140                     getAccText()));
141             getAccSize().setHeight(getAccFontMetrics().getHeight());
142         }
143 
144         // textRect
145         if (getText() == null) {
146             setText("");
147         } else if (!getText().equals("")) {
148             if (getHtmlView() != null) {
149                 // Text is HTML
150                 getTextSize().setWidth(
151                         (int) getHtmlView().getPreferredSpan(View.X_AXIS));
152                 getTextSize().setHeight(
153                         (int) getHtmlView().getPreferredSpan(View.Y_AXIS));
154             } else {
155                 // Text isn't HTML
156                 getTextSize().setWidth(gu.computeStringWidth(context,
157                         getFontMetrics().getFont(), getFontMetrics(),
158                         getText()));
159                 getTextSize().setHeight(getFontMetrics().getHeight());
160             }
161         }
162 
163         if (useCheckAndArrow()) {
164             // checkIcon
165             if (getCheckIcon() != null) {
166                 getCheckSize().setWidth(
167                         SynthGraphicsUtils.getIconWidth(getCheckIcon(), context));
168                 getCheckSize().setHeight(
169                         SynthGraphicsUtils.getIconHeight(getCheckIcon(), context));
170             }
171             // arrowRect
172             if (getArrowIcon() != null) {
173                 getArrowSize().setWidth(
174                         SynthGraphicsUtils.getIconWidth(getArrowIcon(), context));
175                 getArrowSize().setHeight(
176                         SynthGraphicsUtils.getIconHeight(getArrowIcon(), context));
177             }
178         }
179 
180         // labelRect
181         if (isColumnLayout()) {
182             getLabelSize().setWidth(getIconSize().getWidth()
183                     + getTextSize().getWidth() + getGap());
184             getLabelSize().setHeight(MenuItemLayoutHelper.max(
185                     getCheckSize().getHeight(),
186                     getIconSize().getHeight(),
187                     getTextSize().getHeight(),
188                     getAccSize().getHeight(),
189                     getArrowSize().getHeight()));
190         } else {
191             Rectangle textRect = new Rectangle();
192             Rectangle iconRect = new Rectangle();
193             gu.layoutText(context, getFontMetrics(), getText(), getIcon(),
194                     getHorizontalAlignment(), getVerticalAlignment(),
195                     getHorizontalTextPosition(), getVerticalTextPosition(),
196                     getViewRect(), iconRect, textRect, getGap());
197             textRect.width += getLeftTextExtraWidth();
198             Rectangle labelRect = iconRect.union(textRect);
199             getLabelSize().setHeight(labelRect.height);
200             getLabelSize().setWidth(labelRect.width);
201         }
202     }
203 
calcMaxWidths()204     protected void calcMaxWidths() {
205         calcMaxWidth(getCheckSize(), MAX_CHECK_WIDTH);
206         maxAccOrArrowWidth =
207                 calcMaxValue(MAX_ACC_OR_ARROW_WIDTH, getArrowSize().getWidth());
208         maxAccOrArrowWidth =
209                 calcMaxValue(MAX_ACC_OR_ARROW_WIDTH, getAccSize().getWidth());
210 
211         if (isColumnLayout()) {
212             calcMaxWidth(getIconSize(), MAX_ICON_WIDTH);
213             calcMaxWidth(getTextSize(), MAX_TEXT_WIDTH);
214             int curGap = getGap();
215             if ((getIconSize().getMaxWidth() == 0)
216                     || (getTextSize().getMaxWidth() == 0)) {
217                 curGap = 0;
218             }
219             getLabelSize().setMaxWidth(
220                     calcMaxValue(MAX_LABEL_WIDTH, getIconSize().getMaxWidth()
221                             + getTextSize().getMaxWidth() + curGap));
222         } else {
223             // We shouldn't use current icon and text widths
224             // in maximal widths calculation for complex layout.
225             getIconSize().setMaxWidth(getParentIntProperty(
226                     MAX_ICON_WIDTH));
227             calcMaxWidth(getLabelSize(), MAX_LABEL_WIDTH);
228             // If maxLabelWidth is wider
229             // than the widest icon + the widest text + gap,
230             // we should update the maximal text witdh
231             int candidateTextWidth = getLabelSize().getMaxWidth() -
232                     getIconSize().getMaxWidth();
233             if (getIconSize().getMaxWidth() > 0) {
234                 candidateTextWidth -= getGap();
235             }
236             getTextSize().setMaxWidth(calcMaxValue(
237                     MAX_TEXT_WIDTH, candidateTextWidth));
238         }
239     }
240 
getContext()241     public SynthContext getContext() {
242         return context;
243     }
244 
getAccContext()245     public SynthContext getAccContext() {
246         return accContext;
247     }
248 
getStyle()249     public SynthStyle getStyle() {
250         return style;
251     }
252 
getAccStyle()253     public SynthStyle getAccStyle() {
254         return accStyle;
255     }
256 
getGraphicsUtils()257     public SynthGraphicsUtils getGraphicsUtils() {
258         return gu;
259     }
260 
getAccGraphicsUtils()261     public SynthGraphicsUtils getAccGraphicsUtils() {
262         return accGu;
263     }
264 
alignAcceleratorText()265     public boolean alignAcceleratorText() {
266         return alignAcceleratorText;
267     }
268 
getMaxAccOrArrowWidth()269     public int getMaxAccOrArrowWidth() {
270         return maxAccOrArrowWidth;
271     }
272 
prepareForLayout(LayoutResult lr)273     protected void prepareForLayout(LayoutResult lr) {
274         lr.getCheckRect().width = getCheckSize().getMaxWidth();
275         // An item can have an arrow or a check icon at once
276         if (useCheckAndArrow() && (!"".equals(getAccText()))) {
277             lr.getAccRect().width = maxAccOrArrowWidth;
278         } else {
279             lr.getArrowRect().width = maxAccOrArrowWidth;
280         }
281     }
282 
getLTRColumnAlignment()283     public ColumnAlignment getLTRColumnAlignment() {
284         if (alignAcceleratorText()) {
285             return LTR_ALIGNMENT_2;
286         } else {
287             return LTR_ALIGNMENT_1;
288         }
289     }
290 
getRTLColumnAlignment()291     public ColumnAlignment getRTLColumnAlignment() {
292         if (alignAcceleratorText()) {
293             return RTL_ALIGNMENT_2;
294         } else {
295             return RTL_ALIGNMENT_1;
296         }
297     }
298 
layoutIconAndTextInLabelRect(LayoutResult lr)299     protected void layoutIconAndTextInLabelRect(LayoutResult lr) {
300         lr.setTextRect(new Rectangle());
301         lr.setIconRect(new Rectangle());
302         gu.layoutText(context, getFontMetrics(), getText(), getIcon(),
303                 getHorizontalAlignment(), getVerticalAlignment(),
304                 getHorizontalTextPosition(), getVerticalTextPosition(),
305                 lr.getLabelRect(), lr.getIconRect(), lr.getTextRect(), getGap());
306     }
307 }
308