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