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}