/**
* OrbisGIS is a java GIS application dedicated to research in GIScience.
* OrbisGIS is developed by the GIS group of the DECIDE team of the
* Lab-STICC CNRS laboratory, see <http://www.lab-sticc.fr/>.
*
* The GIS group of the DECIDE team is located at :
*
* Laboratoire Lab-STICC – CNRS UMR 6285
* Equipe DECIDE
* UNIVERSITÉ DE BRETAGNE-SUD
* Institut Universitaire de Technologie de Vannes
* 8, Rue Montaigne - BP 561 56017 Vannes Cedex
*
* OrbisGIS is distributed under GPL 3 license.
*
* Copyright (C) 2007-2014 CNRS (IRSTV FR CNRS 2488)
* Copyright (C) 2015-2017 CNRS (Lab-STICC UMR CNRS 6285)
*
* This file is part of OrbisGIS.
*
* OrbisGIS is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* OrbisGIS 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* OrbisGIS. If not, see <http://www.gnu.org/licenses/>.
*
* For more information, please consult: <http://www.orbisgis.org/>
* or contact directly:
* info_at_ orbisgis.org
*/
package org.orbisgis.sif.components.button;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.beans.EventHandler;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import org.orbisgis.sif.icons.SifIcon;
/**
* Drop Down Button, is a button that show a popup when clicked.
*/
public class DropDownButton extends JToggleButton implements
ChangeListener, PopupMenuListener, ActionListener {
private static final long serialVersionUID = 1L;
private boolean popupVisible = false;
private JMenuItem selectedItem=null;
private boolean buttonAsMenuItem = true; //Does this button will be replaced by the selected menu item
private AtomicBoolean listenerInstalled = new AtomicBoolean(false);
/**
* Constructor, use selectMenu to set the icon and other properties to this button
*/
public DropDownButton() {
}
/**
* Initialise this control by using action
* @param action
*/
public DropDownButton(Action action) {
super(action);
}
@Override
public Component add(Component component, int i) {
if(getComponentPopupMenu()==null) {
return super.add(component, i);
} else {
setSelectedIfNone(component);
return getComponentPopupMenu().add(component,i);
}
}
@Override
public Component add(Component component) {
if(getComponentPopupMenu()==null) {
return super.add(component);
} else {
setSelectedIfNone(component);
return getComponentPopupMenu().add(component);
}
}
private void setSelectedIfNone(Component component) {
if(selectedItem==null && component instanceof JMenuItem) {
setSelectedItem((JMenuItem)component);
}
}
@Override
public void remove(Component component) {
if(getComponentPopupMenu()==null) {
super.remove(component);
} else {
getComponentPopupMenu().remove(component);
}
}
@Override
public Component getComponent(int i) {
if(getComponentPopupMenu()==null) {
return super.getComponent(i);
} else {
return getComponentPopupMenu().getComponent(i);
}
}
@Override
public Component[] getComponents() {
if(getComponentPopupMenu()==null) {
return super.getComponents();
} else {
return getComponentPopupMenu().getComponents();
}
}
/**
* Does this button will be replaced by the selected menu item
* @return
*/
public boolean isButtonAsMenuItem() {
return buttonAsMenuItem;
}
/**
* Does this button will be replaced by the selected menu item
* @param buttonAsMenuItem
*/
public void setButtonAsMenuItem(boolean buttonAsMenuItem) {
this.buttonAsMenuItem = buttonAsMenuItem;
}
/**
* Constructor with a provided Icon
* @param iconFile
*/
public DropDownButton(ImageIcon iconFile) {
super(iconFile);
}
private void installListeners() {
if(!listenerInstalled.getAndSet(true)) {
getModel().addChangeListener(this);
this.addActionListener(this);
}
}
/**
* Component initialisation
*/
@Override
public void addNotify() {
super.addNotify();
installListeners();
}
/**
* @return The selected menu item, null if not set
*/
public JMenuItem getSelectedItem() {
return selectedItem;
}
/**
* Set the selected menu item
* If isButtonAsMenuItem, apply the icon and tooltip of the menuitem to this button
* @param menu
*/
public void setSelectedItem(JMenuItem menu) {
selectedItem = menu;
if(buttonAsMenuItem) {
setIcon(menu.getIcon());
setText(menu.getText());
setToolTipText(menu.getToolTipText());
}
}
/**
* The user click on a menu item
* @param ae
*/
public void onSelectMenuAction(ActionEvent ae) {
Object src = ae.getSource();
if(src instanceof JMenuItem) {
setSelectedItem((JMenuItem)src);
}
}
/**
* Merge the provided icon with the dropdown icon
* @return
*/
private static ImageIcon mergeIcons(ImageIcon iconFile) {
Image firstImage = iconFile.getImage();
Image secondImage = SifIcon.getIconImage("btn_dropdown");
int w = firstImage.getWidth(null) + secondImage.getWidth(null);
int h = Math.max(firstImage.getHeight(null), secondImage
.getHeight(null));
BufferedImage resultImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = resultImage.createGraphics();
g2.drawImage(firstImage, 0, 0, null);
g2.drawImage(secondImage, firstImage.getWidth(null), 0, null);
return new ImageIcon(resultImage);
}
@Override
public void setIcon(Icon icon) {
if(icon == null) {
super.setIcon(null);
} else if(icon instanceof ImageIcon) {
super.setIcon(mergeIcons((ImageIcon)icon));
}else{
throw new IllegalArgumentException("DropDown button accept only ImageIcon");
}
}
/*------------------------------[ ChangeListener ]---------------------------------------------------*/
public void stateChanged(ChangeEvent e) {
if (e.getSource().equals(this.getModel())) {
if (popupVisible && !this.getModel().isRollover()) {
this.getModel().setRollover(true);
}
}
}
/**
* To update the button icon and properties when an item has been selected, add a listener
* to each Item
* @param jpm
*/
private void addRecursiveActionListener(MenuElement jpm) {
if(jpm instanceof JPopupMenu || jpm instanceof JMenu) {
for(MenuElement me : jpm.getSubElements()) {
addRecursiveActionListener(me);
}
} else if(jpm instanceof JMenuItem) {
((JMenuItem)jpm).addActionListener(EventHandler.create(ActionListener.class,this,"onSelectMenuAction",""));
}
}
/**
* The lifetime of the popup menu must be at most the same as this button
* @param jpm
*/
@Override
public void setComponentPopupMenu(JPopupMenu jpm) {
jpm.addPopupMenuListener(this);
if(buttonAsMenuItem) {
addRecursiveActionListener(jpm);
}
super.setComponentPopupMenu(jpm);
}
/*------------------------------[ ActionListener ]---------------------------------------------------*/
public void actionPerformed(ActionEvent ae) {
//Trigger actionEvent on the last select menu item
if(buttonAsMenuItem && selectedItem!=null) {
selectedItem.doClick();
}
JPopupMenu popup = getComponentPopupMenu();
popup.show(this, 0, this.getHeight());
}
/*------------------------------[ PopupMenuListener ]---------------------------------------------------*/
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
popupVisible = true;
this.getModel().setRollover(true);
this.getModel().setSelected(true);
}
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
popupVisible = false;
this.getModel().setRollover(false);
this.getModel().setSelected(false);
}
public void popupMenuCanceled(PopupMenuEvent e) {
popupVisible = false;
}
}