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 java.util.logging.*; 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 Logger log = Logger.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 xSetVisible(true); 204 toFront(); 205 } 206 207 /************************************************ 208 * 209 * Implementation of abstract methods 210 * 211 ************************************************/ 212 213 /** 214 * Menu bar is always root window in menu window's 215 * hierarchy 216 */ 217 protected XBaseMenuWindow getParentMenuWindow() { 218 return null; 219 } 220 221 /** 222 * @see XBaseMenuWindow.map 223 */ 224 protected MappingData map() { 225 XMenuItemPeer[] itemVector = copyItems(); 226 int itemCnt = itemVector.length; 227 XMenuItemPeer helpMenu = this.helpMenu; 228 int helpMenuPos = -1; 229 //find helpMenu and move it to the end of array 230 if (helpMenu != null) { 231 //Fixed 6270847: PIT: HELP menu is not shown at the right place when normal menus added to MB are removed, XToolkit 232 for (int i = 0; i < itemCnt; i++) { 233 if (itemVector[i] == helpMenu) { 234 helpMenuPos = i; 235 break; 236 } 237 } 238 if (helpMenuPos != -1 && helpMenuPos != itemCnt - 1) { 239 System.arraycopy(itemVector, helpMenuPos + 1, itemVector, helpMenuPos, itemCnt - 1 - helpMenuPos); 240 itemVector[itemCnt - 1] = helpMenu; 241 } 242 } 243 //We need maximum height before calculating item's bounds 244 int maxHeight = 0; 245 XMenuItemPeer.TextMetrics[] itemMetrics = new XMenuItemPeer.TextMetrics[itemCnt]; 246 for (int i = 0; i < itemCnt; i++) { 247 itemMetrics[i] = itemVector[i].getTextMetrics(); 248 Dimension dim = itemMetrics[i].getTextDimension(); 249 if (dim != null) { 250 maxHeight = Math.max(maxHeight, dim.height); 251 } 252 } 253 //Calculate bounds 254 int nextOffset = 0; 255 int itemHeight = BAR_ITEM_MARGIN_TOP + maxHeight + BAR_ITEM_MARGIN_BOTTOM; 256 int mappedCnt = itemCnt; 257 for (int i = 0; i < itemCnt; i++) { 258 XMenuItemPeer item = itemVector[i]; 259 XMenuItemPeer.TextMetrics metrics = itemMetrics[i]; 260 Dimension dim = metrics.getTextDimension(); 261 if (dim != null) { 262 int itemWidth = BAR_ITEM_MARGIN_LEFT + dim.width + BAR_ITEM_MARGIN_RIGHT; 263 //Fix for 6270757: PIT: Menus and Sub-menus are shown outside the frame, XToolkit 264 //Cut-off items that don't fit in window 265 //At least one item must remain in menu 266 if ((nextOffset + itemWidth > this.width) && (i > 0)) { 267 mappedCnt = i; 268 break; 269 } 270 //If this item is help menu, move it to the right edge 271 if ((i == itemCnt - 1) && helpMenuPos != -1) { 272 nextOffset = Math.max(nextOffset, this.width - itemWidth - BAR_SPACING_RIGHT); 273 } 274 Rectangle bounds = new Rectangle(nextOffset, BAR_SPACING_TOP, itemWidth, itemHeight); 275 //text should be centered vertically in menu item's bounds 276 int y = (maxHeight + dim.height) / 2 - metrics.getTextBaseline(); 277 Point textOrigin = new Point(nextOffset + BAR_ITEM_MARGIN_LEFT, BAR_SPACING_TOP + BAR_ITEM_MARGIN_TOP + y); 278 nextOffset += itemWidth + BAR_ITEM_SPACING; 279 item.map(bounds, textOrigin); 280 } else { 281 Rectangle bounds = new Rectangle(nextOffset, BAR_SPACING_TOP, 0, 0); 282 Point textOrigin = new Point(nextOffset + BAR_ITEM_MARGIN_LEFT, BAR_SPACING_TOP + BAR_ITEM_MARGIN_TOP); 283 } 284 } 285 XMenuItemPeer mappedVector[] = new XMenuItemPeer[mappedCnt]; 286 System.arraycopy(itemVector, 0, mappedVector, 0, mappedCnt); 287 MappingData mappingData = new MappingData(mappedVector, BAR_SPACING_TOP + itemHeight + BAR_SPACING_BOTTOM); 288 return mappingData; 289 } 290 291 /** 292 * @see XBaseMenuWindow.getSubmenuBounds 293 */ 294 protected Rectangle getSubmenuBounds(Rectangle itemBounds, Dimension windowSize) { 295 Rectangle globalBounds = toGlobal(itemBounds); 296 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); 297 Rectangle res; 298 res = fitWindowBelow(globalBounds, windowSize, screenSize); 299 if (res != null) { 300 return res; 301 } 302 res = fitWindowAbove(globalBounds, windowSize, screenSize); 303 if (res != null) { 304 return res; 305 } 306 res = fitWindowRight(globalBounds, windowSize, screenSize); 307 if (res != null) { 308 return res; 309 } 310 res = fitWindowLeft(globalBounds, windowSize, screenSize); 311 if (res != null) { 312 return res; 313 } 314 return fitWindowToScreen(windowSize, screenSize); 315 } 316 317 /** 318 * This function is called when it's likely that 319 * size of items has changed. 320 * Invokes framePeer's updateChildrenSizes() 321 */ 322 protected void updateSize() { 323 resetMapping(); 324 if (framePeer != null) { 325 framePeer.reshapeMenubarPeer(); 326 } 327 } 328 329 /************************************************ 330 * 331 * Utility functions 332 * 333 ************************************************/ 334 335 /** 336 * Returns desired height of menu bar 337 */ 338 int getDesiredHeight() { 339 MappingData mappingData = (MappingData)getMappingData(); 340 return mappingData.getDesiredHeight(); 341 } 342 343 /** 344 * Returns true if framePeer is not null and is enabled 345 * Used to fix 6185057: Disabling a frame does not disable 346 * the menus on the frame, on solaris/linux 347 */ 348 boolean isFramePeerEnabled() { 349 if (framePeer != null) { 350 return framePeer.isEnabled(); 351 } 352 return false; 353 } 354 355 /************************************************ 356 * 357 * Overriden XBaseMenuWindow functions 358 * 359 ************************************************/ 360 361 /** 362 * @see XBaseMenuWindow.doDispose() 363 */ 364 protected void doDispose() { 365 super.doDispose(); 366 XToolkit.targetDisposedPeer(menuBarTarget, this); 367 } 368 369 /************************************************ 370 * 371 * Overriden XWindow general-purpose functions 372 * 373 ************************************************/ 374 375 /** 376 * For menu bars this function is called from framePeer's 377 * reshape(...) and updateChildrenSizes() 378 */ 379 public void reshape(int x, int y, int width, int height) { 380 if ((width != this.width) || (height != this.height)) { 381 resetMapping(); 382 } 383 super.reshape(x, y, width, height); 384 } 385 386 /** 387 * Performs ungrabbing of input 388 * @see XBaseWindow.ungrabInputImpl() 389 */ 390 void ungrabInputImpl() { 391 selectItem(null, false); 392 super.ungrabInputImpl(); 393 postPaintEvent(); 394 } 395 396 /************************************************ 397 * 398 * Overriden XWindow painting & printing 399 * 400 ************************************************/ 401 public void paint(Graphics g) { 402 resetColors(); 403 /* Calculate menubar dimension. */ 404 int width = getWidth(); 405 int height = getHeight(); 406 407 flush(); 408 //Fill background of rectangle 409 g.setColor(getBackgroundColor()); 410 g.fillRect(1, 1, width - 2, height - 2); 411 412 draw3DRect(g, 0, 0, width, height, true); 413 414 //Paint menus 415 MappingData mappingData = (MappingData)getMappingData(); 416 XMenuItemPeer[] itemVector = mappingData.getItems(); 417 XMenuItemPeer selectedItem = getSelectedItem(); 418 for (int i = 0; i < itemVector.length; i++) { 419 XMenuItemPeer item = itemVector[i]; 420 //paint item 421 g.setFont(item.getTargetFont()); 422 Rectangle bounds = item.getBounds(); 423 Point textOrigin = item.getTextOrigin(); 424 if (item == selectedItem) { 425 g.setColor(getSelectedColor()); 426 g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height); 427 draw3DRect(g, bounds.x, bounds.y, bounds.width, bounds.height, false); 428 } 429 if (isFramePeerEnabled() && item.isTargetItemEnabled()) { 430 g.setColor(getForegroundColor()); 431 } else { 432 g.setColor(getDisabledColor()); 433 } 434 g.drawString(item.getTargetLabel(), textOrigin.x, textOrigin.y); 435 } 436 flush(); 437 } 438 439 static final int W_DIFF = (XFramePeer.CROSSHAIR_INSET + 1) * 2; 440 static final int H_DIFF = XFramePeer.BUTTON_Y + XFramePeer.BUTTON_H; 441 442 void print(Graphics g) { 443 //TODO:Implement 444 } 445 446 /************************************************ 447 * 448 * Overriden XBaseMenuWindow event handling 449 * 450 ************************************************/ 451 protected void handleEvent(AWTEvent event) { 452 // explicitly block all events except PaintEvent.PAINT for menus, 453 // that are in the modal blocked window 454 if ((framePeer != null) && 455 (event.getID() != PaintEvent.PAINT)) 456 { 457 if (framePeer.isModalBlocked()) { 458 return; 459 } 460 } 461 switch(event.getID()) { 462 case MouseEvent.MOUSE_PRESSED: 463 case MouseEvent.MOUSE_RELEASED: 464 case MouseEvent.MOUSE_CLICKED: 465 case MouseEvent.MOUSE_MOVED: 466 case MouseEvent.MOUSE_ENTERED: 467 case MouseEvent.MOUSE_EXITED: 468 case MouseEvent.MOUSE_DRAGGED: 469 //Fix for 6185057: Disabling a frame does not disable 470 //the menus on the frame, on solaris/linux 471 if (isFramePeerEnabled()) { 472 doHandleJavaMouseEvent((MouseEvent)event); 473 } 474 break; 475 case KeyEvent.KEY_PRESSED: 476 case KeyEvent.KEY_RELEASED: 477 //Fix for 6185057: Disabling a frame does not disable 478 //the menus on the frame, on solaris/linux 479 if (isFramePeerEnabled()) { 480 doHandleJavaKeyEvent((KeyEvent)event); 481 } 482 break; 483 default: 484 super.handleEvent(event); 485 break; 486 } 487 } 488 489 490 491 /************************************************ 492 * 493 * Overriden XWindow keyboard processing 494 * 495 ************************************************/ 496 497 /* 498 * This function is called from XWindow 499 * @see XWindow.handleF10onEDT() 500 */ 501 void handleF10KeyPress(KeyEvent event) { 502 int keyState = event.getModifiers(); 503 if (((keyState & InputEvent.ALT_MASK) != 0) || 504 ((keyState & InputEvent.SHIFT_MASK) != 0) || 505 ((keyState & InputEvent.CTRL_MASK) != 0)) { 506 return; 507 } 508 grabInput(); 509 selectItem(getFirstSelectableItem(), true); 510 } 511 512 /* 513 * In previous version keys were handled in handleKeyPress. 514 * Now we override this function do disable F10 explicit 515 * processing. All processing is done using KeyEvent. 516 */ 517 public void handleKeyPress(XEvent xev) { 518 XKeyEvent xkey = xev.get_xkey(); 519 if (log.isLoggable(Level.FINE)) { 520 log.fine(xkey.toString()); 521 } 522 if (isEventDisabled(xev)) { 523 return; 524 } 525 final Component currentSource = (Component)getEventSource(); 526 //This is the only difference from XWindow.handleKeyPress 527 //Ancestor's function can invoke handleF10KeyPress here 528 handleKeyPress(xkey); 529 } 530 531 } //class XMenuBarPeer