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 }