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 }