content: /*
   * Copyright 2019 FormDev Software GmbH
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package themeengine.include.com.formdev.flatlaf.ui;
  
  import static themeengine.include.com.formdev.flatlaf.util.UIScale.scale;
  import java.awt.Color;
  import java.awt.Component;
  import java.awt.ComponentOrientation;
  import java.awt.Container;
  import java.awt.Dimension;
  import java.awt.Graphics;
  import java.awt.Graphics2D;
  import java.awt.Insets;
  import java.awt.LayoutManager;
  import java.awt.Rectangle;
  import java.awt.Shape;
  import java.awt.event.ActionEvent;
  import java.awt.event.ActionListener;
  import java.awt.event.FocusEvent;
  import java.awt.event.FocusListener;
  import java.awt.event.MouseListener;
  import java.awt.geom.Rectangle2D;
  import java.beans.PropertyChangeEvent;
  import java.beans.PropertyChangeListener;
  import java.lang.ref.WeakReference;
  import javax.swing.AbstractAction;
  import javax.swing.BorderFactory;
  import javax.swing.ComboBoxEditor;
  import javax.swing.DefaultListCellRenderer;
  import javax.swing.InputMap;
  import javax.swing.JButton;
  import javax.swing.JComboBox;
  import javax.swing.JComponent;
  import javax.swing.JList;
  import javax.swing.JPanel;
  import javax.swing.JScrollBar;
  import javax.swing.JTextField;
  import javax.swing.KeyStroke;
  import javax.swing.ListCellRenderer;
  import javax.swing.LookAndFeel;
  import javax.swing.SwingConstants;
  import javax.swing.UIManager;
  import javax.swing.border.AbstractBorder;
  import javax.swing.border.Border;
  import javax.swing.plaf.ComponentUI;
  import javax.swing.plaf.basic.BasicComboBoxUI;
  import javax.swing.plaf.basic.BasicComboPopup;
  import javax.swing.plaf.basic.ComboPopup;
  import javax.swing.text.JTextComponent;
  import themeengine.include.com.formdev.flatlaf.FlatClientProperties;
  import themeengine.include.com.formdev.flatlaf.util.SystemInfo;
  import themeengine.include.com.formdev.flatlaf.util.UIScale;
  
  /**
   * Provides the Flat LaF UI delegate for {@link javax.swing.JComboBox}.
   *
   * <!-- BasicComboBoxUI -->
   *
   * @uiDefault ComboBox.font						Font
   * @uiDefault ComboBox.background				Color
   * @uiDefault ComboBox.foreground				Color
   * @uiDefault ComboBox.border					Border
   * @uiDefault ComboBox.padding					Insets
   * @uiDefault ComboBox.squareButton				boolean	default is true
   *
   * <!-- FlatComboBoxUI -->
   *
   * @uiDefault ComboBox.minimumWidth				int
   * @uiDefault ComboBox.editorColumns			int
   * @uiDefault ComboBox.maximumRowCount			int
   * @uiDefault ComboBox.buttonStyle				String	auto (default), button or none
   * @uiDefault Component.arrowType				String	triangle (default) or chevron
   * @uiDefault Component.isIntelliJTheme			boolean
   * @uiDefault Component.borderColor				Color
   * @uiDefault Component.disabledBorderColor		Color
   * @uiDefault ComboBox.editableBackground		Color	optional; defaults to ComboBox.background
   * @uiDefault ComboBox.disabledBackground		Color
   * @uiDefault ComboBox.disabledForeground		Color
   * @uiDefault ComboBox.buttonBackground			Color
   * @uiDefault ComboBox.buttonEditableBackground	Color
   * @uiDefault ComboBox.buttonArrowColor			Color
   * @uiDefault ComboBox.buttonDisabledArrowColor	Color
   * @uiDefault ComboBox.buttonHoverArrowColor	Color
   *
   * @author Karl Tauber
   */
  public class FlatComboBoxUI
  	extends BasicComboBoxUI
  {
  	protected int minimumWidth;
  	protected int editorColumns;
  	protected String buttonStyle;
  	protected String arrowType;
  	protected boolean isIntelliJTheme;
  	protected Color borderColor;
  	protected Color disabledBorderColor;
  
  	protected Color editableBackground;
  	protected Color disabledBackground;
  	protected Color disabledForeground;
  
  	protected Color buttonBackground;
  	protected Color buttonEditableBackground;
  	protected Color buttonArrowColor;
  	protected Color buttonDisabledArrowColor;
  	protected Color buttonHoverArrowColor;
  
  	private MouseListener hoverListener;
  	protected boolean hover;
  
  	private WeakReference<Component> lastRendererComponent;
  
  	public static ComponentUI createUI( JComponent c ) {
  		return new FlatComboBoxUI();
  	}
  
  	@Override
  	protected void installListeners() {
  		super.installListeners();
  
  		hoverListener = new FlatUIUtils.HoverListener( null, h -> {
  			if( !comboBox.isEditable() ) {
  				hover = h;
  				if( arrowButton != null )
  					arrowButton.repaint();
  			}
  		} );
  		comboBox.addMouseListener( hoverListener );
  	}
  
  	@Override
  	protected void uninstallListeners() {
  		super.uninstallListeners();
  
  		comboBox.removeMouseListener( hoverListener );
  		hoverListener = null;
  	}
  
  	@Override
  	protected void installDefaults() {
  		super.installDefaults();
  
  		LookAndFeel.installProperty( comboBox, "opaque", false );
  
  		minimumWidth = UIManager.getInt( "ComboBox.minimumWidth" );
  		editorColumns = UIManager.getInt( "ComboBox.editorColumns" );
  		buttonStyle = UIManager.getString( "ComboBox.buttonStyle" );
  		arrowType = UIManager.getString( "Component.arrowType" );
  		isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
  		borderColor = UIManager.getColor( "Component.borderColor" );
  		disabledBorderColor = UIManager.getColor( "Component.disabledBorderColor" );
  
  		editableBackground = UIManager.getColor( "ComboBox.editableBackground" );
  		disabledBackground = UIManager.getColor( "ComboBox.disabledBackground" );
  		disabledForeground = UIManager.getColor( "ComboBox.disabledForeground" );
  
  		buttonBackground = UIManager.getColor( "ComboBox.buttonBackground" );
  		buttonEditableBackground = UIManager.getColor( "ComboBox.buttonEditableBackground" );
  		buttonArrowColor = UIManager.getColor( "ComboBox.buttonArrowColor" );
  		buttonDisabledArrowColor = UIManager.getColor( "ComboBox.buttonDisabledArrowColor" );
  		buttonHoverArrowColor = UIManager.getColor( "ComboBox.buttonHoverArrowColor" );
  
  		// set maximumRowCount
  		int maximumRowCount = UIManager.getInt( "ComboBox.maximumRowCount" );
  		if( maximumRowCount > 0 && maximumRowCount != 8 && comboBox.getMaximumRowCount() == 8 )
  			comboBox.setMaximumRowCount( maximumRowCount );
  
  		// scale
  		padding = UIScale.scale( padding );
  
  		MigLayoutVisualPadding.install( comboBox );
  	}
  
  	@Override
  	protected void uninstallDefaults() {
  		super.uninstallDefaults();
  
  		borderColor = null;
  		disabledBorderColor = null;
  
  		editableBackground = null;
  		disabledBackground = null;
  		disabledForeground = null;
  
  		buttonBackground = null;
  		buttonEditableBackground = null;
  		buttonArrowColor = null;
  		buttonDisabledArrowColor = null;
  		buttonHoverArrowColor = null;
  
  		MigLayoutVisualPadding.uninstall( comboBox );
  	}
  
  	@Override
  	protected LayoutManager createLayoutManager() {
  		return new BasicComboBoxUI.ComboBoxLayoutManager() {
  			@Override
  			public void layoutContainer( Container parent ) {
  				super.layoutContainer( parent );
  
  				if ( editor != null && padding != null ) {
  					// fix editor bounds by subtracting padding
  					editor.setBounds( FlatUIUtils.subtractInsets( editor.getBounds(), padding ) );
  				}
  			}
  		};
  	}
  
  	@Override
  	protected FocusListener createFocusListener() {
  		// repaint combobox to update focus border
  		return new BasicComboBoxUI.FocusHandler() {
  			@Override
  			public void focusGained( FocusEvent e ) {
  				super.focusGained( e );
  				if( comboBox != null && comboBox.isEditable() )
  					comboBox.repaint();
  			}
  
  			@Override
  			public void focusLost( FocusEvent e ) {
  				super.focusLost( e );
  				if( comboBox != null && comboBox.isEditable() )
  					comboBox.repaint();
  			}
  		};
  	}
  
  	@Override
  	protected PropertyChangeListener createPropertyChangeListener() {
  		return new BasicComboBoxUI.PropertyChangeHandler() {
  			@Override
  			public void propertyChange( PropertyChangeEvent e ) {
  				super.propertyChange( e );
  
  				Object source = e.getSource();
  				String propertyName = e.getPropertyName();
  
  				if( editor != null &&
  					((source == comboBox && propertyName == "foreground") ||
  					 (source == editor && propertyName == "enabled")) )
  				{
  					// fix editor component colors
  					updateEditorColors();
  				} else if( editor != null && source == comboBox && propertyName == "componentOrientation" ) {
  					ComponentOrientation o = (ComponentOrientation) e.getNewValue();
  					editor.applyComponentOrientation( o );
  				} else if( editor != null && FlatClientProperties.PLACEHOLDER_TEXT.equals( propertyName ) )
  					editor.repaint();
  				else if( FlatClientProperties.COMPONENT_ROUND_RECT.equals( propertyName ) )
  					comboBox.repaint();
  				else if( FlatClientProperties.MINIMUM_WIDTH.equals( propertyName ) )
  					comboBox.revalidate();
  			}
  		};
  	}
  
  	@Override
  	protected ComboPopup createPopup() {
  		return new FlatComboPopup( comboBox );
  	}
  
  	@Override
  	protected ComboBoxEditor createEditor() {
  		ComboBoxEditor comboBoxEditor = super.createEditor();
  
  		Component editor = comboBoxEditor.getEditorComponent();
  		if( editor instanceof JTextField ) {
  			JTextField textField = (JTextField) editor;
  			textField.setColumns( editorColumns );
  
  			// assign a non-null and non-javax.swing.plaf.UIResource border to the text field,
  			// otherwise it is replaced with default text field border when switching LaF
  			// because javax.swing.plaf.basic.BasicComboBoxEditor.BorderlessTextField.setBorder()
  			// uses "border instanceof javax.swing.plaf.basic.BasicComboBoxEditor.UIResource"
  			// instead of "border instanceof javax.swing.plaf.UIResource"
  			textField.setBorder( BorderFactory.createEmptyBorder() );
  		}
  
  		return comboBoxEditor;
  	}
  
  	@Override
  	protected void configureEditor() {
  		super.configureEditor();
  
  		// explicitly make non-opaque
  		if( editor instanceof JComponent )
  			((JComponent)editor).setOpaque( false );
  
  		editor.applyComponentOrientation( comboBox.getComponentOrientation() );
  
  		updateEditorColors();
  
  		// macOS
  		if( SystemInfo.isMacOS && editor instanceof JTextComponent ) {
  			// delegate actions from editor text field to combobox, which is necessary
  			// because text field on macOS already handle those keys
  			InputMap inputMap = ((JTextComponent)editor).getInputMap();
  			new EditorDelegateAction( inputMap, KeyStroke.getKeyStroke( "UP" ) );
  			new EditorDelegateAction( inputMap, KeyStroke.getKeyStroke( "KP_UP" ) );
  			new EditorDelegateAction( inputMap, KeyStroke.getKeyStroke( "DOWN" ) );
  			new EditorDelegateAction( inputMap, KeyStroke.getKeyStroke( "KP_DOWN" ) );
  			new EditorDelegateAction( inputMap, KeyStroke.getKeyStroke( "HOME" ) );
  			new EditorDelegateAction( inputMap, KeyStroke.getKeyStroke( "END" ) );
  		}
  	}
  
  	private void updateEditorColors() {
  		// use non-UIResource colors because when SwingUtilities.updateComponentTreeUI()
  		// is used, then the editor is updated after the combobox and the
  		// colors are again replaced with default colors
  		boolean isTextComponent = editor instanceof JTextComponent;
  		editor.setForeground( FlatUIUtils.nonUIResource( getForeground( isTextComponent || editor.isEnabled() ) ) );
  
  		if( isTextComponent )
  			((JTextComponent)editor).setDisabledTextColor( FlatUIUtils.nonUIResource( getForeground( false ) ) );
  	}
  
  	@Override
  	protected JButton createArrowButton() {
  		return new FlatComboBoxButton();
  	}
  
  	@Override
  	public void update( Graphics g, JComponent c ) {
  		float focusWidth = FlatUIUtils.getBorderFocusWidth( c );
  		float arc = FlatUIUtils.getBorderArc( c );
  
  		// fill background if opaque to avoid garbage if user sets opaque to true
  		if( c.isOpaque() && (focusWidth > 0 || arc > 0) )
  			FlatUIUtils.paintParentBackground( g, c );
  
  		Graphics2D g2 = (Graphics2D) g;
  		FlatUIUtils.setRenderingHints( g2 );
  
  		int width = c.getWidth();
  		int height = c.getHeight();
  		int arrowX = arrowButton.getX();
  		int arrowWidth = arrowButton.getWidth();
  		boolean paintButton = (comboBox.isEditable() || "button".equals( buttonStyle )) && !"none".equals( buttonStyle );
  		boolean enabled = comboBox.isEnabled();
  		boolean isLeftToRight = comboBox.getComponentOrientation().isLeftToRight();
  
  		// paint background
  		g2.setColor( getBackground( enabled ) );
  		FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
  
  		// paint arrow button background
  		if( enabled ) {
  			g2.setColor( paintButton ? buttonEditableBackground : buttonBackground );
  			Shape oldClip = g2.getClip();
  			if( isLeftToRight )
  				g2.clipRect( arrowX, 0, width - arrowX, height );
  			else
  				g2.clipRect( 0, 0, arrowX + arrowWidth, height );
  			FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
  			g2.setClip( oldClip );
  		}
  
  		// paint vertical line between value and arrow button
  		if( paintButton ) {
  			g2.setColor( enabled ? borderColor : disabledBorderColor );
  			float lw = scale( 1f );
  			float lx = isLeftToRight ? arrowX : arrowX + arrowWidth - lw;
  			g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - 1 - (focusWidth * 2)) );
  		}
  
  		paint( g, c );
  	}
  
  	@Override
  	@SuppressWarnings( "unchecked" )
  	public void paintCurrentValue( Graphics g, Rectangle bounds, boolean hasFocus ) {
  		ListCellRenderer<Object> renderer = comboBox.getRenderer();
  		uninstallCellPaddingBorder( renderer );
  		if( renderer == null )
  			renderer = new DefaultListCellRenderer();
  		Component c = renderer.getListCellRendererComponent( listBox, comboBox.getSelectedItem(), -1, false, false );
  		c.setFont( comboBox.getFont() );
  		c.applyComponentOrientation( comboBox.getComponentOrientation() );
  		uninstallCellPaddingBorder( c );
  
  		boolean enabled = comboBox.isEnabled();
  		c.setBackground( getBackground( enabled ) );
  		c.setForeground( getForeground( enabled ) );
  
  		boolean shouldValidate = (c instanceof JPanel);
  		if( padding != null )
  			bounds = FlatUIUtils.subtractInsets( bounds, padding );
  
  		// increase the size of the rendering area to make sure that the text
  		// is vertically aligned with other component types (e.g. JTextField)
  		Insets rendererInsets = getRendererComponentInsets( c );
  		if( rendererInsets != null )
  			bounds = FlatUIUtils.addInsets( bounds, rendererInsets );
  
  		currentValuePane.paintComponent( g, c, comboBox, bounds.x, bounds.y, bounds.width, bounds.height, shouldValidate );
  	}
  
  	@Override
  	public void paintCurrentValueBackground( Graphics g, Rectangle bounds, boolean hasFocus ) {
  		// not necessary because already painted in update()
  	}
  
  	protected Color getBackground( boolean enabled ) {
  		return enabled
  			? (editableBackground != null && comboBox.isEditable() ? editableBackground : comboBox.getBackground())
  			: (isIntelliJTheme ? FlatUIUtils.getParentBackground( comboBox ) : disabledBackground);
  	}
  
  	protected Color getForeground( boolean enabled ) {
  		return enabled ? comboBox.getForeground() : disabledForeground;
  	}
  
  	@Override
  	public Dimension getMinimumSize( JComponent c ) {
  		Dimension minimumSize = super.getMinimumSize( c );
  		minimumSize.width = Math.max( minimumSize.width, scale( FlatUIUtils.minimumWidth( c, minimumWidth ) ) );
  		return minimumSize;
  	}
  
  	@Override
  	protected Dimension getDefaultSize() {
  		@SuppressWarnings( "unchecked" )
  		ListCellRenderer<Object> renderer = comboBox.getRenderer();
  		uninstallCellPaddingBorder( renderer );
  
  		Dimension size = super.getDefaultSize();
  
  		uninstallCellPaddingBorder( renderer );
  		return size;
  	}
  
  	@Override
  	protected Dimension getDisplaySize() {
  		@SuppressWarnings( "unchecked" )
  		ListCellRenderer<Object> renderer = comboBox.getRenderer();
  		uninstallCellPaddingBorder( renderer );
  
  		Dimension displaySize = super.getDisplaySize();
  
  		// recalculate width without hardcoded 100 under special conditions
  		if( displaySize.width == 100 + padding.left + padding.right &&
  			comboBox.isEditable() &&
  			comboBox.getItemCount() == 0 &&
  			comboBox.getPrototypeDisplayValue() == null )
  		{
  			int width = getDefaultSize().width;
  			width = Math.max( width, editor.getPreferredSize().width );
  			width += padding.left + padding.right;
  			displaySize = new Dimension( width, displaySize.height );
  		}
  
  		uninstallCellPaddingBorder( renderer );
  		return displaySize;
  	}
  
  	@Override
  	protected Dimension getSizeForComponent( Component comp ) {
  		Dimension size = super.getSizeForComponent( comp );
  
  		// remove the renderer border top/bottom insets from the size to make sure that
  		// the combobox gets the same height as other component types (e.g. JTextField)
  		Insets rendererInsets = getRendererComponentInsets( comp );
  		if( rendererInsets != null )
  			size = new Dimension( size.width, size.height - rendererInsets.top - rendererInsets.bottom );
  
  		return size;
  	}
  
  	private Insets getRendererComponentInsets( Component rendererComponent ) {
  		if( rendererComponent instanceof JComponent ) {
  			Border rendererBorder = ((JComponent)rendererComponent).getBorder();
  			if( rendererBorder != null )
  				return rendererBorder.getBorderInsets( rendererComponent );
  		}
  
  		return null;
  	}
  
  	private void uninstallCellPaddingBorder( Object o ) {
  		CellPaddingBorder.uninstall( o );
  		if( lastRendererComponent != null ) {
  			CellPaddingBorder.uninstall( lastRendererComponent );
  			lastRendererComponent = null;
  		}
  	}
  
  	//---- class FlatComboBoxButton -------------------------------------------
  
  	protected class FlatComboBoxButton
  		extends FlatArrowButton
  	{
  		protected FlatComboBoxButton() {
  			this( SwingConstants.SOUTH, arrowType, buttonArrowColor, buttonDisabledArrowColor, buttonHoverArrowColor, null, null );
  		}
  
  		protected FlatComboBoxButton( int direction, String type, Color foreground, Color disabledForeground,
  			Color hoverForeground, Color hoverBackground, Color pressedBackground )
  		{
  			super( direction, type, foreground, disabledForeground, hoverForeground, hoverBackground, pressedBackground );
  		}
  
  		@Override
  		protected boolean isHover() {
  			return super.isHover() || (!comboBox.isEditable() ? hover : false);
  		}
  	}
  
  	//---- class FlatComboPopup -----------------------------------------------
  
  	@SuppressWarnings( { "rawtypes", "unchecked" } )
  	protected class FlatComboPopup
  		extends BasicComboPopup
  	{
  		private CellPaddingBorder paddingBorder;
  
  		protected FlatComboPopup( JComboBox combo ) {
  			super( combo );
  
  			// BasicComboPopup listens to JComboBox.componentOrientation and updates
  			// the component orientation of the list, scroller and popup, but when
  			// switching the LaF and a new combo popup is created, the component
  			// orientation is not applied.
  			ComponentOrientation o = comboBox.getComponentOrientation();
  			list.setComponentOrientation( o );
  			scroller.setComponentOrientation( o );
  			setComponentOrientation( o );
  		}
  
  		@Override
  		protected Rectangle computePopupBounds( int px, int py, int pw, int ph ) {
  			// get maximum display width of all items
  			int displayWidth = getDisplaySize().width;
  
  			// add border insets
  			for( Border border : new Border[] { scroller.getViewportBorder(), scroller.getBorder() } ) {
  				if( border != null ) {
  					Insets borderInsets = border.getBorderInsets( null );
  					displayWidth += borderInsets.left + borderInsets.right;
  				}
  			}
  
  			// add width of vertical scroll bar
  			JScrollBar verticalScrollBar = scroller.getVerticalScrollBar();
  			if( verticalScrollBar != null )
  				displayWidth += verticalScrollBar.getPreferredSize().width;
  
  			// make popup wider if necessary
  			if( displayWidth > pw ) {
  				int diff = displayWidth - pw;
  				pw = displayWidth;
  
  				if( !comboBox.getComponentOrientation().isLeftToRight() )
  					px -= diff;
  			}
  
  			return super.computePopupBounds( px, py, pw, ph );
  		}
  
  		@Override
  		protected void configurePopup() {
  			super.configurePopup();
  
  			Border border = UIManager.getBorder( "PopupMenu.border" );
  			if( border != null )
  				setBorder( border );
  		}
  
  		@Override
  		protected void configureList() {
  			super.configureList();
  
  			list.setCellRenderer( new PopupListCellRenderer() );
  		}
  
  		@Override
  		protected PropertyChangeListener createPropertyChangeListener() {
  			return new BasicComboPopup.PropertyChangeHandler() {
  				@Override
  				public void propertyChange( PropertyChangeEvent e ) {
  					super.propertyChange( e );
  
  					if( e.getPropertyName() == "renderer" )
  						list.setCellRenderer( new PopupListCellRenderer() );
  				}
  			};
  		}
  
  		//---- class PopupListCellRenderer -----
  
  		private class PopupListCellRenderer
  			implements ListCellRenderer
  		{
  			@Override
  			public Component getListCellRendererComponent( JList list, Object value,
  				int index, boolean isSelected, boolean cellHasFocus )
  			{
  				ListCellRenderer renderer = comboBox.getRenderer();
  				CellPaddingBorder.uninstall( renderer );
  				CellPaddingBorder.uninstall( lastRendererComponent );
  
  				if( renderer == null )
  					renderer = new DefaultListCellRenderer();
  				Component c = renderer.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus );
  				c.applyComponentOrientation( comboBox.getComponentOrientation() );
  
  				if( c instanceof JComponent ) {
  					if( paddingBorder == null )
  						paddingBorder = new CellPaddingBorder( padding );
  					paddingBorder.install( (JComponent) c );
  				}
  
  				lastRendererComponent = (c != renderer) ? new WeakReference<>( c ) : null;
  
  				return c;
  			}
  		}
  	}
  
  	//---- class CellPaddingBorder --------------------------------------------
  
  	/**
  	 * Cell padding border used only in popup list.
  	 *
  	 * The insets are the union of the cell padding and the renderer border insets,
  	 * which vertically aligns text in popup list with text in combobox.
  	 *
  	 * The renderer border is painted on the outside of this border.
  	 */
  	private static class CellPaddingBorder
  		extends AbstractBorder
  	{
  		private final Insets padding;
  		private Border rendererBorder;
  
  		CellPaddingBorder( Insets padding ) {
  			this.padding = padding;
  		}
  
  		void install( JComponent rendererComponent ) {
  			Border oldBorder = rendererComponent.getBorder();
  			if( !(oldBorder instanceof CellPaddingBorder) ) {
  				rendererBorder = oldBorder;
  				rendererComponent.setBorder( this );
  			}
  		}
  
  		static void uninstall( Object o ) {
  			if( o instanceof WeakReference )
  				o = ((WeakReference<?>)o).get();
  
  			if( !(o instanceof JComponent) )
  				return;
  
  			JComponent rendererComponent = (JComponent) o;
  			Border border = rendererComponent.getBorder();
  			if( border instanceof CellPaddingBorder ) {
  				CellPaddingBorder paddingBorder = (CellPaddingBorder) border;
  				rendererComponent.setBorder( paddingBorder.rendererBorder );
  				paddingBorder.rendererBorder = null;
  			}
  		}
  
  		@Override
  		public Insets getBorderInsets( Component c, Insets insets ) {
  			if( rendererBorder != null ) {
  				Insets insideInsets = rendererBorder.getBorderInsets( c );
  				insets.top = Math.max( padding.top, insideInsets.top );
  				insets.left = Math.max( padding.left, insideInsets.left );
  				insets.bottom = Math.max( padding.bottom, insideInsets.bottom );
  				insets.right = Math.max( padding.right, insideInsets.right );
  			} else {
  				insets.top = padding.top;
  				insets.left = padding.left;
  				insets.bottom = padding.bottom;
  				insets.right = padding.right;
  			}
  			return insets;
  		}
  
  		@Override
  		public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
  			if( rendererBorder != null )
  				rendererBorder.paintBorder( c, g, x, y, width, height );
  		}
  	}
  
  	//---- class EditorDelegateAction -----------------------------------------
  
  	/**
  	 * Delegates actions from editor text field to combobox.
  	 */
  	private class EditorDelegateAction
  		extends AbstractAction
  	{
  		private final KeyStroke keyStroke;
  
  		EditorDelegateAction( InputMap inputMap, KeyStroke keyStroke ) {
  			this.keyStroke = keyStroke;
  
  			// add to input map
  			inputMap.put( keyStroke, this );
  		}
  
  		@Override
  		public void actionPerformed( ActionEvent e ) {
  			ActionListener action = comboBox.getActionForKeyStroke( keyStroke );
  			if( action != null ) {
  				action.actionPerformed( new ActionEvent( comboBox, e.getID(),
  					e.getActionCommand(), e.getWhen(), e.getModifiers() ) );
  			}
  		}
  	}
  }
  