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