Index: applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryLayer.java
===================================================================
--- applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryLayer.java	(revision 31297)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryLayer.java	(revision 31298)
@@ -2,4 +2,5 @@
 
 import static org.openstreetmap.josm.tools.I18n.tr;
+import static org.openstreetmap.josm.tools.I18n.marktr;
 
 import org.apache.commons.jcs.access.CacheAccess;
@@ -34,10 +35,15 @@
 import org.openstreetmap.josm.data.osm.event.DataSetListener;
 
+import java.awt.AlphaComposite;
 import java.awt.Color;
+import java.awt.Composite;
 import java.awt.Graphics2D;
 import java.awt.Image;
 import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.TexturePaint;
 import java.awt.event.MouseAdapter;
 import java.awt.geom.AffineTransform;
+import java.awt.geom.Area;
 import java.awt.image.AffineTransformOp;
 import java.awt.image.BufferedImage;
@@ -53,455 +59,512 @@
 
 public class MapillaryLayer extends AbstractModifiableLayer implements
-        DataSetListener, EditLayerChangeListener, LayerChangeListener {
-
-    public final static int SEQUENCE_MAX_JUMP_DISTANCE = Main.pref.getInteger(
-            "mapillary.sequence-max-jump-distance", 100);
-
-    public static MapillaryLayer INSTANCE;
-    public static CacheAccess<String, BufferedImageCacheEntry> CACHE;
-    public static MapillaryImage BLUE;
-    public static MapillaryImage RED;
-
-    private final MapillaryData data = MapillaryData.getInstance();
-
-    public List<Bounds> bounds;
-
-
-    private MouseAdapter mouseAdapter;
-
-    private int highlightPointRadius = Main.pref.getInteger(
-            "mappaint.highlight.radius", 7);
-    private int highlightStep = Main.pref.getInteger("mappaint.highlight.step",
-            4);
-
-    public MapillaryLayer() {
-        super(tr("Mapillary Images"));
-        bounds = new ArrayList<>();
-        init();
-    }
-
-    /**
-     * Initializes the Layer.
-     */
-    private void init() {
-        MapillaryLayer.INSTANCE = this;
-        startMouseAdapter();
-        try {
-            CACHE = JCSCacheManager.getCache("Mapillary");
-        } catch (IOException e) {
-            Main.error(e);
-        }
-        if (Main.map != null && Main.map.mapView != null) {
-            Main.map.mapView.addMouseListener(mouseAdapter);
-            Main.map.mapView.addMouseMotionListener(mouseAdapter);
-            Main.map.mapView.addLayer(this);
-            MapView.addEditLayerChangeListener(this, false);
-            MapView.addLayerChangeListener(this);
-            Main.map.mapView.getEditLayer().data.addDataSetListener(this);
-        }
-        MapillaryPlugin.setMenuEnabled(MapillaryPlugin.EXPORT_MENU, true);
-        MapillaryPlugin.setMenuEnabled(MapillaryPlugin.SIGN_MENU, true);
-        Main.map.mapView.setActiveLayer(this);
-        Main.map.repaint();
-    }
-
-    private void startMouseAdapter() {
-        mouseAdapter = new MapillaryMouseAdapter();
-    }
-
-    public synchronized static MapillaryLayer getInstance() {
-        if (MapillaryLayer.INSTANCE == null)
-            MapillaryLayer.INSTANCE = new MapillaryLayer();
-        return MapillaryLayer.INSTANCE;
-    }
-
-    /**
-     * Downloads all images of the area covered by the OSM data. This is only
-     * just for automatic download.
-     */
-    public void download() {
-        checkBigAreas();
-        if (Main.pref.getBoolean("mapillary.download-manually"))
-            return;
-        for (Bounds bounds : Main.map.mapView.getEditLayer().data
-                .getDataSourceBounds()) {
-            if (!this.bounds.contains(bounds)) {
-                this.bounds.add(bounds);
-                new MapillaryDownloader().getImages(bounds.getMin(),
-                        bounds.getMax());
-            }
-        }
-    }
-
-    /**
-     * Checks if the area of the OSM data is too big. This means that probably
-     * lots of Mapillary images are going to be downloaded, slowing down the
-     * program too much. To solve this the automatic is stopped, an alert is
-     * shown and you will have to download areas manually.
-     */
-    private void checkBigAreas() {
-        double area = 0;
-        for (Bounds bounds : Main.map.mapView.getEditLayer().data
-                .getDataSourceBounds()) {
-            area += bounds.getArea();
-        }
-        if (area > MapillaryDownloadViewAction.MAX_AREA) {
-            Main.pref.put("mapillary.download-manually", true);
-            JOptionPane
-                    .showMessageDialog(
-                            Main.parent,
-                            tr("The downloaded OSM area is too big. Download mode has been change to manual. You can change this back to automatic in preferences settings."));
-        }
-    }
-
-    /**
-     * Returns the MapillaryData object, which acts as the database of the
-     * Layer.
-     * 
-     * @return
-     */
-    public MapillaryData getMapillaryData() {
-        return data;
-    }
-
-    /**
-     * Method invoked when the layer is destroyed.
-     */
-    @Override
-    public void destroy() {
-        MapillaryToggleDialog.getInstance().mapillaryImageDisplay
-                .setImage(null);
-        data.getImages().clear();
-        MapillaryLayer.INSTANCE = null;
-        MapillaryData.INSTANCE = null;
-        MapillaryPlugin.setMenuEnabled(MapillaryPlugin.EXPORT_MENU, false);
-        MapillaryPlugin.setMenuEnabled(MapillaryPlugin.SIGN_MENU, false);
-        MapillaryPlugin.setMenuEnabled(MapillaryPlugin.ZOOM_MENU, false);
-        Main.map.mapView.removeMouseListener(mouseAdapter);
-        Main.map.mapView.removeMouseMotionListener(mouseAdapter);
-        MapView.removeEditLayerChangeListener(this);
-        if (Main.map.mapView.getEditLayer() != null)
-            Main.map.mapView.getEditLayer().data.removeDataSetListener(this);
-        super.destroy();
-    }
-
-    /**
-     * Returns true any of the images from the database has been modified.
-     */
-    @Override
-    public boolean isModified() {
-        for (MapillaryAbstractImage image : data.getImages())
-            if (image.isModified())
-                return true;
-        return false;
-    }
-    
-    @Override
-    public void setVisible(boolean visible) {
-        super.setVisible(visible);
-        for (MapillaryAbstractImage img : data.getImages())
-            img.setVisible(visible);
-        MapillaryFilterDialog.getInstance().refresh();
-    }
-
-    /**
-     * Paints the database in the map.
-     */
-    @Override
-    public void paint(Graphics2D g, MapView mv, Bounds box) {
-        synchronized (this) {
-            // Draw colored lines
-            MapillaryLayer.BLUE = null;
-            MapillaryLayer.RED = null;
-            MapillaryToggleDialog.getInstance().blueButton.setEnabled(false);
-            MapillaryToggleDialog.getInstance().redButton.setEnabled(false);
-            
-            // Sets blue and red lines and enables/disables the buttons
-            if (data.getSelectedImage() != null) {
-                MapillaryImage[] closestImages = getClosestImagesFromDifferentSequences();
-                Point selected = mv.getPoint(data.getSelectedImage()
-                        .getLatLon());
-                if (closestImages[0] != null) {
-                    MapillaryLayer.BLUE = closestImages[0];
-                    g.setColor(Color.BLUE);
-                    g.drawLine(mv.getPoint(closestImages[0].getLatLon()).x,
-                            mv.getPoint(closestImages[0].getLatLon()).y,
-                            selected.x, selected.y);
-                    MapillaryToggleDialog.getInstance().blueButton
-                            .setEnabled(true);
-                }
-                if (closestImages[1] != null) {
-                    MapillaryLayer.RED = closestImages[1];
-                    g.setColor(Color.RED);
-                    g.drawLine(mv.getPoint(closestImages[1].getLatLon()).x,
-                            mv.getPoint(closestImages[1].getLatLon()).y,
-                            selected.x, selected.y);
-                    MapillaryToggleDialog.getInstance().redButton
-                            .setEnabled(true);
-                }
-            }
-            g.setColor(Color.WHITE);
-            for (MapillaryAbstractImage imageAbs : data.getImages()) {
-                if (!imageAbs.isVisible())
-                    continue;
-                Point p = mv.getPoint(imageAbs.getLatLon());
-                if (imageAbs instanceof MapillaryImage) {
-                    MapillaryImage image = (MapillaryImage) imageAbs;
-                    Point nextp;
-                    // Draw sequence line
-                    if (image.getSequence() != null
-                            && image.next() != null && image.next().isVisible()) {
-                        nextp = mv.getPoint(image.getSequence().next(image)
-                                .getLatLon());
-                        g.drawLine(p.x, p.y, nextp.x, nextp.y);
-                    }
-
-                    ImageIcon icon;
-                    if (!data.getMultiSelectedImages().contains(image))
-                        icon = MapillaryPlugin.MAP_ICON;
-                    else
-                        icon = MapillaryPlugin.MAP_ICON_SELECTED;
-                    draw(g, image, icon, p);
-                    if (!image.getSigns().isEmpty()) {
-                        g.drawImage(MapillaryPlugin.MAP_SIGN.getImage(), p.x
-                                + icon.getIconWidth() / 2,
-                                p.y - icon.getIconHeight() / 2,
-                                Main.map.mapView);
-                    }
-                } else if (imageAbs instanceof MapillaryImportedImage) {
-                    MapillaryImportedImage image = (MapillaryImportedImage) imageAbs;
-                    ImageIcon icon;
-                    if (!data.getMultiSelectedImages().contains(image))
-                        icon = MapillaryPlugin.MAP_ICON_IMPORTED;
-                    else
-                        icon = MapillaryPlugin.MAP_ICON_SELECTED;
-                    draw(g, image, icon, p);
-                }
-            }
-        }
-    }
-
-    /**
-     * Draws the highlight of the icon.
-     * 
-     * @param g
-     * @param p
-     * @param size
-     */
-    private void drawPointHighlight(Graphics2D g, Point p, int size) {
-        Color oldColor = g.getColor();
-        Color highlightColor = PaintColors.HIGHLIGHT.get();
-        Color highlightColorTransparent = new Color(highlightColor.getRed(),
-                highlightColor.getGreen(), highlightColor.getBlue(), 100);
-        g.setColor(highlightColorTransparent);
-        int s = size + highlightPointRadius;
-        while (s >= size) {
-            int r = (int) Math.floor(s / 2d);
-            g.fillRoundRect(p.x - r, p.y - r, s, s, r, r);
-            s -= highlightStep;
-        }
-        g.setColor(oldColor);
-    }
-
-    /**
-     * Draws the given icon of an image. Also checks if the mouse is over the
-     * image.
-     * 
-     * @param g
-     * @param image
-     * @param icon
-     * @param p
-     */
-    private void draw(Graphics2D g, MapillaryAbstractImage image,
-            ImageIcon icon, Point p) {
-        Image imagetemp = icon.getImage();
-        BufferedImage bi = (BufferedImage) imagetemp;
-        int width = icon.getIconWidth();
-        int height = icon.getIconHeight();
-
-        // Rotate the image
-        double rotationRequired = Math.toRadians(image.getCa());
-        double locationX = width / 2;
-        double locationY = height / 2;
-        AffineTransform tx = AffineTransform.getRotateInstance(
-                rotationRequired, locationX, locationY);
-        AffineTransformOp op = new AffineTransformOp(tx,
-                AffineTransformOp.TYPE_BILINEAR);
-
-        g.drawImage(op.filter(bi, null), p.x - (width / 2), p.y - (height / 2),
-                Main.map.mapView);
-        if (data.getHoveredImage() == image) {
-            drawPointHighlight(g, p, 16);
-        }
-    }
-
-    @Override
-    public Icon getIcon() {
-        return MapillaryPlugin.ICON16;
-    }
-
-    @Override
-    public boolean isMergable(Layer other) {
-        return false;
-    }
-
-    @Override
-    public void mergeFrom(Layer from) {
-        throw new UnsupportedOperationException(
-                "This layer does not support merging yet");
-    }
-
-    @Override
-    public Action[] getMenuEntries() {
-        List<Action> actions = new ArrayList<>();
-        actions.add(LayerListDialog.getInstance().createShowHideLayerAction());
-        actions.add(LayerListDialog.getInstance().createDeleteLayerAction());
-        actions.add(new LayerListPopup.InfoAction(this));
-        return actions.toArray(new Action[actions.size()]);
-    }
-
-    /**
-     * Returns the 2 closest images belonging to a different sequence.
-     * 
-     * @return
-     */
-    private MapillaryImage[] getClosestImagesFromDifferentSequences() {
-        if (!(data.getSelectedImage() instanceof MapillaryImage))
-            return new MapillaryImage[2];
-        MapillaryImage selected = (MapillaryImage) data
-                .getSelectedImage();
-        MapillaryImage[] ret = new MapillaryImage[2];
-        double[] distances = { SEQUENCE_MAX_JUMP_DISTANCE,
-                SEQUENCE_MAX_JUMP_DISTANCE };
-        LatLon selectedCoords = data.getSelectedImage().getLatLon();
-        for (MapillaryAbstractImage imagePrev : data.getImages()) {
-            if (!(imagePrev instanceof MapillaryImage))
-                continue;
-            if (!imagePrev.isVisible())
-                continue;
-            MapillaryImage image = (MapillaryImage) imagePrev;
-            if (image.getLatLon().greatCircleDistance(selectedCoords) < SEQUENCE_MAX_JUMP_DISTANCE
-                    && selected.getSequence() != image.getSequence()) {
-                if ((ret[0] == null && ret[1] == null)
-                        || (image.getLatLon().greatCircleDistance(
-                                selectedCoords) < distances[0] && (ret[1] == null || image
-                                .getSequence() != ret[1].getSequence()))) {
-                    ret[0] = image;
-                    distances[0] = image.getLatLon().greatCircleDistance(
-                            selectedCoords);
-                } else if ((ret[1] == null || image.getLatLon()
-                        .greatCircleDistance(selectedCoords) < distances[1])
-                        && image.getSequence() != ret[0].getSequence()) {
-                    ret[1] = image;
-                    distances[1] = image.getLatLon().greatCircleDistance(
-                            selectedCoords);
-                }
-            }
-        }
-        // Predownloads the thumbnails
-        if (ret[0] != null)
-            new MapillaryCache(ret[0].getKey(), MapillaryCache.Type.THUMBNAIL)
-                    .submit(data, false);
-        if (ret[1] != null)
-            new MapillaryCache(ret[1].getKey(), MapillaryCache.Type.THUMBNAIL)
-                    .submit(data, false);
-        return ret;
-    }
-
-    @Override
-    public Object getInfoComponent() {
-        StringBuilder sb = new StringBuilder();
-        sb.append(tr("Mapillary layer"));
-        sb.append("\n");
-        sb.append(tr("Total images:"));
-        sb.append(" ");
-        sb.append(data.size());
-        sb.append("\n");
-        return sb.toString();
-    }
-
-    @Override
-    public String getToolTipText() {
-        return data.size() + " " + tr("images");
-    }
-
-    // EditDataLayerChanged
-    @Override
-    public void editLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer) {
-    }
-
-    /**
-     * When more data is downloaded, a delayed update is thrown, in order to
-     * wait for the data bounds to be set.
-     * 
-     * @param event
-     */
-    @Override
-    public void dataChanged(DataChangedEvent event) {
-        Main.worker.submit(new delayedDownload());
-    }
-
-    private class delayedDownload extends Thread {
-
-        @Override
-        public void run() {
-            try {
-                sleep(1000);
-            } catch (InterruptedException e) {
-                Main.error(e);
-            }
-            MapillaryLayer.getInstance().download();
-        }
-    }
-
-    @Override
-    public void primitivesAdded(PrimitivesAddedEvent event) {
-    }
-
-    @Override
-    public void primitivesRemoved(PrimitivesRemovedEvent event) {
-    }
-
-    @Override
-    public void tagsChanged(TagsChangedEvent event) {
-    }
-
-    @Override
-    public void nodeMoved(NodeMovedEvent event) {
-    }
-
-    @Override
-    public void wayNodesChanged(WayNodesChangedEvent event) {
-    }
-
-    @Override
-    public void relationMembersChanged(RelationMembersChangedEvent event) {
-    }
-
-    @Override
-    public void otherDatasetChange(AbstractDatasetChangedEvent event) {
-    }
-
-    @Override
-    public void visitBoundingBox(BoundingXYVisitor v) {
-    }
-
-    @Override
-    public void activeLayerChange(Layer oldLayer, Layer newLayer) {
-        if (newLayer == this) {
-            if (data.size() > 0)
-                Main.map.statusLine.setHelpText(tr("Total images: {0}",
-                        data.size()));
-            else
-                Main.map.statusLine.setHelpText(tr("No images found"));
-        }
-    }
-
-    @Override
-    public void layerAdded(Layer newLayer) {
-    }
-
-    @Override
-    public void layerRemoved(Layer oldLayer) {
-    }
+		DataSetListener, EditLayerChangeListener, LayerChangeListener {
+
+	public final static int SEQUENCE_MAX_JUMP_DISTANCE = Main.pref.getInteger(
+			"mapillary.sequence-max-jump-distance", 100);
+
+	public static MapillaryLayer INSTANCE;
+	public static CacheAccess<String, BufferedImageCacheEntry> CACHE;
+	public static MapillaryImage BLUE;
+	public static MapillaryImage RED;
+
+	private final MapillaryData data = MapillaryData.getInstance();
+
+	public ArrayList<Bounds> bounds;
+
+	private MouseAdapter mouseAdapter;
+
+	private int highlightPointRadius = Main.pref.getInteger(
+			"mappaint.highlight.radius", 7);
+	private int highlightStep = Main.pref.getInteger("mappaint.highlight.step",
+			4);
+
+	private volatile TexturePaint hatched;
+
+	public MapillaryLayer() {
+		super(tr("Mapillary Images"));
+		bounds = new ArrayList<>();
+		init();
+	}
+
+	/**
+	 * Initializes the Layer.
+	 */
+	private void init() {
+		MapillaryLayer.INSTANCE = this;
+		startMouseAdapter();
+		try {
+			CACHE = JCSCacheManager.getCache("Mapillary");
+		} catch (IOException e) {
+			Main.error(e);
+		}
+		if (Main.map != null && Main.map.mapView != null) {
+			Main.map.mapView.addMouseListener(mouseAdapter);
+			Main.map.mapView.addMouseMotionListener(mouseAdapter);
+			Main.map.mapView.addLayer(this);
+			MapView.addEditLayerChangeListener(this, false);
+			MapView.addLayerChangeListener(this);
+			Main.map.mapView.getEditLayer().data.addDataSetListener(this);
+		}
+		MapillaryPlugin.setMenuEnabled(MapillaryPlugin.EXPORT_MENU, true);
+		Main.map.mapView.setActiveLayer(this);
+		createHatchTexture();
+		Main.map.repaint();
+	}
+
+	private void startMouseAdapter() {
+		mouseAdapter = new MapillaryMouseAdapter();
+	}
+
+	public synchronized static MapillaryLayer getInstance() {
+		if (MapillaryLayer.INSTANCE == null)
+			MapillaryLayer.INSTANCE = new MapillaryLayer();
+		return MapillaryLayer.INSTANCE;
+	}
+
+	/**
+	 * Downloads all images of the area covered by the OSM data. This is only
+	 * just for automatic download.
+	 */
+	public void download() {
+		checkBigAreas();
+		if (Main.pref.getBoolean("mapillary.download-manually"))
+			return;
+		for (Bounds bounds : Main.map.mapView.getEditLayer().data
+				.getDataSourceBounds()) {
+			if (!this.bounds.contains(bounds)) {
+				this.bounds.add(bounds);
+				new MapillaryDownloader().getImages(bounds.getMin(),
+						bounds.getMax());
+			}
+		}
+	}
+
+	/**
+	 * Checks if the area of the OSM data is too big. This means that probably
+	 * lots of Mapillary images are going to be downloaded, slowing down the
+	 * program too much. To solve this the automatic is stopped, an alert is
+	 * shown and you will have to download areas manually.
+	 */
+	private void checkBigAreas() {
+		double area = 0;
+		for (Bounds bounds : Main.map.mapView.getEditLayer().data
+				.getDataSourceBounds()) {
+			area += bounds.getArea();
+		}
+		if (area > MapillaryDownloadViewAction.MAX_AREA) {
+			Main.pref.put("mapillary.download-manually", true);
+			JOptionPane
+					.showMessageDialog(
+							Main.parent,
+							tr("The downloaded OSM area is too big. Download mode has been change to manual. You can change this back to automatic in preferences settings."));
+		}
+	}
+
+	/**
+	 * Returns the MapillaryData object, which acts as the database of the
+	 * Layer.
+	 * 
+	 * @return
+	 */
+	public MapillaryData getMapillaryData() {
+		return data;
+	}
+
+	/**
+	 * Method invoked when the layer is destroyed.
+	 */
+	@Override
+	public void destroy() {
+		MapillaryToggleDialog.getInstance().mapillaryImageDisplay
+				.setImage(null);
+		data.getImages().clear();
+		MapillaryLayer.INSTANCE = null;
+		MapillaryData.INSTANCE = null;
+		MapillaryPlugin.setMenuEnabled(MapillaryPlugin.EXPORT_MENU, false);
+		MapillaryPlugin.setMenuEnabled(MapillaryPlugin.ZOOM_MENU, false);
+		Main.map.mapView.removeMouseListener(mouseAdapter);
+		Main.map.mapView.removeMouseMotionListener(mouseAdapter);
+		MapView.removeEditLayerChangeListener(this);
+		if (Main.map.mapView.getEditLayer() != null)
+			Main.map.mapView.getEditLayer().data.removeDataSetListener(this);
+		super.destroy();
+	}
+
+	/**
+	 * Returns true any of the images from the database has been modified.
+	 */
+	@Override
+	public boolean isModified() {
+		for (MapillaryAbstractImage image : data.getImages())
+			if (image.isModified())
+				return true;
+		return false;
+	}
+
+	@Override
+	public void setVisible(boolean visible) {
+		super.setVisible(visible);
+		for (MapillaryAbstractImage img : data.getImages())
+			img.setVisible(visible);
+		MapillaryFilterDialog.getInstance().refresh();
+	}
+
+	/**
+	 * Replies background color for downloaded areas.
+	 * 
+	 * @return background color for downloaded areas. Black by default
+	 */
+	private Color getBackgroundColor() {
+		return Main.pref.getColor(marktr("background"), Color.BLACK);
+	}
+
+	/**
+	 * Replies background color for non-downloaded areas.
+	 * 
+	 * @return background color for non-downloaded areas. Yellow by default
+	 */
+	private Color getOutsideColor() {
+		return Main.pref.getColor(marktr("outside downloaded area"),
+				Color.YELLOW);
+	}
+
+	/**
+	 * Initialize the hatch pattern used to paint the non-downloaded area
+	 */
+	private void createHatchTexture() {
+		BufferedImage bi = new BufferedImage(15, 15,
+				BufferedImage.TYPE_INT_ARGB);
+		Graphics2D big = bi.createGraphics();
+		big.setColor(getBackgroundColor());
+		Composite comp = AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
+				0.3f);
+		big.setComposite(comp);
+		big.fillRect(0, 0, 15, 15);
+		big.setColor(getOutsideColor());
+		big.drawLine(0, 15, 15, 0);
+		Rectangle r = new Rectangle(0, 0, 15, 15);
+		hatched = new TexturePaint(bi, r);
+	}
+
+	/**
+	 * Paints the database in the map.
+	 */
+	@Override
+	public void paint(Graphics2D g, MapView mv, Bounds box) {
+		synchronized (this) {
+			if (Main.map.mapView.getActiveLayer() == this) {
+				Rectangle b = mv.getBounds();
+				// on some platforms viewport bounds seem to be offset from the
+				// left,
+				// over-grow it just to be sure
+				b.grow(100, 100);
+				Area a = new Area(b);
+				// now successively subtract downloaded areas
+				for (Bounds bounds : this.bounds) {
+					Point p1 = mv.getPoint(bounds.getMin());
+					Point p2 = mv.getPoint(bounds.getMax());
+					Rectangle r = new Rectangle(Math.min(p1.x, p2.x), Math.min(
+							p1.y, p2.y), Math.abs(p2.x - p1.x), Math.abs(p2.y
+							- p1.y));
+					a.subtract(new Area(r));
+				}
+				// paint remainder
+				g.setPaint(hatched);
+				g.fill(a);
+			}
+
+			// Draw colored lines
+			MapillaryLayer.BLUE = null;
+			MapillaryLayer.RED = null;
+			MapillaryToggleDialog.getInstance().blueButton.setEnabled(false);
+			MapillaryToggleDialog.getInstance().redButton.setEnabled(false);
+
+			// Sets blue and red lines and enables/disables the buttons
+			if (data.getSelectedImage() != null) {
+				MapillaryImage[] closestImages = getClosestImagesFromDifferentSequences();
+				Point selected = mv.getPoint(data.getSelectedImage()
+						.getLatLon());
+				if (closestImages[0] != null) {
+					MapillaryLayer.BLUE = closestImages[0];
+					g.setColor(Color.BLUE);
+					g.drawLine(mv.getPoint(closestImages[0].getLatLon()).x,
+							mv.getPoint(closestImages[0].getLatLon()).y,
+							selected.x, selected.y);
+					MapillaryToggleDialog.getInstance().blueButton
+							.setEnabled(true);
+				}
+				if (closestImages[1] != null) {
+					MapillaryLayer.RED = closestImages[1];
+					g.setColor(Color.RED);
+					g.drawLine(mv.getPoint(closestImages[1].getLatLon()).x,
+							mv.getPoint(closestImages[1].getLatLon()).y,
+							selected.x, selected.y);
+					MapillaryToggleDialog.getInstance().redButton
+							.setEnabled(true);
+				}
+			}
+			g.setColor(Color.WHITE);
+			for (MapillaryAbstractImage imageAbs : data.getImages()) {
+				if (!imageAbs.isVisible())
+					continue;
+				Point p = mv.getPoint(imageAbs.getLatLon());
+				if (imageAbs instanceof MapillaryImage) {
+					MapillaryImage image = (MapillaryImage) imageAbs;
+					Point nextp;
+					// Draw sequence line
+					if (image.getSequence() != null && image.next() != null
+							&& image.next().isVisible()) {
+						nextp = mv.getPoint(image.getSequence().next(image)
+								.getLatLon());
+						g.drawLine(p.x, p.y, nextp.x, nextp.y);
+					}
+
+					ImageIcon icon;
+					if (!data.getMultiSelectedImages().contains(image))
+						icon = MapillaryPlugin.MAP_ICON;
+					else
+						icon = MapillaryPlugin.MAP_ICON_SELECTED;
+					draw(g, image, icon, p);
+					if (!image.getSigns().isEmpty()) {
+						g.drawImage(MapillaryPlugin.MAP_SIGN.getImage(), p.x
+								+ icon.getIconWidth() / 2,
+								p.y - icon.getIconHeight() / 2,
+								Main.map.mapView);
+					}
+				} else if (imageAbs instanceof MapillaryImportedImage) {
+					MapillaryImportedImage image = (MapillaryImportedImage) imageAbs;
+					ImageIcon icon;
+					if (!data.getMultiSelectedImages().contains(image))
+						icon = MapillaryPlugin.MAP_ICON_IMPORTED;
+					else
+						icon = MapillaryPlugin.MAP_ICON_SELECTED;
+					draw(g, image, icon, p);
+				}
+			}
+		}
+	}
+
+	/**
+	 * Draws the highlight of the icon.
+	 * 
+	 * @param g
+	 * @param p
+	 * @param size
+	 */
+	private void drawPointHighlight(Graphics2D g, Point p, int size) {
+		Color oldColor = g.getColor();
+		Color highlightColor = PaintColors.HIGHLIGHT.get();
+		Color highlightColorTransparent = new Color(highlightColor.getRed(),
+				highlightColor.getGreen(), highlightColor.getBlue(), 100);
+		g.setColor(highlightColorTransparent);
+		int s = size + highlightPointRadius;
+		while (s >= size) {
+			int r = (int) Math.floor(s / 2d);
+			g.fillRoundRect(p.x - r, p.y - r, s, s, r, r);
+			s -= highlightStep;
+		}
+		g.setColor(oldColor);
+	}
+
+	/**
+	 * Draws the given icon of an image. Also checks if the mouse is over the
+	 * image.
+	 * 
+	 * @param g
+	 * @param image
+	 * @param icon
+	 * @param p
+	 */
+	private void draw(Graphics2D g, MapillaryAbstractImage image,
+			ImageIcon icon, Point p) {
+		Image imagetemp = icon.getImage();
+		BufferedImage bi = (BufferedImage) imagetemp;
+		int width = icon.getIconWidth();
+		int height = icon.getIconHeight();
+
+		// Rotate the image
+		double rotationRequired = Math.toRadians(image.getCa());
+		double locationX = width / 2;
+		double locationY = height / 2;
+		AffineTransform tx = AffineTransform.getRotateInstance(
+				rotationRequired, locationX, locationY);
+		AffineTransformOp op = new AffineTransformOp(tx,
+				AffineTransformOp.TYPE_BILINEAR);
+
+		g.drawImage(op.filter(bi, null), p.x - (width / 2), p.y - (height / 2),
+				Main.map.mapView);
+		if (data.getHoveredImage() == image) {
+			drawPointHighlight(g, p, 16);
+		}
+	}
+
+	@Override
+	public Icon getIcon() {
+		return MapillaryPlugin.ICON16;
+	}
+
+	@Override
+	public boolean isMergable(Layer other) {
+		return false;
+	}
+
+	@Override
+	public void mergeFrom(Layer from) {
+		throw new UnsupportedOperationException(
+				"This layer does not support merging yet");
+	}
+
+	@Override
+	public Action[] getMenuEntries() {
+		List<Action> actions = new ArrayList<>();
+		actions.add(LayerListDialog.getInstance().createShowHideLayerAction());
+		actions.add(LayerListDialog.getInstance().createDeleteLayerAction());
+		actions.add(new LayerListPopup.InfoAction(this));
+		return actions.toArray(new Action[actions.size()]);
+	}
+
+	/**
+	 * Returns the 2 closest images belonging to a different sequence.
+	 * 
+	 * @return
+	 */
+	private MapillaryImage[] getClosestImagesFromDifferentSequences() {
+		if (!(data.getSelectedImage() instanceof MapillaryImage))
+			return new MapillaryImage[2];
+		MapillaryImage selected = (MapillaryImage) data.getSelectedImage();
+		MapillaryImage[] ret = new MapillaryImage[2];
+		double[] distances = { SEQUENCE_MAX_JUMP_DISTANCE,
+				SEQUENCE_MAX_JUMP_DISTANCE };
+		LatLon selectedCoords = data.getSelectedImage().getLatLon();
+		for (MapillaryAbstractImage imagePrev : data.getImages()) {
+			if (!(imagePrev instanceof MapillaryImage))
+				continue;
+			if (!imagePrev.isVisible())
+				continue;
+			MapillaryImage image = (MapillaryImage) imagePrev;
+			if (image.getLatLon().greatCircleDistance(selectedCoords) < SEQUENCE_MAX_JUMP_DISTANCE
+					&& selected.getSequence() != image.getSequence()) {
+				if ((ret[0] == null && ret[1] == null)
+						|| (image.getLatLon().greatCircleDistance(
+								selectedCoords) < distances[0] && (ret[1] == null || image
+								.getSequence() != ret[1].getSequence()))) {
+					ret[0] = image;
+					distances[0] = image.getLatLon().greatCircleDistance(
+							selectedCoords);
+				} else if ((ret[1] == null || image.getLatLon()
+						.greatCircleDistance(selectedCoords) < distances[1])
+						&& image.getSequence() != ret[0].getSequence()) {
+					ret[1] = image;
+					distances[1] = image.getLatLon().greatCircleDistance(
+							selectedCoords);
+				}
+			}
+		}
+		// Predownloads the thumbnails
+		if (ret[0] != null)
+			new MapillaryCache(ret[0].getKey(), MapillaryCache.Type.THUMBNAIL)
+					.submit(data, false);
+		if (ret[1] != null)
+			new MapillaryCache(ret[1].getKey(), MapillaryCache.Type.THUMBNAIL)
+					.submit(data, false);
+		return ret;
+	}
+
+	@Override
+	public Object getInfoComponent() {
+		StringBuilder sb = new StringBuilder();
+		sb.append(tr("Mapillary layer"));
+		sb.append("\n");
+		sb.append(tr("Total images:"));
+		sb.append(" ");
+		sb.append(data.size());
+		sb.append("\n");
+		return sb.toString();
+	}
+
+	@Override
+	public String getToolTipText() {
+		return data.size() + " " + tr("images");
+	}
+
+	// EditDataLayerChanged
+	@Override
+	public void editLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer) {
+	}
+
+	/**
+	 * When more data is downloaded, a delayed update is thrown, in order to
+	 * wait for the data bounds to be set.
+	 * 
+	 * @param event
+	 */
+	@Override
+	public void dataChanged(DataChangedEvent event) {
+		Main.worker.submit(new delayedDownload());
+	}
+
+	private class delayedDownload extends Thread {
+
+		@Override
+		public void run() {
+			try {
+				sleep(1000);
+			} catch (InterruptedException e) {
+				Main.error(e);
+			}
+			MapillaryLayer.getInstance().download();
+		}
+	}
+
+	@Override
+	public void primitivesAdded(PrimitivesAddedEvent event) {
+	}
+
+	@Override
+	public void primitivesRemoved(PrimitivesRemovedEvent event) {
+	}
+
+	@Override
+	public void tagsChanged(TagsChangedEvent event) {
+	}
+
+	@Override
+	public void nodeMoved(NodeMovedEvent event) {
+	}
+
+	@Override
+	public void wayNodesChanged(WayNodesChangedEvent event) {
+	}
+
+	@Override
+	public void relationMembersChanged(RelationMembersChangedEvent event) {
+	}
+
+	@Override
+	public void otherDatasetChange(AbstractDatasetChangedEvent event) {
+	}
+
+	@Override
+	public void visitBoundingBox(BoundingXYVisitor v) {
+	}
+
+	@Override
+	public void activeLayerChange(Layer oldLayer, Layer newLayer) {
+		if (newLayer == this) {
+			if (data.size() > 0)
+				Main.map.statusLine.setHelpText(tr("Total images: {0}",
+						data.size()));
+			else
+				Main.map.statusLine.setHelpText(tr("No images found"));
+		}
+	}
+
+	@Override
+	public void layerAdded(Layer newLayer) {
+	}
+
+	@Override
+	public void layerRemoved(Layer oldLayer) {
+	}
 }
