Path: blob/master/src/java.desktop/unix/classes/sun/awt/X11/XBaseMenuWindow.java
41159 views
/*1* Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24package sun.awt.X11;2526import java.awt.*;27import java.awt.event.*;2829import sun.awt.*;3031import java.awt.peer.ComponentPeer;32import java.util.ArrayList;33import java.util.Vector;34import sun.util.logging.PlatformLogger;35import sun.java2d.SurfaceData;3637/**38* The abstract class XBaseMenuWindow is the superclass39* of all menu windows.40*/41public abstract class XBaseMenuWindow extends XWindow {4243/************************************************44*45* Data members46*47************************************************/4849private static PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XBaseMenuWindow");5051/*52* Colors are calculated using MotifColorUtilities class53* from backgroundColor and are contained in these vars.54*/55private Color backgroundColor;56private Color foregroundColor;57private Color lightShadowColor;58private Color darkShadowColor;59private Color selectedColor;60private Color disabledColor;6162/**63* Array of items.64*/65private ArrayList<XMenuItemPeer> items;6667/**68* Index of selected item in array of items69*/70private int selectedIndex = -1;7172/**73* Specifies currently showing submenu.74*/75private XMenuPeer showingSubmenu = null;7677/**78* Static synchronizational object.79* Following operations should be synchronized80* using this object:81* 1. Access to items vector82* 2. Access to selection83* 3. Access to showing menu window member84*85* This is lowest level lock,86* no other locks should be taken when87* thread own this lock.88*/89private static Object menuTreeLock = new Object();9091/************************************************92*93* Event processing94*95************************************************/9697/**98* If mouse button is clicked on item showing submenu99* we have to hide its submenu.100* And if mouse button is pressed on such item and101* dragged to another, getShowingSubmenu() is changed.102* So this member saves the item that the user103* presses mouse button on _only_ if it's showing submenu.104*/105private XMenuPeer showingMousePressedSubmenu = null;106107/**108* If the PopupMenu is invoked as a result of right button click109* first mouse event after grabInput would be MouseReleased.110* We need to check if the user has moved mouse after input grab.111* If yes - hide the PopupMenu. If no - do nothing112*/113protected Point grabInputPoint = null;114protected boolean hasPointerMoved = false;115116private AppContext disposeAppContext;117118/************************************************119*120* Mapping data121*122************************************************/123124/**125* Mapping data that is filled in getMappedItems function126* and reset in resetSize function. It contains array of127* items in order that they appear on screen and may contain128* additional data defined by descendants.129*/130private MappingData mappingData;131132static class MappingData implements Cloneable {133134/**135* Array of item in order that they appear on screen136*/137private XMenuItemPeer[] items;138139/**140* Constructs MappingData object with list141* of menu items142*/143MappingData(XMenuItemPeer[] items) {144this.items = items;145}146147/**148* Constructs MappingData without items149* This constructor should be used in case of errors150*/151MappingData() {152this.items = new XMenuItemPeer[0];153}154155public Object clone() {156try {157return super.clone();158} catch (CloneNotSupportedException ex) {159throw new InternalError(ex);160}161}162163public XMenuItemPeer[] getItems() {164return this.items;165}166}167168/************************************************169*170* Construction171*172************************************************/173XBaseMenuWindow() {174super(new XCreateWindowParams(new Object[] {175DELAYED, Boolean.TRUE}));176177disposeAppContext = AppContext.getAppContext();178}179180/************************************************181*182* Abstract methods183*184************************************************/185186/**187* Returns parent menu window (not the X-hierarchy parent window)188*/189protected abstract XBaseMenuWindow getParentMenuWindow();190191/**192* Performs mapping of items in window.193* This function creates and fills specific194* descendant of MappingData195* and sets mapping coordinates of items196* This function should return default menu data197* if errors occur198*/199protected abstract MappingData map();200201/**202* Calculates placement of submenu window203* given bounds of item with submenu and204* size of submenu window. Returns suggested205* rectangle for submenu window in global coordinates206* @param itemBounds the bounding rectangle of item207* in local coordinates208* @param windowSize the desired size of submenu's window209*/210protected abstract Rectangle getSubmenuBounds(Rectangle itemBounds, Dimension windowSize);211212213/**214* This function is to be called if it's likely that size215* of items was changed. It can be called from any thread216* in any locked state, so it should not take locks217*/218protected abstract void updateSize();219220/************************************************221*222* Initialization223*224************************************************/225226/**227* Overrides XBaseWindow.instantPreInit228*/229void instantPreInit(XCreateWindowParams params) {230super.instantPreInit(params);231items = new ArrayList<>();232}233234/************************************************235*236* General-purpose functions237*238************************************************/239240/**241* Returns static lock used for menus242*/243static Object getMenuTreeLock() {244return menuTreeLock;245}246247/**248* This function is called to clear all saved249* size data.250*/251protected void resetMapping() {252mappingData = null;253}254255/**256* Invokes repaint procedure on eventHandlerThread257*/258void postPaintEvent() {259if (isShowing()) {260PaintEvent pe = new PaintEvent(target, PaintEvent.PAINT,261new Rectangle(0, 0, width, height));262postEvent(pe);263}264}265266/************************************************267*268* Utility functions for manipulating items269*270************************************************/271272/**273* Thread-safely returns item at specified index274* @param index the position of the item to be returned.275*/276XMenuItemPeer getItem(int index) {277if (index >= 0) {278synchronized(getMenuTreeLock()) {279if (items.size() > index) {280return items.get(index);281}282}283}284return null;285}286287/**288* Thread-safely creates a copy of the items vector289*/290XMenuItemPeer[] copyItems() {291synchronized(getMenuTreeLock()) {292return items.toArray(new XMenuItemPeer[] {});293}294}295296297/**298* Thread-safely returns selected item299*/300XMenuItemPeer getSelectedItem() {301synchronized(getMenuTreeLock()) {302if (selectedIndex >= 0) {303if (items.size() > selectedIndex) {304return items.get(selectedIndex);305}306}307return null;308}309}310311/**312* Returns showing submenu, if any313*/314XMenuPeer getShowingSubmenu() {315synchronized(getMenuTreeLock()) {316return showingSubmenu;317}318}319320/**321* Adds item to end of items vector.322* Note that this function does not perform323* check for adding duplicate items324* @param item item to add325*/326public void addItem(MenuItem item) {327XMenuItemPeer mp = AWTAccessor.getMenuComponentAccessor().getPeer(item);328if (mp != null) {329mp.setContainer(this);330synchronized(getMenuTreeLock()) {331items.add(mp);332}333} else {334if (log.isLoggable(PlatformLogger.Level.FINE)) {335log.fine("WARNING: Attempt to add menu item without a peer");336}337}338updateSize();339}340341/**342* Removes item at the specified index from items vector.343* @param index the position of the item to be removed344*/345public void delItem(int index) {346synchronized(getMenuTreeLock()) {347if (selectedIndex == index) {348selectItem(null, false);349} else if (selectedIndex > index) {350selectedIndex--;351}352if (index < items.size()) {353items.remove(index);354} else {355if (log.isLoggable(PlatformLogger.Level.FINE)) {356log.fine("WARNING: Attempt to remove non-existing menu item, index : " + index + ", item count : " + items.size());357}358}359}360updateSize();361}362363/**364* Clears items vector and loads specified vector365* @param items vector to be loaded366*/367public void reloadItems(Vector<? extends MenuItem> items) {368synchronized(getMenuTreeLock()) {369this.items.clear();370MenuItem[] itemArray = items.toArray(new MenuItem[] {});371int itemCnt = itemArray.length;372for(int i = 0; i < itemCnt; i++) {373addItem(itemArray[i]);374}375}376}377378/**379* Select specified item and shows/hides submenus if necessary380* We can not select by index, so we need to select by ref.381* @param item the item to be selected, null to clear selection382* @param showWindowIfMenu if the item is XMenuPeer then its383* window is shown/hidden according to this param.384*/385void selectItem(XMenuItemPeer item, boolean showWindowIfMenu) {386synchronized(getMenuTreeLock()) {387XMenuPeer showingSubmenu = getShowingSubmenu();388int newSelectedIndex = (item != null) ? items.indexOf(item) : -1;389if (this.selectedIndex != newSelectedIndex) {390if (log.isLoggable(PlatformLogger.Level.FINEST)) {391log.finest("Selected index changed, was : " + this.selectedIndex + ", new : " + newSelectedIndex);392}393this.selectedIndex = newSelectedIndex;394postPaintEvent();395}396final XMenuPeer submenuToShow = (showWindowIfMenu && (item instanceof XMenuPeer)) ? (XMenuPeer)item : null;397if (submenuToShow != showingSubmenu) {398XToolkit.executeOnEventHandlerThread(target, new Runnable() {399public void run() {400doShowSubmenu(submenuToShow);401}402});403}404}405}406407/**408* Performs hiding of currently showing submenu409* and showing of submenuToShow.410* This function should be executed on eventHandlerThread411* @param submenuToShow submenu to be shown or null412* to hide currently showing submenu413*/414private void doShowSubmenu(XMenuPeer submenuToShow) {415XMenuWindow menuWindowToShow = (submenuToShow != null) ? submenuToShow.getMenuWindow() : null;416Dimension dim = null;417Rectangle bounds = null;418//ensureCreated can invoke XWindowPeer.init() ->419//XWindowPeer.initGraphicsConfiguration() ->420//Window.getGraphicsConfiguration()421//that tries to obtain Component.AWTTreeLock.422//So it should be called outside awtLock()423if (menuWindowToShow != null) {424menuWindowToShow.ensureCreated();425}426XToolkit.awtLock();427try {428synchronized(getMenuTreeLock()) {429if (showingSubmenu != submenuToShow) {430if (log.isLoggable(PlatformLogger.Level.FINEST)) {431log.finest("Changing showing submenu");432}433if (showingSubmenu != null) {434XMenuWindow showingSubmenuWindow = showingSubmenu.getMenuWindow();435if (showingSubmenuWindow != null) {436showingSubmenuWindow.hide();437}438}439if (submenuToShow != null) {440dim = menuWindowToShow.getDesiredSize();441bounds = menuWindowToShow.getParentMenuWindow().getSubmenuBounds(submenuToShow.getBounds(), dim);442menuWindowToShow.show(bounds);443}444showingSubmenu = submenuToShow;445}446}447} finally {448XToolkit.awtUnlock();449}450}451452final void setItemsFont( Font font ) {453XMenuItemPeer[] items = copyItems();454int itemCnt = items.length;455for (int i = 0; i < itemCnt; i++) {456items[i].setFont(font);457}458}459460/************************************************461*462* Utility functions for manipulating mapped items463*464************************************************/465466/**467* Returns array of mapped items, null if error468* This function has to be not synchronized469* and we have to guarantee that we return470* some MappingData to user. It's OK if471* this.mappingData is replaced meanwhile472*/473MappingData getMappingData() {474MappingData mappingData = this.mappingData;475if (mappingData == null) {476mappingData = map();477this.mappingData = mappingData;478}479return (MappingData)mappingData.clone();480}481482/**483* returns item thats mapped coordinates contain484* specified point, null of none.485* @param pt the point in this window's coordinate system486*/487XMenuItemPeer getItemFromPoint(Point pt) {488XMenuItemPeer[] items = getMappingData().getItems();489int cnt = items.length;490for (int i = 0; i < cnt; i++) {491if (items[i].getBounds().contains(pt)) {492return items[i];493}494}495return null;496}497498/**499* Returns first item after currently selected500* item that can be selected according to mapping array.501* (no separators and no disabled items).502* Currently selected item if it's only selectable,503* null if no item can be selected504*/505XMenuItemPeer getNextSelectableItem() {506XMenuItemPeer[] mappedItems = getMappingData().getItems();507XMenuItemPeer selectedItem = getSelectedItem();508int cnt = mappedItems.length;509//Find index of selected item510int selIdx = -1;511for (int i = 0; i < cnt; i++) {512if (mappedItems[i] == selectedItem) {513selIdx = i;514break;515}516}517int idx = (selIdx == cnt - 1) ? 0 : selIdx + 1;518//cycle through mappedItems to find selectable item519//beginning from the next item and moving to the520//beginning of array when end is reached.521//Cycle is finished on selected item itself522for (int i = 0; i < cnt; i++) {523XMenuItemPeer item = mappedItems[idx];524if (!item.isSeparator() && item.isTargetItemEnabled()) {525return item;526}527idx++;528if (idx >= cnt) {529idx = 0;530}531}532//return null if no selectable item was found533return null;534}535536/**537* Returns first item before currently selected538* see getNextSelectableItem() for comments539*/540XMenuItemPeer getPrevSelectableItem() {541XMenuItemPeer[] mappedItems = getMappingData().getItems();542XMenuItemPeer selectedItem = getSelectedItem();543int cnt = mappedItems.length;544//Find index of selected item545int selIdx = -1;546for (int i = 0; i < cnt; i++) {547if (mappedItems[i] == selectedItem) {548selIdx = i;549break;550}551}552int idx = (selIdx <= 0) ? cnt - 1 : selIdx - 1;553//cycle through mappedItems to find selectable item554for (int i = 0; i < cnt; i++) {555XMenuItemPeer item = mappedItems[idx];556if (!item.isSeparator() && item.isTargetItemEnabled()) {557return item;558}559idx--;560if (idx < 0) {561idx = cnt - 1;562}563}564//return null if no selectable item was found565return null;566}567568/**569* Returns first selectable item570* This function is intended for clearing selection571*/572XMenuItemPeer getFirstSelectableItem() {573XMenuItemPeer[] mappedItems = getMappingData().getItems();574int cnt = mappedItems.length;575for (int i = 0; i < cnt; i++) {576XMenuItemPeer item = mappedItems[i];577if (!item.isSeparator() && item.isTargetItemEnabled()) {578return item;579}580}581582return null;583}584585/************************************************586*587* Utility functions for manipulating588* hierarchy of windows589*590************************************************/591592/**593* returns leaf menu window or594* this if no children are showing595*/596XBaseMenuWindow getShowingLeaf() {597synchronized(getMenuTreeLock()) {598XBaseMenuWindow leaf = this;599XMenuPeer leafchild = leaf.getShowingSubmenu();600while (leafchild != null) {601leaf = leafchild.getMenuWindow();602leafchild = leaf.getShowingSubmenu();603}604return leaf;605}606}607608/**609* returns root menu window610* or this if this window is topmost611*/612XBaseMenuWindow getRootMenuWindow() {613synchronized(getMenuTreeLock()) {614XBaseMenuWindow t = this;615XBaseMenuWindow tparent = t.getParentMenuWindow();616while (tparent != null) {617t = tparent;618tparent = t.getParentMenuWindow();619}620return t;621}622}623624/**625* Returns window that contains pt.626* search is started from leaf window627* to return first window in Z-order628* @param pt point in global coordinates629*/630XBaseMenuWindow getMenuWindowFromPoint(Point pt) {631synchronized(getMenuTreeLock()) {632XBaseMenuWindow t = getShowingLeaf();633while (t != null) {634Rectangle r = new Rectangle(t.toGlobal(new Point(0, 0)), t.getSize());635if (r.contains(pt)) {636return t;637}638t = t.getParentMenuWindow();639}640return null;641}642}643644/************************************************645*646* Primitives for getSubmenuBounds647*648* These functions are invoked from getSubmenuBounds649* implementations in different order. They check if window650* of size windowSize fits to the specified edge of651* rectangle itemBounds on the screen of screenSize.652* Return rectangle that occupies the window if it fits or null.653*654************************************************/655656GraphicsConfiguration getCurrentGraphicsConfiguration() {657Component hw = SunToolkit.getHeavyweightComponent(target);658XWindow peer = AWTAccessor.getComponentAccessor().getPeer(hw);659if (peer != null && peer.graphicsConfig != null) {660return peer.graphicsConfig;661}662return graphicsConfig;663}664665/**666* Checks if window fits below specified item667* returns rectangle that the window fits to or null.668* @param itemBounds rectangle of item in global coordinates669* @param windowSize size of submenu window to fit670* @param screenBounds size of screen671*/672Rectangle fitWindowBelow(Rectangle itemBounds, Dimension windowSize, Rectangle screenBounds) {673int width = windowSize.width;674int height = windowSize.height;675//Fix for 6267162: PIT: Popup Menu gets hidden below the screen when opened676//near the periphery of the screen, XToolkit677//Window should be moved if it's outside top-left screen bounds678int x = (itemBounds.x > screenBounds.x) ? itemBounds.x : screenBounds.x;679int y = (itemBounds.y + itemBounds.height > screenBounds.y) ? itemBounds.y + itemBounds.height : screenBounds.y;680if (y + height <= screenBounds.y + screenBounds.height) {681//move it to the left if needed682if (width > screenBounds.width) {683width = screenBounds.width;684}685if (x + width > screenBounds.x + screenBounds.width) {686x = screenBounds.x + screenBounds.width - width;687}688return new Rectangle(x, y, width, height);689} else {690return null;691}692}693694/**695* Checks if window fits above specified item696* returns rectangle that the window fits to or null.697* @param itemBounds rectangle of item in global coordinates698* @param windowSize size of submenu window to fit699* @param screenBounds size of screen700*/701Rectangle fitWindowAbove(Rectangle itemBounds, Dimension windowSize, Rectangle screenBounds) {702int width = windowSize.width;703int height = windowSize.height;704//Fix for 6267162: PIT: Popup Menu gets hidden below the screen when opened705//near the periphery of the screen, XToolkit706//Window should be moved if it's outside bottom-left screen bounds707int x = (itemBounds.x > screenBounds.x) ? itemBounds.x : screenBounds.x;708int y = (itemBounds.y > screenBounds.y + screenBounds.height) ? screenBounds.y + screenBounds.height - height : itemBounds.y - height;709if (y >= screenBounds.y) {710//move it to the left if needed711if (width > screenBounds.width) {712width = screenBounds.width;713}714if (x + width > screenBounds.x + screenBounds.width) {715x = screenBounds.x + screenBounds.width - width;716}717return new Rectangle(x, y, width, height);718} else {719return null;720}721}722723/**724* Checks if window fits to the right specified item725* returns rectangle that the window fits to or null.726* @param itemBounds rectangle of item in global coordinates727* @param windowSize size of submenu window to fit728* @param screenBounds size of screen729*/730Rectangle fitWindowRight(Rectangle itemBounds, Dimension windowSize, Rectangle screenBounds) {731int width = windowSize.width;732int height = windowSize.height;733//Fix for 6267162: PIT: Popup Menu gets hidden below the screen when opened734//near the periphery of the screen, XToolkit735//Window should be moved if it's outside top-left screen bounds736int x = (itemBounds.x + itemBounds.width > screenBounds.x) ? itemBounds.x + itemBounds.width : screenBounds.x;737int y = (itemBounds.y > screenBounds.y) ? itemBounds.y : screenBounds.y;738if (x + width <= screenBounds.x + screenBounds.width) {739//move it to the top if needed740if (height > screenBounds.height) {741height = screenBounds.height;742}743if (y + height > screenBounds.y + screenBounds.height) {744y = screenBounds.y + screenBounds.height - height;745}746return new Rectangle(x, y, width, height);747} else {748return null;749}750}751752/**753* Checks if window fits to the left specified item754* returns rectangle that the window fits to or null.755* @param itemBounds rectangle of item in global coordinates756* @param windowSize size of submenu window to fit757* @param screenBounds size of screen758*/759Rectangle fitWindowLeft(Rectangle itemBounds, Dimension windowSize, Rectangle screenBounds) {760int width = windowSize.width;761int height = windowSize.height;762//Fix for 6267162: PIT: Popup Menu gets hidden below the screen when opened763//near the periphery of the screen, XToolkit764//Window should be moved if it's outside top-right screen bounds765int x = (itemBounds.x < screenBounds.x + screenBounds.width) ? itemBounds.x - width : screenBounds.x + screenBounds.width - width;766int y = (itemBounds.y > screenBounds.y) ? itemBounds.y : screenBounds.y;767if (x >= screenBounds.x) {768//move it to the top if needed769if (height > screenBounds.height) {770height = screenBounds.height;771}772if (y + height > screenBounds.y + screenBounds.height) {773y = screenBounds.y + screenBounds.height - height;774}775return new Rectangle(x, y, width, height);776} else {777return null;778}779}780781/**782* The last thing we can do with the window783* to fit it on screen - move it to the784* top-left edge and cut by screen dimensions785* @param windowSize size of submenu window to fit786* @param screenBounds size of screen787*/788Rectangle fitWindowToScreen(Dimension windowSize, Rectangle screenBounds) {789int width = (windowSize.width < screenBounds.width) ? windowSize.width : screenBounds.width;790int height = (windowSize.height < screenBounds.height) ? windowSize.height : screenBounds.height;791return new Rectangle(screenBounds.x, screenBounds.y, width, height);792}793794795/************************************************796*797* Utility functions for manipulating colors798*799************************************************/800801/**802* This function is called before every painting.803* TODO:It would be better to add PropertyChangeListener804* to target component805* TODO:It would be better to access background color806* not invoking user-overridable function807*/808void resetColors() {809replaceColors((target == null) ? SystemColor.window : target.getBackground());810}811812/**813* Calculates colors of various elements given814* background color. Uses MotifColorUtilities815* @param backgroundColor the color of menu window's816* background.817*/818void replaceColors(Color backgroundColor) {819if (backgroundColor != this.backgroundColor) {820this.backgroundColor = backgroundColor;821822int red = backgroundColor.getRed();823int green = backgroundColor.getGreen();824int blue = backgroundColor.getBlue();825826foregroundColor = new Color(MotifColorUtilities.calculateForegroundFromBackground(red,green,blue));827lightShadowColor = new Color(MotifColorUtilities.calculateTopShadowFromBackground(red,green,blue));828darkShadowColor = new Color(MotifColorUtilities.calculateBottomShadowFromBackground(red,green,blue));829selectedColor = new Color(MotifColorUtilities.calculateSelectFromBackground(red,green,blue));830disabledColor = (backgroundColor.equals(Color.BLACK)) ? foregroundColor.darker() : backgroundColor.darker();831}832}833834Color getBackgroundColor() {835return backgroundColor;836}837838Color getForegroundColor() {839return foregroundColor;840}841842Color getLightShadowColor() {843return lightShadowColor;844}845846Color getDarkShadowColor() {847return darkShadowColor;848}849850Color getSelectedColor() {851return selectedColor;852}853854Color getDisabledColor() {855return disabledColor;856}857858/************************************************859*860* Painting utility functions861*862************************************************/863864/**865* Draws raised or sunken rectangle on specified graphics866* @param g the graphics on which to draw867* @param x the coordinate of left edge in coordinates of graphics868* @param y the coordinate of top edge in coordinates of graphics869* @param width the width of rectangle870* @param height the height of rectangle871* @param raised true to draw raised rectangle, false to draw sunken872*/873void draw3DRect(Graphics g, int x, int y, int width, int height, boolean raised) {874if ((width <= 0) || (height <= 0)) {875return;876}877Color c = g.getColor();878g.setColor(raised ? getLightShadowColor() : getDarkShadowColor());879g.drawLine(x, y, x, y + height - 1);880g.drawLine(x + 1, y, x + width - 1, y);881g.setColor(raised ? getDarkShadowColor() : getLightShadowColor());882g.drawLine(x + 1, y + height - 1, x + width - 1, y + height - 1);883g.drawLine(x + width - 1, y + 1, x + width - 1, y + height - 1);884g.setColor(c);885}886887/************************************************888*889* Overriden utility functions of XWindow890*891************************************************/892893/**894* Filters X events895*/896protected boolean isEventDisabled(XEvent e) {897switch (e.get_type()) {898case XConstants.Expose :899case XConstants.GraphicsExpose :900case XConstants.ButtonPress:901case XConstants.ButtonRelease:902case XConstants.MotionNotify:903case XConstants.KeyPress:904case XConstants.KeyRelease:905case XConstants.DestroyNotify:906return super.isEventDisabled(e);907default:908return true;909}910}911912/**913* Invokes disposal procedure on eventHandlerThread914*/915public void dispose() {916setDisposed(true);917918SunToolkit.invokeLaterOnAppContext(disposeAppContext, new Runnable() {919public void run() {920doDispose();921}922});923}924925/**926* Performs disposal of menu window.927* Should be called only on eventHandlerThread928*/929protected void doDispose() {930xSetVisible(false);931SurfaceData oldData = surfaceData;932surfaceData = null;933if (oldData != null) {934oldData.invalidate();935}936destroy();937}938939/**940* Invokes event processing on eventHandlerThread941* This function needs to be overriden since942* XBaseMenuWindow has no corresponding component943* so events can not be processed using standart means944*/945void postEvent(final AWTEvent event) {946InvocationEvent ev = new InvocationEvent(event.getSource(), new Runnable() {947public void run() {948handleEvent(event);949}950});951super.postEvent(ev);952}953954/**955* The implementation of base window performs processing956* of paint events only. This behaviour is changed in957* descendants.958*/959protected void handleEvent(AWTEvent event) {960switch(event.getID()) {961case PaintEvent.PAINT:962doHandleJavaPaintEvent((PaintEvent)event);963break;964}965}966967/**968* Save location of pointer for further use969* then invoke superclass970*/971public boolean grabInput() {972int rootX;973int rootY;974boolean res;975XToolkit.awtLock();976try {977long root = XlibWrapper.RootWindow(XToolkit.getDisplay(),978getScreenNumber());979res = XlibWrapper.XQueryPointer(XToolkit.getDisplay(), root,980XlibWrapper.larg1, //root981XlibWrapper.larg2, //child982XlibWrapper.larg3, //root_x983XlibWrapper.larg4, //root_y984XlibWrapper.larg5, //child_x985XlibWrapper.larg6, //child_y986XlibWrapper.larg7);//mask987rootX = Native.getInt(XlibWrapper.larg3);988rootY = Native.getInt(XlibWrapper.larg4);989res &= super.grabInput();990} finally {991XToolkit.awtUnlock();992}993if (res) {994//Mouse pointer is on the same display995this.grabInputPoint = new Point(rootX, rootY);996this.hasPointerMoved = false;997} else {998this.grabInputPoint = null;999this.hasPointerMoved = true;1000}1001return res;1002}1003/************************************************1004*1005* Overridable event processing functions1006*1007************************************************/10081009/**1010* Performs repainting1011*/1012void doHandleJavaPaintEvent(PaintEvent event) {1013Rectangle rect = event.getUpdateRect();1014repaint(rect.x, rect.y, rect.width, rect.height);1015}10161017/************************************************1018*1019* User input handling utility functions1020*1021************************************************/10221023/**1024* Performs handling of java mouse event1025* Note that this function should be invoked1026* only from root of menu window's hierarchy1027* that grabs input focus1028*/1029void doHandleJavaMouseEvent( MouseEvent mouseEvent ) {1030if (!XToolkit.isLeftMouseButton(mouseEvent) && !XToolkit.isRightMouseButton(mouseEvent)) {1031return;1032}1033//Window that owns input1034XBaseWindow grabWindow = XAwtState.getGrabWindow();1035//Point of mouse event in global coordinates1036Point ptGlobal = mouseEvent.getLocationOnScreen();1037if (!hasPointerMoved) {1038//Fix for 6301307: NullPointerException while dispatching mouse events, XToolkit1039if (grabInputPoint == null ||1040(Math.abs(ptGlobal.x - grabInputPoint.x) > getMouseMovementSmudge()) ||1041(Math.abs(ptGlobal.y - grabInputPoint.y) > getMouseMovementSmudge())) {1042hasPointerMoved = true;1043}1044}1045//Z-order first descendant of current menu window1046//hierarchy that contain mouse point1047XBaseMenuWindow wnd = getMenuWindowFromPoint(ptGlobal);1048//Item in wnd that contains mouse point, if any1049XMenuItemPeer item = (wnd != null) ? wnd.getItemFromPoint(wnd.toLocal(ptGlobal)) : null;1050//Currently showing leaf window1051XBaseMenuWindow cwnd = getShowingLeaf();1052switch (mouseEvent.getID()) {1053case MouseEvent.MOUSE_PRESSED:1054//This line is to get rid of possible problems1055//That may occur if mouse events are lost1056showingMousePressedSubmenu = null;1057if ((grabWindow == this) && (wnd == null)) {1058//Menus grab input and the user1059//presses mouse button outside1060ungrabInput();1061} else {1062//Menus grab input OR mouse is pressed on menu window1063grabInput();1064if (item != null && !item.isSeparator() && item.isTargetItemEnabled()) {1065//Button is pressed on enabled item1066if (wnd.getShowingSubmenu() == item) {1067//Button is pressed on item that shows1068//submenu. We have to hide its submenu1069//if user clicks on it1070showingMousePressedSubmenu = (XMenuPeer)item;1071}1072wnd.selectItem(item, true);1073} else {1074//Button is pressed on disabled item or empty space1075if (wnd != null) {1076wnd.selectItem(null, false);1077}1078}1079}1080break;1081case MouseEvent.MOUSE_RELEASED:1082//Note that if item is not null, wnd has to be not null1083if (item != null && !item.isSeparator() && item.isTargetItemEnabled()) {1084if (item instanceof XMenuPeer) {1085if (showingMousePressedSubmenu == item) {1086//User clicks on item that shows submenu.1087//Hide the submenu1088if (wnd instanceof XMenuBarPeer) {1089ungrabInput();1090} else {1091wnd.selectItem(item, false);1092}1093}1094} else {1095//Invoke action event1096@SuppressWarnings("deprecation")1097final int modifiers = mouseEvent.getModifiers();1098item.action(mouseEvent.getWhen(), modifiers);1099ungrabInput();1100}1101} else {1102//Mouse is released outside menu items1103if (hasPointerMoved || (wnd instanceof XMenuBarPeer)) {1104ungrabInput();1105}1106}1107showingMousePressedSubmenu = null;1108break;1109case MouseEvent.MOUSE_DRAGGED:1110if (wnd != null) {1111//Mouse is dragged over menu window1112//Move selection to item under cursor1113if (item != null && !item.isSeparator() && item.isTargetItemEnabled()) {1114if (grabWindow == this){1115wnd.selectItem(item, true);1116}1117} else {1118wnd.selectItem(null, false);1119}1120} else {1121//Mouse is dragged outside menu windows1122//clear selection in leaf to reflect it1123if (cwnd != null) {1124cwnd.selectItem(null, false);1125}1126}1127break;1128}1129}11301131/**1132* Performs handling of java keyboard event1133* Note that this function should be invoked1134* only from root of menu window's hierarchy1135* that grabs input focus1136*/1137void doHandleJavaKeyEvent(KeyEvent event) {1138if (log.isLoggable(PlatformLogger.Level.FINER)) {1139log.finer(event.toString());1140}1141if (event.getID() != KeyEvent.KEY_PRESSED) {1142return;1143}1144final int keyCode = event.getKeyCode();1145XBaseMenuWindow cwnd = getShowingLeaf();1146XMenuItemPeer citem = cwnd.getSelectedItem();1147switch(keyCode) {1148case KeyEvent.VK_UP:1149case KeyEvent.VK_KP_UP:1150if (!(cwnd instanceof XMenuBarPeer)) {1151//If active window is not menu bar,1152//move selection up1153cwnd.selectItem(cwnd.getPrevSelectableItem(), false);1154}1155break;1156case KeyEvent.VK_DOWN:1157case KeyEvent.VK_KP_DOWN:1158if (cwnd instanceof XMenuBarPeer) {1159//If active window is menu bar show current submenu1160selectItem(getSelectedItem(), true);1161} else {1162//move selection down1163cwnd.selectItem(cwnd.getNextSelectableItem(), false);1164}1165break;1166case KeyEvent.VK_LEFT:1167case KeyEvent.VK_KP_LEFT:1168if (cwnd instanceof XMenuBarPeer) {1169//leaf window is menu bar1170//select previous item1171selectItem(getPrevSelectableItem(), false);1172} else if (cwnd.getParentMenuWindow() instanceof XMenuBarPeer) {1173//leaf window is direct child of menu bar1174//select previous item of menu bar1175//and show its submenu1176selectItem(getPrevSelectableItem(), true);1177} else {1178//hide leaf moving focus to its parent1179//(equvivalent of pressing ESC)1180XBaseMenuWindow pwnd = cwnd.getParentMenuWindow();1181//Fix for 6272952: PIT: Pressing LEFT ARROW on a popup menu throws NullPointerException, XToolkit1182if (pwnd != null) {1183pwnd.selectItem(pwnd.getSelectedItem(), false);1184}1185}1186break;1187case KeyEvent.VK_RIGHT:1188case KeyEvent.VK_KP_RIGHT:1189if (cwnd instanceof XMenuBarPeer) {1190//leaf window is menu bar1191//select next item1192selectItem(getNextSelectableItem(), false);1193} else if (citem instanceof XMenuPeer) {1194//current item is menu, show its window1195//(equivalent of ENTER)1196cwnd.selectItem(citem, true);1197} else if (this instanceof XMenuBarPeer) {1198//if this is menu bar (not popup menu)1199//and the user presses RIGHT on item (not submenu)1200//select next top-level menu1201selectItem(getNextSelectableItem(), true);1202}1203break;1204case KeyEvent.VK_SPACE:1205case KeyEvent.VK_ENTER:1206//If the current item has submenu show it1207//Perform action otherwise1208if (citem instanceof XMenuPeer) {1209cwnd.selectItem(citem, true);1210} else if (citem != null) {1211@SuppressWarnings("deprecation")1212final int modifiers = event.getModifiers();1213citem.action(event.getWhen(), modifiers);1214ungrabInput();1215}1216break;1217case KeyEvent.VK_ESCAPE:1218//If current window is menu bar or its child - close it1219//If current window is popup menu - close it1220//go one level up otherwise12211222//Fixed 6266513: Incorrect key handling in XAWT popup menu1223//Popup menu should be closed on 'ESC'1224if ((cwnd instanceof XMenuBarPeer) || (cwnd.getParentMenuWindow() instanceof XMenuBarPeer)) {1225ungrabInput();1226} else if (cwnd instanceof XPopupMenuPeer) {1227ungrabInput();1228} else {1229XBaseMenuWindow pwnd = cwnd.getParentMenuWindow();1230pwnd.selectItem(pwnd.getSelectedItem(), false);1231}1232break;1233case KeyEvent.VK_F10:1234//Fixed 6266513: Incorrect key handling in XAWT popup menu1235//All menus should be closed on 'F10'1236ungrabInput();1237break;1238default:1239break;1240}1241}12421243} //class XBaseMenuWindow124412451246