Index: /applications/editors/josm/plugins/public_transport/src/public_transport/SettingsStoptypeCommand.java
===================================================================
--- /applications/editors/josm/plugins/public_transport/src/public_transport/SettingsStoptypeCommand.java	(revision 20772)
+++ /applications/editors/josm/plugins/public_transport/src/public_transport/SettingsStoptypeCommand.java	(revision 20772)
@@ -0,0 +1,92 @@
+package public_transport;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+
+import java.util.Collection;
+import java.util.Vector;
+import javax.swing.DefaultListModel;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.MutableTreeNode;
+
+public class SettingsStoptypeCommand extends Command
+{
+  private class HighwayRailway
+  {
+    public HighwayRailway(Node node)
+    {
+      this.node = node;
+      highway = node.get("highway");
+      railway = node.get("railway");
+    }
+    
+    public Node node;
+    public String highway;
+    public String railway;
+  };
+  
+  private Vector< HighwayRailway > oldStrings = null;
+  private WaypointTableModel waypointTM = null;
+  private DefaultListModel tracksListModel = null;
+  private String type = null;
+  
+  public SettingsStoptypeCommand(StopImporterAction controller)
+  {
+    waypointTM = controller.getWaypointTableModel();
+    tracksListModel = controller.getTracksListModel();
+    type = controller.getDialog().getStoptype();
+    oldStrings = new Vector< HighwayRailway >();
+  }
+  
+  public boolean executeCommand()
+  {
+    oldStrings.clear();
+    for (int i = 0; i < waypointTM.getRowCount(); ++i)
+    {
+      if ((Node)waypointTM.nodes.elementAt(i) != null)
+      {
+	Node node = (Node)waypointTM.nodes.elementAt(i);
+	oldStrings.add(new HighwayRailway(node));
+	StopImporterAction.setTagsWrtType(node, type);
+      }
+    }
+    for (int j = 0; j < tracksListModel.size(); ++j)
+    {
+      TrackReference track = (TrackReference)tracksListModel.elementAt(j);
+      for (int i = 0; i < track.stoplistTM.getRowCount(); ++i)
+      {
+	if ((Node)track.stoplistTM.nodes.elementAt(i) != null)
+	{
+	  Node node = (Node)track.stoplistTM.nodes.elementAt(i);
+	  oldStrings.add(new HighwayRailway(node));
+	  StopImporterAction.setTagsWrtType(node, type);
+	}
+      }
+    }
+    return true;
+  }
+  
+  public void undoCommand()
+  {
+    for (int i = 0; i < oldStrings.size(); ++i)
+    {
+      HighwayRailway hr = oldStrings.elementAt(i);
+      hr.node.put("highway", hr.highway);
+      hr.node.put("railway", hr.railway);
+    }
+  }
+  
+  public void fillModifiedData
+    (Collection< OsmPrimitive > modified, Collection< OsmPrimitive > deleted,
+     Collection< OsmPrimitive > added)
+  {
+  }
+  
+  public MutableTreeNode description()
+  {
+    return new DefaultMutableTreeNode("public_transport.Settings.ChangeStoptype");
+  }
+  
+};
Index: /applications/editors/josm/plugins/public_transport/src/public_transport/StopImporterAction.java
===================================================================
--- /applications/editors/josm/plugins/public_transport/src/public_transport/StopImporterAction.java	(revision 20771)
+++ /applications/editors/josm/plugins/public_transport/src/public_transport/StopImporterAction.java	(revision 20772)
@@ -60,474 +60,5 @@
 
 public class StopImporterAction extends JosmAction
