source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/ConflictDialog.java@ 3034

Last change on this file since 3034 was 3034, checked in by jttt, 14 years ago

Fix #4467 Don't silently drop locally deleted member primitives from downloaded ways and relation (fix the issue when deleted primitive is referenced)

  • Property svn:eol-style set to native
File size: 12.2 KB
Line 
1// License: GPL. See LICENSE file for details.
2package org.openstreetmap.josm.gui.dialogs;
3
4import static org.openstreetmap.josm.tools.I18n.marktr;
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.awt.BorderLayout;
8import java.awt.Color;
9import java.awt.Graphics;
10import java.awt.Point;
11import java.awt.event.ActionEvent;
12import java.awt.event.KeyEvent;
13import java.awt.event.MouseAdapter;
14import java.awt.event.MouseEvent;
15import java.util.Collection;
16import java.util.Iterator;
17import java.util.LinkedList;
18import java.util.concurrent.CopyOnWriteArrayList;
19
20import javax.swing.AbstractAction;
21import javax.swing.JList;
22import javax.swing.JPanel;
23import javax.swing.JScrollPane;
24import javax.swing.ListModel;
25import javax.swing.ListSelectionModel;
26import javax.swing.event.ListDataEvent;
27import javax.swing.event.ListDataListener;
28import javax.swing.event.ListSelectionEvent;
29import javax.swing.event.ListSelectionListener;
30
31import org.openstreetmap.josm.Main;
32import org.openstreetmap.josm.data.SelectionChangedListener;
33import org.openstreetmap.josm.data.conflict.Conflict;
34import org.openstreetmap.josm.data.conflict.ConflictCollection;
35import org.openstreetmap.josm.data.conflict.IConflictListener;
36import org.openstreetmap.josm.data.osm.DataSet;
37import org.openstreetmap.josm.data.osm.Node;
38import org.openstreetmap.josm.data.osm.OsmPrimitive;
39import org.openstreetmap.josm.data.osm.Relation;
40import org.openstreetmap.josm.data.osm.RelationMember;
41import org.openstreetmap.josm.data.osm.Way;
42import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
43import org.openstreetmap.josm.data.osm.visitor.Visitor;
44import org.openstreetmap.josm.gui.MapView;
45import org.openstreetmap.josm.gui.NavigatableComponent;
46import org.openstreetmap.josm.gui.OsmPrimitivRenderer;
47import org.openstreetmap.josm.gui.SideButton;
48import org.openstreetmap.josm.gui.layer.OsmDataLayer;
49import org.openstreetmap.josm.tools.ImageProvider;
50import org.openstreetmap.josm.tools.Shortcut;
51
52/**
53 * This dialog displays the {@see ConflictCollection} of the active {@see OsmDataLayer} in a toggle
54 * dialog on the right of the main frame.
55 *
56 */
57public final class ConflictDialog extends ToggleDialog implements MapView.EditLayerChangeListener, IConflictListener, SelectionChangedListener{
58
59 static public Color getColor() {
60 return Main.pref.getColor(marktr("conflict"), Color.gray);
61 }
62
63 /** the collection of conflicts displayed by this conflict dialog*/
64 private ConflictCollection conflicts;
65
66 /** the model for the list of conflicts */
67 private ConflictListModel model;
68 /** the list widget for the list of conflicts */
69 private JList lstConflicts;
70
71 private ResolveAction actResolve;
72 private SelectAction actSelect;
73
74 /**
75 * builds the GUI
76 */
77 protected void build() {
78 model = new ConflictListModel();
79
80 lstConflicts = new JList(model);
81 lstConflicts.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
82 lstConflicts.setCellRenderer(new OsmPrimitivRenderer());
83 lstConflicts.addMouseListener(new MouseAdapter(){
84 @Override public void mouseClicked(MouseEvent e) {
85 if (e.getClickCount() >= 2) {
86 resolve();
87 }
88 }
89 });
90 lstConflicts.getSelectionModel().addListSelectionListener(new ListSelectionListener(){
91 public void valueChanged(ListSelectionEvent e) {
92 Main.map.mapView.repaint();
93 }
94 });
95
96 add(new JScrollPane(lstConflicts), BorderLayout.CENTER);
97
98 SideButton btnResolve = new SideButton(actResolve = new ResolveAction());
99 lstConflicts.getSelectionModel().addListSelectionListener(actResolve);
100
101 SideButton btnSelect = new SideButton(actSelect = new SelectAction());
102 lstConflicts.getSelectionModel().addListSelectionListener(actSelect);
103
104 JPanel buttonPanel = getButtonPanel(2);
105 buttonPanel.add(btnResolve);
106 buttonPanel.add(btnSelect);
107 add(buttonPanel, BorderLayout.SOUTH);
108 }
109
110 /**
111 * constructor
112 */
113 public ConflictDialog() {
114 super(tr("Conflict"), "conflict", tr("Resolve conflicts."),
115 Shortcut.registerShortcut("subwindow:conflict", tr("Toggle: {0}", tr("Conflict")), KeyEvent.VK_C, Shortcut.GROUP_LAYER), 100);
116
117 build();
118 refreshView();
119 }
120
121 @Override
122 public void showNotify() {
123 DataSet.selListeners.add(this);
124 MapView.addEditLayerChangeListener(this);
125 refreshView();
126 }
127
128 @Override
129 public void hideNotify() {
130 MapView.removeEditLayerChangeListener(this);
131 DataSet.selListeners.remove(this);
132 }
133
134 /**
135 * Launches a conflict resolution dialog for the first selected conflict
136 *
137 */
138 private final void resolve() {
139 if (conflicts == null || model.getSize() == 0) return;
140
141 int index = lstConflicts.getSelectedIndex();
142 if (index < 0) {
143 index = 0;
144 }
145
146 Conflict<? extends OsmPrimitive> c = conflicts.get(index);
147 ConflictResolutionDialog dialog = new ConflictResolutionDialog(Main.parent);
148 dialog.getConflictResolver().populate(c);
149 dialog.setVisible(true);
150
151 lstConflicts.setSelectedIndex(index);
152
153 Main.map.mapView.repaint();
154 }
155
156 /**
157 * refreshes the view of this dialog
158 */
159 public final void refreshView() {
160 OsmDataLayer editLayer = Main.main.getEditLayer();
161 conflicts = editLayer == null?new ConflictCollection():editLayer.getConflicts();
162 model.fireContentChanged();
163 updateTitle(conflicts.size());
164 }
165
166 private void updateTitle(int conflictsCount) {
167 if (conflictsCount > 0) {
168 setTitle(tr("Conflicts: {0} unresolved", conflicts.size()));
169 } else {
170 setTitle(tr("Conflict"));
171 }
172 }
173
174 /**
175 * Paint all conflicts that can be expressed on the main window.
176 */
177 public void paintConflicts(final Graphics g, final NavigatableComponent nc) {
178 Color preferencesColor = getColor();
179 if (preferencesColor.equals(Main.pref.getColor(marktr("background"), Color.black)))
180 return;
181 g.setColor(preferencesColor);
182 Visitor conflictPainter = new AbstractVisitor(){
183 public void visit(Node n) {
184 Point p = nc.getPoint(n);
185 g.drawRect(p.x-1, p.y-1, 2, 2);
186 }
187 public void visit(Node n1, Node n2) {
188 Point p1 = nc.getPoint(n1);
189 Point p2 = nc.getPoint(n2);
190 g.drawLine(p1.x, p1.y, p2.x, p2.y);
191 }
192 public void visit(Way w) {
193 Node lastN = null;
194 for (Node n : w.getNodes()) {
195 if (lastN == null) {
196 lastN = n;
197 continue;
198 }
199 visit(lastN, n);
200 lastN = n;
201 }
202 }
203 public void visit(Relation e) {
204 for (RelationMember em : e.getMembers()) {
205 em.getMember().visit(this);
206 }
207 }
208 };
209 for (Object o : lstConflicts.getSelectedValues()) {
210 if (conflicts == null || !conflicts.hasConflictForMy((OsmPrimitive)o)) {
211 continue;
212 }
213 conflicts.getConflictForMy((OsmPrimitive)o).getTheir().visit(conflictPainter);
214 }
215 }
216
217 public void editLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer) {
218 if (oldLayer != null) {
219 oldLayer.getConflicts().removeConflictListener(this);
220 }
221 if (newLayer != null) {
222 newLayer.getConflicts().addConflictListener(this);
223 }
224 refreshView();
225 }
226
227
228 /**
229 * replies the conflict collection currently held by this dialog; may be null
230 *
231 * @return the conflict collection currently held by this dialog; may be null
232 */
233 public ConflictCollection getConflicts() {
234 return conflicts;
235 }
236
237 public void onConflictsAdded(ConflictCollection conflicts) {
238 refreshView();
239 }
240
241 public void onConflictsRemoved(ConflictCollection conflicts) {
242 refreshView();
243 }
244
245 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
246 lstConflicts.clearSelection();
247 for (OsmPrimitive osm : newSelection) {
248 if (conflicts != null && conflicts.hasConflictForMy(osm)) {
249 int pos = model.indexOf(osm);
250 if (pos >= 0) {
251 lstConflicts.addSelectionInterval(pos, pos);
252 }
253 }
254 }
255 }
256
257 @Override
258 public String helpTopic() {
259 return "Dialogs/ConflictListDialog";
260 }
261
262 /**
263 * The {@see ListModel} for conflicts
264 *
265 */
266 class ConflictListModel implements ListModel {
267
268 private CopyOnWriteArrayList<ListDataListener> listeners;
269
270 public ConflictListModel() {
271 listeners = new CopyOnWriteArrayList<ListDataListener>();
272 }
273
274 public void addListDataListener(ListDataListener l) {
275 if (l != null) {
276 listeners.addIfAbsent(l);
277 }
278 }
279
280 public void removeListDataListener(ListDataListener l) {
281 listeners.remove(l);
282 }
283
284 protected void fireContentChanged() {
285 ListDataEvent evt = new ListDataEvent(
286 this,
287 ListDataEvent.CONTENTS_CHANGED,
288 0,
289 getSize()
290 );
291 Iterator<ListDataListener> it = listeners.iterator();
292 while(it.hasNext()) {
293 it.next().contentsChanged(evt);
294 }
295 }
296
297 public Object getElementAt(int index) {
298 if (index < 0) return null;
299 if (index >= getSize()) return null;
300 return conflicts.get(index).getMy();
301 }
302
303 public int getSize() {
304 if (conflicts == null) return 0;
305 return conflicts.size();
306 }
307
308 public int indexOf(OsmPrimitive my) {
309 if (conflicts == null) return -1;
310 for (int i=0; i < conflicts.size();i++) {
311 if (conflicts.get(i).isMatchingMy(my))
312 return i;
313 }
314 return -1;
315 }
316
317 public OsmPrimitive get(int idx) {
318 if (conflicts == null) return null;
319 return conflicts.get(idx).getMy();
320 }
321 }
322
323 class ResolveAction extends AbstractAction implements ListSelectionListener {
324 public ResolveAction() {
325 putValue(NAME, tr("Resolve"));
326 putValue(SHORT_DESCRIPTION, tr("Open a merge dialog of all selected items in the list above."));
327 putValue(SMALL_ICON, ImageProvider.get("dialogs", "conflict"));
328 putValue("help", "Dialogs/ConflictListDialog#ResolveAction");
329 }
330
331 public void actionPerformed(ActionEvent e) {
332 resolve();
333 }
334
335 public void valueChanged(ListSelectionEvent e) {
336 ListSelectionModel model = (ListSelectionModel)e.getSource();
337 boolean enabled = model.getMinSelectionIndex() >= 0
338 && model.getMaxSelectionIndex() >= model.getMinSelectionIndex();
339 setEnabled(enabled);
340 }
341 }
342
343 class SelectAction extends AbstractAction implements ListSelectionListener {
344 public SelectAction() {
345 putValue(NAME, tr("Select"));
346 putValue(SHORT_DESCRIPTION, tr("Set the selected elements on the map to the selected items in the list above."));
347 putValue(SMALL_ICON, ImageProvider.get("dialogs", "select"));
348 putValue("help", "Dialogs/ConflictListDialog#SelectAction");
349 }
350
351 public void actionPerformed(ActionEvent e) {
352 Collection<OsmPrimitive> sel = new LinkedList<OsmPrimitive>();
353 for (Object o : lstConflicts.getSelectedValues()) {
354 sel.add((OsmPrimitive)o);
355 }
356 Main.main.getCurrentDataSet().setSelected(sel);
357 }
358
359 public void valueChanged(ListSelectionEvent e) {
360 ListSelectionModel model = (ListSelectionModel)e.getSource();
361 boolean enabled = model.getMinSelectionIndex() >= 0
362 && model.getMaxSelectionIndex() >= model.getMinSelectionIndex();
363 setEnabled(enabled);
364 }
365 }
366
367}
Note: See TracBrowser for help on using the repository browser.