/*
 * Copyright (c) 2002, 2006, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package sun.awt.X11;

import java.awt.*;
import java.awt.peer.*;
import java.awt.event.*;

import java.awt.image.BufferedImage;
import java.awt.geom.Point2D;

import java.util.Vector;
import sun.util.logging.PlatformLogger;

public class XMenuWindow extends XBaseMenuWindow {

    
Data members
/************************************************ * * Data members * ************************************************/
private static PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XMenuWindow"); /* * Primary members */ private XMenuPeer menuPeer; /* * dimension constants */ private final static int WINDOW_SPACING_LEFT = 2; private final static int WINDOW_SPACING_RIGHT = 2; private final static int WINDOW_SPACING_TOP = 2; private final static int WINDOW_SPACING_BOTTOM = 2; private final static int WINDOW_ITEM_INDENT = 15; private final static int WINDOW_ITEM_MARGIN_LEFT = 2; private final static int WINDOW_ITEM_MARGIN_RIGHT = 2; private final static int WINDOW_ITEM_MARGIN_TOP = 2; private final static int WINDOW_ITEM_MARGIN_BOTTOM = 2; private final static int WINDOW_SHORTCUT_SPACING = 10; /* * Checkmark */ private static final int CHECKMARK_SIZE = 128; private static final int[] CHECKMARK_X = new int[] {1, 25,56,124,124,85, 64}; // X-coords private static final int[] CHECKMARK_Y = new int[] {59,35,67, 0, 12,66,123}; // Y-coords
Mapping data
/************************************************ * * Mapping data * ************************************************/
static class MappingData extends XBaseMenuWindow.MappingData {
Rectangle for the caption Necessary to fix 6267144: PIT: Popup menu label is not shown, XToolkit
/** * Rectangle for the caption * Necessary to fix 6267144: PIT: Popup menu label is not shown, XToolkit */
private Rectangle captionRect;
Desired size of menu window
/** * Desired size of menu window */
private Dimension desiredSize;
Width of largest checkmark At the same time the left origin of all item's text
/** * Width of largest checkmark * At the same time the left origin * of all item's text */
private int leftMarkWidth;
Left origin of all shortcut labels
/** * Left origin of all shortcut labels */
private int shortcutOrigin;
The origin of right mark (submenu's arrow)
/** * The origin of right mark * (submenu's arrow) */
private int rightMarkOrigin; MappingData(XMenuItemPeer[] items, Rectangle captionRect, Dimension desiredSize, int leftMarkWidth, int shortcutOrigin, int rightMarkOrigin) { super(items); this.captionRect = captionRect; this.desiredSize = desiredSize; this.leftMarkWidth = leftMarkWidth; this.shortcutOrigin = shortcutOrigin; this.rightMarkOrigin = rightMarkOrigin; }
Constructs MappingData without items This constructor should be used in case of errors
/** * Constructs MappingData without items * This constructor should be used in case of errors */
MappingData() { this.desiredSize = new Dimension(0, 0); this.leftMarkWidth = 0; this.shortcutOrigin = 0; this.rightMarkOrigin = 0; } public Rectangle getCaptionRect() { return this.captionRect; } public Dimension getDesiredSize() { return this.desiredSize; } public int getShortcutOrigin() { return this.shortcutOrigin; } public int getLeftMarkWidth() { return this.leftMarkWidth; } public int getRightMarkOrigin() { return this.rightMarkOrigin; } } /************************************************ * * Construction * ************************************************/
Constructs XMenuWindow for specified XMenuPeer null for XPopupMenuWindow
/** * Constructs XMenuWindow for specified XMenuPeer * null for XPopupMenuWindow */
XMenuWindow(XMenuPeer menuPeer) { if (menuPeer != null) { this.menuPeer = menuPeer; this.target = menuPeer.getContainer().target; // Get menus from the target. Vector targetItemVector = null; targetItemVector = getMenuTargetItems(); reloadItems(targetItemVector); } }
Initialization
/************************************************ * * Initialization * ************************************************/
/* * Overriden initialization */ void postInit(XCreateWindowParams params) { super.postInit(params); //Fixed 6267182: PIT: Menu is not visible after //showing and disposing a file dialog, XToolkit //toFront() is called on every show } /************************************************ * * Implementation of abstract methods * ************************************************/
See Also:
  • XBaseMenuWindow.getParentMenuWindow()
/** * @see XBaseMenuWindow.getParentMenuWindow() */
protected XBaseMenuWindow getParentMenuWindow() { return (menuPeer != null) ? menuPeer.getContainer() : null; }
See Also:
  • XBaseMenuWindow.map()
/** * @see XBaseMenuWindow.map() */
protected MappingData map() { //TODO:Implement popup-menu caption mapping and painting and tear-off int itemCnt; if (!isCreated()) { MappingData mappingData = new MappingData(new XMenuItemPeer[0], new Rectangle(0, 0, 0, 0), new Dimension(0, 0), 0, 0, 0); return mappingData; } XMenuItemPeer[] itemVector = copyItems(); itemCnt = itemVector.length; //We need maximum width of components before calculating item's bounds Dimension captionSize = getCaptionSize(); int maxWidth = (captionSize != null) ? captionSize.width : 0; int maxLeftIndent = 0; int maxRightIndent = 0; int maxShortcutWidth = 0; XMenuItemPeer.TextMetrics[] itemMetrics = new XMenuItemPeer.TextMetrics[itemCnt]; for (int i = 0; i < itemCnt; i++) { XMenuItemPeer item = itemVector[i]; itemMetrics[i] = itemVector[i].getTextMetrics(); Dimension dim = itemMetrics[i].getTextDimension(); if (dim != null) { if (itemVector[i] instanceof XCheckboxMenuItemPeer) { maxLeftIndent = Math.max(maxLeftIndent, dim.height); } else if (itemVector[i] instanceof XMenuPeer) { maxRightIndent = Math.max(maxRightIndent, dim.height); } maxWidth = Math.max(maxWidth, dim.width); maxShortcutWidth = Math.max(maxShortcutWidth, itemMetrics[i].getShortcutWidth()); } } //Calculate bounds int nextOffset = WINDOW_SPACING_TOP; int shortcutOrigin = WINDOW_SPACING_LEFT + WINDOW_ITEM_MARGIN_LEFT + maxLeftIndent + maxWidth; if (maxShortcutWidth > 0) { shortcutOrigin = shortcutOrigin + WINDOW_SHORTCUT_SPACING; } int rightMarkOrigin = shortcutOrigin + maxShortcutWidth; int itemWidth = rightMarkOrigin + maxRightIndent + WINDOW_ITEM_MARGIN_RIGHT; int width = WINDOW_SPACING_LEFT + itemWidth + WINDOW_SPACING_RIGHT; //Caption rectangle Rectangle captionRect = null; if (captionSize != null) { captionRect = new Rectangle(WINDOW_SPACING_LEFT, nextOffset, itemWidth, captionSize.height); nextOffset += captionSize.height; } else { captionRect = new Rectangle(WINDOW_SPACING_LEFT, nextOffset, maxWidth, 0); } //Item rectangles for (int i = 0; i < itemCnt; i++) { XMenuItemPeer item = (XMenuItemPeer)itemVector[i]; XMenuItemPeer.TextMetrics metrics = itemMetrics[i]; Dimension dim = metrics.getTextDimension(); if (dim != null) { int itemHeight = WINDOW_ITEM_MARGIN_TOP + dim.height + WINDOW_ITEM_MARGIN_BOTTOM; Rectangle bounds = new Rectangle(WINDOW_SPACING_LEFT, nextOffset, itemWidth, itemHeight); int y = (itemHeight + dim.height) / 2 - metrics.getTextBaseline(); Point textOrigin = new Point(WINDOW_SPACING_LEFT + WINDOW_ITEM_MARGIN_LEFT + maxLeftIndent, nextOffset + y); nextOffset += itemHeight; item.map(bounds, textOrigin); } else { //Text metrics could not be determined because of errors //Map item with empty rectangle Rectangle bounds = new Rectangle(WINDOW_SPACING_LEFT, nextOffset, 0, 0); Point textOrigin = new Point(WINDOW_SPACING_LEFT + WINDOW_ITEM_MARGIN_LEFT + maxLeftIndent, nextOffset); item.map(bounds, textOrigin); } } int height = nextOffset + WINDOW_SPACING_BOTTOM; MappingData mappingData = new MappingData(itemVector, captionRect, new Dimension(width, height), maxLeftIndent, shortcutOrigin, rightMarkOrigin); return mappingData; }
See Also:
  • XBaseMenuWindow.getSubmenuBounds()
/** * @see XBaseMenuWindow.getSubmenuBounds() */
protected Rectangle getSubmenuBounds(Rectangle itemBounds, Dimension windowSize) { Rectangle globalBounds = toGlobal(itemBounds); Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); Rectangle res; res = fitWindowRight(globalBounds, windowSize, screenSize); if (res != null) { return res; } res = fitWindowBelow(globalBounds, windowSize, screenSize); if (res != null) { return res; } res = fitWindowAbove(globalBounds, windowSize, screenSize); if (res != null) { return res; } res = fitWindowLeft(globalBounds, windowSize, screenSize); if (res != null) { return res; } return fitWindowToScreen(windowSize, screenSize); }
It's likely that size of items was changed invoke resizing of window on eventHandlerThread
/** * It's likely that size of items was changed * invoke resizing of window on eventHandlerThread */
protected void updateSize() { resetMapping(); if (isShowing()) { XToolkit.executeOnEventHandlerThread(target, new Runnable() { public void run() { Dimension dim = getDesiredSize(); reshape(x, y, dim.width, dim.height); } }); } } /************************************************ * * Overridable caption-painting functions * Necessary to fix 6267144: PIT: Popup menu label is not shown, XToolkit * ************************************************/
Returns size of menu window's caption or null if window has no caption. Can be overriden for popup menus and tear-off menus
/** * Returns size of menu window's caption or null * if window has no caption. * Can be overriden for popup menus and tear-off menus */
protected Dimension getCaptionSize() { return null; }
Paints menu window's caption. Can be overriden for popup menus and tear-off menus. Default implementation does nothing
/** * Paints menu window's caption. * Can be overriden for popup menus and tear-off menus. * Default implementation does nothing */
protected void paintCaption(Graphics g, Rectangle rect) { } /************************************************ * * General-purpose utility functions * ************************************************/
Returns corresponding menu peer
/** * Returns corresponding menu peer */
XMenuPeer getMenuPeer() { return menuPeer; }
Reads vector of items from target This function is overriden in XPopupMenuPeer
/** * Reads vector of items from target * This function is overriden in XPopupMenuPeer */
Vector getMenuTargetItems() { return menuPeer.getTargetItems(); }
Returns desired size calculated while mapping
/** * Returns desired size calculated while mapping */
Dimension getDesiredSize() { MappingData mappingData = (MappingData)getMappingData(); return mappingData.getDesiredSize(); }
Checks if menu window is created
/** * Checks if menu window is created */
boolean isCreated() { return getWindow() != 0; }
Performs delayed creation of menu window if necessary
/** * Performs delayed creation of menu window if necessary */
boolean ensureCreated() { if (!isCreated()) { XCreateWindowParams params = getDelayedParams(); params.remove(DELAYED); params.add(OVERRIDE_REDIRECT, Boolean.TRUE); params.add(XWindow.TARGET, target); init(params); } return true; }
Init window if it's not inited yet and show it at specified coordinates
Params:
  • bounds – bounding rectangle of window in global coordinates
/** * Init window if it's not inited yet * and show it at specified coordinates * @param bounds bounding rectangle of window * in global coordinates */
void show(Rectangle bounds) { if (!isCreated()) { return; } if (log.isLoggable(PlatformLogger.Level.FINER)) { log.finer("showing menu window + " + getWindow() + " at " + bounds); } XToolkit.awtLock(); try { reshape(bounds.x, bounds.y, bounds.width, bounds.height); xSetVisible(true); //Fixed 6267182: PIT: Menu is not visible after //showing and disposing a file dialog, XToolkit toFront(); selectItem(getFirstSelectableItem(), false); } finally { XToolkit.awtUnlock(); } }
Hides menu window
/** * Hides menu window */
void hide() { selectItem(null, false); xSetVisible(false); } /************************************************ * * Painting * ************************************************/
Paints menu window
/** * Paints menu window */
public void paint(Graphics g) { resetColors(); int width = getWidth(); int height = getHeight(); flush(); //Fill background of rectangle g.setColor(getBackgroundColor()); g.fillRect(1, 1, width - 2, height - 2); draw3DRect(g, 0, 0, width, height, true); //Mapping data MappingData mappingData = (MappingData)getMappingData(); //Paint caption paintCaption(g, mappingData.getCaptionRect()); //Paint menus XMenuItemPeer[] itemVector = mappingData.getItems(); Dimension windowSize = mappingData.getDesiredSize(); XMenuItemPeer selectedItem = getSelectedItem(); for (int i = 0; i < itemVector.length; i++) { XMenuItemPeer item = itemVector[i]; XMenuItemPeer.TextMetrics metrics = item.getTextMetrics(); Rectangle bounds = item.getBounds(); if (item.isSeparator()) { draw3DRect(g, bounds.x, bounds.y + bounds.height / 2, bounds.width, 2, false); } else { //paint item g.setFont(item.getTargetFont()); Point textOrigin = item.getTextOrigin(); Dimension textDim = metrics.getTextDimension(); if (item == selectedItem) { g.setColor(getSelectedColor()); g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height); draw3DRect(g, bounds.x, bounds.y, bounds.width, bounds.height, false); } g.setColor(item.isTargetItemEnabled() ? getForegroundColor() : getDisabledColor()); g.drawString(item.getTargetLabel(), textOrigin.x, textOrigin.y); String shortcutText = item.getShortcutText(); if (shortcutText != null) { g.drawString(shortcutText, mappingData.getShortcutOrigin(), textOrigin.y); } if (item instanceof XMenuPeer) { //calculate arrow coordinates int markWidth = textDim.height * 4 / 5; int markHeight = textDim.height * 4 / 5; int markX = bounds.x + bounds.width - markWidth - WINDOW_SPACING_RIGHT - WINDOW_ITEM_MARGIN_RIGHT; int markY = bounds.y + (bounds.height - markHeight) / 2; //draw arrow g.setColor(item.isTargetItemEnabled() ? getDarkShadowColor() : getDisabledColor()); g.drawLine(markX, markY + markHeight, markX + markWidth, markY + markHeight / 2); g.setColor(item.isTargetItemEnabled() ? getLightShadowColor() : getDisabledColor()); g.drawLine(markX, markY, markX + markWidth, markY + markHeight / 2); g.drawLine(markX, markY, markX, markY + markHeight); } else if (item instanceof XCheckboxMenuItemPeer) { //calculate checkmark coordinates int markWidth = textDim.height * 4 / 5; int markHeight = textDim.height * 4 / 5; int markX = WINDOW_SPACING_LEFT + WINDOW_ITEM_MARGIN_LEFT; int markY = bounds.y + (bounds.height - markHeight) / 2; boolean checkState = ((XCheckboxMenuItemPeer)item).getTargetState(); //draw checkmark if (checkState) { g.setColor(getSelectedColor()); g.fillRect(markX, markY, markWidth, markHeight); draw3DRect(g, markX, markY, markWidth, markHeight, false); int[] px = new int[CHECKMARK_X.length]; int[] py = new int[CHECKMARK_X.length]; for (int j = 0; j < CHECKMARK_X.length; j++) { px[j] = markX + CHECKMARK_X[j] * markWidth / CHECKMARK_SIZE; py[j] = markY + CHECKMARK_Y[j] * markHeight / CHECKMARK_SIZE; } g.setColor(item.isTargetItemEnabled() ? getForegroundColor() : getDisabledColor()); g.fillPolygon(px, py, CHECKMARK_X.length); } else { g.setColor(getBackgroundColor()); g.fillRect(markX, markY, markWidth, markHeight); draw3DRect(g, markX, markY, markWidth, markHeight, true); } } } } flush(); } }