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