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

Last change on this file since 3202 was 3202, checked in by bastiK, 14 years ago

preliminary fix for (see #4861) (Don't raise exception, just give warning on the console)

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