[6380] | 1 | // License: GPL. For details, see LICENSE file.
|
---|
[626] | 2 | package org.openstreetmap.josm.gui.dialogs;
|
---|
| 3 |
|
---|
[582] | 4 | import static org.openstreetmap.josm.tools.I18n.tr;
|
---|
[1195] | 5 | import static org.openstreetmap.josm.tools.I18n.trn;
|
---|
[582] | 6 |
|
---|
[3102] | 7 | import java.awt.Component;
|
---|
[9376] | 8 | import java.awt.GraphicsEnvironment;
|
---|
[967] | 9 | import java.awt.Rectangle;
|
---|
[9369] | 10 | import java.awt.datatransfer.Transferable;
|
---|
[582] | 11 | import java.awt.event.ActionEvent;
|
---|
| 12 | import java.awt.event.ActionListener;
|
---|
| 13 | import java.awt.event.KeyEvent;
|
---|
| 14 | import java.awt.event.MouseEvent;
|
---|
[3102] | 15 | import java.util.ArrayList;
|
---|
[4354] | 16 | import java.util.Arrays;
|
---|
[582] | 17 | import java.util.Collection;
|
---|
[1970] | 18 | import java.util.Collections;
|
---|
[11680] | 19 | import java.util.Comparator;
|
---|
[3102] | 20 | import java.util.HashSet;
|
---|
[582] | 21 | import java.util.LinkedList;
|
---|
[3102] | 22 | import java.util.List;
|
---|
| 23 | import java.util.Set;
|
---|
[582] | 24 |
|
---|
[3099] | 25 | import javax.swing.AbstractAction;
|
---|
[3102] | 26 | import javax.swing.AbstractListModel;
|
---|
| 27 | import javax.swing.DefaultListSelectionModel;
|
---|
[9369] | 28 | import javax.swing.JComponent;
|
---|
[582] | 29 | import javax.swing.JList;
|
---|
[967] | 30 | import javax.swing.JMenuItem;
|
---|
| 31 | import javax.swing.JPopupMenu;
|
---|
[582] | 32 | import javax.swing.ListSelectionModel;
|
---|
[9369] | 33 | import javax.swing.TransferHandler;
|
---|
[3102] | 34 | import javax.swing.event.ListDataEvent;
|
---|
| 35 | import javax.swing.event.ListDataListener;
|
---|
| 36 | import javax.swing.event.ListSelectionEvent;
|
---|
| 37 | import javax.swing.event.ListSelectionListener;
|
---|
[582] | 38 |
|
---|
[7949] | 39 | import org.openstreetmap.josm.actions.AbstractSelectAction;
|
---|
[967] | 40 | import org.openstreetmap.josm.actions.AutoScaleAction;
|
---|
[5793] | 41 | import org.openstreetmap.josm.actions.relation.DownloadSelectedIncompleteMembersAction;
|
---|
| 42 | import org.openstreetmap.josm.actions.relation.EditRelationAction;
|
---|
| 43 | import org.openstreetmap.josm.actions.relation.SelectInRelationListAction;
|
---|
[582] | 44 | import org.openstreetmap.josm.data.SelectionChangedListener;
|
---|
[10332] | 45 | import org.openstreetmap.josm.data.osm.DataSet;
|
---|
[12663] | 46 | import org.openstreetmap.josm.data.osm.DefaultNameFormatter;
|
---|
[967] | 47 | import org.openstreetmap.josm.data.osm.Node;
|
---|
[582] | 48 | import org.openstreetmap.josm.data.osm.OsmPrimitive;
|
---|
[4113] | 49 | import org.openstreetmap.josm.data.osm.OsmPrimitiveComparator;
|
---|
[1195] | 50 | import org.openstreetmap.josm.data.osm.Relation;
|
---|
[967] | 51 | import org.openstreetmap.josm.data.osm.Way;
|
---|
[3102] | 52 | import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent;
|
---|
| 53 | import org.openstreetmap.josm.data.osm.event.DataChangedEvent;
|
---|
| 54 | import org.openstreetmap.josm.data.osm.event.DataSetListener;
|
---|
| 55 | import org.openstreetmap.josm.data.osm.event.DatasetEventManager;
|
---|
[4068] | 56 | import org.openstreetmap.josm.data.osm.event.DatasetEventManager.FireMode;
|
---|
[3102] | 57 | import org.openstreetmap.josm.data.osm.event.NodeMovedEvent;
|
---|
| 58 | import org.openstreetmap.josm.data.osm.event.PrimitivesAddedEvent;
|
---|
| 59 | import org.openstreetmap.josm.data.osm.event.PrimitivesRemovedEvent;
|
---|
| 60 | import org.openstreetmap.josm.data.osm.event.RelationMembersChangedEvent;
|
---|
[2912] | 61 | import org.openstreetmap.josm.data.osm.event.SelectionEventManager;
|
---|
[3102] | 62 | import org.openstreetmap.josm.data.osm.event.TagsChangedEvent;
|
---|
| 63 | import org.openstreetmap.josm.data.osm.event.WayNodesChangedEvent;
|
---|
[12846] | 64 | import org.openstreetmap.josm.data.osm.search.SearchSetting;
|
---|
[967] | 65 | import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
|
---|
[12630] | 66 | import org.openstreetmap.josm.gui.MainApplication;
|
---|
[13564] | 67 | import org.openstreetmap.josm.gui.PrimitiveRenderer;
|
---|
[5821] | 68 | import org.openstreetmap.josm.gui.PopupMenuHandler;
|
---|
[744] | 69 | import org.openstreetmap.josm.gui.SideButton;
|
---|
[9369] | 70 | import org.openstreetmap.josm.gui.datatransfer.PrimitiveTransferable;
|
---|
[10604] | 71 | import org.openstreetmap.josm.gui.datatransfer.data.PrimitiveTransferData;
|
---|
[7750] | 72 | import org.openstreetmap.josm.gui.history.HistoryBrowserDialogManager;
|
---|
[10332] | 73 | import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
|
---|
| 74 | import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
|
---|
[5984] | 75 | import org.openstreetmap.josm.gui.util.GuiHelper;
|
---|
[6025] | 76 | import org.openstreetmap.josm.gui.util.HighlightHelper;
|
---|
[4459] | 77 | import org.openstreetmap.josm.gui.widgets.ListPopupMenu;
|
---|
[3102] | 78 | import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
|
---|
[12846] | 79 | import org.openstreetmap.josm.spi.preferences.Config;
|
---|
[3099] | 80 | import org.openstreetmap.josm.tools.ImageProvider;
|
---|
[5200] | 81 | import org.openstreetmap.josm.tools.InputMapUtils;
|
---|
[1084] | 82 | import org.openstreetmap.josm.tools.Shortcut;
|
---|
[6742] | 83 | import org.openstreetmap.josm.tools.Utils;
|
---|
[12909] | 84 | import org.openstreetmap.josm.tools.bugreport.BugReport;
|
---|
[626] | 85 |
|
---|
| 86 | /**
|
---|
[3102] | 87 | * A small tool dialog for displaying the current selection.
|
---|
[6353] | 88 | * @since 8
|
---|
[626] | 89 | */
|
---|
[10378] | 90 | public class SelectionListDialog extends ToggleDialog {
|
---|
[7001] | 91 | private JList<OsmPrimitive> lstPrimitives;
|
---|
[7781] | 92 | private final DefaultListSelectionModel selectionModel = new DefaultListSelectionModel();
|
---|
| 93 | private final SelectionListModel model = new SelectionListModel(selectionModel);
|
---|
[626] | 94 |
|
---|
[7781] | 95 | private final SelectAction actSelect = new SelectAction();
|
---|
| 96 | private final SearchAction actSearch = new SearchAction();
|
---|
| 97 | private final ShowHistoryAction actShowHistory = new ShowHistoryAction();
|
---|
| 98 | private final ZoomToJOSMSelectionAction actZoomToJOSMSelection = new ZoomToJOSMSelectionAction();
|
---|
| 99 | private final ZoomToListSelection actZoomToListSelection = new ZoomToListSelection();
|
---|
| 100 | private final SelectInRelationListAction actSetRelationSelection = new SelectInRelationListAction();
|
---|
| 101 | private final EditRelationAction actEditRelationSelection = new EditRelationAction();
|
---|
| 102 | private final DownloadSelectedIncompleteMembersAction actDownloadSelIncompleteMembers = new DownloadSelectedIncompleteMembersAction();
|
---|
[626] | 103 |
|
---|
[5821] | 104 | /** the popup menu and its handler */
|
---|
| 105 | private final ListPopupMenu popupMenu;
|
---|
[8308] | 106 | private final transient PopupMenuHandler popupMenuHandler;
|
---|
[4459] | 107 |
|
---|
[967] | 108 | /**
|
---|
[4354] | 109 | * Builds the content panel for this dialog
|
---|
[967] | 110 | */
|
---|
[4354] | 111 | protected void buildContentPanel() {
|
---|
[7005] | 112 | lstPrimitives = new JList<>(model);
|
---|
[3102] | 113 | lstPrimitives.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
---|
| 114 | lstPrimitives.setSelectionModel(selectionModel);
|
---|
[13564] | 115 | lstPrimitives.setCellRenderer(new PrimitiveRenderer());
|
---|
[9369] | 116 | lstPrimitives.setTransferHandler(new SelectionTransferHandler());
|
---|
[9376] | 117 | if (!GraphicsEnvironment.isHeadless()) {
|
---|
| 118 | lstPrimitives.setDragEnabled(true);
|
---|
| 119 | }
|
---|
[626] | 120 |
|
---|
[7750] | 121 | lstPrimitives.getSelectionModel().addListSelectionListener(actSelect);
|
---|
| 122 | lstPrimitives.getSelectionModel().addListSelectionListener(actShowHistory);
|
---|
| 123 |
|
---|
[3102] | 124 | // the select action
|
---|
[5825] | 125 | final SideButton selectButton = new SideButton(actSelect);
|
---|
[10611] | 126 | selectButton.createArrow(e -> SelectionHistoryPopup.launch(selectButton, model.getSelectionHistory()));
|
---|
[626] | 127 |
|
---|
[3102] | 128 | // the search button
|
---|
[5825] | 129 | final SideButton searchButton = new SideButton(actSearch);
|
---|
[13545] | 130 | searchButton.createArrow(e -> SearchPopupMenu.launch(searchButton), true);
|
---|
[967] | 131 |
|
---|
[12279] | 132 | createLayout(lstPrimitives, true, Arrays.asList(
|
---|
[7750] | 133 | selectButton, searchButton, new SideButton(actShowHistory)
|
---|
[12279] | 134 | ));
|
---|
[3102] | 135 | }
|
---|
[967] | 136 |
|
---|
[5984] | 137 | /**
|
---|
| 138 | * Constructs a new {@code SelectionListDialog}.
|
---|
| 139 | */
|
---|
[3102] | 140 | public SelectionListDialog() {
|
---|
[4462] | 141 | super(tr("Selection"), "selectionlist", tr("Open a selection list window."),
|
---|
[4957] | 142 | Shortcut.registerShortcut("subwindow:selection", tr("Toggle: {0}",
|
---|
[4982] | 143 | tr("Current Selection")), KeyEvent.VK_T, Shortcut.ALT_SHIFT),
|
---|
[3102] | 144 | 150, // default height
|
---|
| 145 | true // default is "show dialog"
|
---|
| 146 | );
|
---|
| 147 |
|
---|
[4354] | 148 | buildContentPanel();
|
---|
[3102] | 149 | model.addListDataListener(new TitleUpdater());
|
---|
| 150 | model.addListDataListener(actZoomToJOSMSelection);
|
---|
| 151 |
|
---|
[5821] | 152 | popupMenu = new ListPopupMenu(lstPrimitives);
|
---|
| 153 | popupMenuHandler = setupPopupMenuHandler();
|
---|
| 154 |
|
---|
[10611] | 155 | lstPrimitives.addListSelectionListener(e -> {
|
---|
| 156 | actZoomToListSelection.valueChanged(e);
|
---|
| 157 | popupMenuHandler.setPrimitives(model.getSelected());
|
---|
[5793] | 158 | });
|
---|
[6070] | 159 |
|
---|
[5958] | 160 | lstPrimitives.addMouseListener(new MouseEventHandler());
|
---|
[4957] | 161 |
|
---|
[5200] | 162 | InputMapUtils.addEnterAction(lstPrimitives, actZoomToListSelection);
|
---|
[2658] | 163 | }
|
---|
| 164 |
|
---|
| 165 | @Override
|
---|
| 166 | public void showNotify() {
|
---|
[7781] | 167 | SelectionEventManager.getInstance().addSelectionListener(actShowHistory, FireMode.IN_EDT_CONSOLIDATED);
|
---|
[3102] | 168 | SelectionEventManager.getInstance().addSelectionListener(model, FireMode.IN_EDT_CONSOLIDATED);
|
---|
| 169 | DatasetEventManager.getInstance().addDatasetListener(model, FireMode.IN_EDT);
|
---|
[12636] | 170 | MainApplication.getLayerManager().addActiveLayerChangeListener(actSearch);
|
---|
[10332] | 171 | // editLayerChanged also gets the selection history of the level. Listener calls setJOSMSelection when fired.
|
---|
[12636] | 172 | MainApplication.getLayerManager().addAndFireActiveLayerChangeListener(model);
|
---|
[3469] | 173 | actSearch.updateEnabledState();
|
---|
[967] | 174 | }
|
---|
| 175 |
|
---|
[2621] | 176 | @Override
|
---|
[2658] | 177 | public void hideNotify() {
|
---|
[12636] | 178 | MainApplication.getLayerManager().removeActiveLayerChangeListener(actSearch);
|
---|
| 179 | MainApplication.getLayerManager().removeActiveLayerChangeListener(model);
|
---|
[7781] | 180 | SelectionEventManager.getInstance().removeSelectionListener(actShowHistory);
|
---|
[3102] | 181 | SelectionEventManager.getInstance().removeSelectionListener(model);
|
---|
| 182 | DatasetEventManager.getInstance().removeDatasetListener(model);
|
---|
[2621] | 183 | }
|
---|
| 184 |
|
---|
[3102] | 185 | /**
|
---|
[5958] | 186 | * Responds to double clicks on the list of selected objects and launches the popup menu
|
---|
[3114] | 187 | */
|
---|
[5958] | 188 | class MouseEventHandler extends PopupMenuLauncher {
|
---|
[6025] | 189 | private final HighlightHelper helper = new HighlightHelper();
|
---|
[12846] | 190 | private final boolean highlightEnabled = Config.getPref().getBoolean("draw.target-highlight", true);
|
---|
[8510] | 191 |
|
---|
[8836] | 192 | MouseEventHandler() {
|
---|
[5958] | 193 | super(popupMenu);
|
---|
| 194 | }
|
---|
[6070] | 195 |
|
---|
[3114] | 196 | @Override
|
---|
| 197 | public void mouseClicked(MouseEvent e) {
|
---|
[6025] | 198 | int idx = lstPrimitives.locationToIndex(e.getPoint());
|
---|
| 199 | if (idx < 0) return;
|
---|
[5958] | 200 | if (isDoubleClick(e)) {
|
---|
[13434] | 201 | DataSet ds = MainApplication.getLayerManager().getActiveDataSet();
|
---|
| 202 | if (ds == null) return;
|
---|
[7657] | 203 | OsmPrimitive osm = model.getElementAt(idx);
|
---|
[13434] | 204 | Collection<OsmPrimitive> sel = ds.getSelected();
|
---|
[7657] | 205 | if (sel.size() != 1 || !sel.iterator().next().equals(osm)) {
|
---|
| 206 | // Select primitive if it's not the whole current selection
|
---|
[13434] | 207 | ds.setSelected(Collections.singleton(osm));
|
---|
[7657] | 208 | } else if (osm instanceof Relation) {
|
---|
| 209 | // else open relation editor if applicable
|
---|
| 210 | actEditRelationSelection.actionPerformed(null);
|
---|
| 211 | }
|
---|
[12630] | 212 | } else if (highlightEnabled && MainApplication.isDisplayingMapView() && helper.highlightOnly(model.getElementAt(idx))) {
|
---|
| 213 | MainApplication.getMap().mapView.repaint();
|
---|
[3102] | 214 | }
|
---|
[1814] | 215 | }
|
---|
[6025] | 216 |
|
---|
| 217 | @Override
|
---|
| 218 | public void mouseExited(MouseEvent me) {
|
---|
[6058] | 219 | if (highlightEnabled) helper.clear();
|
---|
[6025] | 220 | super.mouseExited(me);
|
---|
| 221 | }
|
---|
[967] | 222 | }
|
---|
| 223 |
|
---|
[5825] | 224 | private PopupMenuHandler setupPopupMenuHandler() {
|
---|
[5821] | 225 | PopupMenuHandler handler = new PopupMenuHandler(popupMenu);
|
---|
| 226 | handler.addAction(actZoomToJOSMSelection);
|
---|
| 227 | handler.addAction(actZoomToListSelection);
|
---|
| 228 | handler.addSeparator();
|
---|
| 229 | handler.addAction(actSetRelationSelection);
|
---|
| 230 | handler.addAction(actEditRelationSelection);
|
---|
| 231 | handler.addSeparator();
|
---|
[7781] | 232 | handler.addAction(actDownloadSelIncompleteMembers);
|
---|
[5821] | 233 | return handler;
|
---|
| 234 | }
|
---|
| 235 |
|
---|
[3102] | 236 | /**
|
---|
[5821] | 237 | * Replies the popup menu handler.
|
---|
| 238 | * @return The popup menu handler
|
---|
[3102] | 239 | */
|
---|
[5821] | 240 | public PopupMenuHandler getPopupMenuHandler() {
|
---|
| 241 | return popupMenuHandler;
|
---|
[967] | 242 | }
|
---|
| 243 |
|
---|
[5984] | 244 | /**
|
---|
| 245 | * Replies the selected OSM primitives.
|
---|
| 246 | * @return The selected OSM primitives
|
---|
| 247 | */
|
---|
[4536] | 248 | public Collection<OsmPrimitive> getSelectedPrimitives() {
|
---|
| 249 | return model.getSelected();
|
---|
| 250 | }
|
---|
[4957] | 251 |
|
---|
[3102] | 252 | /**
|
---|
| 253 | * Updates the dialog title with a summary of the current JOSM selection
|
---|
| 254 | */
|
---|
| 255 | class TitleUpdater implements ListDataListener {
|
---|
| 256 | protected void updateTitle() {
|
---|
| 257 | setTitle(model.getJOSMSelectionSummary());
|
---|
| 258 | }
|
---|
| 259 |
|
---|
[5793] | 260 | @Override
|
---|
[3102] | 261 | public void contentsChanged(ListDataEvent e) {
|
---|
| 262 | updateTitle();
|
---|
| 263 | }
|
---|
| 264 |
|
---|
[5793] | 265 | @Override
|
---|
[3102] | 266 | public void intervalAdded(ListDataEvent e) {
|
---|
| 267 | updateTitle();
|
---|
| 268 | }
|
---|
| 269 |
|
---|
[5793] | 270 | @Override
|
---|
[3102] | 271 | public void intervalRemoved(ListDataEvent e) {
|
---|
| 272 | updateTitle();
|
---|
| 273 | }
|
---|
[967] | 274 | }
|
---|
| 275 |
|
---|
| 276 | /**
|
---|
[3102] | 277 | * Launches the search dialog
|
---|
[967] | 278 | */
|
---|
[10332] | 279 | static class SearchAction extends AbstractAction implements ActiveLayerChangeListener {
|
---|
[7750] | 280 | /**
|
---|
| 281 | * Constructs a new {@code SearchAction}.
|
---|
| 282 | */
|
---|
[8836] | 283 | SearchAction() {
|
---|
[3102] | 284 | putValue(NAME, tr("Search"));
|
---|
[10378] | 285 | putValue(SHORT_DESCRIPTION, tr("Search for objects"));
|
---|
[10369] | 286 | new ImageProvider("dialogs", "search").getResource().attachImageIcon(this, true);
|
---|
[3102] | 287 | updateEnabledState();
|
---|
[967] | 288 | }
|
---|
[3102] | 289 |
|
---|
[5793] | 290 | @Override
|
---|
[3102] | 291 | public void actionPerformed(ActionEvent e) {
|
---|
| 292 | if (!isEnabled()) return;
|
---|
| 293 | org.openstreetmap.josm.actions.search.SearchAction.search();
|
---|
| 294 | }
|
---|
| 295 |
|
---|
[7750] | 296 | protected void updateEnabledState() {
|
---|
[13434] | 297 | setEnabled(MainApplication.getLayerManager().getActiveDataSet() != null);
|
---|
[3102] | 298 | }
|
---|
| 299 |
|
---|
[5793] | 300 | @Override
|
---|
[10332] | 301 | public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
|
---|
[3102] | 302 | updateEnabledState();
|
---|
| 303 | }
|
---|
[967] | 304 | }
|
---|
| 305 |
|
---|
[3102] | 306 | /**
|
---|
| 307 | * Sets the current JOSM selection to the OSM primitives selected in the list
|
---|
| 308 | * of this dialog
|
---|
| 309 | */
|
---|
[7949] | 310 | class SelectAction extends AbstractSelectAction implements ListSelectionListener {
|
---|
[7750] | 311 | /**
|
---|
| 312 | * Constructs a new {@code SelectAction}.
|
---|
| 313 | */
|
---|
[8836] | 314 | SelectAction() {
|
---|
[3102] | 315 | updateEnabledState();
|
---|
[967] | 316 | }
|
---|
[3102] | 317 |
|
---|
[5799] | 318 | @Override
|
---|
[3102] | 319 | public void actionPerformed(ActionEvent e) {
|
---|
| 320 | Collection<OsmPrimitive> sel = model.getSelected();
|
---|
[8510] | 321 | if (sel.isEmpty()) return;
|
---|
[13434] | 322 | DataSet ds = MainApplication.getLayerManager().getActiveDataSet();
|
---|
| 323 | if (ds == null) return;
|
---|
| 324 | ds.setSelected(sel);
|
---|
[7949] | 325 | model.selectionModel.setSelectionInterval(0, sel.size()-1);
|
---|
[3102] | 326 | }
|
---|
| 327 |
|
---|
[7750] | 328 | protected void updateEnabledState() {
|
---|
[10383] | 329 | setEnabled(!model.isSelectionEmpty());
|
---|
[3102] | 330 | }
|
---|
| 331 |
|
---|
[5799] | 332 | @Override
|
---|
[3102] | 333 | public void valueChanged(ListSelectionEvent e) {
|
---|
| 334 | updateEnabledState();
|
---|
| 335 | }
|
---|
[967] | 336 | }
|
---|
| 337 |
|
---|
[3102] | 338 | /**
|
---|
[7750] | 339 | * The action for showing history information of the current history item.
|
---|
| 340 | */
|
---|
[7781] | 341 | class ShowHistoryAction extends AbstractAction implements ListSelectionListener, SelectionChangedListener {
|
---|
[7750] | 342 | /**
|
---|
| 343 | * Constructs a new {@code ShowHistoryAction}.
|
---|
| 344 | */
|
---|
[8836] | 345 | ShowHistoryAction() {
|
---|
[7750] | 346 | putValue(NAME, tr("History"));
|
---|
| 347 | putValue(SHORT_DESCRIPTION, tr("Display the history of the selected objects."));
|
---|
[10369] | 348 | new ImageProvider("dialogs", "history").getResource().attachImageIcon(this, true);
|
---|
[7781] | 349 | updateEnabledState(model.getSize());
|
---|
[7750] | 350 | }
|
---|
| 351 |
|
---|
| 352 | @Override
|
---|
| 353 | public void actionPerformed(ActionEvent e) {
|
---|
| 354 | Collection<OsmPrimitive> sel = model.getSelected();
|
---|
[7781] | 355 | if (sel.isEmpty() && model.getSize() != 1) {
|
---|
| 356 | return;
|
---|
| 357 | } else if (sel.isEmpty()) {
|
---|
| 358 | sel = Collections.singleton(model.getElementAt(0));
|
---|
| 359 | }
|
---|
[7750] | 360 | HistoryBrowserDialogManager.getInstance().showHistory(sel);
|
---|
| 361 | }
|
---|
| 362 |
|
---|
[7781] | 363 | protected void updateEnabledState(int osmSelectionSize) {
|
---|
| 364 | // See #10830 - allow to click on history button is a single object is selected, even if not selected again in the list
|
---|
[10383] | 365 | setEnabled(!model.isSelectionEmpty() || osmSelectionSize == 1);
|
---|
[7750] | 366 | }
|
---|
| 367 |
|
---|
| 368 | @Override
|
---|
| 369 | public void valueChanged(ListSelectionEvent e) {
|
---|
[7781] | 370 | updateEnabledState(model.getSize());
|
---|
[7750] | 371 | }
|
---|
[7781] | 372 |
|
---|
| 373 | @Override
|
---|
| 374 | public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
|
---|
| 375 | updateEnabledState(newSelection.size());
|
---|
| 376 | }
|
---|
[7750] | 377 | }
|
---|
| 378 |
|
---|
| 379 | /**
|
---|
[3102] | 380 | * The action for zooming to the primitives in the current JOSM selection
|
---|
[3146] | 381 | *
|
---|
[3102] | 382 | */
|
---|
| 383 | class ZoomToJOSMSelectionAction extends AbstractAction implements ListDataListener {
|
---|
| 384 |
|
---|
[8836] | 385 | ZoomToJOSMSelectionAction() {
|
---|
[8510] | 386 | putValue(NAME, tr("Zoom to selection"));
|
---|
[3102] | 387 | putValue(SHORT_DESCRIPTION, tr("Zoom to selection"));
|
---|
[10369] | 388 | new ImageProvider("dialogs/autoscale", "selection").getResource().attachImageIcon(this, true);
|
---|
[3102] | 389 | updateEnabledState();
|
---|
[967] | 390 | }
|
---|
[3105] | 391 |
|
---|
[5793] | 392 | @Override
|
---|
[3102] | 393 | public void actionPerformed(ActionEvent e) {
|
---|
[3327] | 394 | AutoScaleAction.autoScale("selection");
|
---|
[3102] | 395 | }
|
---|
| 396 |
|
---|
| 397 | public void updateEnabledState() {
|
---|
| 398 | setEnabled(model.getSize() > 0);
|
---|
| 399 | }
|
---|
| 400 |
|
---|
[5793] | 401 | @Override
|
---|
[3102] | 402 | public void contentsChanged(ListDataEvent e) {
|
---|
| 403 | updateEnabledState();
|
---|
| 404 | }
|
---|
| 405 |
|
---|
[5793] | 406 | @Override
|
---|
[3102] | 407 | public void intervalAdded(ListDataEvent e) {
|
---|
| 408 | updateEnabledState();
|
---|
| 409 | }
|
---|
| 410 |
|
---|
[5793] | 411 | @Override
|
---|
[3102] | 412 | public void intervalRemoved(ListDataEvent e) {
|
---|
| 413 | updateEnabledState();
|
---|
| 414 | }
|
---|
[967] | 415 | }
|
---|
| 416 |
|
---|
| 417 | /**
|
---|
[3102] | 418 | * The action for zooming to the primitives which are currently selected in
|
---|
| 419 | * the list displaying the JOSM selection
|
---|
[3146] | 420 | *
|
---|
[967] | 421 | */
|
---|
[7750] | 422 | class ZoomToListSelection extends AbstractAction implements ListSelectionListener {
|
---|
| 423 | /**
|
---|
| 424 | * Constructs a new {@code ZoomToListSelection}.
|
---|
| 425 | */
|
---|
[8836] | 426 | ZoomToListSelection() {
|
---|
[3102] | 427 | putValue(NAME, tr("Zoom to selected element(s)"));
|
---|
| 428 | putValue(SHORT_DESCRIPTION, tr("Zoom to selected element(s)"));
|
---|
[10369] | 429 | new ImageProvider("dialogs/autoscale", "selection").getResource().attachImageIcon(this, true);
|
---|
[3102] | 430 | updateEnabledState();
|
---|
[1814] | 431 | }
|
---|
[1677] | 432 |
|
---|
[5793] | 433 | @Override
|
---|
[3102] | 434 | public void actionPerformed(ActionEvent e) {
|
---|
| 435 | BoundingXYVisitor box = new BoundingXYVisitor();
|
---|
| 436 | Collection<OsmPrimitive> sel = model.getSelected();
|
---|
| 437 | if (sel.isEmpty()) return;
|
---|
| 438 | box.computeBoundingBox(sel);
|
---|
| 439 | if (box.getBounds() == null)
|
---|
| 440 | return;
|
---|
| 441 | box.enlargeBoundingBox();
|
---|
[12630] | 442 | MainApplication.getMap().mapView.zoomTo(box);
|
---|
[3102] | 443 | }
|
---|
| 444 |
|
---|
[7750] | 445 | protected void updateEnabledState() {
|
---|
[10383] | 446 | setEnabled(!model.isSelectionEmpty());
|
---|
[3102] | 447 | }
|
---|
| 448 |
|
---|
[3327] | 449 | @Override
|
---|
[3290] | 450 | public void valueChanged(ListSelectionEvent e) {
|
---|
| 451 | updateEnabledState();
|
---|
| 452 | }
|
---|
| 453 | }
|
---|
| 454 |
|
---|
| 455 | /**
|
---|
[3102] | 456 | * The list model for the list of OSM primitives in the current JOSM selection.
|
---|
[3146] | 457 | *
|
---|
[5266] | 458 | * The model also maintains a history of the last {@link SelectionListModel#SELECTION_HISTORY_SIZE}
|
---|
[3102] | 459 | * JOSM selection.
|
---|
[3146] | 460 | *
|
---|
[3102] | 461 | */
|
---|
[12909] | 462 | static class SelectionListModel extends AbstractListModel<OsmPrimitive>
|
---|
[10332] | 463 | implements ActiveLayerChangeListener, SelectionChangedListener, DataSetListener {
|
---|
[3102] | 464 |
|
---|
| 465 | private static final int SELECTION_HISTORY_SIZE = 10;
|
---|
| 466 |
|
---|
[4064] | 467 | // Variable to store history from currentDataSet()
|
---|
| 468 | private LinkedList<Collection<? extends OsmPrimitive>> history;
|
---|
[8308] | 469 | private final transient List<OsmPrimitive> selection = new ArrayList<>();
|
---|
[9078] | 470 | private final DefaultListSelectionModel selectionModel;
|
---|
[3102] | 471 |
|
---|
| 472 | /**
|
---|
| 473 | * Constructor
|
---|
| 474 | * @param selectionModel the selection model used in the list
|
---|
| 475 | */
|
---|
[8836] | 476 | SelectionListModel(DefaultListSelectionModel selectionModel) {
|
---|
[3102] | 477 | this.selectionModel = selectionModel;
|
---|
| 478 | }
|
---|
| 479 |
|
---|
| 480 | /**
|
---|
| 481 | * Replies a summary of the current JOSM selection
|
---|
[3146] | 482 | *
|
---|
[3102] | 483 | * @return a summary of the current JOSM selection
|
---|
| 484 | */
|
---|
[8367] | 485 | public synchronized String getJOSMSelectionSummary() {
|
---|
[3102] | 486 | if (selection.isEmpty()) return tr("Selection");
|
---|
| 487 | int numNodes = 0;
|
---|
| 488 | int numWays = 0;
|
---|
| 489 | int numRelations = 0;
|
---|
| 490 | for (OsmPrimitive p: selection) {
|
---|
| 491 | switch(p.getType()) {
|
---|
| 492 | case NODE: numNodes++; break;
|
---|
| 493 | case WAY: numWays++; break;
|
---|
| 494 | case RELATION: numRelations++; break;
|
---|
[10217] | 495 | default: throw new AssertionError();
|
---|
[3102] | 496 | }
|
---|
[1814] | 497 | }
|
---|
[3102] | 498 | return tr("Sel.: Rel.:{0} / Ways:{1} / Nodes:{2}", numRelations, numWays, numNodes);
|
---|
[1228] | 499 | }
|
---|
| 500 |
|
---|
[3102] | 501 | /**
|
---|
| 502 | * Remembers a JOSM selection the history of JOSM selections
|
---|
[3146] | 503 | *
|
---|
[3102] | 504 | * @param selection the JOSM selection. Ignored if null or empty.
|
---|
| 505 | */
|
---|
| 506 | public void remember(Collection<? extends OsmPrimitive> selection) {
|
---|
[8510] | 507 | if (selection == null) return;
|
---|
| 508 | if (selection.isEmpty()) return;
|
---|
[4064] | 509 | if (history == null) return;
|
---|
[3102] | 510 | if (history.isEmpty()) {
|
---|
| 511 | history.add(selection);
|
---|
| 512 | return;
|
---|
| 513 | }
|
---|
| 514 | if (history.getFirst().equals(selection)) return;
|
---|
| 515 | history.addFirst(selection);
|
---|
[8510] | 516 | for (int i = 1; i < history.size(); ++i) {
|
---|
| 517 | if (history.get(i).equals(selection)) {
|
---|
[3286] | 518 | history.remove(i);
|
---|
| 519 | break;
|
---|
| 520 | }
|
---|
| 521 | }
|
---|
[12846] | 522 | int maxsize = Config.getPref().getInt("select.history-size", SELECTION_HISTORY_SIZE);
|
---|
[3361] | 523 | while (history.size() > maxsize) {
|
---|
[3102] | 524 | history.removeLast();
|
---|
| 525 | }
|
---|
| 526 | }
|
---|
[2912] | 527 |
|
---|
[3102] | 528 | /**
|
---|
| 529 | * Replies the history of JOSM selections
|
---|
[3146] | 530 | *
|
---|
[5903] | 531 | * @return history of JOSM selections
|
---|
[3102] | 532 | */
|
---|
| 533 | public List<Collection<? extends OsmPrimitive>> getSelectionHistory() {
|
---|
| 534 | return history;
|
---|
| 535 | }
|
---|
| 536 |
|
---|
[5799] | 537 | @Override
|
---|
[8367] | 538 | public synchronized OsmPrimitive getElementAt(int index) {
|
---|
[3102] | 539 | return selection.get(index);
|
---|
| 540 | }
|
---|
| 541 |
|
---|
[5799] | 542 | @Override
|
---|
[8367] | 543 | public synchronized int getSize() {
|
---|
[3102] | 544 | return selection.size();
|
---|
| 545 | }
|
---|
| 546 |
|
---|
| 547 | /**
|
---|
[10383] | 548 | * Determines if no OSM primitives are currently selected.
|
---|
| 549 | * @return {@code true} if no OSM primitives are currently selected
|
---|
| 550 | * @since 10383
|
---|
| 551 | */
|
---|
| 552 | public boolean isSelectionEmpty() {
|
---|
| 553 | return selectionModel.isSelectionEmpty();
|
---|
| 554 | }
|
---|
| 555 |
|
---|
| 556 | /**
|
---|
| 557 | * Replies the collection of OSM primitives currently selected in the view of this model
|
---|
[3146] | 558 | *
|
---|
[5903] | 559 | * @return choosen elements in the view
|
---|
[3102] | 560 | */
|
---|
[8367] | 561 | public synchronized Collection<OsmPrimitive> getSelected() {
|
---|
[7005] | 562 | Set<OsmPrimitive> sel = new HashSet<>();
|
---|
[8510] | 563 | for (int i = 0; i < getSize(); i++) {
|
---|
[3102] | 564 | if (selectionModel.isSelectedIndex(i)) {
|
---|
| 565 | sel.add(selection.get(i));
|
---|
| 566 | }
|
---|
[2903] | 567 | }
|
---|
[3102] | 568 | return sel;
|
---|
| 569 | }
|
---|
| 570 |
|
---|
| 571 | /**
|
---|
| 572 | * Sets the OSM primitives to be selected in the view of this model
|
---|
[3146] | 573 | *
|
---|
[3102] | 574 | * @param sel the collection of primitives to select
|
---|
| 575 | */
|
---|
[8367] | 576 | public synchronized void setSelected(Collection<OsmPrimitive> sel) {
|
---|
[11365] | 577 | selectionModel.setValueIsAdjusting(true);
|
---|
[3102] | 578 | selectionModel.clearSelection();
|
---|
[11365] | 579 | if (sel != null) {
|
---|
| 580 | for (OsmPrimitive p: sel) {
|
---|
| 581 | int i = selection.indexOf(p);
|
---|
| 582 | if (i >= 0) {
|
---|
| 583 | selectionModel.addSelectionInterval(i, i);
|
---|
| 584 | }
|
---|
[3102] | 585 | }
|
---|
[2903] | 586 | }
|
---|
[11365] | 587 | selectionModel.setValueIsAdjusting(false);
|
---|
[2903] | 588 | }
|
---|
[3102] | 589 |
|
---|
| 590 | @Override
|
---|
| 591 | protected void fireContentsChanged(Object source, int index0, int index1) {
|
---|
| 592 | Collection<OsmPrimitive> sel = getSelected();
|
---|
| 593 | super.fireContentsChanged(source, index0, index1);
|
---|
| 594 | setSelected(sel);
|
---|
| 595 | }
|
---|
| 596 |
|
---|
| 597 | /**
|
---|
| 598 | * Sets the collection of currently selected OSM objects
|
---|
[3146] | 599 | *
|
---|
[3102] | 600 | * @param selection the collection of currently selected OSM objects
|
---|
| 601 | */
|
---|
[8368] | 602 | public void setJOSMSelection(final Collection<? extends OsmPrimitive> selection) {
|
---|
| 603 | synchronized (this) {
|
---|
| 604 | this.selection.clear();
|
---|
| 605 | if (selection != null) {
|
---|
| 606 | this.selection.addAll(selection);
|
---|
| 607 | sort();
|
---|
| 608 | }
|
---|
[3102] | 609 | }
|
---|
[5984] | 610 | GuiHelper.runInEDTAndWait(new Runnable() {
|
---|
| 611 | @Override public void run() {
|
---|
| 612 | fireContentsChanged(this, 0, getSize());
|
---|
| 613 | if (selection != null) {
|
---|
| 614 | remember(selection);
|
---|
| 615 | }
|
---|
[5713] | 616 | }
|
---|
[5984] | 617 | });
|
---|
[3102] | 618 | }
|
---|
| 619 |
|
---|
| 620 | /**
|
---|
| 621 | * Triggers a refresh of the view for all primitives in {@code toUpdate}
|
---|
| 622 | * which are currently displayed in the view
|
---|
[3146] | 623 | *
|
---|
[3102] | 624 | * @param toUpdate the collection of primitives to update
|
---|
| 625 | */
|
---|
[8367] | 626 | public synchronized void update(Collection<? extends OsmPrimitive> toUpdate) {
|
---|
[3102] | 627 | if (toUpdate == null) return;
|
---|
| 628 | if (toUpdate.isEmpty()) return;
|
---|
| 629 | Collection<OsmPrimitive> sel = getSelected();
|
---|
[8510] | 630 | for (OsmPrimitive p: toUpdate) {
|
---|
[3102] | 631 | int i = selection.indexOf(p);
|
---|
| 632 | if (i >= 0) {
|
---|
[8510] | 633 | super.fireContentsChanged(this, i, i);
|
---|
[3102] | 634 | }
|
---|
| 635 | }
|
---|
| 636 | setSelected(sel);
|
---|
| 637 | }
|
---|
| 638 |
|
---|
| 639 | /**
|
---|
[3124] | 640 | * Sorts the current elements in the selection
|
---|
| 641 | */
|
---|
[8367] | 642 | public synchronized void sort() {
|
---|
[12909] | 643 | int size = selection.size();
|
---|
| 644 | if (size > 1 && size <= Config.getPref().getInt("selection.no_sort_above", 100_000)) {
|
---|
| 645 | boolean quick = size > Config.getPref().getInt("selection.fast_sort_above", 10_000);
|
---|
[12846] | 646 | Comparator<OsmPrimitive> c = Config.getPref().getBoolean("selection.sort_relations_before_ways", true)
|
---|
[11680] | 647 | ? OsmPrimitiveComparator.orderingRelationsWaysNodes()
|
---|
| 648 | : OsmPrimitiveComparator.orderingWaysRelationsNodes();
|
---|
[12909] | 649 | try {
|
---|
| 650 | selection.sort(c.thenComparing(quick
|
---|
| 651 | ? OsmPrimitiveComparator.comparingUniqueId()
|
---|
| 652 | : OsmPrimitiveComparator.comparingNames()));
|
---|
| 653 | } catch (IllegalArgumentException e) {
|
---|
| 654 | throw BugReport.intercept(e).put("size", size).put("quick", quick).put("selection", selection);
|
---|
| 655 | }
|
---|
[3654] | 656 | }
|
---|
[3124] | 657 | }
|
---|
| 658 |
|
---|
[3102] | 659 | /* ------------------------------------------------------------------------ */
|
---|
[10332] | 660 | /* interface ActiveLayerChangeListener */
|
---|
[3102] | 661 | /* ------------------------------------------------------------------------ */
|
---|
[5793] | 662 | @Override
|
---|
[10332] | 663 | public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
|
---|
| 664 | DataSet newData = e.getSource().getEditDataSet();
|
---|
| 665 | if (newData == null) {
|
---|
[3102] | 666 | setJOSMSelection(null);
|
---|
[4064] | 667 | history = null;
|
---|
[3102] | 668 | } else {
|
---|
[10332] | 669 | history = newData.getSelectionHistory();
|
---|
| 670 | setJOSMSelection(newData.getAllSelected());
|
---|
[3102] | 671 | }
|
---|
| 672 | }
|
---|
| 673 |
|
---|
| 674 | /* ------------------------------------------------------------------------ */
|
---|
[13141] | 675 | /* interface SelectionChangedListener */
|
---|
[3102] | 676 | /* ------------------------------------------------------------------------ */
|
---|
[5793] | 677 | @Override
|
---|
[3102] | 678 | public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
|
---|
| 679 | setJOSMSelection(newSelection);
|
---|
| 680 | }
|
---|
| 681 |
|
---|
| 682 | /* ------------------------------------------------------------------------ */
|
---|
| 683 | /* interface DataSetListener */
|
---|
| 684 | /* ------------------------------------------------------------------------ */
|
---|
[5793] | 685 | @Override
|
---|
[3102] | 686 | public void dataChanged(DataChangedEvent event) {
|
---|
| 687 | // refresh the whole list
|
---|
| 688 | fireContentsChanged(this, 0, getSize());
|
---|
| 689 | }
|
---|
| 690 |
|
---|
[5793] | 691 | @Override
|
---|
[3102] | 692 | public void nodeMoved(NodeMovedEvent event) {
|
---|
| 693 | // may influence the display name of primitives, update the data
|
---|
| 694 | update(event.getPrimitives());
|
---|
| 695 | }
|
---|
| 696 |
|
---|
[5793] | 697 | @Override
|
---|
[3102] | 698 | public void otherDatasetChange(AbstractDatasetChangedEvent event) {
|
---|
| 699 | // may influence the display name of primitives, update the data
|
---|
| 700 | update(event.getPrimitives());
|
---|
| 701 | }
|
---|
| 702 |
|
---|
[5793] | 703 | @Override
|
---|
[3102] | 704 | public void relationMembersChanged(RelationMembersChangedEvent event) {
|
---|
| 705 | // may influence the display name of primitives, update the data
|
---|
| 706 | update(event.getPrimitives());
|
---|
| 707 | }
|
---|
| 708 |
|
---|
[5793] | 709 | @Override
|
---|
[3102] | 710 | public void tagsChanged(TagsChangedEvent event) {
|
---|
| 711 | // may influence the display name of primitives, update the data
|
---|
| 712 | update(event.getPrimitives());
|
---|
| 713 | }
|
---|
| 714 |
|
---|
[5793] | 715 | @Override
|
---|
[3102] | 716 | public void wayNodesChanged(WayNodesChangedEvent event) {
|
---|
| 717 | // may influence the display name of primitives, update the data
|
---|
| 718 | update(event.getPrimitives());
|
---|
| 719 | }
|
---|
| 720 |
|
---|
[5793] | 721 | @Override
|
---|
[7949] | 722 | public void primitivesAdded(PrimitivesAddedEvent event) {
|
---|
| 723 | /* ignored - handled by SelectionChangeListener */
|
---|
| 724 | }
|
---|
| 725 |
|
---|
[5793] | 726 | @Override
|
---|
[7949] | 727 | public void primitivesRemoved(PrimitivesRemovedEvent event) {
|
---|
| 728 | /* ignored - handled by SelectionChangeListener*/
|
---|
| 729 | }
|
---|
[967] | 730 | }
|
---|
| 731 |
|
---|
[3102] | 732 | /**
|
---|
| 733 | * A specialized {@link JMenuItem} for presenting one entry of the search history
|
---|
| 734 | *
|
---|
| 735 | * @author Jan Peter Stotz
|
---|
| 736 | */
|
---|
| 737 | protected static class SearchMenuItem extends JMenuItem implements ActionListener {
|
---|
[8308] | 738 | protected final transient SearchSetting s;
|
---|
[3102] | 739 |
|
---|
| 740 | public SearchMenuItem(SearchSetting s) {
|
---|
[7949] | 741 | super(Utils.shortenString(s.toString(),
|
---|
| 742 | org.openstreetmap.josm.actions.search.SearchAction.MAX_LENGTH_SEARCH_EXPRESSION_DISPLAY));
|
---|
[3102] | 743 | this.s = s;
|
---|
| 744 | addActionListener(this);
|
---|
[2930] | 745 | }
|
---|
[3102] | 746 |
|
---|
[5793] | 747 | @Override
|
---|
[3102] | 748 | public void actionPerformed(ActionEvent e) {
|
---|
| 749 | org.openstreetmap.josm.actions.search.SearchAction.searchWithoutHistory(s);
|
---|
| 750 | }
|
---|
[2930] | 751 | }
|
---|
| 752 |
|
---|
[967] | 753 | /**
|
---|
[3102] | 754 | * The popup menu for the search history entries
|
---|
[3146] | 755 | *
|
---|
[967] | 756 | */
|
---|
[3102] | 757 | protected static class SearchPopupMenu extends JPopupMenu {
|
---|
[6889] | 758 | public static void launch(Component parent) {
|
---|
[3176] | 759 | if (org.openstreetmap.josm.actions.search.SearchAction.getSearchHistory().isEmpty())
|
---|
[3102] | 760 | return;
|
---|
| 761 | JPopupMenu menu = new SearchPopupMenu();
|
---|
| 762 | Rectangle r = parent.getBounds();
|
---|
| 763 | menu.show(parent, r.x, r.y + r.height);
|
---|
| 764 | }
|
---|
| 765 |
|
---|
[8468] | 766 | /**
|
---|
| 767 | * Constructs a new {@code SearchPopupMenu}.
|
---|
| 768 | */
|
---|
[3102] | 769 | public SearchPopupMenu() {
|
---|
[3176] | 770 | for (SearchSetting ss: org.openstreetmap.josm.actions.search.SearchAction.getSearchHistory()) {
|
---|
[3102] | 771 | add(new SearchMenuItem(ss));
|
---|
[1814] | 772 | }
|
---|
[3102] | 773 | }
|
---|
[967] | 774 | }
|
---|
| 775 |
|
---|
| 776 | /**
|
---|
| 777 | * A specialized {@link JMenuItem} for presenting one entry of the selection history
|
---|
[1023] | 778 | *
|
---|
[967] | 779 | * @author Jan Peter Stotz
|
---|
| 780 | */
|
---|
[3102] | 781 | protected static class SelectionMenuItem extends JMenuItem implements ActionListener {
|
---|
[8308] | 782 | protected transient Collection<? extends OsmPrimitive> sel;
|
---|
[967] | 783 |
|
---|
| 784 | public SelectionMenuItem(Collection<? extends OsmPrimitive> sel) {
|
---|
| 785 | this.sel = sel;
|
---|
| 786 | int ways = 0;
|
---|
| 787 | int nodes = 0;
|
---|
[1195] | 788 | int relations = 0;
|
---|
[967] | 789 | for (OsmPrimitive o : sel) {
|
---|
[8444] | 790 | if (!o.isSelectable()) continue; // skip unselectable primitives
|
---|
[1814] | 791 | if (o instanceof Way) {
|
---|
[967] | 792 | ways++;
|
---|
[1814] | 793 | } else if (o instanceof Node) {
|
---|
[967] | 794 | nodes++;
|
---|
[1814] | 795 | } else if (o instanceof Relation) {
|
---|
[1195] | 796 | relations++;
|
---|
[1814] | 797 | }
|
---|
[967] | 798 | }
|
---|
[6822] | 799 | StringBuilder text = new StringBuilder();
|
---|
[8395] | 800 | if (ways != 0) {
|
---|
[3102] | 801 | text.append(text.length() > 0 ? ", " : "")
|
---|
| 802 | .append(trn("{0} way", "{0} ways", ways, ways));
|
---|
[1814] | 803 | }
|
---|
[8395] | 804 | if (nodes != 0) {
|
---|
[3102] | 805 | text.append(text.length() > 0 ? ", " : "")
|
---|
| 806 | .append(trn("{0} node", "{0} nodes", nodes, nodes));
|
---|
[1814] | 807 | }
|
---|
[8395] | 808 | if (relations != 0) {
|
---|
[3102] | 809 | text.append(text.length() > 0 ? ", " : "")
|
---|
| 810 | .append(trn("{0} relation", "{0} relations", relations, relations));
|
---|
[1814] | 811 | }
|
---|
[8395] | 812 | if (ways + nodes + relations == 0) {
|
---|
[5036] | 813 | text.append(tr("Unselectable now"));
|
---|
[8510] | 814 | this.sel = new ArrayList<>(); // empty selection
|
---|
[6070] | 815 | }
|
---|
[8468] | 816 | DefaultNameFormatter df = DefaultNameFormatter.getInstance();
|
---|
[8395] | 817 | if (ways + nodes + relations == 1) {
|
---|
[3286] | 818 | text.append(": ");
|
---|
[8510] | 819 | for (OsmPrimitive o : sel) {
|
---|
[3327] | 820 | text.append(o.getDisplayName(df));
|
---|
| 821 | }
|
---|
[3286] | 822 | setText(text.toString());
|
---|
[3327] | 823 | } else {
|
---|
| 824 | setText(tr("Selection: {0}", text));
|
---|
[3286] | 825 | }
|
---|
[967] | 826 | addActionListener(this);
|
---|
| 827 | }
|
---|
| 828 |
|
---|
[5793] | 829 | @Override
|
---|
[967] | 830 | public void actionPerformed(ActionEvent e) {
|
---|
[13434] | 831 | MainApplication.getLayerManager().getActiveDataSet().setSelected(sel);
|
---|
[967] | 832 | }
|
---|
| 833 | }
|
---|
| 834 |
|
---|
| 835 | /**
|
---|
[5958] | 836 | * The popup menu for the JOSM selection history entries
|
---|
[967] | 837 | */
|
---|
[3102] | 838 | protected static class SelectionHistoryPopup extends JPopupMenu {
|
---|
[6889] | 839 | public static void launch(Component parent, Collection<Collection<? extends OsmPrimitive>> history) {
|
---|
[3102] | 840 | if (history == null || history.isEmpty()) return;
|
---|
| 841 | JPopupMenu menu = new SelectionHistoryPopup(history);
|
---|
| 842 | Rectangle r = parent.getBounds();
|
---|
| 843 | menu.show(parent, r.x, r.y + r.height);
|
---|
[967] | 844 | }
|
---|
| 845 |
|
---|
[3102] | 846 | public SelectionHistoryPopup(Collection<Collection<? extends OsmPrimitive>> history) {
|
---|
| 847 | for (Collection<? extends OsmPrimitive> sel : history) {
|
---|
| 848 | add(new SelectionMenuItem(sel));
|
---|
| 849 | }
|
---|
[967] | 850 | }
|
---|
| 851 | }
|
---|
[9369] | 852 |
|
---|
| 853 | /**
|
---|
| 854 | * A transfer handler class for drag-and-drop support.
|
---|
| 855 | */
|
---|
| 856 | protected class SelectionTransferHandler extends TransferHandler {
|
---|
| 857 |
|
---|
| 858 | @Override
|
---|
| 859 | public int getSourceActions(JComponent c) {
|
---|
| 860 | return COPY;
|
---|
| 861 | }
|
---|
| 862 |
|
---|
| 863 | @Override
|
---|
| 864 | protected Transferable createTransferable(JComponent c) {
|
---|
[10604] | 865 | return new PrimitiveTransferable(PrimitiveTransferData.getData(getSelectedPrimitives()));
|
---|
[9369] | 866 | }
|
---|
| 867 | }
|
---|
[626] | 868 | }
|
---|