Skip to content

Commit e9cced1

Browse files
author
taylor.smock
committed
Fix #21605: Add tabs to ImageViewerDialog for use with different image layers
This allows users to have multiple geotagged image layers, and quickly switch between them. This is a complete rework of the functionality introduced in r18591 after user feedback. Changes: * Tabs are now scrollable * Tabs now have a close button * Removes the functions where plugins could send a layer in (plugins should use IGeoImageLayer instead) * Tabs are sorted (by layer order) * GeoImageLayers will use a darker red for selected but not viewed images * Tabs are only opened when the user clicks on a geoimage from a different layer * GpxMarkers now implement IQuadBucketType. This was to speed up the `containsImage` method in MarkerLayer. git-svn-id: https://josm.openstreetmap.de/svn/trunk@18613 0c6e7542-c601-0410-84e7-c038aed88b3b
1 parent 816bcb3 commit e9cced1

File tree

8 files changed

+626
-162
lines changed

8 files changed

+626
-162
lines changed

src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333

3434
import javax.swing.Action;
3535
import javax.swing.Icon;
36+
import javax.swing.ImageIcon;
3637

3738
import org.openstreetmap.josm.actions.AutoScaleAction;
3839
import org.openstreetmap.josm.actions.ExpertToggleAction;
@@ -47,7 +48,9 @@
4748
import org.openstreetmap.josm.data.gpx.GpxData;
4849
import org.openstreetmap.josm.data.gpx.GpxImageEntry;
4950
import org.openstreetmap.josm.data.gpx.GpxTrack;
51+
import org.openstreetmap.josm.data.imagery.street_level.IImageEntry;
5052
import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
53+
import org.openstreetmap.josm.data.preferences.NamedColorProperty;
5154
import org.openstreetmap.josm.gui.MainApplication;
5255
import org.openstreetmap.josm.gui.MapFrame;
5356
import org.openstreetmap.josm.gui.MapFrame.MapModeChangeListener;
@@ -62,22 +65,26 @@
6265
import org.openstreetmap.josm.gui.layer.JumpToMarkerActions.JumpToPreviousMarker;
6366
import org.openstreetmap.josm.gui.layer.Layer;
6467
import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
68+
import org.openstreetmap.josm.gui.util.GuiHelper;
6569
import org.openstreetmap.josm.gui.util.imagery.Vector3D;
6670
import org.openstreetmap.josm.tools.ImageProvider;
71+
import org.openstreetmap.josm.tools.ListenerList;
6772
import org.openstreetmap.josm.tools.Utils;
6873

6974
/**
7075
* Layer displaying geotagged pictures.
7176
* @since 99
7277
*/
7378
public class GeoImageLayer extends AbstractModifiableLayer implements
74-
JumpToMarkerLayer, NavigatableComponent.ZoomChangeListener, ImageDataUpdateListener {
79+
JumpToMarkerLayer, NavigatableComponent.ZoomChangeListener, ImageDataUpdateListener,
80+
IGeoImageLayer {
7581

7682
private static final List<Action> menuAdditions = new LinkedList<>();
7783

7884
private static volatile List<MapMode> supportedMapModes;
7985

8086
private final ImageData data;
87+
private final ListenerList<IGeoImageLayer.ImageChangeListener> imageChangeListeners = ListenerList.create();
8188
GpxData gpxData;
8289
GpxLayer gpxFauxLayer;
8390
GpxData gpxFauxData;
@@ -86,6 +93,19 @@ public class GeoImageLayer extends AbstractModifiableLayer implements
8693

8794
private final Icon icon = ImageProvider.get("dialogs/geoimage/photo-marker");
8895
private final Icon selectedIcon = ImageProvider.get("dialogs/geoimage/photo-marker-selected");
96+
private final Icon selectedIconNotImageViewer = generateSelectedIconNotImageViewer(this.selectedIcon);
97+
98+
private static Icon generateSelectedIconNotImageViewer(Icon selectedIcon) {
99+
Color color = new NamedColorProperty("geoimage.selected.not.image.viewer", new Color(50, 0, 0)).get();
100+
BufferedImage bi = new BufferedImage(selectedIcon.getIconWidth(), selectedIcon.getIconHeight(), BufferedImage.TYPE_INT_ARGB);
101+
Graphics2D g2d = bi.createGraphics();
102+
selectedIcon.paintIcon(null, g2d, 0, 0);
103+
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.5f));
104+
g2d.setColor(color);
105+
g2d.fillRect(0, 0, selectedIcon.getIconWidth(), selectedIcon.getIconHeight());
106+
g2d.dispose();
107+
return new ImageIcon(bi);
108+
}
89109

