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 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 static final int SEPARATOR_WIDTH = 20; 79 private static final 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 * 170 * Access to target's fields 171 * 172 ************************************************/ 173 174 MenuItem getTarget() { 175 return this.target; 176 } 177 178 Font getTargetFont() { 179 if (target == null) { 180 return XWindow.getDefaultFont(); 181 } 182 return AWTAccessor.getMenuComponentAccessor().getFont_NoClientCode(target); 183 } 184 185 String getTargetLabel() { 186 if (target == null) { 187 return ""; 188 } 189 String label = AWTAccessor.getMenuItemAccessor().getLabel(target); 190 return (label == null) ? "" : label; 191 } 192 193 boolean isTargetEnabled() { 194 if (target == null) { 195 return false; 196 } 197 return AWTAccessor.getMenuItemAccessor().isEnabled(target); 198 } 199 200 /** 201 * Returns true if item and all its parents are enabled 202 * This function is used to fix 203 * 6184485: Popup menu is not disabled on XToolkit even when calling setEnabled (false) 204 */ 205 boolean isTargetItemEnabled() { 206 if (target == null) { 207 return false; 208 } 209 return AWTAccessor.getMenuItemAccessor().isItemEnabled(target); 210 } 211 212 String getTargetActionCommand() { 213 if (target == null) { 214 return ""; 215 } 216 return AWTAccessor.getMenuItemAccessor().getActionCommandImpl(target); 217 } 218 219 MenuShortcut getTargetShortcut() { 220 if (target == null) { 221 return null; 222 } 223 return AWTAccessor.getMenuItemAccessor().getShortcut(target); 224 } 225 226 String getShortcutText() { 227 //Fix for 6180413: shortcuts should not be displayed for any of the menuitems in a popup menu 228 if (container == null) { 229 return null; 230 } 231 if (container.getRootMenuWindow() instanceof XPopupMenuPeer) { 232 return null; 233 } 234 MenuShortcut sc = getTargetShortcut(); 235 //TODO:This can potentially call user code 236 return (sc == null) ? null : sc.toString(); 237 } 238 239 240 /************************************************ 241 * 242 * Basic manipulations 243 * 244 ************************************************/ 245 246 /** 247 * This function is called when filling item vectors 248 * in XMenuWindow & XMenuBar. We need it because peers 249 * are created earlier than windows. 250 * @param container the window that this item belongs to. 251 */ 252 void setContainer(XBaseMenuWindow container) { 253 synchronized(XBaseMenuWindow.getMenuTreeLock()) { 254 this.container = container; 255 } 256 } 257 258 /** 259 * returns the window that this item belongs to 260 */ 261 XBaseMenuWindow getContainer() { 262 return this.container; 263 } 264 265 /************************************************ 266 * 267 * Overridable behaviour 268 * 269 ************************************************/ 270 271 /** 272 * This function should be overriden simply to 273 * return false in inherited classes. 274 */ 275 boolean isSeparator() { 276 boolean r = (getTargetLabel().equals("-")); 277 return r; 278 } 279 280 /************************************************ 281 * 282 * Utility functions 283 * 284 ************************************************/ 285 286 /** 287 * Returns true if container exists and is showing 288 */ 289 boolean isContainerShowing() { 290 if (container == null) { 291 return false; 292 } 293 return container.isShowing(); 294 } 295 296 /** 297 * Repaints item if it is showing 298 */ 299 void repaintIfShowing() { 300 if (isContainerShowing()) { 301 container.postPaintEvent(); 302 } 303 } 304 305 /** 306 * This function is invoked when the user clicks 307 * on menu item. 308 * @param when the timestamp of action event 309 */ 310 void action(long when, int modifiers) { 311 if (!isSeparator() && isTargetItemEnabled()) { 312 XWindow.postEventStatic(new ActionEvent(target, ActionEvent.ACTION_PERFORMED, 313 getTargetActionCommand(), when, 314 modifiers)); 315 } 316 } 317 /************************************************ 318 * 319 * Text metrics 320 * 321 ************************************************/ 322 323 /** 324 * Returns text metrics of menu item. 325 * This function does not use any locks 326 * and is guaranteed to return some value 327 * (possibly actual, possibly expired) 328 */ 329 TextMetrics getTextMetrics() { 330 TextMetrics textMetrics = this.textMetrics; 331 if (textMetrics == null) { 332 textMetrics = calcTextMetrics(); 333 this.textMetrics = textMetrics; 334 } 335 return textMetrics; 336 } 337 338 /** 339 * Returns dimensions of item's label. 340 * This function does not use any locks 341 * Returns actual or expired value 342 * or null if error occurs 343 */ 344 /*Dimension getTextDimension() { 345 TextMetrics textMetrics = this.textMetrics; 346 if (textMetrics == null) { 347 textMetrics = calcTextMetrics(); 348 this.textMetrics = textMetrics; 349 } 350 return (textMetrics != null) ? textMetrics.textDimension : null; 351 }*/ 352 353 /** 354 * Returns width of item's shortcut label, 355 * 0 if item has no shortcut. 356 * The height of shortcut can be deternimed 357 * from text dimensions. 358 * This function does not use any locks 359 * and is guaranteed to return some value 360 * (possibly actual, possibly expired) 361 */ 362 /*int getShortcutWidth() { 363 TextMetrics textMetrics = this.textMetrics; 364 if (textMetrics == null) { 365 textMetrics = calcTextMetrics(); 366 this.textMetrics = textMetrics; 367 } 368 return (textMetrics != null) ? textMetrics.shortcutWidth : 0; 369 } 370 371 int getTextBaseline() { 372 TextMetrics textMetrics = this.textMetrics; 373 if (textMetrics == null) { 374 textMetrics = calcTextMetrics(); 375 this.textMetrics = textMetrics; 376 } 377 return (textMetrics != null) ? textMetrics.textBaseline : 0; 378 }*/ 379 380 TextMetrics calcTextMetrics() { 381 if (container == null) { 382 return null; 383 } 384 if (isSeparator()) { 385 return new TextMetrics(new Dimension(SEPARATOR_WIDTH, SEPARATOR_HEIGHT), 0, 0); 386 } 387 Graphics g = container.getGraphics(); 388 if (g == null) { 389 return null; 390 } 391 try { 392 g.setFont(getTargetFont()); 393 FontMetrics fm = g.getFontMetrics(); 394 String str = getTargetLabel(); 395 int width = fm.stringWidth(str); 396 int height = fm.getHeight(); 397 Dimension textDimension = new Dimension(width, height); 398 int textBaseline = fm.getHeight() - fm.getAscent(); 399 String sc = getShortcutText(); 400 int shortcutWidth = (sc == null) ? 0 : fm.stringWidth(sc); 401 return new TextMetrics(textDimension, shortcutWidth, textBaseline); 402 } finally { 403 g.dispose(); 404 } 405 } 406 407 void resetTextMetrics() { 408 textMetrics = null; 409 if (container != null) { 410 container.updateSize(); 411 } 412 } 413 414 /************************************************ 415 * 416 * Mapping utility functions 417 * 418 ************************************************/ 419 420 /** 421 * Sets mapping of item to window. 422 * @param bounds bounds of item in container's coordinates 423 * @param textOrigin point for drawString in container's coordinates 424 * @see XBaseMenuWindow#map() 425 */ 426 void map(Rectangle bounds, Point textOrigin) { 427 this.bounds = bounds; 428 this.textOrigin = textOrigin; 429 } 430 431 /** 432 * returns bounds of item that were previously set by map() function 433 */ 434 Rectangle getBounds() { 435 return bounds; 436 } 437 438 /** 439 * returns origin of item's text that was previously set by map() function 440 */ 441 Point getTextOrigin() { 442 return textOrigin; 443 } 444 445 }