1 /* 2 * Copyright (c) 2002, 2018, 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 if (itemMetrics[i] != null) { 252 Dimension dim = itemMetrics[i].getTextDimension(); 253 if (dim != null) { 254 maxHeight = Math.max(maxHeight, dim.height); 255 } 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 if (metrics == null) { 266 continue; 267 } 268 Dimension dim = metrics.getTextDimension(); 269 if (dim != null) { 270 int itemWidth = BAR_ITEM_MARGIN_LEFT + dim.width + BAR_ITEM_MARGIN_RIGHT; 271 //Fix for 6270757: PIT: Menus and Sub-menus are shown outside the frame, XToolkit 272 //Cut-off items that don't fit in window 273 //At least one item must remain in menu 274 if ((nextOffset + itemWidth > this.width) && (i > 0)) { 275 mappedCnt = i; 276 break; 277 } 278 //If this item is help menu, move it to the right edge 279 if ((i == itemCnt - 1) && helpMenuPos != -1) { 280 nextOffset = Math.max(nextOffset, this.width - itemWidth - BAR_SPACING_RIGHT); 281 } 282 Rectangle bounds = new Rectangle(nextOffset, BAR_SPACING_TOP, itemWidth, itemHeight); 283 //text should be centered vertically in menu item's bounds 284 int y = (maxHeight + dim.height) / 2 - metrics.getTextBaseline(); 285 Point textOrigin = new Point(nextOffset + BAR_ITEM_MARGIN_LEFT, BAR_SPACING_TOP + BAR_ITEM_MARGIN_TOP + y); 286 nextOffset += itemWidth + BAR_ITEM_SPACING; 287 item.map(bounds, textOrigin); 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 Rectangle screenBounds = getCurrentGraphicsConfiguration().getBounds(); 302 Rectangle res; 303 res = fitWindowBelow(globalBounds, windowSize, screenBounds); 304 if (res != null) { 305 return res; 306 } 307 res = fitWindowAbove(globalBounds, windowSize, screenBounds); 308 if (res != null) { 309 return res; 310 } 311 res = fitWindowRight(globalBounds, windowSize, screenBounds); 312 if (res != null) { 313 return res; 314 } 315 res = fitWindowLeft(globalBounds, windowSize, screenBounds); 316 if (res != null) { 317 return res; 318 } 319 return fitWindowToScreen(windowSize, screenBounds); 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 @SuppressWarnings("deprecation") 507 void handleF10KeyPress(KeyEvent event) { 508 int keyState = event.getModifiers(); 509 if (((keyState & InputEvent.ALT_MASK) != 0) || 510 ((keyState & InputEvent.SHIFT_MASK) != 0) || 511 ((keyState & InputEvent.CTRL_MASK) != 0)) { 512 return; 513 } 514 grabInput(); 515 selectItem(getFirstSelectableItem(), true); 516 } 517 518 /* 519 * In previous version keys were handled in handleKeyPress. 520 * Now we override this function do disable F10 explicit 521 * processing. All processing is done using KeyEvent. 522 */ 523 public void handleKeyPress(XEvent xev) { 524 XKeyEvent xkey = xev.get_xkey(); 525 if (log.isLoggable(PlatformLogger.Level.FINE)) { 526 log.fine(xkey.toString()); 527 } 528 if (isEventDisabled(xev)) { 529 return; 530 } 531 final Component currentSource = getEventSource(); 532 //This is the only difference from XWindow.handleKeyPress 533 //Ancestor's function can invoke handleF10KeyPress here 534 handleKeyPress(xkey); 535 } 536 537 } //class XMenuBarPeer