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

Last change on this file since 9993 was 9993, checked in by simon04, 8 years ago

fix #12617 - Unexpected deletion of members by drag-n-drop in relation editor

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