TorMapWidget.cpp

Go to the documentation of this file.
00001 /*
00002 **  This file is part of Vidalia, and is subject to the license terms in the
00003 **  LICENSE file, found in the top level directory of this distribution. If you
00004 **  did not receive the LICENSE file with this file, you may obtain it from the
00005 **  Vidalia source package distributed by the Vidalia Project at
00006 **  http://www.vidalia-project.net/. No part of Vidalia, including this file,
00007 **  may be copied, modified, propagated, or distributed except according to the
00008 **  terms described in the LICENSE file.
00009 */
00010 
00011 /*
00012 ** \file TorMapWidget.cpp
00013 ** \version $Id: TorMapWidget.cpp 4020 2009-08-09 19:10:45Z edmanm $
00014 ** \brief Displays Tor servers and circuits on a map of the world
00015 */
00016 
00017 #include "TorMapWidget.h"
00018 #include "TorMapWidgetInputHandler.h"
00019 #include "TorMapWidgetPopupMenu.h"
00020 #include "Vidalia.h"
00021 
00022 #include <MarbleModel.h>
00023 #include <HttpDownloadManager.h>
00024 
00025 #include <QStringList>
00026 
00027 using namespace Marble;
00028 
00029 /** QPens to use for drawing different map elements */
00030 #define CIRCUIT_NORMAL_PEN      QPen(Qt::blue,  2.0)
00031 #define CIRCUIT_SELECTED_PEN    QPen(Qt::green, 3.0)
00032 
00033 
00034 /** Default constructor */
00035 TorMapWidget::TorMapWidget(QWidget *parent)
00036   : MarbleWidget(parent)
00037 {
00038   setMapThemeId("earth/srtm/srtm.dgml");
00039   setShowScaleBar(false);
00040   setShowCrosshairs(false);
00041   setAnimationsEnabled(true);
00042   setCursor(Qt::OpenHandCursor);
00043 
00044   model()->downloadManager()->setDownloadEnabled(false);
00045 
00046   TorMapWidgetInputHandler *handler = new TorMapWidgetInputHandler();
00047   TorMapWidgetPopupMenu *popupMenu  = new TorMapWidgetPopupMenu(this);
00048 
00049   connect(handler, SIGNAL(featureClicked(QPoint,Qt::MouseButton)),
00050           popupMenu, SLOT(featureClicked(QPoint,Qt::MouseButton)));
00051   connect(popupMenu, SIGNAL(displayRouterInfo(QString)),
00052           this, SIGNAL(displayRouterInfo(QString)));
00053 
00054   /* We can't call setInputHandler() until MarbleWidget has called its
00055    * internal _q_initGui() method, which doesn't happen until a
00056    * QTimer::singleShot(0, this, SLOT(_q_initGui())) timer set in its
00057    * constructor times out. So force that event to process now. */ 
00058   vApp->processEvents(QEventLoop::ExcludeUserInputEvents
00059                         | QEventLoop::ExcludeSocketNotifiers);
00060 
00061   setInputHandler(handler);
00062 }
00063 
00064 /** Destructor */
00065 TorMapWidget::~TorMapWidget()
00066 {
00067   clear();
00068 }
00069 
00070 /** Adds a router to the map. */
00071 void
00072 TorMapWidget::addRouter(const RouterDescriptor &desc, const GeoIp &geoip)
00073 {
00074   QString kml;
00075   qreal lon = geoip.longitude();
00076   qreal lat = geoip.latitude();
00077   quint64 bw;
00078   
00079   bw = qMin(desc.averageBandwidth(), desc.burstBandwidth());
00080   bw = qMin(bw, desc.observedBandwidth());
00081 
00082   kml.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
00083              "<kml xmlns=\"http://earth.google.com/kml/2.0\">"
00084              "<Document>"
00085              "  <Style id=\"normalPlacemark\">"
00086              "    <IconStyle><Icon><href>:/images/icons/placemark-relay.png</href></Icon></IconStyle>"
00087              "  </Style>"
00088              );
00089 
00090   kml.append("<Placemark>");
00091   kml.append("<styleUrl>#normalPlacemark</styleUrl>");
00092   kml.append(QString("<name>%1</name>").arg(desc.name()));
00093   kml.append(QString("<description>%1</description>").arg(desc.id()));
00094   kml.append(QString("<role>1</role>"));
00095   kml.append(QString("<address>%1</address>").arg(geoip.toString()));
00096   kml.append(QString("<CountryNameCode>%1</CountryNameCode>").arg(geoip.country()));
00097   kml.append(QString("<pop>%1</pop>").arg(10 * bw));
00098   kml.append(QString("<Point>"
00099                      "  <coordinates>%1,%2</coordinates>"
00100                      "</Point>").arg(lon).arg(lat));
00101   kml.append("</Placemark>");
00102   kml.append("</Document></kml>");
00103 
00104   QString id = desc.id();
00105   addPlacemarkData(kml, id);
00106   _routers.insert(id, GeoDataCoordinates(lon, lat, 0.0,
00107                                          GeoDataCoordinates::Degree));
00108 }
00109 
00110 /** Adds a circuit to the map using the given ordered list of router IDs. */
00111 void
00112 TorMapWidget::addCircuit(const CircuitId &circid, const QStringList &path)
00113 {
00114   /* XXX: Is it better to do KML LineString-based circuit drawing here,
00115    *      instead of going with a QPainter-based approach? I gave it a brief
00116    *      try once but failed. It might be worth looking into harder if we
00117    *      want to make circuits selectable on the map too.
00118    */
00119 
00120   /* It doesn't make sense to draw a path of length less than two */
00121   if (path.size() < 2)
00122     return;
00123 
00124   if (_circuits.contains(circid)) {
00125     /* Extend an existing path */
00126     CircuitGeoPath *geoPath = _circuits.value(circid);
00127 
00128     QString router = path.at(path.size()-1);
00129     if (_routers.contains(router))
00130       geoPath->first.append(_routers.value(router));
00131   } else {
00132     /* Construct a new path */
00133     CircuitGeoPath *geoPath = new CircuitGeoPath();
00134     geoPath->second = false; /* initially unselected */
00135 
00136     foreach (QString router, path) {
00137       if (_routers.contains(router))
00138         geoPath->first.append(_routers.value(router));
00139     }
00140     geoPath->first.setTessellationFlags(Tessellate | RespectLatitudeCircle);
00141     _circuits.insert(circid, geoPath);
00142   }
00143 
00144   repaint();
00145 }
00146 
00147 /** Removes a circuit from the map. */
00148 void
00149 TorMapWidget::removeCircuit(const CircuitId &circid)
00150 {
00151   CircuitGeoPath *path = _circuits.take(circid);
00152   if (path)
00153     delete path;
00154 
00155   repaint();
00156 }
00157 
00158 /** Selects and highlights the router on the map. */
00159 void
00160 TorMapWidget::selectRouter(const QString &id)
00161 {
00162 #if 0
00163   if (_routers.contains(id)) {
00164     QPair<QPointF, bool> *routerPair = _routers.value(id);
00165     routerPair->second = true;
00166   }
00167   repaint();
00168 #endif
00169 }
00170 
00171 /** Selects and highlights the circuit with the id <b>circid</b> 
00172  * on the map. */
00173 void
00174 TorMapWidget::selectCircuit(const CircuitId &circid)
00175 {
00176   if (_circuits.contains(circid)) {
00177     CircuitGeoPath *path = _circuits.value(circid);
00178     path->second = true;
00179   }
00180 
00181   repaint();
00182 }
00183 
00184 /** Deselects any highlighted routers or circuits */
00185 void
00186 TorMapWidget::deselectAll()
00187 {
00188 #if 0
00189   /* Deselect all router points */
00190   foreach (QString router, _routers.keys()) {
00191     QPair<QPointF,bool> *routerPair = _routers.value(router);
00192     routerPair->second = false;
00193   }
00194 #endif
00195   /* Deselect all circuit paths */
00196   foreach (CircuitGeoPath *path, _circuits.values()) {
00197     path->second = false;
00198   }
00199 
00200   repaint();
00201 }
00202 
00203 /** Clears the list of routers and removes all the data on the map */
00204 void
00205 TorMapWidget::clear()
00206 {
00207   foreach (QString id, _routers.keys()) {
00208     removePlacemarkKey(id);
00209   }
00210 
00211   foreach (CircuitId circid, _circuits.keys()) {
00212     CircuitGeoPath *path = _circuits.take(circid);
00213     delete path;
00214   }
00215 
00216   repaint();
00217 }
00218  
00219 /** Zooms the map to fit entirely within the constraints of the current
00220  * viewport size. */
00221 void
00222 TorMapWidget::zoomToFit()
00223 {
00224   int width  = size().width();
00225   int height = size().height();
00226 
00227   setRadius(qMin(width, height) / 2);
00228 
00229   /* XXX: Calling setRadius() seems to cause Marble to no longer draw the
00230    *      atmosphere. So, re-enable it. */
00231   setShowAtmosphere(true);
00232 }
00233 
00234 /** Zoom to the circuit on the map with the given <b>circid</b>. */
00235 void
00236 TorMapWidget::zoomToCircuit(const CircuitId &circid)
00237 {
00238 #if 0
00239   if (_circuits.contains(circid)) {
00240     QPair<QPainterPath*,bool> *pair = _circuits.value(circid);
00241     QRectF rect = ((QPainterPath *)pair->first)->boundingRect();
00242     if (!rect.isNull()) {
00243       float zoomLevel = 1.0 - qMax(rect.height()/float(MAP_HEIGHT),
00244                                    rect.width()/float(MAP_WIDTH));
00245 
00246       zoom(rect.center().toPoint(), zoomLevel+0.2);
00247     }
00248   }
00249 #endif
00250 }
00251 
00252 /** Zooms in on the router with the given <b>id</b>. */
00253 void
00254 TorMapWidget::zoomToRouter(const QString &id)
00255 {
00256   if (_routers.contains(id)) {
00257     qreal lon, lat;
00258     GeoDataCoordinates coords = _routers.value(id);
00259     coords.geoCoordinates(lon, lat, GeoDataPoint::Degree);
00260 
00261     zoomView(maximumZoom());
00262     centerOn(lon, lat, true);
00263   }
00264 }
00265 
00266 /** Paints the current circuits and streams on the image. */
00267 void
00268 TorMapWidget::customPaint(GeoPainter *painter)
00269 {
00270   bool selected = false;
00271 
00272   painter->autoMapQuality();
00273   painter->setPen(CIRCUIT_NORMAL_PEN);
00274 
00275   foreach (CircuitGeoPath *path, _circuits.values()) {
00276     if (! path->second && selected) {
00277       painter->setPen(CIRCUIT_NORMAL_PEN);
00278       selected = false;
00279     } else if (path->second && ! selected) {
00280       painter->setPen(CIRCUIT_SELECTED_PEN);
00281       selected = true;
00282     }
00283     painter->drawPolyline(path->first);
00284   }
00285 }
00286 

Generated on Mon Aug 30 19:14:02 2010 for Vidalia by  doxygen 1.5.9