001: /*
002: * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025: package sun.awt.X11;
026:
027: import java.awt.*;
028: import java.awt.peer.*;
029: import java.awt.event.*;
030:
031: import java.util.logging.*;
032:
033: import java.lang.reflect.Field;
034: import java.lang.reflect.Method;
035: import java.lang.reflect.InvocationTargetException;
036: import sun.awt.SunToolkit;
037:
038: public class XMenuItemPeer implements MenuItemPeer {
039:
040: /************************************************
041: *
042: * Data members
043: *
044: ************************************************/
045:
046: private static Logger log = Logger
047: .getLogger("sun.awt.X11.XMenuItemPeer");
048:
049: /*
050: * Primary members
051: */
052:
053: /**
054: * Window that this item belongs to.
055: */
056: private XBaseMenuWindow container;
057:
058: /**
059: * Target MenuItem. Note that 'target' member
060: * in XWindow is required for dispatching events.
061: * This member is only used for accessing its fields
062: * and firing ActionEvent & ItemEvent
063: */
064: private MenuItem target;
065:
066: /*
067: * Mapping to window
068: */
069:
070: /**
071: * Rectange occupied by menu item in container's
072: * coordinates. Filled by map(...) function from
073: * XBaseMenuWindow.map()
074: */
075: private Rectangle bounds;
076:
077: /**
078: * Point in container's coordinate system used as
079: * origin by drawText.
080: */
081: private Point textOrigin;
082:
083: /*
084: * Size constants
085: */
086: private final static int SEPARATOR_WIDTH = 20;
087: private final static int SEPARATOR_HEIGHT = 5;
088:
089: /*
090: * MenuItem's fields & methods
091: */
092: private final static Field f_enabled;
093: private final static Field f_label;
094: private final static Field f_shortcut;
095: private final static Method m_getFont;
096: private final static Method m_isItemEnabled;
097: private final static Method m_getActionCommand;
098: static {
099: f_enabled = SunToolkit.getField(MenuItem.class, "enabled");
100: f_label = SunToolkit.getField(MenuItem.class, "label");
101: f_shortcut = SunToolkit.getField(MenuItem.class, "shortcut");
102:
103: m_getFont = SunToolkit.getMethod(MenuComponent.class,
104: "getFont_NoClientCode", null);
105: m_getActionCommand = SunToolkit.getMethod(MenuItem.class,
106: "getActionCommandImpl", null);
107: m_isItemEnabled = SunToolkit.getMethod(MenuItem.class,
108: "isItemEnabled", null);
109: }
110: /************************************************
111: *
112: * Text Metrics
113: *
114: ************************************************/
115:
116: /**
117: * Text metrics are filled in calcTextMetrics function
118: * and reset in resetTextMetrics function. Text metrics
119: * contain calculated dimensions of various components of
120: * menu item.
121: */
122: private TextMetrics textMetrics;
123:
124: static class TextMetrics implements Cloneable {
125: /*
126: * Calculated text size members
127: */
128: private Dimension textDimension;
129: private int shortcutWidth;
130: private int textBaseline;
131:
132: TextMetrics(Dimension textDimension, int shortcutWidth,
133: int textBaseline) {
134: this .textDimension = textDimension;
135: this .shortcutWidth = shortcutWidth;
136: this .textBaseline = textBaseline;
137: }
138:
139: public Object clone() {
140: try {
141: return super .clone();
142: } catch (CloneNotSupportedException ex) {
143: throw new InternalError();
144: }
145: }
146:
147: Dimension getTextDimension() {
148: return this .textDimension;
149: }
150:
151: int getShortcutWidth() {
152: return this .shortcutWidth;
153: }
154:
155: int getTextBaseline() {
156: return this .textBaseline;
157: }
158: }
159:
160: /************************************************
161: *
162: * Construction
163: *
164: ************************************************/
165: XMenuItemPeer(MenuItem target) {
166: this .target = target;
167: }
168:
169: /************************************************
170: *
171: * Implementaion of interface methods
172: *
173: ************************************************/
174:
175: /*
176: * From MenuComponentPeer
177: */
178: public void dispose() {
179: //Empty function
180: }
181:
182: public void setFont(Font font) {
183: resetTextMetrics();
184: repaintIfShowing();
185: }
186:
187: /*
188: * From MenuItemPeer
189: */
190: public void setLabel(String label) {
191: resetTextMetrics();
192: repaintIfShowing();
193: }
194:
195: public void setEnabled(boolean enabled) {
196: repaintIfShowing();
197: }
198:
199: /**
200: * DEPRECATED: Replaced by setEnabled(boolean).
201: * @see java.awt.peer.MenuItemPeer
202: */
203: public void enable() {
204: setEnabled(true);
205: }
206:
207: /**
208: * DEPRECATED: Replaced by setEnabled(boolean).
209: * @see java.awt.peer.MenuItemPeer
210: */
211: public void disable() {
212: setEnabled(false);
213: }
214:
215: /************************************************
216: *
217: * Access to target's fields
218: *
219: ************************************************/
220:
221: MenuItem getTarget() {
222: return this .target;
223: }
224:
225: Font getTargetFont() {
226: if (target == null) {
227: return XWindow.defaultFont;
228: }
229: try {
230: return (Font) m_getFont.invoke(target, new Object[0]);
231: } catch (IllegalAccessException e) {
232: e.printStackTrace();
233: } catch (InvocationTargetException e) {
234: e.printStackTrace();
235: }
236: return XWindow.defaultFont;
237: }
238:
239: String getTargetLabel() {
240: if (target == null) {
241: return "";
242: }
243: try {
244: String label = (String) f_label.get(target);
245: return (label == null) ? "" : label;
246: } catch (IllegalAccessException e) {
247: e.printStackTrace();
248: }
249: return "";
250: }
251:
252: boolean isTargetEnabled() {
253: if (target == null) {
254: return false;
255: }
256: try {
257: return f_enabled.getBoolean(target);
258: } catch (IllegalAccessException e) {
259: e.printStackTrace();
260: }
261: return false;
262: }
263:
264: /**
265: * Returns true if item and all its parents are enabled
266: * This function is used to fix
267: * 6184485: Popup menu is not disabled on XToolkit even when calling setEnabled (false)
268: */
269: boolean isTargetItemEnabled() {
270: if (target == null) {
271: return false;
272: }
273: try {
274: return ((Boolean) m_isItemEnabled.invoke(target,
275: new Object[0])).booleanValue();
276: } catch (IllegalAccessException e) {
277: e.printStackTrace();
278: } catch (InvocationTargetException e) {
279: e.printStackTrace();
280: }
281: return false;
282: }
283:
284: String getTargetActionCommand() {
285: if (target == null) {
286: return "";
287: }
288: try {
289: return (String) m_getActionCommand.invoke(target,
290: (Object[]) null);
291: } catch (IllegalAccessException e) {
292: e.printStackTrace();
293: } catch (InvocationTargetException e) {
294: e.printStackTrace();
295: }
296: return "";
297: }
298:
299: MenuShortcut getTargetShortcut() {
300: if (target == null) {
301: return null;
302: }
303: try {
304: return (MenuShortcut) f_shortcut.get(target);
305: } catch (IllegalAccessException e) {
306: e.printStackTrace();
307: }
308: return null;
309: }
310:
311: String getShortcutText() {
312: //Fix for 6180413: shortcuts should not be displayed for any of the menuitems in a popup menu
313: if (container == null) {
314: return null;
315: }
316: if (container.getRootMenuWindow() instanceof XPopupMenuPeer) {
317: return null;
318: }
319: MenuShortcut sc = getTargetShortcut();
320: //TODO:This can potentially call user code
321: return (sc == null) ? null : sc.toString();
322: }
323:
324: /************************************************
325: *
326: * Basic manipulations
327: *
328: ************************************************/
329:
330: /**
331: * This function is called when filling item vectors
332: * in XMenuWindow & XMenuBar. We need it because peers
333: * are created earlier than windows.
334: * @param container the window that this item belongs to.
335: */
336: void setContainer(XBaseMenuWindow container) {
337: synchronized (XBaseMenuWindow.getMenuTreeLock()) {
338: this .container = container;
339: }
340: }
341:
342: /**
343: * returns the window that this item belongs to
344: */
345: XBaseMenuWindow getContainer() {
346: return this .container;
347: }
348:
349: /************************************************
350: *
351: * Overridable behaviour
352: *
353: ************************************************/
354:
355: /**
356: * This function should be overriden simply to
357: * return false in inherited classes.
358: */
359: boolean isSeparator() {
360: boolean r = (getTargetLabel().equals("-"));
361: return r;
362: }
363:
364: /************************************************
365: *
366: * Utility functions
367: *
368: ************************************************/
369:
370: /**
371: * Returns true if container exists and is showing
372: */
373: boolean isContainerShowing() {
374: if (container == null) {
375: return false;
376: }
377: return container.isShowing();
378: }
379:
380: /**
381: * Repaints item if it is showing
382: */
383: void repaintIfShowing() {
384: if (isContainerShowing()) {
385: container.postPaintEvent();
386: }
387: }
388:
389: /**
390: * This function is invoked when the user clicks
391: * on menu item.
392: * @param when the timestamp of action event
393: */
394: void action(long when) {
395: if (!isSeparator() && isTargetItemEnabled()) {
396: XWindow.postEventStatic(new ActionEvent(target,
397: ActionEvent.ACTION_PERFORMED,
398: getTargetActionCommand(), when, 0));
399: }
400: }
401:
402: /************************************************
403: *
404: * Text metrics
405: *
406: ************************************************/
407:
408: /**
409: * Returns text metrics of menu item.
410: * This function does not use any locks
411: * and is guaranteed to return some value
412: * (possibly actual, possibly expired)
413: */
414: TextMetrics getTextMetrics() {
415: TextMetrics textMetrics = this .textMetrics;
416: if (textMetrics == null) {
417: textMetrics = calcTextMetrics();
418: this .textMetrics = textMetrics;
419: }
420: return textMetrics;
421: }
422:
423: /**
424: * Returns dimensions of item's label.
425: * This function does not use any locks
426: * Returns actual or expired value
427: * or null if error occurs
428: */
429: /*Dimension getTextDimension() {
430: TextMetrics textMetrics = this.textMetrics;
431: if (textMetrics == null) {
432: textMetrics = calcTextMetrics();
433: this.textMetrics = textMetrics;
434: }
435: return (textMetrics != null) ? textMetrics.textDimension : null;
436: }*/
437:
438: /**
439: * Returns width of item's shortcut label,
440: * 0 if item has no shortcut.
441: * The height of shortcut can be deternimed
442: * from text dimensions.
443: * This function does not use any locks
444: * and is guaranteed to return some value
445: * (possibly actual, possibly expired)
446: */
447: /*int getShortcutWidth() {
448: TextMetrics textMetrics = this.textMetrics;
449: if (textMetrics == null) {
450: textMetrics = calcTextMetrics();
451: this.textMetrics = textMetrics;
452: }
453: return (textMetrics != null) ? textMetrics.shortcutWidth : 0;
454: }
455:
456: int getTextBaseline() {
457: TextMetrics textMetrics = this.textMetrics;
458: if (textMetrics == null) {
459: textMetrics = calcTextMetrics();
460: this.textMetrics = textMetrics;
461: }
462: return (textMetrics != null) ? textMetrics.textBaseline : 0;
463: }*/
464:
465: TextMetrics calcTextMetrics() {
466: if (container == null) {
467: return null;
468: }
469: if (isSeparator()) {
470: return new TextMetrics(new Dimension(SEPARATOR_WIDTH,
471: SEPARATOR_HEIGHT), 0, 0);
472: }
473: Graphics g = container.getGraphics();
474: if (g == null) {
475: return null;
476: }
477: try {
478: g.setFont(getTargetFont());
479: FontMetrics fm = g.getFontMetrics();
480: String str = getTargetLabel();
481: int width = fm.stringWidth(str);
482: int height = fm.getHeight();
483: Dimension textDimension = new Dimension(width, height);
484: int textBaseline = fm.getHeight() - fm.getAscent();
485: String sc = getShortcutText();
486: int shortcutWidth = (sc == null) ? 0 : fm.stringWidth(sc);
487: return new TextMetrics(textDimension, shortcutWidth,
488: textBaseline);
489: } finally {
490: g.dispose();
491: }
492: }
493:
494: void resetTextMetrics() {
495: textMetrics = null;
496: if (container != null) {
497: container.updateSize();
498: }
499: }
500:
501: /************************************************
502: *
503: * Mapping utility functions
504: *
505: ************************************************/
506:
507: /**
508: * Sets mapping of item to window.
509: * @param bounds bounds of item in container's coordinates
510: * @param textOrigin point for drawString in container's coordinates
511: * @see XBaseMenuWindow.map()
512: */
513: void map(Rectangle bounds, Point textOrigin) {
514: this .bounds = bounds;
515: this .textOrigin = textOrigin;
516: }
517:
518: /**
519: * returns bounds of item that were previously set by map() function
520: */
521: Rectangle getBounds() {
522: return bounds;
523: }
524:
525: /**
526: * returns origin of item's text that was previously set by map() function
527: */
528: Point getTextOrigin() {
529: return textOrigin;
530: }
531:
532: }
|