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