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.lang.reflect.Field;
032: import java.util.Vector;
033: import java.util.logging.*;
034: import sun.awt.SunToolkit;
035:
036: public class XMenuBarPeer extends XBaseMenuWindow implements
037: MenuBarPeer {
038:
039: /************************************************
040: *
041: * Data members
042: *
043: ************************************************/
044:
045: private static Logger log = Logger
046: .getLogger("sun.awt.X11.XMenuBarPeer");
047:
048: /*
049: * Primary members
050: */
051: private XFramePeer framePeer;
052: private MenuBar menuBarTarget;
053:
054: /*
055: * Index of help menu
056: */
057: private XMenuPeer helpMenu = null;
058:
059: /*
060: * dimension constants
061: */
062: private final static int BAR_SPACING_TOP = 3;
063: private final static int BAR_SPACING_BOTTOM = 3;
064: private final static int BAR_SPACING_LEFT = 3;
065: private final static int BAR_SPACING_RIGHT = 3;
066: private final static int BAR_ITEM_SPACING = 2;
067: private final static int BAR_ITEM_MARGIN_LEFT = 10;
068: private final static int BAR_ITEM_MARGIN_RIGHT = 10;
069: private final static int BAR_ITEM_MARGIN_TOP = 2;
070: private final static int BAR_ITEM_MARGIN_BOTTOM = 2;
071:
072: //fields
073: private static Field f_helpMenu;
074: private static Field f_menus;
075:
076: static {
077: f_helpMenu = SunToolkit.getField(MenuBar.class, "helpMenu");
078: f_menus = SunToolkit.getField(MenuBar.class, "menus");
079: }
080:
081: /************************************************
082: *
083: * Mapping data
084: *
085: ************************************************/
086:
087: /**
088: * XBaseMenuWindow's mappingData is extended with
089: * desired height of menu bar
090: */
091: static class MappingData extends XBaseMenuWindow.MappingData {
092: int desiredHeight;
093:
094: MappingData(XMenuItemPeer[] items, int desiredHeight) {
095: super (items);
096: this .desiredHeight = desiredHeight;
097: }
098:
099: /**
100: * Constructs MappingData without items
101: * This constructor should be used in case of errors
102: */
103: MappingData() {
104: this .desiredHeight = 0;
105: }
106:
107: public int getDesiredHeight() {
108: return this .desiredHeight;
109: }
110: }
111:
112: /************************************************
113: *
114: * Construction
115: *
116: ************************************************/
117: XMenuBarPeer(MenuBar menuBarTarget) {
118: this .menuBarTarget = menuBarTarget;
119: }
120:
121: /************************************************
122: *
123: * Implementaion of interface methods
124: *
125: ************************************************/
126:
127: /*
128: * From MenuComponentPeer
129: */
130: public void setFont(Font f) {
131: resetMapping();
132: setItemsFont(f);
133: postPaintEvent();
134: }
135:
136: /*
137: * From MenuBarPeer
138: */
139:
140: /*
141: * Functions addMenu, delMenu, addHelpMenu
142: * need to have somewhat strange behaivour
143: * deduced from java.awt.MenuBar.
144: * We can not get index of particular item in
145: * MenuBar.menus array, because MenuBar firstly
146: * performs array operations and then calls peer.
147: * So we need to synchronize indicies in 'items'
148: * array with MenuBar.menus. We have to follow
149: * these rules:
150: * 1. Menus are always added to the end of array,
151: * even when helpMenu is present
152: * 2. Removal of any menu item acts as casual
153: * remove from array
154: * 3. MenuBar.setHelpMenu _firstly_ removes
155: * previous helpMenu by calling delMenu() if
156: * necessary, then it performs addMenu(),
157: * and then - addHelpMenu().
158: *
159: * Note that these functions don't perform
160: * type checks and checks for nulls or duplicates
161: */
162: public void addMenu(Menu m) {
163: addItem(m);
164: postPaintEvent();
165: }
166:
167: public void delMenu(int index) {
168: synchronized (getMenuTreeLock()) {
169: XMenuItemPeer item = getItem(index);
170: if (item != null && item == helpMenu) {
171: helpMenu = null;
172: }
173: delItem(index);
174: }
175: postPaintEvent();
176: }
177:
178: public void addHelpMenu(Menu m) {
179: XMenuPeer mp = (XMenuPeer) m.getPeer();
180: synchronized (getMenuTreeLock()) {
181: helpMenu = mp;
182: }
183: postPaintEvent();
184: }
185:
186: /************************************************
187: *
188: * Initialization
189: *
190: ************************************************/
191: /**
192: * called from XFramePeer.setMenuBar
193: */
194: public void init(Frame frame) {
195: this .target = frame;
196: this .framePeer = (XFramePeer) frame.getPeer();
197: XCreateWindowParams params = getDelayedParams();
198: params.remove(DELAYED);
199: params.add(PARENT_WINDOW, framePeer.getShell());
200: params.add(TARGET, frame);
201: init(params);
202: }
203:
204: /**
205: * Overriden initialization
206: */
207: void postInit(XCreateWindowParams params) {
208: super .postInit(params);
209: Vector targetMenuVector = null;
210: Menu targetHelpMenu = null;
211: try {
212: // Get menus from the target.
213: targetMenuVector = (Vector) f_menus.get(menuBarTarget);
214: targetHelpMenu = (Menu) f_helpMenu.get(menuBarTarget);
215: reloadItems(targetMenuVector);
216: } catch (IllegalAccessException iae) {
217: iae.printStackTrace();
218: }
219: if (targetHelpMenu != null) {
220: addHelpMenu(targetHelpMenu);
221: }
222: xSetVisible(true);
223: toFront();
224: }
225:
226: /************************************************
227: *
228: * Implementation of abstract methods
229: *
230: ************************************************/
231:
232: /**
233: * Menu bar is always root window in menu window's
234: * hierarchy
235: */
236: protected XBaseMenuWindow getParentMenuWindow() {
237: return null;
238: }
239:
240: /**
241: * @see XBaseMenuWindow.map
242: */
243: protected MappingData map() {
244: XMenuItemPeer[] itemVector = copyItems();
245: int itemCnt = itemVector.length;
246: XMenuItemPeer helpMenu = this .helpMenu;
247: int helpMenuPos = -1;
248: //find helpMenu and move it to the end of array
249: if (helpMenu != null) {
250: //Fixed 6270847: PIT: HELP menu is not shown at the right place when normal menus added to MB are removed, XToolkit
251: for (int i = 0; i < itemCnt; i++) {
252: if (itemVector[i] == helpMenu) {
253: helpMenuPos = i;
254: break;
255: }
256: }
257: if (helpMenuPos != -1 && helpMenuPos != itemCnt - 1) {
258: System.arraycopy(itemVector, helpMenuPos + 1,
259: itemVector, helpMenuPos, itemCnt - 1
260: - helpMenuPos);
261: itemVector[itemCnt - 1] = helpMenu;
262: }
263: }
264: //We need maximum height before calculating item's bounds
265: int maxHeight = 0;
266: XMenuItemPeer.TextMetrics[] itemMetrics = new XMenuItemPeer.TextMetrics[itemCnt];
267: for (int i = 0; i < itemCnt; i++) {
268: itemMetrics[i] = itemVector[i].getTextMetrics();
269: Dimension dim = itemMetrics[i].getTextDimension();
270: if (dim != null) {
271: maxHeight = Math.max(maxHeight, dim.height);
272: }
273: }
274: //Calculate bounds
275: int nextOffset = 0;
276: int itemHeight = BAR_ITEM_MARGIN_TOP + maxHeight
277: + BAR_ITEM_MARGIN_BOTTOM;
278: int mappedCnt = itemCnt;
279: for (int i = 0; i < itemCnt; i++) {
280: XMenuItemPeer item = itemVector[i];
281: XMenuItemPeer.TextMetrics metrics = itemMetrics[i];
282: Dimension dim = metrics.getTextDimension();
283: if (dim != null) {
284: int itemWidth = BAR_ITEM_MARGIN_LEFT + dim.width
285: + BAR_ITEM_MARGIN_RIGHT;
286: //Fix for 6270757: PIT: Menus and Sub-menus are shown outside the frame, XToolkit
287: //Cut-off items that don't fit in window
288: //At least one item must remain in menu
289: if ((nextOffset + itemWidth > this .width) && (i > 0)) {
290: mappedCnt = i;
291: break;
292: }
293: //If this item is help menu, move it to the right edge
294: if ((i == itemCnt - 1) && helpMenuPos != -1) {
295: nextOffset = Math.max(nextOffset, this .width
296: - itemWidth - BAR_SPACING_RIGHT);
297: }
298: Rectangle bounds = new Rectangle(nextOffset,
299: BAR_SPACING_TOP, itemWidth, itemHeight);
300: //text should be centered vertically in menu item's bounds
301: int y = (maxHeight + dim.height) / 2
302: - metrics.getTextBaseline();
303: Point textOrigin = new Point(nextOffset
304: + BAR_ITEM_MARGIN_LEFT, BAR_SPACING_TOP
305: + BAR_ITEM_MARGIN_TOP + y);
306: nextOffset += itemWidth + BAR_ITEM_SPACING;
307: item.map(bounds, textOrigin);
308: } else {
309: Rectangle bounds = new Rectangle(nextOffset,
310: BAR_SPACING_TOP, 0, 0);
311: Point textOrigin = new Point(nextOffset
312: + BAR_ITEM_MARGIN_LEFT, BAR_SPACING_TOP
313: + BAR_ITEM_MARGIN_TOP);
314: }
315: }
316: XMenuItemPeer mappedVector[] = new XMenuItemPeer[mappedCnt];
317: System.arraycopy(itemVector, 0, mappedVector, 0, mappedCnt);
318: MappingData mappingData = new MappingData(mappedVector,
319: BAR_SPACING_TOP + itemHeight + BAR_SPACING_BOTTOM);
320: return mappingData;
321: }
322:
323: /**
324: * @see XBaseMenuWindow.getSubmenuBounds
325: */
326: protected Rectangle getSubmenuBounds(Rectangle itemBounds,
327: Dimension windowSize) {
328: Rectangle globalBounds = toGlobal(itemBounds);
329: Dimension screenSize = Toolkit.getDefaultToolkit()
330: .getScreenSize();
331: Rectangle res;
332: res = fitWindowBelow(globalBounds, windowSize, screenSize);
333: if (res != null) {
334: return res;
335: }
336: res = fitWindowAbove(globalBounds, windowSize, screenSize);
337: if (res != null) {
338: return res;
339: }
340: res = fitWindowRight(globalBounds, windowSize, screenSize);
341: if (res != null) {
342: return res;
343: }
344: res = fitWindowLeft(globalBounds, windowSize, screenSize);
345: if (res != null) {
346: return res;
347: }
348: return fitWindowToScreen(windowSize, screenSize);
349: }
350:
351: /**
352: * This function is called when it's likely that
353: * size of items has changed.
354: * Invokes framePeer's updateChildrenSizes()
355: */
356: protected void updateSize() {
357: resetMapping();
358: if (framePeer != null) {
359: framePeer.reshapeMenubarPeer();
360: }
361: }
362:
363: /************************************************
364: *
365: * Utility functions
366: *
367: ************************************************/
368:
369: /**
370: * Returns desired height of menu bar
371: */
372: int getDesiredHeight() {
373: MappingData mappingData = (MappingData) getMappingData();
374: return mappingData.getDesiredHeight();
375: }
376:
377: /**
378: * Returns true if framePeer is not null and is enabled
379: * Used to fix 6185057: Disabling a frame does not disable
380: * the menus on the frame, on solaris/linux
381: */
382: boolean isFramePeerEnabled() {
383: if (framePeer != null) {
384: return framePeer.isEnabled();
385: }
386: return false;
387: }
388:
389: /************************************************
390: *
391: * Overriden XBaseMenuWindow functions
392: *
393: ************************************************/
394:
395: /**
396: * @see XBaseMenuWindow.doDispose()
397: */
398: protected void doDispose() {
399: super .doDispose();
400: XToolkit.targetDisposedPeer(menuBarTarget, this );
401: }
402:
403: /************************************************
404: *
405: * Overriden XWindow general-purpose functions
406: *
407: ************************************************/
408:
409: /**
410: * For menu bars this function is called from framePeer's
411: * reshape(...) and updateChildrenSizes()
412: */
413: public void reshape(int x, int y, int width, int height) {
414: if ((width != this .width) || (height != this .height)) {
415: resetMapping();
416: }
417: super .reshape(x, y, width, height);
418: }
419:
420: /**
421: * Performs ungrabbing of input
422: * @see XBaseWindow.ungrabInputImpl()
423: */
424: void ungrabInputImpl() {
425: selectItem(null, false);
426: super .ungrabInputImpl();
427: postPaintEvent();
428: }
429:
430: /************************************************
431: *
432: * Overriden XWindow painting & printing
433: *
434: ************************************************/
435: public void paint(Graphics g) {
436: resetColors();
437: /* Calculate menubar dimension. */
438: int width = getWidth();
439: int height = getHeight();
440:
441: flush();
442: //Fill background of rectangle
443: g.setColor(getBackgroundColor());
444: g.fillRect(1, 1, width - 2, height - 2);
445:
446: draw3DRect(g, 0, 0, width, height, true);
447:
448: //Paint menus
449: MappingData mappingData = (MappingData) getMappingData();
450: XMenuItemPeer[] itemVector = mappingData.getItems();
451: XMenuItemPeer selectedItem = getSelectedItem();
452: for (int i = 0; i < itemVector.length; i++) {
453: XMenuItemPeer item = itemVector[i];
454: //paint item
455: g.setFont(item.getTargetFont());
456: Rectangle bounds = item.getBounds();
457: Point textOrigin = item.getTextOrigin();
458: if (item == selectedItem) {
459: g.setColor(getSelectedColor());
460: g.fillRect(bounds.x, bounds.y, bounds.width,
461: bounds.height);
462: draw3DRect(g, bounds.x, bounds.y, bounds.width,
463: bounds.height, false);
464: }
465: if (isFramePeerEnabled() && item.isTargetItemEnabled()) {
466: g.setColor(getForegroundColor());
467: } else {
468: g.setColor(getDisabledColor());
469: }
470: g.drawString(item.getTargetLabel(), textOrigin.x,
471: textOrigin.y);
472: }
473: flush();
474: }
475:
476: static final int W_DIFF = (XFramePeer.CROSSHAIR_INSET + 1) * 2;
477: static final int H_DIFF = XFramePeer.BUTTON_Y + XFramePeer.BUTTON_H;
478:
479: void print(Graphics g) {
480: //TODO:Implement
481: }
482:
483: /************************************************
484: *
485: * Overriden XBaseMenuWindow event handling
486: *
487: ************************************************/
488: protected void handleEvent(AWTEvent event) {
489: // explicitly block all events except PaintEvent.PAINT for menus,
490: // that are in the modal blocked window
491: if ((framePeer != null) && (event.getID() != PaintEvent.PAINT)) {
492: if (framePeer.isModalBlocked()) {
493: return;
494: }
495: }
496: switch (event.getID()) {
497: case MouseEvent.MOUSE_PRESSED:
498: case MouseEvent.MOUSE_RELEASED:
499: case MouseEvent.MOUSE_CLICKED:
500: case MouseEvent.MOUSE_MOVED:
501: case MouseEvent.MOUSE_ENTERED:
502: case MouseEvent.MOUSE_EXITED:
503: case MouseEvent.MOUSE_DRAGGED:
504: //Fix for 6185057: Disabling a frame does not disable
505: //the menus on the frame, on solaris/linux
506: if (isFramePeerEnabled()) {
507: doHandleJavaMouseEvent((MouseEvent) event);
508: }
509: break;
510: case KeyEvent.KEY_PRESSED:
511: case KeyEvent.KEY_RELEASED:
512: //Fix for 6185057: Disabling a frame does not disable
513: //the menus on the frame, on solaris/linux
514: if (isFramePeerEnabled()) {
515: doHandleJavaKeyEvent((KeyEvent) event);
516: }
517: break;
518: default:
519: super .handleEvent(event);
520: break;
521: }
522: }
523:
524: /************************************************
525: *
526: * Overriden XWindow keyboard processing
527: *
528: ************************************************/
529:
530: /*
531: * This function is called from XWindow
532: * @see XWindow.handleF10onEDT()
533: */
534: void handleF10KeyPress(KeyEvent event) {
535: int keyState = event.getModifiers();
536: if (((keyState & InputEvent.ALT_MASK) != 0)
537: || ((keyState & InputEvent.SHIFT_MASK) != 0)
538: || ((keyState & InputEvent.CTRL_MASK) != 0)) {
539: return;
540: }
541: grabInput();
542: selectItem(getFirstSelectableItem(), true);
543: }
544:
545: /*
546: * In previous version keys were handled in handleKeyPress.
547: * Now we override this function do disable F10 explicit
548: * processing. All processing is done using KeyEvent.
549: */
550: public void handleKeyPress(XEvent xev) {
551: XKeyEvent xkey = xev.get_xkey();
552: if (log.isLoggable(Level.FINE))
553: log.fine(xkey.toString());
554: if (isEventDisabled(xev)) {
555: return;
556: }
557: final Component currentSource = (Component) getEventSource();
558: //This is the only difference from XWindow.handleKeyPress
559: //Ancestor's function can invoke handleF10KeyPress here
560: handleKeyPress(xkey);
561: }
562:
563: } //class XMenuBarPeer
|