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

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

cleanup color preferences a lot

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