Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/solaris/classes/sun/awt/X11/XBaseMenuWindow.java
32288 views
/*1* Copyright (c) 2005, 2013, 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.peer.*;28import java.awt.event.*;29import java.awt.image.ColorModel;3031import sun.awt.*;3233import java.util.ArrayList;34import java.util.Vector;35import sun.util.logging.PlatformLogger;36import sun.java2d.SurfaceData;37import sun.java2d.SunGraphics2D;3839/**40* The abstract class XBaseMenuWindow is the superclass41* of all menu windows.42*/43abstract public class XBaseMenuWindow extends XWindow {4445/************************************************46*47* Data members48*49************************************************/5051private static PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XBaseMenuWindow");5253/*54* Colors are calculated using MotifColorUtilities class55* from backgroundColor and are contained in these vars.56*/57private Color backgroundColor;58private Color foregroundColor;59private Color lightShadowColor;60private Color darkShadowColor;61private Color selectedColor;62private Color disabledColor;6364/**65* Array of items.66*/67private ArrayList<XMenuItemPeer> items;6869/**70* Index of selected item in array of items71*/72private int selectedIndex = -1;7374/**75* Specifies currently showing submenu.76*/77private XMenuPeer showingSubmenu = null;7879/**80* Static synchronizational object.81* Following operations should be synchronized82* using this object:83* 1. Access to items vector84* 2. Access to selection85* 3. Access to showing menu window member86*87* This is lowest level lock,88* no other locks should be taken when89* thread own this lock.90*/91static private Object menuTreeLock = new Object();9293/************************************************94*95* Event processing96*97************************************************/9899/**100* If mouse button is clicked on item showing submenu101* we have to hide its submenu.102* And if mouse button is pressed on such item and103* dragged to another, getShowingSubmenu() is changed.104* So this member saves the item that the user105* presses mouse button on _only_ if it's showing submenu.106*/107private XMenuPeer showingMousePressedSubmenu = null;108109/**110* If the PopupMenu is invoked as a result of right button click111* first mouse event after grabInput would be MouseReleased.112* We need to check if the user has moved mouse after input grab.113* If yes - hide the PopupMenu. If no - do nothing114*/115protected Point grabInputPoint = null;116protected boolean hasPointerMoved = false;117118private AppContext disposeAppContext;119120/************************************************121*122* Mapping data123*124************************************************/125126/**127* Mapping data that is filled in getMappedItems function128* and reset in resetSize function. It contains array of129* items in order that they appear on screen and may contain130* additional data defined by descendants.131*/132private MappingData mappingData;133134static class MappingData implements Cloneable {135136/**137* Array of item in order that they appear on screen138*/139private XMenuItemPeer[] items;140141/**142* Constructs MappingData object with list143* of menu items144*/145MappingData(XMenuItemPeer[] items) {146this.items = items;147}148149/**150* Constructs MappingData without items151* This constructor should be used in case of errors152*/153MappingData() {154this.items = new XMenuItemPeer[0];155}156157public Object clone() {158try {159return super.clone();160} catch (CloneNotSupportedException ex) {161throw new InternalError(ex);162}163}164165public XMenuItemPeer[] getItems() {166return this.items;167}168}169170/************************************************171*172* Construction173*174************************************************/175XBaseMenuWindow() {176super(new XCreateWindowParams(new Object[] {177DELAYED, Boolean.TRUE}));178179disposeAppContext = AppContext.getAppContext();180}181182/************************************************183*184* Abstract methods185*186************************************************/187188/**189* Returns parent menu window (not the X-hierarchy parent window)190*/191protected abstract XBaseMenuWindow getParentMenuWindow();192193/**194* Performs mapping of items in window.195* This function creates and fills specific196* descendant of MappingData197* and sets mapping coordinates of items198* This function should return default menu data199* if errors occur200*/201protected abstract MappingData map();202203/**204* Calculates placement of submenu window205* given bounds of item with submenu and206* size of submenu window. Returns suggested207* rectangle for submenu window in global coordinates208* @param itemBounds the bounding rectangle of item209* in local coordinates210* @param windowSize the desired size of submenu's window211*/212protected abstract Rectangle getSubmenuBounds(Rectangle itemBounds, Dimension windowSize);213214215/**216* This function is to be called if it's likely that size217* of items was changed. It can be called from any thread218* in any locked state, so it should not take locks219*/220protected abstract void updateSize();221222/************************************************223*224* Initialization225*226************************************************/227228/**229* Overrides XBaseWindow.instantPreInit230*/231void instantPreInit(XCreateWindowParams params) {232super.instantPreInit(params);233items = new ArrayList();234}235236/************************************************237*238* General-purpose functions239*240************************************************/241242/**243* Returns static lock used for menus244*/245static Object getMenuTreeLock() {246return menuTreeLock;247}248249/**250* This function is called to clear all saved251* size data.252*/253protected void resetMapping() {254mappingData = null;255}256257/**258* Invokes repaint procedure on eventHandlerThread259*/260void postPaintEvent() {261if (isShowing()) {262PaintEvent pe = new PaintEvent(target, PaintEvent.PAINT,263new Rectangle(0, 0, width, height));264postEvent(pe);265}266}267268/************************************************269*270* Utility functions for manipulating items271*272************************************************/273274/**275* Thread-safely returns item at specified index276* @param index the position of the item to be returned.277*/278XMenuItemPeer getItem(int index) {279if (index >= 0) {280synchronized(getMenuTreeLock()) {281if (items.size() > index) {282return items.get(index);283}284}285}286return null;287}288289/**290* Thread-safely creates a copy of the items vector291*/292XMenuItemPeer[] copyItems() {293synchronized(getMenuTreeLock()) {294return (XMenuItemPeer[])items.toArray(new XMenuItemPeer[] {});295}296}297298299/**300* Thread-safely returns selected item301*/302XMenuItemPeer getSelectedItem() {303synchronized(getMenuTreeLock()) {304if (selectedIndex >= 0) {305if (items.size() > selectedIndex) {306return items.get(selectedIndex);307}308}309return null;310}311}312313/**314* Returns showing submenu, if any315*/316XMenuPeer getShowingSubmenu() {317synchronized(getMenuTreeLock()) {318return showingSubmenu;319}320}321322/**323* Adds item to end of items vector.324* Note that this function does not perform325* check for adding duplicate items326* @param item item to add327*/328public void addItem(MenuItem item) {329XMenuItemPeer mp = (XMenuItemPeer)item.getPeer();330if (mp != null) {331mp.setContainer(this);332synchronized(getMenuTreeLock()) {333items.add(mp);334}335} else {336if (log.isLoggable(PlatformLogger.Level.FINE)) {337log.fine("WARNING: Attempt to add menu item without a peer");338}339}340updateSize();341}342343/**344* Removes item at the specified index from items vector.345* @param index the position of the item to be removed346*/347public void delItem(int index) {348synchronized(getMenuTreeLock()) {349if (selectedIndex == index) {350selectItem(null, false);351} else if (selectedIndex > index) {352selectedIndex--;353}354if (index < items.size()) {355items.remove(index);356} else {357if (log.isLoggable(PlatformLogger.Level.FINE)) {358log.fine("WARNING: Attempt to remove non-existing menu item, index : " + index + ", item count : " + items.size());359}360}361}362updateSize();363}364365/**366* Clears items vector and loads specified vector367* @param items vector to be loaded368*/369public void reloadItems(Vector items) {370synchronized(getMenuTreeLock()) {371this.items.clear();372MenuItem[] itemArray = (MenuItem[])items.toArray(new MenuItem[] {});373int itemCnt = itemArray.length;374for(int i = 0; i < itemCnt; i++) {375addItem(itemArray[i]);376}377}378}379380/**381* Select specified item and shows/hides submenus if necessary382* We can not select by index, so we need to select by ref.383* @param item the item to be selected, null to clear selection384* @param showWindowIfMenu if the item is XMenuPeer then its385* window is shown/hidden according to this param.386*/387void selectItem(XMenuItemPeer item, boolean showWindowIfMenu) {388synchronized(getMenuTreeLock()) {389XMenuPeer showingSubmenu = getShowingSubmenu();390int newSelectedIndex = (item != null) ? items.indexOf(item) : -1;391if (this.selectedIndex != newSelectedIndex) {392if (log.isLoggable(PlatformLogger.Level.FINEST)) {393log.finest("Selected index changed, was : " + this.selectedIndex + ", new : " + newSelectedIndex);394}395this.selectedIndex = newSelectedIndex;396postPaintEvent();397}398final XMenuPeer submenuToShow = (showWindowIfMenu && (item instanceof XMenuPeer)) ? (XMenuPeer)item : null;399if (submenuToShow != showingSubmenu) {400XToolkit.executeOnEventHandlerThread(target, new Runnable() {401public void run() {402doShowSubmenu(submenuToShow);403}404});405}406}407}408409/**410* Performs hiding of currently showing submenu411* and showing of submenuToShow.412* This function should be executed on eventHandlerThread413* @param submenuToShow submenu to be shown or null414* to hide currently showing submenu415*/416private void doShowSubmenu(XMenuPeer submenuToShow) {417XMenuWindow menuWindowToShow = (submenuToShow != null) ? submenuToShow.getMenuWindow() : null;418Dimension dim = null;419Rectangle bounds = null;420//ensureCreated can invoke XWindowPeer.init() ->421//XWindowPeer.initGraphicsConfiguration() ->422//Window.getGraphicsConfiguration()423//that tries to obtain Component.AWTTreeLock.424//So it should be called outside awtLock()425if (menuWindowToShow != null) {426menuWindowToShow.ensureCreated();427}428XToolkit.awtLock();429try {430synchronized(getMenuTreeLock()) {431if (showingSubmenu != submenuToShow) {432if (log.isLoggable(PlatformLogger.Level.FINEST)) {433log.finest("Changing showing submenu");434}435if (showingSubmenu != null) {436XMenuWindow showingSubmenuWindow = showingSubmenu.getMenuWindow();437if (showingSubmenuWindow != null) {438showingSubmenuWindow.hide();439}440}441if (submenuToShow != null) {442dim = menuWindowToShow.getDesiredSize();443bounds = menuWindowToShow.getParentMenuWindow().getSubmenuBounds(submenuToShow.getBounds(), dim);444menuWindowToShow.show(bounds);445}446showingSubmenu = submenuToShow;447}448}449} finally {450XToolkit.awtUnlock();451}452}453454final void setItemsFont( Font font ) {455XMenuItemPeer[] items = copyItems();456int itemCnt = items.length;457for (int i = 0; i < itemCnt; i++) {458items[i].setFont(font);459}460}461462/************************************************463*464* Utility functions for manipulating mapped items465*466************************************************/467468/**469* Returns array of mapped items, null if error470* This function has to be not synchronized471* and we have to guarantee that we return472* some MappingData to user. It's OK if473* this.mappingData is replaced meanwhile474*/475MappingData getMappingData() {476MappingData mappingData = this.mappingData;477if (mappingData == null) {478mappingData = map();479this.mappingData = mappingData;480}481return (MappingData)mappingData.clone();482}483484/**485* returns item thats mapped coordinates contain486* specified point, null of none.487* @param pt the point in this window's coordinate system488*/489XMenuItemPeer getItemFromPoint(Point pt) {490XMenuItemPeer[] items = getMappingData().getItems();491int cnt = items.length;492for (int i = 0; i < cnt; i++) {493if (items[i].getBounds().contains(pt)) {494return items[i];495}496}497return null;498}499500/**501* Returns first item after currently selected502* item that can be selected according to mapping array.503* (no separators and no disabled items).504* Currently selected item if it's only selectable,505* null if no item can be selected506*/507XMenuItemPeer getNextSelectableItem() {508XMenuItemPeer[] mappedItems = getMappingData().getItems();509XMenuItemPeer selectedItem = getSelectedItem();510int cnt = mappedItems.length;511//Find index of selected item512int selIdx = -1;513for (int i = 0; i < cnt; i++) {514if (mappedItems[i] == selectedItem) {515selIdx = i;516break;517}518}519int idx = (selIdx == cnt - 1) ? 0 : selIdx + 1;520//cycle through mappedItems to find selectable item521//beginning from the next item and moving to the522//beginning of array when end is reached.523//Cycle is finished on selected item itself524for (int i = 0; i < cnt; i++) {525XMenuItemPeer item = mappedItems[idx];526if (!item.isSeparator() && item.isTargetItemEnabled()) {527return item;528}529idx++;530if (idx >= cnt) {531idx = 0;532}533}534//return null if no selectable item was found535return null;536}537538/**539* Returns first item before currently selected540* see getNextSelectableItem() for comments541*/542XMenuItemPeer getPrevSelectableItem() {543XMenuItemPeer[] mappedItems = getMappingData().getItems();544XMenuItemPeer selectedItem = getSelectedItem();545int cnt = mappedItems.length;546//Find index of selected item547int selIdx = -1;548for (int i = 0; i < cnt; i++) {549if (mappedItems[i] == selectedItem) {550selIdx = i;551break;552}553}554int idx = (selIdx <= 0) ? cnt - 1 : selIdx - 1;555//cycle through mappedItems to find selectable item556for (int i = 0; i < cnt; i++) {557XMenuItemPeer item = mappedItems[idx];558if (!item.isSeparator() && item.isTargetItemEnabled()) {559return item;560}561idx--;562if (idx < 0) {563idx = cnt - 1;564}565}566//return null if no selectable item was found567return null;568}569570/**571* Returns first selectable item572* This function is intended for clearing selection573*/574XMenuItemPeer getFirstSelectableItem() {575XMenuItemPeer[] mappedItems = getMappingData().getItems();576int cnt = mappedItems.length;577for (int i = 0; i < cnt; i++) {578XMenuItemPeer item = mappedItems[i];579if (!item.isSeparator() && item.isTargetItemEnabled()) {580return item;581}582}583584return null;585}586587/************************************************588*589* Utility functions for manipulating590* hierarchy of windows591*592************************************************/593594/**595* returns leaf menu window or596* this if no children are showing597*/598XBaseMenuWindow getShowingLeaf() {599synchronized(getMenuTreeLock()) {600XBaseMenuWindow leaf = this;601XMenuPeer leafchild = leaf.getShowingSubmenu();602while (leafchild != null) {603leaf = leafchild.getMenuWindow();604leafchild = leaf.getShowingSubmenu();605}606return leaf;607}608}609610/**611* returns root menu window612* or this if this window is topmost613*/614XBaseMenuWindow getRootMenuWindow() {615synchronized(getMenuTreeLock()) {616XBaseMenuWindow t = this;617XBaseMenuWindow tparent = t.getParentMenuWindow();618while (tparent != null) {619t = tparent;620tparent = t.getParentMenuWindow();621}622return t;623}624}625626/**627* Returns window that contains pt.628* search is started from leaf window629* to return first window in Z-order630* @param pt point in global coordinates631*/632XBaseMenuWindow getMenuWindowFromPoint(Point pt) {633synchronized(getMenuTreeLock()) {634XBaseMenuWindow t = getShowingLeaf();635while (t != null) {636Rectangle r = new Rectangle(t.toGlobal(new Point(0, 0)), t.getSize());637if (r.contains(pt)) {638return t;639}640t = t.getParentMenuWindow();641}642return null;643}644}645646/************************************************647*648* Primitives for getSubmenuBounds649*650* These functions are invoked from getSubmenuBounds651* implementations in different order. They check if window652* of size windowSize fits to the specified edge of653* rectangle itemBounds on the screen of screenSize.654* Return rectangle that occupies the window if it fits or null.655*656************************************************/657658/**659* Checks if window fits below specified item660* returns rectangle that the window fits to or null.661* @param itemBounds rectangle of item in global coordinates662* @param windowSize size of submenu window to fit663* @param screenSize size of screen664*/665Rectangle fitWindowBelow(Rectangle itemBounds, Dimension windowSize, Dimension screenSize) {666int width = windowSize.width;667int height = windowSize.height;668//Fix for 6267162: PIT: Popup Menu gets hidden below the screen when opened669//near the periphery of the screen, XToolkit670//Window should be moved if it's outside top-left screen bounds671int x = (itemBounds.x > 0) ? itemBounds.x : 0;672int y = (itemBounds.y + itemBounds.height > 0) ? itemBounds.y + itemBounds.height : 0;673if (y + height <= screenSize.height) {674//move it to the left if needed675if (width > screenSize.width) {676width = screenSize.width;677}678if (x + width > screenSize.width) {679x = screenSize.width - width;680}681return new Rectangle(x, y, width, height);682} else {683return null;684}685}686687/**688* Checks if window fits above specified item689* returns rectangle that the window fits to or null.690* @param itemBounds rectangle of item in global coordinates691* @param windowSize size of submenu window to fit692* @param screenSize size of screen693*/694Rectangle fitWindowAbove(Rectangle itemBounds, Dimension windowSize, Dimension screenSize) {695int width = windowSize.width;696int height = windowSize.height;697//Fix for 6267162: PIT: Popup Menu gets hidden below the screen when opened698//near the periphery of the screen, XToolkit699//Window should be moved if it's outside bottom-left screen bounds700int x = (itemBounds.x > 0) ? itemBounds.x : 0;701int y = (itemBounds.y > screenSize.height) ? screenSize.height - height : itemBounds.y - height;702if (y >= 0) {703//move it to the left if needed704if (width > screenSize.width) {705width = screenSize.width;706}707if (x + width > screenSize.width) {708x = screenSize.width - width;709}710return new Rectangle(x, y, width, height);711} else {712return null;713}714}715716/**717* Checks if window fits to the right specified item718* returns rectangle that the window fits to or null.719* @param itemBounds rectangle of item in global coordinates720* @param windowSize size of submenu window to fit721* @param screenSize size of screen722*/723Rectangle fitWindowRight(Rectangle itemBounds, Dimension windowSize, Dimension screenSize) {724int width = windowSize.width;725int height = windowSize.height;726//Fix for 6267162: PIT: Popup Menu gets hidden below the screen when opened727//near the periphery of the screen, XToolkit728//Window should be moved if it's outside top-left screen bounds729int x = (itemBounds.x + itemBounds.width > 0) ? itemBounds.x + itemBounds.width : 0;730int y = (itemBounds.y > 0) ? itemBounds.y : 0;731if (x + width <= screenSize.width) {732//move it to the top if needed733if (height > screenSize.height) {734height = screenSize.height;735}736if (y + height > screenSize.height) {737y = screenSize.height - height;738}739return new Rectangle(x, y, width, height);740} else {741return null;742}743}744745/**746* Checks if window fits to the left specified item747* returns rectangle that the window fits to or null.748* @param itemBounds rectangle of item in global coordinates749* @param windowSize size of submenu window to fit750* @param screenSize size of screen751*/752Rectangle fitWindowLeft(Rectangle itemBounds, Dimension windowSize, Dimension screenSize) {753int width = windowSize.width;754int height = windowSize.height;755//Fix for 6267162: PIT: Popup Menu gets hidden below the screen when opened756//near the periphery of the screen, XToolkit757//Window should be moved if it's outside top-right screen bounds758int x = (itemBounds.x < screenSize.width) ? itemBounds.x - width : screenSize.width - width;759int y = (itemBounds.y > 0) ? itemBounds.y : 0;760if (x >= 0) {761//move it to the top if needed762if (height > screenSize.height) {763height = screenSize.height;764}765if (y + height > screenSize.height) {766y = screenSize.height - height;767}768return new Rectangle(x, y, width, height);769} else {770return null;771}772}773774/**775* The last thing we can do with the window776* to fit it on screen - move it to the777* top-left edge and cut by screen dimensions778* @param windowSize size of submenu window to fit779* @param screenSize size of screen780*/781Rectangle fitWindowToScreen(Dimension windowSize, Dimension screenSize) {782int width = (windowSize.width < screenSize.width) ? windowSize.width : screenSize.width;783int height = (windowSize.height < screenSize.height) ? windowSize.height : screenSize.height;784return new Rectangle(0, 0, width, height);785}786787788/************************************************789*790* Utility functions for manipulating colors791*792************************************************/793794/**795* This function is called before every painting.796* TODO:It would be better to add PropertyChangeListener797* to target component798* TODO:It would be better to access background color799* not invoking user-overridable function800*/801void resetColors() {802replaceColors((target == null) ? SystemColor.window : target.getBackground());803}804805/**806* Calculates colors of various elements given807* background color. Uses MotifColorUtilities808* @param backgroundColor the color of menu window's809* background.810*/811void replaceColors(Color backgroundColor) {812if (backgroundColor != this.backgroundColor) {813this.backgroundColor = backgroundColor;814815int red = backgroundColor.getRed();816int green = backgroundColor.getGreen();817int blue = backgroundColor.getBlue();818819foregroundColor = new Color(MotifColorUtilities.calculateForegroundFromBackground(red,green,blue));820lightShadowColor = new Color(MotifColorUtilities.calculateTopShadowFromBackground(red,green,blue));821darkShadowColor = new Color(MotifColorUtilities.calculateBottomShadowFromBackground(red,green,blue));822selectedColor = new Color(MotifColorUtilities.calculateSelectFromBackground(red,green,blue));823disabledColor = (backgroundColor.equals(Color.BLACK)) ? foregroundColor.darker() : backgroundColor.darker();824}825}826827Color getBackgroundColor() {828return backgroundColor;829}830831Color getForegroundColor() {832return foregroundColor;833}834835Color getLightShadowColor() {836return lightShadowColor;837}838839Color getDarkShadowColor() {840return darkShadowColor;841}842843Color getSelectedColor() {844return selectedColor;845}846847Color getDisabledColor() {848return disabledColor;849}850851/************************************************852*853* Painting utility functions854*855************************************************/856857/**858* Draws raised or sunken rectangle on specified graphics859* @param g the graphics on which to draw860* @param x the coordinate of left edge in coordinates of graphics861* @param y the coordinate of top edge in coordinates of graphics862* @param width the width of rectangle863* @param height the height of rectangle864* @param raised true to draw raised rectangle, false to draw sunken865*/866void draw3DRect(Graphics g, int x, int y, int width, int height, boolean raised) {867if ((width <= 0) || (height <= 0)) {868return;869}870Color c = g.getColor();871g.setColor(raised ? getLightShadowColor() : getDarkShadowColor());872g.drawLine(x, y, x, y + height - 1);873g.drawLine(x + 1, y, x + width - 1, y);874g.setColor(raised ? getDarkShadowColor() : getLightShadowColor());875g.drawLine(x + 1, y + height - 1, x + width - 1, y + height - 1);876g.drawLine(x + width - 1, y + 1, x + width - 1, y + height - 1);877g.setColor(c);878}879880/************************************************881*882* Overriden utility functions of XWindow883*884************************************************/885886/**887* Filters X events888*/889protected boolean isEventDisabled(XEvent e) {890switch (e.get_type()) {891case XConstants.Expose :892case XConstants.GraphicsExpose :893case XConstants.ButtonPress:894case XConstants.ButtonRelease:895case XConstants.MotionNotify:896case XConstants.KeyPress:897case XConstants.KeyRelease:898case XConstants.DestroyNotify:899return super.isEventDisabled(e);900default:901return true;902}903}904905/**906* Invokes disposal procedure on eventHandlerThread907*/908public void dispose() {909setDisposed(true);910911SunToolkit.invokeLaterOnAppContext(disposeAppContext, new Runnable() {912public void run() {913doDispose();914}915});916}917918/**919* Performs disposal of menu window.920* Should be called only on eventHandlerThread921*/922protected void doDispose() {923xSetVisible(false);924SurfaceData oldData = surfaceData;925surfaceData = null;926if (oldData != null) {927oldData.invalidate();928}929destroy();930}931932/**933* Invokes event processing on eventHandlerThread934* This function needs to be overriden since935* XBaseMenuWindow has no corresponding component936* so events can not be processed using standart means937*/938void postEvent(final AWTEvent event) {939InvocationEvent ev = new InvocationEvent(event.getSource(), new Runnable() {940public void run() {941handleEvent(event);942}943});944super.postEvent(ev);945}946947/**948* The implementation of base window performs processing949* of paint events only. This behaviour is changed in950* descendants.951*/952protected void handleEvent(AWTEvent event) {953switch(event.getID()) {954case PaintEvent.PAINT:955doHandleJavaPaintEvent((PaintEvent)event);956break;957}958}959960/**961* Save location of pointer for further use962* then invoke superclass963*/964public boolean grabInput() {965int rootX;966int rootY;967boolean res;968XToolkit.awtLock();969try {970long root = XlibWrapper.RootWindow(XToolkit.getDisplay(),971getScreenNumber());972res = XlibWrapper.XQueryPointer(XToolkit.getDisplay(), root,973XlibWrapper.larg1, //root974XlibWrapper.larg2, //child975XlibWrapper.larg3, //root_x976XlibWrapper.larg4, //root_y977XlibWrapper.larg5, //child_x978XlibWrapper.larg6, //child_y979XlibWrapper.larg7);//mask980rootX = Native.getInt(XlibWrapper.larg3);981rootY = Native.getInt(XlibWrapper.larg4);982res &= super.grabInput();983} finally {984XToolkit.awtUnlock();985}986if (res) {987//Mouse pointer is on the same display988this.grabInputPoint = new Point(rootX, rootY);989this.hasPointerMoved = false;990} else {991this.grabInputPoint = null;992this.hasPointerMoved = true;993}994return res;995}996/************************************************997*998* Overridable event processing functions999*1000************************************************/10011002/**1003* Performs repainting1004*/1005void doHandleJavaPaintEvent(PaintEvent event) {1006Rectangle rect = event.getUpdateRect();1007repaint(rect.x, rect.y, rect.width, rect.height);1008}10091010/************************************************1011*1012* User input handling utility functions1013*1014************************************************/10151016/**1017* Performs handling of java mouse event1018* Note that this function should be invoked1019* only from root of menu window's hierarchy1020* that grabs input focus1021*/1022void doHandleJavaMouseEvent( MouseEvent mouseEvent ) {1023if (!XToolkit.isLeftMouseButton(mouseEvent) && !XToolkit.isRightMouseButton(mouseEvent)) {1024return;1025}1026//Window that owns input1027XBaseWindow grabWindow = XAwtState.getGrabWindow();1028//Point of mouse event in global coordinates1029Point ptGlobal = mouseEvent.getLocationOnScreen();1030if (!hasPointerMoved) {1031//Fix for 6301307: NullPointerException while dispatching mouse events, XToolkit1032if (grabInputPoint == null ||1033(Math.abs(ptGlobal.x - grabInputPoint.x) > getMouseMovementSmudge()) ||1034(Math.abs(ptGlobal.y - grabInputPoint.y) > getMouseMovementSmudge())) {1035hasPointerMoved = true;1036}1037}1038//Z-order first descendant of current menu window1039//hierarchy that contain mouse point1040XBaseMenuWindow wnd = getMenuWindowFromPoint(ptGlobal);1041//Item in wnd that contains mouse point, if any1042XMenuItemPeer item = (wnd != null) ? wnd.getItemFromPoint(wnd.toLocal(ptGlobal)) : null;1043//Currently showing leaf window1044XBaseMenuWindow cwnd = getShowingLeaf();1045switch (mouseEvent.getID()) {1046case MouseEvent.MOUSE_PRESSED:1047//This line is to get rid of possible problems1048//That may occur if mouse events are lost1049showingMousePressedSubmenu = null;1050if ((grabWindow == this) && (wnd == null)) {1051//Menus grab input and the user1052//presses mouse button outside1053ungrabInput();1054} else {1055//Menus grab input OR mouse is pressed on menu window1056grabInput();1057if (item != null && !item.isSeparator() && item.isTargetItemEnabled()) {1058//Button is pressed on enabled item1059if (wnd.getShowingSubmenu() == item) {1060//Button is pressed on item that shows1061//submenu. We have to hide its submenu1062//if user clicks on it1063showingMousePressedSubmenu = (XMenuPeer)item;1064}1065wnd.selectItem(item, true);1066} else {1067//Button is pressed on disabled item or empty space1068if (wnd != null) {1069wnd.selectItem(null, false);1070}1071}1072}1073break;1074case MouseEvent.MOUSE_RELEASED:1075//Note that if item is not null, wnd has to be not null1076if (item != null && !item.isSeparator() && item.isTargetItemEnabled()) {1077if (item instanceof XMenuPeer) {1078if (showingMousePressedSubmenu == item) {1079//User clicks on item that shows submenu.1080//Hide the submenu1081if (wnd instanceof XMenuBarPeer) {1082ungrabInput();1083} else {1084wnd.selectItem(item, false);1085}1086}1087} else {1088//Invoke action event1089item.action(mouseEvent.getWhen());1090ungrabInput();1091}1092} else {1093//Mouse is released outside menu items1094if (hasPointerMoved || (wnd instanceof XMenuBarPeer)) {1095ungrabInput();1096}1097}1098showingMousePressedSubmenu = null;1099break;1100case MouseEvent.MOUSE_DRAGGED:1101if (wnd != null) {1102//Mouse is dragged over menu window1103//Move selection to item under cursor1104if (item != null && !item.isSeparator() && item.isTargetItemEnabled()) {1105if (grabWindow == this){1106wnd.selectItem(item, true);1107}1108} else {1109wnd.selectItem(null, false);1110}1111} else {1112//Mouse is dragged outside menu windows1113//clear selection in leaf to reflect it1114if (cwnd != null) {1115cwnd.selectItem(null, false);1116}1117}1118break;1119}1120}11211122/**1123* Performs handling of java keyboard event1124* Note that this function should be invoked1125* only from root of menu window's hierarchy1126* that grabs input focus1127*/1128void doHandleJavaKeyEvent(KeyEvent event) {1129if (log.isLoggable(PlatformLogger.Level.FINER)) {1130log.finer(event.toString());1131}1132if (event.getID() != KeyEvent.KEY_PRESSED) {1133return;1134}1135final int keyCode = event.getKeyCode();1136XBaseMenuWindow cwnd = getShowingLeaf();1137XMenuItemPeer citem = cwnd.getSelectedItem();1138switch(keyCode) {1139case KeyEvent.VK_UP:1140case KeyEvent.VK_KP_UP:1141if (!(cwnd instanceof XMenuBarPeer)) {1142//If active window is not menu bar,1143//move selection up1144cwnd.selectItem(cwnd.getPrevSelectableItem(), false);1145}1146break;1147case KeyEvent.VK_DOWN:1148case KeyEvent.VK_KP_DOWN:1149if (cwnd instanceof XMenuBarPeer) {1150//If active window is menu bar show current submenu1151selectItem(getSelectedItem(), true);1152} else {1153//move selection down1154cwnd.selectItem(cwnd.getNextSelectableItem(), false);1155}1156break;1157case KeyEvent.VK_LEFT:1158case KeyEvent.VK_KP_LEFT:1159if (cwnd instanceof XMenuBarPeer) {1160//leaf window is menu bar1161//select previous item1162selectItem(getPrevSelectableItem(), false);1163} else if (cwnd.getParentMenuWindow() instanceof XMenuBarPeer) {1164//leaf window is direct child of menu bar1165//select previous item of menu bar1166//and show its submenu1167selectItem(getPrevSelectableItem(), true);1168} else {1169//hide leaf moving focus to its parent1170//(equvivalent of pressing ESC)1171XBaseMenuWindow pwnd = cwnd.getParentMenuWindow();1172//Fix for 6272952: PIT: Pressing LEFT ARROW on a popup menu throws NullPointerException, XToolkit1173if (pwnd != null) {1174pwnd.selectItem(pwnd.getSelectedItem(), false);1175}1176}1177break;1178case KeyEvent.VK_RIGHT:1179case KeyEvent.VK_KP_RIGHT:1180if (cwnd instanceof XMenuBarPeer) {1181//leaf window is menu bar1182//select next item1183selectItem(getNextSelectableItem(), false);1184} else if (citem instanceof XMenuPeer) {1185//current item is menu, show its window1186//(equivalent of ENTER)1187cwnd.selectItem(citem, true);1188} else if (this instanceof XMenuBarPeer) {1189//if this is menu bar (not popup menu)1190//and the user presses RIGHT on item (not submenu)1191//select next top-level menu1192selectItem(getNextSelectableItem(), true);1193}1194break;1195case KeyEvent.VK_SPACE:1196case KeyEvent.VK_ENTER:1197//If the current item has submenu show it1198//Perform action otherwise1199if (citem instanceof XMenuPeer) {1200cwnd.selectItem(citem, true);1201} else if (citem != null) {1202citem.action(event.getWhen());1203ungrabInput();1204}1205break;1206case KeyEvent.VK_ESCAPE:1207//If current window is menu bar or its child - close it1208//If current window is popup menu - close it1209//go one level up otherwise12101211//Fixed 6266513: Incorrect key handling in XAWT popup menu1212//Popup menu should be closed on 'ESC'1213if ((cwnd instanceof XMenuBarPeer) || (cwnd.getParentMenuWindow() instanceof XMenuBarPeer)) {1214ungrabInput();1215} else if (cwnd instanceof XPopupMenuPeer) {1216ungrabInput();1217} else {1218XBaseMenuWindow pwnd = cwnd.getParentMenuWindow();1219pwnd.selectItem(pwnd.getSelectedItem(), false);1220}1221break;1222case KeyEvent.VK_F10:1223//Fixed 6266513: Incorrect key handling in XAWT popup menu1224//All menus should be closed on 'F10'1225ungrabInput();1226break;1227default:1228break;1229}1230}12311232} //class XBaseMenuWindow123312341235