001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.actions; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.Component; 007import java.awt.GridBagLayout; 008import java.util.List; 009 010import javax.swing.DefaultListCellRenderer; 011import javax.swing.JCheckBox; 012import javax.swing.JLabel; 013import javax.swing.JList; 014import javax.swing.JOptionPane; 015import javax.swing.JPanel; 016 017import org.openstreetmap.josm.gui.ExtendedDialog; 018import org.openstreetmap.josm.gui.MainApplication; 019import org.openstreetmap.josm.gui.layer.Layer; 020import org.openstreetmap.josm.gui.widgets.JosmComboBox; 021import org.openstreetmap.josm.tools.GBC; 022import org.openstreetmap.josm.tools.Shortcut; 023import org.openstreetmap.josm.tools.Utils; 024 025/** 026 * Abstract superclass of different "Merge" actions. 027 * @since 1890 028 */ 029public abstract class AbstractMergeAction extends JosmAction { 030 031 /** 032 * the list cell renderer used to render layer list entries 033 */ 034 public static class LayerListCellRenderer extends DefaultListCellRenderer { 035 036 @Override 037 public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) { 038 Layer layer = (Layer) value; 039 JLabel label = (JLabel) super.getListCellRendererComponent(list, layer.getName(), index, isSelected, cellHasFocus); 040 label.setIcon(layer.getIcon()); 041 label.setToolTipText(layer.getToolTipText()); 042 return label; 043 } 044 } 045 046 /** 047 * <code>TargetLayerDialogResult</code> returned by {@link #askTargetLayer(List, String, boolean)} 048 * containing the selectedTargetLayer and whether the checkbox is ticked 049 * @param <T> type of layer 050 * @since 14338 051 */ 052 public static class TargetLayerDialogResult<T extends Layer> { 053 /** 054 * The selected target layer of type T 055 */ 056 public T selectedTargetLayer; 057 /** 058 * Whether the checkbox is ticked 059 */ 060 public boolean checkboxTicked; 061 062 /** 063 * Constructs a new {@link TargetLayerDialogResult} 064 */ 065 public TargetLayerDialogResult() { 066 } 067 068 /** 069 * Constructs a new {@link TargetLayerDialogResult} 070 * @param sel the selected target layer of type T 071 */ 072 public TargetLayerDialogResult(T sel) { 073 selectedTargetLayer = sel; 074 } 075 076 /** 077 * Constructs a new {@link TargetLayerDialogResult} 078 * @param sel the selected target layer of type T 079 * @param ch whether the checkbox was ticked 080 */ 081 public TargetLayerDialogResult(T sel, boolean ch) { 082 selectedTargetLayer = sel; 083 checkboxTicked = ch; 084 } 085 } 086 087 /** 088 * Constructs a new {@code AbstractMergeAction}. 089 * @param name the action's text as displayed on the menu (if it is added to a menu) 090 * @param iconName the filename of the icon to use 091 * @param tooltip a longer description of the action that will be displayed in the tooltip. Please note 092 * that html is not supported for menu actions on some platforms. 093 * @param shortcut a ready-created shortcut object or null if you don't want a shortcut. But you always 094 * do want a shortcut, remember you can always register it with group=none, so you 095 * won't be assigned a shortcut unless the user configures one. If you pass null here, 096 * the user CANNOT configure a shortcut for your action. 097 * @param register register this action for the toolbar preferences? 098 */ 099 public AbstractMergeAction(String name, String iconName, String tooltip, Shortcut shortcut, boolean register) { 100 super(name, iconName, tooltip, shortcut, register); 101 } 102 103 /** 104 * Constructs a new {@code AbstractMergeAction}. 105 * @param name the action's text as displayed on the menu (if it is added to a menu) 106 * @param iconName the filename of the icon to use 107 * @param tooltip a longer description of the action that will be displayed in the tooltip. Please note 108 * that html is not supported for menu actions on some platforms. 109 * @param shortcut a ready-created shortcut object or null if you don't want a shortcut. But you always 110 * do want a shortcut, remember you can always register it with group=none, so you 111 * won't be assigned a shortcut unless the user configures one. If you pass null here, 112 * the user CANNOT configure a shortcut for your action. 113 * @param register register this action for the toolbar preferences? 114 * @param toolbar identifier for the toolbar preferences. The iconName is used, if this parameter is null 115 * @param installAdapters false, if you don't want to install layer changed and selection changed adapters 116 */ 117 public AbstractMergeAction(String name, String iconName, String tooltip, Shortcut shortcut, 118 boolean register, String toolbar, boolean installAdapters) { 119 super(name, iconName, tooltip, shortcut, register, toolbar, installAdapters); 120 } 121 122 /** 123 * Ask user to choose the target layer. 124 * @param targetLayers list of candidate target layers. 125 * @return the chosen layer 126 * @deprecated to be removed 127 */ 128 @Deprecated 129 protected static Layer askTargetLayer(List<Layer> targetLayers) { 130 return askTargetLayer(targetLayers, false, null, false, tr("Merge")).selectedTargetLayer; 131 } 132 133 /** 134 * Ask user to choose the target layer and shows a checkbox. 135 * @param targetLayers list of candidate target layers. 136 * @param checkbox The text of the checkbox shown to the user. 137 * @param checkboxDefault whether the checkbox is ticked by default 138 * @return The {@link TargetLayerDialogResult} containing the chosen target layer and the state of the checkbox 139 * @deprecated to be removed 140 */ 141 @Deprecated 142 protected static TargetLayerDialogResult<Layer> askTargetLayer(List<Layer> targetLayers, String checkbox, boolean checkboxDefault) { 143 return askTargetLayer(targetLayers, true, checkbox, checkboxDefault, tr("Merge")); 144 } 145 146 /** 147 * Ask user to choose the target layer and shows a checkbox. 148 * @param targetLayers list of candidate target layers. 149 * @param showCheckbox whether the checkbox is shown 150 * @param checkbox The text of the checkbox shown to the user. 151 * @param checkboxDefault whether the checkbox is ticked by default 152 * @param buttonText text of button used to select target layer 153 * @return The {@link TargetLayerDialogResult} containing the chosen target layer and the state of the checkbox 154 * @since 15450 155 */ 156 protected static TargetLayerDialogResult<Layer> askTargetLayer(List<Layer> targetLayers, boolean showCheckbox, 157 String checkbox, boolean checkboxDefault, String buttonText) { 158 return askTargetLayer(targetLayers.toArray(new Layer[0]), 159 tr("Please select the target layer."), checkbox, 160 tr("Select target layer"), 161 buttonText, "dialogs/mergedown", showCheckbox, checkboxDefault); 162 } 163 164 /** 165 * Ask user to choose the target layer. 166 * @param <T> type of layer 167 * @param targetLayers array of proposed target layers 168 * @param label label displayed in dialog 169 * @param title title of dialog 170 * @param buttonText text of button used to select target layer 171 * @param buttonIcon icon name of button used to select target layer 172 * @return chosen target layer 173 */ 174 public static <T extends Layer> T askTargetLayer(T[] targetLayers, String label, String title, String buttonText, String buttonIcon) { 175 return askTargetLayer(targetLayers, label, null, title, buttonText, buttonIcon, false, false).selectedTargetLayer; 176 } 177 178 /** 179 * Ask user to choose the target layer. Can show a checkbox. 180 * @param <T> type of layer 181 * @param targetLayers array of proposed target layers 182 * @param label label displayed in dialog 183 * @param checkbox text of the checkbox displayed 184 * @param title title of dialog 185 * @param buttonText text of button used to select target layer 186 * @param buttonIcon icon name of button used to select target layer 187 * @param showCheckbox whether the checkbox is shown 188 * @param checkboxDefault whether the checkbox is ticked by default 189 * @return The {@link TargetLayerDialogResult} containing the chosen target layer and the state of the checkbox 190 * @since 14338 191 */ 192 @SuppressWarnings("unchecked") 193 public static <T extends Layer> TargetLayerDialogResult<T> askTargetLayer(T[] targetLayers, String label, String checkbox, String title, 194 String buttonText, String buttonIcon, boolean showCheckbox, boolean checkboxDefault) { 195 JosmComboBox<T> layerList = new JosmComboBox<>(targetLayers); 196 layerList.setRenderer(new LayerListCellRenderer()); 197 layerList.setSelectedIndex(0); 198 199 JPanel pnl = new JPanel(new GridBagLayout()); 200 pnl.add(new JLabel(label), GBC.eol()); 201 pnl.add(layerList, GBC.eol().fill(GBC.HORIZONTAL)); 202 203 JCheckBox cb = null; 204 if (showCheckbox) { 205 cb = new JCheckBox(checkbox); 206 cb.setSelected(checkboxDefault); 207 pnl.add(cb, GBC.eol()); 208 } 209 210 ExtendedDialog ed = new ExtendedDialog(MainApplication.getMainFrame(), title, buttonText, tr("Cancel")); 211 ed.setButtonIcons(buttonIcon, "cancel"); 212 ed.setContent(pnl); 213 ed.showDialog(); 214 if (ed.getValue() != 1) { 215 return new TargetLayerDialogResult<>(); 216 } 217 return new TargetLayerDialogResult<>((T) layerList.getSelectedItem(), cb != null && cb.isSelected()); 218 } 219 220 /** 221 * Warns user when there no layers the source layer could be merged to. 222 * @param sourceLayer source layer 223 */ 224 protected void warnNoTargetLayersForSourceLayer(Layer sourceLayer) { 225 String message = tr("<html>There are no layers the source layer<br>''{0}''<br>could be merged to.</html>", 226 Utils.escapeReservedCharactersHTML(sourceLayer.getName())); 227 JOptionPane.showMessageDialog(MainApplication.getMainFrame(), message, tr("No target layers"), JOptionPane.WARNING_MESSAGE); 228 } 229}