Index: src/org/openstreetmap/josm/actions/OpenAction.java
===================================================================
--- src/org/openstreetmap/josm/actions/OpenAction.java	(revision 199)
+++ src/org/openstreetmap/josm/actions/OpenAction.java	(revision 200)
@@ -18,9 +18,9 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.osm.DataSet;
-import org.openstreetmap.josm.gui.layer.MarkerLayer;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.gui.layer.RawGpsLayer;
-import org.openstreetmap.josm.gui.layer.MarkerLayer.Marker;
 import org.openstreetmap.josm.gui.layer.RawGpsLayer.GpsPoint;
+import org.openstreetmap.josm.gui.layer.markerlayer.Marker;
+import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
 import org.openstreetmap.josm.io.OsmReader;
 import org.openstreetmap.josm.io.RawCsvReader;
Index: src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java
===================================================================
--- src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java	(revision 199)
+++ src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java	(revision 200)
@@ -53,5 +53,5 @@
 	static JList instance;
 	private JScrollPane listScrollPane;
-	
+
 	public final static class DeleteLayerAction extends AbstractAction {
 
@@ -91,4 +91,23 @@
 			Layer l = layer == null ? (Layer)instance.getSelectedValue() : layer;
 			l.visible = !l.visible;
+			Main.map.mapView.repaint();
+			instance.repaint();
+		}
+	}
+
+	public final static class ShowHideMarkerText extends AbstractAction {
+		private final Layer layer;
+
+		public ShowHideMarkerText(Layer layer) {
+			super(tr("Show/Hide Text"), ImageProvider.get("dialogs", "showhide"));
+			putValue(SHORT_DESCRIPTION, tr("Toggle visible state of the marker text."));
+			putValue("help", "Dialog/LayerList/ShowHideText");
+			this.layer = layer;
+		}
+
+		public void actionPerformed(ActionEvent e) {
+			Layer l = layer == null ? (Layer)instance.getSelectedValue() : layer;
+			String current = Main.pref.get("marker.show "+l.name,"show");
+			Main.pref.put("marker.show "+l.name, current.equalsIgnoreCase("show") ? "hide" : "show");
 			Main.map.mapView.repaint();
 			instance.repaint();
Index: src/org/openstreetmap/josm/gui/layer/MarkerLayer.java
===================================================================
--- src/org/openstreetmap/josm/gui/layer/MarkerLayer.java	(revision 199)
+++ 	(revision )
@@ -1,172 +1,0 @@
-package org.openstreetmap.josm.gui.layer;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-import static org.openstreetmap.josm.tools.I18n.trn;
-
-import java.awt.Color;
-import java.awt.Component;
-import java.awt.Graphics;
-import java.awt.Point;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.io.File;
-import java.util.Collection;
-
-import javax.swing.Icon;
-import javax.swing.JColorChooser;
-import javax.swing.JMenuItem;
-import javax.swing.JOptionPane;
-import javax.swing.JSeparator;
-import javax.swing.SwingUtilities;
-
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.actions.RenameLayerAction;
-import org.openstreetmap.josm.data.coor.EastNorth;
-import org.openstreetmap.josm.data.coor.LatLon;
-import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
-import org.openstreetmap.josm.gui.MapView;
-import org.openstreetmap.josm.gui.MapView.LayerChangeListener;
-import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
-import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
-import org.openstreetmap.josm.tools.ColorHelper;
-import org.openstreetmap.josm.tools.ImageProvider;
-
-/**
- * A layer holding markers.
- * 
- * Markers are GPS points with a name and, optionally, a symbol code attached;
- * marker layers can be created from waypoints when importing raw GPS data,
- * but they may also come from other sources.
- * 
- * The symbol code is for future use.
- * 
- * The data is read only.
- */
-public class MarkerLayer extends Layer {
-
-	public static class Marker {
-		public final EastNorth eastNorth;
-		public final String text;
-		public final Icon symbol;
-		public Marker(LatLon ll, String t, String s) {
-			eastNorth = Main.proj.latlon2eastNorth(ll); 
-			text = t;
-			Icon symbol = null;
-			try {
-                symbol = ImageProvider.get("symbols",s);
-            } catch (RuntimeException e) {
-    			try {
-                    symbol = ImageProvider.get("nodes",s);
-                } catch (RuntimeException e2) {
-                }
-            }
-            this.symbol = symbol;
-		}
-	}
-
-	/**
-	 * A list of markers.
-	 */
-	public final Collection<Marker> data;
-
-	public MarkerLayer(Collection<Marker> data, String name, File associatedFile) {
-		super(name);
-		this.associatedFile = associatedFile;
-		this.data = data;
-		SwingUtilities.invokeLater(new Runnable(){
-			public void run() {
-				Main.map.mapView.addLayerChangeListener(new LayerChangeListener(){
-					public void activeLayerChange(Layer oldLayer, Layer newLayer) {}
-					public void layerAdded(Layer newLayer) {}
-					public void layerRemoved(Layer oldLayer) {
-						Main.pref.listener.remove(MarkerLayer.this);
-					}
-				});
-			}
-		});
-	}
-
-	/**
-	 * Return a static icon.
-	 */
-	@Override public Icon getIcon() {
-		return ImageProvider.get("layer", "marker");
-	}
-
-	@Override public void paint(Graphics g, MapView mv) {
-		String mkrCol = Main.pref.get("color.gps marker");
-		String mkrColSpecial = Main.pref.get("color.layer "+name);
-		if (!mkrColSpecial.equals(""))
-			g.setColor(ColorHelper.html2color(mkrColSpecial));
-		else if (!mkrCol.equals(""))
-			g.setColor(ColorHelper.html2color(mkrCol));
-		else
-			g.setColor(Color.GRAY);
-
-		for (Marker mkr : data) {
-			Point screen = mv.getPoint(mkr.eastNorth);
-			if (mkr.symbol != null)
-				mkr.symbol.paintIcon(Main.map.mapView, g, screen.x-mkr.symbol.getIconWidth()/2, screen.y-mkr.symbol.getIconHeight()/2);
-			else {
-				g.drawLine(screen.x-2, screen.y-2, screen.x+2, screen.y+2);
-				g.drawLine(screen.x+2, screen.y-2, screen.x-2, screen.y+2);
-			}
-			g.drawString(mkr.text, screen.x+4, screen.y+2);
-		}
-	}
-
-	@Override public String getToolTipText() {
-		return data.size()+" "+trn("marker", "markers", data.size());
-	}
-
-	@Override public void mergeFrom(Layer from) {
-		MarkerLayer layer = (MarkerLayer)from;
-		data.addAll(layer.data);
-	}
-
-	@Override public boolean isMergable(Layer other) {
-		return other instanceof MarkerLayer;
-	}
-
-	@Override public void visitBoundingBox(BoundingXYVisitor v) {
-		for (Marker mkr : data)
-			v.visit(mkr.eastNorth);
-	}
-
-	@Override public Object getInfoComponent() {
-		return "<html>"+trn("{0} consists of {1} marker", "{0} consists of {1} markers", data.size(), name, data.size()) + "</html>";
-	}
-
-	@Override public Component[] getMenuEntries() {
-		JMenuItem color = new JMenuItem(tr("Customize Color"), ImageProvider.get("colorchooser"));
-		color.addActionListener(new ActionListener(){
-			public void actionPerformed(ActionEvent e) {
-				String col = Main.pref.get("color.layer "+name, Main.pref.get("color.gps marker", ColorHelper.color2html(Color.gray)));
-				JColorChooser c = new JColorChooser(ColorHelper.html2color(col));
-				Object[] options = new Object[]{tr("OK"), tr("Cancel"), tr("Default")};
-				int answer = JOptionPane.showOptionDialog(Main.parent, c, tr("Choose a color"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE, null, options, options[0]);
-				switch (answer) {
-				case 0:
-					Main.pref.put("color.layer "+name, ColorHelper.color2html(c.getColor()));
-					break;
-				case 1:
-					return;
-				case 2:
-					Main.pref.put("color.layer "+name, null);
-					break;
-				}
-				Main.map.repaint();
-			}
-		});
-
-		return new Component[] {
-			new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
-			new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
-			new JSeparator(),
-			color,
-			new JMenuItem(new RenameLayerAction(associatedFile, this)),
-			new JSeparator(),
-			new JMenuItem(new LayerListPopup.InfoAction(this))
-		};
-	}
-}
Index: src/org/openstreetmap/josm/gui/layer/markerlayer/AudioMarker.java
===================================================================
--- src/org/openstreetmap/josm/gui/layer/markerlayer/AudioMarker.java	(revision 200)
+++ src/org/openstreetmap/josm/gui/layer/markerlayer/AudioMarker.java	(revision 200)
@@ -0,0 +1,90 @@
+package org.openstreetmap.josm.gui.layer.markerlayer;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.io.IOException;
+import java.net.URL;
+
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.DataLine;
+import javax.sound.sampled.SourceDataLine;
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.LatLon;
+
+/**
+ * Marker class with audio playback capability.
+ * 
+ * @author Frederik Ramm <frederik@remote.org>
+ *
+ */
+public class AudioMarker extends ButtonMarker {
+
+	private URL audioUrl;
+
+	/**
+	 * Verifies the parameter whether a new AudioMarker can be created and return
+	 * one or return <code>null</code>.
+	 */
+	public static AudioMarker create(LatLon ll, String url) {
+		try {
+			return new AudioMarker(ll, new URL(url));
+		} catch (Exception ex) {
+			return null;
+		}
+	}
+
+	private AudioMarker(LatLon ll, URL audioUrl) {
+		super(ll, "speech.png");
+		this.audioUrl = audioUrl;
+	}
+
+	@Override public void actionPerformed(ActionEvent ev) {
+		AudioInputStream audioInputStream = null;
+		try {
+			audioInputStream = AudioSystem.getAudioInputStream(audioUrl);
+		} catch (Exception e) {
+			audioMalfunction(e);
+			return;
+		}
+		AudioFormat	audioFormat = audioInputStream.getFormat();
+		SourceDataLine line = null;
+		DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
+		try {
+			line = (SourceDataLine) AudioSystem.getLine(info);
+			line.open(audioFormat);
+		} catch (Exception e)	{
+			audioMalfunction(e);
+			return;
+		}
+		line.start();
+
+		int	nBytesRead = 0;
+		byte[]	abData = new byte[16384];
+		while (nBytesRead != -1) {
+			try {
+				nBytesRead = audioInputStream.read(abData, 0, abData.length);
+			} catch (IOException e) {
+				audioMalfunction(e);
+				return;
+			}
+			if (nBytesRead >= 0) {
+				/* int	nBytesWritten = */ line.write(abData, 0, nBytesRead);
+			}
+		}
+		line.drain();
+		line.close();
+	}
+
+	void audioMalfunction(Exception ex) {
+		JOptionPane.showMessageDialog(Main.parent, 
+				"<html><b>" + 
+				tr("There was an error while trying to play the sound file for this marker.") +
+				"</b><br>" + ex.getClass().getName() + ":<br><i>" + ex.getMessage() + "</i></html>",
+				tr("Error playing sound"), JOptionPane.ERROR_MESSAGE);
+	}
+}
Index: src/org/openstreetmap/josm/gui/layer/markerlayer/ButtonMarker.java
===================================================================
--- src/org/openstreetmap/josm/gui/layer/markerlayer/ButtonMarker.java	(revision 200)
+++ src/org/openstreetmap/josm/gui/layer/markerlayer/ButtonMarker.java	(revision 200)
@@ -0,0 +1,56 @@
+package org.openstreetmap.josm.gui.layer.markerlayer;
+
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.Point;
+import java.awt.Rectangle;
+
+import javax.swing.BorderFactory;
+import javax.swing.border.BevelBorder;
+import javax.swing.border.Border;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.gui.MapView;
+
+/**
+ * Marker class with button look-and-feel.
+ * 
+ * @author Frederik Ramm <frederik@remote.org>
+ *
+ */
+public class ButtonMarker extends Marker {
+
+	private Rectangle buttonRectangle;
+	
+	public ButtonMarker(LatLon ll, String buttonImage) {
+		super(ll, null, buttonImage);
+		buttonRectangle = new Rectangle(0, 0, symbol.getIconWidth(), symbol.getIconHeight());
+	}
+	
+	@Override public boolean containsPoint(Point p) {
+		Point screen = Main.map.mapView.getPoint(eastNorth);
+		buttonRectangle.setLocation(screen.x+4, screen.y+2);
+		return buttonRectangle.contains(p);
+	}
+	
+	@Override public void paint(Graphics g, MapView mv, boolean mousePressed, String show) {
+		Point screen = mv.getPoint(eastNorth);
+		buttonRectangle.setLocation(screen.x+4, screen.y+2);
+		symbol.paintIcon(mv, g, screen.x+4, screen.y+2);
+		Border b;
+		Point mousePosition = mv.getMousePosition();
+		if (mousePosition == null)
+			return; // mouse outside the whole window
+		
+		if (mousePressed) {
+			b = BorderFactory.createBevelBorder(BevelBorder.LOWERED);
+		} else {
+			b = BorderFactory.createBevelBorder(BevelBorder.RAISED);
+		}
+		Insets inset = b.getBorderInsets(mv);
+		Rectangle r = new Rectangle(buttonRectangle);
+		r.grow((inset.top+inset.bottom)/2, (inset.left+inset.right)/2);
+		b.paintBorder(mv, g, r.x, r.y, r.width, r.height);
+	}
+}
Index: src/org/openstreetmap/josm/gui/layer/markerlayer/ImageMarker.java
===================================================================
--- src/org/openstreetmap/josm/gui/layer/markerlayer/ImageMarker.java	(revision 200)
+++ src/org/openstreetmap/josm/gui/layer/markerlayer/ImageMarker.java	(revision 200)
@@ -0,0 +1,90 @@
+package org.openstreetmap.josm.gui.layer.markerlayer;
+
+import java.awt.BorderLayout;
+import java.awt.Cursor;
+import java.awt.Image;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.net.URL;
+
+import javax.swing.Icon;
+import javax.swing.ImageIcon;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JToggleButton;
+import javax.swing.JViewport;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+/**
+ * Marker representing an image. Uses a special icon, and when clicked,
+ * displays an image view dialog. Re-uses some code from GeoImageLayer.
+ * 
+ * @author Frederik Ramm <frederik@remote.org>
+ *
+ */
+public class ImageMarker extends ButtonMarker {
+
+	public URL imageUrl;
+
+	public static ImageMarker create(LatLon ll, String url) {
+		try {
+			return new ImageMarker(ll, new URL(url));
+		} catch (Exception ex) {
+			return null;
+		}
+	}
+
+	private ImageMarker(LatLon ll, URL imageUrl) {
+		super(ll, "photo.png");
+		this.imageUrl = imageUrl;
+	}
+
+	@Override public void actionPerformed(ActionEvent ev) {
+		final JPanel p = new JPanel(new BorderLayout());
+		final JScrollPane scroll = new JScrollPane(new JLabel(loadScaledImage(imageUrl, 580)));
+		final JViewport vp = scroll.getViewport();
+		p.add(scroll, BorderLayout.CENTER);
+
+		final JToggleButton scale = new JToggleButton(ImageProvider.get("misc", "rectangle"));
+
+		JPanel p2 = new JPanel();
+		p2.add(scale);
+		p.add(p2, BorderLayout.SOUTH);
+		scale.addActionListener(new ActionListener(){
+			public void actionPerformed(ActionEvent ev) {
+				p.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+				if (scale.getModel().isSelected())
+					((JLabel)vp.getView()).setIcon(loadScaledImage(imageUrl, Math.max(vp.getWidth(), vp.getHeight())));
+				else
+					((JLabel)vp.getView()).setIcon(new ImageIcon(imageUrl));
+				p.setCursor(Cursor.getDefaultCursor());
+			}
+		});
+		scale.setSelected(true);
+		JOptionPane pane = new JOptionPane(p, JOptionPane.PLAIN_MESSAGE);
+		JDialog dlg = pane.createDialog(Main.parent, imageUrl.toString());
+		dlg.setModal(false);
+		dlg.setVisible(true);
+	}
+
+	private static Icon loadScaledImage(URL u, int maxSize) {
+		Image img = new ImageIcon(u).getImage();
+		int w = img.getWidth(null);
+		int h = img.getHeight(null);
+		if (w>h) {
+			h = Math.round(maxSize*((float)h/w));
+			w = maxSize;
+		} else {
+			w = Math.round(maxSize*((float)w/h));
+			h = maxSize;
+		}
+		return new ImageIcon(img.getScaledInstance(w, h, Image.SCALE_SMOOTH));
+	}
+
+}
Index: src/org/openstreetmap/josm/gui/layer/markerlayer/Marker.java
===================================================================
--- src/org/openstreetmap/josm/gui/layer/markerlayer/Marker.java	(revision 200)
+++ src/org/openstreetmap/josm/gui/layer/markerlayer/Marker.java	(revision 200)
@@ -0,0 +1,150 @@
+package org.openstreetmap.josm.gui.layer.markerlayer;
+
+import java.awt.Graphics;
+import java.awt.Point;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+
+import javax.swing.Icon;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+/**
+ * Basic marker class. Requires a position, and supports 
+ * a custom icon and a name.
+ * 
+ * This class is also used to create appropriate Marker-type objects
+ * when waypoints are imported.
+ * 
+ * It hosts a public list object, named makers, containing implementations of
+ * the MarkerMaker interface. Whenever a Marker needs to be created, each 
+ * object in makers is called with the waypoint parameters (Lat/Lon and tag
+ * data), and the first one to return a Marker object wins.
+ * 
+ * By default, one the list contains one default "Maker" implementation that
+ * will create AudioMarkers for .wav files, ImageMarkers for .png/.jpg/.jpeg 
+ * files, and WebMarkers for everything else. (The creation of a WebMarker will
+ * fail if there's no vaild URL in the <link> tag, so it might still make sense
+ * to add Makers for such waypoints at the end of the list.)
+ * 
+ * The default implementation only looks at the value of the <link> tag inside
+ * the <wpt> tag of the GPX file.
+ * 
+ * <h2>HowTo implement a new Marker</h2>
+ * <ul>
+ * <li> Subclass Marker or ButtonMarker and override <code>containsPoint</code>
+ *      if you like to respond to user clicks</li>
+ * <li> Override paint, if you want a custom marker look (not "a label and a symbol")</li>
+ * <li> Implement MarkerCreator to return a new instance of your marker class</li>
+ * <li> In you plugin constructor, add an instance of your MarkerCreator
+ *      implementation either on top or bottom of Marker.markerProducers.
+ *      Add at top, if your marker should overwrite an current marker or at bottom
+ *      if you only add a new marker style.</li>
+ * </ul>
+ * 
+ * @author Frederik Ramm <frederik@remote.org>
+ */
+public class Marker implements ActionListener {
+
+	public final EastNorth eastNorth;
+	public final String text;
+	public final Icon symbol;
+
+	/**
+	 * Plugins can add their Marker creation stuff at the bottom or top of this list
+	 * (depending on whether they want to override default behaviour or just add new
+	 * stuff).
+	 */
+	public static LinkedList<MarkerProducers> markerProducers = new LinkedList<MarkerProducers>();
+
+	// Add one Maker specifying the default behaviour.
+	static {
+		Marker.markerProducers.add(new MarkerProducers() {
+			public Marker createMarker(LatLon ll, Map<String,String> data) {
+				String link = data.get("link");
+				if (link == null)
+					return new Marker(ll, data.get("name"), data.get("symbol"));
+				if (link.endsWith(".wav"))
+					return AudioMarker.create(ll, link);
+				else if (link.endsWith(".png") || link.endsWith(".jpg") || link.endsWith(".jpeg") || link.endsWith(".gif"))
+					return ImageMarker.create(ll, link);
+				else
+					return WebMarker.create(ll, link);
+			}
+		});
+	}
+
+	public Marker(LatLon ll, String text, String iconName) {
+		eastNorth = Main.proj.latlon2eastNorth(ll); 
+		this.text = text;
+		Icon symbol = ImageProvider.getIfAvailable("markers",iconName);
+		if (symbol == null)
+			symbol = ImageProvider.getIfAvailable("symbols",iconName);
+		if (symbol == null)
+			symbol = ImageProvider.getIfAvailable("nodes",iconName);
+		this.symbol = symbol;
+	}
+
+	/**
+	 * Checks whether the marker display area contains the given point.
+	 * Markers not interested in mouse clicks may always return false.
+	 * 
+	 * @param p The point to check
+	 * @return <code>true</code> if the marker "hotspot" contains the point.
+	 */
+	public boolean containsPoint(Point p) {
+		return false;
+	}
+
+	/**
+	 * Called when the mouse is clicked in the marker's hotspot. Never
+	 * called for markers which always return false from containsPoint.
+	 * 
+	 * @param ev A dummy ActionEvent
+	 */
+	public void actionPerformed(ActionEvent ev) {
+	}
+
+	/**
+	 * Paints the marker.
+	 * @param g graphics context
+	 * @param mv map view
+	 * @param mousePressed true if the left mouse button is pressed
+	 */
+	public void paint(Graphics g, MapView mv, boolean mousePressed, String show) {
+		Point screen = mv.getPoint(eastNorth);
+		if (symbol != null) {
+			symbol.paintIcon(mv, g, screen.x-symbol.getIconWidth()/2, screen.y-symbol.getIconHeight()/2);
+		} else {
+			g.drawLine(screen.x-2, screen.y-2, screen.x+2, screen.y+2);
+			g.drawLine(screen.x+2, screen.y-2, screen.x-2, screen.y+2);
+		}
+
+		if ((text != null) && (show.equalsIgnoreCase("show")))
+			g.drawString(text, screen.x+4, screen.y+2);
+	}
+
+	/**
+	 * Returns an object of class Marker or one of its subclasses
+	 * created from the parameters given.
+	 *
+	 * @param ll lat/lon for marker
+	 * @param data hash containing keys and values from the GPX waypoint structure
+	 * @return a new Marker object
+	 */
+	public static Marker createMarker(LatLon ll, HashMap<String,String> data) {
+		for (MarkerProducers maker : Marker.markerProducers) {
+			Marker marker = maker.createMarker(ll, data);
+			if (marker != null)
+				return marker;
+		}
+		return null;
+	}
+}
Index: src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java
===================================================================
--- src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java	(revision 200)
+++ src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java	(revision 200)
@@ -0,0 +1,182 @@
+package org.openstreetmap.josm.gui.layer.markerlayer;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+import static org.openstreetmap.josm.tools.I18n.trn;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Point;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.io.File;
+import java.util.Collection;
+
+import javax.swing.Icon;
+import javax.swing.JColorChooser;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JSeparator;
+import javax.swing.SwingUtilities;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.RenameLayerAction;
+import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.gui.MapView.LayerChangeListener;
+import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
+import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.tools.ColorHelper;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+/**
+ * A layer holding markers.
+ * 
+ * Markers are GPS points with a name and, optionally, a symbol code attached;
+ * marker layers can be created from waypoints when importing raw GPS data,
+ * but they may also come from other sources.
+ * 
+ * The symbol code is for future use.
+ * 
+ * The data is read only.
+ */
+public class MarkerLayer extends Layer {
+
+	/**
+	 * A list of markers.
+	 */
+	public final Collection<Marker> data;
+	private boolean mousePressed = false;
+	
+	public MarkerLayer(Collection<Marker> indata, String name, File associatedFile) {
+		super(name);
+		this.associatedFile = associatedFile;
+		this.data = indata;
+		
+		SwingUtilities.invokeLater(new Runnable(){
+			public void run() {
+				Main.map.mapView.addMouseListener(new MouseAdapter() {
+					@Override public void mousePressed(MouseEvent e) {
+						if (e.getButton() != MouseEvent.BUTTON1)
+							return;
+						mousePressed  = true;
+						if (visible)
+							Main.map.mapView.repaint();
+					}
+					@Override public void mouseReleased(MouseEvent ev) {
+						if (ev.getButton() != MouseEvent.BUTTON1)
+							return;
+						mousePressed = false;
+						if (!visible)
+							return;
+						if (ev.getPoint() != null) {
+							for (Marker mkr : data) {
+								if (mkr.containsPoint(ev.getPoint()))
+									mkr.actionPerformed(new ActionEvent(this, 0, null));
+							}
+						}
+						Main.map.mapView.repaint();
+					}
+				});
+
+				Main.map.mapView.addLayerChangeListener(new LayerChangeListener(){
+					public void activeLayerChange(Layer oldLayer, Layer newLayer) {}
+					public void layerAdded(Layer newLayer) {}
+					public void layerRemoved(Layer oldLayer) {
+						Main.pref.listener.remove(MarkerLayer.this);
+					}
+				});
+			}
+		});
+	}
+
+	/**
+	 * Return a static icon.
+	 */
+	@Override public Icon getIcon() {
+		return ImageProvider.get("layer", "marker");
+	}
+
+	@Override public void paint(Graphics g, MapView mv) {
+		boolean mousePressedTmp = mousePressed;
+		Point mousePos = mv.getMousePosition();
+		String mkrCol = Main.pref.get("color.gps marker");
+		String mkrColSpecial = Main.pref.get("color.layer "+name);
+        String mkrTextShow = Main.pref.get("marker.show "+name, "show");
+
+		if (!mkrColSpecial.equals(""))
+			g.setColor(ColorHelper.html2color(mkrColSpecial));
+		else if (!mkrCol.equals(""))
+			g.setColor(ColorHelper.html2color(mkrCol));
+		else
+			g.setColor(Color.GRAY);
+
+		for (Marker mkr : data) {
+			if (mousePos != null && mkr.containsPoint(mousePos)) {
+				mkr.paint(g, mv, mousePressedTmp, mkrTextShow);
+				mousePressedTmp = false;
+			} else {
+				mkr.paint(g, mv, false, mkrTextShow);
+			}
+		}
+	}
+
+	@Override public String getToolTipText() {
+		return data.size()+" "+trn("marker", "markers", data.size());
+	}
+
+	@Override public void mergeFrom(Layer from) {
+		MarkerLayer layer = (MarkerLayer)from;
+		data.addAll(layer.data);
+	}
+
+	@Override public boolean isMergable(Layer other) {
+		return other instanceof MarkerLayer;
+	}
+
+	@Override public void visitBoundingBox(BoundingXYVisitor v) {
+		for (Marker mkr : data)
+			v.visit(mkr.eastNorth);
+	}
+
+	@Override public Object getInfoComponent() {
+		return "<html>"+trn("{0} consists of {1} marker", "{0} consists of {1} markers", data.size(), name, data.size()) + "</html>";
+	}
+
+	@Override public Component[] getMenuEntries() {
+		JMenuItem color = new JMenuItem(tr("Customize Color"), ImageProvider.get("colorchooser"));
+		color.addActionListener(new ActionListener(){
+			public void actionPerformed(ActionEvent e) {
+				String col = Main.pref.get("color.layer "+name, Main.pref.get("color.gps marker", ColorHelper.color2html(Color.gray)));
+				JColorChooser c = new JColorChooser(ColorHelper.html2color(col));
+				Object[] options = new Object[]{tr("OK"), tr("Cancel"), tr("Default")};
+				int answer = JOptionPane.showOptionDialog(Main.parent, c, tr("Choose a color"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE, null, options, options[0]);
+				switch (answer) {
+				case 0:
+					Main.pref.put("color.layer "+name, ColorHelper.color2html(c.getColor()));
+					break;
+				case 1:
+					return;
+				case 2:
+					Main.pref.put("color.layer "+name, null);
+					break;
+				}
+				Main.map.repaint();
+			}
+		});
+
+		return new Component[] {
+			new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
+            new JMenuItem(new LayerListDialog.ShowHideMarkerText(this)),
+			new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
+			new JSeparator(),
+			color,
+			new JMenuItem(new RenameLayerAction(associatedFile, this)),
+			new JSeparator(),
+			new JMenuItem(new LayerListPopup.InfoAction(this))
+		};
+	}
+}
Index: src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerProducers.java
===================================================================
--- src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerProducers.java	(revision 200)
+++ src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerProducers.java	(revision 200)
@@ -0,0 +1,25 @@
+package org.openstreetmap.josm.gui.layer.markerlayer;
+
+import java.util.Map;
+
+import org.openstreetmap.josm.data.coor.LatLon;
+
+/**
+ * This interface has to be implemented by anyone who wants to create markers.
+ * 
+ * When reading a gpx file, all implementations of MarkerMaker registered with 
+ * the Marker are consecutively called until one returns a Marker object.
+ * 
+ * @author Frederik Ramm <frederik@remote.org>
+ */
+public interface MarkerProducers {
+	/**
+	 * Returns a Marker object if this implementation wants to create one for the
+	 * given input data, or <code>null</code> otherwise.
+	 * 
+	 * @param ll lat/lon for the marker position
+	 * @param data A map of all tags found in the <wpt> node of the gpx file. 
+	 * @return A Marker object, or <code>null</code>.
+	 */
+	public Marker createMarker(LatLon ll, Map<String,String> data);
+}
Index: src/org/openstreetmap/josm/gui/layer/markerlayer/WebMarker.java
===================================================================
--- src/org/openstreetmap/josm/gui/layer/markerlayer/WebMarker.java	(revision 200)
+++ src/org/openstreetmap/josm/gui/layer/markerlayer/WebMarker.java	(revision 200)
@@ -0,0 +1,47 @@
+package org.openstreetmap.josm.gui.layer.markerlayer;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.net.URL;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.tools.OpenBrowser;
+
+/**
+ * Marker class with Web URL activation.
+ * 
+ * @author Frederik Ramm <frederik@remote.org>
+ *
+ */
+public class WebMarker extends ButtonMarker {
+
+	public URL webUrl;
+
+	public static WebMarker create (LatLon ll, String url) {
+		try {
+			return new WebMarker(ll, new URL(url));
+		} catch (Exception ex) {
+			return null;
+		}
+	}
+
+	private WebMarker(LatLon ll, URL webUrl) {
+		super(ll, "web.png");
+		this.webUrl = webUrl;
+	}
+
+	@Override public void actionPerformed(ActionEvent ev) {
+		String error = OpenBrowser.displayUrl(webUrl.toString());
+		if (error != null) {
+			JOptionPane.showMessageDialog(Main.parent, 
+					"<html><b>" + 
+					tr("There was an error while trying to display the URL for this marker") +
+					"</b><br>" + tr("(URL was: ") + webUrl.toString() + ")" + "<br>" + error, 
+					tr("Error displaying URL"), JOptionPane.ERROR_MESSAGE);
+		}
+	}
+}
Index: src/org/openstreetmap/josm/io/RawGpsReader.java
===================================================================
--- src/org/openstreetmap/josm/io/RawGpsReader.java	(revision 199)
+++ src/org/openstreetmap/josm/io/RawGpsReader.java	(revision 200)
@@ -8,10 +8,11 @@
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.Stack;
 
 import org.openstreetmap.josm.data.coor.LatLon;
-import org.openstreetmap.josm.gui.layer.MarkerLayer.Marker;
 import org.openstreetmap.josm.gui.layer.RawGpsLayer.GpsPoint;
+import org.openstreetmap.josm.gui.layer.markerlayer.Marker;
 import org.xml.sax.Attributes;
 import org.xml.sax.SAXException;
@@ -43,6 +44,5 @@
 		private Collection<GpsPoint> current = new LinkedList<GpsPoint>();
 		private LatLon currentLatLon;
-		private String currentTime = "";
-		private String currentName = "";
+		private HashMap<String, String> currentTagValues = new HashMap<String, String>();
 		private Stack<String> tags = new Stack<String>();
 
@@ -50,17 +50,16 @@
 			if (qName.equals("wpt") || qName.equals("trkpt")) {
 				try {
-	                double lat = Double.parseDouble(atts.getValue("lat"));
-	                double lon = Double.parseDouble(atts.getValue("lon"));
-	        		if (Math.abs(lat) > 90)
-	        			throw new SAXException(tr("Data error: lat value \"{0}\" is out of bound.", lat));
-	        		if (Math.abs(lon) > 180)
-	        			throw new SAXException(tr("Data error: lon value \"{0}\" is out of bound.", lon));
-	                currentLatLon = new LatLon(lat, lon);
-                } catch (NumberFormatException e) {
-                	e.printStackTrace();
-	                throw new SAXException(e);
-                }
-                currentTime = "";
-                currentName = "";
+					double lat = Double.parseDouble(atts.getValue("lat"));
+					double lon = Double.parseDouble(atts.getValue("lon"));
+					if (Math.abs(lat) > 90)
+						throw new SAXException(tr("Data error: lat value \"{0}\" is out of bound.", lat));
+					if (Math.abs(lon) > 180)
+						throw new SAXException(tr("Data error: lon value \"{0}\" is out of bound.", lon));
+					currentLatLon = new LatLon(lat, lon);
+				} catch (NumberFormatException e) {
+					e.printStackTrace();
+					throw new SAXException(e);
+				}
+				currentTagValues.clear();
 			}
 			tags.push(qName);
@@ -69,5 +68,5 @@
 		@Override public void characters(char[] ch, int start, int length) {
 			String peek = tags.peek();
-			if (peek.equals("time") || peek.equals("name")) {
+			if (peek.equals("time") || peek.equals("name") || peek.equals("link") || peek.equals("symbol")) {
 				String tag = tags.pop();
 				if (tags.empty() || (!tags.peek().equals("wpt") && !tags.peek().equals("trkpt"))) {
@@ -76,5 +75,10 @@
 				}
 				String contents = new String(ch, start, length);
-				if (peek.equals("time")) currentTime += contents; else currentName += contents;
+				String oldContents = currentTagValues.get(peek);
+				if (oldContents == null) {
+					currentTagValues.put(peek, contents);
+				} else {
+					currentTagValues.put(peek, oldContents + contents);	
+				}
 				tags.push(tag);
 			}
@@ -83,18 +87,15 @@
 		@Override public void endElement(String namespaceURI, String localName, String qName) {
 			if (qName.equals("trkpt")) {
-				current.add(new GpsPoint(currentLatLon, currentTime));
-				currentTime = "";
-				currentName = "";
+				current.add(new GpsPoint(currentLatLon, currentTagValues.get("time")));
+				currentTagValues.clear();
 			} else if (qName.equals("wpt")) {
-				markerData.add(new Marker(currentLatLon, currentName, null));
-				currentTime = "";
-				currentName = "";
+				markerData.add(Marker.createMarker(currentLatLon, currentTagValues));
+				currentTagValues.clear();
 			} else if (qName.equals("trkseg") || qName.equals("trk") || qName.equals("gpx")) {
 				newTrack();
-				currentTime = "";
-				currentName = "";
+				currentTagValues.clear();
 			}
 			tags.pop();
-        }
+		}
 
 		private void newTrack() {
Index: src/org/openstreetmap/josm/tools/ImageProvider.java
===================================================================
--- src/org/openstreetmap/josm/tools/ImageProvider.java	(revision 199)
+++ src/org/openstreetmap/josm/tools/ImageProvider.java	(revision 200)
@@ -52,5 +52,20 @@
 	 */
 	public static ImageIcon get(String subdir, String name) {
-		if (subdir != "")
+		ImageIcon icon = getIfAvailable(subdir, name);
+		if (icon == null) {
+			String ext = name.indexOf('.') != -1 ? "" : ".png";
+			throw new NullPointerException("/images/"+subdir+name+ext+" not found");
+		}
+		return icon;
+	}
+
+	/**
+	 * Like {@link #get(String)}, but does not throw and return <code>null</code>
+	 * in case of nothing is found. Use this, if the image to retrieve is optional.
+	 */
+	public static ImageIcon getIfAvailable(String subdir, String name) {
+		if (name == null)
+			return null;
+		if (subdir == null || subdir != "")
 			subdir += "/";
 		String ext = name.indexOf('.') != -1 ? "" : ".png";
Index: src/org/openstreetmap/josm/tools/OpenBrowser.java
===================================================================
--- src/org/openstreetmap/josm/tools/OpenBrowser.java	(revision 199)
+++ src/org/openstreetmap/josm/tools/OpenBrowser.java	(revision 200)
@@ -2,4 +2,10 @@
 
 import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import javax.swing.JApplet;
+
+import org.openstreetmap.josm.Main;
 
 /**
@@ -13,4 +19,14 @@
 	 */
 	public static String displayUrl(String url) {
+		if (Main.applet) {
+			try {
+				JApplet applet = (JApplet) Main.parent;
+				applet.getAppletContext().showDocument(new URL(url));
+				return null;
+			} catch (MalformedURLException mue) {
+				return mue.getMessage();
+			}
+		}
+
 		String os = System.getProperty("os.name");
 		if (os == null)
@@ -37,8 +53,8 @@
 	private static void linux(String url) throws IOException {
 		try {
-	        Runtime.getRuntime().exec("gnome-open " + url);
-        } catch (IOException e) {
-        	Runtime.getRuntime().exec("kfmclient openURL " + url);
-        }
+			Runtime.getRuntime().exec("gnome-open " + url);
+		} catch (IOException e) {
+			Runtime.getRuntime().exec("kfmclient openURL " + url);
+		}
 	}
 
