001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.history;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.awt.Dimension;
007import java.awt.Point;
008import java.util.ArrayList;
009import java.util.Collection;
010import java.util.HashMap;
011import java.util.List;
012import java.util.Map;
013
014import javax.swing.JOptionPane;
015import javax.swing.SwingUtilities;
016
017import org.openstreetmap.josm.Main;
018import org.openstreetmap.josm.data.osm.OsmPrimitive;
019import org.openstreetmap.josm.data.osm.PrimitiveId;
020import org.openstreetmap.josm.data.osm.history.History;
021import org.openstreetmap.josm.data.osm.history.HistoryDataSet;
022import org.openstreetmap.josm.gui.MapView;
023import org.openstreetmap.josm.gui.layer.Layer;
024import org.openstreetmap.josm.tools.BugReportExceptionHandler;
025import org.openstreetmap.josm.tools.Predicate;
026import org.openstreetmap.josm.tools.Utils;
027import org.openstreetmap.josm.tools.WindowGeometry;
028
029public class HistoryBrowserDialogManager implements MapView.LayerChangeListener {
030    static private HistoryBrowserDialogManager instance;
031    static public HistoryBrowserDialogManager getInstance() {
032        if (instance == null) {
033            instance = new HistoryBrowserDialogManager();
034        }
035        return instance;
036    }
037
038    private Map<Long, HistoryBrowserDialog> dialogs;
039
040    protected HistoryBrowserDialogManager() {
041        dialogs = new HashMap<Long, HistoryBrowserDialog>();
042        MapView.addLayerChangeListener(this);
043    }
044
045    public boolean existsDialog(long id) {
046        return dialogs.containsKey(id);
047    }
048
049    public void show(long id, HistoryBrowserDialog dialog) {
050        if (dialogs.values().contains(dialog)) {
051            show(id);
052        } else {
053            placeOnScreen(dialog);
054            dialog.setVisible(true);
055            dialogs.put(id, dialog);
056        }
057    }
058
059    public void show(long id) {
060        if (dialogs.keySet().contains(id)) {
061            dialogs.get(id).toFront();
062        }
063    }
064
065    protected boolean hasDialogWithCloseUpperLeftCorner(Point p) {
066        for (HistoryBrowserDialog dialog: dialogs.values()) {
067            Point corner = dialog.getLocation();
068            if (p.x >= corner.x -5 && corner.x + 5 >= p.x
069                    && p.y >= corner.y -5 && corner.y + 5 >= p.y)
070                return true;
071        }
072        return false;
073    }
074
075    public void placeOnScreen(HistoryBrowserDialog dialog) {
076        WindowGeometry geometry = WindowGeometry.centerOnScreen(new Dimension(800,500));
077        geometry.applySafe(dialog);
078        Point p = dialog.getLocation();
079        while(hasDialogWithCloseUpperLeftCorner(p)) {
080            p.x +=20;
081            p.y += 20;
082        }
083        dialog.setLocation(p);
084    }
085
086    public void hide(HistoryBrowserDialog dialog) {
087        long id = 0;
088        for (long i: dialogs.keySet()) {
089            if (dialogs.get(i) == dialog) {
090                id = i;
091                break;
092            }
093        }
094        if (id > 0) {
095            dialogs.remove(id);
096        }
097        dialog.setVisible(false);
098        dialog.dispose();
099    }
100
101    /**
102     * Hides and destroys all currently visible history browser dialogs
103     *
104     */
105    public void hideAll() {
106        List<HistoryBrowserDialog> dialogs = new ArrayList<HistoryBrowserDialog>();
107        dialogs.addAll(this.dialogs.values());
108        for (HistoryBrowserDialog dialog: dialogs) {
109            dialog.unlinkAsListener();
110            hide(dialog);
111        }
112    }
113
114    public void show(History h) {
115        if (h == null)
116            return;
117        if (existsDialog(h.getId())) {
118            show(h.getId());
119        } else {
120            HistoryBrowserDialog dialog = new HistoryBrowserDialog(h);
121            show(h.getId(), dialog);
122        }
123    }
124
125    /* ----------------------------------------------------------------------------- */
126    /* LayerChangeListener                                                           */
127    /* ----------------------------------------------------------------------------- */
128    @Override
129    public void activeLayerChange(Layer oldLayer, Layer newLayer) {}
130    @Override
131    public void layerAdded(Layer newLayer) {}
132
133    @Override
134    public void layerRemoved(Layer oldLayer) {
135        // remove all history browsers if the number of layers drops to 0
136        //
137        if (Main.isDisplayingMapView() && Main.map.mapView.getNumLayers() == 0) {
138            hideAll();
139        }
140    }
141
142    public void showHistory(final Collection<? extends PrimitiveId> primitives) {
143        final Collection<? extends PrimitiveId> notNewPrimitives = Utils.filter(primitives, notNewPredicate);
144        if (notNewPrimitives.isEmpty()) {
145            JOptionPane.showMessageDialog(
146                    Main.parent,
147                    tr("Please select at least one already uploaded node, way, or relation."),
148                    tr("Warning"),
149                    JOptionPane.WARNING_MESSAGE);
150            return;
151        }
152
153        Collection<PrimitiveId> toLoad = Utils.filter(primitives, unloadedHistoryPredicate);
154        if (!toLoad.isEmpty()) {
155            HistoryLoadTask task = new HistoryLoadTask();
156            for (PrimitiveId p : notNewPrimitives) {
157                task.add(p);
158            }
159            Main.worker.submit(task);
160        }
161
162        Runnable r = new Runnable() {
163
164            @Override
165            public void run() {
166                try {
167                    for (PrimitiveId p : notNewPrimitives) {
168                        final History h = HistoryDataSet.getInstance().getHistory(p);
169                        if (h == null) {
170                            continue;
171                        }
172                        SwingUtilities.invokeLater(new Runnable() {
173                            @Override
174                            public void run() {
175                                show(h);
176                            }
177                        });
178                    }
179                } catch (final Exception e) {
180                    BugReportExceptionHandler.handleException(e);
181                }
182
183            }
184        };
185        Main.worker.submit(r);
186    }
187
188    private final Predicate<PrimitiveId> unloadedHistoryPredicate = new Predicate<PrimitiveId>() {
189
190        HistoryDataSet hds = HistoryDataSet.getInstance();
191
192        @Override
193        public boolean evaluate(PrimitiveId p) {
194            History h = hds.getHistory(p);
195            if (h == null)
196                // reload if the history is not in the cache yet
197                return true;
198            else if (!p.isNew() && h.getByVersion(p.getUniqueId()) == null)
199                // reload if the history object of the selected object is not in the cache yet
200                return true;
201            else
202                return false;
203        }
204    };
205
206    private final Predicate<PrimitiveId> notNewPredicate = new Predicate<PrimitiveId>() {
207
208        @Override
209        public boolean evaluate(PrimitiveId p) {
210            return !p.isNew();
211        }
212    };
213
214}