home *** CD-ROM | disk | FTP | other *** search
/ Chip 1998 November / Chip_1998-11_cd.bin / tema / Cafe / jfc.bin / BasicComboBoxUI.java < prev    next >
Text File  |  1998-02-26  |  37KB  |  1,016 lines

  1. /*
  2.  * @(#)BasicComboBoxUI.java    1.63 98/02/02
  3.  * 
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  */
  20. package com.sun.java.swing.plaf.basic;
  21.  
  22. import java.awt.*;
  23. import java.awt.event.*;
  24. import com.sun.java.swing.*;
  25. import com.sun.java.swing.FocusManager;
  26. import com.sun.java.swing.plaf.*;
  27. import com.sun.java.swing.border.*;
  28. import java.io.Serializable;
  29. import java.beans.PropertyChangeListener;
  30. import java.beans.PropertyChangeEvent;
  31.  
  32. /**
  33.  * Basic UI for JComboBox
  34.  * <p>
  35.  * Warning: serialized objects of this class will not be compatible with
  36.  * future swing releases.  The current serialization support is appropriate 
  37.  * for short term storage or RMI between Swing1.0 applications.  It will
  38.  * not be possible to load serialized Swing1.0 objects with future releases
  39.  * of Swing.  The JDK1.2 release of Swing will be the compatibility
  40.  * baseline for the serialized form of Swing objects.
  41.  *
  42.  * @version 1.63 02/02/98
  43.  * @author Arnaud Weber
  44.  */
  45. public class BasicComboBoxUI 
  46.     extends ComboBoxUI 
  47.     implements 
  48.       LayoutManager, MouseListener, MouseMotionListener, ItemListener, 
  49.       FocusListener, KeyListener, PropertyChangeListener, Serializable 
  50. {
  51.         protected final static Color selectionBackgroundColor = new Color(0,0,128);
  52.         protected static final int BORDER_THICKNESS = 2;
  53.         protected JComboBox comboBox;
  54.         protected CellRendererPane currentValuePane = new CellRendererPane();
  55.         protected JButton   arrowButton;
  56.         protected Component editor;
  57.         protected JPopupMenu menu;
  58.         protected JList   listBox = null;
  59.         protected JScrollPane scrollPane;
  60.         protected Timer     autoscrollTimer;
  61.         protected Point      lastMouseLocation;
  62.         protected static JComboBox showingComboBox = null;
  63.     /** Location of the comboBox last time it was popped up (showPopup). */
  64.     protected Point      popupLocation;
  65.         protected boolean    hasFocus = false;
  66.  
  67.     public static ComponentUI createUI(JComponent c) {
  68.         return new BasicComboBoxUI();
  69.     }
  70.  
  71.     public void installUI(JComponent c) {
  72.         comboBox = (JComboBox)c;
  73.         addArrowButton();
  74.         comboBox.add(currentValuePane);
  75.         comboBox.setLayout(this);
  76.         comboBox.addItemListener(this);
  77.         comboBox.addFocusListener(this);
  78.         comboBox.addMouseListener(this);
  79.         comboBox.addMouseMotionListener(this);
  80.         comboBox.addKeyListener(this);
  81.         comboBox.addPropertyChangeListener(this);
  82.         if(comboBox.getRenderer() == null)
  83.             comboBox.setRenderer((ListCellRenderer)(UIManager.get("ComboBox.renderer")));
  84.         editablePropertyChanged();
  85.         addKeyAccelerators(comboBox);
  86.         configureComboBox();
  87.     }
  88.  
  89.     public void uninstallUI(JComponent c) {
  90.         hidePopup();
  91.         removeEditor();
  92.         removeArrowButton();
  93.         comboBox.setLayout(null);
  94.         comboBox.removeFocusListener(this);
  95.  
  96.         if(listBox != null) {
  97.             listBox.removeMouseListener(this);
  98.             listBox.removeMouseMotionListener(this);
  99.         }
  100.         comboBox.removeItemListener(this);
  101.         comboBox.removeMouseListener(this);
  102.         comboBox.removeMouseMotionListener(this);
  103.         comboBox.removeKeyListener(this);
  104.         comboBox.resetKeyboardActions();
  105.         comboBox.remove(currentValuePane);
  106.         comboBox.removePropertyChangeListener(this);
  107.         if(comboBox.getRenderer() instanceof UIResource)
  108.             comboBox.setRenderer(null);
  109.  
  110.         if(comboBox.getEditor() instanceof UIResource)
  111.             comboBox.setEditor(null);
  112.         comboBox = null;
  113.         unconfigureComboBox();
  114.     }
  115.  
  116.     public boolean isFocusTraversable() {
  117.       return !comboBox.isEditable();
  118.     }
  119.  
  120.     protected void configureComboBox() {
  121.         LookAndFeel.installColorsAndFont(comboBox,"ComboBox.background","ComboBox.foreground",
  122.                                               "ComboBox.font");
  123.     }
  124.  
  125.     protected void unconfigureComboBox() {
  126.     }
  127.     
  128.     public void editablePropertyChanged() {
  129.         if(comboBox.isEditable())
  130.             addEditor();
  131.         else
  132.             removeEditor();
  133.     }
  134.  
  135.     public void enablePropertyChanged() {
  136.         boolean cbIsEnabled = comboBox.isEnabled();
  137.         if(cbIsEnabled) {
  138.             if(editor != null) 
  139.                 editor.setEnabled(true);
  140.             if(arrowButton != null)
  141.                 arrowButton.setEnabled(true);
  142.         } else {
  143.             if(editor != null)
  144.                 editor.setEnabled(false);
  145.             if(arrowButton != null)
  146.                 arrowButton.setEnabled(false);
  147.         }
  148.         comboBox.repaint();
  149.     }
  150.  
  151.     public void addEditor() {
  152.         if(editor != null)
  153.             removeEditor();
  154.         if(comboBox.getEditor() == null)
  155.             comboBox.setEditor((ComboBoxEditor)(UIManager.get("ComboBox.editor")));
  156.         editor = comboBox.getEditor().getEditorComponent();
  157.         comboBox.add(editor);
  158.         editor.setFont(comboBox.getFont());
  159.         editor.setBackground(comboBox.getBackground());
  160.         editor.setForeground(comboBox.getForeground());
  161.         comboBox.configureEditor(comboBox.getEditor(),comboBox.getSelectedItem());
  162.     }
  163.  
  164.     public void removeEditor() {
  165.         if(editor != null) {
  166.             comboBox.remove(editor);
  167.             editor=null;
  168.         }
  169.     }
  170.  
  171.     public void addArrowButton() {
  172.         arrowButton = createArrowButton();
  173.         arrowButton.setRequestFocusEnabled(false);
  174.         arrowButton.addMouseListener(this);
  175.         arrowButton.addMouseMotionListener(this);
  176.         arrowButton.resetKeyboardActions();
  177.         comboBox.add(arrowButton);
  178.     }
  179.  
  180.     public void removeArrowButton() {
  181.         if(arrowButton != null) {
  182.             comboBox.remove(arrowButton);
  183.             arrowButton.removeMouseListener(this);
  184.             arrowButton.removeMouseMotionListener(this);
  185.             arrowButton = null;
  186.         }
  187.     }
  188.  
  189.     protected JList createListBox(ComboBoxModel aModel) {
  190.         return new JList(aModel);
  191.     }
  192.  
  193.     public JList getList() {
  194.     return listBox;
  195.     }
  196.  
  197.     protected JButton createArrowButton() {
  198.         return new BasicArrowButton(BasicArrowButton.SOUTH);
  199.     }
  200.  
  201.     public void paint(Graphics g, JComponent c) {
  202.         Rectangle b = c.getBounds();
  203.         BasicGraphicsUtils.drawEtchedRect(g,0,0,b.width,b.height);
  204.         boolean hasFocus = comboBox.hasFocus();
  205.         if(!comboBox.isEditable()) {
  206.             Rectangle r = rectangleForCurrentValue();
  207.             paintCurrentValueBackground(g,r,hasFocus);
  208.             paintCurrentValue(g,r,hasFocus);
  209.         }
  210.     }
  211.  
  212.     public void paintCurrentValue(Graphics g,Rectangle bounds,boolean hasFocus) {
  213.         ListCellRenderer renderer = comboBox.getRenderer();
  214.         Component c;
  215.         validateMenu();
  216.         if(hasFocus && !popupIsVisible()) {
  217.             c = renderer.getListCellRendererComponent(listBox, comboBox.getSelectedItem(), -1, true, false);
  218.         } else {
  219.             c = renderer.getListCellRendererComponent(listBox, comboBox.getSelectedItem(), -1, false, false);
  220.             c.setBackground(UIManager.getColor("ComboBox.background"));
  221.         }
  222.         c.setFont(comboBox.getFont());
  223.         if(hasFocus && !popupIsVisible()) {
  224.             c.setForeground(listBox.getSelectionForeground());
  225.             c.setBackground(listBox.getSelectionBackground());
  226.         } else {
  227.             if(comboBox.isEnabled()) {
  228.                 c.setForeground(comboBox.getForeground());
  229.                 c.setBackground(comboBox.getBackground());
  230.             } else {
  231.                 c.setForeground(UIManager.getColor("ComboBox.disabledForeground"));
  232.                 c.setBackground(UIManager.getColor("ComboBox.disabledBackground"));
  233.             }
  234.         }
  235.         currentValuePane.paintComponent(g,c,comboBox,bounds.x,bounds.y,
  236.                                         bounds.width,bounds.height);
  237.     }
  238.  
  239.     public void paintCurrentValueBackground(Graphics g,Rectangle bounds,boolean hasFocus) {
  240.         Color t = g.getColor();
  241.         if(comboBox.isEnabled())
  242.             g.setColor(UIManager.getColor("ComboBox.background"));
  243.         else
  244.             g.setColor(UIManager.getColor("ComboBox.disabledBackground"));
  245.         g.fillRect(bounds.x,bounds.y,bounds.width,bounds.height);
  246.         g.setColor(t);
  247.     }
  248.  
  249.     /** Return the dimension the the combo box should have if by default
  250.      *  if there is no current value and no value in the list of possible
  251.      *  values
  252.      */
  253.     protected Dimension getDefaultSize() {
  254.         return new Dimension(100,20);
  255.     }
  256.  
  257.     protected Dimension getMaxPreferredSize() {
  258.         int i,c;
  259.         Dimension result = new Dimension();
  260.         ListCellRenderer renderer = comboBox.getRenderer();
  261.         ComboBoxModel model = comboBox.getModel();
  262.         Component cpn;
  263.         Dimension d;
  264.  
  265.         validateMenu();
  266.         if(renderer != null && model.getSize() > 0) {
  267.             for(i=0,c=model.getSize();i<c;i++) {
  268.                 cpn = renderer.getListCellRendererComponent(listBox, model.getElementAt(i),-1, false, false);
  269.                 currentValuePane.add(cpn);
  270.                 cpn.setFont(comboBox.getFont());
  271.                 d = cpn.getPreferredSize();
  272.                 currentValuePane.remove(cpn);
  273.                 result.width = Math.max(result.width,d.width);
  274.                 result.height = Math.max(result.height,d.height);
  275.             }
  276.  
  277.             if(comboBox.isEditable()) {
  278.                 d = editor.getPreferredSize();
  279.                 result.width = Math.max(result.width,d.width);
  280.                 result.height = Math.max(result.height,d.height);
  281.             }
  282.                 
  283.             return result;
  284.         } else
  285.             return getDefaultSize();
  286.  
  287.     }
  288.  
  289.     protected Dimension getMaxMinimumSize() {
  290.         int i,c;
  291.         Dimension result = new Dimension();
  292.         ListCellRenderer renderer = comboBox.getRenderer();
  293.         ComboBoxModel model = comboBox.getModel();
  294.         Component cpn;
  295.         Dimension d;
  296.  
  297.         validateMenu();
  298.         if(renderer != null && model.getSize() > 0) {
  299.             for(i=0,c=model.getSize();i<c;i++) {
  300.                 cpn = renderer.getListCellRendererComponent(listBox, model.getElementAt(i),-1, false, false);
  301.                 currentValuePane.add(cpn);
  302.                 cpn.setFont(comboBox.getFont());
  303.                 d = cpn.getMinimumSize();
  304.                 currentValuePane.remove(cpn);
  305.                 result.width = Math.max(result.width,d.width);
  306.                 result.height = Math.max(result.height,d.height);
  307.             }
  308.             if(comboBox.isEditable()) {
  309.                 d = editor.getMinimumSize();
  310.                 result.width = Math.max(result.width,d.width);
  311.                 result.height = Math.max(result.height,d.height);
  312.             }
  313.             return result;
  314.         } else
  315.             return getDefaultSize();
  316.  
  317.     }
  318.  
  319.     protected Dimension getMaxMaximumSize() {
  320.         int i,c;
  321.         Dimension result = new Dimension();
  322.         ListCellRenderer renderer = comboBox.getRenderer();
  323.         ComboBoxModel model = comboBox.getModel();
  324.         Component cpn;
  325.         Dimension d;
  326.  
  327.     validateMenu();
  328.         if(renderer != null && model.getSize() > 0) {
  329.             for(i=0,c=model.getSize();i<c;i++) {
  330.                 cpn = renderer.getListCellRendererComponent(listBox, model.getElementAt(i),-1, false, false);
  331.                 currentValuePane.add(cpn);
  332.                 cpn.setFont(comboBox.getFont());
  333.                 d = cpn.getMaximumSize();
  334.                 currentValuePane.remove(cpn);
  335.                 result.width = Math.max(result.width,d.width);
  336.                 result.height = Math.max(result.height,d.height);
  337.             }
  338.             if(comboBox.isEditable()) {
  339.                 d = editor.getMaximumSize();
  340.                 result.width = Math.max(result.width,d.width);
  341.                 result.height = Math.max(result.height,d.height);
  342.             }
  343.             return result;
  344.         } else
  345.             return getDefaultSize();
  346.     }
  347.  
  348.     public Dimension getPreferredSize(JComponent c) {
  349.         Dimension dim;
  350.         int buttonSize;
  351.         dim = getMaxPreferredSize();
  352.         dim.height += (2*BORDER_THICKNESS);
  353.         buttonSize = dim.height - (2*BORDER_THICKNESS);
  354.         dim.width +=  ((2*BORDER_THICKNESS) + buttonSize);
  355.         return dim;
  356.     }
  357.  
  358.     public Dimension getMinimumSize(JComponent c) {
  359.         Dimension dim;
  360.         int buttonSize;
  361.         dim = getMaxMinimumSize();
  362.         dim.height += (2*BORDER_THICKNESS);
  363.         buttonSize = dim.height - (2*BORDER_THICKNESS);
  364.         dim.width +=  ((2*BORDER_THICKNESS) + buttonSize);
  365.         return dim;
  366.     }
  367.  
  368.     public Dimension getMaximumSize(JComponent c) {
  369.         Dimension dim;
  370.         dim = getMaxMaximumSize();
  371.         dim.height += (2*BORDER_THICKNESS);
  372.         dim.width   = Short.MAX_VALUE;
  373.         return dim;
  374.     }
  375.  
  376.     public void addLayoutComponent(String name, Component comp) {}
  377.     public void removeLayoutComponent(Component comp) {}
  378.  
  379.     public Dimension preferredLayoutSize(Container parent) {
  380.         JComboBox cb = (JComboBox)parent;
  381.         return parent.getPreferredSize();
  382.     }
  383.  
  384.     public Dimension minimumLayoutSize(Container parent) {
  385.         JComboBox cb = (JComboBox)parent;
  386.         return parent.getMinimumSize();
  387.     }
  388.  
  389.     protected Rectangle rectangleForCurrentValue() {
  390.         Dimension cbSize = comboBox.getSize();
  391.     int buttonSize = cbSize.height - (2*BORDER_THICKNESS);
  392.         return new Rectangle(BORDER_THICKNESS,BORDER_THICKNESS,
  393.                              cbSize.width - (2*BORDER_THICKNESS) - buttonSize,
  394.                              cbSize.height - (2*BORDER_THICKNESS));
  395.     }
  396.  
  397.     public void layoutContainer(Container parent) {
  398.         JComboBox cb = (JComboBox)parent;
  399.         Dimension cbSize = cb.getSize();
  400.         int buttonSize = cbSize.height - (2*BORDER_THICKNESS);
  401.         Rectangle cvb;
  402.         
  403.         if(editor != null){
  404.             cvb = rectangleForCurrentValue();
  405.             editor.setBounds(cvb);
  406.         }
  407.         if(arrowButton != null)
  408.             arrowButton.setBounds(BORDER_THICKNESS + cbSize.width - (2*BORDER_THICKNESS) - buttonSize,
  409.                                   BORDER_THICKNESS,
  410.                                   buttonSize,buttonSize);
  411.       }
  412.  
  413.     public void itemStateChanged(ItemEvent e) {
  414.         ComboBoxModel model = comboBox.getModel();
  415.         Object v = model.getSelectedItem();
  416.         if(editor != null)
  417.             comboBox.configureEditor(comboBox.getEditor(),v);
  418.         else {
  419.             Rectangle r = rectangleForCurrentValue();
  420.             comboBox.repaint(0,r.x,r.y,r.width,r.height);
  421.         }
  422.         if(popupIsVisible())
  423.             updateListBoxSelection();       
  424.     }
  425.  
  426.     public void focusGained(FocusEvent e) {
  427.         if(!comboBox.isEditable()) {
  428.             repaintCurrentValue();
  429.             hasFocus = true;
  430.         } 
  431.     }
  432.  
  433.     public void focusLost(FocusEvent e) {
  434.         if(!comboBox.isEditable()) {
  435.             repaintCurrentValue();
  436.             hasFocus = false;
  437.         }
  438.     }
  439.  
  440.     public void propertyChange(PropertyChangeEvent e) {
  441.         String propertyName = e.getPropertyName();
  442.         
  443.         if (propertyName.equals("model")) {
  444.             if(listBox != null) {
  445.                 listBox.setModel(comboBox.getModel());
  446.                 if(popupIsVisible())
  447.                     hidePopup();
  448.             }
  449.         }
  450.     }
  451.  
  452.     void repaintCurrentValue() {
  453.         Rectangle r = rectangleForCurrentValue();
  454.         comboBox.repaint(r.x,r.y,r.width,r.height);
  455.     }
  456.  
  457.     public void mouseClicked(MouseEvent anEvent) {}
  458.     public void mouseEntered(MouseEvent anEvent) {}
  459.     public void mouseExited(MouseEvent anEvent) {}
  460.  
  461.     public void mouseReleased(MouseEvent anEvent) {
  462.         Object sv;
  463.         stopAutoscrolling();
  464.         if(popupIsVisible()) {
  465.             if(anEvent.getSource() == listBox) {
  466.                 updateListBoxSelectionForEvent(anEvent,true); /** Just make sure ...**/
  467.                 comboBox.getModel().setSelectedItem(listBox.getSelectedValue());
  468.                 hidePopup();
  469.             } else if(anEvent.getSource() == arrowButton && 
  470.                       !(SwingUtilities.getLocalBounds(arrowButton).contains(anEvent.getPoint()))) {
  471.                 MouseEvent tmp = convertEventToListBox(anEvent);
  472.                 updateListBoxSelectionForEvent(tmp,true);
  473.                 sv = listBox.getSelectedValue();
  474.                 if(sv != null)
  475.                     comboBox.getModel().setSelectedItem(sv);
  476.                 hidePopup();
  477.             } else if(anEvent.getSource() == comboBox &&
  478.                       !(SwingUtilities.getLocalBounds(comboBox).contains(anEvent.getPoint()))) {
  479.                 MouseEvent tmp = convertEventToListBox(anEvent);
  480.                 updateListBoxSelectionForEvent(tmp,true);
  481.                 sv = listBox.getSelectedValue();
  482.                 if(sv != null)
  483.                     comboBox.getModel().setSelectedItem(sv);
  484.                 hidePopup();
  485.             }
  486.         }
  487.     }
  488.  
  489.     protected boolean shouldActivatePopupForEvent(MouseEvent anEvent) {
  490.         Rectangle r = rectangleForCurrentValue();
  491.         if(r.contains(anEvent.getPoint()))
  492.             return true;
  493.         else
  494.             return false;
  495.     }
  496.  
  497.     public void mousePressed(MouseEvent anEvent) {
  498.         Rectangle r;
  499.  
  500.     if(!SwingUtilities.isLeftMouseButton(anEvent))
  501.       return;
  502.  
  503.         if(!comboBox.isEnabled())
  504.             return;
  505.  
  506.         if(anEvent.getSource() == arrowButton) {
  507.             r = SwingUtilities.getLocalBounds(arrowButton);
  508.             if(r.contains(anEvent.getX(),anEvent.getY())) {
  509.                 if(popupIsVisible())
  510.                     hidePopup();
  511.                 else
  512.                     showPopup();
  513.             }
  514.         } else if(anEvent.getSource() == comboBox) {
  515.             if(shouldActivatePopupForEvent(anEvent)) {
  516.                 if(popupIsVisible())
  517.                     hidePopup();
  518.                 else
  519.                     showPopup();
  520.             }
  521.         }
  522.     }
  523.  
  524.  
  525.  
  526.     public void mouseDragged(MouseEvent anEvent) {
  527.         Window sourceWindow;
  528.         if(popupIsVisible()) {
  529.             if((anEvent.getSource() == arrowButton &&
  530.                !(SwingUtilities.getLocalBounds(arrowButton).contains(anEvent.getPoint()))) ||
  531.                (anEvent.getSource() == comboBox &&
  532.                       !(SwingUtilities.getLocalBounds(comboBox).contains(anEvent.getPoint())))) {
  533.                 MouseEvent tmp = convertEventToListBox(anEvent);
  534.                 updateListBoxSelectionForEvent(tmp,true);
  535.                 lastMouseLocation = SwingUtilities.convertPoint((Component)anEvent.getSource(),anEvent.getPoint(),null);
  536.                 sourceWindow = SwingUtilities.windowForComponent((Component)anEvent.getSource());
  537.                 lastMouseLocation.x += sourceWindow.getBounds().x;
  538.                 lastMouseLocation.y += sourceWindow.getBounds().y;
  539.                 startAutoscrolling();
  540.             } 
  541.         }
  542.     }
  543.  
  544.     public void mouseMoved(MouseEvent anEvent) {
  545.         Object source = anEvent.getSource();
  546.         if(popupIsVisible() && source == listBox) {
  547.             Point location = anEvent.getPoint();
  548.             Rectangle r = new Rectangle();
  549.             listBox.computeVisibleRect(r);
  550.             if(r.contains(location))
  551.                 updateListBoxSelectionForEvent(anEvent,false);
  552.         }
  553.     }
  554.  
  555.     public void keyTyped(KeyEvent e) {}
  556.     public void keyReleased(KeyEvent e) {}
  557.  
  558.     public void keyPressed(KeyEvent e) {
  559.  
  560.         if(e.getSource() == comboBox && !comboBox.isEditable() ||
  561.            e.getSource() == listBox  && !comboBox.isEditable()) {
  562.             if(comboBox.selectWithKeyChar(e.getKeyChar()))
  563.                 e.consume();
  564.         }
  565.     }
  566.  
  567.     public void validateMenu() {
  568.         if(menu == null) {
  569.             menu = new JPopupMenu();
  570.             menu.setLayout(new BoxLayout(menu,BoxLayout.Y_AXIS));
  571.             menu.setBorderPainted(true);
  572.             menu.setBorder(BorderFactory.createLineBorder(Color.black));
  573.             menu.setOpaque(false);
  574.             listBox = createListBox(comboBox.getModel());
  575.             listBox.setRequestFocusEnabled(false);
  576.             listBox.setBorder(null);
  577.             listBox.setCellRenderer(comboBox.getRenderer());
  578.             listBox.addMouseListener(this);
  579.             listBox.addMouseMotionListener(this);
  580.             listBox.addKeyListener(this);
  581.             listBox.setBorder(null);
  582.             scrollPane = new JScrollPane(listBox,ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
  583.                                          ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
  584.             scrollPane.setRequestFocusEnabled(false);
  585.             scrollPane.getVerticalScrollBar().setRequestFocusEnabled(false);
  586.             scrollPane.setBorder(null);
  587.             menu.add(scrollPane);
  588.             menu.setDoubleBuffered(true);
  589.             menu.registerKeyboardAction(new AbstractAction() {
  590.                                        public void actionPerformed(ActionEvent e) {
  591.                                            hidePopup();
  592.                                        } 
  593.                                        
  594.                                        public boolean isEnabled() {
  595.                                            return true;
  596.                                        }
  597.                         },KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE,0),JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  598.             menu.registerKeyboardAction(new AbstractAction() {
  599.                                        public void actionPerformed(ActionEvent e) {
  600.                                            hidePopup();
  601.                                        } 
  602.                                        
  603.                                        public boolean isEnabled() {
  604.                                            return true;
  605.                                        }
  606.                         },KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0),JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  607.             addKeyAccelerators(menu);
  608.         }
  609.     }
  610.  
  611.     private Dialog getDialog() {
  612.         Container parent;
  613.         for(parent = comboBox.getParent() ; parent != null && !(parent instanceof Dialog)
  614.                 && !(parent instanceof Window) ; parent = parent.getParent());
  615.         if(parent instanceof Dialog)
  616.             return (Dialog) parent;
  617.         else
  618.             return null;
  619.     }
  620.     
  621.     private boolean inModalDialog() {
  622.         return (getDialog() != null);
  623.     }
  624.     
  625.     protected Rectangle computePopupBounds(int px,int py,int pw,int ph) {
  626.         Rectangle absBounds;
  627.         Rectangle r = new Rectangle(px,py,pw,ph);
  628.         boolean inModalDialog = inModalDialog();
  629.         /** Workaround for modal dialogs. See also JPopupMenu.java **/
  630.         if(inModalDialog) {
  631.             Dialog dlg = getDialog();
  632.             Point p;
  633.             if(dlg instanceof JDialog) {
  634.                 JRootPane rp = ((JDialog)dlg).getRootPane();
  635.                 p = rp.getLocationOnScreen();
  636.                 absBounds = rp.getBounds();
  637.                 absBounds.x = p.x;
  638.                 absBounds.y = p.y;
  639.             } else
  640.                 absBounds = dlg.getBounds();
  641.             p = new Point(absBounds.x,absBounds.y);
  642.             SwingUtilities.convertPointFromScreen(p,comboBox);
  643.             absBounds.x = p.x;
  644.             absBounds.y = p.y;
  645.         } else {
  646.             Point p;
  647.             Dimension scrSize = Toolkit.getDefaultToolkit().getScreenSize();
  648.             absBounds = new Rectangle();
  649.             p = new Point(0,0);
  650.             SwingUtilities.convertPointFromScreen(p,comboBox);
  651.             absBounds.x = p.x;
  652.             absBounds.y = p.y;
  653.             absBounds.width = scrSize.width;
  654.             absBounds.height= scrSize.height;
  655.         }
  656.  
  657.         if(SwingUtilities.isRectangleContainingRectangle(absBounds,r))
  658.             return r;
  659.         else {
  660.             Rectangle r2      = new Rectangle(0,-r.height,r.width,r.height);
  661.             
  662.             if(SwingUtilities.isRectangleContainingRectangle(absBounds,r2))
  663.                 return r2;
  664.  
  665.             if(inModalDialog) {
  666.                 SwingUtilities.computeIntersection(absBounds.x,absBounds.y,absBounds.width,absBounds.height,r);
  667.                 SwingUtilities.computeIntersection(absBounds.x,absBounds.y,absBounds.width,absBounds.height,r2);
  668.                 if(r.height > r2.height)
  669.                     return r;
  670.                 else
  671.                     return r2;
  672.             } else
  673.                 return r2;
  674.         }
  675.     }
  676.     
  677.     public void showPopup() {
  678.         Border border;
  679.         Dimension ds,popupSize;
  680.         Rectangle cbBounds = comboBox.getBounds();
  681.         Rectangle popupBounds;
  682.     popupLocation = new Point(cbBounds.x, cbBounds.y);
  683.  
  684.         if(showingComboBox != null && showingComboBox != comboBox) {
  685.             showingComboBox.getUI().hidePopup();
  686.             showingComboBox = null;
  687.         }
  688.  
  689.         validateMenu();
  690.         comboBox.requestFocus();
  691.  
  692.     setupListBox(listBox, comboBox);
  693.  
  694.         popupSize = new Dimension(cbBounds.width,
  695.                                   getPopupHeightForRowCount(comboBox.getMaximumRowCount()));
  696.         border = menu.getBorder();
  697.         if(border != null) {
  698.             Insets in = border.getBorderInsets(menu);
  699.             popupSize.width -= (in.left + in.right);
  700.             popupSize.height -= (in.top + in.bottom);
  701.         }
  702.         updateListBoxSelection();
  703.         listBox.invalidate();
  704.         popupBounds = computePopupBounds(0,comboBox.getBounds().height,popupSize.width,popupSize.height);
  705.         popupSize.width = popupBounds.width;
  706.         popupSize.height= popupBounds.height;
  707.         scrollPane.setMaximumSize(popupSize);
  708.         scrollPane.setPreferredSize(popupSize);
  709.         scrollPane.setMinimumSize(popupSize);
  710.         menu.setLightWeightPopupEnabled(comboBox.isLightWeightPopupEnabled());
  711.  
  712.         menu.show(comboBox,popupBounds.x,popupBounds.y);
  713.         menu.repaint();
  714.  
  715.         comboBox.registerKeyboardAction(new AbstractAction() {
  716.                                        public void actionPerformed(ActionEvent e) {
  717.                                            hidePopup();
  718.                                        } 
  719.                                        
  720.                                        public boolean isEnabled() {
  721.                                            return true;
  722.                                        }
  723.                         },KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE,0),JComponent.WHEN_IN_FOCUSED_WINDOW);
  724.         comboBox.registerKeyboardAction(new AbstractAction() {
  725.                                        public void actionPerformed(ActionEvent e) {
  726.                                            hidePopup();
  727.                                        } 
  728.                                        
  729.                                        public boolean isEnabled() {
  730.                                            return true;
  731.                                        }
  732.                         },KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0),JComponent.WHEN_IN_FOCUSED_WINDOW);
  733.         repaintCurrentValue();
  734.         listBox.requestFocus();
  735.         showingComboBox = comboBox;
  736.     }
  737.  
  738.     protected void setupListBox(JList listBox, JComboBox comboBox) {
  739.     listBox.setFont(comboBox.getFont());
  740.         listBox.setForeground(comboBox.getForeground());
  741.         listBox.setBackground(comboBox.getBackground());
  742.     }
  743.  
  744.     public void hidePopup() {
  745.         if(menu == null || listBox == null)
  746.             return;
  747.         if(!popupIsVisible())
  748.             return;
  749.  
  750.         menu.setVisible(false);
  751.         requestFocusLater();
  752.         comboBox.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE,0));
  753.         comboBox.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0));
  754.     }
  755.  
  756.    protected void requestFocusLater() {
  757.         if(comboBox.isEditable()) {
  758.             requestFocusLaterForComponent(comboBox.getEditor().getEditorComponent());
  759.         } else {
  760.             requestFocusLaterForComponent(comboBox);
  761.             repaintCurrentValue();
  762.         }       
  763.    }
  764.  
  765.     protected int getPopupHeightForRowCount(int maxRowCount) {
  766.         int currentElementCount = comboBox.getModel().getSize();
  767.  
  768.         if(currentElementCount > 0) {
  769.             Rectangle r = listBox.getCellBounds(0,0);
  770.  
  771.             /** listbox returns a wrong height the first time. Remove this hack when
  772.              *  listbox will be fixed 
  773.              */
  774.  
  775.             if(r.height <=2)
  776.                 r.height = 17;
  777.  
  778.             /** End hack **/
  779.  
  780.             if(maxRowCount < currentElementCount)
  781.                 return (r.height * maxRowCount) + 2;
  782.             else
  783.                 return (r.height * currentElementCount) + 2;
  784.  
  785.         } else 
  786.             return 100;
  787.     }
  788.  
  789.     public boolean popupIsVisible() {
  790.         if(menu == null)
  791.             return false;
  792.         else {
  793.             validateMenu();
  794.             return menu.isVisible();
  795.         }
  796.     }
  797.  
  798.     public void maximumRowCountChanged() {
  799.         if(popupIsVisible()) {
  800.             hidePopup();
  801.             showPopup();
  802.         }
  803.     }
  804.  
  805.     protected void updateListBoxSelection() {
  806.         Object sObject = comboBox.getModel().getSelectedItem();
  807.         if(sObject == null && listBox != null) {
  808.             listBox.setSelectedIndex(-1);
  809.             return;
  810.         } 
  811.         listBox.setSelectedValue(sObject,true);
  812.     }
  813.  
  814.     protected void startAutoscrolling() {
  815.         if(autoscrollTimer == null)
  816.             autoscrollTimer = new Timer(100,new AbstractAction() {
  817.                        public void actionPerformed(ActionEvent e) {
  818.                            autoscroll();
  819.                        }
  820.                        public boolean isEnabled() {
  821.                            return true;
  822.                        }
  823.             });
  824.         autoscrollTimer.start();
  825.     }
  826.  
  827.     protected void stopAutoscrolling() {
  828.         if(autoscrollTimer != null)
  829.             autoscrollTimer.stop();
  830.         lastMouseLocation = null;
  831.     }
  832.  
  833.     protected void autoscroll() {
  834.         if(listBox == null)
  835.             return;
  836.  
  837.     Window win = SwingUtilities.windowForComponent(listBox);
  838.  
  839.         if(lastMouseLocation != null && win != null) {
  840.             Point tmp = new Point(lastMouseLocation.x - win.getBounds().x,
  841.                                   lastMouseLocation.y - win.getBounds().y);
  842.             tmp = SwingUtilities.convertPoint(null,tmp,listBox);
  843.             int index = listBox.locationToIndex(tmp);
  844.             if(index == -1) {
  845.                 if(tmp.y < 0)
  846.                     index = 0;
  847.                 else
  848.                     index = comboBox.getModel().getSize() - 1;
  849.             }
  850.             listBox.setSelectedIndex(index);
  851.             listBox.ensureIndexIsVisible(index);
  852.         }
  853.     }
  854.  
  855.     protected void updateListBoxSelectionForEvent(MouseEvent anEvent,boolean shouldScroll) {
  856.         Point location = anEvent.getPoint();
  857.         if(listBox == null)
  858.             return;
  859.         int index = listBox.locationToIndex(location);
  860.         if(index == -1) {
  861.             if(location.y < 0)
  862.                 index = 0;
  863.             else
  864.                 index = comboBox.getModel().getSize() - 1;
  865.         }
  866.         listBox.setSelectedIndex(index);
  867.         if(shouldScroll)
  868.             listBox.ensureIndexIsVisible(index);
  869.     }
  870.  
  871.     protected void selectNextPossibleValue() {
  872.       int si;
  873.       validateMenu();
  874.       if(popupIsVisible()) 
  875.     si = listBox.getSelectedIndex();
  876.       else
  877.     si = comboBox.getSelectedIndex();
  878.  
  879.       if(si < comboBox.getModel().getSize() - 1)
  880.     comboBox.setSelectedIndex(si+1);
  881.     }
  882.  
  883.     protected void selectPreviousPossibleValue() {
  884.         int si;
  885.         validateMenu();
  886.     if(popupIsVisible())
  887.       si = listBox.getSelectedIndex();
  888.     else
  889.       si = comboBox.getSelectedIndex();
  890.         if(si > 0) 
  891.             comboBox.setSelectedIndex(si-1);
  892.     }
  893.  
  894.     protected void toggleOpenClose() {
  895.         if(popupIsVisible())
  896.             hidePopup();
  897.         else
  898.             showPopup();
  899.     }
  900.  
  901.     protected MouseEvent convertEventToListBox(MouseEvent anEvent) {
  902.         Rectangle bo;
  903.         Window sourceWindow,destWindow;
  904.         MouseEvent tmp = SwingUtilities.convertMouseEvent((Component)anEvent.getSource(),anEvent,null);
  905.         sourceWindow = SwingUtilities.windowForComponent((Component)anEvent.getSource());
  906.         bo = sourceWindow.getBounds();
  907.         tmp.translatePoint(bo.x,bo.y);
  908.  
  909.         destWindow = SwingUtilities.windowForComponent(listBox);
  910.         bo = destWindow.getBounds();
  911.         tmp.translatePoint(-bo.x,-bo.y);
  912.         tmp = SwingUtilities.convertMouseEvent(null,tmp,listBox);
  913.         return tmp;
  914.     }
  915.  
  916.     protected void addKeyAccelerators(JComponent comp) {
  917.             comp.registerKeyboardAction(
  918.                 new AbstractAction() {
  919.                     public void actionPerformed(ActionEvent e) {
  920.                         selectNextPossibleValue();
  921.                     }
  922.                     public boolean isEnabled() {
  923.                         return comboBox.isEnabled();
  924.                     }
  925.             },KeyStroke.getKeyStroke(KeyEvent.VK_DOWN,0),JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  926.  
  927.             comp.registerKeyboardAction(
  928.                  new AbstractAction() {
  929.                     public void actionPerformed(ActionEvent e) {
  930.                         selectPreviousPossibleValue();
  931.                     }
  932.                     public boolean  isEnabled() {
  933.                         return comboBox.isEnabled();
  934.                     }
  935.             },KeyStroke.getKeyStroke(KeyEvent.VK_UP,0),JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  936.  
  937.             comp.registerKeyboardAction(
  938.                 new AbstractAction() {
  939.                    public void actionPerformed(ActionEvent e) {
  940.                        toggleOpenClose();
  941.                    }
  942.                    public boolean isEnabled() {
  943.                        return comboBox.isEnabled();
  944.                    }
  945.                 },KeyStroke.getKeyStroke(KeyEvent.VK_UP,InputEvent.ALT_MASK),JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  946.  
  947.             comp.registerKeyboardAction(
  948.                 new AbstractAction() {
  949.                   public void actionPerformed(ActionEvent e) {
  950.                       toggleOpenClose();
  951.                   }
  952.                   public boolean isEnabled() {
  953.                       return comboBox.isEnabled();
  954.                   }
  955.             },KeyStroke.getKeyStroke(KeyEvent.VK_DOWN,InputEvent.ALT_MASK),JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  956.         }
  957.  
  958.         protected void removeKeyAccelerators(JComponent comp) {
  959.             comp.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN,0));
  960.             comp.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_UP,0));
  961.             comp.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN,InputEvent.ALT_MASK));
  962.             comp.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_UP,InputEvent.ALT_MASK));
  963.         }
  964.  
  965.         protected static void requestFocusLaterForComponent(Component c) {
  966.             new FocusRequestHelper(c);
  967.         }
  968.  
  969.         static class FocusRequestHelper implements WindowListener,ActionListener, Serializable {
  970.             Window window;
  971.             Component component;
  972.             Timer timer;
  973.             FocusRequestHelper(Component c) {
  974.                 window = null;
  975.                 component = c;
  976.                 for(Container p = c.getParent() ; p != null ; p = p.getParent()) 
  977.                     if(p instanceof Window) {
  978.                         window = (Window)p;
  979.                         break;
  980.                     }
  981.                 if(window == null)
  982.                     return;
  983.                 else 
  984.                     window.addWindowListener(this);
  985.             }
  986.  
  987.             public void windowOpened(WindowEvent e){}
  988.             public void windowClosing(WindowEvent e){}
  989.             public void windowClosed(WindowEvent e){ window.removeWindowListener(this);}
  990.             public void windowIconified(WindowEvent e){}
  991.             public void windowDeiconified(WindowEvent e){}
  992.             public void windowActivated(WindowEvent e){
  993.                 window.removeWindowListener(this);
  994.                 startTimer();
  995.             }
  996.             public void windowDeactivated(WindowEvent e){}
  997.  
  998.             void startTimer() {
  999.                 timer = new Timer(20,this);
  1000.                 timer.start();
  1001.             }
  1002.  
  1003.             public void actionPerformed(ActionEvent e) {
  1004.                 if (timer != null) {
  1005.                     timer.stop();
  1006.                 }
  1007.                 if (component != null) {
  1008.                     component.requestFocus();
  1009.                 }
  1010.                 component = null;
  1011.                 timer = null;
  1012.                 window = null;
  1013.             }
  1014.         }
  1015. }
  1016.