kopia lustrzana https://github.com/JOSM/MapWithAI
Create a dynamic menu that shows sources in the current view
This fixes #88. Signed-off-by: Taylor Smock <taylor.smock@kaart.com>pull/1/head
rodzic
786ee5f689
commit
1414061c23
|
@ -1,9 +1,10 @@
|
||||||
// License: GPL. For details, see LICENSE file.
|
// License: GPL. For details, see LICENSE file.
|
||||||
package org.openstreetmap.josm.plugins.mapwithai;
|
package org.openstreetmap.josm.plugins.mapwithai;
|
||||||
|
|
||||||
|
import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
|
||||||
import static org.openstreetmap.josm.tools.I18n.tr;
|
import static org.openstreetmap.josm.tools.I18n.tr;
|
||||||
|
|
||||||
import java.awt.Component;
|
import java.awt.event.KeyEvent;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -12,10 +13,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import javax.swing.Action;
|
|
||||||
import javax.swing.JMenu;
|
|
||||||
import javax.swing.JMenuItem;
|
import javax.swing.JMenuItem;
|
||||||
|
|
||||||
import org.openstreetmap.josm.actions.JosmAction;
|
import org.openstreetmap.josm.actions.JosmAction;
|
||||||
|
@ -46,6 +44,7 @@ import org.openstreetmap.josm.plugins.mapwithai.data.validation.tests.RoutingIsl
|
||||||
import org.openstreetmap.josm.plugins.mapwithai.data.validation.tests.StreetAddressOrder;
|
import org.openstreetmap.josm.plugins.mapwithai.data.validation.tests.StreetAddressOrder;
|
||||||
import org.openstreetmap.josm.plugins.mapwithai.data.validation.tests.StreetAddressTest;
|
import org.openstreetmap.josm.plugins.mapwithai.data.validation.tests.StreetAddressTest;
|
||||||
import org.openstreetmap.josm.plugins.mapwithai.data.validation.tests.StubEndsTest;
|
import org.openstreetmap.josm.plugins.mapwithai.data.validation.tests.StubEndsTest;
|
||||||
|
import org.openstreetmap.josm.plugins.mapwithai.gui.MapWithAIMenu;
|
||||||
import org.openstreetmap.josm.plugins.mapwithai.gui.download.MapWithAIDownloadOptions;
|
import org.openstreetmap.josm.plugins.mapwithai.gui.download.MapWithAIDownloadOptions;
|
||||||
import org.openstreetmap.josm.plugins.mapwithai.gui.download.MapWithAIDownloadSourceType;
|
import org.openstreetmap.josm.plugins.mapwithai.gui.download.MapWithAIDownloadSourceType;
|
||||||
import org.openstreetmap.josm.plugins.mapwithai.gui.preferences.MapWithAIPreferences;
|
import org.openstreetmap.josm.plugins.mapwithai.gui.preferences.MapWithAIPreferences;
|
||||||
|
@ -66,6 +65,8 @@ public final class MapWithAIPlugin extends Plugin implements Destroyable {
|
||||||
|
|
||||||
private PreferencesAction preferenceAction;
|
private PreferencesAction preferenceAction;
|
||||||
|
|
||||||
|
private MapWithAIMenu mapwithaiMenu;
|
||||||
|
|
||||||
private static final Map<Class<? extends JosmAction>, Boolean> MENU_ENTRIES = new LinkedHashMap<>();
|
private static final Map<Class<? extends JosmAction>, Boolean> MENU_ENTRIES = new LinkedHashMap<>();
|
||||||
static {
|
static {
|
||||||
MENU_ENTRIES.put(MapWithAIAction.class, false);
|
MENU_ENTRIES.put(MapWithAIAction.class, false);
|
||||||
|
@ -81,13 +82,17 @@ public final class MapWithAIPlugin extends Plugin implements Destroyable {
|
||||||
|
|
||||||
preferenceSetting = new MapWithAIPreferences();
|
preferenceSetting = new MapWithAIPreferences();
|
||||||
|
|
||||||
final JMenu dataMenu = MainApplication.getMenu().dataMenu;
|
// Add MapWithAI specific menu
|
||||||
|
mapwithaiMenu = new MapWithAIMenu();
|
||||||
|
MainApplication.getMenu().addMenu(mapwithaiMenu, "mapwithai:menu", KeyEvent.VK_M, 9, ht("/Plugin/MapWithAI"));
|
||||||
|
|
||||||
for (final Entry<Class<? extends JosmAction>, Boolean> entry : MENU_ENTRIES.entrySet()) {
|
for (final Entry<Class<? extends JosmAction>, Boolean> entry : MENU_ENTRIES.entrySet()) {
|
||||||
if (Arrays.asList(dataMenu.getMenuComponents()).parallelStream().filter(JMenuItem.class::isInstance)
|
if (Arrays.asList(mapwithaiMenu.getMenuComponents()).parallelStream().filter(JMenuItem.class::isInstance)
|
||||||
.map(JMenuItem.class::cast)
|
.map(JMenuItem.class::cast)
|
||||||
.noneMatch(component -> entry.getKey().equals(component.getAction().getClass()))) {
|
.noneMatch(component -> entry.getKey().equals(component.getAction().getClass()))) {
|
||||||
try {
|
try {
|
||||||
MainMenu.add(dataMenu, entry.getKey().getDeclaredConstructor().newInstance(), entry.getValue());
|
MainMenu.add(mapwithaiMenu, entry.getKey().getDeclaredConstructor().newInstance(),
|
||||||
|
entry.getValue());
|
||||||
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
|
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
|
||||||
| InvocationTargetException | NoSuchMethodException | SecurityException e) {
|
| InvocationTargetException | NoSuchMethodException | SecurityException e) {
|
||||||
Logging.debug(e);
|
Logging.debug(e);
|
||||||
|
@ -98,7 +103,7 @@ public final class MapWithAIPlugin extends Plugin implements Destroyable {
|
||||||
// Add the preferences last to data
|
// Add the preferences last to data
|
||||||
preferenceAction = PreferencesAction.forPreferenceTab(tr("MapWithAI Preferences"), tr("MapWithAI Preferences"),
|
preferenceAction = PreferencesAction.forPreferenceTab(tr("MapWithAI Preferences"), tr("MapWithAI Preferences"),
|
||||||
MapWithAIPreferences.class);
|
MapWithAIPreferences.class);
|
||||||
MainMenu.add(dataMenu, preferenceAction);
|
MainMenu.add(mapwithaiMenu, preferenceAction);
|
||||||
|
|
||||||
VALIDATORS.forEach(clazz -> {
|
VALIDATORS.forEach(clazz -> {
|
||||||
if (!OsmValidator.getAllAvailableTestClasses().contains(clazz)) {
|
if (!OsmValidator.getAllAvailableTestClasses().contains(clazz)) {
|
||||||
|
@ -166,18 +171,7 @@ public final class MapWithAIPlugin extends Plugin implements Destroyable {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
final JMenu dataMenu = MainApplication.getMenu().dataMenu;
|
MainApplication.getMenu().remove(this.mapwithaiMenu);
|
||||||
final Map<Action, Component> actions = Arrays.asList(dataMenu.getMenuComponents()).stream()
|
|
||||||
.filter(JMenuItem.class::isInstance).map(JMenuItem.class::cast)
|
|
||||||
.collect(Collectors.toMap(JMenuItem::getAction, component -> component));
|
|
||||||
|
|
||||||
for (final Entry<Action, Component> action : actions.entrySet()) {
|
|
||||||
if (MENU_ENTRIES.containsKey(action.getKey().getClass())) {
|
|
||||||
dataMenu.remove(action.getValue());
|
|
||||||
} else if (action.getKey().equals(preferenceAction)) {
|
|
||||||
dataMenu.remove(action.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MainApplication.getLayerManager().getLayersOfType(MapWithAILayer.class).stream()
|
MainApplication.getLayerManager().getLayersOfType(MapWithAILayer.class).stream()
|
||||||
.forEach(layer -> MainApplication.getLayerManager().removeLayer(layer));
|
.forEach(layer -> MainApplication.getLayerManager().removeLayer(layer));
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
// License: GPL. For details, see LICENSE file.
|
||||||
|
package org.openstreetmap.josm.plugins.mapwithai.actions;
|
||||||
|
|
||||||
|
import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
|
||||||
|
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
|
||||||
|
import org.openstreetmap.josm.actions.AdaptableAction;
|
||||||
|
import org.openstreetmap.josm.actions.AddImageryLayerAction;
|
||||||
|
import org.openstreetmap.josm.actions.JosmAction;
|
||||||
|
import org.openstreetmap.josm.data.osm.DataSet;
|
||||||
|
import org.openstreetmap.josm.data.osm.OsmData;
|
||||||
|
import org.openstreetmap.josm.gui.MainApplication;
|
||||||
|
import org.openstreetmap.josm.gui.preferences.ToolbarPreferences;
|
||||||
|
import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
|
||||||
|
import org.openstreetmap.josm.gui.util.GuiHelper;
|
||||||
|
import org.openstreetmap.josm.io.OsmTransferException;
|
||||||
|
import org.openstreetmap.josm.plugins.mapwithai.backend.BoundingBoxMapWithAIDownloader;
|
||||||
|
import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAIDataUtils;
|
||||||
|
import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAILayer;
|
||||||
|
import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo;
|
||||||
|
import org.openstreetmap.josm.tools.ImageProvider;
|
||||||
|
import org.openstreetmap.josm.tools.ImageProvider.ImageSizes;
|
||||||
|
import org.openstreetmap.josm.tools.ImageResource;
|
||||||
|
import org.openstreetmap.josm.tools.Logging;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action displayed in MapWithAI menu to add data to the MapWithAI layer.
|
||||||
|
* Largely copied from {@link AddImageryLayerAction}.
|
||||||
|
*/
|
||||||
|
public class AddMapWithAILayerAction extends JosmAction implements AdaptableAction {
|
||||||
|
private final transient MapWithAIInfo info;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new {@code AddMapWithAILayerAction} for the given
|
||||||
|
* {@code MapWithAIInfo}. If an http:// icon is specified, it is fetched
|
||||||
|
* asynchronously.
|
||||||
|
*
|
||||||
|
* @param info The source info
|
||||||
|
*/
|
||||||
|
public AddMapWithAILayerAction(MapWithAIInfo info) {
|
||||||
|
super(info.getName(), /* ICON */"imagery_menu", info.getToolTipText(), null, true,
|
||||||
|
ToolbarPreferences.IMAGERY_PREFIX + info.getToolbarName(), false);
|
||||||
|
setHelpId(ht("/Preferences/Imagery"));
|
||||||
|
this.info = info;
|
||||||
|
installAdapters();
|
||||||
|
|
||||||
|
// change toolbar icon from if specified
|
||||||
|
String icon = info.getIcon();
|
||||||
|
if (icon != null) {
|
||||||
|
new ImageProvider(icon).setOptional(true).getResourceAsync(result -> {
|
||||||
|
if (result != null) {
|
||||||
|
GuiHelper.runInEDT(() -> result.attachImageIcon(this));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
ImageResource resource = new ImageResource(
|
||||||
|
this.info.getSourceCategory().getIcon(ImageSizes.MENU).getImage());
|
||||||
|
resource.attachImageIcon(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
if (!isEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MapWithAILayer layer = MapWithAIDataUtils.getLayer(false);
|
||||||
|
final DataSet ds;
|
||||||
|
final OsmData<?, ?, ?, ?> boundsSource;
|
||||||
|
if (layer != null && !layer.getData().getDataSourceBounds().isEmpty()) {
|
||||||
|
ds = layer.getDataSet();
|
||||||
|
boundsSource = ds;
|
||||||
|
} else if (MainApplication.getLayerManager().getActiveData() != null
|
||||||
|
&& !MainApplication.getLayerManager().getActiveData().getDataSourceBounds().isEmpty()) {
|
||||||
|
boundsSource = MainApplication.getLayerManager().getActiveData();
|
||||||
|
ds = MapWithAIDataUtils.getLayer(true).getDataSet();
|
||||||
|
} else {
|
||||||
|
boundsSource = null;
|
||||||
|
ds = null;
|
||||||
|
}
|
||||||
|
if (boundsSource != null && ds != null) {
|
||||||
|
boundsSource.getDataSourceBounds().forEach(b -> MainApplication.worker.execute(() -> {
|
||||||
|
try {
|
||||||
|
ds.mergeFrom(
|
||||||
|
new BoundingBoxMapWithAIDownloader(b, info, false).parseOsm(NullProgressMonitor.INSTANCE));
|
||||||
|
} catch (OsmTransferException error) {
|
||||||
|
Logging.error(error);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean listenToSelectionChange() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateEnabledState() {
|
||||||
|
setEnabled(!info.isBlacklisted());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "AddMapWithAILayerAction [info=" + info + ']';
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,7 +34,12 @@ import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIType;
|
||||||
import org.openstreetmap.josm.tools.HttpClient;
|
import org.openstreetmap.josm.tools.HttpClient;
|
||||||
import org.openstreetmap.josm.tools.Logging;
|
import org.openstreetmap.josm.tools.Logging;
|
||||||
|
|
||||||
class BoundingBoxMapWithAIDownloader extends BoundingBoxDownloader {
|
/**
|
||||||
|
* A bounding box downloader for MapWithAI
|
||||||
|
*
|
||||||
|
* @author Taylor Smock
|
||||||
|
*/
|
||||||
|
public class BoundingBoxMapWithAIDownloader extends BoundingBoxDownloader {
|
||||||
private final String url;
|
private final String url;
|
||||||
private final boolean crop;
|
private final boolean crop;
|
||||||
|
|
||||||
|
@ -46,6 +51,13 @@ class BoundingBoxMapWithAIDownloader extends BoundingBoxDownloader {
|
||||||
|
|
||||||
private static final int DEFAULT_TIMEOUT = 50_000; // 50 seconds
|
private static final int DEFAULT_TIMEOUT = 50_000; // 50 seconds
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@link BoundingBoxMapWithAIDownloader} object
|
||||||
|
*
|
||||||
|
* @param downloadArea The area to download
|
||||||
|
* @param info The info to use to get the url to download
|
||||||
|
* @param crop Whether or not to crop the download area
|
||||||
|
*/
|
||||||
public BoundingBoxMapWithAIDownloader(Bounds downloadArea, MapWithAIInfo info, boolean crop) {
|
public BoundingBoxMapWithAIDownloader(Bounds downloadArea, MapWithAIInfo info, boolean crop) {
|
||||||
super(downloadArea);
|
super(downloadArea);
|
||||||
this.info = info;
|
this.info = info;
|
||||||
|
|
|
@ -536,4 +536,13 @@ public class MapWithAIInfo extends
|
||||||
}
|
}
|
||||||
return Collections.unmodifiableList(this.conflationIgnoreCategory);
|
return Collections.unmodifiableList(this.conflationIgnoreCategory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a string usable for toolbars
|
||||||
|
*
|
||||||
|
* @return Currently, the name of the source.
|
||||||
|
*/
|
||||||
|
public String getToolbarName() {
|
||||||
|
return this.getName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,229 @@
|
||||||
|
// License: GPL. For details, see LICENSE file.
|
||||||
|
package org.openstreetmap.josm.plugins.mapwithai.gui;
|
||||||
|
|
||||||
|
import static org.openstreetmap.josm.tools.I18n.trc;
|
||||||
|
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.GraphicsEnvironment;
|
||||||
|
import java.awt.MenuComponent;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.swing.Action;
|
||||||
|
import javax.swing.JMenu;
|
||||||
|
import javax.swing.JMenuItem;
|
||||||
|
import javax.swing.JPopupMenu;
|
||||||
|
import javax.swing.event.MenuEvent;
|
||||||
|
import javax.swing.event.MenuListener;
|
||||||
|
|
||||||
|
import org.openstreetmap.josm.actions.JosmAction;
|
||||||
|
import org.openstreetmap.josm.data.coor.LatLon;
|
||||||
|
import org.openstreetmap.josm.data.imagery.Shape;
|
||||||
|
import org.openstreetmap.josm.data.sources.SourceInfo;
|
||||||
|
import org.openstreetmap.josm.gui.ImageryMenu;
|
||||||
|
import org.openstreetmap.josm.gui.MainApplication;
|
||||||
|
import org.openstreetmap.josm.gui.MapView;
|
||||||
|
import org.openstreetmap.josm.gui.MenuScroller;
|
||||||
|
import org.openstreetmap.josm.gui.preferences.imagery.ImageryPreference;
|
||||||
|
import org.openstreetmap.josm.plugins.mapwithai.actions.AddMapWithAILayerAction;
|
||||||
|
import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAICategory;
|
||||||
|
import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo;
|
||||||
|
import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAILayerInfo;
|
||||||
|
import org.openstreetmap.josm.tools.ImageProvider.ImageSizes;
|
||||||
|
import org.openstreetmap.josm.tools.Logging;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MapWithAI menu, holding entries for MapWithAI preferences and dynamic source
|
||||||
|
* entries depending on current mapview coordinates.
|
||||||
|
*
|
||||||
|
* Largely copied from {@link ImageryMenu}, but highly modified.
|
||||||
|
*/
|
||||||
|
public class MapWithAIMenu extends JMenu {
|
||||||
|
/**
|
||||||
|
* Compare MapWithAIInfo objects alphabetically by name.
|
||||||
|
*
|
||||||
|
* MapWithAIInfo objects are normally sorted by country code first (for the
|
||||||
|
* preferences). We don't want this in the MapWithAI menu.
|
||||||
|
*/
|
||||||
|
public static final Comparator<SourceInfo<?, ?, ?, ?>> alphabeticSourceComparator = (ii1, ii2) -> ii1.getName()
|
||||||
|
.toLowerCase(Locale.ENGLISH).compareTo(ii2.getName().toLowerCase(Locale.ENGLISH));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new {@code ImageryMenu}.
|
||||||
|
*/
|
||||||
|
public MapWithAIMenu() {
|
||||||
|
/* I18N: mnemonic: I */
|
||||||
|
super(trc("menu", "MapWithAI"));
|
||||||
|
setupMenuScroller();
|
||||||
|
// build dynamically
|
||||||
|
addMenuListener(new MenuListener() {
|
||||||
|
@Override
|
||||||
|
public void menuSelected(MenuEvent e) {
|
||||||
|
refreshImageryMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void menuDeselected(MenuEvent e) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void menuCanceled(MenuEvent e) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupMenuScroller() {
|
||||||
|
if (!GraphicsEnvironment.isHeadless()) {
|
||||||
|
MenuScroller.setScrollerFor(this, 150, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For layers containing complex shapes, check that center is in one of its
|
||||||
|
* shapes (fix #7910)
|
||||||
|
*
|
||||||
|
* @param info layer info
|
||||||
|
* @param pos center
|
||||||
|
* @return {@code true} if center is in one of info shapes
|
||||||
|
*/
|
||||||
|
private static boolean isPosInOneShapeIfAny(SourceInfo<?, ?, ?, ?> info, LatLon pos) {
|
||||||
|
List<Shape> shapes = info.getBounds().getShapes();
|
||||||
|
return shapes == null || shapes.isEmpty() || shapes.stream().anyMatch(s -> s.contains(pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh imagery menu.
|
||||||
|
*
|
||||||
|
* Outside this class only called in {@link ImageryPreference#initialize()}. (In
|
||||||
|
* order to have actions ready for the toolbar, see #8446.)
|
||||||
|
*/
|
||||||
|
public void refreshImageryMenu() {
|
||||||
|
removeDynamicItems();
|
||||||
|
|
||||||
|
addDynamicSeparator();
|
||||||
|
|
||||||
|
// for each configured ImageryInfo, add a menu entry.
|
||||||
|
final List<MapWithAIInfo> savedLayers = new ArrayList<>(MapWithAILayerInfo.getInstance().getLayers());
|
||||||
|
savedLayers.sort(alphabeticSourceComparator);
|
||||||
|
for (final MapWithAIInfo u : savedLayers) {
|
||||||
|
addDynamic(trackJosmAction(new AddMapWithAILayerAction(u)), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// list all imagery entries where the current map location is within the imagery
|
||||||
|
// bounds
|
||||||
|
if (MainApplication.isDisplayingMapView()) {
|
||||||
|
MapView mv = MainApplication.getMap().mapView;
|
||||||
|
LatLon pos = mv.getProjection().eastNorth2latlon(mv.getCenter());
|
||||||
|
final List<MapWithAIInfo> alreadyInUse = MapWithAILayerInfo.getInstance().getLayers();
|
||||||
|
final List<MapWithAIInfo> inViewLayers = MapWithAILayerInfo.getInstance().getDefaultLayers().stream()
|
||||||
|
.filter(i -> i.getBounds() != null && i.getBounds().contains(pos) && !alreadyInUse.contains(i)
|
||||||
|
&& isPosInOneShapeIfAny(i, pos))
|
||||||
|
.sorted(alphabeticSourceComparator).collect(Collectors.toList());
|
||||||
|
if (!inViewLayers.isEmpty()) {
|
||||||
|
if (inViewLayers.stream().anyMatch(i -> i.getCategory() == i.getCategory().getDefault())) {
|
||||||
|
addDynamicSeparator();
|
||||||
|
}
|
||||||
|
for (MapWithAIInfo i : inViewLayers) {
|
||||||
|
addDynamic(trackJosmAction(new AddMapWithAILayerAction(i)), i.getCategory());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!dynamicNonPhotoItems.isEmpty()) {
|
||||||
|
addDynamicSeparator();
|
||||||
|
for (Entry<MapWithAICategory, List<JMenuItem>> e : dynamicNonPhotoItems.entrySet()) {
|
||||||
|
MapWithAICategory cat = e.getKey();
|
||||||
|
List<JMenuItem> list = e.getValue();
|
||||||
|
if (list.size() > 1) {
|
||||||
|
JMenuItem categoryMenu = new JMenu(cat.getDescription());
|
||||||
|
categoryMenu.setIcon(cat.getIcon(ImageSizes.MENU));
|
||||||
|
for (JMenuItem it : list) {
|
||||||
|
categoryMenu.add(it);
|
||||||
|
}
|
||||||
|
dynamicNonPhotoMenus.add(add(categoryMenu));
|
||||||
|
} else if (!list.isEmpty()) {
|
||||||
|
dynamicNonPhotoMenus.add(add(list.get(0)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List to store temporary "photo" menu items. They will be deleted (and
|
||||||
|
* possibly recreated) when refreshImageryMenu() is called.
|
||||||
|
*/
|
||||||
|
private final List<Object> dynamicItems = new ArrayList<>(20);
|
||||||
|
/**
|
||||||
|
* Map to store temporary "not photo" menu items. They will be deleted (and
|
||||||
|
* possibly recreated) when refreshImageryMenu() is called.
|
||||||
|
*/
|
||||||
|
private final Map<MapWithAICategory, List<JMenuItem>> dynamicNonPhotoItems = new EnumMap<>(MapWithAICategory.class);
|
||||||
|
/**
|
||||||
|
* List to store temporary "not photo" submenus. They will be deleted (and
|
||||||
|
* possibly recreated) when refreshImageryMenu() is called.
|
||||||
|
*/
|
||||||
|
private final List<JMenuItem> dynamicNonPhotoMenus = new ArrayList<>(20);
|
||||||
|
private final List<JosmAction> dynJosmActions = new ArrayList<>(20);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all the items in dynamic items collection
|
||||||
|
*
|
||||||
|
* @since 5803
|
||||||
|
*/
|
||||||
|
private void removeDynamicItems() {
|
||||||
|
dynJosmActions.forEach(JosmAction::destroy);
|
||||||
|
dynJosmActions.clear();
|
||||||
|
dynamicItems.forEach(this::removeDynamicItem);
|
||||||
|
dynamicItems.clear();
|
||||||
|
dynamicNonPhotoMenus.forEach(this::removeDynamicItem);
|
||||||
|
dynamicItems.clear();
|
||||||
|
dynamicNonPhotoItems.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeDynamicItem(Object item) {
|
||||||
|
if (item instanceof JMenuItem) {
|
||||||
|
remove((JMenuItem) item);
|
||||||
|
} else if (item instanceof MenuComponent) {
|
||||||
|
remove((MenuComponent) item);
|
||||||
|
} else if (item instanceof Component) {
|
||||||
|
remove((Component) item);
|
||||||
|
} else {
|
||||||
|
Logging.error("Unknown imagery menu item type: {0}", item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addDynamicSeparator() {
|
||||||
|
JPopupMenu.Separator s = new JPopupMenu.Separator();
|
||||||
|
dynamicItems.add(s);
|
||||||
|
add(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addDynamic(Action a, MapWithAICategory category) {
|
||||||
|
JMenuItem item = createActionComponent(a);
|
||||||
|
item.setAction(a);
|
||||||
|
doAddDynamic(item, category);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doAddDynamic(JMenuItem item, MapWithAICategory category) {
|
||||||
|
if (category == null || category == MapWithAICategory.FEATURED) {
|
||||||
|
dynamicItems.add(this.add(item));
|
||||||
|
} else {
|
||||||
|
dynamicNonPhotoItems.computeIfAbsent(category, x -> new ArrayList<>()).add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Action trackJosmAction(Action action) {
|
||||||
|
if (action instanceof JosmAction) {
|
||||||
|
dynJosmActions.add((JosmAction) action);
|
||||||
|
}
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -88,10 +88,11 @@ public class MapWithAIPluginTest {
|
||||||
menuEntries.setAccessible(true);
|
menuEntries.setAccessible(true);
|
||||||
// + 1 comes from the preferences panel
|
// + 1 comes from the preferences panel
|
||||||
final int addedMenuItems = ((Map<?, ?>) menuEntries.get(plugin)).size() + 1;
|
final int addedMenuItems = ((Map<?, ?>) menuEntries.get(plugin)).size() + 1;
|
||||||
final JMenu dataMenu = MainApplication.getMenu().dataMenu;
|
|
||||||
final int dataMenuSize = dataMenu.getMenuComponentCount();
|
|
||||||
plugin = new MapWithAIPlugin(info);
|
plugin = new MapWithAIPlugin(info);
|
||||||
assertEquals(dataMenuSize + addedMenuItems, dataMenu.getMenuComponentCount(), "Menu items were not added");
|
// Currently adding the menu at the 9th index
|
||||||
|
final JMenu dataMenu = MainApplication.getMenu().getMenu(9);
|
||||||
|
final int dataMenuSize = dataMenu.getMenuComponentCount();
|
||||||
|
assertEquals(addedMenuItems, dataMenu.getMenuComponentCount(), "Menu items were not added");
|
||||||
assertEquals(1,
|
assertEquals(1,
|
||||||
MapPaintStyles.getStyles().getStyleSources().parallelStream()
|
MapPaintStyles.getStyles().getStyleSources().parallelStream()
|
||||||
.filter(source -> source.url != null && source.name.contains("MapWithAI")).count(),
|
.filter(source -> source.url != null && source.name.contains("MapWithAI")).count(),
|
||||||
|
@ -114,8 +115,7 @@ public class MapWithAIPluginTest {
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
plugin = new MapWithAIPlugin(info);
|
plugin = new MapWithAIPlugin(info);
|
||||||
assertEquals(dataMenuSize + addedMenuItems, dataMenu.getMenuComponentCount(),
|
assertEquals(addedMenuItems, dataMenu.getMenuComponentCount(), "The menu items were added multiple times");
|
||||||
"The menu items were added multiple times");
|
|
||||||
assertEquals(1,
|
assertEquals(1,
|
||||||
MapPaintStyles.getStyles().getStyleSources().parallelStream()
|
MapPaintStyles.getStyles().getStyleSources().parallelStream()
|
||||||
.filter(source -> source.url != null && source.name.contains("MapWithAI")).count(),
|
.filter(source -> source.url != null && source.name.contains("MapWithAI")).count(),
|
||||||
|
|
Ładowanie…
Reference in New Issue