Index: applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryMouseAdapter.java
===================================================================
--- applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryMouseAdapter.java	(revision 31297)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryMouseAdapter.java	(revision 31298)
@@ -7,4 +7,5 @@
 
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.mapmode.MapMode;
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
@@ -22,215 +23,228 @@
  */
 public class MapillaryMouseAdapter extends MouseAdapter {
-    private Point start;
-    private int lastButton;
-    private MapillaryAbstractImage closest;
-    private MapillaryAbstractImage lastClicked;
-    private MapillaryData mapillaryData;
-    private MapillaryRecord record;
-
-    private boolean nothingHighlighted;
-
-    public MapillaryMouseAdapter() {
-        mapillaryData = MapillaryData.getInstance();
-        record = MapillaryRecord.getInstance();
-    }
-
-    @Override
-    public void mousePressed(MouseEvent e) {
-        lastButton = e.getButton();
-        if (e.getButton() != MouseEvent.BUTTON1)
-            return;
-        MapillaryAbstractImage closestTemp = getClosest(e.getPoint());
-        if (Main.map.mapView.getActiveLayer() instanceof OsmDataLayer
-                && closestTemp != null) {
-            this.lastClicked = this.closest;
-            MapillaryData.getInstance().setSelectedImage(closestTemp);
-            return;
-        } else if (Main.map.mapView.getActiveLayer() != MapillaryLayer
-                .getInstance())
-            return;
-        if (closestTemp instanceof MapillaryImage || closestTemp == null) {
-            MapillaryImage closest = (MapillaryImage) closestTemp;
-            // Doube click
-            if (e.getClickCount() == 2
-                    && mapillaryData.getSelectedImage() != null
-                    && closest != null) {
-                for (MapillaryAbstractImage img : closest.getSequence()
-                        .getImages()) {
-                    mapillaryData.addMultiSelectedImage(img);
-                }
-            }
-            this.start = e.getPoint();
-            this.lastClicked = this.closest;
-            this.closest = closest;
-            if (mapillaryData.getMultiSelectedImages().contains(closest))
-                return;
-            // ctrl+click
-            if (e.getModifiers() == (MouseEvent.BUTTON1_MASK | MouseEvent.CTRL_MASK)
-                    && closest != null)
-                mapillaryData.addMultiSelectedImage(closest);
-            // shift + click
-            else if (e.getModifiers() == (MouseEvent.BUTTON1_MASK | MouseEvent.SHIFT_MASK)
-                    && this.closest instanceof MapillaryImage
-                    && this.lastClicked instanceof MapillaryImage) {
-                if (this.closest != null
-                        && this.lastClicked != null
-                        && ((MapillaryImage) this.closest).getSequence() == ((MapillaryImage) this.lastClicked)
-                                .getSequence()) {
-                    int i = ((MapillaryImage) this.closest).getSequence()
-                            .getImages().indexOf(this.closest);
-                    int j = ((MapillaryImage) this.lastClicked).getSequence()
-                            .getImages().indexOf(this.lastClicked);
-                    if (i < j)
-                        mapillaryData
-                                .addMultiSelectedImage(new ArrayList<MapillaryAbstractImage>(
-                                        ((MapillaryImage) this.closest)
-                                                .getSequence().getImages()
-                                                .subList(i, j + 1)));
-                    else
-                        mapillaryData
-                                .addMultiSelectedImage(new ArrayList<MapillaryAbstractImage>(
-                                        ((MapillaryImage) this.closest)
-                                                .getSequence().getImages()
-                                                .subList(j, i + 1)));
-                }
-                // click
-            } else
-                mapillaryData.setSelectedImage(closest);
-            // If you select an imported image
-        } else if (closestTemp instanceof MapillaryImportedImage) {
-            MapillaryImportedImage closest = (MapillaryImportedImage) closestTemp;
-            this.start = e.getPoint();
-            this.lastClicked = this.closest;
-            this.closest = closest;
-            if (mapillaryData.getMultiSelectedImages().contains(closest))
-                return;
-            if (e.getModifiers() == (MouseEvent.BUTTON1_MASK | MouseEvent.CTRL_MASK)
-                    && closest != null)
-                mapillaryData.addMultiSelectedImage(closest);
-            else
-                mapillaryData.setSelectedImage(closest);
-        }
-    }
-
-    private MapillaryAbstractImage getClosest(Point clickPoint) {
-        double snapDistance = 10;
-        double minDistance = Double.MAX_VALUE;
-        MapillaryAbstractImage closest = null;
-        for (MapillaryAbstractImage image : mapillaryData.getImages()) {
-            Point imagePoint = Main.map.mapView.getPoint(image.getLatLon());
-            imagePoint.setLocation(imagePoint.getX(), imagePoint.getY());
-            double dist = clickPoint.distanceSq(imagePoint);
-            if (minDistance > dist
-                    && clickPoint.distance(imagePoint) < snapDistance) {
-                minDistance = dist;
-                closest = image;
-            }
-        }
-        return closest;
-    }
-
-    @Override
-    public void mouseDragged(MouseEvent e) {
-        if (Main.map.mapView.getActiveLayer() != MapillaryLayer.getInstance())
-            return;
-
-        if (!Main.pref.getBoolean("mapillary.developer"))
-            for (MapillaryAbstractImage img : MapillaryData.getInstance()
-                    .getMultiSelectedImages()) {
-                if (img instanceof MapillaryImage)
-                    return;
-            }
-        if (MapillaryData.getInstance().getSelectedImage() != null) {
-            if (lastButton == MouseEvent.BUTTON1 && !e.isShiftDown()) {
-                LatLon to = Main.map.mapView.getLatLon(e.getX(), e.getY());
-                LatLon from = Main.map.mapView.getLatLon(start.getX(),
-                        start.getY());
-                for (MapillaryAbstractImage img : MapillaryData.getInstance()
-                        .getMultiSelectedImages()) {
-
-                    img.move(to.getX() - from.getX(), to.getY() - from.getY());
-                }
-                Main.map.repaint();
-            } else if (lastButton == MouseEvent.BUTTON1 && e.isShiftDown()) {
-                this.closest.turn(Math.toDegrees(Math.atan2(
-                        (e.getX() - start.x), -(e.getY() - start.y)))
-                        - closest.getTempCa());
-                for (MapillaryAbstractImage img : MapillaryData.getInstance()
-                        .getMultiSelectedImages()) {
-                    img.turn(Math.toDegrees(Math.atan2((e.getX() - start.x),
-                            -(e.getY() - start.y))) - closest.getTempCa());
-                }
-                Main.map.repaint();
-            }
-        }
-    }
-
-    @Override
-    public void mouseReleased(MouseEvent e) {
-        if (mapillaryData.getSelectedImage() == null)
-            return;
-        if (mapillaryData.getSelectedImage().getTempCa() != mapillaryData
-                .getSelectedImage().getCa()) {
-            double from = mapillaryData.getSelectedImage().getTempCa();
-            double to = mapillaryData.getSelectedImage().getCa();
-            record.addCommand(new CommandTurnImage(mapillaryData
-                    .getMultiSelectedImages(), to - from));
-        } else if (mapillaryData.getSelectedImage().getTempLatLon() != mapillaryData
-                .getSelectedImage().getLatLon()) {
-            LatLon from = mapillaryData.getSelectedImage().getTempLatLon();
-            LatLon to = mapillaryData.getSelectedImage().getLatLon();
-            record.addCommand(new CommandMoveImage(mapillaryData
-                    .getMultiSelectedImages(), to.getX() - from.getX(), to
-                    .getY() - from.getY()));
-        }
-        for (MapillaryAbstractImage img : mapillaryData
-                .getMultiSelectedImages()) {
-            if (img != null)
-                img.stopMoving();
-        }
-    }
-
-    /**
-     * Checks if the mouse is over pictures.
-     */
-    @Override
-    public void mouseMoved(MouseEvent e) {
-        MapillaryAbstractImage closestTemp = getClosest(e.getPoint());
-
-        if (closestTemp != null
-                && Main.map.mapView.getActiveLayer() instanceof OsmDataLayer
-                && Main.map.mapModeSelect.getValue("active") == Boolean.TRUE) {
-            Main.map.mapModeSelect.exitMode();
-        } else if (closestTemp == null
-                && Main.map.mapView.getActiveLayer() instanceof OsmDataLayer
-                && Main.map.mapModeSelect.getValue("active") == Boolean.FALSE) {
-            Main.map.mapModeSelect.enterMode();
-            nothingHighlighted = false;
-        } else if (Main.map.mapModeSelect.getValue("active") == Boolean.FALSE
-                && !nothingHighlighted && Main.map.mapView != null
-                && Main.map.mapView.getEditLayer().data != null) {
-            for (OsmPrimitive primivitive : Main.map.mapView.getEditLayer().data
-                    .allPrimitives()) {
-                primivitive.setHighlighted(false);
-            }
-            nothingHighlighted = true;
-        }
-
-        // TODO check if it is possible to do this while the OSM data layer is
-        // selected.
-        if (MapillaryData.getInstance().getHoveredImage() != closestTemp
-                && closestTemp != null) {
-            MapillaryData.getInstance().setHoveredImage(closestTemp);
-            MapillaryToggleDialog.getInstance().setImage(closestTemp);
-            MapillaryToggleDialog.getInstance().updateImage();
-        } else if (MapillaryData.getInstance().getHoveredImage() != closestTemp
-                && closestTemp == null) {
-            MapillaryData.getInstance().setHoveredImage(null);
-            MapillaryToggleDialog.getInstance().setImage(
-                    MapillaryData.getInstance().getSelectedImage());
-            MapillaryToggleDialog.getInstance().updateImage();
-        }
-        MapillaryData.getInstance().dataUpdated();
-    }
+	private Point start;
+	private int lastButton;
+	private MapillaryAbstractImage closest;
+	private MapillaryAbstractImage lastClicked;
+	private MapillaryData mapillaryData;
+	private MapillaryRecord record;
+
+	private boolean nothingHighlighted;
+	private boolean imageHighlighted = false;
+
+	public MapillaryMouseAdapter() {
+		mapillaryData = MapillaryData.getInstance();
+		record = MapillaryRecord.getInstance();
+	}
+
+	@Override
+	public void mousePressed(MouseEvent e) {
+		lastButton = e.getButton();
+		if (e.getButton() != MouseEvent.BUTTON1)
+			return;
+		MapillaryAbstractImage closestTemp = getClosest(e.getPoint());
+		if (Main.map.mapView.getActiveLayer() instanceof OsmDataLayer
+				&& closestTemp != null
+				&& Main.map.mapMode == Main.map.mapModeSelect) {
+			this.lastClicked = this.closest;
+			MapillaryData.getInstance().setSelectedImage(closestTemp);
+			return;
+		} else if (Main.map.mapView.getActiveLayer() != MapillaryLayer
+				.getInstance())
+			return;
+		if (closestTemp instanceof MapillaryImage || closestTemp == null) {
+			MapillaryImage closest = (MapillaryImage) closestTemp;
+			// Doube click
+			if (e.getClickCount() == 2
+					&& mapillaryData.getSelectedImage() != null
+					&& closest != null) {
+				for (MapillaryAbstractImage img : closest.getSequence()
+						.getImages()) {
+					mapillaryData.addMultiSelectedImage(img);
+				}
+			}
+			this.start = e.getPoint();
+			this.lastClicked = this.closest;
+			this.closest = closest;
+			if (mapillaryData.getMultiSelectedImages().contains(closest))
+				return;
+			// ctrl+click
+			if (e.getModifiers() == (MouseEvent.BUTTON1_MASK | MouseEvent.CTRL_MASK)
+					&& closest != null)
+				mapillaryData.addMultiSelectedImage(closest);
+			// shift + click
+			else if (e.getModifiers() == (MouseEvent.BUTTON1_MASK | MouseEvent.SHIFT_MASK)
+					&& this.closest instanceof MapillaryImage
+					&& this.lastClicked instanceof MapillaryImage) {
+				if (this.closest != null
+						&& this.lastClicked != null
+						&& ((MapillaryImage) this.closest).getSequence() == ((MapillaryImage) this.lastClicked)
+								.getSequence()) {
+					int i = ((MapillaryImage) this.closest).getSequence()
+							.getImages().indexOf(this.closest);
+					int j = ((MapillaryImage) this.lastClicked).getSequence()
+							.getImages().indexOf(this.lastClicked);
+					if (i < j)
+						mapillaryData
+								.addMultiSelectedImage(new ArrayList<MapillaryAbstractImage>(
+										((MapillaryImage) this.closest)
+												.getSequence().getImages()
+												.subList(i, j + 1)));
+					else
+						mapillaryData
+								.addMultiSelectedImage(new ArrayList<MapillaryAbstractImage>(
+										((MapillaryImage) this.closest)
+												.getSequence().getImages()
+												.subList(j, i + 1)));
+				}
+				// click
+			} else
+				mapillaryData.setSelectedImage(closest);
+			// If you select an imported image
+		} else if (closestTemp instanceof MapillaryImportedImage) {
+			MapillaryImportedImage closest = (MapillaryImportedImage) closestTemp;
+			this.start = e.getPoint();
+			this.lastClicked = this.closest;
+			this.closest = closest;
+			if (mapillaryData.getMultiSelectedImages().contains(closest))
+				return;
+			if (e.getModifiers() == (MouseEvent.BUTTON1_MASK | MouseEvent.CTRL_MASK)
+					&& closest != null)
+				mapillaryData.addMultiSelectedImage(closest);
+			else
+				mapillaryData.setSelectedImage(closest);
+		}
+	}
+
+	private MapillaryAbstractImage getClosest(Point clickPoint) {
+		double snapDistance = 10;
+		double minDistance = Double.MAX_VALUE;
+		MapillaryAbstractImage closest = null;
+		for (MapillaryAbstractImage image : mapillaryData.getImages()) {
+			Point imagePoint = Main.map.mapView.getPoint(image.getLatLon());
+			imagePoint.setLocation(imagePoint.getX(), imagePoint.getY());
+			double dist = clickPoint.distanceSq(imagePoint);
+			if (minDistance > dist
+					&& clickPoint.distance(imagePoint) < snapDistance) {
+				minDistance = dist;
+				closest = image;
+			}
+		}
+		return closest;
+	}
+
+	@Override
+	public void mouseDragged(MouseEvent e) {
+		if (Main.map.mapView.getActiveLayer() != MapillaryLayer.getInstance())
+			return;
+
+		if (!Main.pref.getBoolean("mapillary.developer"))
+			for (MapillaryAbstractImage img : MapillaryData.getInstance()
+					.getMultiSelectedImages()) {
+				if (img instanceof MapillaryImage)
+					return;
+			}
+		if (MapillaryData.getInstance().getSelectedImage() != null) {
+			if (lastButton == MouseEvent.BUTTON1 && !e.isShiftDown()) {
+				LatLon to = Main.map.mapView.getLatLon(e.getX(), e.getY());
+				LatLon from = Main.map.mapView.getLatLon(start.getX(),
+						start.getY());
+				for (MapillaryAbstractImage img : MapillaryData.getInstance()
+						.getMultiSelectedImages()) {
+
+					img.move(to.getX() - from.getX(), to.getY() - from.getY());
+				}
+				Main.map.repaint();
+			} else if (lastButton == MouseEvent.BUTTON1 && e.isShiftDown()) {
+				this.closest.turn(Math.toDegrees(Math.atan2(
+						(e.getX() - start.x), -(e.getY() - start.y)))
+						- closest.getTempCa());
+				for (MapillaryAbstractImage img : MapillaryData.getInstance()
+						.getMultiSelectedImages()) {
+					img.turn(Math.toDegrees(Math.atan2((e.getX() - start.x),
+							-(e.getY() - start.y))) - closest.getTempCa());
+				}
+				Main.map.repaint();
+			}
+		}
+	}
+
+	@Override
+	public void mouseReleased(MouseEvent e) {
+		if (mapillaryData.getSelectedImage() == null)
+			return;
+		if (mapillaryData.getSelectedImage().getTempCa() != mapillaryData
+				.getSelectedImage().getCa()) {
+			double from = mapillaryData.getSelectedImage().getTempCa();
+			double to = mapillaryData.getSelectedImage().getCa();
+			record.addCommand(new CommandTurnImage(mapillaryData
+					.getMultiSelectedImages(), to - from));
+		} else if (mapillaryData.getSelectedImage().getTempLatLon() != mapillaryData
+				.getSelectedImage().getLatLon()) {
+			LatLon from = mapillaryData.getSelectedImage().getTempLatLon();
+			LatLon to = mapillaryData.getSelectedImage().getLatLon();
+			record.addCommand(new CommandMoveImage(mapillaryData
+					.getMultiSelectedImages(), to.getX() - from.getX(), to
+					.getY() - from.getY()));
+		}
+		for (MapillaryAbstractImage img : mapillaryData
+				.getMultiSelectedImages()) {
+			if (img != null)
+				img.stopMoving();
+		}
+	}
+
+	/**
+	 * Checks if the mouse is over pictures.
+	 */
+	@Override
+	public void mouseMoved(MouseEvent e) {
+		MapillaryAbstractImage closestTemp = getClosest(e.getPoint());
+		if (Main.map.mapView.getActiveLayer() instanceof OsmDataLayer
+				&& Main.map.mapMode != Main.map.mapModeSelect)
+			return;
+		if (closestTemp != null
+				&& Main.map.mapView.getActiveLayer() instanceof OsmDataLayer
+				&& !imageHighlighted) {
+			Main.map.mapMode.putValue("active", Boolean.FALSE);
+			imageHighlighted = true;
+			System.out.println("1");
+
+		} else if (closestTemp == null
+				&& Main.map.mapView.getActiveLayer() instanceof OsmDataLayer
+				&& imageHighlighted && nothingHighlighted) {
+			nothingHighlighted = false;
+			Main.map.mapMode.putValue("active", Boolean.TRUE);
+			System.out.println("2");
+
+		} else if (imageHighlighted && !nothingHighlighted
+				&& Main.map.mapView != null
+				&& Main.map.mapView.getEditLayer().data != null
+				&& Main.map.mapView.getActiveLayer() instanceof OsmDataLayer) {
+			System.out.println("3");
+
+			for (OsmPrimitive primivitive : Main.map.mapView.getEditLayer().data
+					.allPrimitives()) {
+				primivitive.setHighlighted(false);
+			}
+			imageHighlighted = false;
+			nothingHighlighted = true;
+		}
+
+		// TODO check if it is possible to do this while the OSM data layer is
+		// selected.
+		if (MapillaryData.getInstance().getHoveredImage() != closestTemp
+				&& closestTemp != null) {
+			MapillaryData.getInstance().setHoveredImage(closestTemp);
+			MapillaryToggleDialog.getInstance().setImage(closestTemp);
+			MapillaryToggleDialog.getInstance().updateImage();
+		} else if (MapillaryData.getInstance().getHoveredImage() != closestTemp
+				&& closestTemp == null) {
+			MapillaryData.getInstance().setHoveredImage(null);
+			MapillaryToggleDialog.getInstance().setImage(
+					MapillaryData.getInstance().getSelectedImage());
+			MapillaryToggleDialog.getInstance().updateImage();
+		}
+		MapillaryData.getInstance().dataUpdated();
+	}
 }