-{  
-  private class TrackReference
-    implements Comparable< TrackReference >, TableModelListener
-  {
-    public GpxTrack track;
-    public StoplistTableModel stoplistTM;
-    public String stopwatchStart;
-    public String gpsStartTime;
-    public String gpsSyncTime;
-    public double timeWindow;
-    public double threshold;
-    
-    public TrackReference(GpxTrack track)
-    {
-      this.track = track;
-      this.stoplistTM = new StoplistTableModel(this);
-      this.stopwatchStart = "00:00:00";
-      this.gpsStartTime = null;
-      this.gpsSyncTime = null;
-      if (track != null)
-      {
-	Iterator< GpxTrackSegment > siter = track.getSegments().iterator();
-	while ((siter.hasNext()) && (this.gpsSyncTime == null))
-	{
-	  Iterator< WayPoint > witer = siter.next().getWayPoints().iterator();
-	  if (witer.hasNext())
-	  {
-	    this.gpsStartTime = witer.next().getString("time");
-	    if (this.gpsStartTime != null)
-	      this.gpsSyncTime = this.gpsStartTime.substring(11, 19);
-	  }
-	}
-	if (this.gpsSyncTime == null)
-	{
-	  JOptionPane.showMessageDialog
-	      (null, "The GPX file doesn't contain valid trackpoints. "
-	      + "Please use a GPX file that has trackpoints.", "GPX File Trouble",
-	       JOptionPane.ERROR_MESSAGE);
-	  
-	  this.gpsStartTime = "1970-01-01T00:00:00Z";
-	  this.gpsSyncTime = this.stopwatchStart;
-	}
-      }
-      else
-	this.gpsSyncTime = this.stopwatchStart;
-      this.timeWindow = 20;
-      this.threshold = 20;
-    }
-    
-    public int compareTo(TrackReference tr)
-    {
-      String name = (String)track.getAttributes().get("name");
-      String tr_name = (String)tr.track.getAttributes().get("name");
-      if (name != null)
-      {
-	if (tr_name == null)
-	  return -1;
-	return name.compareTo(tr_name);
-      }
-      return 1;
-    }
-    
-    public String toString()
-    {
-      String buf = (String)track.getAttributes().get("name");
-      if (buf == null)
-	return "unnamed";
-      return buf;
-    }
-    
-    public void tableChanged(TableModelEvent e)
-    {
-      if (e.getType() == TableModelEvent.UPDATE)
-      {
-	double time = StopImporterDialog.parseTime
-	    ((String)stoplistTM.getValueAt(e.getFirstRow(), 0));
-	if (time < 0)
-	{
-	  JOptionPane.showMessageDialog
-	  (null, "Can't parse a time from this string.", "Invalid value",
-	   JOptionPane.ERROR_MESSAGE);
-	   return;
-	}
-
-	LatLon latLon = computeCoor(time);
-	
-	if (stoplistTM.nodes.elementAt(e.getFirstRow()) == null)
-	{
-	  Node node = createNode
-	      (latLon, (String)stoplistTM.getValueAt(e.getFirstRow(), 1));
-	  stoplistTM.nodes.set(e.getFirstRow(), node);
-	}
-	else
-	{
-	  Node node = new Node(stoplistTM.nodes.elementAt(e.getFirstRow()));
-	  node.setCoor(latLon);
-	  node.put("name", (String)stoplistTM.getValueAt(e.getFirstRow(), 1));
-	  Command cmd = new ChangeCommand(stoplistTM.nodes.elementAt(e.getFirstRow()), node);
-	  if (cmd != null) {
-	    Main.main.undoRedo.add(cmd);
-	  }
-	}
-      }
-    }
-    
-    public LatLon computeCoor(double time)
-    {
-      double gpsSyncTime = StopImporterDialog.parseTime(this.gpsSyncTime);
-      double dGpsStartTime = StopImporterDialog.parseTime(gpsStartTime);
-      if (gpsSyncTime < dGpsStartTime - 12*60*60)
-	gpsSyncTime += 24*60*60;
-      double timeDelta = gpsSyncTime - StopImporterDialog.parseTime(stopwatchStart);
-      time += timeDelta;
-	
-      WayPoint wayPoint = null;
-      WayPoint lastWayPoint = null;
-      double wayPointTime = 0;
-      double lastWayPointTime = 0;
-      Iterator< GpxTrackSegment > siter = track.getSegments().iterator();
-      while (siter.hasNext())
-      {
-	Iterator< WayPoint > witer = siter.next().getWayPoints().iterator();
-	while (witer.hasNext())
-	{
-	  wayPoint = witer.next();
-	  String startTime = wayPoint.getString("time");
-	  wayPointTime = StopImporterDialog.parseTime(startTime.substring(11, 19));
-	  if (startTime.substring(11, 19).compareTo(gpsStartTime.substring(11, 19)) == -1)
-	    wayPointTime += 24*60*60;
-	  if (wayPointTime >= time)
-	    break;
-	  lastWayPoint = wayPoint;
-	  lastWayPointTime = wayPointTime;
-	}
-	if (wayPointTime >= time)
-	  break;
-      }
-	
-      double lat = 0;
-      if ((wayPointTime == lastWayPointTime) || (lastWayPoint == null))
-	lat = wayPoint.getCoor().lat();
-      else
-	lat = wayPoint.getCoor().lat()
-	    *(time - lastWayPointTime)/(wayPointTime - lastWayPointTime)
-	    + lastWayPoint.getCoor().lat()
-	    *(wayPointTime - time)/(wayPointTime - lastWayPointTime);
-      double lon = 0;
-      if ((wayPointTime == lastWayPointTime) || (lastWayPoint == null))
-	lon = wayPoint.getCoor().lon();
-      else
-	lon = wayPoint.getCoor().lon()
-	    *(time - lastWayPointTime)/(wayPointTime - lastWayPointTime)
-	    + lastWayPoint.getCoor().lon()
-	    *(wayPointTime - time)/(wayPointTime - lastWayPointTime);
-      
-      return new LatLon(lat, lon);
-    }
-    
-    public void relocateNodes()
-    {
-      for (int i = 0; i < stoplistTM.nodes.size(); ++i)
-      {
-	Node node = stoplistTM.nodes.elementAt(i);
-	if (node == null)
-	  continue;
-	
-	double time = StopImporterDialog.parseTime
-	      ((String)stoplistTM.getValueAt(i, 0));
-	LatLon latLon = computeCoor(time);
-	
-	Node newNode = new Node(node);
-	newNode.setCoor(latLon);
-	Command cmd = new ChangeCommand(node, newNode);
-	if (cmd != null)
-	{
-	  Main.main.undoRedo.add(cmd);
-	}
-      }
-    }
-    
-    public void suggestStops()
-    {
-      Vector< WayPoint > wayPoints = new Vector< WayPoint >();
-      Iterator< GpxTrackSegment > siter = track.getSegments().iterator();
-      while (siter.hasNext())
-      {
-	Iterator< WayPoint > witer = siter.next().getWayPoints().iterator();
-	while (witer.hasNext())
-	  wayPoints.add(witer.next());
-      }
-      Vector< Double > wayPointsDist = new Vector< Double >(wayPoints.size());
-      
-      int i = 0;
-      double time = -48*60*60;
-      double dGpsStartTime = StopImporterDialog.parseTime(gpsStartTime);
-      while ((i < wayPoints.size()) && (time < dGpsStartTime + timeWindow/2))
-      {
-	if (wayPoints.elementAt(i).getString("time") != null)
-	  time = StopImporterDialog.parseTime(wayPoints.elementAt(i)
-	      .getString("time").substring(11,19));
-	if (time < dGpsStartTime)
-	  time += 24*60*60;
-	wayPointsDist.add(Double.valueOf(Double.POSITIVE_INFINITY));
-	++i;
-      }
-      while (i < wayPoints.size())
-      {
-	int j = i;
-	double time2 = time;
-	while ((j > 0) && (time - timeWindow/2 < time2))
-	{
-	  --j;
-	  if (wayPoints.elementAt(j).getString("time") != null)
-	    time2 = StopImporterDialog.parseTime(wayPoints.elementAt(j)
-		.getString("time").substring(11,19));
-	  if (time2 < dGpsStartTime)
-	    time2 += 24*60*60;
-	}
-	int k = i + 1;
-	time2 = time;
-	while ((k < wayPoints.size()) && (time + timeWindow/2 > time2))
-	{
-	  if (wayPoints.elementAt(k).getString("time") != null)
-	    time2 = StopImporterDialog.parseTime(wayPoints.elementAt(k)
-		.getString("time").substring(11,19));
-	  if (time2 < dGpsStartTime)
-	    time2 += 24*60*60;
-	  ++k;
-	}
-	
-	if (j < k)
-	{
-	  double dist = 0;
-	  LatLon latLonI = wayPoints.elementAt(i).getCoor();
-	  for (int l = j; l < k; ++l)
-	  {
-	    double distL = latLonI.greatCircleDistance(wayPoints.elementAt(l).getCoor());
-	    if (distL > dist)
-	      dist = distL;
-	  }
-	  wayPointsDist.add(Double.valueOf(dist));
-	}
-	else
-	  wayPointsDist.add(Double.valueOf(Double.POSITIVE_INFINITY));
-	
-	if (wayPoints.elementAt(i).getString("time") != null)
-	  time = StopImporterDialog.parseTime(wayPoints.elementAt(i)
-	      .getString("time").substring(11,19));
-	if (time < dGpsStartTime)
-	  time += 24*60*60;
-	++i;
-      }
-      
-      Vector< Node > toDelete = new Vector< Node >();
-      for (i = 0; i < stoplistTM.getRowCount(); ++i)
-      {
-	if ((Node)stoplistTM.nodes.elementAt(i) != null)
-	  toDelete.add((Node)stoplistTM.nodes.elementAt(i));
-      }
-      if (!toDelete.isEmpty())
-      {
-	Command cmd = DeleteCommand.delete
-	    (Main.main.getEditLayer(), toDelete);
-	if (cmd == null)
-	  return;
-	Main.main.undoRedo.add(cmd);
-      }
-      stoplistTM.clear();
-      
-      LatLon lastStopCoor = null;
-      for (i = 1; i < wayPoints.size()-1; ++i)
-      {
-	if (wayPointsDist.elementAt(i).doubleValue() >= threshold)
-	  continue;
-	if ((wayPointsDist.elementAt(i).compareTo(wayPointsDist.elementAt(i-1)) != -1)
-		    || (wayPointsDist.elementAt(i).compareTo(wayPointsDist.elementAt(i+1)) != -1))
-	  continue;
-	
-	LatLon latLon = wayPoints.elementAt(i).getCoor();
-	if ((lastStopCoor != null) &&  (lastStopCoor.greatCircleDistance(latLon) < threshold))
-	  continue;
-	
-	if (wayPoints.elementAt(i).getString("time") != null)
-	{
-	  time = StopImporterDialog.parseTime(wayPoints.elementAt(i)
-	      .getString("time").substring(11,19));
-	  double gpsSyncTime = StopImporterDialog.parseTime(this.gpsSyncTime);
-	  if (gpsSyncTime < dGpsStartTime - 12*60*60)
-	    gpsSyncTime += 24*60*60;
-	  double timeDelta = gpsSyncTime - StopImporterDialog.parseTime(stopwatchStart);
-	  time -= timeDelta;
-	  stoplistTM.insertRow(-1, timeOf(time));
-	  Node node = createNode(latLon, "");
-	  stoplistTM.nodes.set(stoplistTM.getRowCount()-1, node);
-	}
-	
-	lastStopCoor = latLon;
-      }
-    }
-  };
-  
-  private class NodeSortEntry implements Comparable< NodeSortEntry >
-  {
-    public Node node = null;
-    public String time = null;
-    public String name = null;
-    public double startTime = 0;
-    
-    public NodeSortEntry(Node node, String time, String name, double startTime)
-    {
-      this.node = node;
-      this.time = time;
-      this.name = name;
-    }
-    
-    public int compareTo(NodeSortEntry nse)
-    {
-      double time = StopImporterDialog.parseTime(this.time);
-      if (time - startTime > 12*60*60)
-	time -= 24*60*60;
-      
-      double nseTime = StopImporterDialog.parseTime(nse.time);
-      if (nseTime - startTime > 12*60*60)
-	nseTime -= 24*60*60;
-      
-      if (time < nseTime)
-	return -1;
-      else if (time > nseTime)
-	return 1;
-      else
-	return 0;
-    }
-  };
-  
-  private class StoplistTableModel extends DefaultTableModel
-  {
-    public Vector< Node > nodes = new Vector< Node >();
-    
-    public StoplistTableModel(TrackReference tr)
-    {
-      addColumn("Time");
-      addColumn("Name");
-      addTableModelListener(tr);
-    }
-    
-    public boolean isCellEditable(int row, int column) {
-      return true;
-    }
-    
-    public void addRow(Object[] obj) {
-      throw new UnsupportedOperationException();
-    }
-    
-    public void insertRow(int insPos, Object[] obj) {
-      throw new UnsupportedOperationException();
-    }
-    
-    public void addRow(String time) {
-      insertRow(-1, time);
-    }
-    
-    public void insertRow(int insPos, String time)
-    {
-      insertRow(insPos, null, time, "");
-    }
-    
-    public void insertRow(int insPos, Node node, String time, String name)
-    {
-      String[] buf = { "", "" };
-      buf[0] = time;
-      buf[1] = name;
-      if (insPos == -1)
-      {
-	nodes.addElement(node);
-	super.addRow(buf);
-      }
-      else
-      {
-	nodes.insertElementAt(node, insPos);
-	super.insertRow(insPos, buf);
-      }
-    }
-    
-    public void clear()
-    {
-      nodes.clear();
-      super.setRowCount(0);
-    }
-  };
-  
-  public class WaypointTableModel extends DefaultTableModel
-      implements TableModelListener
-  {
-    public Vector< Node > nodes = new Vector< Node >();
-    public Vector< LatLon > coors = new Vector< LatLon >();
-    
-    public WaypointTableModel()
-    {
-      addColumn("Time");
-      addColumn("Stopname");
-      addTableModelListener(this);
-    }
-    
-    public boolean isCellEditable(int row, int column)
-    {
-      if (column == 1)
-	return true;
-      return false;
-    }
-    
-    public void addRow(Object[] obj)
-    {
-      throw new UnsupportedOperationException();
-    }
-    
-    public void insertRow(int insPos, Object[] obj)
-    {
-      throw new UnsupportedOperationException();
-    }
-    
-    public void addRow(WayPoint wp)
-    {
-      insertRow(-1, wp);
-    }
-    
-    public void insertRow(int insPos, WayPoint wp)
-    {
-      String[] buf = { "", "" };
-      buf[0] = wp.getString("time");
-      if (buf[0] == null)
-	buf[0] = "";
-      buf[1] = wp.getString("name");
-      if (buf[1] == null)
-	buf[1] = "";
-
-      Node node = createNode(wp.getCoor(), buf[1]);
-      
-      if (insPos == -1)
-      {
-	nodes.addElement(node);
-	coors.addElement(wp.getCoor());
-	super.addRow(buf);
-      }
-      else
-      {
-	nodes.insertElementAt(node, insPos);
-	coors.insertElementAt(wp.getCoor(), insPos);
-	super.insertRow(insPos, buf);
-      }
-    }
-    
-    public void clear()
-    {
-      nodes.clear();
-      super.setRowCount(0);
-    }
-  
-    public void tableChanged(TableModelEvent e)
-    {
-      if (e.getType() == TableModelEvent.UPDATE)
-      {
-	if (nodes.elementAt(e.getFirstRow()) != null)
-	{
-	  Node node = nodes.elementAt(e.getFirstRow());
-	  node.put("name", (String)getValueAt(e.getFirstRow(), 1));
-	}
-      }
-    }
-  };
-  
+{ 
   private static StopImporterDialog dialog = null;
   private static DefaultListModel tracksListModel = null;
@@ -559,4 +90,9 @@
   }
   
+  public TrackReference getCurrentTrack()
+  {
+    return currentTrack;
+  }
+
   public void actionPerformed(ActionEvent event)
   {
@@ -590,33 +126,21 @@
       refreshData();
     }
-    else if ("stopImporter.settingsGPSTimeStart"
-      .equals(event.getActionCommand()))
-    {
-      if (dialog.gpsTimeStartValid())
-      {
-	if (currentTrack != null)
-	{
-	  currentTrack.gpsSyncTime = dialog.getGpsTimeStart();
-	  currentTrack.relocateNodes();
-	}
-      }
-    }
-    else if ("stopImporter.settingsStopwatchStart"
-      .equals(event.getActionCommand()))
-    {
-      if (dialog.stopwatchStartValid())
-      {
-	if (currentTrack != null)
-	{
-	  currentTrack.stopwatchStart = dialog.getStopwatchStart();
-	  currentTrack.relocateNodes();
-	}
-      }
-      else
-      {
-      }
-    }
-    else if ("stopImporter.settingsTimeWindow"
-      .equals(event.getActionCommand()))
+    else if ("stopImporter.settingsGPSTimeStart".equals(event.getActionCommand()))
+    {
+      if ((dialog.gpsTimeStartValid()) && (currentTrack != null))
+      {
+	currentTrack.gpsSyncTime = dialog.getGpsTimeStart();
+	currentTrack.relocateNodes();
+      }
+    }
+    else if ("stopImporter.settingsStopwatchStart".equals(event.getActionCommand()))
+    {
+      if ((dialog.stopwatchStartValid()) && (currentTrack != null))
+      {
+	currentTrack.stopwatchStart = dialog.getStopwatchStart();
+	currentTrack.relocateNodes();
+      }
+    }
+    else if ("stopImporter.settingsTimeWindow".equals(event.getActionCommand()))
     {
       if (currentTrack != null)
@@ -633,209 +157,26 @@
     }
     else if ("stopImporter.stoplistFind".equals(event.getActionCommand()))
-    {
-      if (Main.main.getCurrentDataSet() == null)
-	return;
-      
+      findNodesInTable(dialog.getStoplistTable(), currentTrack.stoplistTM.nodes);
+    else if ("stopImporter.stoplistShow".equals(event.getActionCommand()))
+      showNodesFromTable(dialog.getStoplistTable(), currentTrack.stoplistTM.nodes);
+    else if ("stopImporter.stoplistMark".equals(event.getActionCommand()))
+      markNodesFromTable(dialog.getStoplistTable(), currentTrack.stoplistTM.nodes);
+    else if ("stopImporter.stoplistDetach".equals(event.getActionCommand()))
+    {
+      Main.main.undoRedo.add(new TrackStoplistDetachCommand(this));
       dialog.getStoplistTable().clearSelection();
-      
-      for (int i = 0; i < currentTrack.stoplistTM.getRowCount(); ++i)
-      {
-	if ((currentTrack.stoplistTM.nodes.elementAt(i) != null) &&
-		    (Main.main.getCurrentDataSet().isSelected(currentTrack.stoplistTM.nodes.elementAt(i))))
-	  dialog.getStoplistTable().addRowSelectionInterval(i, i);
-      }
-    }
-    else if ("stopImporter.stoplistShow".equals(event.getActionCommand()))
-    {
-      BoundingXYVisitor box = new BoundingXYVisitor();
-      if (dialog.getStoplistTable().getSelectedRowCount() > 0)
-      {
-	for (int i = 0; i < currentTrack.stoplistTM.getRowCount(); ++i)
-	{
-	  if ((dialog.getStoplistTable().isRowSelected(i)) &&
-		      (currentTrack.stoplistTM.nodes.elementAt(i) != null))
-	  {
-	    currentTrack.stoplistTM.nodes.elementAt(i).visit(box);
-	  }
-	}
-      }
-      else
-      {
-	for (int i = 0; i < currentTrack.stoplistTM.getRowCount(); ++i)
-	{
-	  if (currentTrack.stoplistTM.nodes.elementAt(i) != null)
-	    currentTrack.stoplistTM.nodes.elementAt(i).visit(box);
-	}
-      }
-      if (box.getBounds() == null)
-	return;
-      box.enlargeBoundingBox();
-      Main.map.mapView.recalculateCenterScale(box);
-    }
-    else if ("stopImporter.stoplistMark".equals(event.getActionCommand()))
-    {
-      OsmPrimitive[] osmp = { null };
-      Main.main.getCurrentDataSet().setSelected(osmp);
-      if (dialog.getStoplistTable().getSelectedRowCount() > 0)
-      {
-	for (int i = 0; i < currentTrack.stoplistTM.getRowCount(); ++i)
-	{
-	  if ((dialog.getStoplistTable().isRowSelected(i)) &&
-	    (currentTrack.stoplistTM.nodes.elementAt(i) != null))
-	  {
-	    Main.main.getCurrentDataSet().addSelected(currentTrack.stoplistTM.nodes.elementAt(i));
-	  }
-	}
-      }
-      else
-      {
-	for (int i = 0; i < currentTrack.stoplistTM.getRowCount(); ++i)
-	{
-	  if (currentTrack.stoplistTM.nodes.elementAt(i) != null)
-	    Main.main.getCurrentDataSet().addSelected(currentTrack.stoplistTM.nodes.elementAt(i));
-	}
-      }
-    }
-    else if ("stopImporter.stoplistDetach".equals(event.getActionCommand()))
-    {
-      if (dialog.getStoplistTable().getSelectedRowCount() > 0)
-      {
-	for (int i = 0; i < currentTrack.stoplistTM.getRowCount(); ++i)
-	{
-	  if ((dialog.getStoplistTable().isRowSelected(i)) &&
-		      (currentTrack.stoplistTM.nodes.elementAt(i) != null))
-	  {
-	    currentTrack.stoplistTM.nodes.set(i, null);
-	  }
-	}
-      }
-      else
-      {
-	for (int i = 0; i < currentTrack.stoplistTM.getRowCount(); ++i)
-	{
-	  if (currentTrack.stoplistTM.nodes.elementAt(i) != null)
-	    currentTrack.stoplistTM.nodes.set(i, null);
-	}
-      }
-      dialog.getStoplistTable().clearSelection();
     }
     else if ("stopImporter.stoplistAdd".equals(event.getActionCommand()))
-    {
-      int insPos = dialog.getStoplistTable().getSelectedRow();
-      if (currentTrack != null)
-	currentTrack.stoplistTM.insertRow(insPos, "00:00:00");
-    }
+      Main.main.undoRedo.add(new TrackStoplistAddCommand(this));
     else if ("stopImporter.stoplistDelete".equals(event.getActionCommand()))
-    {
-      Vector< Node > toDelete = new Vector< Node >();
-      if (currentTrack == null)
-	return;
-      for (int i = currentTrack.stoplistTM.getRowCount()-1; i >=0; --i)
-      {
-	if (dialog.getStoplistTable().isRowSelected(i))
-	{
-	  if ((Node)currentTrack.stoplistTM.nodes.elementAt(i) != null)
-	    toDelete.add((Node)currentTrack.stoplistTM.nodes.elementAt(i));
-	  currentTrack.stoplistTM.nodes.removeElementAt(i);
-	  currentTrack.stoplistTM.removeRow(i);
-	}
-      }
-      Command cmd = DeleteCommand.delete
-          (Main.main.getEditLayer(), toDelete);
-      if (cmd != null) {
-	// cmd can be null if the user cancels dialogs DialogCommand displays
-	Main.main.undoRedo.add(cmd);
-      }
-    }
+      Main.main.undoRedo.add(new TrackStoplistDeleteCommand(this));
     else if ("stopImporter.stoplistSort".equals(event.getActionCommand()))
-    {
-      int insPos = dialog.getStoplistTable().getSelectedRow();
-      Vector< NodeSortEntry > nodesToSort = new Vector< NodeSortEntry >();
-      if (currentTrack == null)
-	return;
-      if (dialog.getStoplistTable().getSelectedRowCount() > 0)
-      {
-	for (int i = currentTrack.stoplistTM.getRowCount()-1; i >=0; --i)
-	{
-	  if (dialog.getStoplistTable().isRowSelected(i))
-	  {
-	    nodesToSort.add(new NodeSortEntry
-		(currentTrack.stoplistTM.nodes.elementAt(i),
-		 (String)currentTrack.stoplistTM.getValueAt(i, 0),
-		  (String)currentTrack.stoplistTM.getValueAt(i, 1),
-		   StopImporterDialog.parseTime(currentTrack.stopwatchStart)));
-	    currentTrack.stoplistTM.nodes.removeElementAt(i);
-	    currentTrack.stoplistTM.removeRow(i);
-	  }
-	}
-      }
-      else
-      {
-	for (int i = 0; i < currentTrack.stoplistTM.getRowCount(); ++i)
-	{
-	  nodesToSort.add(new NodeSortEntry
-	      (currentTrack.stoplistTM.nodes.elementAt(i),
-	       (String)currentTrack.stoplistTM.getValueAt(i, 0),
-		(String)currentTrack.stoplistTM.getValueAt(i, 1),
-		 StopImporterDialog.parseTime(currentTrack.stopwatchStart)));
-	}
-	currentTrack.stoplistTM.clear();
-      }
-      
-      Collections.sort(nodesToSort);
-      
-      Iterator< NodeSortEntry > iter = nodesToSort.iterator();
-      while (iter.hasNext())
-      {
-	NodeSortEntry nse = iter.next();
-	currentTrack.stoplistTM.insertRow
-	    (insPos, nse.node, nse.time, nse.name);
-	if (insPos >= 0)
-	  ++insPos;
-      }
-    }
+      Main.main.undoRedo.add(new TrackStoplistSortCommand(this));
     else if ("stopImporter.waypointsFind".equals(event.getActionCommand()))
-    {
-      if (Main.main.getCurrentDataSet() == null)
-	return;
-      
-      dialog.getWaypointsTable().clearSelection();
-      
-      for (int i = 0; i < waypointTM.getRowCount(); ++i)
-      {
-	if ((waypointTM.nodes.elementAt(i) != null) &&
-		    (Main.main.getCurrentDataSet().isSelected(waypointTM.nodes.elementAt(i))))
-	  dialog.getWaypointsTable().addRowSelectionInterval(i, i);
-      }
-    }
+      findNodesInTable(dialog.getWaypointsTable(), waypointTM.nodes);
     else if ("stopImporter.waypointsShow".equals(event.getActionCommand()))
-    {
-      BoundingXYVisitor box = new BoundingXYVisitor();
-      Vector< Integer > consideredLines = getConsideredLines
-        (dialog.getWaypointsTable());
-      for (int i = 0; i < consideredLines.size(); ++i)
-      {
-	int j = consideredLines.elementAt(i);
-	if (waypointTM.nodes.elementAt(j) != null)
-	  waypointTM.nodes.elementAt(j).visit(box);
-      }
-      if (box.getBounds() == null)
-	return;
-      box.enlargeBoundingBox();
-      Main.map.mapView.recalculateCenterScale(box);
-    }
+      showNodesFromTable(dialog.getWaypointsTable(), waypointTM.nodes);
     else if ("stopImporter.waypointsMark".equals(event.getActionCommand()))
-    {
-      OsmPrimitive[] osmp = { null };
-      Main.main.getCurrentDataSet().setSelected(osmp);
-      Vector< Integer > consideredLines = getConsideredLines
-        (dialog.getWaypointsTable());
-      for (int i = 0; i < consideredLines.size(); ++i)
-      {
-	int j = consideredLines.elementAt(i);
-	if (waypointTM.nodes.elementAt(j) != null)
-	  Main.main.getCurrentDataSet().addSelected(waypointTM.nodes.elementAt(j));
-      }
-    }
+      markNodesFromTable(dialog.getWaypointsTable(), waypointTM.nodes);
     else if ("stopImporter.waypointsDetach".equals(event.getActionCommand()))
     {
@@ -844,56 +185,9 @@
     }
     else if ("stopImporter.waypointsAdd".equals(event.getActionCommand()))
-    {
       Main.main.undoRedo.add(new WaypointsEnableCommand(this));
-    }
     else if ("stopImporter.waypointsDelete".equals(event.getActionCommand()))
-    {
       Main.main.undoRedo.add(new WaypointsDisableCommand(this));
-    }
     else if ("stopImporter.settingsStoptype".equals(event.getActionCommand()))
-    {
-      for (int i = 0; i < waypointTM.getRowCount(); ++i)
-      {
-	if ((Node)waypointTM.nodes.elementAt(i) != null)
-        {
-	  Node node = (Node)waypointTM.nodes.elementAt(i);
-	  node.remove("highway");
-	  node.remove("railway");
-          if ("bus".equals(dialog.getStoptype()))
-            node.put("highway", "bus_stop");
-	  else if ("tram".equals(dialog.getStoptype()))
-            node.put("railway", "tram_stop");
-	  else if ("light_rail".equals(dialog.getStoptype()))
-            node.put("railway", "station");
-	  else if ("subway".equals(dialog.getStoptype()))
-            node.put("railway", "station");
-	  else if ("rail".equals(dialog.getStoptype()))
-            node.put("railway", "station");
-        }
-      }
-      for (int j = 0; j < tracksListModel.size(); ++j)
-      {
-	TrackReference track = (TrackReference)tracksListModel.elementAt(j);
-	for (int i = 0; i < track.stoplistTM.getRowCount(); ++i)
-	{
-	  if ((Node)track.stoplistTM.nodes.elementAt(i) != null)
-	  {
-	    Node node = (Node)track.stoplistTM.nodes.elementAt(i);
-	    node.remove("highway");
-	    node.remove("railway");
-	    if ("bus".equals(dialog.getStoptype()))
-	      node.put("highway", "bus_stop");
-	    else if ("tram".equals(dialog.getStoptype()))
-	      node.put("railway", "tram_stop");
-	    else if ("light_rail".equals(dialog.getStoptype()))
-	      node.put("railway", "station");
-	    else if ("subway".equals(dialog.getStoptype()))
-	      node.put("railway", "station");
-	    else if ("rail".equals(dialog.getStoptype()))
-	      node.put("railway", "station");
-	  }
-	}
-      }
-    }
+      Main.main.undoRedo.add(new SettingsStoptypeCommand(this));
   }
 
@@ -953,5 +247,5 @@
       {
 	GpxTrack track = trackIter.next();
-	trackRefs.add(new TrackReference(track));
+	trackRefs.add(new TrackReference(track, this));
       }
       
@@ -962,5 +256,5 @@
 	tracksListModel.addElement(iter.next());
       
-      waypointTM = new WaypointTableModel();
+      waypointTM = new WaypointTableModel(this);
       Iterator< WayPoint > waypointIter = data.waypoints.iterator();
       while (waypointIter.hasNext())
@@ -1004,5 +298,5 @@
   }
 
