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