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

Last change on this file since 2325 was 2325, checked in by Gubaer, 15 years ago

fixed #3783: does not finish download API
Also improved help

  • Property svn:eol-style set to native
File size: 26.0 KB
Line 
1// License: GPL. See LICENSE file for details.
2
3package org.openstreetmap.josm.gui.layer;
4
5import static org.openstreetmap.josm.tools.I18n.marktr;
6import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
7import static org.openstreetmap.josm.tools.I18n.tr;
8import static org.openstreetmap.josm.tools.I18n.trn;
9
10import java.awt.AlphaComposite;
11import java.awt.Color;
12import java.awt.Component;
13import java.awt.Composite;
14import java.awt.Graphics;
15import java.awt.Graphics2D;
16import java.awt.GridBagLayout;
17import java.awt.Point;
18import java.awt.Rectangle;
19import java.awt.TexturePaint;
20import java.awt.event.ActionEvent;
21import java.awt.event.ActionListener;
22import java.awt.geom.Area;
23import java.awt.image.BufferedImage;
24import java.io.File;
25import java.util.ArrayList;
26import java.util.Collection;
27import java.util.HashSet;
28import java.util.Iterator;
29import java.util.LinkedList;
30import java.util.Set;
31
32import javax.swing.AbstractAction;
33import javax.swing.Icon;
34import javax.swing.JButton;
35import javax.swing.JDialog;
36import javax.swing.JLabel;
37import javax.swing.JMenuItem;
38import javax.swing.JOptionPane;
39import javax.swing.JPanel;
40import javax.swing.JSeparator;
41
42import org.openstreetmap.josm.Main;
43import org.openstreetmap.josm.actions.RenameLayerAction;
44import org.openstreetmap.josm.command.PurgePrimitivesCommand;
45import org.openstreetmap.josm.data.conflict.Conflict;
46import org.openstreetmap.josm.data.conflict.ConflictCollection;
47import org.openstreetmap.josm.data.coor.EastNorth;
48import org.openstreetmap.josm.data.coor.LatLon;
49import org.openstreetmap.josm.data.gpx.GpxData;
50import org.openstreetmap.josm.data.gpx.GpxTrack;
51import org.openstreetmap.josm.data.gpx.WayPoint;
52import org.openstreetmap.josm.data.osm.BackreferencedDataSet;
53import org.openstreetmap.josm.data.osm.DataSet;
54import org.openstreetmap.josm.data.osm.DataSource;
55import org.openstreetmap.josm.data.osm.Node;
56import org.openstreetmap.josm.data.osm.OsmPrimitive;
57import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
58import org.openstreetmap.josm.data.osm.Relation;
59import org.openstreetmap.josm.data.osm.Way;
60import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
61import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
62import org.openstreetmap.josm.data.osm.visitor.MapPaintVisitor;
63import org.openstreetmap.josm.data.osm.visitor.MergeVisitor;
64import org.openstreetmap.josm.data.osm.visitor.SimplePaintVisitor;
65import org.openstreetmap.josm.gui.HelpAwareOptionPane;
66import org.openstreetmap.josm.gui.MapView;
67import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec;
68import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
69import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
70import org.openstreetmap.josm.gui.help.HelpBrowser;
71import org.openstreetmap.josm.gui.help.HelpUtil;
72import org.openstreetmap.josm.tools.DateUtils;
73import org.openstreetmap.josm.tools.GBC;
74import org.openstreetmap.josm.tools.ImageProvider;
75import org.openstreetmap.josm.tools.WindowGeometry;
76
77/**
78 * A layer holding data from a specific dataset.
79 * The data can be fully edited.
80 *
81 * @author imi
82 */
83public class OsmDataLayer extends Layer {
84 static public final String REQUIRES_SAVE_TO_DISK_PROP = OsmDataLayer.class.getName() + ".requiresSaveToDisk";
85 static public final String REQUIRES_UPLOAD_TO_SERVER_PROP = OsmDataLayer.class.getName() + ".requiresUploadToServer";
86
87 private boolean requiresSaveToFile = false;
88 private boolean requiresUploadToServer = false;
89
90 protected void setRequiresSaveToFile(boolean newValue) {
91 boolean oldValue = requiresSaveToFile;
92 requiresSaveToFile = newValue;
93 if (oldValue != newValue) {
94 propertyChangeSupport.firePropertyChange(REQUIRES_SAVE_TO_DISK_PROP, oldValue, newValue);
95 }
96 }
97
98 protected void setRequiresUploadToServer(boolean newValue) {
99 boolean oldValue = requiresUploadToServer;
100 requiresUploadToServer = newValue;
101 if (oldValue != newValue) {
102 propertyChangeSupport.firePropertyChange(REQUIRES_UPLOAD_TO_SERVER_PROP, oldValue, newValue);
103 }
104 }
105
106 /** the global counter for created data layers */
107 static private int dataLayerCounter = 0;
108
109 /**
110 * Replies a new unique name for a data layer
111 *
112 * @return a new unique name for a data layer
113 */
114 static public String createNewName() {
115 dataLayerCounter++;
116 return tr("Data Layer {0}", dataLayerCounter);
117 }
118
119 public final static class DataCountVisitor extends AbstractVisitor {
120 public final int[] normal = new int[3];
121 public final int[] deleted = new int[3];
122 public final String[] names = {
123 OsmPrimitiveType.NODE.getAPIName(),
124 OsmPrimitiveType.WAY.getAPIName(),
125 OsmPrimitiveType.RELATION.getAPIName()
126 };
127
128 private void inc(final OsmPrimitive osm, final int i) {
129 normal[i]++;
130 if (osm.isDeleted()) {
131 deleted[i]++;
132 }
133 }
134
135 public void visit(final Node n) {
136 inc(n, 0);
137 }
138
139 public void visit(final Way w) {
140 inc(w, 1);
141 }
142 public void visit(final Relation w) {
143 inc(w, 2);
144 }
145 }
146
147 public interface CommandQueueListener {
148 void commandChanged(int queueSize, int redoSize);
149 }
150
151 /**
152 * The data behind this layer.
153 */
154 public final DataSet data;
155
156 /**
157 * the collection of conflicts detected in this layer
158 */
159 private ConflictCollection conflicts;
160
161 public final LinkedList<DataChangeListener> listenerDataChanged = new LinkedList<DataChangeListener>();
162
163 /**
164 * a paint texture for non-downloaded area
165 */
166 private static TexturePaint hatched;
167
168 static {
169 createHatchTexture();
170 }
171
172 /**
173 * Initialize the hatch pattern used to paint the non-downloaded area
174 */
175 public static void createHatchTexture() {
176 BufferedImage bi = new BufferedImage(15, 15, BufferedImage.TYPE_INT_ARGB);
177 Graphics2D big = bi.createGraphics();
178 big.setColor(Main.pref.getColor(marktr("background"), Color.BLACK));
179 Composite comp = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f);
180 big.setComposite(comp);
181 big.fillRect(0,0,15,15);
182 big.setColor(Main.pref.getColor(marktr("outside downloaded area"), Color.YELLOW));
183 big.drawLine(0,15,15,0);
184 Rectangle r = new Rectangle(0, 0, 15,15);
185 hatched = new TexturePaint(bi, r);
186 }
187
188 /**
189 * Construct a OsmDataLayer.
190 */
191 public OsmDataLayer(final DataSet data, final String name, final File associatedFile) {
192 super(name);
193 this.data = data;
194 this.setAssociatedFile(associatedFile);
195 conflicts = new ConflictCollection();
196 }
197
198 /**
199 * TODO: @return Return a dynamic drawn icon of the map data. The icon is
200 * updated by a background thread to not disturb the running programm.
201 */
202 @Override public Icon getIcon() {
203 return ImageProvider.get("layer", "osmdata_small");
204 }
205
206 /**
207 * Draw all primitives in this layer but do not draw modified ones (they
208 * are drawn by the edit layer).
209 * Draw nodes last to overlap the ways they belong to.
210 */
211 @Override public void paint(final Graphics g, final MapView mv) {
212 boolean active = mv.getActiveLayer() == this;
213 boolean inactive = !active && Main.pref.getBoolean("draw.data.inactive_color", true);
214 boolean virtual = !inactive && mv.isVirtualNodesEnabled();
215
216 // draw the hatched area for non-downloaded region. only draw if we're the active
217 // and bounds are defined; don't draw for inactive layers or loaded GPX files etc
218 if (active && Main.pref.getBoolean("draw.data.downloaded_area", true) && !data.dataSources.isEmpty()) {
219 // initialize area with current viewport
220 Rectangle b = mv.getBounds();
221 // on some platforms viewport bounds seem to be offset from the left,
222 // over-grow it just to be sure
223 b.grow(100, 100);
224 Area a = new Area(b);
225
226 // now succesively subtract downloaded areas
227 for (DataSource src : data.dataSources) {
228 if (src.bounds != null && !src.bounds.min.equals(src.bounds.max)) {
229 EastNorth en1 = mv.getProjection().latlon2eastNorth(src.bounds.min);
230 EastNorth en2 = mv.getProjection().latlon2eastNorth(src.bounds.max);
231 Point p1 = mv.getPoint(en1);
232 Point p2 = mv.getPoint(en2);
233 Rectangle r = new Rectangle(Math.min(p1.x, p2.x),Math.min(p1.y, p2.y),Math.abs(p2.x-p1.x),Math.abs(p2.y-p1.y));
234 a.subtract(new Area(r));
235 }
236 }
237
238 // paint remainder
239 ((Graphics2D)g).setPaint(hatched);
240 ((Graphics2D)g).fill(a);
241 }
242
243 SimplePaintVisitor painter;
244 if (Main.pref.getBoolean("draw.wireframe")) {
245 painter = new SimplePaintVisitor();
246 } else {
247 painter = new MapPaintVisitor();
248 }
249 painter.setGraphics(g);
250 painter.setNavigatableComponent(mv);
251 painter.inactive = inactive;
252 painter.visitAll(data, virtual);
253 Main.map.conflictDialog.paintConflicts(g, mv);
254 }
255
256 @Override public String getToolTipText() {
257 String tool = "";
258 tool += undeletedSize(data.nodes)+" "+trn("node", "nodes", undeletedSize(data.nodes))+", ";
259 tool += undeletedSize(data.ways)+" "+trn("way", "ways", undeletedSize(data.ways));
260 if (data.version != null) {
261 tool += ", " + tr("version {0}", data.version);
262 }
263 File f = getAssociatedFile();
264 if (f != null) {
265 tool = "<html>"+tool+"<br>"+f.getPath()+"</html>";
266 }
267 return tool;
268 }
269
270 @Override public void mergeFrom(final Layer from) {
271 mergeFrom(((OsmDataLayer)from).data);
272 }
273
274 /**
275 * merges the primitives in dataset <code>from</code> into the dataset of
276 * this layer
277 *
278 * @param from the source data set
279 */
280 public void mergeFrom(final DataSet from) {
281 final MergeVisitor visitor = new MergeVisitor(data,from);
282 visitor.merge();
283
284 Area a = data.getDataSourceArea();
285
286 // copy the merged layer's data source info;
287 // only add source rectangles if they are not contained in the
288 // layer already.
289 for (DataSource src : from.dataSources) {
290 if (a == null || !a.contains(src.bounds.asRect())) {
291 data.dataSources.add(src);
292 }
293 }
294
295 // copy the merged layer's API version, downgrade if required
296 if (data.version == null) {
297 data.version = from.version;
298 } else if ("0.5".equals(data.version) ^ "0.5".equals(from.version)) {
299 System.err.println(tr("Warning: mixing 0.6 and 0.5 data results in version 0.5"));
300 data.version = "0.5";
301 }
302
303 int numNewConflicts = 0;
304 for (Conflict<?> c : visitor.getConflicts()) {
305 if (!conflicts.hasConflict(c)) {
306 numNewConflicts++;
307 conflicts.add(c);
308 }
309 }
310 PurgePrimitivesCommand cmd = buildPurgeCommand();
311 if (cmd != null) {
312 Main.main.undoRedo.add(cmd);
313 }
314 fireDataChange();
315 // repaint to make sure new data is displayed properly.
316 Main.map.mapView.repaint();
317 warnNumNewConflicts(
318 numNewConflicts,
319 cmd == null ? 0 : cmd.getPurgedPrimitives().size()
320 );
321 }
322
323 /**
324 * Warns the user about the number of detected conflicts
325 *
326 * @param numNewConflicts the number of detected conflicts
327 * @param numPurgedPrimitives the number of automatically purged objects
328 */
329 protected void warnNumNewConflicts(int numNewConflicts, int numPurgedPrimitives) {
330 if (numNewConflicts == 0 && numPurgedPrimitives == 0) return;
331
332 String msg1 = trn(
333 "There was {0} conflict detected.",
334 "There were {0} conflicts detected.",
335 numNewConflicts,
336 numNewConflicts
337 );
338 String msg2 = trn(
339 "{0} conflict has been <strong>resolved automatically</strong> by purging {0} object<br>from the local dataset because it is deleted on the server.",
340 "{0} conflicts have been <strong>resolved automatically</strong> by purging {0} objects<br> from the local dataset because they are deleted on the server.",
341 numPurgedPrimitives,
342 numPurgedPrimitives
343 );
344 int numRemainingConflicts = numNewConflicts - numPurgedPrimitives;
345 String msg3 = "";
346 if (numRemainingConflicts >0) {
347 msg3 = trn(
348 "{0} conflict remains to be resolved.<br><br>Please open the Conflict List Dialog and manually resolve it.",
349 "{0} conflicts remain to be resolved.<br><br>Please open the Conflict List Dialog and manually resolve them.",
350 numRemainingConflicts,
351 numRemainingConflicts
352 );
353 }
354
355 StringBuffer sb = new StringBuffer();
356 sb.append("<html>").append(msg1);
357 if (numPurgedPrimitives > 0) {
358 sb.append("<br>").append(msg2);
359 }
360 if (numRemainingConflicts > 0) {
361 sb.append("<br>").append(msg3);
362 }
363 sb.append("</html>");
364 if (numNewConflicts > 0) {
365 ButtonSpec[] options = new ButtonSpec[] {
366 new ButtonSpec(
367 tr("OK"),
368 ImageProvider.get("ok"),
369 tr("Click to close this dialog and continue editing"),
370 null /* no specific help */
371 )
372 };
373 HelpAwareOptionPane.showOptionDialog(
374 Main.parent,
375 sb.toString(),
376 tr("Conflicts detected"),
377 JOptionPane.WARNING_MESSAGE,
378 null, /* no icon */
379 options,
380 options[0],
381 ht("/Concepts/Conflict#WarningAboutDetectedConflicts")
382 );
383 }
384 }
385
386 /**
387 * Builds the purge command for primitives which can be purged automatically
388 * from the local dataset because they've been deleted on the
389 * server.
390 *
391 * @return the purge command. <code>null</code> if no primitives have to
392 * be purged
393 */
394 protected PurgePrimitivesCommand buildPurgeCommand() {
395 BackreferencedDataSet ds = new BackreferencedDataSet(data);
396 ds.build();
397 ArrayList<OsmPrimitive> toPurge = new ArrayList<OsmPrimitive>();
398 conflictLoop: for (Conflict<?> c: conflicts) {
399 if (c.getMy().isDeleted() && !c.getTheir().isVisible()) {
400 // Local and server version of the primitive are deleted. We
401 // can purge it from the local dataset.
402 //
403 toPurge.add(c.getMy());
404 } else if (!c.getMy().isModified() && ! c.getTheir().isVisible()) {
405 // We purge deleted *ways* and *relations* automatically if they are
406 // deleted on the server and if they aren't modified in the local
407 // dataset.
408 //
409 if (c.getMy() instanceof Way || c.getMy() instanceof Relation) {
410 toPurge.add(c.getMy());
411 continue conflictLoop;
412 }
413 // We only purge nodes if they aren't part of a modified way.
414 // Otherwise the number of nodes of a modified way could drop
415 // below 2 and we would lose the modified data when the way
416 // gets purged.
417 //
418 for (OsmPrimitive parent: ds.getParents(c.getMy())) {
419 if (parent.isModified() && parent instanceof Way) {
420 continue conflictLoop;
421 }
422 }
423 toPurge.add(c.getMy());
424 }
425 }
426 if (toPurge.isEmpty()) return null;
427 PurgePrimitivesCommand cmd = new PurgePrimitivesCommand(this, toPurge);
428 cmd.setBackreferenceDataSet(ds);
429 return cmd;
430 }
431
432 @Override public boolean isMergable(final Layer other) {
433 return other instanceof OsmDataLayer;
434 }
435
436 @Override public void visitBoundingBox(final BoundingXYVisitor v) {
437 for (final Node n : data.nodes)
438 if (n.isUsable()) {
439 v.visit(n);
440 }
441 }
442
443 /**
444 * Clean out the data behind the layer. This means clearing the redo/undo lists,
445 * really deleting all deleted objects and reset the modified flags. This should
446 * be done after an upload, even after a partial upload.
447 *
448 * @param processed A list of all objects that were actually uploaded.
449 * May be <code>null</code>, which means nothing has been uploaded
450 */
451 public void cleanupAfterUpload(final Collection<OsmPrimitive> processed) {
452 // return immediately if an upload attempt failed
453 if (processed == null || processed.isEmpty())
454 return;
455
456 Main.main.undoRedo.clean(this);
457
458 // if uploaded, clean the modified flags as well
459 final Set<OsmPrimitive> processedSet = new HashSet<OsmPrimitive>(processed);
460 for (final Iterator<Node> it = data.nodes.iterator(); it.hasNext();) {
461 cleanIterator(it, processedSet);
462 }
463 for (final Iterator<Way> it = data.ways.iterator(); it.hasNext();) {
464 cleanIterator(it, processedSet);
465 }
466 for (final Iterator<Relation> it = data.relations.iterator(); it.hasNext();) {
467 cleanIterator(it, processedSet);
468 }
469 }
470
471 /**
472 * Clean the modified flag for the given iterator over a collection if it is in the
473 * list of processed entries.
474 *
475 * @param it The iterator to change the modified and remove the items if deleted.
476 * @param processed A list of all objects that have been successfully progressed.
477 * If the object in the iterator is not in the list, nothing will be changed on it.
478 */
479 private void cleanIterator(final Iterator<? extends OsmPrimitive> it, final Collection<OsmPrimitive> processed) {
480 final OsmPrimitive osm = it.next();
481 if (!processed.remove(osm))
482 return;
483 osm.setModified(false);
484 if (osm.isDeleted()) {
485 it.remove();
486 }
487 }
488
489 /**
490 * @return The number of not-deleted primitives in the list.
491 */
492 private int undeletedSize(final Collection<? extends OsmPrimitive> list) {
493 int size = 0;
494 for (final OsmPrimitive osm : list)
495 if (!osm.isDeleted()) {
496 size++;
497 }
498 return size;
499 }
500
501 @Override public Object getInfoComponent() {
502 final DataCountVisitor counter = new DataCountVisitor();
503 for (final OsmPrimitive osm : data.allPrimitives()) {
504 osm.visit(counter);
505 }
506 final JPanel p = new JPanel(new GridBagLayout());
507 p.add(new JLabel(tr("{0} consists of:", getName())), GBC.eol());
508 for (int i = 0; i < counter.normal.length; ++i) {
509 String s = counter.normal[i]+" "+trn(counter.names[i],counter.names[i]+"s",counter.normal[i]);
510 if (counter.deleted[i] > 0) {
511 s += tr(" ({0} deleted.)",counter.deleted[i]);
512 }
513 p.add(new JLabel(s, ImageProvider.get("data", counter.names[i]), JLabel.HORIZONTAL), GBC.eop().insets(15,0,0,0));
514 }
515 p.add(new JLabel(tr("API version: {0}", (data.version != null) ? data.version : tr("unset"))));
516
517 return p;
518 }
519
520 @Override public Component[] getMenuEntries() {
521 if (Main.applet)
522 return new Component[]{
523 new JMenuItem(LayerListDialog.getInstance().createActivateLayerAction(this)),
524 new JMenuItem(LayerListDialog.getInstance().createShowHideLayerAction(this)),
525 new JMenuItem(LayerListDialog.getInstance().createDeleteLayerAction(this)),
526 new JSeparator(),
527 new JMenuItem(LayerListDialog.getInstance().createMergeLayerAction(this)),
528 new JSeparator(),
529 new JMenuItem(new RenameLayerAction(getAssociatedFile(), this)),
530 new JSeparator(),
531 new JMenuItem(new LayerListPopup.InfoAction(this))};
532 return new Component[]{
533 new JMenuItem(LayerListDialog.getInstance().createActivateLayerAction(this)),
534 new JMenuItem(LayerListDialog.getInstance().createShowHideLayerAction(this)),
535 new JMenuItem(LayerListDialog.getInstance().createDeleteLayerAction(this)),
536 new JSeparator(),
537 new JMenuItem(LayerListDialog.getInstance().createMergeLayerAction(this)),
538 new JMenuItem(new LayerSaveAction(this)),
539 new JMenuItem(new LayerSaveAsAction(this)),
540 new JMenuItem(new LayerGpxExportAction(this)),
541 new JMenuItem(new ConvertToGpxLayerAction()),
542 new JSeparator(),
543 new JMenuItem(new RenameLayerAction(getAssociatedFile(), this)),
544 new JSeparator(),
545 new JMenuItem(new LayerListPopup.InfoAction(this))};
546 }
547
548 public void fireDataChange() {
549 setRequiresSaveToFile(true);
550 setRequiresUploadToServer(true);
551 for (DataChangeListener dcl : listenerDataChanged) {
552 dcl.dataChanged(this);
553 }
554 }
555
556 public static GpxData toGpxData(DataSet data, File file) {
557 GpxData gpxData = new GpxData();
558 gpxData.storageFile = file;
559 HashSet<Node> doneNodes = new HashSet<Node>();
560 for (Way w : data.ways) {
561 if (!w.isUsable()) {
562 continue;
563 }
564 GpxTrack trk = new GpxTrack();
565 gpxData.tracks.add(trk);
566
567 if (w.get("name") != null) {
568 trk.attr.put("name", w.get("name"));
569 }
570
571 ArrayList<WayPoint> trkseg = null;
572 for (Node n : w.getNodes()) {
573 if (!n.isUsable()) {
574 trkseg = null;
575 continue;
576 }
577 if (trkseg == null) {
578 trkseg = new ArrayList<WayPoint>();
579 trk.trackSegs.add(trkseg);
580 }
581 if (!n.isTagged()) {
582 doneNodes.add(n);
583 }
584 WayPoint wpt = new WayPoint(n.getCoor());
585 if (!n.isTimestampEmpty())
586 {
587 wpt.attr.put("time", DateUtils.fromDate(n.getTimestamp()));
588 wpt.setTime();
589 }
590 trkseg.add(wpt);
591 }
592 }
593
594 // what is this loop meant to do? it creates waypoints but never
595 // records them?
596 for (Node n : data.nodes) {
597 if (n.incomplete || n.isDeleted() || doneNodes.contains(n)) {
598 continue;
599 }
600 WayPoint wpt = new WayPoint(n.getCoor());
601 if (!n.isTimestampEmpty()) {
602 wpt.attr.put("time", DateUtils.fromDate(n.getTimestamp()));
603 wpt.setTime();
604 }
605 String name = n.get("name");
606 if (name != null) {
607 wpt.attr.put("name", name);
608 }
609 }
610 return gpxData;
611 }
612
613 public GpxData toGpxData() {
614 return toGpxData(data, getAssociatedFile());
615 }
616
617 public class ConvertToGpxLayerAction extends AbstractAction {
618 public ConvertToGpxLayerAction() {
619 super(tr("Convert to GPX layer"), ImageProvider.get("converttogpx"));
620 }
621 public void actionPerformed(ActionEvent e) {
622 Main.main.addLayer(new GpxLayer(toGpxData(), tr("Converted from: {0}", getName())));
623 Main.main.removeLayer(OsmDataLayer.this);
624 }
625 }
626
627 public boolean containsPoint(LatLon coor) {
628 // we'll assume that if this has no data sources
629 // that it also has no borders
630 if (this.data.dataSources.isEmpty())
631 return true;
632
633 boolean layer_bounds_point = false;
634 for (DataSource src : this.data.dataSources) {
635 if (src.bounds.contains(coor)) {
636 layer_bounds_point = true;
637 break;
638 }
639 }
640 return layer_bounds_point;
641 }
642
643 /**
644 * replies the set of conflicts currently managed in this layer
645 *
646 * @return the set of conflicts currently managed in this layer
647 */
648 public ConflictCollection getConflicts() {
649 return conflicts;
650 }
651
652 /**
653 * Replies true if the data managed by this layer needs to be uploaded to
654 * the server because it contains at least one modified primitive.
655 *
656 * @return true if the data managed by this layer needs to be uploaded to
657 * the server because it contains at least one modified primitive; false,
658 * otherwise
659 */
660 public boolean requiresUploadToServer() {
661 return requiresUploadToServer;
662 }
663
664 /**
665 * Replies true if the data managed by this layer needs to be saved to
666 * a file. Only replies true if a file is assigned to this layer and
667 * if the data managed by this layer has been modified since the last
668 * save operation to the file.
669 *
670 * @return true if the data managed by this layer needs to be saved to
671 * a file
672 */
673 public boolean requiresSaveToFile() {
674 return getAssociatedFile() != null && requiresSaveToFile;
675 }
676
677 /**
678 * Initializes the layer after a successful load of OSM data from a file
679 *
680 */
681 public void onPostLoadFromFile() {
682 setRequiresSaveToFile(false);
683 setRequiresUploadToServer(data.isModified());
684 }
685
686 /**
687 * Initializes the layer after a successful save of OSM data to a file
688 *
689 */
690 public void onPostSaveToFile() {
691 setRequiresSaveToFile(false);
692 setRequiresUploadToServer(data.isModified());
693 }
694
695 /**
696 * Initializes the layer after a successful upload to the server
697 *
698 */
699 public void onPostUploadToServer() {
700 setRequiresUploadToServer(data.isModified());
701 // keep requiresSaveToDisk unchanged
702 }
703}
Note: See TracBrowser for help on using the repository browser.