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

Last change on this file since 1857 was 1857, checked in by Gubaer, 15 years ago

replaced JOptionDialog.show... by OptionPaneUtil.show....
improved relation editor

File size: 21.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.HashSet;
9import java.util.Iterator;
10import java.util.LinkedList;
11import java.util.List;
12import java.util.Vector;
13import java.util.concurrent.CopyOnWriteArrayList;
14
15import javax.swing.DefaultListSelectionModel;
16import javax.swing.ListSelectionModel;
17import javax.swing.table.AbstractTableModel;
18
19import org.openstreetmap.josm.data.osm.DataSet;
20import org.openstreetmap.josm.data.osm.Node;
21import org.openstreetmap.josm.data.osm.OsmPrimitive;
22import org.openstreetmap.josm.data.osm.Relation;
23import org.openstreetmap.josm.data.osm.RelationMember;
24import org.openstreetmap.josm.data.osm.Way;
25
26public class MemberTableModel extends AbstractTableModel {
27
28 private ArrayList<RelationMember> members;
29 private DefaultListSelectionModel listSelectionModel;
30 private CopyOnWriteArrayList<IMemberModelListener> listeners;
31
32 /**
33 * constructor
34 */
35 public MemberTableModel() {
36 members = new ArrayList<RelationMember>();
37 listeners = new CopyOnWriteArrayList<IMemberModelListener>();
38 }
39
40 public void addMemberModelListener(IMemberModelListener listener) {
41 synchronized (listeners) {
42 if (listener != null && !listeners.contains(listener)) {
43 listeners.add(listener);
44 }
45 }
46 }
47
48 public void removeMemberModelListener(IMemberModelListener listener) {
49 synchronized (listeners) {
50 if (listener != null && listeners.contains(listener)) {
51 listeners.remove(listener);
52 }
53 }
54 }
55
56 protected void fireMakeMemberVisible(int index) {
57 synchronized (listeners) {
58 for (IMemberModelListener listener : listeners) {
59 listener.makeMemberVisible(index);
60 }
61 }
62 }
63
64 public void populate(Relation relation) {
65 members.clear();
66 if (relation != null && relation.members != null) {
67 // make sure we work with clones of the relation members
68 // in the model.
69 members.addAll(new Relation(relation).members);
70 }
71 fireTableDataChanged();
72 }
73
74 public int getColumnCount() {
75 return 3;
76 }
77
78 public int getRowCount() {
79 return members.size();
80 }
81
82 public Object getValueAt(int rowIndex, int columnIndex) {
83 switch (columnIndex) {
84 case 0:
85 return members.get(rowIndex).role;
86 case 1:
87 return members.get(rowIndex).member;
88 case 2:
89 return linked(rowIndex);
90 }
91 // should not happen
92 return null;
93 }
94
95 @Override
96 public boolean isCellEditable(int rowIndex, int columnIndex) {
97 return columnIndex == 0;
98 }
99
100 @Override
101 public void setValueAt(Object value, int rowIndex, int columnIndex) {
102 RelationMember member = members.get(rowIndex);
103 member.role = value.toString();
104 }
105
106 public OsmPrimitive getReferredPrimitive(int idx) {
107 return members.get(idx).member;
108 }
109
110 public void moveUp(int[] selectedRows) {
111 if (!canMoveUp(selectedRows))
112 return;
113
114 for (int row : selectedRows) {
115 RelationMember member1 = members.get(row);
116 RelationMember member2 = members.get(row - 1);
117 members.set(row, member2);
118 members.set(row - 1, member1);
119 }
120 fireTableDataChanged();
121 getSelectionModel();
122 listSelectionModel.clearSelection();
123 for (int row : selectedRows) {
124 row--;
125 listSelectionModel.addSelectionInterval(row, row);
126 }
127 fireMakeMemberVisible(selectedRows[0] - 1);
128 }
129
130 public void moveDown(int[] selectedRows) {
131 if (!canMoveDown(selectedRows))
132 return;
133
134 for (int i = selectedRows.length - 1; i >= 0; i--) {
135 int row = selectedRows[i];
136 RelationMember member1 = members.get(row);
137 RelationMember member2 = members.get(row + 1);
138 members.set(row, member2);
139 members.set(row + 1, member1);
140 }
141 fireTableDataChanged();
142 getSelectionModel();
143 listSelectionModel.clearSelection();
144 for (int row : selectedRows) {
145 row++;
146 listSelectionModel.addSelectionInterval(row, row);
147 }
148 fireMakeMemberVisible(selectedRows[0] + 1);
149 }
150
151 public void remove(int[] selectedRows) {
152 if (!canRemove(selectedRows))
153 return;
154 int offset = 0;
155 for (int row : selectedRows) {
156 row -= offset;
157 members.remove(row);
158 offset++;
159 }
160 fireTableDataChanged();
161 }
162
163 public boolean canMoveUp(int[] rows) {
164 if (rows == null || rows.length == 0)
165 return false;
166 Arrays.sort(rows);
167 return rows[0] > 0 && members.size() > 0;
168 }
169
170 public boolean canMoveDown(int[] rows) {
171 if (rows == null || rows.length == 0)
172 return false;
173 Arrays.sort(rows);
174 return members.size() > 0 && rows[rows.length - 1] < members.size() - 1;
175 }
176
177 public boolean canRemove(int[] rows) {
178 if (rows == null || rows.length == 0)
179 return false;
180 return true;
181 }
182
183 public DefaultListSelectionModel getSelectionModel() {
184 if (listSelectionModel == null) {
185 listSelectionModel = new DefaultListSelectionModel();
186 listSelectionModel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
187 }
188 return listSelectionModel;
189 }
190
191 public void updateMemberReferences(DataSet ds) {
192 for (RelationMember member : members) {
193 if (member.member.id == 0) {
194 continue;
195 }
196 OsmPrimitive primitive = ds.getPrimitiveById(member.member.id);
197 if (primitive != null) {
198 member.member = primitive;
199 }
200 }
201 fireTableDataChanged();
202 }
203
204 public void removeMembersReferringTo(List<? extends OsmPrimitive> primitives) {
205 if (primitives == null)
206 return;
207 Iterator<RelationMember> it = members.iterator();
208 while (it.hasNext()) {
209 RelationMember member = it.next();
210 if (primitives.contains(member.member)) {
211 it.remove();
212 }
213 }
214 fireTableDataChanged();
215 }
216
217 public void applyToRelation(Relation relation) {
218 relation.members.clear();
219 relation.members.addAll(members);
220 }
221
222 public boolean hasSameMembersAs(Relation relation) {
223 if (relation == null)
224 return false;
225 if (relation.members.size() != members.size())
226 return false;
227 for (int i = 0; i < relation.members.size(); i++) {
228 if (!relation.members.get(i).equals(members.get(i)))
229 return false;
230 }
231 return true;
232 }
233
234 public boolean hasIncompleteMembers() {
235 for (RelationMember member : members) {
236 if (member.member.incomplete)
237 return true;
238 }
239 return false;
240 }
241
242 protected List<Integer> getSelectedIndices() {
243 ArrayList<Integer> selectedIndices = new ArrayList<Integer>();
244 for (int i = 0; i < members.size(); i++) {
245 if (getSelectionModel().isSelectedIndex(i)) {
246 selectedIndices.add(i);
247 }
248 }
249 return selectedIndices;
250 }
251
252 public void addMembersAtBeginning(List<OsmPrimitive> primitives) {
253 if (primitives == null)
254 return;
255 for (OsmPrimitive primitive : primitives) {
256 RelationMember member = new RelationMember("", primitive);
257 members.add(0, member);
258 }
259 fireTableDataChanged();
260 getSelectionModel().clearSelection();
261 for (int i = 0; i < primitives.size(); i++) {
262 getSelectionModel().addSelectionInterval(i, i);
263 }
264 fireMakeMemberVisible(0);
265 }
266
267 public void addMembersAtEnd(List<? extends OsmPrimitive> primitives) {
268 if (primitives == null)
269 return;
270
271 for (OsmPrimitive primitive : primitives) {
272 RelationMember member = new RelationMember("", primitive);
273 members.add(member);
274 }
275 fireTableDataChanged();
276 getSelectionModel().clearSelection();
277 for (int i = 0; i < primitives.size(); i++) {
278 getSelectionModel().addSelectionInterval(members.size() - 1 - i, members.size() - 1 - i);
279 }
280 fireMakeMemberVisible(members.size() - 1);
281 }
282
283 public void addMembersBeforeIdx(List<? extends OsmPrimitive> primitives, int idx) {
284 if (primitives == null)
285 return;
286
287 for (OsmPrimitive primitive : primitives) {
288 RelationMember member = new RelationMember("", primitive);
289 members.add(idx, member);
290 }
291 fireTableDataChanged();
292 getSelectionModel().clearSelection();
293 for (int i = 0; i < primitives.size(); i++) {
294 getSelectionModel().addSelectionInterval(idx + i, idx + i);
295 }
296 fireMakeMemberVisible(idx);
297 }
298
299 public void addMembersAfterIdx(List<? extends OsmPrimitive> primitives, int idx) {
300 if (primitives == null)
301 return;
302 int j = 1;
303 for (OsmPrimitive primitive : primitives) {
304 RelationMember member = new RelationMember("", primitive);
305 members.add(idx + j, member);
306 j++;
307 }
308 fireTableDataChanged();
309 getSelectionModel().clearSelection();
310 for (int i = 0; i < primitives.size(); i++) {
311 getSelectionModel().addSelectionInterval(idx + 1 + i, idx + 1 + i);
312 }
313 fireMakeMemberVisible(idx + 1);
314 }
315
316 /**
317 * Replies the number of members which refer to a particular primitive
318 *
319 * @param primitive the primitive
320 * @return the number of members which refer to a particular primitive
321 */
322 public int getNumMembersWithPrimitive(OsmPrimitive primitive) {
323 int count = 0;
324 for (RelationMember member : members) {
325 if (member.member.equals(primitive)) {
326 count++;
327 }
328 }
329 return count;
330 }
331
332 /**
333 * updates the role of the members given by the indices in <code>idx</code>
334 *
335 * @param idx the array of indices
336 * @param role the new role
337 */
338 public void updateRole(int[] idx, String role) {
339 if (idx == null || idx.length == 0)
340 return;
341 for (int row : idx) {
342 members.get(row).role = role;
343 }
344 fireTableDataChanged();
345 for (int row : idx) {
346 getSelectionModel().addSelectionInterval(row, row);
347 }
348 }
349
350 /**
351 * Get the currently selected relation members
352 *
353 * @return a collection with the currently selected relation members
354 */
355 public Collection<RelationMember> getSelectedMembers() {
356 ArrayList<RelationMember> selectedMembers = new ArrayList<RelationMember>();
357 for (int i : getSelectedIndices()) {
358 selectedMembers.add(members.get(i));
359 }
360 return selectedMembers;
361 }
362
363 /**
364 * Selects the members in the collection selectedMembers
365 *
366 * @param selectedMembers the collection of selected members
367 */
368 public void setSelectedMembers(Collection<RelationMember> selectedMembers) {
369 if (selectedMembers == null || selectedMembers.isEmpty())
370 return;
371
372 // lookup the indices for the respective members
373 //
374 ArrayList<Integer> selectedIndices = new ArrayList<Integer>();
375 for (RelationMember member : selectedMembers) {
376 int idx = members.indexOf(member);
377 if (idx >= 0 && !selectedIndices.contains(idx)) {
378 selectedIndices.add(idx);
379 }
380 }
381
382 // select the members
383 //
384 Collections.sort(selectedIndices);
385 getSelectionModel().clearSelection();
386 for (int row : selectedIndices) {
387 getSelectionModel().addSelectionInterval(row, row);
388 }
389
390 // make the first selected member visible
391 //
392 if (selectedIndices.size() > 0) {
393 fireMakeMemberVisible(selectedIndices.get(0));
394 }
395 }
396
397 /**
398 * Replies true if the index-th relation members referrs
399 * to an editable relation, i.e. a relation which is not
400 * incomplete.
401 *
402 * @param index the index
403 * @return true, if the index-th relation members referrs
404 * to an editable relation, i.e. a relation which is not
405 * incomplete
406 */
407 public boolean isEditableRelation(int index) {
408 if (index < 0 || index >= members.size())
409 return false;
410 RelationMember member = members.get(index);
411 if (!(member.member instanceof Relation))
412 return false;
413 Relation r = (Relation) member.member;
414 return !r.incomplete;
415 }
416
417 /**
418 * Replies true if there is at least one relation member in this model
419 * which refers to at least on the primitives in <code>primitives</code>.
420 *
421 * @param primitives the collection of primitives
422 * @return true if there is at least one relation member in this model
423 * which refers to at least on the primitives in <code>primitives</code>; false
424 * otherwise
425 */
426 public boolean hasMembersReferringTo(Collection<OsmPrimitive> primitives) {
427 if (primitives == null || primitives.isEmpty())
428 return false;
429 HashSet<OsmPrimitive> referrers = new HashSet<OsmPrimitive>();
430 for(RelationMember member : members) {
431 referrers.add(member.member);
432 }
433 Iterator<OsmPrimitive> it = primitives.iterator();
434 while(it.hasNext()) {
435 OsmPrimitive referred = it.next();
436 if (referrers.contains(referred))
437 return true;
438 }
439 return false;
440 }
441
442 /**
443 * get a node we can link against when sorting members
444 * @param element the element we want to link against
445 * @param linked_element already linked against element
446 * @return the unlinked node if element is a way, the node itself if element is a node, null otherwise
447 */
448 private static Node getUnusedNode(RelationMember element, RelationMember linked_element)
449 {
450 Node result = null;
451
452 if (element.member instanceof Way) {
453 Way w = (Way) element.member;
454 if (linked_element.member instanceof Way) {
455 Way x = (Way) linked_element.member;
456 if ((w.firstNode() == x.firstNode()) || (w.firstNode() == x.lastNode())) {
457 result = w.lastNode();
458 } else {
459 result = w.firstNode();
460 }
461 } else if (linked_element.member instanceof Node) {
462 Node m = (Node) linked_element.member;
463 if (w.firstNode() == m) {
464 result = w.lastNode();
465 } else {
466 result = w.firstNode();
467 }
468 }
469 } else if (element.member instanceof Node) {
470 Node n = (Node) element.member;
471 result = n;
472 }
473
474 return result;
475 }
476
477 void sort() {
478 RelationNodeMap map = new RelationNodeMap(members);
479 Vector<LinkedList<Integer>> segments;
480 LinkedList<Integer> segment;
481 Node startSearchNode;
482 Node endSearchNode;
483 boolean something_done;
484
485 /*
486 * sort any 2 or more connected elements together may be slow with many unconnected members
487 */
488
489 if (map.isEmpty()) {
490 // empty relation or incomplete members
491 return;
492 }
493 segments = new Vector<LinkedList<Integer>>();
494
495 while (!map.isEmpty()) {
496 // find an element for the next segment
497 // try first element in relation if we just started
498 // otherwise, or if first element is another relation, just fetch some element from the
499 // map
500 Integer next;
501 if ((segments.size() == 0) && map.remove(0, members.get(0))) {
502 next = 0;
503 } else {
504 next = map.pop();
505 if (next == null) {
506 break;
507 }
508 }
509
510 segment = new LinkedList<Integer>();
511 segment.add(next);
512 segments.add(segment);
513
514 do {
515 something_done = false;
516 startSearchNode = null;
517 endSearchNode = null;
518 if (segment.size() == 1) {
519 // only one element in segment, so try to link against each linkable node of element
520 RelationMember m = members.get(segment.getFirst());
521 if (m.member instanceof Way) {
522 Way w = (Way) m.member;
523 endSearchNode = w.lastNode();
524 if (w.lastNode() != w.firstNode())
525 {
526 startSearchNode = w.firstNode();
527 }
528 } else if (m.member instanceof Node) {
529 Node n = (Node) m.member;
530 endSearchNode = n;
531 }
532 } else {
533 // add unused node of first element and unused node of last element
534 // start with the first element (compared to next element)
535 startSearchNode = getUnusedNode(members.get(segment.getFirst()), members.get(segment.get(1)));
536
537 // now the same for the last element (compared to previous element)
538 endSearchNode = getUnusedNode(members.get(segment.getLast()), members.get(segment.get(segment.size() - 2)));
539 }
540
541 // let's see if we can find connected elements for endSearchNode and startSearchNode
542 if (startSearchNode != null) {
543 Integer m2 = map.find(startSearchNode, segment.getFirst());
544 if (m2 != null) {
545 segment.add(0, m2);
546 map.remove(m2, members.get(m2));
547 something_done = true;
548 }
549 }
550 if (endSearchNode != null) {
551 Integer m2 = map.find(endSearchNode, segment.getLast());
552 if (m2 != null) {
553 segment.add(segment.size(), m2);
554 map.remove(m2, members.get(m2));
555 something_done = true;
556 }
557 }
558 } while (something_done);
559
560 }
561
562 if (segments.size() > 0) {
563 // append map.remaining() to segments list (as a single segment)
564 segment = new LinkedList<Integer>();
565 segment.addAll(map.getRemaining());
566 segments.add(segment);
567
568 // now we need to actually re-order the relation members
569 ArrayList<RelationMember> newmembers = new ArrayList<RelationMember>();
570 for (LinkedList<Integer> segment2 : segments) {
571 for (Integer p : segment2) {
572 newmembers.add(members.get(p));
573 }
574 }
575 members.clear();
576 members.addAll(newmembers);
577
578 fireTableDataChanged();
579 }
580 }
581
582 // simple version of code that was removed from GenericReleationEditor
583 // no recursion and no forward/backward support
584 // TODO: add back the number of linked elements
585 private WayConnectionType linked(int i) {
586 // this method is aimed at finding out whether the
587 // relation member is "linked" with the next, i.e. whether
588 // (if both are ways) these ways are connected. It should
589 // really produce a much more beautiful output (with a linkage
590 // symbol somehow placed between the two member lines!),
591 // so... FIXME ;-)
592
593 WayConnectionType link = WayConnectionType.none;
594 RelationMember m1 = members.get(i);
595 RelationMember m2 = members.get((i + 1) % members.size());
596 Way way1 = null;
597 Way way2 = null;
598
599 if (m1.member instanceof Way) {
600 way1 = (Way) m1.member;
601 }
602 if (m2.member instanceof Way) {
603 way2 = (Way) m2.member;
604 }
605 if ((way1 != null) && (way2 != null)) {
606 Node way1first = way1.firstNode();
607 Node way1last = way1.lastNode();
608 Node way2first = way2.firstNode();
609 Node way2last = way2.lastNode();
610 if (way1first != null && way2first != null && (way1first == way2first)) {
611 link = WayConnectionType.tail_to_tail;
612 } else if (way1first != null && way2last != null && (way1first == way2last)) {
613 link = WayConnectionType.tail_to_head;
614 } else if (way1last != null && way2first != null && (way1last == way2first)) {
615 link = WayConnectionType.head_to_tail;
616 } else if (way1last != null && way2last != null && (way1last == way2last)) {
617 link = WayConnectionType.head_to_head;
618 }
619 }
620
621 return link;
622 }
623}
Note: See TracBrowser for help on using the repository browser.