90110
boolean useThumbs;
91111
private final ExecutorService thumbsLoaderExecutor =
@@ -172,6 +192,14 @@ public GeoImageLayer(final List<ImageEntry> data, GpxData gpxData, final String
172192
this.useThumbs = useThumbs;
173193
this.data.addImageDataUpdateListener(this);
174194
this.data.setLayer(this);
195+
synchronized (ImageViewerDialog.class) {
196+
if (!ImageViewerDialog.hasInstance()) {
197+
GuiHelper.runInEDTAndWait(ImageViewerDialog::createInstance);
198+
}
199+
}
200+
if (getInvalidGeoImages().size() == data.size()) {
201+
ImageViewerDialog.getInstance().displayImages(Collections.singletonList(this.data.getFirstImage()));
202+
}
175203
}
176204

177205
private final class ImageMouseListener extends MouseAdapter {
@@ -232,8 +260,9 @@ public void mouseReleased(MouseEvent ev) {
232260
}
233261
} else {
234262
data.setSelectedImage(img);
235-
ImageViewerDialog.getInstance().displayImages(GeoImageLayer.this, Collections.singletonList(img));
263+
ImageViewerDialog.getInstance().displayImages(Collections.singletonList(img));
236264
}
265+
GeoImageLayer.this.invalidate(); // Needed to update which image is being shown in the image viewer in the mapview
237266
}
238267
}
239268
}
@@ -247,11 +276,45 @@ public static void create(Collection<File> files, GpxLayer gpxLayer) {
247276
MainApplication.worker.execute(new ImagesLoader(files, gpxLayer));
248277
}
249278

279+
@Override
280+
public void clearSelection() {
281+
this.getImageData().clearSelectedImage();
282+
}
283+
284+
@Override
285+
public boolean containsImage(IImageEntry<?> imageEntry) {
286+
if (imageEntry instanceof ImageEntry) {
287+
return this.data.getImages().contains(imageEntry);
288+
}
289+
return false;
290+
}
291+
250292
@Override
251293
public Icon getIcon() {
252294
return ImageProvider.get("dialogs/geoimage", ImageProvider.ImageSizes.LAYER);
253295
}
254296

297+
@Override
298+
public List<ImageEntry> getSelection() {
299+
return this.getImageData().getSelectedImages();
300+
}
301+
302+
@Override
303+
public List<IImageEntry<?>> getInvalidGeoImages() {
304+
return this.getImageData().getImages().stream().filter(entry -> entry.getPos() == null || entry.getExifCoor() == null
305+
|| !entry.getExifCoor().isValid() || !entry.getPos().isValid()).collect(toList());
306+
}
307+
308+
@Override
309+
public void addImageChangeListener(ImageChangeListener listener) {
310+
this.imageChangeListeners.addListener(listener);
311+
}
312+
313+
@Override
314+
public void removeImageChangeListener(ImageChangeListener listener) {
315+
this.imageChangeListeners.removeListener(listener);
316+
}
317+
255318
/**
256319
* Register actions on the layer
257320
* @param addition the action to be added
@@ -451,6 +514,7 @@ public void paint(Graphics2D g, MapView mv, Bounds bounds) {
451514
}
452515
}
453516

517+
final IImageEntry<?> currentImage = ImageViewerDialog.getCurrentImage();
454518
for (ImageEntry e: data.getSelectedImages()) {
455519
if (e != null && e.getPos() != null) {
456520
Point p = mv.getPoint(e.getPos());
@@ -465,10 +529,14 @@ public void paint(Graphics2D g, MapView mv, Bounds bounds) {
465529
if (useThumbs && e.hasThumbnail()) {
466530
g.setColor(new Color(128, 0, 0, 122));
467531
g.fillRect(p.x - imgDim.width / 2, p.y - imgDim.height / 2, imgDim.width, imgDim.height);
468-
} else {
532+
} else if (e.equals(currentImage)) {
469533
selectedIcon.paintIcon(mv, g,
470534
p.x - imgDim.width / 2,
471535
p.y - imgDim.height / 2);
536+
} else {
537+
selectedIconNotImageViewer.paintIcon(mv, g,
538+
p.x - imgDim.width / 2,
539+
p.y - imgDim.height / 2);
472540
}
473541
}
474542
}
@@ -884,6 +952,7 @@ public void setUseThumbs(boolean useThumbs) {
884952
@Override
885953
public void selectedImageChanged(ImageData data) {
886954
showCurrentPhoto();
955+
this.imageChangeListeners.fireEvent(e -> e.imageChanged(this, null, data.getSelectedImages()));
887956
}
888957

889958
@Override
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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 18613
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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ public void selectImage(ImageViewerDialog imageViewerDialog, IImageEntry<?> entr
147147
if (entry instanceof ImageEntry) {
148148
this.dataSet.setSelectedImage((ImageEntry) entry);
149149
}
150-
imageViewerDialog.displayImages(this.dataSet.getLayer(), Collections.singletonList(entry));
150+
imageViewerDialog.displayImages(Collections.singletonList(entry));
151151
}
152152

153153
@Override

0 commit comments

Comments
 (0)