001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.plugins.streetside.mode;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.awt.Graphics2D;
007import java.awt.Point;
008import java.awt.event.InputEvent;
009import java.awt.event.MouseEvent;
010import java.util.Objects;
011import java.util.concurrent.ConcurrentSkipListSet;
012
013import javax.swing.SwingUtilities;
014
015import org.openstreetmap.josm.data.Bounds;
016import org.openstreetmap.josm.data.coor.LatLon;
017import org.openstreetmap.josm.data.osm.OsmPrimitive;
018import org.openstreetmap.josm.gui.MainApplication;
019import org.openstreetmap.josm.gui.MapView;
020import org.openstreetmap.josm.gui.layer.OsmDataLayer;
021import org.openstreetmap.josm.plugins.streetside.StreetsideAbstractImage;
022import org.openstreetmap.josm.plugins.streetside.StreetsideData;
023import org.openstreetmap.josm.plugins.streetside.StreetsideImage;
024import org.openstreetmap.josm.plugins.streetside.StreetsideLayer;
025import org.openstreetmap.josm.plugins.streetside.gui.StreetsideMainDialog;
026import org.openstreetmap.josm.plugins.streetside.history.StreetsideRecord;
027import org.openstreetmap.josm.plugins.streetside.history.commands.CommandMove;
028import org.openstreetmap.josm.plugins.streetside.history.commands.CommandTurn;
029import org.openstreetmap.josm.plugins.streetside.utils.StreetsideProperties;
030
031/**
032 * Handles the input event related with the layer. Mainly clicks.
033 *
034 * @author nokutu
035 */
036public class SelectMode extends AbstractMode {
037  private StreetsideAbstractImage closest;
038  private StreetsideAbstractImage lastClicked;
039  private final StreetsideRecord record;
040  private boolean nothingHighlighted;
041  private boolean imageHighlighted;
042
043  /**
044   * Main constructor.
045   */
046  public SelectMode() {
047    record = StreetsideRecord.getInstance();
048  }
049
050  @Override
051  public void mousePressed(MouseEvent e) {
052    if (e.getButton() != MouseEvent.BUTTON1) {
053      return;
054    }
055    StreetsideAbstractImage closest = getClosest(e.getPoint());
056    if (!(MainApplication.getLayerManager().getActiveLayer() instanceof StreetsideLayer)
057            && closest != null && MainApplication.getMap().mapMode == MainApplication.getMap().mapModeSelect) {
058      lastClicked = this.closest;
059      StreetsideLayer.getInstance().getData().setSelectedImage(closest);
060      return;
061    } else if (MainApplication.getLayerManager().getActiveLayer() != StreetsideLayer.getInstance()) {
062      return;
063    }
064    // Double click
065    if (e.getClickCount() == 2 && StreetsideLayer.getInstance().getData().getSelectedImage() != null && closest != null) {
066      closest.getSequence().getImages().forEach(StreetsideLayer.getInstance().getData()::addMultiSelectedImage);
067    }
068    lastClicked = this.closest;
069    this.closest = closest;
070    if (closest != null && StreetsideLayer.getInstance().getData().getMultiSelectedImages().contains(closest)) {
071      return;
072    }
073    // ctrl+click
074    if (e.getModifiers() == (InputEvent.BUTTON1_MASK | InputEvent.CTRL_MASK) && closest != null) {
075      StreetsideLayer.getInstance().getData().addMultiSelectedImage(closest);
076      // shift + click
077    } else if (
078            e.getModifiers() == (InputEvent.BUTTON1_MASK | InputEvent.SHIFT_MASK)
079                    && lastClicked instanceof StreetsideImage
080            ) {
081      if (this.closest != null
082              && this.closest.getSequence() == (lastClicked).getSequence()) {
083        int i = this.closest.getSequence().getImages().indexOf(this.closest);
084        int j = lastClicked.getSequence().getImages().indexOf(lastClicked);
085        StreetsideLayer.getInstance().getData().addMultiSelectedImage(
086                i < j
087                        ? new ConcurrentSkipListSet<>(this.closest.getSequence().getImages().subList(i, j + 1))
088                        : new ConcurrentSkipListSet<>(this.closest.getSequence().getImages().subList(j, i + 1))
089        );
090      }
091      // click
092    } else {
093      StreetsideLayer.getInstance().getData().setSelectedImage(closest);
094    }
095  }
096
097  @Override
098  public void mouseDragged(MouseEvent e) {
099    StreetsideAbstractImage highlightImg = StreetsideLayer.getInstance().getData().getHighlightedImage();
100    if (
101            MainApplication.getLayerManager().getActiveLayer() == StreetsideLayer.getInstance()
102                && SwingUtilities.isLeftMouseButton(e)
103                && highlightImg != null && highlightImg.getLatLon() != null
104            ) {
105      Point highlightImgPoint = MainApplication.getMap().mapView.getPoint(highlightImg.getTempLatLon());
106      if (e.isShiftDown()) { // turn
107        StreetsideLayer.getInstance().getData().getMultiSelectedImages().parallelStream().filter(img -> !(img instanceof StreetsideImage) || StreetsideProperties.DEVELOPER.get())
108                .forEach(img -> img.turn(Math.toDegrees(Math.atan2(e.getX() - highlightImgPoint.getX(), -e.getY() + highlightImgPoint.getY())) - highlightImg.getTempHe()));
109      } else { // move
110        LatLon eventLatLon = MainApplication.getMap().mapView.getLatLon(e.getX(), e.getY());
111        LatLon imgLatLon = MainApplication.getMap().mapView.getLatLon(highlightImgPoint.getX(), highlightImgPoint.getY());
112        StreetsideLayer.getInstance().getData().getMultiSelectedImages().parallelStream().filter(img -> !(img instanceof StreetsideImage) || StreetsideProperties.DEVELOPER.get())
113                .forEach(img -> img.move(eventLatLon.getX() - imgLatLon.getX(), eventLatLon.getY() - imgLatLon.getY()));
114      }
115      StreetsideLayer.invalidateInstance();
116    }
117  }
118
119  @Override
120  public void mouseReleased(MouseEvent e) {
121    final StreetsideData data = StreetsideLayer.getInstance().getData();
122    if (data.getSelectedImage() == null) {
123      return;
124    }
125    if (!Objects.equals(data.getSelectedImage().getTempHe(), data.getSelectedImage().getMovingHe())) {
126      double from = data.getSelectedImage().getTempHe();
127      double to = data.getSelectedImage().getMovingHe();
128      record.addCommand(new CommandTurn(data.getMultiSelectedImages(), to - from));
129    } else if (!Objects.equals(data.getSelectedImage().getTempLatLon(), data.getSelectedImage().getMovingLatLon())) {
130      LatLon from = data.getSelectedImage().getTempLatLon();
131      LatLon to = data.getSelectedImage().getMovingLatLon();
132      record.addCommand(new CommandMove(data.getMultiSelectedImages(), to.getX() - from.getX(), to.getY() - from.getY()));
133    }
134    data.getMultiSelectedImages().parallelStream().filter(Objects::nonNull).forEach(StreetsideAbstractImage::stopMoving);
135    StreetsideLayer.invalidateInstance();
136  }
137
138  /**
139   * Checks if the mouse is over pictures.
140   */
141  @Override
142  public void mouseMoved(MouseEvent e) {
143    if (MainApplication.getLayerManager().getActiveLayer() instanceof OsmDataLayer
144            && MainApplication.getMap().mapMode != MainApplication.getMap().mapModeSelect) {
145      return;
146    }
147    if (!StreetsideProperties.HOVER_ENABLED.get()) {
148      return;
149    }
150
151    StreetsideAbstractImage closestTemp = getClosest(e.getPoint());
152
153    final OsmDataLayer editLayer = MainApplication.getLayerManager().getEditLayer();
154    if (editLayer != null) {
155      if (closestTemp != null && !imageHighlighted) {
156        if (MainApplication.getMap().mapMode != null) {
157          MainApplication.getMap().mapMode.putValue("active", Boolean.FALSE);
158        }
159        imageHighlighted = true;
160      } else if (closestTemp == null && imageHighlighted && nothingHighlighted) {
161        if (MainApplication.getMap().mapMode != null) {
162          MainApplication.getMap().mapMode.putValue("active", Boolean.TRUE);
163        }
164        nothingHighlighted = false;
165      } else if (imageHighlighted && !nothingHighlighted && editLayer.data != null) {
166        for (OsmPrimitive primivitive : MainApplication.getLayerManager().getEditLayer().data.allPrimitives()) {
167          primivitive.setHighlighted(false);
168        }
169        imageHighlighted = false;
170        nothingHighlighted = true;
171      }
172    }
173
174    if (StreetsideLayer.getInstance().getData().getHighlightedImage() != closestTemp && closestTemp != null) {
175      StreetsideLayer.getInstance().getData().setHighlightedImage(closestTemp);
176      StreetsideMainDialog.getInstance().setImage(closestTemp);
177      StreetsideMainDialog.getInstance().updateImage(false);
178
179    } else if (StreetsideLayer.getInstance().getData().getHighlightedImage() != closestTemp && closestTemp == null) {
180      StreetsideLayer.getInstance().getData().setHighlightedImage(null);
181      StreetsideMainDialog.getInstance().setImage(StreetsideLayer.getInstance().getData().getSelectedImage());
182      StreetsideMainDialog.getInstance().updateImage();
183    }
184
185    StreetsideLayer.invalidateInstance();
186  }
187
188  @Override
189  public void paint(Graphics2D g, MapView mv, Bounds box) {
190  }
191
192  @Override
193  public String toString() {
194    return tr("Select mode");
195  }
196}