Index: /applications/editors/josm/plugins/surveyor/build.xml
===================================================================
--- /applications/editors/josm/plugins/surveyor/build.xml	(revision 34590)
+++ /applications/editors/josm/plugins/surveyor/build.xml	(revision 34591)
@@ -14,8 +14,8 @@
     -->
     <property name="plugin.author" value="Christof Dallermassl"/>
-    <property name="plugin.class" value="at.dallermassl.josm.plugin.surveyor.SurveyorPlugin"/>
+    <property name="plugin.class" value="org.openstreetmap.josm.plugins.surveyor.SurveyorPlugin"/>
     <property name="plugin.description" value="Allow adding markers/nodes on current gps positions."/>
     <property name="plugin.icon" value="images/surveyormenu.png"/>
-    <property name="plugin.link" value="https://wiki.openstreetmap.org/index.php/JOSM/Plugins/Surveyor"/>
+    <property name="plugin.link" value="https://wiki.openstreetmap.org/wiki/JOSM/Plugins/Surveyor"/>
     <property name="plugin.requires" value="livegps"/>
     <property name="plugin.stage" value="60"/>
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 34590)
+++ /applications/editors/josm/plugins/surveyor/src/org/dinopolis/util/io/Tokenizer.java	(revision 34591)
@@ -23,6 +23,4 @@
 
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
 import java.io.PushbackReader;
 import java.io.Reader;
