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

Last change on this file since 8256 was 7966, checked in by bastiK, 9 years ago

fixed #10934 - JOSM>Imagery menu order and contents - Don't use hidden country grouping in menu

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