Ignore:
Timestamp:
10.03.2010 10:00:20 (2 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.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • 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        } 
Note: See TracChangeset for help on using the changeset viewer.