source: josm/branch/0.5/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java@ 329

Last change on this file since 329 was 329, checked in by framm, 17 years ago

This commit is a manual merge of all changes that have been made to
the intermediate "core_0.5" branch on the main OSM repository,
bevore JOSM was moved to openstreetmap.de.

Changes incorporated here:

r4464@svn.openstreetmap.org
r4466@svn.openstreetmap.org
r4468@svn.openstreetmap.org
r4469@svn.openstreetmap.org
r4479@svn.openstreetmap.org

File size: 10.8 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.gui.layer;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.trn;
6
7import java.awt.Color;
8import java.awt.Component;
9import java.awt.Graphics;
10import java.awt.GridBagLayout;
11import java.awt.Point;
12import java.awt.event.ActionEvent;
13import java.io.File;
14import java.util.Collection;
15import java.util.HashSet;
16import java.util.Iterator;
17import java.util.LinkedList;
18import java.util.Set;
19
20import javax.swing.Icon;
21import javax.swing.JLabel;
22import javax.swing.JMenuItem;
23import javax.swing.JOptionPane;
24import javax.swing.JPanel;
25import javax.swing.JSeparator;
26
27import org.openstreetmap.josm.Main;
28import org.openstreetmap.josm.actions.GpxExportAction;
29import org.openstreetmap.josm.actions.RenameLayerAction;
30import org.openstreetmap.josm.actions.SaveAction;
31import org.openstreetmap.josm.actions.SaveAsAction;
32import org.openstreetmap.josm.command.Command;
33import org.openstreetmap.josm.data.coor.EastNorth;
34import org.openstreetmap.josm.data.osm.DataSet;
35import org.openstreetmap.josm.data.osm.DataSource;
36import org.openstreetmap.josm.data.osm.Relation;
37import org.openstreetmap.josm.data.osm.Node;
38import org.openstreetmap.josm.data.osm.OsmPrimitive;
39import org.openstreetmap.josm.data.osm.Way;
40import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
41import org.openstreetmap.josm.data.osm.visitor.MergeVisitor;
42import org.openstreetmap.josm.data.osm.visitor.SimplePaintVisitor;
43import org.openstreetmap.josm.data.osm.visitor.Visitor;
44import org.openstreetmap.josm.gui.MapView;
45import org.openstreetmap.josm.gui.dialogs.ConflictDialog;
46import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
47import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
48import org.openstreetmap.josm.tools.GBC;
49import org.openstreetmap.josm.tools.ImageProvider;
50
51/**
52 * A layer holding data from a specific dataset.
53 * The data can be fully edited.
54 *
55 * @author imi
56 */
57public class OsmDataLayer extends Layer {
58
59 public final static class DataCountVisitor implements Visitor {
60 public final int[] normal = new int[3];
61 public final int[] deleted = new int[3];
62 public final String[] names = {"node", "way", "relation"};
63
64 private void inc(final OsmPrimitive osm, final int i) {
65 normal[i]++;
66 if (osm.deleted)
67 deleted[i]++;
68 }
69
70 public void visit(final Node n) {
71 inc(n, 0);
72 }
73
74 public void visit(final Way w) {
75 inc(w, 1);
76 }
77 public void visit(final Relation w) {
78 inc(w, 2);
79 }
80 }
81
82 public interface ModifiedChangedListener {
83 void modifiedChanged(boolean value, OsmDataLayer source);
84 }
85 public interface CommandQueueListener {
86 void commandChanged(int queueSize, int redoSize);
87 }
88
89 /**
90 * @deprecated Use Main.main.undoRedo.add(...) instead.
91 */
92 @Deprecated public void add(final Command c) {
93 Main.main.undoRedo.add(c);
94 }
95
96 /**
97 * The data behind this layer.
98 */
99 public final DataSet data;
100
101 /**
102 * Whether the data of this layer was modified during the session.
103 */
104 private boolean modified = false;
105 /**
106 * Whether the data was modified due an upload of the data to the server.
107 */
108 public boolean uploadedModified = false;
109
110 public final LinkedList<ModifiedChangedListener> listenerModified = new LinkedList<ModifiedChangedListener>();
111
112 private SimplePaintVisitor mapPainter = new SimplePaintVisitor();
113
114 /**
115 * Construct a OsmDataLayer.
116 */
117 public OsmDataLayer(final DataSet data, final String name, final File associatedFile) {
118 super(name);
119 this.data = data;
120 this.associatedFile = associatedFile;
121 }
122
123 /**
124 * TODO: @return Return a dynamic drawn icon of the map data. The icon is
125 * updated by a background thread to not disturb the running programm.
126 */
127 @Override public Icon getIcon() {
128 return ImageProvider.get("layer", "osmdata_small");
129 }
130
131 /**
132 * Draw all primitives in this layer but do not draw modified ones (they
133 * are drawn by the edit layer).
134 * Draw nodes last to overlap the ways they belong to.
135 */
136 @Override public void paint(final Graphics g, final MapView mv) {
137 boolean inactive = Main.map.mapView.getActiveLayer() != this && Main.pref.getBoolean("draw.data.inactive_color", true);
138 if (Main.pref.getBoolean("draw.data.downloaded_area", false)) {
139 // FIXME this is inefficient; instead a proper polygon has to be built, and instead
140 // of drawing the outline, the outlying areas should perhaps be shaded.
141 for (DataSource src : data.dataSources) {
142 if (src.bounds != null) {
143 EastNorth en1 = Main.proj.latlon2eastNorth(src.bounds.min);
144 EastNorth en2 = Main.proj.latlon2eastNorth(src.bounds.max);
145 Point p1 = mv.getPoint(en1);
146 Point p2 = mv.getPoint(en2);
147 Color color = inactive ? SimplePaintVisitor.getPreferencesColor("inactive", Color.DARK_GRAY) :
148 SimplePaintVisitor.getPreferencesColor("downloaded Area", Color.YELLOW);
149 g.setColor(color);
150 g.drawRect(Math.min(p1.x,p2.x), Math.min(p1.y, p2.y), Math.abs(p2.x-p1.x), Math.abs(p2.y-p1.y));
151 }
152 }
153 }
154 mapPainter.setGraphics(g);
155 mapPainter.setNavigatableComponent(mv);
156 mapPainter.inactive = inactive;
157 mapPainter.visitAll(data);
158 Main.map.conflictDialog.paintConflicts(g, mv);
159 }
160
161 @Override public String getToolTipText() {
162 String tool = "";
163 tool += undeletedSize(data.nodes)+" "+trn("node", "nodes", undeletedSize(data.nodes))+", ";
164 tool += undeletedSize(data.ways)+" "+trn("way", "ways", undeletedSize(data.ways));
165 if (associatedFile != null)
166 tool = "<html>"+tool+"<br>"+associatedFile.getPath()+"</html>";
167 return tool;
168 }
169
170 @Override public void mergeFrom(final Layer from) {
171 final MergeVisitor visitor = new MergeVisitor(data,((OsmDataLayer)from).data);
172 for (final OsmPrimitive osm : ((OsmDataLayer)from).data.allPrimitives())
173 osm.visit(visitor);
174 visitor.fixReferences();
175
176 // copy the merged layer's data source info
177 for (DataSource src : ((OsmDataLayer)from).data.dataSources)
178 data.dataSources.add(src);
179
180 if (visitor.conflicts.isEmpty())
181 return;
182 final ConflictDialog dlg = Main.map.conflictDialog;
183 dlg.add(visitor.conflicts);
184 JOptionPane.showMessageDialog(Main.parent,tr("There were conflicts during import."));
185 if (!dlg.isVisible())
186 dlg.action.actionPerformed(new ActionEvent(this, 0, ""));
187 }
188
189 @Override public boolean isMergable(final Layer other) {
190 return other instanceof OsmDataLayer;
191 }
192
193 @Override public void visitBoundingBox(final BoundingXYVisitor v) {
194 for (final Node n : data.nodes)
195 if (!n.deleted)
196 v.visit(n);
197 }
198
199 /**
200 * Clean out the data behind the layer. This means clearing the redo/undo lists,
201 * really deleting all deleted objects and reset the modified flags. This is done
202 * after a successfull upload.
203 *
204 * @param processed A list of all objects, that were actually uploaded.
205 * May be <code>null</code>, which means nothing has been uploaded but
206 * saved to disk instead. Note that an empty collection for "processed"
207 * means that an upload has been attempted but failed.
208 */
209 public void cleanData(final Collection<OsmPrimitive> processed, boolean dataAdded) {
210
211 // return immediately if an upload attempt failed
212 if (processed != null && processed.isEmpty() && !dataAdded)
213 return;
214
215 Main.main.undoRedo.clean();
216
217 // if uploaded, clean the modified flags as well
218 if (processed != null) {
219 final Set<OsmPrimitive> processedSet = new HashSet<OsmPrimitive>(processed);
220 for (final Iterator<Node> it = data.nodes.iterator(); it.hasNext();)
221 cleanIterator(it, processedSet);
222 for (final Iterator<Way> it = data.ways.iterator(); it.hasNext();)
223 cleanIterator(it, processedSet);
224 }
225
226 // update the modified flag
227 if (associatedFile != null && processed != null && !dataAdded)
228 return; // do nothing when uploading non-harmful changes.
229
230 // modified if server changed the data (esp. the id).
231 uploadedModified = associatedFile != null && processed != null && dataAdded;
232 setModified(uploadedModified);
233 }
234
235 /**
236 * Clean the modified flag for the given iterator over a collection if it is in the
237 * list of processed entries.
238 *
239 * @param it The iterator to change the modified and remove the items if deleted.
240 * @param processed A list of all objects that have been successfully progressed.
241 * If the object in the iterator is not in the list, nothing will be changed on it.
242 */
243 private void cleanIterator(final Iterator<? extends OsmPrimitive> it, final Collection<OsmPrimitive> processed) {
244 final OsmPrimitive osm = it.next();
245 if (!processed.remove(osm))
246 return;
247 osm.modified = false;
248 if (osm.deleted)
249 it.remove();
250 }
251
252 public boolean isModified() {
253 return modified;
254 }
255
256 public void setModified(final boolean modified) {
257 if (modified == this.modified)
258 return;
259 this.modified = modified;
260 for (final ModifiedChangedListener l : listenerModified)
261 l.modifiedChanged(modified, this);
262 }
263
264 /**
265 * @return The number of not-deleted primitives in the list.
266 */
267 private int undeletedSize(final Collection<? extends OsmPrimitive> list) {
268 int size = 0;
269 for (final OsmPrimitive osm : list)
270 if (!osm.deleted)
271 size++;
272 return size;
273 }
274
275 @Override public Object getInfoComponent() {
276 final DataCountVisitor counter = new DataCountVisitor();
277 for (final OsmPrimitive osm : data.allPrimitives())
278 osm.visit(counter);
279 final JPanel p = new JPanel(new GridBagLayout());
280 p.add(new JLabel(tr("{0} consists of:", name)), GBC.eol());
281 for (int i = 0; i < counter.normal.length; ++i) {
282 String s = counter.normal[i]+" "+trn(counter.names[i],counter.names[i]+"s",counter.normal[i]);
283 if (counter.deleted[i] > 0)
284 s += tr(" ({0} deleted.)",counter.deleted[i]);
285 p.add(new JLabel(s, ImageProvider.get("data", counter.names[i]), JLabel.HORIZONTAL), GBC.eop().insets(15,0,0,0));
286 }
287 return p;
288 }
289
290 @Override public Component[] getMenuEntries() {
291 if (Main.applet) {
292 return new Component[]{
293 new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
294 new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
295 new JSeparator(),
296 new JMenuItem(new RenameLayerAction(associatedFile, this)),
297 new JSeparator(),
298 new JMenuItem(new LayerListPopup.InfoAction(this))};
299 }
300 return new Component[]{
301 new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
302 new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
303 new JSeparator(),
304 new JMenuItem(new SaveAction(this)),
305 new JMenuItem(new SaveAsAction(this)),
306 new JMenuItem(new GpxExportAction(this)),
307 new JSeparator(),
308 new JMenuItem(new RenameLayerAction(associatedFile, this)),
309 new JSeparator(),
310 new JMenuItem(new LayerListPopup.InfoAction(this))};
311 }
312
313
314 public void setMapPainter(SimplePaintVisitor mapPainter) {
315 this.mapPainter = mapPainter;
316 }
317}
Note: See TracBrowser for help on using the repository browser.