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

Last change on this file since 3210 was 3210, checked in by bastiK, 15 years ago

autocompletion rework; breaks tageditor plugin and possibly other plugins
(merged the 2 autocompletion systems; adds presets values for the properties dialog )

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