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.GraphicsEnvironment;
008import java.awt.GridBagLayout;
009import java.util.List;
010
011import javax.swing.DefaultListCellRenderer;
012import javax.swing.JLabel;
013import javax.swing.JList;
014import javax.swing.JOptionPane;
015import javax.swing.JPanel;
016
017import org.openstreetmap.josm.Main;
018import org.openstreetmap.josm.gui.ExtendedDialog;
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     * Constructs a new {@code AbstractMergeAction}.
048     * @param name the action's text as displayed on the menu (if it is added to a menu)
049     * @param iconName the filename of the icon to use
050     * @param tooltip  a longer description of the action that will be displayed in the tooltip. Please note
051     *           that html is not supported for menu actions on some platforms.
052     * @param shortcut a ready-created shortcut object or null if you don't want a shortcut. But you always
053     *            do want a shortcut, remember you can always register it with group=none, so you
054     *            won't be assigned a shortcut unless the user configures one. If you pass null here,
055     *            the user CANNOT configure a shortcut for your action.
056     * @param register register this action for the toolbar preferences?
057     */
058    public AbstractMergeAction(String name, String iconName, String tooltip, Shortcut shortcut, boolean register) {
059        super(name, iconName, tooltip, shortcut, register);
060    }
061
062    /**
063     * Constructs a new {@code AbstractMergeAction}.
064     * @param name the action's text as displayed on the menu (if it is added to a menu)
065     * @param iconName the filename of the icon to use
066     * @param tooltip  a longer description of the action that will be displayed in the tooltip. Please note
067     *           that html is not supported for menu actions on some platforms.
068     * @param shortcut a ready-created shortcut object or null if you don't want a shortcut. But you always
069     *            do want a shortcut, remember you can always register it with group=none, so you
070     *            won't be assigned a shortcut unless the user configures one. If you pass null here,
071     *            the user CANNOT configure a shortcut for your action.
072     * @param register register this action for the toolbar preferences?
073     * @param toolbar identifier for the toolbar preferences. The iconName is used, if this parameter is null
074     * @param installAdapters false, if you don't want to install layer changed and selection changed adapters
075     */
076    public AbstractMergeAction(String name, String iconName, String tooltip, Shortcut shortcut,
077    boolean register, String toolbar, boolean installAdapters) {
078        super(name, iconName, tooltip, shortcut, register, toolbar, installAdapters);
079    }
080
081    /**
082     * Ask user to choose the target layer.
083     * @param targetLayers list of candidate target layers.
084     * @return the chosen layer
085     */
086    protected static Layer askTargetLayer(List<Layer> targetLayers) {
087        return askTargetLayer(targetLayers.toArray(new Layer[0]),
088                tr("Please select the target layer."),
089                tr("Select target layer"),
090                tr("Merge"), "dialogs/mergedown");
091    }
092
093    /**
094     * Asks a target layer.
095     * @param <T> type of layer
096     * @param targetLayers array of proposed target layers
097     * @param label label displayed in dialog
098     * @param title title of dialog
099     * @param buttonText text of button used to select target layer
100     * @param buttonIcon icon name of button used to select target layer
101     * @return choosen target layer
102     */
103    @SuppressWarnings("unchecked")
104    public static <T extends Layer> T askTargetLayer(T[] targetLayers, String label, String title, String buttonText, String buttonIcon) {
105        JosmComboBox<T> layerList = new JosmComboBox<>(targetLayers);
106        layerList.setRenderer(new LayerListCellRenderer());
107        layerList.setSelectedIndex(0);
108
109        JPanel pnl = new JPanel(new GridBagLayout());
110        pnl.add(new JLabel(label), GBC.eol());
111        pnl.add(layerList, GBC.eol().fill(GBC.HORIZONTAL));
112        if (GraphicsEnvironment.isHeadless()) {
113            // return first layer in headless mode, for unit tests
114            return targetLayers[0];
115        }
116        ExtendedDialog ed = new ExtendedDialog(Main.parent, title, buttonText, tr("Cancel"));
117        ed.setButtonIcons(buttonIcon, "cancel");
118        ed.setContent(pnl);
119        ed.showDialog();
120        if (ed.getValue() != 1) {
121            return null;
122        }
123        return (T) layerList.getSelectedItem();
124    }
125
126    /**
127     * Warns user when there no layers the source layer could be merged to.
128     * @param sourceLayer source layer
129     */
130    protected void warnNoTargetLayersForSourceLayer(Layer sourceLayer) {
131        String message = tr("<html>There are no layers the source layer<br>''{0}''<br>could be merged to.</html>",
132                Utils.escapeReservedCharactersHTML(sourceLayer.getName()));
133        if (!GraphicsEnvironment.isHeadless()) {
134            JOptionPane.showMessageDialog(Main.parent, message, tr("No target layers"), JOptionPane.WARNING_MESSAGE);
135        }
136    }
137}