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