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}