Index: applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryPlugin.java
===================================================================
--- applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryPlugin.java	(revision 31297)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryPlugin.java	(revision 31298)
@@ -33,108 +33,106 @@
 public class MapillaryPlugin extends Plugin implements EditLayerChangeListener {
 
-    public static final ImageIcon ICON24 = new ImageProvider("icon24.png")
-            .get();
-    public static final ImageIcon ICON16 = new ImageProvider("icon16.png")
-            .get();
-    public static final ImageIcon MAP_ICON = new ImageProvider("mapicon.png")
-            .get();
-    public static final ImageIcon MAP_ICON_SELECTED = new ImageProvider(
-            "mapiconselected.png").get();
-    public static final ImageIcon MAP_ICON_IMPORTED = new ImageProvider(
-            "mapiconimported.png").get();
-    public static final ImageIcon MAP_SIGN = new ImageProvider("sign.png")
-            .get();
-    public static final int ICON_SIZE = 24;
+	public static final ImageIcon ICON24 = new ImageProvider("icon24.png")
+			.get();
+	public static final ImageIcon ICON16 = new ImageProvider("icon16.png")
+			.get();
+	public static final ImageIcon MAP_ICON = new ImageProvider("mapicon.png")
+			.get();
+	public static final ImageIcon MAP_ICON_SELECTED = new ImageProvider(
+			"mapiconselected.png").get();
+	public static final ImageIcon MAP_ICON_IMPORTED = new ImageProvider(
+			"mapiconimported.png").get();
+	public static final ImageIcon MAP_SIGN = new ImageProvider("sign.png")
+			.get();
+	public static final int ICON_SIZE = 24;
 
-    public static CacheAccess<String, BufferedImageCacheEntry> CACHE;
+	public static CacheAccess<String, BufferedImageCacheEntry> CACHE;
 
-    private final MapillaryDownloadAction downloadAction;
-    private final MapillaryExportAction exportAction;
-    private final MapillaryImportAction importAction;
-    private final MapillarySignAction signAction;
-    private final MapillaryZoomAction zoomAction;
-    private final MapillaryDownloadViewAction downloadViewAction;
+	private final MapillaryDownloadAction downloadAction;
+	private final MapillaryExportAction exportAction;
+	private final MapillaryImportAction importAction;
+	private final MapillaryZoomAction zoomAction;
+	private final MapillaryDownloadViewAction downloadViewAction;
 
-    public static JMenuItem DOWNLOAD_MENU;
-    public static JMenuItem EXPORT_MENU;
-    public static JMenuItem IMPORT_MENU;
-    public static JMenuItem SIGN_MENU;
-    public static JMenuItem ZOOM_MENU;
-    public static JMenuItem DOWNLOAD_VIEW_MENU;
+	public static JMenuItem DOWNLOAD_MENU;
+	public static JMenuItem EXPORT_MENU;
+	public static JMenuItem IMPORT_MENU;
+	public static JMenuItem ZOOM_MENU;
+	public static JMenuItem DOWNLOAD_VIEW_MENU;
 
-    public MapillaryPlugin(PluginInformation info) {
-        super(info);
-        downloadAction = new MapillaryDownloadAction();
-        exportAction = new MapillaryExportAction();
-        importAction = new MapillaryImportAction();
-        signAction = new MapillarySignAction();
-        zoomAction = new MapillaryZoomAction();
-        downloadViewAction = new MapillaryDownloadViewAction();
+	public MapillaryPlugin(PluginInformation info) {
+		super(info);
+		downloadAction = new MapillaryDownloadAction();
+		exportAction = new MapillaryExportAction();
+		importAction = new MapillaryImportAction();
+		zoomAction = new MapillaryZoomAction();
+		downloadViewAction = new MapillaryDownloadViewAction();
 
-        DOWNLOAD_MENU = MainMenu.add(Main.main.menu.imageryMenu,
-                downloadAction, false);
-        EXPORT_MENU = MainMenu.add(Main.main.menu.fileMenu, exportAction,
-                false, 14);
-        IMPORT_MENU = MainMenu.add(Main.main.menu.fileMenu, importAction,
-                false, 14);
-        SIGN_MENU = MainMenu.add(Main.main.menu.dataMenu, signAction, false);
-        ZOOM_MENU = MainMenu
-                .add(Main.main.menu.viewMenu, zoomAction, false, 15);
-        DOWNLOAD_VIEW_MENU = MainMenu.add(Main.main.menu.fileMenu,
-                downloadViewAction, false, 14);
+		DOWNLOAD_MENU = MainMenu.add(Main.main.menu.imageryMenu,
+				downloadAction, false);
+		EXPORT_MENU = MainMenu.add(Main.main.menu.fileMenu, exportAction,
+				false, 14);
+		IMPORT_MENU = MainMenu.add(Main.main.menu.fileMenu, importAction,
+				false, 14);
+		ZOOM_MENU = MainMenu
+				.add(Main.main.menu.viewMenu, zoomAction, false, 15);
+		DOWNLOAD_VIEW_MENU = MainMenu.add(Main.main.menu.fileMenu,
+				downloadViewAction, false, 14);
 
-        EXPORT_MENU.setEnabled(false);
-        DOWNLOAD_MENU.setEnabled(false);
-        IMPORT_MENU.setEnabled(false);
-        SIGN_MENU.setEnabled(false);
-        ZOOM_MENU.setEnabled(false);
-        DOWNLOAD_VIEW_MENU.setEnabled(false);
+		EXPORT_MENU.setEnabled(false);
+		DOWNLOAD_MENU.setEnabled(false);
+		IMPORT_MENU.setEnabled(false);
+		ZOOM_MENU.setEnabled(false);
+		DOWNLOAD_VIEW_MENU.setEnabled(false);
 
-        MapView.addEditLayerChangeListener(this);
-        try {
-            CACHE = JCSCacheManager.getCache("mapillary", 10, 10000,
-                    this.getPluginDir() + "/cache/");
-        } catch (IOException e) {
-            Main.error(e);
-        }
-    }
+		MapView.addEditLayerChangeListener(this);
+		try {
+			CACHE = JCSCacheManager.getCache("mapillary", 10, 10000,
+					this.getPluginDir() + "/cache/");
+		} catch (IOException e) {
+			Main.error(e);
+		}
+	}
 
-    /**
-     * Called when the JOSM map frame is created or destroyed.
-     */
-    @Override
-    public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
-        if (oldFrame == null && newFrame != null) { // map frame added
-            Main.map.addToggleDialog(MapillaryToggleDialog.getInstance(), false);
-            Main.map.addToggleDialog(MapillaryHistoryDialog.getInstance(), false);
-            Main.map.addToggleDialog(MapillaryFilterDialog.getInstance(), false);
-        }
-        if (oldFrame != null && newFrame == null) { // map frame destroyed
-            MapillaryToggleDialog.destroyInstance();
-            MapillaryHistoryDialog.destroyInstance();
-            MapillaryFilterDialog.destroyInstance();
-        }
-    }
+	/**
+	 * Called when the JOSM map frame is created or destroyed.
+	 */
+	@Override
+	public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
+		if (oldFrame == null && newFrame != null) { // map frame added
+			Main.map.addToggleDialog(MapillaryToggleDialog.getInstance(), false);
+			Main.map.addToggleDialog(MapillaryHistoryDialog.getInstance(),
+					false);
+			Main.map.addToggleDialog(MapillaryFilterDialog.getInstance(), false);
+		}
+		if (oldFrame != null && newFrame == null) { // map frame destroyed
+			MapillaryToggleDialog.destroyInstance();
+			MapillaryHistoryDialog.destroyInstance();
+			MapillaryFilterDialog.destroyInstance();
+		}
+	}
 
