package org.japura.gui;
import javax.swing.*;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
import javax.swing.event.MenuKeyEvent;
import javax.swing.event.MenuKeyListener;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.LayoutManager;
import java.awt.event.*;
/**
* <P>
* Copyright (C) 2011-2012 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 abstract class AbstractComboBox extends JComponent{
private static final long serialVersionUID = 5685295148232238680L;
private Popup popup;
private EmbeddedComponent embeddedComponent;
private long lastViewChanged;
private WrapperComponent wrapperComponent;
private JComboBox comboBox;
private String prototypeDisplayValue;
public AbstractComboBox() {
setFocusable(true);
super.setLayout(new BorderLayout());
add(getWrapperComponent(), BorderLayout.CENTER);
addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
getComboBox().requestFocusInWindow();
}
});
addAncestorListener(new AncestorListener() {
public void ancestorAdded(AncestorEvent event) {
setPopupVisible(false);
}
public void ancestorRemoved(AncestorEvent event) {
setPopupVisible(false);
}
public void ancestorMoved(AncestorEvent event) {
if (event.getSource() != AbstractComboBox.this)
setPopupVisible(false);
}
});
}
protected abstract JComponent getPopupComponent();
protected final JComboBox getComboBox() {
if (comboBox == null) {
comboBox = new ComboBox();
}
return comboBox;
}
@Override
public void setBackground(Color bg) {
getComboBox().setBackground(bg);
}
@Override
public void setForeground(Color fg) {
getComboBox().setForeground(fg);
}
@Override
public Color getForeground() {
return getComboBox().getForeground();
}
@Override
public Color getBackground() {
return getComboBox().getBackground();
}
private WrapperComponent getWrapperComponent() {
if (wrapperComponent == null) {
MouseListener listener = new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
long time = Math.abs(lastViewChanged - e.getWhen());
if (time > 20) {
changePopupVisible();
}
}
};
wrapperComponent = new WrapperComponent(getComboBox(), listener);
}
return wrapperComponent;
}
public EmbeddedComponent removeEmbeddedComponent() {
EmbeddedComponent ret = embeddedComponent;
unregisterEmbeddedComponent(ret.getComponent());
embeddedComponent = null;
return ret;
}
protected abstract void unregisterEmbeddedComponent(JComponent oldEmbeddedComponent);
protected abstract void registerEmbeddedComponent(JComponent newEmbeddedComponent);
public void setEmbeddedComponent(EmbeddedComponent embeddedComponent) {
if (this.embeddedComponent != null) {
JComponent comp = embeddedComponent.getComponent();
unregisterEmbeddedComponent(comp);
}
this.embeddedComponent = embeddedComponent;
JComponent comp = embeddedComponent.getComponent();
registerEmbeddedComponent(comp);
}
public EmbeddedComponent getEmbeddedComponent() {
return embeddedComponent;
}
private void changePopupVisible() {
setPopupVisible(!isPopupVisible());
}
public boolean isPopupVisible() {
return getPopup().isVisible();
}
public abstract void setPopupVisible(boolean visible);
public void setPrototypeDisplayValue(String prototypeDisplayValue) {
this.prototypeDisplayValue = prototypeDisplayValue;
}
public String getPrototypeDisplayValue() {
return prototypeDisplayValue;
}
@Override
public final void setLayout(LayoutManager mgr) {}
protected abstract void updateComboBox();
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
getWrapperComponent().setEnabled(enabled);
}
protected final Popup getPopup() {
if (popup == null) {
popup = new Popup();
popup.setLayout(new BorderLayout());
popup.setBorder(BorderFactory.createEmptyBorder());
popup.add(getPopupComponent());
if (getEmbeddedComponent() != null) {
Anchor anchor = getEmbeddedComponent().getAnchor();
if (anchor == null) {
throw new IllegalArgumentException("Null Anchor");
}
JPanel embeddedPanel = new JPanel();
embeddedPanel.setLayout(new BorderLayout());
JComponent comp = getEmbeddedComponent().getComponent();
embeddedPanel.add(comp);
if (anchor.equals(Anchor.NORTH)) {
popup.add(embeddedPanel, BorderLayout.NORTH);
embeddedPanel.setBorder(BorderFactory.createMatteBorder(1, 1, 0, 1,
Color.black));
} else if (anchor.equals(Anchor.SOUTH)) {
popup.add(embeddedPanel, BorderLayout.SOUTH);
embeddedPanel.setBorder(BorderFactory.createMatteBorder(0, 1, 1, 1,
Color.black));
} else {
throw new IllegalArgumentException(
"Illegal anchor. Must be NORTH or SOUTH");
}
}
popup.addMenuKeyListener(new MenuKeyListener() {
@Override
public void menuKeyTyped(MenuKeyEvent e) {}
@Override
public void menuKeyReleased(MenuKeyEvent e) {}
@Override
public void menuKeyPressed(MenuKeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
setPopupVisible(false);
} else if (e.getKeyCode() == KeyEvent.VK_TAB) {
setPopupVisible(false);
if (e.getModifiersEx() == KeyEvent.SHIFT_DOWN_MASK) {
transferFocusBackward();
} else {
transferFocus();
}
}
}
});
}
return popup;
}
private class ComboBox extends JComboBox {
private static final long serialVersionUID = 2L;
public ComboBox() {
getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, false), "none");
getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), "none");
for (KeyListener l : getKeyListeners()) {
removeKeyListener(l);
}
}
/*
* @Override public void processKeyEvent(KeyEvent e) { if (e.getKeyCode() ==
* KeyEvent.VK_DOWN) { AbstractComboBox.this.setPopupVisible(true); } else {
* super.processKeyEvent(e); } }
*/
}
protected class Popup extends JPopupMenu{
private static final long serialVersionUID = -7940039384534412109L;
public Popup() {
for (MenuKeyListener l : getMenuKeyListeners()) {
removeMenuKeyListener(l);
}
}
@Override
public void setVisible(boolean b) {
lastViewChanged = System.currentTimeMillis();
super.setVisible(b);
if (b == false) {
popup = null;
}
}
}
}