source: osm/applications/editors/josm/plugins/reltoolbox/src/relcontext/RelContextDialog.java@ 35351

Last change on this file since 35351 was 34788, checked in by gerdp, 6 years ago

see #17040 call removeActiveLayerChangeListener() in destroy()

  • Property svn:eol-style set to native
File size: 32.0 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package relcontext;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.BorderLayout;
7import java.awt.Color;
8import java.awt.Component;
9import java.awt.Dialog.ModalityType;
10import java.awt.Dimension;
11import java.awt.GridBagLayout;
12import java.awt.Point;
13import java.awt.event.ActionEvent;
14import java.awt.event.ActionListener;
15import java.awt.event.ItemEvent;
16import java.awt.event.ItemListener;
17import java.awt.event.KeyEvent;
18import java.awt.event.MouseAdapter;
19import java.awt.event.MouseEvent;
20import java.awt.event.MouseListener;
21import java.beans.PropertyChangeEvent;
22import java.beans.PropertyChangeListener;
23import java.io.BufferedReader;
24import java.io.InputStream;
25import java.io.InputStreamReader;
26import java.nio.charset.StandardCharsets;
27import java.util.ArrayList;
28import java.util.Collection;
29import java.util.Collections;
30import java.util.HashMap;
31import java.util.List;
32import java.util.Map;
33import java.util.Set;
34import java.util.StringTokenizer;
35import java.util.TreeSet;
36
37import javax.swing.AbstractListModel;
38import javax.swing.Action;
39import javax.swing.Box;
40import javax.swing.ComboBoxModel;
41import javax.swing.JButton;
42import javax.swing.JCheckBoxMenuItem;
43import javax.swing.JComboBox;
44import javax.swing.JComponent;
45import javax.swing.JDialog;
46import javax.swing.JLabel;
47import javax.swing.JOptionPane;
48import javax.swing.JPanel;
49import javax.swing.JPopupMenu;
50import javax.swing.JScrollPane;
51import javax.swing.JTable;
52import javax.swing.ListSelectionModel;
53import javax.swing.SwingUtilities;
54import javax.swing.event.ListSelectionEvent;
55import javax.swing.event.ListSelectionListener;
56import javax.swing.table.DefaultTableCellRenderer;
57import javax.swing.table.DefaultTableModel;
58import javax.swing.table.TableColumnModel;
59
60import org.openstreetmap.josm.actions.JosmAction;
61import org.openstreetmap.josm.command.ChangeRelationMemberRoleCommand;
62import org.openstreetmap.josm.command.Command;
63import org.openstreetmap.josm.command.SequenceCommand;
64import org.openstreetmap.josm.data.UndoRedoHandler;
65import org.openstreetmap.josm.data.osm.DataSelectionListener;
66import org.openstreetmap.josm.data.osm.DataSet;
67import org.openstreetmap.josm.data.osm.DefaultNameFormatter;
68import org.openstreetmap.josm.data.osm.IPrimitive;
69import org.openstreetmap.josm.data.osm.OsmPrimitive;
70import org.openstreetmap.josm.data.osm.Relation;
71import org.openstreetmap.josm.data.osm.RelationMember;
72import org.openstreetmap.josm.data.osm.event.DatasetEventManager;
73import org.openstreetmap.josm.data.osm.event.DatasetEventManager.FireMode;
74import org.openstreetmap.josm.data.osm.event.SelectionEventManager;
75import org.openstreetmap.josm.gui.MainApplication;
76import org.openstreetmap.josm.gui.PrimitiveRenderer;
77import org.openstreetmap.josm.gui.dialogs.ToggleDialog;
78import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
79import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
80import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingComboBox;
81import org.openstreetmap.josm.spi.preferences.Config;
82import org.openstreetmap.josm.tools.GBC;
83import org.openstreetmap.josm.tools.Logging;
84import org.openstreetmap.josm.tools.Shortcut;
85
86import relcontext.actions.AddRemoveMemberAction;
87import relcontext.actions.ClearChosenRelationAction;
88import relcontext.actions.CreateMultipolygonAction;
89import relcontext.actions.CreateRelationAction;
90import relcontext.actions.DeleteChosenRelationAction;
91import relcontext.actions.DownloadChosenRelationAction;
92import relcontext.actions.DownloadParentsAction;
93import relcontext.actions.DuplicateChosenRelationAction;
94import relcontext.actions.EditChosenRelationAction;
95import relcontext.actions.FindRelationAction;
96import relcontext.actions.ReconstructPolygonAction;
97import relcontext.actions.ReconstructRouteAction;
98import relcontext.actions.RelationHelpAction;
99import relcontext.actions.SelectInRelationPanelAction;
100import relcontext.actions.SelectMembersAction;
101import relcontext.actions.SelectRelationAction;
102import relcontext.actions.SortAndFixAction;
103
104/**
105 * The new, advanced relation editing panel.
106 *
107 * @author Zverik
108 */
109public class RelContextDialog extends ToggleDialog implements ActiveLayerChangeListener, ChosenRelationListener, DataSelectionListener {
110
111 public static final String PREF_PREFIX = "reltoolbox";
112
113 private final DefaultTableModel relationsData;
114 private ChosenRelation chosenRelation;
115 private JPanel chosenRelationPanel;
116 private ChosenRelationPopupMenu popupMenu;
117 private MultipolygonSettingsPopup multiPopupMenu;
118 private RoleComboBoxModel roleBoxModel;
119 private SortAndFixAction sortAndFixAction;
120 // actions saved for unregistering on dialog destroying
121 private final EnterRoleAction enterRoleAction;
122 private final FindRelationAction findRelationAction;
123 private final CreateMultipolygonAction createMultipolygonAction;
124 private final CreateRelationAction createRelationAction;
125 private final AddRemoveMemberAction addRemoveMemberAction;
126
127 public RelContextDialog() {
128 super(tr("Relation Toolbox"), PREF_PREFIX, tr("Open relation/multipolygon editor panel"), null, 150, true);
129
130 chosenRelation = new ChosenRelation();
131 chosenRelation.addChosenRelationListener(this);
132 MainApplication.getLayerManager().addActiveLayerChangeListener(chosenRelation);
133
134 popupMenu = new ChosenRelationPopupMenu(chosenRelation);
135 multiPopupMenu = new MultipolygonSettingsPopup();
136
137 JPanel rcPanel = new JPanel(new BorderLayout());
138
139 relationsData = new RelationTableModel();
140 relationsData.setColumnIdentifiers(new String[] {tr("Member Of"), tr("Role")});
141 final JTable relationsTable = new JTable(relationsData);
142 configureRelationsTable(relationsTable);
143 rcPanel.add(new JScrollPane(relationsTable,
144 JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER), BorderLayout.CENTER);
145
146 final MouseListener relationMouseAdapter = new ChosenRelationMouseAdapter();
147 final JComboBox<String> roleBox = new JComboBox<>();
148 roleBoxModel = new RoleComboBoxModel(roleBox);
149 roleBox.setModel(roleBoxModel);
150 roleBox.addMouseListener(relationMouseAdapter);
151 roleBox.addItemListener(new ItemListener() {
152 @Override
153 public void itemStateChanged(ItemEvent e) {
154 if (e.getStateChange() == ItemEvent.DESELECTED) return;
155 String memberRole = roleBoxModel.getSelectedMembersRole();
156 String selectedRole = roleBoxModel.isAnotherRoleSelected() ? askForRoleName() : roleBoxModel.getSelectedRole();
157 if (memberRole != null && selectedRole != null && !memberRole.equals(selectedRole)) {
158 applyRoleToSelection(selectedRole.trim());
159 }
160 }
161 });
162 roleBox.setVisible(false);
163 enterRoleAction = new EnterRoleAction(); // just for the shortcut
164 sortAndFixAction = new SortAndFixAction(chosenRelation);
165
166 // [±][X] relation U [AZ][Down][Edit]
167 chosenRelationPanel = new JPanel(new GridBagLayout());
168 addRemoveMemberAction = new AddRemoveMemberAction(chosenRelation, sortAndFixAction);
169 chosenRelationPanel.add(new JButton(addRemoveMemberAction), GBC.std());
170 chosenRelationPanel.add(sizeButton(new JButton(new ClearChosenRelationAction(chosenRelation)), 32, 0), GBC.std());
171 final ChosenRelationComponent chosenRelationComponent = new ChosenRelationComponent(chosenRelation);
172 chosenRelationComponent.addMouseListener(relationMouseAdapter);
173 chosenRelationPanel.add(chosenRelationComponent, GBC.std().fill().insets(5, 0, 5, 0));
174 chosenRelationPanel.add(roleBox, GBC.std().fill().insets(5, 0, 5, 0));
175 final JButton sortAndFixButton = (JButton) sizeButton(new JButton(sortAndFixAction), 32, 0);
176 chosenRelationPanel.add(sortAndFixButton, GBC.std().fill(GBC.VERTICAL));
177 final Action downloadChosenRelationAction = new DownloadChosenRelationAction(chosenRelation);
178 final JButton downloadButton = (JButton) sizeButton(new JButton(downloadChosenRelationAction), 32, 0);
179 chosenRelationPanel.add(downloadButton, GBC.std().fill(GBC.VERTICAL));
180 chosenRelationPanel.add(new JButton(new EditChosenRelationAction(chosenRelation)), GBC.eol().fill(GBC.VERTICAL));
181
182 rcPanel.add(chosenRelationPanel, BorderLayout.NORTH);
183
184 roleBox.addPropertyChangeListener("enabled", new PropertyChangeListener() {
185 @Override
186 public void propertyChange(PropertyChangeEvent evt) {
187 boolean showRoleBox = roleBox.isEnabled();
188 roleBox.setVisible(showRoleBox);
189 chosenRelationComponent.setVisible(!showRoleBox);
190 }
191 });
192
193 sortAndFixAction.addPropertyChangeListener(new PropertyChangeListener() {
194 @Override
195 public void propertyChange(PropertyChangeEvent evt) {
196 sortAndFixButton.setVisible(sortAndFixAction.isEnabled());
197 }
198 });
199 sortAndFixButton.setVisible(false);
200
201 downloadChosenRelationAction.addPropertyChangeListener(new PropertyChangeListener() {
202 @Override
203 public void propertyChange(PropertyChangeEvent evt) {
204 downloadButton.setVisible(downloadChosenRelationAction.isEnabled());
205 }
206 });
207 downloadButton.setVisible(false);
208 if (Config.getPref().getBoolean(PREF_PREFIX + ".hidetopline", false)) {
209 chosenRelationPanel.setVisible(false);
210 }
211
212 // [+][Multi] [X]Adm [X]Tags [X]1
213 JPanel bottomLine = new JPanel(new GridBagLayout());
214 createRelationAction = new CreateRelationAction(chosenRelation);
215 bottomLine.add(new JButton(createRelationAction), GBC.std());
216 createMultipolygonAction = new CreateMultipolygonAction(chosenRelation);
217 final JButton multipolygonButton = new JButton(createMultipolygonAction);
218 bottomLine.add(multipolygonButton, GBC.std());
219 // bottomLine.add(sizeButton(new JButton(new MultipolygonSettingsAction()), 16, 0), GBC.std().fill(GBC.VERTICAL));
220 bottomLine.add(Box.createHorizontalGlue(), GBC.std().fill());
221 findRelationAction = new FindRelationAction(chosenRelation);
222 bottomLine.add(new JButton(findRelationAction), GBC.eol());
223 rcPanel.add(sizeButton(bottomLine, 0, 24), BorderLayout.SOUTH);
224
225 multipolygonButton.addMouseListener(new MouseAdapter() {
226 @Override
227 public void mousePressed(MouseEvent e) {
228 checkPopup(e);
229 }
230
231 @Override
232 public void mouseReleased(MouseEvent e) {
233 checkPopup(e);
234 }
235
236 private void checkPopup(MouseEvent e) {
237 if (e.isPopupTrigger()) {
238 multiPopupMenu.show(e.getComponent(), e.getX(), e.getY());
239 }
240 }
241 });
242
243 createLayout(rcPanel, false, null);
244 }
245
246 private static final Color CHOSEN_RELATION_COLOR = new Color(255, 255, 128);
247
248 private void configureRelationsTable(final JTable relationsTable) {
249 relationsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
250 relationsTable.setTableHeader(null);
251 relationsTable.addMouseListener(new MouseAdapter() {
252 @Override
253 public void mouseClicked(MouseEvent e) {
254 Point p = e.getPoint();
255 int row = relationsTable.rowAtPoint(p);
256 if (SwingUtilities.isLeftMouseButton(e) && row >= 0) {
257 Relation relation = (Relation) relationsData.getValueAt(row, 0);
258 if (e.getClickCount() > 1) {
259 MainApplication.getLayerManager().getEditLayer().data.setSelected(relation);
260 }
261 }
262 }
263
264 @Override
265 public void mousePressed(MouseEvent e) {
266 checkPopup(e);
267 }
268
269 @Override
270 public void mouseReleased(MouseEvent e) {
271 checkPopup(e);
272 }
273
274 public void checkPopup(MouseEvent e) {
275 if (e.isPopupTrigger()) {
276 Point p = e.getPoint();
277 int row = relationsTable.rowAtPoint(p);
278 if (row > -1) {
279 Relation relation = (Relation) relationsData.getValueAt(row, 0);
280 JPopupMenu menu = chosenRelation.isSame(relation) ? popupMenu
281 : new ChosenRelationPopupMenu(new StaticChosenRelation(relation));
282 menu.show(relationsTable, p.x, p.y-5);
283 }
284 }
285 }
286 });
287
288 TableColumnModel columns = relationsTable.getColumnModel();
289 columns.getColumn(0).setCellRenderer(new PrimitiveRenderer() {
290 @Override
291 protected String getComponentToolTipText(IPrimitive value) {
292 return null;
293 }
294
295 @Override
296 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
297 int row, int column) {
298 Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
299 if (!isSelected && value instanceof Relation && chosenRelation.isSame(value)) {
300 c.setBackground(CHOSEN_RELATION_COLOR);
301 } else {
302 c.setBackground(table.getBackground());
303 }
304 return c;
305 }
306
307 });
308 columns.getColumn(1).setCellRenderer(new DefaultTableCellRenderer() {
309 @Override
310 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
311 int row, int column) {
312 Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
313 if (!isSelected && chosenRelation.isSame(table.getValueAt(row, 0))) {
314 c.setBackground(CHOSEN_RELATION_COLOR);
315 } else {
316 c.setBackground(table.getBackground());
317 }
318 return c;
319 }
320 });
321 columns.getColumn(1).setPreferredWidth(40);
322 columns.getColumn(0).setPreferredWidth(220);
323 relationsTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
324 @Override
325 public void valueChanged(ListSelectionEvent e) {
326 int selectedRow = relationsTable.getSelectedRow();
327 if (selectedRow >= 0) {
328 chosenRelation.set((Relation) relationsData.getValueAt(selectedRow, 0));
329 relationsTable.clearSelection();
330 }
331 }
332 });
333 }
334
335 private JComponent sizeButton(JComponent b, int width, int height) {
336 Dimension pref = b.getPreferredSize();
337 b.setPreferredSize(new Dimension(width <= 0 ? pref.width : width, height <= 0 ? pref.height : height));
338 return b;
339 }
340
341 @Override
342 public void hideNotify() {
343 SelectionEventManager.getInstance().removeSelectionListener(this);
344 MainApplication.getLayerManager().removeActiveLayerChangeListener(this);
345 DatasetEventManager.getInstance().removeDatasetListener(chosenRelation);
346 chosenRelation.clear();
347 }
348
349 @Override
350 public void showNotify() {
351 SelectionEventManager.getInstance().addSelectionListenerForEdt(this);
352 MainApplication.getLayerManager().addActiveLayerChangeListener(this);
353 DatasetEventManager.getInstance().addDatasetListener(chosenRelation, FireMode.IN_EDT);
354 }
355
356 public ChosenRelation getChosenRelation() {
357 return chosenRelation;
358 }
359
360 @Override
361 public void chosenRelationChanged(Relation oldRelation, Relation newRelation) {
362 if (chosenRelationPanel != null && Config.getPref().getBoolean(PREF_PREFIX + ".hidetopline", false)) {
363 chosenRelationPanel.setVisible(newRelation != null);
364 }
365 DataSet ds = MainApplication.getLayerManager().getEditDataSet();
366 if (ds != null) {
367 doSelectionChanged(ds.getSelected());
368 }
369 roleBoxModel.update();
370 }
371
372 @Override
373 public void selectionChanged(SelectionChangeEvent event) {
374 doSelectionChanged(event.getSelection());
375 }
376
377 private void doSelectionChanged(Collection<? extends OsmPrimitive> newSelection) {
378 if (!isVisible() || relationsData == null)
379 return;
380 roleBoxModel.update();
381 // repopulate relations table
382 relationsData.setRowCount(0);
383 sortAndFixAction.chosenRelationChanged(chosenRelation.get(), chosenRelation.get());
384 if (newSelection == null)
385 return;
386
387 Set<Relation> relations = new TreeSet<>(
388 DefaultNameFormatter.getInstance().getRelationComparator());
389 for (OsmPrimitive element : newSelection) {
390 for (OsmPrimitive ref : element.getReferrers()) {
391 if (ref instanceof Relation && !ref.isIncomplete() && !ref.isDeleted()) {
392 relations.add((Relation) ref);
393 }
394 }
395 }
396
397 for (Relation rel : relations) {
398 String role = null;
399 for (RelationMember m : rel.getMembers()) {
400 for (OsmPrimitive element : newSelection) {
401 if (m.getMember().equals(element)) {
402 if (role == null) {
403 role = m.getRole();
404 } else if (!role.equals(m.getRole())) {
405 role = tr("<different>");
406 break;
407 }
408 }
409 }
410 }
411 relationsData.addRow(new Object[] {rel, role == null ? "" : role});
412 }
413 for (OsmPrimitive element : newSelection) {
414 if (element instanceof Relation && !chosenRelation.isSame(element)) {
415 relationsData.addRow(new Object[] {element, ""});
416 }
417 }
418 }
419
420 private void updateSelection() {
421 if (MainApplication.getLayerManager().getEditDataSet() == null) {
422 doSelectionChanged(Collections.<OsmPrimitive>emptyList());
423 } else {
424 doSelectionChanged(MainApplication.getLayerManager().getEditDataSet().getSelected());
425 }
426 }
427
428 @Override
429 public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
430 updateSelection();
431 }
432
433 @Override
434 public void destroy() {
435 enterRoleAction.destroy();
436 findRelationAction.destroy();
437 createMultipolygonAction.destroy();
438 createRelationAction.destroy();
439 addRemoveMemberAction.destroy();
440 MainApplication.getLayerManager().removeActiveLayerChangeListener(chosenRelation);
441 super.destroy();
442 }
443
444 private static final String POSSIBLE_ROLES_FILE = "relcontext/possible_roles.txt";
445 private static final Map<String, List<String>> possibleRoles = loadRoles();
446
447 private static Map<String, List<String>> loadRoles() {
448 Map<String, List<String>> result = new HashMap<>();
449
450 ClassLoader classLoader = RelContextDialog.class.getClassLoader();
451 try (InputStream possibleRolesStream = classLoader.getResourceAsStream(POSSIBLE_ROLES_FILE);
452 BufferedReader r = new BufferedReader(new InputStreamReader(possibleRolesStream, StandardCharsets.UTF_8));
453 ) {
454 while (r.ready()) {
455 String line = r.readLine();
456 String[] typeAndRoles = line.split(":", 2);
457 if (typeAndRoles.length == 2 && typeAndRoles[1].length() > 0) {
458 String type = typeAndRoles[0].trim();
459 StringTokenizer t = new StringTokenizer(typeAndRoles[1], " ,;\"");
460 List<String> roles = new ArrayList<>();
461 while (t.hasMoreTokens()) {
462 roles.add(t.nextToken());
463 }
464 result.put(type, roles);
465 }
466 }
467 } catch (Exception e) {
468 Logging.error("[RelToolbox] Error reading possible roles file.");
469 Logging.error(e);
470 }
471 return result;
472 }
473
474 private String askForRoleName() {
475 JPanel panel = new JPanel(new GridBagLayout());
476
477 List<String> items = new ArrayList<>();
478 for (String role : roleBoxModel.getRoles()) {
479 if (role.length() > 1) {
480 items.add(role);
481 }
482 }
483 final AutoCompletingComboBox role = new AutoCompletingComboBox();
484 role.setPossibleItems(items);
485 role.setEditable(true);
486
487 panel.add(new JLabel(tr("Role")), GBC.std());
488 panel.add(Box.createHorizontalStrut(10), GBC.std());
489 panel.add(role, GBC.eol().fill(GBC.HORIZONTAL));
490
491 final JOptionPane optionPane = new JOptionPane(panel, JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION) {
492 @Override
493 public void selectInitialValue() {
494 role.requestFocusInWindow();
495 role.getEditor().selectAll();
496 }
497 };
498 final JDialog dlg = optionPane.createDialog(MainApplication.getMainFrame(), tr("Specify role"));
499 dlg.setModalityType(ModalityType.DOCUMENT_MODAL);
500
501 role.getEditor().addActionListener(new ActionListener() {
502 @Override
503 public void actionPerformed(ActionEvent e) {
504 dlg.setVisible(false);
505 optionPane.setValue(JOptionPane.OK_OPTION);
506 }
507 });
508
509 dlg.setVisible(true);
510
511 Object answer = optionPane.getValue();
512 if (answer == null || answer == JOptionPane.UNINITIALIZED_VALUE
513 || (answer instanceof Integer && (Integer) answer != JOptionPane.OK_OPTION))
514 return null;
515
516 return role.getEditor().getItem().toString().trim();
517 }
518
519 private class ChosenRelationMouseAdapter extends MouseAdapter {
520 @Override
521 public void mouseClicked(MouseEvent e) {
522 if (e.isControlDown() || !(e.getComponent() instanceof JComboBox)) // do not use left click handler on combo box
523 if (SwingUtilities.isLeftMouseButton(e) && chosenRelation.get() != null
524 && MainApplication.getLayerManager().getEditLayer() != null) {
525 MainApplication.getLayerManager().getEditLayer().data.setSelected(chosenRelation.get());
526 }
527 }
528
529 @Override
530 public void mousePressed(MouseEvent e) {
531 checkPopup(e);
532 }
533
534 @Override
535 public void mouseReleased(MouseEvent e) {
536 checkPopup(e);
537 }
538
539 private void checkPopup(MouseEvent e) {
540 if (e.isPopupTrigger() && chosenRelation.get() != null) {
541 popupMenu.show(e.getComponent(), e.getX(), e.getY() - 5);
542 }
543 }
544 }
545
546 private static class ChosenRelationPopupMenu extends JPopupMenu {
547 ChosenRelationPopupMenu(ChosenRelation chosenRelation) {
548 add(new SelectMembersAction(chosenRelation));
549 add(new SelectRelationAction(chosenRelation));
550 add(new DuplicateChosenRelationAction(chosenRelation));
551 add(new DeleteChosenRelationAction(chosenRelation));
552 add(new DownloadParentsAction(chosenRelation));
553 add(new ReconstructPolygonAction(chosenRelation));
554 add(new ReconstructRouteAction(chosenRelation));
555 addSeparator();
556 add(new SelectInRelationPanelAction(chosenRelation));
557 add(new RelationHelpAction(chosenRelation));
558 }
559 }
560
561 protected void applyRoleToSelection(String role) {
562 if (chosenRelation != null && chosenRelation.get() != null
563 && MainApplication.getLayerManager().getEditDataSet() != null
564 && !MainApplication.getLayerManager().getEditDataSet().selectionEmpty()) {
565 Collection<OsmPrimitive> selected = MainApplication.getLayerManager().getEditDataSet().getSelected();
566 Relation r = chosenRelation.get();
567 List<Command> commands = new ArrayList<>();
568 for (int i = 0; i < r.getMembersCount(); i++) {
569 RelationMember m = r.getMember(i);
570 if (selected.contains(m.getMember())) {
571 if (!role.equals(m.getRole())) {
572 commands.add(new ChangeRelationMemberRoleCommand(r, i, role));
573 }
574 }
575 }
576 if (!commands.isEmpty()) {
577 // UndoRedoHandler.getInstance().add(new ChangeCommand(chosenRelation.get(), r));
578 UndoRedoHandler.getInstance().add(new SequenceCommand(tr("Change relation member roles to {0}", role), commands));
579 }
580 }
581 }
582
583 private static class RelationTableModel extends DefaultTableModel {
584 @Override
585 public boolean isCellEditable(int row, int column) {
586 return false;
587 }
588
589 @Override
590 public Class<?> getColumnClass(int columnIndex) {
591 return columnIndex == 0 ? Relation.class : String.class;
592 }
593 }
594
595 private static class MultipolygonSettingsPopup extends JPopupMenu implements ActionListener {
596 MultipolygonSettingsPopup() {
597 addMenuItem("boundary", tr("Create administrative boundary relations"));
598 addMenuItem("boundaryways", tr("Add tags boundary and admin_level to boundary relation ways"));
599 addMenuItem("tags", tr("Move area tags from contour to relation"));
600 addMenuItem("alltags", tr("When moving tags, consider even non-repeating ones"));
601 addMenuItem("allowsplit", tr("Always split ways of neighbouring multipolygons"));
602 }
603
604 protected final JCheckBoxMenuItem addMenuItem(String property, String title) {
605 String fullProperty = PREF_PREFIX + ".multipolygon." + property;
606 JCheckBoxMenuItem item = new JCheckBoxMenuItem(tr(title));
607 item.setSelected(Config.getPref().getBoolean(fullProperty, CreateMultipolygonAction.getDefaultPropertyValue(property)));
608 item.setActionCommand(fullProperty);
609 item.addActionListener(this);
610 add(item);
611 return item;
612 }
613
614 @Override
615 public void actionPerformed(ActionEvent e) {
616 String property = e.getActionCommand();
617 if (property != null && property.length() > 0 && e.getSource() instanceof JCheckBoxMenuItem) {
618 boolean value = ((JCheckBoxMenuItem) e.getSource()).isSelected();
619 Config.getPref().putBoolean(property, value);
620 show(getInvoker(), getX(), getY());
621 }
622 }
623 }
624
625 private class EnterRoleAction extends JosmAction implements ChosenRelationListener {
626
627 EnterRoleAction() {
628 super(tr("Change role"), (String) null, tr("Enter role for selected members"),
629 Shortcut.registerShortcut("reltoolbox:changerole", tr("Relation Toolbox: {0}", tr("Enter role for selected members")),
630 KeyEvent.VK_R, Shortcut.ALT_CTRL), false, "relcontext/enterrole", true);
631 chosenRelation.addChosenRelationListener(this);
632 updateEnabledState();
633 }
634
635 @Override
636 public void actionPerformed(ActionEvent e) {
637 if (roleBoxModel.membersRole != null) {
638 String role = askForRoleName();
639 if (role != null) {
640 applyRoleToSelection(role);
641 }
642 }
643 }
644
645 @Override
646 public void chosenRelationChanged(Relation oldRelation, Relation newRelation) {
647 setEnabled(newRelation != null);
648 }
649 }
650
651 private class RoleComboBoxModel extends AbstractListModel<String> implements ComboBoxModel<String> {
652 private List<String> roles = new ArrayList<>();
653 private int selectedIndex = -1;
654 private JComboBox<String> combobox;
655 private String membersRole;
656 private final String EMPTY_ROLE = tr("<empty>");
657 private final String ANOTHER_ROLE = tr("another...");
658
659 RoleComboBoxModel(JComboBox<String> combobox) {
660 super();
661 this.combobox = combobox;
662 update();
663 }
664
665 public void update() {
666 membersRole = getSelectedMembersRoleIntl();
667 if (membersRole == null) {
668 if (combobox.isEnabled()) {
669 combobox.setEnabled(false);
670 }
671 return;
672 }
673 if (!combobox.isEnabled()) {
674 combobox.setEnabled(true);
675 }
676
677 List<String> items = new ArrayList<>();
678 if (chosenRelation != null && chosenRelation.get() != null) {
679 if (chosenRelation.isMultipolygon()) {
680 items.add("outer");
681 items.add("inner");
682 }
683 if (chosenRelation.get().get("type") != null) {
684 List<String> values = possibleRoles.get(chosenRelation.get().get("type"));
685 if (values != null) {
686 items.addAll(values);
687 }
688 }
689 for (RelationMember m : chosenRelation.get().getMembers()) {
690 if (m.getRole().length() > 0 && !items.contains(m.getRole())) {
691 items.add(m.getRole());
692 }
693 }
694 }
695 items.add(EMPTY_ROLE);
696 if (!items.contains(membersRole)) {
697 items.add(0, membersRole);
698 }
699 items.add(ANOTHER_ROLE);
700 roles = Collections.unmodifiableList(items);
701
702 if (membersRole != null) {
703 setSelectedItem(membersRole);
704 } else {
705 fireContentsChanged(this, -1, -1);
706 }
707 combobox.repaint();
708 }
709
710 public String getSelectedMembersRole() {
711 return membersRole == EMPTY_ROLE ? "" : membersRole;
712 }
713
714 public boolean isAnotherRoleSelected() {
715 return getSelectedRole() != null && getSelectedRole().equals(ANOTHER_ROLE);
716 }
717
718 private String getSelectedMembersRoleIntl() {
719 String role = null;
720 if (chosenRelation != null && chosenRelation.get() != null
721 && MainApplication.getLayerManager().getEditDataSet() != null
722 && !MainApplication.getLayerManager().getEditDataSet().selectionEmpty()) {
723 Collection<OsmPrimitive> selected = MainApplication.getLayerManager().getEditDataSet().getSelected();
724 for (RelationMember m : chosenRelation.get().getMembers()) {
725 if (selected.contains(m.getMember())) {
726 if (role == null) {
727 role = m.getRole();
728 } else if (m.getRole() != null && !role.equals(m.getRole())) {
729 role = tr("<different>");
730 break;
731 }
732 }
733 }
734 }
735 return role == null ? null : role.length() == 0 ? EMPTY_ROLE : role;
736 }
737
738 public List<String> getRoles() {
739 return roles;
740 }
741
742 @Override
743 public int getSize() {
744 return roles.size();
745 }
746
747 @Override
748 public String getElementAt(int index) {
749 return getRole(index);
750 }
751
752 public String getRole(int index) {
753 return roles.get(index);
754 }
755
756 @Override
757 public void setSelectedItem(Object anItem) {
758 int newIndex = anItem == null ? -1 : roles.indexOf(anItem);
759 if (newIndex != selectedIndex) {
760 selectedIndex = newIndex;
761 fireContentsChanged(this, -1, -1);
762 }
763 }
764
765 @Override
766 public Object getSelectedItem() {
767 return selectedIndex < 0 || selectedIndex >= getSize() ? null : getRole(selectedIndex);
768 }
769
770 public String getSelectedRole() {
771 String role = selectedIndex < 0 || selectedIndex >= getSize() ? null : getRole(selectedIndex);
772 return role != null && role.equals(EMPTY_ROLE) ? "" : role;
773 }
774 }
775}
Note: See TracBrowser for help on using the repository browser.