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

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

replaced calls to JOptionPane.* by calls to OptionPaneUtil.*

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