Vidalia 0.3.1
TorMapWidget.cpp
Go to the documentation of this file.
1/*
2** This file is part of Vidalia, and is subject to the license terms in the
3** LICENSE file, found in the top level directory of this distribution. If you
4** did not receive the LICENSE file with this file, you may obtain it from the
5** Vidalia source package distributed by the Vidalia Project at
6** http://www.torproject.org/projects/vidalia.html. No part of Vidalia,
7** including this file, may be copied, modified, propagated, or distributed
8** except according to the terms described in the LICENSE file.
9*/
10
11/*
12** \file TorMapWidget.cpp
13** \brief Displays Tor servers and circuits on a map of the world
14*/
15
16#include "TorMapWidget.h"
19#include "Vidalia.h"
20
21#include <MarbleModel.h>
22#include <HttpDownloadManager.h>
23
24#include <QStringList>
25
26using namespace Marble;
27
28/** QPens to use for drawing different map elements */
29#define CIRCUIT_NORMAL_PEN QPen(Qt::blue, 2.0)
30#define CIRCUIT_SELECTED_PEN QPen(Qt::green, 3.0)
31
32
33/** Default constructor */
35 : MarbleWidget(parent)
36{
37 setMapThemeId("earth/srtm/srtm.dgml");
38 setShowScaleBar(false);
39 setShowCrosshairs(false);
40 setAnimationsEnabled(true);
41 setCursor(Qt::OpenHandCursor);
42
43 model()->downloadManager()->setDownloadEnabled(false);
44
46 TorMapWidgetPopupMenu *popupMenu = new TorMapWidgetPopupMenu(this);
47
48 connect(handler, SIGNAL(featureClicked(QPoint,Qt::MouseButton)),
49 popupMenu, SLOT(featureClicked(QPoint,Qt::MouseButton)));
50 connect(popupMenu, SIGNAL(displayRouterInfo(QString)),
51 this, SIGNAL(displayRouterInfo(QString)));
52
53 /* We can't call setInputHandler() until MarbleWidget has called its
54 * internal _q_initGui() method, which doesn't happen until a
55 * QTimer::singleShot(0, this, SLOT(_q_initGui())) timer set in its
56 * constructor times out. So force that event to process now. */
57 vApp->processEvents(QEventLoop::ExcludeUserInputEvents
58 | QEventLoop::ExcludeSocketNotifiers);
59
60 setInputHandler(handler);
61}
62
63/** Destructor */
65{
66 clear();
67}
68
69/** Adds a router to the map. */
70void
72{
73 QString kml;
74 qreal lon = geoip.longitude();
75 qreal lat = geoip.latitude();
76 quint64 bw;
77
78 bw = qMin(desc.averageBandwidth(), desc.burstBandwidth());
79 bw = qMin(bw, desc.observedBandwidth());
80
81 kml.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
82 "<kml xmlns=\"http://earth.google.com/kml/2.0\">"
83 "<Document>"
84 " <Style id=\"normalPlacemark\">"
85 " <IconStyle><Icon><href>:/images/icons/placemark-relay.png</href></Icon></IconStyle>"
86 " </Style>"
87 );
88
89 kml.append("<Placemark>");
90 kml.append("<styleUrl>#normalPlacemark</styleUrl>");
91 kml.append(QString("<name>%1</name>").arg(desc.name()));
92 kml.append(QString("<description>%1</description>").arg(desc.id()));
93 kml.append(QString("<role>1</role>"));
94 kml.append(QString("<address>%1</address>").arg(geoip.toString()));
95 kml.append(QString("<CountryNameCode>%1</CountryNameCode>").arg(geoip.country()));
96 kml.append(QString("<pop>%1</pop>").arg(10 * bw));
97 kml.append(QString("<Point>"
98 " <coordinates>%1,%2</coordinates>"
99 "</Point>").arg(lon).arg(lat));
100 kml.append("</Placemark>");
101 kml.append("</Document></kml>");
102
103 QString id = desc.id();
104 addPlacemarkData(kml, id);
105 _routers.insert(id, GeoDataCoordinates(lon, lat, 0.0,
106 GeoDataCoordinates::Degree));
107}
108
109/** Adds a circuit to the map using the given ordered list of router IDs. */
110void
111TorMapWidget::addCircuit(const CircuitId &circid, const QStringList &path)
112{
113 /* XXX: Is it better to do KML LineString-based circuit drawing here,
114 * instead of going with a QPainter-based approach? I gave it a brief
115 * try once but failed. It might be worth looking into harder if we
116 * want to make circuits selectable on the map too.
117 */
118
119 /* It doesn't make sense to draw a path of length less than two */
120 if (path.size() < 2)
121 return;
122
123 if (_circuits.contains(circid)) {
124 /* Extend an existing path */
125 CircuitGeoPath *geoPath = _circuits.value(circid);
126
127 QString router = path.at(path.size()-1);
128 if (_routers.contains(router))
129 geoPath->first.append(_routers.value(router));
130 } else {
131 /* Construct a new path */
132 CircuitGeoPath *geoPath = new CircuitGeoPath();
133 geoPath->second = false; /* initially unselected */
134
135 foreach (QString router, path) {
136 if (_routers.contains(router))
137 geoPath->first.append(_routers.value(router));
138 }
139 geoPath->first.setTessellationFlags(Tessellate | RespectLatitudeCircle);
140 _circuits.insert(circid, geoPath);
141 }
142
143 repaint();
144}
145
146/** Removes a circuit from the map. */
147void
149{
150 CircuitGeoPath *path = _circuits.take(circid);
151 if (path)
152 delete path;
153
154 repaint();
155}
156
157/** Selects and highlights the router on the map. */
158void
160{
161#if 0
162 if (_routers.contains(id)) {
163 QPair<QPointF, bool> *routerPair = _routers.value(id);
164 routerPair->second = true;
165 }
166 repaint();
167#endif
168}
169
170/** Selects and highlights the circuit with the id <b>circid</b>
171 * on the map. */
172void
174{
175 if (_circuits.contains(circid)) {
176 CircuitGeoPath *path = _circuits.value(circid);
177 path->second = true;
178 }
179
180 repaint();
181}
182
183/** Deselects any highlighted routers or circuits */
184void
186{
187#if 0
188 /* Deselect all router points */
189 foreach (QString router, _routers.keys()) {
190 QPair<QPointF,bool> *routerPair = _routers.value(router);
191 routerPair->second = false;
192 }
193#endif
194 /* Deselect all circuit paths */
195 foreach (CircuitGeoPath *path, _circuits.values()) {
196 path->second = false;
197 }
198
199 repaint();
200}
201
202/** Clears the list of routers and removes all the data on the map */
203void
205{
206 foreach (QString id, _routers.keys()) {
207 removePlacemarkKey(id);
208 }
209
210 foreach (CircuitId circid, _circuits.keys()) {
211 CircuitGeoPath *path = _circuits.take(circid);
212 delete path;
213 }
214
215 repaint();
216}
217
218/** Zooms the map to fit entirely within the constraints of the current
219 * viewport size. */
220void
222{
223 int width = size().width();
224 int height = size().height();
225
226 setRadius(qMin(width, height) / 2);
227
228 /* XXX: Calling setRadius() seems to cause Marble to no longer draw the
229 * atmosphere. So, re-enable it. */
230 setShowAtmosphere(true);
231}
232
233/** Zoom to the circuit on the map with the given <b>circid</b>. */
234void
236{
237#if 0
238 if (_circuits.contains(circid)) {
239 QPair<QPainterPath*,bool> *pair = _circuits.value(circid);
240 QRectF rect = ((QPainterPath *)pair->first)->boundingRect();
241 if (!rect.isNull()) {
242 float zoomLevel = 1.0 - qMax(rect.height()/float(MAP_HEIGHT),
243 rect.width()/float(MAP_WIDTH));
244
245 zoom(rect.center().toPoint(), zoomLevel+0.2);
246 }
247 }
248#endif
249}
250
251/** Zooms in on the router with the given <b>id</b>. */
252void
254{
255 if (_routers.contains(id)) {
256 qreal lon, lat;
257 GeoDataCoordinates coords = _routers.value(id);
258 coords.geoCoordinates(lon, lat, GeoDataPoint::Degree);
259
260 zoomView(maximumZoom());
261 centerOn(lon, lat, true);
262 }
263}
264
265/** Paints the current circuits and streams on the image. */
266void
267TorMapWidget::customPaint(GeoPainter *painter)
268{
269 bool selected = false;
270
271 painter->autoMapQuality();
272 painter->setPen(CIRCUIT_NORMAL_PEN);
273
274 foreach (CircuitGeoPath *path, _circuits.values()) {
275 if (! path->second && selected) {
276 painter->setPen(CIRCUIT_NORMAL_PEN);
277 selected = false;
278 } else if (path->second && ! selected) {
279 painter->setPen(CIRCUIT_SELECTED_PEN);
280 selected = true;
281 }
282 painter->drawPolyline(path->first);
283 }
284}
285
QString CircuitId
Definition: Circuit.h:24
stop errmsg connect(const QHostAddress &address, quint16 port)
#define MAP_HEIGHT
#define MAP_WIDTH
#define CIRCUIT_SELECTED_PEN
#define CIRCUIT_NORMAL_PEN
QPair< Marble::GeoDataLineString, bool > CircuitGeoPath
Definition: TorMapWidget.h:34
#define vApp
Definition: Vidalia.h:37
float longitude() const
Definition: GeoIpRecord.h:54
QString country() const
Definition: GeoIpRecord.h:69
QString toString() const
Definition: GeoIpRecord.cpp:64
float latitude() const
Definition: GeoIpRecord.h:49
quint64 observedBandwidth() const
quint64 averageBandwidth() const
quint64 burstBandwidth() const
QString id() const
QString name() const
void removeCircuit(const CircuitId &circid)
void addRouter(const RouterDescriptor &desc, const GeoIpRecord &geoip)
void zoomToRouter(const QString &id)
void selectCircuit(const CircuitId &circid)
virtual void customPaint(Marble::GeoPainter *painter)
void displayRouterInfo(const QString &id)
QHash< CircuitId, CircuitGeoPath * > _circuits
Definition: TorMapWidget.h:84
void deselectAll()
void zoomToCircuit(const CircuitId &circid)
QHash< QString, Marble::GeoDataCoordinates > _routers
Definition: TorMapWidget.h:82
void selectRouter(const QString &id)
void addCircuit(const CircuitId &circid, const QStringList &path)
TorMapWidget(QWidget *parent=0)