-    public static void setMenuEnabled(JMenuItem menu, boolean value) {
-        menu.setEnabled(value);
-    }
+	public static void setMenuEnabled(JMenuItem menu, boolean value) {
+		menu.setEnabled(value);
+		menu.getAction().setEnabled(value);
+	}
 
-    @Override
-    public PreferenceSetting getPreferenceSetting() {
-        return new MapillaryPreferenceSetting();
-    }
+	@Override
+	public PreferenceSetting getPreferenceSetting() {
+		return new MapillaryPreferenceSetting();
+	}
 
-    @Override
-    public void editLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer) {
-        if (oldLayer == null && newLayer != null) {
-            setMenuEnabled(DOWNLOAD_MENU, true);
-            setMenuEnabled(IMPORT_MENU, true);
-            setMenuEnabled(DOWNLOAD_VIEW_MENU, true);
-        } else if (oldLayer != null && newLayer == null) {
-            setMenuEnabled(DOWNLOAD_MENU, false);
-            setMenuEnabled(IMPORT_MENU, false);
-            setMenuEnabled(DOWNLOAD_VIEW_MENU, false);
-        }
-    }
+	@Override
+	public void editLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer) {
+		if (oldLayer == null && newLayer != null) {
+			setMenuEnabled(DOWNLOAD_MENU, true);
+			if (Main.pref.getBoolean("mapillary.download-manually"))
+				setMenuEnabled(IMPORT_MENU, true);
+			setMenuEnabled(DOWNLOAD_VIEW_MENU, true);
+		} else if (oldLayer != null && newLayer == null) {
+			setMenuEnabled(DOWNLOAD_MENU, false);
+			setMenuEnabled(IMPORT_MENU, false);
+			setMenuEnabled(DOWNLOAD_VIEW_MENU, false);
+		}
+	}
 }
