Index: applications/editors/josm/plugins/surveyor/build.xml
===================================================================
--- applications/editors/josm/plugins/surveyor/build.xml	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/build.xml	(revision 2936)
@@ -0,0 +1,53 @@
+<project name="surveyor" default="dist" basedir=".">
+
+	<property name="josm.build.dir" value="../JOSM"/>
+	<property name="josm.home.dir" value="${user.home}/.josm"/>
+	<property name="plugin.build.dir" value="bin"/>
+
+
+	
+	<target name="dist" depends="compile">
+		<!-- images -->
+		<copy todir="${plugin.build.dir}/images">
+			<fileset dir="src/images" />
+		</copy>
+			<!-- copy configuration xml files -->
+		<copy todir="${plugin.build.dir}">
+			<fileset dir="src"> 
+				<include name="*.xml"/>
+  		</fileset>
+		</copy>
+		
+		<!-- create josm-custom.jar -->
+		<jar destfile="${ant.project.name}.jar" basedir="${plugin.build.dir}">
+			<manifest>
+                <attribute name="Plugin-Class" value="at.dallermassl.josm.plugin.surveyor.SurveyorPlugin" />
+                <attribute name="Plugin-Description" value="Allow adding markers/nodes on current gps positions." />
+			</manifest>
+		</jar>
+	</target>
+
+	<target name="compile" depends="init">
+		<mkdir dir="bin"/>
+		<javac srcdir="src" destdir="${plugin.build.dir}" debug="true" source="1.5" target="1.5">
+			<classpath>
+				<pathelement path="${josm.build.dir}/build"/>
+	      <fileset dir="${josm.build.dir}/lib">
+	        <include name="**/*.jar"/>
+	      </fileset>
+			</classpath>
+	  </javac>
+	</target>
+
+	<target name="install" depends="dist">
+		<copy file="${ant.project.name}.jar" todir="${josm.home.dir}/plugins" />
+	</target>
+
+	<target name="init">
+	</target>
+
+	<target name="clean">
+		<delete dir="${plugin.build.dir}" />
+	</target>
+
+</project>
Index: applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/ActionConstants.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/ActionConstants.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/ActionConstants.java	(revision 2936)
@@ -0,0 +1,19 @@
+package at.dallermassl.josm.plugin.surveyor;
+
+import javax.swing.Action;
+
+/**
+ * Definition of constants for actions (as {@link Action#SELECTED_KEY} only exists since java 1.6
+ * Followed tutorial at http://www.javalobby.org/java/forums/t53484.html
+ * @author cdaller
+ *
+ */
+public final class ActionConstants {
+ 
+	/**
+	 * 
+	 */
+	private ActionConstants() { }
+
+    public static final String SELECTED_KEY = "actionConstants.selected";
+}
Index: applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/AutoSaveAction.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/AutoSaveAction.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/AutoSaveAction.java	(revision 2936)
@@ -0,0 +1,65 @@
+/**
+ * 
+ */
+package at.dallermassl.josm.plugin.surveyor;
+
+import java.awt.event.ActionEvent;
+import java.text.MessageFormat;
+import java.util.Date;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import javax.swing.AbstractAction;
+import javax.swing.AbstractButton;
+
+import at.dallermassl.josm.plugin.surveyor.action.SetWaypointAction;
+
+import livegps.LiveGpsLayer;
+
+/**
+ * @author cdaller
+ *
+ */
+public class AutoSaveAction extends AbstractAction {
+    private static final long AUTO_SAVE_PERIOD_SEC = 60; // once a minute
+    public static final String GPS_FILE_NAME_PATTERN = "surveyor-{0,date,yyyyMMdd-HHmmss}.gpx";
+    public static final String OSM_FILE_NAME_PATTERN = "surveyor-{0,date,yyyyMMdd-HHmmss}.osm";
+    private boolean autoSave = false;
+    private Timer gpsDataTimer;
+    
+    
+    public AutoSaveAction(String name) {
+        super(name);
+    }
+
+    /* (non-Javadoc)
+     * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
+     */
+    public void actionPerformed(ActionEvent e) {
+        if (e.getSource() instanceof AbstractButton) {
+            autoSave = ((AbstractButton)e.getSource()).isSelected();
+        }
+        
+        if(autoSave) {
+            if(gpsDataTimer == null) {
+                gpsDataTimer = new Timer();
+            }
+            TimerTask task;
+            
+            String gpxFilename = MessageFormat.format(GPS_FILE_NAME_PATTERN, new Date());
+            task = new AutoSaveGpsAndMarkerLayerTimeTask(gpxFilename, 
+                LiveGpsLayer.LAYER_NAME, SetWaypointAction.MARKER_LAYER_NAME);
+            gpsDataTimer.schedule(task, 1000, AUTO_SAVE_PERIOD_SEC * 1000);
+            
+            String osmFilename = MessageFormat.format(OSM_FILE_NAME_PATTERN, new Date());
+            task = new AutoSaveEditLayerTimerTask(osmFilename);
+            gpsDataTimer.schedule(task, 5000, AUTO_SAVE_PERIOD_SEC * 1000);
+        } else {
+            if(gpsDataTimer != null) {
+                gpsDataTimer.cancel();
+            }
+        }
+        
+        
+    }
+}
Index: applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/AutoSaveEditLayerTimerTask.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/AutoSaveEditLayerTimerTask.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/AutoSaveEditLayerTimerTask.java	(revision 2936)
@@ -0,0 +1,65 @@
+/**
+ * 
+ */
+package at.dallermassl.josm.plugin.surveyor;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.TimerTask;
+
+import javax.swing.JOptionPane;
+
+import livegps.LiveGpsLock;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.io.OsmWriter;
+import org.openstreetmap.josm.io.XmlWriter;
+
+/**
+ * @author cdaller
+ *
+ */
+public class AutoSaveEditLayerTimerTask extends TimerTask {
+    private File file;
+    
+    public AutoSaveEditLayerTimerTask(String filename) {
+        file = new File(filename);
+    }
+
+    /* (non-Javadoc)
+     * @see java.util.TimerTask#run()
+     */
+    @Override
+    public void run() {
+        if(Main.map == null || Main.map.mapView == null || Main.map.mapView.editLayer == null) {
+            return;
+        }
+        OsmDataLayer layer = Main.map.mapView.editLayer;
+        try {
+            DataSet dataset = layer.data;
+
+            File outFile = layer.associatedFile;
+            if(outFile == null) {
+                outFile = file;
+            }
+            System.out.println("AutoSaving osm data to file " + outFile.getAbsolutePath());
+            synchronized(LiveGpsLock.class) {
+                XmlWriter.output(new FileOutputStream(outFile), new OsmWriter.All(dataset, false));
+            }
+            System.out.println("AutoSaving finished");
+        } catch (IOException x) {
+            x.printStackTrace();
+            JOptionPane.showMessageDialog(Main.parent, 
+                tr("Error while exporting {0}", file.getAbsoluteFile())+":\n" + x.getMessage(), 
+                tr("Error"), 
+                JOptionPane.ERROR_MESSAGE);
+        }       
+    }
+
+
+}
Index: applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/AutoSaveGpsAndMarkerLayerTimeTask.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/AutoSaveGpsAndMarkerLayerTimeTask.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/AutoSaveGpsAndMarkerLayerTimeTask.java	(revision 2936)
@@ -0,0 +1,56 @@
+/**
+ * 
+ */
+package at.dallermassl.josm.plugin.surveyor;
+
+import java.util.Collection;
+
+import org.openstreetmap.josm.gui.layer.RawGpsLayer;
+import org.openstreetmap.josm.gui.layer.markerlayer.Marker;
+import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
+import org.openstreetmap.josm.io.XmlWriter.OsmWriterInterface;
+
+/**
+ * TimerTask that writes the data of a {@link RawGpsLayer} and from a {@link MarkerLayer} to a 
+ * gpx file.
+ * Every time the task is run, the layers are retrieved from the map view so it even works
+ * if the layer does not exist at the start of the timer task. If the layer does not exist,
+ * the file is not written.
+ * 
+ * @author cdaller
+ *
+ */
+public class AutoSaveGpsAndMarkerLayerTimeTask extends AutoSaveGpsLayerTimerTask {
+    private String markerLayerName;
+
+    /**
+     * Constructor using the file to write to and the names of the layers.
+     * @param filename the file to write to.
+     * @param gpsLayername the name of the layer holding the gps data.
+     * @param markerLayerName the name of the layer holding markers. 
+     */
+    public AutoSaveGpsAndMarkerLayerTimeTask(String filename, String gpsLayername, String markerLayerName) {
+        super(filename, gpsLayername);
+        this.markerLayerName = markerLayerName;
+    }
+
+    /* (non-Javadoc)
+     * @see at.dallermassl.josm.plugin.surveyor.AutoSaveGpsLayerTimerTask#getXmlWriter()
+     */
+    @Override
+    public OsmWriterInterface getXmlWriter() {
+        Collection<Collection<RawGpsLayer.GpsPoint>> gpsPoints = null;
+        Collection<Marker> markers = null;
+        
+        RawGpsLayer gpsLayer = findGpsLayer(getGpsLayerName(), RawGpsLayer.class);
+        if(gpsLayer != null) {
+            gpsPoints = gpsLayer.data;
+        }
+        MarkerLayer markerLayer = findGpsLayer(markerLayerName, MarkerLayer.class);
+        if(markerLayer != null) {
+            markers = markerLayer.data;
+        }
+        return new GpxTrackMarkerWriter(gpsPoints, markers);
+    }
+
+}
Index: applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/AutoSaveGpsLayerTimerTask.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/AutoSaveGpsLayerTimerTask.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/AutoSaveGpsLayerTimerTask.java	(revision 2936)
@@ -0,0 +1,118 @@
+/**
+ * 
+ */
+package at.dallermassl.josm.plugin.surveyor;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.TimerTask;
+
+import javax.swing.JOptionPane;
+
+import livegps.LiveGpsLayer;
+import livegps.LiveGpsLock;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.RawGpsLayer;
+import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
+import org.openstreetmap.josm.io.GpxWriter;
+import org.openstreetmap.josm.io.XmlWriter;
+
+/**
+ * TimerTask that writes the data of a {@link RawGpsLayer} to a gpx file.
+ * Every time the task is run, the layer is retrieved from the map view so it even works
+ * if the layer does not exist at the start of the timer task. If the layer does not exist,
+ * the file is not written.
+ * 
+ * @author cdaller
+ *
+ */
+public class AutoSaveGpsLayerTimerTask extends TimerTask {
+    private String gpsLayerName;
+    private File file;
+    
+    /**
+     * Constructor using the file to write to and the name of the layer.
+     * @param filename the file to write to.
+     * @param gpsLayername the name of the layer holding the gps data.
+     */
+    public AutoSaveGpsLayerTimerTask(String filename, String layerName) {
+        super();
+        this.gpsLayerName = layerName;
+        this.file = new File(filename);
+    }
+    
+    /**
+     * @return the gpsLayerName
+     */
+    public String getGpsLayerName() {
+        return this.gpsLayerName;
+    }
+
+
+    /* (non-Javadoc)
+     * @see java.util.TimerTask#run()
+     */
+    @Override
+    public void run() {
+        System.out.println("AutoSaving data to file " + file.getAbsolutePath());
+
+        try {
+            XmlWriter.OsmWriterInterface writer = getXmlWriter();
+            if(writer != null) {
+                // synchronize on layer to prevent concurrent adding of data to the layer
+                // quite a hack, but no other object to sync available :-(
+                // @see LiveGpsLayer
+                synchronized(LiveGpsLock.class) {
+                    XmlWriter.output(new FileOutputStream(file), writer);
+                }                
+            }
+        } catch (IOException x) {
+            x.printStackTrace();
+            JOptionPane.showMessageDialog(Main.parent, 
+                tr("Error while exporting {0}", file.getAbsoluteFile())+":\n" + x.getMessage(), 
+                tr("Error"), 
+                JOptionPane.ERROR_MESSAGE);
+        }       
+    }
+    
+    /**
+     * Returns the layer with the given name and type from the map view or <code>null</code>.
+     * @param <LayerType> the type of the layer.
+     * @param layerName the name of the layer.
+     * @param layerType the type of the layer.
+     * @return the layer or <code>null</code>.
+     */
+    public <LayerType extends Layer> LayerType findGpsLayer(String layerName, Class<LayerType> layerType) {
+        LayerType result = null;
+        if(Main.map != null && Main.map.mapView != null) {
+            for(Layer layer : Main.map.mapView.getAllLayers()) {
+                if(layerName.equals(layer.name) && layerType.isAssignableFrom(layer.getClass())) {
+                    result = (LayerType) layer;
+                    break;
+                }
+            }
+        }
+        return result;
+    }
+    
+    
+    /**
+     * Returns the writer that writes the data. Override this method, if another type
+     * of writer should be used.
+     * @return the writer.
+     */
+    public XmlWriter.OsmWriterInterface getXmlWriter() {
+        RawGpsLayer gpsLayer = findGpsLayer(gpsLayerName, RawGpsLayer.class);
+        if(gpsLayer == null) {
+            return null;
+        }
+        return new GpxWriter.Trk(gpsLayer.data);
+    }
+
+
+}
Index: applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/ButtonDescription.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/ButtonDescription.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/ButtonDescription.java	(revision 2936)
@@ -0,0 +1,246 @@
+/**
+ * 
+ */
+package at.dallermassl.josm.plugin.surveyor;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.swing.AbstractButton;
+import javax.swing.Action;
+import javax.swing.ActionMap;
+import javax.swing.ComponentInputMap;
+import javax.swing.Icon;
+import javax.swing.InputMap;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JToggleButton;
+import javax.swing.KeyStroke;
+import javax.swing.SwingUtilities;
+import javax.swing.plaf.ActionMapUIResource;
+
+import org.openstreetmap.josm.tools.ImageProvider;
+
+/**
+ * @author cdaller
+ *
+ */
+public class ButtonDescription {
+    private String label;
+    private String hotkey;
+    private String iconName;
+    private ButtonType type;
+    private List<SurveyorActionDescription> actions;
+    private GpsDataSource gpsDataSource;
+
+    /**
+     * Default Constructor 
+     */
+    public ButtonDescription() {
+        super();
+    }
+    /**
+     * @param hotkey
+     * @param actions a list of actions to be performed.
+     * @param type if <code>null</code> {@link ButtonType#SINGLE} is used. 
+     */
+    public ButtonDescription(String label, String hotkey, String iconName, String buttonAction, ButtonType type) {
+        this(label, hotkey, iconName, createFromOneElement(new SurveyorActionDescription(buttonAction)), type);
+    }
+
+    /**
+     * @param hotkey
+     * @param actions a list of actions to be performed.
+     * @param type if <code>null</code> {@link ButtonType#SINGLE} is used. 
+     */
+    public ButtonDescription(String label, String hotkey, String iconName, SurveyorActionDescription actionDescription, ButtonType type) {
+        this(label, hotkey, iconName, createFromOneElement(actionDescription), type);
+    }
+
+    /**
+     * Helper method to create a list from one element.
+     * @param buttonActionClassName the action's class name.
+     * @return a list holding one ButtonActionDescription element.
+     */
+    private static List<SurveyorActionDescription> createFromOneElement(SurveyorActionDescription actionDescription) {
+        List<SurveyorActionDescription> list = new ArrayList<SurveyorActionDescription>();
+        list.add(actionDescription);
+        return list;
+    }
+    
+    /**
+     * @param hotkey
+     * @param actions a list of actions to be performed.
+     * @param type if <code>null</code> {@link ButtonType#SINGLE} is used. 
+     */
+    public ButtonDescription(String label, String hotkey, String iconName, List<SurveyorActionDescription> actions, ButtonType type) {
+        super();
+        this.label = label;
+        this.hotkey = hotkey;
+        this.iconName = iconName;
+        this.type = type == null ? ButtonType.SINGLE : type;
+        this.actions = actions;
+    }
+
+    /**
+     * @return the actions
+     */
+    public List<SurveyorActionDescription> getActions() {
+        return this.actions;
+    }
+    /**
+     * @param actions the actions to set
+     */
+    public void setActions(List<SurveyorActionDescription> actions) {
+        this.actions = actions;
+    }
+    /**
+     * @return the hotkey
+     */
+    public String getHotkey() {
+        return this.hotkey;
+    }
+    /**
+     * @param hotkey the hotkey to set
+     */
+    public void setHotkey(String hotkey) {
+        this.hotkey = hotkey;
+    }
+    /**
+     * @return the label
+     */
+    public String getLabel() {
+        return this.label;
+    }
+
+    /**
+     * @param label the label to set
+     */
+    public void setLabel(String label) {
+        this.label = label;
+    }
+
+    /**
+     * @return the type
+     */
+    public ButtonType getType() {
+        return this.type;
+    }
+    
+    /**
+     * Set the button type as a string.
+     * @param type the type of the button
+     * @see ButtonType
+     */
+    public void setType(String type) {
+        try {
+        this.type = ButtonType.valueOf(type.toUpperCase());
+        } catch (IllegalArgumentException e) {
+            System.err.println("Unkown button type '" + type + "' given. Allowed values are " + Arrays.toString(ButtonType.values()));
+        }
+    }
+    /**
+     * @param type the type to set
+     */
+    public void setType(ButtonType type) {
+        this.type = type;
+    }
+    
+    /**
+     * Sets the name of the icon.
+     * @param icon
+     */
+    public void setIcon(String icon) {
+        this.iconName = icon;
+    }
+    
+    /**
+     * Creates the button.
+     * @return the button.
+     */
+    public JComponent createComponent() {
+        
+        String actionName = tr(getLabel()) + " (" + hotkey + ")";
+           
+        Icon icon = ImageProvider.getIfAvailable("markers",iconName);
+        if (icon == null)
+            icon = ImageProvider.getIfAvailable("symbols",iconName);
+        if (icon == null)
+            icon = ImageProvider.getIfAvailable("nodes",iconName);
+
+        MetaAction action = new MetaAction(actionName, icon);
+        action.setActions(actions);
+        action.setGpsDataSource(gpsDataSource);
+                
+        AbstractButton button;
+        if(type == ButtonType.TOGGLE) {
+            button = new JToggleButton(action);
+            connectActionAndButton(action, button);
+        } else {
+            button = new JButton(action);
+        }
+        button.setActionCommand(label);
+
+        // connect component keyboard map with buttons:
+        ActionMap actionMap = new ActionMapUIResource();
+        actionMap.put(actionName, action);
+
+        InputMap keyMap = new ComponentInputMap(button);
+        keyMap.put(KeyStroke.getKeyStroke(hotkey), actionName);
+
+        SwingUtilities.replaceUIActionMap(button, actionMap);
+        SwingUtilities.replaceUIInputMap(button, JComponent.WHEN_IN_FOCUSED_WINDOW, keyMap);
+        return button;
+    }
+    
+    private static void connectActionAndButton(Action action, AbstractButton button) {
+        SelectionStateAdapter adapter = new SelectionStateAdapter(action, button);
+        adapter.configure();
+    }
+ 
+    /**
+     * Class that connects the selection state of the action 
+     * to the selection state of the button.
+     * 
+     * @author R.J. Lorimer
+     */
+    private static class SelectionStateAdapter implements PropertyChangeListener, ItemListener {
+        private Action action;
+        private AbstractButton button;
+        public SelectionStateAdapter(Action theAction, AbstractButton theButton) {
+            action = theAction;
+            button = theButton;         
+        }
+        protected void configure() {
+            action.addPropertyChangeListener(this);
+            button.addItemListener(this);
+        }
+        public void itemStateChanged(ItemEvent e) {
+            boolean value = e.getStateChange() == ItemEvent.SELECTED;
+            Boolean valueObj = Boolean.valueOf(value);
+            action.putValue(ActionConstants.SELECTED_KEY, valueObj);
+        }
+        
+        public void propertyChange(PropertyChangeEvent evt) {
+            if(evt.getPropertyName().equals(ActionConstants.SELECTED_KEY)) {
+                Boolean newSelectedState = (Boolean)evt.getNewValue();
+                button.setSelected(newSelectedState.booleanValue());
+            }
+        }
+    }
+
+    /**
+     * Set the source of gps data.
+     * @param gpsDataSource the source from where gps data can be obtained.
+     */
+    public void setGpsDataSource(GpsDataSource gpsDataSource) {
+        this.gpsDataSource = gpsDataSource;
+    }
+}
Index: applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/ButtonType.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/ButtonType.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/ButtonType.java	(revision 2936)
@@ -0,0 +1,13 @@
+/**
+ * 
+ */
+package at.dallermassl.josm.plugin.surveyor;
+
+/**
+ * @author cdaller
+ *
+ */
+public enum ButtonType { 
+    SINGLE, 
+    TOGGLE 
+}
Index: applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/GpsActionEvent.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/GpsActionEvent.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/GpsActionEvent.java	(revision 2936)
@@ -0,0 +1,38 @@
+/**
+ * 
+ */
+package at.dallermassl.josm.plugin.surveyor;
+
+import java.awt.event.ActionEvent;
+
+import org.openstreetmap.josm.data.coor.LatLon;
+
+/**
+ * @author cdaller
+ *
+ */
+public class GpsActionEvent extends ActionEvent {
+    private LatLon coordinates;
+    
+
+    /**
+     * @param e
+     * @param latitude
+     * @param longitude
+     */
+    public GpsActionEvent(ActionEvent e, double latitude, double longitude) {
+        super(e.getSource(), e.getID(), e.getActionCommand(), e.getWhen(), e.getModifiers());
+        coordinates = new LatLon(latitude, longitude);
+    }
+
+
+    /**
+     * @return the coordinates
+     */
+    public LatLon getCoordinates() {
+        return this.coordinates;
+    }
+
+ 
+
+}
Index: applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/GpsDataSource.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/GpsDataSource.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/GpsDataSource.java	(revision 2936)
@@ -0,0 +1,19 @@
+/**
+ * 
+ */
+package at.dallermassl.josm.plugin.surveyor;
+
+import livegps.LiveGpsData;
+
+/**
+ * @author cdaller
+ *
+ */
+public interface GpsDataSource {
+    /**
+     * Returns gps data.
+     * @return gps data.
+     */
+    public LiveGpsData getGpsData();
+
+}
Index: applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/GpxTrackMarkerWriter.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/GpxTrackMarkerWriter.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/GpxTrackMarkerWriter.java	(revision 2936)
@@ -0,0 +1,109 @@
+/**
+ * 
+ */
+package at.dallermassl.josm.plugin.surveyor;
+
+import java.io.PrintWriter;
+import java.util.Collection;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.gui.layer.RawGpsLayer.GpsPoint;
+import org.openstreetmap.josm.gui.layer.markerlayer.Marker;
+import org.openstreetmap.josm.io.XmlWriter;
+import org.openstreetmap.josm.io.XmlWriter.OsmWriterInterface;
+
+/**
+ * Gpx writer that writes tracks and markers as waypoints.
+ *  
+ * @author cdaller
+ *
+ */
+public class GpxTrackMarkerWriter implements XmlWriter.OsmWriterInterface {
+	
+	private final Collection<Collection<GpsPoint>> gpsData;
+	private final Collection<Marker> markers;
+	
+	public GpxTrackMarkerWriter(Collection<Collection<GpsPoint>> gpsData, Collection<Marker> markers) {
+		this.gpsData = gpsData;
+		this.markers = markers;
+	}
+
+	public void header(PrintWriter out) {
+		out.println("<gpx version='1.1' creator='JOSM' xmlns='http://www.topografix.com/GPX/1/1'>");
+	}
+
+	public void write(PrintWriter out) {
+		// calculate bounds
+		Bounds b = new Bounds(new LatLon(Double.MAX_VALUE, Double.MAX_VALUE), new LatLon(Double.MIN_VALUE, Double.MIN_VALUE));
+		b = extendBounds(b);
+		
+		out.println("  <metadata>");
+		out.println("    <bounds minlat='"+b.min.lat()+"' minlon='"+b.min.lon()+"' maxlat='"+b.max.lat()+"' maxlon='"+b.max.lon()+"' />");
+		out.println("  </metadata>");
+
+		if(gpsData != null && gpsData.size() > 0) {
+			out.println("  <trk>");
+			for (Collection<GpsPoint> c : gpsData) {
+				out.println("    <trkseg>");
+				LatLon last = null;
+				for (GpsPoint p : c) {
+					// skip double entries
+					if (p.latlon.equals(last))
+						continue;
+					last =  p.latlon;
+					LatLon ll = p.latlon;
+					out.print("      <trkpt lat='"+ll.lat()+"' lon='"+ll.lon()+"'");
+					if (p.time != null && p.time.length()!=0) {
+						out.println(">");
+						out.println("        <time>"+p.time+"</time>");
+						out.println("      </trkpt>");
+					} else
+						out.println(" />");
+				}
+				out.println("    </trkseg>");
+			}
+			out.println("  </trk>");
+		}
+		
+		if(markers != null && markers.size() > 0) {
+			LatLon latLon;
+			for(Marker marker : markers) {
+				latLon = Main.proj.eastNorth2latlon(marker.eastNorth);
+				out.print("  <wpt");
+				out.print(" lat='" + latLon.lat() + "'");
+				out.print(" lon='" + latLon.lon() + "'");
+				out.println(">");
+				out.println("  <name>" + marker.text + "</name>");
+				out.println("</wpt>");
+			}
+		}
+	}
+
+	/**
+     * @param bounds
+     */
+    public Bounds extendBounds(Bounds bounds) {
+    	if(gpsData != null) {
+    		for (Collection<GpsPoint> c : gpsData) {
+    			for (GpsPoint p : c) {
+    				bounds.extend(p.latlon);
+    			}
+    		}
+    	}
+    	if(markers != null) {
+    		LatLon latLon;
+    		for(Marker marker : markers) {
+    			latLon = Main.proj.eastNorth2latlon(marker.eastNorth);
+    			bounds.extend(latLon);
+    		}
+    	}
+        return bounds;
+    }
+
+	public void footer(PrintWriter out) {
+		out.println("</gpx>");
+    }
+}
+
Index: applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/MetaAction.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/MetaAction.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/MetaAction.java	(revision 2936)
@@ -0,0 +1,181 @@
+/**
+ * 
+ */
+package at.dallermassl.josm.plugin.surveyor;
+
+import java.awt.BorderLayout;
+import java.awt.event.ActionEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import javax.swing.AbstractAction;
+import javax.swing.Icon;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+
+import at.dallermassl.josm.plugin.surveyor.action.DialogClosingThread;
+
+import livegps.LiveGpsData;
+
+/**
+ * Action that fires a {@link SurveyorAction} to the registered actions.
+ * 
+ * @author cdaller
+ * 
+ */
+public class MetaAction extends AbstractAction {
+    private List<SurveyorActionDescription> actions;
+    private GpsDataSource gpsDataSource;
+    private long lastActionCall = 0;
+    public static final long MIN_TIME_DIFF = 500; // 500ms
+
+    /**
+     * 
+     */
+    public MetaAction() {
+        // TODO Auto-generated constructor stub
+    }
+
+    /**
+     * @param name
+     */
+    public MetaAction(String name) {
+        super(name);
+        // TODO Auto-generated constructor stub
+    }
+
+    /**
+     * @param name
+     * @param icon
+     */
+    public MetaAction(String name, Icon icon) {
+        super(name, icon);
+        // TODO Auto-generated constructor stub
+    }
+
+    /**
+     * @return the actions
+     */
+    public List<SurveyorActionDescription> getActions() {
+        return this.actions;
+    }
+
+    /**
+     * @param actions
+     *            the actions to set
+     */
+    public void setActions(List<SurveyorActionDescription> actions) {
+        this.actions = actions;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
+     */
+    public void actionPerformed(ActionEvent e) {
+        // check if action was called by repeating key presses too long pressed):
+        long time = System.currentTimeMillis();
+        if ((time - lastActionCall) < MIN_TIME_DIFF) {
+            lastActionCall = time;
+// System.out.println("repeating key detected");
+            return;
+        }
+        lastActionCall = time;
+        // System.out.println("meta action '" + super.toString() + "' called");
+
+        // toggle on/off
+        Boolean selected = (Boolean) getValue(ActionConstants.SELECTED_KEY);
+        if (selected == null || selected == Boolean.FALSE) {
+            selected = Boolean.TRUE;
+        } else {
+            selected = Boolean.FALSE;
+        }
+        putValue(ActionConstants.SELECTED_KEY, selected);
+
+        LiveGpsData gpsData = gpsDataSource.getGpsData();
+        if (gpsData != null) {
+            double latitude = gpsData.getLatitude();
+            double longitude = gpsData.getLongitude();
+            GpsActionEvent gpsEvent = new GpsActionEvent(e, latitude, longitude);
+            for (SurveyorActionDescription action : actions) {
+                action.actionPerformed(gpsEvent);
+            }
+        } else {
+            System.out.println("Surveyor: no gps data available!");
+        }
+    }
+
+    public void openDialog(JFrame frame) {
+        final JOptionPane optionPane = new JOptionPane("The only way to close this dialog is by\n"
+                        + "pressing one of the following buttons.\n" + "Do you understand?",
+            JOptionPane.QUESTION_MESSAGE, JOptionPane.YES_NO_OPTION);
+
+        final JDialog dialog = new JDialog(frame, "Click a button", true);
+        dialog.setContentPane(optionPane);
+        optionPane.addPropertyChangeListener(new PropertyChangeListener() {
+            public void propertyChange(PropertyChangeEvent e) {
+                String prop = e.getPropertyName();
+
+                if (dialog.isVisible() && (e.getSource() == optionPane)
+                                && (prop.equals(JOptionPane.VALUE_PROPERTY))) {
+                    // If you were going to check something
+                    // before closing the window, you'd do
+                    // it here.
+                    dialog.setVisible(false);
+                }
+            }
+        });
+        Thread closer = new DialogClosingThread(dialog);
+        closer.start();
+        dialog.pack();
+        dialog.setVisible(true);
+        
+
+        System.out.println("value: " + optionPane.getValue().getClass());
+
+//        int value = ((Integer) optionPane.getValue()).intValue();
+//        if (value == JOptionPane.YES_OPTION) {
+//            System.out.println("yes");
+//        } else if (value == JOptionPane.NO_OPTION) {
+//            System.out.println("no");
+//        }
+
+    }
+
+    /**
+     * @param gpsDataSource
+     */
+    public void setGpsDataSource(GpsDataSource gpsDataSource) {
+        this.gpsDataSource = gpsDataSource;
+    }
+    
+    public static void main(String[] args) {
+      //1. Create the frame.
+        JFrame frame = new JFrame("FrameDemo");
+
+        //2. Optional: What happens when the frame closes?
+        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+
+        //3. Create components and put them in the frame.
+        //...create emptyLabel...
+        frame.getContentPane().add(new JLabel("test"), BorderLayout.CENTER);
+
+        //4. Size the frame.
+        frame.pack();
+        frame.setSize(600,400);
+        frame.setLocation(0,0);
+
+        //5. Show it.
+        frame.setVisible(true);
+        System.out.println("after visible");
+        new MetaAction().openDialog(frame);
+    }
+
+}
Index: applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/SurveyorAction.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/SurveyorAction.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/SurveyorAction.java	(revision 2936)
@@ -0,0 +1,25 @@
+/**
+ * 
+ */
+package at.dallermassl.josm.plugin.surveyor;
+
+import java.util.List;
+
+/**
+ * @author cdaller
+ *
+ */
+public interface SurveyorAction {
+
+    /**
+     * Action callback indicating that the action should do something.
+     * @param event the event.
+     */
+    public void actionPerformed(GpsActionEvent event);
+    
+    /**
+     * Sets the parameters for the action execution.
+     * @param parameters the parameters.
+     */
+    public void setParameters(List<String> parameters);
+}
Index: applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/SurveyorActionDescription.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/SurveyorActionDescription.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/SurveyorActionDescription.java	(revision 2936)
@@ -0,0 +1,111 @@
+/**
+ * 
+ */
+package at.dallermassl.josm.plugin.surveyor;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.dinopolis.util.io.Tokenizer;
+
+/**
+ * @author cdaller
+ *
+ */
+public class SurveyorActionDescription {
+    private String actionClass;
+    private List<String> params;
+    private SurveyorAction action;
+    
+    
+    /**
+     * Default Constructor 
+     */
+    public SurveyorActionDescription() {
+        super();
+    }
+    /**
+     * @param actionClass
+     * @param params
+     */
+    public SurveyorActionDescription(String actionClass) {
+        super();
+        this.actionClass = actionClass;
+    }
+    /**
+     * @param actionClass
+     * @param params
+     */
+    public SurveyorActionDescription(String actionClass, List<String> params) {
+        super();
+        this.actionClass = actionClass;
+        this.params = params;
+    }
+    /**
+     * @param actionClass
+     * @param params
+     */
+    public SurveyorActionDescription(String actionClass, String[] params) {
+        super();
+        this.actionClass = actionClass;
+        this.params = new ArrayList<String>();
+        for (int index = 0; index < params.length; index++) {
+            this.params.add(params[index]);
+        }
+    }
+    /**
+     * @return the actionClass
+     */
+    private String getActionClass() {
+        return this.actionClass;
+    }
+    /**
+     * @param actionClass the actionClass to set
+     */
+    private void setActionClass(String actionClass) {
+        this.actionClass = actionClass;
+    }
+    /**
+     * @return the params
+     */
+    private List<String> getParameterList() {
+        return this.params;
+    }
+    /**
+     * @param params the params to set
+     */
+    private void setParameterList(List<String> params) {
+        this.params = params;
+    }
+    
+    public void actionPerformed(GpsActionEvent e) {
+        if(action == null) {
+            action = SurveyorActionFactory.getInstance(actionClass);
+            action.setParameters(getParameterList());
+        }
+        action.actionPerformed(e);
+    }
+    
+    /**
+     * Sets the classname of the action to use. Callback method of xml parser.
+     * @param claszName the name of the action class.
+     */
+    public void setClass_(String claszName) {
+        setActionClass(claszName);
+    }
+    
+    /**
+     * Set the params as a comma separated string.
+     * @param paramString the comma separated string for the parameters.
+     */
+    public void setParams(String paramString) {
+        Tokenizer tokenizer = new Tokenizer(paramString, ",");
+        try {
+            params = tokenizer.nextLine();
+        } catch (IOException ignore) {
+        }
+    }
+
+}
Index: applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/SurveyorActionFactory.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/SurveyorActionFactory.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/SurveyorActionFactory.java	(revision 2936)
@@ -0,0 +1,48 @@
+/**
+ * 
+ */
+package at.dallermassl.josm.plugin.surveyor;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Simple factory that creates a class instance from a classname. It caches the instances, so
+ * the action instances are used as singletons!
+ * A package name of "at.dallermassl.josm.plugin.surveyor.action" is assumed, if the class could
+ * not be found. 
+ * 
+ * @author cdaller
+ *
+ */
+public class SurveyorActionFactory {
+    private static Map<String, SurveyorAction>actionCache = new HashMap<String, SurveyorAction>();
+    public static final String DEFAULT_PACKAGE = SurveyorActionFactory.class.getPackage().getName() + ".action";
+
+    /**
+     * @param actionClass
+     * @return
+     */
+    public static SurveyorAction getInstance(String actionClass) {
+        try {
+            SurveyorAction action = actionCache.get(actionClass);
+            if(action == null) {
+                try {
+                    action = (SurveyorAction)Class.forName(actionClass).newInstance();
+                } catch (ClassNotFoundException e) {
+                    actionClass = DEFAULT_PACKAGE + "." + actionClass;
+                    action = (SurveyorAction)Class.forName(actionClass).newInstance();
+                }
+                actionCache.put(actionClass, action);
+            }
+            return action;
+        } catch (InstantiationException e) {
+            throw new RuntimeException("Could not create action class '" + actionClass + "'", e);
+        } catch (IllegalAccessException e) {
+            throw new RuntimeException("Could not create action class '" + actionClass + "'", e);
+        } catch (ClassNotFoundException e) {
+            throw new RuntimeException("Could not create action class '" + actionClass + "'", e);
+        }
+    }
+
+}
Index: applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/SurveyorComponent.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/SurveyorComponent.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/SurveyorComponent.java	(revision 2936)
@@ -0,0 +1,169 @@
+/**
+ * 
+ */
+package at.dallermassl.josm.plugin.surveyor;
+
+import java.awt.Dimension;
+import java.awt.GridLayout;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.swing.JComponent;
+import javax.swing.JFrame;
+
+import livegps.LiveGpsData;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.annotation.AnnotationPreset;
+import org.openstreetmap.josm.gui.annotation.AnnotationPreset.Check;
+import org.openstreetmap.josm.gui.annotation.AnnotationPreset.Combo;
+import org.openstreetmap.josm.gui.annotation.AnnotationPreset.Item;
+import org.openstreetmap.josm.gui.annotation.AnnotationPreset.Key;
+import org.openstreetmap.josm.gui.annotation.AnnotationPreset.Label;
+import org.openstreetmap.josm.gui.annotation.AnnotationPreset.Text;
+import org.openstreetmap.josm.tools.XmlObjectParser;
+import org.xml.sax.SAXException;
+
+/**
+ * @author cdaller
+ * 
+ */
+public class SurveyorComponent extends JComponent implements PropertyChangeListener, GpsDataSource {
+    
+    private LiveGpsData gpsData;
+    private int rows = 3;
+    private int columns = 3;
+    private int width = 0;
+    private int height = 0;
+
+    public SurveyorComponent() {
+        super();
+    }
+    
+    /**
+     * Set the number of rows as a string (callback method from xml parser).
+     * @param rowsString the row string.
+     */
+    public void setRows(String rowsString) {
+        rows = Integer.parseInt(rowsString);
+        setLayout(new GridLayout(rows, columns));
+    }
+    
+    /**
+     * Set the number of columns as a string (callback method from xml parser).
+     * @param columnsString the column string.
+     */
+    public void setColumns(String columnsString) {
+        columns = Integer.parseInt(columnsString);
+        setLayout(new GridLayout(rows, columns));
+    }
+    
+    /**
+     * Set the width as a string.
+     * @param widthString the width of the component.
+     */
+    public void setWidth(String widthString) {
+        width = Integer.parseInt(widthString);
+        if(width > 0 && height > 0) {
+            super.setPreferredSize(new Dimension(width, height));
+        }
+    }
+    
+    /**
+     * Set the width as a string.
+     * @param widthString the width of the component.
+     */
+    public void setHeight(String heightString) {
+        height = Integer.parseInt(heightString);
+        if(width > 0 && height > 0) {
+            super.setPreferredSize(new Dimension(width, height));
+        }
+    }
+
+    public void setGridSize(int rows, int cols) {
+        setLayout(new GridLayout(rows, cols));
+    }
+
+    public void addButton(ButtonDescription description) {
+        description.setGpsDataSource(this);
+        add(description.createComponent());
+    }
+    
+    
+
+    public static void main(String[] args) {
+        
+        
+        // parse xml file and create component from it:
+        Reader in = new InputStreamReader(SurveyorComponent.class.getClassLoader().getResourceAsStream("surveyor.xml"));
+        XmlObjectParser parser = new XmlObjectParser();
+        parser.mapOnStart("surveyor", SurveyorComponent.class);
+        parser.map("button", ButtonDescription.class);
+        parser.map("action", SurveyorActionDescription.class);
+
+        SurveyorComponent surveyorComponent = null;
+        try {
+            parser.start(in);
+            List<SurveyorActionDescription> actions = new ArrayList<SurveyorActionDescription>();
+            while(parser.hasNext()) {
+                Object object = parser.next();
+                if (object instanceof SurveyorComponent) {
+                    System.out.println("SurveyorComponent " + object);
+                    surveyorComponent = (SurveyorComponent) object;
+                } else if (object instanceof ButtonDescription) {
+                    System.out.println("ButtonDescription " + object);
+                    ((ButtonDescription)object).setActions(actions);
+                    surveyorComponent.addButton(((ButtonDescription)object));
+                    actions.clear();
+                } else if (object instanceof SurveyorActionDescription) {
+                    System.out.println("SurveyorActionDescription " + object);
+                    actions.add((SurveyorActionDescription)object);
+                } else {
+                    System.err.println("unknown " + object);
+                }
+            }
+        } catch (SAXException e) {
+            e.printStackTrace();
+        }        
+        
+//        SurveyorComponent surveyorComponent = new SurveyorComponent();
+//        surveyorComponent.setGridSize(3,3);
+//        surveyorComponent.addButton(new ButtonDescription("Tunnel", "T", "images/symbols/tunnel.png", "ConsolePrinterAction", ButtonType.SINGLE));
+//        surveyorComponent.addButton(new ButtonDescription("Bridge", "B", null, "ConsolePrinterAction", ButtonType.TOGGLE));
+//        surveyorComponent.addButton(new ButtonDescription("Motorway", "M", null, "ConsolePrinterAction", null));
+//        surveyorComponent.addButton(new ButtonDescription("Primary", "P", null, "ConsolePrinterAction", null));
+//        surveyorComponent.addButton(new ButtonDescription("Secondary", "S", null, "ConsolePrinterAction", null));
+//        surveyorComponent.addButton(new ButtonDescription("Unclassified", "U", null, "ConsolePrinterAction", null));
+//        surveyorComponent.addButton(new ButtonDescription("Residential", "R", null, "ConsolePrinterAction", null));
+//        surveyorComponent.addButton(new ButtonDescription("Parking", "P", "images/symbols/parking.png", "ConsolePrinterAction", null));
+        
+        JFrame frame = new JFrame();
+        frame.add(surveyorComponent);
+        frame.pack();
+        frame.setVisible(true);
+        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+    }
+
+    /* (non-Javadoc)
+     * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
+     */
+    public void propertyChange(PropertyChangeEvent evt) {
+        if("gpsdata".equals(evt.getPropertyName())) {
+            gpsData = (LiveGpsData) evt.getNewValue();
+        }
+        
+    }
+
+    /* (non-Javadoc)
+     * @see at.dallermassl.josm.plugin.surveyor.GpsDataSource#getGpsData()
+     */
+    public LiveGpsData getGpsData() {
+        return gpsData;
+    }
+
+}
Index: applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/SurveyorPlugin.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/SurveyorPlugin.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/SurveyorPlugin.java	(revision 2936)
@@ -0,0 +1,42 @@
+/**
+ * 
+ */
+package at.dallermassl.josm.plugin.surveyor;
+
+import javax.swing.AbstractAction;
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JMenuItem;
+import javax.swing.KeyStroke;
+
+import livegps.LiveGpsPlugin;
+
+/**
+ * Plugin that uses live gps data and a button panel to add nodes/waypoints etc at the current
+ * position.
+ * 
+ * TODO: auto save marker layer and data layer?
+ * TODO: in action retrieve buttontype state to set on/off values
+ * @author cdaller
+ *
+ */
+public class SurveyorPlugin extends LiveGpsPlugin {
+
+    /**
+     * 
+     */
+    public SurveyorPlugin() {
+        super();
+        SurveyorShowAction surveyorAction = new SurveyorShowAction("Surveyor", this);
+        JMenuItem surveyorMenuItem = new JMenuItem(surveyorAction);
+        surveyorAction.putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke.getKeyStroke("alt S"));
+//        surveyorMenuItem.addActionListener(new ActionListener() {
+        getLgpsMenu().addSeparator();
+        getLgpsMenu().add(surveyorMenuItem);
+        
+        AutoSaveAction autoSaveAction = new AutoSaveAction("AutoSave LiveData");
+        JCheckBoxMenuItem autoSaveMenu = new JCheckBoxMenuItem(autoSaveAction);
+        getLgpsMenu().add(autoSaveMenu);
+        
+    }
+    
+}
Index: applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/SurveyorShowAction.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/SurveyorShowAction.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/SurveyorShowAction.java	(revision 2936)
@@ -0,0 +1,148 @@
+/**
+ * 
+ */
+package at.dallermassl.josm.plugin.surveyor;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.AbstractAction;
+import javax.swing.JFrame;
+import javax.swing.JOptionPane;
+
+import livegps.LiveGpsPlugin;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.tools.XmlObjectParser;
+import org.xml.sax.SAXException;
+
+/**
+ * @author cdaller
+ *
+ */
+public class SurveyorShowAction extends AbstractAction {
+    private static final String DEFAULT_SOURCE = "resource://surveyor.xml";
+    private JFrame surveyorFrame;
+    private LiveGpsPlugin gpsPlugin;
+
+    public SurveyorShowAction(LiveGpsPlugin gpsPlugin) {
+        this(null, gpsPlugin);
+    }
+    
+    public SurveyorShowAction(String name, LiveGpsPlugin gpsPlugin) {
+        super(name);
+        this.gpsPlugin = gpsPlugin;
+    }
+
+
+    /* (non-Javadoc)
+     * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
+     */
+    public void actionPerformed(ActionEvent e) {
+        if(surveyorFrame == null) {
+            surveyorFrame = new JFrame();
+
+            SurveyorComponent comp = createComponent();
+//          comp.setGridSize(3,3);
+//          comp.addButton(new ButtonDescription("Tunnel", "T", "images/symbols/tunnel.png", "ConsolePrinterAction", ButtonType.SINGLE));
+//          comp.addButton(new ButtonDescription("Bridge", "B", null, "ConsolePrinterAction", ButtonType.TOGGLE));
+//          comp.addButton(new ButtonDescription("Motorway", "M", null, "ConsolePrinterAction", null));
+//          comp.addButton(new ButtonDescription("Primary", "I", null, "ConsolePrinterAction", null));
+//          comp.addButton(new ButtonDescription("Secondary", "S", null, "ConsolePrinterAction", null));
+//          comp.addButton(new ButtonDescription("Unclassified", "U", null, "ConsolePrinterAction", null));
+//          comp.addButton(new ButtonDescription("Residential", "R", null,
+//          new SurveyorActionDescription("SetWaypointAction", new String[] {"residential", "images/reorder.png"}), null));
+//          comp.addButton(new ButtonDescription("Parking", "P", "images/symbols/parking.png", 
+//          new SurveyorActionDescription("SetNodeAction", new String[] {"amenity", "parking", "createdby", "surveyor"}), null));
+            
+            // add component as gps event listener:
+            gpsPlugin.addPropertyChangeListener(comp);
+            surveyorFrame.add(comp);
+            surveyorFrame.pack();
+            surveyorFrame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
+            surveyorFrame.setTitle((String)getValue(AbstractAction.NAME));
+            // <FIXXME date="28.04.2007" author="cdaller">
+            // TODO get old size/pos of frame from properties
+            // </FIXXME> 
+        }
+        surveyorFrame.setVisible(true);
+
+    }
+
+    public SurveyorComponent createComponent() {
+        InputStream in = null;
+        String source = Main.pref.get("surveyor.source");
+        if(source == null || source.length() == 0) {
+            source = DEFAULT_SOURCE;
+            Main.pref.put("surveyor.source", DEFAULT_SOURCE);
+            // <FIXXME date="04.05.2007" author="cdaller">
+            // TODO copy xml file to .josm directory if it does not exist!
+            // </FIXXME> 
+        }
+        SurveyorComponent component= null;
+        try {
+            if (source.startsWith("http") || source.startsWith("ftp") || source.startsWith("file"))
+                in = new URL(source).openStream();
+            else if (source.startsWith("resource://"))
+                in = getClass().getResourceAsStream(source.substring("resource:/".length()));
+            else
+                in = new FileInputStream(source);
+            component = createComponent(in);
+            in.close();
+            return component;
+        } catch (IOException e) {
+            e.printStackTrace();
+            JOptionPane.showMessageDialog(Main.parent, tr("Could not read surveyor definition: {0}",source));
+        } catch (SAXException e) {
+            e.printStackTrace();
+            JOptionPane.showMessageDialog(Main.parent, tr("Error parsing {0}: ", source)+e.getMessage());
+        }
+        return component;
+
+    }
+
+    /**
+     * Parse an xml file containing the definitions for the surveyor component.
+     * @param in the inputstream to read the xml from.
+     * @return the component.
+     * @throws SAXException if the xml could not be read.
+     */
+    public SurveyorComponent createComponent(InputStream in) throws SAXException {
+        XmlObjectParser parser = new XmlObjectParser();
+        parser.mapOnStart("surveyor", SurveyorComponent.class);
+        parser.map("button", ButtonDescription.class);
+        parser.map("action", SurveyorActionDescription.class);
+
+        SurveyorComponent surveyorComponent = null;
+        parser.start(new BufferedReader(new InputStreamReader(in)));
+        List<SurveyorActionDescription> actions = new ArrayList<SurveyorActionDescription>();
+        while(parser.hasNext()) {
+            Object object = parser.next();
+            if (object instanceof SurveyorComponent) {
+                //System.out.println("SurveyorComponent " + object);
+                surveyorComponent = (SurveyorComponent) object;
+            } else if (object instanceof ButtonDescription) {
+                //System.out.println("ButtonDescription " + object);
+                ((ButtonDescription)object).setActions(actions);
+                surveyorComponent.addButton(((ButtonDescription)object));
+                actions = new ArrayList<SurveyorActionDescription>();
+            } else if (object instanceof SurveyorActionDescription) {
+                //System.out.println("SurveyorActionDescription " + object);
+                actions.add((SurveyorActionDescription)object);
+            } else {
+                System.err.println("surveyor: unknown xml element: " + object);
+            }
+        }
+        return surveyorComponent;
+    }
+
+}
Index: applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/action/AbstractSurveyorAction.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/action/AbstractSurveyorAction.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/action/AbstractSurveyorAction.java	(revision 2936)
@@ -0,0 +1,33 @@
+/**
+ * 
+ */
+package at.dallermassl.josm.plugin.surveyor.action;
+
+import java.util.List;
+
+import at.dallermassl.josm.plugin.surveyor.SurveyorAction;
+
+/**
+ * @author cdaller
+ *
+ */
+public abstract class AbstractSurveyorAction implements SurveyorAction {
+    private List<String> parameters;
+    
+    /**
+     * Returns the parameters.
+     * @return the parameters
+     */
+    public List<String> getParameters() {
+        return parameters;
+    }
+        
+    /* (non-Javadoc)
+     * @see at.dallermassl.josm.plugin.surveyor.SurveyorAction#setParameters(java.util.List)
+     */
+    @Override
+    public void setParameters(List<String> parameters) {
+        this.parameters = parameters;
+    }
+
+}
Index: applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/action/AnnotationPresetAction.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/action/AnnotationPresetAction.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/action/AnnotationPresetAction.java	(revision 2936)
@@ -0,0 +1,89 @@
+/**
+ * 
+ */
+package at.dallermassl.josm.plugin.surveyor.action;
+
+import java.util.List;
+import java.util.Map.Entry;
+
+import javax.swing.Action;
+
+import livegps.LiveGpsLock;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.gui.annotation.AnnotationPreset;
+import org.openstreetmap.josm.gui.preferences.AnnotationPresetPreference;
+
+import at.dallermassl.josm.plugin.surveyor.GpsActionEvent;
+import at.dallermassl.josm.plugin.surveyor.SurveyorAction;
+
+/**
+ * @author cdaller
+ *
+ */
+public class AnnotationPresetAction implements SurveyorAction {
+    private String presetName;
+    private AnnotationPreset preset;
+    
+
+    /* (non-Javadoc)
+     * @see at.dallermassl.josm.plugin.surveyor.SurveyorAction#actionPerformed(at.dallermassl.josm.plugin.surveyor.GpsActionEvent)
+     */
+    @Override
+    public void actionPerformed(GpsActionEvent event) {
+        if(preset == null) {
+            return;
+        }
+        LatLon coordinates = event.getCoordinates();
+        System.out.println(getClass().getSimpleName() + " KOORD: " + coordinates.lat() + ", " 
+            + coordinates.lon() + ", preset=" + presetName);
+        Node node = new Node(coordinates);
+        node.put("created_by", "JOSM-surveyor-plugin");
+        synchronized(LiveGpsLock.class) {
+            Main.main.editLayer().data.nodes.add(node);
+            Main.ds.setSelected(node);
+        }
+        Main.map.repaint();
+        
+        // call an annotationpreset to add additional properties...
+        
+        AnnotationPreset preset = getAnnotationPreset("Restrictions");
+        preset.actionPerformed(null);
+
+    }
+
+
+    /* (non-Javadoc)
+     * @see at.dallermassl.josm.plugin.surveyor.SurveyorAction#setParameters(java.util.List)
+     */
+    @Override
+    public void setParameters(List<String> parameters) {
+        if(parameters.size() == 0) {
+            throw new IllegalArgumentException("No annotation preset name given!");
+        }
+        presetName = parameters.get(0);
+        preset = getAnnotationPreset(presetName);
+        if(preset == null) {
+            System.err.println("No valid preset '" + parameters.get(0) + "' found - disable action!");
+            return;
+        }        
+    }
+
+    /**
+     * Returns the preset with the given name or <code>null</code>.
+     * @param name the name of the annotation preset.
+     * @return  the preset with the given name.
+     */
+    protected AnnotationPreset getAnnotationPreset(String name) {
+        for(AnnotationPreset preset : AnnotationPresetPreference.annotationPresets) {
+            if(name.equals(preset.getValue(Action.NAME))) {
+                return preset;
+            }
+        }
+        return null;
+    }
+
+
+}
Index: applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/action/ConsolePrinterAction.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/action/ConsolePrinterAction.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/action/ConsolePrinterAction.java	(revision 2936)
@@ -0,0 +1,30 @@
+/**
+ * 
+ */
+package at.dallermassl.josm.plugin.surveyor.action;
+
+import java.util.List;
+
+import javax.swing.AbstractButton;
+
+import org.openstreetmap.josm.data.coor.LatLon;
+
+import at.dallermassl.josm.plugin.surveyor.SurveyorAction;
+import at.dallermassl.josm.plugin.surveyor.GpsActionEvent;
+
+/**
+ * @author cdaller
+ *
+ */
+public class ConsolePrinterAction extends AbstractSurveyorAction {
+
+    /* (non-Javadoc)
+     * @see at.dallermassl.josm.plugin.surveyor.ButtonAction#actionPerformed(at.dallermassl.josm.plugin.surveyor.GpsActionEvent, java.util.List)
+     */
+    public void actionPerformed(GpsActionEvent event) {
+        LatLon coordinates = event.getCoordinates();
+        System.out.println(getClass().getSimpleName() + " KOORD: " + coordinates.lat() + ", " 
+            + coordinates.lon() + " params: " + getParameters());
+    }
+
+}
Index: applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/action/DialogClosingThread.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/action/DialogClosingThread.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/action/DialogClosingThread.java	(revision 2936)
@@ -0,0 +1,54 @@
+/**
+ * 
+ */
+package at.dallermassl.josm.plugin.surveyor.action;
+
+import javax.swing.JDialog;
+
+/**
+ * @author cdaller
+ *
+ */
+public class DialogClosingThread extends Thread {
+    private static long DEFAULT_TIMEOUT = 5000;
+    private JDialog dialog;
+    private long timeout;
+    private String dialogTitle;
+    private long loopCount;
+    
+    /**
+     * Using the given dialog and the default timeout.
+     * @param dialog
+     */
+    public DialogClosingThread(JDialog dialog) {
+        this(dialog, DEFAULT_TIMEOUT);
+    }   
+       
+    /**
+     * @param dialog
+     * @param timeout
+     */
+    public DialogClosingThread(JDialog dialog, long timeout) {
+        super();
+        this.dialog = dialog;
+        this.timeout = timeout;
+        this.loopCount = timeout / 1000;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Thread#run()
+     */
+    @Override
+    public void run() {
+        String title = dialog.getTitle();
+        while(loopCount > 0) {
+            dialog.setTitle(title + " (" + loopCount + "sec)");
+            --loopCount;
+            try {
+                sleep(1000);
+            } catch(InterruptedException ignore) {}
+        }
+        dialog.setVisible(false);
+        dialog.dispose();
+    }
+}
Index: applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/action/PlayAudioAction.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/action/PlayAudioAction.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/action/PlayAudioAction.java	(revision 2936)
@@ -0,0 +1,93 @@
+/**
+ * 
+ */
+package at.dallermassl.josm.plugin.surveyor.action;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.Clip;
+import javax.sound.sampled.DataLine;
+import javax.sound.sampled.LineUnavailableException;
+import javax.sound.sampled.UnsupportedAudioFileException;
+
+import org.openstreetmap.josm.Main;
+
+import at.dallermassl.josm.plugin.surveyor.GpsActionEvent;
+import at.dallermassl.josm.plugin.surveyor.SurveyorAction;
+
+/**
+ * Action that plays an audio file.
+ * 
+ * @author cdaller
+ *
+ */
+public class PlayAudioAction extends AbstractSurveyorAction {
+    private File audioFile = null;
+
+    /* (non-Javadoc)
+     * @see at.dallermassl.josm.plugin.surveyor.SurveyorAction#actionPerformed(at.dallermassl.josm.plugin.surveyor.GpsActionEvent)
+     */
+    @Override
+    public void actionPerformed(GpsActionEvent event) {
+        try {
+            if(audioFile == null) {
+                audioFile = new File(getParameters().get(0));
+                if(!audioFile.exists()) {
+                    audioFile = new File(Main.pref.getPreferencesDir(), getParameters().get(0));
+                    if(!audioFile.exists()) {
+                        System.err.println("Audio file " + getParameters().get(0) + " not found!");
+                        return;
+                    }
+                }
+            }
+            // From file
+            AudioInputStream stream = AudioSystem.getAudioInputStream(audioFile);
+        
+            // From URL
+//            stream = AudioSystem.getAudioInputStream(new URL("http://hostname/audiofile"));
+        
+            // At present, ALAW and ULAW encodings must be converted
+            // to PCM_SIGNED before it can be played
+            AudioFormat format = stream.getFormat();
+            if (format.getEncoding() != AudioFormat.Encoding.PCM_SIGNED) {
+                format = new AudioFormat(
+                        AudioFormat.Encoding.PCM_SIGNED,
+                        format.getSampleRate(),
+                        format.getSampleSizeInBits()*2,
+                        format.getChannels(),
+                        format.getFrameSize()*2,
+                        format.getFrameRate(),
+                        true);        // big endian
+                stream = AudioSystem.getAudioInputStream(format, stream);
+            }
+        
+            // Create the clip
+            DataLine.Info info = new DataLine.Info(
+                Clip.class, stream.getFormat(), ((int)stream.getFrameLength()*format.getFrameSize()));
+            Clip clip = (Clip) AudioSystem.getLine(info);
+        
+            // This method does not return until the audio file is completely loaded
+            clip.open(stream);
+        
+            // Start playing
+            clip.start();
+        } catch (MalformedURLException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        } catch (LineUnavailableException e) {
+            e.printStackTrace();
+        } catch (UnsupportedAudioFileException e) {
+            e.printStackTrace();
+        }
+        
+    }
+}
Index: applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/action/SetNodeAction.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/action/SetNodeAction.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/action/SetNodeAction.java	(revision 2936)
@@ -0,0 +1,82 @@
+/**
+ * 
+ */
+package at.dallermassl.josm.plugin.surveyor.action;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map.Entry;
+
+import javax.swing.Action;
+
+import livegps.LiveGpsLock;
+
+import org.dinopolis.util.collection.Tuple;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.gui.annotation.AnnotationPreset;
+import org.openstreetmap.josm.gui.preferences.AnnotationPresetPreference;
+
+import at.dallermassl.josm.plugin.surveyor.GpsActionEvent;
+import at.dallermassl.josm.plugin.surveyor.SurveyorAction;
+
+/**
+ * Action that sets a node into the data layer. The first parameter is used as a property key
+ * the second as the property value (e.g. amenity=parking). If there are more than two parameters
+ * they are used as key/value pairs.
+ * @author cdaller
+ *
+ */
+public class SetNodeAction implements SurveyorAction {
+    private Collection<Tuple<String, String>> keyValues;
+    
+    /**
+     * Default Constructor
+     */
+    public SetNodeAction() {
+        
+    }
+    
+    /* (non-Javadoc)
+     * @see at.dallermassl.josm.plugin.surveyor.SurveyorAction#setParameters(java.util.List)
+     */
+    @Override
+    public void setParameters(List<String> parameters) {
+        keyValues = new ArrayList<Tuple<String, String>>();
+        int pos;
+        String key;
+        String value;
+        for (String keyValuePair : parameters) {
+            pos = keyValuePair.indexOf('=');
+            if(pos > 0) {
+                key = keyValuePair.substring(0, pos);
+                value = keyValuePair.substring(pos + 1);
+                keyValues.add(new Tuple(key, value));
+            } else {
+                System.err.println("SetNodeAction: ignoring invalid key value pair: " + keyValuePair);
+            }
+        }        
+    }
+
+    /* (non-Javadoc)
+     * @see at.dallermassl.josm.plugin.surveyor.ButtonAction#actionPerformed(at.dallermassl.josm.plugin.surveyor.GpsActionEvent)
+     */
+    public void actionPerformed(GpsActionEvent event) {
+        LatLon coordinates = event.getCoordinates();
+        System.out.println(getClass().getSimpleName() + " KOORD: " + coordinates.lat() + ", " + coordinates.lon() + " params: " + keyValues);
+        Node node = new Node(coordinates);
+        for(Entry<String, String> entry : keyValues) {
+            node.put(entry.getKey(), entry.getValue());
+        }
+        node.put("created_by", "JOSM-surveyor-plugin");
+        synchronized(LiveGpsLock.class) {
+            Main.main.editLayer().data.nodes.add(node);
+            Main.ds.setSelected(node);
+        }
+        Main.map.repaint();        
+    }
+
+    
+}
Index: applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/action/SetWaypointAction.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/action/SetWaypointAction.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/action/SetWaypointAction.java	(revision 2936)
@@ -0,0 +1,86 @@
+/**
+ * 
+ */
+package at.dallermassl.josm.plugin.surveyor.action;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.swing.JToggleButton;
+
+import livegps.LiveGpsLock;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.markerlayer.Marker;
+import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
+
+import at.dallermassl.josm.plugin.surveyor.GpsActionEvent;
+import at.dallermassl.josm.plugin.surveyor.SurveyorAction;
+
+/**
+ * Action that sets a marker into a marker layer. The first parameter of the action
+ * is used as the text of the marker, the second (if it exists) as an icon.
+ * 
+ * @author cdaller
+ *
+ */
+public class SetWaypointAction extends AbstractSurveyorAction {
+    private MarkerLayer markerLayer;
+    public static final String MARKER_LAYER_NAME = "surveyorwaypointlayer";
+    
+    /**
+     * Default Condstructor
+     */
+    public SetWaypointAction() {
+        
+    }
+
+
+    /* (non-Javadoc)
+     * @see at.dallermassl.josm.plugin.surveyor.ButtonAction#actionPerformed(at.dallermassl.josm.plugin.surveyor.GpsActionEvent, java.util.List)
+     */
+    public void actionPerformed(GpsActionEvent event) {
+        LatLon coordinates = event.getCoordinates();
+        System.out.println(getClass().getSimpleName() + " KOORD: " + coordinates.lat() + ", " + coordinates.lon());
+        MarkerLayer layer = getGpsLayer();
+        String markerTitle = getParameters().get(0);
+        Object source = event.getSource();
+        if(source instanceof JToggleButton) {
+            if(((JToggleButton)source).isSelected()) {
+                markerTitle = markerTitle + " " + tr("start");
+            } else {
+                markerTitle = markerTitle + " " + tr("end");                
+            }
+        }
+        
+        String iconName = getParameters().size() > 1 ? getParameters().get(1) : null;
+        synchronized(LiveGpsLock.class) {
+            layer.data.add(new Marker(event.getCoordinates(), markerTitle, iconName));
+        }
+        Main.map.repaint();
+    }
+    
+    public MarkerLayer getGpsLayer() {
+        if(markerLayer == null) {
+            Collection<Layer> layers = Main.map.mapView.getAllLayers();
+            for (Layer layer : layers) {
+                if(MARKER_LAYER_NAME.equals(layer.name)) {
+                    markerLayer = (MarkerLayer) layer;
+                    break;
+                }
+            }
+            // not found:
+            if(markerLayer == null) {
+                markerLayer = new MarkerLayer(new ArrayList<Marker>(), MARKER_LAYER_NAME, null);
+                Main.main.addLayer(markerLayer);
+            }
+        }
+        return markerLayer;
+    }
+
+}
Index: applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/action/SystemExecuteAction.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/action/SystemExecuteAction.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/at/dallermassl/josm/plugin/surveyor/action/SystemExecuteAction.java	(revision 2936)
@@ -0,0 +1,67 @@
+/**
+ * 
+ */
+package at.dallermassl.josm.plugin.surveyor.action;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import at.dallermassl.josm.plugin.surveyor.GpsActionEvent;
+import at.dallermassl.josm.plugin.surveyor.SurveyorAction;
+
+/**
+ * @author cdaller
+ *
+ */
+public class SystemExecuteAction extends AbstractSurveyorAction {
+
+    /* (non-Javadoc)
+     * @see at.dallermassl.josm.plugin.surveyor.SurveyorAction#actionPerformed(at.dallermassl.josm.plugin.surveyor.GpsActionEvent, java.util.List)
+     */
+    @Override
+    public void actionPerformed(GpsActionEvent event) {
+        final ProcessBuilder builder = new ProcessBuilder(getParameters());
+        Map<String, String> environ = builder.environment();
+        builder.directory(new File(System.getProperty("user.home")));
+
+        System.out.println("Directory : " + builder.directory());
+        Thread executionThread = new Thread() {
+
+            /* (non-Javadoc)
+             * @see java.lang.Thread#run()
+             */
+            @Override
+            public void run() {
+                try {
+                    final Process process = builder.start();
+                    InputStream is = process.getInputStream();
+                    InputStreamReader isr = new InputStreamReader(is);
+                    BufferedReader br = new BufferedReader(isr);
+                    String line;
+
+                    while ((line = br.readLine()) != null) {
+                        System.out.println(getClass().getSimpleName() + ": " +  line);
+                    }
+
+                    System.out.println(getClass().getSimpleName() + "Program terminated!");
+                } catch (Throwable t) {
+                    t.printStackTrace();
+                }
+            }
+
+        };
+        executionThread.start();
+//        try {
+//            System.in.read();
+//        } catch (IOException e) {
+//            // TODO Auto-generated catch block
+//            e.printStackTrace();
+//        }
+    }
+}
Index: applications/editors/josm/plugins/surveyor/src/livegps/LiveGpsAcquirer.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/livegps/LiveGpsAcquirer.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/livegps/LiveGpsAcquirer.java	(revision 2936)
@@ -0,0 +1,190 @@
+package livegps;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openstreetmap.josm.data.coor.LatLon;
+
+public class LiveGpsAcquirer implements Runnable {
+	Socket gpsdSocket;
+	BufferedReader gpsdReader;
+	boolean connected = false;
+	String gpsdHost = "localhost";
+	int gpsdPort = 2947;
+	boolean shutdownFlag = false;
+    private List<PropertyChangeListener> propertyChangeListener = new ArrayList<PropertyChangeListener>();
+	
+	public LiveGpsAcquirer() {
+		
+	}
+    
+    /**
+     * Adds a property change listener to the acquirer. 
+     * @param listener the new listener
+     */
+    public void addPropertyChangeListener(PropertyChangeListener listener) {
+        if(!propertyChangeListener.contains(listener)) {
+            propertyChangeListener.add(listener);
+        }
+    }
+    
+    /**
+     * Fire a gps status change event. Fires events with key "gpsstatus" and a {@link LiveGpsStatus}
+     * object as value.
+     * @param status the status.
+     * @param statusMessage the status message.
+     */
+    public void fireGpsStatusChangeEvent(LiveGpsStatus.GpsStatus status, String statusMessage) {
+        PropertyChangeEvent event = new PropertyChangeEvent(this, "gpsstatus", null, new LiveGpsStatus(status, statusMessage));
+        firePropertyChangeEvent(event);
+    }
+
+    /**
+     * Fire a gps data change event to all listeners. Fires events with key "gpsdata" and a 
+     * {@link LiveGpsData} object as values.
+     * @param oldData the old gps data.
+     * @param newData the new gps data.
+     */
+    public void fireGpsDataChangeEvent(LiveGpsData oldData, LiveGpsData newData) {
+        PropertyChangeEvent event = new PropertyChangeEvent(this, "gpsdata", oldData, newData);
+        firePropertyChangeEvent(event);
+    }
+    
+    /**
+     * Fires the given event to all listeners.
+     * @param event the event to fire.
+     */
+    protected void firePropertyChangeEvent(PropertyChangeEvent event) {
+        for (PropertyChangeListener listener : propertyChangeListener) {
+            listener.propertyChange(event);
+        }        
+    }
+
+	public void run() {	
+	    LiveGpsData oldGpsData = null;
+	    LiveGpsData gpsData = null;
+        shutdownFlag = false;
+		while(!shutdownFlag) {
+			double lat = 0;
+			double lon = 0;
+            float speed = 0;
+            float course = 0;
+			boolean haveFix = false;
+
+			try
+			{
+				if (!connected)
+				{
+                    fireGpsStatusChangeEvent(LiveGpsStatus.GpsStatus.CONNECTING, tr("Connecting"));
+					InetAddress[] addrs = InetAddress.getAllByName(gpsdHost);
+					for (int i=0; i < addrs.length && gpsdSocket == null; i++) {
+						try {
+							gpsdSocket = new Socket(addrs[i], gpsdPort);
+							break;
+						} catch (Exception e) {
+							gpsdSocket = null;
+						}
+					}
+					
+					if (gpsdSocket != null)
+					{
+						gpsdReader = new BufferedReader(new InputStreamReader(gpsdSocket.getInputStream()));
+						gpsdSocket.getOutputStream().write(new byte[] { 'w', 13, 10 });
+                        fireGpsStatusChangeEvent(LiveGpsStatus.GpsStatus.CONNECTED, tr("Connected"));
+						connected = true;
+					}
+				}
+
+
+                if(connected) {
+                    String line = gpsdReader.readLine();
+                    if (line == null) break;
+                    String words[] = line.split(",");
+
+                    if ((words.length == 0) || (!words[0].equals("GPSD"))) {
+                        // unexpected response.
+                        continue;
+                    }
+
+                    for (int i = 1; i < words.length; i++) {
+
+                        if ((words[i].length() < 2) || (words[i].charAt(1) != '=')) {
+                            // unexpected response.
+                            continue;
+                        }
+
+                        char what = words[i].charAt(0);
+                        String value = words[i].substring(2);
+                        oldGpsData = gpsData;
+                        gpsData = new LiveGpsData();
+                        switch(what) {
+                        case 'O':
+                            // full report, tab delimited.
+                            String[] status = value.split("\\s+");
+                            if (status.length >= 5) {
+                                lat = Double.parseDouble(status[3]);
+                                lon = Double.parseDouble(status[4]);
+                                try {
+                                    speed = Float.parseFloat(status[9]);
+                                    course = Float.parseFloat(status[8]);
+                                    //view.setSpeed(speed);
+                                    //view.setCourse(course);
+                                } catch (NumberFormatException nex) {}
+                                haveFix = true;
+                            }
+                            break;
+                        case 'P':	
+                            // position report, tab delimited.
+                            String[] pos = value.split("\\s+");
+                            if (pos.length >= 2) {
+                                lat = Double.parseDouble(pos[0]);
+                                lon = Double.parseDouble(pos[1]);
+                                speed = Float.NaN;
+                                course = Float.NaN;
+                                haveFix = true;
+                            }
+                        default:
+                            // not interested
+                        }
+                        gpsData.setFix(haveFix);
+                        if (haveFix) {
+                            //view.setCurrentPosition(lat, lon);
+                            gpsData.setLatLon(new LatLon(lat, lon));
+                            gpsData.setSpeed(speed);
+                            gpsData.setCourse(course);
+                            fireGpsDataChangeEvent(oldGpsData, gpsData);
+                        }
+                    }
+                } else {
+                    // not connected:
+                    try { Thread.sleep(1000); } catch (InterruptedException ignore) {};
+                }
+			} catch(IOException iox) {
+				connected = false;
+                gpsData.setFix(false);
+                fireGpsDataChangeEvent(oldGpsData, gpsData);
+                fireGpsStatusChangeEvent(LiveGpsStatus.GpsStatus.CONNECTION_FAILED, tr("Connection Failed"));
+				try { Thread.sleep(1000); } catch (InterruptedException ignore) {};
+				// send warning to layer
+
+			}
+		}
+        fireGpsStatusChangeEvent(LiveGpsStatus.GpsStatus.DISCONNECTED, tr("Disconnected"));
+		if (gpsdSocket != null) try { gpsdSocket.close(); } catch (Exception ignore) {};
+	}
+    
+
+	
+	public void shutdown()
+	{
+		shutdownFlag = true;
+	}
+}
Index: applications/editors/josm/plugins/surveyor/src/livegps/LiveGpsData.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/livegps/LiveGpsData.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/livegps/LiveGpsData.java	(revision 2936)
@@ -0,0 +1,143 @@
+/**
+ * 
+ */
+package livegps;
+
+import org.openstreetmap.josm.data.coor.LatLon;
+
+/**
+ * @author cdaller
+ *
+ */
+public class LiveGpsData {
+    private LatLon latLon;
+    private float course;
+    private float speed;
+    private boolean fix;
+    /**
+     * @param latitude
+     * @param longitude
+     * @param course
+     * @param speed
+     * @param haveFix
+     */
+    public LiveGpsData(double latitude, double longitude, float course, float speed, boolean haveFix) {
+        super();
+        this.latLon = new LatLon(latitude, longitude);
+        this.course = course;
+        this.speed = speed;
+        this.fix = haveFix;
+    }
+    /**
+     * 
+     */
+    public LiveGpsData() {
+        // TODO Auto-generated constructor stub
+    }
+    /**
+     * @return the course
+     */
+    public float getCourse() {
+        return this.course;
+    }
+    /**
+     * @param course the course to set
+     */
+    public void setCourse(float course) {
+        this.course = course;
+    }
+    /**
+     * @return the haveFix
+     */
+    public boolean isFix() {
+        return this.fix;
+    }
+    /**
+     * @param haveFix the haveFix to set
+     */
+    public void setFix(boolean haveFix) {
+        this.fix = haveFix;
+    }
+    /**
+     * @return the latitude
+     */
+    public double getLatitude() {
+        return this.latLon.lat();
+    }
+    /**
+     * @return the longitude
+     */
+    public double getLongitude() {
+        return this.latLon.lon();
+    }
+    /**
+     * @return the speed in metres per second!
+     */
+    public float getSpeed() {
+        return this.speed;
+    }
+    /**
+     * @param speed the speed to set
+     */
+    public void setSpeed(float speed) {
+        this.speed = speed;
+    }
+    
+    /**
+     * @return the latlon
+     */
+    public LatLon getLatLon() {
+        return this.latLon;
+    }
+    
+    /**
+     * @param latLon
+     */
+    public void setLatLon(LatLon latLon) {
+        this.latLon = latLon;
+    }
+    
+    public String toString() {
+        return getClass().getSimpleName() + "[fix=" + fix + ", lat=" + latLon.lat() 
+        + ", long=" + latLon.lon() + ", speed=" + speed + ", course=" + course + "]";
+        
+    }
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + Float.floatToIntBits(this.course);
+        result = prime * result + ((this.latLon == null) ? 0 : this.latLon.hashCode());
+        result = prime * result + Float.floatToIntBits(this.speed);
+        return result;
+    }
+    /* (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        final LiveGpsData other = (LiveGpsData) obj;
+        if (Float.floatToIntBits(this.course) != Float.floatToIntBits(other.course))
+            return false;
+        if (this.latLon == null) {
+            if (other.latLon != null)
+                return false;
+        } else if (!this.latLon.equals(other.latLon))
+            return false;
+        if (Float.floatToIntBits(this.speed) != Float.floatToIntBits(other.speed))
+            return false;
+        return true;
+    }
+
+    
+    
+}
Index: applications/editors/josm/plugins/surveyor/src/livegps/LiveGpsDialog.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/livegps/LiveGpsDialog.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/livegps/LiveGpsDialog.java	(revision 2936)
@@ -0,0 +1,113 @@
+/**
+ * 
+ */
+package livegps;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.GridLayout;
+import java.awt.Point;
+import java.awt.event.KeyEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.gui.dialogs.ToggleDialog;
+
+/**
+ * @author cdaller
+ *
+ */
+public class LiveGpsDialog extends ToggleDialog implements PropertyChangeListener {
+    private JLabel statusLabel;
+    private JLabel wayLabel;
+    private JLabel latLabel;
+    private JLabel longLabel;
+    private JLabel courseLabel;
+    private JLabel speedLabel;
+    private JPanel panel;
+
+    /**
+     * @param name
+     * @param iconName
+     * @param tooltip
+     * @param shortCut
+     * @param preferredHeight
+     */
+    public LiveGpsDialog(final MapFrame mapFrame) {
+        super(tr("Live GPS"), "livegps", tr("Show GPS data."), KeyEvent.VK_G, 100);
+        panel = new JPanel();
+        panel.setLayout(new GridLayout(6,2));
+        panel.add(new JLabel(tr("Status")));
+        panel.add(statusLabel = new JLabel());
+        panel.add(new JLabel(tr("Way Info")));
+        panel.add(wayLabel = new JLabel());
+        panel.add(new JLabel(tr("Latitude")));
+        panel.add(latLabel = new JLabel());
+        panel.add(new JLabel(tr("Longitude")));
+        panel.add(longLabel = new JLabel());
+        panel.add(new JLabel(tr("Speed")));
+        panel.add(speedLabel = new JLabel());
+        panel.add(new JLabel(tr("Course")));
+        panel.add(courseLabel = new JLabel());
+        add(new JScrollPane(panel), BorderLayout.CENTER);
+    }
+    
+    /* (non-Javadoc)
+     * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
+     */
+    public void propertyChange(PropertyChangeEvent evt) {
+        if (!isVisible())
+            return;
+        if("gpsdata".equals(evt.getPropertyName())) {
+            LiveGpsData data = (LiveGpsData) evt.getNewValue();
+            if(data.isFix()) {
+//                fixLabel.setText("fix");
+                panel.setBackground(Color.WHITE);
+                latLabel.setText(data.getLatitude() + "deg");
+                longLabel.setText(data.getLongitude() + "deg");
+                speedLabel.setText((data.getSpeed() * 3.6f) + "km/h"); // m(s to km/h
+                courseLabel.setText(data.getCourse() + "deg");
+                
+                EastNorth eastnorth = Main.proj.latlon2eastNorth(data.getLatLon()); 
+                Point xy = Main.map.mapView.getPoint(eastnorth); 
+                Way way = Main.map.mapView.getNearestWay(xy);
+                if(way != null) {
+                    String label = way.get("name") + " (" + way.get("highway") + ")";
+                    wayLabel.setText(label);
+                } else {
+                    wayLabel.setText("unknown");
+                }
+                
+            } else {
+//                fixLabel.setText("no fix");
+                latLabel.setText("");
+                longLabel.setText("");
+                speedLabel.setText("");
+                courseLabel.setText("");
+                panel.setBackground(Color.RED);
+            }
+        } else if ("gpsstatus".equals(evt.getPropertyName())) {
+            LiveGpsStatus status = (LiveGpsStatus) evt.getNewValue();
+            statusLabel.setText(status.getStatusMessage());
+            if(status.getStatus() != LiveGpsStatus.GpsStatus.CONNECTED) {
+                panel.setBackground(Color.RED);
+            } else {
+                panel.setBackground(Color.WHITE);                
+            }
+        }
+        
+    }
+
+
+
+}
Index: applications/editors/josm/plugins/surveyor/src/livegps/LiveGpsLayer.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/livegps/LiveGpsLayer.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/livegps/LiveGpsLayer.java	(revision 2936)
@@ -0,0 +1,148 @@
+package livegps;
+
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.gui.layer.RawGpsLayer;
+
+public class LiveGpsLayer extends RawGpsLayer implements PropertyChangeListener {
+    public final static String LAYER_NAME = "LiveGPS layer";
+	LatLon lastPos;
+	GpsPoint lastPoint;
+	Collection<GpsPoint> trackBeingWritten;
+	float speed;
+	float course;
+	String status;
+	//JLabel lbl;
+	boolean autocenter;
+	
+	public LiveGpsLayer(Collection<Collection<GpsPoint>> data)
+	{
+		super (data, LAYER_NAME, null);
+		if (data.isEmpty())
+		{
+			data.add(new ArrayList<GpsPoint>());
+		}
+		// use last track in collection:
+		for (Collection<GpsPoint> track : data) { 
+		    trackBeingWritten = track;
+		}
+		//lbl = new JLabel();
+	}
+	
+	void setCurrentPosition(double lat, double lon)
+	{
+		LatLon thisPos = new LatLon(lat, lon);
+		if ((lastPos != null) && (thisPos.equalsEpsilon(lastPos))) {
+			// no change in position
+			// maybe show a "paused" cursor or some such
+			return;
+		}
+			
+		lastPos = thisPos;
+		lastPoint = new GpsPoint (thisPos, new Date().toString());
+		// synchronize when adding data, as otherwise the autosave action
+		// needs concurrent access and this results in an exception!
+		synchronized (LiveGpsLock.class) {
+		    trackBeingWritten.add(lastPoint);            
+        }
+		if (autocenter) {
+		    center();
+		}
+		
+		//Main.map.repaint();
+	}
+
+	public void center()
+	{
+		if (lastPoint != null) 
+			Main.map.mapView.zoomTo(lastPoint.eastNorth, Main.map.mapView.getScale());
+	}
+	
+//	void setStatus(String status)
+//	{
+//		this.status = status;
+//		Main.map.repaint();
+//        System.out.println("LiveGps status: " + status);
+//	}
+	
+	void setSpeed(float metresPerSecond)
+	{
+		speed = metresPerSecond;
+		//Main.map.repaint();
+	}
+
+	void setCourse(float degrees)
+	{
+		course = degrees;
+		//Main.map.repaint();
+	}
+	
+	void setAutoCenter(boolean ac)
+	{
+		autocenter = ac;
+	}
+
+	@Override public void paint(Graphics g, MapView mv)
+	{
+		super.paint(g, mv);
+//		int statusHeight = 50;
+//		Rectangle mvs = mv.getBounds();
+//		mvs.y = mvs.y + mvs.height - statusHeight;
+//		mvs.height = statusHeight;
+//		g.setColor(new Color(1.0f, 1.0f, 1.0f, 0.8f)); 
+//		g.fillRect(mvs.x, mvs.y, mvs.width, mvs.height);
+		
+		if (lastPoint != null)
+		{
+			Point screen = mv.getPoint(lastPoint.eastNorth);
+			g.setColor(Color.RED);
+			g.drawOval(screen.x-10, screen.y-10,20,20);
+			g.drawOval(screen.x-9, screen.y-9,18,18);
+		}
+        
+//		lbl.setText("gpsd: "+status+" Speed: " + speed + " Course: "+course);
+//		lbl.setBounds(0, 0, mvs.width-10, mvs.height-10);
+//		Graphics sub = g.create(mvs.x+5, mvs.y+5, mvs.width-10, mvs.height-10);
+//		lbl.paint(sub);
+        
+//        if(status != null) {
+//            g.setColor(Color.WHITE);
+//            g.drawString("gpsd: " + status, 5, mv.getBounds().height - 15); // lower left corner
+//        }
+	}
+    
+    /* (non-Javadoc)
+     * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
+     */
+    public void propertyChange(PropertyChangeEvent evt) {
+        if(!visible) {
+            return;
+        }
+        if("gpsdata".equals(evt.getPropertyName())) {
+            LiveGpsData data = (LiveGpsData) evt.getNewValue();
+            if(data.isFix()) {
+                setCurrentPosition(data.getLatitude(), data.getLongitude());
+                if(!Float.isNaN(data.getSpeed())) {
+                    setSpeed(data.getSpeed());
+                }
+                if(!Float.isNaN(data.getCourse())) {
+                    setCourse(data.getCourse());
+                }
+                Main.map.repaint();
+            }
+        }
+        
+    }
+
+}
Index: applications/editors/josm/plugins/surveyor/src/livegps/LiveGpsLock.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/livegps/LiveGpsLock.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/livegps/LiveGpsLock.java	(revision 2936)
@@ -0,0 +1,16 @@
+/**
+ * 
+ */
+package livegps;
+
+/**
+ * This class is only used to prevent concurrent object modification. So all classes that
+ * read or write live gps data must synchronize to this class. Especially the save action
+ * takes quite long, so concurrency problems occur.
+ * 
+ * @author cdaller
+ *
+ */
+public class LiveGpsLock {
+
+}
Index: applications/editors/josm/plugins/surveyor/src/livegps/LiveGpsPlugin.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/livegps/LiveGpsPlugin.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/livegps/LiveGpsPlugin.java	(revision 2936)
@@ -0,0 +1,143 @@
+package livegps;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.beans.PropertyChangeListener;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.KeyStroke;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.gui.layer.RawGpsLayer.GpsPoint;
+import org.openstreetmap.josm.plugins.Plugin;
+
+public class LiveGpsPlugin extends Plugin
+{
+	private LiveGpsAcquirer acquirer = null;
+	private Thread acquirerThread = null;
+    private JMenu lgpsmenu;
+    private JCheckBoxMenuItem lgpscapture;
+    private JMenuItem lgpscenter;
+    private JCheckBoxMenuItem lgpsautocenter;
+    private LiveGpsDialog lgpsdialog;
+    List<PropertyChangeListener>listenerQueue;
+    
+	private Collection<Collection<GpsPoint>> data = new ArrayList<Collection<GpsPoint>>();
+    private LiveGpsLayer lgpslayer;
+    
+    public LiveGpsPlugin() 
+    {        
+        JMenuBar menu = Main.main.menu;
+        lgpsmenu = new JMenu("LiveGPS");
+        lgpsmenu.setMnemonic(KeyEvent.VK_G);
+        menu.add(lgpsmenu, 2);
+        lgpscapture = new JCheckBoxMenuItem("Capture GPS Track");
+        lgpscapture.setSelected(false);
+        lgpscapture.setAccelerator(KeyStroke.getKeyStroke("alt R"));
+        lgpscapture.addActionListener(new ActionListener() {
+        	public void actionPerformed(ActionEvent ev) {
+        	    enableTracking(lgpscapture.isSelected());
+        	}
+        });
+        lgpsmenu.add(lgpscapture);
+
+        lgpscenter = new JMenuItem("Center Once", KeyEvent.VK_C);
+        lgpscenter.addActionListener(new ActionListener() {
+        	public void actionPerformed(ActionEvent ev) {
+        		lgpslayer.center();
+        	}
+        });
+        lgpsmenu.add(lgpscenter);
+        
+        
+        lgpsautocenter = new JCheckBoxMenuItem("Auto-Center on current position");
+        lgpsautocenter.setSelected(false);
+        lgpsautocenter.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent ev) {
+                lgpslayer.setAutoCenter(lgpsautocenter.isSelected());
+                if (lgpsautocenter.isSelected()) lgpslayer.center();
+            }
+        });
+        lgpsmenu.add(lgpsautocenter);        
+    }
+    
+    /**
+     * Enable or disable gps tracking
+     * @param enable if <code>true</code> tracking is started.
+     */
+    public void enableTracking(boolean enable) {
+        if ((acquirer != null) && (!enable))
+        {
+            acquirer.shutdown();
+            acquirerThread = null;
+        }
+        else if(enable)
+        {
+            if (acquirer == null) {
+                acquirer = new LiveGpsAcquirer();
+                if (lgpslayer == null) {
+                    lgpslayer = new LiveGpsLayer(data);
+                    Main.main.addLayer(lgpslayer);
+                }
+                // connect layer with acquirer:
+                addPropertyChangeListener(lgpslayer);
+                // add all listeners that were added before the acquirer existed:
+                if(listenerQueue != null) {
+                    for(PropertyChangeListener listener : listenerQueue) {
+                        addPropertyChangeListener(listener);
+                    }
+                    listenerQueue.clear();
+                }
+            }
+            if(acquirerThread == null) {
+                acquirerThread = new Thread(acquirer);
+                acquirerThread.start();
+            }
+        }
+    }
+    
+    
+    /**
+     * Add a listener for gps events.
+     * @param listener the listener.
+     */
+    public void addPropertyChangeListener(PropertyChangeListener listener) {
+        if(acquirer != null) {
+            acquirer.addPropertyChangeListener(listener);
+        } else {
+            if(listenerQueue == null) {
+                listenerQueue = new ArrayList<PropertyChangeListener>();
+            }
+            listenerQueue.add(listener);
+        }
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.openstreetmap.josm.plugins.Plugin#mapFrameInitialized(org.openstreetmap.josm.gui.MapFrame, org.openstreetmap.josm.gui.MapFrame)
+     */
+    @Override
+    public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
+        // add dialog
+        newFrame.addToggleDialog(lgpsdialog = new LiveGpsDialog(newFrame));
+        // connect listeners with acquirer:
+        addPropertyChangeListener(lgpsdialog);
+    }
+
+
+    /**
+     * @return the lgpsmenu
+     */
+    public JMenu getLgpsMenu() {
+        return this.lgpsmenu;
+    }
+
+}
Index: applications/editors/josm/plugins/surveyor/src/livegps/LiveGpsStatus.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/livegps/LiveGpsStatus.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/livegps/LiveGpsStatus.java	(revision 2936)
@@ -0,0 +1,49 @@
+/**
+ * 
+ */
+package livegps;
+
+/**
+ * @author cdaller
+ *
+ */
+public class LiveGpsStatus {
+    public enum GpsStatus {CONNECTING, CONNECTED, DISCONNECTED, CONNECTION_FAILED};
+    private String statusMessage;
+    private GpsStatus status;
+
+    /**
+     * @param status
+     * @param statusMessage
+     */
+    public LiveGpsStatus(GpsStatus status, String statusMessage) {
+        super();
+        this.status = status;
+        this.statusMessage = statusMessage;
+    }
+/**
+     * @return the status
+     */
+    public GpsStatus getStatus() {
+        return this.status;
+    }
+    /**
+     * @param status the status to set
+     */
+    public void setStatus(GpsStatus status) {
+        this.status = status;
+    }
+    /**
+     * @return the statusMessage
+     */
+    public String getStatusMessage() {
+        return this.statusMessage;
+    }
+    /**
+     * @param statusMessage the statusMessage to set
+     */
+    public void setStatusMessage(String statusMessage) {
+        this.statusMessage = statusMessage;
+    }
+
+}
Index: applications/editors/josm/plugins/surveyor/src/org/dinopolis/util/collection/Tuple.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/org/dinopolis/util/collection/Tuple.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/org/dinopolis/util/collection/Tuple.java	(revision 2936)
@@ -0,0 +1,131 @@
+/**
+ * 
+ */
+package org.dinopolis.util.collection;
+
+import java.util.Map;
+
+/**
+ * Simple implementation of a tuple (two objects).
+ * 
+ * @author cdaller
+ *
+ */
+public class Tuple<T1 extends Object, T2 extends Object> implements Map.Entry<T1, T2>{
+    T1 first;
+    T2 second;
+    
+    /**
+     * Default Constructor 
+     */
+    public Tuple() {
+    }
+    
+    /**
+     * Constructor filling the values.
+     * @param first the first value.
+     * @param second the second value.
+     */
+    public Tuple(T1 one, T2 two) {
+        this.first = one;
+        this.second = two;
+    }
+
+    /**
+     * @return the first
+     */
+    public T1 getFirst() {
+        return this.first;
+    }
+
+    /**
+     * @param first the first to set
+     */
+    public void setFirst(T1 first) {
+        this.first = first;
+    }
+
+    /**
+     * @return the second
+     */
+    public T2 getSecond() {
+        return this.second;
+    }
+
+    /**
+     * @param second the second to set
+     */
+    public T2 setSecond(T2 second) {
+        T2 oldValue = this.second;
+        this.second = second;
+        return oldValue;
+    }
+
+    /* (non-Javadoc)
+     * @see java.util.Map.Entry#getKey()
+     */
+    @Override
+    public T1 getKey() {
+        return getFirst();
+    }
+
+    /* (non-Javadoc)
+     * @see java.util.Map.Entry#getValue()
+     */
+    @Override
+    public T2 getValue() {
+        return getSecond();
+    }
+
+    /* (non-Javadoc)
+     * @see java.util.Map.Entry#setValue(java.lang.Object)
+     */
+    @Override
+    public T2 setValue(T2 value) {
+        return setSecond(value);
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((this.first == null) ? 0 : this.first.hashCode());
+        result = prime * result + ((this.second == null) ? 0 : this.second.hashCode());
+        return result;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        final Tuple other = (Tuple) obj;
+        if (this.first == null) {
+            if (other.first != null)
+                return false;
+        } else if (!this.first.equals(other.first))
+            return false;
+        if (this.second == null) {
+            if (other.second != null)
+                return false;
+        } else if (!this.second.equals(other.second))
+            return false;
+        return true;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#toString()
+     */
+    public String toString() {
+        return "[" + first + "=" + second + "]";
+    }
+}
Index: applications/editors/josm/plugins/surveyor/src/org/dinopolis/util/io/Tokenizer.java
===================================================================
--- applications/editors/josm/plugins/surveyor/src/org/dinopolis/util/io/Tokenizer.java	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/org/dinopolis/util/io/Tokenizer.java	(revision 2936)
@@ -0,0 +1,943 @@
+/***********************************************************************
+ * @(#)$RCSfile: Tokenizer.java,v $   $Revision: 1.6 $$Date: 2006/04/21 14:14:56 $
+ *
+ * Copyright (c) 2002 IICM, Graz University of Technology
+ * Inffeldgasse 16c, A-8010 Graz, Austria.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License (LGPL)
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ ***********************************************************************/
+
+package org.dinopolis.util.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PushbackReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+//----------------------------------------------------------------------
+/**
+
+ * This tokenizer merges the benefits of the java.lang.StringTokenizer
+ * class and the java.io.StreamTokenizer class. It provides a low
+ * level and a high level interface to the tokenizer. The low level
+ * interface consists of the method pair nextToken() and getWord(),
+ * where the first returns the type of token in the parsing process,
+ * and the latter returns the String element itself.
+ * <p>
+ * The high level interface consists of the methods hasNextLine() and
+ * nextLine(). They use the low level interface to parse the data line
+ * by line and create a list of strings from it.
+ * <p>
+ * It is unsure, if it is wise to mix the usage of the high and
+ * the low level interface. For normal usage, the high level interface
+ * should be more comfortable to use and does not provide any
+ * drawbacks.
+ * <p>
+
+ * An example for the high level interface:
+ * <pre>
+ *    try
+ *    {
+ *          // simple example, tokenizing string, no escape, but quoted
+ *          // works:
+ *      System.out.println("example 1");
+ *      Tokenizer tokenizer = new Tokenizer("text,,,\"another,text\"");
+ *      List tokens;
+ *      while(tokenizer.hasNextLine())
+ *      {
+ *        tokens = tokenizer.nextLine();
+ *        System.out.println(tokens.get(0)); // prints 'text'
+ *        System.out.println(tokens.get(1)); // prints ''
+ *        System.out.println(tokens.get(2)); // prints ''
+ *        System.out.println(tokens.get(3)); // prints 'another,text'
+ *      }
+ *
+ *      System.out.println("example 2");
+ *          // simple example, tokenizing string, using escape char and
+ *          // quoted strings:
+ *      tokenizer = new Tokenizer("text,text with\\,comma,,\"another,text\"");
+ *      tokenizer.respectEscapedCharacters(true);
+ *      while(tokenizer.hasNextLine())
+ *      {
+ *        tokens = tokenizer.nextLine();
+ *        System.out.println(tokens.get(0)); // prints 'text'
+ *        System.out.println(tokens.get(1)); // prints 'text with, comma'
+ *        System.out.println(tokens.get(2)); // prints ''
+ *        System.out.println(tokens.get(3)); // prints 'another,text'
+ *      }
+ *    }
+ *    catch(Exception ioe)
+ *    {
+ *      ioe.printStackTrace();
+ *    }
+ * </pre>
+ * <p>
+ * The advantages compared to the StreamTokenizer class are: Unlike
+ * the StreamTokenizer, this Tokenizer class returns the delimiters as
+ * tokens and therefore may be used to tokenize e.g. comma separated
+ * files with empty fields (the StreamTokenizer handles multiple
+ * delimiters in a row like one delimiter).
+ * <p>
+ * The tokenizer respect quoted words, so the delimiter is ignored if
+ * inside quotes. And it may handle escaped characters (like an
+ * escaped quote character, or an escaped new line). So the line
+ * <code>eric,"he said, \"great!\""</code> returns <code>eric</code>
+ * and <code>he said, "great!"</code> as words.
+ * <p>
+ * Low level interface: The design of the Tokenizer allows to get
+ * empty columns as well as treat multiple delimiters in a row as one
+ * delimiter. For the first approach trigger the values on every
+ * DELIMITER and EOF token whereas for the second, trigger only on
+ * WORD tokens.
+ * <p>
+ * If one wants to be informed about empty words as well, use the
+ * Tokenizer like in the following code fragment:
+ *  <pre>
+ *   Tokenizer tokenizer = new Tokenizer("text,,,another text");
+ *   String word = "";
+ *   int token;
+ *   while((token = tokenizer.nextToken()) != Tokenizer.EOF)
+ *   {
+ *     switch(token)
+ *     {
+ *     case Tokenizer.EOL:
+ *       System.out.println("word: "+word);
+ *       word = "";
+ *       System.out.println("-------------");
+ *       break;
+ *     case Tokenizer.WORD:
+ *       word = tokenizer.getWord();
+ *       break;
+ *     case Tokenizer.QUOTED_WORD:
+ *       word = tokenizer.getWord() + " (quoted)";
+ *       break;
+ *     case Tokenizer.DELIMITER:
+ *       System.out.println("word: "+word);
+ *       word = "";
+ *       break;
+ *     default:
+ *       System.err.println("Unknown Token: "+token);
+ *     }
+ *   }
+ * </pre>
+ * In this example, if the delimiter is set to a comma, a line like
+ * <code>column1,,,"column4,partofcolumn4"</code> would be treated correctly.
+ * <p>
+ * This tokenizer uses the LF character as end of line characters. It
+ * ignores any CR characters, so it can be used in windows
+ * environments as well.
+ *
+ * @author Christof Dallermassl
+ * @version $Revision: 1.6 $
+ */
+
+public class Tokenizer
+{
+  /** the reader to read from */
+  protected PushbackReader reader_;
+  /** the buffer to create the tokens */
+  protected StringBuffer buffer_;
+  /** all characters in this string are used as delimiters */
+  protected String delimiters_ = ",";
+  /** the escape character */
+  protected int escapeChar_ = '\\';
+  /** the quote character */
+  protected int quoteChar_ = '"';
+
+  /** if true, characters are treated as escaped */
+  protected boolean escapeMode_ = false;
+
+  /** if true, end of line is respected */
+  protected boolean eolIsSignificant_ = true;
+  /** if true, escape characters are respected */
+  protected boolean respectEscapedChars_ = false;
+  /** if true, quoted words are respected */
+  protected boolean respectQuotedWords_ = true;
+
+  /** line count */
+  protected int lineCount_ = 1;
+
+  /** end of file marker */
+  protected boolean eofReached_ = false;
+
+  /** the last token that was found */
+  protected int lastToken_ = NOT_STARTED;
+
+  /** end of file token */
+  public static final int EOF = -1;
+  /** end of line token */
+  public static final int EOL = 0;
+  /** word token */
+  public static final int WORD = 1;
+  /** quoted word token */
+  public static final int QUOTED_WORD = 2;
+  /** delimiter token */
+  public static final int DELIMITER = 3;
+  /** error token */
+  public static final int ERROR = 4;
+  /** not started token */
+  public static final int NOT_STARTED = 5;
+
+
+//----------------------------------------------------------------------
+/**
+ * Creates a tokenizer that reads from the given string. It uses the
+ * comma as delimiter, does not respect escape characters but respects
+ * quoted words.
+ *
+ * @param string the string to read from.
+ */
+  public Tokenizer(String string)
+  {
+    this(new StringReader(string));
+  }
+
+//----------------------------------------------------------------------
+/**
+ * Creates a tokenizer that reads from the given string. All
+ * characters in the given delimiters string are used as
+ * delimiter. The tokenizer does not respect escape characters but
+ * respects quoted words.
+ *
+ * @param string the string to read from.
+ * @param delimiters the delimiters to use.
+ */
+  public Tokenizer(String string, String delimiters)
+  {
+    this(new StringReader(string));
+    setDelimiters(delimiters);
+  }
+
+//----------------------------------------------------------------------
+/**
+ * Creates a tokenizer that reads from the given string. It uses the
+ * comma as delimiter, does not respect escape characters but respects
+ * quoted words.
+ *
+ * @param inStream the stream to read from.
+ */
+  public Tokenizer(InputStream inStream)
+  {
+    this(new InputStreamReader(inStream));
+  }
+
+//----------------------------------------------------------------------
+/**
+ * Creates a tokenizer that reads from the given reader. It uses the
+ * comma as delimiter, does not respect escape characters but respects
+ * quoted words.
+ *
+ * @param reader the reader to read from.
+ */
+  public Tokenizer(Reader reader)
+  {
+    reader_ = new PushbackReader(reader,2);
+    buffer_ = new StringBuffer();
+  }
+
+//----------------------------------------------------------------------
+/**
+ * Set the delimiter character. The default is the comma.
+ *
+ * @param delimiterChar the delimiter character.
+ */
+  public void setDelimiter(int delimiterChar)
+  {
+    delimiters_ = new String(new char[]{(char)delimiterChar});
+  }
+
+//----------------------------------------------------------------------
+/**
+ * Get the first delimiter character.
+ *
+ * @return the delimiter character.
+ * @deprecated use the getDelimiters() method now
+ */
+  public int getDelimiter()
+  {
+    return(delimiters_.charAt(0));
+  }
+
+//----------------------------------------------------------------------
+/**
+ * Set the delimiter characters. All characters in the delimiters are
+ * used as delimiter.
+ *
+ * @param delimiters the delimiter characters.
+ */
+  public void setDelimiters(String delimiters)
+  {
+    delimiters_ = delimiters;
+  }
+
+//----------------------------------------------------------------------
+/**
+ * Get the delimiter character.
+ *
+ * @return the delimiter character.
+ */
+  public String getDelimiters()
+  {
+    return(delimiters_);
+  }
+
+//----------------------------------------------------------------------
+/**
+ * Set the escape character. The default is the backslash.
+ *
+ * @param escapeChar the escape character.
+ */
+  public void setEscapeChar(int escapeChar)
+  {
+    escapeChar_ = escapeChar;
+  }
+
+//----------------------------------------------------------------------
+/**
+ * Get the escape character.
+ *
+ * @return the escape character.
+ */
+  public int getEscapeChar()
+  {
+    return(escapeChar_);
+  }
+
+//----------------------------------------------------------------------
+/**
+ * If escape characters should be respected, set the param to
+ * <code>true</code>. The default is to ignore escape characters.
+ *
+ * @param respectEscaped If escape characters should be respected,
+ * set the param to <code>true</code>.
+ */
+  public void respectEscapedCharacters(boolean respectEscaped)
+  {
+    respectEscapedChars_ = respectEscaped;
+  }
+
+//----------------------------------------------------------------------
+/**
+ * Returns <code>true</code>, if escape character is respected.
+ *
+ * @return <code>true</code>, if escape character is respected.
+ */
+  public boolean respectEscapedCharacters()
+  {
+    return(respectEscapedChars_);
+  }
+
+//----------------------------------------------------------------------
+/**
+ * Get the quote character.
+ *
+ * @return the quote character.
+ */
+  public int getQuoteChar()
+  {
+    return (quoteChar_);
+  }
+
+//----------------------------------------------------------------------
+/**
+ * Set the quote character. The default is the double quote.
+ *
+ * @param quoteChar the quote character.
+ */
+  public void setQuoteChar(int quoteChar)
+  {
+    quoteChar_ = quoteChar;
+  }
+
+//----------------------------------------------------------------------
+/**
+ * If quoted words should be respected, set the param to
+ * <code>true</code>. The default is to respect quoted words.
+ *
+ * @param respectQuotes If quoted words should be respected,
+ * set the param to <code>true</code>.
+ */
+  public void respectQuotedWords(boolean respectQuotes)
+  {
+    respectQuotedWords_ = respectQuotes;
+  }
+
+//----------------------------------------------------------------------
+/**
+ * Returns <code>true</code>, if quoted words are respected.
+ *
+ * @return <code>true</code>, if quoted words are respected.
+ */
+  public boolean respectQuotedWords()
+  {
+    return(respectQuotedWords_);
+  }
+
+//----------------------------------------------------------------------
+/**
+ * If set to <code>true</code> the end of line is signaled by the EOL
+ * token.  If set to <code>false</code> end of line is treated as a
+ * normal delimiter. The default value is true;
+ *
+ * @param significant if the end of line is treated as a special token
+ * or as a delimiter.
+ */
+  public void eolIsSignificant(boolean significant)
+  {
+    eolIsSignificant_ = significant;
+  }
+
+//----------------------------------------------------------------------
+/**
+ * Returns <code>true</code>, if in case of an end of line detected,
+ * an EOL token is returned. If <code>false</code>, the end of line is
+ * treated as a normal delimiter.
+ *
+ * @return <code>true</code>, if in case of an end of line detected,
+ * an EOL token is returned. If <code>false</code>, the end of line is
+ * treated as a normal delimiter.
+ */
+  public boolean isEolSignificant()
+  {
+    return(eolIsSignificant_);
+  }
+
+
+//----------------------------------------------------------------------
+/**
+ * Returns the current line number of the reader.
+ *
+ * @return the current line number of the reader.
+ */
+  public int getLineNumber()
+  {
+    return(lineCount_);
+  }
+
+//----------------------------------------------------------------------
+/**
+ * Returns the value of the token. If the token was of the type WORD,
+ * the word is returned.
+ *
+ * @return the value of the token.
+ */
+  public String getWord()
+  {
+    return(buffer_.toString());
+  }
+
+//----------------------------------------------------------------------
+/**
+ * Returns the last token that was returned from the nextToken() method.
+ *
+ * @return the last token.
+ */
+  public int getLastToken()
+  {
+    return(lastToken_);
+  }
+
+//----------------------------------------------------------------------
+/**
+ * Returns true, if the given character is seen as a delimiter. This
+ * method respects escape_mode, so if the escape character was found
+ * before, it has to act accordingly (usually, return false, even if
+ * the character is a delimiter).
+ *
+ * @param character the character to check for delimiter
+ * @return true, if the given character is seen as a delimiter.
+ */
+  protected boolean isDelimiter(int character)
+  {
+        // check for escape mode:
+    if(escapeMode_)
+      return(false);
+
+    return(delimiters_.indexOf(character) >= 0);
+  }
+
+//----------------------------------------------------------------------
+/**
+ * Returns true, if the given character is seen as a quote
+ * character. This method respects escape_mode, so if the escape
+ * character was found before, it has to act accordingly (usually,
+ * return false, even if the character is a quote character).
+ *
+ * @param character the character to check for quote.
+ * @return true, if the given character is seen as a quote character.
+ */
+  protected boolean isQuoteChar(int character)
+  {
+    if(!respectQuotedWords_)
+      return(false);
+
+        // check for escape mode:
+    if(escapeMode_)
+      return(false);
+
+    return(character == quoteChar_);
+  }
+
+//----------------------------------------------------------------------
+/**
+ * Returns true, if the given character is seen as a escape
+ * character. This method respects escape_mode, so if the escape
+ * character was found before, it has to act accordingly (usually,
+ * return false, even if the character is a escape character).
+ * @param character the character to check for escape character.
+ * @return true, if the given character is seen as a escape character.
+ */
+  protected boolean isEscapeChar(int character)
+  {
+    if(!respectEscapedChars_)
+      return(false);
+
+        // check for escape mode:
+    if(escapeMode_)
+      return(false);
+
+    return(character == escapeChar_);
+  }
+
+//----------------------------------------------------------------------
+/**
+ * Returns true, if the given character is seen as a end of line
+ * character. This method respects end of line_mode, so if the end of
+ * line character was found before, it has to act accordingly
+ * (usually, return false, even if the character is a end of line
+ * character).
+ * @param character the character to check for end of line.
+ * @return true, if the given character is seen as a end of line
+ * character.
+ */
+  protected boolean isEndOfLine(int character)
+  {
+        // check for escape mode:
+    if(escapeMode_)
+    {
+      if(character == '\n')   // add line count, even if in escape mode!
+        lineCount_++;
+      return(false);
+    }
+    if(character == -1)
+      eofReached_ = true;
+
+    return((character=='\n') || (character=='\r') || (character == -1));
+  }
+
+//----------------------------------------------------------------------
+/**
+ * Closes the tokenizer (and the reader is uses internally).
+ *
+ * @exception IOException if an error occured.
+ */
+  public void close()
+    throws IOException
+  {
+    reader_.close();
+  }
+
+//----------------------------------------------------------------------
+/**
+ * Reads and returns the next character from the reader and checks for
+ * the escape character. If an escape character is read, a flag is set
+ * and the next character is read. A newline following the escape
+ * character is ignored.
+ *
+ * @return the next character.
+ * @exception IOException if an error occured.
+ */
+  protected int readNextChar()
+    throws IOException
+  {
+    int next_char = reader_.read();
+    if(escapeMode_)
+    {
+      escapeMode_ = false;
+    }
+    else
+    {
+      if(isEscapeChar(next_char))
+      {
+            // ignore escape char itself:
+        next_char = reader_.read();
+
+            // check for newline and ignore it:
+        if(isEndOfLine(next_char))
+        {
+          lineCount_++;
+          next_char = reader_.read();
+              // ignore CR:
+          if(next_char == '\r')
+          {
+            next_char = readNextChar();
+          }
+        }
+        escapeMode_ = true;
+      }
+    }
+        // ignore CR:
+    if(next_char == '\r')
+    {
+      next_char = readNextChar();
+    }
+    return(next_char);
+  }
+
+//----------------------------------------------------------------------
+/**
+ * Returns the next token from the reader. The token's value may be
+ * WORD, QUOTED_WORD, EOF, EOL, or DELIMITER. In the case or WORD or
+ * QUOTED_WORD the actual word can be obtained by the use of the
+ * getWord method.
+ *
+ * @return the next token.
+ * @exception IOException if an error occured.
+ */
+  public int nextToken()
+    throws IOException
+  {
+    buffer_.setLength(0);
+
+    int next_char;
+    next_char = readNextChar();
+
+        // handle EOF:
+    if(eofReached_)
+    {
+      lastToken_ = EOF;
+      return(EOF);
+    }
+
+        // handle EOL:
+    if(isEndOfLine(next_char))
+    {
+      lineCount_++;
+      if(eolIsSignificant_)
+      {
+        lastToken_ = EOL;
+        return(EOL);
+      }
+      else
+      {
+        lastToken_ = DELIMITER;
+        return(DELIMITER);
+      }
+    }
+
+        // handle DELIMITER
+    if(isDelimiter(next_char))
+    {
+      lastToken_ = DELIMITER;
+      return(DELIMITER);
+    }
+
+        // handle quoted words:
+    if(isQuoteChar(next_char))
+    {
+      while(true)
+      {
+        next_char = readNextChar();
+        if(isEndOfLine(next_char))
+        {
+          lastToken_ = ERROR;
+          return(ERROR);
+        }
+        else
+        {
+          if(isQuoteChar(next_char))
+          {
+            lastToken_ = QUOTED_WORD;
+            return(QUOTED_WORD);
+          }
+
+              // no special char, then append to buffer:
+          buffer_.append((char)next_char);
+        }
+      }
+    }
+
+        // handle 'normal' words:
+    while(true)
+    {
+      buffer_.append((char)next_char);
+      next_char = readNextChar();
+      if(isDelimiter(next_char) || isEndOfLine(next_char))
+      {
+        reader_.unread(next_char);
+        lastToken_ = WORD;
+        return(WORD);
+      }
+    }
+  }
+
+//----------------------------------------------------------------------
+/**
+ * Returns true, if the tokenizer can return another line.
+ *
+ * @return true, if the tokenizer can return another line.
+ * @exception IOException if an error occured.
+ */
+  public boolean hasNextLine()
+    throws IOException
+  {
+    if(lastToken_ == EOF)
+      return(false);
+
+    if((lastToken_ == EOL) || (lastToken_ == NOT_STARTED))
+    {
+      int next_char = readNextChar();
+      if(next_char == -1)
+        return(false);
+
+      reader_.unread(next_char);
+    }
+    return(true);
+  }
+
+
+//----------------------------------------------------------------------
+/**
+ * Returns a list of elements (Strings) from the next line of the
+ * tokenizer. If there are multiple delimiters without any values in
+ * between, empty (zero length) strings are added to the list. They
+ * may be removed by the use of the {@link
+ * #removeZeroLengthElements(List)} method.
+ *
+ * @return a list of elements (Strings) from the next line of the
+ * tokenizer.
+ * @exception IOException if an error occured.
+ */
+  public List<String> nextLine()
+    throws IOException
+  {
+    int token = nextToken();
+    List<String> list = new ArrayList<String>();
+    String word = "";
+//    while(token != Tokenizer.EOF)
+    while(true)
+    {
+      switch(token)
+      {
+        case Tokenizer.WORD:
+          word = getWord();
+          break;
+        case Tokenizer.QUOTED_WORD:
+          word = getWord();
+          break;
+        case Tokenizer.DELIMITER:
+          list.add(word);
+          word = "";
+          break;
+        case Tokenizer.EOL:
+        case Tokenizer.EOF:
+          list.add(word);
+          return(list);
+        default:
+          System.err.println("Unknown Token: "+token);
+      }
+      token = nextToken();
+    }
+//    return(list);
+  }
+
+//----------------------------------------------------------------------
+/**
+ * This helper method removes all zero length elements from the given
+ * list and returns it. The given list is not changed!
+ *
+ * @param list the list of String objects to remove the zero elements from.
+ * @return a copy of the given list where all zero length elements are removed.
+ */
+  public static List<String> removeZeroLengthElements(List<String> list)
+  {
+    return removeZeroLengthElements(list, false);
+  }
+
+//----------------------------------------------------------------------
+  /**
+   * This helper method trims all elements and removes all zero length
+   * (length is taken after trimming leading and trailing spaces) elements from the given
+   * list and returns it. This method copies the (trimmed and) non-zero elements to a
+   * new list.
+   *
+   * @param list the list of String objects to remove the zero elements from.
+   * @param trim if set to <code>true</code>, all leading and trailing spaces are removed from
+   * the elements. This is done, before the length is compared to zero (and the element
+   * may be removed if the length is zero). If set to <code>true</code>, elements
+   * that only consist of spaces are removed as well!
+   * @return the list where all zero length elements are remove.
+   */
+    public static List<String> removeZeroLengthElements(List<String> list, boolean trim)
+    {
+      Iterator<String> iterator = list.iterator();
+      String value;
+      List<String> new_list = new ArrayList<String>();
+      while(iterator.hasNext())
+      {
+        value = iterator.next();
+        if (trim)
+          value = value.trim();
+        if(value.length() != 0)
+          new_list.add(value);
+      }
+      return(new_list);
+    }
+
+//  /**
+//   * Demonstrates the low level interface.
+//   * @param args command line arguments.
+//   */
+//  protected static void testLowLevel(String[] args)
+//  {
+//    try
+//    {
+//      String filename;
+//      if(args.length > 0)
+//        filename = args[0];
+//      else
+//        filename = "/filer/cdaller/tmp/test.csv";
+//
+//      Tokenizer tokenizer = new Tokenizer(new BufferedReader(new FileReader(filename)));
+////      Tokenizer tokenizer = new Tokenizer("column1,\"quoted column2\",column3\\, with quoted comma");
+//      tokenizer.setDelimiter(',');
+////      tokenizer.eolIsSignificant(false);
+//      tokenizer.respectEscapedCharacters(true);
+//      tokenizer.respectQuotedWords(true);
+//
+//      int token;
+//      while((token = tokenizer.nextToken()) != Tokenizer.EOF)
+//      {
+//        switch(token)
+//        {
+//        case Tokenizer.EOL:
+//          System.out.println("------------- ");
+//          break;
+//        case Tokenizer.WORD:
+//          System.out.println("line" +tokenizer.getLineNumber() +" word: "+tokenizer.getWord());
+//          break;
+//        case Tokenizer.QUOTED_WORD:
+//          System.out.println("line" +tokenizer.getLineNumber() +" quoted word: "+tokenizer.getWord());
+//          break;
+//        case Tokenizer.DELIMITER:
+//          System.out.println("delimiter");
+//          break;
+//        default:
+//          System.err.println("Unknown Token: "+token);
+//        }
+//      }
+//      tokenizer.close();
+//    }
+//    catch(Exception ioe)
+//    {
+//      ioe.printStackTrace();
+//    }
+//  }
+//
+//
+//  /**
+//   * Demonstration of the high level interface.
+//   * @param args command line arguments.
+//   */
+//  protected static void testHighLevel(String[] args)
+//  {
+//    try
+//    {
+//      String filename;
+//      if(args.length > 0)
+//        filename = args[0];
+//      else
+//        filename = "/filer/cdaller/tmp/test.csv";
+//
+//      Tokenizer tokenizer = new Tokenizer(new BufferedReader(new FileReader(filename)));
+////      Tokenizer tokenizer = new Tokenizer("column1,\"quoted column2\",column3\\, with quoted comma");
+//      tokenizer.setDelimiter(',');
+////      tokenizer.eolIsSignificant(false);
+//      tokenizer.respectEscapedCharacters(true);
+//      tokenizer.respectQuotedWords(true);
+//
+//      List list;
+//      while(tokenizer.hasNextLine())
+//      {
+//        list = tokenizer.nextLine();
+//        System.out.println("List: "+list);
+//        System.out.println("List w/o zero length elements: "+removeZeroLengthElements(list));
+//        System.out.println("--");
+//      }
+//
+//    }
+//    catch(Exception ioe)
+//    {
+//      ioe.printStackTrace();
+//    }
+//  }
+//
+//   /**
+//   *  Demo code for the high level interface.
+//   */
+//  protected static void testHighLevelExample()
+//  {
+//    try
+//    {
+//          // simple example, tokenizing string, no escape, but quoted
+//          // works:
+//      System.out.println("example 1");
+//      Tokenizer tokenizer = new Tokenizer("text,,,\"another,text\"");
+//      List tokens;
+//      while(tokenizer.hasNextLine())
+//      {
+//        tokens = tokenizer.nextLine();
+//        System.out.println(tokens.get(0)); // prints 'text'
+//        System.out.println(tokens.get(1)); // prints ''
+//        System.out.println(tokens.get(2)); // prints ''
+//        System.out.println(tokens.get(3)); // prints 'another,text'
+//      }
+//
+//      System.out.println("example 2");
+//          // simple example, tokenizing string, using escape char and
+//          // quoted strings:
+//      tokenizer = new Tokenizer("text,text with\\,comma,,\"another,text\"");
+//      tokenizer.respectEscapedCharacters(true);
+//      while(tokenizer.hasNextLine())
+//      {
+//        tokens = tokenizer.nextLine();
+//        System.out.println(tokens.get(0)); // prints 'text'
+//        System.out.println(tokens.get(1)); // prints 'text with, comma'
+//        System.out.println(tokens.get(2)); // prints ''
+//        System.out.println(tokens.get(3)); // prints 'another,text'
+//      }
+//    }
+//    catch(Exception ioe)
+//    {
+//      ioe.printStackTrace();
+//    }
+//  }
+//
+//  public static void main(String[] args)
+//  {
+////    testLowLevel(args);
+////    testHighLevel(args);
+////    testGeonetUTF8(args);
+//    testHighLevelExample();
+//  }
+}
+
+
Index: applications/editors/josm/plugins/surveyor/src/surveyor.xml
===================================================================
--- applications/editors/josm/plugins/surveyor/src/surveyor.xml	(revision 2936)
+++ applications/editors/josm/plugins/surveyor/src/surveyor.xml	(revision 2936)
@@ -0,0 +1,60 @@
+<?xml version="1.0"?>
+<surveyor rows="3" columns="3" width="0" height="0">
+  <!-- icons can either be absolute paths or relative paths to the .josm directory -->
+  <!-- action class: either fully qualified classnames or if not found, 
+       package at.dallermassl.josm.plugin.surveyor.action is assumed -->
+  <button label="Tunnel Start" hotkey="T" icon="tunnel">
+    <action class="ConsolePrinterAction" params="tunnel start,tunnel"/>
+    <action class="SetWaypointAction" params="tunnel start"/>
+    <!--action class="PlayAudioAction" params="/usr/share/sounds/KDE_Window_Iconify.wav"/-->
+    <action class="SystemExecuteAction" params="mplayer,-quiet,/usr/share/apps/klettres/de/alpha/t.ogg"/>
+  </button>
+  <button label="Bridge" hotkey="B" type="toggle">
+    <action class="SetWaypointAction" params="bridge"/>
+    <action class="SystemExecuteAction" params="mplayer,-quiet,/usr/share/apps/klettres/de/alpha/b.ogg"/>
+  </button>
+  <button label="Village/City" hotkey="V" icon="place">
+    <action class="AnnotationPresetAction" params="Places"/>
+    <action class="SystemExecuteAction" params="mplayer,-quiet,/usr/share/apps/klettres/de/alpha/v.ogg"/>
+  </button>
+  <button label="Parking" hotkey="P" icon="parking">
+    <action class="SetNodeAction" params="amenity=parking"/>
+    <action class="SystemExecuteAction" params="mplayer,-quiet,/usr/share/apps/klettres/de/alpha/p.ogg"/>
+  </button>
+  <button label="One Way" hotkey="O" icon="noentry">
+    <action class="SetWaypointAction" params="oneway=yes"/>
+    <action class="SystemExecuteAction" params="mplayer,-quiet,/usr/share/apps/klettres/de/alpha/o.ogg"/>
+  </button>
+  <button label="Church" hotkey="C" icon="church">
+    <action class="SetNodeAction" params="amenity=place_of_worship,denomination=christian"/>
+    <action class="SystemExecuteAction" params="mplayer,-quiet,/usr/share/apps/klettres/de/alpha/c.ogg"/>
+  </button>
+  <button label="Fuel Station" hotkey="F" icon="fuel">
+    <action class="SetNodeAction" params="amenity=fuel"/>
+    <action class="SystemExecuteAction" params="mplayer,-quiet,/usr/share/apps/klettres/de/alpha/f.ogg"/>
+  </button>
+  <button label="Hotel" hotkey="H" icon="bed">
+    <action class="SetNodeAction" params="tourism=hotel"/>
+    <action class="SystemExecuteAction" params="mplayer,-quiet,/usr/share/apps/klettres/de/alpha/h.ogg"/>
+  </button>
+  <button label="Motorway" hotkey="1">
+    <action class="SetWaypointAction" params="Motorway"/>
+    <action class="SystemExecuteAction" params="mplayer,-quiet,/usr/share/apps/klettres/de/alpha/x.ogg"/>
+  </button>
+  <button label="Primary" hotkey="2">
+    <action class="SetWaypointAction" params="Primary"/>
+    <action class="SystemExecuteAction" params="mplayer,-quiet,/usr/share/apps/klettres/de/alpha/x.ogg"/>
+  </button>
+  <button label="Secondary" hotkey="3">
+    <action class="SetWaypointAction" params="Secondary"/>
+    <action class="SystemExecuteAction" params="mplayer,-quiet,/usr/share/apps/klettres/de/alpha/x.ogg"/>
+  </button>
+  <button label="Unclassified" hotkey="4">
+    <action class="SetWaypointAction" params="Unclassified"/>
+    <action class="SystemExecuteAction" params="mplayer,-quiet,/usr/share/apps/klettres/de/alpha/x.ogg"/>
+  </button>
+  <button label="Residential" hotkey="5">
+    <action class="SetWaypointAction" params="Residential"/>
+    <action class="SystemExecuteAction" params="mplayer,-quiet,/usr/share/apps/klettres/de/alpha/x.ogg"/>
+  </button>
+</surveyor>