-  private Node createNode(LatLon latLon, String name)
+  public Node createNode(LatLon latLon, String name)
   {
     return createNode(latLon, dialog.getStoptype(), name);
@@ -1037,5 +331,22 @@
   }
 
-  /* returns a collection of all slected lines or
+  /* sets the tags of the node according to the type */
+  public static void setTagsWrtType(Node node, String type)
+  {
+    node.remove("highway");
+    node.remove("railway");
+    if ("bus".equals(type))
+      node.put("highway", "bus_stop");
+    else if ("tram".equals(type))
+      node.put("railway", "tram_stop");
+    else if ("light_rail".equals(type))
+      node.put("railway", "station");
+    else if ("subway".equals(type))
+      node.put("railway", "station");
+    else if ("rail".equals(type))
+      node.put("railway", "station");
+  }
+  
+  /* returns a collection of all selected lines or
      a collection of all lines otherwise */
   public static Vector< Integer > getConsideredLines(JTable table)
@@ -1056,5 +367,54 @@
   }
 
-  private static String timeOf(double t)
+  /* marks the table items whose nodes are marked on the map */
+  public static void findNodesInTable(JTable table, Vector< Node > nodes)
+  {
+    if (Main.main.getCurrentDataSet() == null)
+      return;
+      
+    table.clearSelection();
+      
+    for (int i = 0; i < table.getRowCount(); ++i)
+    {
+      if ((nodes.elementAt(i) != null) &&
+	   (Main.main.getCurrentDataSet().isSelected(nodes.elementAt(i))))
+	table.addRowSelectionInterval(i, i);
+    }
+  }
+  
+  /* shows the nodes that correspond to the marked lines in the table.
+     If no lines are marked in the table, show all nodes from the vector */
+  public static void showNodesFromTable(JTable table, Vector< Node > nodes)
+  {
+    BoundingXYVisitor box = new BoundingXYVisitor();
+    Vector< Integer > consideredLines = getConsideredLines(table);
+    for (int i = 0; i < consideredLines.size(); ++i)
+    {
+      int j = consideredLines.elementAt(i);
+      if (nodes.elementAt(j) != null)
+	nodes.elementAt(j).visit(box);
+    }
+    if (box.getBounds() == null)
+      return;
+    box.enlargeBoundingBox();
+    Main.map.mapView.recalculateCenterScale(box);
+  }
+  
+  /* marks the nodes that correspond to the marked lines in the table.
+  If no lines are marked in the table, mark all nodes from the vector */
+  public static void markNodesFromTable(JTable table, Vector< Node > nodes)
+  {
+    OsmPrimitive[] osmp = { null };
+    Main.main.getCurrentDataSet().setSelected(osmp);
+    Vector< Integer > consideredLines = getConsideredLines(table);
+    for (int i = 0; i < consideredLines.size(); ++i)
+    {
+      int j = consideredLines.elementAt(i);
+      if (nodes.elementAt(j) != null)
+	Main.main.getCurrentDataSet().addSelected(nodes.elementAt(j));
+    }
+  }
+  
+  public static String timeOf(double t)
   {
     t -= Math.floor(t/24/60/60)*24*60*60;
Index: /applications/editors/josm/plugins/public_transport/src/public_transport/TrackReference.java
===================================================================
--- /applications/editors/josm/plugins/public_transport/src/public_transport/TrackReference.java	(revision 20772)
+++ /applications/editors/josm/plugins/public_transport/src/public_transport/TrackReference.java	(revision 20772)
@@ -0,0 +1,322 @@
+package public_transport;
+
+import static org.openstreetmap.josm.tools.I18n.marktr;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.Iterator;
+import java.util.Vector;
+import javax.swing.JOptionPane;
+import javax.swing.event.TableModelEvent;
+import javax.swing.event.TableModelListener;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.command.ChangeCommand;
+import org.openstreetmap.josm.command.DeleteCommand;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.gpx.GpxTrack;
+import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
+import org.openstreetmap.josm.data.gpx.WayPoint;
+import org.openstreetmap.josm.data.osm.Node;
+
+public class TrackReference
+    implements Comparable< TrackReference >, TableModelListener
+{
+  public GpxTrack track;
+  public TrackStoplistTableModel stoplistTM;
+  public String stopwatchStart;
+  public String gpsStartTime;
+  public String gpsSyncTime;
+  public double timeWindow;
+  public double threshold;
+  private StopImporterAction controller = null;
+    
+  public TrackReference(GpxTrack track, StopImporterAction controller)
+  {
+    this.track = track;
+    this.stoplistTM = new TrackStoplistTableModel(this);
+    this.stopwatchStart = "00:00:00";
+    this.gpsStartTime = null;
+    this.gpsSyncTime = null;
+    this.controller = controller;
+    if (track != null)
+    {
+      Iterator< GpxTrackSegment > siter = track.getSegments().iterator();
+      while ((siter.hasNext()) && (this.gpsSyncTime == null))
+      {
+	Iterator< WayPoint > witer = siter.next().getWayPoints().iterator();
+	if (witer.hasNext())
+	{
+	  this.gpsStartTime = witer.next().getString("time");
+	  if (this.gpsStartTime != null)
+	    this.gpsSyncTime = this.gpsStartTime.substring(11, 19);
+	}
+      }
+      if (this.gpsSyncTime == null)
+      {
+	JOptionPane.showMessageDialog
+	    (null, "The GPX file doesn't contain valid trackpoints. "
+	    + "Please use a GPX file that has trackpoints.", "GPX File Trouble",
+     JOptionPane.ERROR_MESSAGE);
+	  
+	this.gpsStartTime = "1970-01-01T00:00:00Z";
+	this.gpsSyncTime = this.stopwatchStart;
+      }
+    }
+    else
+      this.gpsSyncTime = this.stopwatchStart;
+    this.timeWindow = 20;
+    this.threshold = 20;
+  }
+    
+  public int compareTo(TrackReference tr)
+  {
+    String name = (String)track.getAttributes().get("name");
+    String tr_name = (String)tr.track.getAttributes().get("name");
+    if (name != null)
+    {
+      if (tr_name == null)
+	return -1;
+      return name.compareTo(tr_name);
+    }
+    return 1;
+  }
+    
+  public String toString()
+  {
+    String buf = (String)track.getAttributes().get("name");
+    if (buf == null)
+      return "unnamed";
+    return buf;
+  }
+    
+  public void tableChanged(TableModelEvent e)
+  {
+    if ((e.getType() == TableModelEvent.UPDATE) && (e.getFirstRow() >= 0))
+    {
+      double time = StopImporterDialog.parseTime
+	    ((String)stoplistTM.getValueAt(e.getFirstRow(), 0));
+      if (time < 0)
+      {
+	JOptionPane.showMessageDialog
+	    (null, "Can't parse a time from this string.", "Invalid value",
+	     JOptionPane.ERROR_MESSAGE);
+	return;
+      }
+
+      LatLon latLon = computeCoor(time);
+	
+      if (stoplistTM.nodes.elementAt(e.getFirstRow()) == null)
+      {
+	Node node = controller.createNode
+	    (latLon, (String)stoplistTM.getValueAt(e.getFirstRow(), 1));
+	stoplistTM.nodes.set(e.getFirstRow(), node);
+      }
+      else
+      {
+	Node node = new Node(stoplistTM.nodes.elementAt(e.getFirstRow()));
+	node.setCoor(latLon);
+	node.put("name", (String)stoplistTM.getValueAt(e.getFirstRow(), 1));
+	Command cmd = new ChangeCommand(stoplistTM.nodes.elementAt(e.getFirstRow()), node);
+	if (cmd != null) {
+	  Main.main.undoRedo.add(cmd);
+	}
+      }
+    }
+  }
+    
+  public LatLon computeCoor(double time)
+  {
+    double gpsSyncTime = StopImporterDialog.parseTime(this.gpsSyncTime);
+    double dGpsStartTime = StopImporterDialog.parseTime(gpsStartTime);
+    if (gpsSyncTime < dGpsStartTime - 12*60*60)
+      gpsSyncTime += 24*60*60;
+    double timeDelta = gpsSyncTime - StopImporterDialog.parseTime(stopwatchStart);
+    time += timeDelta;
+	
+    WayPoint wayPoint = null;
+    WayPoint lastWayPoint = null;
+    double wayPointTime = 0;
+    double lastWayPointTime = 0;
+    Iterator< GpxTrackSegment > siter = track.getSegments().iterator();
+    while (siter.hasNext())
+    {
+      Iterator< WayPoint > witer = siter.next().getWayPoints().iterator();
+      while (witer.hasNext())
+      {
+	wayPoint = witer.next();
+	String startTime = wayPoint.getString("time");
+	wayPointTime = StopImporterDialog.parseTime(startTime.substring(11, 19));
+	if (startTime.substring(11, 19).compareTo(gpsStartTime.substring(11, 19)) == -1)
+	  wayPointTime += 24*60*60;
+	if (wayPointTime >= time)
+	  break;
+	lastWayPoint = wayPoint;
+	lastWayPointTime = wayPointTime;
+      }
+      if (wayPointTime >= time)
+	break;
+    }
+	
+    double lat = 0;
+    if ((wayPointTime == lastWayPointTime) || (lastWayPoint == null))
+      lat = wayPoint.getCoor().lat();
+    else
+      lat = wayPoint.getCoor().lat()
+	  *(time - lastWayPointTime)/(wayPointTime - lastWayPointTime)
+	  + lastWayPoint.getCoor().lat()
+	  *(wayPointTime - time)/(wayPointTime - lastWayPointTime);
+    double lon = 0;
+    if ((wayPointTime == lastWayPointTime) || (lastWayPoint == null))
+      lon = wayPoint.getCoor().lon();
+    else
+      lon = wayPoint.getCoor().lon()
+	  *(time - lastWayPointTime)/(wayPointTime - lastWayPointTime)
+	  + lastWayPoint.getCoor().lon()
+	  *(wayPointTime - time)/(wayPointTime - lastWayPointTime);
+      
+    return new LatLon(lat, lon);
+  }
+    
+  public void relocateNodes()
+  {
+    for (int i = 0; i < stoplistTM.nodes.size(); ++i)
+    {
+      Node node = stoplistTM.nodes.elementAt(i);
+      if (node == null)
+	continue;
+	
+      double time = StopImporterDialog.parseTime
+	    ((String)stoplistTM.getValueAt(i, 0));
+      LatLon latLon = computeCoor(time);
+	
+      Node newNode = new Node(node);
+      newNode.setCoor(latLon);
+      Command cmd = new ChangeCommand(node, newNode);
+      if (cmd != null)
+      {
+	Main.main.undoRedo.add(cmd);
+      }
+    }
+  }
+    
+  public void suggestStops()
+  {
+    Vector< WayPoint > wayPoints = new Vector< WayPoint >();
+    Iterator< GpxTrackSegment > siter = track.getSegments().iterator();
+    while (siter.hasNext())
+    {
+      Iterator< WayPoint > witer = siter.next().getWayPoints().iterator();
+      while (witer.hasNext())
+	wayPoints.add(witer.next());
+    }
+    Vector< Double > wayPointsDist = new Vector< Double >(wayPoints.size());
+      
+    int i = 0;
+    double time = -48*60*60;
+    double dGpsStartTime = StopImporterDialog.parseTime(gpsStartTime);
+    while ((i < wayPoints.size()) && (time < dGpsStartTime + timeWindow/2))
+    {
+      if (wayPoints.elementAt(i).getString("time") != null)
+	time = StopImporterDialog.parseTime(wayPoints.elementAt(i)
+	    .getString("time").substring(11,19));
+      if (time < dGpsStartTime)
+	time += 24*60*60;
+      wayPointsDist.add(Double.valueOf(Double.POSITIVE_INFINITY));
+      ++i;
+    }
+    while (i < wayPoints.size())
+    {
+      int j = i;
+      double time2 = time;
+      while ((j > 0) && (time - timeWindow/2 < time2))
+      {
+	--j;
+	if (wayPoints.elementAt(j).getString("time") != null)
+	  time2 = StopImporterDialog.parseTime(wayPoints.elementAt(j)
+	      .getString("time").substring(11,19));
+	if (time2 < dGpsStartTime)
+	  time2 += 24*60*60;
+      }
+      int k = i + 1;
+      time2 = time;
+      while ((k < wayPoints.size()) && (time + timeWindow/2 > time2))
+      {
+	if (wayPoints.elementAt(k).getString("time") != null)
+	  time2 = StopImporterDialog.parseTime(wayPoints.elementAt(k)
+	      .getString("time").substring(11,19));
+	if (time2 < dGpsStartTime)
+	  time2 += 24*60*60;
+	++k;
+      }
+	
+      if (j < k)
+      {
+	double dist = 0;
+	LatLon latLonI = wayPoints.elementAt(i).getCoor();
+	for (int l = j; l < k; ++l)
+	{
+	  double distL = latLonI.greatCircleDistance(wayPoints.elementAt(l).getCoor());
+	  if (distL > dist)
+	    dist = distL;
+	}
+	wayPointsDist.add(Double.valueOf(dist));
+      }
+      else
+	wayPointsDist.add(Double.valueOf(Double.POSITIVE_INFINITY));
+	
+      if (wayPoints.elementAt(i).getString("time") != null)
+	time = StopImporterDialog.parseTime(wayPoints.elementAt(i)
+	    .getString("time").substring(11,19));
+      if (time < dGpsStartTime)
+	time += 24*60*60;
+      ++i;
+    }
+      
+    Vector< Node > toDelete = new Vector< Node >();
+    for (i = 0; i < stoplistTM.getRowCount(); ++i)
+    {
+      if ((Node)stoplistTM.nodes.elementAt(i) != null)
+	toDelete.add((Node)stoplistTM.nodes.elementAt(i));
+    }
+    if (!toDelete.isEmpty())
+    {
+      Command cmd = DeleteCommand.delete
+	  (Main.main.getEditLayer(), toDelete);
+      if (cmd == null)
+	return;
+      Main.main.undoRedo.add(cmd);
+    }
+    stoplistTM.clear();
+      
+    LatLon lastStopCoor = null;
+    for (i = 1; i < wayPoints.size()-1; ++i)
+    {
+      if (wayPointsDist.elementAt(i).doubleValue() >= threshold)
+	continue;
+      if ((wayPointsDist.elementAt(i).compareTo(wayPointsDist.elementAt(i-1)) != -1)
+	   || (wayPointsDist.elementAt(i).compareTo(wayPointsDist.elementAt(i+1)) != -1))
+	continue;
+	
+      LatLon latLon = wayPoints.elementAt(i).getCoor();
+      if ((lastStopCoor != null) &&  (lastStopCoor.greatCircleDistance(latLon) < threshold))
+	continue;
+	
+      if (wayPoints.elementAt(i).getString("time") != null)
+      {
+	time = StopImporterDialog.parseTime(wayPoints.elementAt(i)
+	    .getString("time").substring(11,19));
+	double gpsSyncTime = StopImporterDialog.parseTime(this.gpsSyncTime);
+	if (gpsSyncTime < dGpsStartTime - 12*60*60)
+	  gpsSyncTime += 24*60*60;
+	double timeDelta = gpsSyncTime - StopImporterDialog.parseTime(stopwatchStart);
+	time -= timeDelta;
+	stoplistTM.insertRow(-1, StopImporterAction.timeOf(time));
+	Node node = controller.createNode(latLon, "");
+	stoplistTM.nodes.set(stoplistTM.getRowCount()-1, node);
+      }
+	
+      lastStopCoor = latLon;
+    }
+  }
+};
Index: /applications/editors/josm/plugins/public_transport/src/public_transport/TrackStoplistAddCommand.java
===================================================================
--- /applications/editors/josm/plugins/public_transport/src/public_transport/TrackStoplistAddCommand.java	(revision 20772)
+++ /applications/editors/josm/plugins/public_transport/src/public_transport/TrackStoplistAddCommand.java	(revision 20772)
@@ -0,0 +1,49 @@
+package public_transport;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+
+import java.util.Collection;
+import java.util.Vector;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.MutableTreeNode;
+
+public class TrackStoplistAddCommand extends Command
+{
+  private int workingLine;
+  private TrackStoplistTableModel stoplistTM = null;
+  
+  public TrackStoplistAddCommand(StopImporterAction controller)
+  {
+    stoplistTM = controller.getCurrentTrack().stoplistTM;
+    workingLine = controller.getDialog().getStoplistTable().getSelectedRow();
+  }
+  
+  public boolean executeCommand()
+  {
+    stoplistTM.insertRow(workingLine, "00:00:00");
+    return true;
+  }
+  
+  public void undoCommand()
+  {
+    int workingLine = this.workingLine;
+    if (workingLine < 0)
+      workingLine = stoplistTM.getRowCount()-1;
+    stoplistTM.nodes.removeElementAt(workingLine);
+    stoplistTM.removeRow(workingLine);
+  }
+  
+  public void fillModifiedData
+    (Collection< OsmPrimitive > modified, Collection< OsmPrimitive > deleted,
+     Collection< OsmPrimitive > added)
+  {
+  }
+  
+  public MutableTreeNode description()
+  {
+    return new DefaultMutableTreeNode("public_transport.TrackStoplist.Add");
+  }
+};
Index: /applications/editors/josm/plugins/public_transport/src/public_transport/TrackStoplistDeleteCommand.java
===================================================================
--- /applications/editors/josm/plugins/public_transport/src/public_transport/TrackStoplistDeleteCommand.java	(revision 20772)
+++ /applications/editors/josm/plugins/public_transport/src/public_transport/TrackStoplistDeleteCommand.java	(revision 20772)
@@ -0,0 +1,99 @@
+package public_transport;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+
+import java.util.Collection;
+import java.util.Vector;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.MutableTreeNode;
+
+public class TrackStoplistDeleteCommand extends Command
+{
+  private class NodeTimeName
+  {
+    NodeTimeName(Node node, String time, String name)
+    {
+      this.node = node;
+      this.time = time;
+      this.name = name;
+    }
+    
+    public Node node;
+    public String time;
+    public String name;
+  };
+  
+  private Vector< Integer > workingLines = null;
+  private Vector< NodeTimeName > nodesForUndo = null;
+  private TrackStoplistTableModel stoplistTM = null;
+  
+  public TrackStoplistDeleteCommand(StopImporterAction controller)
+  {
+    stoplistTM = controller.getCurrentTrack().stoplistTM;
+    workingLines = new Vector< Integer >();
+    nodesForUndo = new Vector< NodeTimeName >();
+    
+    // use selected lines or all lines if no line is selected
+    int[] selectedLines = controller.getDialog().getStoplistTable().getSelectedRows();
+    if (selectedLines.length > 0)
+    {
+      for (int i = 0; i < selectedLines.length; ++i)
+      {
+	workingLines.add(selectedLines[i]);
+      }
+    }
+    else
+    {
+      for (int i = 0; i < stoplistTM.getRowCount(); ++i)
+	workingLines.add(new Integer(i));
+    }
+  }
+  
+  public boolean executeCommand()
+  {
+    nodesForUndo.clear();
+    for (int i = workingLines.size()-1; i >= 0; --i)
+    {
+      int j = workingLines.elementAt(i).intValue();
+      Node node = stoplistTM.nodes.elementAt(j);
+      nodesForUndo.add(new NodeTimeName
+	  (node, (String)stoplistTM.getValueAt(j, 0),
+	   (String)stoplistTM.getValueAt(j, 1)));
+      stoplistTM.nodes.removeElementAt(j);
+      stoplistTM.removeRow(j);
+      if (node == null)
+	continue;
+      Main.main.getCurrentDataSet().removePrimitive(node);
+      node.setDeleted(true);
+    }
+    return true;
+  }
+  
+  public void undoCommand()
+  {
+    for (int i = 0; i < workingLines.size(); ++i)
+    {
+      int j = workingLines.elementAt(i).intValue();
+      NodeTimeName ntn = nodesForUndo.elementAt(workingLines.size() - i - 1);
+      stoplistTM.insertRow(j, ntn.node, ntn.time, ntn.name);
+      if (ntn.node == null)
+	continue;
+      ntn.node.setDeleted(false);
+      Main.main.getCurrentDataSet().addPrimitive(ntn.node);
+    }
+  }
+  
+  public void fillModifiedData
+    (Collection< OsmPrimitive > modified, Collection< OsmPrimitive > deleted,
+     Collection< OsmPrimitive > added)
+  {
+  }
+  
+  public MutableTreeNode description()
+  {
+    return new DefaultMutableTreeNode("public_transport.TrackStoplist.Delete");
+  }
+};
Index: /applications/editors/josm/plugins/public_transport/src/public_transport/TrackStoplistDetachCommand.java
===================================================================
--- /applications/editors/josm/plugins/public_transport/src/public_transport/TrackStoplistDetachCommand.java	(revision 20772)
+++ /applications/editors/josm/plugins/public_transport/src/public_transport/TrackStoplistDetachCommand.java	(revision 20772)
@@ -0,0 +1,80 @@
+package public_transport;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+
+import java.util.Collection;
+import java.util.Vector;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.MutableTreeNode;
+
+public class TrackStoplistDetachCommand extends Command
+{
+  private Vector< Integer > workingLines = null;
+  private Vector< Node > nodesForUndo = null;
+  private TrackStoplistTableModel stoplistTM = null;
+  
+  public TrackStoplistDetachCommand(StopImporterAction controller)
+  {
+    stoplistTM = controller.getCurrentTrack().stoplistTM;
+    workingLines = new Vector< Integer >();
+    nodesForUndo = new Vector< Node >();
+    
+    // use either selected lines or all lines if no line is selected
+    int[] selectedLines = controller.getDialog().getStoplistTable().getSelectedRows();
+    Vector< Integer > consideredLines = new Vector< Integer >();
+    if (selectedLines.length > 0)
+    {
+      for (int i = 0; i < selectedLines.length; ++i)
+	consideredLines.add(selectedLines[i]);
+    }
+    else
+    {
+      for (int i = 0; i < stoplistTM.getRowCount(); ++i)
+	consideredLines.add(new Integer(i));
+    }
+    
+    // keep only lines where a node can be added
+    for (int i = 0; i < consideredLines.size(); ++i)
+    {
+      if (stoplistTM.nodes.elementAt(consideredLines.elementAt(i)) != null)
+	workingLines.add(consideredLines.elementAt(i));
+    }
+  }
+  
+  public boolean executeCommand()
+  {
+    nodesForUndo.clear();
+    for (int i = 0; i < workingLines.size(); ++i)
+    {
+      int j = workingLines.elementAt(i).intValue();
+      Node node = stoplistTM.nodes.elementAt(j);
+      nodesForUndo.add(node);
+      stoplistTM.nodes.set(j, null);
+    }
+    return true;
+  }
+  
+  public void undoCommand()
+  {
+    for (int i = 0; i < workingLines.size(); ++i)
+    {
+      int j = workingLines.elementAt(i).intValue();
+      Node node = nodesForUndo.elementAt(i);
+      stoplistTM.nodes.set(j, node);
+    }
+  }
+  
+  public void fillModifiedData
+    (Collection< OsmPrimitive > modified, Collection< OsmPrimitive > deleted,
+     Collection< OsmPrimitive > added)
+  {
+  }
+  
+  public MutableTreeNode description()
+  {
+    return new DefaultMutableTreeNode("public_transport.TrackStoplist.Detach");
+  }
+};
Index: /applications/editors/josm/plugins/public_transport/src/public_transport/TrackStoplistSortCommand.java
===================================================================
--- /applications/editors/josm/plugins/public_transport/src/public_transport/TrackStoplistSortCommand.java	(revision 20772)
+++ /applications/editors/josm/plugins/public_transport/src/public_transport/TrackStoplistSortCommand.java	(revision 20772)
@@ -0,0 +1,128 @@
+package public_transport;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Vector;
+import javax.swing.DefaultListModel;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.MutableTreeNode;
+
+public class TrackStoplistSortCommand extends Command
+{
+  private TrackStoplistTableModel stoplistTM = null;
+  private Vector< Vector< Object > > tableDataModel = null;
+  private Vector< Node > nodes = null;
+  private Vector< Integer > workingLines = null;
+  private int insPos;
+  private String stopwatchStart;
+  
+  public TrackStoplistSortCommand(StopImporterAction controller)
+  {
+    stoplistTM = controller.getCurrentTrack().stoplistTM;
+    workingLines = new Vector< Integer >();
+    insPos = controller.getDialog().getStoplistTable().getSelectedRow();
+    stopwatchStart = controller.getCurrentTrack().stopwatchStart;
+    
+    // use either selected lines or all lines if no line is selected
+    int[] selectedLines = controller.getDialog().getStoplistTable().getSelectedRows();
+    if (selectedLines.length > 0)
+    {
+      for (int i = 0; i < selectedLines.length; ++i)
+	workingLines.add(selectedLines[i]);
+    }
+    else
+    {
+      for (int i = 0; i < stoplistTM.getRowCount(); ++i)
+	workingLines.add(new Integer(i));
+    }
+  }
+  
+  @SuppressWarnings("unchecked")
+  public boolean executeCommand()
+  {
+    tableDataModel = (Vector< Vector< Object > >)stoplistTM.getDataVector()
+	.clone();
+    nodes = (Vector< Node >)stoplistTM.nodes.clone();
+    
+    Vector< NodeSortEntry > nodesToSort = new Vector< NodeSortEntry >();
+    for (int i = workingLines.size()-1; i >= 0; --i)
+    {
+      int j = workingLines.elementAt(i).intValue();
+      nodesToSort.add(new NodeSortEntry
+	  (stoplistTM.nodes.elementAt(j), (String)stoplistTM.getValueAt(j, 0),
+	    (String)stoplistTM.getValueAt(j, 1),
+	     StopImporterDialog.parseTime(stopwatchStart)));
+      stoplistTM.nodes.removeElementAt(j);
+      stoplistTM.removeRow(j);
+    }
+    
+    Collections.sort(nodesToSort);
+    
+    int insPos = this.insPos;
+    Iterator< NodeSortEntry > iter = nodesToSort.iterator();
+    while (iter.hasNext())
+    {
+      NodeSortEntry nse = iter.next();
+      stoplistTM.insertRow(insPos, nse.node, nse.time, nse.name);
+      if (insPos >= 0)
+	++insPos;
+    }
+    return true;
+  }
+  
+  public void undoCommand()
+  {
+    stoplistTM.setDataVector(tableDataModel);
+    stoplistTM.nodes = nodes;
+  }
+  
+  public void fillModifiedData
+    (Collection< OsmPrimitive > modified, Collection< OsmPrimitive > deleted,
+     Collection< OsmPrimitive > added)
+  {
+  }
+  
+  public MutableTreeNode description()
+  {
+    return new DefaultMutableTreeNode("public_transport.TrackStoplist.Sort");
+  }
+  
+  private class NodeSortEntry implements Comparable< NodeSortEntry >
+  {
+    public Node node = null;
+    public String time = null;
+    public String name = null;
+    public double startTime = 0;
+    
+    public NodeSortEntry(Node node, String time, String name, double startTime)
+    {
+      this.node = node;
+      this.time = time;
+      this.name = name;
+    }
+    
+    public int compareTo(NodeSortEntry nse)
+    {
+      double time = StopImporterDialog.parseTime(this.time);
+      if (time - startTime > 12*60*60)
+	time -= 24*60*60;
+      
+      double nseTime = StopImporterDialog.parseTime(nse.time);
+      if (nseTime - startTime > 12*60*60)
+	nseTime -= 24*60*60;
+      
+      if (time < nseTime)
+	return -1;
+      else if (time > nseTime)
+	return 1;
+      else
+	return 0;
+    }
+  };
+};
Index: /applications/editors/josm/plugins/public_transport/src/public_transport/TrackStoplistTableModel.java
===================================================================
--- /applications/editors/josm/plugins/public_transport/src/public_transport/TrackStoplistTableModel.java	(revision 20772)
+++ /applications/editors/josm/plugins/public_transport/src/public_transport/TrackStoplistTableModel.java	(revision 20772)
@@ -0,0 +1,77 @@
+package public_transport;
+
+import static org.openstreetmap.josm.tools.I18n.marktr;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.Vector;
+import javax.swing.table.DefaultTableModel;
+
+import org.openstreetmap.josm.data.osm.Node;
+
+public class TrackStoplistTableModel extends DefaultTableModel
+{
+  public Vector< Node > nodes = new Vector< Node >();
+  public Vector< String > columns = null;
+    
+  public TrackStoplistTableModel(TrackReference tr)
+  {
+    if (columns == null)
+    {
+      columns = new Vector< String >();
+      columns.add("Time");
+      columns.add("Name");
+    }
+      
+    setColumnIdentifiers(columns);
+    addTableModelListener(tr);
+  }
+    
+  public boolean isCellEditable(int row, int column) {
+    return true;
+  }
+    
+  public void addRow(Object[] obj) {
+    throw new UnsupportedOperationException();
+  }
+    
+  public void insertRow(int insPos, Object[] obj) {
+    throw new UnsupportedOperationException();
+  }
+    
+  public void addRow(String time) {
+    insertRow(-1, time);
+  }
+    
+  public void insertRow(int insPos, String time)
+  {
+    insertRow(insPos, null, time, "");
+  }
+    
+  public void insertRow(int insPos, Node node, String time, String name)
+  {
+    String[] buf = { "", "" };
+    buf[0] = time;
+    buf[1] = name;
+    if (insPos == -1)
+    {
+      nodes.addElement(node);
+      super.addRow(buf);
+    }
+    else
+    {
+      nodes.insertElementAt(node, insPos);
+      super.insertRow(insPos, buf);
+    }
+  }
+    
+  public void clear()
+  {
+    nodes.clear();
+    super.setRowCount(0);
+  }
+    
+  public void setDataVector(Vector< Vector< Object > > dataVector)
+  {
+    setDataVector(dataVector, columns);
+  }
+};
Index: /applications/editors/josm/plugins/public_transport/src/public_transport/WaypointTableModel.java
===================================================================
--- /applications/editors/josm/plugins/public_transport/src/public_transport/WaypointTableModel.java	(revision 20772)
+++ /applications/editors/josm/plugins/public_transport/src/public_transport/WaypointTableModel.java	(revision 20772)
@@ -0,0 +1,95 @@
+package public_transport;
+
+import static org.openstreetmap.josm.tools.I18n.marktr;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.Vector;
+import javax.swing.event.TableModelEvent;
+import javax.swing.event.TableModelListener;
+import javax.swing.table.DefaultTableModel;
+
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.gpx.WayPoint;
+import org.openstreetmap.josm.data.osm.Node;
+
+public class WaypointTableModel extends DefaultTableModel
+      implements TableModelListener
+{
+  private StopImporterAction controller = null;
+  public Vector< Node > nodes = new Vector< Node >();
+  public Vector< LatLon > coors = new Vector< LatLon >();
+    
+  public WaypointTableModel(StopImporterAction controller)
+  {
+    this.controller = controller;
+    addColumn("Time");
+    addColumn("Stopname");
+    addTableModelListener(this);
+  }
+    
+  public boolean isCellEditable(int row, int column)
+  {
+    if (column == 1)
+      return true;
+    return false;
+  }
+    
+  public void addRow(Object[] obj)
+  {
+    throw new UnsupportedOperationException();
+  }
+    
+  public void insertRow(int insPos, Object[] obj)
+  {
+    throw new UnsupportedOperationException();
+  }
+    
+  public void addRow(WayPoint wp)
+  {
+    insertRow(-1, wp);
+  }
+    
+  public void insertRow(int insPos, WayPoint wp)
+  {
+    String[] buf = { "", "" };
+    buf[0] = wp.getString("time");
+    if (buf[0] == null)
+      buf[0] = "";
+    buf[1] = wp.getString("name");
+    if (buf[1] == null)
+      buf[1] = "";
+
+    Node node = controller.createNode(wp.getCoor(), buf[1]);
+      
+    if (insPos == -1)
+    {
+      nodes.addElement(node);
+      coors.addElement(wp.getCoor());
+      super.addRow(buf);
+    }
+    else
+    {
+      nodes.insertElementAt(node, insPos);
+      coors.insertElementAt(wp.getCoor(), insPos);
+      super.insertRow(insPos, buf);
+    }
+  }
+    
+  public void clear()
+  {
+    nodes.clear();
+    super.setRowCount(0);
+  }
+  
+  public void tableChanged(TableModelEvent e)
+  {
+    if (e.getType() == TableModelEvent.UPDATE)
+    {
+      if (nodes.elementAt(e.getFirstRow()) != null)
+      {
+	Node node = nodes.elementAt(e.getFirstRow());
+	node.put("name", (String)getValueAt(e.getFirstRow(), 1));
+      }
+    }
+  }
+};
Index: /applications/editors/josm/plugins/public_transport/src/public_transport/WaypointsDetachCommand.java
===================================================================
--- /applications/editors/josm/plugins/public_transport/src/public_transport/WaypointsDetachCommand.java	(revision 20771)
+++ /applications/editors/josm/plugins/public_transport/src/public_transport/WaypointsDetachCommand.java	(revision 20772)
@@ -15,11 +15,9 @@
   private Vector< Integer > workingLines = null;
   private Vector< Node > nodesForUndo = null;
-  private StopImporterAction.WaypointTableModel waypointTM = null;
-  private String type = null;
+  private WaypointTableModel waypointTM = null;
   
   public WaypointsDetachCommand(StopImporterAction controller)
   {
     waypointTM = controller.getWaypointTableModel();
-    type = controller.getDialog().getStoptype();
     workingLines = new Vector< Integer >();
     nodesForUndo = new Vector< Node >();
Index: /applications/editors/josm/plugins/public_transport/src/public_transport/WaypointsDisableCommand.java
===================================================================
--- /applications/editors/josm/plugins/public_transport/src/public_transport/WaypointsDisableCommand.java	(revision 20771)
+++ /applications/editors/josm/plugins/public_transport/src/public_transport/WaypointsDisableCommand.java	(revision 20772)
@@ -15,11 +15,9 @@
   private Vector< Integer > workingLines = null;
   private Vector< Node > nodesForUndo = null;
-  private StopImporterAction.WaypointTableModel waypointTM = null;
-  private String type = null;
+  private WaypointTableModel waypointTM = null;
   
   public WaypointsDisableCommand(StopImporterAction controller)
   {
     waypointTM = controller.getWaypointTableModel();
-    type = controller.getDialog().getStoptype();
     workingLines = new Vector< Integer >();
     nodesForUndo = new Vector< Node >();
@@ -70,7 +68,9 @@
       int j = workingLines.elementAt(i).intValue();
       Node node = nodesForUndo.elementAt(i);
+      waypointTM.nodes.set(j, node);
+      if (node == null)
+	continue;
       node.setDeleted(false);
       Main.main.getCurrentDataSet().addPrimitive(node);
-      waypointTM.nodes.set(j, node);
     }
   }
Index: /applications/editors/josm/plugins/public_transport/src/public_transport/WaypointsEnableCommand.java
===================================================================
--- /applications/editors/josm/plugins/public_transport/src/public_transport/WaypointsEnableCommand.java	(revision 20771)
+++ /applications/editors/josm/plugins/public_transport/src/public_transport/WaypointsEnableCommand.java	(revision 20772)
@@ -14,5 +14,5 @@
 {
   private Vector< Integer > workingLines = null;
-  private StopImporterAction.WaypointTableModel waypointTM = null;
+  private WaypointTableModel waypointTM = null;
   private String type = null;
   
@@ -64,4 +64,6 @@
       Node node = waypointTM.nodes.elementAt(j);
       waypointTM.nodes.set(j, null);
+      if (node == null)
+	continue;
       Main.main.getCurrentDataSet().removePrimitive(node);
       node.setDeleted(true);
