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 }