package org.japura.gui;
import org.japura.gui.event.ButtonTextFieldEvent;
import org.japura.gui.event.ButtonTextFieldListener;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.event.CaretListener;
import javax.swing.event.EventListenerList;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.text.Document;
import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.net.URL;
import java.util.LinkedHashMap;
/**
* Copyright (C) 2010-2015 Carlos Eduardo Leite de Andrade
* <P>
* This library is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
* <P>
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
* <P>
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <A
* HREF="www.gnu.org/licenses/">www.gnu.org/licenses/</A>
* <P>
* For more information, contact: <A HREF="www.japura.org">www.japura.org</A>
* <P>
*
* @author Carlos Eduardo Leite de Andrade
*/
public class ButtonTextField extends JPanel{
private static final long serialVersionUID = 2L;
private EventListenerList listeners = new EventListenerList();
private JTextField field;
private IconLabel icon;
private IconLabel selectedDropDownIcon;
private JPanel dropDownButtonPanel;
private JLabel dropDownButton;
private LinkedHashMap<String, Action> actions;
private String currentButton;
private MouseAdapter popupMouseAdapter;
private JPopupMenu actionsChooser;
private JPanel actionsRoot;
private DefaultListModel listModel;
private JList actionsList;
private ListRenderer listRenderer;
private Icon fixedIcon;
private int maxWidth;
private int maxHeight;
private Cursor iconCursor = new Cursor(Cursor.HAND_CURSOR);
private static int marginGap = 3;
public ButtonTextField(String text) {
this(new JTextField(text));
updateMargin();
}
public ButtonTextField(int columns) {
this(new JTextField(columns));
updateMargin();
}
public ButtonTextField(String text, int columns) {
this(new JTextField(text, columns));
updateMargin();
}
public ButtonTextField() {
this(new JTextField());
updateMargin();
}
public ButtonTextField(JTextField field) {
if (field == null) {
field = new JTextField();
}
this.field = field;
setBorder(field.getBorder());
field.setBorder(null);
field.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("margin")) {
Insets margin = getField().getMargin();
if (margin == null) {
getField().setBorder(null);
} else {
Border border =
BorderFactory.createEmptyBorder(margin.top, margin.left,
margin.bottom, margin.right);
getField().setBorder(border);
}
}
}
});
updateMargin();
actions = new LinkedHashMap<String, Action>();
setBackground(Color.WHITE);
setOpaque(true);
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.fill = GridBagConstraints.VERTICAL;
gbc.weighty = 1;
add(getDropDownButtonPanel(), gbc);
gbc.gridx = 1;
gbc.gridy = 0;
gbc.weighty = 0;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
add(getField(), gbc);
gbc.gridx = 2;
gbc.gridy = 0;
gbc.weightx = 0;
add(getIcon(), gbc);
}
public void addButtonTextFieldListener(ButtonTextFieldListener listener) {
listeners.add(ButtonTextFieldListener.class, listener);
}
public void removeButtonTextFieldListener(ButtonTextFieldListener listener) {
listeners.remove(ButtonTextFieldListener.class, listener);
}
private void updateMargin() {
getField().setMargin(new Insets(0, marginGap, 0, marginGap));
}
public Icon getFixedIcon() {
return fixedIcon;
}
public void setFixedIcon(Icon fixedIcon) {
this.fixedIcon = fixedIcon;
if (getFixedIcon() != null) {
getIcon().setIcon(getFixedIcon());
} else {
if (icon != null) {
Action action = actions.get(currentButton);
Icon icon = action.getIcon();
getIcon().setIcon(icon);
} else {
getIcon().setIcon(null);
}
}
updateMaxSizes();
}
public boolean isDropDownVisible() {
return getDropDownButtonPanel().isVisible();
}
public void setDropDownVisible(boolean visible) {
getDropDownButtonPanel().setVisible(visible);
updateDropDownIconBorder();
}
@Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
Dimension dim = new Dimension();
// field
Dimension fieldDim = field.getPreferredSize();
Insets fieldInsets = field.getInsets();
dim.width += fieldDim.width + fieldInsets.left + fieldInsets.right;
dim.height += fieldDim.height + fieldInsets.top + fieldInsets.bottom;
// options
if (isDropDownVisible()) {
Dimension dropDownDim = getDropDownButtonPanel().getPreferredSize();
Insets dropDownInsets = getDropDownButtonPanel().getInsets();
int dropDownHeight =
dropDownDim.height + dropDownInsets.top + dropDownInsets.bottom;
dim.width +=
dropDownDim.width + dropDownInsets.left + dropDownInsets.right;
dim.height = Math.max(dim.height, dropDownHeight);
}
// icon
if (isIconVisible()) {
if (getFixedIcon() != null) {
Dimension iconDim = getIcon().getPreferredSize();
dim.width += iconDim.width;
dim.height = Math.max(dim.height, iconDim.height);
} else {
Insets iconInsets = getIcon().getInsets();
int iconHeight = maxHeight + iconInsets.top + iconInsets.bottom;
dim.width += maxWidth + iconInsets.left + iconInsets.right;
dim.height = Math.max(dim.height, iconHeight);
}
}
// border
Insets insets = getInsets();
dim.width += insets.left + insets.right;
dim.height += insets.bottom + insets.top;
return dim;
}
public void fireCurrentButtonActionListeners() {
if (currentButton != null && isEnabled()) {
Action action = actions.get(currentButton);
ActionListener listener = action.getListener();
if (listener != null) {
listener.actionPerformed(new ActionEvent(this,
ActionEvent.ACTION_PERFORMED, ""));
}
}
}
private void showButtonsChooser() {
if (isEnabled()) {
Dimension dim = getSize();
Dimension bcDim = getActionsRoot().getPreferredSize();
Insets insets = getActionsChooser().getInsets();
int width = dim.width;
width = bcDim.width + insets.left + insets.right;
int height = bcDim.height + insets.bottom + insets.top;
Dimension newDim = new Dimension(width, height);
getActionsChooser().setPreferredSize(newDim);
getActionsChooser().show(this, 0, dim.height);
}
}
private JPopupMenu getActionsChooser() {
if (actionsChooser == null) {
actionsChooser = new JPopupMenu();
actionsChooser.add(getActionsRoot());
}
return actionsChooser;
}
private JPanel getActionsRoot() {
if (actionsRoot == null) {
actionsRoot = new JPanel();
actionsRoot.setLayout(new BorderLayout());
actionsRoot.setBorder(BorderFactory.createLineBorder(Color.WHITE, 5));
actionsRoot.add(getActionsList(), BorderLayout.CENTER);
}
return actionsRoot;
}
private DefaultListModel getListModel() {
if (listModel == null) {
listModel = new DefaultListModel();
}
return listModel;
}
private ListRenderer getListRenderer() {
if (listRenderer == null) {
listRenderer = new ListRenderer();
}
return listRenderer;
}
private JList getActionsList() {
if (actionsList == null) {
actionsList = new JList();
actionsList.setCellRenderer(getListRenderer());
actionsList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
actionsList.setModel(getListModel());
actionsList.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting() == false) {
if (actionsList.getSelectedIndex() > -1) {
actionsList.clearSelection();
}
}
}
});
actionsList.addMouseMotionListener(new MouseMotionListener() {
@Override
public void mouseDragged(MouseEvent e) {}
@Override
public void mouseMoved(MouseEvent e) {
listRenderer.mouseOverIndex =
actionsList.locationToIndex(e.getPoint());
actionsList.repaint();
}
});
actionsList.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
String actionName = null;
if (listRenderer.mouseOverIndex >= 0
&& listRenderer.mouseOverIndex < getListModel().size()) {
actionName =
(String) getListModel().get(listRenderer.mouseOverIndex);
}
getActionsChooser().setVisible(false);
listRenderer.mouseOverIndex = -1;
if (actionName != null) {
setCurrentButton(actionName);
}
}
@Override
public void mouseExited(MouseEvent e) {
listRenderer.mouseOverIndex = -1;
actionsList.repaint();
}
});
}
return actionsList;
}
public void addButton(String buttonName, ActionListener listener) {
addButton(buttonName, null, listener);
}
public void addButton(String buttonName, Icon icon, ActionListener listener) {
Action action = new Action(listener, icon);
actions.put(buttonName, action);
getListModel().addElement(buttonName);
if (currentButton == null) {
setCurrentButton(buttonName);
}
updateMaxSizes();
updateDropDownIconBorder();
}
private void updateDropDownIconBorder() {
if (isDropDownVisible()) {
for (Action action : actions.values()) {
if (action.getIcon() != null) {
selectedDropDownIcon.setBorder(BorderFactory.createEmptyBorder(2, 0,
2, 3));
return;
}
}
}
selectedDropDownIcon.setBorder(BorderFactory.createEmptyBorder());
}
private void updateMaxSizes() {
maxWidth = 0;
maxHeight = 0;
for (Action action : actions.values()) {
if (action.getIcon() != null) {
maxWidth = Math.max(maxWidth, action.getIcon().getIconWidth());
maxHeight = Math.max(maxHeight, action.getIcon().getIconHeight());
}
}
}
public JTextField getField() {
return field;
}
public boolean isIconVisible() {
return getIcon().isVisible();
}
public void setIconVisible(boolean visible) {
getIcon().setVisible(visible);
}
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
field.setEnabled(enabled);
getIcon().setEnabled(enabled);
getDropDownButton().setEnabled(enabled);
}
public void setCurrentButton(String buttonName) {
Action action = actions.get(buttonName);
if (action != null) {
ButtonTextFieldEvent event =
new ButtonTextFieldEvent(currentButton, buttonName,
System.currentTimeMillis());
for (ButtonTextFieldListener l : listeners
.getListeners(ButtonTextFieldListener.class)) {
l.buttonChanged(event);
}
this.currentButton = buttonName;
Icon icon = action.getIcon();
if (icon != null) {
getSelectedDropDownIcon().setIcon(icon);
}
else {
getSelectedDropDownIcon().setIcon(null);
}
if (getFixedIcon() == null) {
getIcon().setIcon(icon);
}
if (action.getListener() != null) {
getIcon().setCursor(getIconCursor());
}
else {
getIcon().setCursor(null);
}
}
}
public String getCurrentButtonName() {
return currentButton;
}
private IconLabel getIcon() {
if (icon == null) {
icon = new IconLabel(true);
icon.setBorder(BorderFactory.createEmptyBorder(2, 0, 2, 2));
icon.setBackground(field.getBackground());
icon.setOpaque(true);
icon.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
fireCurrentButtonActionListeners();
}
});
}
return icon;
}
public void setIconCursor(Cursor cursor) {
if (cursor != null) {
getIcon().setCursor(cursor);
}
}
public Cursor getIconCursor() {
return iconCursor;
}
@Override
public void setBackground(Color background) {
super.setBackground(background);
if (field != null) {
field.setBackground(background);
getIcon().setBackground(background);
}
}
private MouseAdapter getPopupMouseAdapter() {
if (popupMouseAdapter == null) {
popupMouseAdapter = new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
showButtonsChooser();
}
};
}
return popupMouseAdapter;
}
private JLabel getDropDownButton() {
if (dropDownButton == null) {
URL url = getClass().getResource("/images/jpr_splitbuttondown.png");
dropDownButton = new JLabel();
dropDownButton.setIcon(new ImageIcon(url));
dropDownButton.addMouseListener(getPopupMouseAdapter());
}
return dropDownButton;
}
public boolean isSelectedDropDownIconVisible() {
return getSelectedDropDownIcon().isVisible();
}
public void setDropDownButtonBackground(Color color) {
getDropDownButtonPanel().setBackground(color);
}
public Color getDropDownButtonBackground() {
return getDropDownButtonPanel().getBackground();
}
public void setSelectedDropDownIconVisible(boolean visible) {
getSelectedDropDownIcon().setVisible(visible);
}
private IconLabel getSelectedDropDownIcon() {
if (selectedDropDownIcon == null) {
selectedDropDownIcon = new IconLabel(false);
selectedDropDownIcon.addMouseListener(getPopupMouseAdapter());
}
return selectedDropDownIcon;
}
private JPanel getDropDownButtonPanel() {
if (dropDownButtonPanel == null) {
dropDownButtonPanel = new JPanel(new BorderLayout());
dropDownButtonPanel.addMouseListener(getPopupMouseAdapter());
dropDownButtonPanel.add(getSelectedDropDownIcon(), BorderLayout.WEST);
dropDownButtonPanel.add(getDropDownButton(), BorderLayout.CENTER);
Border out = BorderFactory.createMatteBorder(0, 0, 0, 1, Color.GRAY);
Border in = BorderFactory.createEmptyBorder(0, 2, 0, 2);
Border botder = BorderFactory.createCompoundBorder(out, in);
dropDownButtonPanel.setBorder(botder);
}
return dropDownButtonPanel;
}
/**
* @see JTextField#setText(String)
*/
public void setText(String text) {
getField().setText(text);
}
/**
* @see JTextField#getText()
*/
public String getText() {
return getField().getText();
}
/**
* @see JTextField#setDocument(Document)
*/
public void setDocument(Document doc) {
getField().setDocument(doc);
}
/**
* @see JTextField#getDocument()
*/
public Document getDocument() {
return getField().getDocument();
}
@Override
public synchronized void addFocusListener(FocusListener listener) {
getField().addFocusListener(listener);
}
/**
* @see JTextField#addKeyListener(KeyListener)
*/
public synchronized void addKeyListener(KeyListener listener) {
getField().addKeyListener(listener);
}
/**
* @see JTextField#addCaretListener(CaretListener)
*/
public void addCaretListener(CaretListener listener) {
getField().addCaretListener(listener);
}
private static class Action{
private ActionListener listener;
private Icon icon;
public Action(ActionListener listener, Icon icon) {
this.listener = listener;
if (icon != null) {
this.icon = icon;
}
}
protected ActionListener getListener() {
return listener;
}
protected Icon getIcon() {
return icon;
}
}
private class IconLabel extends JLabel{
private static final long serialVersionUID = 729241592604471900L;
private boolean fieldIcon;
public IconLabel(boolean fieldIcon) {
super("", null, JLabel.CENTER);
this.fieldIcon = fieldIcon;
}
@Override
public Dimension getPreferredSize() {
if (fieldIcon) {
return super.getPreferredSize();
}
Insets insets = getInsets();
int w = insets.left + insets.right + maxWidth;
int h = insets.top + insets.bottom + maxHeight;
return new Dimension(w, h);
}
}
private class ListRenderer extends JPanel implements ListCellRenderer{
private static final long serialVersionUID = -7811567885926598910L;
public int mouseOverIndex;
private Border mouseOverBorder;
private Border mouseOutBorder;
private JLabel label;
private IconLabel icon;
public ListRenderer() {
label = new JLabel();
icon = new IconLabel(false);
setLayout(new BorderLayout(2, 0));
add(label, BorderLayout.CENTER);
add(icon, BorderLayout.WEST);
setBackground(Color.WHITE);
label.setOpaque(true);
}
@Override
public Component getListCellRendererComponent(JList list, Object value,
int index,
boolean isSelected,
boolean cellHasFocus) {
String name = (String) value;
label.setText(name);
Action action = actions.get(name);
if (action.getIcon() != null) {
icon.setIcon(action.getIcon());
} else {
icon.setIcon(null);
}
if (index == mouseOverIndex) {
label.setBorder(getMouseOverBorder());
label.setBackground(Color.BLACK);
label.setForeground(Color.WHITE);
} else {
label.setBorder(getMouseOutBorder());
label.setBackground(Color.WHITE);
label.setForeground(Color.BLACK);
}
return this;
}
public Border getMouseOverBorder() {
if (mouseOverBorder == null) {
mouseOverBorder = BorderFactory.createLineBorder(Color.BLACK, 3);
}
return mouseOverBorder;
}
public Border getMouseOutBorder() {
if (mouseOutBorder == null) {
mouseOutBorder = BorderFactory.createLineBorder(Color.WHITE, 3);
}
return mouseOutBorder;
}
}
}