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

Last change on this file since 4932 was 4879, checked in by stoecker, 12 years ago

fix #7339 - wrong relation member position in delete confirmation

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