1 /* 2 * Copyright (c) 2002, 2015, 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 static final int BAR_SPACING_TOP = 3; 60 private static final int BAR_SPACING_BOTTOM = 3; 61 private static final int BAR_SPACING_LEFT = 3; 62 private static final int BAR_SPACING_RIGHT = 3; 63 private static final int BAR_ITEM_SPACING = 2; 64 private static final int BAR_ITEM_MARGIN_LEFT = 10; 65 private static final int BAR_ITEM_MARGIN_RIGHT = 10; 66 private static final int BAR_ITEM_MARGIN_TOP = 2; 67 private static final 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 = AWTAccessor.getMenuComponentAccessor().getPeer(m); 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 = AWTAccessor.getComponentAccessor().getPeer(frame); 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<Menu> 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 = graphicsConfig.getBounds().getSize(); 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 = 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