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

Last change on this file since 3233 was 3233, checked in by stoecker, 14 years ago

fix minor color issues

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