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

Last change on this file since 3095 was 3095, checked in by jttt, 14 years ago

Changes in multipolygon handling (see #4661), cosmetics

  • Property svn:eol-style set to native
File size: 55.5 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;
5import static org.openstreetmap.josm.tools.I18n.trn;
6
7import java.awt.BorderLayout;
8import java.awt.Dimension;
9import java.awt.FlowLayout;
10import java.awt.GridBagConstraints;
11import java.awt.GridBagLayout;
12import java.awt.event.ActionEvent;
13import java.awt.event.FocusAdapter;
14import java.awt.event.FocusEvent;
15import java.awt.event.KeyEvent;
16import java.awt.event.MouseAdapter;
17import java.awt.event.MouseEvent;
18import java.awt.event.WindowAdapter;
19import java.awt.event.WindowEvent;
20import java.beans.PropertyChangeEvent;
21import java.beans.PropertyChangeListener;
22import java.util.ArrayList;
23import java.util.Collection;
24import java.util.Collections;
25import java.util.HashSet;
26import java.util.Iterator;
27import java.util.List;
28import java.util.logging.Logger;
29
30import javax.swing.AbstractAction;
31import javax.swing.BorderFactory;
32import javax.swing.JComponent;
33import javax.swing.JLabel;
34import javax.swing.JOptionPane;
35import javax.swing.JPanel;
36import javax.swing.JScrollPane;
37import javax.swing.JSplitPane;
38import javax.swing.JTabbedPane;
39import javax.swing.JToolBar;
40import javax.swing.KeyStroke;
41import javax.swing.event.ChangeEvent;
42import javax.swing.event.ChangeListener;
43import javax.swing.event.DocumentEvent;
44import javax.swing.event.DocumentListener;
45import javax.swing.event.ListSelectionEvent;
46import javax.swing.event.ListSelectionListener;
47import javax.swing.event.TableModelEvent;
48import javax.swing.event.TableModelListener;
49
50import org.openstreetmap.josm.Main;
51import org.openstreetmap.josm.command.AddCommand;
52import org.openstreetmap.josm.command.ChangeCommand;
53import org.openstreetmap.josm.command.ConflictAddCommand;
54import org.openstreetmap.josm.data.conflict.Conflict;
55import org.openstreetmap.josm.data.osm.DataSet;
56import org.openstreetmap.josm.data.osm.OsmPrimitive;
57import org.openstreetmap.josm.data.osm.Relation;
58import org.openstreetmap.josm.data.osm.RelationMember;
59import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
60import org.openstreetmap.josm.gui.DefaultNameFormatter;
61import org.openstreetmap.josm.gui.HelpAwareOptionPane;
62import org.openstreetmap.josm.gui.SideButton;
63import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec;
64import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction;
65import org.openstreetmap.josm.gui.help.HelpUtil;
66import org.openstreetmap.josm.gui.layer.OsmDataLayer;
67import org.openstreetmap.josm.gui.tagging.AutoCompletingTextField;
68import org.openstreetmap.josm.gui.tagging.TagEditorPanel;
69import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionCache;
70import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionList;
71import org.openstreetmap.josm.tools.ImageProvider;
72import org.openstreetmap.josm.tools.Shortcut;
73import org.openstreetmap.josm.tools.WindowGeometry;
74
75/**
76 * This dialog is for editing relations.
77 *
78 */
79public class GenericRelationEditor extends RelationEditor {
80 @SuppressWarnings("unused")
81 static private final Logger logger = Logger.getLogger(GenericRelationEditor.class.getName());
82
83 /** the tag table and its model */
84 private TagEditorPanel tagEditorPanel;
85 private ReferringRelationsBrowser referrerBrowser;
86 private ReferringRelationsBrowserModel referrerModel;
87
88 /** the member table */
89 private MemberTable memberTable;
90 private MemberTableModel memberTableModel;
91
92 /** the model for the selection table */
93 private SelectionTableModel selectionTableModel;
94
95 private AutoCompletingTextField tfRole;
96
97 /**
98 * Creates a new relation editor for the given relation. The relation will be saved if the user
99 * selects "ok" in the editor.
100 *
101 * If no relation is given, will create an editor for a new relation.
102 *
103 * @param layer the {@see OsmDataLayer} the new or edited relation belongs to
104 * @param relation relation to edit, or null to create a new one.
105 * @param selectedMembers a collection of members which shall be selected initially
106 */
107 public GenericRelationEditor(OsmDataLayer layer, Relation relation, Collection<RelationMember> selectedMembers) {
108 super(layer, relation, selectedMembers);
109
110 setRememberWindowGeometry(getClass().getName() + ".geometry",
111 WindowGeometry.centerInWindow(Main.parent, new Dimension(700, 650)));
112
113 // initialize the autocompletion infrastructure
114 //
115 AutoCompletionCache.getCacheForLayer(getLayer()).initFromDataSet();
116
117 // init the various models
118 //
119 memberTableModel = new MemberTableModel(getLayer());
120 memberTableModel.register();
121 selectionTableModel = new SelectionTableModel(getLayer());
122 selectionTableModel.register();
123 referrerModel = new ReferringRelationsBrowserModel(relation);
124
125 tagEditorPanel = new TagEditorPanel();
126 // populate the models
127 //
128 if (relation != null) {
129 tagEditorPanel.getModel().initFromPrimitive(relation);
130 //this.tagEditorModel.initFromPrimitive(relation);
131 this.memberTableModel.populate(relation);
132 if (!getLayer().data.getRelations().contains(relation)) {
133 // treat it as a new relation if it doesn't exist in the
134 // data set yet.
135 setRelation(null);
136 }
137 } else {
138 tagEditorPanel.getModel().clear();
139 this.memberTableModel.populate(null);
140 }
141 tagEditorPanel.getModel().ensureOneTag();
142
143 JSplitPane pane = buildSplitPane();
144 pane.setPreferredSize(new Dimension(100, 100));
145
146 JPanel pnl = new JPanel();
147 pnl.setLayout(new BorderLayout());
148 pnl.add(pane, BorderLayout.CENTER);
149 pnl.setBorder(BorderFactory.createRaisedBevelBorder());
150
151 getContentPane().setLayout(new BorderLayout());
152 JTabbedPane tabbedPane = new JTabbedPane();
153 tabbedPane.add(tr("Tags and Members"), pnl);
154 referrerBrowser = new ReferringRelationsBrowser(getLayer(), referrerModel, this);
155 tabbedPane.add(tr("Parent Relations"), referrerBrowser);
156 tabbedPane.add(tr("Child Relations"), new ChildRelationBrowser(getLayer(), relation));
157 tabbedPane.addChangeListener(
158 new ChangeListener() {
159 public void stateChanged(ChangeEvent e) {
160 JTabbedPane sourceTabbedPane = (JTabbedPane) e.getSource();
161 int index = sourceTabbedPane.getSelectedIndex();
162 String title = sourceTabbedPane.getTitleAt(index);
163 if (title.equals(tr("Parent Relations"))) {
164 referrerBrowser.init();
165 }
166 }
167 }
168 );
169
170 getContentPane().add(buildToolBar(), BorderLayout.NORTH);
171 getContentPane().add(tabbedPane, BorderLayout.CENTER);
172 getContentPane().add(buildOkCancelButtonPanel(), BorderLayout.SOUTH);
173
174 setSize(findMaxDialogSize());
175
176 addWindowListener(
177 new WindowAdapter() {
178 @Override
179 public void windowOpened(WindowEvent e) {
180 cleanSelfReferences();
181 }
182 }
183 );
184
185 memberTableModel.setSelectedMembers(selectedMembers);
186 HelpUtil.setHelpContext(getRootPane(),ht("/Dialog/RelationEditor"));
187 }
188
189 /**
190 * Creates the toolbar
191 *
192 * @return the toolbar
193 */
194 protected JToolBar buildToolBar() {
195 JToolBar tb = new JToolBar();
196 tb.setFloatable(false);
197 tb.add(new ApplyAction());
198 tb.add(new DuplicateRelationAction());
199 DeleteCurrentRelationAction deleteAction = new DeleteCurrentRelationAction();
200 addPropertyChangeListener(deleteAction);
201 tb.add(deleteAction);
202 return tb;
203 }
204
205 /**
206 * builds the panel with the OK and the Cancel button
207 *
208 * @return the panel with the OK and the Cancel button
209 */
210 protected JPanel buildOkCancelButtonPanel() {
211 JPanel pnl = new JPanel();
212 pnl.setLayout(new FlowLayout(FlowLayout.CENTER));
213
214 pnl.add(new SideButton(new OKAction()));
215 pnl.add(new SideButton(new CancelAction()));
216 pnl.add(new SideButton(new ContextSensitiveHelpAction(ht("/Dialog/RelationEditor"))));
217 return pnl;
218 }
219
220 /**
221 * builds the panel with the tag editor
222 *
223 * @return the panel with the tag editor
224 */
225 protected JPanel buildTagEditorPanel() {
226 JPanel pnl = new JPanel();
227 pnl.setLayout(new GridBagLayout());
228
229 GridBagConstraints gc = new GridBagConstraints();
230 gc.gridx = 0;
231 gc.gridy = 0;
232 gc.gridheight = 1;
233 gc.gridwidth = 1;
234 gc.fill = GridBagConstraints.HORIZONTAL;
235 gc.anchor = GridBagConstraints.FIRST_LINE_START;
236 gc.weightx = 1.0;
237 gc.weighty = 0.0;
238 pnl.add(new JLabel(tr("Tags")), gc);
239
240 gc.gridx = 0;
241 gc.gridy = 1;
242 gc.fill = GridBagConstraints.BOTH;
243 gc.anchor = GridBagConstraints.CENTER;
244 gc.weightx = 1.0;
245 gc.weighty = 1.0;
246 pnl.add(tagEditorPanel, gc);
247 return pnl;
248 }
249
250 /**
251 * builds the panel for the relation member editor
252 *
253 * @return the panel for the relation member editor
254 */
255 protected JPanel buildMemberEditorPanel() {
256 final JPanel pnl = new JPanel();
257 pnl.setLayout(new GridBagLayout());
258 // setting up the member table
259 memberTable = new MemberTable(getLayer(),memberTableModel);
260 memberTable.addMouseListener(new MemberTableDblClickAdapter());
261 memberTableModel.addMemberModelListener(memberTable);
262
263 final JScrollPane scrollPane = new JScrollPane(memberTable);
264
265 GridBagConstraints gc = new GridBagConstraints();
266 gc.gridx = 0;
267 gc.gridy = 0;
268 gc.gridheight = 1;
269 gc.gridwidth = 3;
270 gc.fill = GridBagConstraints.HORIZONTAL;
271 gc.anchor = GridBagConstraints.FIRST_LINE_START;
272 gc.weightx = 1.0;
273 gc.weighty = 0.0;
274 pnl.add(new JLabel(tr("Members")), gc);
275
276 gc.gridx = 0;
277 gc.gridy = 1;
278 gc.gridheight = 1;
279 gc.gridwidth = 1;
280 gc.fill = GridBagConstraints.VERTICAL;
281 gc.anchor = GridBagConstraints.NORTHWEST;
282 gc.weightx = 0.0;
283 gc.weighty = 1.0;
284 pnl.add(buildLeftButtonPanel(), gc);
285
286 gc.gridx = 1;
287 gc.gridy = 1;
288 gc.fill = GridBagConstraints.BOTH;
289 gc.anchor = GridBagConstraints.CENTER;
290 gc.weightx = 0.6;
291 gc.weighty = 1.0;
292 pnl.add(scrollPane, gc);
293
294 // --- role editing
295 JPanel p3 = new JPanel(new FlowLayout(FlowLayout.LEFT));
296 p3.add(new JLabel(tr("Apply Role:")));
297 tfRole = new AutoCompletingTextField(10);
298 tfRole.setToolTipText(tr("Enter a role and apply it to the selected relation members"));
299 tfRole.addFocusListener(new FocusAdapter() {
300 @Override
301 public void focusGained(FocusEvent e) {
302 tfRole.selectAll();
303 }
304 });
305 tfRole.setAutoCompletionList(new AutoCompletionList());
306 tfRole.addFocusListener(
307 new FocusAdapter() {
308 @Override
309 public void focusGained(FocusEvent e) {
310 AutoCompletionList list = tfRole.getAutoCompletionList();
311 AutoCompletionCache.getCacheForLayer(Main.main.getEditLayer()).populateWithMemberRoles(list);
312 }
313 }
314 );
315 p3.add(tfRole);
316 SetRoleAction setRoleAction = new SetRoleAction();
317 memberTableModel.getSelectionModel().addListSelectionListener(setRoleAction);
318 tfRole.getDocument().addDocumentListener(setRoleAction);
319 tfRole.addActionListener(setRoleAction);
320 memberTableModel.getSelectionModel().addListSelectionListener(
321 new ListSelectionListener() {
322 public void valueChanged(ListSelectionEvent e) {
323 tfRole.setEnabled(memberTable.getSelectedRowCount() > 0);
324 }
325 }
326 );
327 tfRole.setEnabled(memberTable.getSelectedRowCount() > 0);
328 SideButton btnApply = new SideButton(setRoleAction);
329 btnApply.setPreferredSize(new Dimension(20,20));
330 btnApply.setText("");
331 p3.add(btnApply);
332
333 gc.gridx = 1;
334 gc.gridy = 2;
335 gc.fill = GridBagConstraints.BOTH;
336 gc.anchor = GridBagConstraints.CENTER;
337 gc.weightx = 1.0;
338 gc.weighty = 0.0;
339 pnl.add(p3, gc);
340
341 JPanel pnl2 = new JPanel();
342 pnl2.setLayout(new GridBagLayout());
343
344 gc.gridx = 0;
345 gc.gridy = 0;
346 gc.gridheight = 1;
347 gc.gridwidth = 3;
348 gc.fill = GridBagConstraints.HORIZONTAL;
349 gc.anchor = GridBagConstraints.FIRST_LINE_START;
350 gc.weightx = 1.0;
351 gc.weighty = 0.0;
352 pnl2.add(new JLabel(tr("Selection")), gc);
353
354 gc.gridx = 0;
355 gc.gridy = 1;
356 gc.gridheight = 1;
357 gc.gridwidth = 1;
358 gc.fill = GridBagConstraints.VERTICAL;
359 gc.anchor = GridBagConstraints.NORTHWEST;
360 gc.weightx = 0.0;
361 gc.weighty = 1.0;
362 pnl2.add(buildSelectionControlButtonPanel(), gc);
363
364 gc.gridx = 1;
365 gc.gridy = 1;
366 gc.weightx = 1.0;
367 gc.weighty = 1.0;
368 gc.fill = GridBagConstraints.BOTH;
369 pnl2.add(buildSelectionTablePanel(), gc);
370
371 final JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
372 splitPane.setLeftComponent(pnl);
373 splitPane.setRightComponent(pnl2);
374 splitPane.setOneTouchExpandable(false);
375 addWindowListener(new WindowAdapter() {
376 @Override
377 public void windowOpened(WindowEvent e) {
378 // has to be called when the window is visible, otherwise
379 // no effect
380 splitPane.setDividerLocation(0.6);
381 }
382 });
383
384 JPanel pnl3 = new JPanel();
385 pnl3.setLayout(new BorderLayout());
386 pnl3.add(splitPane, BorderLayout.CENTER);
387 return pnl3;
388 }
389
390 /**
391 * builds the panel with the table displaying the currently selected primitives
392 *
393 * @return
394 */
395 protected JPanel buildSelectionTablePanel() {
396 JPanel pnl = new JPanel();
397 pnl.setLayout(new BorderLayout());
398 SelectionTable tbl = new SelectionTable(selectionTableModel, new SelectionTableColumnModel(memberTableModel));
399 tbl.setMemberTableModel(memberTableModel);
400 JScrollPane pane = new JScrollPane(tbl);
401 pnl.add(pane, BorderLayout.CENTER);
402 return pnl;
403 }
404
405 /**
406 * builds the {@see JSplitPane} which divides the editor in an upper and a lower half
407 *
408 * @return the split panel
409 */
410 protected JSplitPane buildSplitPane() {
411 final JSplitPane pane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
412 pane.setTopComponent(buildTagEditorPanel());
413 pane.setBottomComponent(buildMemberEditorPanel());
414 pane.setOneTouchExpandable(true);
415 addWindowListener(new WindowAdapter() {
416 @Override
417 public void windowOpened(WindowEvent e) {
418 // has to be called when the window is visible, otherwise
419 // no effect
420 pane.setDividerLocation(0.3);
421 }
422 });
423 return pane;
424 }
425
426 /**
427 * build the panel with the buttons on the left
428 *
429 * @return
430 */
431 protected JToolBar buildLeftButtonPanel() {
432 JToolBar tb = new JToolBar();
433 tb.setOrientation(JToolBar.VERTICAL);
434 tb.setFloatable(false);
435
436 // -- move up action
437 MoveUpAction moveUpAction = new MoveUpAction();
438 memberTableModel.getSelectionModel().addListSelectionListener(moveUpAction);
439 tb.add(moveUpAction);
440
441 // -- move down action
442 MoveDownAction moveDownAction = new MoveDownAction();
443 memberTableModel.getSelectionModel().addListSelectionListener(moveDownAction);
444 tb.add(moveDownAction);
445
446 tb.addSeparator();
447
448 // -- edit action
449 EditAction editAction = new EditAction();
450 memberTableModel.getSelectionModel().addListSelectionListener(editAction);
451 tb.add(editAction);
452
453 // -- delete action
454 RemoveAction removeSelectedAction = new RemoveAction();
455 memberTable.getSelectionModel().addListSelectionListener(removeSelectedAction);
456 tb.add(removeSelectedAction);
457
458 tb.addSeparator();
459 // -- sort action
460 SortAction sortAction = new SortAction();
461 memberTableModel.addTableModelListener(sortAction);
462 tb.add(sortAction);
463
464 // -- reverse action
465 ReverseAction reverseAction = new ReverseAction();
466 memberTableModel.addTableModelListener(reverseAction);
467 tb.add(reverseAction);
468
469 tb.addSeparator();
470
471 // -- download action
472 DownloadIncompleteMembersAction downloadIncompleteMembersAction = new DownloadIncompleteMembersAction();
473 memberTable.getModel().addTableModelListener(downloadIncompleteMembersAction);
474 tb.add(downloadIncompleteMembersAction);
475
476 // -- download selected action
477 DownloadSelectedIncompleteMembersAction downloadSelectedIncompleteMembersAction = new DownloadSelectedIncompleteMembersAction();
478 memberTable.getModel().addTableModelListener(downloadSelectedIncompleteMembersAction);
479 memberTable.getSelectionModel().addListSelectionListener(downloadSelectedIncompleteMembersAction);
480 tb.add(downloadSelectedIncompleteMembersAction);
481
482 return tb;
483 }
484
485 /**
486 * build the panel with the buttons for adding or removing the current selection
487 *
488 * @return
489 */
490 protected JToolBar buildSelectionControlButtonPanel() {
491 JToolBar tb = new JToolBar(JToolBar.VERTICAL);
492 tb.setFloatable(false);
493
494 // -- add at end action
495 AddSelectedAtEndAction addSelectedAtEndAction = new AddSelectedAtEndAction();
496 selectionTableModel.addTableModelListener(addSelectedAtEndAction);
497 tb.add(addSelectedAtEndAction);
498
499 // -- select members action
500 SelectedMembersForSelectionAction selectMembersForSelectionAction = new SelectedMembersForSelectionAction();
501 selectionTableModel.addTableModelListener(selectMembersForSelectionAction);
502 memberTableModel.addTableModelListener(selectMembersForSelectionAction);
503 tb.add(selectMembersForSelectionAction);
504
505 tb.addSeparator();
506
507 // -- remove selected action
508 RemoveSelectedAction removeSelectedAction = new RemoveSelectedAction();
509 selectionTableModel.addTableModelListener(removeSelectedAction);
510 tb.add(removeSelectedAction);
511
512 // -- select action
513 SelectPrimitivesForSelectedMembersAction selectAction = new SelectPrimitivesForSelectedMembersAction();
514 memberTable.getSelectionModel().addListSelectionListener(selectAction);
515 tb.add(selectAction);
516
517 tb.addSeparator();
518
519 // -- add at start action
520 AddSelectedAtStartAction addSelectionAction = new AddSelectedAtStartAction();
521 selectionTableModel.addTableModelListener(addSelectionAction);
522 tb.add(addSelectionAction);
523
524 // -- add before selected action
525 AddSelectedBeforeSelection addSelectedBeforeSelectionAction = new AddSelectedBeforeSelection();
526 selectionTableModel.addTableModelListener(addSelectedBeforeSelectionAction);
527 memberTableModel.getSelectionModel().addListSelectionListener(addSelectedBeforeSelectionAction);
528 tb.add(addSelectedBeforeSelectionAction);
529
530 // -- add after selected action
531 AddSelectedAfterSelection addSelectedAfterSelectionAction = new AddSelectedAfterSelection();
532 selectionTableModel.addTableModelListener(addSelectedAfterSelectionAction);
533 memberTableModel.getSelectionModel().addListSelectionListener(addSelectedAfterSelectionAction);
534 tb.add(addSelectedAfterSelectionAction);
535
536 return tb;
537 }
538
539 @Override
540 protected Dimension findMaxDialogSize() {
541 return new Dimension(700, 650);
542 }
543
544 @Override
545 public void setVisible(boolean visible) {
546 if (visible) {
547 tagEditorPanel.initAutoCompletion(getLayer());
548 }
549 super.setVisible(visible);
550 if (!visible) {
551 // make sure all registered listeners are unregistered
552 //
553 selectionTableModel.unregister();
554 memberTableModel.unregister();
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 Relation newRelation = new Relation();
1021 tagEditorPanel.getModel().applyToPrimitive(newRelation);
1022 memberTableModel.applyToRelation(newRelation);
1023 List<RelationMember> newMembers = new ArrayList<RelationMember>();
1024 for (RelationMember rm: newRelation.getMembers()) {
1025 if (!rm.getMember().isDeleted()) {
1026 newMembers.add(rm);
1027 }
1028 }
1029 if (newRelation.getMembersCount() != newMembers.size()) {
1030 newRelation.setMembers(newMembers);
1031 String msg = tr("One or more members of this new relation have been deleted while the relation editor\n" +
1032 "was open. They have been removed from the relation members list.");
1033 JOptionPane.showMessageDialog(Main.parent, msg, tr("Warning"), JOptionPane.WARNING_MESSAGE);
1034 }
1035 // If the user wanted to create a new relation, but hasn't added any members or
1036 // tags, don't add an empty relation
1037 if (newRelation.getMembersCount() == 0 && !newRelation.hasKeys())
1038 return;
1039 Main.main.undoRedo.add(new AddCommand(getLayer(),newRelation));
1040
1041 // make sure everybody is notified about the changes
1042 //
1043 getLayer().data.fireSelectionChanged();
1044 GenericRelationEditor.this.setRelation(newRelation);
1045 RelationDialogManager.getRelationDialogManager().updateContext(
1046 getLayer(),
1047 getRelation(),
1048 GenericRelationEditor.this
1049 );
1050 }
1051
1052 /**
1053 * Apply the updates for an existing relation which has been changed
1054 * outside of the relation editor.
1055 *
1056 */
1057 protected void applyExistingConflictingRelation() {
1058 Relation editedRelation = new Relation(getRelation());
1059 tagEditorPanel.getModel().applyToPrimitive(editedRelation);
1060 memberTableModel.applyToRelation(editedRelation);
1061 Conflict<Relation> conflict = new Conflict<Relation>(getRelation(), editedRelation);
1062 Main.main.undoRedo.add(new ConflictAddCommand(getLayer(),conflict));
1063 }
1064
1065 /**
1066 * Apply the updates for an existing relation which has not been changed
1067 * outside of the relation editor.
1068 *
1069 */
1070 protected void applyExistingNonConflictingRelation() {
1071 Relation editedRelation = new Relation(getRelation());
1072 tagEditorPanel.getModel().applyToPrimitive(editedRelation);
1073 memberTableModel.applyToRelation(editedRelation);
1074 Main.main.undoRedo.add(new ChangeCommand(getRelation(), editedRelation));
1075 getLayer().data.fireSelectionChanged();
1076 getLayer().fireDataChange();
1077 // this will refresh the snapshot and update the dialog title
1078 //
1079 setRelation(getRelation());
1080 }
1081
1082 protected boolean confirmClosingBecauseOfDirtyState() {
1083 ButtonSpec [] options = new ButtonSpec[] {
1084 new ButtonSpec(
1085 tr("Yes, create a conflict and close"),
1086 ImageProvider.get("ok"),
1087 tr("Click to create a conflict and close this relation editor") ,
1088 null /* no specific help topic */
1089 ),
1090 new ButtonSpec(
1091 tr("No, continue editing"),
1092 ImageProvider.get("cancel"),
1093 tr("Click to return to the relation editor and to resume relation editing") ,
1094 null /* no specific help topic */
1095 )
1096 };
1097
1098 int ret = HelpAwareOptionPane.showOptionDialog(
1099 Main.parent,
1100 tr("<html>This relation has been changed outside of the editor.<br>"
1101 + "You cannot apply your changes and continue editing.<br>"
1102 + "<br>"
1103 + "Do you want to create a conflict and close the editor?</html>"),
1104 tr("Conflict in data"),
1105 JOptionPane.WARNING_MESSAGE,
1106 null,
1107 options,
1108 options[0], // OK is default
1109 "/Dialog/RelationEditor#RelationChangedOutsideOfEditor"
1110 );
1111 return ret == 0;
1112 }
1113
1114 protected void warnDoubleConflict() {
1115 JOptionPane.showMessageDialog(
1116 Main.parent,
1117 tr("<html>Layer ''{0}'' already has a conflict for primitive<br>"
1118 + "''{1}''.<br>"
1119 + "Please resolve this conflict first, then try again.</html>",
1120 getLayer().getName(),
1121 getRelation().getDisplayName(DefaultNameFormatter.getInstance())
1122 ),
1123 tr("Double conflict"),
1124 JOptionPane.WARNING_MESSAGE
1125 );
1126 }
1127 }
1128
1129 class ApplyAction extends SavingAction {
1130 public ApplyAction() {
1131 putValue(SHORT_DESCRIPTION, tr("Apply the current updates"));
1132 putValue(SMALL_ICON, ImageProvider.get("save"));
1133 putValue(NAME, tr("Apply"));
1134 setEnabled(true);
1135 }
1136
1137 public void run() {
1138 if (getRelation() == null) {
1139 applyNewRelation();
1140 } else if (!memberTableModel.hasSameMembersAs(getRelationSnapshot())
1141 || tagEditorPanel.getModel().isDirty()) {
1142 if (isDirtyRelation()) {
1143 if (confirmClosingBecauseOfDirtyState()) {
1144 if (getLayer().getConflicts().hasConflictForMy(getRelation())) {
1145 warnDoubleConflict();
1146 return;
1147 }
1148 applyExistingConflictingRelation();
1149 setVisible(false);
1150 }
1151 } else {
1152 applyExistingNonConflictingRelation();
1153 }
1154 }
1155 }
1156
1157 public void actionPerformed(ActionEvent e) {
1158 run();
1159 }
1160 }
1161
1162 class OKAction extends SavingAction {
1163 public OKAction() {
1164 putValue(SHORT_DESCRIPTION, tr("Apply the updates and close the dialog"));
1165 putValue(SMALL_ICON, ImageProvider.get("ok"));
1166 putValue(NAME, tr("OK"));
1167 setEnabled(true);
1168 }
1169
1170 public void run() {
1171 if (getRelation() == null) {
1172 applyNewRelation();
1173 } else if (!memberTableModel.hasSameMembersAs(getRelationSnapshot())
1174 || tagEditorPanel.getModel().isDirty()) {
1175 if (isDirtyRelation()) {
1176 if (confirmClosingBecauseOfDirtyState()) {
1177 if (getLayer().getConflicts().hasConflictForMy(getRelation())) {
1178 warnDoubleConflict();
1179 return;
1180 }
1181 applyExistingConflictingRelation();
1182 } else
1183 return;
1184 } else {
1185 applyExistingNonConflictingRelation();
1186 }
1187 }
1188 setVisible(false);
1189 }
1190
1191 public void actionPerformed(ActionEvent e) {
1192 run();
1193 }
1194 }
1195
1196 class CancelAction extends AbstractAction {
1197 public CancelAction() {
1198 putValue(SHORT_DESCRIPTION, tr("Cancel the updates and close the dialog"));
1199 putValue(SMALL_ICON, ImageProvider.get("cancel"));
1200 putValue(NAME, tr("Cancel"));
1201
1202 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
1203 .put(KeyStroke.getKeyStroke("ESCAPE"), "ESCAPE");
1204 getRootPane().getActionMap().put("ESCAPE", this);
1205 setEnabled(true);
1206 }
1207
1208 public void actionPerformed(ActionEvent e) {
1209 setVisible(false);
1210 }
1211 }
1212
1213 class AddTagAction extends AbstractAction {
1214 public AddTagAction() {
1215 putValue(SHORT_DESCRIPTION, tr("Add an empty tag"));
1216 putValue(SMALL_ICON, ImageProvider.get("dialogs", "add"));
1217 // putValue(NAME, tr("Cancel"));
1218 setEnabled(true);
1219 }
1220
1221 public void actionPerformed(ActionEvent e) {
1222 tagEditorPanel.getModel().appendNewTag();
1223 }
1224 }
1225
1226 class DownloadIncompleteMembersAction extends AbstractAction implements TableModelListener {
1227 public DownloadIncompleteMembersAction() {
1228 putValue(SHORT_DESCRIPTION, tr("Download all incomplete members"));
1229 putValue(SMALL_ICON, ImageProvider.get("dialogs/relation", "downloadincomplete"));
1230 putValue(NAME, tr("Download Members"));
1231 Shortcut.registerShortcut("relationeditor:downloadincomplete", tr("Relation Editor: Download Members"),
1232 KeyEvent.VK_K, Shortcut.GROUP_MNEMONIC);
1233 updateEnabledState();
1234 }
1235
1236 public void actionPerformed(ActionEvent e) {
1237 if (!isEnabled())
1238 return;
1239 Main.worker.submit(new DownloadRelationMemberTask(
1240 getRelation(),
1241 memberTableModel.getIncompleteMemberPrimitives(),
1242 getLayer(),
1243 memberTableModel,
1244 GenericRelationEditor.this)
1245 );
1246 }
1247
1248 protected void updateEnabledState() {
1249 setEnabled(
1250 getRelation() != null
1251 && memberTableModel.hasIncompleteMembers()
1252 );
1253 }
1254
1255 public void tableChanged(TableModelEvent e) {
1256 updateEnabledState();
1257 }
1258 }
1259
1260 class DownloadSelectedIncompleteMembersAction extends AbstractAction implements ListSelectionListener, TableModelListener{
1261 public DownloadSelectedIncompleteMembersAction() {
1262 putValue(SHORT_DESCRIPTION, tr("Download selected incomplete members"));
1263 putValue(SMALL_ICON, ImageProvider.get("dialogs/relation", "downloadincompleteselected"));
1264 putValue(NAME, tr("Download Members"));
1265 Shortcut.registerShortcut("relationeditor:downloadincomplete", tr("Relation Editor: Download Members"),
1266 KeyEvent.VK_K, Shortcut.GROUP_MNEMONIC);
1267 updateEnabledState();
1268 }
1269
1270 public void actionPerformed(ActionEvent e) {
1271 if (!isEnabled())
1272 return;
1273 Main.worker.submit(new DownloadRelationMemberTask(
1274 getRelation(),
1275 memberTableModel.getSelectedIncompleteMemberPrimitives(),
1276 getLayer(),
1277 memberTableModel,
1278 GenericRelationEditor.this)
1279 );
1280 }
1281
1282 protected void updateEnabledState() {
1283 setEnabled(
1284 getRelation() != null
1285 && memberTableModel.hasIncompleteSelectedMembers()
1286 );
1287 }
1288
1289 public void valueChanged(ListSelectionEvent e) {
1290 updateEnabledState();
1291 }
1292
1293 public void tableChanged(TableModelEvent e) {
1294 updateEnabledState();
1295 }
1296 }
1297
1298 class SetRoleAction extends AbstractAction implements ListSelectionListener, DocumentListener {
1299 public SetRoleAction() {
1300 putValue(SHORT_DESCRIPTION, tr("Sets a role for the selected members"));
1301 putValue(SMALL_ICON, ImageProvider.get("apply"));
1302 putValue(NAME, tr("Apply Role"));
1303 refreshEnabled();
1304 }
1305
1306 protected void refreshEnabled() {
1307 setEnabled(memberTable.getSelectedRowCount() > 0);
1308 }
1309
1310 protected boolean isEmptyRole() {
1311 return tfRole.getText() == null || tfRole.getText().trim().equals("");
1312 }
1313
1314 protected boolean confirmSettingEmptyRole(int onNumMembers) {
1315 String message = "<html>"
1316 + trn("You are setting an empty role on {0} primitive.",
1317 "You are setting an empty role on {0} primitives.", onNumMembers, onNumMembers)
1318 + "<br>"
1319 + tr("This is equal to deleting the roles of these primitives.") +
1320 "<br>"
1321 + tr("Do you really want to apply the new role?") + "</html>";
1322 String [] options = new String[] {
1323 tr("Yes, apply it"),
1324 tr("No, do not apply")
1325 };
1326 int ret = ConditionalOptionPaneUtil.showOptionDialog(
1327 "relation_editor.confirm_applying_empty_role",
1328 Main.parent,
1329 message,
1330 tr("Confirm empty role"),
1331 JOptionPane.YES_NO_OPTION,
1332 JOptionPane.WARNING_MESSAGE,
1333 options,
1334 options[0]
1335 );
1336 switch(ret) {
1337 case JOptionPane.YES_OPTION: return true;
1338 case ConditionalOptionPaneUtil.DIALOG_DISABLED_OPTION: return true;
1339 default:
1340 return false;
1341 }
1342 }
1343
1344 public void actionPerformed(ActionEvent e) {
1345 if (isEmptyRole()) {
1346 if (! confirmSettingEmptyRole(memberTable.getSelectedRowCount()))
1347 return;
1348 }
1349 memberTableModel.updateRole(memberTable.getSelectedRows(), tfRole.getText());
1350 }
1351
1352 public void valueChanged(ListSelectionEvent e) {
1353 refreshEnabled();
1354 }
1355
1356 public void changedUpdate(DocumentEvent e) {
1357 refreshEnabled();
1358 }
1359
1360 public void insertUpdate(DocumentEvent e) {
1361 refreshEnabled();
1362 }
1363
1364 public void removeUpdate(DocumentEvent e) {
1365 refreshEnabled();
1366 }
1367 }
1368
1369 /**
1370 * Creates a new relation with a copy of the current editor state
1371 *
1372 */
1373 class DuplicateRelationAction extends AbstractAction {
1374 public DuplicateRelationAction() {
1375 putValue(SHORT_DESCRIPTION, tr("Create a copy of this relation and open it in another editor window"));
1376 // FIXME provide an icon
1377 putValue(SMALL_ICON, ImageProvider.get("duplicate"));
1378 putValue(NAME, tr("Duplicate"));
1379 setEnabled(true);
1380 }
1381
1382 public void actionPerformed(ActionEvent e) {
1383 Relation copy = new Relation();
1384 tagEditorPanel.getModel().applyToPrimitive(copy);
1385 memberTableModel.applyToRelation(copy);
1386 RelationEditor editor = RelationEditor.getEditor(getLayer(), copy, memberTableModel.getSelectedMembers());
1387 editor.setVisible(true);
1388 }
1389 }
1390
1391 /**
1392 * Action for editing the currently selected relation
1393 *
1394 *
1395 */
1396 class EditAction extends AbstractAction implements ListSelectionListener {
1397 public EditAction() {
1398 putValue(SHORT_DESCRIPTION, tr("Edit the relation the currently selected relation member refers to"));
1399 putValue(SMALL_ICON, ImageProvider.get("dialogs", "edit"));
1400 //putValue(NAME, tr("Edit"));
1401 refreshEnabled();
1402 }
1403
1404 protected void refreshEnabled() {
1405 setEnabled(memberTable.getSelectedRowCount() == 1
1406 && memberTableModel.isEditableRelation(memberTable.getSelectedRow()));
1407 }
1408
1409 protected Collection<RelationMember> getMembersForCurrentSelection(Relation r) {
1410 Collection<RelationMember> members = new HashSet<RelationMember>();
1411 Collection<OsmPrimitive> selection = getLayer().data.getSelected();
1412 for (RelationMember member: r.getMembers()) {
1413 if (selection.contains(member.getMember())) {
1414 members.add(member);
1415 }
1416 }
1417 return members;
1418 }
1419
1420 public void run() {
1421 int idx = memberTable.getSelectedRow();
1422 if (idx < 0)
1423 return;
1424 OsmPrimitive primitive = memberTableModel.getReferredPrimitive(idx);
1425 if (!(primitive instanceof Relation))
1426 return;
1427 Relation r = (Relation) primitive;
1428 if (r.isIncomplete())
1429 return;
1430
1431 RelationEditor editor = RelationEditor.getEditor(getLayer(), r, getMembersForCurrentSelection(r));
1432 editor.setVisible(true);
1433 }
1434
1435 public void actionPerformed(ActionEvent e) {
1436 if (!isEnabled())
1437 return;
1438 run();
1439 }
1440
1441 public void valueChanged(ListSelectionEvent e) {
1442 refreshEnabled();
1443 }
1444 }
1445
1446 class MemberTableDblClickAdapter extends MouseAdapter {
1447 @Override
1448 public void mouseClicked(MouseEvent e) {
1449 if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2) {
1450 new EditAction().run();
1451 }
1452 }
1453 }
1454}
Note: See TracBrowser for help on using the repository browser.