source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java@ 1748

Last change on this file since 1748 was 1748, checked in by stoecker, 15 years ago

applied relation sort - fix #27789 - patch by cjw

  • Property svn:eol-style set to native
File size: 29.9 KB
Line 
1package org.openstreetmap.josm.gui.dialogs.relation;
2
3import static org.openstreetmap.josm.tools.I18n.marktr;
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Dimension;
7import java.awt.GridBagLayout;
8import java.awt.GridLayout;
9import java.awt.event.ActionEvent;
10import java.awt.event.ActionListener;
11import java.awt.event.KeyEvent;
12import java.io.IOException;
13import java.util.ArrayList;
14import java.util.Arrays;
15import java.util.Collection;
16import java.util.Map.Entry;
17
18import javax.swing.JLabel;
19import javax.swing.JOptionPane;
20import javax.swing.JPanel;
21import javax.swing.JScrollPane;
22import javax.swing.JTabbedPane;
23import javax.swing.JTable;
24import javax.swing.ListSelectionModel;
25import javax.swing.event.ListSelectionEvent;
26import javax.swing.event.ListSelectionListener;
27import javax.swing.event.TableModelEvent;
28import javax.swing.event.TableModelListener;
29import javax.swing.table.DefaultTableModel;
30
31import org.openstreetmap.josm.Main;
32import org.openstreetmap.josm.command.AddCommand;
33import org.openstreetmap.josm.command.ChangeCommand;
34import org.openstreetmap.josm.data.osm.DataSet;
35import org.openstreetmap.josm.data.osm.DataSource;
36import org.openstreetmap.josm.data.osm.Node;
37import org.openstreetmap.josm.data.osm.OsmPrimitive;
38import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
39import org.openstreetmap.josm.data.osm.Relation;
40import org.openstreetmap.josm.data.osm.RelationMember;
41import org.openstreetmap.josm.data.osm.Way;
42import org.openstreetmap.josm.data.osm.visitor.MergeVisitor;
43import org.openstreetmap.josm.gui.OsmPrimitivRenderer;
44import org.openstreetmap.josm.gui.SideButton;
45import org.openstreetmap.josm.gui.dialogs.ConflictDialog;
46import org.openstreetmap.josm.io.OsmServerObjectReader;
47import org.openstreetmap.josm.io.OsmTransferException;
48import org.openstreetmap.josm.tools.GBC;
49import org.openstreetmap.josm.tools.Shortcut;
50import org.xml.sax.SAXException;
51
52enum WayConnectionType
53{
54 none,
55 head_to_head,
56 tail_to_tail,
57 head_to_tail,
58 tail_to_head;
59 @Override
60 public String toString()
61 {
62 String result = "";
63 switch(this)
64 {
65 case head_to_head:
66 result = "-><-";
67 break;
68 case head_to_tail:
69 result = "->->";
70 break;
71 case tail_to_head:
72 result = "<-<-";
73 break;
74 case tail_to_tail:
75 result = "<-->";
76 break;
77 }
78
79 return result;
80 }
81}
82
83/**
84 * This dialog is for editing relations.
85 *
86 * In the basic form, it provides two tables, one with the relation tags
87 * and one with the relation members. (Relation tags can be edited through
88 * the normal properties dialog as well, if you manage to get a relation
89 * selected!)
90 *
91 * @author Frederik Ramm <frederik@remote.org>
92 *
93 */
94public class GenericRelationEditor extends RelationEditor {
95
96 private JLabel status;
97
98 /**
99 * The property data.
100 */
101 private final DefaultTableModel propertyData = new DefaultTableModel() {
102 @Override public boolean isCellEditable(int row, int column) {
103 return true;
104 }
105 @Override public Class<?> getColumnClass(int columnIndex) {
106 return String.class;
107 }
108 };
109
110 /**
111 * The membership data.
112 */
113 private final DefaultTableModel memberData = new DefaultTableModel() {
114 @Override public boolean isCellEditable(int row, int column) {
115 return column == 0;
116 }
117 @Override public Class<?> getColumnClass(int columnIndex) {
118 return columnIndex == 1 ? OsmPrimitive.class : String.class;
119 }
120 };
121
122 /**
123 * The properties and membership lists.
124 */
125 private final JTable propertyTable = new JTable(propertyData);
126 private final JTable memberTable = new JTable(memberData);
127
128 // We need this twice, so cache result
129 protected final static String applyChangesText = tr("Apply Changes");
130
131 /**
132 * Creates a new relation editor for the given relation. The relation
133 * will be saved if the user selects "ok" in the editor.
134 *
135 * If no relation is given, will create an editor for a new relation.
136 *
137 * @param relation relation to edit, or null to create a new one.
138 */
139 public GenericRelationEditor(Relation relation, Collection<RelationMember> selectedMembers )
140 {
141 // Initalizes ExtendedDialog
142 super(relation, selectedMembers);
143
144 JPanel bothTables = setupBasicLayout(selectedMembers);
145
146 JTabbedPane tabPane = new JTabbedPane();
147 tabPane.add(bothTables, tr("Basic"));
148
149 // This sets the minimum size before scrollbars appear on the dialog
150 tabPane.setPreferredSize(new Dimension(100, 100));
151 contentConstraints = GBC.eol().fill().insets(5,10,5,0);
152 setupDialog(tabPane, new String[] { "ok.png", "cancel.png" });
153 // FIXME: Make it remember last screen position
154 setSize(findMaxDialogSize());
155
156 try { setAlwaysOnTop(true); } catch (SecurityException sx) {}
157 setVisible(true);
158 }
159
160
161 /**
162 * Basic Editor panel has two blocks: a tag table at the top and a membership list below
163 * @param selectedMembers
164 * @return a JPanel with the described layout
165 */
166 private JPanel setupBasicLayout(Collection<RelationMember> selectedMembers) {
167 // setting up the properties table
168 propertyData.setColumnIdentifiers(new String[]{tr("Key"),tr("Value")});
169 propertyTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
170 propertyData.addTableModelListener(new TableModelListener() {
171 public void tableChanged(TableModelEvent tme) {
172 if (tme.getType() == TableModelEvent.UPDATE) {
173 int row = tme.getFirstRow();
174
175 if (!(tme.getColumn() == 0 && row == propertyData.getRowCount() -1)) {
176 clone.entrySet().clear();
177 for (int i = 0; i < propertyData.getRowCount(); i++) {
178 String key = propertyData.getValueAt(i, 0).toString();
179 String value = propertyData.getValueAt(i, 1).toString();
180 if (key.length() > 0 && value.length() > 0) {
181 clone.put(key, value);
182 }
183 }
184 refreshTables();
185 }
186 }
187 }
188 });
189 propertyTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
190
191 // setting up the member table
192
193 memberData.setColumnIdentifiers(new String[]{tr("Role"),tr("Occupied By"), tr("linked")});
194 memberTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
195 memberTable.getColumnModel().getColumn(1).setCellRenderer(new OsmPrimitivRenderer());
196 memberData.addTableModelListener(new TableModelListener() {
197 public void tableChanged(TableModelEvent tme) {
198 if (tme.getType() == TableModelEvent.UPDATE && tme.getColumn() == 0) {
199 int row = tme.getFirstRow();
200 clone.members.get(row).role = memberData.getValueAt(row, 0).toString();
201 }
202 }
203 });
204 ListSelectionModel lsm = memberTable.getSelectionModel();
205 lsm.addListSelectionListener(new ListSelectionListener() {
206 public void valueChanged(ListSelectionEvent e) {
207 ArrayList<OsmPrimitive> sel;
208 int cnt = memberTable.getSelectedRowCount();
209 if(cnt > 0)
210 {
211 sel = new ArrayList<OsmPrimitive>(cnt);
212 for (int i : memberTable.getSelectedRows()) {
213 sel.add((OsmPrimitive)memberTable.getValueAt(i, 1));
214 }
215 }
216 else
217 {
218 cnt = memberTable.getRowCount();
219 sel = new ArrayList<OsmPrimitive>(cnt);
220 for (int i = 0; i < cnt; ++i) {
221 sel.add((OsmPrimitive)memberTable.getValueAt(i, 1));
222 }
223 }
224 Main.ds.setSelected(sel);
225 }
226 });
227 memberTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
228
229 // combine both tables and wrap them in a scrollPane
230 JPanel bothTables = new JPanel();
231 bothTables.setLayout(new GridBagLayout());
232 bothTables.add(new JLabel(tr("Tags (empty value deletes tag)")), GBC.eol().fill(GBC.HORIZONTAL));
233 bothTables.add(new JScrollPane(propertyTable), GBC.eop().fill(GBC.BOTH));
234 bothTables.add(status = new JLabel(tr("Members")), GBC.eol().fill(GBC.HORIZONTAL));
235 // this is not exactly pretty but the four buttons simply don't fit in one line.
236 // we should have smaller buttons for situations like this.
237 JPanel buttonPanel = setupBasicButtons();
238
239 bothTables.add(new JScrollPane(memberTable), GBC.eol().fill(GBC.BOTH));
240 bothTables.add(buttonPanel, GBC.eop().fill(GBC.HORIZONTAL));
241 refreshTables();
242
243 if (selectedMembers != null) {
244 boolean scrolled = false;
245 for (int i = 0; i < memberData.getRowCount(); i++) {
246 for (RelationMember m : selectedMembers) {
247 if (m.member == memberData.getValueAt(i, 1)
248 && m.role.equals(memberData.getValueAt(i, 0))) {
249 memberTable.addRowSelectionInterval(i, i);
250 if (!scrolled) {
251 // Ensure that the first member is visible
252 memberTable.scrollRectToVisible(memberTable.getCellRect(i, 0, true));
253 scrolled = true;
254 }
255 break;
256 }
257 }
258
259 }
260 }
261 return bothTables;
262 }
263
264 /**
265 * Creates the buttons for the basic editing layout
266 * @return JPanel with basic buttons
267 */
268 private JPanel setupBasicButtons() {
269 JPanel buttonPanel = new JPanel(new GridLayout(2, 4));
270
271 buttonPanel.add(createButton(marktr("Move Up"), "moveup", tr("Move the currently selected members up"), KeyEvent.VK_N, new ActionListener() {
272 public void actionPerformed(ActionEvent e) {
273 moveMembers(-1);
274 }
275 }));
276
277 buttonPanel.add(createButton(marktr("Add Selected"),"addselected",
278 tr("Add all currently selected objects as members"), KeyEvent.VK_D, new ActionListener() {
279 public void actionPerformed(ActionEvent e) {
280 addSelected();
281 }
282 }));
283
284 buttonPanel.add(createButton(marktr("Remove Selected"),"removeselected",
285 tr("Remove all currently selected objects from relation"), KeyEvent.VK_S, new ActionListener() {
286 public void actionPerformed(ActionEvent e) {
287 deleteSelected();
288 }
289 }));
290
291 buttonPanel.add(createButton(marktr("Sort"), "sort",
292 tr("Sort the selected relation members or the whole list"), KeyEvent.VK_O, new ActionListener() {
293 public void actionPerformed(ActionEvent e) {
294 sort();
295 }
296 }));
297
298 buttonPanel.add(createButton(marktr("Move Down"), "movedown", tr("Move the currently selected members down"), KeyEvent.VK_J, new ActionListener() {
299 public void actionPerformed(ActionEvent e) {
300 moveMembers(1);
301 }
302 }));
303
304 buttonPanel.add(createButton(marktr("Remove"),"remove",
305 tr("Remove the member in the current table row from this relation"), KeyEvent.VK_X, new ActionListener() {
306 public void actionPerformed(ActionEvent e) {
307 int[] rows = memberTable.getSelectedRows();
308 RelationMember mem = new RelationMember();
309 for (int row : rows) {
310 mem.role = memberTable.getValueAt(row, 0).toString();
311 mem.member = (OsmPrimitive) memberTable.getValueAt(row, 1);
312 clone.members.remove(mem);
313 }
314 refreshTables();
315 }
316 }));
317
318 buttonPanel.add(createButton(marktr("Download Members"),"downloadincomplete",
319 tr("Download all incomplete ways and nodes in relation"), KeyEvent.VK_K, new ActionListener() {
320 public void actionPerformed(ActionEvent e) {
321 downloadRelationMembers();
322 refreshTables();
323 }
324 }));
325
326 return buttonPanel;
327 }
328
329 private class NodeCompare implements java.util.Comparator<Node>
330 {
331 public int compare(Node a, Node b)
332 {
333 int result = 0;
334
335 if ((a.id == 0) && (b.id == 0))
336 {
337 if (a.getCoor().lat() == b.getCoor().lat())
338 {
339 result = Double.compare(a.getCoor().lon(), b.getCoor().lon());
340 }
341 else
342 {
343 result = Double.compare(a.getCoor().lat(), b.getCoor().lat());
344 }
345 }
346 else
347 {
348 result = a.compareTo(b);
349 }
350
351 return result;
352 }
353 }
354
355 private void sort() {
356 java.util.TreeMap<Node, java.util.TreeSet<Integer>> points =
357 new java.util.TreeMap<Node, java.util.TreeSet<Integer>>(new NodeCompare());
358 java.util.TreeMap<Node, Integer> nodes =
359 new java.util.TreeMap<Node, Integer>(new NodeCompare());
360 int i;
361 boolean lastWayStartUsed = true;
362
363 // TODO: sort only selected rows
364
365 for (i = 1; i < clone.members.size(); ++i)
366 {
367 RelationMember m = clone.members.get(i);
368 if (m.member.incomplete)
369 {
370 // TODO: emit some message that sorting failed
371 return;
372 }
373 try
374 {
375 Way w = (Way)m.member;
376 if (!points.containsKey(w.firstNode()))
377 {
378 points.put(w.firstNode(), new java.util.TreeSet<Integer>());
379 }
380 points.get(w.firstNode()).add(Integer.valueOf(i));
381
382 if (!points.containsKey(w.lastNode()))
383 {
384 points.put(w.lastNode(), new java.util.TreeSet<Integer>());
385 }
386 points.get(w.lastNode()).add(Integer.valueOf(i));
387 }
388 catch(ClassCastException e1)
389 {
390 try
391 {
392 Node n = (Node)m.member;
393 nodes.put(n, Integer.valueOf(i));
394 }
395 catch(ClassCastException e2)
396 {
397 System.err.println("relation member sort: member " + i + " is not a way or node");
398 return;
399 }
400 }
401 }
402
403 for (i = 0; i < clone.members.size(); ++i)
404 {
405 RelationMember m = clone.members.get(i);
406 Integer m2 = null;
407 Node searchNode = null;
408 try
409 {
410 Way w = (Way)m.member;
411
412 if (lastWayStartUsed || ((i == 0) && !m.role.equals("backward")))
413 {
414 // try end node
415 searchNode = w.lastNode();
416 }
417 else /* if ((m2 == null) && (!lastWayStartUsed || (i == 0))) */
418 {
419 searchNode = w.firstNode();
420 }
421 }
422 catch(ClassCastException e1)
423 {
424 try
425 {
426 Node n = (Node)m.member;
427 searchNode = n;
428 }
429 catch(ClassCastException e2)
430 {
431 // impossible
432 }
433 }
434
435 try {
436 m2 = nodes.get(searchNode);
437 if (m2 == null)
438 {
439 m2 = points.get(searchNode).first();
440 if (m.member == clone.members.get(m2).member)
441 {
442 m2 = points.get(searchNode).last();
443 }
444 }
445 } catch(NullPointerException f) {}
446 catch(java.util.NoSuchElementException e) {}
447
448 if (m2 == null)
449 {
450 // TODO: emit some message that sorting failed
451 System.err.println("relation member sort: could not find linked way or node for member " + i);
452 System.err.println("last way start used = " + lastWayStartUsed);
453 break;
454 }
455
456 if (m2 != null)
457 {
458 try
459 {
460 Way next = (Way)clone.members.get(m2).member;
461 lastWayStartUsed = searchNode.equals(next.firstNode());
462 }
463 catch(ClassCastException e)
464 {
465 }
466 }
467
468 if ((m2 < clone.members.size()) && ((i+1) < clone.members.size()))
469 {
470 RelationMember a = clone.members.get(i+1);
471 RelationMember b = clone.members.get(m2);
472
473 if (m2 != (i+1))
474 {
475 System.err.println("swapping " + (i+1) + " and " + m2);
476 clone.members.set(i+1, b);
477 clone.members.set(m2, a);
478
479 try
480 {
481 if (!points.get(((Way)b.member).firstNode()).remove(m2))
482 {
483 System.err.println("relation member sort: could not remove start mapping for " + m2);
484 }
485 if (!points.get(((Way)b.member).lastNode()).remove(m2))
486 {
487 System.err.println("relation member sort: could not remove end mapping for " + m2);
488 }
489 }
490 catch(ClassCastException e1)
491 {
492 nodes.remove(b.member);
493 }
494
495 try
496 {
497 points.get(((Way)a.member).firstNode()).add(m2);
498 points.get(((Way)a.member).lastNode()).add(m2);
499 }
500 catch(ClassCastException e1)
501 {
502 nodes.put((Node)a.member, m2);
503 }
504 }
505 try
506 {
507 if (!points.get(((Way)a.member).firstNode()).remove(i+1))
508 {
509 System.err.println("relation member sort: could not remove start mapping for " + (i+1));
510 }
511 if (!points.get(((Way)a.member).lastNode()).remove(i+1))
512 {
513 System.err.println("relation member sort: could not remove end mapping for " + (i+1));
514 }
515 }
516 catch(ClassCastException e1)
517 {
518 nodes.remove(a.member);
519 }
520 }
521 }
522
523 refreshTables();
524 }
525
526
527 /**
528 * This function saves the user's changes. Must be invoked manually.
529 */
530 private void applyChanges() {
531 if (GenericRelationEditor.this.relation == null) {
532 // If the user wanted to create a new relation, but hasn't added any members or
533 // tags, don't add an empty relation
534 if(clone.members.size() == 0 && !clone.isTagged())
535 return;
536 Main.main.undoRedo.add(new AddCommand(clone));
537 DataSet.fireSelectionChanged(Main.ds.getSelected());
538 } else if (!GenericRelationEditor.this.relation.realEqual(clone, true)) {
539 Main.main.undoRedo.add(new ChangeCommand(GenericRelationEditor.this.relation, clone));
540 DataSet.fireSelectionChanged(Main.ds.getSelected());
541 }
542 }
543
544 @Override
545 protected void buttonAction(ActionEvent evt) {
546 String a = evt.getActionCommand();
547 if(applyChangesText.equals(a)) {
548 applyChanges();
549 }
550
551 setVisible(false);
552 }
553
554 @Override
555 protected Dimension findMaxDialogSize() {
556 // FIXME: Make it remember dialog size
557 return new Dimension(600, 500);
558 }
559
560 private void refreshTables() {
561 // re-load property data
562 int numLinked = 0;
563
564 propertyData.setRowCount(0);
565 for (Entry<String, String> e : clone.entrySet()) {
566 propertyData.addRow(new Object[]{e.getKey(), e.getValue()});
567 }
568 propertyData.addRow(new Object[]{"", ""});
569
570 // re-load membership data
571
572 memberData.setRowCount(0);
573 for (int i=0; i<clone.members.size(); i++) {
574
575 // this whole section is aimed at finding out whether the
576 // relation member is "linked" with the next, i.e. whether
577 // (if both are ways) these ways are connected. It should
578 // really produce a much more beautiful output (with a linkage
579 // symbol somehow places between the two member lines!), and
580 // it should cache results, so... FIXME ;-)
581
582 RelationMember em = clone.members.get(i);
583 WayConnectionType link = WayConnectionType.none;
584 RelationMember m = em;
585 RelationMember way1 = null;
586 RelationMember way2 = null;
587 int depth = 0;
588
589 while (m != null && depth < 10) {
590 if (m.member instanceof Way) {
591 way1 = m;
592 break;
593 } else if (m.member instanceof Relation) {
594 if (m.member == this.relation) {
595 break;
596 }
597 m = ((Relation)m.member).lastMember();
598 depth++;
599 } else {
600 break;
601 }
602 }
603 if (way1 != null) {
604 int next = (i+1) % clone.members.size();
605 while (next != i) {
606 m = clone.members.get(next);
607 next = (next + 1) % clone.members.size();
608 depth = 0;
609 while (m != null && depth < 10) {
610 if (m.member instanceof Way) {
611 way2 = m;
612 break;
613 } else if (m.member instanceof Relation) {
614 if (m.member == this.relation) {
615 break;
616 }
617 m = ((Relation)(m.member)).firstMember();
618 depth++;
619 } else {
620 break;
621 }
622 }
623 if (way2 != null) {
624 break;
625 }
626 }
627 }
628 if (way2 != null) {
629 Node way1first = ((Way)(way1.member)).firstNode();
630 Node way1last = ((Way)(way1.member)).lastNode();
631 Node way2first = ((Way)(way2.member)).firstNode();
632 Node way2last = ((Way)(way2.member)).lastNode();
633 if (way1.role.equals("forward")) {
634 way1first = null;
635 } else if (way1.role.equals("backward")) {
636 way1last = null;
637 }
638 if (way2.role.equals("forward")) {
639 way2last = null;
640 } else if (way2.role.equals("backward")) {
641 way2first = null;
642 }
643
644 if (way1first != null && way2first != null && way1first.equals(way2first)) {
645 link = WayConnectionType.tail_to_tail;
646 } else if (way1first != null && way2last != null && way1first.equals(way2last)) {
647 link = WayConnectionType.tail_to_head;
648 } else if (way1last != null && way2first != null && way1last.equals(way2first)) {
649 link = WayConnectionType.head_to_tail;
650 } else if (way1last != null && way2last != null && way1last.equals(way2last)) {
651 link = WayConnectionType.head_to_head;
652 }
653
654 // end of section to determine linkedness.
655 if (link != WayConnectionType.none)
656 {
657 ++numLinked;
658 }
659
660 }
661 memberData.addRow(new Object[]{em.role, em.member, link});
662 }
663 status.setText(tr("Members: {0} (linked: {1})", clone.members.size(), numLinked));
664 }
665
666 private SideButton createButton(String name, String iconName, String tooltip, int mnemonic, ActionListener actionListener) {
667 return
668 new SideButton(name, iconName, "relationEditor",
669 tooltip,
670 Shortcut.registerShortcut("relationeditor:"+iconName,
671 tr("Relation Editor: {0}", name == null ? tooltip : name),
672 mnemonic,
673 Shortcut.GROUP_MNEMONIC),
674 actionListener
675 );
676 }
677
678 private void addSelected() {
679 for (OsmPrimitive p : Main.ds.getSelected()) {
680 // ordered relations may have the same member multiple times.
681 // TODO: visual indication of the fact that one is there more than once?
682 RelationMember em = new RelationMember();
683 em.member = p;
684 em.role = "";
685 // when working with ordered relations, we make an effort to
686 // add the element before the first selected member.
687 int[] rows = memberTable.getSelectedRows();
688 if (rows.length > 0) {
689 clone.members.add(rows[0], em);
690 } else {
691 clone.members.add(em);
692 }
693 }
694 refreshTables();
695 }
696
697 private void deleteSelected() {
698 for (OsmPrimitive p : Main.ds.getSelected()) {
699 Relation c = new Relation(clone);
700 for (RelationMember rm : c.members) {
701 if (rm.member == p)
702 {
703 RelationMember mem = new RelationMember();
704 mem.role = rm.role;
705 mem.member = rm.member;
706 clone.members.remove(mem);
707 }
708 }
709 }
710 refreshTables();
711 }
712
713 private void moveMembers(int direction) {
714 int[] rows = memberTable.getSelectedRows();
715 if (rows.length == 0) return;
716
717 // check if user attempted to move anything beyond the boundary of the list
718 if (rows[0] + direction < 0) return;
719 if (rows[rows.length-1] + direction >= clone.members.size()) return;
720
721 RelationMember m[] = new RelationMember[clone.members.size()];
722
723 // first move all selected rows from the member list into a new array,
724 // displaced by the move amount
725 for (Integer i: rows) {
726 m[i+direction] = clone.members.get(i);
727 clone.members.set(i, null);
728 }
729
730 // now fill the empty spots in the destination array with the remaining
731 // elements.
732 int i = 0;
733 for (RelationMember rm : clone.members) {
734 if (rm != null) {
735 while (m[i] != null) {
736 i++;
737 }
738 m[i++] = rm;
739 }
740 }
741
742 // and write the array back into the member list.
743 clone.members.clear();
744 clone.members.addAll(Arrays.asList(m));
745 refreshTables();
746 ListSelectionModel lsm = memberTable.getSelectionModel();
747 lsm.setValueIsAdjusting(true);
748 for (Integer j: rows) {
749 lsm.addSelectionInterval(j + direction, j + direction);
750 }
751 lsm.setValueIsAdjusting(false);
752 }
753
754 private void downloadRelationMembers() {
755 boolean download = false;
756 for (RelationMember member : clone.members) {
757 if (member.member.incomplete) {
758 download = true;
759 break;
760 }
761 }
762 if (download) {
763 OsmServerObjectReader reader = new OsmServerObjectReader(clone.id, OsmPrimitiveType.RELATION, true);
764 try {
765 DataSet dataSet = reader.parseOsm();
766 if (dataSet != null) {
767 final MergeVisitor visitor = new MergeVisitor(Main.main
768 .editLayer().data, dataSet);
769 visitor.merge();
770
771 // copy the merged layer's data source info
772 for (DataSource src : dataSet.dataSources) {
773 Main.main.editLayer().data.dataSources.add(src);
774 }
775 Main.main.editLayer().fireDataChange();
776
777 if (visitor.getConflicts().isEmpty())
778 return;
779 final ConflictDialog dlg = Main.map.conflictDialog;
780 dlg.add(visitor.getConflicts());
781 JOptionPane.showMessageDialog(Main.parent,
782 tr("There were conflicts during import."));
783 if (!dlg.isVisible()) {
784 dlg.action
785 .actionPerformed(new ActionEvent(this, 0, ""));
786 }
787 }
788 } catch(OsmTransferException e) {
789 e.printStackTrace();
790 if (e.getCause() != null) {
791 if (e.getCause() instanceof SAXException) {
792 JOptionPane.showMessageDialog(this,tr("Error parsing server response.")+": "+e.getCause().getMessage(),
793 tr("Error"), JOptionPane.ERROR_MESSAGE);
794 } else if(e.getCause() instanceof IOException) {
795 JOptionPane.showMessageDialog(this,tr("Cannot connect to server.")+": "+e.getCause().getMessage(),
796 tr("Error"), JOptionPane.ERROR_MESSAGE);
797 }
798 } else {
799 JOptionPane.showMessageDialog(this,tr("Error when communicating with server.")+": "+e.getMessage(),
800 tr("Error"), JOptionPane.ERROR_MESSAGE);
801 }
802 }
803 }
804 }
805}
Note: See TracBrowser for help on using the repository browser.