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