source: josm/trunk/src/org/openstreetmap/josm/gui/actionsupport/DeleteFromRelationConfirmationDialog.java@ 5983

Last change on this file since 5983 was 5983, checked in by Don-vip, 11 years ago

fix #8213 - Disable editing of cells in DeleteFromRelationConfirmationDialog (fix ArrayIndexOutOfBoundsException when trying to)

  • Property svn:eol-style set to native
File size: 12.0 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.actionsupport;
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.event.ActionEvent;
12import java.awt.event.WindowAdapter;
13import java.awt.event.WindowEvent;
14import java.util.ArrayList;
15import java.util.Collection;
16import java.util.Collections;
17import java.util.Comparator;
18import java.util.HashSet;
19import java.util.Set;
20
21import javax.swing.AbstractAction;
22import javax.swing.JDialog;
23import javax.swing.JOptionPane;
24import javax.swing.JPanel;
25import javax.swing.JScrollPane;
26import javax.swing.JTable;
27import javax.swing.event.TableModelEvent;
28import javax.swing.event.TableModelListener;
29import javax.swing.table.DefaultTableColumnModel;
30import javax.swing.table.DefaultTableModel;
31import javax.swing.table.TableColumn;
32
33import org.openstreetmap.josm.Main;
34import org.openstreetmap.josm.data.osm.NameFormatter;
35import org.openstreetmap.josm.data.osm.OsmPrimitive;
36import org.openstreetmap.josm.data.osm.RelationToChildReference;
37import org.openstreetmap.josm.gui.DefaultNameFormatter;
38import org.openstreetmap.josm.gui.OsmPrimitivRenderer;
39import org.openstreetmap.josm.gui.SideButton;
40import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction;
41import org.openstreetmap.josm.gui.help.HelpUtil;
42import org.openstreetmap.josm.gui.widgets.HtmlPanel;
43import org.openstreetmap.josm.tools.ImageProvider;
44import org.openstreetmap.josm.tools.WindowGeometry;
45
46/**
47 * This dialog is used to get a user confirmation that a collection of primitives can be removed
48 * from their parent relations.
49 * @since 2308
50 */
51public class DeleteFromRelationConfirmationDialog extends JDialog implements TableModelListener {
52 /** the unique instance of this dialog */
53 static private DeleteFromRelationConfirmationDialog instance;
54
55 /**
56 * Replies the unique instance of this dialog
57 *
58 * @return The unique instance of this dialog
59 */
60 static public DeleteFromRelationConfirmationDialog getInstance() {
61 if (instance == null) {
62 instance = new DeleteFromRelationConfirmationDialog();
63 }
64 return instance;
65 }
66
67 /** the data model */
68 private RelationMemberTableModel model;
69 private HtmlPanel htmlPanel;
70 private boolean canceled;
71 private SideButton btnOK;
72
73 protected JPanel buildRelationMemberTablePanel() {
74 JTable table = new JTable(model, new RelationMemberTableColumnModel());
75 JPanel pnl = new JPanel();
76 pnl.setLayout(new BorderLayout());
77 pnl.add(new JScrollPane(table));
78 return pnl;
79 }
80
81 protected JPanel buildButtonPanel() {
82 JPanel pnl = new JPanel();
83 pnl.setLayout(new FlowLayout());
84 pnl.add(btnOK = new SideButton(new OKAction()));
85 btnOK.setFocusable(true);
86 pnl.add(new SideButton(new CancelAction()));
87 pnl.add(new SideButton(new ContextSensitiveHelpAction(ht("/Action/Delete#DeleteFromRelations"))));
88 return pnl;
89 }
90
91 protected void build() {
92 model = new RelationMemberTableModel();
93 model.addTableModelListener(this);
94 getContentPane().setLayout(new BorderLayout());
95 getContentPane().add(htmlPanel = new HtmlPanel(), BorderLayout.NORTH);
96 getContentPane().add(buildRelationMemberTablePanel(), BorderLayout.CENTER);
97 getContentPane().add(buildButtonPanel(), BorderLayout.SOUTH);
98
99 HelpUtil.setHelpContext(this.getRootPane(), ht("/Action/Delete#DeleteFromRelations"));
100
101 addWindowListener(new WindowEventHandler());
102 }
103
104 protected void updateMessage() {
105 int numObjectsToDelete = model.getNumObjectsToDelete();
106 int numParentRelations = model.getNumParentRelations();
107 String msg;
108 if (numObjectsToDelete == 1 && numParentRelations == 1) {
109 msg = tr("<html>Please confirm to remove <strong>1 object</strong> from <strong>1 relation</strong>.</html>");
110 } else if (numObjectsToDelete == 1 && numParentRelations > 1) {
111 msg = tr("<html>Please confirm to remove <strong>1 object</strong> from <strong>{0} relations</strong>.</html>", numParentRelations);
112 } else if (numObjectsToDelete > 1 && numParentRelations == 1) {
113 msg = tr("<html>Please confirm to remove <strong>1 object</strong> from <strong>{0} relations</strong>.</html>", numParentRelations);
114 } else {
115 msg = tr("<html>Please confirm to remove <strong>{0} objects</strong> from <strong>{1} relations</strong>.</html>", numObjectsToDelete,numParentRelations);
116 }
117 htmlPanel.getEditorPane().setText(msg);
118 invalidate();
119 }
120
121 protected void updateTitle() {
122 int numObjectsToDelete = model.getNumObjectsToDelete();
123 if (numObjectsToDelete > 0) {
124 setTitle(trn("Deleting {0} object", "Deleting {0} objects", numObjectsToDelete, numObjectsToDelete));
125 } else {
126 setTitle(tr("Delete objects"));
127 }
128 }
129
130 /**
131 * Constructs a new {@code DeleteFromRelationConfirmationDialog}.
132 */
133 public DeleteFromRelationConfirmationDialog() {
134 super(JOptionPane.getFrameForComponent(Main.parent), "", ModalityType.DOCUMENT_MODAL);
135 build();
136 }
137
138 /**
139 * Replies the data model used in this dialog
140 *
141 * @return the data model
142 */
143 public RelationMemberTableModel getModel() {
144 return model;
145 }
146
147 /**
148 * Replies true if the dialog was canceled
149 *
150 * @return true if the dialog was canceled
151 */
152 public boolean isCanceled() {
153 return canceled;
154 }
155
156 protected void setCanceled(boolean canceled) {
157 this.canceled = canceled;
158 }
159
160 @Override
161 public void setVisible(boolean visible) {
162 if (visible) {
163 new WindowGeometry(
164 getClass().getName() + ".geometry",
165 WindowGeometry.centerInWindow(
166 Main.parent,
167 new Dimension(400,200)
168 )
169 ).applySafe(this);
170 setCanceled(false);
171 } else if(!visible && isShowing()) {
172 new WindowGeometry(this).remember(getClass().getName() + ".geometry");
173 }
174 super.setVisible(visible);
175 }
176
177 public void tableChanged(TableModelEvent e) {
178 updateMessage();
179 updateTitle();
180 }
181
182 /**
183 * The table model which manages the list of relation-to-child references
184 *
185 */
186 public static class RelationMemberTableModel extends DefaultTableModel {
187 private ArrayList<RelationToChildReference> data;
188
189 /**
190 * Constructs a new {@code RelationMemberTableModel}.
191 */
192 public RelationMemberTableModel() {
193 data = new ArrayList<RelationToChildReference>();
194 }
195
196 @Override
197 public int getRowCount() {
198 if (data == null) return 0;
199 return data.size();
200 }
201
202 protected void sort() {
203 Collections.sort(
204 data,
205 new Comparator<RelationToChildReference>() {
206 private NameFormatter nf = DefaultNameFormatter.getInstance();
207 public int compare(RelationToChildReference o1, RelationToChildReference o2) {
208 int cmp = o1.getChild().getDisplayName(nf).compareTo(o2.getChild().getDisplayName(nf));
209 if (cmp != 0) return cmp;
210 cmp = o1.getParent().getDisplayName(nf).compareTo(o2.getParent().getDisplayName(nf));
211 if (cmp != 0) return cmp;
212 return Integer.valueOf(o1.getPosition()).compareTo(o2.getPosition());
213 }
214 }
215 );
216 }
217
218 public void populate(Collection<RelationToChildReference> references) {
219 data.clear();
220 if (references != null) {
221 data.addAll(references);
222 }
223 sort();
224 fireTableDataChanged();
225 }
226
227 public Set<OsmPrimitive> getObjectsToDelete() {
228 HashSet<OsmPrimitive> ret = new HashSet<OsmPrimitive>();
229 for (RelationToChildReference ref: data) {
230 ret.add(ref.getChild());
231 }
232 return ret;
233 }
234
235 public int getNumObjectsToDelete() {
236 return getObjectsToDelete().size();
237 }
238
239 public Set<OsmPrimitive> getParentRelations() {
240 HashSet<OsmPrimitive> ret = new HashSet<OsmPrimitive>();
241 for (RelationToChildReference ref: data) {
242 ret.add(ref.getParent());
243 }
244 return ret;
245 }
246
247 public int getNumParentRelations() {
248 return getParentRelations().size();
249 }
250
251 @Override
252 public Object getValueAt(int rowIndex, int columnIndex) {
253 if (data == null) return null;
254 RelationToChildReference ref = data.get(rowIndex);
255 switch(columnIndex) {
256 case 0: return ref.getChild();
257 case 1: return ref.getParent();
258 case 2: return ref.getPosition()+1;
259 case 3: return ref.getRole();
260 default:
261 assert false: "Illegal column index";
262 }
263 return null;
264 }
265
266 @Override
267 public boolean isCellEditable(int row, int column) {
268 return false;
269 }
270 }
271
272 private static class RelationMemberTableColumnModel extends DefaultTableColumnModel{
273
274 protected void createColumns() {
275 TableColumn col = null;
276
277 // column 0 - To Delete
278 col = new TableColumn(0);
279 col.setHeaderValue(tr("To delete"));
280 col.setResizable(true);
281 col.setWidth(100);
282 col.setPreferredWidth(100);
283 col.setCellRenderer(new OsmPrimitivRenderer());
284 addColumn(col);
285
286 // column 0 - From Relation
287 col = new TableColumn(1);
288 col.setHeaderValue(tr("From Relation"));
289 col.setResizable(true);
290 col.setWidth(100);
291 col.setPreferredWidth(100);
292 col.setCellRenderer(new OsmPrimitivRenderer());
293 addColumn(col);
294
295 // column 1 - Pos.
296 col = new TableColumn(2);
297 col.setHeaderValue(tr("Pos."));
298 col.setResizable(true);
299 col.setWidth(30);
300 col.setPreferredWidth(30);
301 addColumn(col);
302
303 // column 2 - Role
304 col = new TableColumn(3);
305 col.setHeaderValue(tr("Role"));
306 col.setResizable(true);
307 col.setWidth(50);
308 col.setPreferredWidth(50);
309 addColumn(col);
310 }
311
312 public RelationMemberTableColumnModel() {
313 createColumns();
314 }
315 }
316
317 class OKAction extends AbstractAction {
318 public OKAction() {
319 putValue(NAME, tr("OK"));
320 putValue(SMALL_ICON, ImageProvider.get("ok"));
321 putValue(SHORT_DESCRIPTION, tr("Click to close the dialog and remove the object from the relations"));
322 }
323
324 public void actionPerformed(ActionEvent e) {
325 setCanceled(false);
326 setVisible(false);
327 }
328 }
329
330 class CancelAction extends AbstractAction {
331 public CancelAction() {
332 putValue(NAME, tr("Cancel"));
333 putValue(SMALL_ICON, ImageProvider.get("cancel"));
334 putValue(SHORT_DESCRIPTION, tr("Click to close the dialog and to abort deleting the objects"));
335 }
336
337 public void actionPerformed(ActionEvent e) {
338 setCanceled(true);
339 setVisible(false);
340 }
341 }
342
343 class WindowEventHandler extends WindowAdapter {
344
345 @Override
346 public void windowClosing(WindowEvent e) {
347 setCanceled(true);
348 }
349
350 @Override
351 public void windowOpened(WindowEvent e) {
352 btnOK.requestFocusInWindow();
353 }
354 }
355}
Note: See TracBrowser for help on using the repository browser.