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.awt.AWTAccessor;
  33 import sun.util.logging.PlatformLogger;
  34 
  35 public class XPopupMenuPeer extends XMenuWindow implements PopupMenuPeer {
  36 
  37     /************************************************
  38      *
  39      * Data members
  40      *
  41      ************************************************/
  42     private static PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XBaseMenuWindow");
  43 
  44     /*
  45      * Primary members
  46      */
  47     private XComponentPeer componentPeer;
  48     private PopupMenu popupMenuTarget;
  49 
  50     /*
  51      * If mouse button is clicked on item showing submenu
  52      * we have to hide its submenu.
  53      * This member saves the submenu under cursor
  54      * Only if it's showing
  55      */
  56     private XMenuPeer showingMousePressedSubmenu = null;
  57 
  58     /*
  59      * Painting constants
  60      */
  61     private static final int CAPTION_MARGIN_TOP = 4;
  62     private static final int CAPTION_SEPARATOR_HEIGHT = 6;
  63 
  64     /************************************************
  65      *
  66      * Construction
  67      *
  68      ************************************************/
  69     XPopupMenuPeer(PopupMenu target) {
  70         super(null);
  71         this.popupMenuTarget = target;
  72     }
  73 
  74     /************************************************
  75      *
  76      * Implementation of interface methods
  77      *
  78      ************************************************/
  79     /*
  80      * From MenuComponentPeer
  81      */
  82     public void setFont(Font f) {
  83         resetMapping();
  84         setItemsFont(f);
  85         postPaintEvent();
  86     }
  87 
  88     /*
  89      * From MenuItemPeer
  90      */
  91     public void setLabel(String label) {
  92         resetMapping();
  93         postPaintEvent();
  94     }
  95 
  96 
  97     public void setEnabled(boolean enabled) {
  98         postPaintEvent();
  99     }
 100 
 101     /**
 102      * DEPRECATED:  Replaced by setEnabled(boolean).
 103      * @see java.awt.peer.MenuItemPeer
 104      */
 105     public void enable() {
 106         setEnabled( true );
 107     }
 108 
 109     /**
 110      * DEPRECATED:  Replaced by setEnabled(boolean).
 111      * @see java.awt.peer.MenuItemPeer
 112      */
 113     public void disable() {
 114         setEnabled( false );
 115     }
 116 
 117     /*
 118      * From MenuPeer
 119      */
 120     /**
 121      * addSeparator routines are not used
 122      * in peers. Shared code invokes addItem("-")
 123      * for adding separators
 124      */
 125     public void addSeparator() {
 126         if (log.isLoggable(PlatformLogger.Level.FINER)) {
 127             log.finer("addSeparator is not implemented");
 128         }
 129     }
 130 
 131     /*
 132      * From PopupMenuPeer
 133      */
 134     public void show(Event e) {
 135         target = (Component)e.target;
 136         // Get menus from the target.
 137         Vector<MenuItem> targetItemVector = getMenuTargetItems();
 138         if (targetItemVector != null) {
 139             reloadItems(targetItemVector);
 140             //Fix for 6287092: JCK15a: api/java_awt/interactive/event/EventTests.html#EventTest0015 fails, mustang
 141             Point tl = target.getLocationOnScreen();
 142             Point pt = new Point(tl.x + e.x, tl.y + e.y);
 143             //Fixed 6266513: Incorrect key handling in XAWT popup menu
 144             //No item should be selected when showing popup menu
 145             if (!ensureCreated()) {
 146                 return;
 147             }
 148             Dimension dim = getDesiredSize();
 149             //Fix for 6267162: PIT: Popup Menu gets hidden below the screen when opened
 150             //near the periphery of the screen, XToolkit
 151             Rectangle bounds = getWindowBounds(pt, dim);
 152             reshape(bounds);
 153             xSetVisible(true);
 154             toFront();
 155             selectItem(null, false);
 156             grabInput();
 157         }
 158     }
 159 
 160     /************************************************
 161      *
 162      * Access to target's fields
 163      *
 164      ************************************************/
 165 
 166     //Fix for 6267144: PIT: Popup menu label is not shown, XToolkit
 167     Font getTargetFont() {
 168         if (popupMenuTarget == null) {
 169             return XWindow.getDefaultFont();
 170         }
 171         return AWTAccessor.getMenuComponentAccessor()
 172                    .getFont_NoClientCode(popupMenuTarget);
 173     }
 174 
 175     //Fix for 6267144: PIT: Popup menu label is not shown, XToolkit
 176     String getTargetLabel() {
 177         if (target == null) {
 178             return "";
 179         }
 180         return AWTAccessor.getMenuItemAccessor().getLabel(popupMenuTarget);
 181     }
 182 
 183     //Fix for 6184485: Popup menu is not disabled on XToolkit even when calling setEnabled (false)
 184     boolean isTargetEnabled() {
 185         if (popupMenuTarget == null) {
 186             return false;
 187         }
 188         return AWTAccessor.getMenuItemAccessor().isEnabled(popupMenuTarget);
 189     }
 190 
 191     Vector<MenuItem> getMenuTargetItems() {
 192         if (popupMenuTarget == null) {
 193             return null;
 194         }
 195         return AWTAccessor.getMenuAccessor().getItems(popupMenuTarget);
 196     }
 197 
 198     /************************************************
 199      *
 200      * Utility functions
 201      *
 202      ************************************************/
 203 
 204     //Fix for 6267162: PIT: Popup Menu gets hidden below the screen when opened
 205     //near the periphery of the screen, XToolkit
 206 
 207     /**
 208      * Calculates placement of popup menu window
 209      * given origin in global coordinates and
 210      * size of menu window. Returns suggested
 211      * rectangle for menu window in global coordinates
 212      * @param origin the origin point specified in show()
 213      * function converted to global coordinates
 214      * @param windowSize the desired size of menu's window
 215      */
 216     protected Rectangle getWindowBounds(Point origin, Dimension windowSize) {
 217         Rectangle globalBounds = new Rectangle(origin.x, origin.y, 0, 0);
 218         Dimension screenSize = graphicsConfig.getBounds().getSize();        
 219         Rectangle res;
 220         res = fitWindowRight(globalBounds, windowSize, screenSize);
 221         if (res != null) {
 222             return res;
 223         }
 224         res = fitWindowLeft(globalBounds, windowSize, screenSize);
 225         if (res != null) {
 226             return res;
 227         }
 228         res = fitWindowBelow(globalBounds, windowSize, screenSize);
 229         if (res != null) {
 230             return res;
 231         }
 232         res = fitWindowAbove(globalBounds, windowSize, screenSize);
 233         if (res != null) {
 234             return res;
 235         }
 236         return fitWindowToScreen(windowSize, screenSize);
 237    }
 238 
 239     /************************************************
 240      *
 241      * Overriden XMenuWindow caption-painting functions
 242      * Necessary to fix 6267144: PIT: Popup menu label is not shown, XToolkit
 243      *
 244      ************************************************/
 245     /**
 246      * Returns height of menu window's caption.
 247      * Can be overriden for popup menus and tear-off menus
 248      */
 249     protected Dimension getCaptionSize() {
 250         String s = getTargetLabel();
 251         if (s.equals("")) {
 252             return null;
 253         }
 254         Graphics g = getGraphics();
 255         if (g == null) {
 256             return null;
 257         }
 258         try {
 259             g.setFont(getTargetFont());
 260             FontMetrics fm = g.getFontMetrics();
 261             String str = getTargetLabel();
 262             int width = fm.stringWidth(str);
 263             int height = CAPTION_MARGIN_TOP + fm.getHeight() + CAPTION_SEPARATOR_HEIGHT;
 264             Dimension textDimension = new Dimension(width, height);
 265             return textDimension;
 266         } finally {
 267             g.dispose();
 268         }
 269     }
 270 
 271     /**
 272      * Paints menu window's caption.
 273      * Can be overriden for popup menus and tear-off menus.
 274      * Default implementation does nothing
 275      */
 276     protected void paintCaption(Graphics g, Rectangle rect) {
 277         String s = getTargetLabel();
 278         if (s.equals("")) {
 279             return;
 280         }
 281         g.setFont(getTargetFont());
 282         FontMetrics fm = g.getFontMetrics();
 283         String str = getTargetLabel();
 284         int width = fm.stringWidth(str);
 285         int textx = rect.x + (rect.width - width) / 2;
 286         int texty = rect.y + CAPTION_MARGIN_TOP + fm.getAscent();
 287         int sepy = rect.y + rect.height - CAPTION_SEPARATOR_HEIGHT / 2;
 288         g.setColor(isTargetEnabled() ? getForegroundColor() : getDisabledColor());
 289         g.drawString(s, textx, texty);
 290         draw3DRect(g, rect.x, sepy,  rect.width, 2, false);
 291     }
 292 
 293     /************************************************
 294      *
 295      * Overriden XBaseMenuWindow functions
 296      *
 297      ************************************************/
 298     protected void doDispose() {
 299         super.doDispose();
 300         XToolkit.targetDisposedPeer(popupMenuTarget, this);
 301     }
 302 
 303     protected void handleEvent(AWTEvent event) {
 304         switch(event.getID()) {
 305         case MouseEvent.MOUSE_PRESSED:
 306         case MouseEvent.MOUSE_RELEASED:
 307         case MouseEvent.MOUSE_CLICKED:
 308         case MouseEvent.MOUSE_MOVED:
 309         case MouseEvent.MOUSE_ENTERED:
 310         case MouseEvent.MOUSE_EXITED:
 311         case MouseEvent.MOUSE_DRAGGED:
 312             doHandleJavaMouseEvent((MouseEvent)event);
 313             break;
 314         case KeyEvent.KEY_PRESSED:
 315         case KeyEvent.KEY_RELEASED:
 316             doHandleJavaKeyEvent((KeyEvent)event);
 317             break;
 318         default:
 319             super.handleEvent(event);
 320             break;
 321         }
 322     }
 323 
 324     /************************************************
 325      *
 326      * Overriden XWindow general-purpose functions
 327      *
 328      ************************************************/
 329     void ungrabInputImpl() {
 330         hide();
 331     }
 332 
 333     /************************************************
 334      *
 335      * Overriden XWindow keyboard processing
 336      *
 337      ************************************************/
 338 
 339     /*
 340      * In previous version keys were handled in handleKeyPress.
 341      * Now we override this function do disable F10 explicit
 342      * processing. All processing is done using KeyEvent.
 343      */
 344     public void handleKeyPress(XEvent xev) {
 345         XKeyEvent xkey = xev.get_xkey();
 346         if (log.isLoggable(PlatformLogger.Level.FINE)) {
 347             log.fine(xkey.toString());
 348         }
 349         if (isEventDisabled(xev)) {
 350             return;
 351         }
 352         final Component currentSource = getEventSource();
 353         handleKeyPress(xkey);
 354     }
 355 
 356 }