All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openbp.swing.components.popupfield.JPopupField Maven / Gradle / Ivy

/*
 *   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 org.openbp.swing.components.popupfield;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.Iterator;

import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
import javax.swing.plaf.basic.BasicArrowButton;

import org.openbp.common.listener.AWTListenerSupport;
import org.openbp.swing.plaf.sky.SimpleBorder;
import org.openbp.swing.plaf.sky.SkyTheme;

/**
 * Text field component that displays a small arrow button to the right that is
 * used to activate a popup menu/dialog.
 * This component is used as base class for selection fields, file path widgets etc.
 *
 * @author Heiko Erhardt
 */
public abstract class JPopupField extends JPanel
{
	/** Label */
	protected JLabel label;

	/** Arrow button */
	protected JButton arrowButton;

	/** Text field */
	protected JTextField textField;

	/** Listeners */
	private AWTListenerSupport listenerSupport;

	/**
	 * Default constructor.
	 */
	public JPopupField()
	{
		super(new BorderLayout());
		setOpaque(true);

		setFocusable(false);

		// Create the components
		textField = createTextField();
		arrowButton = createArrowButton();

		// 2 pixels distance to the button
		textField.setBorder(new CompoundBorder(new EmptyBorder(0, 0, 0, 2), textField.getBorder()));

		// Text field to the left
		add(textField, BorderLayout.CENTER);

		// Button to the right
		add(arrowButton, BorderLayout.EAST);

		installAncestorListener();
	}

	/**
	 * Creates the text field.
	 */
	protected JTextField createTextField()
	{
		JTextField tf = new JTextField();

		PopupFieldKeyListener keyListener = new PopupFieldKeyListener();
		addKeyListener(keyListener);
		tf.addKeyListener(keyListener);

		tf.addFocusListener(new PopupFieldFocusListener());

		return tf;
	}

