--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/awt/X11/XBaseMenuWindow.java Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,1226 @@
+/*
+ * Copyright 2005-2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.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.ColorModel;
+
+import sun.awt.*;
+
+import java.util.ArrayList;
+import java.util.Vector;
+import java.util.logging.*;
+import sun.java2d.SurfaceData;
+import sun.java2d.SunGraphics2D;
+
+/**
+ * The abstract class XBaseMenuWindow is the superclass
+ * of all menu windows.
+ */
+abstract public class XBaseMenuWindow extends XWindow {
+
+ /************************************************
+ *
+ * Data members
+ *
+ ************************************************/
+
+ private static Logger log = Logger.getLogger("sun.awt.X11.XBaseMenuWindow");
+
+ /*
+ * Colors are calculated using MotifColorUtilities class
+ * from backgroundColor and are contained in these vars.
+ */
+ private Color backgroundColor;
+ private Color foregroundColor;
+ private Color lightShadowColor;
+ private Color darkShadowColor;
+ private Color selectedColor;
+ private Color disabledColor;
+
+ /**
+ * Array of items.
+ */
+ private ArrayList<XMenuItemPeer> items;
+
+ /**
+ * Index of selected item in array of items
+ */
+ private int selectedIndex = -1;
+
+ /**
+ * Specifies currently showing submenu.
+ */
+ private XMenuPeer showingSubmenu = null;
+
+ /**
+ * Static synchronizational object.
+ * Following operations should be synchronized
+ * using this object:
+ * 1. Access to items vector
+ * 2. Access to selection
+ * 3. Access to showing menu window member
+ *
+ * This is lowest level lock,
+ * no other locks should be taken when
+ * thread own this lock.
+ */
+ static private Object menuTreeLock = new Object();
+
+ /************************************************
+ *
+ * Event processing
+ *
+ ************************************************/
+
+ /**
+ * If mouse button is clicked on item showing submenu
+ * we have to hide its submenu.
+ * And if mouse button is pressed on such item and
+ * dragged to another, getShowingSubmenu() is changed.
+ * So this member saves the item that the user
+ * presses mouse button on _only_ if it's showing submenu.
+ */
+ private XMenuPeer showingMousePressedSubmenu = null;
+
+ /**
+ * If the PopupMenu is invoked as a result of right button click
+ * first mouse event after grabInput would be MouseReleased.
+ * We need to check if the user has moved mouse after input grab.
+ * If yes - hide the PopupMenu. If no - do nothing
+ */
+ protected Point grabInputPoint = null;
+ protected boolean hasPointerMoved = false;
+
+ /************************************************
+ *
+ * Mapping data
+ *
+ ************************************************/
+
+ /**
+ * Mapping data that is filled in getMappedItems function
+ * and reset in resetSize function. It contains array of
+ * items in order that they appear on screen and may contain
+ * additional data defined by descendants.
+ */
+ private MappingData mappingData;
+
+ static class MappingData implements Cloneable {
+
+ /**
+ * Array of item in order that they appear on screen
+ */
+ private XMenuItemPeer[] items;
+
+ /**
+ * Constructs MappingData object with list
+ * of menu items
+ */
+ MappingData(XMenuItemPeer[] items) {
+ this.items = items;
+ }
+
+ /**
+ * Constructs MappingData without items
+ * This constructor should be used in case of errors
+ */
+ MappingData() {
+ this.items = new XMenuItemPeer[0];
+ }
+
+ public Object clone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException ex) {
+ throw new InternalError();
+ }
+ }
+
+ public XMenuItemPeer[] getItems() {
+ return this.items;
+ }
+ }
+
+ /************************************************
+ *
+ * Construction
+ *
+ ************************************************/
+ XBaseMenuWindow() {
+ super(new XCreateWindowParams(new Object[] {
+ DELAYED, Boolean.TRUE}));
+ }
+
+ /************************************************
+ *
+ * Abstract methods
+ *
+ ************************************************/
+
+ /**
+ * Returns parent menu window (not the X-heirarchy parent window)
+ */
+ protected abstract XBaseMenuWindow getParentMenuWindow();
+
+ /**
+ * Performs mapping of items in window.
+ * This function creates and fills specific
+ * descendant of MappingData
+ * and sets mapping coordinates of items
+ * This function should return default menu data
+ * if errors occur
+ */
+ protected abstract MappingData map();
+
+ /**
+ * Calculates placement of submenu window
+ * given bounds of item with submenu and
+ * size of submenu window. Returns suggested
+ * rectangle for submenu window in global coordinates
+ * @param itemBounds the bounding rectangle of item
+ * in local coordinates
+ * @param windowSize the desired size of submenu's window
+ */
+ protected abstract Rectangle getSubmenuBounds(Rectangle itemBounds, Dimension windowSize);
+
+
+ /**
+ * This function is to be called if it's likely that size
+ * of items was changed. It can be called from any thread
+ * in any locked state, so it should not take locks
+ */
+ protected abstract void updateSize();
+
+ /************************************************
+ *
+ * Initialization
+ *
+ ************************************************/
+
+ /**
+ * Overrides XBaseWindow.instantPreInit
+ */
+ void instantPreInit(XCreateWindowParams params) {
+ super.instantPreInit(params);
+ items = new ArrayList();
+ }
+
+ /************************************************
+ *
+ * General-purpose functions
+ *
+ ************************************************/
+
+ /**
+ * Returns static lock used for menus
+ */
+ static Object getMenuTreeLock() {
+ return menuTreeLock;
+ }
+
+ /**
+ * This function is called to clear all saved
+ * size data.
+ */
+ protected void resetMapping() {
+ mappingData = null;
+ }
+
+ /**
+ * Invokes repaint procedure on eventHandlerThread
+ */
+ void postPaintEvent() {
+ if (isShowing()) {
+ PaintEvent pe = new PaintEvent(target, PaintEvent.PAINT,
+ new Rectangle(0, 0, width, height));
+ postEvent(pe);
+ }
+ }
+
+ /************************************************
+ *
+ * Utility functions for manipulating items
+ *
+ ************************************************/
+
+ /**
+ * Thread-safely returns item at specified index
+ * @param index the position of the item to be returned.
+ */
+ XMenuItemPeer getItem(int index) {
+ if (index >= 0) {
+ synchronized(getMenuTreeLock()) {
+ if (items.size() > index) {
+ return items.get(index);
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Thread-safely creates a copy of the items vector
+ */
+ XMenuItemPeer[] copyItems() {
+ synchronized(getMenuTreeLock()) {
+ return (XMenuItemPeer[])items.toArray(new XMenuItemPeer[] {});
+ }
+ }
+
+
+ /**
+ * Thread-safely returns selected item
+ */
+ XMenuItemPeer getSelectedItem() {
+ synchronized(getMenuTreeLock()) {
+ if (selectedIndex >= 0) {
+ if (items.size() > selectedIndex) {
+ return items.get(selectedIndex);
+ }
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Returns showing submenu, if any
+ */
+ XMenuPeer getShowingSubmenu() {
+ synchronized(getMenuTreeLock()) {
+ return showingSubmenu;
+ }
+ }
+
+ /**
+ * Adds item to end of items vector.
+ * Note that this function does not perform
+ * check for adding duplicate items
+ * @param item item to add
+ */
+ public void addItem(MenuItem item) {
+ XMenuItemPeer mp = (XMenuItemPeer)item.getPeer();
+ if (mp != null) {
+ mp.setContainer(this);
+ synchronized(getMenuTreeLock()) {
+ items.add(mp);
+ }
+ } else {
+ if (log.isLoggable(Level.FINE)) {
+ log.fine("WARNING: Attempt to add menu item without a peer");
+ }
+ }
+ updateSize();
+ }
+
+ /**
+ * Removes item at the specified index from items vector.
+ * @param index the position of the item to be removed
+ */
+ public void delItem(int index) {
+ synchronized(getMenuTreeLock()) {
+ if (selectedIndex == index) {
+ selectItem(null, false);
+ } else if (selectedIndex > index) {
+ selectedIndex--;
+ }
+ if (index < items.size()) {
+ items.remove(index);
+ } else {
+ if (log.isLoggable(Level.FINE)) {
+ log.fine("WARNING: Attempt to remove non-existing menu item, index : " + index + ", item count : " + items.size());
+ }
+ }
+ }
+ updateSize();
+ }
+
+ /**
+ * Clears items vector and loads specified vector
+ * @param items vector to be loaded
+ */
+ public void reloadItems(Vector items) {
+ synchronized(getMenuTreeLock()) {
+ this.items.clear();
+ MenuItem[] itemArray = (MenuItem[])items.toArray(new MenuItem[] {});
+ int itemCnt = itemArray.length;
+ for(int i = 0; i < itemCnt; i++) {
+ addItem(itemArray[i]);
+ }
+ }
+ }
+
+ /**
+ * Select specified item and shows/hides submenus if necessary
+ * We can not select by index, so we need to select by ref.
+ * @param item the item to be selected, null to clear selection
+ * @param showWindowIfMenu if the item is XMenuPeer then its
+ * window is shown/hidden according to this param.
+ */
+ void selectItem(XMenuItemPeer item, boolean showWindowIfMenu) {
+ synchronized(getMenuTreeLock()) {
+ XMenuPeer showingSubmenu = getShowingSubmenu();
+ int newSelectedIndex = (item != null) ? items.indexOf(item) : -1;
+ if (this.selectedIndex != newSelectedIndex) {
+ if (log.isLoggable(Level.FINEST)) {
+ log.finest("Selected index changed, was : " + this.selectedIndex + ", new : " + newSelectedIndex);
+ }
+ this.selectedIndex = newSelectedIndex;
+ postPaintEvent();
+ }
+ final XMenuPeer submenuToShow = (showWindowIfMenu && (item instanceof XMenuPeer)) ? (XMenuPeer)item : null;
+ if (submenuToShow != showingSubmenu) {
+ XToolkit.executeOnEventHandlerThread(target, new Runnable() {
+ public void run() {
+ doShowSubmenu(submenuToShow);
+ }
+ });
+ }
+ }
+ }
+
+ /**
+ * Performs hiding of currently showing submenu
+ * and showing of submenuToShow.
+ * This function should be executed on eventHandlerThread
+ * @param submenuToShow submenu to be shown or null
+ * to hide currently showing submenu
+ */
+ private void doShowSubmenu(XMenuPeer submenuToShow) {
+ XMenuWindow menuWindowToShow = (submenuToShow != null) ? submenuToShow.getMenuWindow() : null;
+ Dimension dim = null;
+ Rectangle bounds = null;
+ //ensureCreated can invoke XWindowPeer.init() ->
+ //XWindowPeer.initGraphicsConfiguration() ->
+ //Window.getGraphicsConfiguration()
+ //that tries to obtain Component.AWTTreeLock.
+ //So it should be called outside awtLock()
+ if (menuWindowToShow != null) {
+ menuWindowToShow.ensureCreated();
+ }
+ XToolkit.awtLock();
+ try {
+ synchronized(getMenuTreeLock()) {
+ if (showingSubmenu != submenuToShow) {
+ if (log.isLoggable(Level.FINER)) {
+ log.finest("Changing showing submenu");
+ }
+ if (showingSubmenu != null) {
+ XMenuWindow showingSubmenuWindow = showingSubmenu.getMenuWindow();
+ if (showingSubmenuWindow != null) {
+ showingSubmenuWindow.hide();
+ }
+ }
+ if (submenuToShow != null) {
+ dim = menuWindowToShow.getDesiredSize();
+ bounds = menuWindowToShow.getParentMenuWindow().getSubmenuBounds(submenuToShow.getBounds(), dim);
+ menuWindowToShow.show(bounds);
+ }
+ showingSubmenu = submenuToShow;
+ }
+ }
+ } finally {
+ XToolkit.awtUnlock();
+ }
+ }
+
+ final void setItemsFont( Font font ) {
+ XMenuItemPeer[] items = copyItems();
+ int itemCnt = items.length;
+ for (int i = 0; i < itemCnt; i++) {
+ items[i].setFont(font);
+ }
+ }
+
+ /************************************************
+ *
+ * Utility functions for manipulating mapped items
+ *
+ ************************************************/
+
+ /**
+ * Returns array of mapped items, null if error
+ * This function has to be not synchronized
+ * and we have to guarantee that we return
+ * some MappingData to user. It's OK if
+ * this.mappingData is replaced meanwhile
+ */
+ MappingData getMappingData() {
+ MappingData mappingData = this.mappingData;
+ if (mappingData == null) {
+ mappingData = map();
+ this.mappingData = mappingData;
+ }
+ return (MappingData)mappingData.clone();
+ }
+
+ /**
+ * returns item thats mapped coordinates contain
+ * specified point, null of none.
+ * @param pt the point in this window's coordinate system
+ */
+ XMenuItemPeer getItemFromPoint(Point pt) {
+ XMenuItemPeer[] items = getMappingData().getItems();
+ int cnt = items.length;
+ for (int i = 0; i < cnt; i++) {
+ if (items[i].getBounds().contains(pt)) {
+ return items[i];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns first item after currently selected
+ * item that can be selected according to mapping array.
+ * (no separators and no disabled items).
+ * Currently selected item if it's only selectable,
+ * null if no item can be selected
+ */
+ XMenuItemPeer getNextSelectableItem() {
+ XMenuItemPeer[] mappedItems = getMappingData().getItems();
+ XMenuItemPeer selectedItem = getSelectedItem();
+ int cnt = mappedItems.length;
+ //Find index of selected item
+ int selIdx = -1;
+ for (int i = 0; i < cnt; i++) {
+ if (mappedItems[i] == selectedItem) {
+ selIdx = i;
+ break;
+ }
+ }
+ int idx = (selIdx == cnt - 1) ? 0 : selIdx + 1;
+ //cycle through mappedItems to find selectable item
+ //beginning from the next item and moving to the
+ //beginning of array when end is reached.
+ //Cycle is finished on selected item itself
+ for (int i = 0; i < cnt; i++) {
+ XMenuItemPeer item = mappedItems[idx];
+ if (!item.isSeparator() && item.isTargetItemEnabled()) {
+ return item;
+ }
+ idx++;
+ if (idx >= cnt) {
+ idx = 0;
+ }
+ }
+ //return null if no selectable item was found
+ return null;
+ }
+
+ /**
+ * Returns first item before currently selected
+ * see getNextSelectableItem() for comments
+ */
+ XMenuItemPeer getPrevSelectableItem() {
+ XMenuItemPeer[] mappedItems = getMappingData().getItems();
+ XMenuItemPeer selectedItem = getSelectedItem();
+ int cnt = mappedItems.length;
+ //Find index of selected item
+ int selIdx = -1;
+ for (int i = 0; i < cnt; i++) {
+ if (mappedItems[i] == selectedItem) {
+ selIdx = i;
+ break;
+ }
+ }
+ int idx = (selIdx <= 0) ? cnt - 1 : selIdx - 1;
+ //cycle through mappedItems to find selectable item
+ for (int i = 0; i < cnt; i++) {
+ XMenuItemPeer item = mappedItems[idx];
+ if (!item.isSeparator() && item.isTargetItemEnabled()) {
+ return item;
+ }
+ idx--;
+ if (idx < 0) {
+ idx = cnt - 1;
+ }
+ }
+ //return null if no selectable item was found
+ return null;
+ }
+
+ /**
+ * Returns first selectable item
+ * This function is intended for clearing selection
+ */
+ XMenuItemPeer getFirstSelectableItem() {
+ XMenuItemPeer[] mappedItems = getMappingData().getItems();
+ int cnt = mappedItems.length;
+ for (int i = 0; i < cnt; i++) {
+ XMenuItemPeer item = mappedItems[i];
+ if (!item.isSeparator() && item.isTargetItemEnabled()) {
+ return item;
+ }
+ }
+
+ return null;
+ }
+
+ /************************************************
+ *
+ * Utility functions for manipulating
+ * hierarchy of windows
+ *
+ ************************************************/
+
+ /**
+ * returns leaf menu window or
+ * this if no children are showing
+ */
+ XBaseMenuWindow getShowingLeaf() {
+ synchronized(getMenuTreeLock()) {
+ XBaseMenuWindow leaf = this;
+ XMenuPeer leafchild = leaf.getShowingSubmenu();
+ while (leafchild != null) {
+ leaf = leafchild.getMenuWindow();
+ leafchild = leaf.getShowingSubmenu();
+ }
+ return leaf;
+ }
+ }
+
+ /**
+ * returns root menu window
+ * or this if this window is topmost
+ */
+ XBaseMenuWindow getRootMenuWindow() {
+ synchronized(getMenuTreeLock()) {
+ XBaseMenuWindow t = this;
+ XBaseMenuWindow tparent = t.getParentMenuWindow();
+ while (tparent != null) {
+ t = tparent;
+ tparent = t.getParentMenuWindow();
+ }
+ return t;
+ }
+ }
+
+ /**
+ * Returns window that contains pt.
+ * search is started from leaf window
+ * to return first window in Z-order
+ * @param pt point in global coordinates
+ */
+ XBaseMenuWindow getMenuWindowFromPoint(Point pt) {
+ synchronized(getMenuTreeLock()) {
+ XBaseMenuWindow t = getShowingLeaf();
+ while (t != null) {
+ Rectangle r = new Rectangle(t.toGlobal(new Point(0, 0)), t.getSize());
+ if (r.contains(pt)) {
+ return t;
+ }
+ t = t.getParentMenuWindow();
+ }
+ return null;
+ }
+ }
+
+ /************************************************
+ *
+ * Primitives for getSubmenuBounds
+ *
+ * These functions are invoked from getSubmenuBounds
+ * implementations in different order. They check if window
+ * of size windowSize fits to the specified edge of
+ * rectangle itemBounds on the screen of screenSize.
+ * Return rectangle that occupies the window if it fits or null.
+ *
+ ************************************************/
+
+ /**
+ * Checks if window fits below specified item
+ * returns rectangle that the window fits to or null.
+ * @param itemBounds rectangle of item in global coordinates
+ * @param windowSize size of submenu window to fit
+ * @param screenSize size of screen
+ */
+ Rectangle fitWindowBelow(Rectangle itemBounds, Dimension windowSize, Dimension screenSize) {
+ int width = windowSize.width;
+ int height = windowSize.height;
+ //Fix for 6267162: PIT: Popup Menu gets hidden below the screen when opened
+ //near the periphery of the screen, XToolkit
+ //Window should be moved if it's outside top-left screen bounds
+ int x = (itemBounds.x > 0) ? itemBounds.x : 0;
+ int y = (itemBounds.y + itemBounds.height > 0) ? itemBounds.y + itemBounds.height : 0;
+ if (y + height <= screenSize.height) {
+ //move it to the left if needed
+ if (width > screenSize.width) {
+ width = screenSize.width;
+ }
+ if (x + width > screenSize.width) {
+ x = screenSize.width - width;
+ }
+ return new Rectangle(x, y, width, height);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Checks if window fits above specified item
+ * returns rectangle that the window fits to or null.
+ * @param itemBounds rectangle of item in global coordinates
+ * @param windowSize size of submenu window to fit
+ * @param screenSize size of screen
+ */
+ Rectangle fitWindowAbove(Rectangle itemBounds, Dimension windowSize, Dimension screenSize) {
+ int width = windowSize.width;
+ int height = windowSize.height;
+ //Fix for 6267162: PIT: Popup Menu gets hidden below the screen when opened
+ //near the periphery of the screen, XToolkit
+ //Window should be moved if it's outside bottom-left screen bounds
+ int x = (itemBounds.x > 0) ? itemBounds.x : 0;
+ int y = (itemBounds.y > screenSize.height) ? screenSize.height - height : itemBounds.y - height;
+ if (y >= 0) {
+ //move it to the left if needed
+ if (width > screenSize.width) {
+ width = screenSize.width;
+ }
+ if (x + width > screenSize.width) {
+ x = screenSize.width - width;
+ }
+ return new Rectangle(x, y, width, height);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Checks if window fits to the right specified item
+ * returns rectangle that the window fits to or null.
+ * @param itemBounds rectangle of item in global coordinates
+ * @param windowSize size of submenu window to fit
+ * @param screenSize size of screen
+ */
+ Rectangle fitWindowRight(Rectangle itemBounds, Dimension windowSize, Dimension screenSize) {
+ int width = windowSize.width;
+ int height = windowSize.height;
+ //Fix for 6267162: PIT: Popup Menu gets hidden below the screen when opened
+ //near the periphery of the screen, XToolkit
+ //Window should be moved if it's outside top-left screen bounds
+ int x = (itemBounds.x + itemBounds.width > 0) ? itemBounds.x + itemBounds.width : 0;
+ int y = (itemBounds.y > 0) ? itemBounds.y : 0;
+ if (x + width <= screenSize.width) {
+ //move it to the top if needed
+ if (height > screenSize.height) {
+ height = screenSize.height;
+ }
+ if (y + height > screenSize.height) {
+ y = screenSize.height - height;
+ }
+ return new Rectangle(x, y, width, height);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Checks if window fits to the left specified item
+ * returns rectangle that the window fits to or null.
+ * @param itemBounds rectangle of item in global coordinates
+ * @param windowSize size of submenu window to fit
+ * @param screenSize size of screen
+ */
+ Rectangle fitWindowLeft(Rectangle itemBounds, Dimension windowSize, Dimension screenSize) {
+ int width = windowSize.width;
+ int height = windowSize.height;
+ //Fix for 6267162: PIT: Popup Menu gets hidden below the screen when opened
+ //near the periphery of the screen, XToolkit
+ //Window should be moved if it's outside top-right screen bounds
+ int x = (itemBounds.x < screenSize.width) ? itemBounds.x - width : screenSize.width - width;
+ int y = (itemBounds.y > 0) ? itemBounds.y : 0;
+ if (x >= 0) {
+ //move it to the top if needed
+ if (height > screenSize.height) {
+ height = screenSize.height;
+ }
+ if (y + height > screenSize.height) {
+ y = screenSize.height - height;
+ }
+ return new Rectangle(x, y, width, height);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * The last thing we can do with the window
+ * to fit it on screen - move it to the
+ * top-left edge and cut by screen dimensions
+ * @param windowSize size of submenu window to fit
+ * @param screenSize size of screen
+ */
+ Rectangle fitWindowToScreen(Dimension windowSize, Dimension screenSize) {
+ int width = (windowSize.width < screenSize.width) ? windowSize.width : screenSize.width;
+ int height = (windowSize.height < screenSize.height) ? windowSize.height : screenSize.height;
+ return new Rectangle(0, 0, width, height);
+ }
+
+
+ /************************************************
+ *
+ * Utility functions for manipulating colors
+ *
+ ************************************************/
+
+ /**
+ * This function is called before every painting.
+ * TODO:It would be better to add PropertyChangeListener
+ * to target component
+ * TODO:It would be better to access background color
+ * not invoking user-overridable function
+ */
+ void resetColors() {
+ replaceColors((target == null) ? SystemColor.window : target.getBackground());
+ }
+
+ /**
+ * Calculates colors of various elements given
+ * background color. Uses MotifColorUtilities
+ * @param backgroundColor the color of menu window's
+ * background.
+ */
+ void replaceColors(Color backgroundColor) {
+ if (backgroundColor != this.backgroundColor) {
+ this.backgroundColor = backgroundColor;
+
+ int red = backgroundColor.getRed();
+ int green = backgroundColor.getGreen();
+ int blue = backgroundColor.getBlue();
+
+ foregroundColor = new Color(MotifColorUtilities.calculateForegroundFromBackground(red,green,blue));
+ lightShadowColor = new Color(MotifColorUtilities.calculateTopShadowFromBackground(red,green,blue));
+ darkShadowColor = new Color(MotifColorUtilities.calculateBottomShadowFromBackground(red,green,blue));
+ selectedColor = new Color(MotifColorUtilities.calculateSelectFromBackground(red,green,blue));
+ disabledColor = (backgroundColor.equals(Color.BLACK)) ? foregroundColor.darker() : backgroundColor.darker();
+ }
+ }
+
+ Color getBackgroundColor() {
+ return backgroundColor;
+ }
+
+ Color getForegroundColor() {
+ return foregroundColor;
+ }
+
+ Color getLightShadowColor() {
+ return lightShadowColor;
+ }
+
+ Color getDarkShadowColor() {
+ return darkShadowColor;
+ }
+
+ Color getSelectedColor() {
+ return selectedColor;
+ }
+
+ Color getDisabledColor() {
+ return disabledColor;
+ }
+
+ /************************************************
+ *
+ * Painting utility functions
+ *
+ ************************************************/
+
+ /**
+ * Draws raised or sunken rectangle on specified graphics
+ * @param g the graphics on which to draw
+ * @param x the coordinate of left edge in coordinates of graphics
+ * @param y the coordinate of top edge in coordinates of graphics
+ * @param width the width of rectangle
+ * @param height the height of rectangle
+ * @param raised true to draw raised rectangle, false to draw sunken
+ */
+ void draw3DRect(Graphics g, int x, int y, int width, int height, boolean raised) {
+ if ((width <= 0) || (height <= 0)) {
+ return;
+ }
+ Color c = g.getColor();
+ g.setColor(raised ? getLightShadowColor() : getDarkShadowColor());
+ g.drawLine(x, y, x, y + height - 1);
+ g.drawLine(x + 1, y, x + width - 1, y);
+ g.setColor(raised ? getDarkShadowColor() : getLightShadowColor());
+ g.drawLine(x + 1, y + height - 1, x + width - 1, y + height - 1);
+ g.drawLine(x + width - 1, y + 1, x + width - 1, y + height - 1);
+ g.setColor(c);
+ }
+
+ /************************************************
+ *
+ * Overriden utility functions of XWindow
+ *
+ ************************************************/
+
+ /**
+ * Filters X events
+ */
+ protected boolean isEventDisabled(XEvent e) {
+ switch (e.get_type()) {
+ case XlibWrapper.Expose :
+ case XlibWrapper.GraphicsExpose :
+ case XlibWrapper.ButtonPress:
+ case XlibWrapper.ButtonRelease:
+ case XlibWrapper.MotionNotify:
+ case XlibWrapper.KeyPress:
+ case XlibWrapper.KeyRelease:
+ case XlibWrapper.DestroyNotify:
+ return super.isEventDisabled(e);
+ default:
+ return true;
+ }
+ }
+
+ /**
+ * Invokes disposal procedure on eventHandlerThread
+ */
+ public void dispose() {
+ setDisposed(true);
+ EventQueue.invokeLater(new Runnable() {
+ public void run() {
+ doDispose();
+ }
+ });
+ }
+
+ /**
+ * Performs disposal of menu window.
+ * Should be called only on eventHandlerThread
+ */
+ protected void doDispose() {
+ xSetVisible(false);
+ SurfaceData oldData = surfaceData;
+ surfaceData = null;
+ if (oldData != null) {
+ oldData.invalidate();
+ }
+ XToolkit.targetDisposedPeer(target, this);
+ destroy();
+ }
+
+ /**
+ * Invokes event processing on eventHandlerThread
+ * This function needs to be overriden since
+ * XBaseMenuWindow has no corresponding component
+ * so events can not be processed using standart means
+ */
+ void postEvent(final AWTEvent event) {
+ EventQueue.invokeLater(new Runnable() {
+ public void run() {
+ handleEvent(event);
+ }
+ });
+ }
+
+ /**
+ * The implementation of base window performs processing
+ * of paint events only. This behaviour is changed in
+ * descendants.
+ */
+ protected void handleEvent(AWTEvent event) {
+ switch(event.getID()) {
+ case PaintEvent.PAINT:
+ doHandleJavaPaintEvent((PaintEvent)event);
+ break;
+ }
+ }
+
+ /**
+ * Save location of pointer for further use
+ * then invoke superclass
+ */
+ public boolean grabInput() {
+ int rootX;
+ int rootY;
+ boolean res;
+ XToolkit.awtLock();
+ try {
+ long root = XlibWrapper.RootWindow(XToolkit.getDisplay(),
+ getScreenNumber());
+ res = XlibWrapper.XQueryPointer(XToolkit.getDisplay(), root,
+ XlibWrapper.larg1, //root
+ XlibWrapper.larg2, //child
+ XlibWrapper.larg3, //root_x
+ XlibWrapper.larg4, //root_y
+ XlibWrapper.larg5, //child_x
+ XlibWrapper.larg6, //child_y
+ XlibWrapper.larg7);//mask
+ rootX = Native.getInt(XlibWrapper.larg3);
+ rootY = Native.getInt(XlibWrapper.larg4);
+ res &= super.grabInput();
+ } finally {
+ XToolkit.awtUnlock();
+ }
+ if (res) {
+ //Mouse pointer is on the same display
+ this.grabInputPoint = new Point(rootX, rootY);
+ this.hasPointerMoved = false;
+ } else {
+ this.grabInputPoint = null;
+ this.hasPointerMoved = true;
+ }
+ return res;
+ }
+ /************************************************
+ *
+ * Overridable event processing functions
+ *
+ ************************************************/
+
+ /**
+ * Performs repainting
+ */
+ void doHandleJavaPaintEvent(PaintEvent event) {
+ Rectangle rect = event.getUpdateRect();
+ repaint(rect.x, rect.y, rect.width, rect.height);
+ }
+
+ /************************************************
+ *
+ * User input handling utility functions
+ *
+ ************************************************/
+
+ /**
+ * Performs handling of java mouse event
+ * Note that this function should be invoked
+ * only from root of menu window's hierarchy
+ * that grabs input focus
+ */
+ void doHandleJavaMouseEvent( MouseEvent mouseEvent ) {
+ if (!XToolkit.isLeftMouseButton(mouseEvent) && !XToolkit.isRightMouseButton(mouseEvent)) {
+ return;
+ }
+ //Window that owns input
+ XBaseWindow grabWindow = XAwtState.getGrabWindow();
+ //Point of mouse event in global coordinates
+ Point ptGlobal = mouseEvent.getLocationOnScreen();
+ if (!hasPointerMoved) {
+ //Fix for 6301307: NullPointerException while dispatching mouse events, XToolkit
+ if (grabInputPoint == null ||
+ (Math.abs(ptGlobal.x - grabInputPoint.x) > getMouseMovementSmudge()) ||
+ (Math.abs(ptGlobal.y - grabInputPoint.y) > getMouseMovementSmudge())) {
+ hasPointerMoved = true;
+ }
+ }
+ //Z-order first descendant of current menu window
+ //hierarchy that contain mouse point
+ XBaseMenuWindow wnd = getMenuWindowFromPoint(ptGlobal);
+ //Item in wnd that contains mouse point, if any
+ XMenuItemPeer item = (wnd != null) ? wnd.getItemFromPoint(wnd.toLocal(ptGlobal)) : null;
+ //Currently showing leaf window
+ XBaseMenuWindow cwnd = getShowingLeaf();
+ switch (mouseEvent.getID()) {
+ case MouseEvent.MOUSE_PRESSED:
+ //This line is to get rid of possible problems
+ //That may occur if mouse events are lost
+ showingMousePressedSubmenu = null;
+ if ((grabWindow == this) && (wnd == null)) {
+ //Menus grab input and the user
+ //presses mouse button outside
+ ungrabInput();
+ } else {
+ //Menus grab input OR mouse is pressed on menu window
+ grabInput();
+ if (item != null && !item.isSeparator() && item.isTargetItemEnabled()) {
+ //Button is pressed on enabled item
+ if (wnd.getShowingSubmenu() == item) {
+ //Button is pressed on item that shows
+ //submenu. We have to hide its submenu
+ //if user clicks on it
+ showingMousePressedSubmenu = (XMenuPeer)item;
+ }
+ wnd.selectItem(item, true);
+ } else {
+ //Button is pressed on disabled item or empty space
+ if (wnd != null) {
+ wnd.selectItem(null, false);
+ }
+ }
+ }
+ break;
+ case MouseEvent.MOUSE_RELEASED:
+ //Note that if item is not null, wnd has to be not null
+ if (item != null && !item.isSeparator() && item.isTargetItemEnabled()) {
+ if (item instanceof XMenuPeer) {
+ if (showingMousePressedSubmenu == item) {
+ //User clicks on item that shows submenu.
+ //Hide the submenu
+ if (wnd instanceof XMenuBarPeer) {
+ ungrabInput();
+ } else {
+ wnd.selectItem(item, false);
+ }
+ }
+ } else {
+ //Invoke action event
+ item.action(mouseEvent.getWhen());
+ ungrabInput();
+ }
+ } else {
+ //Mouse is released outside menu items
+ if (hasPointerMoved || (wnd instanceof XMenuBarPeer)) {
+ ungrabInput();
+ }
+ }
+ showingMousePressedSubmenu = null;
+ break;
+ case MouseEvent.MOUSE_DRAGGED:
+ if (wnd != null) {
+ //Mouse is dragged over menu window
+ //Move selection to item under cursor
+ if (item != null && !item.isSeparator() && item.isTargetItemEnabled()) {
+ if (grabWindow == this){
+ wnd.selectItem(item, true);
+ }
+ } else {
+ wnd.selectItem(null, false);
+ }
+ } else {
+ //Mouse is dragged outside menu windows
+ //clear selection in leaf to reflect it
+ if (cwnd != null) {
+ cwnd.selectItem(null, false);
+ }
+ }
+ break;
+ }
+ }
+
+ /**
+ * Performs handling of java keyboard event
+ * Note that this function should be invoked
+ * only from root of menu window's hierarchy
+ * that grabs input focus
+ */
+ void doHandleJavaKeyEvent(KeyEvent event) {
+ if (log.isLoggable(Level.FINER)) log.finer(event.toString());
+ if (event.getID() != KeyEvent.KEY_PRESSED) {
+ return;
+ }
+ final int keyCode = event.getKeyCode();
+ XBaseMenuWindow cwnd = getShowingLeaf();
+ XMenuItemPeer citem = cwnd.getSelectedItem();
+ switch(keyCode) {
+ case KeyEvent.VK_UP:
+ case KeyEvent.VK_KP_UP:
+ if (!(cwnd instanceof XMenuBarPeer)) {
+ //If active window is not menu bar,
+ //move selection up
+ cwnd.selectItem(cwnd.getPrevSelectableItem(), false);
+ }
+ break;
+ case KeyEvent.VK_DOWN:
+ case KeyEvent.VK_KP_DOWN:
+ if (cwnd instanceof XMenuBarPeer) {
+ //If active window is menu bar show current submenu
+ selectItem(getSelectedItem(), true);
+ } else {
+ //move selection down
+ cwnd.selectItem(cwnd.getNextSelectableItem(), false);
+ }
+ break;
+ case KeyEvent.VK_LEFT:
+ case KeyEvent.VK_KP_LEFT:
+ if (cwnd instanceof XMenuBarPeer) {
+ //leaf window is menu bar
+ //select previous item
+ selectItem(getPrevSelectableItem(), false);
+ } else if (cwnd.getParentMenuWindow() instanceof XMenuBarPeer) {
+ //leaf window is direct child of menu bar
+ //select previous item of menu bar
+ //and show its submenu
+ selectItem(getPrevSelectableItem(), true);
+ } else {
+ //hide leaf moving focus to its parent
+ //(equvivalent of pressing ESC)
+ XBaseMenuWindow pwnd = cwnd.getParentMenuWindow();
+ //Fix for 6272952: PIT: Pressing LEFT ARROW on a popup menu throws NullPointerException, XToolkit
+ if (pwnd != null) {
+ pwnd.selectItem(pwnd.getSelectedItem(), false);
+ }
+ }
+ break;
+ case KeyEvent.VK_RIGHT:
+ case KeyEvent.VK_KP_RIGHT:
+ if (cwnd instanceof XMenuBarPeer) {
+ //leaf window is menu bar
+ //select next item
+ selectItem(getNextSelectableItem(), false);
+ } else if (citem instanceof XMenuPeer) {
+ //current item is menu, show its window
+ //(equivalent of ENTER)
+ cwnd.selectItem(citem, true);
+ } else if (this instanceof XMenuBarPeer) {
+ //if this is menu bar (not popup menu)
+ //and the user presses RIGHT on item (not submenu)
+ //select next top-level menu
+ selectItem(getNextSelectableItem(), true);
+ }
+ break;
+ case KeyEvent.VK_SPACE:
+ case KeyEvent.VK_ENTER:
+ //If the current item has submenu show it
+ //Perform action otherwise
+ if (citem instanceof XMenuPeer) {
+ cwnd.selectItem(citem, true);
+ } else if (citem != null) {
+ citem.action(event.getWhen());
+ ungrabInput();
+ }
+ break;
+ case KeyEvent.VK_ESCAPE:
+ //If current window is menu bar or its child - close it
+ //If current window is popup menu - close it
+ //go one level up otherwise
+
+ //Fixed 6266513: Incorrect key handling in XAWT popup menu
+ //Popup menu should be closed on 'ESC'
+ if ((cwnd instanceof XMenuBarPeer) || (cwnd.getParentMenuWindow() instanceof XMenuBarPeer)) {
+ ungrabInput();
+ } else if (cwnd instanceof XPopupMenuPeer) {
+ ungrabInput();
+ } else {
+ XBaseMenuWindow pwnd = cwnd.getParentMenuWindow();
+ pwnd.selectItem(pwnd.getSelectedItem(), false);
+ }
+ break;
+ case KeyEvent.VK_F10:
+ //Fixed 6266513: Incorrect key handling in XAWT popup menu
+ //All menus should be closed on 'F10'
+ ungrabInput();
+ break;
+ default:
+ break;
+ }
+ }
+
+} //class XBaseMenuWindow