source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/DeleteFromRelationConfirmationDialog.java@ 19050

Last change on this file since 19050 was 19050, checked in by taylor.smock, 14 months ago

Revert most var changes from r19048, fix most new compile warnings and checkstyle issues

Also, document why various ErrorProne checks were originally disabled and fix
generic SonarLint issues.

  • Property svn:eol-style set to native
File size: 18.1 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs;
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.GridBagLayout;
12import java.awt.event.ActionEvent;
13import java.awt.event.WindowAdapter;
14import java.awt.event.WindowEvent;
15import java.io.Serializable;
16import java.util.ArrayList;
17import java.util.Collection;
18import java.util.Comparator;
19import java.util.List;
20import java.util.Set;
21import java.util.stream.Collectors;
22
23import javax.swing.AbstractAction;
24import javax.swing.JButton;
25import javax.swing.JDialog;
26import javax.swing.JPanel;
27import javax.swing.JScrollPane;
28import javax.swing.JTable;
29import javax.swing.event.TableModelEvent;
30import javax.swing.event.TableModelListener;
31import javax.swing.table.DefaultTableColumnModel;
32import javax.swing.table.DefaultTableModel;
33import javax.swing.table.TableColumn;
34
35import org.openstreetmap.josm.data.osm.DefaultNameFormatter;
36import org.openstreetmap.josm.data.osm.NameFormatter;
37import org.openstreetmap.josm.data.osm.OsmPrimitive;
38import org.openstreetmap.josm.data.osm.Relation;
39import org.openstreetmap.josm.data.osm.RelationToChildReference;
40import org.openstreetmap.josm.gui.MainApplication;
41import org.openstreetmap.josm.gui.PrimitiveRenderer;
42import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction;
43import org.openstreetmap.josm.gui.help.HelpUtil;
44import org.openstreetmap.josm.gui.util.GuiHelper;
45import org.openstreetmap.josm.gui.util.WindowGeometry;
46import org.openstreetmap.josm.gui.widgets.HtmlPanel;
47import org.openstreetmap.josm.tools.GBC;
48import org.openstreetmap.josm.tools.I18n;
49import org.openstreetmap.josm.tools.ImageProvider;
50import org.openstreetmap.josm.tools.Pair;
51import org.openstreetmap.josm.tools.Utils;
52
53/**
54 * This dialog is used to get a user confirmation that a collection of primitives can be removed
55 * from their parent relations.
56 * @since 2308
57 */
58public class DeleteFromRelationConfirmationDialog extends JDialog implements TableModelListener {
59 /** the unique instance of this dialog */
60 private static DeleteFromRelationConfirmationDialog instance;
61
62 /**
63 * Replies the unique instance of this dialog
64 *
65 * @return The unique instance of this dialog
66 */
67 public static synchronized DeleteFromRelationConfirmationDialog getInstance() {
68 if (instance == null) {
69 instance = new DeleteFromRelationConfirmationDialog();
70 }
71 return instance;
72 }
73
74 /** the data model */
75 private RelationMemberTableModel model;
76 /** The data model for deleting relations */
77 private RelationDeleteModel deletedRelationsModel;
78 /** The table to hide/show if the relations to delete are not empty*/
79 private final HtmlPanel htmlPanel = new HtmlPanel();
80 private boolean canceled;
81 private final JButton btnOK = new JButton(new OKAction());
82
83 protected JPanel buildRelationMemberTablePanel() {
84 JTable table = new JTable(model, new RelationMemberTableColumnModel());
85 JPanel pnl = new JPanel(new GridBagLayout());
86 pnl.add(new JScrollPane(table), GBC.eol().fill());
87 JTable deletedRelationsTable = new JTable(this.deletedRelationsModel, new RelationDeleteTableColumnModel());
88 JScrollPane deletedRelationsModelTableScrollPane = new JScrollPane(deletedRelationsTable);
89 this.deletedRelationsModel.addTableModelListener(
90 e -> deletedRelationsModelTableScrollPane.setVisible(this.deletedRelationsModel.getRowCount() > 0));
91 // Default to not visible
92 deletedRelationsModelTableScrollPane.setVisible(false);
93 pnl.add(deletedRelationsModelTableScrollPane, GBC.eol().fill());
94 return pnl;
95 }
96
97 protected JPanel buildButtonPanel() {
98 JPanel pnl = new JPanel(new FlowLayout());
99 pnl.add(btnOK);
100 btnOK.setFocusable(true);
101 pnl.add(new JButton(new CancelAction()));
102 pnl.add(new JButton(new ContextSensitiveHelpAction(ht("/Action/Delete#DeleteFromRelations"))));
103 return pnl;
104 }
105
106 protected final void build() {
107 model = new RelationMemberTableModel();
108 model.addTableModelListener(this);
109 this.deletedRelationsModel = new RelationDeleteModel();
110 this.deletedRelationsModel.addTableModelListener(this);
111 getContentPane().setLayout(new BorderLayout());
112 getContentPane().add(htmlPanel, BorderLayout.NORTH);
113 getContentPane().add(buildRelationMemberTablePanel(), BorderLayout.CENTER);
114 getContentPane().add(buildButtonPanel(), BorderLayout.SOUTH);
115
116 HelpUtil.setHelpContext(this.getRootPane(), ht("/Action/Delete#DeleteFromRelations"));
117
118 addWindowListener(new WindowEventHandler());
119 }
120
121 protected void updateMessage() {
122 int numObjectsToDelete = this.model.getNumObjectsToDelete() + this.deletedRelationsModel.getNumObjectsToDelete();
123 int numParentRelations = this.model.getNumParentRelations() + this.deletedRelationsModel.getNumParentRelations();
124 final String msg1 = trn(
125 "Please confirm to remove <strong>{0} object</strong>.",
126 "Please confirm to remove <strong>{0} objects</strong>.",
127 numObjectsToDelete, numObjectsToDelete);
128 final String msg2 = trn(
129 "{0} relation is affected.",
130 "{0} relations are affected.",
131 numParentRelations, numParentRelations);
132 @I18n.QuirkyPluralString
133 final String msg = "<html>" + msg1 + ' ' + msg2 + "</html>";
134 htmlPanel.getEditorPane().setText(msg);
135 invalidate();
136 }
137
138 protected void updateTitle() {
139 int numObjectsToDelete = this.model.getNumObjectsToDelete() + this.deletedRelationsModel.getNumObjectsToDelete();
140 if (numObjectsToDelete > 0) {
141 setTitle(trn("Deleting {0} object", "Deleting {0} objects", numObjectsToDelete, numObjectsToDelete));
142 } else {
143 setTitle(tr("Delete objects"));
144 }
145 }
146
147 /**
148 * Constructs a new {@code DeleteFromRelationConfirmationDialog}.
149 */
150 public DeleteFromRelationConfirmationDialog() {
151 super(GuiHelper.getFrameForComponent(MainApplication.getMainFrame()), "", ModalityType.DOCUMENT_MODAL);
152 build();
153 }
154
155 /**
156 * Replies the data model used in this dialog
157 *
158 * @return the data model
159 */
160 public RelationMemberTableModel getModel() {
161 return model;
162 }
163
164 /**
165 * Replies the data model used for relations that should probably be deleted.
166 * @return the data model
167 * @since 18395
168 */
169 public RelationDeleteModel getDeletedRelationsModel() {
170 return this.deletedRelationsModel;
171 }
172
173 /**
174 * Replies true if the dialog was canceled
175 *
176 * @return true if the dialog was canceled
177 */
178 public boolean isCanceled() {
179 return canceled;
180 }
181
182 protected void setCanceled(boolean canceled) {
183 this.canceled = canceled;
184 }
185
186 @Override
187 public void setVisible(boolean visible) {
188 if (visible) {
189 new WindowGeometry(
190 getClass().getName() + ".geometry",
191 WindowGeometry.centerInWindow(
192 MainApplication.getMainFrame(),
193 new Dimension(400, 200)
194 )
195 ).applySafe(this);
196 setCanceled(false);
197 } else {
198 if (isShowing()) { // Avoid IllegalComponentStateException like in #8775
199 new WindowGeometry(this).remember(getClass().getName() + ".geometry");
200 }
201 model.data.clear();
202 this.deletedRelationsModel.data.clear();
203 }
204 super.setVisible(visible);
205 }
206
207 @Override
208 public void tableChanged(TableModelEvent e) {
209 updateMessage();
210 updateTitle();
211 }
212
213 /**
214 * The table model which manages the list of relation-to-child references
215 */
216 public static class RelationMemberTableModel extends DefaultTableModel {
217 private static final class RelationToChildReferenceComparator implements Comparator<RelationToChildReference>, Serializable {
218 private static final long serialVersionUID = 1L;
219 @Override
220 public int compare(RelationToChildReference o1, RelationToChildReference o2) {
221 NameFormatter nf = DefaultNameFormatter.getInstance();
222 int cmp = o1.getChild().getDisplayName(nf).compareTo(o2.getChild().getDisplayName(nf));
223 if (cmp != 0) return cmp;
224 cmp = o1.getParent().getDisplayName(nf).compareTo(o2.getParent().getDisplayName(nf));
225 if (cmp != 0) return cmp;
226 return Integer.compare(o1.getPosition(), o2.getPosition());
227 }
228 }
229
230 private final transient List<RelationToChildReference> data;
231
232 /**
233 * Constructs a new {@code RelationMemberTableModel}.
234 */
235 public RelationMemberTableModel() {
236 data = new ArrayList<>();
237 }
238
239 @Override
240 public int getRowCount() {
241 if (data == null) return 0;
242 return data.size();
243 }
244
245 /**
246 * Sets the data that should be displayed in the list.
247 * @param references A list of references to display
248 */
249 public void populate(Collection<RelationToChildReference> references) {
250 data.clear();
251 if (references != null) {
252 data.addAll(references);
253 }
254 data.sort(new RelationToChildReferenceComparator());
255 fireTableDataChanged();
256 }
257
258 /**
259 * Gets the list of children that are currently displayed.
260 * @return The children.
261 */
262 public Set<OsmPrimitive> getObjectsToDelete() {
263 return data.stream().map(RelationToChildReference::getChild).collect(Collectors.toSet());
264 }
265
266 /**
267 * Gets the number of elements {@link #getObjectsToDelete()} would return.
268 * @return That number.
269 */
270 public int getNumObjectsToDelete() {
271 return getObjectsToDelete().size();
272 }
273
274 /**
275 * Gets the set of parent relations
276 * @return All parent relations of the references
277 */
278 public Set<OsmPrimitive> getParentRelations() {
279 return data.stream().map(RelationToChildReference::getParent).collect(Collectors.toSet());
280 }
281
282 /**
283 * Gets the number of elements {@link #getParentRelations()} would return.
284 * @return That number.
285 */
286 public int getNumParentRelations() {
287 return getParentRelations().size();
288 }
289
290 @Override
291 public Object getValueAt(int rowIndex, int columnIndex) {
292 if (data == null) return null;
293 RelationToChildReference ref = data.get(rowIndex);
294 switch (columnIndex) {
295 case 0: return ref.getChild();
296 case 1: return ref.getParent();
297 case 2: return ref.getPosition()+1;
298 case 3: return ref.getRole();
299 default:
300 assert false : "Illegal column index";
301 }
302 return null;
303 }
304
305 @Override
306 public boolean isCellEditable(int row, int column) {
307 return false;
308 }
309 }
310
311 private static class RelationMemberTableColumnModel extends DefaultTableColumnModel {
312
313 protected final void createColumns() {
314
315 // column 0 - To Delete
316 TableColumn col = new TableColumn(0);
317 col.setHeaderValue(tr("To delete"));
318 col.setResizable(true);
319 col.setWidth(100);
320 col.setPreferredWidth(100);
321 col.setCellRenderer(new PrimitiveRenderer());
322 addColumn(col);
323
324 // column 0 - From Relation
325 col = new TableColumn(1);
326 col.setHeaderValue(tr("From Relation"));
327 col.setResizable(true);
328 col.setWidth(100);
329 col.setPreferredWidth(100);
330 col.setCellRenderer(new PrimitiveRenderer());
331 addColumn(col);
332
333 // column 1 - Pos.
334 col = new TableColumn(2);
335 col.setHeaderValue(tr("Pos."));
336 col.setResizable(true);
337 col.setWidth(30);
338 col.setPreferredWidth(30);
339 addColumn(col);
340
341 // column 2 - Role
342 col = new TableColumn(3);
343 col.setHeaderValue(tr("Role"));
344 col.setResizable(true);
345 col.setWidth(50);
346 col.setPreferredWidth(50);
347 addColumn(col);
348 }
349
350 RelationMemberTableColumnModel() {
351 createColumns();
352 }
353 }
354
355 /**
356 * The table model which manages relations that will be deleted, if their children are deleted.
357 * @since 18395
358 */
359 public static class RelationDeleteModel extends DefaultTableModel {
360 private final transient List<Pair<Relation, Boolean>> data = new ArrayList<>();
361
362 @Override
363 public int getRowCount() {
364 // This is called in the super constructor. Before we have instantiated the list. Removing the null check
365 // WILL LEAD TO A SILENT NPE!
366 if (this.data == null) {
367 return 0;
368 }
369 return this.data.size();
370 }
371
372 /**
373 * Sets the data that should be displayed in the list.
374 * @param references A list of references to display
375 */
376 public void populate(Collection<Pair<Relation, Boolean>> references) {
377 this.data.clear();
378 if (references != null) {
379 this.data.addAll(references);
380 }
381 this.data.sort(Comparator.comparing(pair -> pair.a));
382 fireTableDataChanged();
383 }
384
385 /**
386 * Gets the list of children that are currently displayed.
387 * @return The children.
388 */
389 public Set<Relation> getObjectsToDelete() {
390 return this.data.stream().filter(relation -> relation.b).map(relation -> relation.a).collect(Collectors.toSet());
391 }
392
393 /**
394 * Gets the number of elements {@link #getObjectsToDelete()} would return.
395 * @return That number.
396 */
397 public int getNumObjectsToDelete() {
398 return getObjectsToDelete().size();
399 }
400
401 /**
402 * Gets the set of parent relations
403 * @return All parent relations of the references
404 */
405 public Set<OsmPrimitive> getParentRelations() {
406 return this.data.stream()
407 .flatMap(pair -> Utils.filteredCollection(pair.a.getReferrers(), Relation.class).stream())
408 .collect(Collectors.toSet());
409 }
410
411 /**
412 * Gets the number of elements {@link #getParentRelations()} would return.
413 * @return That number.
414 */
415 public int getNumParentRelations() {
416 return getParentRelations().size();
417 }
418
419 @Override
420 public Object getValueAt(int rowIndex, int columnIndex) {
421 if (this.data.isEmpty()) {
422 return null;
423 }
424 Pair<Relation, Boolean> ref = this.data.get(rowIndex);
425 switch (columnIndex) {
426 case 0: return ref.a;
427 case 1: return ref.b;
428 default:
429 assert false : "Illegal column index";
430 }
431 return null;
432 }
433
434 @Override
435 public boolean isCellEditable(int row, int column) {
436 return !this.data.isEmpty() && column == 1;
437 }
438
439 @Override
440 public void setValueAt(Object aValue, int row, int column) {
441 if (this.data.size() > row && column == 1 && aValue instanceof Boolean) {
442 this.data.get(row).b = ((Boolean) aValue);
443 }
444 }
445
446 @Override
447 public Class<?> getColumnClass(int columnIndex) {
448 switch (columnIndex) {
449 case 0:
450 return Relation.class;
451 case 1:
452 return Boolean.class;
453 default:
454 return super.getColumnClass(columnIndex);
455 }
456 }
457 }
458
459 private static class RelationDeleteTableColumnModel extends DefaultTableColumnModel {
460 protected final void createColumns() {
461 // column 0 - To Delete
462 TableColumn col = new TableColumn(0);
463 col.setHeaderValue(tr("Relation"));
464 col.setResizable(true);
465 col.setWidth(100);
466 col.setPreferredWidth(100);
467 col.setCellRenderer(new PrimitiveRenderer());
468 addColumn(col);
469
470 // column 0 - From Relation
471 col = new TableColumn(1);
472 final String toDelete = tr("To delete");
473 col.setHeaderValue(toDelete);
474 col.setResizable(true);
475 col.setPreferredWidth(toDelete.length());
476 addColumn(col);
477 }
478
479 RelationDeleteTableColumnModel() {
480 createColumns();
481 }
482 }
483
484 class OKAction extends AbstractAction {
485 OKAction() {
486 putValue(NAME, tr("OK"));
487 new ImageProvider("ok").getResource().attachImageIcon(this);
488 putValue(SHORT_DESCRIPTION, tr("Click to close the dialog and remove the object from the relations"));
489 }
490
491 @Override
492 public void actionPerformed(ActionEvent e) {
493 setCanceled(false);
494 setVisible(false);
495 }
496 }
497
498 class CancelAction extends AbstractAction {
499 CancelAction() {
500 putValue(NAME, tr("Cancel"));
501 new ImageProvider("cancel").getResource().attachImageIcon(this);
502 putValue(SHORT_DESCRIPTION, tr("Click to close the dialog and to abort deleting the objects"));
503 }
504
505 @Override
506 public void actionPerformed(ActionEvent e) {
507 setCanceled(true);
508 setVisible(false);
509 }
510 }
511
512 class WindowEventHandler extends WindowAdapter {
513
514 @Override
515 public void windowClosing(WindowEvent e) {
516 setCanceled(true);
517 }
518
519 @Override
520 public void windowOpened(WindowEvent e) {
521 btnOK.requestFocusInWindow();
522 }
523 }
524}
Note: See TracBrowser for help on using the repository browser.