|
1 /* |
|
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 */ |
|
25 package sun.awt.X11; |
|
26 |
|
27 import java.awt.*; |
|
28 import java.awt.peer.*; |
|
29 import java.awt.event.*; |
|
30 |
|
31 import java.lang.reflect.Field; |
|
32 import java.util.Vector; |
|
33 import java.util.logging.*; |
|
34 import sun.awt.SunToolkit; |
|
35 |
|
36 public 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 |