View Javadoc
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 
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 
125     private boolean getAlignAcceleratorText(String propertyPrefix) {
126         return style.getBoolean(context,
127                 propertyPrefix + ".alignAcceleratorText", true);
128     }
129 
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 
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 
242     public SynthContext getContext() {
243         return context;
244     }
245 
246     public SynthContext getAccContext() {
247         return accContext;
248     }
249 
250     public SynthStyle getStyle() {
251         return style;
252     }
253 
254     public SynthStyle getAccStyle() {
255         return accStyle;
256     }
257 
258     public SynthGraphicsUtils getGraphicsUtils() {
259         return gu;
260     }
261 
262     public SynthGraphicsUtils getAccGraphicsUtils() {
263         return accGu;
264     }
265 
266     public boolean alignAcceleratorText() {
267         return alignAcceleratorText;
268     }
269 
270     public int getMaxAccOrArrowWidth() {
271         return maxAccOrArrowWidth;
272     }
273 
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 
284     public ColumnAlignment getLTRColumnAlignment() {
285         if (alignAcceleratorText()) {
286             return LTR_ALIGNMENT_2;
287         } else {
288             return LTR_ALIGNMENT_1;
289         }
290     }
291 
292     public ColumnAlignment getRTLColumnAlignment() {
293         if (alignAcceleratorText()) {
294             return RTL_ALIGNMENT_2;
295         } else {
296             return RTL_ALIGNMENT_1;
297         }
298     }
299 
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 }