Index: applications/editors/josm/plugins/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapLayer.java
===================================================================
--- applications/editors/josm/plugins/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapLayer.java	(revision 24485)
+++ applications/editors/josm/plugins/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapLayer.java	(revision 24486)
@@ -4,4 +4,5 @@
 
 import java.awt.Color;
+import java.awt.Font;
 import java.awt.Graphics;
 import java.awt.Graphics2D;
@@ -104,4 +105,6 @@
     JCheckBoxMenuItem autoZoomPopup;
     Tile showMetadataTile;
+    private Image attrImage;
+    private Font attrFont = Font.decode("Arial-10");
 
     void redraw()
@@ -115,4 +118,14 @@
         int origZoom = currentZoomLevel;
         tileSource = SlippyMapPreferences.getMapSource();
+        boolean requireAttr = tileSource.requiresAttribution();
+        if(requireAttr) {
+            attrImage = tileSource.getAttributionImage();
+            if(attrImage == null) {
+                System.out.println("Attribution image was null.");
+            } else {
+                System.out.println("Got an attribution image " + attrImage.getHeight(this) + "x" + attrImage.getWidth(this));
+            }
+        }
+        
         // The minimum should also take care of integer parsing
         // errors which would leave us with a zoom of -1 otherwise
@@ -817,4 +830,5 @@
             return;
         }
+
         if (lastTopLeft != null && lastBotRight != null && topLeft.equalsEpsilon(lastTopLeft)
                 && botRight.equalsEpsilon(lastBotRight) && bufferImage != null
@@ -908,4 +922,19 @@
             this.paintTileText(ts, t, g, mv, currentZoomLevel, t);
         }
+
+        if (tileSource.requiresAttribution()) {
+            // Draw attribution
+            g.setColor(Color.white);
+            Font font = g.getFont();
+            g.setFont(attrFont);
+            g.drawString(tileSource.getAttributionText(currentZoomLevel, topLeft, botRight), 5 + attrImage.getWidth(this) + 2, mv.getHeight() - 5);
+            g.setFont(font);
+            
+            // Draw attribution logo
+            if(attrImage != null) {
+                g.drawImage(attrImage, 5, mv.getHeight() - attrImage.getHeight(this) - 5, this);
+            }
+        }
+
         oldg.drawImage(bufferImage, 0, 0, null);
 
Index: applications/editors/josm/plugins/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPreferences.java
===================================================================
--- applications/editors/josm/plugins/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPreferences.java	(revision 24485)
+++ applications/editors/josm/plugins/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPreferences.java	(revision 24486)
@@ -3,5 +3,10 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
+import java.awt.Image;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -11,4 +16,13 @@
 import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+import org.xml.sax.helpers.XMLReaderFactory;
 
 /**
@@ -277,6 +291,89 @@
 
     public static class BingAerial extends OsmTileSource.AbstractOsmTileSource {
+        private static String API_KEY = "Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU";
+        private static List<Attribution> attributions;
+        
         public BingAerial() {
             super("Bing Aerial Maps", "http://ecn.t2.tiles.virtualearth.net/tiles/");
+            
+            attributions = loadAttributionText();
+            System.err.println("Added " + attributions.size() + " attributions.");
+        }
+        
+        class Attribution {
+            String attribution;
+            int minZoom;
+            int maxZoom;
+            Bounds bounds;
+        }
+        
+        class AttrHandler extends DefaultHandler {
+
+            private String string;
+            private Attribution curr;
+            private List<Attribution> attributions = new ArrayList<Attribution>();
+            private double southLat;
+            private double northLat;
+            private double eastLon;
+            private double westLon;
+            private boolean inCoverage = false;
+
+            public void startElement(String uri, String stripped, String tagName,
+                    Attributes attrs) throws SAXException {
+                if("ImageryProvider".equals(tagName)) {
+                    curr = new Attribution();
+                } else if("CoverageArea".equals(tagName)) {
+                    inCoverage = true;
+                }
+            }
+
+            public void characters(char[] ch, int start, int length)
+                    throws SAXException {
+                string = new String(ch, start, length);
+            }
+
+            public void endElement(String uri, String stripped, String tagName)
+                    throws SAXException {
+                if("ImageryProvider".equals(tagName)) {
+                    attributions.add(curr);
+                } else if("Attribution".equals(tagName)) {
+                    curr.attribution = string;
+                } else if(inCoverage && "ZoomMin".equals(tagName)) {
+                    curr.minZoom = Integer.parseInt(string);
+                } else if(inCoverage && "ZoomMax".equals(tagName)) {
+                    curr.maxZoom = Integer.parseInt(string);
+                } else if(inCoverage && "SouthLatitude".equals(tagName)) {
+                    southLat = Double.parseDouble(string);
+                } else if(inCoverage && "NorthLatitude".equals(tagName)) {
+                    northLat = Double.parseDouble(string);
+                } else if(inCoverage && "EastLongitude".equals(tagName)) {
+                    eastLon = Double.parseDouble(string);
+                } else if(inCoverage && "WestLongitude".equals(tagName)) {
+                    westLon = Double.parseDouble(string);
+                } else if("BoundingBox".equals(tagName)) {
+                    curr.bounds = new Bounds(northLat, westLon, southLat, eastLon);
+                } else if("CoverageArea".equals(tagName)) {
+                    inCoverage = false;
+                } 
+                string = "";
+            }
+        }
+
+        private List<Attribution> loadAttributionText() {
+            try {
+                URL u = new URL("http://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial/0,0?zl=1&mapVersion=v1&key="+API_KEY+"&include=ImageryProviders&output=xml");
+                InputStream stream = u.openStream();
+                XMLReader parser = XMLReaderFactory.createXMLReader();
+                AttrHandler handler = new AttrHandler();
+                parser.setContentHandler(handler);
+                parser.parse(new InputSource(stream));
+                return handler.attributions;
+            } catch (IOException e) {
+                System.err.println("Could not open Bing aerials attribution metadata.");
+            } catch (SAXException e) {
+                System.err.println("Could not parse Bing aerials attribution metadata.");
+                e.printStackTrace();
+            }
+            return Collections.emptyList();
         }
 
@@ -299,4 +396,31 @@
         public TileUpdate getTileUpdate() {
             return TileUpdate.IfNoneMatch;
+        }
+        
+        public boolean requiresAttribution() {
+            return true;
+        }
+
+        public Image getAttributionImage() {
+            return ImageProvider.get("bing_maps").getImage();
+        }
+        
+        public String getAttributionText(int zoom, LatLon topLeft, LatLon botRight) {
+            Bounds windowBounds = new Bounds(topLeft, botRight);
+            StringBuilder a = new StringBuilder();
+            for (Attribution attr : attributions) {
+                Bounds attrBounds = attr.bounds;
+                if(zoom <= attr.maxZoom && zoom >= attr.minZoom) {
+                    if(windowBounds.getMin().lon() < attrBounds.getMax().lon()
+                            && windowBounds.getMax().lon() > attrBounds.getMin().lon()
+                            && windowBounds.getMax().lat() < attrBounds.getMin().lat()
+                            && windowBounds.getMin().lat() > attrBounds.getMax().lat()) {
+                        a.append(attr.attribution);
+                        a.append(" ");
+                    } else {
+                    }
+                }
+            }
+            return a.toString();
         }
     }
@@ -422,5 +546,5 @@
         sources.add(new OsmTileSource.TilesAtHome());
 	// *PLEASE* do not enable BingAerial until we have legal approval.
-        //sources.add(new BingAerial());
+        sources.add(new BingAerial());
         sources.add(new Coastline());
         sources.add(new FreeMapySkPokus());
