source: josm/trunk/src/org/openstreetmap/josm/gui/ImageryMenu.java@ 8399

Last change on this file since 8399 was 8399, checked in by Don-vip, 9 years ago

see #11397, see #11447 - partial revert of r8308 (SONARJAVA-1061: FP on S1948: no issue should be raised when using Collections of Serializable objects)

  • Property svn:eol-style set to native
File size: 10.2 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.trc;
6
7import java.awt.Component;
8import java.awt.GraphicsEnvironment;
9import java.awt.MenuComponent;
10import java.awt.event.ActionEvent;
11import java.util.ArrayList;
12import java.util.Collection;
13import java.util.Collections;
14import java.util.Comparator;
15import java.util.Iterator;
16import java.util.List;
17
18import javax.swing.Action;
19import javax.swing.JComponent;
20import javax.swing.JMenu;
21import javax.swing.JMenuItem;
22import javax.swing.JPopupMenu;
23import javax.swing.event.MenuEvent;
24import javax.swing.event.MenuListener;
25
26import org.openstreetmap.josm.Main;
27import org.openstreetmap.josm.actions.AddImageryLayerAction;
28import org.openstreetmap.josm.actions.JosmAction;
29import org.openstreetmap.josm.actions.MapRectifierWMSmenuAction;
30import org.openstreetmap.josm.data.coor.LatLon;
31import org.openstreetmap.josm.data.imagery.ImageryInfo;
32import org.openstreetmap.josm.data.imagery.ImageryLayerInfo;
33import org.openstreetmap.josm.data.imagery.Shape;
34import org.openstreetmap.josm.gui.MapView.LayerChangeListener;
35import org.openstreetmap.josm.gui.layer.ImageryLayer;
36import org.openstreetmap.josm.gui.layer.Layer;
37import org.openstreetmap.josm.gui.preferences.imagery.ImageryPreference;
38import org.openstreetmap.josm.tools.ImageProvider;
39
40/**
41 * Imagery menu, holding entries for imagery preferences, offset actions and dynamic imagery entries
42 * depending on current maview coordinates.
43 * @since 3737
44 */
45public class ImageryMenu extends JMenu implements LayerChangeListener {
46
47 /**
48 * Compare ImageryInfo objects alphabetically by name.
49 *
50 * ImageryInfo objects are normally sorted by country code first
51 * (for the preferences). We don't want this in the imagery menu.
52 */
53 public static final Comparator<ImageryInfo> alphabeticImageryComparator = new Comparator<ImageryInfo>() {
54 @Override
55 public int compare(ImageryInfo ii1, ImageryInfo ii2) {
56 return ii1.getName().toLowerCase().compareTo(ii2.getName().toLowerCase());
57 }
58 };
59
60 private transient Action offsetAction = new JosmAction(
61 tr("Imagery offset"), "mapmode/adjustimg", tr("Adjust imagery offset"), null, false, false) {
62 {
63 putValue("toolbar", "imagery-offset");
64 Main.toolbar.register(this);
65 }
66 @Override
67 public void actionPerformed(ActionEvent e) {
68 Collection<ImageryLayer> layers = Main.map.mapView.getLayersOfType(ImageryLayer.class);
69 if (layers.isEmpty()) {
70 setEnabled(false);
71 return;
72 }
73 Component source = null;
74 if (e.getSource() instanceof Component) {
75 source = (Component)e.getSource();
76 }
77 JPopupMenu popup = new JPopupMenu();
78 if (layers.size() == 1) {
79 JComponent c = layers.iterator().next().getOffsetMenuItem(popup);
80 if (c instanceof JMenuItem) {
81 ((JMenuItem) c).getAction().actionPerformed(e);
82 } else {
83 if (source == null) return;
84 popup.show(source, source.getWidth()/2, source.getHeight()/2);
85 }
86 return;
87 }
88 if (source == null) return;
89 for (ImageryLayer layer : layers) {
90 JMenuItem layerMenu = layer.getOffsetMenuItem();
91 layerMenu.setText(layer.getName());
92 layerMenu.setIcon(layer.getIcon());
93 popup.add(layerMenu);
94 }
95 popup.show(source, source.getWidth()/2, source.getHeight()/2);
96 }
97 };
98
99 private final JMenuItem singleOffset = new JMenuItem(offsetAction);
100 private JMenuItem offsetMenuItem = singleOffset;
101 private final MapRectifierWMSmenuAction rectaction = new MapRectifierWMSmenuAction();
102
103 /**
104 * Constructs a new {@code ImageryMenu}.
105 * @param subMenu submenu in that contains plugin-managed additional imagery layers
106 */
107 public ImageryMenu(JMenu subMenu) {
108 super(tr("Imagery"));
109 setupMenuScroller();
110 MapView.addLayerChangeListener(this);
111 // build dynamically
112 addMenuListener(new MenuListener() {
113 @Override
114 public void menuSelected(MenuEvent e) {
115 refreshImageryMenu();
116 }
117
118 @Override
119 public void menuDeselected(MenuEvent e) {
120 }
121
122 @Override
123 public void menuCanceled(MenuEvent e) {
124 }
125 });
126 MainMenu.add(subMenu, rectaction);
127 }
128
129 private void setupMenuScroller() {
130 if (!GraphicsEnvironment.isHeadless()) {
131 MenuScroller.setScrollerFor(this, 150, 2);
132 }
133 }
134
135 /**
136 * Refresh imagery menu.
137 *
138 * Outside this class only called in {@link ImageryPreference#initialize()}.
139 * (In order to have actions ready for the toolbar, see #8446.)
140 */
141 public void refreshImageryMenu() {
142 removeDynamicItems();
143
144 addDynamic(offsetMenuItem);
145 addDynamicSeparator();
146
147 // for each configured ImageryInfo, add a menu entry.
148 final List<ImageryInfo> savedLayers = new ArrayList<>(ImageryLayerInfo.instance.getLayers());
149 Collections.sort(savedLayers, alphabeticImageryComparator);
150 for (final ImageryInfo u : savedLayers) {
151 addDynamic(new AddImageryLayerAction(u));
152 }
153
154 // list all imagery entries where the current map location
155 // is within the imagery bounds
156 if (Main.isDisplayingMapView()) {
157 MapView mv = Main.map.mapView;
158 LatLon pos = mv.getProjection().eastNorth2latlon(mv.getCenter());
159 final List<ImageryInfo> inViewLayers = new ArrayList<>();
160
161 for (ImageryInfo i : ImageryLayerInfo.instance.getDefaultLayers()) {
162 if (i.getBounds() != null && i.getBounds().contains(pos)) {
163 inViewLayers.add(i);
164 }
165 }
166 // Do not suggest layers already in use
167 inViewLayers.removeAll(ImageryLayerInfo.instance.getLayers());
168 // For layers containing complex shapes, check that center is in one
169 // of its shapes (fix #7910)
170 for (Iterator<ImageryInfo> iti = inViewLayers.iterator(); iti.hasNext(); ) {
171 List<Shape> shapes = iti.next().getBounds().getShapes();
172 if (shapes != null && !shapes.isEmpty()) {
173 boolean found = false;
174 for (Iterator<Shape> its = shapes.iterator(); its.hasNext() && !found; ) {
175 found = its.next().contains(pos);
176 }
177 if (!found) {
178 iti.remove();
179 }
180 }
181 }
182 if (!inViewLayers.isEmpty()) {
183 Collections.sort(inViewLayers, alphabeticImageryComparator);
184 addDynamicSeparator();
185 for (ImageryInfo i : inViewLayers) {
186 addDynamic(new AddImageryLayerAction(i));
187 }
188 }
189 }
190
191 addDynamicSeparator();
192 JMenu subMenu = Main.main.menu.imagerySubMenu;
193 int heightUnrolled = 30*(getItemCount()+subMenu.getItemCount());
194 if (heightUnrolled < Main.panel.getHeight()) {
195 // add all items of submenu if they will fit on screen
196 int n = subMenu.getItemCount();
197 for (int i=0; i<n; i++) {
198 addDynamic(subMenu.getItem(i).getAction());
199 }
200 } else {
201 // or add the submenu itself
202 addDynamic(subMenu);
203 }
204 }
205
206 private JMenuItem getNewOffsetMenu(){
207 if (!Main.isDisplayingMapView()) {
208 offsetAction.setEnabled(false);
209 return singleOffset;
210 }
211 Collection<ImageryLayer> layers = Main.map.mapView.getLayersOfType(ImageryLayer.class);
212 if (layers.isEmpty()) {
213 offsetAction.setEnabled(false);
214 return singleOffset;
215 }
216 offsetAction.setEnabled(true);
217 JMenu newMenu = new JMenu(trc("layer","Offset"));
218 newMenu.setIcon(ImageProvider.get("mapmode", "adjustimg"));
219 newMenu.setAction(offsetAction);
220 if (layers.size() == 1)
221 return (JMenuItem)layers.iterator().next().getOffsetMenuItem(newMenu);
222 for (ImageryLayer layer : layers) {
223 JMenuItem layerMenu = layer.getOffsetMenuItem();
224 layerMenu.setText(layer.getName());
225 layerMenu.setIcon(layer.getIcon());
226 newMenu.add(layerMenu);
227 }
228 return newMenu;
229 }
230
231 public void refreshOffsetMenu() {
232 offsetMenuItem = getNewOffsetMenu();
233 }
234
235 @Override
236 public void activeLayerChange(Layer oldLayer, Layer newLayer) {
237 }
238
239 @Override
240 public void layerAdded(Layer newLayer) {
241 if (newLayer instanceof ImageryLayer) {
242 refreshOffsetMenu();
243 }
244 }
245
246 @Override
247 public void layerRemoved(Layer oldLayer) {
248 if (oldLayer instanceof ImageryLayer) {
249 refreshOffsetMenu();
250 }
251 }
252
253 /**
254 * Collection to store temporary menu items. They will be deleted
255 * (and possibly recreated) when refreshImageryMenu() is called.
256 * @since 5803
257 */
258 private List<Object> dynamicItems = new ArrayList<>(20);
259
260 /**
261 * Remove all the items in @field dynamicItems collection
262 * @since 5803
263 */
264 private void removeDynamicItems() {
265 for (Object item : dynamicItems) {
266 if (item instanceof JMenuItem) {
267 remove((JMenuItem)item);
268 }
269 if (item instanceof MenuComponent) {
270 remove((MenuComponent)item);
271 }
272 if (item instanceof Component) {
273 remove((Component)item);
274 }
275 }
276 dynamicItems.clear();
277 }
278
279 private void addDynamicSeparator() {
280 JPopupMenu.Separator s = new JPopupMenu.Separator();
281 dynamicItems.add(s);
282 add(s);
283 }
284
285 private void addDynamic(Action a) {
286 dynamicItems.add( this.add(a) );
287 }
288
289 private void addDynamic(JMenuItem it) {
290 dynamicItems.add( this.add(it) );
291 }
292}
Note: See TracBrowser for help on using the repository browser.