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

Last change on this file since 3289 was 3289, checked in by stoecker, 14 years ago

added role support to presets

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