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