Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/solaris/classes/sun/awt/X11/XChoicePeer.java
32288 views
/*1* Copyright (c) 2003, 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*/2425package sun.awt.X11;2627import java.awt.*;28import java.awt.peer.*;29import java.awt.event.*;30import sun.util.logging.PlatformLogger;3132// FIXME: tab traversal should be disabled when mouse is captured (4816336)3334// FIXME: key and mouse events should not be delivered to listeners when the Choice is unfurled. Must override handleNativeKey/MouseEvent (4816336)3536// FIXME: test programmatic add/remove/clear/etc3738// FIXME: account for unfurling at the edge of the screen39// Note: can't set x,y on layout(), 'cause moving the top-level to the40// edge of the screen won't call layout(). Just do it on paint, I guess4142// TODO: make painting more efficient (i.e. when down arrow is pressed, only two items should need to be repainted.4344public class XChoicePeer extends XComponentPeer implements ChoicePeer, ToplevelStateListener {45private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XChoicePeer");4647private static final int MAX_UNFURLED_ITEMS = 10; // Maximum number of48// items to be displayed49// at a time in an50// unfurled Choice51// Description of these constants in ListHelper52public final static int TEXT_SPACE = 1;53public final static int BORDER_WIDTH = 1;54public final static int ITEM_MARGIN = 1;55public final static int SCROLLBAR_WIDTH = 15;565758// SHARE THESE!59private static final Insets focusInsets = new Insets(0,0,0,0);606162static final int WIDGET_OFFSET = 18;6364// Stolen from Tiny65static final int TEXT_XPAD = 8;66static final int TEXT_YPAD = 6;6768// FIXME: Motif uses a different focus color for the item within69// the unfurled Choice list and for when the Choice itself is focused and70// popped up.71static final Color focusColor = Color.black;7273// TODO: there is a time value that the mouse is held down. If short74// enough, the Choice stays popped down. If long enough, Choice75// is furled when the mouse is released7677private boolean unfurled = false; // Choice list is popped down7879private boolean dragging = false; // Mouse was pressed and is being80// dragged over the (unfurled)81// Choice8283private boolean mouseInSB = false; // Mouse is interacting with the84// scrollbar8586private boolean firstPress = false; // mouse was pressed on87// furled Choice so we88// not need to furl the89// Choice when MOUSE_RELEASED occurred9091// 6425067. Mouse was pressed on furled choice and dropdown list appeared over Choice itself92// and then there were no mouse movements until MOUSE_RELEASE.93// This scenario leads to ItemStateChanged as the choice logic uses94// MouseReleased event to send ItemStateChanged. To prevent it we should95// use a combination of firstPress and wasDragged variables.96// The only difference in dragging and wasDragged is: last one will not97// set to false on mouse ungrab. It become false after MouseRelased() finishes.98private boolean wasDragged = false;99private ListHelper helper;100private UnfurledChoice unfurledChoice;101102// TODO: Choice remembers where it was scrolled to when unfurled - it's not103// always to the currently selected item.104105// Indicates whether or not to paint selected item in the choice.106// Default is to paint107private boolean drawSelectedItem = true;108109// If set, indicates components under which choice popup should be showed.110// The choice's popup width and location should be adjust to appear111// under both choice and alignUnder component.112private Component alignUnder;113114// If cursor is outside of an unfurled Choice when the mouse is115// released, Choice item should NOT be updated. Remember the proper index.116private int dragStartIdx = -1;117118// Holds the listener (XFileDialogPeer) which the processing events from the choice119// See 6240074 for more information120private XChoicePeerListener choiceListener;121122XChoicePeer(Choice target) {123super(target);124}125126void preInit(XCreateWindowParams params) {127super.preInit(params);128Choice target = (Choice)this.target;129int numItems = target.getItemCount();130unfurledChoice = new UnfurledChoice(target);131getToplevelXWindow().addToplevelStateListener(this);132helper = new ListHelper(unfurledChoice,133getGUIcolors(),134numItems,135false,136true,137false,138target.getFont(),139MAX_UNFURLED_ITEMS,140TEXT_SPACE,141ITEM_MARGIN,142BORDER_WIDTH,143SCROLLBAR_WIDTH);144}145146void postInit(XCreateWindowParams params) {147super.postInit(params);148Choice target = (Choice)this.target;149int numItems = target.getItemCount();150151// Add all items152for (int i = 0; i < numItems; i++) {153helper.add(target.getItem(i));154}155if (!helper.isEmpty()) {156helper.select(target.getSelectedIndex());157helper.setFocusedIndex(target.getSelectedIndex());158}159helper.updateColors(getGUIcolors());160updateMotifColors(getPeerBackground());161}162163public boolean isFocusable() { return true; }164165// 6399679. check if super.setBounds() actually changes the size of the166// component and then compare current Choice size with a new one. If167// they differs then hide dropdown menu168public void setBounds(int x, int y, int width, int height, int op) {169int oldX = this.x;170int oldY = this.y;171int oldWidth = this.width;172int oldHeight = this.height;173super.setBounds(x, y, width, height, op);174if (unfurled && (oldX != this.x || oldY != this.y || oldWidth != this.width || oldHeight != this.height) ) {175hidePopdownMenu();176}177}178179public void focusGained(FocusEvent e) {180// TODO: only need to paint the focus bit181super.focusGained(e);182repaint();183}184185/*186* Fix for 6246503 : Disabling a choice after selection locks keyboard, mouse and makes the system unusable, Xtoolkit187* if setEnabled(false) invoked we should close opened choice in188* order to prevent keyboard/mouse lock.189*/190public void setEnabled(boolean value) {191super.setEnabled(value);192helper.updateColors(getGUIcolors());193if (!value && unfurled){194hidePopdownMenu();195}196}197198public void focusLost(FocusEvent e) {199// TODO: only need to paint the focus bit?200super.focusLost(e);201repaint();202}203204void ungrabInputImpl() {205if (unfurled) {206unfurled = false;207dragging = false;208mouseInSB = false;209unfurledChoice.setVisible(false);210}211212super.ungrabInputImpl();213}214215void handleJavaKeyEvent(KeyEvent e) {216if (e.getID() == KeyEvent.KEY_PRESSED) {217keyPressed(e);218}219}220221public void keyPressed(KeyEvent e) {222switch(e.getKeyCode()) {223// UP & DOWN are same if furled or unfurled224case KeyEvent.VK_DOWN:225case KeyEvent.VK_KP_DOWN: {226if (helper.getItemCount() > 1) {227helper.down();228int newIdx = helper.getSelectedIndex();229230((Choice)target).select(newIdx);231postEvent(new ItemEvent((Choice)target,232ItemEvent.ITEM_STATE_CHANGED,233((Choice)target).getItem(newIdx),234ItemEvent.SELECTED));235repaint();236}237break;238}239case KeyEvent.VK_UP:240case KeyEvent.VK_KP_UP: {241if (helper.getItemCount() > 1) {242helper.up();243int newIdx = helper.getSelectedIndex();244245((Choice)target).select(newIdx);246postEvent(new ItemEvent((Choice)target,247ItemEvent.ITEM_STATE_CHANGED,248((Choice)target).getItem(newIdx),249ItemEvent.SELECTED));250repaint();251}252break;253}254case KeyEvent.VK_PAGE_DOWN:255if (unfurled && !dragging) {256int oldIdx = helper.getSelectedIndex();257helper.pageDown();258int newIdx = helper.getSelectedIndex();259if (oldIdx != newIdx) {260((Choice)target).select(newIdx);261postEvent(new ItemEvent((Choice)target,262ItemEvent.ITEM_STATE_CHANGED,263((Choice)target).getItem(newIdx),264ItemEvent.SELECTED));265repaint();266}267}268break;269case KeyEvent.VK_PAGE_UP:270if (unfurled && !dragging) {271int oldIdx = helper.getSelectedIndex();272helper.pageUp();273int newIdx = helper.getSelectedIndex();274if (oldIdx != newIdx) {275((Choice)target).select(newIdx);276postEvent(new ItemEvent((Choice)target,277ItemEvent.ITEM_STATE_CHANGED,278((Choice)target).getItem(newIdx),279ItemEvent.SELECTED));280repaint();281}282}283break;284case KeyEvent.VK_ESCAPE:285case KeyEvent.VK_ENTER:286if (unfurled) {287if (dragging){288if (e.getKeyCode() == KeyEvent.VK_ESCAPE){289//This also happens on290// - MouseButton2,3, etc. press291// - ENTER press292helper.select(dragStartIdx);293} else { //KeyEvent.VK_ENTER:294int newIdx = helper.getSelectedIndex();295((Choice)target).select(newIdx);296postEvent(new ItemEvent((Choice)target,297ItemEvent.ITEM_STATE_CHANGED,298((Choice)target).getItem(newIdx),299ItemEvent.SELECTED));300}301}302hidePopdownMenu();303dragging = false;304wasDragged = false;305mouseInSB = false;306307// See 6240074 for more information308if (choiceListener != null){309choiceListener.unfurledChoiceClosing();310}311}312break;313default:314if (unfurled) {315Toolkit.getDefaultToolkit().beep();316}317break;318}319}320321public boolean handlesWheelScrolling() { return true; }322323void handleJavaMouseWheelEvent(MouseWheelEvent e) {324if (unfurled && helper.isVSBVisible()) {325if (ListHelper.doWheelScroll(helper.getVSB(), null, e)) {326repaint();327}328}329}330331void handleJavaMouseEvent(MouseEvent e) {332super.handleJavaMouseEvent(e);333int i = e.getID();334switch (i) {335case MouseEvent.MOUSE_PRESSED:336mousePressed(e);337break;338case MouseEvent.MOUSE_RELEASED:339mouseReleased(e);340break;341case MouseEvent.MOUSE_DRAGGED:342mouseDragged(e);343break;344}345}346347public void mousePressed(MouseEvent e) {348/*349* fix for 5003166: a Choice on XAWT shouldn't react to any350* mouse button presses except left. This involves presses on351* Choice but not on opened part of choice.352*/353if (e.getButton() == MouseEvent.BUTTON1){354dragStartIdx = helper.getSelectedIndex();355if (unfurled) {356//fix 6259328: PIT: Choice scrolls when dragging the parent frame while drop-down is active, XToolkit357if (! (isMouseEventInChoice(e) ||358unfurledChoice.isMouseEventInside(e)))359{360hidePopdownMenu();361}362// Press on unfurled Choice. Highlight the item under the cursor,363// but don't send item event or set the text on the button yet364unfurledChoice.trackMouse(e);365}366else {367// Choice is up - unfurl it368grabInput();369unfurledChoice.toFront();370firstPress = true;371wasDragged = false;372unfurled = true;373}374}375}376377/*378* helper method for mouseReleased routine379*/380void hidePopdownMenu(){381ungrabInput();382unfurledChoice.setVisible(false);383unfurled = false;384}385386public void mouseReleased(MouseEvent e) {387if (unfurled) {388if (mouseInSB) {389unfurledChoice.trackMouse(e);390}391else {392// We pressed and dragged onto the Choice, or, this is the393// second release after clicking to make the Choice "stick"394// unfurled.395// This release should ungrab/furl, and set the new item if396// release was over the unfurled Choice.397398// Fix for 6239944 : Choice shouldn't close its399// pop-down menu if user presses Mouse on Choice's Scrollbar400// some additional cases like releasing mouse outside401// of Choice are considered too402boolean isMouseEventInside = unfurledChoice.isMouseEventInside( e );403boolean isMouseInListArea = unfurledChoice.isMouseInListArea( e );404405// Fixed 6318746: REG: File Selection is failing406// We shouldn't restore the selected item407// if the mouse was dragged outside the drop-down choice area408if (!helper.isEmpty() && !isMouseInListArea && dragging) {409// Set the selected item back how it was.410((Choice)target).select(dragStartIdx);411}412413// Choice must be closed if user releases mouse on414// pop-down menu on the second click415if ( !firstPress && isMouseInListArea) {416hidePopdownMenu();417}418// Choice must be closed if user releases mouse419// outside of Choice's pop-down menu on the second click420if ( !firstPress && !isMouseEventInside) {421hidePopdownMenu();422}423//if user drags Mouse on pop-down menu, Scrollbar or424// outside the Choice425if ( firstPress && dragging) {426hidePopdownMenu();427}428/* this could happen when user has opened a Choice and429* released mouse button. Then he drags mouse on the430* Scrollbar and releases mouse again.431*/432if ( !firstPress && !isMouseInListArea &&433isMouseEventInside && dragging)434{435hidePopdownMenu();436}437438if (!helper.isEmpty()) {439// Only update the Choice if the mouse button is released440// over the list of items.441if (unfurledChoice.isMouseInListArea(e)) {442int newIdx = helper.getSelectedIndex();443if (newIdx >= 0) {444// Update the selected item in the target now that445// the mouse selection is complete.446if (newIdx != dragStartIdx) {447((Choice)target).select(newIdx);448// NOTE: We get a repaint when Choice.select()449// calls our peer.select().450}451if (wasDragged && e.getButton() != MouseEvent.BUTTON1){452((Choice)target).select(dragStartIdx);453}454455/*fix for 6239941 : Choice triggers ItemEvent when selecting an item with right mouse button, Xtoolkit456* We should generate ItemEvent if only457* LeftMouseButton used */458if (e.getButton() == MouseEvent.BUTTON1 &&459(!firstPress || wasDragged ))460{461postEvent(new ItemEvent((Choice)target,462ItemEvent.ITEM_STATE_CHANGED,463((Choice)target).getItem(newIdx),464ItemEvent.SELECTED));465}466467// see 6240074 for more information468if (choiceListener != null) {469choiceListener.unfurledChoiceClosing();470}471}472}473}474// See 6243382 for more information475unfurledChoice.trackMouse(e);476}477}478479dragging = false;480wasDragged = false;481firstPress = false;482dragStartIdx = -1;483}484485public void mouseDragged(MouseEvent e) {486/*487* fix for 5003166. On Motif user are unable to drag488* mouse inside opened Choice if he drags the mouse with489* different from LEFT mouse button ( e.g. RIGHT or MIDDLE).490* This fix make impossible to drag mouse inside opened choice491* with other mouse buttons rather then LEFT one.492*/493if ( e.getModifiers() == MouseEvent.BUTTON1_MASK ){494dragging = true;495wasDragged = true;496unfurledChoice.trackMouse(e);497}498}499500// Stolen from TinyChoicePeer501public Dimension getMinimumSize() {502// TODO: move this impl into ListHelper?503FontMetrics fm = getFontMetrics(target.getFont());504Choice c = (Choice)target;505int w = 0;506for (int i = c.countItems() ; i-- > 0 ;) {507w = Math.max(fm.stringWidth(c.getItem(i)), w);508}509return new Dimension(w + TEXT_XPAD + WIDGET_OFFSET,510fm.getMaxAscent() + fm.getMaxDescent() + TEXT_YPAD);511}512513/*514* Layout the...515*/516public void layout() {517/*518Dimension size = target.getSize();519Font f = target.getFont();520FontMetrics fm = target.getFontMetrics(f);521String text = ((Choice)target).getLabel();522523textRect.height = fm.getHeight();524525checkBoxSize = getChoiceSize(fm);526527// Note - Motif appears to use an left inset that is slightly528// scaled to the checkbox/font size.529cbX = borderInsets.left + checkBoxInsetFromText;530cbY = size.height / 2 - checkBoxSize / 2;531int minTextX = borderInsets.left + 2 * checkBoxInsetFromText + checkBoxSize;532// FIXME: will need to account for alignment?533// FIXME: call layout() on alignment changes534//textRect.width = fm.stringWidth(text);535textRect.width = fm.stringWidth(text == null ? "" : text);536textRect.x = Math.max(minTextX, size.width / 2 - textRect.width / 2);537textRect.y = size.height / 2 - textRect.height / 2 + borderInsets.top;538539focusRect.x = focusInsets.left;540focusRect.y = focusInsets.top;541focusRect.width = size.width-(focusInsets.left+focusInsets.right)-1;542focusRect.height = size.height-(focusInsets.top+focusInsets.bottom)-1;543544myCheckMark = AffineTransform.getScaleInstance((double)target.getFont().getSize() / MASTER_SIZE, (double)target.getFont().getSize() / MASTER_SIZE).createTransformedShape(MASTER_CHECKMARK);545*/546547}548549/**550* Paint the choice551*/552@Override553void paintPeer(final Graphics g) {554flush();555Dimension size = getPeerSize();556// TODO: when mouse is down over button, widget should be drawn depressed557g.setColor(getPeerBackground());558g.fillRect(0, 0, width, height);559560drawMotif3DRect(g, 1, 1, width-2, height-2, false);561drawMotif3DRect(g, width - WIDGET_OFFSET, (height / 2) - 3, 12, 6, false);562563if (!helper.isEmpty() && helper.getSelectedIndex() != -1) {564g.setFont(getPeerFont());565FontMetrics fm = g.getFontMetrics();566String lbl = helper.getItem(helper.getSelectedIndex());567if (lbl != null && drawSelectedItem) {568g.setClip(1, 1, width - WIDGET_OFFSET - 2, height);569if (isEnabled()) {570g.setColor(getPeerForeground());571g.drawString(lbl, 5, (height + fm.getMaxAscent()-fm.getMaxDescent())/2);572}573else {574g.setColor(getPeerBackground().brighter());575g.drawString(lbl, 5, (height + fm.getMaxAscent()-fm.getMaxDescent())/2);576g.setColor(getPeerBackground().darker());577g.drawString(lbl, 4, ((height + fm.getMaxAscent()-fm.getMaxDescent())/2)-1);578}579g.setClip(0, 0, width, height);580}581}582if (hasFocus()) {583paintFocus(g,focusInsets.left,focusInsets.top,size.width-(focusInsets.left+focusInsets.right)-1,size.height-(focusInsets.top+focusInsets.bottom)-1);584}585if (unfurled) {586unfurledChoice.repaint();587}588flush();589}590591protected void paintFocus(Graphics g,592int x, int y, int w, int h) {593g.setColor(focusColor);594g.drawRect(x,y,w,h);595}596597598599/*600* ChoicePeer methods stolen from TinyChoicePeer601*/602603public void select(int index) {604helper.select(index);605helper.setFocusedIndex(index);606repaint();607}608609public void add(String item, int index) {610helper.add(item, index);611repaint();612}613614public void remove(int index) {615boolean selected = (index == helper.getSelectedIndex());616boolean visibled = (index >= helper.firstDisplayedIndex() && index <= helper.lastDisplayedIndex());617helper.remove(index);618if (selected) {619if (helper.isEmpty()) {620helper.select(-1);621}622else {623helper.select(0);624}625}626/*627* Fix for 6248016628* After removing the item of the choice we need to reshape unfurled choice629* in order to keep actual bounds of the choice630*/631632/*633* condition added only for performance634*/635if (!unfurled) {636// Fix 6292186: PIT: Choice is not refreshed properly when the last item gets removed, XToolkit637// We should take into account that there is no 'select' invoking (hence 'repaint')638// if the choice is empty (see Choice.java method removeNoInvalidate())639// The condition isn't 'visibled' since it would be cause of the twice repainting640if (helper.isEmpty()) {641repaint();642}643return;644}645646/*647* condition added only for performance648* the count of the visible items changed649*/650if (visibled){651Rectangle r = unfurledChoice.placeOnScreen();652unfurledChoice.reshape(r.x, r.y, r.width, r.height);653return;654}655656/*657* condition added only for performance658* the structure of visible items changed659* if removable item is non visible and non selected then there is no repaint660*/661if (visibled || selected){662repaint();663}664}665666public void removeAll() {667helper.removeAll();668helper.select(-1);669/*670* Fix for 6248016671* After removing the item of the choice we need to reshape unfurled choice672* in order to keep actual bounds of the choice673*/674Rectangle r = unfurledChoice.placeOnScreen();675unfurledChoice.reshape(r.x, r.y, r.width, r.height);676repaint();677}678679/**680* DEPRECATED: Replaced by add(String, int).681*/682public void addItem(String item, int index) {683add(item, index);684}685686public void setFont(Font font) {687super.setFont(font);688helper.setFont(this.font);689}690691public void setForeground(Color c) {692super.setForeground(c);693helper.updateColors(getGUIcolors());694}695696public void setBackground(Color c) {697super.setBackground(c);698unfurledChoice.setBackground(c);699helper.updateColors(getGUIcolors());700updateMotifColors(c);701}702703public void setDrawSelectedItem(boolean value) {704drawSelectedItem = value;705}706707public void setAlignUnder(Component comp) {708alignUnder = comp;709}710711// see 6240074 for more information712public void addXChoicePeerListener(XChoicePeerListener l){713choiceListener = l;714}715716// see 6240074 for more information717public void removeXChoicePeerListener(){718choiceListener = null;719}720721public boolean isUnfurled(){722return unfurled;723}724725/* fix for 6261352. We should detect if current parent Window (containing a Choice) become iconified and hide pop-down menu with grab release.726* In this case we should hide pop-down menu.727*/728//calls from XWindowPeer. Could accept X-styled state events729public void stateChangedICCCM(int oldState, int newState) {730if (unfurled && oldState != newState){731hidePopdownMenu();732}733}734735//calls from XFramePeer. Could accept Frame's states.736public void stateChangedJava(int oldState, int newState) {737if (unfurled && oldState != newState){738hidePopdownMenu();739}740}741742/**************************************************************************/743/* Common functionality between List & Choice744/**************************************************************************/745746/**747* Inner class for the unfurled Choice list748* Much, much more docs749*/750class UnfurledChoice extends XWindow /*implements XScrollbarClient*/ {751752// First try - use Choice as the target753754public UnfurledChoice(Component target) {755super(target);756}757758// Override so we can do our own create()759public void preInit(XCreateWindowParams params) {760// A parent of this window is the target, at this point: wrong.761// Remove parent window; in the following preInit() call we'll calculate as a default762// a correct root window which is the proper parent for override redirect.763params.delete(PARENT_WINDOW);764super.preInit(params);765// Reset bounds(we'll set them later), set overrideRedirect766params.remove(BOUNDS);767params.add(OVERRIDE_REDIRECT, Boolean.TRUE);768}769770// Generally, bounds should be:771// x = target.x772// y = target.y + target.height773// w = Max(target.width, getLongestItemWidth) + possible vertScrollbar774// h = Min(MAX_UNFURLED_ITEMS, target.getItemCount()) * itemHeight775Rectangle placeOnScreen() {776int numItemsDisplayed;777// Motif paints an empty Choice the same size as a single item778if (helper.isEmpty()) {779numItemsDisplayed = 1;780}781else {782int numItems = helper.getItemCount();783numItemsDisplayed = Math.min(MAX_UNFURLED_ITEMS, numItems);784}785Point global = XChoicePeer.this.toGlobal(0,0);786Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();787788if (alignUnder != null) {789Rectangle choiceRec = XChoicePeer.this.getBounds();790choiceRec.setLocation(0, 0);791choiceRec = XChoicePeer.this.toGlobal(choiceRec);792Rectangle alignUnderRec = new Rectangle(alignUnder.getLocationOnScreen(), alignUnder.getSize()); // TODO: Security?793Rectangle result = choiceRec.union(alignUnderRec);794// we've got the left and width, calculate top and height795width = result.width;796x = result.x;797y = result.y + result.height;798height = 2*BORDER_WIDTH +799numItemsDisplayed*(helper.getItemHeight()+2*ITEM_MARGIN);800} else {801x = global.x;802y = global.y + XChoicePeer.this.height;803width = Math.max(XChoicePeer.this.width,804helper.getMaxItemWidth() + 2 * (BORDER_WIDTH + ITEM_MARGIN + TEXT_SPACE) + (helper.isVSBVisible() ? SCROLLBAR_WIDTH : 0));805height = 2*BORDER_WIDTH +806numItemsDisplayed*(helper.getItemHeight()+2*ITEM_MARGIN);807}808// Don't run off the edge of the screen809if (x < 0) {810x = 0;811}812else if (x + width > screen.width) {813x = screen.width - width;814}815816if (y + height > screen.height) {817y = global.y - height;818}819if (y < 0) {820y = 0;821}822return new Rectangle(x, y, width, height);823}824825public void toFront() {826// see 6240074 for more information827if (choiceListener != null)828choiceListener.unfurledChoiceOpening(helper);829830Rectangle r = placeOnScreen();831reshape(r.x, r.y, r.width, r.height);832super.toFront();833setVisible(true);834}835836/*837* Track a MouseEvent (either a drag or a press) and paint a new838* selected item, if necessary.839*/840// FIXME: first unfurl after move is not at edge of the screen onto second monitor doesn't841// track mouse correctly. Problem is w/ UnfurledChoice coords842public void trackMouse(MouseEvent e) {843// Event coords are relative to the button, so translate a bit844Point local = toLocalCoords(e);845846// If x,y is over unfurled Choice,847// highlight item under cursor848849switch (e.getID()) {850case MouseEvent.MOUSE_PRESSED:851// FIXME: If the Choice is unfurled and the mouse is pressed852// outside of the Choice, the mouse should ungrab on the853// the press, not the release854if (helper.isInVertSB(getBounds(), local.x, local.y)) {855mouseInSB = true;856helper.handleVSBEvent(e, getBounds(), local.x, local.y);857}858else {859trackSelection(local.x, local.y);860}861break;862case MouseEvent.MOUSE_RELEASED:863if (mouseInSB) {864mouseInSB = false;865helper.handleVSBEvent(e, getBounds(), local.x, local.y);866}else{867// See 6243382 for more information868helper.trackMouseReleasedScroll();869}870/*871else {872trackSelection(local.x, local.y);873}874*/875break;876case MouseEvent.MOUSE_DRAGGED:877if (mouseInSB) {878helper.handleVSBEvent(e, getBounds(), local.x, local.y);879}880else {881// See 6243382 for more information882helper.trackMouseDraggedScroll(local.x, local.y, width, height);883trackSelection(local.x, local.y);884}885break;886}887}888889private void trackSelection(int transX, int transY) {890if (!helper.isEmpty()) {891if (transX > 0 && transX < width &&892transY > 0 && transY < height) {893int newIdx = helper.y2index(transY);894if (log.isLoggable(PlatformLogger.Level.FINE)) {895log.fine("transX=" + transX + ", transY=" + transY896+ ",width=" + width + ", height=" + height897+ ", newIdx=" + newIdx + " on " + target);898}899if ((newIdx >=0) && (newIdx < helper.getItemCount())900&& (newIdx != helper.getSelectedIndex()))901{902helper.select(newIdx);903unfurledChoice.repaint();904}905}906}907// FIXME: If dragged off top or bottom, scroll if there's a vsb908// (ICK - we'll need a timer or our own event or something)909}910911/*912* fillRect with current Background color on the whole dropdown list.913*/914public void paintBackground() {915final Graphics g = getGraphics();916if (g != null) {917try {918g.setColor(getPeerBackground());919g.fillRect(0, 0, width, height);920} finally {921g.dispose();922}923}924}925/*926* 6405689. In some cases we should erase background to eliminate painting927* artefacts.928*/929@Override930public void repaint() {931if (!isVisible()) {932return;933}934if (helper.checkVsbVisibilityChangedAndReset()){935paintBackground();936}937super.repaint();938}939@Override940public void paintPeer(Graphics g) {941//System.out.println("UC.paint()");942Choice choice = (Choice)target;943Color colors[] = XChoicePeer.this.getGUIcolors();944draw3DRect(g, getSystemColors(), 0, 0, width - 1, height - 1, true);945draw3DRect(g, getSystemColors(), 1, 1, width - 3, height - 3, true);946947helper.paintAllItems(g,948colors,949getBounds());950}951952public void setVisible(boolean vis) {953xSetVisible(vis);954955if (!vis && alignUnder != null) {956alignUnder.requestFocusInWindow();957}958}959960/**961* Return a MouseEvent's Point in coordinates relative to the962* UnfurledChoice.963*/964private Point toLocalCoords(MouseEvent e) {965// Event coords are relative to the button, so translate a bit966Point global = e.getLocationOnScreen();967968global.x -= x;969global.y -= y;970return global;971}972973/* Returns true if the MouseEvent coords (which are based on the Choice)974* are inside of the UnfurledChoice.975*/976private boolean isMouseEventInside(MouseEvent e) {977Point local = toLocalCoords(e);978if (local.x > 0 && local.x < width &&979local.y > 0 && local.y < height) {980return true;981}982return false;983}984985/**986* Tests if the mouse cursor is in the Unfurled Choice, yet not987* in the vertical scrollbar988*/989private boolean isMouseInListArea(MouseEvent e) {990if (isMouseEventInside(e)) {991Point local = toLocalCoords(e);992Rectangle bounds = getBounds();993if (!helper.isInVertSB(bounds, local.x, local.y)) {994return true;995}996}997return false;998}9991000/*1001* Overridden from XWindow() because we don't want to send1002* ComponentEvents1003*/1004public void handleConfigureNotifyEvent(XEvent xev) {}1005public void handleMapNotifyEvent(XEvent xev) {}1006public void handleUnmapNotifyEvent(XEvent xev) {}1007} //UnfurledChoice10081009public void dispose() {1010if (unfurledChoice != null) {1011unfurledChoice.destroy();1012}1013super.dispose();1014}10151016/*1017* fix for 6239938 : Choice drop-down does not disappear when it loses1018* focus, on XToolkit1019* We are able to handle all _Key_ events received by Choice when1020* it is in opened state without sending it to EventQueue.1021* If Choice is in closed state we should behave like before: send1022* all events to EventQueue.1023* To be compatible with Motif we should handle all KeyEvents in1024* Choice if it is opened. KeyEvents should be sent into Java if Choice is not opened.1025*/1026boolean prePostEvent(final AWTEvent e) {1027if (unfurled){1028// fix for 6253211: PIT: MouseWheel events not triggered for Choice drop down in XAWT1029if (e instanceof MouseWheelEvent){1030return super.prePostEvent(e);1031}1032//fix 6252982: PIT: Keyboard FocusTraversal not working when choice's drop-down is visible, on XToolkit1033if (e instanceof KeyEvent){1034// notify XWindow that this event had been already handled and no need to post it again1035InvocationEvent ev = new InvocationEvent(target, new Runnable() {1036public void run() {1037if(target.isFocusable() &&1038getParentTopLevel().isFocusableWindow() )1039{1040handleJavaKeyEvent((KeyEvent)e);1041}1042}1043});1044postEvent(ev);10451046return true;1047} else {1048if (e instanceof MouseEvent){1049// Fix for 6240046 : REG:Choice's Drop-down does not disappear when clicking somewhere, after popup menu is disposed1050// if user presses Right Mouse Button on opened (unfurled)1051// Choice then we mustn't open a popup menu. We could filter1052// Mouse Events and handle them in XChoicePeer if Choice1053// currently in opened state.1054MouseEvent me = (MouseEvent)e;1055int eventId = e.getID();1056// fix 6251983: PIT: MouseDragged events not triggered1057// fix 6251988: PIT: Choice consumes MouseReleased, MouseClicked events when clicking it with left button,1058if ((unfurledChoice.isMouseEventInside(me) ||1059(!firstPress && eventId == MouseEvent.MOUSE_DRAGGED)))1060{1061return handleMouseEventByChoice(me);1062}1063// MouseMoved events should be fired in Choice's comp if it's not opened1064// Shouldn't generate Moved Events. CR : 62519951065if (eventId == MouseEvent.MOUSE_MOVED){1066return handleMouseEventByChoice(me);1067}1068//fix for 6272965: PIT: Choice triggers MousePressed when pressing mouse outside comp while drop-down is active, XTkt1069if ( !firstPress && !( isMouseEventInChoice(me) ||1070unfurledChoice.isMouseEventInside(me)) &&1071( eventId == MouseEvent.MOUSE_PRESSED ||1072eventId == MouseEvent.MOUSE_RELEASED ||1073eventId == MouseEvent.MOUSE_CLICKED )1074)1075{1076return handleMouseEventByChoice(me);1077}1078}1079}//else KeyEvent1080}//if unfurled1081return super.prePostEvent(e);1082}10831084//convenient method1085//do not generate this kind of Events1086public boolean handleMouseEventByChoice(final MouseEvent me){1087InvocationEvent ev = new InvocationEvent(target, new Runnable() {1088public void run() {1089handleJavaMouseEvent(me);1090}1091});1092postEvent(ev);10931094return true;1095}10961097/* Returns true if the MouseEvent coords1098* are inside of the Choice itself (it doesnt's depends on1099* if this choice opened or not).1100*/1101private boolean isMouseEventInChoice(MouseEvent e) {1102int x = e.getX();1103int y = e.getY();1104Rectangle choiceRect = getBounds();11051106if (x < 0 || x > choiceRect.width ||1107y < 0 || y > choiceRect.height)1108{1109return false;1110}1111return true;1112}1113}111411151116