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

Last change on this file since 2581 was 2581, checked in by Gubaer, 14 years ago

fixed #4109: Sorting button in relation editor is disabled
fixed #4097: Sort button in Relation Dialog is always disabled.

  • Property svn:eol-style set to native
File size: 53.6 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
324 gc.gridx = 1;
325 gc.gridy = 2;
326 gc.fill = GridBagConstraints.BOTH;
327 gc.anchor = GridBagConstraints.CENTER;
328 gc.weightx = 1.0;
329 gc.weighty = 0.0;
330 pnl.add(p3, gc);
331
332 JPanel pnl2 = new JPanel();
333 pnl2.setLayout(new GridBagLayout());
334
335 gc.gridx = 0;
336 gc.gridy = 0;
337 gc.gridheight = 1;
338 gc.gridwidth = 3;
339 gc.fill = GridBagConstraints.HORIZONTAL;
340 gc.anchor = GridBagConstraints.FIRST_LINE_START;
341 gc.weightx = 1.0;
342 gc.weighty = 0.0;
343 pnl2.add(new JLabel(tr("Selection")), gc);
344
345 gc.gridx = 0;
346 gc.gridy = 1;
347 gc.gridheight = 1;
348 gc.gridwidth = 1;
349 gc.fill = GridBagConstraints.VERTICAL;
350 gc.anchor = GridBagConstraints.NORTHWEST;
351 gc.weightx = 0.0;
352 gc.weighty = 1.0;
353 pnl2.add(buildSelectionControlButtonPanel(), gc);
354
355 gc.gridx = 1;
356 gc.gridy = 1;
357 gc.weightx = 1.0;
358 gc.weighty = 1.0;
359 gc.fill = GridBagConstraints.BOTH;
360 pnl2.add(buildSelectionTablePanel(), gc);
361
362 final JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
363 splitPane.setLeftComponent(pnl);
364 splitPane.setRightComponent(pnl2);
365 splitPane.setOneTouchExpandable(false);
366 addWindowListener(new WindowAdapter() {
367 @Override
368 public void windowOpened(WindowEvent e) {
369 // has to be called when the window is visible, otherwise
370 // no effect
371 splitPane.setDividerLocation(0.6);
372 }
373 });
374
375 JPanel pnl3 = new JPanel();
376 pnl3.setLayout(new BorderLayout());
377 pnl3.add(splitPane, BorderLayout.CENTER);
378 return pnl3;
379 }
380
381 /**
382 * builds the panel with the table displaying the currently selected primitives
383 *
384 * @return
385 */
386 protected JPanel buildSelectionTablePanel() {
387 JPanel pnl = new JPanel();
388 pnl.setLayout(new BorderLayout());
389 SelectionTable tbl = new SelectionTable(selectionTableModel, new SelectionTableColumnModel(memberTableModel));
390 tbl.setMemberTableModel(memberTableModel);
391 JScrollPane pane = new JScrollPane(tbl);
392 pnl.add(pane, BorderLayout.CENTER);
393 return pnl;
394 }
395
396 /**
397 * builds the {@see JSplitPane} which divides the editor in an upper and a lower half
398 *
399 * @return the split panel
400 */
401 protected JSplitPane buildSplitPane() {
402 final JSplitPane pane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
403 pane.setTopComponent(buildTagEditorPanel());
404 pane.setBottomComponent(buildMemberEditorPanel());
405 pane.setOneTouchExpandable(true);
406 addWindowListener(new WindowAdapter() {
407 @Override
408 public void windowOpened(WindowEvent e) {
409 // has to be called when the window is visible, otherwise
410 // no effect
411 pane.setDividerLocation(0.3);
412 }
413 });
414 return pane;
415 }
416
417 /**
418 * build the panel with the buttons on the left
419 *
420 * @return
421 */
422 protected JToolBar buildLeftButtonPanel() {
423 JToolBar tb = new JToolBar();
424 tb.setOrientation(JToolBar.VERTICAL);
425 tb.setFloatable(false);
426
427 // -- move up action
428 MoveUpAction moveUpAction = new MoveUpAction();
429 memberTableModel.getSelectionModel().addListSelectionListener(moveUpAction);
430 tb.add(moveUpAction);
431
432 // -- move down action
433 MoveDownAction moveDownAction = new MoveDownAction();
434 memberTableModel.getSelectionModel().addListSelectionListener(moveDownAction);
435 tb.add(moveDownAction);
436
437 tb.addSeparator();
438
439 // -- edit action
440 EditAction editAction = new EditAction();
441 memberTableModel.getSelectionModel().addListSelectionListener(editAction);
442 tb.add(editAction);
443
444 // -- delete action
445 RemoveAction removeSelectedAction = new RemoveAction();
446 memberTable.getSelectionModel().addListSelectionListener(removeSelectedAction);
447 tb.add(removeSelectedAction);
448
449 tb.addSeparator();
450 // -- sort action
451 SortAction sortAction = new SortAction();
452 memberTableModel.addTableModelListener(sortAction);
453 tb.add(sortAction);
454
455 tb.addSeparator();
456
457 // -- download action
458 DownloadIncompleteMembersAction downloadIncompleteMembersAction = new DownloadIncompleteMembersAction();
459 memberTable.getModel().addTableModelListener(downloadIncompleteMembersAction);
460 tb.add(downloadIncompleteMembersAction);
461
462 // -- download selected action
463 DownloadSelectedIncompleteMembersAction downloadSelectedIncompleteMembersAction = new DownloadSelectedIncompleteMembersAction();
464 memberTable.getModel().addTableModelListener(downloadSelectedIncompleteMembersAction);
465 memberTable.getSelectionModel().addListSelectionListener(downloadSelectedIncompleteMembersAction);
466 tb.add(downloadSelectedIncompleteMembersAction);
467
468 return tb;
469 }
470
471 /**
472 * build the panel with the buttons for adding or removing the current selection
473 *
474 * @return
475 */
476 protected JToolBar buildSelectionControlButtonPanel() {
477 JToolBar tb = new JToolBar(JToolBar.VERTICAL);
478 tb.setFloatable(false);
479
480
481 // -- add at end action
482 AddSelectedAtEndAction addSelectedAtEndAction = new AddSelectedAtEndAction();
483 selectionTableModel.addTableModelListener(addSelectedAtEndAction);
484 tb.add(addSelectedAtEndAction);
485
486 // -- select members action
487 SelectedMembersForSelectionAction selectMembersForSelectionAction = new SelectedMembersForSelectionAction();
488 selectionTableModel.addTableModelListener(selectMembersForSelectionAction);
489 memberTableModel.addTableModelListener(selectMembersForSelectionAction);
490 tb.add(selectMembersForSelectionAction);
491
492 tb.addSeparator();
493
494 // -- remove selected action
495 RemoveSelectedAction removeSelectedAction = new RemoveSelectedAction();
496 selectionTableModel.addTableModelListener(removeSelectedAction);
497 tb.add(removeSelectedAction);
498
499 // -- select action
500 SelectPrimitivesForSelectedMembersAction selectAction = new SelectPrimitivesForSelectedMembersAction();
501 memberTable.getSelectionModel().addListSelectionListener(selectAction);
502 tb.add(selectAction);
503
504 tb.addSeparator();
505
506 // -- add at start action
507 AddSelectedAtStartAction addSelectionAction = new AddSelectedAtStartAction();
508 selectionTableModel.addTableModelListener(addSelectionAction);
509 tb.add(addSelectionAction);
510
511 // -- add before selected action
512 AddSelectedBeforeSelection addSelectedBeforeSelectionAction = new AddSelectedBeforeSelection();
513 selectionTableModel.addTableModelListener(addSelectedBeforeSelectionAction);
514 memberTableModel.getSelectionModel().addListSelectionListener(addSelectedBeforeSelectionAction);
515 tb.add(addSelectedBeforeSelectionAction);
516
517 // -- add after selected action
518 AddSelectedAfterSelection addSelectedAfterSelectionAction = new AddSelectedAfterSelection();
519 selectionTableModel.addTableModelListener(addSelectedAfterSelectionAction);
520 memberTableModel.getSelectionModel().addListSelectionListener(addSelectedAfterSelectionAction);
521 tb.add(addSelectedAfterSelectionAction);
522
523 return tb;
524 }
525
526 @Override
527 protected Dimension findMaxDialogSize() {
528 // FIXME: Make it remember dialog size
529 return new Dimension(700, 650);
530 }
531
532 @Override
533 public void setVisible(boolean visible) {
534 if (visible) {
535 tagEditorPanel.initAutoCompletion(getLayer());
536 }
537 super.setVisible(visible);
538 if (!visible) {
539 // make sure all registered listeners are unregistered
540 //
541 selectionTableModel.unregister();
542 DataSet.selListeners.remove(memberTableModel);
543 DataSet.selListeners.remove(selectionTableModel);
544 getLayer().data.removeDataSetListener(memberTableModel);
545 getLayer().listenerDataChanged.remove(memberTableModel);
546 dispose();
547 }
548 }
549
550 /**
551 * checks whether the current relation has members referring to itself. If so,
552 * warns the users and provides an option for removing these members.
553 *
554 */
555 protected void cleanSelfReferences() {
556 ArrayList<OsmPrimitive> toCheck = new ArrayList<OsmPrimitive>();
557 toCheck.add(getRelation());
558 if (memberTableModel.hasMembersReferringTo(toCheck)) {
559 int ret = ConditionalOptionPaneUtil.showOptionDialog(
560 "clean_relation_self_references",
561 Main.parent,
562 tr("<html>There is at least one member in this relation referring<br>"
563 + "to the relation itself.<br>"
564 + "This creates circular dependencies and is discouraged.<br>"
565 + "How do you want to proceed with circular dependencies?</html>"),
566 tr("Warning"),
567 JOptionPane.YES_NO_OPTION,
568 JOptionPane.WARNING_MESSAGE,
569 new String[]{tr("Remove them, clean up relation"), tr("Ignore them, leave relation as is")},
570 tr("Remove them, clean up relation")
571 );
572 switch(ret) {
573 case ConditionalOptionPaneUtil.DIALOG_DISABLED_OPTION: return;
574 case JOptionPane.CLOSED_OPTION: return;
575 case JOptionPane.NO_OPTION: return;
576 case JOptionPane.YES_OPTION:
577 memberTableModel.removeMembersReferringTo(toCheck);
578 break;
579 }
580 }
581 }
582
583 class AddAbortException extends Exception {
584 }
585
586 abstract class AddFromSelectionAction extends AbstractAction {
587 protected boolean isPotentialDuplicate(OsmPrimitive primitive) {
588 return memberTableModel.hasMembersReferringTo(Collections.singleton(primitive));
589 }
590
591 protected boolean confirmAddingPrimtive(OsmPrimitive primitive) throws AddAbortException {
592 String msg = tr("<html>This relation already has one or more members referring to<br>"
593 + "the primitive ''{0}''<br>"
594 + "<br>"
595 + "Do you really want to add another relation member?</html>",
596 primitive.getDisplayName(DefaultNameFormatter.getInstance())
597 );
598 int ret = ConditionalOptionPaneUtil.showOptionDialog(
599 "add_primitive_to_relation",
600 Main.parent,
601 msg,
602 tr("Multiple members referring to same primitive"),
603 JOptionPane.YES_NO_CANCEL_OPTION,
604 JOptionPane.WARNING_MESSAGE,
605 null,
606 null
607 );
608 switch(ret) {
609 case ConditionalOptionPaneUtil.DIALOG_DISABLED_OPTION : return true;
610 case JOptionPane.YES_OPTION: return true;
611 case JOptionPane.NO_OPTION: return false;
612 case JOptionPane.CLOSED_OPTION: return false;
613 case JOptionPane.CANCEL_OPTION: throw new AddAbortException();
614 }
615 // should not happen
616 return false;
617 }
618
619 protected void warnOfCircularReferences(OsmPrimitive primitive) {
620 String msg = tr("<html>You are trying to add a relation to itself.<br>"
621 + "<br>"
622 + "This creates circular references and is therefore discouraged.<br>"
623 + "Skipping relation ''{0}''.</html>",
624 primitive.getDisplayName(DefaultNameFormatter.getInstance())
625 );
626 JOptionPane.showMessageDialog(
627 Main.parent,
628 msg,
629 tr("Warning"),
630 JOptionPane.WARNING_MESSAGE
631 );
632 }
633
634 protected List<OsmPrimitive> filterConfirmedPrimitives(List<OsmPrimitive> primitives) throws AddAbortException {
635 if (primitives == null || primitives.isEmpty())
636 return primitives;
637 ArrayList<OsmPrimitive> ret = new ArrayList<OsmPrimitive>();
638 Iterator<OsmPrimitive> it = primitives.iterator();
639 while(it.hasNext()) {
640 OsmPrimitive primitive = it.next();
641 if (primitive instanceof Relation && getRelation() != null && getRelation().equals(primitive)) {
642 warnOfCircularReferences(primitive);
643 continue;
644 }
645 if (isPotentialDuplicate(primitive)) {
646 if (confirmAddingPrimtive(primitive)) {
647 ret.add(primitive);
648 }
649 continue;
650 } else {
651 ret.add(primitive);
652 }
653 }
654 return ret;
655 }
656 }
657
658 class AddSelectedAtStartAction extends AddFromSelectionAction implements TableModelListener {
659 public AddSelectedAtStartAction() {
660 putValue(SHORT_DESCRIPTION,
661 tr("Add all primitives selected in the current dataset before the first member"));
662 putValue(SMALL_ICON, ImageProvider.get("dialogs/conflict", "copystartright"));
663 // putValue(NAME, tr("Add Selected"));
664 refreshEnabled();
665 }
666
667 protected void refreshEnabled() {
668 setEnabled(selectionTableModel.getRowCount() > 0 && memberTableModel.getRowCount() > 0);
669 }
670
671 public void actionPerformed(ActionEvent e) {
672 try {
673 List<OsmPrimitive> toAdd = filterConfirmedPrimitives(selectionTableModel.getSelection());
674 memberTableModel.addMembersAtBeginning(toAdd);
675 } catch(AddAbortException ex) {
676 // do nothing
677 }
678 }
679
680 public void tableChanged(TableModelEvent e) {
681 refreshEnabled();
682 }
683 }
684
685 class AddSelectedAtEndAction extends AddFromSelectionAction implements TableModelListener {
686 public AddSelectedAtEndAction() {
687 putValue(SHORT_DESCRIPTION, tr("Add all primitives selected in the current dataset after the last member"));
688 putValue(SMALL_ICON, ImageProvider.get("dialogs/conflict", "copyendright"));
689 // putValue(NAME, tr("Add Selected"));
690 refreshEnabled();
691 }
692
693 protected void refreshEnabled() {
694 setEnabled(selectionTableModel.getRowCount() > 0);
695 }
696
697 public void actionPerformed(ActionEvent e) {
698 try {
699 List<OsmPrimitive> toAdd = filterConfirmedPrimitives(selectionTableModel.getSelection());
700 memberTableModel.addMembersAtEnd(toAdd);
701 } catch(AddAbortException ex) {
702 // do nothing
703 }
704 }
705
706 public void tableChanged(TableModelEvent e) {
707 refreshEnabled();
708 }
709 }
710
711 class AddSelectedBeforeSelection extends AddFromSelectionAction implements TableModelListener, ListSelectionListener {
712 public AddSelectedBeforeSelection() {
713 putValue(SHORT_DESCRIPTION,
714 tr("Add all primitives selected in the current dataset before the first selected member"));
715 putValue(SMALL_ICON, ImageProvider.get("dialogs/conflict", "copybeforecurrentright"));
716 // putValue(NAME, tr("Add Selected"));
717 refreshEnabled();
718 }
719
720 protected void refreshEnabled() {
721 setEnabled(selectionTableModel.getRowCount() > 0
722 && memberTableModel.getSelectionModel().getMinSelectionIndex() >= 0);
723 }
724
725 public void actionPerformed(ActionEvent e) {
726 try {
727 List<OsmPrimitive> toAdd = filterConfirmedPrimitives(selectionTableModel.getSelection());
728 memberTableModel.addMembersBeforeIdx(toAdd, memberTableModel
729 .getSelectionModel().getMinSelectionIndex());
730 } catch(AddAbortException ex) {
731 // do nothing
732 }
733
734 }
735
736 public void tableChanged(TableModelEvent e) {
737 refreshEnabled();
738 }
739
740 public void valueChanged(ListSelectionEvent e) {
741 refreshEnabled();
742 }
743 }
744
745 class AddSelectedAfterSelection extends AddFromSelectionAction implements TableModelListener, ListSelectionListener {
746 public AddSelectedAfterSelection() {
747 putValue(SHORT_DESCRIPTION,
748 tr("Add all primitives selected in the current dataset after the last selected member"));
749 putValue(SMALL_ICON, ImageProvider.get("dialogs/conflict", "copyaftercurrentright"));
750 // putValue(NAME, tr("Add Selected"));
751 refreshEnabled();
752 }
753
754 protected void refreshEnabled() {
755 setEnabled(selectionTableModel.getRowCount() > 0
756 && memberTableModel.getSelectionModel().getMinSelectionIndex() >= 0);
757 }
758
759 public void actionPerformed(ActionEvent e) {
760 try {
761 List<OsmPrimitive> toAdd = filterConfirmedPrimitives(selectionTableModel.getSelection());
762 memberTableModel.addMembersAfterIdx(toAdd, memberTableModel
763 .getSelectionModel().getMaxSelectionIndex());
764 } catch(AddAbortException ex) {
765 // do nothing
766 }
767 }
768
769 public void tableChanged(TableModelEvent e) {
770 refreshEnabled();
771 }
772
773 public void valueChanged(ListSelectionEvent e) {
774 refreshEnabled();
775 }
776 }
777
778 class RemoveSelectedAction extends AbstractAction implements TableModelListener {
779 public RemoveSelectedAction() {
780 putValue(SHORT_DESCRIPTION, tr("Remove all members referring to one of the selected primitives"));
781 putValue(SMALL_ICON, ImageProvider.get("dialogs/relation", "deletemembers"));
782 // putValue(NAME, tr("Remove Selected"));
783 Shortcut.registerShortcut("relationeditor:removeselected", tr("Relation Editor: Remove Selected"),
784 KeyEvent.VK_S, Shortcut.GROUP_MNEMONIC);
785
786 updateEnabledState();
787 }
788
789 protected void updateEnabledState() {
790 DataSet ds = getLayer().data;
791 if (ds == null || ds.getSelected().isEmpty()) {
792 setEnabled(false);
793 return;
794 }
795 // only enable the action if we have members referring to the
796 // selected primitives
797 //
798 setEnabled(memberTableModel.hasMembersReferringTo(ds.getSelected()));
799 }
800
801 public void actionPerformed(ActionEvent e) {
802 memberTableModel.removeMembersReferringTo(selectionTableModel.getSelection());
803 }
804
805 public void tableChanged(TableModelEvent e) {
806 updateEnabledState();
807 }
808 }
809
810 /**
811 * Selects members in the relation editor which refer to primitives in the current
812 * selection of the context layer.
813 *
814 */
815 class SelectedMembersForSelectionAction extends AbstractAction implements TableModelListener {
816 public SelectedMembersForSelectionAction() {
817 putValue(SHORT_DESCRIPTION, tr("Select relation members which refer to primitives in the current selection"));
818 putValue(SMALL_ICON, ImageProvider.get("dialogs/relation", "selectmembers"));
819 updateEnabledState();
820 }
821
822 protected void updateEnabledState() {
823 boolean enabled = selectionTableModel.getRowCount() > 0
824 && !memberTableModel.getChildPrimitives(getLayer().data.getSelected()).isEmpty();
825
826 if (enabled) {
827 putValue(SHORT_DESCRIPTION, tr("Select relation members which refer to {0} primitives in the current selection",memberTableModel.getChildPrimitives(getLayer().data.getSelected()).size()));
828 } else {
829 putValue(SHORT_DESCRIPTION, tr("Select relation members which refer to primitives in the current selection"));
830 }
831 setEnabled(enabled);
832 }
833
834 public void actionPerformed(ActionEvent e) {
835 memberTableModel.selectMembersReferringTo(getLayer().data.getSelected());
836 }
837
838 public void tableChanged(TableModelEvent e) {
839 updateEnabledState();
840
841 }
842 }
843
844 /**
845 * Selects primitives in the layer this editor belongs to. The selected primitives are
846 * equal to the set of primitives the currently selected relation members refer to.
847 *
848 */
849 class SelectPrimitivesForSelectedMembersAction extends AbstractAction implements ListSelectionListener {
850 public SelectPrimitivesForSelectedMembersAction() {
851 putValue(SHORT_DESCRIPTION, tr("Select primitives for selected relation members"));
852 putValue(SMALL_ICON, ImageProvider.get("dialogs/relation", "selectprimitives"));
853 updateEnabledState();
854 }
855
856 protected void updateEnabledState() {
857 setEnabled(memberTable.getSelectedRowCount() > 0);
858 }
859
860 public void actionPerformed(ActionEvent e) {
861 getLayer().data.setSelected(memberTableModel.getSelectedChildPrimitives());
862 }
863
864 public void valueChanged(ListSelectionEvent e) {
865 updateEnabledState();
866 }
867 }
868
869 class SortAction extends AbstractAction implements TableModelListener {
870 public SortAction() {
871 putValue(SHORT_DESCRIPTION, tr("Sort the relation members"));
872 putValue(SMALL_ICON, ImageProvider.get("dialogs", "sort"));
873 putValue(NAME, tr("Sort"));
874 Shortcut.registerShortcut("relationeditor:sort", tr("Relation Editor: Sort"), KeyEvent.VK_T,
875 Shortcut.GROUP_MNEMONIC);
876 updateEnabledState();
877 }
878
879 public void actionPerformed(ActionEvent e) {
880 memberTableModel.sort();
881 }
882
883 protected void updateEnabledState() {
884 setEnabled(memberTableModel.getRowCount() > 0);
885 }
886
887 public void tableChanged(TableModelEvent e) {
888 updateEnabledState();
889 }
890 }
891
892 class MoveUpAction extends AbstractAction implements ListSelectionListener {
893 public MoveUpAction() {
894 putValue(SHORT_DESCRIPTION, tr("Move the currently selected members up"));
895 putValue(SMALL_ICON, ImageProvider.get("dialogs", "moveup"));
896 // putValue(NAME, tr("Move Up"));
897 Shortcut.registerShortcut("relationeditor:moveup", tr("Relation Editor: Move Up"), KeyEvent.VK_N,
898 Shortcut.GROUP_MNEMONIC);
899 setEnabled(false);
900 }
901
902 public void actionPerformed(ActionEvent e) {
903 memberTableModel.moveUp(memberTable.getSelectedRows());
904 }
905
906 public void valueChanged(ListSelectionEvent e) {
907 setEnabled(memberTableModel.canMoveUp(memberTable.getSelectedRows()));
908 }
909 }
910
911 class MoveDownAction extends AbstractAction implements ListSelectionListener {
912 public MoveDownAction() {
913 putValue(SHORT_DESCRIPTION, tr("Move the currently selected members down"));
914 putValue(SMALL_ICON, ImageProvider.get("dialogs", "movedown"));
915 // putValue(NAME, tr("Move Down"));
916 Shortcut.registerShortcut("relationeditor:moveup", tr("Relation Editor: Move Down"), KeyEvent.VK_J,
917 Shortcut.GROUP_MNEMONIC);
918 setEnabled(false);
919 }
920
921 public void actionPerformed(ActionEvent e) {
922 memberTableModel.moveDown(memberTable.getSelectedRows());
923 }
924
925 public void valueChanged(ListSelectionEvent e) {
926 setEnabled(memberTableModel.canMoveDown(memberTable.getSelectedRows()));
927 }
928 }
929
930 class RemoveAction extends AbstractAction implements ListSelectionListener {
931 public RemoveAction() {
932 putValue(SHORT_DESCRIPTION, tr("Remove the currently selected members from this relation"));
933 putValue(SMALL_ICON, ImageProvider.get("dialogs", "remove"));
934 // putValue(NAME, tr("Remove"));
935 Shortcut.registerShortcut("relationeditor:remove", tr("Relation Editor: Remove"), KeyEvent.VK_J,
936 Shortcut.GROUP_MNEMONIC);
937 setEnabled(false);
938 }
939
940 public void actionPerformed(ActionEvent e) {
941 memberTableModel.remove(memberTable.getSelectedRows());
942 }
943
944 public void valueChanged(ListSelectionEvent e) {
945 setEnabled(memberTableModel.canRemove(memberTable.getSelectedRows()));
946 }
947 }
948
949 class DeleteCurrentRelationAction extends AbstractAction implements PropertyChangeListener{
950 public DeleteCurrentRelationAction() {
951 putValue(SHORT_DESCRIPTION, tr("Delete the currently edited relation"));
952 putValue(SMALL_ICON, ImageProvider.get("dialogs", "delete"));
953 putValue(NAME, tr("Delete"));
954 updateEnabledState();
955 }
956
957 public void run() {
958 Relation toDelete = getRelation();
959 if (toDelete == null)
960 return;
961 org.openstreetmap.josm.actions.mapmode.DeleteAction.deleteRelation(
962 getLayer(),
963 toDelete
964 );
965 }
966
967 public void actionPerformed(ActionEvent e) {
968 run();
969 }
970
971 protected void updateEnabledState() {
972 setEnabled(getRelationSnapshot() != null);
973 }
974
975 public void propertyChange(PropertyChangeEvent evt) {
976 if (evt.getPropertyName().equals(RELATION_SNAPSHOT_PROP)) {
977 updateEnabledState();
978 }
979 }
980 }
981
982 abstract class SavingAction extends AbstractAction {
983 /**
984 * apply updates to a new relation
985 */
986 protected void applyNewRelation() {
987 // If the user wanted to create a new relation, but hasn't added any members or
988 // tags, don't add an empty relation
989 if (memberTableModel.getRowCount() == 0 && tagEditorPanel.getModel().getKeys().isEmpty())
990 return;
991 Relation newRelation = new Relation();
992 tagEditorPanel.getModel().applyToPrimitive(newRelation);
993 memberTableModel.applyToRelation(newRelation);
994 Main.main.undoRedo.add(new AddCommand(getLayer(),newRelation));
995
996 // make sure everybody is notified about the changes
997 //
998 getLayer().data.fireSelectionChanged();
999 getLayer().fireDataChange();
1000 GenericRelationEditor.this.setRelation(newRelation);
1001 RelationDialogManager.getRelationDialogManager().updateContext(
1002 getLayer(),
1003 getRelation(),
1004 GenericRelationEditor.this
1005 );
1006 }
1007
1008 /**
1009 * Apply the updates for an existing relation which has not been changed
1010 * outside of the relation editor.
1011 *
1012 */
1013 protected void applyExistingConflictingRelation() {
1014 Relation editedRelation = new Relation(getRelation());
1015 tagEditorPanel.getModel().applyToPrimitive(editedRelation);
1016 memberTableModel.applyToRelation(editedRelation);
1017 Conflict<Relation> conflict = new Conflict<Relation>(getRelation(), editedRelation);
1018 Main.main.undoRedo.add(new ConflictAddCommand(getLayer(),conflict));
1019 }
1020
1021 /**
1022 * Apply the updates for an existing relation which has been changed
1023 * outside of the relation editor.
1024 *
1025 */
1026 protected void applyExistingNonConflictingRelation() {
1027 Relation editedRelation = new Relation(getRelation());
1028 tagEditorPanel.getModel().applyToPrimitive(editedRelation);
1029 memberTableModel.applyToRelation(editedRelation);
1030 Main.main.undoRedo.add(new ChangeCommand(getRelation(), editedRelation));
1031 getLayer().data.fireSelectionChanged();
1032 getLayer().fireDataChange();
1033 // this will refresh the snapshot and update the dialog title
1034 //
1035 setRelation(getRelation());
1036 }
1037
1038 protected boolean confirmClosingBecauseOfDirtyState() {
1039 ButtonSpec [] options = new ButtonSpec[] {
1040 new ButtonSpec(
1041 tr("Yes, create a conflict and close"),
1042 ImageProvider.get("ok"),
1043 tr("Click to create a conflict and close this relation editor") ,
1044 null /* no specific help topic */
1045 ),
1046 new ButtonSpec(
1047 tr("No, continue editing"),
1048 ImageProvider.get("cancel"),
1049 tr("Click to to return to the relation editor and to resume relation editing") ,
1050 null /* no specific help topic */
1051 )
1052 };
1053
1054 int ret = HelpAwareOptionPane.showOptionDialog(
1055 Main.parent,
1056 tr("<html>This relation has been changed outside of the editor.<br>"
1057 + "You can't apply your changes and continue editing.<br>"
1058 + "<br>"
1059 + "Do you want to create a conflict and close the editor?</html>"),
1060 tr("Conflict in data"),
1061 JOptionPane.WARNING_MESSAGE,
1062 null,
1063 options,
1064 options[0], // OK is default
1065 "/Dialog/RelationEditor#RelationChangedOutsideOfEditor"
1066 );
1067 return ret == 0;
1068 }
1069
1070 protected void warnDoubleConflict() {
1071 JOptionPane.showMessageDialog(
1072 Main.parent,
1073 tr("<html>Layer ''{0}'' already has a conflict for primitive<br>"
1074 + "''{1}''.<br>"
1075 + "Please resolve this conflict first, then try again.</html>",
1076 getLayer().getName(),
1077 getRelation().getDisplayName(DefaultNameFormatter.getInstance())
1078 ),
1079 tr("Double conflict"),
1080 JOptionPane.WARNING_MESSAGE
1081 );
1082 }
1083 }
1084
1085 class ApplyAction extends SavingAction {
1086 public ApplyAction() {
1087 putValue(SHORT_DESCRIPTION, tr("Apply the current updates"));
1088 putValue(SMALL_ICON, ImageProvider.get("save"));
1089 putValue(NAME, tr("Apply"));
1090 setEnabled(true);
1091 }
1092
1093 public void run() {
1094 if (getRelation() == null) {
1095 applyNewRelation();
1096 } else if (!memberTableModel.hasSameMembersAs(getRelationSnapshot())
1097 || tagEditorPanel.getModel().isDirty()) {
1098 if (isDirtyRelation()) {
1099 if (confirmClosingBecauseOfDirtyState()) {
1100 if (getLayer().getConflicts().hasConflictForMy(getRelation())) {
1101 warnDoubleConflict();
1102 return;
1103 }
1104 applyExistingConflictingRelation();
1105 setVisible(false);
1106 }
1107 } else {
1108 applyExistingNonConflictingRelation();
1109 }
1110 }
1111 }
1112
1113 public void actionPerformed(ActionEvent e) {
1114 run();
1115 }
1116 }
1117
1118 class OKAction extends SavingAction {
1119 public OKAction() {
1120 putValue(SHORT_DESCRIPTION, tr("Apply the updates and close the dialog"));
1121 putValue(SMALL_ICON, ImageProvider.get("ok"));
1122 putValue(NAME, tr("OK"));
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 } else
1139 return;
1140 } else {
1141 applyExistingNonConflictingRelation();
1142 }
1143 }
1144 setVisible(false);
1145 }
1146
1147 public void actionPerformed(ActionEvent e) {
1148 run();
1149 }
1150 }
1151
1152 class CancelAction extends AbstractAction {
1153 public CancelAction() {
1154 putValue(SHORT_DESCRIPTION, tr("Cancel the updates and close the dialog"));
1155 putValue(SMALL_ICON, ImageProvider.get("cancel"));
1156 putValue(NAME, tr("Cancel"));
1157
1158 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
1159 .put(KeyStroke.getKeyStroke("ESCAPE"), "ESCAPE");
1160 getRootPane().getActionMap().put("ESCAPE", this);
1161 setEnabled(true);
1162 }
1163
1164 public void actionPerformed(ActionEvent e) {
1165 setVisible(false);
1166 }
1167 }
1168
1169 class AddTagAction extends AbstractAction {
1170 public AddTagAction() {
1171 putValue(SHORT_DESCRIPTION, tr("Add an empty tag"));
1172 putValue(SMALL_ICON, ImageProvider.get("dialogs", "add"));
1173 // putValue(NAME, tr("Cancel"));
1174 setEnabled(true);
1175 }
1176
1177 public void actionPerformed(ActionEvent e) {
1178 tagEditorPanel.getModel().appendNewTag();
1179 }
1180 }
1181
1182 class DownloadIncompleteMembersAction extends AbstractAction implements TableModelListener {
1183 public DownloadIncompleteMembersAction() {
1184 putValue(SHORT_DESCRIPTION, tr("Download all incomplete members"));
1185 putValue(SMALL_ICON, ImageProvider.get("dialogs/relation", "downloadincomplete"));
1186 putValue(NAME, tr("Download Members"));
1187 Shortcut.registerShortcut("relationeditor:downloadincomplete", tr("Relation Editor: Download Members"),
1188 KeyEvent.VK_K, Shortcut.GROUP_MNEMONIC);
1189 updateEnabledState();
1190 }
1191
1192 public void actionPerformed(ActionEvent e) {
1193 if (!isEnabled())
1194 return;
1195 Main.worker.submit(new DownloadRelationMemberTask(
1196 getRelation(),
1197 memberTableModel.getIncompleteMemberPrimitives(),
1198 getLayer(),
1199 memberTableModel,
1200 GenericRelationEditor.this)
1201 );
1202 }
1203
1204 protected void updateEnabledState() {
1205 setEnabled(
1206 getRelation() != null
1207 && !getRelation().isNew()
1208 && memberTableModel.hasIncompleteMembers()
1209 );
1210 }
1211
1212 public void tableChanged(TableModelEvent e) {
1213 updateEnabledState();
1214 }
1215 }
1216
1217 class DownloadSelectedIncompleteMembersAction extends AbstractAction implements ListSelectionListener, TableModelListener{
1218 public DownloadSelectedIncompleteMembersAction() {
1219 putValue(SHORT_DESCRIPTION, tr("Download selected incomplete members"));
1220 putValue(SMALL_ICON, ImageProvider.get("dialogs/relation", "downloadincompleteselected"));
1221 putValue(NAME, tr("Download Members"));
1222 Shortcut.registerShortcut("relationeditor:downloadincomplete", tr("Relation Editor: Download Members"),
1223 KeyEvent.VK_K, Shortcut.GROUP_MNEMONIC);
1224 updateEnabledState();
1225 }
1226
1227 public void actionPerformed(ActionEvent e) {
1228 if (!isEnabled())
1229 return;
1230 Main.worker.submit(new DownloadRelationMemberTask(
1231 getRelation(),
1232 memberTableModel.getSelectedIncompleteMemberPrimitives(),
1233 getLayer(),
1234 memberTableModel,
1235 GenericRelationEditor.this)
1236 );
1237 }
1238
1239 protected void updateEnabledState() {
1240 setEnabled(
1241 getRelation() != null
1242 && !getRelation().isNew()
1243 && memberTableModel.hasIncompleteSelectedMembers()
1244 );
1245 }
1246
1247 public void valueChanged(ListSelectionEvent e) {
1248 updateEnabledState();
1249 }
1250
1251 public void tableChanged(TableModelEvent e) {
1252 updateEnabledState();
1253 }
1254 }
1255
1256 class SetRoleAction extends AbstractAction implements ListSelectionListener, DocumentListener {
1257 public SetRoleAction() {
1258 putValue(SHORT_DESCRIPTION, tr("Sets a role for the selected members"));
1259 // FIXME: find better icon
1260 putValue(SMALL_ICON, ImageProvider.get("ok"));
1261 putValue(NAME, tr("Apply Role"));
1262 refreshEnabled();
1263 }
1264
1265 protected void refreshEnabled() {
1266 setEnabled(memberTable.getSelectedRowCount() > 0);
1267 }
1268
1269 protected boolean isEmptyRole() {
1270 return tfRole.getText() == null || tfRole.getText().trim().equals("");
1271 }
1272
1273 protected boolean confirmSettingEmptyRole(int onNumMembers) {
1274 String message = tr("<html>You are setting an empty role on {0} primitives.<br>"
1275 + "This is equal to deleting the roles of these primitives.<br>"
1276 + "Do you really want to apply the new role?</html>", onNumMembers);
1277 String [] options = new String[] {
1278 tr("Yes, apply it"),
1279 tr("No, don't apply")
1280 };
1281 int ret = ConditionalOptionPaneUtil.showOptionDialog(
1282 "relation_editor.confirm_applying_empty_role",
1283 Main.parent,
1284 message,
1285 tr("Confirm empty role"),
1286 JOptionPane.YES_NO_OPTION,
1287 JOptionPane.WARNING_MESSAGE,
1288 options,
1289 options[0]
1290 );
1291 switch(ret) {
1292 case JOptionPane.YES_OPTION: return true;
1293 case ConditionalOptionPaneUtil.DIALOG_DISABLED_OPTION: return true;
1294 default:
1295 return false;
1296 }
1297 }
1298
1299 public void actionPerformed(ActionEvent e) {
1300 if (isEmptyRole()) {
1301 if (! confirmSettingEmptyRole(memberTable.getSelectedRowCount()))
1302 return;
1303 }
1304 memberTableModel.updateRole(memberTable.getSelectedRows(), tfRole.getText());
1305 }
1306
1307 public void valueChanged(ListSelectionEvent e) {
1308 refreshEnabled();
1309 }
1310
1311 public void changedUpdate(DocumentEvent e) {
1312 refreshEnabled();
1313 }
1314
1315 public void insertUpdate(DocumentEvent e) {
1316 refreshEnabled();
1317 }
1318
1319 public void removeUpdate(DocumentEvent e) {
1320 refreshEnabled();
1321 }
1322 }
1323
1324 /**
1325 * Creates a new relation with a copy of the current editor state
1326 *
1327 */
1328 class DuplicateRelationAction extends AbstractAction {
1329 public DuplicateRelationAction() {
1330 putValue(SHORT_DESCRIPTION, tr("Create a copy of this relation and open it in another editor window"));
1331 // FIXME provide an icon
1332 putValue(SMALL_ICON, ImageProvider.get("duplicate"));
1333 putValue(NAME, tr("Duplicate"));
1334 setEnabled(true);
1335 }
1336
1337 public void actionPerformed(ActionEvent e) {
1338 Relation copy = new Relation();
1339 tagEditorPanel.getModel().applyToPrimitive(copy);
1340 memberTableModel.applyToRelation(copy);
1341 RelationEditor editor = RelationEditor.getEditor(getLayer(), copy, memberTableModel.getSelectedMembers());
1342 editor.setVisible(true);
1343 }
1344 }
1345
1346 /**
1347 * Action for editing the currently selected relation
1348 *
1349 *
1350 */
1351 class EditAction extends AbstractAction implements ListSelectionListener {
1352 public EditAction() {
1353 putValue(SHORT_DESCRIPTION, tr("Edit the relation the currently selected relation member refers to"));
1354 putValue(SMALL_ICON, ImageProvider.get("dialogs", "edit"));
1355 //putValue(NAME, tr("Edit"));
1356 refreshEnabled();
1357 }
1358
1359 protected void refreshEnabled() {
1360 setEnabled(memberTable.getSelectedRowCount() == 1
1361 && memberTableModel.isEditableRelation(memberTable.getSelectedRow()));
1362 }
1363
1364 protected Collection<RelationMember> getMembersForCurrentSelection(Relation r) {
1365 Collection<RelationMember> members = new HashSet<RelationMember>();
1366 Collection<OsmPrimitive> selection = getLayer().data.getSelected();
1367 for (RelationMember member: r.getMembers()) {
1368 if (selection.contains(member.getMember())) {
1369 members.add(member);
1370 }
1371 }
1372 return members;
1373 }
1374
1375 public void run() {
1376 int idx = memberTable.getSelectedRow();
1377 if (idx < 0)
1378 return;
1379 OsmPrimitive primitive = memberTableModel.getReferredPrimitive(idx);
1380 if (!(primitive instanceof Relation))
1381 return;
1382 Relation r = (Relation) primitive;
1383 if (r.isIncomplete())
1384 return;
1385
1386 RelationEditor editor = RelationEditor.getEditor(getLayer(), r, getMembersForCurrentSelection(r));
1387 editor.setVisible(true);
1388 }
1389
1390 public void actionPerformed(ActionEvent e) {
1391 if (!isEnabled())
1392 return;
1393 run();
1394 }
1395
1396 public void valueChanged(ListSelectionEvent e) {
1397 refreshEnabled();
1398 }
1399 }
1400
1401 class MemberTableDblClickAdapter extends MouseAdapter {
1402 @Override
1403 public void mouseClicked(MouseEvent e) {
1404 if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2) {
1405 new EditAction().run();
1406 }
1407 }
1408 }
1409}
Note: See TracBrowser for help on using the repository browser.