Index: /applications/editors/josm/plugins/public_transport/src/public_transport/GTFSAddCommand.java
===================================================================
--- /applications/editors/josm/plugins/public_transport/src/public_transport/GTFSAddCommand.java	(revision 22148)
+++ /applications/editors/josm/plugins/public_transport/src/public_transport/GTFSAddCommand.java	(revision 22148)
@@ -0,0 +1,89 @@
+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.JLabel;
+
+public class GTFSAddCommand extends Command
+{
+  private Vector< Integer > workingLines = null;
+  private Vector< String > typesForUndo = null;
+  private GTFSStopTableModel gtfsStopTM = null;
+  private String type = null;
+  
+  public GTFSAddCommand(GTFSImporterAction controller)
+  {
+    gtfsStopTM = controller.getGTFSStopTableModel();
+    type = controller.getDialog().getStoptype();
+    workingLines = new Vector< Integer >();
+    typesForUndo = new Vector< String >();
+    
+    // use either selected lines or all lines if no line is selected
+    int[] selectedLines = controller.getDialog().getGTFSStopTable().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 < gtfsStopTM.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 (gtfsStopTM.nodes.elementAt(consideredLines.elementAt(i)) == null)
+	workingLines.add(consideredLines.elementAt(i));
+    }
+  }
+  
+  public boolean executeCommand()
+  {
+    typesForUndo.clear();
+    for (int i = 0; i < workingLines.size(); ++i)
+    {
+      int j = workingLines.elementAt(i).intValue();
+      typesForUndo.add((String)gtfsStopTM.getValueAt(j, 2));
+      Node node = GTFSImporterAction.createNode
+        (gtfsStopTM.coors.elementAt(j), (String)gtfsStopTM.getValueAt(j, 0),
+         (String)gtfsStopTM.getValueAt(j, 1));
+      gtfsStopTM.nodes.set(j, node);
+      gtfsStopTM.setValueAt("added", j, 2);
+    }
+    return true;
+  }
+  
+  public void undoCommand()
+  {
+    for (int i = 0; i < workingLines.size(); ++i)
+    {
+      int j = workingLines.elementAt(i).intValue();
+      Node node = gtfsStopTM.nodes.elementAt(j);
+      gtfsStopTM.nodes.set(j, null);
+      gtfsStopTM.setValueAt(typesForUndo.elementAt(i), j, 2);
+      if (node == null)
+	continue;
+      Main.main.getCurrentDataSet().removePrimitive(node);
+      node.setDeleted(true);
+    }
+  }
+  
+  public void fillModifiedData
+    (Collection< OsmPrimitive > modified, Collection< OsmPrimitive > deleted,
+     Collection< OsmPrimitive > added)
+  {
+  }
+  
+  @Override public JLabel getDescription()
+  {
+    return new JLabel("public_transport.GTFSStops.Enable");
+  }
+};
Index: /applications/editors/josm/plugins/public_transport/src/public_transport/GTFSCatchCommand.java
===================================================================
--- /applications/editors/josm/plugins/public_transport/src/public_transport/GTFSCatchCommand.java	(revision 22148)
+++ /applications/editors/josm/plugins/public_transport/src/public_transport/GTFSCatchCommand.java	(revision 22148)
@@ -0,0 +1,112 @@
+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.Iterator;
+import java.util.Vector;
+import javax.swing.JLabel;
+
+public class GTFSCatchCommand extends Command
+{
+  private Vector< Integer > workingLines = null;
+  private Node undoMapNode = null;
+  private Node undoTableNode = null;
+  private GTFSStopTableModel gtfsStopTM = null;
+  private String type = null;
+  
+  public GTFSCatchCommand(GTFSImporterAction controller)
+  {
+    gtfsStopTM = controller.getGTFSStopTableModel();
+    workingLines = new Vector< Integer >();
+    
+    // use either selected lines or all lines if no line is selected
+    int[] selectedLines = controller.getDialog().getGTFSStopTable().getSelectedRows();
+    if (selectedLines.length != 1)
+      return;
+    workingLines.add(selectedLines[0]);
+  }
+  
+  public boolean executeCommand()
+  {
+    if (workingLines.size() != 1)
+      return false;
+    Node dest = null;
+    Iterator< Node > iter =
+        Main.main.getCurrentDataSet().getSelectedNodes().iterator();
+    int j = workingLines.elementAt(0);
+    while (iter.hasNext())
+    {
+      Node n = iter.next();
+      if ((n != null) && (n.equals(gtfsStopTM.nodes.elementAt(j))))
+	continue;
+      if (dest != null)
+	return false;
+      dest = n;
+    }
+    if (dest == null)
+      return false;
+    undoMapNode = new Node(dest);
+    
+    Node node = gtfsStopTM.nodes.elementAt(j);
+    undoTableNode = node;
+    if (node != null)
+    {
+      Main.main.getCurrentDataSet().removePrimitive(node);
+      node.setDeleted(true);
+    }
+    
+    dest.setCoor(gtfsStopTM.coors.elementAt(j));
+    dest.put("highway", "bus_stop");
+    dest.put("stop_id", (String)gtfsStopTM.getValueAt(j, 0));
+    if (dest.get("name") == null)
+      dest.put("name", (String)gtfsStopTM.getValueAt(j, 1));
+    dest.put("note", "moved by gtfs import");
+    gtfsStopTM.nodes.set(j, dest);
+    type = (String)gtfsStopTM.getValueAt(j, 2);
+    gtfsStopTM.setValueAt("fed", j, 2);
+    
+    return true;
+  }
+  
+  public void undoCommand()
+  {
+    if (workingLines.size() != 1)
+      return;
+    int j = workingLines.elementAt(0);
+    
+    Node node = gtfsStopTM.nodes.elementAt(j);
+    if (node != null)
+    {
+      Main.main.getCurrentDataSet().removePrimitive(node);
+      node.setDeleted(true);
+    }
+    
+    if (undoMapNode != null)
+    {
+      undoMapNode.setDeleted(false);
+      Main.main.getCurrentDataSet().addPrimitive(undoMapNode);
+    }
+    if (undoTableNode != null)
+    {
+      undoTableNode.setDeleted(false);
+      Main.main.getCurrentDataSet().addPrimitive(undoTableNode);
+    }
+    gtfsStopTM.nodes.set(j, undoTableNode);
+    gtfsStopTM.setValueAt(type, j, 2);
+  }
+  
+  public void fillModifiedData
+    (Collection< OsmPrimitive > modified, Collection< OsmPrimitive > deleted,
+     Collection< OsmPrimitive > added)
+  {
+  }
+  
+  @Override public JLabel getDescription()
+  {
+    return new JLabel("public_transport.GTFSStops.Catch");
+  }
+};
Index: /applications/editors/josm/plugins/public_transport/src/public_transport/GTFSDeleteCommand.java
===================================================================
--- /applications/editors/josm/plugins/public_transport/src/public_transport/GTFSDeleteCommand.java	(revision 22148)
+++ /applications/editors/josm/plugins/public_transport/src/public_transport/GTFSDeleteCommand.java	(revision 22148)
@@ -0,0 +1,93 @@
+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.JLabel;
+
+public class GTFSDeleteCommand extends Command
+{
+  private Vector< Integer > workingLines = null;
+  private Vector< Node > nodesForUndo = null;
+  private Vector< String > typesForUndo = null;
+  private GTFSStopTableModel gtfsStopTM = null;
+  
+  public GTFSDeleteCommand(GTFSImporterAction controller)
+  {
+    gtfsStopTM = controller.getGTFSStopTableModel();
+    workingLines = new Vector< Integer >();
+    nodesForUndo = new Vector< Node >();
+    typesForUndo = new Vector< String >();
+    
+    // use either selected lines or all lines if no line is selected
+    int[] selectedLines = controller.getDialog().getGTFSStopTable().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 < gtfsStopTM.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 (gtfsStopTM.nodes.elementAt(consideredLines.elementAt(i)) != null)
+	workingLines.add(consideredLines.elementAt(i));
+    }
+  }
+  
+  public boolean executeCommand()
+  {
+    nodesForUndo.clear();
+    typesForUndo.clear();
+    for (int i = 0; i < workingLines.size(); ++i)
+    {
+      int j = workingLines.elementAt(i).intValue();
+      Node node = gtfsStopTM.nodes.elementAt(j);
+      nodesForUndo.add(node);
+      typesForUndo.add((String)gtfsStopTM.getValueAt(j, 2));
+      if (node == null)
+	continue;
+      gtfsStopTM.nodes.set(j, null);
+      gtfsStopTM.setValueAt("skipped", j, 2);
+      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();
+      Node node = nodesForUndo.elementAt(i);
+      gtfsStopTM.nodes.set(j, node);
+      gtfsStopTM.setValueAt(typesForUndo.elementAt(i), j, 2);
+      if (node == null)
+	continue;
+      node.setDeleted(false);
+      Main.main.getCurrentDataSet().addPrimitive(node);
+    }
+  }
+  
+  public void fillModifiedData
+    (Collection< OsmPrimitive > modified, Collection< OsmPrimitive > deleted,
+     Collection< OsmPrimitive > added)
+  {
+  }
+  
+  @Override public JLabel getDescription()
+  {
+    return new JLabel("public_transport.GTFS.Disable");
+  }
+};
Index: /applications/editors/josm/plugins/public_transport/src/public_transport/GTFSImporterAction.java
===================================================================
--- /applications/editors/josm/plugins/public_transport/src/public_transport/GTFSImporterAction.java	(revision 22148)
+++ /applications/editors/josm/plugins/public_transport/src/public_transport/GTFSImporterAction.java	(revision 22148)
@@ -0,0 +1,560 @@
+package public_transport;
+
+import static org.openstreetmap.josm.tools.I18n.marktr;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Container;
+import java.awt.Frame;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.InputStream;
+import java.io.IOException;
+import java.text.DecimalFormat;
+import java.text.Format;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Vector;
+import java.util.zip.GZIPInputStream;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.DefaultListModel;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JDialog;
+import javax.swing.JFileChooser;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTabbedPane;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.ListSelectionModel;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.event.TableModelEvent;
+import javax.swing.event.TableModelListener;
+import javax.swing.table.DefaultTableModel;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+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.GpxData;
+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.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
+import org.openstreetmap.josm.io.GpxReader;
+
+import org.xml.sax.SAXException;
+
+public class GTFSImporterAction extends JosmAction
+{ 
+  private static GTFSImporterDialog dialog = null;
+  private static DefaultListModel tracksListModel = null;
+  private static Vector< String > data = null;
+  private static TrackReference currentTrack = null;
+  private static GTFSStopTableModel gtfsStopTM = null;
+  public boolean inEvent = false;
+  
+  public GTFSImporterAction()
+  {
+    super(tr("Create Stops from GTFS ..."), null,
+	  tr("Create Stops from a GTFS file"), null, true);
+  }
+
+  public GTFSStopTableModel getGTFSStopTableModel()
+  {
+    return gtfsStopTM;
+  }
+  
+  public GTFSImporterDialog getDialog()
+  {
+    return dialog;
+  }
+
+  public DefaultListModel getTracksListModel()
+  {
+    if (tracksListModel == null)
+      tracksListModel = new DefaultListModel();
+    return tracksListModel;
+  }
+  
+  public TrackReference getCurrentTrack()
+  {
+    return currentTrack;
+  }
+
+  public void actionPerformed(ActionEvent event)
+  {
+    DataSet mainDataSet = Main.main.getCurrentDataSet();
+    
+    if (dialog == null)
+      dialog = new GTFSImporterDialog(this);
+    
+    dialog.setVisible(true);
+
+    if (tr("Create Stops from GTFS ...").equals(event.getActionCommand()))
+    {
+      String curDir = Main.pref.get("lastDirectory");
+      if (curDir.equals(""))
+      {
+	curDir = ".";
+      }
+      JFileChooser fc = new JFileChooser(new File(curDir));
+      fc.setDialogTitle("Select GTFS file (stops.txt)");  
+      fc.setMultiSelectionEnabled(false);
+      
+      int answer = fc.showOpenDialog(Main.parent);
+      if (answer != JFileChooser.APPROVE_OPTION)
+	return;
+      
+      if (!fc.getCurrentDirectory().getAbsolutePath().equals(curDir))
+	Main.pref.put("lastDirectory", fc.getCurrentDirectory().getAbsolutePath());
+      
+      importData(fc.getSelectedFile());
+      
+      refreshData();
+    }
+/*    else if ("stopImporter.settingsGPSTimeStart".equals(event.getActionCommand()))
+    {
+      if ((!inEvent) && (dialog.gpsTimeStartValid()) && (currentTrack != null))
+	Main.main.undoRedo.add(new TrackStoplistRelocateCommand(this));
+    }
+    else if ("stopImporter.settingsStopwatchStart".equals(event.getActionCommand()))
+    {
+      if ((!inEvent) && (dialog.stopwatchStartValid()) && (currentTrack != null))
+	Main.main.undoRedo.add(new TrackStoplistRelocateCommand(this));
+    }
+    else if ("stopImporter.settingsTimeWindow".equals(event.getActionCommand()))
+    {
+      if (currentTrack != null)
+	currentTrack.timeWindow = dialog.getTimeWindow();
+    }
+    else if ("stopImporter.settingsThreshold".equals(event.getActionCommand()))
+    {
+      if (currentTrack != null)
+	currentTrack.threshold = dialog.getThreshold();
+    }
+    else if ("stopImporter.settingsSuggestStops".equals(event.getActionCommand()))
+      Main.main.undoRedo.add(new TrackSuggestStopsCommand(this));
+    else if ("stopImporter.stoplistFind".equals(event.getActionCommand()))
+      findNodesInTable(dialog.getStoplistTable(), currentTrack.stoplistTM.getNodes());
+    else if ("stopImporter.stoplistShow".equals(event.getActionCommand()))
+      showNodesFromTable(dialog.getStoplistTable(), currentTrack.stoplistTM.getNodes());
+    else if ("stopImporter.stoplistMark".equals(event.getActionCommand()))
+      markNodesFromTable(dialog.getStoplistTable(), currentTrack.stoplistTM.getNodes());
+    else if ("stopImporter.stoplistDetach".equals(event.getActionCommand()))
+    {
+      Main.main.undoRedo.add(new TrackStoplistDetachCommand(this));
+      dialog.getStoplistTable().clearSelection();
+    }*/
+    else if ("gtfsImporter.gtfsStopsAdd".equals(event.getActionCommand()))
+      Main.main.undoRedo.add(new GTFSAddCommand(this));
+    else if ("gtfsImporter.gtfsStopsDelete".equals(event.getActionCommand()))
+      Main.main.undoRedo.add(new GTFSDeleteCommand(this));
+    else if ("gtfsImporter.gtfsStopsCatch".equals(event.getActionCommand()))
+      Main.main.undoRedo.add(new GTFSCatchCommand(this));
+    else if ("gtfsImporter.gtfsStopsJoin".equals(event.getActionCommand()))
+      Main.main.undoRedo.add(new GTFSJoinCommand(this));
+    else if ("gtfsImporter.gtfsStopsFind".equals(event.getActionCommand()))
+      findNodesInTable(dialog.getGTFSStopTable(), gtfsStopTM.nodes);
+    else if ("gtfsImporter.gtfsStopsShow".equals(event.getActionCommand()))
+      showNodesFromTable(dialog.getGTFSStopTable(), gtfsStopTM.nodes);
+    else if ("gtfsImporter.gtfsStopsMark".equals(event.getActionCommand()))
+      markNodesFromTable(dialog.getGTFSStopTable(), gtfsStopTM.nodes);
+  }
+
+  private void importData(final File file)
+  {
+    try 
+    {
+      FileReader is = new FileReader(file);
+      final BufferedReader r = new BufferedReader(is);
+      
+      if (data == null)
+	data = new Vector< String >();
+      else
+	data.clear();
+      
+      while (r.ready())
+	data.add(r.readLine());
+    }
+    catch (FileNotFoundException e) 
+    {
+      e.printStackTrace();
+      JOptionPane.showMessageDialog(null, tr("File \"{0}\" does not exist", file.getName()));
+    }
+    catch (IOException e)
+    {
+      e.printStackTrace();
+      JOptionPane.showMessageDialog(null, tr("IOException \"{0}\" occurred", e.toString()));
+    }
+  }
+
+  private void refreshData()
+  {
+    if (data != null)
+    {
+      Vector< Node > existingStops = new Vector< Node >();
+      
+      if (Main.main.getCurrentDataSet() == null)
+      {
+        JOptionPane.showMessageDialog(null, "There exists no dataset."
+	    + " Try to download data from the server or open an OSM file.",
+     "No data found", JOptionPane.ERROR_MESSAGE);
+      
+        System.out.println("Public Transport: StopInserter: No data found");
+      }
+      else
+      {
+	Iterator< Node > iter =
+	    Main.main.getCurrentDataSet().getNodes().iterator();
+	while (iter.hasNext())
+	{
+	  Node node = iter.next();
+	  if ("bus_stop".equals(node.get("highway")))
+	    existingStops.add(node);
+	}
+      }
+      
+      Iterator< String > iter = data.iterator();
+      if (iter.hasNext())
+	gtfsStopTM = new GTFSStopTableModel(this, iter.next());
+      else
+      {
+	JOptionPane.showMessageDialog
+	(null, "The GTFS file was empty.", "No data found",
+	 JOptionPane.ERROR_MESSAGE);
+	 
+	 System.out.println("Public Transport: GTFSImporter: No data found");
+      }
+      
+      while (iter.hasNext())
+      {
+	String s = iter.next();
+	gtfsStopTM.addRow(s, existingStops);
+      }
+      dialog.setGTFSStopTableModel(gtfsStopTM);
+    }
+    else
+    {
+      JOptionPane.showMessageDialog
+      (null, "The GTFS file was empty.", "No data found",
+       JOptionPane.ERROR_MESSAGE);
+      
+      System.out.println("Public Transport: GTFSImporter: No data found");
+    }
+  }
+  
+//   public void tracksSelectionChanged(int selectedPos)
+//   {
+//     if (selectedPos >= 0)
+//     {
+//       currentTrack = ((TrackReference)tracksListModel.elementAt(selectedPos));
+//       dialog.setTrackValid(true);
+//       
+//       //Prepare Settings
+//       dialog.setSettings
+// 	  (currentTrack.gpsSyncTime, currentTrack.stopwatchStart,
+// 	   currentTrack.timeWindow, currentTrack.threshold);
+//       
+//       //Prepare Stoplist
+//       dialog.setStoplistTableModel
+//           (((TrackReference)tracksListModel.elementAt(selectedPos)).stoplistTM);
+//     }
+//     else
+//     {
+//       currentTrack = null;
+//       dialog.setTrackValid(false);
+//     }
+//   }
+
+  public static Node createNode(LatLon latLon, String id, String name)
+  {
+    Node node = new Node(latLon);
+    node.put("highway", "bus_stop");
+    node.put("stop_id", id);
+    node.put("name", name);
+    if (Main.main.getCurrentDataSet() == null)
+    {
+      JOptionPane.showMessageDialog(null, "There exists no dataset."
+	  + " Try to download data from the server or open an OSM file.",
+   "No data found", JOptionPane.ERROR_MESSAGE);
+      
+      System.out.println("Public Transport: StopInserter: No data found");
+	    
+      return null;
+    }
+    Main.main.getCurrentDataSet().addPrimitive(node);
+    return node;
+  }
+
+  /* returns a collection of all selected lines or
+     a collection of all lines otherwise */
+  public static Vector< Integer > getConsideredLines(JTable table)
+  {
+    int[] selectedLines = table.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 < table.getRowCount(); ++i)
+	consideredLines.add(new Integer(i));
+    }
+    return consideredLines;
+  }
+
+  /* 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;
+    
+    int hour = (int)Math.floor(t/60/60);
+    t -=  Math.floor(t/60/60)*60*60;
+    int minute = (int)Math.floor(t/60);
+    t -=  Math.floor(t/60)*60;
+    double second = t;
+    
+    Format format = new DecimalFormat("00");
+    Format formatS = new DecimalFormat("00.###");
+    return (format.format(hour) + ":" + format.format(minute) + ":"
+	+ formatS.format(second));
+  }
+  
+  public Action getFocusAddAction()
+  {
+    return new FocusAddAction();
+  }
+  
+  private class FocusAddAction extends AbstractAction
+  {
+    public void actionPerformed(ActionEvent e)
+    {
+      Main.main.undoRedo.add(new GTFSAddCommand(GTFSImporterAction.this));
+      showNodesFromTable(dialog.getGTFSStopTable(), gtfsStopTM.nodes);
+    }
+  };
+  
+/*  public Action getFocusWaypointShelterAction(String shelter)
+  {
+    return new FocusWaypointShelterAction(shelter);
+  }
+
+  public Action getFocusWaypointDeleteAction()
+  {
+    return new AbstractAction()
+    {
+      public void actionPerformed(ActionEvent e)
+      {
+	JTable table = dialog.getWaypointsTable();
+	int row = table.getEditingRow();
+	if (row < 0)
+	  return;
+	table.clearSelection();
+	table.addRowSelectionInterval(row, row);
+/*	Main.main.undoRedo.add
+	    (new WaypointsDisableCommand(GTFSImporterAction.this));*
+      }
+    };
+  }
+
+  public Action getFocusTrackStoplistNameAction()
+  {
+    return new FocusTrackStoplistNameAction();
+  }
+  
+  public Action getFocusTrackStoplistShelterAction(String shelter)
+  {
+    return new FocusTrackStoplistShelterAction(shelter);
+  }
+
+  public Action getFocusStoplistDeleteAction()
+  {
+    return new AbstractAction()
+    {
+      public void actionPerformed(ActionEvent e)
+      {
+	JTable table = dialog.getStoplistTable();
+	int row = table.getEditingRow();
+	if (row < 0)
+	  return;
+	table.clearSelection();
+	table.addRowSelectionInterval(row, row);
+/*	Main.main.undoRedo.add
+	    (new TrackStoplistDeleteCommand(GTFSImporterAction.this));*
+      }
+    };
+  }
+
+  private class FocusWaypointNameAction extends AbstractAction
+  {
+    public void actionPerformed(ActionEvent e)
+    {
+      JTable table = dialog.getWaypointsTable();
+      showNodesFromTable(table, waypointTM.nodes);
+      markNodesFromTable(table, waypointTM.nodes);
+      int row = table.getEditingRow();
+      if (row < 0)
+	row = 0;
+      waypointTM.inEvent = true;
+      if (table.getCellEditor() != null)
+      {
+	if (!table.getCellEditor().stopCellEditing())
+	  table.getCellEditor().cancelCellEditing();
+      }
+      table.editCellAt(row, 1);
+      table.getCellEditor().getTableCellEditorComponent
+	  (table, "", true, row, 1);
+      waypointTM.inEvent = false;
+    }
+  };
+  
+  private class FocusWaypointShelterAction extends AbstractAction
+  {
+    private String defaultShelter = null;
+    
+    public FocusWaypointShelterAction(String defaultShelter)
+    {
+      this.defaultShelter = defaultShelter;
+    }
+    
+    public void actionPerformed(ActionEvent e)
+    {
+      JTable table = dialog.getWaypointsTable();
+      showNodesFromTable(table, waypointTM.nodes);
+      markNodesFromTable(table, waypointTM.nodes);
+      int row = table.getEditingRow();
+      if (row < 0)
+	row = 0;
+      waypointTM.inEvent = true;
+      if (table.getCellEditor() != null)
+      {
+	if (!table.getCellEditor().stopCellEditing())
+	  table.getCellEditor().cancelCellEditing();
+      }
+      table.editCellAt(row, 2);
+      waypointTM.inEvent = false;
+      table.getCellEditor().getTableCellEditorComponent
+          (table, defaultShelter, true, row, 2);
+    }
+  };
+  
+  private class FocusTrackStoplistNameAction extends AbstractAction
+  {
+    public void actionPerformed(ActionEvent e)
+    {
+      JTable table = dialog.getStoplistTable();
+      showNodesFromTable(table, currentTrack.stoplistTM.getNodes());
+      markNodesFromTable(table, currentTrack.stoplistTM.getNodes());
+      int row = table.getEditingRow();
+      if (row < 0)
+	row = 0;
+      currentTrack.inEvent = true;
+      if (table.getCellEditor() != null)
+      {
+	if (!table.getCellEditor().stopCellEditing())
+	  table.getCellEditor().cancelCellEditing();
+      }
+      table.editCellAt(row, 1);
+      table.getCellEditor().getTableCellEditorComponent
+          (table, "", true, row, 1);
+      currentTrack.inEvent = false;
+    }
+  };
+  
+  private class FocusTrackStoplistShelterAction extends AbstractAction
+  {
+    private String defaultShelter = null;
+    
+    public FocusTrackStoplistShelterAction(String defaultShelter)
+    {
+      this.defaultShelter = defaultShelter;
+    }
+    
+    public void actionPerformed(ActionEvent e)
+    {
+      JTable table = dialog.getStoplistTable();
+      showNodesFromTable(table, currentTrack.stoplistTM.getNodes());
+      markNodesFromTable(table, currentTrack.stoplistTM.getNodes());
+      int row = table.getEditingRow();
+      if (row < 0)
+	row = 0;
+      currentTrack.inEvent = true;
+      if (table.getCellEditor() != null)
+      {
+	if (!table.getCellEditor().stopCellEditing())
+	  table.getCellEditor().cancelCellEditing();
+      }
+      table.editCellAt(row, 2);
+      currentTrack.inEvent = false;
+      table.getCellEditor().getTableCellEditorComponent
+          (table, defaultShelter, true, row, 2);
+    }
+  };*/
+}
Index: /applications/editors/josm/plugins/public_transport/src/public_transport/GTFSImporterDialog.java
===================================================================
--- /applications/editors/josm/plugins/public_transport/src/public_transport/GTFSImporterDialog.java	(revision 22148)
+++ /applications/editors/josm/plugins/public_transport/src/public_transport/GTFSImporterDialog.java	(revision 22148)
@@ -0,0 +1,552 @@
+package public_transport;
+
+import static org.openstreetmap.josm.tools.I18n.marktr;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Container;
+import java.awt.Frame;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.IOException;
+import java.text.DecimalFormat;
+import java.text.Format;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Vector;
+import java.util.zip.GZIPInputStream;
+
+import javax.swing.DefaultCellEditor;
+import javax.swing.DefaultListModel;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.JDialog;
+import javax.swing.JFileChooser;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTabbedPane;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.KeyStroke;
+import javax.swing.ListSelectionModel;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.event.TableModelEvent;
+import javax.swing.event.TableModelListener;
+import javax.swing.table.DefaultTableModel;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+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.GpxData;
+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.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
+import org.openstreetmap.josm.io.GpxReader;
+
+import org.xml.sax.SAXException;
+
+public class GTFSImporterDialog
+{  
+  private JDialog jDialog = null;
+  private JTabbedPane tabbedPane = null;
+  private JComboBox cbStoptype = null;
+  private JList tracksList = null;
+  private JTextField tfGPSTimeStart = null;
+  private JTextField tfStopwatchStart = null;
+  private JTextField tfTimeWindow = null;
+  private JTextField tfThreshold = null;
+  private JTable stoplistTable = null;
+  private JTable gtfsStopTable = null;
+  
+  public GTFSImporterDialog(GTFSImporterAction controller)
+  {
+    Frame frame = JOptionPane.getFrameForComponent(Main.parent);
+    jDialog = new JDialog(frame, "Create Stops from GTFS", false);
+    tabbedPane = new JTabbedPane();
+    JPanel tabSettings = new JPanel();
+    tabbedPane.addTab(marktr("Settings"), tabSettings);
+    JPanel tabWaypoints = new JPanel();
+    tabbedPane.addTab(marktr("GTFS-Stops"), tabWaypoints);
+    tabbedPane.setEnabledAt(0, false);
+    tabbedPane.setEnabledAt(1, true);
+    jDialog.add(tabbedPane);
+      
+    //Settings Tab
+    JPanel contentPane = tabSettings;
+    GridBagLayout gridbag = new GridBagLayout();
+    GridBagConstraints layoutCons = new GridBagConstraints();
+    contentPane.setLayout(gridbag);
+      
+    JLabel label = new JLabel("Type of stops to add");
+      
+    layoutCons.gridx = 0;
+    layoutCons.gridy = 0;
+    layoutCons.gridwidth = 2;
+    layoutCons.weightx = 0.0;
+    layoutCons.weighty = 0.0;
+    layoutCons.fill = GridBagConstraints.BOTH;
+    gridbag.setConstraints(label, layoutCons);
+    contentPane.add(label);
+    
+    cbStoptype = new JComboBox();
+    cbStoptype.setEditable(false);
+    cbStoptype.addItem("bus");
+    cbStoptype.addItem("tram");
+    cbStoptype.addItem("light_rail");
+    cbStoptype.addItem("subway");
+    cbStoptype.addItem("rail");
+    cbStoptype.setActionCommand("gtfsImporter.settingsStoptype");
+    cbStoptype.addActionListener(controller);
+    
+    layoutCons.gridx = 0;
+    layoutCons.gridy = 1;
+    layoutCons.gridwidth = 1;
+    layoutCons.weightx = 0.0;
+    layoutCons.weighty = 0.0;
+    layoutCons.fill = GridBagConstraints.BOTH;
+    gridbag.setConstraints(cbStoptype, layoutCons);
+    contentPane.add(cbStoptype);
+      
+    label = new JLabel("Time on your GPS device");
+      
+    layoutCons.gridx = 0;
+    layoutCons.gridy = 2;
+    layoutCons.gridwidth = 2;
+    layoutCons.weightx = 0.0;
+    layoutCons.weighty = 0.0;
+    layoutCons.fill = GridBagConstraints.BOTH;
+    gridbag.setConstraints(label, layoutCons);
+    contentPane.add(label);
+      
+    tfGPSTimeStart = new JTextField("00:00:00", 15);
+    tfGPSTimeStart.setActionCommand("gtfsImporter.settingsGPSTimeStart");
+    tfGPSTimeStart.addActionListener(controller);
+      
+    layoutCons.gridx = 0;
+    layoutCons.gridy = 3;
+    layoutCons.gridwidth = 1;
+    layoutCons.weightx = 0.0;
+    layoutCons.weighty = 0.0;
+    layoutCons.fill = GridBagConstraints.BOTH;
+    gridbag.setConstraints(tfGPSTimeStart, layoutCons);
+    contentPane.add(tfGPSTimeStart);
+      
+    label = new JLabel("HH:MM:SS.sss");
+      
+    layoutCons.gridx = 1;
+    layoutCons.gridy = 3;
+    layoutCons.gridwidth = 1;
+    layoutCons.weightx = 0.0;
+    layoutCons.weighty = 0.0;
+    layoutCons.fill = GridBagConstraints.BOTH;
+    gridbag.setConstraints(label, layoutCons);
+    contentPane.add(label);
+            
+    label = new JLabel("Time on your stopwatch");
+      
+    layoutCons.gridx = 0;
+    layoutCons.gridy = 4;
+    layoutCons.gridwidth = 2;
+    layoutCons.weightx = 0.0;
+    layoutCons.weighty = 0.0;
+    layoutCons.fill = GridBagConstraints.BOTH;
+    gridbag.setConstraints(label, layoutCons);
+    contentPane.add(label);
+      
+    tfStopwatchStart = new JTextField("00:00:00", 15);
+    tfStopwatchStart.setActionCommand("gtfsImporter.settingsStopwatchStart");
+    tfStopwatchStart.addActionListener(controller);
+      
+    layoutCons.gridx = 0;
+    layoutCons.gridy = 5;
+    layoutCons.gridwidth = 1;
+    layoutCons.weightx = 0.0;
+    layoutCons.weighty = 0.0;
+    layoutCons.fill = GridBagConstraints.BOTH;
+    gridbag.setConstraints(tfStopwatchStart, layoutCons);
+    contentPane.add(tfStopwatchStart);
+      
+    label = new JLabel("HH:MM:SS.sss");
+      
+    layoutCons.gridx = 1;
+    layoutCons.gridy = 5;
+    layoutCons.gridwidth = 1;
+    layoutCons.weightx = 0.0;
+    layoutCons.weighty = 0.0;
+    layoutCons.fill = GridBagConstraints.BOTH;
+    gridbag.setConstraints(label, layoutCons);
+    contentPane.add(label);
+      
+    label = new JLabel("Time window");
+      
+    layoutCons.gridx = 0;
+    layoutCons.gridy = 6;
+    layoutCons.gridwidth = 2;
+    layoutCons.weightx = 0.0;
+    layoutCons.weighty = 0.0;
+    layoutCons.fill = GridBagConstraints.BOTH;
+    gridbag.setConstraints(label, layoutCons);
+    contentPane.add(label);
+      
+    tfTimeWindow = new JTextField("15", 4);
+    tfTimeWindow.setActionCommand("gtfsImporter.settingsTimeWindow");
+    tfTimeWindow.addActionListener(controller);
+      
+    layoutCons.gridx = 0;
+    layoutCons.gridy = 7;
+    layoutCons.gridwidth = 1;
+    layoutCons.weightx = 0.0;
+    layoutCons.weighty = 0.0;
+    layoutCons.fill = GridBagConstraints.BOTH;
+    gridbag.setConstraints(tfTimeWindow, layoutCons);
+    contentPane.add(tfTimeWindow);
+      
+    label = new JLabel("seconds");
+      
+    layoutCons.gridx = 1;
+    layoutCons.gridy = 7;
+    layoutCons.gridwidth = 1;
+    layoutCons.weightx = 0.0;
+    layoutCons.weighty = 0.0;
+    layoutCons.fill = GridBagConstraints.BOTH;
+    gridbag.setConstraints(label, layoutCons);
+    contentPane.add(label);
+      
+    label = new JLabel("Move Threshold");
+      
+    layoutCons.gridx = 0;
+    layoutCons.gridy = 8;
+    layoutCons.gridwidth = 2;
+    layoutCons.weightx = 0.0;
+    layoutCons.weighty = 0.0;
+    layoutCons.fill = GridBagConstraints.BOTH;
+    gridbag.setConstraints(label, layoutCons);
+    contentPane.add(label);
+      
+    tfThreshold = new JTextField("20", 4);
+    tfThreshold.setActionCommand("gtfsImporter.settingsThreshold");
+    tfThreshold.addActionListener(controller);
+      
+    layoutCons.gridx = 0;
+    layoutCons.gridy = 9;
+    layoutCons.gridwidth = 1;
+    layoutCons.weightx = 0.0;
+    layoutCons.weighty = 0.0;
+    layoutCons.fill = GridBagConstraints.BOTH;
+    gridbag.setConstraints(tfThreshold, layoutCons);
+    contentPane.add(tfThreshold);
+      
+    label = new JLabel("meters");
+      
+    layoutCons.gridx = 1;
+    layoutCons.gridy = 9;
+    layoutCons.gridwidth = 1;
+    layoutCons.weightx = 0.0;
+    layoutCons.weighty = 0.0;
+    layoutCons.fill = GridBagConstraints.BOTH;
+    gridbag.setConstraints(label, layoutCons);
+    contentPane.add(label);
+      
+    JButton bSuggestStops = new JButton("Suggest Stops");
+    bSuggestStops.setActionCommand("gtfsImporter.settingsSuggestStops");
+    bSuggestStops.addActionListener(controller);
+      
+    layoutCons.gridx = 0;
+    layoutCons.gridy = 10;
+    layoutCons.gridwidth = 3;
+    layoutCons.weightx = 1.0;
+    layoutCons.weighty = 0.0;
+    layoutCons.fill = GridBagConstraints.BOTH;
+    gridbag.setConstraints(bSuggestStops, layoutCons);
+    contentPane.add(bSuggestStops);
+      
+    //Waypoints Tab
+    contentPane = tabWaypoints;
+    gridbag = new GridBagLayout();
+    layoutCons = new GridBagConstraints();
+    contentPane.setLayout(gridbag);
+    contentPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put
+        (KeyStroke.getKeyStroke("alt N"), "gtfsImporter.gtfsStopsFocusAdd");
+    contentPane.getActionMap().put
+	("gtfsImporter.gtfsStopsFocusAdd", controller.getFocusAddAction());
+/*    contentPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put
+        (KeyStroke.getKeyStroke("alt S"), "gtfsImporter.focusShelterYes");
+    contentPane.getActionMap().put
+	("gtfsImporter.focusShelterYes",
+	 controller.getFocusWaypointShelterAction("yes"));
+    contentPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put
+        (KeyStroke.getKeyStroke("alt T"), "gtfsImporter.focusShelterNo");
+    contentPane.getActionMap().put
+	("gtfsImporter.focusShelterNo",
+	 controller.getFocusWaypointShelterAction("no"));
+    contentPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put
+        (KeyStroke.getKeyStroke("alt U"), "gtfsImporter.focusShelterImplicit");
+    contentPane.getActionMap().put
+	("gtfsImporter.focusShelterImplicit",
+	 controller.getFocusWaypointShelterAction("implicit"));
+    contentPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put
+        (KeyStroke.getKeyStroke("alt D"), "gtfsImporter.gtfsStopsDelete");
+    contentPane.getActionMap().put
+	("gtfsImporter.gtfsStopsDelete",
+	 controller.getFocusWaypointDeleteAction());*/
+      
+    gtfsStopTable = new JTable();
+    JScrollPane tableSP = new JScrollPane(gtfsStopTable);
+    
+    layoutCons.gridx = 0;
+    layoutCons.gridy = 0;
+    layoutCons.gridwidth = 4;
+    layoutCons.weightx = 1.0;
+    layoutCons.weighty = 1.0;
+    layoutCons.fill = GridBagConstraints.BOTH;
+    gridbag.setConstraints(tableSP, layoutCons);
+    contentPane.add(tableSP);
+      
+    JButton bFind = new JButton("Find");
+    bFind.setActionCommand("gtfsImporter.gtfsStopsFind");
+    bFind.addActionListener(controller);
+      
+    layoutCons.gridx = 0;
+    layoutCons.gridy = 1;
+    layoutCons.gridwidth = 1;
+    layoutCons.weightx = 1.0;
+    layoutCons.weighty = 0.0;
+    layoutCons.fill = GridBagConstraints.BOTH;
+    gridbag.setConstraints(bFind, layoutCons);
+    contentPane.add(bFind);
+      
+    JButton bShow = new JButton("Show");
+    bShow.setActionCommand("gtfsImporter.gtfsStopsShow");
+    bShow.addActionListener(controller);
+      
+    layoutCons.gridx = 0;
+    layoutCons.gridy = 2;
+    layoutCons.gridwidth = 1;
+    layoutCons.weightx = 1.0;
+    layoutCons.weighty = 0.0;
+    layoutCons.fill = GridBagConstraints.BOTH;
+    gridbag.setConstraints(bShow, layoutCons);
+    contentPane.add(bShow);
+      
+    JButton bMark = new JButton("Mark");
+    bMark.setActionCommand("gtfsImporter.gtfsStopsMark");
+    bMark.addActionListener(controller);
+      
+    layoutCons.gridx = 1;
+    layoutCons.gridy = 1;
+    layoutCons.gridheight = 2;
+    layoutCons.gridwidth = 1;
+    layoutCons.weightx = 1.0;
+    layoutCons.weighty = 0.0;
+    layoutCons.fill = GridBagConstraints.BOTH;
+    gridbag.setConstraints(bMark, layoutCons);
+    contentPane.add(bMark);
+      
+    JButton bCatch = new JButton("Catch");
+    bCatch.setActionCommand("gtfsImporter.gtfsStopsCatch");
+    bCatch.addActionListener(controller);
+      
+    layoutCons.gridx = 2;
+    layoutCons.gridy = 1;
+    layoutCons.gridheight = 1;
+    layoutCons.gridwidth = 1;
+    layoutCons.weightx = 1.0;
+    layoutCons.weighty = 0.0;
+    layoutCons.fill = GridBagConstraints.BOTH;
+    gridbag.setConstraints(bCatch, layoutCons);
+    contentPane.add(bCatch);
+      
+    JButton bJoin = new JButton("Join");
+    bJoin.setActionCommand("gtfsImporter.gtfsStopsJoin");
+    bJoin.addActionListener(controller);
+      
+    layoutCons.gridx = 2;
+    layoutCons.gridy = 2;
+    layoutCons.gridheight = 1;
+    layoutCons.gridwidth = 1;
+    layoutCons.weightx = 1.0;
+    layoutCons.weighty = 0.0;
+    layoutCons.fill = GridBagConstraints.BOTH;
+    gridbag.setConstraints(bJoin, layoutCons);
+    contentPane.add(bJoin);
+      
+    JButton bAdd = new JButton("Enable");
+    bAdd.setActionCommand("gtfsImporter.gtfsStopsAdd");
+    bAdd.addActionListener(controller);
+      
+    layoutCons.gridx = 3;
+    layoutCons.gridy = 1;
+    layoutCons.gridheight = 1;
+    layoutCons.gridwidth = 1;
+    layoutCons.weightx = 1.0;
+    layoutCons.weighty = 0.0;
+    layoutCons.fill = GridBagConstraints.BOTH;
+    gridbag.setConstraints(bAdd, layoutCons);
+    contentPane.add(bAdd);
+      
+    JButton bDelete = new JButton("Disable");
+    bDelete.setActionCommand("gtfsImporter.gtfsStopsDelete");
+    bDelete.addActionListener(controller);
+      
+    layoutCons.gridx = 3;
+    layoutCons.gridy = 2;
+    layoutCons.gridwidth = 1;
+    layoutCons.weightx = 1.0;
+    layoutCons.weighty = 0.0;
+    layoutCons.fill = GridBagConstraints.BOTH;
+    gridbag.setConstraints(bDelete, layoutCons);
+    contentPane.add(bDelete);
+      
+    jDialog.pack();
+    jDialog.setLocationRelativeTo(frame);
+  }
+  
+  public void setTrackValid(boolean valid)
+  {
+    tabbedPane.setEnabledAt(2, valid);
+  }
+  
+  public void setVisible(boolean visible)
+  {
+    jDialog.setVisible(visible);
+  }
+  
+  public void setSettings
+      (String gpsSyncTime, String stopwatchStart,
+       double timeWindow, double threshold)
+  {
+    tfGPSTimeStart.setText(gpsSyncTime);
+    tfStopwatchStart.setText(stopwatchStart);
+    tfTimeWindow.setText(Double.toString(timeWindow));
+    tfThreshold.setText(Double.toString(threshold));
+  }
+  
+  public String getStoptype()
+  {
+    return (String)cbStoptype.getSelectedItem();
+  }
+  
+  public boolean gpsTimeStartValid()
+  {
+    if (parseTime(tfGPSTimeStart.getText()) >= 0)
+    {
+      return true;
+    }
+    else
+    {
+      JOptionPane.showMessageDialog
+	  (null, "Can't parse a time from this string.", "Invalid value",
+	   JOptionPane.ERROR_MESSAGE);
+      return false;
+    }
+  }
+  
+  public String getGpsTimeStart()
+  {
+    return tfGPSTimeStart.getText();
+  }
+  
+  public void setGpsTimeStart(String s)
+  {
+    tfGPSTimeStart.setText(s);
+  }
+  
+  public boolean stopwatchStartValid()
+  {
+    if (parseTime(tfStopwatchStart.getText()) >= 0)
+    {
+      return true;
+    }
+    else
+    {
+      JOptionPane.showMessageDialog
+	  (null, "Can't parse a time from this string.", "Invalid value",
+	   JOptionPane.ERROR_MESSAGE);
+      return false;
+    }
+  }
+  
+  public String getStopwatchStart()
+  {
+    return tfStopwatchStart.getText();
+  }
+  
+  public void setStopwatchStart(String s)
+  {
+    tfStopwatchStart.setText(s);
+  }
+  
+  public double getTimeWindow()
+  {
+    return Double.parseDouble(tfTimeWindow.getText());
+  }
+  
+  public double getThreshold()
+  {
+    return Double.parseDouble(tfThreshold.getText());
+  }
+  
+  public JTable getGTFSStopTable()
+  {
+    return gtfsStopTable;
+  }
+  
+  public void setGTFSStopTableModel(GTFSStopTableModel model)
+  {
+    gtfsStopTable.setModel(model);
+    int width = gtfsStopTable.getPreferredSize().width;
+    gtfsStopTable.getColumnModel().getColumn(0).setPreferredWidth((int)(width * 0.3));
+    gtfsStopTable.getColumnModel().getColumn(1).setPreferredWidth((int)(width * 0.6));
+    gtfsStopTable.getColumnModel().getColumn(2).setPreferredWidth((int)(width * 0.1));
+  }
+  
+  public static double parseTime(String s)
+  {
+    double result = 0;
+    if ((s.charAt(2) != ':') || (s.charAt(2) != ':')
+	 || (s.length() < 8))
+      return -1;
+    int hour = Integer.parseInt(s.substring(0, 2));
+    int minute = Integer.parseInt(s.substring(3, 5));
+    double second = Double.parseDouble(s.substring(6, s.length()));
+    if ((hour < 0) || (hour > 23) || (minute < 0) || (minute > 59)
+	 || (second < 0) || (second >= 60.0))
+      return -1;
+    return (second + minute*60 + hour*60*60);
+  }
+  
+/*  private class TracksLSL implements ListSelectionListener
+  {
+    GTFSImporterAction root = null;
+    
+    public TracksLSL(GTFSImporterAction sia)
+    {
+      root = sia;
+    }
+    
+    public void valueChanged(ListSelectionEvent e)
+    {
+      int selectedPos = tracksList.getAnchorSelectionIndex();
+      if (tracksList.isSelectedIndex(selectedPos))
+	root.tracksSelectionChanged(selectedPos);
+      else
+	root.tracksSelectionChanged(-1);
+    }
+  };*/
+}
Index: /applications/editors/josm/plugins/public_transport/src/public_transport/GTFSJoinCommand.java
===================================================================
--- /applications/editors/josm/plugins/public_transport/src/public_transport/GTFSJoinCommand.java	(revision 22148)
+++ /applications/editors/josm/plugins/public_transport/src/public_transport/GTFSJoinCommand.java	(revision 22148)
@@ -0,0 +1,110 @@
+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.Iterator;
+import java.util.Vector;
+import javax.swing.JLabel;
+
+public class GTFSJoinCommand extends Command
+{
+  private Vector< Integer > workingLines = null;
+  private Node undoMapNode = null;
+  private Node undoTableNode = null;
+  private GTFSStopTableModel gtfsStopTM = null;
+  private String type = null;
+  
+  public GTFSJoinCommand(GTFSImporterAction controller)
+  {
+    gtfsStopTM = controller.getGTFSStopTableModel();
+    workingLines = new Vector< Integer >();
+    
+    // use either selected lines or all lines if no line is selected
+    int[] selectedLines = controller.getDialog().getGTFSStopTable().getSelectedRows();
+    if (selectedLines.length != 1)
+      return;
+    workingLines.add(selectedLines[0]);
+  }
+  
+  public boolean executeCommand()
+  {
+    if (workingLines.size() != 1)
+      return false;
+    Node dest = null;
+    Iterator< Node > iter =
+        Main.main.getCurrentDataSet().getSelectedNodes().iterator();
+    int j = workingLines.elementAt(0);
+    while (iter.hasNext())
+    {
+      Node n = iter.next();
+      if ((n != null) && (n.equals(gtfsStopTM.nodes.elementAt(j))))
+	continue;
+      if (dest != null)
+	return false;
+      dest = n;
+    }
+    if (dest == null)
+      return false;
+    undoMapNode = new Node(dest);
+    
+    Node node = gtfsStopTM.nodes.elementAt(j);
+    undoTableNode = node;
+    if (node != null)
+    {
+      Main.main.getCurrentDataSet().removePrimitive(node);
+      node.setDeleted(true);
+    }
+    
+    dest.put("highway", "bus_stop");
+    dest.put("stop_id", (String)gtfsStopTM.getValueAt(j, 0));
+    if (dest.get("name") == null)
+      dest.put("name", (String)gtfsStopTM.getValueAt(j, 1));
+    gtfsStopTM.nodes.set(j, dest);
+    type = (String)gtfsStopTM.getValueAt(j, 2);
+    gtfsStopTM.setValueAt("moved", j, 2);
+    
+    return true;
+  }
+  
+  public void undoCommand()
+  {
+    if (workingLines.size() != 1)
+      return;
+    int j = workingLines.elementAt(0);
+    
+    Node node = gtfsStopTM.nodes.elementAt(j);
+    if (node != null)
+    {
+      Main.main.getCurrentDataSet().removePrimitive(node);
+      node.setDeleted(true);
+    }
+    
+    if (undoMapNode != null)
+    {
+      undoMapNode.setDeleted(false);
+      Main.main.getCurrentDataSet().addPrimitive(undoMapNode);
+    }
+    if (undoTableNode != null)
+    {
+      undoTableNode.setDeleted(false);
+      Main.main.getCurrentDataSet().addPrimitive(undoTableNode);
+    }
+    gtfsStopTM.nodes.set(j, undoTableNode);
+    gtfsStopTM.setValueAt(type, j, 2);
+  }
+  
+  public void fillModifiedData
+    (Collection< OsmPrimitive > modified, Collection< OsmPrimitive > deleted,
+     Collection< OsmPrimitive > added)
+  {
+  }
+  
+  @Override public JLabel getDescription()
+  {
+    return new JLabel("public_transport.GTFSStops.Join");
+  }
+};
Index: /applications/editors/josm/plugins/public_transport/src/public_transport/GTFSStopTableModel.java
===================================================================
--- /applications/editors/josm/plugins/public_transport/src/public_transport/GTFSStopTableModel.java	(revision 22148)
+++ /applications/editors/josm/plugins/public_transport/src/public_transport/GTFSStopTableModel.java	(revision 22148)
@@ -0,0 +1,191 @@
+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.event.TableModelEvent;
+import javax.swing.event.TableModelListener;
+import javax.swing.table.DefaultTableModel;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.gpx.WayPoint;
+import org.openstreetmap.josm.data.osm.DataSource;
+import org.openstreetmap.josm.data.osm.Node;
+
+public class GTFSStopTableModel extends DefaultTableModel
+      implements TableModelListener
+{
+  private GTFSImporterAction controller = null;
+  public Vector< Node > nodes = new Vector< Node >();
+  public Vector< LatLon > coors = new Vector< LatLon >();
+  private int idCol = -1;
+  private int nameCol = -1;
+  private int latCol = -1;
+  private int lonCol = -1;
+  
+  public GTFSStopTableModel(GTFSImporterAction controller,
+			    String columnConfig)
+  {
+    int pos = columnConfig.indexOf(',');
+    int oldPos = 0;
+    int i = 0;
+    while (pos > -1)
+    {
+      String title = columnConfig.substring(oldPos, pos);
+      if ("stop_id".equals(title))
+	idCol = i;
+      else if ("stop_name".equals(title))
+	nameCol = i;
+      else if ("stop_lat".equals(title))
+	latCol = i;
+      else if ("stop_lon".equals(title))
+	lonCol = i;
+      ++i;
+      oldPos = pos + 1;
+      pos = columnConfig.indexOf(',', oldPos);
+    }
+    String title = columnConfig.substring(oldPos);
+    if ("stop_id".equals(title))
+      idCol = i;
+    else if ("stop_name".equals(title))
+      nameCol = i;
+    else if ("stop_lat".equals(title))
+      latCol = i;
+    else if ("stop_lon".equals(title))
+      lonCol = i;
+    
+    this.controller = controller;
+    addColumn("Id");
+    addColumn("Name");
+    addColumn("State");
+    addTableModelListener(this);
+  }
+    
+  public boolean isCellEditable(int row, int column)
+  {
+    return false;
+  }
+    
+  public void addRow(Object[] obj)
+  {
+    throw new UnsupportedOperationException();
+  }
+    
+  public void insertRow(int insPos, Object[] obj)
+  {
+    throw new UnsupportedOperationException();
+  }
+    
+  public void addRow(String s)
+  {
+    insertRow(-1, s, new Vector< Node >());
+  }
+  
+  public void addRow(String s, Vector< Node > existingStops)
+  {
+    insertRow(-1, s, existingStops);
+  }
+  
+  public void insertRow(int insPos, String s, Vector< Node > existingStops)
+  {
+    String[] buf = { "", "", "pending" };
+    int pos = s.indexOf(',');
+    int oldPos = 0;
+    int i = 0;
+    double lat = 0;
+    double lon = 0;
+    while (pos > -1)
+    {
+      if (i == idCol)
+	buf[0] = s.substring(oldPos, pos);
+      else if (i == nameCol)
+	buf[1] = s.substring(oldPos, pos);
+      else if (i == latCol)
+	lat = Double.parseDouble(s.substring(oldPos, pos));
+      else if (i == lonCol)
+	lon = Double.parseDouble(s.substring(oldPos, pos));
+      ++i;
+      oldPos = pos + 1;
+      pos = s.indexOf(',', oldPos);
+    }
+    if (i == idCol)
+      buf[0] = s.substring(oldPos);
+    else if (i == nameCol)
+      buf[1] = s.substring(oldPos);
+    else if (i == latCol)
+      lat = Double.parseDouble(s.substring(oldPos));
+    else if (i == lonCol)
+      lon = Double.parseDouble(s.substring(oldPos));
+    
+    LatLon coor = new LatLon(lat, lon);
+    
+    if (Main.main.getCurrentDataSet() != null)
+    {
+      boolean inside = false;
+      Iterator< DataSource > iter =
+          Main.main.getCurrentDataSet().dataSources.iterator();
+      while (iter.hasNext())
+      {
+	if (iter.next().bounds.contains(coor))
+	{
+	  inside = true;
+	  break;
+	}
+      }
+      if (!inside)
+	buf[2] = "outside";
+    }
+    
+    boolean nearBusStop = false;
+    Iterator< Node > iter = existingStops.iterator();
+    while (iter.hasNext())
+    {
+      Node node = iter.next();
+      if (coor.greatCircleDistance(node.getCoor()) < 1000)
+      {
+	nearBusStop = true;
+	break;
+      }
+    }
+    
+    if (insPos == -1)
+    {
+      if ((nearBusStop) || !("pending".equals(buf[2])))
+	nodes.addElement(null);
+      else
+      {
+	Node node = GTFSImporterAction.createNode(coor, buf[0], buf[1]);
+	nodes.addElement(node);
+	buf[2] = "added";
+      }
+      coors.addElement(coor);
+      super.addRow(buf);
+    }
+    else
+    {
+      if ((nearBusStop) || !("pending".equals(buf[2])))
+	nodes.insertElementAt(null, insPos);
+      else
+      {
+	Node node = GTFSImporterAction.createNode(coor, buf[0], buf[1]);
+	nodes.insertElementAt(node, insPos);
+	buf[2] = "added";
+      }
+      coors.insertElementAt(coor, insPos);
+      super.insertRow(insPos, buf);
+    }
+  }
+    
+  public void clear()
+  {
+    nodes.clear();
+    super.setRowCount(0);
+  }
+  
+  public void tableChanged(TableModelEvent e)
+  {
+  }
+};
Index: /applications/editors/josm/plugins/public_transport/src/public_transport/PublicTransportPlugin.java
===================================================================
--- /applications/editors/josm/plugins/public_transport/src/public_transport/PublicTransportPlugin.java	(revision 22147)
+++ /applications/editors/josm/plugins/public_transport/src/public_transport/PublicTransportPlugin.java	(revision 22148)
@@ -44,4 +44,5 @@
     jMenu.add(new JMenuItem(new StopImporterAction()));
     jMenu.add(new JMenuItem(new RoutePatternAction()));
+    jMenu.add(new JMenuItem(new GTFSImporterAction()));
     setEnabledAll(true);
   }
