Index: src/org/openstreetmap/josm/data/osm/visitor/MapPaintVisitor.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/visitor/MapPaintVisitor.java	(revision 2340)
+++ src/org/openstreetmap/josm/data/osm/visitor/MapPaintVisitor.java	(working copy)
@@ -26,6 +26,8 @@
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.TreeSet;
+import java.util.Comparator;
 
 import javax.swing.ImageIcon;
 
@@ -52,7 +54,6 @@
 public class MapPaintVisitor extends SimplePaintVisitor {
     protected boolean useRealWidth;
     protected boolean zoomLevelDisplay;
-    protected int fillAreas;
     protected boolean drawMultipolygon;
     protected boolean drawRestriction;
     protected boolean leftHandTraffic;
@@ -73,7 +74,6 @@
     protected double circum;
     protected double dist;
     protected Collection<String> regionalNameOrder;
-    protected Boolean selectedCall;
     protected Boolean useStyleCache;
     private static int paintid = 0;
     private static int viewid = 0;
@@ -139,7 +139,8 @@
      * @param n The node to draw.
      */
     @Override
-    public void visit(Node n) {
+    public void visit(Node n) {}
+    public void drawNode(Node n) {
         /* check, if the node is visible at all */
         if((n.getEastNorth().east()  > maxEN.east() ) ||
                 (n.getEastNorth().north() > maxEN.north()) ||
@@ -175,8 +176,9 @@
      * Draw a line for all segments, according to tags.
      * @param w The way to draw.
      */
-    @Override
-    public void visit(Way w) {
+    @Override 
+    public void visit(Way w) {}
+    public void drawWay(Way w, int fillAreas) {
         if(w.getNodesCount() < 2)
         {
             w.mappaintVisibleCode = viewid;
@@ -390,9 +392,9 @@
         displaySegments();
     }
 
-    public Collection<Way> joinWays(Collection<Way> join, OsmPrimitive errs)
+    public Collection<PolyData> joinWays(Collection<Way> join, OsmPrimitive errs)
     {
-        Collection<Way> res = new LinkedList<Way>();
+        Collection<PolyData> res = new LinkedList<PolyData>();
         Object[] joinArray = join.toArray();
         int left = join.size();
         while(left != 0)
@@ -479,11 +481,7 @@
             {
                 w = new Way(w);
                 w.setNodes(n);
-                if (selected) {
-                    data.addSelected(Collections.singleton(w),false /* don't notify listeners */);
-                } else {
-                    data.clearSelection(w);
-                }
+                // Do not mess with the DataSet's contents here.
             }
             if(!w.isClosed())
             {
@@ -493,7 +491,9 @@
                             w.getDisplayName(DefaultNameFormatter.getInstance())), true);
                 }
             }
-            res.add(w);
+            PolyData pd = new PolyData(w);
+            pd.selected = selected;
+            res.add(pd);
         } /* while(left != 0) */
 
         return res;
@@ -504,6 +504,7 @@
     {
         if(osm instanceof Way)
         {
+            Way w = (Way)osm;
             if(style instanceof AreaElemStyle)
             {
                 Way way = (Way)osm;
@@ -530,28 +531,12 @@
         osm.mappaintDrawnCode = paintid;
     }
 
-    @Override
-    public void visit(Relation r) {
-
+    public void visit(Relation r) {};
+    public void paintUnselectedRelation(Relation r) {
         r.mappaintVisibleCode = 0;
 
-        /* TODO: is it possible to do this like the nodes/ways code? */
-        //if(profilerOmitDraw)
-        //    return;
-
-        if(selectedCall)
+        if (drawMultipolygon && "multipolygon".equals(r.get("type")))
         {
-            for (RelationMember m : r.getMembers())
-            {
-                if (m.isNode() && !m.getMember().incomplete && !m.getMember().isDeleted() && !m.getMember().isFiltered())
-                {
-                    drawSelectedMember(m.getMember(), styles != null ? getPrimitiveStyle(m.getMember()) : null, true, true);
-                }
-            }
-            return;
-        }
-        else if (drawMultipolygon && "multipolygon".equals(r.get("type")))
-        {
             if(drawMultipolygon(r))
                 return;
         }
@@ -564,7 +549,7 @@
         {
             for (RelationMember m : r.getMembers())
             {
-                if (m.isWay() && !m.getMember().incomplete && !m.getMember().isDeleted()) /* nodes drawn on second call */
+                if (m.isWay() && drawable(m.getMember()))
                 {
                     drawSelectedMember(m.getMember(), styles != null ? getPrimitiveStyle(m.getMember())
                             : null, true, true);
@@ -844,6 +829,99 @@
         }
     }
 
+    class PolyData {
+        public Polygon poly = new Polygon();
+        public Way way;
+        public boolean selected = false;
+        private Point p = null;
+        private Collection<Polygon> inner = null;
+        PolyData(Way w)
+        {
+            way = w;
+            for (Node n : w.getNodes())
+            {
+                p = nc.getPoint(n);
+                poly.addPoint(p.x,p.y);
+            }
+        }
+        public int contains(Polygon p)
+        {
+            int contains = p.npoints;
+            for(int i = 0; i < p.npoints; ++i)
+            {
+                if(poly.contains(p.xpoints[i],p.ypoints[i])) {
+                    --contains;
+                }
+            }
+            if(contains == 0) return 1;
+            if(contains == p.npoints) return 0;
+            return 2;
+        }
+        public void addInner(Polygon p)
+        {
+            if(inner == null) {
+                inner = new ArrayList<Polygon>();
+            }
+            inner.add(p);
+        }
+        public boolean isClosed()
+        {
+            return (poly.npoints >= 3
+                    && poly.xpoints[0] == poly.xpoints[poly.npoints-1]
+                                                       && poly.ypoints[0] == poly.ypoints[poly.npoints-1]);
+        }
+        public Polygon get()
+        {
+            if(inner != null)
+            {
+                for (Polygon pp : inner)
+                {
+                    for(int i = 0; i < pp.npoints; ++i) {
+                        poly.addPoint(pp.xpoints[i],pp.ypoints[i]);
+                    }
+                    poly.addPoint(p.x,p.y);
+                }
+                inner = null;
+            }
+            return poly;
+        }
+    }
+    void addInnerToOuters(Relation r, boolean incomplete, PolyData pdInner, LinkedList<PolyData> outerPolygons)
+    {
+        Way wInner = pdInner.way;
+        if(wInner != null && !wInner.isClosed())
+        {
+            Point pInner = nc.getPoint(wInner.getNode(0));
+            pdInner.poly.addPoint(pInner.x,pInner.y);
+        }
+        PolyData o = null;
+        for (PolyData pdOuter : outerPolygons)
+        {
+            Integer c = pdOuter.contains(pdInner.poly);
+            if(c >= 1)
+            {
+                if(c > 1 && pdOuter.way != null && pdOuter.way.isClosed())
+                {
+                    r.putError(tr("Intersection between ways ''{0}'' and ''{1}''.",
+                            pdOuter.way.getDisplayName(DefaultNameFormatter.getInstance()), wInner.getDisplayName(DefaultNameFormatter.getInstance())), true);
+                }
+                if(o == null || o.contains(pdOuter.poly) > 0) {
+                    o = pdOuter;
+                }
+            }
+        }
+        if(o == null)
+        {
+            if(!incomplete)
+            {
+                r.putError(tr("Inner way ''{0}'' is outside.",
+                        wInner.getDisplayName(DefaultNameFormatter.getInstance())), true);
+            }
+            o = outerPolygons.get(0);
+        }
+        o.addInner(pdInner.poly);
+    }
+
     public Boolean drawMultipolygon(Relation r) {
         Collection<Way> inner = new LinkedList<Way>();
         Collection<Way> outer = new LinkedList<Way>();
@@ -861,13 +939,10 @@
                         m.getMember().getDisplayName(DefaultNameFormatter.getInstance())), true);
             } else if(m.getMember().incomplete) {
                 incomplete = true;
-            } else
-            {
-                if(m.isWay())
-                {
+            } else {
+                if(m.isWay()) {
                     Way w = m.getWay();
-                    if(w.getNodesCount() < 2)
-                    {
+                    if(w.getNodesCount() < 2) {
                         r.putError(tr("Way ''{0}'' with less than two points.",
                                 w.getDisplayName(DefaultNameFormatter.getInstance())), true);
                     }
@@ -875,8 +950,7 @@
                         inner.add(w);
                     } else if("outer".equals(m.getRole())) {
                         outer.add(w);
-                    } else
-                    {
+                    } else {
                         r.putError(tr("No useful role ''{0}'' for Way ''{1}''.",
                                 m.getRole(), w.getDisplayName(DefaultNameFormatter.getInstance())), true);
                         if(!m.hasRole()) {
@@ -911,7 +985,8 @@
         {
             Boolean zoomok = isZoomOk(wayStyle);
             Boolean visible = false;
-            Collection<Way> join = new LinkedList<Way>();
+            Collection<Way> outerjoin = new LinkedList<Way>();
+            Collection<Way> innerjoin = new LinkedList<Way>();
 
             drawn = true;
             for (Way w : outer)
@@ -919,152 +994,48 @@
                 if(w.isClosed()) {
                     outerclosed.add(w);
                 } else {
-                    join.add(w);
+                    outerjoin.add(w);
                 }
             }
-            if(join.size() != 0)
-            {
-                for(Way w : joinWays(join, incomplete ? null : r)) {
-                    outerclosed.add(w);
-                }
-            }
-
-            join.clear();
             for (Way w : inner)
             {
                 if(w.isClosed()) {
                     innerclosed.add(w);
                 } else {
-                    join.add(w);
+                    innerjoin.add(w);
                 }
             }
-            if(join.size() != 0)
+            if(outerclosed.size() == 0 && outerjoin.size() == 0)
             {
-                for(Way w : joinWays(join, incomplete ? null : r)) {
-                    innerclosed.add(w);
-                }
-            }
-
-            if(outerclosed.size() == 0)
-            {
                 r.putError(tr("No outer way for multipolygon ''{0}''.",
                         r.getDisplayName(DefaultNameFormatter.getInstance())), true);
                 visible = true; /* prevent killing remaining ways */
             }
             else if(zoomok)
             {
-                class PolyData {
-                    public Polygon poly = new Polygon();
-                    public Way way;
-                    private Point p = null;
-                    private Collection<Polygon> inner = null;
-                    PolyData(Way w)
-                    {
-                        way = w;
-                        for (Node n : w.getNodes())
-                        {
-                            p = nc.getPoint(n);
-                            poly.addPoint(p.x,p.y);
-                        }
-                    }
-                    public int contains(Polygon p)
-                    {
-                        int contains = p.npoints;
-                        for(int i = 0; i < p.npoints; ++i)
-                        {
-                            if(poly.contains(p.xpoints[i],p.ypoints[i])) {
-                                --contains;
-                            }
-                        }
-                        if(contains == 0) return 1;
-                        if(contains == p.npoints) return 0;
-                        return 2;
-                    }
-                    public void addInner(Polygon p)
-                    {
-                        if(inner == null) {
-                            inner = new ArrayList<Polygon>();
-                        }
-                        inner.add(p);
-                    }
-                    public boolean isClosed()
-                    {
-                        return (poly.npoints >= 3
-                                && poly.xpoints[0] == poly.xpoints[poly.npoints-1]
-                                                                   && poly.ypoints[0] == poly.ypoints[poly.npoints-1]);
-                    }
-                    public Polygon get()
-                    {
-                        if(inner != null)
-                        {
-                            for (Polygon pp : inner)
-                            {
-                                for(int i = 0; i < pp.npoints; ++i) {
-                                    poly.addPoint(pp.xpoints[i],pp.ypoints[i]);
-                                }
-                                poly.addPoint(p.x,p.y);
-                            }
-                            inner = null;
-                        }
-                        return poly;
-                    }
+                LinkedList<PolyData> outerPoly = new LinkedList<PolyData>();
+                for (Way w : outerclosed) {
+                    outerPoly.add(new PolyData(w));
                 }
-                LinkedList<PolyData> poly = new LinkedList<PolyData>();
-                for (Way w : outerclosed)
-                {
-                    poly.add(new PolyData(w));
-                }
+                outerPoly.addAll(joinWays(outerjoin, incomplete ? null : r));
                 for (Way wInner : innerclosed)
                 {
-                    Polygon polygon = new Polygon();
-
-                    for (Node n : wInner.getNodes())
-                    {
-                        Point pInner = nc.getPoint(n);
-                        polygon.addPoint(pInner.x,pInner.y);
-                    }
-                    if(!wInner.isClosed())
-                    {
-                        Point pInner = nc.getPoint(wInner.getNode(0));
-                        polygon.addPoint(pInner.x,pInner.y);
-                    }
-                    PolyData o = null;
-                    for (PolyData pd : poly)
-                    {
-                        Integer c = pd.contains(polygon);
-                        if(c >= 1)
-                        {
-                            if(c > 1 && pd.way.isClosed())
-                            {
-                                r.putError(tr("Intersection between ways ''{0}'' and ''{1}''.",
-                                        pd.way.getDisplayName(DefaultNameFormatter.getInstance()), wInner.getDisplayName(DefaultNameFormatter.getInstance())), true);
-                            }
-                            if(o == null || o.contains(pd.poly) > 0) {
-                                o = pd;
-                            }
-                        }
-                    }
-                    if(o == null)
-                    {
-                        if(!incomplete)
-                        {
-                            r.putError(tr("Inner way ''{0}'' is outside.",
-                                    wInner.getDisplayName(DefaultNameFormatter.getInstance())), true);
-                        }
-                        o = poly.get(0);
-                    }
-                    o.addInner(polygon);
+                    PolyData pdInner = new PolyData(wInner);
+                    // incomplete is probably redundant
+                    addInnerToOuters(r, incomplete, pdInner, outerPoly);
                 }
+                for (PolyData pdInner : joinWays(innerjoin, incomplete ? null : r)) {
+                    addInnerToOuters(r, incomplete, pdInner, outerPoly);
+                }
                 AreaElemStyle areaStyle = (AreaElemStyle)wayStyle;
-                for (PolyData pd : poly)
-                {
+                for (PolyData pd : outerPoly) {
                     Polygon p = pd.get();
-                    if(isPolygonVisible(p))
-                    {
-                        drawAreaPolygon(p, (data.isSelected(pd.way) || data.isSelected(r)) ? selectedColor
-                                : areaStyle.color);
-                        visible = true;
-                    }
+                    if(!isPolygonVisible(p))
+                        continue;
+
+                    boolean selected = pd.selected || data.isSelected(pd.way) || data.isSelected(r);
+                    drawAreaPolygon(p, selected ? selectedColor : areaStyle.color);
+                    visible = true;
                 }
             }
             if(!visible) /* nothing visible, so disable relation and all its ways */
@@ -1083,6 +1054,8 @@
                 ElemStyle innerStyle = getPrimitiveStyle(wInner);
                 if(innerStyle == null)
                 {
+                    if (data.isSelected(wInner))
+                        continue;
                     if(zoomok && (wInner.mappaintDrawnCode != paintid
                             || outer.size() == 0))
                     {
@@ -1114,6 +1087,9 @@
                 ElemStyle outerStyle = getPrimitiveStyle(wOuter);
                 if(outerStyle == null)
                 {
+                    // Selected ways are drawn at the very end
+                    if (data.isSelected(wOuter))
+                        continue;
                     if(zoomok)
                     {
                         drawWay(wOuter, ((AreaElemStyle)wayStyle).line,
@@ -1414,6 +1390,11 @@
         }
     }
 
+    boolean drawable(OsmPrimitive osm)
+    {
+        return !osm.isDeleted() && !osm.isFiltered() && !osm.incomplete;
+    }
+
     @Override
     public void getColors()
     {
@@ -1425,6 +1406,23 @@
 
     DataSet data;
 
+    <T extends OsmPrimitive> Collection<T> selectedLast(final DataSet data, Collection <T> prims) {
+        ArrayList<T> sorted = new ArrayList<T>(prims);
+        Collections.sort(sorted, 
+            new Comparator<T>() {
+                public int compare(T o1, T o2) {
+                    boolean s1 = data.isSelected(o1);
+                    boolean s2 = data.isSelected(o2);
+                    if (s1 && !s2)
+                        return 1;
+                    if (!s1 && s2)
+                        return -1;
+                    return o1.compareTo(o2);
+                }
+            });
+        return sorted;
+    }
+
     /* Shows areas before non-areas */
     @Override
     public void visitAll(DataSet data, Boolean virtual) {
@@ -1433,7 +1431,7 @@
         //profilerOmitDraw = Main.pref.getBoolean("mappaint.profiler.omitdraw",false);
 
         useStyleCache = Main.pref.getBoolean("mappaint.cache",true);
-        fillAreas = Main.pref.getInteger("mappaint.fillareas", 10000000);
+        int fillAreas = Main.pref.getInteger("mappaint.fillareas", 10000000);
         fillAlpha = Math.min(255, Math.max(0, Integer.valueOf(Main.pref.getInteger("mappaint.fillalpha", 50))));
         showNames = Main.pref.getInteger("mappaint.shownames", 10000000);
         showIcons = Main.pref.getInteger("mappaint.showicons", 10000000);
@@ -1468,7 +1466,6 @@
         maxEN = nc.getEastNorth(nc.getWidth()-1,0);
 
 
-        selectedCall = false;
         ++paintid;
         viewid = nc.getViewID();
 
@@ -1491,9 +1488,9 @@
             //    profilerN = 0;
             for (final Relation osm : data.relations)
             {
-                if(!osm.isDeleted() && !osm.isFiltered() && !osm.incomplete && osm.mappaintVisibleCode != viewid)
+                if(drawable(osm) && osm.mappaintVisibleCode != viewid)
                 {
-                    osm.visit(this);
+                    paintUnselectedRelation(osm);
                     //            profilerN++;
                 }
             }
@@ -1506,18 +1503,17 @@
 
             /*** AREAS ***/
             //    profilerN = 0;
-            for (final Way osm : data.ways)
-            {
-                if (!osm.incomplete && !osm.isDeleted() && !osm.isFiltered()
+            for (final Way osm : selectedLast(data, data.ways)) {
+                if (drawable(osm)
                         && osm.mappaintVisibleCode != viewid && osm.mappaintDrawnCode != paintid)
                 {
                     if(isPrimitiveArea(osm) && osm.mappaintDrawnAreaCode != paintid)
                     {
-                        osm.visit(this);
+                        drawWay(osm, fillAreas);
                         //                profilerN++;
-                    } else {
+                    }// else {
                         noAreaWays.add(osm);
-                    }
+                    //}
                 }
             }
 
@@ -1530,10 +1526,9 @@
 
             /*** WAYS ***/
             //    profilerN = 0;
-            fillAreas = 0;
-            for (final OsmPrimitive osm : noAreaWays)
+            for (final Way osm : noAreaWays)
             {
-                osm.visit(this);
+                drawWay(osm, 0);
                 //        profilerN++;
             }
 
@@ -1548,11 +1543,11 @@
         {
             /*** WAYS (filling disabled)  ***/
             //    profilerN = 0;
-            for (final OsmPrimitive osm : data.ways)
-                if (!osm.incomplete && !osm.isDeleted() && !osm.isFiltered() && !data.isSelected(osm)
-                        && osm.mappaintVisibleCode != viewid )
+            for (final Way way : data.ways)
+                if (drawable(way) && !data.isSelected(way)
+                        && way.mappaintVisibleCode != viewid )
                 {
-                    osm.visit(this);
+                    drawWay(way, 0);
                     //            profilerN++;
                 }
 
@@ -1565,13 +1560,31 @@
         }
 
         /*** SELECTED  ***/
-        selectedCall = true;
         //profilerN = 0;
         for (final OsmPrimitive osm : data.getSelected()) {
             if (!osm.incomplete && !osm.isDeleted() && !(osm instanceof Node)
-                    && osm.mappaintVisibleCode != viewid && osm.mappaintDrawnCode != paintid)
+                    && osm.mappaintVisibleCode != viewid && osm.mappaintDrawnCode != paintid
+                    )
             {
-                osm.visit(this);
+                osm.visit(new AbstractVisitor() {
+                    public void visit(Way w) {
+                        drawWay(w, 0);
+                    }
+                    public void visit(Node n) {
+                        drawNode(n);
+                    }
+                    public void visit(Relation r) {
+                        /* TODO: is it possible to do this like the nodes/ways code? */
+                        //if(profilerOmitDraw)
+                        //    return;
+                        r.mappaintVisibleCode = 0;
+                        for (RelationMember m : r.getMembers()) {
+                            if (m.isNode() && drawable(m.getMember())) {
+                                drawSelectedMember(m.getMember(), styles != null ? getPrimitiveStyle(m.getMember()) : null, true, true);
+                            }
+                        }
+                    }
+                });
                 //        profilerN++;
             }
         }
@@ -1587,11 +1600,11 @@
 
         /*** NODES ***/
         //profilerN = 0;
-        for (final OsmPrimitive osm : data.nodes)
+        for (final Node osm : data.nodes)
             if (!osm.incomplete && !osm.isDeleted() && (data.isSelected(osm) || !osm.isFiltered())
                     && osm.mappaintVisibleCode != viewid && osm.mappaintDrawnCode != paintid)
             {
-                osm.visit(this);
+                drawNode(osm);
                 //        profilerN++;
             }
 
