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

Last change on this file since 36431 was 36217, checked in by GerdP, 14 months ago

fix #23521: fix some memory leaks

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