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

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