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

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

I18N

  • Property svn:eol-style set to native
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 {0}", 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 AudioMarker ret = newAudioMarker; // save to have return value
309 for (Marker m : data) {
310 if (m.getClass() == AudioMarker.class) {
311 am = (AudioMarker) m;
312 if (newAudioMarker != null && offset < am.offset) {
313 newAudioMarker.adjustOffset(am.syncOffset()); // i.e. same as predecessor
314 newData.add(newAudioMarker);
315 newAudioMarker = null;
316 }
317 }
318 newData.add(m);
319 }
320
321 if (newAudioMarker != null) {
322 if (am != null)
323 newAudioMarker.adjustOffset(am.syncOffset()); // i.e. same as predecessor
324 newData.add(newAudioMarker); // insert at end
325 }
326
327 // replace the collection
328 data.clear();
329 data.addAll(newData);
330 return ret;
331 }
332
333 public static void playAudio() {
334 if (Main.map == null || Main.map.mapView == null)
335 return;
336 for (Layer layer : Main.map.mapView.getAllLayers()) {
337 if (layer.getClass() == MarkerLayer.class) {
338 MarkerLayer markerLayer = (MarkerLayer) layer;
339 for (Marker marker : markerLayer.data) {
340 if (marker.getClass() == AudioMarker.class) {
341 ((AudioMarker)marker).play();
342 break;
343 }
344 }
345 }
346 }
347 }
348
349 public static void playNextMarker() {
350 playAdjacentMarker(true);
351 }
352
353 public static void playPreviousMarker() {
354 playAdjacentMarker(false);
355 }
356
357 private static void playAdjacentMarker(boolean next) {
358 Marker startMarker = AudioMarker.recentlyPlayedMarker();
359 if (startMarker == null) {
360 // message?
361 return;
362 }
363 Marker previousMarker = null;
364 boolean nextTime = false;
365 if (Main.map == null || Main.map.mapView == null)
366 return;
367 for (Layer layer : Main.map.mapView.getAllLayers()) {
368 if (layer.getClass() == MarkerLayer.class) {
369 MarkerLayer markerLayer = (MarkerLayer) layer;
370 for (Marker marker : markerLayer.data) {
371 if (marker == startMarker) {
372 if (next) {
373 nextTime = true;
374 } else {
375 if (previousMarker == null)
376 previousMarker = startMarker; // if no previous one, play the first one again
377 ((AudioMarker)previousMarker).play();
378 break;
379 }
380 } else if (nextTime && marker.getClass() == AudioMarker.class) {
381 ((AudioMarker)marker).play();
382 return;
383 }
384 if (marker.getClass() == AudioMarker.class)
385 previousMarker = marker;
386 }
387 if (nextTime) {
388 // there was no next marker in that layer, so play the last one again
389 ((AudioMarker)startMarker).play();
390 return;
391 }
392 }
393 }
394 }
395
396}
Note: See TracBrowser for help on using the repository browser.