Index: trunk/src/org/openstreetmap/josm/gui/download/BoundingBoxSelection.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/download/BoundingBoxSelection.java	(revision 2360)
+++ trunk/src/org/openstreetmap/josm/gui/download/BoundingBoxSelection.java	(revision 2361)
@@ -39,6 +39,7 @@
 
     private JTextField[] latlon = null;
-    final JTextArea osmUrl = new JTextArea();
-    final JTextArea showUrl = new JTextArea();
+    private final JTextArea osmUrl = new JTextArea();
+    private final JTextArea showUrl = new JTextArea();
+    private DownloadDialog parent;
 
     
@@ -54,5 +55,5 @@
         latlon[0].addActionListener(latChecker);
 
-        latChecker = new LatValueChecker(latlon[3]);
+        latChecker = new LatValueChecker(latlon[2]);
         latlon[2].addFocusListener(latChecker);
         latlon[2].addActionListener(latChecker);
@@ -72,11 +73,5 @@
         JPanel dlg = new JPanel(new GridBagLayout());
 
-        class osmUrlRefresher implements DocumentListener {
-            public void changedUpdate(DocumentEvent e) { parseURL(gui); }
-            public void insertUpdate(DocumentEvent e) { parseURL(gui); }
-            public void removeUpdate(DocumentEvent e) { parseURL(gui); }
-        }
-
-        osmUrl.getDocument().addDocumentListener(new osmUrlRefresher());
+        osmUrl.getDocument().addDocumentListener(new OsmUrlRefresher());
 
         // select content on receiving focus. this seems to be the default in the
@@ -104,4 +99,5 @@
 
         gui.addDownloadAreaSelector(dlg, tr("Bounding Box"));
+        this.parent = gui;
     }
 
@@ -184,4 +180,5 @@
                 return;            
             }
+            setErrorMessage(null);
         }
         
@@ -225,4 +222,5 @@
                 return;
             }
+            setErrorMessage(null);
         }
         
@@ -248,3 +246,9 @@
         }        
     }
+    
+    class OsmUrlRefresher implements DocumentListener {
+        public void changedUpdate(DocumentEvent e) { parseURL(parent); }
+        public void insertUpdate(DocumentEvent e) { parseURL(parent); }
+        public void removeUpdate(DocumentEvent e) { parseURL(parent); }
+    }
 }
Index: trunk/src/org/openstreetmap/josm/gui/download/PlaceSelection.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/download/PlaceSelection.java	(revision 2360)
+++ trunk/src/org/openstreetmap/josm/gui/download/PlaceSelection.java	(revision 2361)
@@ -3,21 +3,28 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
+import java.awt.BorderLayout;
 import java.awt.Component;
-import java.awt.Cursor;
 import java.awt.Dimension;
+import java.awt.GridBagConstraints;
 import java.awt.GridBagLayout;
+import java.awt.Insets;
 import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.net.HttpURLConnection;
 import java.net.URL;
-
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import javax.swing.AbstractAction;
+import javax.swing.BorderFactory;
+import javax.swing.DefaultListSelectionModel;
 import javax.swing.JButton;
-import javax.swing.JComponent;
 import javax.swing.JLabel;
-import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.JScrollPane;
@@ -25,8 +32,13 @@
 import javax.swing.JTextField;
 import javax.swing.ListSelectionModel;
+import javax.swing.UIManager;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
 import javax.swing.event.ListSelectionEvent;
 import javax.swing.event.ListSelectionListener;
-import javax.swing.table.DefaultTableCellRenderer;
+import javax.swing.table.DefaultTableColumnModel;
 import javax.swing.table.DefaultTableModel;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.table.TableColumn;
 import javax.xml.parsers.SAXParserFactory;
 
@@ -34,5 +46,9 @@
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.coor.LatLon;
-import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+import org.openstreetmap.josm.gui.ExceptionDialogUtil;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.io.OsmTransferException;
+import org.openstreetmap.josm.tools.ImageProvider;
 import org.xml.sax.Attributes;
 import org.xml.sax.InputSource;
