001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui;
003
004import java.awt.BorderLayout;
005import java.awt.Color;
006import java.awt.Image;
007import java.awt.Insets;
008import java.awt.event.ActionListener;
009import java.beans.PropertyChangeEvent;
010import java.beans.PropertyChangeListener;
011
012import javax.swing.Action;
013import javax.swing.BorderFactory;
014import javax.swing.Icon;
015import javax.swing.ImageIcon;
016import javax.swing.JButton;
017import javax.swing.SwingConstants;
018import javax.swing.plaf.basic.BasicArrowButton;
019
020import org.openstreetmap.josm.tools.Destroyable;
021import org.openstreetmap.josm.tools.ImageProvider;
022
023/**
024 * Button that is usually used in toggle dialogs
025 */
026public class SideButton extends JButton implements Destroyable {
027    private static final int iconHeight = 20;
028
029    private transient PropertyChangeListener propertyChangeListener;
030
031    public SideButton(Action action) {
032        super(action);
033        fixIcon(action);
034        doStyle();
035    }
036
037    public SideButton(Action action, boolean usename) {
038        super(action);
039        if (!usename) {
040            setText(null);
041            fixIcon(action);
042            doStyle();
043        }
044    }
045
046    public SideButton(Action action, String imagename) {
047        super(action);
048        setIcon(makeIcon(imagename));
049        doStyle();
050    }
051
052    private void fixIcon(Action action) {
053        // need to listen for changes, so that putValue() that are called after the
054        // SideButton is constructed get the proper icon size
055        if (action != null) {
056            action.addPropertyChangeListener(propertyChangeListener = new PropertyChangeListener() {
057                @Override
058                public void propertyChange(PropertyChangeEvent evt) {
059                    if (evt.getPropertyName() == javax.swing.Action.SMALL_ICON) {
060                        fixIcon(null);
061                    }
062                }
063            });
064        }
065        Icon i = getIcon();
066        if (i instanceof ImageIcon && i.getIconHeight() != iconHeight) {
067            setIcon(getScaledImage(((ImageIcon) i).getImage()));
068        }
069    }
070
071    /**
072     * Scales the given image proportionally so that the height is "iconHeight"
073     * @param im original image
074     * @return scaled image
075     */
076    private static ImageIcon getScaledImage(Image im) {
077        int newWidth = im.getWidth(null) *  iconHeight / im.getHeight(null);
078        return new ImageIcon(im.getScaledInstance(newWidth, iconHeight, Image.SCALE_SMOOTH));
079    }
080
081    public static ImageIcon makeIcon(String imagename) {
082        Image im = ImageProvider.get("dialogs", imagename).getImage();
083        return getScaledImage(im);
084    }
085
086    private void doStyle() {
087        setLayout(new BorderLayout());
088        setIconTextGap(2);
089        setMargin(new Insets(0, 0, 0, 0));
090    }
091
092    public void createArrow(ActionListener listener) {
093        setMargin(new Insets(0, 0, 0, 0));
094        BasicArrowButton arrowButton = new BasicArrowButton(SwingConstants.SOUTH, null, null, Color.BLACK, null);
095        arrowButton.setBorder(BorderFactory.createEmptyBorder());
096        add(arrowButton, BorderLayout.EAST);
097        arrowButton.addActionListener(listener);
098    }
099
100    @Override
101    public void destroy() {
102        Action action = getAction();
103        if (action instanceof Destroyable) {
104            ((Destroyable) action).destroy();
105        }
106        if (action != null) {
107            if (propertyChangeListener != null) {
108                action.removePropertyChangeListener(propertyChangeListener);
109            }
110            setAction(null);
111        }
112    }
113}