source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableModel.java@ 13925

Last change on this file since 13925 was 13925, checked in by Don-vip, 6 years ago

fix #13467 - use DataSelectionListener everywhere. Deprecate SelectionChangedListener

  • Property svn:eol-style set to native
File size: 29.3 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs.relation;
3
4import java.util.ArrayList;
5import java.util.Arrays;
6import java.util.BitSet;
7import java.util.Collection;
8import java.util.Collections;
9import java.util.EnumSet;
10import java.util.HashSet;
11import java.util.List;
12import java.util.Set;
13import java.util.TreeSet;
14import java.util.concurrent.CopyOnWriteArrayList;
15import java.util.stream.Collectors;
16
17import javax.swing.DefaultListSelectionModel;
18import javax.swing.ListSelectionModel;
19import javax.swing.event.TableModelEvent;
20import javax.swing.event.TableModelListener;
21import javax.swing.table.AbstractTableModel;
22
23import org.openstreetmap.josm.data.osm.DataSelectionListener;
24import org.openstreetmap.josm.data.osm.OsmPrimitive;
25import org.openstreetmap.josm.data.osm.Relation;
26import org.openstreetmap.josm.data.osm.RelationMember;
27import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent;
28import org.openstreetmap.josm.data.osm.event.DataChangedEvent;
29import org.openstreetmap.josm.data.osm.event.DataSetListener;
30import org.openstreetmap.josm.data.osm.event.NodeMovedEvent;
31import org.openstreetmap.josm.data.osm.event.PrimitivesAddedEvent;
32import org.openstreetmap.josm.data.osm.event.PrimitivesRemovedEvent;
33import org.openstreetmap.josm.data.osm.event.RelationMembersChangedEvent;
34import org.openstreetmap.josm.data.osm.event.SelectionEventManager;
35import org.openstreetmap.josm.data.osm.event.TagsChangedEvent;
36import org.openstreetmap.josm.data.osm.event.WayNodesChangedEvent;
37import org.openstreetmap.josm.gui.MainApplication;
38import org.openstreetmap.josm.gui.dialogs.relation.sort.RelationSorter;
39import org.openstreetmap.josm.gui.dialogs.relation.sort.WayConnectionType;
40import org.openstreetmap.josm.gui.dialogs.relation.sort.WayConnectionTypeCalculator;
41import org.openstreetmap.josm.gui.layer.OsmDataLayer;
42import org.openstreetmap.josm.gui.tagging.presets.TaggingPreset;
43import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetHandler;
44import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetType;
45import org.openstreetmap.josm.gui.tagging.presets.TaggingPresets;
46import org.openstreetmap.josm.gui.util.GuiHelper;
47import org.openstreetmap.josm.gui.widgets.OsmPrimitivesTableModel;
48import org.openstreetmap.josm.tools.JosmRuntimeException;
49import org.openstreetmap.josm.tools.bugreport.BugReport;
50
51/**
52 * This is the base model used for the {@link MemberTable}. It holds the member data.
53 */
54public class MemberTableModel extends AbstractTableModel
55implements TableModelListener, DataSelectionListener, DataSetListener, OsmPrimitivesTableModel {
56
57 /**
58 * data of the table model: The list of members and the cached WayConnectionType of each member.
59 **/
60 private final transient List<RelationMember> members;
61 private transient List<WayConnectionType> connectionType;
62 private final transient Relation relation;
63
64 private DefaultListSelectionModel listSelectionModel;
65 private final transient CopyOnWriteArrayList<IMemberModelListener> listeners;
66 private final transient OsmDataLayer layer;
67 private final transient TaggingPresetHandler presetHandler;
68
69 private final transient WayConnectionTypeCalculator wayConnectionTypeCalculator = new WayConnectionTypeCalculator();
70 private final transient RelationSorter relationSorter = new RelationSorter();
71
72 /**
73 * constructor
74 * @param relation relation
75 * @param layer data layer
76 * @param presetHandler tagging preset handler
77 */
78 public MemberTableModel(Relation relation, OsmDataLayer layer, TaggingPresetHandler presetHandler) {
79 this.relation = relation;
80 this.members = new ArrayList<>();
81 this.listeners = new CopyOnWriteArrayList<>();
82 this.layer = layer;
83 this.presetHandler = presetHandler;
84 addTableModelListener(this);
85 }
86
87 /**
88 * Returns the data layer.
89 * @return the data layer
90 */
91 public OsmDataLayer getLayer() {
92 return layer;
93 }
94
95 /**
96 * Registers listeners (selection change and dataset change).
97 */
98 public void register() {
99 SelectionEventManager.getInstance().addSelectionListener(this);
100 getLayer().data.addDataSetListener(this);
101 }
102
103 /**
104 * Unregisters listeners (selection change and dataset change).
105 */
106 public void unregister() {
107 SelectionEventManager.getInstance().removeSelectionListener(this);
108 getLayer().data.removeDataSetListener(this);
109 }
110
111 /* --------------------------------------------------------------------------- */
112 /* Interface DataSelectionListener */
113 /* --------------------------------------------------------------------------- */
114 @Override
115 public void selectionChanged(SelectionChangeEvent event) {
116 if (MainApplication.getLayerManager().getActiveDataLayer() != this.layer) return;
117 // just trigger a repaint
118 Collection<RelationMember> sel = getSelectedMembers();
119 fireTableDataChanged();
120 setSelectedMembers(sel);
121 }
122
123 /* --------------------------------------------------------------------------- */
124 /* Interface DataSetListener */
125 /* --------------------------------------------------------------------------- */
126 @Override
127 public void dataChanged(DataChangedEvent event) {
128 // just trigger a repaint - the display name of the relation members may have changed
129 Collection<RelationMember> sel = getSelectedMembers();
130 GuiHelper.runInEDT(this::fireTableDataChanged);
131 setSelectedMembers(sel);
132 }
133
134 @Override
135 public void nodeMoved(NodeMovedEvent event) {
136 // ignore
137 }
138
139 @Override
140 public void primitivesAdded(PrimitivesAddedEvent event) {
141 // ignore
142 }
143
144 @Override
145 public void primitivesRemoved(PrimitivesRemovedEvent event) {
146 // ignore - the relation in the editor might become out of sync with the relation
147 // in the dataset. We will deal with it when the relation editor is closed or
148 // when the changes in the editor are applied.
149 }
150
151 @Override
152 public void relationMembersChanged(RelationMembersChangedEvent event) {
153 // ignore - the relation in the editor might become out of sync with the relation
154 // in the dataset. We will deal with it when the relation editor is closed or
155 // when the changes in the editor are applied.
156 }
157
158 @Override
159 public void tagsChanged(TagsChangedEvent event) {
160 // just refresh the respective table cells
161 //
162 Collection<RelationMember> sel = getSelectedMembers();
163 for (int i = 0; i < members.size(); i++) {
164 if (members.get(i).getMember() == event.getPrimitive()) {
165 fireTableCellUpdated(i, 1 /* the column with the primitive name */);
166 }
167 }
168 setSelectedMembers(sel);
169 }
170
171 @Override
172 public void wayNodesChanged(WayNodesChangedEvent event) {
173 // ignore
174 }
175
176 @Override
177 public void otherDatasetChange(AbstractDatasetChangedEvent event) {
178 // ignore
179 }
180
181 /* --------------------------------------------------------------------------- */
182
183 public void addMemberModelListener(IMemberModelListener listener) {
184 if (listener != null) {
185 listeners.addIfAbsent(listener);
186 }
187 }
188
189 public void removeMemberModelListener(IMemberModelListener listener) {
190 listeners.remove(listener);
191 }
192
193 protected void fireMakeMemberVisible(int index) {
194 for (IMemberModelListener listener : listeners) {
195 listener.makeMemberVisible(index);
196 }
197 }
198
199 /**
200 * Populates this model from the given relation.
201 * @param relation relation
202 */
203 public void populate(Relation relation) {
204 members.clear();
205 if (relation != null) {
206 // make sure we work with clones of the relation members in the model.
207 members.addAll(new Relation(relation).getMembers());
208 }
209 fireTableDataChanged();
210 }
211
212 @Override
213 public int getColumnCount() {
214 return 3;
215 }
216
217 @Override
218 public int getRowCount() {
219 return members.size();
220 }
221
222 @Override
223 public Object getValueAt(int rowIndex, int columnIndex) {
224 switch (columnIndex) {
225 case 0:
226 return members.get(rowIndex).getRole();
227 case 1:
228 return members.get(rowIndex).getMember();
229 case 2:
230 return getWayConnection(rowIndex);
231 }
232 // should not happen
233 return null;
234 }
235
236 @Override
237 public boolean isCellEditable(int rowIndex, int columnIndex) {
238 return columnIndex == 0;
239 }
240
241 @Override
242 public void setValueAt(Object value, int rowIndex, int columnIndex) {
243 // fix #10524 - IndexOutOfBoundsException: Index: 2, Size: 2
244 if (rowIndex >= members.size()) {
245 return;
246 }
247 RelationMember member = members.get(rowIndex);
248 String role = value.toString();
249 if (member.hasRole(role))
250 return;
251 RelationMember newMember = new RelationMember(role, member.getMember());
252 members.remove(rowIndex);
253 members.add(rowIndex, newMember);
254 fireTableDataChanged();
255 }
256
257 @Override
258 public OsmPrimitive getReferredPrimitive(int idx) {
259 return members.get(idx).getMember();
260 }
261
262 public void moveUp(int... selectedRows) {
263 if (!canMoveUp(selectedRows))
264 return;
265
266 for (int row : selectedRows) {
267 RelationMember member1 = members.get(row);
268 RelationMember member2 = members.get(row - 1);
269 members.set(row, member2);
270 members.set(row - 1, member1);
271 }
272 fireTableDataChanged();
273 getSelectionModel().setValueIsAdjusting(true);
274 getSelectionModel().clearSelection();
275 BitSet selected = new BitSet();
276 for (int row : selectedRows) {
277 row--;
278 selected.set(row);
279 }
280 addToSelectedMembers(selected);
281 getSelectionModel().setValueIsAdjusting(false);
282 fireMakeMemberVisible(selectedRows[0] - 1);
283 }
284
285 public void moveDown(int... selectedRows) {
286 if (!canMoveDown(selectedRows))
287 return;
288
289 for (int i = selectedRows.length - 1; i >= 0; i--) {
290 int row = selectedRows[i];
291 RelationMember member1 = members.get(row);
292 RelationMember member2 = members.get(row + 1);
293 members.set(row, member2);
294 members.set(row + 1, member1);
295 }
296 fireTableDataChanged();
297 getSelectionModel();
298 getSelectionModel().setValueIsAdjusting(true);
299 getSelectionModel().clearSelection();
300 BitSet selected = new BitSet();
301 for (int row : selectedRows) {
302 row++;
303 selected.set(row);
304 }
305 addToSelectedMembers(selected);
306 getSelectionModel().setValueIsAdjusting(false);
307 fireMakeMemberVisible(selectedRows[0] + 1);
308 }
309
310 public void remove(int... selectedRows) {
311 if (!canRemove(selectedRows))
312 return;
313 int offset = 0;
314 for (int row : selectedRows) {
315 row -= offset;
316 if (members.size() > row) {
317 members.remove(row);
318 offset++;
319 }
320 }
321 fireTableDataChanged();
322 }
323
324 public boolean canMoveUp(int... rows) {
325 if (rows == null || rows.length == 0)
326 return false;
327 Arrays.sort(rows);
328 return rows[0] > 0 && !members.isEmpty();
329 }
330
331 public boolean canMoveDown(int... rows) {
332 if (rows == null || rows.length == 0)
333 return false;
334 Arrays.sort(rows);
335 return !members.isEmpty() && rows[rows.length - 1] < members.size() - 1;
336 }
337
338 public boolean canRemove(int... rows) {
339 return rows != null && rows.length != 0;
340 }
341
342 public DefaultListSelectionModel getSelectionModel() {
343 if (listSelectionModel == null) {
344 listSelectionModel = new DefaultListSelectionModel();
345 listSelectionModel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
346 }
347 return listSelectionModel;
348 }
349
350 public void removeMembersReferringTo(List<? extends OsmPrimitive> primitives) {
351 if (primitives == null)
352 return;
353 members.removeIf(member -> primitives.contains(member.getMember()));
354 fireTableDataChanged();
355 }
356
357 /**
358 * Applies this member model to the given relation.
359 * @param relation relation
360 */
361 public void applyToRelation(Relation relation) {
362 relation.setMembers(members.stream()
363 .filter(rm -> !rm.getMember().isDeleted()).collect(Collectors.toList()));
364 }
365
366 public boolean hasSameMembersAs(Relation relation) {
367 if (relation == null)
368 return false;
369 if (relation.getMembersCount() != members.size())
370 return false;
371 for (int i = 0; i < relation.getMembersCount(); i++) {
372 if (!relation.getMember(i).equals(members.get(i)))
373 return false;
374 }
375 return true;
376 }
377
378 /**
379 * Replies the set of incomplete primitives
380 *
381 * @return the set of incomplete primitives
382 */
383 public Set<OsmPrimitive> getIncompleteMemberPrimitives() {
384 Set<OsmPrimitive> ret = new HashSet<>();
385 for (RelationMember member : members) {
386 if (member.getMember().isIncomplete()) {
387 ret.add(member.getMember());
388 }
389 }
390 return ret;
391 }
392
393 /**
394 * Replies the set of selected incomplete primitives
395 *
396 * @return the set of selected incomplete primitives
397 */
398 public Set<OsmPrimitive> getSelectedIncompleteMemberPrimitives() {
399 Set<OsmPrimitive> ret = new HashSet<>();
400 for (RelationMember member : getSelectedMembers()) {
401 if (member.getMember().isIncomplete()) {
402 ret.add(member.getMember());
403 }
404 }
405 return ret;
406 }
407
408 /**
409 * Replies true if at least one the relation members is incomplete
410 *
411 * @return true if at least one the relation members is incomplete
412 */
413 public boolean hasIncompleteMembers() {
414 for (RelationMember member : members) {
415 if (member.getMember().isIncomplete())
416 return true;
417 }
418 return false;
419 }
420
421 /**
422 * Replies true if at least one of the selected members is incomplete
423 *
424 * @return true if at least one of the selected members is incomplete
425 */
426 public boolean hasIncompleteSelectedMembers() {
427 for (RelationMember member : getSelectedMembers()) {
428 if (member.getMember().isIncomplete())
429 return true;
430 }
431 return false;
432 }
433
434 protected List<Integer> getSelectedIndices() {
435 List<Integer> selectedIndices = new ArrayList<>();
436 for (int i = 0; i < members.size(); i++) {
437 if (getSelectionModel().isSelectedIndex(i)) {
438 selectedIndices.add(i);
439 }
440 }
441 return selectedIndices;
442 }
443
444 private void addMembersAtIndex(List<? extends OsmPrimitive> primitives, int index) {
445 if (primitives == null)
446 return;
447 int idx = index;
448 for (OsmPrimitive primitive : primitives) {
449 final RelationMember member = getRelationMemberForPrimitive(primitive);
450 members.add(idx++, member);
451 }
452 fireTableDataChanged();
453 getSelectionModel().clearSelection();
454 getSelectionModel().addSelectionInterval(index, index + primitives.size() - 1);
455 fireMakeMemberVisible(index);
456 }
457
458 RelationMember getRelationMemberForPrimitive(final OsmPrimitive primitive) {
459 final Collection<TaggingPreset> presets = TaggingPresets.getMatchingPresets(
460 EnumSet.of(relation != null ? TaggingPresetType.forPrimitive(relation) : TaggingPresetType.RELATION),
461 presetHandler.getSelection().iterator().next().getKeys(), false);
462 Collection<String> potentialRoles = new TreeSet<>();
463 for (TaggingPreset tp : presets) {
464 String suggestedRole = tp.suggestRoleForOsmPrimitive(primitive);
465 if (suggestedRole != null) {
466 potentialRoles.add(suggestedRole);
467 }
468 }
469 // TODO: propose user to choose role among potential ones instead of picking first one
470 final String role = potentialRoles.isEmpty() ? "" : potentialRoles.iterator().next();
471 return new RelationMember(role == null ? "" : role, primitive);
472 }
473
474 void addMembersAtIndexKeepingOldSelection(final Iterable<RelationMember> newMembers, final int index) {
475 int idx = index;
476 for (RelationMember member : newMembers) {
477 members.add(idx++, member);
478 }
479 invalidateConnectionType();
480 fireTableRowsInserted(index, idx - 1);
481 }
482
483 public void addMembersAtBeginning(List<? extends OsmPrimitive> primitives) {
484 addMembersAtIndex(primitives, 0);
485 }
486
487 public void addMembersAtEnd(List<? extends OsmPrimitive> primitives) {
488 addMembersAtIndex(primitives, members.size());
489 }
490
491 public void addMembersBeforeIdx(List<? extends OsmPrimitive> primitives, int idx) {
492 addMembersAtIndex(primitives, idx);
493 }
494
495 public void addMembersAfterIdx(List<? extends OsmPrimitive> primitives, int idx) {
496 addMembersAtIndex(primitives, idx + 1);
497 }
498
499 /**
500 * Replies the number of members which refer to a particular primitive
501 *
502 * @param primitive the primitive
503 * @return the number of members which refer to a particular primitive
504 */
505 public int getNumMembersWithPrimitive(OsmPrimitive primitive) {
506 int count = 0;
507 for (RelationMember member : members) {
508 if (member.getMember().equals(primitive)) {
509 count++;
510 }
511 }
512 return count;
513 }
514
515 /**
516 * updates the role of the members given by the indices in <code>idx</code>
517 *
518 * @param idx the array of indices
519 * @param role the new role
520 */
521 public void updateRole(int[] idx, String role) {
522 if (idx == null || idx.length == 0)
523 return;
524 for (int row : idx) {
525 // fix #7885 - IndexOutOfBoundsException: Index: 39, Size: 39
526 if (row >= members.size()) {
527 continue;
528 }
529 RelationMember oldMember = members.get(row);
530 RelationMember newMember = new RelationMember(role, oldMember.getMember());
531 members.remove(row);
532 members.add(row, newMember);
533 }
534 fireTableDataChanged();
535 BitSet selected = new BitSet();
536 for (int row : idx) {
537 selected.set(row);
538 }
539 addToSelectedMembers(selected);
540 }
541
542 /**
543 * Get the currently selected relation members
544 *
545 * @return a collection with the currently selected relation members
546 */
547 public Collection<RelationMember> getSelectedMembers() {
548 List<RelationMember> selectedMembers = new ArrayList<>();
549 for (int i : getSelectedIndices()) {
550 selectedMembers.add(members.get(i));
551 }
552 return selectedMembers;
553 }
554
555 /**
556 * Replies the set of selected referers. Never null, but may be empty.
557 *
558 * @return the set of selected referers
559 */
560 public Collection<OsmPrimitive> getSelectedChildPrimitives() {
561 Collection<OsmPrimitive> ret = new ArrayList<>();
562 for (RelationMember m: getSelectedMembers()) {
563 ret.add(m.getMember());
564 }
565 return ret;
566 }
567
568 /**
569 * Replies the set of selected referers. Never null, but may be empty.
570 * @param referenceSet reference set
571 *
572 * @return the set of selected referers
573 */
574 public Set<OsmPrimitive> getChildPrimitives(Collection<? extends OsmPrimitive> referenceSet) {
575 Set<OsmPrimitive> ret = new HashSet<>();
576 if (referenceSet == null) return null;
577 for (RelationMember m: members) {
578 if (referenceSet.contains(m.getMember())) {
579 ret.add(m.getMember());
580 }
581 }
582 return ret;
583 }
584
585 /**
586 * Selects the members in the collection selectedMembers
587 *
588 * @param selectedMembers the collection of selected members
589 */
590 public void setSelectedMembers(Collection<RelationMember> selectedMembers) {
591 if (selectedMembers == null || selectedMembers.isEmpty()) {
592 getSelectionModel().clearSelection();
593 return;
594 }
595
596 // lookup the indices for the respective members
597 //
598 Set<Integer> selectedIndices = new HashSet<>();
599 for (RelationMember member : selectedMembers) {
600 for (int idx = 0; idx < members.size(); ++idx) {
601 if (member.equals(members.get(idx))) {
602 selectedIndices.add(idx);
603 }
604 }
605 }
606 setSelectedMembersIdx(selectedIndices);
607 }
608
609 /**
610 * Selects the members in the collection selectedIndices
611 *
612 * @param selectedIndices the collection of selected member indices
613 */
614 public void setSelectedMembersIdx(Collection<Integer> selectedIndices) {
615 if (selectedIndices == null || selectedIndices.isEmpty()) {
616 getSelectionModel().clearSelection();
617 return;
618 }
619 // select the members
620 //
621 getSelectionModel().setValueIsAdjusting(true);
622 getSelectionModel().clearSelection();
623 BitSet selected = new BitSet();
624 for (int row : selectedIndices) {
625 selected.set(row);
626 }
627 addToSelectedMembers(selected);
628 getSelectionModel().setValueIsAdjusting(false);
629 // make the first selected member visible
630 //
631 if (!selectedIndices.isEmpty()) {
632 fireMakeMemberVisible(Collections.min(selectedIndices));
633 }
634 }
635
636 /**
637 * Add one or more members indices to the selection.
638 * Detect groups of consecutive indices.
639 * Only one costly call of addSelectionInterval is performed for each group
640
641 * @param selectedIndices selected indices as a bitset
642 * @return number of groups
643 */
644 private int addToSelectedMembers(BitSet selectedIndices) {
645 if (selectedIndices == null || selectedIndices.isEmpty()) {
646 return 0;
647 }
648 // select the members
649 //
650 int start = selectedIndices.nextSetBit(0);
651 int end;
652 int steps = 0;
653 int last = selectedIndices.length();
654 while (start >= 0) {
655 end = selectedIndices.nextClearBit(start);
656 steps++;
657 getSelectionModel().addSelectionInterval(start, end-1);
658 start = selectedIndices.nextSetBit(end);
659 if (start < 0 || end == last)
660 break;
661 }
662 return steps;
663 }
664
665 /**
666 * Replies true if the index-th relation members refers
667 * to an editable relation, i.e. a relation which is not
668 * incomplete.
669 *
670 * @param index the index
671 * @return true, if the index-th relation members refers
672 * to an editable relation, i.e. a relation which is not
673 * incomplete
674 */
675 public boolean isEditableRelation(int index) {
676 if (index < 0 || index >= members.size())
677 return false;
678 RelationMember member = members.get(index);
679 if (!member.isRelation())
680 return false;
681 Relation r = member.getRelation();
682 return !r.isIncomplete();
683 }
684
685 /**
686 * Replies true if there is at least one relation member given as {@code members}
687 * which refers to at least on the primitives in {@code primitives}.
688 *
689 * @param members the members
690 * @param primitives the collection of primitives
691 * @return true if there is at least one relation member in this model
692 * which refers to at least on the primitives in <code>primitives</code>; false
693 * otherwise
694 */
695 public static boolean hasMembersReferringTo(Collection<RelationMember> members, Collection<OsmPrimitive> primitives) {
696 if (primitives == null || primitives.isEmpty())
697 return false;
698 Set<OsmPrimitive> referrers = new HashSet<>();
699 for (RelationMember member : members) {
700 referrers.add(member.getMember());
701 }
702 for (OsmPrimitive referred : primitives) {
703 if (referrers.contains(referred))
704 return true;
705 }
706 return false;
707 }
708
709 /**
710 * Replies true if there is at least one relation member in this model
711 * which refers to at least on the primitives in <code>primitives</code>.
712 *
713 * @param primitives the collection of primitives
714 * @return true if there is at least one relation member in this model
715 * which refers to at least on the primitives in <code>primitives</code>; false
716 * otherwise
717 */
718 public boolean hasMembersReferringTo(Collection<OsmPrimitive> primitives) {
719 return hasMembersReferringTo(members, primitives);
720 }
721
722 /**
723 * Selects all members which refer to {@link OsmPrimitive}s in the collections
724 * <code>primitmives</code>. Does nothing is primitives is null.
725 *
726 * @param primitives the collection of primitives
727 */
728 public void selectMembersReferringTo(Collection<? extends OsmPrimitive> primitives) {
729 if (primitives == null) return;
730 getSelectionModel().setValueIsAdjusting(true);
731 getSelectionModel().clearSelection();
732 BitSet selected = new BitSet();
733 for (int i = 0; i < members.size(); i++) {
734 RelationMember m = members.get(i);
735 if (primitives.contains(m.getMember())) {
736 selected.set(i);
737 }
738 }
739 addToSelectedMembers(selected);
740 getSelectionModel().setValueIsAdjusting(false);
741 if (!getSelectedIndices().isEmpty()) {
742 fireMakeMemberVisible(getSelectedIndices().get(0));
743 }
744 }
745
746 /**
747 * Replies true if <code>primitive</code> is currently selected in the layer this
748 * model is attached to
749 *
750 * @param primitive the primitive
751 * @return true if <code>primitive</code> is currently selected in the layer this
752 * model is attached to, false otherwise
753 */
754 public boolean isInJosmSelection(OsmPrimitive primitive) {
755 return layer.data.isSelected(primitive);
756 }
757
758 /**
759 * Sort the selected relation members by the way they are linked.
760 */
761 public void sort() {
762 List<RelationMember> selectedMembers = new ArrayList<>(getSelectedMembers());
763 List<RelationMember> sortedMembers;
764 List<RelationMember> newMembers;
765 if (selectedMembers.size() <= 1) {
766 newMembers = relationSorter.sortMembers(members);
767 sortedMembers = newMembers;
768 } else {
769 sortedMembers = relationSorter.sortMembers(selectedMembers);
770 List<Integer> selectedIndices = getSelectedIndices();
771 newMembers = new ArrayList<>();
772 boolean inserted = false;
773 for (int i = 0; i < members.size(); i++) {
774 if (selectedIndices.contains(i)) {
775 if (!inserted) {
776 newMembers.addAll(sortedMembers);
777 inserted = true;
778 }
779 } else {
780 newMembers.add(members.get(i));
781 }
782 }
783 }
784
785 if (members.size() != newMembers.size())
786 throw new AssertionError();
787
788 members.clear();
789 members.addAll(newMembers);
790 fireTableDataChanged();
791 setSelectedMembers(sortedMembers);
792 }
793
794 /**
795 * Sort the selected relation members and all members below by the way they are linked.
796 */
797 public void sortBelow() {
798 final List<RelationMember> subList = members.subList(Math.max(0, getSelectionModel().getMinSelectionIndex()), members.size());
799 final List<RelationMember> sorted = relationSorter.sortMembers(subList);
800 subList.clear();
801 subList.addAll(sorted);
802 fireTableDataChanged();
803 setSelectedMembers(sorted);
804 }
805
806 WayConnectionType getWayConnection(int i) {
807 try {
808 if (connectionType == null) {
809 connectionType = wayConnectionTypeCalculator.updateLinks(members);
810 }
811 return connectionType.get(i);
812 } catch (JosmRuntimeException | IllegalArgumentException | IllegalStateException e) {
813 throw BugReport.intercept(e).put("i", i).put("members", members).put("relation", relation);
814 }
815 }
816
817 @Override
818 public void tableChanged(TableModelEvent e) {
819 invalidateConnectionType();
820 }
821
822 private void invalidateConnectionType() {
823 connectionType = null;
824 }
825
826 /**
827 * Reverse the relation members.
828 */
829 public void reverse() {
830 List<Integer> selectedIndices = getSelectedIndices();
831 List<Integer> selectedIndicesReversed = getSelectedIndices();
832
833 if (selectedIndices.size() <= 1) {
834 Collections.reverse(members);
835 fireTableDataChanged();
836 setSelectedMembers(members);
837 } else {
838 Collections.reverse(selectedIndicesReversed);
839
840 List<RelationMember> newMembers = new ArrayList<>(members);
841
842 for (int i = 0; i < selectedIndices.size(); i++) {
843 newMembers.set(selectedIndices.get(i), members.get(selectedIndicesReversed.get(i)));
844 }
845
846 if (members.size() != newMembers.size()) throw new AssertionError();
847 members.clear();
848 members.addAll(newMembers);
849 fireTableDataChanged();
850 setSelectedMembersIdx(selectedIndices);
851 }
852 }
853}
Note: See TracBrowser for help on using the repository browser.