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

Last change on this file since 2017 was 2017, checked in by Gubaer, 15 years ago

removed OptionPaneUtil
cleanup of deprecated Layer API
cleanup of deprecated APIs in OsmPrimitive and Way
cleanup of imports

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