Index: applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/actions/MapillarySignAction.java
===================================================================
--- applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/actions/MapillarySignAction.java	(revision 31297)
+++ 	(revision )
@@ -1,37 +1,0 @@
-package org.openstreetmap.josm.plugins.mapillary.actions;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.awt.event.ActionEvent;
-import java.awt.event.KeyEvent;
-
-import org.openstreetmap.josm.actions.JosmAction;
-import org.openstreetmap.josm.plugins.mapillary.gui.MapillaryToggleDialog;
-import org.openstreetmap.josm.tools.ImageProvider;
-import org.openstreetmap.josm.tools.Shortcut;
-
-/**
- * Switches the window mode from normal to sign and viceversa.
- * 
- * @author nokutu
- * @see MapillaryToggleDialog
- *
- */
-public class MapillarySignAction extends JosmAction {
-
-    public MapillarySignAction() {
-        super(tr("Switch sign/normal mode"),
-                new ImageProvider("icon24sign.png"),
-                tr("Switch sign/normal mode"), Shortcut.registerShortcut(
-                        "Mapillary sign",
-                        tr("Switch Mapillary plugin's sign mode on/off"),
-                        KeyEvent.VK_M, Shortcut.NONE), false, "mapillarySign",
-                false);
-        this.setEnabled(false);
-    }
-
-    @Override
-    public void actionPerformed(ActionEvent e) {
-        MapillaryToggleDialog.getInstance().switchMode();
-    }
-}
Index: applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/downloads/MapillarySequenceDownloadThread.java
===================================================================
--- applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/downloads/MapillarySequenceDownloadThread.java	(revision 31297)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/downloads/MapillarySequenceDownloadThread.java	(revision 31298)
@@ -79,25 +79,14 @@
                                 "captured_at").longValue());
 
