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

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

added: improved tag editor grid in relation editor (borrowed code from tag editor plugin)
removed: realEqual() on OsmPrimitive, Node, etc.

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