	/**
	 * Creates the arrow button.
	 */
	protected JButton createArrowButton()
	{
		JButton btn = new BasicArrowButton(SwingConstants.SOUTH);
		btn.setBorder(new SimpleBorder(0, 2, 0, 2));

		btn.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				if (isEnabled())
				{
					showPopup();
				}
			}
		});
		btn.setEnabled(true);

		// We do not want the button to be focusable, it can be clicked only
		btn.setFocusable(false);
		btn.setRequestFocusEnabled(false);

		// Make the button the same size as the text field
		Dimension size = new Dimension(btn.getMinimumSize());
		int h = textField.getHeight();
		size.height = h;
		btn.setMinimumSize(size);
		btn.setMaximumSize(size);
		btn.setPreferredSize(size);

		return btn;
	}

	/**
	 * Installs an ancestor listener that hides the popup on any change.
	 */
	protected void installAncestorListener()
	{
		addAncestorListener(new AncestorListener()
		{
			public void ancestorAdded(AncestorEvent event)
			{
				hidePopup();
			}

			public void ancestorRemoved(AncestorEvent event)
			{
				hidePopup();
			}

			public void ancestorMoved(AncestorEvent event)
			{
				if (event.getSource() != JPopupField.this)
					hidePopup();
			}
		});
	}

	public void setHighlight()
	{
		if (!isEditable())
		{
			textField.setBackground(SkyTheme.COLOR_HIGHLIGHT);
		}
		else
		{
			textField.setSelectionStart(0);
			int length = textField.getText().length();
			textField.setSelectionEnd(length);
		}
	}

	public void resetHighlight()
	{
		if (!isEditable())
		{
			textField.setBackground(UIManager.getColor("TextField.background"));
		}
		else
		{
			textField.setCaretPosition(0);
			textField.setSelectionStart(0);
			textField.setSelectionEnd(0);
		}
	}

	//////////////////////////////////////////////////
	// @@ JComponent overrides
	//////////////////////////////////////////////////

	/**
	 * Requests the focus for this component.
	 */
	public void requestFocus()
	{
		// Delegate the focus to the text field
		textField.requestFocus();
	}

	/**
	 * Gets the enabled state.
	 */
	public boolean isEnabled()
	{
		return textField.isEnabled();
	}

	/**
	 * Sets the enabled state.
	 */
	public void setEnabled(boolean enabled)
	{
		textField.setEnabled(enabled);
		arrowButton.setEnabled(enabled);
	}

	/**
	 * Selects a field value using a key character.
	 * By default, this method does nothing and returns false.
	 *
	 * @param selectionChar Selection character
	 * @return
	 *		true	An item was selected.
* false No item was found that begins with the specified character. */ protected boolean selectWithKeyChar(char selectionChar) { return false; } ////////////////////////////////////////////////// // @@ Popup operations ////////////////////////////////////////////////// /** * Shows the popup. */ public void showPopup() { setPopupVisible(true); } /** * Hides the popup. */ public void hidePopup() { setPopupVisible(false); } /** * Gets the popup visibility. * @return * true The popup is shown.
* false The popup is hidden. */ public abstract boolean isPopupVisible(); /** * Sets the popup visibility. * @param popupVisible * true Shows the popup.
* false Hides the popup. */ public abstract void setPopupVisible(boolean popupVisible); ////////////////////////////////////////////////// // @@ Property access ////////////////////////////////////////////////// /** * Gets the text displayed in the popup field. * @return The text or null if the field is empty */ public String getText() { String text = textField.getText(); return text.length() != 0 ? text : null; } /** * Sets the text displayed in the popup field. */ public void setText(String text) { textField.setText(text); } /** * Gets the text field. */ public JTextField getTextField() { return textField; } /** * Gets the label text of in the popup field. */ public String getLabelText() { return label != null ? label.getText() : null; } /** * Sets the label text of in the popup field. */ public void setLabelText(String labelText) { if (label != null) { remove(label); label = null; } if (labelText != null) { label = new JLabel(labelText); label.setBorder(new EmptyBorder(0, 0, 0, 5)); add(label, BorderLayout.WEST); } } /** * Gets the editable flag. */ public boolean isEditable() { return textField.isEditable(); } /** * Sets the editable flag. */ public void setEditable(boolean editable) { textField.setEditable(editable); } ////////////////////////////////////////////////// // @@ Listener support ////////////////////////////////////////////////// /** * Fires an 'action performed' message to all registered action listeners * if an item has been selected from the popup menu or using keyboard selection. * The action command will be "selected" in this case. */ public void fireActionPerformed() { if (listenerSupport != null && listenerSupport.containsListeners(ActionListener.class)) { listenerSupport.fireActionPerformed(new ActionEvent(this, 0, "selected")); } } /** * Adds an action listener to the listener list. * An ActionEvent will get fired in response to choosing an item from the popup. * * @param listener The listener to be added */ public synchronized void addActionListener(ActionListener listener) { if (listenerSupport == null) { listenerSupport = new AWTListenerSupport(); } listenerSupport.addListener(ActionListener.class, listener); } /** * Removes an action listener from the listener list. * * @param listener The listener to be removed */ public synchronized void removeActionListener(ActionListener listener) { if (listenerSupport != null) { listenerSupport.removeListener(ActionListener.class, listener); } } /** * Fires a 'popup' message to all registered popup listeners * if an item has been selected from the popup menu or using keyboard selection. * * @param cause The cause for the popup event (see the {@link PopupEvent} class) */ public void firePopup(int cause) { if (listenerSupport != null) { PopupEvent e = null; for (Iterator it = listenerSupport.getListenerIterator(PopupListener.class); it.hasNext();) { if (e == null) e = new PopupEvent(this, cause); ((PopupListener) it.next()).popupStateChanged(e); } } } /** * Adds a popup listener to the listener list. * An PopupEvent will get fired in response to choosing an item from the popup. * * @param listener The listener to be added */ public synchronized void addPopupListener(PopupListener listener) { if (listenerSupport == null) { listenerSupport = new AWTListenerSupport(); } listenerSupport.addListener(PopupListener.class, listener); } /** * Removes a popup listener from the listener list. * * @param listener The listener to be removed */ public synchronized void removePopupListener(PopupListener listener) { if (listenerSupport != null) { listenerSupport.removeListener(PopupListener.class, listener); } } ////////////////////////////////////////////////// // @@ Helper classes ////////////////////////////////////////////////// /** * Key listener for the popup field. */ private class PopupFieldKeyListener extends KeyAdapter { /** * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent) */ public void keyPressed(KeyEvent e) { int keyCode = e.getKeyCode(); switch (keyCode) { case KeyEvent.VK_SPACE: // SPACE shows the popup if the field is not editable (in editable fields, we might want to input a space) if (!isEditable() && !isPopupVisible()) { e.consume(); showPopup(); return; } break; case KeyEvent.VK_ENTER: case KeyEvent.VK_DOWN: if (!isPopupVisible()) { if (e.isControlDown() || e.isAltDown() || e.isAltGraphDown()) { // CTRL/ALT ENTER/DOWN shows item browser dialog e.consume(); showPopup(); return; } // Simply ENTER triggers the action if (keyCode == KeyEvent.VK_ENTER) { fireActionPerformed(); } } break; case KeyEvent.VK_TAB: hidePopup(); break; default: // Perform first-character selection only in non-edit fields if (!isEditable()) { char c = e.getKeyChar(); if (selectWithKeyChar(c)) { e.consume(); return; } } break; } } } /** * Focus listener for the popup field. */ private class PopupFieldFocusListener implements FocusListener { /** * @see java.awt.event.FocusListener#focusGained(java.awt.event.FocusEvent) */ public void focusGained(FocusEvent e) { setHighlight(); } /** * @see java.awt.event.FocusListener#focusLost(java.awt.event.FocusEvent) */ public void focusLost(FocusEvent e) { resetHighlight(); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy