gt:                     MouseEvent me = (MouseEvent)e;
context: /*
   * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.  Oracle designates this
   * particular file as subject to the "Classpath" exception as provided
   * by Oracle in the LICENSE file that accompanied this code.
   *
   * This code is distributed in the hope that it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   * version 2 for more details (a copy is included in the LICENSE file that
   * accompanied this code).
   *
   * You should have received a copy of the GNU General Public License version
   * 2 along with this work; if not, write to the Free Software Foundation,
   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   *
   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   * or visit www.oracle.com if you need additional information or have any
   * questions.
   */
  
  package sun.awt.X11;
  
  import java.awt.*;
  import java.awt.peer.*;
  import java.awt.event.*;
  import sun.util.logging.PlatformLogger;
  
  // FIXME: tab traversal should be disabled when mouse is captured (4816336)
  
  // FIXME: key and mouse events should not be delivered to listeners when the Choice is unfurled.  Must override handleNativeKey/MouseEvent (4816336)
  
  // FIXME: test programmatic add/remove/clear/etc
  
  // FIXME: account for unfurling at the edge of the screen
  // Note: can't set x,y on layout(), 'cause moving the top-level to the
  // edge of the screen won't call layout().  Just do it on paint, I guess
  
  // TODO: make painting more efficient (i.e. when down arrow is pressed, only two items should need to be repainted.
  
  public class XChoicePeer extends XComponentPeer implements ChoicePeer, ToplevelStateListener {
      private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XChoicePeer");
  
      private static final int MAX_UNFURLED_ITEMS = 10;  // Maximum number of
      // items to be displayed
      // at a time in an
      // unfurled Choice
      // Description of these constants in ListHelper
      public final static int TEXT_SPACE = 1;
      public final static int BORDER_WIDTH = 1;
      public final static int ITEM_MARGIN = 1;
      public final static int SCROLLBAR_WIDTH = 15;
  
  
      // SHARE THESE!
      private static final Insets focusInsets = new Insets(0,0,0,0);
  
  
      static final int WIDGET_OFFSET = 18;
  
      // Stolen from Tiny
      static final int            TEXT_XPAD = 8;
      static final int            TEXT_YPAD = 6;
  
      // FIXME: Motif uses a different focus color for the item within
      // the unfurled Choice list and for when the Choice itself is focused and
      // popped up.
      static final Color focusColor = Color.black;
  
      // TODO: there is a time value that the mouse is held down.  If short
      // enough,  the Choice stays popped down.  If long enough, Choice
      // is furled when the mouse is released
  
      private boolean unfurled = false;        // Choice list is popped down
  
      private boolean dragging = false;        // Mouse was pressed and is being
                                               // dragged over the (unfurled)
                                               // Choice
  
      private boolean mouseInSB = false;       // Mouse is interacting with the
                                               // scrollbar
  
      private boolean firstPress = false;      // mouse was pressed on
                                               // furled Choice so we
                                               // not need to furl the
                                               // Choice when MOUSE_RELEASED occurred
  
      // 6425067. Mouse was pressed on furled choice and dropdown list appeared over Choice itself
      // and then there were no mouse movements until MOUSE_RELEASE.
      // This scenario leads to ItemStateChanged as the choice logic uses
      // MouseReleased event to send ItemStateChanged. To prevent it we should
      // use a combination of firstPress and wasDragged variables.
      // The only difference in dragging and wasDragged is: last one will not
      // set to false on mouse ungrab. It become false after MouseRelased() finishes.
      private boolean wasDragged = false;
      private ListHelper helper;
      private UnfurledChoice unfurledChoice;
  
      // TODO: Choice remembers where it was scrolled to when unfurled - it's not
      // always to the currently selected item.
  
      // Indicates whether or not to paint selected item in the choice.
      // Default is to paint
      private boolean drawSelectedItem = true;
  
      // If set, indicates components under which choice popup should be showed.
      // The choice's popup width and location should be adjust to appear
      // under both choice and alignUnder component.
      private Component alignUnder;
  
      // If cursor is outside of an unfurled Choice when the mouse is
      // released, Choice item should NOT be updated.  Remember the proper index.
      private int dragStartIdx = -1;
  
      // Holds the listener (XFileDialogPeer) which the processing events from the choice
      // See 6240074 for more information
      private XChoicePeerListener choiceListener;
  
      XChoicePeer(Choice target) {
          super(target);
      }
  
      void preInit(XCreateWindowParams params) {
          super.preInit(params);
          Choice target = (Choice)this.target;
          int numItems = target.getItemCount();
          unfurledChoice = new UnfurledChoice(target);
          getToplevelXWindow().addToplevelStateListener(this);
          helper = new ListHelper(unfurledChoice,
                                  getGUIcolors(),
                                  numItems,
                                  false,
                                  true,
                                  false,
                                  target.getFont(),
                                  MAX_UNFURLED_ITEMS,
                                  TEXT_SPACE,
                                  ITEM_MARGIN,
                                  BORDER_WIDTH,
                                  SCROLLBAR_WIDTH);
      }
  
      void postInit(XCreateWindowParams params) {
          super.postInit(params);
          Choice target = (Choice)this.target;
          int numItems = target.getItemCount();
  
          // Add all items
          for (int i = 0; i < numItems; i++) {
              helper.add(target.getItem(i));
          }
          if (!helper.isEmpty()) {
              helper.select(target.getSelectedIndex());
              helper.setFocusedIndex(target.getSelectedIndex());
          }
          helper.updateColors(getGUIcolors());
          updateMotifColors(getPeerBackground());
      }
  
      public boolean isFocusable() { return true; }
  
      // 6399679. check if super.setBounds() actually changes the size of the
      // component and then compare current Choice size with a new one. If
      // they differs then hide dropdown menu
      public void setBounds(int x, int y, int width, int height, int op) {
          int oldX = this.x;
          int oldY = this.y;
          int oldWidth = this.width;
          int oldHeight = this.height;
          super.setBounds(x, y, width, height, op);
          if (unfurled && (oldX != this.x || oldY != this.y || oldWidth != this.width || oldHeight != this.height) ) {
              hidePopdownMenu();
          }
      }
  
      public void focusGained(FocusEvent e) {
          // TODO: only need to paint the focus bit
          super.focusGained(e);
          repaint();
      }
  
      /*
       * Fix for 6246503 : Disabling a choice after selection locks keyboard, mouse and makes the system unusable, Xtoolkit
       * if setEnabled(false) invoked we should close opened choice in
       * order to prevent keyboard/mouse lock.
       */
      public void setEnabled(boolean value) {
          super.setEnabled(value);
          helper.updateColors(getGUIcolors());
          if (!value && unfurled){
              hidePopdownMenu();
          }
      }
  
      public void focusLost(FocusEvent e) {
          // TODO: only need to paint the focus bit?
          super.focusLost(e);
          repaint();
      }
  
      void ungrabInputImpl() {
          if (unfurled) {
              unfurled = false;
              dragging = false;
              mouseInSB = false;
              unfurledChoice.setVisible(false);
          }
  
          super.ungrabInputImpl();
      }
  
      void handleJavaKeyEvent(KeyEvent e) {
          if (e.getID() == KeyEvent.KEY_PRESSED) {
              keyPressed(e);
          }
      }
  
      public void keyPressed(KeyEvent e) {
          switch(e.getKeyCode()) {
              // UP & DOWN are same if furled or unfurled
            case KeyEvent.VK_DOWN:
            case KeyEvent.VK_KP_DOWN: {
                if (helper.getItemCount() > 1) {
                    helper.down();
                    int newIdx = helper.getSelectedIndex();
  
                    ((Choice)target).select(newIdx);
                    postEvent(new ItemEvent((Choice)target,
                                            ItemEvent.ITEM_STATE_CHANGED,
                                            ((Choice)target).getItem(newIdx),
                                            ItemEvent.SELECTED));
                    repaint();
                }
                break;
            }
            case KeyEvent.VK_UP:
            case KeyEvent.VK_KP_UP: {
                if (helper.getItemCount() > 1) {
                    helper.up();
                    int newIdx = helper.getSelectedIndex();
  
                    ((Choice)target).select(newIdx);
                    postEvent(new ItemEvent((Choice)target,
                                            ItemEvent.ITEM_STATE_CHANGED,
                                            ((Choice)target).getItem(newIdx),
                                            ItemEvent.SELECTED));
                    repaint();
                }
                break;
            }
            case KeyEvent.VK_PAGE_DOWN:
                if (unfurled && !dragging) {
                    int oldIdx = helper.getSelectedIndex();
                    helper.pageDown();
                    int newIdx = helper.getSelectedIndex();
                    if (oldIdx != newIdx) {
                        ((Choice)target).select(newIdx);
                        postEvent(new ItemEvent((Choice)target,
                                                ItemEvent.ITEM_STATE_CHANGED,
                                                ((Choice)target).getItem(newIdx),
                                                ItemEvent.SELECTED));
                        repaint();
                    }
                }
                break;
            case KeyEvent.VK_PAGE_UP:
                if (unfurled && !dragging) {
                    int oldIdx = helper.getSelectedIndex();
                    helper.pageUp();
                    int newIdx = helper.getSelectedIndex();
                    if (oldIdx != newIdx) {
                        ((Choice)target).select(newIdx);
                        postEvent(new ItemEvent((Choice)target,
                                                ItemEvent.ITEM_STATE_CHANGED,
                                                ((Choice)target).getItem(newIdx),
                                                ItemEvent.SELECTED));
                        repaint();
                    }
                }
                break;
            case KeyEvent.VK_ESCAPE:
            case KeyEvent.VK_ENTER:
                if (unfurled) {
                    if (dragging){
                        if (e.getKeyCode() == KeyEvent.VK_ESCAPE){
                            //This also happens on
                            // - MouseButton2,3, etc. press
                            // - ENTER press
                            helper.select(dragStartIdx);
                        } else { //KeyEvent.VK_ENTER:
                            int newIdx = helper.getSelectedIndex();
                            ((Choice)target).select(newIdx);
                            postEvent(new ItemEvent((Choice)target,
                                                    ItemEvent.ITEM_STATE_CHANGED,
                                                    ((Choice)target).getItem(newIdx),
                                                    ItemEvent.SELECTED));
                        }
                    }
                    hidePopdownMenu();
                    dragging = false;
                    wasDragged = false;
                    mouseInSB = false;
  
                    // See 6240074 for more information
                    if (choiceListener != null){
                        choiceListener.unfurledChoiceClosing();
                    }
                }
                break;
            default:
                if (unfurled) {
                    Toolkit.getDefaultToolkit().beep();
                }
                break;
          }
      }
  
      public boolean handlesWheelScrolling() { return true; }
  
      void handleJavaMouseWheelEvent(MouseWheelEvent e) {
          if (unfurled && helper.isVSBVisible()) {
              if (ListHelper.doWheelScroll(helper.getVSB(), null, e)) {
                  repaint();
              }
          }
      }
  
      void handleJavaMouseEvent(MouseEvent e) {
          super.handleJavaMouseEvent(e);
          int i = e.getID();
          switch (i) {
            case MouseEvent.MOUSE_PRESSED:
                mousePressed(e);
                break;
            case MouseEvent.MOUSE_RELEASED:
                mouseReleased(e);
                break;
            case MouseEvent.MOUSE_DRAGGED:
                mouseDragged(e);
                break;
          }
      }
  
      public void mousePressed(MouseEvent e) {
          /*
           * fix for 5003166: a Choice on XAWT shouldn't react to any
           * mouse button presses except left. This involves presses on
           * Choice but not on opened part of choice.
           */
          if (e.getButton() == MouseEvent.BUTTON1){
              dragStartIdx = helper.getSelectedIndex();
              if (unfurled) {
                  //fix 6259328: PIT: Choice scrolls when dragging the parent frame while drop-down is active, XToolkit
                  if (! (isMouseEventInChoice(e) ||
                         unfurledChoice.isMouseEventInside(e)))
                  {
                      hidePopdownMenu();
                  }
                  // Press on unfurled Choice.  Highlight the item under the cursor,
                  // but don't send item event or set the text on the button yet
                  unfurledChoice.trackMouse(e);
              }
              else {
                  // Choice is up - unfurl it
                  grabInput();
                  unfurledChoice.toFront();
                  firstPress = true;
                  wasDragged = false;
                  unfurled = true;
              }
          }
      }
  
      /*
       * helper method for mouseReleased routine
       */
      void hidePopdownMenu(){
          ungrabInput();
          unfurledChoice.setVisible(false);
          unfurled = false;
      }
  
      public void mouseReleased(MouseEvent e) {
          if (unfurled) {
              if (mouseInSB) {
                  unfurledChoice.trackMouse(e);
              }
              else {
                  // We pressed and dragged onto the Choice, or, this is the
                  // second release after clicking to make the Choice "stick"
                  // unfurled.
                  // This release should ungrab/furl, and set the new item if
                  // release was over the unfurled Choice.
  
                  // Fix for 6239944 : Choice shouldn't close its
                  // pop-down menu if user presses Mouse on Choice's Scrollbar
                  // some additional cases like releasing mouse outside
                  // of Choice are considered too
                  boolean isMouseEventInside = unfurledChoice.isMouseEventInside( e );
                  boolean isMouseInListArea = unfurledChoice.isMouseInListArea( e );
  
                  // Fixed 6318746: REG: File Selection is failing
                  // We shouldn't restore the selected item
                  // if the mouse was dragged outside the drop-down choice area
                  if (!helper.isEmpty() && !isMouseInListArea && dragging) {
                      // Set the selected item back how it was.
                      ((Choice)target).select(dragStartIdx);
                  }
  
                  // Choice must be closed if user releases mouse on
                  // pop-down menu on the second click
                  if ( !firstPress && isMouseInListArea) {
                      hidePopdownMenu();
                  }
                  // Choice must be closed if user releases mouse
                  // outside of Choice's pop-down menu  on the second click
                  if ( !firstPress && !isMouseEventInside) {
                      hidePopdownMenu();
                  }
                  //if user drags Mouse on pop-down menu, Scrollbar or
                  // outside the Choice
                  if ( firstPress && dragging) {
                      hidePopdownMenu();
                  }
                  /* this could happen when user has opened a Choice and
                   * released mouse button. Then he drags mouse on the
                   * Scrollbar and releases mouse again.
                   */
                  if ( !firstPress && !isMouseInListArea &&
                       isMouseEventInside && dragging)
                  {
                      hidePopdownMenu();
                  }
  
                  if (!helper.isEmpty()) {
                      // Only update the Choice if the mouse button is released
                      // over the list of items.
                      if (unfurledChoice.isMouseInListArea(e)) {
                          int newIdx = helper.getSelectedIndex();
                          if (newIdx >= 0) {
                              // Update the selected item in the target now that
                              // the mouse selection is complete.
                              if (newIdx != dragStartIdx) {
                                  ((Choice)target).select(newIdx);
                                  // NOTE: We get a repaint when Choice.select()
                                  // calls our peer.select().
                              }
                              if (wasDragged && e.getButton() != MouseEvent.BUTTON1){
                                  ((Choice)target).select(dragStartIdx);
                              }
  
                              /*fix for 6239941 : Choice triggers ItemEvent when selecting an item with right mouse button, Xtoolkit
                              * We should generate ItemEvent if only
                              * LeftMouseButton used */
                              if (e.getButton() == MouseEvent.BUTTON1 &&
                                  (!firstPress || wasDragged ))
                              {
                                  postEvent(new ItemEvent((Choice)target,
                                                          ItemEvent.ITEM_STATE_CHANGED,
                                                          ((Choice)target).getItem(newIdx),
                                                          ItemEvent.SELECTED));
                              }
  
                              // see 6240074 for more information
                              if (choiceListener != null) {
                                  choiceListener.unfurledChoiceClosing();
                              }
                          }
                      }
                  }
                  // See 6243382 for more information
                  unfurledChoice.trackMouse(e);
              }
          }
  
          dragging = false;
          wasDragged = false;
          firstPress = false;
          dragStartIdx = -1;
      }
  
      public void mouseDragged(MouseEvent e) {
          /*
           * fix for 5003166. On Motif user are unable to drag
           * mouse inside opened Choice if he drags the mouse with
           * different from LEFT mouse button ( e.g. RIGHT or MIDDLE).
           * This fix make impossible to drag mouse inside opened choice
           * with other mouse buttons rather then LEFT one.
           */
          if ( e.getModifiers() == MouseEvent.BUTTON1_MASK ){
              dragging = true;
              wasDragged = true;
              unfurledChoice.trackMouse(e);
          }
      }
  
      // Stolen from TinyChoicePeer
      public Dimension getMinimumSize() {
          // TODO: move this impl into ListHelper?
          FontMetrics fm = getFontMetrics(target.getFont());
          Choice c = (Choice)target;
          int w = 0;
          for (int i = c.countItems() ; i-- > 0 ;) {
              w = Math.max(fm.stringWidth(c.getItem(i)), w);
          }
          return new Dimension(w + TEXT_XPAD + WIDGET_OFFSET,
                               fm.getMaxAscent() + fm.getMaxDescent() + TEXT_YPAD);
      }
  
      /*
       * Layout the...
       */
      public void layout() {
          /*
            Dimension size = target.getSize();
            Font f = target.getFont();
            FontMetrics fm = target.getFontMetrics(f);
            String text = ((Choice)target).getLabel();
  
            textRect.height = fm.getHeight();
  
            checkBoxSize = getChoiceSize(fm);
  
            // Note - Motif appears to use an left inset that is slightly
            // scaled to the checkbox/font size.
            cbX = borderInsets.left + checkBoxInsetFromText;
            cbY = size.height / 2 - checkBoxSize / 2;
            int minTextX = borderInsets.left + 2 * checkBoxInsetFromText + checkBoxSize;
            // FIXME: will need to account for alignment?
            // FIXME: call layout() on alignment changes
            //textRect.width = fm.stringWidth(text);
            textRect.width = fm.stringWidth(text == null ? "" : text);
            textRect.x = Math.max(minTextX, size.width / 2 - textRect.width / 2);
            textRect.y = size.height / 2 - textRect.height / 2 + borderInsets.top;
  
            focusRect.x = focusInsets.left;
            focusRect.y = focusInsets.top;
            focusRect.width = size.width-(focusInsets.left+focusInsets.right)-1;
            focusRect.height = size.height-(focusInsets.top+focusInsets.bottom)-1;
  
            myCheckMark = AffineTransform.getScaleInstance((double)target.getFont().getSize() / MASTER_SIZE, (double)target.getFont().getSize() / MASTER_SIZE).createTransformedShape(MASTER_CHECKMARK);
          */
  
      }
  
      /**
       * Paint the choice
       */
      public void paint(Graphics g) {
          flush();
          Dimension size = getPeerSize();
  
          // TODO: when mouse is down over button, widget should be drawn depressed
          g.setColor(getPeerBackground());
          g.fillRect(0, 0, width, height);
  
          drawMotif3DRect(g, 1, 1, width-2, height-2, false);
          drawMotif3DRect(g, width - WIDGET_OFFSET, (height / 2) - 3, 12, 6, false);
  
          if (!helper.isEmpty() && helper.getSelectedIndex() != -1) {
              g.setFont(getPeerFont());
              FontMetrics fm = g.getFontMetrics();
              String lbl = helper.getItem(helper.getSelectedIndex());
              if (lbl != null && drawSelectedItem) {
                  g.setClip(1, 1, width - WIDGET_OFFSET - 2, height);
                  if (isEnabled()) {
                      g.setColor(getPeerForeground());
                      g.drawString(lbl, 5, (height + fm.getMaxAscent()-fm.getMaxDescent())/2);
                  }
                  else {
                      g.setColor(getPeerBackground().brighter());
                      g.drawString(lbl, 5, (height + fm.getMaxAscent()-fm.getMaxDescent())/2);
                      g.setColor(getPeerBackground().darker());
                      g.drawString(lbl, 4, ((height + fm.getMaxAscent()-fm.getMaxDescent())/2)-1);
                  }
                  g.setClip(0, 0, width, height);
              }
          }
          if (hasFocus()) {
              paintFocus(g,focusInsets.left,focusInsets.top,size.width-(focusInsets.left+focusInsets.right)-1,size.height-(focusInsets.top+focusInsets.bottom)-1);
          }
          if (unfurled) {
              unfurledChoice.repaint();
          }
          flush();
      }
  
      protected void paintFocus(Graphics g,
                                int x, int y, int w, int h) {
          g.setColor(focusColor);
          g.drawRect(x,y,w,h);
      }
  
  
  
      /*
       * ChoicePeer methods stolen from TinyChoicePeer
       */
  
      public void select(int index) {
          helper.select(index);
          helper.setFocusedIndex(index);
          repaint();
      }
  
      public void add(String item, int index) {
          helper.add(item, index);
          repaint();
      }
  
      public void remove(int index) {
          boolean selected = (index == helper.getSelectedIndex());
          boolean visibled = (index >= helper.firstDisplayedIndex() && index <= helper.lastDisplayedIndex());
          helper.remove(index);
          if (selected) {
              if (helper.isEmpty()) {
                  helper.select(-1);
              }
              else {
                  helper.select(0);
              }
          }
          /*
           * Fix for 6248016
           * After removing the item of the choice we need to reshape unfurled choice
           * in order to keep actual bounds of the choice
           */
  
          /*
           * condition added only for performance
           */
          if (!unfurled) {
              // Fix 6292186: PIT: Choice is not refreshed properly when the last item gets removed, XToolkit
              // We should take into account that there is no 'select' invoking (hence 'repaint')
              // if the choice is empty (see Choice.java method removeNoInvalidate())
              // The condition isn't 'visibled' since it would be cause of the twice repainting
              if (helper.isEmpty()) {
                  repaint();
              }
              return;
          }
  
          /*
           * condition added only for performance
           * the count of the visible items changed
           */
          if (visibled){
              Rectangle r = unfurledChoice.placeOnScreen();
              unfurledChoice.reshape(r.x, r.y, r.width, r.height);
              return;
          }
  
          /*
           * condition added only for performance
           * the structure of visible items changed
           * if removable item is non visible and non selected then there is no repaint
           */
          if (visibled || selected){
              repaint();
          }
      }
  
      public void removeAll() {
          helper.removeAll();
          helper.select(-1);
          /*
           * Fix for 6248016
           * After removing the item of the choice we need to reshape unfurled choice
           * in order to keep actual bounds of the choice
           */
          Rectangle r = unfurledChoice.placeOnScreen();
          unfurledChoice.reshape(r.x, r.y, r.width, r.height);
          repaint();
      }
  
      /**
       * DEPRECATED: Replaced by add(String, int).
       */
      public void addItem(String item, int index) {
          add(item, index);
      }
  
      public void setFont(Font font) {
          super.setFont(font);
          helper.setFont(this.font);
      }
  
      public void setForeground(Color c) {
          super.setForeground(c);
          helper.updateColors(getGUIcolors());
      }
  
      public void setBackground(Color c) {
          super.setBackground(c);
          unfurledChoice.setBackground(c);
          helper.updateColors(getGUIcolors());
          updateMotifColors(c);
      }
  
      public void setDrawSelectedItem(boolean value) {
          drawSelectedItem = value;
      }
  
      public void setAlignUnder(Component comp) {
          alignUnder = comp;
      }
  
      // see 6240074 for more information
      public void addXChoicePeerListener(XChoicePeerListener l){
          choiceListener = l;
      }
  
      // see 6240074 for more information
      public void removeXChoicePeerListener(){
          choiceListener = null;
      }
  
      public boolean isUnfurled(){
          return unfurled;
      }
  
      /* fix for 6261352. We should detect if current parent Window (containing a Choice) become iconified and hide pop-down menu with grab release.
       * In this case we should hide pop-down menu.
       */
      //calls from XWindowPeer. Could accept X-styled state events
      public void stateChangedICCCM(int oldState, int newState) {
          if (unfurled && oldState != newState){
                  hidePopdownMenu();
          }
      }
  
      //calls from XFramePeer. Could accept Frame's states.
      public void stateChangedJava(int oldState, int newState) {
          if (unfurled && oldState != newState){
              hidePopdownMenu();
          }
      }
  
      /**************************************************************************/
      /* Common functionality between List & Choice
         /**************************************************************************/
  
      /**
       * Inner class for the unfurled Choice list
       * Much, much more docs
       */
      class UnfurledChoice extends XWindow /*implements XScrollbarClient*/ {
  
          // First try - use Choice as the target
  
          public UnfurledChoice(Component target) {
              super(target);
          }
  
          // Override so we can do our own create()
          public void preInit(XCreateWindowParams params) {
              // A parent of this window is the target, at this point: wrong.
              // Remove parent window; in the following preInit() call we'll calculate as a default
              // a correct root window which is the proper parent for override redirect.
              params.delete(PARENT_WINDOW);
              super.preInit(params);
              // Reset bounds(we'll set them later), set overrideRedirect
              params.remove(BOUNDS);
              params.add(OVERRIDE_REDIRECT, Boolean.TRUE);
          }
  
          // Generally, bounds should be:
          //  x = target.x
          //  y = target.y + target.height
          //  w = Max(target.width, getLongestItemWidth) + possible vertScrollbar
          //  h = Min(MAX_UNFURLED_ITEMS, target.getItemCount()) * itemHeight
          Rectangle placeOnScreen() {
              int numItemsDisplayed;
              // Motif paints an empty Choice the same size as a single item
              if (helper.isEmpty()) {
                  numItemsDisplayed = 1;
              }
              else {
                  int numItems = helper.getItemCount();
                  numItemsDisplayed = Math.min(MAX_UNFURLED_ITEMS, numItems);
              }
              Point global = XChoicePeer.this.toGlobal(0,0);
              Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
  
              if (alignUnder != null) {
                  Rectangle choiceRec = XChoicePeer.this.getBounds();
                  choiceRec.setLocation(0, 0);
                  choiceRec = XChoicePeer.this.toGlobal(choiceRec);
                  Rectangle alignUnderRec = new Rectangle(alignUnder.getLocationOnScreen(), alignUnder.getSize()); // TODO: Security?
                  Rectangle result = choiceRec.union(alignUnderRec);
                  // we've got the left and width, calculate top and height
                  width = result.width;
                  x = result.x;
                  y = result.y + result.height;
                  height = 2*BORDER_WIDTH +
                      numItemsDisplayed*(helper.getItemHeight()+2*ITEM_MARGIN);
              } else {
                  x = global.x;
                  y = global.y + XChoicePeer.this.height;
                  width = Math.max(XChoicePeer.this.width,
                                   helper.getMaxItemWidth() + 2 * (BORDER_WIDTH + ITEM_MARGIN + TEXT_SPACE) + (helper.isVSBVisible() ? SCROLLBAR_WIDTH : 0));
                  height = 2*BORDER_WIDTH +
                      numItemsDisplayed*(helper.getItemHeight()+2*ITEM_MARGIN);
              }
              // Don't run off the edge of the screen
              if (x < 0) {
                  x = 0;
              }
              else if (x + width > screen.width) {
                  x = screen.width - width;
              }
  
              if (y < 0) {
                  y = 0;
              }
              else if (y + height > screen.height) {
                  y = screen.height - height;
              }
              return new Rectangle(x, y, width, height);
          }
  
          public void toFront() {
              // see 6240074 for more information
              if (choiceListener != null)
                  choiceListener.unfurledChoiceOpening(helper);
  
              Rectangle r = placeOnScreen();
              reshape(r.x, r.y, r.width, r.height);
              super.toFront();
              setVisible(true);
          }
  
          /*
           * Track a MouseEvent (either a drag or a press) and paint a new
           * selected item, if necessary.
           */
          // FIXME: first unfurl after move is not at edge of the screen  onto second monitor doesn't
          // track mouse correctly.  Problem is w/ UnfurledChoice coords
          public void trackMouse(MouseEvent e) {
              // Event coords are relative to the button, so translate a bit
              Point local = toLocalCoords(e);
  
              // If x,y is over unfurled Choice,
              // highlight item under cursor
  
              switch (e.getID()) {
                case MouseEvent.MOUSE_PRESSED:
                    // FIXME: If the Choice is unfurled and the mouse is pressed
                    // outside of the Choice, the mouse should ungrab on the
                    // the press, not the release
                    if (helper.isInVertSB(getBounds(), local.x, local.y)) {
                        mouseInSB = true;
                        helper.handleVSBEvent(e, getBounds(), local.x, local.y);
                    }
                    else {
                        trackSelection(local.x, local.y);
                    }
                    break;
                case MouseEvent.MOUSE_RELEASED:
                    if (mouseInSB) {
                        mouseInSB = false;
                        helper.handleVSBEvent(e, getBounds(), local.x, local.y);
                    }else{
                        // See 6243382 for more information
                        helper.trackMouseReleasedScroll();
                    }
                    /*
                      else {
                      trackSelection(local.x, local.y);
                      }
                    */
                    break;
                case MouseEvent.MOUSE_DRAGGED:
                    if (mouseInSB) {
                        helper.handleVSBEvent(e, getBounds(), local.x, local.y);
                    }
                    else {
                        // See 6243382 for more information
                        helper.trackMouseDraggedScroll(local.x, local.y, width, height);
                        trackSelection(local.x, local.y);
                    }
                    break;
              }
          }
  
          private void trackSelection(int transX, int transY) {
              if (!helper.isEmpty()) {
                  if (transX > 0 && transX < width &&
                      transY > 0 && transY < height) {
                      int newIdx = helper.y2index(transY);
                      if (log.isLoggable(PlatformLogger.Level.FINE)) {
                          log.fine("transX=" + transX + ", transY=" + transY
                                   + ",width=" + width + ", height=" + height
                                   + ", newIdx=" + newIdx + " on " + target);
                      }
                      if ((newIdx >=0) && (newIdx < helper.getItemCount())
                          && (newIdx != helper.getSelectedIndex()))
                      {
                          helper.select(newIdx);
                          unfurledChoice.repaint();
                      }
                  }
              }
              // FIXME: If dragged off top or bottom, scroll if there's a vsb
              // (ICK - we'll need a timer or our own event or something)
          }
  
          /*
           * fillRect with current Background color on the whole dropdown list.
           */
          public void paintBackground(){
              Graphics g = getGraphics();
              g.setColor(getPeerBackground());
              g.fillRect(0, 0, width, height);
          }
  
          /*
           * 6405689. In some cases we should erase background to eliminate painting
           * artefacts.
           */
          public void repaint() {
              if (!isVisible()) {
                  return;
              }
              if (helper.checkVsbVisibilityChangedAndReset()){
                  paintBackground();
              }
              super.repaint();
          }
  
          public void paint(Graphics g) {
              //System.out.println("UC.paint()");
              Choice choice = (Choice)target;
              Color colors[] = XChoicePeer.this.getGUIcolors();
              draw3DRect(g, getSystemColors(), 0, 0, width - 1, height - 1, true);
              draw3DRect(g, getSystemColors(), 1, 1, width - 3, height - 3, true);
  
              helper.paintAllItems(g,
                                   colors,
                                   getBounds());
          }
  
          public void setVisible(boolean vis) {
              xSetVisible(vis);
  
              if (!vis && alignUnder != null) {
                  alignUnder.requestFocusInWindow();
              }
          }
  
          /**
           * Return a MouseEvent's Point in coordinates relative to the
           * UnfurledChoice.
           */
          private Point toLocalCoords(MouseEvent e) {
              // Event coords are relative to the button, so translate a bit
              Point global = e.getLocationOnScreen();
  
              global.x -= x;
              global.y -= y;
              return global;
          }
  
          /* Returns true if the MouseEvent coords (which are based on the Choice)
           * are inside of the UnfurledChoice.
           */
          private boolean isMouseEventInside(MouseEvent e) {
              Point local = toLocalCoords(e);
              if (local.x > 0 && local.x < width &&
                  local.y > 0 && local.y < height) {
                  return true;
              }
              return false;
          }
  
          /**
           * Tests if the mouse cursor is in the Unfurled Choice, yet not
           * in the vertical scrollbar
           */
          private boolean isMouseInListArea(MouseEvent e) {
              if (isMouseEventInside(e)) {
                  Point local = toLocalCoords(e);
                  Rectangle bounds = getBounds();
                  if (!helper.isInVertSB(bounds, local.x, local.y)) {
                      return true;
                  }
              }
              return false;
          }
  
          /*
           * Overridden from XWindow() because we don't want to send
           * ComponentEvents
           */
          public void handleConfigureNotifyEvent(XEvent xev) {}
          public void handleMapNotifyEvent(XEvent xev) {}
          public void handleUnmapNotifyEvent(XEvent xev) {}
      } //UnfurledChoice
  
      public void dispose() {
          if (unfurledChoice != null) {
              unfurledChoice.destroy();
          }
          super.dispose();
      }
  
      /*
       * fix for 6239938 : Choice drop-down does not disappear when it loses
       * focus, on XToolkit
       * We are able to handle all _Key_ events received by Choice when
       * it is in opened state without sending it to EventQueue.
       * If Choice is in closed state we should behave like before: send
       * all events to EventQueue.
       * To be compatible with Motif we should handle all KeyEvents in
       * Choice if it is opened. KeyEvents should be sent into Java if Choice is not opened.
       */
      boolean prePostEvent(final AWTEvent e) {
          if (unfurled){
              // fix for 6253211: PIT: MouseWheel events not triggered for Choice drop down in XAWT
              if (e instanceof MouseWheelEvent){
                  return super.prePostEvent(e);
              }
              //fix 6252982: PIT: Keyboard FocusTraversal not working when choice's drop-down is visible, on XToolkit
              if (e instanceof KeyEvent){
                  // notify XWindow that this event had been already handled and no need to post it again
                  InvocationEvent ev = new InvocationEvent(target, new Runnable() {
                      public void run() {
                          if(target.isFocusable() &&
                                  getParentTopLevel().isFocusableWindow() )
                          {
                              handleJavaKeyEvent((KeyEvent)e);
                          }
                      }
                  });
                  postEvent(ev);
  
                  return true;
              } else {
                  if (e instanceof MouseEvent){
                      // Fix for 6240046 : REG:Choice's Drop-down does not disappear when clicking somewhere, after popup menu is disposed
                      // if user presses Right Mouse Button on opened (unfurled)
                      // Choice then we mustn't open a popup menu. We could filter
                      // Mouse Events and handle them in XChoicePeer if Choice
                      // currently in opened state.
  