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

Last change on this file since 2131 was 2131, checked in by Gubaer, 15 years ago

see #3407: NPE in ConflictDialog when using OpenStreetBug plugin

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