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

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