Ticket #15593: todo_plugin_multiselect_improvements_v1.patch
| File todo_plugin_multiselect_improvements_v1.patch, 15.3 KB (added by , 4 years ago) |
|---|
-
plugins/todo/src/org/openstreetmap/josm/plugins/todo/TodoDialog.java
20 20 import javax.swing.JList; 21 21 import javax.swing.ListSelectionModel; 22 22 import javax.swing.SwingUtilities; 23 import javax.swing.event.ListDataEvent; 24 import javax.swing.event.ListDataListener; 23 25 import javax.swing.event.ListSelectionEvent; 24 26 import javax.swing.event.ListSelectionListener; 25 27 … … 27 29 import org.openstreetmap.josm.actions.AutoScaleAction.AutoScaleMode; 28 30 import org.openstreetmap.josm.data.osm.DataSelectionListener; 29 31 import org.openstreetmap.josm.data.osm.OsmPrimitive; 32 import org.openstreetmap.josm.data.osm.event.DatasetEventManager; 30 33 import org.openstreetmap.josm.data.osm.event.SelectionEventManager; 34 import org.openstreetmap.josm.data.osm.event.DatasetEventManager.FireMode; 31 35 import org.openstreetmap.josm.gui.MainApplication; 32 36 import org.openstreetmap.josm.gui.SideButton; 33 37 import org.openstreetmap.josm.gui.dialogs.ToggleDialog; … … 35 39 import org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener; 36 40 import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent; 37 41 import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent; 42 import org.openstreetmap.josm.gui.util.HighlightHelper; 38 43 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 39 44 import org.openstreetmap.josm.gui.widgets.ListPopupMenu; 40 45 import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher; 46 import org.openstreetmap.josm.spi.preferences.Config; 41 47 import org.openstreetmap.josm.tools.ImageProvider; 48 import org.openstreetmap.josm.tools.InputMapUtils; 42 49 import org.openstreetmap.josm.tools.Shortcut; 43 50 44 51 /** … … 52 59 private final TodoListModel model = new TodoListModel(selectionModel); 53 60 private final JList<TodoListItem> lstPrimitives = new JList<>(model); 54 61 private final AddAction actAdd = new AddAction(model); 62 private final SelectAction actSelect = new SelectAction(model); 55 63 private final PassAction actPass = new PassAction(model); 56 64 private final MarkAction actMark = new MarkAction(model); 57 65 private final MarkSelectedAction actMarkSelected = new MarkSelectedAction(model); … … 72 80 KeyEvent.VK_T, Shortcut.CTRL_SHIFT), 150); 73 81 buildContentPanel(); 74 82 83 model.addListDataListener(new TitleUpdater()); 84 75 85 MainApplication.getLayerManager().addLayerChangeListener(this); 86 DatasetEventManager.getInstance().addDatasetListener(model, FireMode.IN_EDT); 76 87 lstPrimitives.addMouseListener(new DblClickHandler()); 77 88 lstPrimitives.addMouseListener(new TodoPopupLauncher()); 78 89 toggleAction.addPropertyChangeListener(this); 90 91 InputMapUtils.addEnterAction(lstPrimitives, actSelect); 79 92 } 80 93 81 94 /** … … 83 96 */ 84 97 protected void buildContentPanel() { 85 98 lstPrimitives.setSelectionModel(selectionModel); 86 lstPrimitives.setSelectionMode(ListSelectionModel. SINGLE_SELECTION);99 lstPrimitives.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); 87 100 lstPrimitives.setCellRenderer(new TodoListItemRenderer()); 88 101 lstPrimitives.setTransferHandler(null); 89 102 90 103 // the select action 91 SelectAction actSelect = new SelectAction(model);92 104 SideButton selectButton = new SideButton(actSelect); 93 105 lstPrimitives.getSelectionModel().addListSelectionListener(actSelect); 106 actSelect.updateEnabledState(); 94 107 95 108 // the add button 96 109 SideButton addButton = new SideButton(actAdd); … … 147 160 } 148 161 layer.data.setSelected(items); 149 162 } 150 zoom(layer); 163 if (!layer.data.selectionEmpty()) { 164 zoom(layer); 165 } 151 166 } 152 167 153 168 protected void updateTitle() { … … 161 176 actAdd.updateEnabledState(); 162 177 } 163 178 179 @Override 180 public void hideNotify() { 181 SelectionEventManager.getInstance().removeSelectionListener(actAdd); 182 SelectionEventManager.getInstance().removeSelectionListener(actMarkSelected); 183 } 184 164 185 protected Collection<TodoListItem> getItems() { 165 186 OsmDataLayer layer = MainApplication.getLayerManager().getEditLayer(); 166 187 if (layer == null) … … 186 207 } 187 208 188 209 public void updateEnabledState() { 189 setEnabled( model.getSelected() != null);210 setEnabled(!model.isSelectionEmpty()); 190 211 } 191 212 192 213 @Override … … 262 283 @Override 263 284 public void actionPerformed(ActionEvent e) { 264 285 model.addItems(getItems()); 265 updateTitle();266 286 } 267 287 268 288 public void updateEnabledState() { … … 293 313 @Override 294 314 public void actionPerformed(ActionEvent e) { 295 315 model.markItems(getItems()); 296 updateTitle();297 316 } 298 317 299 318 public void updateEnabledState() { … … 310 329 } 311 330 } 312 331 313 private class MarkAction extends AbstractAction implements ListSelectionListener {332 private static class MarkAction extends AbstractAction implements ListSelectionListener { 314 333 315 334 TodoListModel model; 316 335 … … 324 343 325 344 @Override 326 345 public void actionPerformed(ActionEvent arg0) { 327 model.mark Selected();346 model.markItems(model.getSelected()); 328 347 selectAndZoom(model.getSelected()); 329 updateTitle();330 348 } 331 349 332 350 public void updateEnabledState() { 333 setEnabled( model.getSelected() != null);351 setEnabled(!model.isSelectionEmpty()); 334 352 } 335 353 336 354 @Override … … 339 357 } 340 358 } 341 359 342 private class MarkAllAction extends AbstractAction {360 private static class MarkAllAction extends AbstractAction { 343 361 344 362 TodoListModel model; 345 363 … … 354 372 public void actionPerformed(ActionEvent arg0) { 355 373 model.markAll(); 356 374 selectAndZoom(model.getSelected()); 357 updateTitle();358 375 } 359 360 376 } 361 377 362 private class UnmarkAllAction extends AbstractAction {378 private static class UnmarkAllAction extends AbstractAction { 363 379 364 380 TodoListModel model; 365 381 … … 373 389 @Override 374 390 public void actionPerformed(ActionEvent arg0) { 375 391 model.unmarkAll(); 376 updateTitle();377 392 } 378 393 } 379 394 380 private class ClearAction extends AbstractAction {395 private static class ClearAction extends AbstractAction { 381 396 382 397 TodoListModel model; 383 398 … … 391 406 @Override 392 407 public void actionPerformed(ActionEvent arg0) { 393 408 model.clear(); 394 updateTitle();395 409 } 396 410 } 397 411 … … 411 425 * The popup menu launcher. 412 426 */ 413 427 class TodoPopupLauncher extends PopupMenuLauncher { 428 private final HighlightHelper helper = new HighlightHelper(); 429 private final boolean highlightEnabled = Config.getPref().getBoolean("draw.target-highlight", true); 430 431 TodoPopupLauncher() { 432 super(popupMenu); 433 } 434 414 435 @Override 415 public void launch(MouseEvent evt) { 416 int idx = lstPrimitives.locationToIndex(evt.getPoint()); 417 if (idx >= 0) 418 model.setSelected(model.getElementAt(idx)); 436 public void mouseClicked(MouseEvent e) { 437 int idx = lstPrimitives.locationToIndex(e.getPoint()); 438 if (idx < 0) return; 439 if (highlightEnabled && MainApplication.isDisplayingMapView() && 440 helper.highlightOnly(model.getElementAt(idx).primitive)) { 441 MainApplication.getMap().mapView.repaint(); 442 } 443 } 419 444 420 popupMenu.show(lstPrimitives, evt.getX(), evt.getY()); 445 @Override 446 public void mouseExited(MouseEvent me) { 447 if (highlightEnabled) helper.clear(); 448 super.mouseExited(me); 421 449 } 422 450 } 423 451 … … 439 467 } 440 468 } 441 469 470 /** 471 * Updates the dialog title with a summary of the current todo list status 472 */ 473 class TitleUpdater implements ListDataListener { 474 @Override 475 public void contentsChanged(ListDataEvent e) { 476 updateTitle(); 477 } 478 479 @Override 480 public void intervalAdded(ListDataEvent e) { 481 updateTitle(); 482 } 483 484 @Override 485 public void intervalRemoved(ListDataEvent e) { 486 updateTitle(); 487 } 488 } 489 442 490 @Override 443 491 public void propertyChange(PropertyChangeEvent arg0) { 444 492 actAdd.updateEnabledState(); … … 448 496 public void destroy() { 449 497 super.destroy(); 450 498 MainApplication.getLayerManager().removeLayerChangeListener(this); 499 DatasetEventManager.getInstance().removeDatasetListener(model); 451 500 MainApplication.unregisterActionShortcut(actPass, sctPass); 452 501 MainApplication.unregisterActionShortcut(actMark, sctMark); 453 502 } … … 460 509 @Override 461 510 public void layerRemoving(LayerRemoveEvent e) { 462 511 if (e.getRemovedLayer() instanceof OsmDataLayer) { 463 if (model.purgeLayerItems((OsmDataLayer) e.getRemovedLayer())) { 464 updateTitle(); 465 } 512 model.purgeLayerItems((OsmDataLayer) e.getRemovedLayer()); 466 513 } 467 514 } 468 515 -
plugins/todo/src/org/openstreetmap/josm/plugins/todo/TodoListModel.java
6 6 import java.util.ArrayList; 7 7 import java.util.Collection; 8 8 import java.util.List; 9 import java.util.stream.Collectors; 10 import java.util.stream.IntStream; 9 11 10 12 import javax.swing.AbstractListModel; 11 13 import javax.swing.DefaultListSelectionModel; 12 14 15 import org.openstreetmap.josm.data.osm.OsmPrimitive; 16 import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent; 17 import org.openstreetmap.josm.data.osm.event.DataChangedEvent; 18 import org.openstreetmap.josm.data.osm.event.DataSetListener; 19 import org.openstreetmap.josm.data.osm.event.NodeMovedEvent; 20 import org.openstreetmap.josm.data.osm.event.PrimitivesAddedEvent; 21 import org.openstreetmap.josm.data.osm.event.PrimitivesRemovedEvent; 22 import org.openstreetmap.josm.data.osm.event.RelationMembersChangedEvent; 23 import org.openstreetmap.josm.data.osm.event.TagsChangedEvent; 24 import org.openstreetmap.josm.data.osm.event.WayNodesChangedEvent; 13 25 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 26 import org.openstreetmap.josm.gui.util.TableHelper; 14 27 15 public class TodoListModel extends AbstractListModel<TodoListItem> { 28 /** 29 * The list model for the todo list items. 30 * 31 * The model also maintains a list of already completed items 32 * 33 */ 34 public class TodoListModel extends AbstractListModel<TodoListItem> implements DataSetListener { 16 35 17 36 private final ArrayList<TodoListItem> todoList = new ArrayList<>(); 18 37 private final ArrayList<TodoListItem> doneList = new ArrayList<>(); … … 32 51 return todoList.size(); 33 52 } 34 53 54 public boolean isSelectionEmpty() { 55 return selectionModel.isSelectionEmpty(); 56 } 57 35 58 public int getDoneSize() { 36 59 return doneList.size(); 37 60 } 38 61 39 public TodoListItem getSelected() { 40 if (getSize() == 0 || selectionModel.isSelectionEmpty() || selectionModel.getMinSelectionIndex() >= getSize()) 41 return null; 42 return todoList.get(selectionModel.getMinSelectionIndex()); 62 public synchronized Collection<TodoListItem> getSelected() { 63 return IntStream.range(0, getSize()) 64 .filter(selectionModel::isSelectedIndex) 65 .mapToObj(todoList::get) 66 .collect(Collectors.toSet()); 43 67 } 44 68 69 public Collection<TodoListItem> getItemsForPrimitives(Collection<? extends OsmPrimitive> primitives) { 70 return todoList.stream().filter(i -> primitives.contains(i.primitive)) 71 .collect(Collectors.toList()); 72 } 73 45 74 public List<TodoListItem> getTodoList() { 46 75 return todoList; 47 76 } … … 104 133 selectionModel.setSelectionInterval(sel, sel); 105 134 } 106 135 107 public void setSelected(TodoListItem element) { 108 int sel = todoList.indexOf(element); 109 if (sel == -1) 110 return; 111 selectionModel.setSelectionInterval(sel, sel); 136 public synchronized void setSelected(Collection<TodoListItem> sel) { 137 TableHelper.setSelectedIndices(selectionModel, 138 sel != null ? sel.stream().mapToInt(todoList::indexOf) : IntStream.empty()); 112 139 } 113 140 114 141 public void markAll() { … … 120 147 super.fireIntervalRemoved(this, 0, size-1); 121 148 } 122 149 150 public void removeItems(Collection<TodoListItem> items) { 151 if (items == null || items.isEmpty()) 152 return; 153 154 int size = getSize(); 155 156 todoList.removeAll(items); 157 doneList.removeAll(items); 158 159 super.fireIntervalRemoved(this, 0, size-1); 160 } 161 123 162 public void markItems(Collection<TodoListItem> items) { 124 163 if (items == null || items.isEmpty()) 125 164 return; … … 175 214 else return tr("Todo list {0}/{1} ({2}%)", getDoneSize(), totalSize, 100.0*getDoneSize()/totalSize); 176 215 } 177 216 217 /** 218 * Triggers a refresh of the view for all items in {@code toUpdate} 219 * which are currently displayed in the view 220 * 221 * @param toUpdate the collection of items to update 222 */ 223 public synchronized void update(Collection<? extends TodoListItem> toUpdate) { 224 if (toUpdate == null) return; 225 if (toUpdate.isEmpty()) return; 226 Collection<TodoListItem> sel = getSelected(); 227 for (TodoListItem p: toUpdate) { 228 int i = todoList.indexOf(p); 229 if (i >= 0) { 230 super.fireContentsChanged(this, i, i); 231 } 232 } 233 setSelected(sel); 234 } 235 236 @Override 237 public void primitivesAdded(PrimitivesAddedEvent event) { 238 // ignored 239 } 240 241 @Override 242 public void primitivesRemoved(PrimitivesRemovedEvent event) { 243 removeItems(getItemsForPrimitives(event.getPrimitives())); 244 } 245 246 @Override 247 public void tagsChanged(TagsChangedEvent event) { 248 update(getItemsForPrimitives(event.getPrimitives())); 249 } 250 251 @Override 252 public void nodeMoved(NodeMovedEvent event) { 253 update(getItemsForPrimitives(event.getPrimitives())); 254 } 255 256 @Override 257 public void wayNodesChanged(WayNodesChangedEvent event) { 258 update(getItemsForPrimitives(event.getPrimitives())); 259 } 260 261 @Override 262 public void relationMembersChanged(RelationMembersChangedEvent event) { 263 update(getItemsForPrimitives(event.getPrimitives())); 264 } 265 266 @Override 267 public void otherDatasetChange(AbstractDatasetChangedEvent event) { 268 update(getItemsForPrimitives(event.getPrimitives())); 269 } 270 271 @Override 272 public void dataChanged(DataChangedEvent event) { 273 update(getItemsForPrimitives(event.getPrimitives())); 274 for (AbstractDatasetChangedEvent e : event.getEvents()) { 275 if (e instanceof PrimitivesRemovedEvent) { 276 primitivesRemoved((PrimitivesRemovedEvent) e); 277 } 278 } 279 } 178 280 }
