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

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

see #14741 - rework initialization of Main.Panel so that it is done after PlatformHook.preStartupHook, as any other Swing code

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