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