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

Last change on this file since 1855 was 1855, checked in by Gubaer, 16 years ago

fixed NPE

  • Property svn:eol-style set to native
File size: 49.6 KB
Line 
1package org.openstreetmap.josm.gui.dialogs.relation;
2
3import static org.openstreetmap.josm.tools.I18n.tr;
4
5import java.awt.BorderLayout;
6import java.awt.Dialog;
7import java.awt.Dimension;
8import java.awt.FlowLayout;
9import java.awt.GridBagConstraints;
10import java.awt.GridBagLayout;
11import java.awt.Insets;
12import java.awt.event.ActionEvent;
13import java.awt.event.ComponentAdapter;
14import java.awt.event.ComponentEvent;
15import java.awt.event.FocusAdapter;
16import java.awt.event.FocusEvent;
17import java.awt.event.KeyEvent;
18import java.awt.event.MouseAdapter;
19import java.awt.event.MouseEvent;
20import java.awt.event.WindowAdapter;
21import java.awt.event.WindowEvent;
22import java.io.IOException;
23import java.util.ArrayList;
24import java.util.Collection;
25import java.util.Collections;
26import java.util.HashSet;
27import java.util.Iterator;
28import java.util.List;
29import java.util.logging.Logger;
30
31import javax.swing.AbstractAction;
32import javax.swing.BorderFactory;
33import javax.swing.JButton;
34import javax.swing.JComponent;
35import javax.swing.JDialog;
36import javax.swing.JLabel;
37import javax.swing.JOptionPane;
38import javax.swing.JPanel;
39import javax.swing.JScrollPane;
40import javax.swing.JSplitPane;
41import javax.swing.JTabbedPane;
42import javax.swing.JTable;
43import javax.swing.JTextField;
44import javax.swing.KeyStroke;
45import javax.swing.SwingUtilities;
46import javax.swing.event.ChangeEvent;
47import javax.swing.event.ChangeListener;
48import javax.swing.event.DocumentEvent;
49import javax.swing.event.DocumentListener;
50import javax.swing.event.ListSelectionEvent;
51import javax.swing.event.ListSelectionListener;
52import javax.swing.event.TableModelEvent;
53import javax.swing.event.TableModelListener;
54
55import org.openstreetmap.josm.Main;
56import org.openstreetmap.josm.command.AddCommand;
57import org.openstreetmap.josm.command.ChangeCommand;
58import org.openstreetmap.josm.data.conflict.Conflict;
59import org.openstreetmap.josm.data.osm.DataSet;
60import org.openstreetmap.josm.data.osm.DataSource;
61import org.openstreetmap.josm.data.osm.OsmPrimitive;
62import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
63import org.openstreetmap.josm.data.osm.Relation;
64import org.openstreetmap.josm.data.osm.RelationMember;
65import org.openstreetmap.josm.data.osm.visitor.MergeVisitor;
66import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
67import org.openstreetmap.josm.gui.OptionPaneUtil;
68import org.openstreetmap.josm.gui.PleaseWaitRunnable;
69import org.openstreetmap.josm.gui.PrimitiveNameFormatter;
70import org.openstreetmap.josm.gui.SideButton;
71import org.openstreetmap.josm.gui.dialogs.relation.ac.AutoCompletionCache;
72import org.openstreetmap.josm.gui.dialogs.relation.ac.AutoCompletionList;
73import org.openstreetmap.josm.gui.layer.OsmDataLayer;
74import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor;
75import org.openstreetmap.josm.gui.progress.ProgressMonitor;
76import org.openstreetmap.josm.io.OsmApi;
77import org.openstreetmap.josm.io.OsmServerObjectReader;
78import org.openstreetmap.josm.io.OsmTransferException;
79import org.openstreetmap.josm.tools.ImageProvider;
80import org.openstreetmap.josm.tools.Shortcut;
81import org.xml.sax.SAXException;
82
83/**
84 * This dialog is for editing relations.
85 *
86 * In the basic form, it provides two tables, one with the relation tags and one with the relation
87 * members. (Relation tags can be edited through the normal properties dialog as well, if you manage
88 * to get a relation selected!)
89 *
90 * @author Frederik Ramm <frederik@remote.org>
91 *
92 */
93public class GenericRelationEditor extends RelationEditor {
94
95 static private final Logger logger = Logger.getLogger(GenericRelationEditor.class.getName());
96 static private final Dimension DEFAULT_EDITOR_DIMENSION = new Dimension(700, 500);
97
98 /** the tag table and its model */
99 private TagEditorModel tagEditorModel;
100 private TagTable tagTable;
101 private AutoCompletionCache acCache;
102 private AutoCompletionList acList;
103 private ReferringRelationsBrowser referrerBrowser;
104 private ReferringRelationsBrowserModel referrerModel;
105
106 /** the member table */
107 private MemberTable memberTable;
108 private MemberTableModel memberTableModel;
109
110 /** the model for the selection table */
111 private SelectionTableModel selectionTableModel;
112
113 private JTextField tfRole;
114
115 /**
116 * Creates a new relation editor for the given relation. The relation will be saved if the user
117 * selects "ok" in the editor.
118 *
119 * If no relation is given, will create an editor for a new relation.
120 *
121 * @param layer the {@see OsmDataLayer} the new or edited relation belongs to
122 * @param relation relation to edit, or null to create a new one.
123 * @param selectedMembers a collection of members which shall be selected initially
124 */
125 public GenericRelationEditor(OsmDataLayer layer, Relation relation, Collection<RelationMember> selectedMembers) {
126 super(layer, relation, selectedMembers);
127
128 // initialize the autocompletion infrastructure
129 //
130 acCache = AutoCompletionCache.getCacheForLayer(Main.map.mapView.getEditLayer());
131 acList = new AutoCompletionList();
132
133 // init the various models
134 //
135 tagEditorModel = new TagEditorModel();
136 memberTableModel = new MemberTableModel();
137 selectionTableModel = new SelectionTableModel(getLayer());
138 referrerModel = new ReferringRelationsBrowserModel(relation);
139
140 // populate the models
141 //
142 if (relation != null) {
143 this.tagEditorModel.initFromPrimitive(relation);
144 this.memberTableModel.populate(relation);
145 } else {
146 tagEditorModel.clear();
147 this.memberTableModel.populate(null);
148 }
149 memberTableModel.setSelectedMembers(selectedMembers);
150 tagEditorModel.ensureOneTag();
151
152 JSplitPane pane = buildSplitPane();
153 pane.setPreferredSize(new Dimension(100, 100));
154
155 JPanel pnl = new JPanel();
156 pnl.setLayout(new BorderLayout());
157 pnl.add(pane, BorderLayout.CENTER);
158 pnl.setBorder(BorderFactory.createRaisedBevelBorder());
159
160 getContentPane().setLayout(new BorderLayout());
161 JTabbedPane tabbedPane = new JTabbedPane();
162 tabbedPane.add(tr("Tags and Members"), pnl);
163 referrerBrowser = new ReferringRelationsBrowser(getLayer(), referrerModel, this);
164 tabbedPane.add(tr("Parent Relations"), referrerBrowser);
165 tabbedPane.add(tr("Child Relations"), new ChildRelationBrowser(getLayer(), relation));
166 tabbedPane.addChangeListener(
167 new ChangeListener() {
168 public void stateChanged(ChangeEvent e) {
169 JTabbedPane sourceTabbedPane = (JTabbedPane) e.getSource();
170 int index = sourceTabbedPane.getSelectedIndex();
171 String title = sourceTabbedPane.getTitleAt(index);
172 if (title.equals(tr("Parent Relations"))) {
173 referrerBrowser.init();
174 }
175 }
176 }
177 );
178
179 getContentPane().add(tabbedPane, BorderLayout.CENTER);
180 getContentPane().add(buildOkCancelButtonPanel(), BorderLayout.SOUTH);
181
182 setSize(findMaxDialogSize());
183 try {
184 setAlwaysOnTop(true);
185 } catch (SecurityException e) {
186 logger.warning(tr("Caught security exception for setAlwaysOnTop(). Ignoring. Exception was: {0}", e
187 .toString()));
188 }
189
190 addWindowListener(
191 new WindowAdapter() {
192 @Override
193 public void windowOpened(WindowEvent e) {
194 cleanSelfReferences();
195 }
196 }
197 );
198
199 }
200
201 /**
202 * builds the panel with the OK and the Cancel button
203 *
204 * @return the panel with the OK and the Cancel button
205 */
206 protected JPanel buildOkCancelButtonPanel() {
207 JPanel pnl = new JPanel();
208 pnl.setLayout(new FlowLayout(FlowLayout.CENTER));
209
210 pnl.add(new SideButton(new OKAction()));
211 pnl.add(new SideButton(new CancelAction()));
212
213 return pnl;
214 }
215
216 /**
217 * build the panel with the buttons on the left
218 *
219 * @return
220 */
221 protected JPanel buildTagEditorControlPanel() {
222 JPanel pnl = new JPanel();
223 pnl.setLayout(new GridBagLayout());
224
225 GridBagConstraints gc = new GridBagConstraints();
226 gc.gridx = 0;
227 gc.gridy = 0;
228 gc.gridheight = 1;
229 gc.gridwidth = 1;
230 gc.insets = new Insets(0, 5, 0, 5);
231 gc.fill = GridBagConstraints.HORIZONTAL;
232 gc.anchor = GridBagConstraints.CENTER;
233 gc.weightx = 0.0;
234 gc.weighty = 0.0;
235
236 // -----
237 AddTagAction addTagAction = new AddTagAction();
238 pnl.add(new JButton(addTagAction), gc);
239
240 // -----
241 gc.gridy = 1;
242 DeleteTagAction deleteTagAction = new DeleteTagAction();
243 tagTable.getSelectionModel().addListSelectionListener(deleteTagAction);
244 pnl.add(new JButton(deleteTagAction), gc);
245
246 // ------
247 // just grab the remaining space
248 gc.gridy = 2;
249 gc.weighty = 1.0;
250 gc.fill = GridBagConstraints.BOTH;
251 pnl.add(new JPanel(), gc);
252 return pnl;
253 }
254
255 /**
256 * builds the panel with the tag editor
257 *
258 * @return the panel with the tag editor
259 */
260 protected JPanel buildTagEditorPanel() {
261 JPanel pnl = new JPanel();
262 pnl.setLayout(new GridBagLayout());
263
264 // setting up the tag table
265 //
266 tagTable = new TagTable(tagEditorModel);
267 acCache.initFromJOSMDataset();
268 TagCellEditor editor = ((TagCellEditor) tagTable.getColumnModel().getColumn(0).getCellEditor());
269 editor.setAutoCompletionCache(acCache);
270 editor.setAutoCompletionList(acList);
271 editor = ((TagCellEditor) tagTable.getColumnModel().getColumn(1).getCellEditor());
272 editor.setAutoCompletionCache(acCache);
273 editor.setAutoCompletionList(acList);
274
275 final JScrollPane scrollPane = new JScrollPane(tagTable);
276
277 // this adapters ensures that the width of the tag table columns is adjusted
278 // to the width of the scroll pane viewport. Also tried to overwrite
279 // getPreferredViewportSize() in JTable, but did not work.
280 //
281 scrollPane.addComponentListener(new ComponentAdapter() {
282 @Override
283 public void componentResized(ComponentEvent e) {
284 super.componentResized(e);
285 Dimension d = scrollPane.getViewport().getExtentSize();
286 tagTable.adjustColumnWidth(d.width);
287 }
288 });
289
290 GridBagConstraints gc = new GridBagConstraints();
291 gc.gridx = 0;
292 gc.gridy = 0;
293 gc.gridheight = 1;
294 gc.gridwidth = 3;
295 gc.fill = GridBagConstraints.HORIZONTAL;
296 gc.anchor = GridBagConstraints.FIRST_LINE_START;
297 gc.weightx = 1.0;
298 gc.weighty = 0.0;
299 pnl.add(new JLabel(tr("Tags")), gc);
300
301 gc.gridx = 0;
302 gc.gridy = 1;
303 gc.gridheight = 1;
304 gc.gridwidth = 1;
305 gc.fill = GridBagConstraints.VERTICAL;
306 gc.anchor = GridBagConstraints.NORTHWEST;
307 gc.weightx = 0.0;
308 gc.weighty = 1.0;
309 pnl.add(buildTagEditorControlPanel(), gc);
310
311 gc.gridx = 1;
312 gc.gridy = 1;
313 gc.fill = GridBagConstraints.BOTH;
314 gc.anchor = GridBagConstraints.CENTER;
315 gc.weightx = 0.8;
316 gc.weighty = 1.0;
317 pnl.add(scrollPane, gc);
318 return pnl;
319 }
320
321 /**
322 * builds the panel for the relation member editor
323 *
324 * @return the panel for the relation member editor
325 */
326 protected JPanel buildMemberEditorPanel() {
327 final JPanel pnl = new JPanel();
328 pnl.setLayout(new GridBagLayout());
329 // setting up the member table
330 memberTable = new MemberTable(memberTableModel);
331
332 memberTable.getSelectionModel().addListSelectionListener(new SelectionSynchronizer());
333 memberTable.addMouseListener(new MemberTableDblClickAdapter());
334
335 final JScrollPane scrollPane = new JScrollPane(memberTable);
336 // this adapters ensures that the width of the tag table columns is adjusted
337 // to the width of the scroll pane viewport. Also tried to overwrite
338 // getPreferredViewportSize() in JTable, but did not work.
339 //
340 scrollPane.addComponentListener(new ComponentAdapter() {
341 @Override
342 public void componentResized(ComponentEvent e) {
343 super.componentResized(e);
344 Dimension d = scrollPane.getViewport().getExtentSize();
345 memberTable.adjustColumnWidth(d.width);
346 }
347 });
348
349 GridBagConstraints gc = new GridBagConstraints();
350 gc.gridx = 0;
351 gc.gridy = 0;
352 gc.gridheight = 1;
353 gc.gridwidth = 3;
354 gc.fill = GridBagConstraints.HORIZONTAL;
355 gc.anchor = GridBagConstraints.FIRST_LINE_START;
356 gc.weightx = 1.0;
357 gc.weighty = 0.0;
358 pnl.add(new JLabel(tr("Members")), gc);
359
360 gc.gridx = 0;
361 gc.gridy = 1;
362 gc.gridheight = 1;
363 gc.gridwidth = 1;
364 gc.fill = GridBagConstraints.VERTICAL;
365 gc.anchor = GridBagConstraints.NORTHWEST;
366 gc.weightx = 0.0;
367 gc.weighty = 1.0;
368 pnl.add(buildLeftButtonPanel(), gc);
369
370 gc.gridx = 1;
371 gc.gridy = 1;
372 gc.fill = GridBagConstraints.BOTH;
373 gc.anchor = GridBagConstraints.CENTER;
374 gc.weightx = 0.6;
375 gc.weighty = 1.0;
376 pnl.add(scrollPane, gc);
377
378 JPanel pnl2 = new JPanel();
379 pnl2.setLayout(new GridBagLayout());
380
381 gc.gridx = 0;
382 gc.gridy = 0;
383 gc.gridheight = 1;
384 gc.gridwidth = 3;
385 gc.fill = GridBagConstraints.HORIZONTAL;
386 gc.anchor = GridBagConstraints.FIRST_LINE_START;
387 gc.weightx = 1.0;
388 gc.weighty = 0.0;
389 pnl2.add(new JLabel(tr("Selection")), gc);
390
391 gc.gridx = 0;
392 gc.gridy = 1;
393 gc.gridheight = 1;
394 gc.gridwidth = 1;
395 gc.fill = GridBagConstraints.VERTICAL;
396 gc.anchor = GridBagConstraints.NORTHWEST;
397 gc.weightx = 0.0;
398 gc.weighty = 1.0;
399 pnl2.add(buildSelectionControlButtonPanel(), gc);
400
401 gc.gridx = 1;
402 gc.gridy = 1;
403 gc.weightx = 1.0;
404 gc.weighty = 1.0;
405 gc.fill = GridBagConstraints.BOTH;
406 pnl2.add(buildSelectionTablePanel(), gc);
407
408 final JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
409 splitPane.setLeftComponent(pnl);
410 splitPane.setRightComponent(pnl2);
411 splitPane.setOneTouchExpandable(false);
412 addWindowListener(new WindowAdapter() {
413 @Override
414 public void windowOpened(WindowEvent e) {
415 // has to be called when the window is visible, otherwise
416 // no effect
417 splitPane.setDividerLocation(0.6);
418 }
419 });
420
421
422 JPanel pnl3 = new JPanel();
423 pnl3.setLayout(new BorderLayout());
424 pnl3.add(splitPane, BorderLayout.CENTER);
425 pnl3.add(buildButtonPanel(), BorderLayout.SOUTH);
426 return pnl3;
427 }
428
429 /**
430 * builds the panel with the table displaying the currently selected primitives
431 *
432 * @return
433 */
434 protected JPanel buildSelectionTablePanel() {
435 JPanel pnl = new JPanel();
436 pnl.setLayout(new BorderLayout());
437 JTable tbl = new JTable(selectionTableModel, new SelectionTableColumnModel(memberTableModel));
438 tbl.setEnabled(false);
439 JScrollPane pane = new JScrollPane(tbl);
440 pnl.add(pane, BorderLayout.CENTER);
441 return pnl;
442 }
443
444 /**
445 * builds the {@see JSplitPane} which divides the editor in an upper and a lower half
446 *
447 * @return the split panel
448 */
449 protected JSplitPane buildSplitPane() {
450 final JSplitPane pane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
451 pane.setTopComponent(buildTagEditorPanel());
452 pane.setBottomComponent(buildMemberEditorPanel());
453 pane.setOneTouchExpandable(true);
454 addWindowListener(new WindowAdapter() {
455 @Override
456 public void windowOpened(WindowEvent e) {
457 // has to be called when the window is visible, otherwise
458 // no effect
459 pane.setDividerLocation(0.3);
460 }
461 });
462 return pane;
463 }
464
465 /**
466 * build the panel with the buttons on the left
467 *
468 * @return
469 */
470 protected JPanel buildLeftButtonPanel() {
471 JPanel pnl = new JPanel();
472 pnl.setLayout(new GridBagLayout());
473
474 GridBagConstraints gc = new GridBagConstraints();
475 gc.gridx = 0;
476 gc.gridy = 0;
477 gc.gridheight = 1;
478 gc.gridwidth = 1;
479 gc.insets = new Insets(0, 5, 0, 5);
480 gc.fill = GridBagConstraints.HORIZONTAL;
481 gc.anchor = GridBagConstraints.CENTER;
482 gc.weightx = 0.0;
483 gc.weighty = 0.0;
484 MoveUpAction moveUpAction = new MoveUpAction();
485 memberTableModel.getSelectionModel().addListSelectionListener(moveUpAction);
486 pnl.add(new JButton(moveUpAction), gc);
487
488 // -----
489 gc.gridy = 1;
490 MoveDownAction moveDownAction = new MoveDownAction();
491 memberTableModel.getSelectionModel().addListSelectionListener(moveDownAction);
492 pnl.add(new JButton(moveDownAction), gc);
493
494 // ------
495 gc.gridy = 2;
496 RemoveAction removeSelectedAction = new RemoveAction();
497 memberTable.getSelectionModel().addListSelectionListener(removeSelectedAction);
498 pnl.add(new JButton(removeSelectedAction), gc);
499
500 // ------
501 gc.gridy = 3;
502 SortAction sortAction = new SortAction();
503 pnl.add(new JButton(sortAction), gc);
504
505 // ------
506 // just grab the remaining space
507 gc.gridy = 4;
508 gc.weighty = 1.0;
509 gc.fill = GridBagConstraints.BOTH;
510 pnl.add(new JPanel(), gc);
511 return pnl;
512 }
513
514 /**
515 * build the panel with the buttons for adding or removing the current selection
516 *
517 * @return
518 */
519 protected JPanel buildSelectionControlButtonPanel() {
520 JPanel pnl = new JPanel();
521 pnl.setLayout(new GridBagLayout());
522
523 GridBagConstraints gc = new GridBagConstraints();
524 gc.gridx = 0;
525 gc.gridy = 0;
526 gc.gridheight = 1;
527 gc.gridwidth = 1;
528 gc.insets = new Insets(0, 5, 0, 5);
529 gc.fill = GridBagConstraints.HORIZONTAL;
530 gc.anchor = GridBagConstraints.CENTER;
531 gc.weightx = 0.0;
532 gc.weighty = 0.0;
533 AddSelectedAtEndAction addSelectedAtEndAction = new AddSelectedAtEndAction();
534 selectionTableModel.addTableModelListener(addSelectedAtEndAction);
535 pnl.add(new JButton(addSelectedAtEndAction), gc);
536
537 // -----
538 gc.gridy = 1;
539 RemoveSelectedAction removeSelectedAction = new RemoveSelectedAction();
540 selectionTableModel.addTableModelListener(removeSelectedAction);
541 pnl.add(new JButton(removeSelectedAction), gc);
542
543 // ------
544 // just grab the remaining space
545 gc.gridy = 2;
546 gc.weighty = 1.0;
547 gc.fill = GridBagConstraints.BOTH;
548 pnl.add(new JPanel(), gc);
549
550 // -----
551 gc.gridy = 3;
552 gc.weighty = 0.0;
553 AddSelectedAtStartAction addSelectionAction = new AddSelectedAtStartAction();
554 selectionTableModel.addTableModelListener(addSelectionAction);
555 pnl.add(new JButton(addSelectionAction), gc);
556
557 // -----
558 gc.gridy = 4;
559 AddSelectedBeforeSelection addSelectedBeforeSelectionAction = new AddSelectedBeforeSelection();
560 selectionTableModel.addTableModelListener(addSelectedBeforeSelectionAction);
561 memberTableModel.getSelectionModel().addListSelectionListener(addSelectedBeforeSelectionAction);
562 pnl.add(new JButton(addSelectedBeforeSelectionAction), gc);
563
564 // -----
565 gc.gridy = 5;
566 AddSelectedAfterSelection addSelectedAfterSelectionAction = new AddSelectedAfterSelection();
567 selectionTableModel.addTableModelListener(addSelectedAfterSelectionAction);
568 memberTableModel.getSelectionModel().addListSelectionListener(addSelectedAfterSelectionAction);
569 pnl.add(new JButton(addSelectedAfterSelectionAction), gc);
570
571 return pnl;
572 }
573
574 /**
575 * Creates the buttons for the basic editing layout
576 * @return {@see JPanel} with basic buttons
577 */
578 protected JPanel buildButtonPanel() {
579 JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
580 buttonPanel.add(new SideButton(new DownlaodAction()));
581 buttonPanel.add(new JLabel(tr("Role:")));
582 tfRole = new JTextField(10);
583 tfRole.addFocusListener(new FocusAdapter() {
584 @Override
585 public void focusGained(FocusEvent e) {
586 tfRole.selectAll();
587 }
588 });
589 buttonPanel.add(tfRole);
590 SetRoleAction setRoleAction = new SetRoleAction();
591 memberTableModel.getSelectionModel().addListSelectionListener(setRoleAction);
592 buttonPanel.add(new SideButton(setRoleAction));
593 tfRole.getDocument().addDocumentListener(setRoleAction);
594
595 // --- copy relation action
596 buttonPanel.add(new SideButton(new DuplicateRelationAction()));
597
598 // -- edit action
599 EditAction editAction = new EditAction();
600 memberTableModel.getSelectionModel().addListSelectionListener(editAction);
601 buttonPanel.add(new SideButton(editAction));
602 return buttonPanel;
603 }
604
605 @Override
606 protected Dimension findMaxDialogSize() {
607 // FIXME: Make it remember dialog size
608 return new Dimension(700, 500);
609 }
610
611 /**
612 * Asynchronously download the members of the currently edited relation
613 *
614 */
615 private void downloadRelationMembers() {
616 if (!memberTableModel.hasIncompleteMembers())
617 return;
618 Main.worker.submit(new DownloadTask(this));
619 }
620
621 @Override
622 public void dispose() {
623 selectionTableModel.unregister();
624 super.dispose();
625 }
626
627 @Override
628 public void setVisible(boolean b) {
629 super.setVisible(b);
630 if (!b) {
631 dispose();
632 }
633 }
634
635 /**
636 * checks whether the current relation has members referring to itself. If so,
637 * warns the users and provides an option for removing these members.
638 *
639 */
640 protected void cleanSelfReferences() {
641 ArrayList<OsmPrimitive> toCheck = new ArrayList<OsmPrimitive>();
642 toCheck.add(getRelation());
643 if (memberTableModel.hasMembersReferringTo(toCheck)) {
644 int ret = ConditionalOptionPaneUtil.showOptionDialog(
645 "clean_relation_self_references",
646 Main.parent,
647 tr("<html>There is at least one member in this relation referring<br>"
648 + "to the relation itself.<br>"
649 + "This creates circular dependencies and is dicuraged.<br>"
650 + "How do you want to proceed with circular dependencies?</html>"),
651 tr("Warning"),
652 JOptionPane.YES_NO_OPTION,
653 JOptionPane.WARNING_MESSAGE,
654 new String[]{tr("Remove them, clean up relation"), tr("Ignore them, leave relation as is")},
655 tr("Remove them, clean up relation")
656 );
657 switch(ret) {
658 case ConditionalOptionPaneUtil.DIALOG_DISABLED_OPTION: return;
659 case JOptionPane.CLOSED_OPTION: return;
660 case JOptionPane.NO_OPTION: return;
661 case JOptionPane.YES_OPTION:
662 memberTableModel.removeMembersReferringTo(toCheck);
663 break;
664 }
665 }
666 }
667
668 class AddAbortException extends Exception {
669 }
670
671 abstract class AddFromSelectionAction extends AbstractAction {
672 private PrimitiveNameFormatter nameFormatter = new PrimitiveNameFormatter();
673
674 protected boolean isPotentialDuplicate(OsmPrimitive primitive) {
675 return memberTableModel.hasMembersReferringTo(Collections.singleton(primitive));
676 }
677
678 protected boolean confirmAddingPrimtive(OsmPrimitive primitive) throws AddAbortException {
679 String msg = tr("<html>This relation already has one or more members referring to<br>"
680 + "the primitive ''{0}''<br>"
681 + "<br>"
682 + "Do you really want to add another relation member?</html>",
683 nameFormatter.getName(primitive)
684 );
685 int ret = ConditionalOptionPaneUtil.showOptionDialog(
686 "add_primitive_to_relation",
687 Main.parent,
688 msg,
689 tr("Multiple members referring to same primitive"),
690 JOptionPane.YES_NO_CANCEL_OPTION,
691 JOptionPane.WARNING_MESSAGE,
692 null,
693 null
694 );
695 switch(ret) {
696 case ConditionalOptionPaneUtil.DIALOG_DISABLED_OPTION : return true;
697 case JOptionPane.YES_OPTION: return true;
698 case JOptionPane.NO_OPTION: return false;
699 case JOptionPane.CLOSED_OPTION: return false;
700 case JOptionPane.CANCEL_OPTION: throw new AddAbortException();
701 }
702 // should not happen
703 return false;
704 }
705
706 protected void warnOfCircularReferences(OsmPrimitive primitive) {
707 String msg = tr("<html>You are trying to add a relation to itself.<br>"
708 + "<br>"
709 + "This creates circular references and is therefore discouraged.<br>"
710 + "Skipping relation ''{0}''.</html>",
711 this.nameFormatter.getName(primitive)
712 );
713 OptionPaneUtil.showMessageDialog(
714 Main.parent,
715 msg,
716 tr("Warning"),
717 JOptionPane.WARNING_MESSAGE
718 );
719 }
720
721 protected List<OsmPrimitive> filterConfirmedPrimitives(List<OsmPrimitive> primitives) throws AddAbortException {
722 if (primitives == null || primitives.isEmpty())
723 return primitives;
724 ArrayList<OsmPrimitive> ret = new ArrayList<OsmPrimitive>();
725 Iterator<OsmPrimitive> it = primitives.iterator();
726 while(it.hasNext()) {
727 OsmPrimitive primitive = it.next();
728 if (primitive instanceof Relation && getRelation() != null && getRelation().equals(primitive)) {
729 warnOfCircularReferences(primitive);
730 continue;
731 }
732 if (isPotentialDuplicate(primitive)) {
733 if (confirmAddingPrimtive(primitive)) {
734 ret.add(primitive);
735 }
736 continue;
737 } else {
738 ret.add(primitive);
739 }
740 }
741 return ret;
742 }
743 }
744
745 class AddSelectedAtStartAction extends AddFromSelectionAction implements TableModelListener {
746 public AddSelectedAtStartAction() {
747 putValue(SHORT_DESCRIPTION,
748 tr("Add all primitives selected in the current dataset before the first member"));
749 putValue(SMALL_ICON, ImageProvider.get("dialogs/conflict", "copystartright"));
750 // putValue(NAME, tr("Add Selected"));
751 refreshEnabled();
752 }
753
754 protected void refreshEnabled() {
755 setEnabled(selectionTableModel.getRowCount() > 0 && memberTableModel.getRowCount() > 0);
756 }
757
758 public void actionPerformed(ActionEvent e) {
759 try {
760 List<OsmPrimitive> toAdd = filterConfirmedPrimitives(selectionTableModel.getSelection());
761 memberTableModel.addMembersAtBeginning(toAdd);
762 } catch(AddAbortException ex) {
763 // do nothing
764 }
765 }
766
767 public void tableChanged(TableModelEvent e) {
768 refreshEnabled();
769 }
770 }
771
772 class AddSelectedAtEndAction extends AddFromSelectionAction implements TableModelListener {
773 public AddSelectedAtEndAction() {
774 putValue(SHORT_DESCRIPTION, tr("Add all primitives selected in the current dataset after the last member"));
775 putValue(SMALL_ICON, ImageProvider.get("dialogs/conflict", "copyendright"));
776 // putValue(NAME, tr("Add Selected"));
777 refreshEnabled();
778 }
779
780 protected void refreshEnabled() {
781 setEnabled(selectionTableModel.getRowCount() > 0);
782 }
783
784 public void actionPerformed(ActionEvent e) {
785 try {
786 List<OsmPrimitive> toAdd = filterConfirmedPrimitives(selectionTableModel.getSelection());
787 memberTableModel.addMembersAtEnd(toAdd);
788 } catch(AddAbortException ex) {
789 // do nothing
790 }
791 }
792
793 public void tableChanged(TableModelEvent e) {
794 refreshEnabled();
795 }
796 }
797
798 class AddSelectedBeforeSelection extends AddFromSelectionAction implements TableModelListener, ListSelectionListener {
799 public AddSelectedBeforeSelection() {
800 putValue(SHORT_DESCRIPTION,
801 tr("Add all primitives selected in the current dataset before the first selected member"));
802 putValue(SMALL_ICON, ImageProvider.get("dialogs/conflict", "copybeforecurrentright"));
803 // putValue(NAME, tr("Add Selected"));
804 refreshEnabled();
805 }
806
807 protected void refreshEnabled() {
808 setEnabled(selectionTableModel.getRowCount() > 0
809 && memberTableModel.getSelectionModel().getMinSelectionIndex() >= 0);
810 }
811
812 public void actionPerformed(ActionEvent e) {
813 try {
814 List<OsmPrimitive> toAdd = filterConfirmedPrimitives(selectionTableModel.getSelection());
815 memberTableModel.addMembersBeforeIdx(toAdd, memberTableModel
816 .getSelectionModel().getMinSelectionIndex());
817 } catch(AddAbortException ex) {
818 // do nothing
819 }
820
821
822 }
823
824 public void tableChanged(TableModelEvent e) {
825 refreshEnabled();
826 }
827
828 public void valueChanged(ListSelectionEvent e) {
829 refreshEnabled();
830 }
831 }
832
833 class AddSelectedAfterSelection extends AddFromSelectionAction implements TableModelListener, ListSelectionListener {
834 public AddSelectedAfterSelection() {
835 putValue(SHORT_DESCRIPTION,
836 tr("Add all primitives selected in the current dataset after the last selected member"));
837 putValue(SMALL_ICON, ImageProvider.get("dialogs/conflict", "copyaftercurrentright"));
838 // putValue(NAME, tr("Add Selected"));
839 refreshEnabled();
840 }
841
842 protected void refreshEnabled() {
843 setEnabled(selectionTableModel.getRowCount() > 0
844 && memberTableModel.getSelectionModel().getMinSelectionIndex() >= 0);
845 }
846
847 public void actionPerformed(ActionEvent e) {
848 try {
849 List<OsmPrimitive> toAdd = filterConfirmedPrimitives(selectionTableModel.getSelection());
850 memberTableModel.addMembersAfterIdx(toAdd, memberTableModel
851 .getSelectionModel().getMaxSelectionIndex());
852 } catch(AddAbortException ex) {
853 // do nothing
854 }
855 }
856
857 public void tableChanged(TableModelEvent e) {
858 refreshEnabled();
859 }
860
861 public void valueChanged(ListSelectionEvent e) {
862 refreshEnabled();
863 }
864 }
865
866 class RemoveSelectedAction extends AbstractAction implements TableModelListener {
867 public RemoveSelectedAction() {
868 putValue(SHORT_DESCRIPTION, tr("Remove all currently selected objects from relation"));
869 putValue(SMALL_ICON, ImageProvider.get("dialogs", "removeselected"));
870 // putValue(NAME, tr("Remove Selected"));
871 Shortcut.registerShortcut("relationeditor:removeselected", tr("Relation Editor: Remove Selected"),
872 KeyEvent.VK_S, Shortcut.GROUP_MNEMONIC);
873
874 updateEnabledState();
875 }
876
877 protected void updateEnabledState() {
878 DataSet ds = getLayer().data;
879 if (ds == null || ds.getSelected().isEmpty()) {
880 setEnabled(false);
881 return;
882 }
883 // only enable the action if we have members referring to the
884 // selected primitives
885 //
886 setEnabled(memberTableModel.hasMembersReferringTo(ds.getSelected()));
887 }
888
889 public void actionPerformed(ActionEvent e) {
890 memberTableModel.removeMembersReferringTo(selectionTableModel.getSelection());
891 }
892
893 public void tableChanged(TableModelEvent e) {
894 updateEnabledState();
895 }
896 }
897
898 class SortAction extends AbstractAction {
899 public SortAction() {
900 putValue(SHORT_DESCRIPTION, tr("Sort the relation members"));
901 putValue(SMALL_ICON, ImageProvider.get("dialogs", "sort"));
902 // putValue(NAME, tr("Sort"));
903 Shortcut.registerShortcut("relationeditor:sort", tr("Relation Editor: Sort"), KeyEvent.VK_T,
904 Shortcut.GROUP_MNEMONIC);
905 //setEnabled(false);
906 }
907
908 public void actionPerformed(ActionEvent e) {
909 memberTableModel.sort();
910 }
911 }
912
913 class MoveUpAction extends AbstractAction implements ListSelectionListener {
914 public MoveUpAction() {
915 putValue(SHORT_DESCRIPTION, tr("Move the currently selected members up"));
916 putValue(SMALL_ICON, ImageProvider.get("dialogs", "moveup"));
917 // putValue(NAME, tr("Move Up"));
918 Shortcut.registerShortcut("relationeditor:moveup", tr("Relation Editor: Move Up"), KeyEvent.VK_N,
919 Shortcut.GROUP_MNEMONIC);
920 setEnabled(false);
921 }
922
923 public void actionPerformed(ActionEvent e) {
924 memberTableModel.moveUp(memberTable.getSelectedRows());
925 }
926
927 public void valueChanged(ListSelectionEvent e) {
928 setEnabled(memberTableModel.canMoveUp(memberTable.getSelectedRows()));
929 }
930 }
931
932 class MoveDownAction extends AbstractAction implements ListSelectionListener {
933 public MoveDownAction() {
934 putValue(SHORT_DESCRIPTION, tr("Move the currently selected members down"));
935 putValue(SMALL_ICON, ImageProvider.get("dialogs", "movedown"));
936 // putValue(NAME, tr("Move Down"));
937 Shortcut.registerShortcut("relationeditor:moveup", tr("Relation Editor: Move Down"), KeyEvent.VK_J,
938 Shortcut.GROUP_MNEMONIC);
939 setEnabled(false);
940 }
941
942 public void actionPerformed(ActionEvent e) {
943 memberTableModel.moveDown(memberTable.getSelectedRows());
944 }
945
946 public void valueChanged(ListSelectionEvent e) {
947 setEnabled(memberTableModel.canMoveDown(memberTable.getSelectedRows()));
948 }
949 }
950
951 class RemoveAction extends AbstractAction implements ListSelectionListener {
952 public RemoveAction() {
953 putValue(SHORT_DESCRIPTION, tr("Remove the member in the current table row from this relation"));
954 putValue(SMALL_ICON, ImageProvider.get("dialogs", "remove"));
955 // putValue(NAME, tr("Remove"));
956 Shortcut.registerShortcut("relationeditor:remove", tr("Relation Editor: Remove"), KeyEvent.VK_J,
957 Shortcut.GROUP_MNEMONIC);
958 setEnabled(false);
959 }
960
961 public void actionPerformed(ActionEvent e) {
962 memberTableModel.remove(memberTable.getSelectedRows());
963 }
964
965 public void valueChanged(ListSelectionEvent e) {
966 setEnabled(memberTableModel.canRemove(memberTable.getSelectedRows()));
967 }
968 }
969
970 class OKAction extends AbstractAction {
971 /**
972 * apply updates to a new relation
973 */
974 protected void applyNewRelation() {
975 // If the user wanted to create a new relation, but hasn't added any members or
976 // tags, don't add an empty relation
977 if (memberTableModel.getRowCount() == 0 && tagEditorModel.getKeys().isEmpty())
978 return;
979 Relation newRelation = new Relation();
980 tagEditorModel.applyToPrimitive(newRelation);
981 memberTableModel.applyToRelation(newRelation);
982 Main.main.undoRedo.add(new AddCommand(newRelation));
983 DataSet.fireSelectionChanged(getLayer().data.getSelected());
984 }
985
986 /**
987 * apply updates to an existing relation
988 */
989 protected void applyExistingRelation() {
990 Relation editedRelation = new Relation(getRelation());
991 tagEditorModel.applyToPrimitive(editedRelation);
992 memberTableModel.applyToRelation(editedRelation);
993 if (isDirtyRelation()) {
994 Conflict<Relation> conflict = new Conflict<Relation>(getRelation(), editedRelation);
995 getLayer().getConflicts().add(conflict);
996 OptionPaneUtil.showMessageDialog(
997 Main.parent,
998 tr("<html>The relation has changed outside of the editor.<br>"
999 + "Your edits can't be applied directly, a conflict has been created instead.</html>"),
1000 tr("Warning"),
1001 JOptionPane.WARNING_MESSAGE
1002 );
1003 } else {
1004 tagEditorModel.applyToPrimitive(editedRelation);
1005 memberTableModel.applyToRelation(editedRelation);
1006 Main.main.undoRedo.add(new ChangeCommand(getRelation(), editedRelation));
1007 DataSet.fireSelectionChanged(getLayer().data.getSelected());
1008 }
1009 }
1010
1011 /**
1012 * Applies updates
1013 */
1014 protected void applyChanges() {
1015 if (getRelation() == null) {
1016 applyNewRelation();
1017 } else if (!memberTableModel.hasSameMembersAs(getRelationSnapshot())
1018 || tagEditorModel.isDirty()) {
1019 applyExistingRelation();
1020 }
1021 }
1022
1023 public OKAction() {
1024 putValue(SHORT_DESCRIPTION, tr("Apply the updates and close the dialog"));
1025 putValue(SMALL_ICON, ImageProvider.get("ok"));
1026 putValue(NAME, tr("Apply"));
1027 setEnabled(true);
1028 }
1029
1030 public void actionPerformed(ActionEvent e) {
1031 applyChanges();
1032 setVisible(false);
1033 }
1034 }
1035
1036 class CancelAction extends AbstractAction {
1037 public CancelAction() {
1038 putValue(SHORT_DESCRIPTION, tr("Cancel the updates and close the dialog"));
1039 putValue(SMALL_ICON, ImageProvider.get("cancel"));
1040 putValue(NAME, tr("Cancel"));
1041
1042 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
1043 .put(KeyStroke.getKeyStroke("ESCAPE"), "ESCAPE");
1044 getRootPane().getActionMap().put("ESCAPE", this);
1045 setEnabled(true);
1046 }
1047
1048 public void actionPerformed(ActionEvent e) {
1049 setVisible(false);
1050 }
1051 }
1052
1053 class AddTagAction extends AbstractAction {
1054 public AddTagAction() {
1055 putValue(SHORT_DESCRIPTION, tr("Add an empty tag"));
1056 putValue(SMALL_ICON, ImageProvider.get("dialogs", "add"));
1057 // putValue(NAME, tr("Cancel"));
1058 setEnabled(true);
1059 }
1060
1061 public void actionPerformed(ActionEvent e) {
1062 tagEditorModel.appendNewTag();
1063 }
1064 }
1065
1066 class DeleteTagAction extends AbstractAction implements ListSelectionListener {
1067 public DeleteTagAction() {
1068 putValue(SHORT_DESCRIPTION, tr("Delete the currently selected tags"));
1069 putValue(SMALL_ICON, ImageProvider.get("dialogs", "delete"));
1070 // putValue(NAME, tr("Cancel"));
1071 refreshEnabled();
1072 }
1073
1074 public void actionPerformed(ActionEvent e) {
1075 run();
1076 }
1077
1078 /**
1079 * delete a selection of tag names
1080 */
1081 protected void deleteTagNames() {
1082 int[] rows = tagTable.getSelectedRows();
1083 tagEditorModel.deleteTagNames(rows);
1084 }
1085
1086 /**
1087 * delete a selection of tag values
1088 */
1089 protected void deleteTagValues() {
1090 int[] rows = tagTable.getSelectedRows();
1091 tagEditorModel.deleteTagValues(rows);
1092 }
1093
1094 /**
1095 * delete a selection of tags
1096 */
1097 protected void deleteTags() {
1098 tagEditorModel.deleteTags(tagTable.getSelectedRows());
1099 }
1100
1101 public void run() {
1102 if (!isEnabled())
1103 return;
1104 if (tagTable.getSelectedColumnCount() == 1) {
1105 if (tagTable.getSelectedColumn() == 0) {
1106 deleteTagNames();
1107 } else if (tagTable.getSelectedColumn() == 1) {
1108 deleteTagValues();
1109 } else
1110 // should not happen
1111 //
1112 throw new IllegalStateException("unexpected selected clolumn: getSelectedColumn() is "
1113 + tagTable.getSelectedColumn());
1114 } else if (tagTable.getSelectedColumnCount() == 2) {
1115 deleteTags();
1116 }
1117 if (tagEditorModel.getRowCount() == 0) {
1118 tagEditorModel.ensureOneTag();
1119 }
1120 }
1121
1122 protected void refreshEnabled() {
1123 setEnabled(tagTable.getSelectedRowCount() > 0 || tagTable.getSelectedColumnCount() > 0);
1124 }
1125
1126 public void valueChanged(ListSelectionEvent e) {
1127 refreshEnabled();
1128 }
1129 }
1130
1131 class DownlaodAction extends AbstractAction {
1132 public DownlaodAction() {
1133 putValue(SHORT_DESCRIPTION, tr("Download all incomplete ways and nodes in relation"));
1134 putValue(SMALL_ICON, ImageProvider.get("dialogs", "downloadincomplete"));
1135 putValue(NAME, tr("Download Members"));
1136 Shortcut.registerShortcut("relationeditor:downloadincomplete", tr("Relation Editor: Download Members"),
1137 KeyEvent.VK_K, Shortcut.GROUP_MNEMONIC);
1138 setEnabled(true);
1139 }
1140
1141 public void actionPerformed(ActionEvent e) {
1142 downloadRelationMembers();
1143 }
1144 }
1145
1146 class SetRoleAction extends AbstractAction implements ListSelectionListener, DocumentListener {
1147 public SetRoleAction() {
1148 putValue(SHORT_DESCRIPTION, tr("Sets a role for the selected members"));
1149 // FIXME: find better icon
1150 putValue(SMALL_ICON, ImageProvider.get("ok"));
1151 putValue(NAME, tr("Apply Role"));
1152 refreshEnabled();
1153 }
1154
1155 protected void refreshEnabled() {
1156 setEnabled(memberTable.getSelectedRowCount() > 0 && !tfRole.getText().equals(""));
1157 }
1158
1159 public void actionPerformed(ActionEvent e) {
1160 memberTableModel.updateRole(memberTable.getSelectedRows(), tfRole.getText());
1161 }
1162
1163 public void valueChanged(ListSelectionEvent e) {
1164 refreshEnabled();
1165 }
1166
1167 public void changedUpdate(DocumentEvent e) {
1168 refreshEnabled();
1169 }
1170
1171 public void insertUpdate(DocumentEvent e) {
1172 refreshEnabled();
1173 }
1174
1175 public void removeUpdate(DocumentEvent e) {
1176 refreshEnabled();
1177 }
1178 }
1179
1180 /**
1181 * Creates a new relation with a copy of the current editor state
1182 *
1183 */
1184 class DuplicateRelationAction extends AbstractAction {
1185 public DuplicateRelationAction() {
1186 putValue(SHORT_DESCRIPTION, tr("Create a copy of this relation and open it in another editor window"));
1187 // FIXME provide an icon
1188 putValue(SMALL_ICON, ImageProvider.get("duplicate"));
1189 putValue(NAME, tr("Duplicate"));
1190 setEnabled(true);
1191 }
1192
1193 public void actionPerformed(ActionEvent e) {
1194 Relation copy = new Relation();
1195 tagEditorModel.applyToPrimitive(copy);
1196 memberTableModel.applyToRelation(copy);
1197 getLayer().data.addPrimitive(copy);
1198 getLayer().fireDataChange();
1199 RelationEditor editor = RelationEditor.getEditor(getLayer(), copy, memberTableModel.getSelectedMembers());
1200 editor.setVisible(true);
1201 }
1202 }
1203
1204 /**
1205 * Action for editing the currently selected relation
1206 *
1207 *
1208 */
1209 class EditAction extends AbstractAction implements ListSelectionListener {
1210 public EditAction() {
1211 putValue(SHORT_DESCRIPTION, tr("Edit the relation the currently selected relation member refers to"));
1212 putValue(SMALL_ICON, ImageProvider.get("dialogs", "edit"));
1213 putValue(NAME, tr("Edit"));
1214 refreshEnabled();
1215 }
1216
1217 protected void refreshEnabled() {
1218 setEnabled(memberTable.getSelectedRowCount() == 1
1219 && memberTableModel.isEditableRelation(memberTable.getSelectedRow()));
1220 }
1221
1222 public void run() {
1223 int idx = memberTable.getSelectedRow();
1224 if (idx < 0)
1225 return;
1226 OsmPrimitive primitive = memberTableModel.getReferredPrimitive(idx);
1227 if (!(primitive instanceof Relation))
1228 return;
1229 Relation r = (Relation) primitive;
1230 if (r.incomplete)
1231 return;
1232 RelationEditor editor = RelationEditor.getEditor(getLayer(), r, null);
1233 editor.setVisible(true);
1234 }
1235
1236 public void actionPerformed(ActionEvent e) {
1237 if (!isEnabled())
1238 return;
1239 run();
1240 }
1241
1242 public void valueChanged(ListSelectionEvent e) {
1243 refreshEnabled();
1244 }
1245 }
1246
1247 class MemberTableDblClickAdapter extends MouseAdapter {
1248 @Override
1249 public void mouseClicked(MouseEvent e) {
1250 if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2) {
1251 new EditAction().run();
1252 }
1253 }
1254 }
1255
1256 class SelectionSynchronizer implements ListSelectionListener {
1257 public void valueChanged(ListSelectionEvent e) {
1258 ArrayList<OsmPrimitive> sel;
1259 int cnt = memberTable.getSelectedRowCount();
1260 if (cnt <= 0)
1261 return;
1262 sel = new ArrayList<OsmPrimitive>(cnt);
1263 for (int i : memberTable.getSelectedRows()) {
1264 sel.add(memberTableModel.getReferredPrimitive(i));
1265 }
1266 getLayer().data.setSelected(sel);
1267 }
1268 }
1269
1270 /**
1271 * The asynchronous task for downloading relation members.
1272 *
1273 *
1274 */
1275 class DownloadTask extends PleaseWaitRunnable {
1276 private boolean cancelled;
1277 private int conflictsCount;
1278 private Exception lastException;
1279
1280 public DownloadTask(Dialog parent) {
1281 super(tr("Download relation members"), new PleaseWaitProgressMonitor(parent), false /*
1282 * don't
1283 * ignore
1284 * exception
1285 */);
1286 }
1287
1288 @Override
1289 protected void cancel() {
1290 cancelled = true;
1291 OsmApi.getOsmApi().cancel();
1292 }
1293
1294 protected void showLastException() {
1295 String msg = lastException.getMessage();
1296 if (msg == null) {
1297 msg = lastException.toString();
1298 }
1299 OptionPaneUtil.showMessageDialog(
1300 Main.parent,
1301 msg,
1302 tr("Error"),
1303 JOptionPane.ERROR_MESSAGE
1304 );
1305 }
1306
1307 @Override
1308 protected void finish() {
1309 if (cancelled)
1310 return;
1311 memberTableModel.updateMemberReferences(getLayer().data);
1312 if (lastException != null) {
1313 showLastException();
1314 }
1315
1316 if (conflictsCount > 0) {
1317 OptionPaneUtil.showMessageDialog(
1318 Main.parent,
1319 tr("There were {0} conflicts during import.", conflictsCount),
1320 tr("Warning"),
1321 JOptionPane.WARNING_MESSAGE
1322 );
1323 }
1324 }
1325
1326 @Override
1327 protected void realRun() throws SAXException, IOException, OsmTransferException {
1328 try {
1329 progressMonitor.indeterminateSubTask("");
1330 OsmServerObjectReader reader = new OsmServerObjectReader(getRelation().id, OsmPrimitiveType.RELATION,
1331 true);
1332 DataSet dataSet = reader.parseOsm(progressMonitor
1333 .createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
1334 if (dataSet != null) {
1335 final MergeVisitor visitor = new MergeVisitor(getLayer().data, dataSet);
1336 visitor.merge();
1337
1338 // copy the merged layer's data source info
1339 for (DataSource src : dataSet.dataSources) {
1340 getLayer().data.dataSources.add(src);
1341 }
1342 // FIXME: this is necessary because there are dialogs listening
1343 // for DataChangeEvents which manipulate Swing components on this
1344 // thread.
1345 //
1346 SwingUtilities.invokeLater(new Runnable() {
1347 public void run() {
1348 getLayer().fireDataChange();
1349 }
1350 });
1351 if (!visitor.getConflicts().isEmpty()) {
1352 getLayer().getConflicts().add(visitor.getConflicts());
1353 conflictsCount = visitor.getConflicts().size();
1354 }
1355 }
1356 } catch (Exception e) {
1357 if (cancelled) {
1358 System.out.println(tr("Warning: ignoring exception because task is cancelled. Exception: {0}", e
1359 .toString()));
1360 return;
1361 }
1362 lastException = e;
1363 }
1364 }
1365 }
1366
1367
1368}
Note: See TracBrowser for help on using the repository browser.