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

Last change on this file since 999 was 999, checked in by stoecker, 16 years ago

close bug #1588, code cleanup by bruce89

  • Property svn:eol-style set to native
File size: 12.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.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.associatedFile = 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("link");
83 if (firstTime < 0 && wpt_has_link) {
84 firstTime = time;
85 for (GpxLink oneLink : (Collection<GpxLink>) wpt.attr.get("link")) {
86 lastLinkedFile = oneLink.uri;
87 break;
88 }
89 }
90 if (wpt_has_link) {
91 for (GpxLink oneLink : (Collection<GpxLink>) wpt.attr.get("link")) {
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 @Override public void paint(Graphics g, MapView mv) {
150 boolean mousePressedTmp = mousePressed;
151 Point mousePos = mv.getMousePosition();
152 String mkrTextShow = Main.pref.get("marker.show "+name, "show");
153
154 g.setColor(Main.pref.getColor(marktr("gps marker"), "layer "+name, Color.gray));
155
156 for (Marker mkr : data) {
157 if (mousePos != null && mkr.containsPoint(mousePos)) {
158 mkr.paint(g, mv, mousePressedTmp, mkrTextShow);
159 mousePressedTmp = false;
160 } else {
161 mkr.paint(g, mv, false, mkrTextShow);
162 }
163 }
164 }
165
166 @Override public String getToolTipText() {
167 return data.size()+" "+trn("marker", "markers", data.size());
168 }
169
170 @Override public void mergeFrom(Layer from) {
171 MarkerLayer layer = (MarkerLayer)from;
172 data.addAll(layer.data);
173 }
174
175 @Override public boolean isMergable(Layer other) {
176 return other instanceof MarkerLayer;
177 }
178
179 @Override public void visitBoundingBox(BoundingXYVisitor v) {
180 for (Marker mkr : data)
181 v.visit(mkr.eastNorth);
182 }
183
184 @Override public Object getInfoComponent() {
185 return "<html>"+trn("{0} consists of {1} marker", "{0} consists of {1} markers", data.size(), name, data.size()) + "</html>";
186 }
187
188 @Override public Component[] getMenuEntries() {
189 JMenuItem color = new JMenuItem(tr("Customize Color"), ImageProvider.get("colorchooser"));
190 color.putClientProperty("help", "Action/LayerCustomizeColor");
191 color.addActionListener(new ActionListener(){
192 public void actionPerformed(ActionEvent e) {
193 JColorChooser c = new JColorChooser(Main.pref.getColor(marktr("gps marker"), "layer "+name, Color.gray));
194 Object[] options = new Object[]{tr("OK"), tr("Cancel"), tr("Default")};
195 int answer = JOptionPane.showOptionDialog(Main.parent, c, tr("Choose a color"), JOptionPane.OK_CANCEL_OPTION,
196 JOptionPane.PLAIN_MESSAGE, null, options, options[0]);
197 switch (answer) {
198 case 0:
199 Main.pref.putColor("layer "+name, c.getColor());
200 break;
201 case 1:
202 return;
203 case 2:
204 Main.pref.putColor("layer "+name, null);
205 break;
206 }
207 Main.map.repaint();
208 }
209 });
210
211 JMenuItem syncaudio = new JMenuItem(tr("Synchronize Audio"), ImageProvider.get("audio-sync"));
212 syncaudio.putClientProperty("help", "Action/SynchronizeAudio");
213 syncaudio.addActionListener(new ActionListener(){
214 public void actionPerformed(ActionEvent e) {
215 if (! AudioPlayer.paused()) {
216 JOptionPane.showMessageDialog(Main.parent,tr("You need to pause audio at the moment when you hear your synchronization cue."));
217 return;
218 }
219 AudioMarker recent = AudioMarker.recentlyPlayedMarker();
220 if (synchronizeAudioMarkers(recent)) {
221 JOptionPane.showMessageDialog(Main.parent, tr("Audio synchronized at point {0}.", recent.text));
222 } else {
223 JOptionPane.showMessageDialog(Main.parent,tr("Unable to synchronize in layer being played."));
224 }
225 }
226 });
227
228 JMenuItem moveaudio = new JMenuItem(tr("Make Audio Marker At Play Head"), ImageProvider.get("addmarkers"));
229 moveaudio.putClientProperty("help", "Action/MakeAudioMarkerAtPlayHead");
230 moveaudio.addActionListener(new ActionListener(){
231 public void actionPerformed(ActionEvent e) {
232 if (! AudioPlayer.paused()) {
233 JOptionPane.showMessageDialog(Main.parent,tr("You need to have paused audio at the point on the track where you want the marker."));
234 return;
235 }
236 PlayHeadMarker playHeadMarker = Main.map.mapView.playHeadMarker;
237 if (playHeadMarker == null)
238 return;
239 addAudioMarker(playHeadMarker.time, playHeadMarker.eastNorth);
240 Main.map.mapView.repaint();
241 }
242 });
243
244 Collection<Component> components = new ArrayList<Component>();
245 components.add(new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)));
246 components.add(new JMenuItem(new LayerListDialog.ShowHideMarkerText(this)));
247 components.add(new JMenuItem(new LayerListDialog.DeleteLayerAction(this)));
248 components.add(new JSeparator());
249 components.add(color);
250 components.add(new JSeparator());
251 components.add(syncaudio);
252 if (Main.pref.getBoolean("marker.traceaudio", true)) {
253 components.add (moveaudio);
254 }
255 components.add(new JMenuItem(new RenameLayerAction(associatedFile, this)));
256 components.add(new JSeparator());
257 components.add(new JMenuItem(new LayerListPopup.InfoAction(this)));
258 return components.toArray(new Component[0]);
259 }
260
261 public boolean synchronizeAudioMarkers(AudioMarker startMarker) {
262 if (startMarker != null && ! data.contains(startMarker)) {
263 startMarker = null;
264 }
265 if (startMarker == null) {
266 // find the first audioMarker in this layer
267 for (Marker m : data) {
268 if (m instanceof AudioMarker) {
269 startMarker = (AudioMarker) m;
270 break;
271 }
272 }
273 }
274 if (startMarker == null)
275 return false;
276
277 // apply adjustment to all subsequent audio markers in the layer
278 double adjustment = AudioPlayer.position() - startMarker.offset; // in seconds
279 boolean seenStart = false;
280 URL url = startMarker.url();
281 for (Marker m : data) {
282 if (m == startMarker)
283 seenStart = true;
284 if (seenStart) {
285 AudioMarker ma = (AudioMarker) m; // it must be an AudioMarker
286 if (ma.url().equals(url))
287 ma.adjustOffset(adjustment);
288 }
289 }
290 return true;
291 }
292
293 public AudioMarker addAudioMarker(double time, EastNorth en) {
294 // find first audio marker to get absolute start time
295 double offset = 0.0;
296 AudioMarker am = null;
297 for (Marker m : data) {
298 if (m.getClass() == AudioMarker.class) {
299 am = (AudioMarker)m;
300 offset = time - am.time;
301 break;
302 }
303 }
304 if (am == null) {
305 JOptionPane.showMessageDialog(Main.parent,tr("No existing audio markers in this layer to offset from."));
306 return null;
307 }
308
309 // make our new marker
310 AudioMarker newAudioMarker = AudioMarker.create(Main.proj.eastNorth2latlon(en),
311 AudioMarker.inventName(offset), AudioPlayer.url().toString(), this, time, offset);
312
313 // insert it at the right place in a copy the collection
314 Collection<Marker> newData = new ArrayList<Marker>();
315 am = null;
316 AudioMarker ret = newAudioMarker; // save to have return value
317 for (Marker m : data) {
318 if (m.getClass() == AudioMarker.class) {
319 am = (AudioMarker) m;
320 if (newAudioMarker != null && offset < am.offset) {
321 newAudioMarker.adjustOffset(am.syncOffset()); // i.e. same as predecessor
322 newData.add(newAudioMarker);
323 newAudioMarker = null;
324 }
325 }
326 newData.add(m);
327 }
328
329 if (newAudioMarker != null) {
330 if (am != null)
331 newAudioMarker.adjustOffset(am.syncOffset()); // i.e. same as predecessor
332 newData.add(newAudioMarker); // insert at end
333 }
334
335 // replace the collection
336 data.clear();
337 data.addAll(newData);
338 return ret;
339 }
340
341 public static void playAudio() {
342 if (Main.map == null || Main.map.mapView == null)
343 return;
344 for (Layer layer : Main.map.mapView.getAllLayers()) {
345 if (layer.getClass() == MarkerLayer.class) {
346 MarkerLayer markerLayer = (MarkerLayer) layer;
347 for (Marker marker : markerLayer.data) {
348 if (marker.getClass() == AudioMarker.class) {
349 ((AudioMarker)marker).play();
350 break;
351 }
352 }
353 }
354 }
355 }
356
357 public static void playNextMarker() {
358 playAdjacentMarker(true);
359 }
360
361 public static void playPreviousMarker() {
362 playAdjacentMarker(false);
363 }
364
365 private static void playAdjacentMarker(boolean next) {
366 Marker startMarker = AudioMarker.recentlyPlayedMarker();
367 if (startMarker == null) {
368 // message?
369 return;
370 }
371 Marker previousMarker = null;
372 boolean nextTime = false;
373 if (Main.map == null || Main.map.mapView == null)
374 return;
375 for (Layer layer : Main.map.mapView.getAllLayers()) {
376 if (layer.getClass() == MarkerLayer.class) {
377 MarkerLayer markerLayer = (MarkerLayer) layer;
378 for (Marker marker : markerLayer.data) {
379 if (marker == startMarker) {
380 if (next) {
381 nextTime = true;
382 } else {
383 if (previousMarker == null)
384 previousMarker = startMarker; // if no previous one, play the first one again
385 ((AudioMarker)previousMarker).play();
386 break;
387 }
388 } else if (nextTime && marker.getClass() == AudioMarker.class) {
389 ((AudioMarker)marker).play();
390 return;
391 }
392 if (marker.getClass() == AudioMarker.class)
393 previousMarker = marker;
394 }
395 if (nextTime) {
396 // there was no next marker in that layer, so play the last one again
397 ((AudioMarker)startMarker).play();
398 return;
399 }
400 }
401 }
402 }
403
404}
Note: See TracBrowser for help on using the repository browser.