source: josm/trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java@ 5502

Last change on this file since 5502 was 5502, checked in by Don-vip, 12 years ago

fix some compilation warnings

  • Property svn:eol-style set to native
File size: 18.5 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.gui.layer.markerlayer;
3
4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
5import static org.openstreetmap.josm.tools.I18n.marktr;
6import static org.openstreetmap.josm.tools.I18n.tr;
7import static org.openstreetmap.josm.tools.I18n.trn;
8
9import java.awt.Color;
10import java.awt.Component;
11import java.awt.Graphics2D;
12import java.awt.Point;
13import java.awt.event.ActionEvent;
14import java.awt.event.MouseAdapter;
15import java.awt.event.MouseEvent;
16import java.io.File;
17import java.net.URL;
18import java.util.ArrayList;
19import java.util.Collection;
20import java.util.List;
21
22import javax.swing.AbstractAction;
23import javax.swing.Action;
24import javax.swing.Icon;
25import javax.swing.JCheckBoxMenuItem;
26import javax.swing.JOptionPane;
27
28import org.openstreetmap.josm.Main;
29import org.openstreetmap.josm.actions.RenameLayerAction;
30import org.openstreetmap.josm.data.Bounds;
31import org.openstreetmap.josm.data.coor.LatLon;
32import org.openstreetmap.josm.data.gpx.GpxData;
33import org.openstreetmap.josm.data.gpx.GpxLink;
34import org.openstreetmap.josm.data.gpx.WayPoint;
35import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
36import org.openstreetmap.josm.gui.MapView;
37import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
38import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
39import org.openstreetmap.josm.gui.layer.CustomizeColor;
40import org.openstreetmap.josm.gui.layer.GpxLayer;
41import org.openstreetmap.josm.gui.layer.JumpToMarkerActions.JumpToMarkerLayer;
42import org.openstreetmap.josm.gui.layer.JumpToMarkerActions.JumpToNextMarker;
43import org.openstreetmap.josm.gui.layer.JumpToMarkerActions.JumpToPreviousMarker;
44import org.openstreetmap.josm.gui.layer.Layer;
45import org.openstreetmap.josm.tools.AudioPlayer;
46import org.openstreetmap.josm.tools.ImageProvider;
47
48/**
49 * A layer holding markers.
50 *
51 * Markers are GPS points with a name and, optionally, a symbol code attached;
52 * marker layers can be created from waypoints when importing raw GPS data,
53 * but they may also come from other sources.
54 *
55 * The symbol code is for future use.
56 *
57 * The data is read only.
58 */
59public class MarkerLayer extends Layer implements JumpToMarkerLayer {
60
61 /**
62 * A list of markers.
63 */
64 public final List<Marker> data;
65 private boolean mousePressed = false;
66 public GpxLayer fromLayer = null;
67 private Marker currentMarker;
68
69 @Deprecated
70 public MarkerLayer(GpxData indata, String name, File associatedFile, GpxLayer fromLayer, boolean addMouseHandlerInConstructor) {
71 this(indata, name, associatedFile, fromLayer);
72 }
73
74 public MarkerLayer(GpxData indata, String name, File associatedFile, GpxLayer fromLayer) {
75 super(name);
76 this.setAssociatedFile(associatedFile);
77 this.data = new ArrayList<Marker>();
78 this.fromLayer = fromLayer;
79 double firstTime = -1.0;
80 String lastLinkedFile = "";
81
82 for (WayPoint wpt : indata.waypoints) {
83 /* calculate time differences in waypoints */
84 double time = wpt.time;
85 boolean wpt_has_link = wpt.attr.containsKey(GpxData.META_LINKS);
86 if (firstTime < 0 && wpt_has_link) {
87 firstTime = time;
88 for (Object oneLink : wpt.getCollection(GpxData.META_LINKS)) {
89 if (oneLink instanceof GpxLink) {
90 lastLinkedFile = ((GpxLink)oneLink).uri;
91 break;
92 }
93 }
94 }
95 if (wpt_has_link) {
96 for (Object oneLink : wpt.getCollection(GpxData.META_LINKS)) {
97 if (oneLink instanceof GpxLink) {
98 String uri = ((GpxLink)oneLink).uri;
99 if (!uri.equals(lastLinkedFile)) {
100 firstTime = time;
101 }
102 lastLinkedFile = uri;
103 break;
104 }
105 }
106 }
107 Marker m = Marker.createMarker(wpt, indata.storageFile, this, time, time - firstTime);
108 if (m != null) {
109 data.add(m);
110 }
111 }
112 }
113
114 @Override
115 public void hookUpMapView() {
116 Main.map.mapView.addMouseListener(new MouseAdapter() {
117 @Override public void mousePressed(MouseEvent e) {
118 if (e.getButton() != MouseEvent.BUTTON1)
119 return;
120 boolean mousePressedInButton = false;
121 if (e.getPoint() != null) {
122 for (Marker mkr : data) {
123 if (mkr.containsPoint(e.getPoint())) {
124 mousePressedInButton = true;
125 break;
126 }
127 }
128 }
129 if (! mousePressedInButton)
130 return;
131 mousePressed = true;
132 if (isVisible()) {
133 Main.map.mapView.repaint();
134 }
135 }
136 @Override public void mouseReleased(MouseEvent ev) {
137 if (ev.getButton() != MouseEvent.BUTTON1 || ! mousePressed)
138 return;
139 mousePressed = false;
140 if (!isVisible())
141 return;
142 if (ev.getPoint() != null) {
143 for (Marker mkr : data) {
144 if (mkr.containsPoint(ev.getPoint())) {
145 mkr.actionPerformed(new ActionEvent(this, 0, null));
146 }
147 }
148 }
149 Main.map.mapView.repaint();
150 }
151 });
152 }
153
154 /**
155 * Return a static icon.
156 */
157 @Override public Icon getIcon() {
158 return ImageProvider.get("layer", "marker_small");
159 }
160
161 @Override
162 public Color getColor(boolean ignoreCustom)
163 {
164 String name = getName();
165 return Main.pref.getColor(marktr("gps marker"), name != null ? "layer "+name : null, Color.gray);
166 }
167
168 /* for preferences */
169 static public Color getGenericColor()
170 {
171 return Main.pref.getColor(marktr("gps marker"), Color.gray);
172 }
173
174 @Override public void paint(Graphics2D g, MapView mv, Bounds box) {
175 boolean showTextOrIcon = isTextOrIconShown();
176 g.setColor(getColor(true));
177
178 if (mousePressed) {
179 boolean mousePressedTmp = mousePressed;
180 Point mousePos = mv.getMousePosition(); // Get mouse position only when necessary (it's the slowest part of marker layer painting)
181 for (Marker mkr : data) {
182 if (mousePos != null && mkr.containsPoint(mousePos)) {
183 mkr.paint(g, mv, mousePressedTmp, showTextOrIcon);
184 mousePressedTmp = false;
185 }
186 }
187 } else {
188 for (Marker mkr : data) {
189 mkr.paint(g, mv, false, showTextOrIcon);
190 }
191 }
192 }
193
194 @Override public String getToolTipText() {
195 return data.size()+" "+trn("marker", "markers", data.size());
196 }
197
198 @Override public void mergeFrom(Layer from) {
199 MarkerLayer layer = (MarkerLayer)from;
200 data.addAll(layer.data);
201 }
202
203 @Override public boolean isMergable(Layer other) {
204 return other instanceof MarkerLayer;
205 }
206
207 @Override public void visitBoundingBox(BoundingXYVisitor v) {
208 for (Marker mkr : data) {
209 v.visit(mkr.getEastNorth());
210 }
211 }
212
213 @Override public Object getInfoComponent() {
214 return "<html>"+trn("{0} consists of {1} marker", "{0} consists of {1} markers", data.size(), getName(), data.size()) + "</html>";
215 }
216
217 @Override public Action[] getMenuEntries() {
218 Collection<Action> components = new ArrayList<Action>();
219 components.add(LayerListDialog.getInstance().createShowHideLayerAction());
220 components.add(new ShowHideMarkerText(this));
221 components.add(LayerListDialog.getInstance().createDeleteLayerAction());
222 components.add(SeparatorLayerAction.INSTANCE);
223 components.add(new CustomizeColor(this));
224 components.add(SeparatorLayerAction.INSTANCE);
225 components.add(new SynchronizeAudio());
226 if (Main.pref.getBoolean("marker.traceaudio", true)) {
227 components.add (new MoveAudio());
228 }
229 components.add(new JumpToNextMarker(this));
230 components.add(new JumpToPreviousMarker(this));
231 components.add(new RenameLayerAction(getAssociatedFile(), this));
232 components.add(SeparatorLayerAction.INSTANCE);
233 components.add(new LayerListPopup.InfoAction(this));
234 return components.toArray(new Action[0]);
235 }
236
237 public boolean synchronizeAudioMarkers(AudioMarker startMarker) {
238 if (startMarker != null && ! data.contains(startMarker)) {
239 startMarker = null;
240 }
241 if (startMarker == null) {
242 // find the first audioMarker in this layer
243 for (Marker m : data) {
244 if (m instanceof AudioMarker) {
245 startMarker = (AudioMarker) m;
246 break;
247 }
248 }
249 }
250 if (startMarker == null)
251 return false;
252
253 // apply adjustment to all subsequent audio markers in the layer
254 double adjustment = AudioPlayer.position() - startMarker.offset; // in seconds
255 boolean seenStart = false;
256 URL url = startMarker.url();
257 for (Marker m : data) {
258 if (m == startMarker) {
259 seenStart = true;
260 }
261 if (seenStart) {
262 AudioMarker ma = (AudioMarker) m; // it must be an AudioMarker
263 if (ma.url().equals(url)) {
264 ma.adjustOffset(adjustment);
265 }
266 }
267 }
268 return true;
269 }
270
271 public AudioMarker addAudioMarker(double time, LatLon coor) {
272 // find first audio marker to get absolute start time
273 double offset = 0.0;
274 AudioMarker am = null;
275 for (Marker m : data) {
276 if (m.getClass() == AudioMarker.class) {
277 am = (AudioMarker)m;
278 offset = time - am.time;
279 break;
280 }
281 }
282 if (am == null) {
283 JOptionPane.showMessageDialog(
284 Main.parent,
285 tr("No existing audio markers in this layer to offset from."),
286 tr("Error"),
287 JOptionPane.ERROR_MESSAGE
288 );
289 return null;
290 }
291
292 // make our new marker
293 AudioMarker newAudioMarker = new AudioMarker(coor,
294 null, AudioPlayer.url(), this, time, offset);
295
296 // insert it at the right place in a copy the collection
297 Collection<Marker> newData = new ArrayList<Marker>();
298 am = null;
299 AudioMarker ret = newAudioMarker; // save to have return value
300 for (Marker m : data) {
301 if (m.getClass() == AudioMarker.class) {
302 am = (AudioMarker) m;
303 if (newAudioMarker != null && offset < am.offset) {
304 newAudioMarker.adjustOffset(am.syncOffset()); // i.e. same as predecessor
305 newData.add(newAudioMarker);
306 newAudioMarker = null;
307 }
308 }
309 newData.add(m);
310 }
311
312 if (newAudioMarker != null) {
313 if (am != null) {
314 newAudioMarker.adjustOffset(am.syncOffset()); // i.e. same as predecessor
315 }
316 newData.add(newAudioMarker); // insert at end
317 }
318
319 // replace the collection
320 data.clear();
321 data.addAll(newData);
322 return ret;
323 }
324
325 public void jumpToNextMarker() {
326 if (currentMarker == null) {
327 currentMarker = data.get(0);
328 } else {
329 boolean foundCurrent = false;
330 for (Marker m: data) {
331 if (foundCurrent) {
332 currentMarker = m;
333 break;
334 } else if (currentMarker == m) {
335 foundCurrent = true;
336 }
337 }
338 }
339 Main.map.mapView.zoomTo(currentMarker.getEastNorth());
340 }
341
342 public void jumpToPreviousMarker() {
343 if (currentMarker == null) {
344 currentMarker = data.get(data.size() - 1);
345 } else {
346 boolean foundCurrent = false;
347 for (int i=data.size() - 1; i>=0; i--) {
348 Marker m = data.get(i);
349 if (foundCurrent) {
350 currentMarker = m;
351 break;
352 } else if (currentMarker == m) {
353 foundCurrent = true;
354 }
355 }
356 }
357 Main.map.mapView.zoomTo(currentMarker.getEastNorth());
358 }
359
360 public static void playAudio() {
361 playAdjacentMarker(null, true);
362 }
363
364 public static void playNextMarker() {
365 playAdjacentMarker(AudioMarker.recentlyPlayedMarker(), true);
366 }
367
368 public static void playPreviousMarker() {
369 playAdjacentMarker(AudioMarker.recentlyPlayedMarker(), false);
370 }
371
372 private static Marker getAdjacentMarker(Marker startMarker, boolean next, Layer layer) {
373 Marker previousMarker = null;
374 boolean nextTime = false;
375 if (layer.getClass() == MarkerLayer.class) {
376 MarkerLayer markerLayer = (MarkerLayer) layer;
377 for (Marker marker : markerLayer.data) {
378 if (marker == startMarker) {
379 if (next) {
380 nextTime = true;
381 } else {
382 if (previousMarker == null) {
383 previousMarker = startMarker; // if no previous one, play the first one again
384 }
385 return previousMarker;
386 }
387 }
388 else if (marker.getClass() == AudioMarker.class)
389 {
390 if(nextTime || startMarker == null)
391 return marker;
392 previousMarker = marker;
393 }
394 }
395 if (nextTime) // there was no next marker in that layer, so play the last one again
396 return startMarker;
397 }
398 return null;
399 }
400
401 private static void playAdjacentMarker(Marker startMarker, boolean next) {
402 Marker m = null;
403 if (Main.map == null || Main.map.mapView == null)
404 return;
405 Layer l = Main.map.mapView.getActiveLayer();
406 if(l != null) {
407 m = getAdjacentMarker(startMarker, next, l);
408 }
409 if(m == null)
410 {
411 for (Layer layer : Main.map.mapView.getAllLayers())
412 {
413 m = getAdjacentMarker(startMarker, next, layer);
414 if(m != null) {
415 break;
416 }
417 }
418 }
419 if(m != null) {
420 ((AudioMarker)m).play();
421 }
422 }
423
424 /**
425 * Get state of text display.
426 * @return <code>true</code> if text should be shown, <code>false</code> otherwise.
427 */
428 private boolean isTextOrIconShown() {
429 String current = Main.pref.get("marker.show "+getName(),"show");
430 return "show".equalsIgnoreCase(current);
431 }
432
433 public static final class ShowHideMarkerText extends AbstractAction implements LayerAction {
434 private final MarkerLayer layer;
435
436 public ShowHideMarkerText(MarkerLayer layer) {
437 super(tr("Show Text/Icons"), ImageProvider.get("dialogs", "showhide"));
438 putValue(SHORT_DESCRIPTION, tr("Toggle visible state of the marker text and icons."));
439 putValue("help", ht("/Action/ShowHideTextIcons"));
440 this.layer = layer;
441 }
442
443
444 public void actionPerformed(ActionEvent e) {
445 Main.pref.put("marker.show "+layer.getName(), layer.isTextOrIconShown() ? "hide" : "show");
446 Main.map.mapView.repaint();
447 }
448
449
450 @Override
451 public Component createMenuComponent() {
452 JCheckBoxMenuItem showMarkerTextItem = new JCheckBoxMenuItem(this);
453 showMarkerTextItem.setState(layer.isTextOrIconShown());
454 return showMarkerTextItem;
455 }
456
457 @Override
458 public boolean supportLayers(List<Layer> layers) {
459 return layers.size() == 1 && layers.get(0) instanceof MarkerLayer;
460 }
461 }
462
463
464 private class SynchronizeAudio extends AbstractAction {
465
466 public SynchronizeAudio() {
467 super(tr("Synchronize Audio"), ImageProvider.get("audio-sync"));
468 putValue("help", ht("/Action/SynchronizeAudio"));
469 }
470
471 @Override
472 public void actionPerformed(ActionEvent e) {
473 if (! AudioPlayer.paused()) {
474 JOptionPane.showMessageDialog(
475 Main.parent,
476 tr("You need to pause audio at the moment when you hear your synchronization cue."),
477 tr("Warning"),
478 JOptionPane.WARNING_MESSAGE
479 );
480 return;
481 }
482 AudioMarker recent = AudioMarker.recentlyPlayedMarker();
483 if (synchronizeAudioMarkers(recent)) {
484 JOptionPane.showMessageDialog(
485 Main.parent,
486 tr("Audio synchronized at point {0}.", recent.getText()),
487 tr("Information"),
488 JOptionPane.INFORMATION_MESSAGE
489 );
490 } else {
491 JOptionPane.showMessageDialog(
492 Main.parent,
493 tr("Unable to synchronize in layer being played."),
494 tr("Error"),
495 JOptionPane.ERROR_MESSAGE
496 );
497 }
498 }
499 }
500
501 private class MoveAudio extends AbstractAction {
502
503 public MoveAudio() {
504 super(tr("Make Audio Marker at Play Head"), ImageProvider.get("addmarkers"));
505 putValue("help", ht("/Action/MakeAudioMarkerAtPlayHead"));
506 }
507
508 @Override
509 public void actionPerformed(ActionEvent e) {
510 if (! AudioPlayer.paused()) {
511 JOptionPane.showMessageDialog(
512 Main.parent,
513 tr("You need to have paused audio at the point on the track where you want the marker."),
514 tr("Warning"),
515 JOptionPane.WARNING_MESSAGE
516 );
517 return;
518 }
519 PlayHeadMarker playHeadMarker = Main.map.mapView.playHeadMarker;
520 if (playHeadMarker == null)
521 return;
522 addAudioMarker(playHeadMarker.time, playHeadMarker.getCoor());
523 Main.map.mapView.repaint();
524 }
525 }
526
527}
Note: See TracBrowser for help on using the repository browser.