1 /* 2 * Copyright (c) 2002, 2013, 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 sun.awt.AWTAccessor; 32 33 public class XMenuItemPeer implements MenuItemPeer { 34 35 /************************************************ 36 * 37 * Data members 38 * 39 ************************************************/ 40 41 /* 42 * Primary members 43 */ 44 45 /** 46 * Window that this item belongs to. 47 */ 48 private XBaseMenuWindow container; 49 50 /** 51 * Target MenuItem. Note that 'target' member 52 * in XWindow is required for dispatching events. 53 * This member is only used for accessing its fields 54 * and firing ActionEvent & ItemEvent 55 */ 56 private MenuItem target; 57 58 /* 59 * Mapping to window 60 */ 61 62 /** 63 * Rectangle occupied by menu item in container's 64 * coordinates. Filled by map(...) function from 65 * XBaseMenuWindow.map() 66 */ 67 private Rectangle bounds; 68 69 /** 70 * Point in container's coordinate system used as 71 * origin by drawText. 72 */ 73 private Point textOrigin; 74 75 /* 76 * Size constants 77 */ 78 private final static int SEPARATOR_WIDTH = 20; 79 private final static int SEPARATOR_HEIGHT = 5; 80 81 /************************************************ 82 * 83 * Text Metrics 84 * 85 ************************************************/ 86 87 /** 88 * Text metrics are filled in calcTextMetrics function 89 * and reset in resetTextMetrics function. Text metrics 90 * contain calculated dimensions of various components of 91 * menu item. 92 */ 93 private TextMetrics textMetrics; 94 95 static class TextMetrics implements Cloneable { 96 /* 97 * Calculated text size members 98 */ 99 private Dimension textDimension; 100 private int shortcutWidth; 101 private int textBaseline; 102 103 TextMetrics(Dimension textDimension, int shortcutWidth, int textBaseline) { 104 this.textDimension = textDimension; 105 this.shortcutWidth = shortcutWidth; 106 this.textBaseline = textBaseline; 107 } 108 109 public Object clone() { 110 try { 111 return super.clone(); 112 } catch (CloneNotSupportedException ex) { 113 throw new InternalError(ex); 114 } 115 } 116 117 Dimension getTextDimension() { 118 return this.textDimension; 119 } 120 121 int getShortcutWidth() { 122 return this.shortcutWidth; 123 } 124 125 int getTextBaseline() { 126 return this.textBaseline; 127 } 128 } 129 130 /************************************************ 131 * 132 * Construction 133 * 134 ************************************************/ 135 XMenuItemPeer(MenuItem target) { 136 this.target = target; 137 } 138 139 /************************************************ 140 * 141 * Implementaion of interface methods 142 * 143 ************************************************/ 144 145 /* 146 * From MenuComponentPeer 147 */ 148 public void dispose() { 149 //Empty function 150 } 151 152 public void setFont(Font font) { 153 resetTextMetrics(); 154 repaintIfShowing(); 155 } 156 /* 157 * From MenuItemPeer 158 */ 159 public void setLabel(String label) { 160 resetTextMetrics(); 161 repaintIfShowing(); 162 } 163 164 public void setEnabled(boolean enabled) { 165 repaintIfShowing(); 166 } 167 168 /** 169 * DEPRECATED: Replaced by setEnabled(boolean). 170 * @see java.awt.peer.MenuItemPeer 171 */ 172 public void enable() { 173 setEnabled( true ); 174 } 175 176 /** 177 * DEPRECATED: Replaced by setEnabled(boolean). 178 * @see java.awt.peer.MenuItemPeer 179 */ 180 public void disable() { 181 setEnabled( false ); 182 } 183 184 /************************************************ 185 * 186 * Access to target's fields 187 * 188 ************************************************/ 189 190 MenuItem getTarget() { 191 return this.target; 192 } 193 194 Font getTargetFont() { 195 if (target == null) { 196 return XWindow.getDefaultFont(); 197 } 198 return AWTAccessor.getMenuComponentAccessor().getFont_NoClientCode(target); 199 } 200 201 String getTargetLabel() { 202 if (target == null) { 203 return ""; 204 } 205 String label = AWTAccessor.getMenuItemAccessor().getLabel(target); 206 return (label == null) ? "" : label; 207 } 208 209 boolean isTargetEnabled() { 210 if (target == null) { 211 return false; 212 } 213 return AWTAccessor.getMenuItemAccessor().isEnabled(target); 214 } 215 216 /** 217 * Returns true if item and all its parents are enabled 218 * This function is used to fix 219 * 6184485: Popup menu is not disabled on XToolkit even when calling setEnabled (false) 220 */ 221 boolean isTargetItemEnabled() { 222 if (target == null) { 223 return false; 224 } 225 return AWTAccessor.getMenuItemAccessor().isItemEnabled(target); 226 } 227 228 String getTargetActionCommand() { 229 if (target == null) { 230 return ""; 231 } 232 return AWTAccessor.getMenuItemAccessor().getActionCommandImpl(target); 233 } 234 235 MenuShortcut getTargetShortcut() { 236 if (target == null) { 237 return null; 238 } 239 return AWTAccessor.getMenuItemAccessor().getShortcut(target); 240 } 241 242 String getShortcutText() { 243 //Fix for 6180413: shortcuts should not be displayed for any of the menuitems in a popup menu 244 if (container == null) { 245 return null; 246 } 247 if (container.getRootMenuWindow() instanceof XPopupMenuPeer) { 248 return null; 249 } 250 MenuShortcut sc = getTargetShortcut(); 251 //TODO:This can potentially call user code 252 return (sc == null) ? null : sc.toString(); 253 } 254 255 256 /************************************************ 257 * 258 * Basic manipulations 259 * 260 ************************************************/ 261 262 /** 263 * This function is called when filling item vectors 264 * in XMenuWindow & XMenuBar. We need it because peers 265 * are created earlier than windows. 266 * @param container the window that this item belongs to. 267 */ 268 void setContainer(XBaseMenuWindow container) { 269 synchronized(XBaseMenuWindow.getMenuTreeLock()) { 270 this.container = container; 271 } 272 } 273 274 /** 275 * returns the window that this item belongs to 276 */ 277 XBaseMenuWindow getContainer() { 278 return this.container; 279 } 280 281 /************************************************ 282 * 283 * Overridable behaviour 284 * 285 ************************************************/ 286 287 /** 288 * This function should be overriden simply to 289 * return false in inherited classes. 290 */ 291 boolean isSeparator() { 292 boolean r = (getTargetLabel().equals("-")); 293 return r; 294 } 295 296 /************************************************ 297 * 298 * Utility functions 299 * 300 ************************************************/ 301 302 /** 303 * Returns true if container exists and is showing 304 */ 305 boolean isContainerShowing() { 306 if (container == null) { 307 return false; 308 } 309 return container.isShowing(); 310 } 311 312 /** 313 * Repaints item if it is showing 314 */ 315 void repaintIfShowing() { 316 if (isContainerShowing()) { 317 container.postPaintEvent(); 318 } 319 } 320 321 /** 322 * This function is invoked when the user clicks 323 * on menu item. 324 * @param when the timestamp of action event 325 */ 326 void action(long when) { 327 if (!isSeparator() && isTargetItemEnabled()) { 328 XWindow.postEventStatic(new ActionEvent(target, ActionEvent.ACTION_PERFORMED, 329 getTargetActionCommand(), when, 330 0)); 331 } 332 } 333 /************************************************ 334 * 335 * Text metrics 336 * 337 ************************************************/ 338 339 /** 340 * Returns text metrics of menu item. 341 * This function does not use any locks 342 * and is guaranteed to return some value 343 * (possibly actual, possibly expired) 344 */ 345 TextMetrics getTextMetrics() { 346 TextMetrics textMetrics = this.textMetrics; 347 if (textMetrics == null) { 348 textMetrics = calcTextMetrics(); 349 this.textMetrics = textMetrics; 350 } 351 return textMetrics; 352 } 353 354 /** 355 * Returns dimensions of item's label. 356 * This function does not use any locks 357 * Returns actual or expired value 358 * or null if error occurs 359 */ 360 /*Dimension getTextDimension() { 361 TextMetrics textMetrics = this.textMetrics; 362 if (textMetrics == null) { 363 textMetrics = calcTextMetrics(); 364 this.textMetrics = textMetrics; 365 } 366 return (textMetrics != null) ? textMetrics.textDimension : null; 367 }*/ 368 369 /** 370 * Returns width of item's shortcut label, 371 * 0 if item has no shortcut. 372 * The height of shortcut can be deternimed 373 * from text dimensions. 374 * This function does not use any locks 375 * and is guaranteed to return some value 376 * (possibly actual, possibly expired) 377 */ 378 /*int getShortcutWidth() { 379 TextMetrics textMetrics = this.textMetrics; 380 if (textMetrics == null) { 381 textMetrics = calcTextMetrics(); 382 this.textMetrics = textMetrics; 383 } 384 return (textMetrics != null) ? textMetrics.shortcutWidth : 0; 385 } 386 387 int getTextBaseline() { 388 TextMetrics textMetrics = this.textMetrics; 389 if (textMetrics == null) { 390 textMetrics = calcTextMetrics(); 391 this.textMetrics = textMetrics; 392 } 393 return (textMetrics != null) ? textMetrics.textBaseline : 0; 394 }*/ 395 396 TextMetrics calcTextMetrics() { 397 if (container == null) { 398 return null; 399 } 400 if (isSeparator()) { 401 return new TextMetrics(new Dimension(SEPARATOR_WIDTH, SEPARATOR_HEIGHT), 0, 0); 402 } 403 Graphics g = container.getGraphics(); 404 if (g == null) { 405 return null; 406 } 407 try { 408 g.setFont(getTargetFont()); 409 FontMetrics fm = g.getFontMetrics(); 410 String str = getTargetLabel(); 411 int width = fm.stringWidth(str); 412 int height = fm.getHeight(); 413 Dimension textDimension = new Dimension(width, height); 414 int textBaseline = fm.getHeight() - fm.getAscent(); 415 String sc = getShortcutText(); 416 int shortcutWidth = (sc == null) ? 0 : fm.stringWidth(sc); 417 return new TextMetrics(textDimension, shortcutWidth, textBaseline); 418 } finally { 419 g.dispose(); 420 } 421 } 422 423 void resetTextMetrics() { 424 textMetrics = null; 425 if (container != null) { 426 container.updateSize(); 427 } 428 } 429 430 /************************************************ 431 * 432 * Mapping utility functions 433 * 434 ************************************************/ 435 436 /** 437 * Sets mapping of item to window. 438 * @param bounds bounds of item in container's coordinates 439 * @param textOrigin point for drawString in container's coordinates 440 * @see XBaseMenuWindow.map() 441 */ 442 void map(Rectangle bounds, Point textOrigin) { 443 this.bounds = bounds; 444 this.textOrigin = textOrigin; 445 } 446 447 /** 448 * returns bounds of item that were previously set by map() function 449 */ 450 Rectangle getBounds() { 451 return bounds; 452 } 453 454 /** 455 * returns origin of item's text that was previously set by map() function 456 */ 457 Point getTextOrigin() { 458 return textOrigin; 459 } 460 461 }