001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.dialogs;
003
004import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
005import static org.openstreetmap.josm.tools.I18n.tr;
006
007import java.awt.Component;
008import java.awt.Dimension;
009import java.awt.event.ActionEvent;
010import java.util.ArrayList;
011import java.util.Arrays;
012import java.util.Collections;
013import java.util.List;
014
015import javax.swing.AbstractAction;
016import javax.swing.Action;
017import javax.swing.JMenuItem;
018import javax.swing.JOptionPane;
019import javax.swing.JPopupMenu;
020
021import org.openstreetmap.josm.Main;
022import org.openstreetmap.josm.gui.ExtendedDialog;
023import org.openstreetmap.josm.gui.layer.Layer;
024import org.openstreetmap.josm.gui.layer.Layer.LayerAction;
025import org.openstreetmap.josm.gui.layer.Layer.MultiLayerAction;
026import org.openstreetmap.josm.gui.layer.Layer.SeparatorLayerAction;
027import org.openstreetmap.josm.tools.ImageProvider;
028
029/**
030 * Popup menu handler for the layer list.
031 */
032public class LayerListPopup extends JPopupMenu {
033
034    /**
035     * An action that displays the layer information.
036     * @see Layer#getInfoComponent()
037     */
038    public static final class InfoAction extends AbstractAction {
039        private final transient Layer layer;
040
041        /**
042         * Constructs a new {@code InfoAction} for the given layer.
043         * @param layer The layer
044         */
045        public InfoAction(Layer layer) {
046            super(tr("Info"));
047            new ImageProvider("info").getResource().attachImageIcon(this, true);
048            putValue("help", ht("/Action/LayerInfo"));
049            this.layer = layer;
050        }
051
052        @Override
053        public void actionPerformed(ActionEvent e) {
054            Object object = layer.getInfoComponent();
055            if (object instanceof Component) {
056                ExtendedDialog ed = new ExtendedDialog(
057                        Main.parent, tr("Information about layer"),
058                        tr("OK"));
059                ed.setButtonIcons("ok");
060                ed.setIcon(JOptionPane.INFORMATION_MESSAGE);
061                ed.setContent((Component) object);
062                ed.setResizable(layer.isInfoResizable());
063                ed.setMinimumSize(new Dimension(270, 170));
064                ed.showDialog();
065            } else {
066                JOptionPane.showMessageDialog(
067                        Main.parent, object,
068                        tr("Information about layer"),
069                        JOptionPane.INFORMATION_MESSAGE
070                        );
071            }
072        }
073    }
074
075    /**
076     * Constructs a new {@code LayerListPopup}.
077     * @param selectedLayers list of selected layers
078     */
079    public LayerListPopup(List<Layer> selectedLayers) {
080
081        List<Action> actions;
082        if (selectedLayers.size() == 1) {
083            Action[] entries = selectedLayers.get(0).getMenuEntries();
084            actions = entries != null ? Arrays.asList(entries) : Collections.emptyList();
085        } else {
086            // Very simple algorithm - first selected layer has actions order as in getMenuEntries, actions from other layers go to the end
087            actions = new ArrayList<>();
088            boolean separatorAdded = true;
089            for (Action a: selectedLayers.get(0).getMenuEntries()) {
090                if (!separatorAdded && a instanceof SeparatorLayerAction) {
091                    separatorAdded = true;
092                    actions.add(a);
093                } else if (a instanceof LayerAction && ((LayerAction) a).supportLayers(selectedLayers)) {
094                    separatorAdded = false;
095                    if (a instanceof MultiLayerAction)
096                        a = ((MultiLayerAction) a).getMultiLayerAction(selectedLayers);
097                    actions.add(a);
098                }
099            }
100            // This will usually add no action, because if some action support all selected layers then it was probably used also in first layer
101            for (int i = 1; i < selectedLayers.size(); i++) {
102                separatorAdded = false;
103                for (Action a: selectedLayers.get(i).getMenuEntries()) {
104                    if (a instanceof LayerAction && !(a instanceof MultiLayerAction)
105                    && ((LayerAction) a).supportLayers(selectedLayers) && !actions.contains(a)) {
106                        if (!separatorAdded) {
107                            separatorAdded = true;
108                            actions.add(SeparatorLayerAction.INSTANCE);
109                        }
110                        actions.add(a);
111                    }
112                }
113            }
114        }
115        if (!actions.isEmpty() && actions.get(actions.size() - 1) instanceof SeparatorLayerAction) {
116            actions.remove(actions.size() - 1);
117        }
118        for (Action a : actions) {
119            if (a instanceof LayerAction) {
120                add(((LayerAction) a).createMenuComponent());
121            } else {
122                add(new JMenuItem(a));
123            }
124        }
125    }
126}