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

Revision 4982, 12.5 KB checked in by stoecker, 3 months ago (diff)

see #7226 - patch by akks (fixed a bit) - fix shortcut deprecations

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