This fixes one weird NPE (see associated bug report) and I also cleaned up some inconsistencies that I came over and some warnings that Eclipse came over :-)
2007-01-06 Roman Kennke <[EMAIL PROTECTED]> PR 30337 * javax/swing/plaf/basic/BasicComboBoxUI.java (installUI): Install popup and list here. Don't configure the arrow button and editor here. (installComponents): Don't install popup and list here. (Moved to installUI). Configure arrow button here and check for null. (addEditor): Configure editor here. (configureArrowButton): Directly fetch listeners from popup. (paintCurrentValue): Removed unused local variables. (layoutContainer): Removed unused local variables. (PropertyChangeHandler.propertyChange): Don't invalidate minimumSize on each property change. Avoid calling getPropertyName() repeatedly. Clean up. Call addEditor() when editor changes. Configure and unconfigure editor when editable changes. Use 'model' instead of non-existing 'dataModel' property. * javax/swing/plaf/basic/BasicComboPopup.java (uninstallingUI): Remove property change listener and item listener here. Uninstall list listeners. Set model to null to prevent leakage. (configureList): Don't sync list selection there. (uninstallComboBoxListeners): Moved to uninstallingUI. (uninstallListeners): Moved to uninstallingUI. * javax/swing/plaf/metal/MetalComboBoxUI.java (createPopup): Call super. (getMinimumSize): Removed unused statement. /Roman
Index: javax/swing/plaf/basic/BasicComboBoxUI.java =================================================================== RCS file: /cvsroot/classpath/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java,v retrieving revision 1.40 diff -u -1 -5 -r1.40 BasicComboBoxUI.java --- javax/swing/plaf/basic/BasicComboBoxUI.java 24 Jul 2006 15:04:05 -0000 1.40 +++ javax/swing/plaf/basic/BasicComboBoxUI.java 6 Jan 2007 15:38:45 -0000 @@ -203,52 +203,49 @@ * Installs the UI for the given [EMAIL PROTECTED] JComponent}. * * @param c the JComponent to install a UI for. * * @see #uninstallUI(JComponent) */ public void installUI(JComponent c) { super.installUI(c); if (c instanceof JComboBox) { isMinimumSizeDirty = true; comboBox = (JComboBox) c; installDefaults(); + popup = createPopup(); + listBox = popup.getList(); // Set editor and renderer for the combo box. Editor is used // only if combo box becomes editable, otherwise renderer is used // to paint the selected item; combobox is not editable by default. ListCellRenderer renderer = comboBox.getRenderer(); if (renderer == null || renderer instanceof UIResource) comboBox.setRenderer(createRenderer()); ComboBoxEditor currentEditor = comboBox.getEditor(); if (currentEditor == null || currentEditor instanceof UIResource) { currentEditor = createEditor(); comboBox.setEditor(currentEditor); } - editor = currentEditor.getEditorComponent(); installComponents(); installListeners(); - if (arrowButton != null) - configureArrowButton(); - if (editor != null) - configureEditor(); comboBox.setLayout(createLayoutManager()); comboBox.setFocusable(true); installKeyboardActions(); comboBox.putClientProperty(BasicLookAndFeel.DONT_CANCEL_POPUP, Boolean.TRUE); } } /** * Uninstalls the UI for the given [EMAIL PROTECTED] JComponent}. * * @param c The JComponent that is having this UI removed. * * @see #installUI(JComponent) */ @@ -461,37 +458,35 @@ * @return A new component that will be responsible for displaying/editing * the selected item in the combo box. */ protected ComboBoxEditor createEditor() { return new BasicComboBoxEditor.UIResource(); } /** * Installs the components for this JComboBox. ArrowButton, main * part of combo box (upper part) and popup list of items are created and * configured here. */ protected void installComponents() { - // create drop down list of items - popup = createPopup(); - listBox = popup.getList(); - // create and install arrow button arrowButton = createArrowButton(); comboBox.add(arrowButton); + if (arrowButton != null) + configureArrowButton(); if (comboBox.isEditable()) addEditor(); comboBox.add(currentValuePane); } /** * Uninstalls components from this [EMAIL PROTECTED] JComboBox}. * * @see #installComponents() */ protected void uninstallComponents() { // uninstall arrow button @@ -509,31 +504,35 @@ ComboBoxEditor currentEditor = comboBox.getEditor(); if (currentEditor instanceof UIResource) { comboBox.setEditor(null); editor = null; } } /** * Adds the current editor to the combo box. */ public void addEditor() { removeEditor(); editor = comboBox.getEditor().getEditorComponent(); - comboBox.add(editor); + if (editor != null) + { + configureEditor(); + comboBox.add(editor); + } } /** * Removes the current editor from the combo box. */ public void removeEditor() { if (editor != null) { unconfigureEditor(); comboBox.remove(editor); } } /** @@ -560,34 +559,32 @@ if (keyListener != null) editor.removeKeyListener(keyListener); } /** * Configures the arrow button. * * @see #configureArrowButton() */ public void configureArrowButton() { if (arrowButton != null) { arrowButton.setEnabled(comboBox.isEnabled()); arrowButton.setFocusable(false); - if (popupMouseListener != null) - arrowButton.addMouseListener(popupMouseListener); - if (popupMouseMotionListener != null) - arrowButton.addMouseMotionListener(popupMouseMotionListener); + arrowButton.addMouseListener(popup.getMouseListener()); + arrowButton.addMouseMotionListener(popup.getMouseMotionListener()); // Mark the button as not closing the popup, we handle this ourselves. arrowButton.putClientProperty(BasicLookAndFeel.DONT_CANCEL_POPUP, Boolean.TRUE); } } /** * Unconfigures the arrow button. * * @see #configureArrowButton() * * @specnote The specification says this method is implementation specific * and should not be used or overridden. */ @@ -843,33 +840,30 @@ { return comboBox.getInsets(); } /** * Paints currently selected value in the main part of the combo * box (part without popup). * * @param g graphics context * @param bounds Rectangle representing the size of the area in which * selected item should be drawn * @param hasFocus true if combo box has focus and false otherwise */ public void paintCurrentValue(Graphics g, Rectangle bounds, boolean hasFocus) { - Object currentValue = comboBox.getSelectedItem(); - boolean isPressed = arrowButton.getModel().isPressed(); - /* Gets the component to be drawn for the current value. * If there is currently no selected item we will take an empty * String as replacement. */ ListCellRenderer renderer = comboBox.getRenderer(); if (comboBox.getSelectedIndex() != -1) { Component comp; if (hasFocus && ! isPopupVisible(comboBox)) { comp = renderer.getListCellRendererComponent(listBox, comboBox.getSelectedItem(), -1, true, false); } else { @@ -1097,31 +1091,30 @@ /** * Arranges the components in the container. It puts arrow * button right end part of the comboBox. If the comboBox is editable * then editor is placed to the left of arrow button, starting from the * beginning. * * @param parent Container that should be layed out. */ public void layoutContainer(Container parent) { // Position editor component to the left of arrow button if combo box is // editable Insets i = getInsets(); int arrowSize = comboBox.getHeight() - (i.top + i.bottom); - int editorWidth = comboBox.getBounds().width - arrowSize; if (arrowButton != null) arrowButton.setBounds(comboBox.getWidth() - (i.right + arrowSize), i.top, arrowSize, arrowSize); if (editor != null) editor.setBounds(rectangleForCurrentValue()); } } /** * Handles focus changes occuring in the combo box. This class is * responsible for repainting combo box whenever focus is gained or lost * and also for hiding popup list of items whenever combo box loses its * focus. */ @@ -1333,65 +1326,80 @@ * Creates a new instance. */ public PropertyChangeHandler() { // Nothing to do here. } /** * Invoked whenever bound property of JComboBox changes. * * @param e the event. */ public void propertyChange(PropertyChangeEvent e) { // Lets assume every change invalidates the minimumsize. - isMinimumSizeDirty = true; - - if (e.getPropertyName().equals("enabled")) + String propName = e.getPropertyName(); + if (propName.equals("enabled")) { - arrowButton.setEnabled(comboBox.isEnabled()); + boolean enabled = comboBox.isEnabled(); + if (editor != null) + editor.setEnabled(enabled); + if (arrowButton != null) + arrowButton.setEnabled(enabled); - if (comboBox.isEditable()) - comboBox.getEditor().getEditorComponent().setEnabled( - comboBox.isEnabled()); + comboBox.repaint(); + } + else if (propName.equals("editor")) + { + addEditor(); + comboBox.revalidate(); } else if (e.getPropertyName().equals("editable")) { if (comboBox.isEditable()) { - configureEditor(); addEditor(); } else { - unconfigureEditor(); removeEditor(); } comboBox.revalidate(); - comboBox.repaint(); } - else if (e.getPropertyName().equals("dataModel")) + else if (e.getPropertyName().equals("model")) { // remove ListDataListener from old model and add it to new model ComboBoxModel oldModel = (ComboBoxModel) e.getOldValue(); - if (oldModel != null) + if (oldModel != null && listDataListener != null) oldModel.removeListDataListener(listDataListener); - if ((ComboBoxModel) e.getNewValue() != null) + ComboBoxModel newModel = (ComboBoxModel) e.getNewValue(); + if (newModel != null && listDataListener != null) comboBox.getModel().addListDataListener(listDataListener); + + if (editor != null) + { + comboBox.configureEditor(comboBox.getEditor(), + comboBox.getSelectedItem()); + } + isMinimumSizeDirty = true; + comboBox.revalidate(); + comboBox.repaint(); } else if (e.getPropertyName().equals("font")) { Font font = (Font) e.getNewValue(); - editor.setFont(font); + if (editor != null) + { + editor.setFont(font); + } listBox.setFont(font); - arrowButton.setFont(font); + isMinimumSizeDirty = true; comboBox.revalidate(); - comboBox.repaint(); } // FIXME: Need to handle changes in other bound properties. } } } Index: javax/swing/plaf/basic/BasicComboPopup.java =================================================================== RCS file: /cvsroot/classpath/classpath/javax/swing/plaf/basic/BasicComboPopup.java,v retrieving revision 1.19 diff -u -1 -5 -r1.19 BasicComboPopup.java --- javax/swing/plaf/basic/BasicComboPopup.java 13 Jun 2006 09:28:57 -0000 1.19 +++ javax/swing/plaf/basic/BasicComboPopup.java 6 Jan 2007 15:38:45 -0000 @@ -259,33 +259,42 @@ * Returns KeyListener listening to key events occuring in the combo box. * This method returns null because KeyHandler is not longer used. * * @return KeyListener */ public KeyListener getKeyListener() { return keyListener; } /** * This method uninstalls the UI for the given JComponent. */ public void uninstallingUI() { + if (propertyChangeListener != null) + { + comboBox.removePropertyChangeListener(propertyChangeListener); + } + if (itemListener != null) + { + comboBox.removeItemListener(itemListener); + } uninstallComboBoxModelListeners(comboBox.getModel()); - uninstallListeners(); uninstallKeyboardActions(); + uninstallListListeners(); + list.setModel(null); } /** * This method uninstalls listeners that were listening to changes occuring * in the comb box's data model * * @param model data model for the combo box from which to uninstall * listeners */ protected void uninstallComboBoxModelListeners(ComboBoxModel model) { model.removeListDataListener(listDataListener); } /** @@ -454,31 +463,30 @@ * This method configures the list of comboBox's items by setting default * properties and installing listeners. */ protected void configureList() { list.setFont(comboBox.getFont()); list.setForeground(comboBox.getForeground()); list.setBackground(comboBox.getBackground()); Color sfg = UIManager.getColor("ComboBox.selectionForeground"); list.setSelectionForeground(sfg); Color sbg = UIManager.getColor("ComboBox.selectionBackground"); list.setSelectionBackground(sbg); list.setBorder(null); list.setCellRenderer(comboBox.getRenderer()); list.setFocusable(false); - syncListSelection(); list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); installListListeners(); } /** * This method installs list listeners. */ protected void installListListeners() { // mouse listener listening to mouse events occuring in the // combo box's list of items. listMouseListener = createListMouseListener(); list.addMouseListener(listMouseListener); // mouse listener listening to mouse motion events occuring in the @@ -998,64 +1006,42 @@ uninstallComboBoxModelListeners(oldModel); ComboBoxModel newModel = (ComboBoxModel) e.getNewValue(); list.setModel(newModel); installComboBoxModelListeners(newModel); if (comboBox.getItemCount() > 0) comboBox.setSelectedIndex(0); if (isVisible()) hide(); } } } // ------ private helper methods -------------------- /** - * This method uninstalls listeners installed by the UI - */ - private void uninstallListeners() - { - uninstallComboBoxListeners(); - uninstallComboBoxModelListeners(comboBox.getModel()); - } - - /** * This method uninstalls Listeners registered with combo boxes list of * items */ private void uninstallListListeners() { list.removeMouseListener(listMouseListener); listMouseListener = null; list.removeMouseMotionListener(listMouseMotionListener); listMouseMotionListener = null; } - /** - * This method uninstalls listeners listening to combo box associated with - * this popup menu - */ - private void uninstallComboBoxListeners() - { - comboBox.removeItemListener(itemListener); - itemListener = null; - - comboBox.removePropertyChangeListener(propertyChangeListener); - propertyChangeListener = null; - } - void syncListSelection() { int index = comboBox.getSelectedIndex(); if (index == -1) list.clearSelection(); else list.setSelectedIndex(index); } // -------------------------------------------------------------------- // The following classes are here only for backwards API compatibility // They aren't used. // -------------------------------------------------------------------- /** Index: javax/swing/plaf/metal/MetalComboBoxUI.java =================================================================== RCS file: /cvsroot/classpath/classpath/javax/swing/plaf/metal/MetalComboBoxUI.java,v retrieving revision 1.12 diff -u -1 -5 -r1.12 MetalComboBoxUI.java --- javax/swing/plaf/metal/MetalComboBoxUI.java 21 Mar 2006 14:58:20 -0000 1.12 +++ javax/swing/plaf/metal/MetalComboBoxUI.java 6 Jan 2007 15:38:45 -0000 @@ -205,31 +205,31 @@ * * @return An editor. */ protected ComboBoxEditor createEditor() { return new MetalComboBoxEditor.UIResource(); } /** * Creates a popup for the combo box. * * @return A popup. */ protected ComboPopup createPopup() { - return new MetalComboPopup(comboBox); + return super.createPopup(); } /** * Creates a new button for use in rendering the JComboBox. * * @return A button. */ protected JButton createArrowButton() { JButton button = new MetalComboBoxButton(comboBox, new MetalComboBoxIcon(), new CellRendererPane(), listBox); button.setMargin(new Insets(0, 1, 1, 3)); return button; } @@ -293,31 +293,30 @@ * @param c the component * * @return The minimum size for the combo box. */ public Dimension getMinimumSize(JComponent c) { if (!isMinimumSizeDirty) return new Dimension(cachedMinimumSize); Dimension d; if (!comboBox.isEditable() && arrowButton != null && arrowButton instanceof MetalComboBoxButton) { MetalComboBoxButton b = (MetalComboBoxButton) arrowButton; d = getDisplaySize(); - Insets insets = b.getInsets(); Insets arrowInsets = b.getInsets(); Insets comboInsets = comboBox.getInsets(); Icon icon = b.getComboIcon(); d.width += comboInsets.left + comboInsets.right; d.width += arrowInsets.left + arrowInsets.right; d.width += arrowInsets.right + icon.getIconWidth(); d.height += comboInsets.top + comboInsets.bottom; d.height += arrowInsets.top + arrowInsets.bottom; } else if (comboBox.isEditable() && arrowButton != null && editor != null) { d = super.getMinimumSize(c); Insets arrowMargin = arrowButton.getMargin(); d.height += arrowMargin.top + arrowMargin.bottom; d.width += arrowMargin.left + arrowMargin.right;