-                int first = -1;
-                int last = -1;
-                int pos = 0;
+               
 
+                List<MapillaryImage> finalImages = new ArrayList<>(images);
                 // Here it gets only those images which are in the downloaded
                 // area.
                 for (MapillaryAbstractImage img : images) {
-                    if (first == -1 && isInside(img))
-                        first = pos;
-                    else if (first != -1 && last == -1 && !isInside(img))
-                        last = pos;
-                    else if (last != -1 && isInside(img))
-                        last = -1;
-                    pos++;
+                    if (!isInside(img))
+                    	finalImages.remove(img);
                 }
-                if (last == -1) {
-                    last = pos;
-                }
-                if (first == -1)
-                    continue;
-                List<MapillaryImage> finalImages = images.subList(first, last);
+                
                 for (MapillaryImage img : finalImages) {
                     MapillaryData.getInstance().getImages().remove(img);
Index: applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/gui/MapillaryPreferenceSetting.java
===================================================================
--- applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/gui/MapillaryPreferenceSetting.java	(revision 31297)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/gui/MapillaryPreferenceSetting.java	(revision 31298)
@@ -12,4 +12,5 @@
 import org.openstreetmap.josm.gui.preferences.SubPreferenceSetting;
 import org.openstreetmap.josm.gui.preferences.TabPreferenceSetting;
+import org.openstreetmap.josm.plugins.mapillary.MapillaryPlugin;
 
 public class MapillaryPreferenceSetting implements SubPreferenceSetting {
@@ -51,4 +52,6 @@
         Main.pref.put("mapillary.reverse-buttons", reverseButtons.isSelected());
         Main.pref.put("mapillary.download-manually", downloadMode.isSelected());
+        MapillaryPlugin.setMenuEnabled(MapillaryPlugin.DOWNLOAD_VIEW_MENU, downloadMode.isSelected());
+        
         Main.pref.put("mapillary.display-hour", displayHour.isSelected());
         Main.pref.put("mapillary.format-24", format24.isSelected());
Index: applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/gui/MapillaryToggleDialog.java
===================================================================
--- applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/gui/MapillaryToggleDialog.java	(revision 31297)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/gui/MapillaryToggleDialog.java	(revision 31298)
@@ -43,445 +43,312 @@
  */
 public class MapillaryToggleDialog extends ToggleDialog implements
-        ICachedLoaderListener, MapillaryDataListener {
-
-    public final static int NORMAL_MODE = 0;
-    public final static int SIGN_MODE = 1;
-
-    public final static String BASE_TITLE = "Mapillary picture";
-
-    public static MapillaryToggleDialog INSTANCE;
-
-    public volatile MapillaryAbstractImage image;
-
-    public final SideButton nextButton = new SideButton(new nextPictureAction());
-    public final SideButton previousButton = new SideButton(
-            new previousPictureAction());
-    public final SideButton redButton = new SideButton(new redAction());
-    public final SideButton blueButton = new SideButton(new blueAction());
-    private List<SideButton> normalMode;
-
-    public final SideButton nextSignButton = new SideButton(
-            new NextSignAction());
-    public final SideButton previousSignButton = new SideButton(
-            new PreviousSignAction());
-    private List<SideButton> signMode;
-
-    private int mode;
-
-    private JPanel buttonsPanel;
-
-    public MapillaryImageDisplay mapillaryImageDisplay;
-
-    private MapillaryCache imageCache;
-    private MapillaryCache thumbnailCache;
-
-    public MapillaryToggleDialog() {
-        super(tr(BASE_TITLE), "mapillary.png", tr("Open Mapillary window"),
-                Shortcut.registerShortcut(tr("Mapillary dialog"),
-                        tr("Open Mapillary main dialog"), KeyEvent.VK_M,
-                        Shortcut.NONE), 200);
-        MapillaryData.getInstance().addListener(this);
-
-        mapillaryImageDisplay = new MapillaryImageDisplay();
-
-        blueButton.setForeground(Color.BLUE);
-        redButton.setForeground(Color.RED);
-
-        normalMode = Arrays.asList(new SideButton[] { blueButton,
-                previousButton, nextButton, redButton });
-        signMode = Arrays.asList(new SideButton[] { previousSignButton,
-                nextSignButton });
-
-        mode = NORMAL_MODE;
-
-        createLayout(mapillaryImageDisplay, normalMode,
-                Main.pref.getBoolean("mapillary.reverse-buttons"));
-        disableAllButtons();
-    }
-
-    public static MapillaryToggleDialog getInstance() {
-        if (INSTANCE == null)
-            INSTANCE = new MapillaryToggleDialog();
-        return INSTANCE;
-    }
-
-    public static void destroyInstance() {
-        INSTANCE = null;
-    }
-
-    /**
-     * Switches from one mode to the other one.
-     */
-    public void switchMode() {
-        this.removeAll();
-        List<SideButton> list = null;
-        if (mode == NORMAL_MODE) {
-            list = signMode;
-            mode = SIGN_MODE;
-        } else if (mode == SIGN_MODE) {
-            list = normalMode;
-            mode = NORMAL_MODE;
-        }
-
-        createLayout(mapillaryImageDisplay, list,
-                Main.pref.getBoolean("mapillary.reverse-buttons"));
-        disableAllButtons();
-        updateImage();
-    }
-
-    /**
-     * Downloads the image of the selected MapillaryImage and sets in the
-     * MapillaryImageDisplay object.
-     */
-    public synchronized void updateImage() {
-        if (!SwingUtilities.isEventDispatchThread()) {
-            SwingUtilities.invokeLater(new Runnable() {
-                @Override
-                public void run() {
-                    updateImage();
-                }
-            });
-        } else {
-            if (MapillaryLayer.INSTANCE == null) {
-                return;
-            }
-            if (this.image == null) {
-                mapillaryImageDisplay.setImage(null);
-                setTitle(tr(BASE_TITLE));
-                disableAllButtons();
-                return;
-            }
-            if (image instanceof MapillaryImage) {
-                mapillaryImageDisplay.hyperlink.setVisible(true);
-                MapillaryImage mapillaryImage = (MapillaryImage) this.image;
-                String title = tr(BASE_TITLE);
-                if (mapillaryImage.getUser() != null)
-                    title += " -- " + mapillaryImage.getUser();
-                if (mapillaryImage.getCapturedAt() != 0)
-                    title += " -- " + mapillaryImage.getDate();
-                setTitle(title);
-                if (mode == NORMAL_MODE) {
-                    this.nextButton.setEnabled(true);
-                    this.previousButton.setEnabled(true);
-                    if (mapillaryImage.next() == null || !mapillaryImage.next().isVisible())
-                        this.nextButton.setEnabled(false);
-                    if (mapillaryImage.previous() == null || !mapillaryImage.previous().isVisible())
-                        this.previousButton.setEnabled(false);
-                } else if (mode == SIGN_MODE) {
-                    previousSignButton.setEnabled(true);
-                    nextSignButton.setEnabled(true);
-                    int i = MapillaryData
-                            .getInstance()
-                            .getImages()
-                            .indexOf(
-                                    MapillaryData.getInstance()
-                                            .getSelectedImage());
-                    int first = -1;
-                    int last = -1;
-                    int c = 0;
-                    for (MapillaryAbstractImage img : MapillaryData
-                            .getInstance().getImages()) {
-                        if (img instanceof MapillaryImage)
-                            if (!((MapillaryImage) img).getSigns().isEmpty()) {
-                                if (first == -1)
-                                    first = c;
-                                last = c;
-                            }
-                        c++;
-                    }
-                    if (first >= i)
-                        previousSignButton.setEnabled(false);
-                    if (last <= i)
-                        nextSignButton.setEnabled(false);
-                }
-
-                mapillaryImageDisplay.hyperlink.setURL(mapillaryImage.getKey());
-                // Downloads the thumbnail.
-                this.mapillaryImageDisplay.setImage(null);
-                if (thumbnailCache != null)
-                    thumbnailCache.cancelOutstandingTasks();
-                thumbnailCache = new MapillaryCache(mapillaryImage.getKey(),
-                        MapillaryCache.Type.THUMBNAIL);
-                thumbnailCache.submit(this, false);
-
-                // Downloads the full resolution image.
-                if (imageCache != null)
-                    imageCache.cancelOutstandingTasks();
-                imageCache = new MapillaryCache(mapillaryImage.getKey(),
-                        MapillaryCache.Type.FULL_IMAGE);
-                imageCache.submit(this, false);
-            } else if (image instanceof MapillaryImportedImage) {
-                mapillaryImageDisplay.hyperlink.setVisible(false);
-                this.nextButton.setEnabled(false);
-                this.previousButton.setEnabled(false);
-                MapillaryImportedImage mapillaryImage = (MapillaryImportedImage) this.image;
-                try {
-                    mapillaryImageDisplay.setImage(mapillaryImage.getImage());
-                } catch (IOException e) {
-                    Main.error(e);
-                }
-                mapillaryImageDisplay.hyperlink.setURL(null);
-            }
-        }
-    }
-
-    private void disableAllButtons() {
-        nextButton.setEnabled(false);
-        previousButton.setEnabled(false);
-        blueButton.setEnabled(false);
-        redButton.setEnabled(false);
-        nextSignButton.setEnabled(false);
-        previousSignButton.setEnabled(false);
-        mapillaryImageDisplay.hyperlink.setVisible(false);
-    }
-
-    /**
-     * Sets a new MapillaryImage to be shown.
-     * 
-     * @param image
-     */
-    public synchronized void setImage(MapillaryAbstractImage image) {
-        this.image = image;
-    }
-
-    /**
-     * Returns the MapillaryImage objects which is being shown.
-     * 
-     * @return
-     */
-    public synchronized MapillaryAbstractImage getImage() {
-        return this.image;
-    }
-
-    /**
-     * Action class form the next image button.
-     * 
-     * @author Jorge
-     *
-     */
-    class nextPictureAction extends AbstractAction {
-        public nextPictureAction() {
-            putValue(NAME, tr("Next picture"));
-            putValue(SHORT_DESCRIPTION,
-                    tr("Shows the next picture in the sequence"));
-        }
-
-        @Override
-        public void actionPerformed(ActionEvent e) {
-            if (MapillaryToggleDialog.getInstance().getImage() != null) {
-                MapillaryData.getInstance().selectNext();
-            }
-        }
-    }
-
-    /**
-     * Action class for the previous image button.
-     * 
-     * @author Jorge
-     *
-     */
-    class previousPictureAction extends AbstractAction {
-        public previousPictureAction() {
-            putValue(NAME, tr("Previous picture"));
-            putValue(SHORT_DESCRIPTION,
-                    tr("Shows the previous picture in the sequence"));
-        }
-
-        @Override
-        public void actionPerformed(ActionEvent e) {
-            if (MapillaryToggleDialog.getInstance().getImage() != null) {
-                MapillaryData.getInstance().selectPrevious();
-            }
-        }
-    }
-
-    /**
-     * Action class to jump to the image following the red line.
-     * 
-     * @author nokutu
-     *
-     */
-    class redAction extends AbstractAction {
-        public redAction() {
-            putValue(NAME, tr("Jump to red"));
-            putValue(
-                    SHORT_DESCRIPTION,
-                    tr("Jumps to the picture at the other side of the red line"));
-        }
-
-        @Override
-        public void actionPerformed(ActionEvent e) {
-            if (MapillaryToggleDialog.getInstance().getImage() != null) {
-                MapillaryData.getInstance().setSelectedImage(
-                        MapillaryLayer.RED, true);
-            }
-        }
-    }
-
-    /**
-     * Action class to jump to the image following the blue line.
-     * 
-     * @author nokutu
-     *
-     */
-    class blueAction extends AbstractAction {
-        public blueAction() {
-            putValue(NAME, tr("Jump to blue"));
-            putValue(
-                    SHORT_DESCRIPTION,
-                    tr("Jumps to the picture at the other side of the blue line"));
-        }
-
-        @Override
-        public void actionPerformed(ActionEvent e) {
-            if (MapillaryToggleDialog.getInstance().getImage() != null) {
-                MapillaryData.getInstance().setSelectedImage(
-                        MapillaryLayer.BLUE, true);
-            }
-        }
-    }
-
-    /**
-     * When the pictures are returned from the cache, they are set in the
-     * {@link MapillaryImageDisplay} object.
-     */
-    @Override
-    public void loadingFinished(CacheEntry data,
-            CacheEntryAttributes attributes, LoadResult result) {
-        if (!SwingUtilities.isEventDispatchThread()) {
-            SwingUtilities.invokeLater(new Runnable() {
-                @Override
-                public void run() {
-                    updateImage();
-                }
-            });
-        } else if (data != null && result == LoadResult.SUCCESS) {
-            try {
-                BufferedImage img = ImageIO.read(new ByteArrayInputStream(data
-                        .getContent()));
-                if (this.mapillaryImageDisplay.getImage() == null)
-                    mapillaryImageDisplay.setImage(img);
-                else if (img.getHeight() > this.mapillaryImageDisplay
-                        .getImage().getHeight()) {
-                    mapillaryImageDisplay.setImage(img);
-                }
-            } catch (IOException e) {
-                Main.error(e);
-            }
-        }
-    }
-
-    /**
-     * Creates the layout of the dialog.
-     * 
-     * @param data
-     *            The content of the dialog
-     * @param buttons
-     *            The buttons where you can click
-     * @param reverse
-     *            {@code true} if the buttons should go at the top;
-     *            {@code false} otherwise.
-     */
-    public void createLayout(Component data, List<SideButton> buttons,
-            boolean reverse) {
-        this.removeAll();
-        JPanel panel = new JPanel();
-        panel.setLayout(new BorderLayout());
-        panel.add(data, BorderLayout.CENTER);
-        if (reverse) {
-            buttonsPanel = new JPanel(new GridLayout(1, 1));
-            if (!buttons.isEmpty() && buttons.get(0) != null) {
-                final JPanel buttonRowPanel = new JPanel(Main.pref.getBoolean(
-                        "dialog.align.left", false) ? new FlowLayout(
-                        FlowLayout.LEFT) : new GridLayout(1, buttons.size()));
-                buttonsPanel.add(buttonRowPanel);
-                for (SideButton button : buttons)
-                    buttonRowPanel.add(button);
-            }
-            panel.add(buttonsPanel, BorderLayout.NORTH);
-            createLayout(panel, true, null);
-        } else
-            createLayout(panel, true, buttons);
-        this.add(titleBar, BorderLayout.NORTH);
-    }
-
-    @Override
-    public void selectedImageChanged(MapillaryAbstractImage oldImage,
-            MapillaryAbstractImage newImage) {
-        setImage(MapillaryData.getInstance().getSelectedImage());
-        updateImage();
-    }
-
-    /**
-     * Action class to jump to the next picture containing a sign.
-     * 
-     * @author nokutu
-     *
-     */
-    class NextSignAction extends AbstractAction {
-        public NextSignAction() {
-            putValue(NAME, tr("Next Sign"));
-            putValue(SHORT_DESCRIPTION,
-                    tr("Jumps to the next picture that contains a sign"));
-        }
-
-        @Override
-        public void actionPerformed(ActionEvent e) {
-            if (MapillaryToggleDialog.getInstance().getImage() != null) {
-                int i = MapillaryData
-                        .getInstance()
-                        .getImages()
-                        .indexOf(MapillaryData.getInstance().getSelectedImage());
-                for (int j = i + 1; j < MapillaryData.getInstance().getImages()
-                        .size(); j++) {
-                    MapillaryAbstractImage img = MapillaryData.getInstance()
-                            .getImages().get(j);
-                    if (img instanceof MapillaryImage)
-                        if (!((MapillaryImage) img).getSigns().isEmpty()) {
-                            MapillaryData.getInstance().setSelectedImage(img,
-                                    true);
-                            return;
-                        }
-                }
-            }
-        }
-    }
-
-    /**
-     * Action class to jump to the previous picture containing a sign.
-     * 
-     * @author nokutu
-     *
-     */
-    class PreviousSignAction extends AbstractAction {
-        public PreviousSignAction() {
-            putValue(NAME, tr("Previous Sign"));
-            putValue(SHORT_DESCRIPTION,
-                    tr("Jumps to the previous picture that contains a sign"));
-        }
-
-        @Override
-        public void actionPerformed(ActionEvent e) {
-            if (MapillaryToggleDialog.getInstance().getImage() != null) {
-                int i = MapillaryData
-                        .getInstance()
-                        .getImages()
-                        .indexOf(MapillaryData.getInstance().getSelectedImage());
-                for (int j = i - 1; j >= 0; j--) {
-                    MapillaryAbstractImage img = MapillaryData.getInstance()
-                            .getImages().get(j);
-                    if (img instanceof MapillaryImage)
-                        if (!((MapillaryImage) img).getSigns().isEmpty()) {
-                            MapillaryData.getInstance().setSelectedImage(img,
-                                    true);
-                            return;
-                        }
-                }
-            }
-        }
-    }
-
-    @Override
-    public void imagesAdded() {
-    }
+		ICachedLoaderListener, MapillaryDataListener {
+
+	public final static String BASE_TITLE = "Mapillary picture";
+
+	public static MapillaryToggleDialog INSTANCE;
+
+	public volatile MapillaryAbstractImage image;
+
+	public final SideButton nextButton = new SideButton(new nextPictureAction());
+	public final SideButton previousButton = new SideButton(
+			new previousPictureAction());
+	public final SideButton redButton = new SideButton(new redAction());
+	public final SideButton blueButton = new SideButton(new blueAction());
+
+	private JPanel buttonsPanel;
+
+	public MapillaryImageDisplay mapillaryImageDisplay;
+
+	private MapillaryCache imageCache;
+	private MapillaryCache thumbnailCache;
+
+	public MapillaryToggleDialog() {
+		super(tr(BASE_TITLE), "mapillary.png", tr("Open Mapillary window"),
+				Shortcut.registerShortcut(tr("Mapillary dialog"),
+						tr("Open Mapillary main dialog"), KeyEvent.VK_M,
+						Shortcut.NONE), 200);
+		MapillaryData.getInstance().addListener(this);
+
+		mapillaryImageDisplay = new MapillaryImageDisplay();
+
+		blueButton.setForeground(Color.BLUE);
+		redButton.setForeground(Color.RED);
+
+		createLayout(
+				mapillaryImageDisplay,
+				Arrays.asList(new SideButton[] { blueButton, previousButton,
+						nextButton, redButton }),
+				Main.pref.getBoolean("mapillary.reverse-buttons"));
+		disableAllButtons();
+	}
+
+	public static MapillaryToggleDialog getInstance() {
+		if (INSTANCE == null)
+			INSTANCE = new MapillaryToggleDialog();
+		return INSTANCE;
+	}
+
+	public static void destroyInstance() {
+		INSTANCE = null;
+	}
+
+	/**
+	 * Downloads the image of the selected MapillaryImage and sets in the
+	 * MapillaryImageDisplay object.
+	 */
+	public synchronized void updateImage() {
+		if (!SwingUtilities.isEventDispatchThread()) {
+			SwingUtilities.invokeLater(new Runnable() {
+				@Override
+				public void run() {
+					updateImage();
+				}
+			});
+		} else {
+			if (MapillaryLayer.INSTANCE == null) {
+				return;
+			}
+			if (this.image == null) {
+				mapillaryImageDisplay.setImage(null);
+				setTitle(tr(BASE_TITLE));
+				disableAllButtons();
+				return;
+			}
+			if (image instanceof MapillaryImage) {
+				mapillaryImageDisplay.hyperlink.setVisible(true);
+				MapillaryImage mapillaryImage = (MapillaryImage) this.image;
+				String title = tr(BASE_TITLE);
+				if (mapillaryImage.getUser() != null)
+					title += " -- " + mapillaryImage.getUser();
+				if (mapillaryImage.getCapturedAt() != 0)
+					title += " -- " + mapillaryImage.getDate();
+				setTitle(title);
+				this.nextButton.setEnabled(true);
+				this.previousButton.setEnabled(true);
+				if (mapillaryImage.next() == null
+						|| !mapillaryImage.next().isVisible())
+					this.nextButton.setEnabled(false);
+				if (mapillaryImage.previous() == null
+						|| !mapillaryImage.previous().isVisible())
+					this.previousButton.setEnabled(false);
+
+				mapillaryImageDisplay.hyperlink.setURL(mapillaryImage.getKey());
+				// Downloads the thumbnail.
+				this.mapillaryImageDisplay.setImage(null);
+				if (thumbnailCache != null)
+					thumbnailCache.cancelOutstandingTasks();
+				thumbnailCache = new MapillaryCache(mapillaryImage.getKey(),
+						MapillaryCache.Type.THUMBNAIL);
+				thumbnailCache.submit(this, false);
+
+				// Downloads the full resolution image.
+				if (imageCache != null)
+					imageCache.cancelOutstandingTasks();
+				imageCache = new MapillaryCache(mapillaryImage.getKey(),
+						MapillaryCache.Type.FULL_IMAGE);
+				imageCache.submit(this, false);
+			} else if (image instanceof MapillaryImportedImage) {
+				mapillaryImageDisplay.hyperlink.setVisible(false);
+				this.nextButton.setEnabled(false);
+				this.previousButton.setEnabled(false);
+				MapillaryImportedImage mapillaryImage = (MapillaryImportedImage) this.image;
+				try {
+					mapillaryImageDisplay.setImage(mapillaryImage.getImage());
+				} catch (IOException e) {
+					Main.error(e);
+				}
+				mapillaryImageDisplay.hyperlink.setURL(null);
+			}
+		}
+	}
+
+	private void disableAllButtons() {
+		nextButton.setEnabled(false);
+		previousButton.setEnabled(false);
+		blueButton.setEnabled(false);
+		redButton.setEnabled(false);
+		mapillaryImageDisplay.hyperlink.setVisible(false);
+	}
+
+	/**
+	 * Sets a new MapillaryImage to be shown.
+	 * 
+	 * @param image
+	 */
+	public synchronized void setImage(MapillaryAbstractImage image) {
+		this.image = image;
+	}
+
+	/**
+	 * Returns the MapillaryImage objects which is being shown.
+	 * 
+	 * @return
+	 */
+	public synchronized MapillaryAbstractImage getImage() {
+		return this.image;
+	}
+
+	/**
+	 * Action class form the next image button.
+	 * 
+	 * @author Jorge
+	 *
+	 */
+	class nextPictureAction extends AbstractAction {
+		public nextPictureAction() {
+			putValue(NAME, tr("Next picture"));
+			putValue(SHORT_DESCRIPTION,
+					tr("Shows the next picture in the sequence"));
+		}
+
+		@Override
+		public void actionPerformed(ActionEvent e) {
+			if (MapillaryToggleDialog.getInstance().getImage() != null) {
+				MapillaryData.getInstance().selectNext();
+			}
+		}
+	}
+
+	/**
+	 * Action class for the previous image button.
+	 * 
+	 * @author Jorge
+	 *
+	 */
+	class previousPictureAction extends AbstractAction {
+		public previousPictureAction() {
+			putValue(NAME, tr("Previous picture"));
+			putValue(SHORT_DESCRIPTION,
+					tr("Shows the previous picture in the sequence"));
+		}
+
+		@Override
+		public void actionPerformed(ActionEvent e) {
+			if (MapillaryToggleDialog.getInstance().getImage() != null) {
+				MapillaryData.getInstance().selectPrevious();
+			}
+		}
+	}
+
+	/**
+	 * Action class to jump to the image following the red line.
+	 * 
+	 * @author nokutu
+	 *
+	 */
+	class redAction extends AbstractAction {
+		public redAction() {
+			putValue(NAME, tr("Jump to red"));
+			putValue(
+					SHORT_DESCRIPTION,
+					tr("Jumps to the picture at the other side of the red line"));
+		}
+
+		@Override
+		public void actionPerformed(ActionEvent e) {
+			if (MapillaryToggleDialog.getInstance().getImage() != null) {
+				MapillaryData.getInstance().setSelectedImage(
+						MapillaryLayer.RED, true);
+			}
+		}
+	}
+
+	/**
+	 * Action class to jump to the image following the blue line.
+	 * 
+	 * @author nokutu
+	 *
+	 */
+	class blueAction extends AbstractAction {
+		public blueAction() {
+			putValue(NAME, tr("Jump to blue"));
+			putValue(
+					SHORT_DESCRIPTION,
+					tr("Jumps to the picture at the other side of the blue line"));
+		}
+
+		@Override
+		public void actionPerformed(ActionEvent e) {
+			if (MapillaryToggleDialog.getInstance().getImage() != null) {
+				MapillaryData.getInstance().setSelectedImage(
+						MapillaryLayer.BLUE, true);
+			}
+		}
+	}
+
+	/**
+	 * When the pictures are returned from the cache, they are set in the
+	 * {@link MapillaryImageDisplay} object.
+	 */
+	@Override
+	public void loadingFinished(CacheEntry data,
+			CacheEntryAttributes attributes, LoadResult result) {
+		if (!SwingUtilities.isEventDispatchThread()) {
+			SwingUtilities.invokeLater(new Runnable() {
+				@Override
+				public void run() {
+					updateImage();
+				}
+			});
+		} else if (data != null && result == LoadResult.SUCCESS) {
+			try {
+				BufferedImage img = ImageIO.read(new ByteArrayInputStream(data
+						.getContent()));
+				if (this.mapillaryImageDisplay.getImage() == null)
+					mapillaryImageDisplay.setImage(img);
+				else if (img.getHeight() > this.mapillaryImageDisplay
+						.getImage().getHeight()) {
+					mapillaryImageDisplay.setImage(img);
+				}
+			} catch (IOException e) {
+				Main.error(e);
+			}
+		}
+	}
+
+	/**
+	 * Creates the layout of the dialog.
+	 * 
+	 * @param data
+	 *            The content of the dialog
+	 * @param buttons
+	 *            The buttons where you can click
+	 * @param reverse
+	 *            {@code true} if the buttons should go at the top;
+	 *            {@code false} otherwise.
+	 */
+	public void createLayout(Component data, List<SideButton> buttons,
+			boolean reverse) {
+		this.removeAll();
+		JPanel panel = new JPanel();
+		panel.setLayout(new BorderLayout());
+		panel.add(data, BorderLayout.CENTER);
+		if (reverse) {
+			buttonsPanel = new JPanel(new GridLayout(1, 1));
+			if (!buttons.isEmpty() && buttons.get(0) != null) {
+				final JPanel buttonRowPanel = new JPanel(Main.pref.getBoolean(
+						"dialog.align.left", false) ? new FlowLayout(
+						FlowLayout.LEFT) : new GridLayout(1, buttons.size()));
+				buttonsPanel.add(buttonRowPanel);
+				for (SideButton button : buttons)
+					buttonRowPanel.add(button);
+			}
+			panel.add(buttonsPanel, BorderLayout.NORTH);
+			createLayout(panel, true, null);
+		} else
+			createLayout(panel, true, buttons);
+		this.add(titleBar, BorderLayout.NORTH);
+	}
+
+	@Override
+	public void selectedImageChanged(MapillaryAbstractImage oldImage,
+			MapillaryAbstractImage newImage) {
+		setImage(MapillaryData.getInstance().getSelectedImage());
+		updateImage();
+	}
+
+	@Override
+	public void imagesAdded() {
+	}
 }
