1 /*
2  * Copyright (c) 2002, 2013, 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 final static int SEPARATOR_WIDTH = 20;
79     private final static 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      * DEPRECATED:  Replaced by setEnabled(boolean).
170      * @see java.awt.peer.MenuItemPeer
171      */
enable()172     public void enable() {
173         setEnabled( true );
174     }
175 
176     /**
177      * DEPRECATED:  Replaced by setEnabled(boolean).
178      * @see java.awt.peer.MenuItemPeer
179      */
disable()180     public void disable() {
181         setEnabled( false );
182     }
183 
184     /************************************************
185      *
186      * Access to target's fields
187      *
188      ************************************************/
189 
getTarget()190     MenuItem getTarget() {
191         return this.target;
192     }
193 
getTargetFont()194     Font getTargetFont() {
195         if (target == null) {
196             return XWindow.getDefaultFont();
197         }
198         return AWTAccessor.getMenuComponentAccessor().getFont_NoClientCode(target);
199     }
200 
getTargetLabel()201     String getTargetLabel() {
202         if (target == null) {
203             return "";
204         }
205         String label = AWTAccessor.getMenuItemAccessor().getLabel(target);
206         return (label == null) ? "" : label;
207     }
208 
isTargetEnabled()209     boolean isTargetEnabled() {
210         if (target == null) {
211             return false;
212         }
213         return AWTAccessor.getMenuItemAccessor().isEnabled(target);
214     }
215 
216     /**
217      * Returns true if item and all its parents are enabled
218      * This function is used to fix
219      * 6184485: Popup menu is not disabled on XToolkit even when calling setEnabled (false)
220      */
isTargetItemEnabled()221     boolean isTargetItemEnabled() {
222         if (target == null) {
223             return false;
224         }
225         return AWTAccessor.getMenuItemAccessor().isItemEnabled(target);
226     }
227 
getTargetActionCommand()228     String getTargetActionCommand() {
229         if (target == null) {
230             return "";
231         }
232         return AWTAccessor.getMenuItemAccessor().getActionCommandImpl(target);
233     }
234 
getTargetShortcut()235     MenuShortcut getTargetShortcut() {
236         if (target == null) {
237             return null;
238         }
239         return AWTAccessor.getMenuItemAccessor().getShortcut(target);
240     }
241 
getShortcutText()242     String getShortcutText() {
243         //Fix for 6180413: shortcuts should not be displayed for any of the menuitems in a popup menu
244         if (container == null) {
245             return null;
246         }
247         if (container.getRootMenuWindow() instanceof XPopupMenuPeer) {
248             return null;
249         }
250         MenuShortcut sc = getTargetShortcut();
251         //TODO:This can potentially call user code
252         return (sc == null) ? null : sc.toString();
253     }
254 
255 
256     /************************************************
257      *
258      * Basic manipulations
259      *
260      ************************************************/
261 
262     /**
263      * This function is called when filling item vectors
264      * in XMenuWindow & XMenuBar. We need it because peers
265      * are created earlier than windows.
266      * @param container the window that this item belongs to.
267      */
setContainer(XBaseMenuWindow container)268     void setContainer(XBaseMenuWindow container) {
269         synchronized(XBaseMenuWindow.getMenuTreeLock()) {
270             this.container = container;
271         }
272     }
273 
274     /**
275      * returns the window that this item belongs to
276      */
getContainer()277     XBaseMenuWindow getContainer() {
278         return this.container;
279     }
280 
281     /************************************************
282      *
283      * Overridable behaviour
284      *
285      ************************************************/
286 
287     /**
288      * This function should be overriden simply to
289      * return false in inherited classes.
290      */
isSeparator()291     boolean isSeparator() {
292         boolean r = (getTargetLabel().equals("-"));
293         return r;
294     }
295 
296     /************************************************
297      *
298      * Utility functions
299      *
300      ************************************************/
301 
302     /**
303      * Returns true if container exists and is showing
304      */
isContainerShowing()305     boolean isContainerShowing() {
306         if (container == null) {
307             return false;
308         }
309         return container.isShowing();
310     }
311 
312     /**
313      * Repaints item if it is showing
314      */
repaintIfShowing()315     void repaintIfShowing() {
316         if (isContainerShowing()) {
317             container.postPaintEvent();
318         }
319     }
320 
321     /**
322      * This function is invoked when the user clicks
323      * on menu item.
324      * @param when the timestamp of action event
325      */
action(long when)326     void action(long when) {
327         if (!isSeparator() && isTargetItemEnabled()) {
328             XWindow.postEventStatic(new ActionEvent(target, ActionEvent.ACTION_PERFORMED,
329                                                     getTargetActionCommand(), when,
330                                                     0));
331         }
332     }
333     /************************************************
334      *
335      * Text metrics
336      *
337      ************************************************/
338 
339     /**
340      * Returns text metrics of menu item.
341      * This function does not use any locks
342      * and is guaranteed to return some value
343      * (possibly actual, possibly expired)
344      */
getTextMetrics()345     TextMetrics getTextMetrics() {
346         TextMetrics textMetrics = this.textMetrics;
347         if (textMetrics == null) {
348             textMetrics = calcTextMetrics();
349             this.textMetrics = textMetrics;
350         }
351         return textMetrics;
352     }
353 
354     /**
355      * Returns dimensions of item's label.
356      * This function does not use any locks
357      * Returns actual or expired  value
358      * or null if error occurs
359      */
360     /*Dimension getTextDimension() {
361         TextMetrics textMetrics = this.textMetrics;
362         if (textMetrics == null) {
363             textMetrics = calcTextMetrics();
364             this.textMetrics = textMetrics;
365         }
366         return (textMetrics != null) ? textMetrics.textDimension : null;
367         }*/
368 
369     /**
370      * Returns width of item's shortcut label,
371      * 0 if item has no shortcut.
372      * The height of shortcut can be deternimed
373      * from text dimensions.
374      * This function does not use any locks
375      * and is guaranteed to return some value
376      * (possibly actual, possibly expired)
377      */
378     /*int getShortcutWidth() {
379         TextMetrics textMetrics = this.textMetrics;
380         if (textMetrics == null) {
381             textMetrics = calcTextMetrics();
382             this.textMetrics = textMetrics;
383         }
384         return (textMetrics != null) ? textMetrics.shortcutWidth : 0;
385     }
386 
387     int getTextBaseline() {
388         TextMetrics textMetrics = this.textMetrics;
389         if (textMetrics == null) {
390             textMetrics = calcTextMetrics();
391             this.textMetrics = textMetrics;
392         }
393         return (textMetrics != null) ? textMetrics.textBaseline : 0;
394         }*/
395 
calcTextMetrics()396     TextMetrics calcTextMetrics() {
397         if (container == null) {
398             return null;
399         }
400         if (isSeparator()) {
401             return new TextMetrics(new Dimension(SEPARATOR_WIDTH, SEPARATOR_HEIGHT), 0, 0);
402         }
403         Graphics g = container.getGraphics();
404         if (g == null) {
405             return null;
406         }
407         try {
408             g.setFont(getTargetFont());
409             FontMetrics fm = g.getFontMetrics();
410             String str = getTargetLabel();
411             int width = fm.stringWidth(str);
412             int height = fm.getHeight();
413             Dimension textDimension = new Dimension(width, height);
414             int textBaseline = fm.getHeight() - fm.getAscent();
415             String sc = getShortcutText();
416             int shortcutWidth = (sc == null) ? 0 : fm.stringWidth(sc);
417             return new TextMetrics(textDimension, shortcutWidth, textBaseline);
418         } finally {
419             g.dispose();
420         }
421     }
422 
resetTextMetrics()423     void resetTextMetrics() {
424         textMetrics = null;
425         if (container != null) {
426             container.updateSize();
427         }
428     }
429 
430     /************************************************
431      *
432      * Mapping utility functions
433      *
434      ************************************************/
435 
436     /**
437      * Sets mapping of item to window.
438      * @param bounds bounds of item in container's coordinates
439      * @param textOrigin point for drawString in container's coordinates
440      * @see XBaseMenuWindow.map()
441      */
map(Rectangle bounds, Point textOrigin)442     void map(Rectangle bounds, Point textOrigin) {
443         this.bounds = bounds;
444         this.textOrigin = textOrigin;
445     }
446 
447     /**
448      * returns bounds of item that were previously set by map() function
449      */
getBounds()450     Rectangle getBounds() {
451         return bounds;
452     }
453 
454     /**
455      * returns origin of item's text that was previously set by map() function
456      */
getTextOrigin()457     Point getTextOrigin() {
458         return textOrigin;
459     }
460 
461 }
462