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

Last change on this file since 2700 was 2700, checked in by mjulius, 14 years ago

fixes #4226 - add reverse members command to relation editor

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