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