Ticket #21605: 21605.4.patch
| File 21605.4.patch, 48.8 KB (added by , 3 years ago) |
|---|
-
src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java
IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 diff --git a/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java b/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java
a b 47 47 import org.openstreetmap.josm.data.gpx.GpxData; 48 48 import org.openstreetmap.josm.data.gpx.GpxImageEntry; 49 49 import org.openstreetmap.josm.data.gpx.GpxTrack; 50 import org.openstreetmap.josm.data.imagery.street_level.IImageEntry; 50 51 import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor; 51 52 import org.openstreetmap.josm.gui.MainApplication; 52 53 import org.openstreetmap.josm.gui.MapFrame; … … 62 63 import org.openstreetmap.josm.gui.layer.JumpToMarkerActions.JumpToPreviousMarker; 63 64 import org.openstreetmap.josm.gui.layer.Layer; 64 65 import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener; 66 import org.openstreetmap.josm.gui.util.GuiHelper; 65 67 import org.openstreetmap.josm.gui.util.imagery.Vector3D; 66 68 import org.openstreetmap.josm.tools.ImageProvider; 69 import org.openstreetmap.josm.tools.ListenerList; 67 70 import org.openstreetmap.josm.tools.Utils; 68 71 69 72 /** … … 71 74 * @since 99 72 75 */ 73 76 public class GeoImageLayer extends AbstractModifiableLayer implements 74 JumpToMarkerLayer, NavigatableComponent.ZoomChangeListener, ImageDataUpdateListener { 77 JumpToMarkerLayer, NavigatableComponent.ZoomChangeListener, ImageDataUpdateListener, 78 IGeoImageLayer { 75 79 76 80 private static final List<Action> menuAdditions = new LinkedList<>(); 77 81 78 82 private static volatile List<MapMode> supportedMapModes; 79 83 80 84 private final ImageData data; 85 private final ListenerList<IGeoImageLayer.ImageChangeListener> imageChangeListeners = ListenerList.create(); 81 86 GpxData gpxData; 82 87 GpxLayer gpxFauxLayer; 83 88 GpxData gpxFauxData; … … 172 177 this.useThumbs = useThumbs; 173 178 this.data.addImageDataUpdateListener(this); 174 179 this.data.setLayer(this); 180 if (!ImageViewerDialog.hasInstance()) { 181 this.data.setSelectedImage(this.data.getFirstImage()); 182 // This must be called *after* this layer is added to the layer manager. 183 // But it must also be run in the EDT. By adding this to the worker queue 184 // and then running the actual code in the EDT, we ensure that the layer 185 // will be added to the layer manager regardless of whether or not this 186 // was instantiated in the worker thread or the EDT thread. 187 MainApplication.worker.submit(() -> GuiHelper.runInEDT(() -> 188 ImageViewerDialog.getInstance().displayImages(Collections.singletonList(this.data.getSelectedImage())))); 189 } 175 190 } 176 191 177 192 private final class ImageMouseListener extends MouseAdapter { … … 232 247 } 233 248 } else { 234 249 data.setSelectedImage(img); 235 ImageViewerDialog.getInstance().displayImages( GeoImageLayer.this,Collections.singletonList(img));250 ImageViewerDialog.getInstance().displayImages(Collections.singletonList(img)); 236 251 } 237 252 } 238 253 } … … 247 262 MainApplication.worker.execute(new ImagesLoader(files, gpxLayer)); 248 263 } 249 264 265 @Override 266 public void clearSelection() { 267 this.getImageData().clearSelectedImage(); 268 } 269 270 @Override 271 public boolean containsImage(IImageEntry<?> imageEntry) { 272 if (imageEntry instanceof ImageEntry) { 273 return this.data.getImages().contains(imageEntry); 274 } 275 return false; 276 } 277 250 278 @Override 251 279 public Icon getIcon() { 252 280 return ImageProvider.get("dialogs/geoimage", ImageProvider.ImageSizes.LAYER); 253 281 } 254 282 283 @Override 284 public List<ImageEntry> getSelection() { 285 return this.getImageData().getSelectedImages(); 286 } 287 288 @Override 289 public List<IImageEntry<?>> getInvalidGeoImages() { 290 return this.getImageData().getImages().stream().filter(entry -> entry.getPos() == null || entry.getExifCoor() == null 291 || !entry.getExifCoor().isValid() || !entry.getPos().isValid()).collect(toList()); 292 } 293 294 @Override 295 public void addImageChangeListener(ImageChangeListener listener) { 296 this.imageChangeListeners.addListener(listener); 297 } 298 299 @Override 300 public void removeImageChangeListener(ImageChangeListener listener) { 301 this.imageChangeListeners.removeListener(listener); 302 } 303 255 304 /** 256 305 * Register actions on the layer 257 306 * @param addition the action to be added … … 884 933 @Override 885 934 public void selectedImageChanged(ImageData data) { 886 935 showCurrentPhoto(); 936 this.imageChangeListeners.fireEvent(e -> e.imageChanged(this, null, data.getSelectedImages())); 887 937 } 888 938 889 939 @Override -
new file src/org/openstreetmap/josm/gui/layer/geoimage/IGeoImageLayer.java
IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 diff --git a/src/org/openstreetmap/josm/gui/layer/geoimage/IGeoImageLayer.java b/src/org/openstreetmap/josm/gui/layer/geoimage/IGeoImageLayer.java new file mode 100644
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui.layer.geoimage; 3 4 import java.util.Collections; 5 import java.util.List; 6 7 import org.openstreetmap.josm.data.imagery.street_level.IImageEntry; 8 9 /** 10 * An interface for layers which want to show images 11 * @since xxx 12 */ 13 public interface IGeoImageLayer { 14 /** 15 * Clear the selection of the layer 16 */ 17 void clearSelection(); 18 19 /** 20 * Get the current selection 21 * @return The currently selected images 22 */ 23 List<? extends IImageEntry<?>> getSelection(); 24 25 /** 26 * Get the invalid geo images for this layer (specifically, those that <i>cannot</i> be displayed on the map) 27 * @return The list of invalid geo images 28 */ 29 default List<IImageEntry<?>> getInvalidGeoImages() { 30 return Collections.emptyList(); 31 } 32 33 /** 34 * Check if the layer contains the specified image 35 * @param imageEntry The entry to look for 36 * @return {@code true} if this layer contains the image 37 */ 38 boolean containsImage(IImageEntry<?> imageEntry); 39 40 /** 41 * Add a listener for when images change 42 * @param listener The listener to call 43 */ 44 void addImageChangeListener(ImageChangeListener listener); 45 46 /** 47 * Remove a listener for when images change 48 * @param listener The listener to remove 49 */ 50 void removeImageChangeListener(ImageChangeListener listener); 51 52 /** 53 * Listen for image changes 54 */ 55 interface ImageChangeListener { 56 /** 57 * Called when the selected image(s) change 58 * @param source The source of the change 59 * @param oldImages The previously selected image(s) 60 * @param newImages The newly selected image(s) 61 */ 62 void imageChanged(IGeoImageLayer source, List<? extends IImageEntry<?>> oldImages, List<? extends IImageEntry<?>> newImages); 63 } 64 } -
src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java
IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 diff --git a/src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java b/src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java
a b 147 147 if (entry instanceof ImageEntry) { 148 148 this.dataSet.setSelectedImage((ImageEntry) entry); 149 149 } 150 imageViewerDialog.displayImages( this.dataSet.getLayer(),Collections.singletonList(entry));150 imageViewerDialog.displayImages(Collections.singletonList(entry)); 151 151 } 152 152 153 153 @Override -
src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java
IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 diff --git a/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java b/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java
a b 11 11 import java.awt.GridBagConstraints; 12 12 import java.awt.GridBagLayout; 13 13 import java.awt.event.ActionEvent; 14 import java.awt.event.ActionListener;15 14 import java.awt.event.KeyEvent; 16 15 import java.awt.event.WindowEvent; 17 16 import java.io.IOException; … … 23 22 import java.util.Arrays; 24 23 import java.util.Collections; 25 24 import java.util.Comparator; 26 import java.util.HashMap;27 25 import java.util.List; 28 import java.util.Map;29 26 import java.util.Objects; 30 27 import java.util.Optional; 31 28 import java.util.concurrent.Future; 32 29 import java.util.function.UnaryOperator; 33 30 import java.util.stream.Collectors; 31 import java.util.stream.IntStream; 32 import java.util.stream.Stream; 34 33 35 34 import javax.swing.AbstractAction; 35 import javax.swing.AbstractButton; 36 36 import javax.swing.Box; 37 37 import javax.swing.JButton; 38 38 import javax.swing.JLabel; … … 42 42 import javax.swing.SwingConstants; 43 43 import javax.swing.SwingUtilities; 44 44 45 import org.openstreetmap.josm.actions.ExpertToggleAction; 45 46 import org.openstreetmap.josm.actions.JosmAction; 46 47 import org.openstreetmap.josm.data.ImageData; 47 import org.openstreetmap.josm.data.ImageData.ImageDataUpdateListener;48 48 import org.openstreetmap.josm.data.imagery.street_level.IImageEntry; 49 49 import org.openstreetmap.josm.gui.ExtendedDialog; 50 50 import org.openstreetmap.josm.gui.MainApplication; … … 64 64 import org.openstreetmap.josm.gui.layer.imagery.ImageryFilterSettings; 65 65 import org.openstreetmap.josm.gui.util.GuiHelper; 66 66 import org.openstreetmap.josm.gui.util.imagery.Vector3D; 67 import org.openstreetmap.josm.gui.widgets.HideableTabbedPane; 68 import org.openstreetmap.josm.spi.preferences.Config; 67 69 import org.openstreetmap.josm.tools.ImageProvider; 68 70 import org.openstreetmap.josm.tools.Logging; 69 71 import org.openstreetmap.josm.tools.PlatformManager; … … 73 75 /** 74 76 * Dialog to view and manipulate geo-tagged images from a {@link GeoImageLayer}. 75 77 */ 76 public final class ImageViewerDialog extends ToggleDialog implements LayerChangeListener, ActiveLayerChangeListener , ImageDataUpdateListener{78 public final class ImageViewerDialog extends ToggleDialog implements LayerChangeListener, ActiveLayerChangeListener { 77 79 private static final String GEOIMAGE_FILLER = marktr("Geoimage: {0}"); 78 80 private static final String DIALOG_FOLDER = "dialogs"; 79 81 … … 124 126 return dialog; 125 127 } 126 128 129 /** 130 * Check if there is an instance for the {@link ImageViewerDialog} 131 * @return {@code true} if there is a static singleton instance of {@link ImageViewerDialog} 132 * @since xxx 133 */ 134 public static boolean hasInstance() { 135 return dialog != null; 136 } 137 138 /** 139 * Destroy the current dialog 140 */ 141 private static void destroyInstance() { 142 dialog = null; 143 } 144 127 145 private JButton btnLast; 128 146 private JButton btnNext; 129 147 private JButton btnPrevious; … … 135 153 private JButton btnDeleteFromDisk; 136 154 private JToggleButton tbCentre; 137 155 /** The layer tab (used to select images when multiple layers provide images, makes for easy switching) */ 138 private JPanellayers;156 private HideableTabbedPane layers; 139 157 140 158 private ImageViewerDialog() { 141 159 super(tr("Geotagged Images"), "geoimage", tr("Display geotagged images"), Shortcut.registerShortcut("tools:geotagged", … … 168 186 169 187 private void build() { 170 188 JPanel content = new JPanel(new BorderLayout()); 171 this.layers = new JPanel(new GridBagLayout()); 172 content.add(layers, BorderLayout.NORTH); 173 174 content.add(imgDisplay, BorderLayout.CENTER); 189 this.layers = new HideableTabbedPane(); 190 content.add(layers, BorderLayout.CENTER); 175 191 176 192 Dimension buttonDim = new Dimension(26, 26); 177 193 … … 187 203 btnLast = createNavigationButton(imageLastAction, buttonDim); 188 204 189 205 tbCentre = new JToggleButton(imageCenterViewAction); 206 tbCentre.setSelected(Config.getPref().getBoolean("geoimage.viewer.centre.on.image", false)); 190 207 tbCentre.setPreferredSize(buttonDim); 191 208 192 209 JButton btnZoomBestFit = new JButton(imageZoomAction); … … 196 213 btnCollapse.setAlignmentY(Component.TOP_ALIGNMENT); 197 214 198 215 JPanel buttons = new JPanel(); 199 buttons.add(btnFirst); 200 buttons.add(btnPrevious); 201 buttons.add(btnNext); 202 buttons.add(btnLast); 203 buttons.add(Box.createRigidArea(new Dimension(7, 0))); 204 buttons.add(tbCentre); 205 buttons.add(btnZoomBestFit); 206 buttons.add(Box.createRigidArea(new Dimension(7, 0))); 207 buttons.add(btnDelete); 208 buttons.add(btnDeleteFromDisk); 209 buttons.add(Box.createRigidArea(new Dimension(7, 0))); 210 buttons.add(btnCopyPath); 211 buttons.add(btnOpenExternal); 212 buttons.add(Box.createRigidArea(new Dimension(7, 0))); 213 buttons.add(createButton(visibilityAction, buttonDim)); 216 addButtonGroup(buttons, this.btnFirst, this.btnPrevious, this.btnNext, this.btnLast); 217 addButtonGroup(buttons, this.tbCentre, btnZoomBestFit); 218 addButtonGroup(buttons, this.btnDelete, this.btnDeleteFromDisk); 219 addButtonGroup(buttons, this.btnCopyPath, this.btnOpenExternal); 220 addButtonGroup(buttons, createButton(visibilityAction, buttonDim)); 214 221 215 222 JPanel bottomPane = new JPanel(new GridBagLayout()); 216 223 GridBagConstraints gc = new GridBagConstraints(); … … 231 238 createLayout(content, false, null); 232 239 } 233 240 234 private void updateLayers() { 235 if (this.tabbedEntries.size() <= 1) { 241 /** 242 * Add a button group to a panel 243 * @param buttonPanel The panel holding the buttons 244 * @param buttons The button group to add 245 */ 246 private static void addButtonGroup(JPanel buttonPanel, AbstractButton... buttons) { 247 if (buttonPanel.getComponentCount() != 0) { 248 buttonPanel.add(Box.createRigidArea(new Dimension(7, 0))); 249 } 250 251 for (AbstractButton jButton : buttons) { 252 buttonPanel.add(jButton); 253 } 254 } 255 256 /** 257 * Update the tabs for the different image layers 258 * @param changed {@code true} if the tabs changed 259 */ 260 private void updateLayers(boolean changed) { 261 MainLayerManager layerManager = MainApplication.getLayerManager(); 262 List<IGeoImageLayer> geoImageLayers = layerManager.getLayers().stream() 263 .filter(IGeoImageLayer.class::isInstance).map(IGeoImageLayer.class::cast).collect(Collectors.toList()); 264 if (geoImageLayers.isEmpty()) { 236 265 this.layers.setVisible(false); 237 this.layers.removeAll();238 266 } else { 239 267 this.layers.setVisible(true); 240 // Remove all old components 241 this.layers.removeAll(); 242 MainLayerManager layerManager = MainApplication.getLayerManager(); 243 List<Layer> invalidLayers = this.tabbedEntries.keySet().stream().filter(layer -> !layerManager.containsLayer(layer)) 244 .collect(Collectors.toList()); 245 // `null` is for anything using the old methods, without telling us what layer it comes from. 246 invalidLayers.remove(null); 247 // We need to do multiple calls to avoid ConcurrentModificationExceptions 248 invalidLayers.forEach(this.tabbedEntries::remove); 249 addButtonsForImageLayers(); 268 if (changed) { 269 addButtonsForImageLayers(); 270 } 271 MoveImgDisplayPanel<?> selected = (MoveImgDisplayPanel<?>) this.layers.getSelectedComponent(); 272 if ((this.imgDisplay.getParent() == null || this.imgDisplay.getParent().getParent() == null) 273 && selected != null && selected.layer.containsImage(this.currentEntry)) { 274 selected.setVisible(selected.isVisible()); 275 } else if (selected != null && !selected.layer.containsImage(this.currentEntry)) { 276 this.getImageTabs().filter(m -> m.layer.containsImage(this.currentEntry)).mapToInt(this.layers::indexOfComponent).findFirst() 277 .ifPresent(this.layers::setSelectedIndex); 278 } 250 279 this.layers.invalidate(); 251 280 } 281 this.layers.getParent().invalidate(); 252 282 this.revalidate(); 253 283 } 254 284 … … 256 286 * Add the buttons for image layers 257 287 */ 258 288 private void addButtonsForImageLayers() { 259 final IImageEntry<?> current; 260 synchronized (this) { 261 current = this.currentEntry; 262 } 263 List<JButton> layerButtons = new ArrayList<>(this.tabbedEntries.size()); 264 if (this.tabbedEntries.containsKey(null)) { 265 List<IImageEntry<?>> nullEntries = this.tabbedEntries.get(null); 266 JButton layerButton = createImageLayerButton(null, nullEntries); 267 layerButtons.add(layerButton); 268 layerButton.setEnabled(!nullEntries.contains(current)); 289 List<MoveImgDisplayPanel<?>> alreadyAdded = this.getImageTabs().collect(Collectors.toList()); 290 List<Layer> availableLayers = MainApplication.getLayerManager().getLayers(); 291 for (IGeoImageLayer layer : availableLayers.stream() 292 .sorted(Comparator.comparingInt(entry -> /*reverse*/-availableLayers.indexOf(entry))) 293 .filter(IGeoImageLayer.class::isInstance).map(IGeoImageLayer.class::cast).collect(Collectors.toList())) { 294 final int index = availableLayers.size() - availableLayers.indexOf((Layer) layer); 295 final String label = (ExpertToggleAction.isExpert() ? "[" + index + "] " : "") + ((Layer) layer).getLabel(); 296 final Optional<MoveImgDisplayPanel<?>> originalPanel = alreadyAdded.stream() 297 .filter(m -> Objects.equals(m.layer, layer)).findFirst(); 298 if (originalPanel.isPresent()) { 299 int componentIndex = this.layers.indexOfComponent(originalPanel.get()); 300 this.layers.setTitleAt(componentIndex, label); 301 } else { 302 this.layers.addTab(label, new MoveImgDisplayPanel<>(this.imgDisplay, (Layer & IGeoImageLayer) layer)); 303 } 269 304 } 270 for (Map.Entry<Layer, List<IImageEntry<?>>> entry : 271 this.tabbedEntries.entrySet().stream().filter(entry -> entry.getKey() != null) 272 .sorted(Comparator.comparing(entry -> entry.getKey().getName())).collect(Collectors.toList())) { 273 JButton layerButton = createImageLayerButton(entry.getKey(), entry.getValue()); 274 layerButtons.add(layerButton); 275 layerButton.setEnabled(!entry.getValue().contains(current)); 305 this.getImageTabs().map(p -> p.layer).filter(layer -> !availableLayers.contains(layer)) 306 // We have to collect to a list prior to removal -- if we don't, then the stream may get a layer at index 0, remove that layer, 307 // and then get a layer at index 1, which was previously at index 2. 308 .collect(Collectors.toList()).forEach(this::removeImageTab); 309 } 310 311 /** 312 * Remove a tab for a layer from the {@link #layers} tab pane 313 * @param layer The layer to remove 314 */ 315 private void removeImageTab(Layer layer) { 316 // This must be reversed to avoid removing the wrong tab 317 for (int i = this.layers.getTabCount() - 1; i >= 0; i--) { 318 Component component = this.layers.getComponentAt(i); 319 if (component instanceof MoveImgDisplayPanel) { 320 MoveImgDisplayPanel<?> moveImgDisplayPanel = (MoveImgDisplayPanel<?>) component; 321 if (Objects.equals(layer, moveImgDisplayPanel.layer)) { 322 this.layers.removeTabAt(i); 323 this.layers.remove(moveImgDisplayPanel); 324 } 325 } 276 326 } 277 layerButtons.forEach(this.layers::add);278 327 } 279 328 280 329 /** 281 * Create a button for a specific layer and its entries 282 * 283 * @param layer The layer to switch to 284 * @param entries The entries to display 285 * @return The button to use to switch to the specified layer 330 * Get the {@link MoveImgDisplayPanel} objects in {@link #layers}. 331 * @return The individual panels 286 332 */ 287 private static JButton createImageLayerButton(Layer layer, List<IImageEntry<?>> entries) {288 final JButton layerButton = new JButton();289 layerButton.addActionListener(new ImageActionListener(layer, entries));290 layerButton.setText(layer != null ? layer.getLabel() : tr("Default"));291 return layerButton;333 private Stream<MoveImgDisplayPanel<?>> getImageTabs() { 334 return IntStream.range(0, this.layers.getTabCount()) 335 .mapToObj(this.layers::getComponentAt) 336 .filter(MoveImgDisplayPanel.class::isInstance) 337 .map(m -> (MoveImgDisplayPanel<?>) m); 292 338 } 293 339 294 340 @Override … … 309 355 imageZoomAction.destroy(); 310 356 cancelLoadingImage(); 311 357 super.destroy(); 312 d ialog = null;358 destroyInstance(); 313 359 } 314 360 315 361 /** … … 433 479 } 434 480 } 435 481 436 /**437 * A listener that is called to change the viewing layer438 */439 private static class ImageActionListener implements ActionListener {440 441 private final Layer layer;442 private final List<IImageEntry<?>> entries;443 444 ImageActionListener(Layer layer, List<IImageEntry<?>> entries) {445 this.layer = layer;446 this.entries = entries;447 }448 449 @Override450 public void actionPerformed(ActionEvent e) {451 ImageViewerDialog.getInstance().displayImages(this.layer, this.entries);452 }453 }454 455 482 private class ImageFirstAction extends ImageRememberAction { 456 483 ImageFirstAction() { 457 484 super(null, new ImageProvider(DIALOG_FOLDER, "first"), tr("First"), Shortcut.registerShortcut( … … 478 505 public void actionPerformed(ActionEvent e) { 479 506 final JToggleButton button = (JToggleButton) e.getSource(); 480 507 centerView = button.isEnabled() && button.isSelected(); 508 Config.getPref().putBoolean("geoimage.viewer.centre.on.image", centerView); 481 509 if (centerView && currentEntry != null && currentEntry.getPos() != null) { 482 510 MainApplication.getMap().mapView.zoomTo(currentEntry.getPos()); 483 511 } … … 618 646 } 619 647 } 620 648 649 /** 650 * A JPanel whose entire purpose is to display an image by (a) moving the imgDisplay arround and (b) setting the imgDisplay as a child 651 * for this panel. 652 */ 653 private static class MoveImgDisplayPanel<T extends Layer & IGeoImageLayer> extends JPanel { 654 private final T layer; 655 private final ImageDisplay imgDisplay; 656 MoveImgDisplayPanel(ImageDisplay imgDisplay, T layer) { 657 super(new BorderLayout()); 658 this.layer = layer; 659 this.imgDisplay = imgDisplay; 660 } 661 662 @Override 663 public void setVisible(boolean visible) { 664 super.setVisible(visible); 665 if (visible) { 666 if (!this.layer.getSelection().isEmpty() && !this.layer.getSelection().contains(ImageViewerDialog.getCurrentImage())) { 667 ImageViewerDialog.getInstance().displayImages(this.layer.getSelection()); 668 } 669 if (this.imgDisplay.getParent() != this) { 670 this.add(this.imgDisplay, BorderLayout.CENTER); 671 this.imgDisplay.invalidate(); 672 this.revalidate(); 673 } 674 } 675 } 676 } 677 621 678 /** 622 679 * Enables (or disables) the "Previous" button. 623 680 * @param value {@code true} to enable the button, {@code false} otherwise … … 651 708 return wasEnabled; 652 709 } 653 710 654 /** Used for tabbed panes */655 private final transient Map<Layer, List<IImageEntry<?>>> tabbedEntries = new HashMap<>();656 711 private transient IImageEntry<? extends IImageEntry<?>> currentEntry; 657 712 658 713 /** … … 679 734 * @param entries image entries 680 735 * @since 18246 681 736 */ 682 public void displayImages(List<IImageEntry<?>> entries) { 683 this.displayImages((Layer) null, entries); 684 } 685 686 /** 687 * Displays images for the given layer. 688 * @param layer The layer to use for the tab ui 689 * @param entries image entries 690 * @since 18591 691 */ 692 public void displayImages(Layer layer, List<IImageEntry<?>> entries) { 737 public void displayImages(List<? extends IImageEntry<?>> entries) { 693 738 boolean imageChanged; 694 739 IImageEntry<?> entry = entries != null && entries.size() == 1 ? entries.get(0) : null; 695 740 … … 710 755 } 711 756 } 712 757 713 if (entries == null || entries.isEmpty() || entries.stream().allMatch(Objects::isNull)) { 714 this.tabbedEntries.remove(layer); 758 759 final boolean updateRequired; 760 final List<IGeoImageLayer> imageLayers = MainApplication.getLayerManager().getLayers().stream() 761 .filter(IGeoImageLayer.class::isInstance).map(IGeoImageLayer.class::cast).collect(Collectors.toList()); 762 if (!Config.getPref().getBoolean("geoimage.viewer.show.tabs", true)) { 763 updateRequired = true; 764 // Clear the selected images in other geoimage layers 765 this.getImageTabs().map(m -> m.layer).filter(IGeoImageLayer.class::isInstance).map(IGeoImageLayer.class::cast) 766 .filter(l -> !Objects.equals(entries, l.getSelection())) 767 .forEach(IGeoImageLayer::clearSelection); 715 768 } else { 716 this.tabbedEntries.put(layer, entries);769 updateRequired = imageLayers.stream().anyMatch(l -> this.getImageTabs().map(m -> m.layer).noneMatch(l::equals)); 717 770 } 718 this.updateLayers( );771 this.updateLayers(updateRequired); 719 772 if (entry != null) { 720 773 this.updateButtonsNonNullEntry(entry, imageChanged); 721 } else if ( this.tabbedEntries.isEmpty()) {774 } else if (imageLayers.isEmpty()) { 722 775 this.updateButtonsNullEntry(entries); 723 776 return; 724 777 } else { 725 Map.Entry<Layer, List<IImageEntry<?>>> realEntry = 726 this.tabbedEntries.entrySet().stream().filter(mapEntry -> mapEntry.getValue().size() == 1).findFirst().orElse(null); 727 if (realEntry == null) { 778 IGeoImageLayer layer = imageLayers.stream().filter(l -> l.getSelection().size() == 1).findFirst().orElse(null); 779 if (layer == null) { 728 780 this.updateButtonsNullEntry(entries); 729 781 } else { 730 this.displayImages( realEntry.getKey(), realEntry.getValue());782 this.displayImages(layer.getSelection()); 731 783 } 732 784 return; 733 785 } … … 744 796 * Update buttons for null entry 745 797 * @param entries {@code true} if multiple images are selected 746 798 */ 747 private void updateButtonsNullEntry(List< IImageEntry<?>> entries) {799 private void updateButtonsNullEntry(List<? extends IImageEntry<?>> entries) { 748 800 boolean hasMultipleImages = entries != null && entries.size() > 1; 749 801 // if this method is called to reinitialize dialog content with a blank image, 750 802 // do not actually show the dialog again with a blank image if currently hidden (fix #10672) 751 setTitle(tr("Geotagged Images"));803 this.updateTitle(); 752 804 imgDisplay.setImage(null); 753 805 imgDisplay.setOsdText(""); 754 806 setNextEnabled(false); … … 787 839 btnCopyPath.setEnabled(true); 788 840 btnOpenExternal.setEnabled(true); 789 841 790 setTitle(tr("Geotagged Images") + (!entry.getDisplayName().isEmpty() ? " - " + entry.getDisplayName() : ""));842 this.updateTitle(); 791 843 StringBuilder osd = new StringBuilder(entry.getDisplayName()); 792 844 if (entry.getElevation() != null) { 793 845 osd.append(tr("\nAltitude: {0} m", Math.round(entry.getElevation()))); … … 818 870 imgDisplay.setOsdText(osd.toString()); 819 871 } 820 872 821 /** 822 * Displays images for the given layer. 823 * @param ignoredData the image data (unused, may be {@code null}) 824 * @param entries image entries 825 * @since 18246 (signature) 826 * @deprecated Use {@link #displayImages(List)} (The data param is no longer used) 827 */ 828 @Deprecated 829 public void displayImages(ImageData ignoredData, List<IImageEntry<?>> entries) { 830 this.displayImages(entries); 873 private void updateTitle() { 874 final IImageEntry<?> entry; 875 synchronized (this) { 876 entry = this.currentEntry; 877 } 878 String baseTitle = Optional.of(this.layers.getSelectedComponent()) 879 .filter(MoveImgDisplayPanel.class::isInstance).map(MoveImgDisplayPanel.class::cast) 880 .map(m -> m.layer).map(Layer::getLabel).orElse(tr("Geotagged Images")); 881 if (entry == null) { 882 this.setTitle(baseTitle); 883 } else { 884 this.setTitle(baseTitle + (!entry.getDisplayName().isEmpty() ? " - " + entry.getDisplayName() : "")); 885 } 831 886 } 832 887 833 private static boolean isLastImageSelected(List< IImageEntry<?>> data) {888 private static boolean isLastImageSelected(List<? extends IImageEntry<?>> data) { 834 889 return data.stream().anyMatch(image -> data.contains(image.getLastImage())); 835 890 } 836 891 837 private static boolean isFirstImageSelected(List< IImageEntry<?>> data) {892 private static boolean isFirstImageSelected(List<? extends IImageEntry<?>> data) { 838 893 return data.stream().anyMatch(image -> data.contains(image.getFirstImage())); 839 894 } 840 895 … … 857 912 if (btnCollapse != null) { 858 913 btnCollapse.setVisible(!isDocked); 859 914 } 860 this.updateLayers( );915 this.updateLayers(true); 861 916 } 862 917 863 918 /** … … 904 959 905 960 @Override 906 961 public void layerRemoving(LayerRemoveEvent e) { 907 if (e.getRemovedLayer() instanceof GeoImageLayer && this.currentEntry instanceof ImageEntry) { 908 ImageData removedData = ((GeoImageLayer) e.getRemovedLayer()).getImageData(); 909 if (removedData == ((ImageEntry) this.currentEntry).getDataSet()) { 910 displayImages(e.getRemovedLayer(), null); 911 } 912 removedData.removeImageDataUpdateListener(this); 962 if (e.getRemovedLayer() instanceof IGeoImageLayer && ((IGeoImageLayer) e.getRemovedLayer()).containsImage(this.currentEntry)) { 963 displayImages(null); 913 964 } 914 // Unfortunately, there will be no way to remove the default null layer. This will be fixed as plugins update. 915 this.tabbedEntries.remove(e.getRemovedLayer()); 965 this.updateLayers(true); 916 966 } 917 967 918 968 @Override 919 969 public void layerOrderChanged(LayerOrderChangeEvent e) { 920 // ignored970 this.updateLayers(true); 921 971 } 922 972 923 973 @Override … … 941 991 } 942 992 943 993 private void registerOnLayer(Layer layer) { 944 if (layer instanceof GeoImageLayer) { 945 ((GeoImageLayer) layer).getImageData().addImageDataUpdateListener(this); 994 if (layer instanceof IGeoImageLayer) { 995 layer.addPropertyChangeListener(l -> { 996 final List<?> currentTabLayers = this.getImageTabs().map(m -> m.layer).collect(Collectors.toList()); 997 if (Layer.NAME_PROP.equals(l.getPropertyName()) && currentTabLayers.contains(layer)) { 998 this.updateLayers(true); 999 if (((IGeoImageLayer) layer).containsImage(this.currentEntry)) { 1000 this.updateTitle(); 1001 } 1002 } else if (Layer.VISIBLE_PROP.equals(l.getPropertyName()) && currentTabLayers.contains(layer)) { 1003 this.getImageTabs().filter(m -> Objects.equals(m.layer, layer)).mapToInt(this.layers::indexOfComponent) 1004 .filter(i -> i >= 0).forEach(i -> this.layers.setEnabledAt(i, layer.isVisible())); 1005 } 1006 }); 946 1007 } 947 1008 } 948 1009 … … 959 1020 imgLoadingFuture = null; 960 1021 } 961 1022 } 962 963 @Override964 public void selectedImageChanged(ImageData data) {965 if (this.currentEntry != data.getSelectedImage() && this.currentEntry instanceof ImageEntry &&966 !data.getSelectedImages().contains(this.currentEntry)) {967 displayImages(data.getLayer(), new ArrayList<>(data.getSelectedImages()));968 }969 }970 971 @Override972 public void imageDataUpdated(ImageData data) {973 displayImages(data.getLayer(), new ArrayList<>(data.getSelectedImages()));974 }975 1023 } -
src/org/openstreetmap/josm/gui/layer/geoimage/RemoteEntry.java
IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 diff --git a/src/org/openstreetmap/josm/gui/layer/geoimage/RemoteEntry.java b/src/org/openstreetmap/josm/gui/layer/geoimage/RemoteEntry.java
a b 19 19 import org.openstreetmap.josm.data.coor.ILatLon; 20 20 import org.openstreetmap.josm.data.imagery.street_level.IImageEntry; 21 21 import org.openstreetmap.josm.data.imagery.street_level.Projections; 22 import org.openstreetmap.josm.gui.layer.Layer;23 22 import org.openstreetmap.josm.tools.HttpClient; 24 23 import org.openstreetmap.josm.tools.JosmRuntimeException; 25 24 … … 33 32 private final Supplier<RemoteEntry> nextImage; 34 33 private final Supplier<RemoteEntry> previousImage; 35 34 private final Supplier<RemoteEntry> lastImage; 36 private final Layer layer;37 35 private int width; 38 36 private int height; 39 37 private ILatLon pos; … … 54 52 55 53 /** 56 54 * Create a new remote entry 57 * @param layer The originating layer, used for tabs in the image viewer58 55 * @param uri The URI to use 59 56 * @param firstImage first image supplier 60 57 * @param nextImage next image supplier 61 58 * @param lastImage last image supplier 62 59 * @param previousImage previous image supplier 63 60 */ 64 public RemoteEntry( Layer layer,URI uri, Supplier<RemoteEntry> firstImage, Supplier<RemoteEntry> previousImage,61 public RemoteEntry(URI uri, Supplier<RemoteEntry> firstImage, Supplier<RemoteEntry> previousImage, 65 62 Supplier<RemoteEntry> nextImage, Supplier<RemoteEntry> lastImage) { 66 63 Objects.requireNonNull(uri); 67 64 Objects.requireNonNull(firstImage); … … 73 70 this.previousImage = previousImage; 74 71 this.nextImage = nextImage; 75 72 this.lastImage = lastImage; 76 this.layer = layer;77 73 } 78 74 79 75 @Override … … 319 315 320 316 @Override 321 317 public void selectImage(ImageViewerDialog imageViewerDialog, IImageEntry<?> entry) { 322 imageViewerDialog.displayImages( this.layer,Collections.singletonList(entry));318 imageViewerDialog.displayImages(Collections.singletonList(entry)); 323 319 } 324 320 325 321 @Override -
src/org/openstreetmap/josm/gui/layer/markerlayer/ImageMarker.java
IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 diff --git a/src/org/openstreetmap/josm/gui/layer/markerlayer/ImageMarker.java b/src/org/openstreetmap/josm/gui/layer/markerlayer/ImageMarker.java
a b 4 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 5 6 6 import java.awt.event.ActionEvent; 7 import java.net.URI; 7 8 import java.net.URISyntaxException; 8 9 import java.net.URL; 9 10 import java.time.Instant; … … 16 17 import org.openstreetmap.josm.data.gpx.GpxConstants; 17 18 import org.openstreetmap.josm.data.gpx.GpxLink; 18 19 import org.openstreetmap.josm.data.gpx.WayPoint; 20 import org.openstreetmap.josm.data.imagery.street_level.IImageEntry; 19 21 import org.openstreetmap.josm.gui.Notification; 20 22 import org.openstreetmap.josm.gui.layer.geoimage.ImageViewerDialog; 21 23 import org.openstreetmap.josm.gui.layer.geoimage.RemoteEntry; … … 40 42 41 43 @Override 42 44 public void actionPerformed(ActionEvent ev) { 43 ImageViewerDialog.getInstance().displayImages(this.parentLayer, Collections.singletonList(getRemoteEntry())); 45 this.parentLayer.setCurrentMarker(this); 46 ImageViewerDialog.getInstance().displayImages(Collections.singletonList(getRemoteEntry())); 44 47 } 45 48 46 privateRemoteEntry getRemoteEntry() {49 RemoteEntry getRemoteEntry() { 47 50 try { 48 final RemoteEntry remoteEntry = new RemoteEntry(this.parentLayer,imageUrl.toURI(), getFirstImage(), getPreviousImage(),51 final RemoteEntry remoteEntry = new MarkerRemoteEntry(imageUrl.toURI(), getFirstImage(), getPreviousImage(), 49 52 getNextImage(), getLastImage()); 50 53 // First, extract EXIF data 51 54 remoteEntry.extractExif(); … … 128 131 wpt.put(GpxConstants.META_LINKS, Collections.singleton(link)); 129 132 return wpt; 130 133 } 134 135 private class MarkerRemoteEntry extends RemoteEntry { 136 /** 137 * Create a new remote entry 138 * 139 * @param uri The URI to use 140 * @param firstImage first image supplier 141 * @param previousImage previous image supplier 142 * @param nextImage next image supplier 143 * @param lastImage last image supplier 144 */ 145 public MarkerRemoteEntry(URI uri, Supplier<RemoteEntry> firstImage, Supplier<RemoteEntry> previousImage, 146 Supplier<RemoteEntry> nextImage, Supplier<RemoteEntry> lastImage) { 147 super(uri, firstImage, previousImage, nextImage, lastImage); 148 } 149 150 @Override 151 public void selectImage(ImageViewerDialog imageViewerDialog, IImageEntry<?> entry) { 152 ImageMarker.this.parentLayer.setCurrentMarker(ImageMarker.this); 153 super.selectImage(imageViewerDialog, entry); 154 } 155 } 131 156 } -
src/org/openstreetmap/josm/gui/layer/markerlayer/Marker.java
IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 diff --git a/src/org/openstreetmap/josm/gui/layer/markerlayer/Marker.java b/src/org/openstreetmap/josm/gui/layer/markerlayer/Marker.java
a b 20 20 21 21 import javax.swing.ImageIcon; 22 22 23 import org.openstreetmap.josm.data.IQuadBucketType; 23 24 import org.openstreetmap.josm.data.Preferences; 24 25 import org.openstreetmap.josm.data.coor.CachedLatLon; 25 26 import org.openstreetmap.josm.data.coor.EastNorth; … … 27 28 import org.openstreetmap.josm.data.coor.LatLon; 28 29 import org.openstreetmap.josm.data.gpx.GpxConstants; 29 30 import org.openstreetmap.josm.data.gpx.WayPoint; 31 import org.openstreetmap.josm.data.osm.BBox; 30 32 import org.openstreetmap.josm.data.osm.search.SearchCompiler.Match; 31 33 import org.openstreetmap.josm.gui.MapView; 32 34 import org.openstreetmap.josm.gui.layer.GpxLayer; … … 75 77 * 76 78 * @author Frederik Ramm 77 79 */ 78 public class Marker implements TemplateEngineDataProvider, ILatLon, Destroyable {80 public class Marker implements TemplateEngineDataProvider, ILatLon, Destroyable, IQuadBucketType { 79 81 80 82 /** 81 83 * Plugins can add their Marker creation stuff at the bottom or top of this list … … 447 449 private String getPreferenceKey() { 448 450 return "draw.rawgps." + getTextTemplateKey(); 449 451 } 452 453 @Override 454 public BBox getBBox() { 455 return new BBox(this); 456 } 450 457 } -
src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java
IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 diff --git a/src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java b/src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java
a b 19 19 import java.net.URISyntaxException; 20 20 import java.util.ArrayList; 21 21 import java.util.Collection; 22 import java.util.Collections; 22 23 import java.util.Comparator; 23 24 import java.util.HashMap; 24 25 import java.util.List; 26 import java.util.ListIterator; 25 27 import java.util.Map; 28 import java.util.Objects; 26 29 import java.util.Optional; 27 30 28 31 import javax.swing.AbstractAction; … … 41 44 import org.openstreetmap.josm.data.gpx.GpxLink; 42 45 import org.openstreetmap.josm.data.gpx.IGpxLayerPrefs; 43 46 import org.openstreetmap.josm.data.gpx.WayPoint; 47 import org.openstreetmap.josm.data.imagery.street_level.IImageEntry; 48 import org.openstreetmap.josm.data.osm.BBox; 49 import org.openstreetmap.josm.data.osm.QuadBuckets; 44 50 import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor; 45 51 import org.openstreetmap.josm.data.preferences.IntegerProperty; 46 52 import org.openstreetmap.josm.data.preferences.NamedColorProperty; … … 55 61 import org.openstreetmap.josm.gui.layer.JumpToMarkerActions.JumpToNextMarker; 56 62 import org.openstreetmap.josm.gui.layer.JumpToMarkerActions.JumpToPreviousMarker; 57 63 import org.openstreetmap.josm.gui.layer.Layer; 64 import org.openstreetmap.josm.gui.layer.geoimage.IGeoImageLayer; 65 import org.openstreetmap.josm.gui.layer.geoimage.RemoteEntry; 58 66 import org.openstreetmap.josm.gui.layer.gpx.ConvertFromMarkerLayerAction; 59 67 import org.openstreetmap.josm.gui.preferences.display.GPXSettingsPanel; 60 68 import org.openstreetmap.josm.io.audio.AudioPlayer; 61 69 import org.openstreetmap.josm.spi.preferences.Config; 62 70 import org.openstreetmap.josm.tools.ColorHelper; 63 71 import org.openstreetmap.josm.tools.ImageProvider; 72 import org.openstreetmap.josm.tools.ListenerList; 64 73 import org.openstreetmap.josm.tools.Logging; 65 74 import org.openstreetmap.josm.tools.Utils; 66 75 … … 75 84 * 76 85 * The data is read only. 77 86 */ 78 public class MarkerLayer extends Layer implements JumpToMarkerLayer {87 public class MarkerLayer extends Layer implements JumpToMarkerLayer, IGeoImageLayer { 79 88 80 89 /** 81 90 * A list of markers. … … 89 98 final int markerSize = new IntegerProperty("draw.rawgps.markers.size", 4).get(); 90 99 final BasicStroke markerStroke = new StrokeProperty("draw.rawgps.markers.stroke", "1").get(); 91 100 101 private final ListenerList<IGeoImageLayer.ImageChangeListener> imageChangeListenerListenerList = ListenerList.create(); 102 92 103 /** 93 104 * The default color that is used for drawing markers. 94 105 */ … … 401 412 MainApplication.getMap().mapView.zoomTo(currentMarker); 402 413 } 403 414 415 /** 416 * Set the current marker 417 * @param newMarker The marker to set 418 */ 419 void setCurrentMarker(Marker newMarker) { 420 this.currentMarker = newMarker; 421 } 422 404 423 public static void playAudio() { 405 424 playAdjacentMarker(null, true); 406 425 } … … 495 514 this.realcolor = Optional.ofNullable(color).orElse(DEFAULT_COLOR_PROPERTY.get()); 496 515 } 497 516 517 @Override 518 public void clearSelection() { 519 this.currentMarker = null; 520 } 521 522 @Override 523 public List<? extends IImageEntry<?>> getSelection() { 524 if (this.currentMarker instanceof ImageMarker) { 525 return Collections.singletonList(((ImageMarker) this.currentMarker).getRemoteEntry()); 526 } 527 return Collections.emptyList(); 528 } 529 530 @Override 531 public boolean containsImage(IImageEntry<?> imageEntry) { 532 if (imageEntry instanceof RemoteEntry) { 533 RemoteEntry entry = (RemoteEntry) imageEntry; 534 if (entry.getPos() != null && entry.getPos().isLatLonKnown()) { 535 List<Marker> markers = this.data.search(new BBox(entry.getPos())); 536 return checkIfListContainsEntry(markers, entry); 537 } else if (entry.getExifCoor() != null && entry.getExifCoor().isLatLonKnown()) { 538 List<Marker> markers = this.data.search(new BBox(entry.getExifCoor())); 539 return checkIfListContainsEntry(markers, entry); 540 } else { 541 return checkIfListContainsEntry(this.data, entry); 542 } 543 } 544 return false; 545 } 546 547 /** 548 * Check if a list contains an entry 549 * @param markerList The list to look through 550 * @param imageEntry The image entry to check 551 * @return {@code true} if the entry is in the list 552 */ 553 private static boolean checkIfListContainsEntry(List<Marker> markerList, RemoteEntry imageEntry) { 554 for (Marker marker : markerList) { 555 if (marker instanceof ImageMarker) { 556 ImageMarker imageMarker = (ImageMarker) marker; 557 try { 558 if (Objects.equals(imageMarker.imageUrl.toURI(), imageEntry.getImageURI())) { 559 return true; 560 } 561 } catch (URISyntaxException e) { 562 Logging.trace(e); 563 } 564 } 565 } 566 return false; 567 } 568 569 @Override 570 public void addImageChangeListener(ImageChangeListener listener) { 571 this.imageChangeListenerListenerList.addListener(listener); 572 } 573 574 @Override 575 public void removeImageChangeListener(ImageChangeListener listener) { 576 this.imageChangeListenerListenerList.removeListener(listener); 577 } 578 498 579 private final class MarkerMouseAdapter extends MouseAdapter { 499 580 @Override 500 581 public void mousePressed(MouseEvent e) { … … 627 708 * the data of a MarkerLayer 628 709 * @since 18287 629 710 */ 630 public class MarkerData extends ArrayList<Marker> implementsIGpxLayerPrefs {711 public class MarkerData extends QuadBuckets<Marker> implements List<Marker>, IGpxLayerPrefs { 631 712 632 713 private Map<String, String> ownLayerPrefs; 714 private final List<Marker> markerList = new ArrayList<>(); 633 715 634 716 @Override 635 717 public Map<String, String> getLayerPrefs() { … … 658 740 fromLayer.data.setModified(value); 659 741 } 660 742 } 743 744 @Override 745 public boolean addAll(int index, Collection<? extends Marker> c) { 746 c.forEach(this::add); 747 return this.markerList.addAll(index, c); 748 } 749 750 @Override 751 public boolean addAll(Collection<? extends Marker> objects) { 752 return this.markerList.addAll(objects) && super.addAll(objects); 753 } 754 755 @Override 756 public Marker get(int index) { 757 return this.markerList.get(index); 758 } 759 760 @Override 761 public Marker set(int index, Marker element) { 762 Marker original = this.markerList.set(index, element); 763 this.remove(original); 764 return original; 765 } 766 767 @Override 768 public void add(int index, Marker element) { 769 this.add(element); 770 this.markerList.add(index, element); 771 } 772 773 @Override 774 public Marker remove(int index) { 775 Marker toRemove = this.markerList.remove(index); 776 this.remove(toRemove); 777 return toRemove; 778 } 779 780 @Override 781 public int indexOf(Object o) { 782 return this.markerList.indexOf(o); 783 } 784 785 @Override 786 public int lastIndexOf(Object o) { 787 return this.markerList.lastIndexOf(o); 788 } 789 790 @Override 791 public ListIterator<Marker> listIterator() { 792 return this.markerList.listIterator(); 793 } 794 795 @Override 796 public ListIterator<Marker> listIterator(int index) { 797 return this.markerList.listIterator(index); 798 } 799 800 @Override 801 public List<Marker> subList(int fromIndex, int toIndex) { 802 return this.markerList.subList(fromIndex, toIndex); 803 } 804 805 @Override 806 public boolean retainAll(Collection<?> objects) { 807 return this.markerList.retainAll(objects) && super.retainAll(objects); 808 } 809 810 @Override 811 public boolean contains(Object o) { 812 return this.markerList.contains(o) && super.contains(o); 813 } 661 814 } 662 815 }
