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 */ 127 protected static Layer askTargetLayer(List<Layer> targetLayers) { 128 return askTargetLayer(targetLayers, false, null, false).selectedTargetLayer; 129 } 130 131 /** 132 * Ask user to choose the target layer and shows a checkbox. 133 * @param targetLayers list of candidate target layers. 134 * @param checkbox The text of the checkbox shown to the user. 135 * @param checkboxDefault whether the checkbox is ticked by default 136 * @return The {@link TargetLayerDialogResult} containing the chosen target layer and the state of the checkbox 137 */ 138 protected static TargetLayerDialogResult<Layer> askTargetLayer(List<Layer> targetLayers, String checkbox, boolean checkboxDefault) { 139 return askTargetLayer(targetLayers, true, checkbox, checkboxDefault); 140 } 141 142 /** 143 * Ask user to choose the target layer and shows a checkbox. 144 * @param targetLayers list of candidate target layers. 145 * @param showCheckbox whether the checkbox is shown 146 * @param checkbox The text of the checkbox shown to the user. 147 * @param checkboxDefault whether the checkbox is ticked by default 148 * @return The {@link TargetLayerDialogResult} containing the chosen target layer and the state of the checkbox 149 */ 150 protected static TargetLayerDialogResult<Layer> askTargetLayer(List<Layer> targetLayers, boolean showCheckbox, 151 String checkbox, boolean checkboxDefault) { 152 return askTargetLayer(targetLayers.toArray(new Layer[0]), 153 tr("Please select the target layer."), checkbox, 154 tr("Select target layer"), 155 tr("Merge"), "dialogs/mergedown", showCheckbox, checkboxDefault); 156 } 157 158 /** 159 * Ask user to choose the target layer. 160 * @param <T> type of layer 161 * @param targetLayers array of proposed target layers 162 * @param label label displayed in dialog 163 * @param title title of dialog 164 * @param buttonText text of button used to select target layer 165 * @param buttonIcon icon name of button used to select target layer 166 * @return chosen target layer 167 */ 168 public static <T extends Layer> T askTargetLayer(T[] targetLayers, String label, String title, String buttonText, String buttonIcon) { 169 return askTargetLayer(targetLayers, label, null, title, buttonText, buttonIcon, false, false).selectedTargetLayer; 170 } 171 172 /** 173 * Ask user to choose the target layer. Can show a checkbox. 174 * @param <T> type of layer 175 * @param targetLayers array of proposed target layers 176 * @param label label displayed in dialog 177 * @param checkbox text of the checkbox displayed 178 * @param title title of dialog 179 * @param buttonText text of button used to select target layer 180 * @param buttonIcon icon name of button used to select target layer 181 * @param showCheckbox whether the checkbox is shown 182 * @param checkboxDefault whether the checkbox is ticked by default 183 * @return The {@link TargetLayerDialogResult} containing the chosen target layer and the state of the checkbox 184 * @since 14338 185 */ 186 @SuppressWarnings("unchecked") 187 public static <T extends Layer> TargetLayerDialogResult<T> askTargetLayer(T[] targetLayers, String label, String checkbox, String title, 188 String buttonText, String buttonIcon, boolean showCheckbox, boolean checkboxDefault) { 189 JosmComboBox<T> layerList = new JosmComboBox<>(targetLayers); 190 layerList.setRenderer(new LayerListCellRenderer()); 191 layerList.setSelectedIndex(0); 192 193 JPanel pnl = new JPanel(new GridBagLayout()); 194 pnl.add(new JLabel(label), GBC.eol()); 195 pnl.add(layerList, GBC.eol().fill(GBC.HORIZONTAL)); 196 197 JCheckBox cb = null; 198 if (showCheckbox) { 199 cb = new JCheckBox(checkbox); 200 cb.setSelected(checkboxDefault); 201 pnl.add(cb, GBC.eol()); 202 } 203 204 ExtendedDialog ed = new ExtendedDialog(MainApplication.getMainFrame(), title, buttonText, tr("Cancel")); 205 ed.setButtonIcons(buttonIcon, "cancel"); 206 ed.setContent(pnl); 207 ed.showDialog(); 208 if (ed.getValue() != 1) { 209 return new TargetLayerDialogResult<>(); 210 } 211 return new TargetLayerDialogResult<>((T) layerList.getSelectedItem(), cb != null && cb.isSelected()); 212 } 213 214 /** 215 * Warns user when there no layers the source layer could be merged to. 216 * @param sourceLayer source layer 217 */ 218 protected void warnNoTargetLayersForSourceLayer(Layer sourceLayer) { 219 String message = tr("<html>There are no layers the source layer<br>''{0}''<br>could be merged to.</html>", 220 Utils.escapeReservedCharactersHTML(sourceLayer.getName())); 221 JOptionPane.showMessageDialog(MainApplication.getMainFrame(), message, tr("No target layers"), JOptionPane.WARNING_MESSAGE); 222 } 223}