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

Last change on this file since 1646 was 1646, checked in by stoecker, 15 years ago

cleanup in file access

  • Property svn:eol-style set to native
File size: 15.7 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.Icon;
22import javax.swing.JColorChooser;
23import javax.swing.JMenuItem;
24import javax.swing.JOptionPane;
25import javax.swing.JSeparator;
26import javax.swing.SwingUtilities;
27
28import org.openstreetmap.josm.Main;
29import org.openstreetmap.josm.actions.RenameLayerAction;
30import org.openstreetmap.josm.data.coor.EastNorth;
31import org.openstreetmap.josm.data.gpx.GpxData;
32import org.openstreetmap.josm.data.gpx.GpxLink;
33import org.openstreetmap.josm.data.gpx.WayPoint;
34import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
35import org.openstreetmap.josm.gui.MapView;
36import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
37import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
38import org.openstreetmap.josm.gui.layer.GpxLayer;
39import org.openstreetmap.josm.gui.layer.Layer;
40import org.openstreetmap.josm.tools.AudioPlayer;
41import org.openstreetmap.josm.tools.ImageProvider;
42
43/**
44 * A layer holding markers.
45 *
46 * Markers are GPS points with a name and, optionally, a symbol code attached;
47 * marker layers can be created from waypoints when importing raw GPS data,
48 * but they may also come from other sources.
49 *
50 * The symbol code is for future use.
51 *
52 * The data is read only.
53 */
54public class MarkerLayer extends Layer {
55
56 /**
57 * A list of markers.
58 */
59 public final Collection<Marker> data;
60 private boolean mousePressed = false;
61 public GpxLayer fromLayer = null;
62
63 /*
64 private Icon audioTracerIcon = null;
65 private EastNorth playheadPosition = null;
66 private static Timer timer = null;
67 private static double audioAnimationInterval = 0.0; // seconds
68 private static double playheadTime = -1.0;
69 */
70 public MarkerLayer(GpxData indata, String name, File associatedFile, GpxLayer fromLayer) {
71
72 super(name);
73 this.setAssociatedFile(associatedFile);
74 this.data = new ArrayList<Marker>();
75 this.fromLayer = fromLayer;
76 double firstTime = -1.0;
77 String lastLinkedFile = "";
78
79 for (WayPoint wpt : indata.waypoints) {
80 /* calculate time differences in waypoints */
81 double time = wpt.time;
82 boolean wpt_has_link = wpt.attr.containsKey(GpxData.META_LINKS);
83 if (firstTime < 0 && wpt_has_link) {
84 firstTime = time;
85 for (GpxLink oneLink : (Collection<GpxLink>) wpt.attr.get(GpxData.META_LINKS)) {
86 lastLinkedFile = oneLink.uri;
87 break;
88 }
89 }
90 if (wpt_has_link) {
91 for (GpxLink oneLink : (Collection<GpxLink>) wpt.attr.get(GpxData.META_LINKS)) {
92 if (!oneLink.uri.equals(lastLinkedFile))firstTime = time;
93 lastLinkedFile = oneLink.uri;
94 break;
95 }
96 }
97 Marker m = Marker.createMarker(wpt, indata.storageFile, this, time, time - firstTime);
98 if (m != null)
99 data.add(m);
100 }
101
102 SwingUtilities.invokeLater(new Runnable(){
103 public void run() {
104 Main.map.mapView.addMouseListener(new MouseAdapter() {
105 @Override public void mousePressed(MouseEvent e) {
106 if (e.getButton() != MouseEvent.BUTTON1)
107 return;
108 boolean mousePressedInButton = false;
109 if (e.getPoint() != null) {
110 for (Marker mkr : data) {
111 if (mkr.containsPoint(e.getPoint())) {
112 mousePressedInButton = true;
113 break;
114 }
115 }
116 }
117 if (! mousePressedInButton)
118 return;
119 mousePressed = true;
120 if (visible)
121 Main.map.mapView.repaint();
122 }
123 @Override public void mouseReleased(MouseEvent ev) {
124 if (ev.getButton() != MouseEvent.BUTTON1 || ! mousePressed)
125 return;
126 mousePressed = false;
127 if (!visible)
128 return;
129 if (ev.getPoint() != null) {
130 for (Marker mkr : data) {
131 if (mkr.containsPoint(ev.getPoint()))
132 mkr.actionPerformed(new ActionEvent(this, 0, null));
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 "+name, "show");
158
159 g.setColor(getColor(name));
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.eastNorth);
187 }
188
189 @Override public Object getInfoComponent() {
190 return "<html>"+trn("{0} consists of {1} marker", "{0} consists of {1} markers", data.size(), name, data.size()) + "</html>";
191 }
192
193 @Override public Component[] getMenuEntries() {
194 JMenuItem color = new JMenuItem(tr("Customize Color"), ImageProvider.get("colorchooser"));
195 color.putClientProperty("help", "Action/LayerCustomizeColor");
196 color.addActionListener(new ActionListener(){
197 public void actionPerformed(ActionEvent e) {
198 JColorChooser c = new JColorChooser(getColor(name));
199 Object[] options = new Object[]{tr("OK"), tr("Cancel"), tr("Default")};
200 int answer = JOptionPane.showOptionDialog(Main.parent, c, tr("Choose a color"), JOptionPane.OK_CANCEL_OPTION,
201 JOptionPane.PLAIN_MESSAGE, null, options, options[0]);
202 switch (answer) {
203 case 0:
204 Main.pref.putColor("layer "+name, c.getColor());
205 break;
206 case 1:
207 return;
208 case 2:
209 Main.pref.putColor("layer "+name, null);
210 break;
211 }
212 Main.map.repaint();
213 }
214 });
215
216 JMenuItem syncaudio = new JMenuItem(tr("Synchronize Audio"), ImageProvider.get("audio-sync"));
217 syncaudio.putClientProperty("help", "Action/SynchronizeAudio");
218 syncaudio.addActionListener(new ActionListener(){
219 public void actionPerformed(ActionEvent e) {
220 if (! AudioPlayer.paused()) {
221 JOptionPane.showMessageDialog(Main.parent,tr("You need to pause audio at the moment when you hear your synchronization cue."));
222 return;
223 }
224 AudioMarker recent = AudioMarker.recentlyPlayedMarker();
225 if (synchronizeAudioMarkers(recent)) {
226 JOptionPane.showMessageDialog(Main.parent, tr("Audio synchronized at point {0}.", recent.text));
227 } else {
228 JOptionPane.showMessageDialog(Main.parent,tr("Unable to synchronize in layer being played."));
229 }
230 }
231 });
232
233 JMenuItem moveaudio = new JMenuItem(tr("Make Audio Marker at Play Head"), ImageProvider.get("addmarkers"));
234 moveaudio.putClientProperty("help", "Action/MakeAudioMarkerAtPlayHead");
235 moveaudio.addActionListener(new ActionListener(){
236 public void actionPerformed(ActionEvent e) {
237 if (! AudioPlayer.paused()) {
238 JOptionPane.showMessageDialog(Main.parent,tr("You need to have paused audio at the point on the track where you want the marker."));
239 return;
240 }
241 PlayHeadMarker playHeadMarker = Main.map.mapView.playHeadMarker;
242 if (playHeadMarker == null)
243 return;
244 addAudioMarker(playHeadMarker.time, playHeadMarker.eastNorth);
245 Main.map.mapView.repaint();
246 }
247 });
248
249 Collection<Component> components = new ArrayList<Component>();
250 components.add(new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)));
251 components.add(new JMenuItem(new LayerListDialog.ShowHideMarkerText(this)));
252 components.add(new JMenuItem(new LayerListDialog.DeleteLayerAction(this)));
253 components.add(new JSeparator());
254 components.add(color);
255 components.add(new JSeparator());
256 components.add(syncaudio);
257 if (Main.pref.getBoolean("marker.traceaudio", true)) {
258 components.add (moveaudio);
259 }
260 components.add(new JMenuItem(new RenameLayerAction(getAssociatedFile(), this)));
261 components.add(new JSeparator());
262 components.add(new JMenuItem(new LayerListPopup.InfoAction(this)));
263 return components.toArray(new Component[0]);
264 }
265
266 public boolean synchronizeAudioMarkers(AudioMarker startMarker) {
267 if (startMarker != null && ! data.contains(startMarker)) {
268 startMarker = null;
269 }
270 if (startMarker == null) {
271 // find the first audioMarker in this layer
272 for (Marker m : data) {
273 if (m instanceof AudioMarker) {
274 startMarker = (AudioMarker) m;
275 break;
276 }
277 }
278 }
279 if (startMarker == null)
280 return false;
281
282 // apply adjustment to all subsequent audio markers in the layer
283 double adjustment = AudioPlayer.position() - startMarker.offset; // in seconds
284 boolean seenStart = false;
285 URL url = startMarker.url();
286 for (Marker m : data) {
287 if (m == startMarker)
288 seenStart = true;
289 if (seenStart) {
290 AudioMarker ma = (AudioMarker) m; // it must be an AudioMarker
291 if (ma.url().equals(url))
292 ma.adjustOffset(adjustment);
293 }
294 }
295 return true;
296 }
297
298 public AudioMarker addAudioMarker(double time, EastNorth en) {
299 // find first audio marker to get absolute start time
300 double offset = 0.0;
301 AudioMarker am = null;
302 for (Marker m : data) {
303 if (m.getClass() == AudioMarker.class) {
304 am = (AudioMarker)m;
305 offset = time - am.time;
306 break;
307 }
308 }
309 if (am == null) {
310 JOptionPane.showMessageDialog(Main.parent,tr("No existing audio markers in this layer to offset from."));
311 return null;
312 }
313
314 // make our new marker
315 AudioMarker newAudioMarker = AudioMarker.create(Main.proj.eastNorth2latlon(en),
316 AudioMarker.inventName(offset), AudioPlayer.url().toString(), this, time, offset);
317
318 // insert it at the right place in a copy the collection
319 Collection<Marker> newData = new ArrayList<Marker>();
320 am = null;
321 AudioMarker ret = newAudioMarker; // save to have return value
322 for (Marker m : data) {
323 if (m.getClass() == AudioMarker.class) {
324 am = (AudioMarker) m;
325 if (newAudioMarker != null && offset < am.offset) {
326 newAudioMarker.adjustOffset(am.syncOffset()); // i.e. same as predecessor
327 newData.add(newAudioMarker);
328 newAudioMarker = null;
329 }
330 }
331 newData.add(m);
332 }
333
334 if (newAudioMarker != null) {
335 if (am != null)
336 newAudioMarker.adjustOffset(am.syncOffset()); // i.e. same as predecessor
337 newData.add(newAudioMarker); // insert at end
338 }
339
340 // replace the collection
341 data.clear();
342 data.addAll(newData);
343 return ret;
344 }
345
346 public static void playAudio() {
347 if (Main.map == null || Main.map.mapView == null)
348 return;
349 for (Layer layer : Main.map.mapView.getAllLayers()) {
350 if (layer.getClass() == MarkerLayer.class) {
351 MarkerLayer markerLayer = (MarkerLayer) layer;
352 for (Marker marker : markerLayer.data) {
353 if (marker.getClass() == AudioMarker.class) {
354 ((AudioMarker)marker).play();
355 break;
356 }
357 }
358 }
359 }
360 }
361
362 public static void playNextMarker() {
363 playAdjacentMarker(true);
364 }
365
366 public static void playPreviousMarker() {
367 playAdjacentMarker(false);
368 }
369
370 private static void playAdjacentMarker(boolean next) {
371 Marker startMarker = AudioMarker.recentlyPlayedMarker();
372 if (startMarker == null) {
373 // message?
374 return;
375 }
376 Marker previousMarker = null;
377 boolean nextTime = false;
378 if (Main.map == null || Main.map.mapView == null)
379 return;
380 for (Layer layer : Main.map.mapView.getAllLayers()) {
381 if (layer.getClass() == MarkerLayer.class) {
382 MarkerLayer markerLayer = (MarkerLayer) layer;
383 for (Marker marker : markerLayer.data) {
384 if (marker == startMarker) {
385 if (next) {
386 nextTime = true;
387 } else {
388 if (previousMarker == null)
389 previousMarker = startMarker; // if no previous one, play the first one again
390 ((AudioMarker)previousMarker).play();
391 break;
392 }
393 } else if (nextTime && marker.getClass() == AudioMarker.class) {
394 ((AudioMarker)marker).play();
395 return;
396 }
397 if (marker.getClass() == AudioMarker.class)
398 previousMarker = marker;
399 }
400 if (nextTime) {
401 // there was no next marker in that layer, so play the last one again
402 ((AudioMarker)startMarker).play();
403 return;
404 }
405 }
406 }
407 }
408
409}
Note: See TracBrowser for help on using the repository browser.