1/*
2 * Copyright (c) 2002, 2008, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package javax.swing.plaf.synth;
27
28import sun.swing.StringUIClientPropertyKey;
29import sun.swing.MenuItemLayoutHelper;
30
31import javax.swing.*;
32import javax.swing.text.View;
33import 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 */
59class 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
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
124    private boolean getAlignAcceleratorText(String propertyPrefix) {
125        return style.getBoolean(context,
126                propertyPrefix + ".alignAcceleratorText", true);
127    }
128
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
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
241    public SynthContext getContext() {
242        return context;
243    }
244
245    public SynthContext getAccContext() {
246        return accContext;
247    }
248
249    public SynthStyle getStyle() {
250        return style;
251    }
252
253    public SynthStyle getAccStyle() {
254        return accStyle;
255    }
256
257    public SynthGraphicsUtils getGraphicsUtils() {
258        return gu;
259    }
260
261    public SynthGraphicsUtils getAccGraphicsUtils() {
262        return accGu;
263    }
264
265    public boolean alignAcceleratorText() {
266        return alignAcceleratorText;
267    }
268
269    public int getMaxAccOrArrowWidth() {
270        return maxAccOrArrowWidth;
271    }
272
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
283    public ColumnAlignment getLTRColumnAlignment() {
284        if (alignAcceleratorText()) {
285            return LTR_ALIGNMENT_2;
286        } else {
287            return LTR_ALIGNMENT_1;
288        }
289    }
290
291    public ColumnAlignment getRTLColumnAlignment() {
292        if (alignAcceleratorText()) {
293            return RTL_ALIGNMENT_2;
294        } else {
295            return RTL_ALIGNMENT_1;
296        }
297    }
298
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