Changeset 3102 in josm


Ignore:
Timestamp:
Mar 10, 2010 10:00:20 AM (3 years ago)
Author:
Gubaer
Message:

fixed #4651: Ability to download incomplete relation from selection
fixed #4098: Popup Menu entry "download relation members" in relation dialog should be "download incomplete relation members"
fixed two NPEs in RelationListDialog and SelectionListDialog
refactored SelectionListDialog to support better user feedback (enabled/disabled buttons and menu items)
Finally removed the sort() method on DataSet, marked as FIXME since a long time.

CAVEAT: DataSet.getSelected() now returns an unmodifiable list instead of a copy of the selection list. This may lead to UnsupportedOperationExceptions in the next few days. I tried to make sure the JOSM core uses getSelected() only for reading, but I didn't check the plugins.

Location:
trunk/src/org/openstreetmap/josm
Files:
9 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/actions/mapmode/DrawAction.java

    r2692 r3102  
    324324        Collection<OsmPrimitive> selection = ds.getSelected(); 
    325325        Collection<Command> cmds = new LinkedList<Command>(); 
    326         Collection<OsmPrimitive> newSelection = ds.getSelected(); 
     326        Collection<OsmPrimitive> newSelection = new LinkedList<OsmPrimitive>(ds.getSelected()); 
    327327 
    328328        ArrayList<Way> reuseWays = new ArrayList<Way>(), 
  • trunk/src/org/openstreetmap/josm/actions/search/SearchAction.java

    r2912 r3102  
    5555        if (!isEnabled()) 
    5656            return; 
    57         SearchSetting s = lastSearch; 
    58         if (s == null) { 
    59             s = new SearchSetting(); 
    60         } 
    61         SearchSetting se = showSearchDialog(s); 
    62         if(se != null) { 
    63             searchWithHistory(se); 
    64         } 
     57        search(); 
    6558    } 
    6659 
     
    7467 
    7568    public static SearchSetting showSearchDialog(SearchSetting initialValues) { 
    76  
     69        if (initialValues == null) { 
     70            initialValues = new SearchSetting(); 
     71        } 
    7772        // -- prepare the combo box with the search expressions 
    7873        // 
     
    178173 
    179174    /** 
     175     * Launches the dialog for specifying search criteria and runs 
     176     * a search 
     177     */ 
     178    public static void search() { 
     179        SearchSetting se = showSearchDialog(lastSearch); 
     180        if(se != null) { 
     181            searchWithHistory(se); 
     182        } 
     183    } 
     184 
     185    /** 
    180186     * Adds the search specified by the settings in <code>s</code> to the 
    181187     * search history and performs the search. 
  • trunk/src/org/openstreetmap/josm/data/osm/DataSet.java

    r3008 r3102  
    99import java.util.Collection; 
    1010import java.util.Collections; 
    11 import java.util.Comparator; 
    1211import java.util.HashMap; 
    1312import java.util.HashSet; 
     
    313312 
    314313    /** 
    315      * Return a list of all selected objects. Even keys are returned. 
    316      * @return List of all selected objects. 
     314     * Replies an unmodifiable collection of primitives currently selected 
     315     * in this dataset 
     316     *  
     317     * @return unmodifiable collection of primitives 
    317318     */ 
    318319    public Collection<OsmPrimitive> getSelected() { 
    319         // It would be nice to have this be a copy-on-write list 
    320         // or an Collections.unmodifiableList().  It would be 
    321         // much faster for large selections.  May users just 
    322         // call this, and only check the .size(). 
    323         return new ArrayList<OsmPrimitive>(selectedPrimitives); 
     320        return Collections.unmodifiableSet(selectedPrimitives); 
    324321    } 
    325322 
     
    610607        } 
    611608        return a; 
    612     } 
    613  
    614     // Provide well-defined sorting for collections of OsmPrimitives. 
    615     // FIXME: probably not a good place to put this code. 
    616     public static OsmPrimitive[] sort(Collection<? extends OsmPrimitive> list) { 
    617         OsmPrimitive[] selArr = new OsmPrimitive[list.size()]; 
    618         final HashMap<Object, String> h = new HashMap<Object, String>(); 
    619         selArr = list.toArray(selArr); 
    620         Arrays.sort(selArr, new Comparator<OsmPrimitive>() { 
    621             public int compare(OsmPrimitive a, OsmPrimitive b) { 
    622                 if (a.getClass() == b.getClass()) { 
    623                     String as = h.get(a); 
    624                     if (as == null) { 
    625                         as = a.getName() != null ? a.getName() : Long.toString(a.getId()); 
    626                         h.put(a, as); 
    627                     } 
    628                     String bs = h.get(b); 
    629                     if (bs == null) { 
    630                         bs = b.getName() != null ? b.getName() : Long.toString(b.getId()); 
    631                         h.put(b, bs); 
    632                     } 
    633                     int res = as.compareTo(bs); 
    634                     if (res != 0) 
    635                         return res; 
    636                 } 
    637                 return a.compareTo(b); 
    638             } 
    639         }); 
    640         return selArr; 
    641609    } 
    642610 
  • trunk/src/org/openstreetmap/josm/data/osm/Relation.java

    r3032 r3102  
    398398        } 
    399399    } 
     400 
     401    /** 
     402     * Replies true if at least one child primitive is incomplete 
     403     *  
     404     * @return true if at least one child primitive is incomplete 
     405     */ 
     406    public boolean hasIncompleteMembers() { 
     407        for (RelationMember rm: members) { 
     408            if (rm.getMember().isIncomplete()) return true; 
     409        } 
     410        return false; 
     411    } 
     412 
     413    /** 
     414     * Replies a collection with the incomplete children this relation 
     415     * refers to 
     416     *  
     417     * @return the incomplete children. Empty collection if no children are incomplete. 
     418     */ 
     419    public Collection<OsmPrimitive> getIncompleteMembers() { 
     420        Set<OsmPrimitive> ret = new HashSet<OsmPrimitive>(); 
     421        for (RelationMember rm: members) { 
     422            if (!rm.getMember().isIncomplete()) { 
     423                continue; 
     424            } 
     425            ret.add(rm.getMember()); 
     426        } 
     427        return ret; 
     428    } 
    400429} 
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/MapPaintVisitor.java

    r2982 r3102  
    1717import java.util.Comparator; 
    1818import java.util.LinkedList; 
     19import java.util.List; 
    1920 
    2021import org.openstreetmap.josm.Main; 
     
    674675            Collection<Way> noAreaWays = new LinkedList<Way>(); 
    675676 
     677            List<Relation> relations = data.searchRelations(bbox); 
    676678            /*** RELATIONS ***/ 
    677679            for (final Relation osm: data.searchRelations(bbox)) { 
  • trunk/src/org/openstreetmap/josm/gui/dialogs/RelationListDialog.java

    r3020 r3102  
    1515import java.util.Comparator; 
    1616import java.util.HashSet; 
     17import java.util.Iterator; 
    1718import java.util.List; 
    1819import java.util.Set; 
     
    5253import org.openstreetmap.josm.gui.SideButton; 
    5354import org.openstreetmap.josm.gui.MapView.LayerChangeListener; 
     55import org.openstreetmap.josm.gui.dialogs.relation.DownloadRelationMemberTask; 
    5456import org.openstreetmap.josm.gui.dialogs.relation.DownloadRelationTask; 
    5557import org.openstreetmap.josm.gui.dialogs.relation.RelationEditor; 
     
    234236 
    235237        @Override public void mouseClicked(MouseEvent e) { 
     238            if (Main.main.getEditLayer() == null) return; 
    236239            if (e.getClickCount() == 2 && SwingUtilities.isLeftMouseButton(e)) { 
    237240                if (e.isControlDown()) { 
     
    254257        } 
    255258        @Override public void mousePressed(MouseEvent e) { 
     259            if (Main.main.getEditLayer() == null) return; 
    256260            if (e.isPopupTrigger()) { 
    257261                openPopup(e); 
     
    259263        } 
    260264        @Override public void mouseReleased(MouseEvent e) { 
     265            if (Main.main.getEditLayer() == null) return; 
    261266            if (e.isPopupTrigger()) { 
    262267                openPopup(e); 
     
    507512                    Main.map.mapView.getEditLayer()) 
    508513            ); 
     514        } 
     515    } 
     516 
     517    /** 
     518     * Action for downloading incomplete members of selected relations 
     519     *  
     520     */ 
     521    class DownloadSelectedIncompleteMembersAction extends AbstractAction implements ListSelectionListener{ 
     522        public DownloadSelectedIncompleteMembersAction() { 
     523            putValue(SHORT_DESCRIPTION, tr("Download incomplete members of selected relations")); 
     524            putValue(SMALL_ICON, ImageProvider.get("dialogs/relation", "downloadincompleteselected")); 
     525            putValue(NAME, tr("Download incomplete members")); 
     526            updateEnabledState(); 
     527        } 
     528 
     529        public Set<OsmPrimitive> buildSetOfIncompleteMembers(List<Relation> rels) { 
     530            Set<OsmPrimitive> ret = new HashSet<OsmPrimitive>(); 
     531            for(Relation r: rels) { 
     532                ret.addAll(r.getIncompleteMembers()); 
     533            } 
     534            return ret; 
     535        } 
     536 
     537        public void actionPerformed(ActionEvent e) { 
     538            if (!isEnabled()) 
     539                return; 
     540            List<Relation> rels = model.getSelectedRelationsWithIncompleteMembers(); 
     541            if (rels.isEmpty()) return; 
     542            Main.worker.submit(new DownloadRelationMemberTask( 
     543                    rels, 
     544                    buildSetOfIncompleteMembers(rels), 
     545                    Main.map.mapView.getEditLayer() 
     546            )); 
     547        } 
     548 
     549        protected void updateEnabledState() { 
     550            setEnabled(!model.getSelectedRelationsWithIncompleteMembers().isEmpty()); 
     551        } 
     552 
     553        public void valueChanged(ListSelectionEvent e) { 
     554            updateEnabledState(); 
    509555        } 
    510556    } 
     
    623669        } 
    624670 
     671        /** 
     672         * Replies the list of selected relations with incomplete members 
     673         *  
     674         * @return the list of selected relations with incomplete members 
     675         */ 
     676        public List<Relation> getSelectedRelationsWithIncompleteMembers() { 
     677            List<Relation> ret = getSelectedNonNewRelations(); 
     678            Iterator<Relation> it = ret.iterator(); 
     679            while(it.hasNext()) { 
     680                Relation r = it.next(); 
     681                if (!r.hasIncompleteMembers()) { 
     682                    it.remove(); 
     683                } 
     684            } 
     685            return ret; 
     686        } 
     687 
    625688        public Object getElementAt(int index) { 
    626689            return relations.get(index); 
     
    715778            add(downloadMembersAction); 
    716779 
     780            // -- download incomplete members action 
     781            // 
     782            DownloadSelectedIncompleteMembersAction downloadSelectedIncompleteMembers = new DownloadSelectedIncompleteMembersAction(); 
     783            displaylist.addListSelectionListener(downloadSelectedIncompleteMembers); 
     784            add(downloadSelectedIncompleteMembers); 
     785 
     786            addSeparator(); 
     787 
    717788            // -- select members action 
    718789            // 
  • trunk/src/org/openstreetmap/josm/gui/dialogs/SelectionListDialog.java

    r3101 r3102  
    22package org.openstreetmap.josm.gui.dialogs; 
    33 
    4 import static org.openstreetmap.josm.tools.I18n.marktr; 
    54import static org.openstreetmap.josm.tools.I18n.tr; 
    65import static org.openstreetmap.josm.tools.I18n.trn; 
     
    87import java.awt.BorderLayout; 
    98import java.awt.Color; 
     9import java.awt.Component; 
     10import java.awt.GridLayout; 
    1011import java.awt.Rectangle; 
    1112import java.awt.event.ActionEvent; 
    1213import java.awt.event.ActionListener; 
    1314import java.awt.event.KeyEvent; 
    14 import java.awt.event.MouseAdapter; 
    1515import java.awt.event.MouseEvent; 
     16import java.util.ArrayList; 
    1617import java.util.Collection; 
    1718import java.util.Collections; 
     19import java.util.Comparator; 
     20import java.util.HashSet; 
    1821import java.util.LinkedList; 
    19 import java.util.NoSuchElementException; 
     22import java.util.List; 
     23import java.util.Set; 
    2024 
    2125import javax.swing.AbstractAction; 
     26import javax.swing.AbstractListModel; 
    2227import javax.swing.BorderFactory; 
    23 import javax.swing.DefaultListModel; 
     28import javax.swing.DefaultListSelectionModel; 
     29import javax.swing.JButton; 
    2430import javax.swing.JList; 
    2531import javax.swing.JMenuItem; 
     
    2935import javax.swing.ListSelectionModel; 
    3036import javax.swing.SwingConstants; 
     37import javax.swing.event.ListDataEvent; 
     38import javax.swing.event.ListDataListener; 
     39import javax.swing.event.ListSelectionEvent; 
     40import javax.swing.event.ListSelectionListener; 
    3141import javax.swing.plaf.basic.BasicArrowButton; 
    3242 
    3343import org.openstreetmap.josm.Main; 
    3444import org.openstreetmap.josm.actions.AutoScaleAction; 
    35 import org.openstreetmap.josm.actions.search.SearchAction; 
    3645import org.openstreetmap.josm.actions.search.SearchAction.SearchSetting; 
    3746import org.openstreetmap.josm.data.SelectionChangedListener; 
    38 import org.openstreetmap.josm.data.osm.DataSet; 
     47import org.openstreetmap.josm.data.osm.NameFormatter; 
    3948import org.openstreetmap.josm.data.osm.Node; 
    4049import org.openstreetmap.josm.data.osm.OsmPrimitive; 
    4150import org.openstreetmap.josm.data.osm.Relation; 
    4251import org.openstreetmap.josm.data.osm.Way; 
     52import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent; 
     53import org.openstreetmap.josm.data.osm.event.DataChangedEvent; 
     54import org.openstreetmap.josm.data.osm.event.DataSetListener; 
     55import org.openstreetmap.josm.data.osm.event.DatasetEventManager; 
     56import org.openstreetmap.josm.data.osm.event.NodeMovedEvent; 
     57import org.openstreetmap.josm.data.osm.event.PrimitivesAddedEvent; 
     58import org.openstreetmap.josm.data.osm.event.PrimitivesRemovedEvent; 
     59import org.openstreetmap.josm.data.osm.event.RelationMembersChangedEvent; 
    4360import org.openstreetmap.josm.data.osm.event.SelectionEventManager; 
     61import org.openstreetmap.josm.data.osm.event.TagsChangedEvent; 
     62import org.openstreetmap.josm.data.osm.event.WayNodesChangedEvent; 
    4463import org.openstreetmap.josm.data.osm.event.DatasetEventManager.FireMode; 
    4564import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor; 
     65import org.openstreetmap.josm.gui.DefaultNameFormatter; 
    4666import org.openstreetmap.josm.gui.MapView; 
    4767import org.openstreetmap.josm.gui.OsmPrimitivRenderer; 
    4868import org.openstreetmap.josm.gui.SideButton; 
    4969import org.openstreetmap.josm.gui.MapView.EditLayerChangeListener; 
     70import org.openstreetmap.josm.gui.dialogs.relation.DownloadRelationMemberTask; 
    5071import org.openstreetmap.josm.gui.layer.OsmDataLayer; 
     72import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher; 
    5173import org.openstreetmap.josm.tools.ImageProvider; 
    5274import org.openstreetmap.josm.tools.Shortcut; 
    5375 
    5476/** 
    55  * A small tool dialog for displaying the current selection. The selection manager 
    56  * respects clicks into the selection list. Ctrl-click will remove entries from 
    57  * the list while single click will make the clicked entry the only selection. 
    58  * 
    59  * @author imi 
     77 * A small tool dialog for displaying the current selection. 
     78 *  
    6079 */ 
    61 public class SelectionListDialog extends ToggleDialog implements SelectionChangedListener, MapView.EditLayerChangeListener { 
    62  
    63     private static final int SELECTION_HISTORY_SIZE = 10; 
    64  
    65     /** 
    66      * The selection's list data. 
    67      */ 
    68     private final DefaultListModel list = new DefaultListModel(); 
    69  
    70     private LinkedList<Collection<? extends OsmPrimitive>> selectionHistory; 
    71  
    72     /** 
    73      * The display list. 
    74      */ 
    75     private JList displaylist = new JList(list); 
    76     private SideButton selectButton; 
    77     private SideButton searchButton; 
    78     private JPopupMenu popupMenu; 
    79     private JMenuItem zoomToElement; 
     80public class SelectionListDialog extends ToggleDialog  { 
     81 
     82    private JList lstPrimitives; 
     83    private SelectionListModel model; 
    8084 
    8185    private SelectAction actSelect; 
    82  
    83     /** 
    84      * If the selection changed event is triggered with newSelection equals 
    85      * this element, the newSelection will not be added to the selection history 
    86      */ 
    87     private Collection<? extends OsmPrimitive> historyIgnoreSelection = null; 
    88  
    89     public SelectionListDialog() { 
    90         super(tr("Current Selection"), "selectionlist", tr("Open a selection list window."), 
    91                 Shortcut.registerShortcut("subwindow:selection", tr("Toggle: {0}", tr("Current Selection")), KeyEvent.VK_T, Shortcut.GROUP_LAYER, Shortcut.SHIFT_DEFAULT), 150, true); 
    92  
    93         selectionHistory = new LinkedList<Collection<? extends OsmPrimitive>>(); 
    94         popupMenu = new JPopupMenu(); 
    95         displaylist.setCellRenderer(new OsmPrimitivRenderer()); 
    96         displaylist.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); 
    97         displaylist.addMouseListener(new MouseAdapter() { 
    98             @Override 
    99             public void mouseClicked(MouseEvent e) { 
    100                 if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1) { 
    101                     updateMap(); 
    102                 } 
    103             } 
    104  
    105             @Override 
    106             public void mousePressed(MouseEvent e) { 
    107                 showPopupMenu(e); 
    108             } 
    109  
    110             @Override 
    111             public void mouseReleased(MouseEvent e) { 
    112                 showPopupMenu(e); 
    113             } 
    114  
    115         }); 
    116  
    117         add(new JScrollPane(displaylist), BorderLayout.CENTER); 
    118  
    119         JPanel buttonPanel = getButtonPanel(2); 
    120         selectButton = new SideButton(actSelect = new SelectAction()); 
    121         buttonPanel.add(selectButton); 
     86    private SearchAction actSearch; 
     87    private ZoomToJOSMSelectionAction actZoomToJOSMSelection; 
     88    private ZoomToListSelection actZoomToListSelection; 
     89    private DownloadSelectedIncompleteMembersAction actDownloadSelectedIncompleteMembers; 
     90 
     91    /** 
     92     * Builds the panel with the list of selected OSM primitives 
     93     *  
     94     * @return the panel with the list of selected OSM primitives 
     95     */ 
     96    protected JPanel buildListPanel() { 
     97        JPanel pnl = new JPanel(new BorderLayout()); 
     98        DefaultListSelectionModel selectionModel  = new DefaultListSelectionModel(); 
     99        model = new SelectionListModel(selectionModel); 
     100        lstPrimitives = new JList(model); 
     101        lstPrimitives.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); 
     102        lstPrimitives.setSelectionModel(selectionModel); 
     103        lstPrimitives.setCellRenderer(new OsmPrimitivRenderer()); 
     104        pnl.add(new JScrollPane(lstPrimitives), BorderLayout.CENTER); 
     105 
     106        return pnl; 
     107    } 
     108 
     109    /** 
     110     * Builds the row of action buttons at the bottom of this dialog 
     111     *  
     112     * @return the panel 
     113     */ 
     114    protected JPanel buildActionPanel() { 
     115        JPanel pnl = new  JPanel(new GridLayout(1,2)); 
     116 
     117        // the select action 
     118        final JButton selectButton = new SideButton(actSelect = new SelectAction()); 
     119        lstPrimitives.getSelectionModel().addListSelectionListener(actSelect); 
     120        pnl.add(selectButton); 
    122121        BasicArrowButton selectionHistoryMenuButton = createArrowButton(selectButton); 
    123122        selectionHistoryMenuButton.addActionListener(new ActionListener() { 
    124123            public void actionPerformed(ActionEvent e) { 
    125                 showSelectionHistoryMenu(); 
     124                SelectionHistoryPopup.launch(selectButton, model.getSelectionHistory()); 
    126125            } 
    127126        }); 
    128         add(buttonPanel, BorderLayout.SOUTH); 
    129  
    130         zoomToElement = new JMenuItem(tr("Zoom to selected element(s)")); 
    131         zoomToElement.addActionListener(new ActionListener() { 
    132             public void actionPerformed(ActionEvent e) { 
    133                 zoomToSelectedElement(); 
    134             } 
    135         }); 
    136  
    137         searchButton = new SideButton(marktr("Search"), "search", "SelectionList", tr("Search for objects."), 
    138                 Main.main.menu.search); 
    139         buttonPanel.add(searchButton); 
     127 
     128        // the search button 
     129        final JButton searchButton = new SideButton(actSearch = new SearchAction()); 
     130        pnl.add(searchButton); 
    140131 
    141132        BasicArrowButton searchHistoryMenuButton = createArrowButton(searchButton); 
    142133        searchHistoryMenuButton.addActionListener(new ActionListener() { 
    143134            public void actionPerformed(ActionEvent e) { 
    144                 showSearchHistoryMenu(); 
     135                SearchPopupMenu.launch(searchButton); 
    145136            } 
    146137        }); 
    147138 
    148         popupMenu.add(zoomToElement); 
    149         JMenuItem zoomToSelection = new JMenuItem(tr("Zoom to selection")); 
    150         zoomToSelection.addActionListener(new ActionListener() { 
    151             public void actionPerformed(ActionEvent e) { 
    152                 zoomToSelection(); 
    153             } 
    154         }); 
    155         popupMenu.add(zoomToSelection); 
    156  
    157         if (Main.main.getCurrentDataSet() != null) { 
    158             selectionChanged(Main.main.getCurrentDataSet().getSelected()); 
    159         } 
    160  
     139        return pnl; 
     140    } 
     141 
     142    /** 
     143     * Builds the content panel for this dialog 
     144     *  
     145     * @return the content panel 
     146     */ 
     147    protected JPanel buildContentPanel() { 
     148        JPanel pnl = new JPanel(new BorderLayout()); 
     149        pnl.add(buildListPanel(), BorderLayout.CENTER); 
     150        pnl.add(buildActionPanel(), BorderLayout.SOUTH); 
     151        return pnl; 
     152    } 
     153 
     154    public SelectionListDialog() { 
     155        super(tr("Current Selection"), "selectionlist", tr("Open a selection list window."), 
     156                Shortcut.registerShortcut("subwindow:selection", tr("Toggle: {0}", tr("Current Selection")), KeyEvent.VK_T, Shortcut.GROUP_LAYER, Shortcut.SHIFT_DEFAULT), 
     157                150, // default height 
     158                true // default is "show dialog" 
     159        ); 
     160 
     161        add(buildContentPanel(), BorderLayout.CENTER); 
     162        model.addListDataListener(new TitleUpdater()); 
     163        actZoomToJOSMSelection = new ZoomToJOSMSelectionAction(); 
     164        model.addListDataListener(actZoomToJOSMSelection); 
     165 
     166        actZoomToListSelection = new ZoomToListSelection(); 
     167        lstPrimitives.getSelectionModel().addListSelectionListener(actZoomToListSelection); 
     168 
     169        actDownloadSelectedIncompleteMembers = new DownloadSelectedIncompleteMembersAction(); 
     170        lstPrimitives.getSelectionModel().addListSelectionListener(actDownloadSelectedIncompleteMembers); 
     171 
     172        lstPrimitives.addMouseListener(new SelectionPopupMenuLauncher()); 
    161173    } 
    162174 
    163175    @Override 
    164176    public void showNotify() { 
    165         SelectionEventManager.getInstance().addSelectionListener(this, FireMode.IN_EDT_CONSOLIDATED); 
    166         MapView.addEditLayerChangeListener(this); 
    167         MapView.addEditLayerChangeListener(actSelect); 
    168         updateSelection(); 
     177        MapView.addEditLayerChangeListener(model); 
     178        SelectionEventManager.getInstance().addSelectionListener(model, FireMode.IN_EDT_CONSOLIDATED); 
     179        DatasetEventManager.getInstance().addDatasetListener(model, FireMode.IN_EDT); 
     180        MapView.addEditLayerChangeListener(actSearch); 
    169181    } 
    170182 
    171183    @Override 
    172184    public void hideNotify() { 
    173         SelectionEventManager.getInstance().removeSelectionListener(this); 
    174         MapView.removeEditLayerChangeListener(this); 
    175         MapView.removeEditLayerChangeListener(actSelect); 
    176         updateTitle(0, 0, 0); 
    177     } 
    178  
    179     private BasicArrowButton createArrowButton(SideButton parentButton) { 
     185        MapView.removeEditLayerChangeListener(actSearch); 
     186        MapView.removeEditLayerChangeListener(model); 
     187        SelectionEventManager.getInstance().removeSelectionListener(model); 
     188        DatasetEventManager.getInstance().removeDatasetListener(model); 
     189    } 
     190 
     191    private BasicArrowButton createArrowButton(JButton parentButton) { 
    180192        BasicArrowButton arrowButton = new BasicArrowButton(SwingConstants.SOUTH, null, null, Color.BLACK, null); 
    181193        arrowButton.setBorder(BorderFactory.createEmptyBorder()); 
     
    185197    } 
    186198 
    187     @Override 
    188     public void setVisible(boolean b) { 
    189         super.setVisible(b); 
    190         if (b && Main.main.getCurrentDataSet() != null) { 
    191             selectionChanged(Main.main.getCurrentDataSet().getSelected()); 
    192         } 
    193     } 
    194  
    195     protected void showPopupMenu(MouseEvent e) { 
    196         if (e.isPopupTrigger()) { 
    197             zoomToElement.setVisible(displaylist.getSelectedIndex() >= 0); 
    198             popupMenu.show(e.getComponent(), e.getX(), e.getY()); 
    199         } 
    200     } 
    201  
    202     public void zoomToSelection() { 
    203         new AutoScaleAction("selection").actionPerformed(null); 
    204     } 
    205  
    206     /** 
    207      * Zooms to the element(s) selected in {@link #displaylist} 
    208      */ 
    209     public void zoomToSelectedElement() { 
    210         BoundingXYVisitor box = new BoundingXYVisitor(); 
    211         int[] selected = displaylist.getSelectedIndices(); 
    212         if (selected.length == 0) 
    213             return; 
    214         for (int i = 0; i < selected.length; i++) { 
    215             Object o = list.get(selected[i]); 
    216             if (o instanceof OsmPrimitive) { 
    217                 ((OsmPrimitive) o).visit(box); 
    218             } 
    219         } 
    220         if (box.getBounds() == null) 
    221             return; 
    222         box.enlargeBoundingBox(); 
    223         Main.map.mapView.recalculateCenterScale(box); 
    224     } 
    225  
    226     private void showSelectionHistoryMenu() { 
    227         if (selectionHistory.size() == 0) 
    228             return; 
    229         JPopupMenu historyMenu = new JPopupMenu(); 
    230         for (Collection<? extends OsmPrimitive> sel : selectionHistory) { 
    231             SelectionMenuItem item = new SelectionMenuItem(sel); 
    232             historyMenu.add(item); 
    233         } 
    234         Rectangle r = selectButton.getBounds(); 
    235         historyMenu.show(selectButton, r.x, r.y + r.height); 
    236     } 
    237  
    238     private void showSearchHistoryMenu() { 
    239         if (SearchAction.searchHistory.size() == 0) 
    240             return; 
    241         JPopupMenu historyMenu = new JPopupMenu(); 
    242         for (SearchAction.SearchSetting s : SearchAction.searchHistory) { 
    243             SearchMenuItem item = new SearchMenuItem(s); 
    244             historyMenu.add(item); 
    245         } 
    246         Rectangle r = searchButton.getBounds(); 
    247         historyMenu.show(searchButton, r.x, r.y + r.height); 
    248     } 
    249  
    250     /** 
    251      * Called when the selection in the dataset changed. 
    252      * @param newSelection The new selection array. 
    253      */ 
    254     public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) { 
    255         if (list == null || !isVisible()) 
    256             return; // selection changed may be received in base class constructor before init 
    257         OsmPrimitive selArr[] = DataSet.sort(newSelection); 
    258         list.setSize(selArr.length); 
    259         int i = 0; 
    260         for (OsmPrimitive osm : selArr) { 
    261             list.setElementAt(osm, i++); 
    262         } 
    263  
    264         int ways = 0; 
    265         int nodes = 0; 
    266         int relations = 0; 
    267         for (OsmPrimitive o : newSelection) { 
    268             if (o instanceof Way) { 
    269                 ways++; 
    270             } else if (o instanceof Node) { 
    271                 nodes++; 
    272             } else if (o instanceof Relation) { 
    273                 relations++; 
    274             } 
    275         } 
    276  
    277         updateTitle(nodes, ways, relations); 
    278  
    279         if (selectionHistory != null && newSelection.size() > 0 && !newSelection.equals(historyIgnoreSelection)) { 
    280             historyIgnoreSelection = null; 
    281             try { 
    282                 // Check if the newSelection has already been added to the history 
    283                 Collection<? extends OsmPrimitive> first = selectionHistory.getFirst(); 
    284                 if (first.equals(newSelection)) 
    285                     return; 
    286             } catch (NoSuchElementException e) { 
    287             } 
    288             selectionHistory.addFirst(newSelection); 
    289             while (selectionHistory.size() > SELECTION_HISTORY_SIZE) { 
    290                 selectionHistory.removeLast(); 
    291             } 
    292         } 
    293     } 
    294  
    295     private void updateTitle(int nodes, int ways, int relations) { 
    296         if( (nodes+ways+relations) != 0) { 
    297             setTitle(tr("Sel.: Rel.:{0} / Ways:{1} / Nodes:{2}", relations, ways, nodes)); 
    298         } else { 
    299             setTitle(tr("Selection")); 
    300         } 
    301     } 
    302  
    303     /** 
    304      * Sets the selection of the map to the current selected items. 
    305      */ 
    306     public void updateMap() { 
    307         Collection<OsmPrimitive> sel = new LinkedList<OsmPrimitive>(); 
    308         for (int i = 0; i < list.getSize(); ++i) 
    309             if (displaylist.isSelectedIndex(i)) { 
    310                 sel.add((OsmPrimitive) list.get(i)); 
    311             } 
    312         Main.main.getCurrentDataSet().setSelected(sel); 
     199    /** 
     200     * The popup menu launcher 
     201     */ 
     202    class SelectionPopupMenuLauncher extends PopupMenuLauncher { 
     203        private SelectionPopup popup = new SelectionPopup(); 
     204 
     205        @Override 
     206        public void launch(MouseEvent evt) { 
     207            if (model.getSelected().isEmpty()) { 
     208                int idx = lstPrimitives.locationToIndex(evt.getPoint()); 
     209                if (idx < 0) return; 
     210                model.setSelected(Collections.singleton((OsmPrimitive)model.getElementAt(idx))); 
     211            } 
     212            popup.show(lstPrimitives, evt.getX(), evt.getY()); 
     213        } 
     214    } 
     215 
     216    /** 
     217     * The popup menu for the selection list 
     218     */ 
     219    class SelectionPopup extends JPopupMenu { 
     220        public SelectionPopup() { 
     221            add(actZoomToJOSMSelection); 
     222            add(actZoomToListSelection); 
     223            addSeparator(); 
     224            add(actDownloadSelectedIncompleteMembers); 
     225        } 
     226    } 
     227 
     228    /** 
     229     * Updates the dialog title with a summary of the current JOSM selection 
     230     */ 
     231    class TitleUpdater implements ListDataListener { 
     232        protected void updateTitle() { 
     233            setTitle(model.getJOSMSelectionSummary()); 
     234        } 
     235 
     236        public void contentsChanged(ListDataEvent e) { 
     237            updateTitle(); 
     238        } 
     239 
     240        public void intervalAdded(ListDataEvent e) { 
     241            updateTitle(); 
     242        } 
     243 
     244        public void intervalRemoved(ListDataEvent e) { 
     245            updateTitle(); 
     246        } 
     247    } 
     248 
     249    /** 
     250     * Launches the search dialog 
     251     */ 
     252    class SearchAction extends AbstractAction implements EditLayerChangeListener { 
     253        public SearchAction() { 
     254            putValue(NAME, tr("Search")); 
     255            putValue(SHORT_DESCRIPTION,   tr("Search for objects")); 
     256            putValue(SMALL_ICON, ImageProvider.get("dialogs","select")); 
     257            updateEnabledState(); 
     258        } 
     259 
     260        public void actionPerformed(ActionEvent e) { 
     261            if (!isEnabled()) return; 
     262            org.openstreetmap.josm.actions.search.SearchAction.search(); 
     263        } 
     264 
     265        public void updateEnabledState() { 
     266            setEnabled(Main.main != null && Main.main.getEditLayer() != null); 
     267        } 
     268 
     269        @Override 
     270        public void editLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer) { 
     271            updateEnabledState(); 
     272        } 
     273    } 
     274 
     275    /** 
     276     * Sets the current JOSM selection to the OSM primitives selected in the list 
     277     * of this dialog 
     278     */ 
     279    class SelectAction extends AbstractAction implements ListSelectionListener { 
     280        public SelectAction() { 
     281            putValue(NAME, tr("Select")); 
     282            putValue(SHORT_DESCRIPTION,  tr("Set the selected elements on the map to the selected items in the list above.")); 
     283            putValue(SMALL_ICON, ImageProvider.get("dialogs","select")); 
     284            updateEnabledState(); 
     285        } 
     286 
     287        @Override 
     288        public void actionPerformed(ActionEvent e) { 
     289            Collection<OsmPrimitive> sel = model.getSelected(); 
     290            if (sel.isEmpty())return; 
     291            if (Main.map == null || Main.map.mapView == null || Main.map.mapView.getEditLayer() == null) return; 
     292            Main.map.mapView.getEditLayer().data.setSelected(sel); 
     293        } 
     294 
     295        public void updateEnabledState() { 
     296            setEnabled(!model.getSelected().isEmpty()); 
     297        } 
     298 
     299        public void valueChanged(ListSelectionEvent e) { 
     300            updateEnabledState(); 
     301        } 
     302    } 
     303 
     304    /** 
     305     * The action for zooming to the primitives in the current JOSM selection 
     306     *  
     307     */ 
     308    class ZoomToJOSMSelectionAction extends AbstractAction implements ListDataListener { 
     309 
     310        public ZoomToJOSMSelectionAction() { 
     311            putValue(NAME,tr("Zoom to selection")); 
     312            putValue(SHORT_DESCRIPTION, tr("Zoom to selection")); 
     313            putValue(SMALL_ICON, ImageProvider.get("dialogs/autoscale", "selection")); 
     314            updateEnabledState(); 
     315        } 
     316        @Override 
     317        public void actionPerformed(ActionEvent e) { 
     318            new AutoScaleAction("selection").autoScale(); 
     319        } 
     320 
     321        public void updateEnabledState() { 
     322            setEnabled(model.getSize() > 0); 
     323        } 
     324 
     325        public void contentsChanged(ListDataEvent e) { 
     326            updateEnabledState(); 
     327        } 
     328 
     329        public void intervalAdded(ListDataEvent e) { 
     330            updateEnabledState(); 
     331        } 
     332 
     333        public void intervalRemoved(ListDataEvent e) { 
     334            updateEnabledState(); 
     335        } 
     336    } 
     337 
     338    /** 
     339     * The action for zooming to the primitives which are currently selected in 
     340     * the list displaying the JOSM selection 
     341     *  
     342     */ 
     343    class ZoomToListSelection extends AbstractAction implements ListSelectionListener{ 
     344        public ZoomToListSelection() { 
     345            putValue(NAME, tr("Zoom to selected element(s)")); 
     346            putValue(SHORT_DESCRIPTION, tr("Zoom to selected element(s)")); 
     347            putValue(SMALL_ICON, ImageProvider.get("dialogs/autoscale", "selection")); 
     348            updateEnabledState(); 
     349        } 
     350 
     351        public void actionPerformed(ActionEvent e) { 
     352            BoundingXYVisitor box = new BoundingXYVisitor(); 
     353            Collection<OsmPrimitive> sel = model.getSelected(); 
     354            if (sel.isEmpty()) return; 
     355            box.computeBoundingBox(sel); 
     356            if (box.getBounds() == null) 
     357                return; 
     358            box.enlargeBoundingBox(); 
     359            Main.map.mapView.recalculateCenterScale(box); 
     360        } 
     361 
     362        public void updateEnabledState() { 
     363            setEnabled(!model.getSelected().isEmpty()); 
     364        } 
     365 
     366        public void valueChanged(ListSelectionEvent e) { 
     367            updateEnabledState(); 
     368        } 
     369    } 
     370 
     371    /** 
     372     * The list model for the list of OSM primitives in the current JOSM selection. 
     373     *  
     374     * The model also maintains a history of the last {@see SelectionListModel#SELECTION_HISTORY_SIZE} 
     375     * JOSM selection. 
     376     *  
     377     */ 
     378    static private class SelectionListModel extends AbstractListModel implements EditLayerChangeListener, SelectionChangedListener, DataSetListener{ 
     379 
     380        private static final int SELECTION_HISTORY_SIZE = 10; 
     381 
     382        private final LinkedList<Collection<? extends OsmPrimitive>> history = new LinkedList<Collection<? extends OsmPrimitive>>(); 
     383        private final List<OsmPrimitive> selection = new ArrayList<OsmPrimitive>(); 
     384        private DefaultListSelectionModel selectionModel; 
     385 
     386        /** 
     387         * Constructor 
     388         * @param selectionModel the selection model used in the list 
     389         */ 
     390        public SelectionListModel(DefaultListSelectionModel selectionModel) { 
     391            this.selectionModel = selectionModel; 
     392        } 
     393 
     394        /** 
     395         * Replies a summary of the current JOSM selection 
     396         *  
     397         * @return a summary of the current JOSM selection 
     398         */ 
     399        public String getJOSMSelectionSummary() { 
     400            if (selection.isEmpty()) return tr("Selection"); 
     401            int numNodes = 0; 
     402            int numWays = 0; 
     403            int numRelations = 0; 
     404            for (OsmPrimitive p: selection) { 
     405                switch(p.getType()) { 
     406                case NODE: numNodes++; break; 
     407                case WAY: numWays++; break; 
     408                case RELATION: numRelations++; break; 
     409                } 
     410            } 
     411            return tr("Sel.: Rel.:{0} / Ways:{1} / Nodes:{2}", numRelations, numWays, numNodes); 
     412        } 
     413 
     414        /** 
     415         * Remembers a JOSM selection the history of JOSM selections 
     416         *  
     417         * @param selection the JOSM selection. Ignored if null or empty. 
     418         */ 
     419        public void remember(Collection<? extends OsmPrimitive> selection) { 
     420            if (selection == null)return; 
     421            if (selection.isEmpty())return; 
     422            if (history.isEmpty()) { 
     423                history.add(selection); 
     424                return; 
     425            } 
     426            if (history.getFirst().equals(selection)) return; 
     427            history.addFirst(selection); 
     428            while (history.size() > SELECTION_HISTORY_SIZE) { 
     429                history.removeLast(); 
     430            } 
     431        } 
     432 
     433        /** 
     434         * Replies the history of JOSM selections 
     435         *  
     436         * @return 
     437         */ 
     438        public List<Collection<? extends OsmPrimitive>> getSelectionHistory() { 
     439            return history; 
     440        } 
     441 
     442        @Override 
     443        public Object getElementAt(int index) { 
     444            return selection.get(index); 
     445        } 
     446 
     447        @Override 
     448        public int getSize() { 
     449            return selection.size(); 
     450        } 
     451 
     452        /** 
     453         * Replies the collection of OSM primitives currently selected in the view 
     454         * of this model 
     455         *  
     456         * @return 
     457         */ 
     458        public Collection<OsmPrimitive> getSelected() { 
     459            Set<OsmPrimitive> sel = new HashSet<OsmPrimitive>(); 
     460            for(int i=0; i< getSize();i++) { 
     461                if (selectionModel.isSelectedIndex(i)) { 
     462                    sel.add(selection.get(i)); 
     463                } 
     464            } 
     465            return sel; 
     466        } 
     467 
     468        /** 
     469         * Sets the OSM primitives to be selected in the view of this model 
     470         *  
     471         * @param sel the collection of primitives to select 
     472         */ 
     473        public void setSelected(Collection<OsmPrimitive> sel) { 
     474            selectionModel.clearSelection(); 
     475            if (sel == null) return; 
     476            for (OsmPrimitive p: sel){ 
     477                int i = selection.indexOf(p); 
     478                if (i >= 0){ 
     479                    selectionModel.addSelectionInterval(i, i); 
     480                } 
     481            } 
     482        } 
     483 
     484        @Override 
     485        protected void fireContentsChanged(Object source, int index0, int index1) { 
     486            Collection<OsmPrimitive> sel = getSelected(); 
     487            super.fireContentsChanged(source, index0, index1); 
     488            setSelected(sel); 
     489        } 
     490 
     491        /** 
     492         * Sets the collection of currently selected OSM objects 
     493         *  
     494         * @param selection the collection of currently selected OSM objects 
     495         */ 
     496        public void setJOSMSelection(Collection<? extends OsmPrimitive> selection) { 
     497            this.selection.clear(); 
     498            if (selection == null) { 
     499                fireContentsChanged(this, 0, getSize()); 
     500                return; 
     501            } 
     502            this.selection.addAll(selection); 
     503            sort(); 
     504            fireContentsChanged(this, 0, getSize()); 
     505            remember(selection); 
     506        } 
     507 
     508        /** 
     509         * Sorts the primitives in the list 
     510         */ 
     511        public void sort() { 
     512            Collections.sort( 
     513                    this.selection, 
     514                    new Comparator<OsmPrimitive>() { 
     515                        NameFormatter nf = DefaultNameFormatter.getInstance(); 
     516                        @Override 
     517                        public int compare(OsmPrimitive o1, OsmPrimitive o2) { 
     518 
     519                            if (o1.getType() != o2.getType()) 
     520                                return o1.getType().compareTo(o2.getType()); 
     521                            return o1.getDisplayName(nf).compareTo(o2.getDisplayName(nf)); 
     522                        } 
     523                    } 
     524            ); 
     525        } 
     526 
     527        /** 
     528         * Triggers a refresh of the view for all primitives in {@code toUpdate} 
     529         * which are currently displayed in the view 
     530         *  
     531         * @param toUpdate the collection of primitives to update 
     532         */ 
     533        public void update(Collection<? extends OsmPrimitive> toUpdate) { 
     534            if (toUpdate == null) return; 
     535            if (toUpdate.isEmpty()) return; 
     536            Collection<OsmPrimitive> sel = getSelected(); 
     537            for (OsmPrimitive p: toUpdate){ 
     538                int i = selection.indexOf(p); 
     539                if (i >= 0) { 
     540                    super.fireContentsChanged(this, i,i); 
     541                } 
     542            } 
     543            setSelected(sel); 
     544        } 
     545 
     546        /** 
     547         * Replies the list of selected relations with incomplete members 
     548         *  
     549         * @return the list of selected relations with incomplete members 
     550         */ 
     551        public List<Relation> getSelectedRelationsWithIncompleteMembers() { 
     552            List<Relation> ret = new LinkedList<Relation>(); 
     553            for(int i=0; i<getSize(); i++) { 
     554                if (!selectionModel.isSelectedIndex(i)) { 
     555                    continue; 
     556                } 
     557                OsmPrimitive p = selection.get(i); 
     558                if (! (p instanceof Relation)) { 
     559                    continue; 
     560                } 
     561                if (p.isNew()) { 
     562                    continue; 
     563                } 
     564                Relation r = (Relation)p; 
     565                if (r.hasIncompleteMembers()) { 
     566                    ret.add(r); 
     567                } 
     568            } 
     569            return ret; 
     570        } 
     571 
     572        /* ------------------------------------------------------------------------ */ 
     573        /* interface EditLayerChangeListener                                        */ 
     574        /* ------------------------------------------------------------------------ */ 
     575        public void editLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer) { 
     576            if (newLayer == null) { 
     577                setJOSMSelection(null); 
     578            } else { 
     579                setJOSMSelection(newLayer.data.getSelected()); 
     580            } 
     581        } 
     582 
     583        /* ------------------------------------------------------------------------ */ 
     584        /* interface SelectionChangeListener                                        */ 
     585        /* ------------------------------------------------------------------------ */ 
     586        public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) { 
     587            setJOSMSelection(newSelection); 
     588        } 
     589 
     590        /* ------------------------------------------------------------------------ */ 
     591        /* interface DataSetListener                                                */ 
     592        /* ------------------------------------------------------------------------ */ 
     593        public void dataChanged(DataChangedEvent event) { 
     594            // refresh the whole list 
     595            fireContentsChanged(this, 0, getSize()); 
     596        } 
     597 
     598        public void nodeMoved(NodeMovedEvent event) { 
     599            // may influence the display name of primitives, update the data 
     600            update(event.getPrimitives()); 
     601        } 
     602 
     603        public void otherDatasetChange(AbstractDatasetChangedEvent event) { 
     604            // may influence the display name of primitives, update the data 
     605            update(event.getPrimitives()); 
     606        } 
     607 
     608        public void relationMembersChanged(RelationMembersChangedEvent event) { 
     609            // may influence the display name of primitives, update the data 
     610            update(event.getPrimitives()); 
     611        } 
     612 
     613        public void tagsChanged(TagsChangedEvent event) { 
     614            // may influence the display name of primitives, update the data 
     615            update(event.getPrimitives()); 
     616        } 
     617 
     618        public void wayNodesChanged(WayNodesChangedEvent event) { 
     619            // may influence the display name of primitives, update the data 
     620            update(event.getPrimitives()); 
     621        } 
     622 
     623        @Override 
     624        public void primtivesAdded(PrimitivesAddedEvent event) {/* ignored - handled by SelectionChangeListener */} 
     625        @Override 
     626        public void primtivesRemoved(PrimitivesRemovedEvent event) {/* ignored - handled by SelectionChangeListener*/} 
     627    } 
     628 
     629    /** 
     630     * A specialized {@link JMenuItem} for presenting one entry of the search history 
     631     * 
     632     * @author Jan Peter Stotz 
     633     */ 
     634    protected static class SearchMenuItem extends JMenuItem implements ActionListener { 
     635        protected SearchSetting s; 
     636 
     637        public SearchMenuItem(SearchSetting s) { 
     638            super(s.toString()); 
     639            this.s = s; 
     640            addActionListener(this); 
     641        } 
     642 
     643        public void actionPerformed(ActionEvent e) { 
     644            org.openstreetmap.josm.actions.search.SearchAction.searchWithoutHistory(s); 
     645        } 
     646    } 
     647 
     648    /** 
     649     * The popup menu for the search history entries 
     650     *  
     651     */ 
     652    protected static class SearchPopupMenu extends JPopupMenu { 
     653        static public void launch(Component parent) { 
     654            if (org.openstreetmap.josm.actions.search.SearchAction.searchHistory.isEmpty()) 
     655                return; 
     656            JPopupMenu menu = new SearchPopupMenu(); 
     657            Rectangle r = parent.getBounds(); 
     658            menu.show(parent, r.x, r.y + r.height); 
     659        } 
     660 
     661        public SearchPopupMenu() { 
     662            for (SearchSetting ss: org.openstreetmap.josm.actions.search.SearchAction.searchHistory) { 
     663                add(new SearchMenuItem(ss)); 
     664            } 
     665        } 
    313666    } 
    314667 
     
    318671     * @author Jan Peter Stotz 
    319672     */ 
    320     protected class SelectionMenuItem extends JMenuItem implements ActionListener { 
     673    protected static class SelectionMenuItem extends JMenuItem implements ActionListener { 
    321674        protected Collection<? extends OsmPrimitive> sel; 
    322675 
     
    336689                } 
    337690            } 
    338             String text = ""; 
     691            StringBuffer text = new StringBuffer(); 
    339692            if(ways != 0) { 
    340                 text += (text.length() > 0 ? ", " : "") 
    341                 + trn("{0} way", "{0} ways", ways, ways); 
     693                text.append(text.length() > 0 ? ", " : "") 
     694                .append(trn("{0} way", "{0} ways", ways, ways)); 
    342695            } 
    343696            if(nodes != 0) { 
    344                 text += (text.length() > 0 ? ", " : "") 
    345                 + trn("{0} node", "{0} nodes", nodes, nodes); 
     697                text.append(text.length() > 0 ? ", " : "") 
     698                .append(trn("{0} node", "{0} nodes", nodes, nodes)); 
    346699            } 
    347700            if(relations != 0) { 
    348                 text += (text.length() > 0 ? ", " : "") 
    349                 + trn("{0} relation", "{0} relations", relations, relations); 
     701                text.append(text.length() > 0 ? ", " : "") 
     702                .append(trn("{0} relation", "{0} relations", relations, relations)); 
    350703            } 
    351704            setText(tr("Selection: {0}", text)); 
     
    354707 
    355708        public void actionPerformed(ActionEvent e) { 
    356             historyIgnoreSelection = sel; 
    357709            Main.main.getCurrentDataSet().setSelected(sel); 
    358710        } 
    359  
    360     } 
    361  
    362     /** 
    363      * A specialized {@link JMenuItem} for presenting one entry of the search history 
     711    } 
     712 
     713    /** 
     714     * The popup menue for the JOSM selection history entries 
    364715     * 
    365      * @author Jan Peter Stotz 
    366      */ 
    367     protected static class SearchMenuItem extends JMenuItem implements ActionListener { 
    368         protected SearchSetting s; 
    369  
    370         public SearchMenuItem(SearchSetting s) { 
    371             super(s.toString()); 
    372             this.s = s; 
    373             addActionListener(this); 
     716     */ 
     717    protected static class SelectionHistoryPopup extends JPopupMenu { 
     718        static public void launch(Component parent, Collection<Collection<? extends OsmPrimitive>> history) { 
     719            if (history == null || history.isEmpty()) return; 
     720            JPopupMenu menu = new SelectionHistoryPopup(history); 
     721            Rectangle r = parent.getBounds(); 
     722            menu.show(parent, r.x, r.y + r.height); 
     723        } 
     724 
     725        public SelectionHistoryPopup(Collection<Collection<? extends OsmPrimitive>> history) { 
     726            for (Collection<? extends OsmPrimitive> sel : history) { 
     727                add(new SelectionMenuItem(sel)); 
     728            } 
     729        } 
     730    } 
     731 
     732    /** 
     733     * Action for downloading incomplete members of selected relations 
     734     *  
     735     */ 
     736    class DownloadSelectedIncompleteMembersAction extends AbstractAction implements ListSelectionListener{ 
     737        public DownloadSelectedIncompleteMembersAction() { 
     738            putValue(SHORT_DESCRIPTION, tr("Download incomplete members of selected relations")); 
     739            putValue(SMALL_ICON, ImageProvider.get("dialogs/relation", "downloadincompleteselected")); 
     740            putValue(NAME, tr("Download incomplete members")); 
     741            updateEnabledState(); 
     742        } 
     743 
     744        public Set<OsmPrimitive> buildSetOfIncompleteMembers(List<Relation> rels) { 
     745            Set<OsmPrimitive> ret = new HashSet<OsmPrimitive>(); 
     746            for(Relation r: rels) { 
     747                ret.addAll(r.getIncompleteMembers()); 
     748            } 
     749            return ret; 
    374750        } 
    375751 
    376752        public void actionPerformed(ActionEvent e) { 
    377             SearchAction.searchWithoutHistory(s); 
    378         } 
    379  
    380     } 
    381  
    382     private void updateSelection() { 
    383         if (Main.main.getCurrentDataSet() == null) { 
    384             selectionChanged(Collections.<OsmPrimitive>emptyList()); 
    385         } else { 
    386             selectionChanged(Main.main.getCurrentDataSet().getSelected()); 
    387         } 
    388     } 
    389  
    390     public void editLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer) { 
    391         updateSelection(); 
    392     } 
    393  
    394     class SelectAction extends AbstractAction implements EditLayerChangeListener { 
    395         public SelectAction() { 
    396             putValue(NAME, tr("Select")); 
    397             putValue(SHORT_DESCRIPTION,  tr("Set the selected elements on the map to the selected items in the list above.")); 
    398             putValue(SMALL_ICON, ImageProvider.get("dialogs","select")); 
    399             updateEnabledState(); 
    400         } 
    401  
    402         @Override 
    403         public void actionPerformed(ActionEvent e) { 
    404             updateMap(); 
    405         } 
    406  
    407         public void updateEnabledState() { 
    408             setEnabled(Main.main != null && Main.main.getEditLayer() != null); 
    409         } 
    410  
    411         @Override 
    412         public void editLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer) { 
     753            if (!isEnabled()) 
     754                return; 
     755            List<Relation> rels = model.getSelectedRelationsWithIncompleteMembers(); 
     756            if (rels.isEmpty()) return; 
     757            Main.worker.submit(new DownloadRelationMemberTask( 
     758                    rels, 
     759                    buildSetOfIncompleteMembers(rels), 
     760                    Main.map.mapView.getEditLayer() 
     761            )); 
     762        } 
     763 
     764        protected void updateEnabledState() { 
     765            setEnabled(!model.getSelectedRelationsWithIncompleteMembers().isEmpty()); 
     766        } 
     767 
     768        public void valueChanged(ListSelectionEvent e) { 
    413769            updateEnabledState(); 
    414770        } 
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/DownloadRelationMemberTask.java

    r3083 r3102  
    88import java.io.IOException; 
    99import java.util.Collection; 
     10import java.util.HashSet; 
     11import java.util.Set; 
    1012 
    1113import javax.swing.SwingUtilities; 
     
    3234    private boolean cancelled; 
    3335    private Exception lastException; 
    34     private Relation parent; 
     36    private final Set<Relation> parents = new HashSet<Relation>(); 
    3537    private Collection<OsmPrimitive> children; 
    3638    private OsmDataLayer curLayer; 
    3739    private MultiFetchServerObjectReader objectReader; 
    3840 
    39     public DownloadRelationMemberTask(Relation parent, Collection<OsmPrimitive> children, OsmDataLayer curLayer, MemberTableModel memberTableModel, Dialog dialog) { 
     41    public DownloadRelationMemberTask(Relation parent, Collection<OsmPrimitive> children, OsmDataLayer curLayer, Dialog dialog) { 
    4042        super(tr("Download relation members"), new PleaseWaitProgressMonitor(dialog), false /* don't ignore exception */); 
    41         this.parent = parent; 
     43        this.parents.add(parent); 
    4244        this.children = children; 
    4345        this.curLayer = curLayer; 
    4446    } 
    4547 
    46     public DownloadRelationMemberTask(Relation parent, Collection<OsmPrimitive> children, OsmDataLayer curLayer, MemberTableModel memberTableModel) { 
     48    public DownloadRelationMemberTask(Relation parent, Collection<OsmPrimitive> children, OsmDataLayer curLayer) { 
    4749        super(tr("Download relation members"), false /* don't ignore exception */); 
    48         this.parent = parent; 
     50        this.parents.add(parent); 
     51        this.children = children; 
     52        this.curLayer = curLayer; 
     53    } 
     54 
     55    /** 
     56     * Creates a download task for downloading the child primitives {@code children} for all parent 
     57     * relations in {@code parents}. 
     58     *  
     59     * @param parents the collection of parent relations 
     60     * @param children the collection of child primitives to download 
     61     * @param curLayer the current OSM layer 
     62     */ 
     63    public DownloadRelationMemberTask(Collection<Relation> parents, Collection<OsmPrimitive> children, OsmDataLayer curLayer) { 
     64        super(tr("Download relation members"), false /* don't ignore exception */); 
     65        this.parents.addAll(parents); 
    4966        this.children = children; 
    5067        this.curLayer = curLayer; 
     
    7188    } 
    7289 
     90    protected String buildDownloadFeedbackMessage() { 
     91        if (parents.size() == 1) { 
     92            Relation parent = parents.iterator().next(); 
     93            return trn("Downloading {0} incomplete child of relation ''{1}''", 
     94                    "Downloading {0} incomplete children of relation ''{1}''", 
     95                    children.size(), 
     96                    children.size(), 
     97                    parent.getDisplayName(DefaultNameFormatter.getInstance()) 
     98            ); 
     99        } else 
     100            return trn("Downloading {0} incomplete child of {1} parent relations", 
     101                    "Downloading {0} incomplete children of  {1} parent relations", 
     102                    children.size(), 
     103                    children.size(), 
     104                    parents.size() 
     105            ); 
     106    } 
     107 
    73108    @Override 
    74109    protected void realRun() throws SAXException, IOException, OsmTransferException { 
     
    80115            objectReader.append(children); 
    81116            progressMonitor.indeterminateSubTask( 
    82                     trn("Downloading {0} incomplete child of relation ''{1}''", 
    83                             "Downloading {0} incomplete children of relation ''{1}''", 
    84                             children.size(), 
    85                             children.size(), 
    86                             parent.getDisplayName(DefaultNameFormatter.getInstance()) 
    87                     ) 
     117                    buildDownloadFeedbackMessage() 
    88118            ); 
    89119            final DataSet dataSet = objectReader.parseOsm(progressMonitor 
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java

    r3095 r3102  
    12411241                    memberTableModel.getIncompleteMemberPrimitives(), 
    12421242                    getLayer(), 
    1243                     memberTableModel, 
    12441243                    GenericRelationEditor.this) 
    12451244            ); 
     
    12751274                    memberTableModel.getSelectedIncompleteMemberPrimitives(), 
    12761275                    getLayer(), 
    1277                     memberTableModel, 
    12781276                    GenericRelationEditor.this) 
    12791277            ); 
Note: See TracChangeset for help on using the changeset viewer.