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