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 * @since 744
026 */
027public class SideButton extends JButton implements Destroyable {
028    private static final int iconHeight = ImageProvider.ImageSizes.SIDEBUTTON.getImageSize();
029
030    private transient PropertyChangeListener propertyChangeListener;
031
032    /**
033     * Constructs a new {@code SideButton}.
034     * @param action action used to specify the new button
035     */
036    public SideButton(Action action) {
037        super(action);
038        fixIcon(action);
039        doStyle();
040    }
041
042    /**
043     * Constructs a new {@code SideButton}.
044     * @param action action used to specify the new button
045     * @param usename use action name
046     */
047    public SideButton(Action action, boolean usename) {
048        super(action);
049        if (!usename) {
050            setText(null);
051            fixIcon(action);
052            doStyle();
053        }
054    }
055
056    /**
057     * Constructs a new {@code SideButton}.
058     * @param action action used to specify the new button
059     * @param imagename image name in "dialogs" directory
060     */
061    public SideButton(Action action, String imagename) {
062        super(action);
063        setIcon(getScaledImage(ImageProvider.get("dialogs", imagename).getImage()));
064        doStyle();
065    }
066
067    private void fixIcon(Action action) {
068        // need to listen for changes, so that putValue() that are called after the
069        // SideButton is constructed get the proper icon size
070        if (action != null) {
071            propertyChangeListener = new PropertyChangeListener() {
072                @Override
073                public void propertyChange(PropertyChangeEvent evt) {
074                    if (javax.swing.Action.SMALL_ICON.equals(evt.getPropertyName())) {
075                        fixIcon(null);
076                    }
077                }
078            };
079            action.addPropertyChangeListener(propertyChangeListener);
080        }
081        Icon i = getIcon();
082        if (i instanceof ImageIcon && i.getIconHeight() != iconHeight) {
083            setIcon(getScaledImage(((ImageIcon) i).getImage()));
084        }
085    }
086
087    /**
088     * Scales the given image proportionally so that the height is "iconHeight"
089     * @param im original image
090     * @return scaled image
091     */
092    private static ImageIcon getScaledImage(Image im) {
093        int newWidth = im.getWidth(null) *  iconHeight / im.getHeight(null);
094        return new ImageIcon(im.getScaledInstance(newWidth, iconHeight, Image.SCALE_SMOOTH));
095    }
096
097    private void doStyle() {
098        setLayout(new BorderLayout());
099        setIconTextGap(2);
100        setMargin(new Insets(0, 0, 0, 0));
101    }
102
103    public BasicArrowButton createArrow(ActionListener listener) {
104        setMargin(new Insets(0, 0, 0, 0));
105        BasicArrowButton arrowButton = new BasicArrowButton(SwingConstants.SOUTH, null, null, Color.BLACK, null);
106        arrowButton.setBorder(BorderFactory.createEmptyBorder());
107        add(arrowButton, BorderLayout.EAST);
108        arrowButton.addActionListener(listener);
109        return arrowButton;
110    }
111
112    @Override
113    public void destroy() {
114        Action action = getAction();
115        if (action instanceof Destroyable) {
116            ((Destroyable) action).destroy();
117        }
118        if (action != null) {
119            if (propertyChangeListener != null) {
120                action.removePropertyChangeListener(propertyChangeListener);
121            }
122            setAction(null);
123        }
124    }
125}