@@ -42,163 +58,46 @@
 public class PlaceSelection implements DownloadSelection {
 
-    private JTextField searchTerm = new JTextField();
-    private JButton submitSearch = new JButton(tr("Search..."));
-    private DefaultTableModel searchResults = new DefaultTableModel() {
-        @Override public boolean isCellEditable(int row, int col) { return false; }
-    };
-    private JTable searchResultDisplay = new JTable(searchResults);
-    private boolean updatingSelf;
-
-    /**
-     * Data storage for search results.
-     */
-    class SearchResult
-    {
-        public String name;
-        public String type;
-        public String nearestPlace;
-        public String description;
-        public double lat;
-        public double lon;
-        public int zoom;
-    }
-
-    /**
-     * A very primitive parser for the name finder's output.
-     * Structure of xml described here:  http://wiki.openstreetmap.org/index.php/Name_finder
-     *
-     */
-    private class Parser extends DefaultHandler
-    {
-        private SearchResult currentResult = null;
-        private StringBuffer description = null;
-        private int depth = 0;
-        /**
-         * Detect starting elements.
-         *
-         */
-        @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException
-        {
-            depth++;
-            try
-            {
-                if (qName.equals("searchresults"))
-                {
-                    searchResults.setRowCount(0);
-                }
-                else if (qName.equals("named") && (depth == 2))
-                {
-                    currentResult = new PlaceSelection.SearchResult();
-                    currentResult.name = atts.getValue("name");
-                    currentResult.type = atts.getValue("info");
-                    currentResult.lat = Double.parseDouble(atts.getValue("lat"));
-                    currentResult.lon = Double.parseDouble(atts.getValue("lon"));
-                    currentResult.zoom = Integer.parseInt(atts.getValue("zoom"));
-                    searchResults.addRow(new Object[] { currentResult, currentResult, currentResult, currentResult });
-                }
-                else if (qName.equals("description") && (depth == 3))
-                {
-                    description = new StringBuffer();
-                }
-                else if (qName.equals("named") && (depth == 4))
-                {
-                    // this is a "named" place in the nearest places list.
-                    String info = atts.getValue("info");
-                    if ("city".equals(info) || "town".equals(info) || "village".equals(info)) {
-                        currentResult.nearestPlace = atts.getValue("name");
-                    }
-                }
-            }
-            catch (NumberFormatException x)
-            {
-                x.printStackTrace(); // SAXException does not chain correctly
-                throw new SAXException(x.getMessage(), x);
-            }
-            catch (NullPointerException x)
-            {
-                x.printStackTrace(); // SAXException does not chain correctly
-                throw new SAXException(tr("Null pointer exception, possibly some missing tags."), x);
-            }
-        }
-        /**
-         * Detect ending elements.
-         */
-        @Override public void endElement(String namespaceURI, String localName, String qName) throws SAXException
-        {
-
-            if (qName.equals("searchresults"))
-            {
-            }
-            else if (qName.equals("description") && description != null)
-            {
-                currentResult.description = description.toString();
-                description = null;
-            }
-            depth--;
-
-        }
-        /**
-         * Read characters for description.
-         */
-        @Override public void characters(char[] data, int start, int length) throws org.xml.sax.SAXException
-        {
-            if (description != null)
-            {
-                description.append(data, start, length);
-            }
-        }
-    }
-
-    /**
-     * This queries David Earl's server. Needless to say, stuff should be configurable, and
-     * error handling improved.
-     */
-    public void queryServer(final JComponent component)
-    {
-        final Cursor oldCursor = component.getCursor();
-
-        // had to put this in a thread as it wouldn't update the cursor properly before.
-        Runnable r = new Runnable() {
-            public void run() {
-                try
-                {
-                    String searchtext = searchTerm.getText();
-                    if(searchtext.length()==0)
-                    {
-                        JOptionPane.showMessageDialog(
-                                Main.parent,
-                                tr("Please enter a search string"),
-                                tr("Information"),
-                                JOptionPane.INFORMATION_MESSAGE
-                        );
-                    }
-                    else
-                    {
-                        component.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
-                        component.repaint();
-                        URL url = new URL("http://gazetteer.openstreetmap.org/namefinder/search.xml?find="
-                                +java.net.URLEncoder.encode(searchTerm.getText(), "UTF-8"));
-                        HttpURLConnection activeConnection = (HttpURLConnection)url.openConnection();
-                        //System.out.println("got return: "+activeConnection.getResponseCode());
-                        activeConnection.setConnectTimeout(15000);
-                        InputStream inputStream = activeConnection.getInputStream();
-                        InputSource inputSource = new InputSource(new InputStreamReader(inputStream, "UTF-8"));
-                        SAXParserFactory.newInstance().newSAXParser().parse(inputSource, new Parser());
-                    }
-                }
-                catch (Exception x)
-                {
-                    x.printStackTrace();
-                    JOptionPane.showMessageDialog(
-                            Main.parent,
-                            tr("Cannot read place search results from server"),
-                            tr("Error"),
-                            JOptionPane.ERROR_MESSAGE
-                    );
-                }
-                component.setCursor(oldCursor);
-            }
-        };
-        new Thread(r).start();
+    private JTextField tfSearchExpression;
+    private JButton btnSearch;
+    private NamedResultTableModel model;
+    private JTable tblSearchResults;
+    private DownloadDialog parent;
+  
+    protected JPanel buildSearchPanel() {
+        JPanel panel = new JPanel();
+        panel.setLayout(new GridBagLayout());
+        GridBagConstraints gc = new GridBagConstraints();
+        
+        // the label for the search field 
+        //
+        gc.gridwidth = 2;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx  =1.0;
+        gc.insets = new Insets(5, 5, 0, 5);
+        panel.add(new JLabel(tr("Enter a place name to search for:")), gc);
+        
+        // the search expression field
+        //
+        tfSearchExpression = new JTextField();
+        tfSearchExpression.setToolTipText(tr("Enter a place name to search for"));
+        gc.gridx = 0;
+        gc.gridy = 1;
+        gc.gridwidth = 1;
+        panel.add(tfSearchExpression,  gc);
+
+        // the search button
+        //
+        SearchAction searchAction = new SearchAction();
+        btnSearch = new JButton(searchAction);
+        tfSearchExpression.getDocument().addDocumentListener(searchAction);
+        tfSearchExpression.addActionListener(searchAction);
+
+        gc.gridx = 1;
+        gc.gridy = 1;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.weightx = 0.0;
+        panel.add(btnSearch,  gc);
+   
+        return panel;   
     }
 
@@ -210,126 +109,400 @@
     public void addGui(final DownloadDialog gui) {
         JPanel panel = new JPanel();
-        panel.setLayout(new GridBagLayout());
-
-        // this is manually tuned so that it looks nice on a GNOME
-        // desktop - maybe needs some cross platform proofing.
-        panel.add(new JLabel(tr("Enter a place name to search for:")), GBC.eol().insets(5, 5, 5, 5));
-        panel.add(searchTerm, GBC.std().fill(GBC.HORIZONTAL).insets(5, 0, 5, 4));
-        panel.add(submitSearch, GBC.eol().insets(5, 0, 5, 5));
-        Dimension btnSize = submitSearch.getPreferredSize();
-        btnSize.setSize(btnSize.width, btnSize.height * 0.8);
-        submitSearch.setPreferredSize(btnSize);
-
-        GBC c = GBC.std().fill().insets(5, 0, 5, 5);
-        c.gridwidth = 2;
-        JScrollPane scrollPane = new JScrollPane(searchResultDisplay);
+        panel.setLayout(new BorderLayout());
+        panel.add(buildSearchPanel(), BorderLayout.NORTH);
+
+        DefaultListSelectionModel selectionModel = new DefaultListSelectionModel();
+        model = new NamedResultTableModel(selectionModel);
+        tblSearchResults = new JTable(model, new NamedResultTableColumnModel());
+        tblSearchResults.setSelectionModel(selectionModel);
+        JScrollPane scrollPane = new JScrollPane(tblSearchResults);
         scrollPane.setPreferredSize(new Dimension(200,200));
-        panel.add(scrollPane, c);
-        gui.addDownloadAreaSelector(panel, tr("Places"));
-
-        scrollPane.setPreferredSize(scrollPane.getPreferredSize());
-
-        // when the button is clicked
-        submitSearch.addActionListener(new ActionListener() {
-            public void actionPerformed(ActionEvent e) {
-                queryServer(gui.getRootPane());
-            }
-        });
-
-        searchTerm.addActionListener(new ActionListener() {
-            public void actionPerformed(ActionEvent e) {
-                queryServer(gui.getRootPane());
-            }
-        });
-
-        searchResults.addColumn(tr("name"));
-        searchResults.addColumn(tr("type"));
-        searchResults.addColumn(tr("near"));
-        searchResults.addColumn(tr("zoom"));
-
-        // TODO - this is probably not the coolest way to set relative sizes?
-        searchResultDisplay.getColumn(tr("name")).setPreferredWidth(200);
-        searchResultDisplay.getColumn(tr("type")).setPreferredWidth(100);
-        searchResultDisplay.getColumn(tr("near")).setPreferredWidth(100);
-        searchResultDisplay.getColumn(tr("zoom")).setPreferredWidth(50);
-        searchResultDisplay.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
-
-        // display search results in a table. for simplicity, the table contains
-        // the same SearchResult object in each of the four columns, but it is rendered
-        // differently depending on the column.
-        searchResultDisplay.setDefaultRenderer(Object.class, new DefaultTableCellRenderer() {
-            @Override public Component getTableCellRendererComponent(JTable table, Object value,
-                    boolean isSelected, boolean hasFocus, int row, int column) {
-                super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
-                if (value != null) {
-                    SearchResult sr = (SearchResult) value;
-                    switch(column) {
-                    case 0:
-                        setText(sr.name);
-                        break;
-                    case 1:
-                        setText(sr.type);
-                        break;
-                    case 2:
-                        setText(sr.nearestPlace);
-                        break;
-                    case 3:
-                        setText(Integer.toString(sr.zoom));
-                        break;
-                    }
-                    setToolTipText("<html>"+((SearchResult)value).description+"</html>");
-                }
-                return this;
-            }
-        });
-
-        // if item is selected in list, notify dialog
-        searchResultDisplay.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
-            public void valueChanged(ListSelectionEvent lse) {
-                if (lse.getValueIsAdjusting()) return;
-                SearchResult r = null;
-                try
-                {
-                    r = (SearchResult) searchResults.getValueAt(lse.getFirstIndex(), 0);
-                }
-                catch (Exception x)
-                {
-                    // Ignore
-                }
-                if (r != null)
-                {
-                    double size = 180.0 / Math.pow(2, r.zoom);
-                    Bounds b = new Bounds(
-                        new LatLon(
-                            r.lat - size / 2,
-                            r.lat + size / 2
-                         ),
-                         new LatLon(
-                            r.lon - size,
-                            r.lon + size
-                         )
-                    );
-                    gui.boundingBoxChanged(b,null);
-                }
-            }
-        });
-
-        // TODO - we'd like to finish the download dialog upon double-click but
-        // don't know how to bypass the JOptionPane in which the whole thing is
-        // displayed.
-        searchResultDisplay.addMouseListener(new MouseAdapter() {
+        panel.add(scrollPane, BorderLayout.CENTER);
+        
+        gui.addDownloadAreaSelector(panel, tr("Areas around Places"));
+
+        scrollPane.setPreferredSize(scrollPane.getPreferredSize());        
+        tblSearchResults.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+        tblSearchResults.getSelectionModel().addListSelectionListener(new ListSelectionHandler());
+        tblSearchResults.addMouseListener(new MouseAdapter() {
             @Override public void mouseClicked(MouseEvent e) {
                 if (e.getClickCount() > 1) {
-                    if (searchResultDisplay.getSelectionModel().getMinSelectionIndex() > -1) {
-                        // add sensible action here.
+                    SearchResult sr = model.getSelectedSearchResult();
+                    if (sr == null) return;
+                    parent.startDownload(sr.getDownloadArea());
+                }
+            }
+        });        
+        parent = gui;
+    }
+
+    public void setDownloadArea(Bounds area) {
+       tblSearchResults.clearSelection();
+    }
+        
+    /**
+     * Data storage for search results.
+     */
+    static private class SearchResult {
+        public String name;
+        public String info;
+        public String nearestPlace;
+        public String description;
+        public double lat;
+        public double lon;
+        public int zoom;
+        public int osmId;
+        public OsmPrimitiveType type;
+        
+        public Bounds getDownloadArea() {
+            double size = 180.0 / Math.pow(2, zoom);
+            Bounds b = new Bounds(
+                    new LatLon(lat - size / 2, lon - size), 
+                    new LatLon(lat + size / 2, lon+ size)
+                    );
+            return b;
+        }
+    }
+    
+    
+    /**
+     * A very primitive parser for the name finder's output.
+     * Structure of xml described here:  http://wiki.openstreetmap.org/index.php/Name_finder
+     *
+     */
+    private class NameFinderResultParser extends DefaultHandler {
+        private SearchResult currentResult = null;
+        private StringBuffer description = null;
+        private int depth = 0;
+        private List<SearchResult> data = new LinkedList<SearchResult>();
+
+        /**
+         * Detect starting elements.
+         * 
+         */
+        @Override
+        public void startElement(String namespaceURI, String localName, String qName, Attributes atts)
+                throws SAXException {
+            depth++;
+            try {
+                if (qName.equals("searchresults")) {
+                    // do nothing
+                } else if (qName.equals("named") && (depth == 2)) {
+                    currentResult = new PlaceSelection.SearchResult();
+                    currentResult.name = atts.getValue("name");
+                    currentResult.info = atts.getValue("info");
+                    currentResult.lat = Double.parseDouble(atts.getValue("lat"));
+                    currentResult.lon = Double.parseDouble(atts.getValue("lon"));
+                    currentResult.zoom = Integer.parseInt(atts.getValue("zoom"));
+                    currentResult.osmId = Integer.parseInt(atts.getValue("id"));
+                    currentResult.type = OsmPrimitiveType.from(atts.getValue("type"));
+                    data.add(currentResult);
+                } else if (qName.equals("description") && (depth == 3)) {
+                    description = new StringBuffer();
+                } else if (qName.equals("named") && (depth == 4)) {
+                    // this is a "named" place in the nearest places list.
+                    String info = atts.getValue("info");
+                    if ("city".equals(info) || "town".equals(info) || "village".equals(info)) {
+                        currentResult.nearestPlace = atts.getValue("name");
                     }
                 }
-            }
-        });
-
-    }
-
-    public void setDownloadArea(Bounds area) {
-       searchResultDisplay.clearSelection();
+            } catch (NumberFormatException x) {
+                x.printStackTrace(); // SAXException does not chain correctly
+                throw new SAXException(x.getMessage(), x);
+            } catch (NullPointerException x) {
+                x.printStackTrace(); // SAXException does not chain correctly
+                throw new SAXException(tr("Null pointer exception, possibly some missing tags."), x);
+            }
+        }
+
+        /**
+         * Detect ending elements.
+         */
+        @Override
+        public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
+            if (qName.equals("searchresults")) {
+            } else if (qName.equals("description") && description != null) {
+                currentResult.description = description.toString();
+                description = null;
+            }
+            depth--;
+
+        }
+
+        /**
+         * Read characters for description.
+         */
+        @Override
+        public void characters(char[] data, int start, int length) throws org.xml.sax.SAXException {
+            if (description != null) {
+                description.append(data, start, length);
+            }
+        }
+        
+        public List<SearchResult> getResult() {
+            return data;
+        }
+    }
+    
+    class SearchAction extends AbstractAction implements DocumentListener {
+
+        public SearchAction() {
+            putValue(NAME, tr("Search ..."));
+            putValue(SMALL_ICON, ImageProvider.get("dialogs","search"));
+            putValue(SHORT_DESCRIPTION, tr("Click to start searching for places"));
+            updateEnabledState();
+        }
+        
+        public void actionPerformed(ActionEvent e) {
+            if (!isEnabled() || tfSearchExpression.getText().trim().length() == 0)
+                return;
+            NameQueryTask task = new NameQueryTask(tfSearchExpression.getText());
+            Main.worker.submit(task);
+        }
+        
+        protected void updateEnabledState() {
+            setEnabled(tfSearchExpression.getText().trim().length() > 0);
+        }
+
+        public void changedUpdate(DocumentEvent e) {
+            updateEnabledState();
+        }
+
+        public void insertUpdate(DocumentEvent e) {
+            updateEnabledState();
+        }
+
+        public void removeUpdate(DocumentEvent e) {
+            updateEnabledState();
+        }
+    }
+    
+    
+    class NameQueryTask extends PleaseWaitRunnable {
+        
+        private String searchExpression;
+        private HttpURLConnection connection;
+        private List<SearchResult> data;
+        private boolean canceled = false;
+        private Exception lastException;
+        
+        public NameQueryTask(String searchExpression) {
+            super(tr("Querying name server"),false /* don't ignore exceptions */);
+            this.searchExpression = searchExpression;
+        }
+        
+        
+        @Override
+        protected void cancel() {
+            this.canceled = true;
+            synchronized (this) {
+                if (connection != null) {
+                    connection.disconnect();                    
+                }                
+            }            
+        }
+
+        @Override
+        protected void finish() {
+            if (canceled) 
+                return;
+            if (lastException != null) {
+                ExceptionDialogUtil.explainException(lastException);
+                return;
+            }
+            model.setData(this.data);            
+        }
+
+        @Override
+        protected void realRun() throws SAXException, IOException, OsmTransferException {            
+            try {
+                getProgressMonitor().indeterminateSubTask(tr("Querying name server ..."));
+                    URL url = new URL("http://gazetteer.openstreetmap.org/namefinder/search.xml?find="
+                            +java.net.URLEncoder.encode(searchExpression, "UTF-8"));
+                    synchronized(this) {
+                        connection = (HttpURLConnection)url.openConnection();
+                    }
+                    connection.setConnectTimeout(15000);
+                    InputStream inputStream = connection.getInputStream();
+                    InputSource inputSource = new InputSource(new InputStreamReader(inputStream, "UTF-8"));
+                    NameFinderResultParser parser = new NameFinderResultParser();
+                    SAXParserFactory.newInstance().newSAXParser().parse(inputSource, parser);
+                    this.data = parser.getResult();
+            } catch(Exception e) {
+                if (canceled) {
+                    // ignore exception 
+                    return;
+                }
+                lastException = e;
+            }
+        }
+    }
+    
+    class NamedResultTableModel extends DefaultTableModel {
+        private ArrayList<SearchResult> data;
+        private ListSelectionModel selectionModel;
+        
+        public NamedResultTableModel(ListSelectionModel selectionModel) {
+            data = new ArrayList<SearchResult>();
+            this.selectionModel = selectionModel;
+        }
+        @Override
+        public int getRowCount() {
+            if (data == null) return 0;
+            return data.size();
+        }
+
+        @Override
+        public Object getValueAt(int row, int column) {
+            if (data == null) return null;
+            return data.get(row);
+        }
+        
+        public void setData(List<SearchResult> data) {
+            if (data == null) {
+                this.data.clear();
+            } else {
+                this.data  =new ArrayList<SearchResult>(data);
+            }
+            fireTableDataChanged();
+        }
+        @Override
+        public boolean isCellEditable(int row, int column) {
+            return false; 
+        }
+        
+        public SearchResult getSelectedSearchResult() {
+            if (selectionModel.getMinSelectionIndex() < 0) {
+                return null;
+            }
+            return data.get(selectionModel.getMinSelectionIndex());
+        }
+    }
+    
+    class NamedResultTableColumnModel extends DefaultTableColumnModel {
+        protected void createColumns() {
+            TableColumn col = null;
+            NamedResultCellRenderer renderer = new NamedResultCellRenderer();
+
+            // column 0 - Name
+            col = new TableColumn(0);
+            col.setHeaderValue(tr("Name"));
+            col.setResizable(true);
+            col.setPreferredWidth(200);
+            col.setCellRenderer(renderer);
+            addColumn(col);
+            
+            // column 1 - Version
+            col = new TableColumn(1);
+            col.setHeaderValue(tr("Type"));
+            col.setResizable(true);
+            col.setPreferredWidth(100);
+            col.setCellRenderer(renderer);
+            addColumn(col);
+            
+            // column 2 - Near
+            col = new TableColumn(2);
+            col.setHeaderValue(tr("Near"));
+            col.setResizable(true);
+            col.setPreferredWidth(100);
+            col.setCellRenderer(renderer);
+            addColumn(col);
+            
+
+            // column 3 - Zoom
+            col = new TableColumn(3);
+            col.setHeaderValue(tr("Zoom"));
+            col.setResizable(true);
+            col.setPreferredWidth(50);
+            col.setCellRenderer(renderer);
+            addColumn(col);
+        }
+
+        public NamedResultTableColumnModel() {
+            createColumns();
+        }
+    }
+    
+    class ListSelectionHandler implements ListSelectionListener {
+        public void valueChanged(ListSelectionEvent lse) {
+            SearchResult r = null;
+            try {
+                r = (SearchResult) model.getValueAt(lse.getFirstIndex(), 0);
+            } catch (Exception x) {
+                // Ignore
+            }
+            if (r != null) {
+                parent.boundingBoxChanged(r.getDownloadArea(), PlaceSelection.this);
+            }
+        }
+    }
+    
+    class NamedResultCellRenderer extends JLabel implements TableCellRenderer {
+        public NamedResultCellRenderer() {
+            setOpaque(true);
+            setBorder(BorderFactory.createEmptyBorder(2,2,2,2));
+        }
+        
+        protected void reset() {
+            setText("");
+            setIcon(null);
+        }
+        
+        protected void renderColor(boolean selected) {
+            if (selected) {
+                setForeground(UIManager.getColor("Table.selectionForeground"));
+                setBackground(UIManager.getColor("Table.selectionBackground"));
+            } else {
+                setForeground(UIManager.getColor("Table.foreground"));
+                setBackground(UIManager.getColor("Table.background"));                
+            }
+        }
+        
+        protected String lineWrapDescription(String description) {
+            StringBuffer ret = new StringBuffer();
+            StringBuffer line = new StringBuffer();
+            StringTokenizer tok = new StringTokenizer(description, " ");
+            while(tok.hasMoreElements()) {
+                String t = tok.nextToken();
+                if (line.length() == 0) {
+                    line.append(t);
+                } else if (line.length() < 80) {
+                    line.append(" ").append(t);
+                } else {
+                    line.append(" ").append(t).append("<br>");
+                    ret.append(line);
+                    line = new StringBuffer();
+                }
+            }
+            ret.insert(0, "<html>");
+            ret.append("</html>");
+            return ret.toString();
+        }
+        
+        public Component getTableCellRendererComponent(JTable table, Object value,
+                boolean isSelected, boolean hasFocus, int row, int column) {
+            
+            reset();
+            renderColor(isSelected);
+            
+            if (value == null) return this;
+            SearchResult sr = (SearchResult) value;
+            switch(column) {
+            case 0:
+                setText(sr.name);
+                break;
+            case 1:
+                setText(sr.info);
+                break;
+            case 2:
+                setText(sr.nearestPlace);
+                break;
+            case 3:
+                setText(Integer.toString(sr.zoom));
+                break;
+            }
+            setToolTipText(lineWrapDescription(sr.description));
+            return this;
+        }
     }
 }
Index: trunk/src/org/openstreetmap/josm/io/OsmServerBackreferenceReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmServerBackreferenceReader.java	(revision 2360)
+++ trunk/src/org/openstreetmap/josm/io/OsmServerBackreferenceReader.java	(revision 2361)
@@ -83,5 +83,4 @@
      *
      * @param id  the id of the primitive. > 0 expected
-     * @param type the type of the primitive. Must not be null.
      * @param readFull true, if referers should be read fully (i.e. including their immediate children)
      *