@@ -228,17 +226,4 @@
 //----------------------------------------------------------------------
 /**
- * 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
Index: /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/ActionConstants.java
===================================================================
--- /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/ActionConstants.java	(revision 34591)
+++ /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/ActionConstants.java	(revision 34591)
@@ -0,0 +1,20 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.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/org/openstreetmap/josm/plugins/surveyor/AutoSaveAction.java
===================================================================
--- /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/AutoSaveAction.java	(revision 34591)
+++ /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/AutoSaveAction.java	(revision 34591)
@@ -0,0 +1,61 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.surveyor;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.text.MessageFormat;
+import java.util.Date;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import javax.swing.AbstractButton;
+
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.tools.Shortcut;
+
+import livegps.LiveGpsLayer;
+
+/**
+ * @author cdaller
+ *
+ */
+public class AutoSaveAction extends JosmAction {
+    private static final long serialVersionUID = -8608679323231116043L;
+    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() {
+        super(tr("AutoSave LiveData"), "autosave.png", tr("Save captured data to file every minute."),
+        Shortcut.registerShortcut("surveyor:autosave", tr("Tool: {0}", tr("AutoSave LiveData")),
+        KeyEvent.VK_S, Shortcut.ALT_CTRL_SHIFT), true);
+    }
+
+    @Override
+    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 AutoSaveGpsLayerTimerTask(gpxFilename, LiveGpsLayer.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/org/openstreetmap/josm/plugins/surveyor/AutoSaveEditLayerTimerTask.java
===================================================================
--- /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/AutoSaveEditLayerTimerTask.java	(revision 34591)
+++ /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/AutoSaveEditLayerTimerTask.java	(revision 34591)
@@ -0,0 +1,64 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.surveyor;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.util.TimerTask;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.io.OsmWriter;
+import org.openstreetmap.josm.io.OsmWriterFactory;
+import org.openstreetmap.josm.tools.Logging;
+
+/**
+ * @author cdaller
+ *
+ */
+public class AutoSaveEditLayerTimerTask extends TimerTask {
+    private File file;
+
+    public AutoSaveEditLayerTimerTask(String filename) {
+        file = new File(filename);
+    }
+
+    @Override
+    public void run() {
+        OsmDataLayer layer = MainApplication.getLayerManager().getEditLayer();
+        if (layer == null) {
+            return;
+        }
+        try {
+            DataSet dataset = layer.data;
+
+            // write to temporary file, on success, rename tmp file to target file:
+            File tmpFile = new File(file.getAbsoluteFile()+".tmp");
+            Logging.info("AutoSaving osm data to file " + file.getAbsolutePath());
+            synchronized (SurveyorLock.class) {
+                OsmWriter w = OsmWriterFactory.createOsmWriter(new PrintWriter(new OutputStreamWriter(
+                        new FileOutputStream(tmpFile), StandardCharsets.UTF_8)), false, dataset.getVersion());
+                w.header();
+                w.writeDataSources(dataset);
+                w.writeContent(dataset);
+                w.footer();
+            }
+            tmpFile.renameTo(file);
+        } catch (IOException ex) {
+            Logging.error(ex);
+            JOptionPane.showMessageDialog(MainApplication.getMainFrame(),
+                tr("Error while exporting {0}: {1}", file.getAbsoluteFile(), ex.getMessage()),
+                tr("Error"),
+                JOptionPane.ERROR_MESSAGE);
+        }
+    }
+}
Index: /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/AutoSaveGpsLayerTimerTask.java
===================================================================
--- /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/AutoSaveGpsLayerTimerTask.java	(revision 34591)
+++ /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/AutoSaveGpsLayerTimerTask.java	(revision 34591)
@@ -0,0 +1,80 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.surveyor;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.TimerTask;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.layer.GpxLayer;
+import org.openstreetmap.josm.io.GpxWriter;
+import org.openstreetmap.josm.plugins.surveyor.util.LayerUtil;
+
+/**
+ * TimerTask that writes the data of a {@link GpxLayer} 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;
+    }
+
+    @Override
+    public void run() {
+
+        try {
+            GpxLayer gpsLayer = LayerUtil.findGpsLayer(gpsLayerName, GpxLayer.class);
+            if (gpsLayer == null) {
+                return;
+            }
+            // write to temporary file, on success, rename tmp file to target file:
+            File tmpFile = new File(file.getAbsoluteFile()+".tmp");
+            System.out.println("AutoSaving data to file " + file.getAbsolutePath());
+            // synchronize on layer to prevent concurrent adding of data to the layer
+            // quite a hack, but no other object to sync available :-(
+            // @see LiveGpsLayer
+            PrintWriter out = new PrintWriter(Files.newBufferedWriter(tmpFile.toPath(), StandardCharsets.UTF_8));
+            try (GpxWriter gpxWriter = new GpxWriter(out)) {
+                gpxWriter.write(gpsLayer.data);
+            }
+            tmpFile.renameTo(file);
+        } catch (IOException ioExc) {
+            ioExc.printStackTrace();
+            JOptionPane.showMessageDialog(MainApplication.getMainFrame(),
+                tr("Error while exporting {0}: {1}", file.getAbsoluteFile(), ioExc.getMessage()),
+                tr("Error"),
+                JOptionPane.ERROR_MESSAGE);
+        }
+    }
+}
Index: /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/ButtonDescription.java
===================================================================
--- /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/ButtonDescription.java	(revision 34591)
+++ /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/ButtonDescription.java	(revision 34591)
@@ -0,0 +1,258 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.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;
+
+/**
+ * this class represents a button as it is described in the xml file.
+ * @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 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 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<>();
+        list.add(actionDescription);
+        return list;
+    }
+
+    /**
+     * @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;
+        if (type == null) {
+            this.type = ButtonType.SINGLE;
+        } else {
+            this.type = 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 getButtonType() {
+        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("Unknown button type '" + type + "' given. Allowed values are " + Arrays.toString(ButtonType.values()));
+        }
+    }
+    
+    /**
+     * @param type the type to set
+     */
+    public void setButtonType(ButtonType type) {
+        this.type = type;
+    }
+
+    /**
+     * Sets the name of the 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(null, iconName);
+        if (icon == null)
+            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;
+        
+        SelectionStateAdapter(Action theAction, AbstractButton theButton) {
+            action = theAction;
+            button = theButton;
+        }
+        
+        protected void configure() {
+            action.addPropertyChangeListener(this);
+            button.addItemListener(this);
+        }
+
+        @Override
+        public void itemStateChanged(ItemEvent e) {
+            boolean value = e.getStateChange() == ItemEvent.SELECTED;
+            Boolean valueObj = Boolean.valueOf(value);
+            action.putValue(ActionConstants.SELECTED_KEY, valueObj);
+        }
+
+        @Override
+        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/org/openstreetmap/josm/plugins/surveyor/ButtonType.java
===================================================================
--- /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/ButtonType.java	(revision 34591)
+++ /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/ButtonType.java	(revision 34591)
@@ -0,0 +1,11 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.surveyor;
+
+/**
+ * @author cdaller
+ *
+ */
+public enum ButtonType {
+    SINGLE,
+    TOGGLE
+}
Index: /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/GpsActionEvent.java
===================================================================
--- /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/GpsActionEvent.java	(revision 34591)
+++ /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/GpsActionEvent.java	(revision 34591)
@@ -0,0 +1,27 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.surveyor;
+
+import java.awt.event.ActionEvent;
+
+import org.openstreetmap.josm.data.coor.LatLon;
+
+/**
+ * @author cdaller
+ *
+ */
+public class GpsActionEvent extends ActionEvent {
+    private static final long serialVersionUID = 2674961758007055637L;
+    private LatLon coordinates;
+
+    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/org/openstreetmap/josm/plugins/surveyor/GpsDataSource.java
===================================================================
--- /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/GpsDataSource.java	(revision 34591)
+++ /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/GpsDataSource.java	(revision 34591)
@@ -0,0 +1,16 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.surveyor;
+
+import livegps.LiveGpsData;
+
+/**
+ * @author cdaller
+ *
+ */
+public interface GpsDataSource {
+    /**
+     * Returns gps data.
+     * @return gps data.
+     */
+    LiveGpsData getGpsData();
+}
Index: /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/MetaAction.java
===================================================================
--- /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/MetaAction.java	(revision 34591)
+++ /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/MetaAction.java	(revision 34591)
@@ -0,0 +1,102 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.surveyor;
+
+import java.awt.event.ActionEvent;
+import java.util.List;
+
+import javax.swing.AbstractAction;
+import javax.swing.Icon;
+import javax.swing.JFrame;
+
+import org.openstreetmap.josm.spi.preferences.Config;
+
+import livegps.LiveGpsData;
+
+/**
+ * Action that fires a {@link SurveyorAction} to the registered actions.
+ *
+ * @author cdaller
+ *
+ */
+public class MetaAction extends AbstractAction {
+    private static final long serialVersionUID = -1523524381092575809L;
+    private List<SurveyorActionDescription> actions;
+    private GpsDataSource gpsDataSource;
+    private long lastActionCall = 0;
+    public static final long MIN_TIME_DIFF = 500; // 500ms
+
+    /**
+     *
+     */
+    public MetaAction() {
+    }
+
+    public MetaAction(String name) {
+        super(name);
+    }
+
+    public MetaAction(String name, Icon icon) {
+        super(name, icon);
+    }
+
+    /**
+     * @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;
+    }
+
+    @Override
+    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;
+            return;
+        }
+        lastActionCall = time;
+
+        // 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 && gpsData.isFix()) {
+            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!");
+            // TEST for offline test only:
+            if (Config.getPref().getBoolean("surveyor.debug")) {
+                for (SurveyorActionDescription action : actions) {
+                    action.actionPerformed(new GpsActionEvent(e, 0, 0));
+                }
+            }
+        }
+        JFrame frame = SurveyorPlugin.getSurveyorFrame();
+        if (frame != null && frame.isVisible()) {
+            frame.toFront();
+        }
+    }
+
+    public void setGpsDataSource(GpsDataSource gpsDataSource) {
+        this.gpsDataSource = gpsDataSource;
+    }
+}
Index: /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/SurveyorAction.java
===================================================================
--- /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/SurveyorAction.java	(revision 34591)
+++ /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/SurveyorAction.java	(revision 34591)
@@ -0,0 +1,23 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.surveyor;
+
+import java.util.List;
+
+/**
+ * @author cdaller
+ *
+ */
+public interface SurveyorAction {
+
+    /**
+     * Action callback indicating that the action should do something.
+     * @param event the event.
+     */
+    void actionPerformed(GpsActionEvent event);
+
+    /**
+     * Sets the parameters for the action execution.
+     * @param parameters the parameters.
+     */
+    void setParameters(List<String> parameters);
+}
Index: /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/SurveyorActionDescription.java
===================================================================
--- /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/SurveyorActionDescription.java	(revision 34591)
+++ /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/SurveyorActionDescription.java	(revision 34591)
@@ -0,0 +1,104 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.surveyor;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.dinopolis.util.io.Tokenizer;
+import org.openstreetmap.josm.tools.Logging;
+
+/**
+ * @author cdaller
+ *
+ */
+public class SurveyorActionDescription {
+    private String actionClass;
+    private List<String> params;
+    private SurveyorAction action;
+
+    /**
+     * Default Constructor
+     */
+    public SurveyorActionDescription() {
+        super();
+    }
+    
+    public SurveyorActionDescription(String actionClass) {
+        super();
+        this.actionClass = actionClass;
+    }
+    
+    public SurveyorActionDescription(String actionClass, List<String> params) {
+        super();
+        this.actionClass = actionClass;
+        this.params = params;
+    }
+    
+    public SurveyorActionDescription(String actionClass, String[] params) {
+        super();
+        this.actionClass = actionClass;
+        this.params = new ArrayList<>();
+        for (int index = 0; index < params.length; index++) {
+            this.params.add(params[index]);
+        }
+    }
+    
+    /**
+     * @return the actionClass
+     */
+    public String getActionClass() {
+        return this.actionClass;
+    }
+    
+    /**
+     * @param actionClass the actionClass to set
+     */
+    public void setActionClass(String actionClass) {
+        this.actionClass = actionClass;
+    }
+    
+    /**
+     * @return the params
+     */
+    public List<String> getParameterList() {
+        return this.params;
+    }
+    
+    /**
+     * @param params the params to set
+     */
+    public 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) {
+            Logging.debug(ignore);
+        }
+    }
+
+}
Index: /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/SurveyorActionFactory.java
===================================================================
--- /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/SurveyorActionFactory.java	(revision 34591)
+++ /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/SurveyorActionFactory.java	(revision 34591)
@@ -0,0 +1,42 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.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 final class SurveyorActionFactory {
+    private static Map<String, SurveyorAction> actionCache = new HashMap<>();
+    public static final String DEFAULT_PACKAGE = SurveyorActionFactory.class.getPackage().getName() + ".action";
+
+    private SurveyorActionFactory() {
+        // Hide default contructir for utilities classes
+    }
+    
+    public static SurveyorAction getInstance(String actionClass) {
+        try {
+            SurveyorAction action = actionCache.get(actionClass);
+            if (action == null) {
+                try {
+                    action = (SurveyorAction) Class.forName(actionClass).getDeclaredConstructor().newInstance();
+                } catch (ClassNotFoundException e) {
+                    actionClass = DEFAULT_PACKAGE + "." + actionClass;
+                    action = (SurveyorAction) Class.forName(actionClass).getDeclaredConstructor().newInstance();
+                }
+                actionCache.put(actionClass, action);
+            }
+            return action;
+        } catch (ReflectiveOperationException e) {
+            throw new RuntimeException("Could not create action class '" + actionClass + "'", e);
+        }
+    }
+
+}
Index: /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/SurveyorComponent.java
===================================================================
--- /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/SurveyorComponent.java	(revision 34591)
+++ /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/SurveyorComponent.java	(revision 34591)
@@ -0,0 +1,123 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.surveyor;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.GridLayout;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.spi.preferences.Config;
+
+import livegps.LiveGpsData;
+
+/**
+ * @author cdaller
+ *
+ */
+public class SurveyorComponent extends JComponent implements PropertyChangeListener, GpsDataSource {
+    private static final long serialVersionUID = 4539838472057529042L;
+    private LiveGpsData gpsData;
+    private int rows = 0;
+    private int columns = 0;
+    private int width = 0;
+    private int height = 0;
+    private JLabel streetLabel;
+    private JPanel buttonPanel;
+    private Set<String> hotKeys;
+
+    public SurveyorComponent() {
+        super();
+        hotKeys = new HashSet<>();
+        setLayout(new BorderLayout());
+        streetLabel = new JLabel(tr("Way: "));
+        float fontSize = Float.parseFloat(Config.getPref().get(SurveyorPlugin.PREF_KEY_STREET_NAME_FONT_SIZE, "35"));
+        Config.getPref().put(SurveyorPlugin.PREF_KEY_STREET_NAME_FONT_SIZE, String.valueOf(fontSize));
+        streetLabel.setFont(streetLabel.getFont().deriveFont(35f));
+        add(streetLabel, BorderLayout.NORTH);
+        buttonPanel = new JPanel();
+        add(buttonPanel, BorderLayout.CENTER);
+    }
+
+    /**
+     * 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);
+        buttonPanel.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) {
+        System.out.println("setting columns to " +columnsString);
+        columns = Integer.parseInt(columnsString);
+        buttonPanel.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) {
+        if (description.getHotkey() != "" && hotKeys.contains(description.getHotkey())) {
+            JOptionPane.showMessageDialog(MainApplication.getMainFrame(), 
+                    tr("Duplicate hotkey for button ''{0}'' - button will be ignored!", description.getLabel()));
+        } else {
+            if (rows == 0 && columns == 0) {
+                setColumns("4");
+            }
+            description.setGpsDataSource(this);
+            buttonPanel.add(description.createComponent());
+            hotKeys.add(description.getHotkey());
+        }
+    }
+
+    @Override
+    public void propertyChange(PropertyChangeEvent evt) {
+        if ("gpsdata".equals(evt.getPropertyName())) {
+            gpsData = (LiveGpsData) evt.getNewValue();
+            streetLabel.setText(tr("Way: ") + gpsData.getWayInfo());
+        }
+    }
+
+    @Override
+    public LiveGpsData getGpsData() {
+        return gpsData;
+    }
+}
Index: /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/SurveyorLock.java
===================================================================
--- /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/SurveyorLock.java	(revision 34591)
+++ /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/SurveyorLock.java	(revision 34591)
@@ -0,0 +1,6 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.surveyor;
+
+public class SurveyorLock {
+
+}
Index: /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/SurveyorPlugin.java
===================================================================
--- /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/SurveyorPlugin.java	(revision 34591)
+++ /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/SurveyorPlugin.java	(revision 34591)
@@ -0,0 +1,67 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.surveyor;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JFrame;
+import javax.swing.JMenu;
+
+import org.openstreetmap.josm.gui.MainMenu;
+import org.openstreetmap.josm.plugins.PluginHandler;
+import org.openstreetmap.josm.plugins.PluginInformation;
+
+import livegps.LiveGpsPlugin;
+
+// CHECKSTYLE.OFF: HideUtilityClassConstructorCheck
+
+/**
+ * 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 final class SurveyorPlugin {
+
+    private static JFrame surveyorFrame;
+    public static final String PREF_KEY_STREET_NAME_FONT_SIZE = "surveyor.way.fontsize";
+    
+    /**
+     * Constructs a new {@code SurveyorPlugin}.
+     * @param info plugin information
+     */
+    public SurveyorPlugin(PluginInformation info) {
+
+        LiveGpsPlugin gpsPlugin = (LiveGpsPlugin) PluginHandler.getPlugin("livegps");
+        if (gpsPlugin == null)
+            throw new IllegalStateException(tr("SurveyorPlugin needs LiveGpsPlugin, but could not find it!"));
+
+        JMenu m = gpsPlugin.getLgpsMenu();
+        m.addSeparator();
+        MainMenu.add(m, new SurveyorShowAction(gpsPlugin));
+
+        AutoSaveAction autoSaveAction = new AutoSaveAction();
+        JCheckBoxMenuItem autoSaveMenu = new JCheckBoxMenuItem(autoSaveAction);
+        m.add(autoSaveMenu);
+        autoSaveMenu.setAccelerator(autoSaveAction.getShortcut().getKeyStroke());
+    }
+
+    /**
+     * @return the surveyorFrame
+     */
+    public static JFrame getSurveyorFrame() {
+        return surveyorFrame;
+    }
+
+    /**
+     * @param surveyorFrame the surveyorFrame to set
+     */
+    public static void setSurveyorFrame(JFrame surveyorFrame) {
+        SurveyorPlugin.surveyorFrame = surveyorFrame;
+    }
+
+    // CHECKSTYLE.ON: HideUtilityClassConstructorCheck
+}
Index: /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/SurveyorShowAction.java
===================================================================
--- /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/SurveyorShowAction.java	(revision 34591)
+++ /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/SurveyorShowAction.java	(revision 34591)
@@ -0,0 +1,164 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.surveyor;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.AbstractAction;
+import javax.swing.ActionMap;
+import javax.swing.InputMap;
+import javax.swing.JFrame;
+import javax.swing.JOptionPane;
+import javax.swing.KeyStroke;
+
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.plugins.surveyor.util.ResourceLoader;
+import org.openstreetmap.josm.spi.preferences.Config;
+import org.openstreetmap.josm.tools.Logging;
+import org.openstreetmap.josm.tools.Shortcut;
+import org.openstreetmap.josm.tools.XmlObjectParser;
+import org.xml.sax.SAXException;
+
+import livegps.LiveGpsPlugin;
+
+/**
+ * @author cdaller
+ *
+ */
+public class SurveyorShowAction extends JosmAction {
+    private static final long serialVersionUID = 2184570223633094734L;
+    private static final String DEFAULT_SOURCE = "resource://resources/surveyor.xml";
+    private JFrame surveyorFrame;
+    private LiveGpsPlugin gpsPlugin;
+
+    public SurveyorShowAction(LiveGpsPlugin gpsPlugin) {
+        super(tr("Surveyor..."), "surveyormenu.png", tr("Open surveyor tool."),
+        Shortcut.registerShortcut("surveyor:open", tr("Tool: {0}", tr("Surveyor...")),
+        KeyEvent.VK_R, Shortcut.CTRL_SHIFT), true);
+        this.gpsPlugin = gpsPlugin;
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent e) {
+        if (surveyorFrame == null) {
+            surveyorFrame = new JFrame();
+
+            SurveyorComponent comp = createComponent();
+
+            // add component as gps event listener:
+            gpsPlugin.addPropertyChangeListener(comp);
+
+            // add some hotkeys to the component:
+            ActionMap actionMap = comp.getActionMap();
+            InputMap inputMap = comp.getInputMap();
+            // zoomout:
+            actionMap.put("zoomout", new AbstractAction() {
+                @Override
+                public void actionPerformed(ActionEvent e) {
+                    if (MainApplication.getMap() != null && MainApplication.getMap().mapView != null) {
+                        MainApplication.getMap().mapView.zoomToFactor(2);
+                    }
+                }
+            });
+            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0), "zoomout");
+            // zoomin:
+            actionMap.put("zoomin", new AbstractAction() {
+                @Override
+                public void actionPerformed(ActionEvent e) {
+                    if (MainApplication.getMap() != null && MainApplication.getMap().mapView != null) {
+                        MainApplication.getMap().mapView.zoomToFactor(1/2);
+                    }
+                }
+            });
+            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0), "zoomin");
+            // autocenter:
+            actionMap.put("autocenter", new AbstractAction() {
+                @Override
+                public void actionPerformed(ActionEvent e) {
+                    // toggle autocenter
+                    gpsPlugin.setAutoCenter(!gpsPlugin.isAutoCenter());
+                }
+            });
+            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, 0), "autocenter");
+
+            surveyorFrame.add(comp);
+            surveyorFrame.pack();
+            surveyorFrame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
+            //surveyorFrame.setTitle((String)getValue(AbstractAction.NAME));
+            surveyorFrame.setTitle(tr("Surveyor"));
+            // <FIXXME date="28.04.2007" author="cdaller">
+            // TODO get old pos of frame from properties
+            // </FIXXME>
+            SurveyorPlugin.setSurveyorFrame(surveyorFrame);
+        }
+        surveyorFrame.setAlwaysOnTop(true);
+        surveyorFrame.setVisible(true);
+
+    }
+
+    public SurveyorComponent createComponent() {
+        String source = Config.getPref().get("surveyor.source");
+        if (source == null || source.length() == 0) {
+            source = DEFAULT_SOURCE;
+            Config.getPref().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>
+        }
+        try (InputStream in = ResourceLoader.getInputStream(source)) {
+            return createComponent(in);
+        } catch (IOException e) {
+            Logging.error(e);
+            JOptionPane.showMessageDialog(MainApplication.getMainFrame(), tr("Could not read surveyor definition: {0}", source));
+        } catch (SAXException e) {
+            Logging.error(e);
+            JOptionPane.showMessageDialog(MainApplication.getMainFrame(), tr("Error parsing {0}: {1}", source, e.getMessage()));
+        }
+        return null;
+    }
+
+    /**
+     * 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, StandardCharsets.UTF_8)));
+        List<SurveyorActionDescription> actions = new ArrayList<>();
+        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<>();
+            } else if (object instanceof SurveyorActionDescription) {
+                //System.out.println("SurveyorActionDescription " + object);
+                actions.add((SurveyorActionDescription) object);
+            } else {
+                Logging.error("surveyor: unknown xml element: " + object);
+            }
+        }
+        return surveyorComponent;
+    }
+}
Index: /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/action/AbstractSurveyorAction.java
===================================================================
--- /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/action/AbstractSurveyorAction.java	(revision 34591)
+++ /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/action/AbstractSurveyorAction.java	(revision 34591)
@@ -0,0 +1,27 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.surveyor.action;
+
+import java.util.List;
+
+import org.openstreetmap.josm.plugins.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;
+    }
+
+    @Override
+    public void setParameters(List<String> parameters) {
+        this.parameters = parameters;
+    }
+}
Index: /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/action/BeepAction.java
===================================================================
--- /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/action/BeepAction.java	(revision 34591)
+++ /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/action/BeepAction.java	(revision 34591)
@@ -0,0 +1,43 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.surveyor.action;
+
+import java.awt.Toolkit;
+import java.util.List;
+
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.plugins.surveyor.GpsActionEvent;
+import org.openstreetmap.josm.plugins.surveyor.SurveyorAction;
+import org.openstreetmap.josm.tools.Logging;
+
+/**
+ * @author cdaller
+ *
+ */
+public class BeepAction implements SurveyorAction {
+    int beepNumber = 1;
+
+    @Override
+    public void actionPerformed(GpsActionEvent event) {
+        // run as a separate thread
+        MainApplication.worker.execute(() -> {
+            for (int index = 0; index < beepNumber; ++index) {
+                Toolkit.getDefaultToolkit().beep();
+                try {
+                    Thread.sleep(200);
+                } catch (InterruptedException ignore) {
+                    Logging.debug(ignore);
+                }
+            }
+        });
+    }
+
+    @Override
+    public void setParameters(List<String> parameters) {
+        try {
+            beepNumber = Integer.parseInt(parameters.get(0));
+        } catch (NumberFormatException e) {
+            // print but recover
+            Logging.warn(e);
+        }
+    }
+}
Index: /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/action/ConsolePrinterAction.java
===================================================================
--- /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/action/ConsolePrinterAction.java	(revision 34591)
+++ /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/action/ConsolePrinterAction.java	(revision 34591)
@@ -0,0 +1,19 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.surveyor.action;
+
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.plugins.surveyor.GpsActionEvent;
+
+/**
+ * @author cdaller
+ *
+ */
+public class ConsolePrinterAction extends AbstractSurveyorAction {
+
+    @Override
+    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/org/openstreetmap/josm/plugins/surveyor/action/PlayAudioAction.java
===================================================================
--- /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/action/PlayAudioAction.java	(revision 34591)
+++ /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/action/PlayAudioAction.java	(revision 34591)
@@ -0,0 +1,71 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.surveyor.action;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+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.gui.MainApplication;
+import org.openstreetmap.josm.plugins.surveyor.GpsActionEvent;
+import org.openstreetmap.josm.plugins.surveyor.util.ResourceLoader;
+import org.openstreetmap.josm.tools.Logging;
+
+/**
+ * Action that plays an audio file.
+ *
+ * @author cdaller
+ *
+ */
+public class PlayAudioAction extends AbstractSurveyorAction {
+    private String audioSource = null;
+
+    @Override
+    public void actionPerformed(GpsActionEvent event) {
+        // run as a separate thread
+        MainApplication.worker.execute(() -> {
+            try {
+                if (audioSource == null) {
+                    audioSource = getParameters().get(0);
+                }
+                InputStream in = new BufferedInputStream(ResourceLoader.getInputStream(audioSource));
+                AudioInputStream stream = AudioSystem.getAudioInputStream(in);
+
+                // 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 (IOException | LineUnavailableException | UnsupportedAudioFileException e1) {
+                Logging.error(e1);
+            }
+        });
+    }
+}
Index: /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/action/SetNodeAction.java
===================================================================
--- /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/action/SetNodeAction.java	(revision 34591)
+++ /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/action/SetNodeAction.java	(revision 34591)
@@ -0,0 +1,69 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.surveyor.action;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.plugins.surveyor.GpsActionEvent;
+import org.openstreetmap.josm.plugins.surveyor.SurveyorAction;
+import org.openstreetmap.josm.plugins.surveyor.SurveyorLock;
+import org.openstreetmap.josm.tools.Pair;
+
+/**
+ * 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<Pair<String, String>> keyValues;
+
+    /**
+     * Default Constructor
+     */
+    public SetNodeAction() {
+
+    }
+
+    @Override
+    public void setParameters(List<String> parameters) {
+        keyValues = new ArrayList<>();
+        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 Pair<>(key, value));
+            } else {
+                System.err.println("SetNodeAction: ignoring invalid key value pair: " + keyValuePair);
+            }
+        }
+    }
+
+    @Override
+    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 (Pair<String, String> entry : keyValues) {
+            node.put(entry.a, entry.b);
+        }
+        synchronized (SurveyorLock.class) {
+            DataSet ds = MainApplication.getLayerManager().getEditDataSet();
+            if (ds != null) {
+                ds.addPrimitive(node);
+                ds.setSelected(node);
+            }
+        }
+        MainApplication.getMap().repaint();
+    }
+}
Index: /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/action/SetWaypointAction.java
===================================================================
--- /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/action/SetWaypointAction.java	(revision 34591)
+++ /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/action/SetWaypointAction.java	(revision 34591)
@@ -0,0 +1,135 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.surveyor.action;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.Collection;
+
+import javax.swing.JToggleButton;
+
+import org.openstreetmap.josm.data.gpx.GpxData;
+import org.openstreetmap.josm.data.gpx.WayPoint;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.layer.GpxLayer;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.markerlayer.Marker;
+import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
+import org.openstreetmap.josm.plugins.surveyor.GpsActionEvent;
+import org.openstreetmap.josm.plugins.surveyor.SurveyorLock;
+import org.openstreetmap.josm.plugins.surveyor.SurveyorPlugin;
+import org.openstreetmap.josm.plugins.surveyor.action.gui.DialogClosingThread;
+import org.openstreetmap.josm.plugins.surveyor.action.gui.WaypointDialog;
+import org.openstreetmap.josm.plugins.surveyor.util.LayerUtil;
+import org.openstreetmap.josm.tools.Logging;
+
+import livegps.LiveGpsLayer;
+
+/**
+ * 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 LiveGpsLayer liveGpsLayer;
+    private MarkerLayer markerLayer;
+    public static final String MARKER_LAYER_NAME = tr("Surveyor waypoint layer");
+    private WaypointDialog dialog;
+
+    /**
+     * Default Constructor.
+     */
+    public SetWaypointAction() {
+
+    }
+
+    @Override
+    public void actionPerformed(GpsActionEvent event) {
+        String markerTitle = getParameters().get(0);
+        Object source = event.getSource();
+        if (source instanceof JToggleButton) {
+            if (((JToggleButton) source).isSelected()) {
+                markerTitle = tr("{0} start", markerTitle);
+            } else {
+                markerTitle = tr("{0} end", markerTitle);
+            }
+        }
+        
+        String iconName = getParameters().size() > 1 ? getParameters().get(1).trim() : null;
+
+        long timeout = DialogClosingThread.DEFAULT_TIMEOUT;
+        if (getParameters().size() > 2) {
+            try {
+                timeout = Integer.parseInt(getParameters().get(2));
+            } catch (NumberFormatException e) {
+                Logging.error(e.getMessage());
+            }
+        }
+        
+        String markerText = markerTitle;
+        
+        if (timeout > 0) {
+            if (dialog == null) {
+                dialog = new WaypointDialog();
+            }
+    
+            String inputText = dialog.openDialog(SurveyorPlugin.getSurveyorFrame(), tr("Waypoint Description"), timeout*1000);
+            if (inputText != null && inputText.length() > 0) {
+                inputText = inputText.replaceAll("<", "_"); // otherwise the gpx file is ruined
+                markerText = markerText + " " + inputText;
+            }
+        }
+
+        // add the waypoint to the marker layer AND to the gpx layer
+        // (easy export of data + waypoints):
+        MarkerLayer layer = getMarkerLayer();
+        GpxLayer gpsLayer = getGpxLayer();
+        WayPoint waypoint = new WayPoint(event.getCoordinates());
+        waypoint.attr.put("name", markerText);
+        if (iconName != null && !iconName.isEmpty())
+            waypoint.attr.put("sym", iconName);
+        synchronized (SurveyorLock.class) {
+            layer.data.add(new Marker(event.getCoordinates(), markerText, iconName, null, -1.0, 0.0));
+            if (gpsLayer != null) {
+                gpsLayer.data.waypoints.add(waypoint);
+            }
+        }
+
+        MainApplication.getMap().repaint();
+    }
+
+    /**
+     * Returns the marker layer with the name {@link #MARKER_LAYER_NAME}.
+     * @return the marker layer with the name {@link #MARKER_LAYER_NAME}.
+     */
+    public MarkerLayer getMarkerLayer() {
+        if (markerLayer == null) {
+            markerLayer = LayerUtil.findGpsLayer(MARKER_LAYER_NAME, MarkerLayer.class);
+
+            if (markerLayer == null) {
+                // not found, add a new one
+                markerLayer = new MarkerLayer(new GpxData(), MARKER_LAYER_NAME, null, null);
+                MainApplication.getLayerManager().addLayer(markerLayer);
+            }
+        }
+        return markerLayer;
+    }
+
+    /**
+     * Returns the gpx layer that is filled by the live gps data.
+     * @return the gpx layer that is filled by the live gps data.
+     */
+    public GpxLayer getGpxLayer() {
+        if (liveGpsLayer == null) {
+            Collection<Layer> layers = MainApplication.getLayerManager().getLayers();
+            for (Layer layer : layers) {
+                if (layer instanceof LiveGpsLayer) {
+                    liveGpsLayer = (LiveGpsLayer) layer;
+                    break;
+                }
+            }
+        }
+        return liveGpsLayer;
+    }
+}
Index: /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/action/SystemExecuteAction.java
===================================================================
--- /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/action/SystemExecuteAction.java	(revision 34591)
+++ /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/action/SystemExecuteAction.java	(revision 34591)
@@ -0,0 +1,48 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.surveyor.action;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+
+import org.openstreetmap.josm.plugins.surveyor.GpsActionEvent;
+import org.openstreetmap.josm.tools.Logging;
+
+/**
+ * @author cdaller
+ *
+ */
+public class SystemExecuteAction extends AbstractSurveyorAction {
+
+    @Override
+    public void actionPerformed(GpsActionEvent event) {
+        final ProcessBuilder builder = new ProcessBuilder(getParameters());
+        builder.directory(new File(System.getProperty("user.home")));
+
+        Logging.debug("Directory : " + builder.directory());
+        Thread executionThread = new Thread() {
+
+            @Override
+            public void run() {
+                try {
+                    final Process process = builder.start();
+                    InputStream is = process.getInputStream();
+                    InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8);
+                    BufferedReader br = new BufferedReader(isr);
+                    String line;
+
+                    while ((line = br.readLine()) != null) {
+                        Logging.info(getClass().getSimpleName() + ": " + line);
+                    }
+
+                    Logging.info(getClass().getSimpleName() + "Program terminated!");
+                } catch (Exception t) {
+                    Logging.error(t);
+                }
+            }
+        };
+        executionThread.start();
+    }
+}
Index: /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/action/TaggingPresetAction.java
===================================================================
--- /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/action/TaggingPresetAction.java	(revision 34591)
+++ /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/action/TaggingPresetAction.java	(revision 34591)
@@ -0,0 +1,69 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.surveyor.action;
+
+import java.util.List;
+
+import javax.swing.Action;
+
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.gui.tagging.presets.TaggingPreset;
+import org.openstreetmap.josm.gui.tagging.presets.TaggingPresets;
+import org.openstreetmap.josm.plugins.surveyor.GpsActionEvent;
+import org.openstreetmap.josm.plugins.surveyor.SurveyorAction;
+
+/**
+ * @author cdaller
+ *
+ */
+public class TaggingPresetAction implements SurveyorAction {
+    private String presetName;
+    private TaggingPreset preset;
+
+    @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...
+        preset.actionPerformed(null);
+
+    }
+
+    @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 TaggingPreset getAnnotationPreset(String name) {
+        for (TaggingPreset preset : TaggingPresets.getTaggingPresets()) {
+            if (name.equals(preset.getValue(Action.NAME))) {
+                return preset;
+            }
+        }
+        return null;
+    }
+}
Index: /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/action/gui/DialogClosingThread.java
===================================================================
--- /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/action/gui/DialogClosingThread.java	(revision 34591)
+++ /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/action/gui/DialogClosingThread.java	(revision 34591)
@@ -0,0 +1,114 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.surveyor.action.gui;
+
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+
+import javax.swing.JDialog;
+import javax.swing.JTextField;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+
+import org.openstreetmap.josm.tools.Logging;
+
+/**
+ * @author cdaller
+ *
+ */
+public class DialogClosingThread extends Thread implements KeyListener, DocumentListener {
+    public static final long DEFAULT_TIMEOUT = 5000;
+    private JDialog dialog;
+    private long timeout;
+    private long loopCount;
+
+    /**
+     * Using the given dialog and the default timeout.
+     */
+    public DialogClosingThread(JDialog dialog) {
+        this(dialog, DEFAULT_TIMEOUT);
+    }
+
+    public DialogClosingThread(JDialog dialog, long timeout) {
+        super();
+        this.dialog = dialog;
+        this.timeout = timeout;
+        this.loopCount = timeout / 1000;
+    }
+
+    @Override
+    public void run() {
+        String title = dialog.getTitle();
+        while (loopCount > 0) {
+            dialog.setTitle(title + " (" + loopCount + "sec)");
+            --loopCount;
+            try {
+                sleep(1000);
+            } catch (InterruptedException ignore) {
+                Logging.debug(ignore);
+            }
+        }
+
+        dialog.setVisible(false);
+        dialog.dispose();
+    }
+
+    public void reset() {
+        this.loopCount = timeout / 1000;
+    }
+
+    @Override
+    public void keyPressed(KeyEvent e) {
+        reset();
+        Logging.debug("keypressed: " + e.getKeyCode());
+    }
+
+    @Override
+    public void keyReleased(KeyEvent e) {
+        reset();
+        Logging.debug("keyreleased: " + e.getKeyCode());
+    }
+
+    @Override
+    public void keyTyped(KeyEvent e) {
+        reset();
+        Logging.debug("keytyped: " + e.getKeyCode());
+    }
+
+    public void observe(Container container) {
+        for (Component component : container.getComponents()) {
+            if (component instanceof JTextField) {
+                observe((JTextField) component);
+            } else {
+                observe(component);
+            }
+        }
+    }
+
+    public void observe(Component component) {
+        component.addKeyListener(this);
+    }
+
+    public void observe(JTextField textfield) {
+        textfield.getDocument().addDocumentListener(this);
+    }
+
+    @Override
+    public void changedUpdate(DocumentEvent e) {
+        reset();
+        Logging.debug("changedUpdate: " + e);
+    }
+
+    @Override
+    public void insertUpdate(DocumentEvent e) {
+        reset();
+        Logging.debug("insertUpdate: " + e);
+    }
+
+    @Override
+    public void removeUpdate(DocumentEvent e) {
+        reset();
+        Logging.debug("removeUpdate: " + e);
+    }
+}
Index: /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/action/gui/WaypointDialog.java
===================================================================
--- /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/action/gui/WaypointDialog.java	(revision 34591)
+++ /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/action/gui/WaypointDialog.java	(revision 34591)
@@ -0,0 +1,53 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.surveyor.action.gui;
+
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JOptionPane;
+import javax.swing.JTextField;
+
+/**
+ * @author cdaller
+ *
+ */
+public class WaypointDialog {
+
+    public String openDialog(JFrame frame, String message, long timeout) {
+
+        JTextField textField = new JTextField(10);
+
+        //Create an array of the text and components to be displayed.
+        Object[] array = {message, textField};
+
+        //Create an array specifying the number of dialog buttons and their text.
+        Object[] options = {"OK"};
+
+        //Create the JOptionPane.
+        final JOptionPane optionPane = new JOptionPane(array,
+                                    JOptionPane.QUESTION_MESSAGE,
+                                    JOptionPane.OK_OPTION,
+                                    null,
+                                    options,
+                                    options[0]);
+
+        final JDialog dialog = new JDialog(frame, "Enter Description", true);
+        DialogClosingThread closer = new DialogClosingThread(dialog, timeout);
+        closer.observe(textField);
+        dialog.setContentPane(optionPane);
+        optionPane.addPropertyChangeListener(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);
+            }
+        });
+        closer.start();
+        dialog.pack();
+        dialog.setVisible(true);
+
+        return textField.getText();
+    }
+}
Index: /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/util/LayerUtil.java
===================================================================
--- /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/util/LayerUtil.java	(revision 34591)
+++ /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/util/LayerUtil.java	(revision 34591)
@@ -0,0 +1,37 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.surveyor.util;
+
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.layer.Layer;
+
+/**
+ * @author cdaller
+ *
+ */
+public final class LayerUtil {
+
+    private LayerUtil() {
+        // Hide default contructir for utilities classes
+    }
+    
+    /**
+     * 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>.
+     */
+    @SuppressWarnings("unchecked")
+    public static <LayerType extends Layer> LayerType findGpsLayer(String layerName, Class<LayerType> layerType) {
+        Layer result = null;
+        if (MainApplication.getMap() != null && MainApplication.getMap().mapView != null) {
+            for (Layer layer : MainApplication.getLayerManager().getLayers()) {
+                if (layerName.equals(layer.getName()) && layerType.isAssignableFrom(layer.getClass())) {
+                    result = layer;
+                    break;
+                }
+            }
+        }
+        return (LayerType) result;
+    }
+}
Index: /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/util/ResourceLoader.java
===================================================================
--- /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/util/ResourceLoader.java	(revision 34591)
+++ /applications/editors/josm/plugins/surveyor/src/org/openstreetmap/josm/plugins/surveyor/util/ResourceLoader.java	(revision 34591)
@@ -0,0 +1,39 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.surveyor.util;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+/**
+ * @author cdaller
+ *
+ */
+public final class ResourceLoader {
+
+    private ResourceLoader() {
+
+    }
+
+    /**
+     * Returns an inputstream from urls, files and classloaders, depending on the name.
+     * @param source the source: if starting with &quot;http://&quot;, &quot;ftp://&quot; or
+     * &quot;file://&quot; source is interpreted as an URL. If starting with &quot;resource://&quot;
+     * the classloader is used. All other sources are interpreted as filenames.
+     * @return the inputstream.
+     * @throws IOException if an error occurs on opening the url, or if the file is not found.
+     */
+    @SuppressWarnings("resource")
+    public static InputStream getInputStream(String source) throws IOException {
+        InputStream in = null;
+        if (source.startsWith("http://") || source.startsWith("https://") || source.startsWith("ftp://") || source.startsWith("file:")) {
+            in = new URL(source).openStream();
+        } else if (source.startsWith("resource://")) {
+            in = ResourceLoader.class.getResourceAsStream(source.substring("resource:/".length()));
+        } else {
+            in = new FileInputStream(source);
+        }
+        return in;
+    }
+}
