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

Last change on this file since 13734 was 12643, checked in by Don-vip, 7 years ago

see #15182 - deprecate Main.main.menu. Replacement: gui.MainApplication.getMenu()

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