1 /*
2  * Copyright (c) 2002, 2018, 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 package sun.awt.X11;
26 
27 import java.awt.*;
28 import java.awt.peer.*;
29 import java.awt.event.*;
30 
31 import sun.awt.AWTAccessor;
32 
33 public class XMenuItemPeer implements MenuItemPeer {
34 
35     /************************************************
36      *
37      * Data members
38      *
39      ************************************************/
40 
41     /*
42      * Primary members
43      */
44 
45     /**
46      * Window that this item belongs to.
47      */
48     private XBaseMenuWindow container;
49 
50     /**
51      * Target MenuItem. Note that 'target' member
52      * in XWindow is required for dispatching events.
53      * This member is only used for accessing its fields
54      * and firing ActionEvent & ItemEvent
55      */
56     private MenuItem target;
57 
58     /*
59      * Mapping to window
60      */
61 
62     /**
63      * Rectangle occupied by menu item in container's
64      * coordinates. Filled by map(...) function from
65      * XBaseMenuWindow.map()
66      */
67     private Rectangle bounds;
68 
69     /**
70      * Point in container's coordinate system used as
71      * origin by drawText.
72      */
73     private Point textOrigin;
74 
75     /*
76      * Size constants
77      */
78     private static final int SEPARATOR_WIDTH = 20;
79     private static final int SEPARATOR_HEIGHT = 5;
80 
81     /************************************************
82      *
83      * Text Metrics
84      *
85      ************************************************/
86 
87     /**
88      * Text metrics are filled in calcTextMetrics function
89      * and reset in resetTextMetrics function. Text metrics
90      * contain calculated dimensions of various components of
91      * menu item.
92      */
93     private TextMetrics textMetrics;
94 
95     static class TextMetrics implements Cloneable {
96         /*
97          * Calculated text size members
98          */
99         private Dimension textDimension;
100         private int shortcutWidth;
101         private int textBaseline;
102 
TextMetrics(Dimension textDimension, int shortcutWidth, int textBaseline)103         TextMetrics(Dimension textDimension, int shortcutWidth, int textBaseline) {
104             this.textDimension = textDimension;
105             this.shortcutWidth = shortcutWidth;
106             this.textBaseline = textBaseline;
107         }
108 
clone()109         public Object clone() {
110             try {
111                 return super.clone();
112             } catch (CloneNotSupportedException ex) {
113                 throw new InternalError(ex);
114             }
115         }
116 
getTextDimension()117         Dimension getTextDimension() {
118             return this.textDimension;
119         }
120 
getShortcutWidth()121         int getShortcutWidth() {
122             return this.shortcutWidth;
123         }
124 
getTextBaseline()125         int getTextBaseline() {
126             return this.textBaseline;
127         }
128     }
129 
130     /************************************************
131      *
132      * Construction
133      *
134      ************************************************/
XMenuItemPeer(MenuItem target)135     XMenuItemPeer(MenuItem target) {
136         this.target = target;
137     }
138 
139     /************************************************
140      *
141      * Implementaion of interface methods
142      *
143      ************************************************/
144 
145     /*
146      * From MenuComponentPeer
147      */
dispose()148     public void dispose() {
149         //Empty function
150     }
151 
setFont(Font font)152     public void setFont(Font font) {
153         resetTextMetrics();
154         repaintIfShowing();
155     }
156     /*
157      * From MenuItemPeer
158      */
setLabel(String label)159     public void setLabel(String label) {
160         resetTextMetrics();
161         repaintIfShowing();
162     }
163 
setEnabled(boolean enabled)164     public void setEnabled(boolean enabled) {
165         repaintIfShowing();
166     }
167 
168     /************************************************
169      *
170      * Access to target's fields
171      *
172      ************************************************/
173 
getTarget()174     MenuItem getTarget() {
175         return this.target;
176     }
177 
getTargetFont()178     Font getTargetFont() {
179         if (target == null) {
180             return XWindow.getDefaultFont();
181         }
182         return AWTAccessor.getMenuComponentAccessor().getFont_NoClientCode(target);
183     }
184 
getTargetLabel()185     String getTargetLabel() {
186         if (target == null) {
187             return "";
188         }
189         String label = AWTAccessor.getMenuItemAccessor().getLabel(target);
190         return (label == null) ? "" : label;
191     }
192 
isTargetEnabled()193     boolean isTargetEnabled() {
194         if (target == null) {
195             return false;
196         }
197         return AWTAccessor.getMenuItemAccessor().isEnabled(target);
198     }
199 
200     /**
201      * Returns true if item and all its parents are enabled
202      * This function is used to fix
203      * 6184485: Popup menu is not disabled on XToolkit even when calling setEnabled (false)
204      */
isTargetItemEnabled()205     boolean isTargetItemEnabled() {
206         if (target == null) {
207             return false;
208         }
209         return AWTAccessor.getMenuItemAccessor().isItemEnabled(target);
210     }
211 
getTargetActionCommand()212     String getTargetActionCommand() {
213         if (target == null) {
214             return "";
215         }
216         return AWTAccessor.getMenuItemAccessor().getActionCommandImpl(target);
217     }
218 
getTargetShortcut()219     MenuShortcut getTargetShortcut() {
220         if (target == null) {
221             return null;
222         }
223         return AWTAccessor.getMenuItemAccessor().getShortcut(target);
224     }
225 
getShortcutText()226     String getShortcutText() {
227         //Fix for 6180413: shortcuts should not be displayed for any of the menuitems in a popup menu
228         if (container == null) {
229             return null;
230         }
231         if (container.getRootMenuWindow() instanceof XPopupMenuPeer) {
232             return null;
233         }
234         MenuShortcut sc = getTargetShortcut();
235         //TODO:This can potentially call user code
236         return (sc == null) ? null : sc.toString();
237     }
238 
239 
240     /************************************************
241      *
242      * Basic manipulations
243      *
244      ************************************************/
245 
246     /**
247      * This function is called when filling item vectors
248      * in XMenuWindow & XMenuBar. We need it because peers
249      * are created earlier than windows.
250      * @param container the window that this item belongs to.
251      */
setContainer(XBaseMenuWindow container)252     void setContainer(XBaseMenuWindow container) {
253         synchronized(XBaseMenuWindow.getMenuTreeLock()) {
254             this.container = container;
255         }
256     }
257 
258     /**
259      * returns the window that this item belongs to
260      */
getContainer()261     XBaseMenuWindow getContainer() {
262         return this.container;
263     }
264 
265     /************************************************
266      *
267      * Overridable behaviour
268      *
269      ************************************************/
270 
271     /**
272      * This function should be overriden simply to
273      * return false in inherited classes.
274      */
isSeparator()275     boolean isSeparator() {
276         boolean r = (getTargetLabel().equals("-"));
277         return r;
278     }
279 
280     /************************************************
281      *
282      * Utility functions
283      *
284      ************************************************/
285 
286     /**
287      * Returns true if container exists and is showing
288      */
isContainerShowing()289     boolean isContainerShowing() {
290         if (container == null) {
291             return false;
292         }
293         return container.isShowing();
294     }
295 
296     /**
297      * Repaints item if it is showing
298      */
repaintIfShowing()299     void repaintIfShowing() {
300         if (isContainerShowing()) {
301             container.postPaintEvent();
302         }
303     }
304 
305     /**
306      * This function is invoked when the user clicks
307      * on menu item.
308      * @param when the timestamp of action event
309      */
action(long when, int modifiers)310     void action(long when, int modifiers) {
311         if (!isSeparator() && isTargetItemEnabled()) {
312             XWindow.postEventStatic(new ActionEvent(target, ActionEvent.ACTION_PERFORMED,
313                                                     getTargetActionCommand(), when,
314                                                     modifiers));
315         }
316     }
317     /************************************************
318      *
319      * Text metrics
320      *
321      ************************************************/
322 
323     /**
324      * Returns text metrics of menu item.
325      * This function does not use any locks
326      * and is guaranteed to return some value
327      * (possibly actual, possibly expired)
328      */
getTextMetrics()329     TextMetrics getTextMetrics() {
330         TextMetrics textMetrics = this.textMetrics;
331         if (textMetrics == null) {
332             textMetrics = calcTextMetrics();
333             this.textMetrics = textMetrics;
334         }
335         return textMetrics;
336     }
337 
338     /**
339      * Returns dimensions of item's label.
340      * This function does not use any locks
341      * Returns actual or expired  value
342      * or null if error occurs
343      */
344     /*Dimension getTextDimension() {
345         TextMetrics textMetrics = this.textMetrics;
346         if (textMetrics == null) {
347             textMetrics = calcTextMetrics();
348             this.textMetrics = textMetrics;
349         }
350         return (textMetrics != null) ? textMetrics.textDimension : null;
351         }*/
352 
353     /**
354      * Returns width of item's shortcut label,
355      * 0 if item has no shortcut.
356      * The height of shortcut can be deternimed
357      * from text dimensions.
358      * This function does not use any locks
359      * and is guaranteed to return some value
360      * (possibly actual, possibly expired)
361      */
362     /*int getShortcutWidth() {
363         TextMetrics textMetrics = this.textMetrics;
364         if (textMetrics == null) {
365             textMetrics = calcTextMetrics();
366             this.textMetrics = textMetrics;
367         }
368         return (textMetrics != null) ? textMetrics.shortcutWidth : 0;
369     }
370 
371     int getTextBaseline() {
372         TextMetrics textMetrics = this.textMetrics;
373         if (textMetrics == null) {
374             textMetrics = calcTextMetrics();
375             this.textMetrics = textMetrics;
376         }
377         return (textMetrics != null) ? textMetrics.textBaseline : 0;
378         }*/
379 
calcTextMetrics()380     TextMetrics calcTextMetrics() {
381         if (container == null) {
382             return null;
383         }
384         if (isSeparator()) {
385             return new TextMetrics(new Dimension(SEPARATOR_WIDTH, SEPARATOR_HEIGHT), 0, 0);
386         }
387         Graphics g = container.getGraphics();
388         if (g == null) {
389             return null;
390         }
391         try {
392             g.setFont(getTargetFont());
393             FontMetrics fm = g.getFontMetrics();
394             String str = getTargetLabel();
395             int width = fm.stringWidth(str);
396             int height = fm.getHeight();
397             Dimension textDimension = new Dimension(width, height);
398             int textBaseline = fm.getHeight() - fm.getAscent();
399             String sc = getShortcutText();
400             int shortcutWidth = (sc == null) ? 0 : fm.stringWidth(sc);
401             return new TextMetrics(textDimension, shortcutWidth, textBaseline);
402         } finally {
403             g.dispose();
404         }
405     }
406 
resetTextMetrics()407     void resetTextMetrics() {
408         textMetrics = null;
409         if (container != null) {
410             container.updateSize();
411         }
412     }
413 
414     /************************************************
415      *
416      * Mapping utility functions
417      *
418      ************************************************/
419 
420     /**
421      * Sets mapping of item to window.
422      * @param bounds bounds of item in container's coordinates
423      * @param textOrigin point for drawString in container's coordinates
424      * @see XBaseMenuWindow#map()
425      */
map(Rectangle bounds, Point textOrigin)426     void map(Rectangle bounds, Point textOrigin) {
427         this.bounds = bounds;
428         this.textOrigin = textOrigin;
429     }
430 
431     /**
432      * returns bounds of item that were previously set by map() function
433      */
getBounds()434     Rectangle getBounds() {
435         return bounds;
436     }
437 
438     /**
439      * returns origin of item's text that was previously set by map() function
440      */
getTextOrigin()441     Point getTextOrigin() {
442         return textOrigin;
443     }
444 
445 }
446