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.Main; 021import org.openstreetmap.josm.tools.Destroyable; 022import org.openstreetmap.josm.tools.ImageProvider; 023import org.openstreetmap.josm.tools.ImageResource; 024 025/** 026 * Button that is usually used in toggle dialogs. 027 * @since 744 028 */ 029public class SideButton extends JButton implements Destroyable { 030 031 private transient PropertyChangeListener propertyChangeListener; 032 033 /** 034 * Constructs a new {@code SideButton}. 035 * @param action action used to specify the new button 036 * @since 744 037 */ 038 public SideButton(Action action) { 039 super(action); 040 ImageResource icon = (ImageResource) action.getValue("ImageResource"); 041 if (icon != null) { 042 setIcon(icon.getImageIconBounded( 043 ImageProvider.ImageSizes.SIDEBUTTON.getImageDimension())); 044 } else if (getIcon() != null) { /* TODO: remove when calling code is fixed, replace by exception */ 045 Main.warn("Old style SideButton usage for action " + action); 046 fixIcon(action); 047 } 048 doStyle(); 049 } 050 051 /** 052 * Constructs a new {@code SideButton}. 053 * @param action action used to specify the new button 054 * @param usename use action name 055 * @since 2710 056 */ 057 public SideButton(Action action, boolean usename) { 058 this(action); 059 if (!usename) { 060 setText(null); 061 } 062 } 063 064 /** 065 * Constructs a new {@code SideButton}. 066 * @param action action used to specify the new button 067 * @param imagename image name in "dialogs" directory 068 * @since 2747 069 */ 070 public SideButton(Action action, String imagename) { 071 super(action); 072 setIcon(ImageProvider.get("dialogs", imagename, ImageProvider.ImageSizes.SIDEBUTTON)); 073 doStyle(); 074 } 075 076 /** 077 * Fix icon size 078 * @param action the action 079 * @deprecated This method is old style and will be removed together with the removal 080 * of old constructor code 081 */ 082 @Deprecated 083 private void fixIcon(Action action) { 084 // need to listen for changes, so that putValue() that are called after the 085 // SideButton is constructed get the proper icon size 086 if (action != null) { 087 propertyChangeListener = new PropertyChangeListener() { 088 @Override 089 public void propertyChange(PropertyChangeEvent evt) { 090 if (javax.swing.Action.SMALL_ICON.equals(evt.getPropertyName())) { 091 fixIcon(null); 092 } 093 } 094 }; 095 action.addPropertyChangeListener(propertyChangeListener); 096 } 097 int iconHeight = ImageProvider.ImageSizes.SIDEBUTTON.getImageDimension().height; 098 Icon i = getIcon(); 099 if (i instanceof ImageIcon && i.getIconHeight() != iconHeight) { 100 Image im = ((ImageIcon) i).getImage(); 101 int newWidth = im.getWidth(null) * iconHeight / im.getHeight(null); 102 ImageIcon icon = new ImageIcon(im.getScaledInstance(newWidth, iconHeight, Image.SCALE_SMOOTH)); 103 setIcon(icon); 104 } 105 } 106 107 /** 108 * Do the style settings for the side button layout 109 */ 110 private void doStyle() { 111 setLayout(new BorderLayout()); 112 setIconTextGap(2); 113 setMargin(new Insets(0, 0, 0, 0)); 114 } 115 116 /** 117 * Create the arrow for opening a drop-down menu 118 * @param listener listener to use for button actions (e.g. pressing) 119 * @return the created button 120 * @since 9668 121 */ 122 public BasicArrowButton createArrow(ActionListener listener) { 123 setMargin(new Insets(0, 0, 0, 0)); 124 BasicArrowButton arrowButton = new BasicArrowButton(SwingConstants.SOUTH, null, null, Color.BLACK, null); 125 arrowButton.setBorder(BorderFactory.createEmptyBorder()); 126 add(arrowButton, BorderLayout.EAST); 127 arrowButton.addActionListener(listener); 128 return arrowButton; 129 } 130 131 @Override 132 public void destroy() { 133 Action action = getAction(); 134 if (action instanceof Destroyable) { 135 ((Destroyable) action).destroy(); 136 } 137 if (action != null) { 138 if (propertyChangeListener != null) { 139 action.removePropertyChangeListener(propertyChangeListener); 140 } 141 setAction(null); 142 } 143 } 144}