blob: 65dda8489f15299ee7f9893f1e50f816b9d9b7e7 [file] [log] [blame]
/*
* Copyright (c) 2002, 2017, 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.util.Vector;
import sun.util.logging.PlatformLogger;
import sun.awt.AWTAccessor;
public class XMenuBarPeer extends XBaseMenuWindow implements MenuBarPeer {
/************************************************
*
* Data members
*
************************************************/
private static PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XMenuBarPeer");
/*
* Primary members
*/
private XFramePeer framePeer;
private MenuBar menuBarTarget;
/*
* Index of help menu
*/
private XMenuPeer helpMenu = null;
/*
* dimension constants
*/
private static final int BAR_SPACING_TOP = 3;
private static final int BAR_SPACING_BOTTOM = 3;
private static final int BAR_SPACING_LEFT = 3;
private static final int BAR_SPACING_RIGHT = 3;
private static final int BAR_ITEM_SPACING = 2;
private static final int BAR_ITEM_MARGIN_LEFT = 10;
private static final int BAR_ITEM_MARGIN_RIGHT = 10;
private static final int BAR_ITEM_MARGIN_TOP = 2;
private static final int BAR_ITEM_MARGIN_BOTTOM = 2;
/************************************************
*
* Mapping data
*
************************************************/
/**
* XBaseMenuWindow's mappingData is extended with
* desired height of menu bar
*/
static class MappingData extends XBaseMenuWindow.MappingData {
int desiredHeight;
MappingData(XMenuItemPeer[] items, int desiredHeight) {
super(items);
this.desiredHeight = desiredHeight;
}
/**
* Constructs MappingData without items
* This constructor should be used in case of errors
*/
MappingData() {
this.desiredHeight = 0;
}
public int getDesiredHeight() {
return this.desiredHeight;
}
}
/************************************************
*
* Construction
*
************************************************/
XMenuBarPeer(MenuBar menuBarTarget) {
this.menuBarTarget = menuBarTarget;
}
/************************************************
*
* Implementaion of interface methods
*
************************************************/
/*
* From MenuComponentPeer
*/
public void setFont(Font f) {
resetMapping();
setItemsFont(f);
postPaintEvent();
}
/*
* From MenuBarPeer
*/
/*
* Functions addMenu, delMenu, addHelpMenu
* need to have somewhat strange behaivour
* deduced from java.awt.MenuBar.
* We can not get index of particular item in
* MenuBar.menus array, because MenuBar firstly
* performs array operations and then calls peer.
* So we need to synchronize indicies in 'items'
* array with MenuBar.menus. We have to follow
* these rules:
* 1. Menus are always added to the end of array,
* even when helpMenu is present
* 2. Removal of any menu item acts as casual
* remove from array
* 3. MenuBar.setHelpMenu _firstly_ removes
* previous helpMenu by calling delMenu() if
* necessary, then it performs addMenu(),
* and then - addHelpMenu().
*
* Note that these functions don't perform
* type checks and checks for nulls or duplicates
*/
public void addMenu(Menu m) {
addItem(m);
postPaintEvent();
}
public void delMenu(int index) {
synchronized(getMenuTreeLock()) {
XMenuItemPeer item = getItem(index);
if (item != null && item == helpMenu) {
helpMenu = null;
}
delItem(index);
}
postPaintEvent();
}
public void addHelpMenu(Menu m) {
XMenuPeer mp = AWTAccessor.getMenuComponentAccessor().getPeer(m);
synchronized(getMenuTreeLock()) {
helpMenu = mp;
}
postPaintEvent();
}
/************************************************
*
* Initialization
*
************************************************/
/**
* called from XFramePeer.setMenuBar
*/
public void init(Frame frame) {
this.target = frame;
this.framePeer = AWTAccessor.getComponentAccessor().getPeer(frame);
XCreateWindowParams params = getDelayedParams();
params.remove(DELAYED);
params.add(PARENT_WINDOW, framePeer.getShell());
params.add(TARGET, frame);
init(params);
}
/**
* Overriden initialization
*/
void postInit(XCreateWindowParams params) {
super.postInit(params);
// Get menus from the target.
Vector<Menu> targetMenuVector = AWTAccessor.getMenuBarAccessor()
.getMenus(menuBarTarget);
Menu targetHelpMenu = AWTAccessor.getMenuBarAccessor()
.getHelpMenu(menuBarTarget);
reloadItems(targetMenuVector);
if (targetHelpMenu != null) {
addHelpMenu(targetHelpMenu);
}
xSetVisible(true);
toFront();
}
/************************************************
*
* Implementation of abstract methods
*
************************************************/
/**
* Menu bar is always root window in menu window's
* hierarchy
*/
protected XBaseMenuWindow getParentMenuWindow() {
return null;
}
/**
* @see XBaseMenuWindow#map
*/
protected MappingData map() {
XMenuItemPeer[] itemVector = copyItems();
int itemCnt = itemVector.length;
XMenuItemPeer helpMenu = this.helpMenu;
int helpMenuPos = -1;
//find helpMenu and move it to the end of array
if (helpMenu != null) {
//Fixed 6270847: PIT: HELP menu is not shown at the right place when normal menus added to MB are removed, XToolkit
for (int i = 0; i < itemCnt; i++) {
if (itemVector[i] == helpMenu) {
helpMenuPos = i;
break;
}
}
if (helpMenuPos != -1 && helpMenuPos != itemCnt - 1) {
System.arraycopy(itemVector, helpMenuPos + 1, itemVector, helpMenuPos, itemCnt - 1 - helpMenuPos);
itemVector[itemCnt - 1] = helpMenu;
}
}
//We need maximum height before calculating item's bounds
int maxHeight = 0;
XMenuItemPeer.TextMetrics[] itemMetrics = new XMenuItemPeer.TextMetrics[itemCnt];
for (int i = 0; i < itemCnt; i++) {
itemMetrics[i] = itemVector[i].getTextMetrics();
if (itemMetrics[i] != null) {
Dimension dim = itemMetrics[i].getTextDimension();
if (dim != null) {
maxHeight = Math.max(maxHeight, dim.height);
}
}
}
//Calculate bounds
int nextOffset = 0;
int itemHeight = BAR_ITEM_MARGIN_TOP + maxHeight + BAR_ITEM_MARGIN_BOTTOM;
int mappedCnt = itemCnt;
for (int i = 0; i < itemCnt; i++) {
XMenuItemPeer item = itemVector[i];
XMenuItemPeer.TextMetrics metrics = itemMetrics[i];
if (metrics == null) {
continue;
}
Dimension dim = metrics.getTextDimension();
if (dim != null) {
int itemWidth = BAR_ITEM_MARGIN_LEFT + dim.width + BAR_ITEM_MARGIN_RIGHT;
//Fix for 6270757: PIT: Menus and Sub-menus are shown outside the frame, XToolkit
//Cut-off items that don't fit in window
//At least one item must remain in menu
if ((nextOffset + itemWidth > this.width) && (i > 0)) {
mappedCnt = i;
break;
}
//If this item is help menu, move it to the right edge
if ((i == itemCnt - 1) && helpMenuPos != -1) {
nextOffset = Math.max(nextOffset, this.width - itemWidth - BAR_SPACING_RIGHT);
}
Rectangle bounds = new Rectangle(nextOffset, BAR_SPACING_TOP, itemWidth, itemHeight);
//text should be centered vertically in menu item's bounds
int y = (maxHeight + dim.height) / 2 - metrics.getTextBaseline();
Point textOrigin = new Point(nextOffset + BAR_ITEM_MARGIN_LEFT, BAR_SPACING_TOP + BAR_ITEM_MARGIN_TOP + y);
nextOffset += itemWidth + BAR_ITEM_SPACING;
item.map(bounds, textOrigin);
}
}
XMenuItemPeer mappedVector[] = new XMenuItemPeer[mappedCnt];
System.arraycopy(itemVector, 0, mappedVector, 0, mappedCnt);
MappingData mappingData = new MappingData(mappedVector, BAR_SPACING_TOP + itemHeight + BAR_SPACING_BOTTOM);
return mappingData;
}
/**
* @see XBaseMenuWindow#getSubmenuBounds
*/
protected Rectangle getSubmenuBounds(Rectangle itemBounds, Dimension windowSize) {
Rectangle globalBounds = toGlobal(itemBounds);
Dimension screenSize = graphicsConfig.getBounds().getSize();
Rectangle res;
res = fitWindowBelow(globalBounds, windowSize, screenSize);
if (res != null) {
return res;
}
res = fitWindowAbove(globalBounds, windowSize, screenSize);
if (res != null) {
return res;
}
res = fitWindowRight(globalBounds, windowSize, screenSize);
if (res != null) {
return res;
}
res = fitWindowLeft(globalBounds, windowSize, screenSize);
if (res != null) {
return res;
}
return fitWindowToScreen(windowSize, screenSize);
}
/**
* This function is called when it's likely that
* size of items has changed.
* Invokes framePeer's updateChildrenSizes()
*/
protected void updateSize() {
resetMapping();
if (framePeer != null) {
framePeer.reshapeMenubarPeer();
}
}
/************************************************
*
* Utility functions
*
************************************************/
/**
* Returns desired height of menu bar
*/
int getDesiredHeight() {
MappingData mappingData = (MappingData)getMappingData();
return mappingData.getDesiredHeight();
}
/**
* Returns true if framePeer is not null and is enabled
* Used to fix 6185057: Disabling a frame does not disable
* the menus on the frame, on solaris/linux
*/
boolean isFramePeerEnabled() {
if (framePeer != null) {
return framePeer.isEnabled();
}
return false;
}
/************************************************
*
* Overriden XBaseMenuWindow functions
*
************************************************/
/**
* @see XBaseMenuWindow#doDispose()
*/
protected void doDispose() {
super.doDispose();
XToolkit.targetDisposedPeer(menuBarTarget, this);
}
/************************************************
*
* Overriden XWindow general-purpose functions
*
************************************************/
/**
* For menu bars this function is called from framePeer's
* reshape(...) and updateChildrenSizes()
*/
public void reshape(int x, int y, int width, int height) {
if ((width != this.width) || (height != this.height)) {
resetMapping();
}
super.reshape(x, y, width, height);
}
/**
* Performs ungrabbing of input
* @see XBaseWindow#ungrabInputImpl()
*/
void ungrabInputImpl() {
selectItem(null, false);
super.ungrabInputImpl();
postPaintEvent();
}
/************************************************
*
* Overriden XWindow painting & printing
*
************************************************/
public void paintPeer(Graphics g) {
resetColors();
/* Calculate menubar dimension. */
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);
//Paint menus
MappingData mappingData = (MappingData)getMappingData();
XMenuItemPeer[] itemVector = mappingData.getItems();
XMenuItemPeer selectedItem = getSelectedItem();
for (int i = 0; i < itemVector.length; i++) {
XMenuItemPeer item = itemVector[i];
//paint item
g.setFont(item.getTargetFont());
Rectangle bounds = item.getBounds();
Point textOrigin = item.getTextOrigin();
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);
}
if (isFramePeerEnabled() && item.isTargetItemEnabled()) {
g.setColor(getForegroundColor());
} else {
g.setColor(getDisabledColor());
}
g.drawString(item.getTargetLabel(), textOrigin.x, textOrigin.y);
}
flush();
}
static final int W_DIFF = (XFramePeer.CROSSHAIR_INSET + 1) * 2;
static final int H_DIFF = XFramePeer.BUTTON_Y + XFramePeer.BUTTON_H;
void print(Graphics g) {
//TODO:Implement
}
/************************************************
*
* Overriden XBaseMenuWindow event handling
*
************************************************/
protected void handleEvent(AWTEvent event) {
// explicitly block all events except PaintEvent.PAINT for menus,
// that are in the modal blocked window
if ((framePeer != null) &&
(event.getID() != PaintEvent.PAINT))
{
if (framePeer.isModalBlocked()) {
return;
}
}
switch(event.getID()) {
case MouseEvent.MOUSE_PRESSED:
case MouseEvent.MOUSE_RELEASED:
case MouseEvent.MOUSE_CLICKED:
case MouseEvent.MOUSE_MOVED:
case MouseEvent.MOUSE_ENTERED:
case MouseEvent.MOUSE_EXITED:
case MouseEvent.MOUSE_DRAGGED:
//Fix for 6185057: Disabling a frame does not disable
//the menus on the frame, on solaris/linux
if (isFramePeerEnabled()) {
doHandleJavaMouseEvent((MouseEvent)event);
}
break;
case KeyEvent.KEY_PRESSED:
case KeyEvent.KEY_RELEASED:
//Fix for 6185057: Disabling a frame does not disable
//the menus on the frame, on solaris/linux
if (isFramePeerEnabled()) {
doHandleJavaKeyEvent((KeyEvent)event);
}
break;
default:
super.handleEvent(event);
break;
}
}
/************************************************
*
* Overriden XWindow keyboard processing
*
************************************************/
/*
* This function is called from XWindow
* @see XWindow.handleF10onEDT()
*/
void handleF10KeyPress(KeyEvent event) {
int keyState = event.getModifiers();
if (((keyState & InputEvent.ALT_MASK) != 0) ||
((keyState & InputEvent.SHIFT_MASK) != 0) ||
((keyState & InputEvent.CTRL_MASK) != 0)) {
return;
}
grabInput();
selectItem(getFirstSelectableItem(), true);
}
/*
* In previous version keys were handled in handleKeyPress.
* Now we override this function do disable F10 explicit
* processing. All processing is done using KeyEvent.
*/
public void handleKeyPress(XEvent xev) {
XKeyEvent xkey = xev.get_xkey();
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine(xkey.toString());
}
if (isEventDisabled(xev)) {
return;
}
final Component currentSource = getEventSource();
//This is the only difference from XWindow.handleKeyPress
//Ancestor's function can invoke handleF10KeyPress here
handleKeyPress(xkey);
}
} //class XMenuBarPeer