source: josm/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java@ 90

Last change on this file since 90 was 90, checked in by imi, 18 years ago
  • fixed that toggle dialog buttons are sync with the dialogs
  • added search for timestamp (e.g. timestamp:06-3-25)
File size: 9.9 KB
Line 
1package org.openstreetmap.josm.gui.layer;
2
3import java.awt.Graphics;
4import java.awt.GridBagLayout;
5import java.awt.event.ActionEvent;
6import java.util.Collection;
7import java.util.HashSet;
8import java.util.Iterator;
9import java.util.LinkedList;
10import java.util.Set;
11import java.util.Stack;
12
13import javax.swing.Icon;
14import javax.swing.JLabel;
15import javax.swing.JMenuItem;
16import javax.swing.JOptionPane;
17import javax.swing.JPanel;
18import javax.swing.JPopupMenu;
19
20import org.openstreetmap.josm.Main;
21import org.openstreetmap.josm.actions.GpxExportAction;
22import org.openstreetmap.josm.actions.SaveAction;
23import org.openstreetmap.josm.command.Command;
24import org.openstreetmap.josm.data.osm.DataSet;
25import org.openstreetmap.josm.data.osm.Segment;
26import org.openstreetmap.josm.data.osm.Node;
27import org.openstreetmap.josm.data.osm.OsmPrimitive;
28import org.openstreetmap.josm.data.osm.Way;
29import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
30import org.openstreetmap.josm.data.osm.visitor.MergeVisitor;
31import org.openstreetmap.josm.data.osm.visitor.SimplePaintVisitor;
32import org.openstreetmap.josm.data.osm.visitor.Visitor;
33import org.openstreetmap.josm.gui.MapView;
34import org.openstreetmap.josm.gui.dialogs.ConflictDialog;
35import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
36import org.openstreetmap.josm.tools.GBC;
37import org.openstreetmap.josm.tools.ImageProvider;
38
39/**
40 * A layer holding data from a specific dataset.
41 * The data can be fully edited.
42 *
43 * @author imi
44 */
45public class OsmDataLayer extends Layer {
46
47 public final static class DataCountVisitor implements Visitor {
48 public final int[] normal = new int[3];
49 public final int[] deleted = new int[3];
50 public final String[] names = {"node", "segment", "way"};
51
52 private void inc(OsmPrimitive osm, int i) {
53 normal[i]++;
54 if (osm.deleted)
55 deleted[i]++;
56 }
57
58 public void visit(Node n) {
59 inc(n, 0);
60 }
61
62 public void visit(Segment ls) {
63 inc(ls, 1);
64 }
65
66 public void visit(Way w) {
67 inc(w, 2);
68 }
69 }
70
71 public interface ModifiedChangedListener {
72 void modifiedChanged(boolean value, OsmDataLayer source);
73 }
74
75 private static Icon icon;
76
77 /**
78 * The data behind this layer.
79 */
80 public final DataSet data;
81
82 /**
83 * Whether the data of this layer was modified during the session.
84 */
85 private boolean modified = false;
86 /**
87 * Whether the data was modified due an upload of the data to the server.
88 */
89 public boolean uploadedModified = false;
90 /**
91 * Whether the data (or pieces of the data) was loaded from disk rather than from
92 * the server directly. This affects the modified state.
93 */
94 private boolean fromDisk = false;
95 /**
96 * All commands that were made on the dataset.
97 */
98 private LinkedList<Command> commands = new LinkedList<Command>();
99 /**
100 * The stack for redoing commands
101 */
102 private Stack<Command> redoCommands = new Stack<Command>();
103
104 /**
105 * List of all listeners for changes of modified flag.
106 */
107 LinkedList<ModifiedChangedListener> listener;
108
109
110 /**
111 * Construct a OsmDataLayer.
112 */
113 public OsmDataLayer(DataSet data, String name, boolean fromDisk) {
114 super(name);
115 this.data = data;
116 this.fromDisk = fromDisk;
117 }
118
119 /**
120 * TODO: @return Return a dynamic drawn icon of the map data. The icon is
121 * updated by a background thread to not disturb the running programm.
122 */
123 @Override public Icon getIcon() {
124 if (icon == null)
125 icon = ImageProvider.get("layer", "osmdata");
126 return icon;
127 }
128
129 /**
130 * Draw all primitives in this layer but do not draw modified ones (they
131 * are drawn by the edit layer).
132 * Draw nodes last to overlap the segments they belong to.
133 */
134 @Override public void paint(Graphics g, MapView mv) {
135 SimplePaintVisitor visitor = new SimplePaintVisitor(g, mv);
136 for (OsmPrimitive osm : data.segments)
137 if (!osm.deleted)
138 osm.visit(visitor);
139 for (OsmPrimitive osm : data.ways)
140 if (!osm.deleted)
141 osm.visit(visitor);
142 for (OsmPrimitive osm : data.nodes)
143 if (!osm.deleted)
144 osm.visit(visitor);
145 for (OsmPrimitive osm : data.getSelected())
146 if (!osm.deleted)
147 osm.visit(visitor);
148 Main.main.getMapFrame().conflictDialog.paintConflicts(g, mv);
149 }
150
151 @Override public String getToolTipText() {
152 return undeletedSize(data.nodes)+" nodes, "+
153 undeletedSize(data.segments)+" segments, "+
154 undeletedSize(data.ways)+" streets.";
155 }
156
157 @Override public void mergeFrom(Layer from) {
158 final MergeVisitor visitor = new MergeVisitor(data);
159 for (OsmPrimitive osm : ((OsmDataLayer)from).data.allPrimitives())
160 osm.visit(visitor);
161 visitor.fixReferences();
162 if (visitor.conflicts.isEmpty())
163 return;
164 ConflictDialog dlg = Main.main.getMapFrame().conflictDialog;
165 dlg.add(visitor.conflicts);
166 JOptionPane.showMessageDialog(Main.main, "There were conflicts during import.");
167 if (!dlg.isVisible())
168 dlg.action.actionPerformed(new ActionEvent(this, 0, ""));
169 }
170
171 @Override public boolean isMergable(Layer other) {
172 return other instanceof OsmDataLayer;
173 }
174
175 @Override public void visitBoundingBox(BoundingXYVisitor v) {
176 for (Node n : data.nodes)
177 v.visit(n);
178 }
179
180 /**
181 * @return the last command added or <code>null</code> if no command in queue.
182 */
183 public Command lastCommand() {
184 return commands.isEmpty() ? null : commands.getLast();
185 }
186
187 /**
188 * Execute the command and add it to the intern command queue. Also mark all
189 * primitives in the command as modified.
190 */
191 public void add(Command c) {
192 c.executeCommand();
193 commands.add(c);
194 redoCommands.clear();
195 // TODO: Replace with listener scheme
196 Main.main.undoAction.setEnabled(true);
197 Main.main.redoAction.setEnabled(false);
198 setModified(true);
199 }
200
201 /**
202 * Undoes the last added command.
203 */
204 public void undo() {
205 if (commands.isEmpty())
206 return;
207 Command c = commands.removeLast();
208 c.undoCommand();
209 redoCommands.push(c);
210 //TODO: Replace with listener scheme
211 Main.main.undoAction.setEnabled(!commands.isEmpty());
212 Main.main.redoAction.setEnabled(true);
213 if (commands.isEmpty())
214 setModified(uploadedModified);
215 }
216 /**
217 * Redoes the last undoed command.
218 */
219 public void redo() {
220 if (redoCommands.isEmpty())
221 return;
222 Command c = redoCommands.pop();
223 c.executeCommand();
224 commands.add(c);
225 //TODO: Replace with listener scheme
226 Main.main.undoAction.setEnabled(true);
227 Main.main.redoAction.setEnabled(!redoCommands.isEmpty());
228 setModified(true);
229 }
230
231 /**
232 * Clean out the data behind the layer. This means clearing the redo/undo lists,
233 * really deleting all deleted objects and reset the modified flags. This is done
234 * after a successfull upload.
235 *
236 * @param uploaded <code>true</code>, if the data was uploaded, false if saved to disk
237 * @param processed A list of all objects, that were actually uploaded.
238 * May be <code>null</code>, which means nothing has been uploaded but
239 * saved to disk instead.
240 */
241 public void cleanData(Collection<OsmPrimitive> processed, boolean dataAdded) {
242 redoCommands.clear();
243 commands.clear();
244
245 // if uploaded, clean the modified flags as well
246 if (processed != null) {
247 Set<OsmPrimitive> processedSet = new HashSet<OsmPrimitive>(processed);
248 for (Iterator<Node> it = data.nodes.iterator(); it.hasNext();)
249 cleanIterator(it, processedSet);
250 for (Iterator<Segment> it = data.segments.iterator(); it.hasNext();)
251 cleanIterator(it, processedSet);
252 for (Iterator<Way> it = data.ways.iterator(); it.hasNext();)
253 cleanIterator(it, processedSet);
254 }
255
256 // update the modified flag
257
258 if (fromDisk && processed != null && !dataAdded)
259 return; // do nothing when uploading non-harmful changes.
260
261 // modified if server changed the data (esp. the id).
262 uploadedModified = fromDisk && processed != null && dataAdded;
263 setModified(uploadedModified);
264 //TODO: Replace with listener scheme
265 Main.main.undoAction.setEnabled(false);
266 Main.main.redoAction.setEnabled(false);
267 }
268
269 /**
270 * Clean the modified flag for the given iterator over a collection if it is in the
271 * list of processed entries.
272 *
273 * @param it The iterator to change the modified and remove the items if deleted.
274 * @param processed A list of all objects that have been successfully progressed.
275 * If the object in the iterator is not in the list, nothing will be changed on it.
276 */
277 private void cleanIterator(Iterator<? extends OsmPrimitive> it, Collection<OsmPrimitive> processed) {
278 OsmPrimitive osm = it.next();
279 if (!processed.remove(osm))
280 return;
281 osm.modified = false;
282 if (osm.deleted)
283 it.remove();
284 }
285
286 public boolean isModified() {
287 return modified;
288 }
289
290 public void setModified(boolean modified) {
291 if (modified == this.modified)
292 return;
293 this.modified = modified;
294 if (listener != null)
295 for (ModifiedChangedListener l : listener)
296 l.modifiedChanged(modified, this);
297 }
298
299 /**
300 * Add the parameter to the intern list of listener for modified state.
301 * @param l Listener to add to the list. Must not be null.
302 */
303 public void addModifiedListener(ModifiedChangedListener l) {
304 if (listener == null)
305 listener = new LinkedList<ModifiedChangedListener>();
306 listener.add(l);
307 }
308
309 /**
310 * @return The number of not-deleted primitives in the list.
311 */
312 private int undeletedSize(Collection<? extends OsmPrimitive> list) {
313 int size = 0;
314 for (OsmPrimitive osm : list)
315 if (!osm.deleted)
316 size++;
317 return size;
318 }
319
320 @Override public Object getInfoComponent() {
321 DataCountVisitor counter = new DataCountVisitor();
322 for (OsmPrimitive osm : data.allPrimitives())
323 osm.visit(counter);
324 JPanel p = new JPanel(new GridBagLayout());
325 p.add(new JLabel(name+" consists of:"), GBC.eol());
326 for (int i = 0; i < counter.normal.length; ++i) {
327 String s = counter.normal[i]+" "+counter.names[i]+(counter.normal[i] != 1 ?"s":"");
328 if (counter.deleted[i] > 0)
329 s += " ("+counter.deleted[i]+" deleted)";
330 p.add(new JLabel(s, ImageProvider.get("data", counter.names[i]), JLabel.HORIZONTAL), GBC.eol().insets(15,0,0,0));
331 }
332 return p;
333 }
334
335 @Override public void addMenuEntries(JPopupMenu menu) {
336 menu.add(new JMenuItem(new SaveAction()));
337 menu.add(new JMenuItem(new GpxExportAction(this)));
338 menu.addSeparator();
339 menu.add(new LayerListPopup.InfoAction(this));
340 }
341}
Note: See TracBrowser for help on using the repository browser.