Index: /applications/editors/josm/plugins/livegps/livegps/LiveGpsAcquirer.java
===================================================================
--- /applications/editors/josm/plugins/livegps/livegps/LiveGpsAcquirer.java	(revision 3076)
+++ /applications/editors/josm/plugins/livegps/livegps/LiveGpsAcquirer.java	(revision 3076)
@@ -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/livegps/livegps/LiveGpsLayer.java
===================================================================
--- /applications/editors/josm/plugins/livegps/livegps/LiveGpsLayer.java	(revision 3076)
+++ /applications/editors/josm/plugins/livegps/livegps/LiveGpsLayer.java	(revision 3076)
@@ -0,0 +1,147 @@
+package livegps;
+
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Point;
+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/livegps/livegps/LiveGpsPlugin.java
===================================================================
--- /applications/editors/josm/plugins/livegps/livegps/LiveGpsPlugin.java	(revision 3076)
+++ /applications/editors/josm/plugins/livegps/livegps/LiveGpsPlugin.java	(revision 3076)
@@ -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;
+    }
+
+}
