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

Last change on this file since 4077 was 3973, checked in by mjulius, 13 years ago

Zoom to conflict now zooms to first selected conflict if there is one selected. Otherwise it zooms to all conflicts (as before).
closes #3266

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