Vidalia 0.2.10
|
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 4378 2010-08-05 20:28:54Z 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 GeoIpRecord &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