[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[vidalia-svn] r3889: Hopefully fixed the tomfoolery I created. (in vidalia/branches/extension-api/src/vidalia: . NetworkMapPlugin network)
Author: tyree731
Date: 2009-06-25 00:04:58 -0400 (Thu, 25 Jun 2009)
New Revision: 3889
Added:
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/CircuitItem.cpp
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/CircuitItem.h
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/CircuitListWidget.cpp
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/CircuitListWidget.h
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIp.cpp
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIp.h
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpCache.cpp
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpCache.h
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpCacheItem.cpp
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpCacheItem.h
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpRequest.cpp
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpRequest.h
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpResolver.cpp
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpResolver.h
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpResponse.cpp
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpResponse.h
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/NetViewer.cpp
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/NetViewer.h
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/NetViewer.ui
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterDescriptorView.cpp
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterDescriptorView.h
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterInfoDialog.cpp
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterInfoDialog.h
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterInfoDialog.ui
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterListItem.cpp
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterListItem.h
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterListWidget.cpp
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterListWidget.h
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/StreamItem.cpp
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/StreamItem.h
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/TorMapImageView.cpp
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/TorMapImageView.h
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/TorMapWidget.cpp
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/TorMapWidget.h
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/TorMapWidgetInputHandler.cpp
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/TorMapWidgetInputHandler.h
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/TorMapWidgetPopupMenu.cpp
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/TorMapWidgetPopupMenu.h
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/ZImageView.cpp
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/ZImageView.h
Removed:
vidalia/branches/extension-api/src/vidalia/network/CircuitItem.cpp
vidalia/branches/extension-api/src/vidalia/network/CircuitItem.h
vidalia/branches/extension-api/src/vidalia/network/CircuitListWidget.cpp
vidalia/branches/extension-api/src/vidalia/network/CircuitListWidget.h
vidalia/branches/extension-api/src/vidalia/network/GeoIp.cpp
vidalia/branches/extension-api/src/vidalia/network/GeoIp.h
vidalia/branches/extension-api/src/vidalia/network/GeoIpCache.cpp
vidalia/branches/extension-api/src/vidalia/network/GeoIpCache.h
vidalia/branches/extension-api/src/vidalia/network/GeoIpCacheItem.cpp
vidalia/branches/extension-api/src/vidalia/network/GeoIpCacheItem.h
vidalia/branches/extension-api/src/vidalia/network/GeoIpRequest.cpp
vidalia/branches/extension-api/src/vidalia/network/GeoIpRequest.h
vidalia/branches/extension-api/src/vidalia/network/GeoIpResolver.cpp
vidalia/branches/extension-api/src/vidalia/network/GeoIpResolver.h
vidalia/branches/extension-api/src/vidalia/network/GeoIpResponse.cpp
vidalia/branches/extension-api/src/vidalia/network/GeoIpResponse.h
vidalia/branches/extension-api/src/vidalia/network/NetViewer.cpp
vidalia/branches/extension-api/src/vidalia/network/NetViewer.h
vidalia/branches/extension-api/src/vidalia/network/NetViewer.ui
vidalia/branches/extension-api/src/vidalia/network/RouterDescriptorView.cpp
vidalia/branches/extension-api/src/vidalia/network/RouterDescriptorView.h
vidalia/branches/extension-api/src/vidalia/network/RouterInfoDialog.cpp
vidalia/branches/extension-api/src/vidalia/network/RouterInfoDialog.h
vidalia/branches/extension-api/src/vidalia/network/RouterInfoDialog.ui
vidalia/branches/extension-api/src/vidalia/network/RouterListItem.cpp
vidalia/branches/extension-api/src/vidalia/network/RouterListItem.h
vidalia/branches/extension-api/src/vidalia/network/RouterListWidget.cpp
vidalia/branches/extension-api/src/vidalia/network/RouterListWidget.h
vidalia/branches/extension-api/src/vidalia/network/StreamItem.cpp
vidalia/branches/extension-api/src/vidalia/network/StreamItem.h
vidalia/branches/extension-api/src/vidalia/network/TorMapImageView.cpp
vidalia/branches/extension-api/src/vidalia/network/TorMapImageView.h
vidalia/branches/extension-api/src/vidalia/network/TorMapWidget.cpp
vidalia/branches/extension-api/src/vidalia/network/TorMapWidget.h
vidalia/branches/extension-api/src/vidalia/network/TorMapWidgetInputHandler.cpp
vidalia/branches/extension-api/src/vidalia/network/TorMapWidgetInputHandler.h
vidalia/branches/extension-api/src/vidalia/network/TorMapWidgetPopupMenu.cpp
vidalia/branches/extension-api/src/vidalia/network/TorMapWidgetPopupMenu.h
vidalia/branches/extension-api/src/vidalia/network/ZImageView.cpp
vidalia/branches/extension-api/src/vidalia/network/ZImageView.h
Modified:
vidalia/branches/extension-api/src/vidalia/CMakeLists.txt
vidalia/branches/extension-api/src/vidalia/MainWindow.cpp
vidalia/branches/extension-api/src/vidalia/MainWindow.h
vidalia/branches/extension-api/src/vidalia/MainWindow.ui
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/CMakeLists.txt
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/NetworkMapPlugin.cpp
vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/NetworkMapPlugin.h
vidalia/branches/extension-api/src/vidalia/PluginManager.cpp
vidalia/branches/extension-api/src/vidalia/PluginManager.h
vidalia/branches/extension-api/src/vidalia/VidaliaPanel.h
Log:
Hopefully fixed the tomfoolery I created.
Modified: vidalia/branches/extension-api/src/vidalia/CMakeLists.txt
===================================================================
--- vidalia/branches/extension-api/src/vidalia/CMakeLists.txt 2009-06-25 03:57:32 UTC (rev 3888)
+++ vidalia/branches/extension-api/src/vidalia/CMakeLists.txt 2009-06-25 04:04:58 UTC (rev 3889)
@@ -19,7 +19,6 @@
${CMAKE_CURRENT_SOURCE_DIR}/config
${CMAKE_CURRENT_SOURCE_DIR}/help/browser
${CMAKE_CURRENT_SOURCE_DIR}/log
- ${CMAKE_CURRENT_SOURCE_DIR}/network
${CMAKE_CURRENT_SOURCE_DIR}/tray
${MARBLE_INCLUDE_DIR}
)
@@ -152,14 +151,6 @@
)
endif(USE_MINIUPNPC)
-## Dashboard sources
-set(vidalia_SRCS ${vidalia_SRCS}
- Dashboard.cpp
-)
-qt4_wrap_cpp(vidalia_SRCS
- Dashboard.h
-)
-
## Help browser sources
set(vidalia_SRCS ${vidalia_SRCS}
help/browser/HelpBrowser.cpp
@@ -170,14 +161,6 @@
help/browser/HelpTextBrowser.h
)
-## Home Panel sources
-set(vidalia_SRCS ${vidalia_SRCS}
- HomePanel.cpp
-)
-qt4_wrap_cpp(vidalia_SRCS
- HomePanel.h
-)
-
## Message log sources
set(vidalia_SRCS ${vidalia_SRCS}
log/LogFile.cpp
@@ -194,54 +177,6 @@
log/MessageLog.h
)
-## Network map sources
-set(vidalia_SRCS ${vidalia_SRCS}
- network/CircuitItem.cpp
- network/CircuitListWidget.cpp
- network/GeoIpCache.cpp
- network/GeoIpCacheItem.cpp
- network/GeoIp.cpp
- network/GeoIpRequest.cpp
- network/GeoIpResolver.cpp
- network/GeoIpResponse.cpp
- network/NetViewer.cpp
- network/RouterDescriptorView.cpp
- network/RouterInfoDialog.cpp
- network/RouterListItem.cpp
- network/RouterListWidget.cpp
- network/StreamItem.cpp
-)
-qt4_wrap_cpp(vidalia_SRCS
- network/CircuitListWidget.h
- network/GeoIpCache.h
- network/GeoIpResolver.h
- network/NetViewer.h
- network/RouterDescriptorView.h
- network/RouterInfoDialog.h
- network/RouterListWidget.h
-)
-if (USE_MARBLE)
- set(vidalia_SRCS ${vidalia_SRCS}
- network/TorMapWidget.cpp
- network/TorMapWidgetInputHandler.cpp
- network/TorMapWidgetPopupMenu.cpp
- )
- qt4_wrap_cpp(vidalia_SRCS
- network/TorMapWidget.h
- network/TorMapWidgetInputHandler.h
- network/TorMapWidgetPopupMenu.h
- )
-else(USE_MARBLE)
- set(vidalia_SRCS ${vidalia_SRCS}
- network/TorMapImageView.cpp
- network/ZImageView.cpp
- )
- qt4_wrap_cpp(vidalia_SRCS
- network/TorMapImageView.h
- network/ZImageView.h
- )
-endif(USE_MARBLE)
-
## Choose the correct tray icon implementation for the current platform
set(vidalia_SRCS ${vidalia_SRCS} tray/TrayIcon.cpp)
qt4_wrap_cpp(vidalia_SRCS tray/TrayIcon.h)
@@ -275,14 +210,11 @@
VMessageBox.h
HelperProcess.h
ControlPasswordInputDialog.h
- PluginManager.h
)
## Specify all the Qt Designer .ui files
qt4_wrap_ui(vidalia_SRCS
ControlPasswordInputDialog.ui
- Dashboard.ui
- HomePanel.ui
MainWindow.ui
about/AboutDialog.ui
about/LicenseDialog.ui
@@ -298,8 +230,6 @@
config/ServicePage.ui
help/browser/HelpBrowser.ui
log/MessageLog.ui
- network/NetViewer.ui
- network/RouterInfoDialog.ui
)
if (USE_MINIUPNPC)
Modified: vidalia/branches/extension-api/src/vidalia/MainWindow.cpp
===================================================================
--- vidalia/branches/extension-api/src/vidalia/MainWindow.cpp 2009-06-25 03:57:32 UTC (rev 3888)
+++ vidalia/branches/extension-api/src/vidalia/MainWindow.cpp 2009-06-25 04:04:58 UTC (rev 3889)
@@ -114,17 +114,16 @@
/* Create all the dialogs of which we only want one instance */
_messageLog = new MessageLog();
_bandwidthGraph = new BandwidthGraph();
- _dashboard = new Dashboard();
- _homePanel = new HomePanel();
- _netViewer = new NetViewer();
_configDialog = new ConfigDialog();
_menuBar = 0;
connect(_messageLog, SIGNAL(helpRequested(QString)),
this, SLOT(showHelpDialog(QString)));
+#if 0
connect(_netViewer, SIGNAL(helpRequested(QString)),
this, SLOT(showHelpDialog(QString)));
connect(_configDialog, SIGNAL(helpRequested(QString)),
this, SLOT(showHelpDialog(QString)));
+#endif
/* Create the actions that will go in the tray menu */
createActions();
@@ -142,14 +141,6 @@
/* Populate the tab bar with plugins */
populateTabs(_pluginManager->plugins());
-#if 0
- ui.tabMainPanel->addTab(_homePanel, tr("Home Panel"));
- ui.tabMainPanel->addTab(_dashboard, tr("Dashboard"));
- ui.tabMainPanel->addTab(_netViewer, tr("Network Viewer"));
- ui.tabMainPanel->addTab(_messageLog, tr("Message Log"));
-#endif
- ui.tabMainPanel->removeTab(0);
- ui.tabMainPanel->removeTab(0);
/* Create a new TorControl object, used to communicate with Tor */
_torControl = Vidalia::torControl();
@@ -230,11 +221,8 @@
{
_trayIcon.hide();
delete _messageLog;
- delete _dashboard;
- delete _homePanel;
delete _pluginManager;
delete _bandwidthGraph;
- delete _netViewer;
delete _configDialog;
}
@@ -461,9 +449,11 @@
connect(_messageAct, SIGNAL(triggered()),
_messageLog, SLOT(showWindow()));
+#if 0
_networkAct = new QAction(tr("Network Map"), this);
connect(_networkAct, SIGNAL(triggered()),
_netViewer, SLOT(showWindow()));
+#endif
_controlPanelAct = new QAction(tr("Control Panel"), this);
connect(_controlPanelAct, SIGNAL(triggered()), this, SLOT(show()));
@@ -646,7 +636,9 @@
MainWindow::populateTabs(QList<VidaliaPluginInterface*> plugins)
{
foreach (VidaliaPluginInterface* plugin, plugins) {
+ vInfo("Survey says...");
if (plugin->panel()) {
+ vInfo(plugin->panel()->tabLabel());
ui.tabMainPanel->addTab(plugin->panel(), plugin->panel()->tabIcon(),
plugin->panel()->tabLabel());
}
Modified: vidalia/branches/extension-api/src/vidalia/MainWindow.h
===================================================================
--- vidalia/branches/extension-api/src/vidalia/MainWindow.h 2009-06-25 03:57:32 UTC (rev 3888)
+++ vidalia/branches/extension-api/src/vidalia/MainWindow.h 2009-06-25 04:04:58 UTC (rev 3889)
@@ -27,10 +27,7 @@
#include "MessageLog.h"
#include "BandwidthGraph.h"
#include "ConfigDialog.h"
-#include "Dashboard.h"
#include "HelpBrowser.h"
-#include "HomePanel.h"
-#include "NetViewer.h"
#include "PluginManager.h"
#include "VidaliaPluginInterface.h"
@@ -234,16 +231,10 @@
bool _delayedShutdownStarted;
/** Set to true if Vidalia started its own Tor process. */
bool _isVidaliaRunningTor;
- /** A Dashboard object which displays the user's dashboard */
- Dashboard* _dashboard;
- /** A HomePanel object which displays the user's home panel */
- HomePanel* _homePanel;
/** A MessageLog object which handles logging Tor messages */
MessageLog* _messageLog;
/** A BandwidthGraph object which handles monitoring Tor bandwidth usage */
BandwidthGraph* _bandwidthGraph;
- /** A NetViewer object which displays the Tor network graphically */
- NetViewer* _netViewer;
/** A ConfigDialog object which lets the user configure Tor and Vidalia */
ConfigDialog* _configDialog;
/** A Plugin Manager object which loads and manages Vidalia Plugins. */
Modified: vidalia/branches/extension-api/src/vidalia/MainWindow.ui
===================================================================
--- vidalia/branches/extension-api/src/vidalia/MainWindow.ui 2009-06-25 03:57:32 UTC (rev 3888)
+++ vidalia/branches/extension-api/src/vidalia/MainWindow.ui 2009-06-25 04:04:58 UTC (rev 3889)
@@ -46,16 +46,6 @@
<property name="documentMode">
<bool>true</bool>
</property>
- <widget class="QWidget" name="tab">
- <attribute name="title">
- <string>Tab 1</string>
- </attribute>
- </widget>
- <widget class="QWidget" name="tab_2">
- <attribute name="title">
- <string>Tab 2</string>
- </attribute>
- </widget>
</widget>
</item>
</layout>
Modified: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/CMakeLists.txt
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/CMakeLists.txt 2009-06-25 03:57:32 UTC (rev 3888)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/CMakeLists.txt 2009-06-25 04:04:58 UTC (rev 3889)
@@ -34,21 +34,44 @@
)
qt4_wrap_cpp(networkmapplugin_SRCS
NetworkMapPlugin.h
- CircuitItem.h
+# CircuitItem.h
CircuitListWidget.h
GeoIpCache.h
- GeoIpCacheItem.h
- GeoIp.h
- GeoIpRequest.h
+# GeoIpCacheItem.h
+# GeoIp.h
+# GeoIpRequest.h
GeoIpResolver.h
- GeoIpResponse.h
+# GeoIpResponse.h
NetViewer.h
RouterDescriptorView.h
RouterInfoDialog.h
- RouterListItem.h
+# RouterListItem.h
RouterListWidget.h
- StreamItem.h
+# StreamItem.h
)
+
+if (USE_MARBLE)
+ set(networkmapplugin_SRCS ${networkmapplugin_SRCS}
+ TorMapWidget.cpp
+ TorMapWidgetInputHandler.cpp
+ TorMapWidgetPopupMenu.cpp
+ )
+ qt4_wrap_cpp(networkmapplugin_SRCS
+ TorMapWidget.h
+ TorMapWidgetInputHandler.h
+ TorMapWidgetPopupMenu.h
+ )
+else(USE_MARBLE)
+ set(networkmapplugin_SRCS ${networkmapplugin_SRCS}
+ TorMapImageView.cpp
+ ZImageView.cpp
+ )
+ qt4_wrap_cpp(networkmapplugin_SRCS
+ TorMapImageView.h
+ ZImageView.h
+ )
+endif(USE_MARBLE)
+
qt4_wrap_ui(networkmapplugin_SRCS
NetViewer.ui
RouterInfoDialog.ui
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/CircuitItem.cpp (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/CircuitItem.cpp)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/CircuitItem.cpp (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/CircuitItem.cpp 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,76 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file CircuitItem.cpp
+** \version $Id$
+** \brief Item representing a Tor circuit and its status
+*/
+
+#include "CircuitItem.h"
+#include "CircuitListWidget.h"
+
+
+/** Constructor */
+CircuitItem::CircuitItem(const Circuit &circuit)
+{
+ /* Update the displayed text */
+ update(circuit);
+}
+
+/** Updates the status and path of this circuit item. */
+void
+CircuitItem::update(const Circuit &circuit)
+{
+ QString displayedPath;
+
+ /* Save the Circuit object */
+ _circuit = circuit;
+
+ /* Use a semi-meaningful value if the path is empty */
+ displayedPath = circuit.length() > 0 ? circuit.routerNames().join(",")
+ : tr("<Path Empty>");
+
+ /* Update the column fields */
+ setText(CircuitListWidget::ConnectionColumn, displayedPath);
+ setToolTip(CircuitListWidget::ConnectionColumn, displayedPath);
+ setText(CircuitListWidget::StatusColumn, circuit.statusString());
+ setToolTip(CircuitListWidget::StatusColumn, circuit.statusString());
+}
+
+/** Adds a stream as a child of this circuit. */
+void
+CircuitItem::addStream(StreamItem *stream)
+{
+ addChild(stream);
+}
+
+/** Removes the stream item from this circuit and frees its memory */
+void
+CircuitItem::removeStream(StreamItem *stream)
+{
+ int index = indexOfChild(stream);
+ if (index > -1) {
+ delete takeChild(index);
+ }
+}
+
+/** Returns a list of all stream items on this circuit. */
+QList<StreamItem *>
+CircuitItem::streams() const
+{
+ QList<StreamItem *> streams;
+ int n = childCount();
+ for (int i = 0; i < n; i++) {
+ streams << (StreamItem *)child(i);
+ }
+ return streams;
+}
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/CircuitItem.h (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/CircuitItem.h)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/CircuitItem.h (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/CircuitItem.h 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,55 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file CircuitItem.h
+** \version $Id$
+** \brief List item representing a Tor circuit and its status
+*/
+
+#ifndef _CIRCUITITEM_H
+#define _CIRCUITITEM_H
+
+#include "StreamItem.h"
+
+#include "Circuit.h"
+#include "Stream.h"
+
+#include <QApplication>
+#include <QTreeWidgetItem>
+
+
+class CircuitItem : public QTreeWidgetItem
+{
+ Q_DECLARE_TR_FUNCTIONS(CircuitItem)
+
+public:
+ /** Default constructor */
+ CircuitItem(const Circuit &circuit);
+
+ /** Adds a stream to this circuit item */
+ void addStream(StreamItem *stream);
+ /** Removes the stream item from the list and frees its memory. */
+ void removeStream(StreamItem *stream);
+ /** Updates the status of this circuit item using the given circuit. */
+ void update(const Circuit &circuit);
+ /** Returns the ID for this circuit. */
+ CircuitId id() const { return _circuit.id(); }
+ /** Returns the Circuit object for this item. */
+ Circuit circuit() const { return _circuit; }
+ /** Returns a list of all stream items on this circuit. */
+ QList<StreamItem *> streams() const;
+
+private:
+ Circuit _circuit; /**< Circuit associated with this item. */
+};
+
+#endif
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/CircuitListWidget.cpp (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/CircuitListWidget.cpp)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/CircuitListWidget.cpp (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/CircuitListWidget.cpp 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,363 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file CircuitListWidget.cpp
+** \version $Id$
+** \brief Collection of Tor circuits as CircuitItems
+*/
+
+#include "config.h"
+#include "CircuitListWidget.h"
+#include "Vidalia.h"
+
+#include <QPoint>
+#include <QTimer>
+
+#define IMG_CLOSE ":/images/22x22/edit-delete.png"
+#define IMG_ZOOM ":/images/22x22/page-zoom.png"
+
+#define CLOSED_CIRCUIT_REMOVE_DELAY 3000
+#define FAILED_CIRCUIT_REMOVE_DELAY 5000
+#define CLOSED_STREAM_REMOVE_DELAY 3000
+#define FAILED_STREAM_REMOVE_DELAY 4000
+
+
+/** Default constructor. */
+CircuitListWidget::CircuitListWidget(QWidget *parent)
+: QTreeWidget(parent)
+{
+ /* Create and initialize columns */
+ setHeaderLabels(QStringList() << tr("Connection") << tr("Status"));
+
+ /* Find out when a circuit has been selected */
+ connect(this, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
+ this, SLOT(onSelectionChanged(QTreeWidgetItem*,QTreeWidgetItem*)));
+ connect(this, SIGNAL(customContextMenuRequested(QPoint)),
+ this, SLOT(customContextMenuRequested(QPoint)));
+
+ /* Respond to the Delete key by closing whatever circuits or streams are
+ * selected. */
+ vApp->createShortcut(QKeySequence::Delete, this, this,
+ SLOT(closeSelectedConnections()));
+}
+
+/** Called when the user changes the UI translation. */
+void
+CircuitListWidget::retranslateUi()
+{
+ setHeaderLabels(QStringList() << tr("Connection") << tr("Status"));
+ for (int i = 0; i < topLevelItemCount(); i++) {
+ CircuitItem *circuitItem = dynamic_cast<CircuitItem *>(topLevelItem(i));
+ circuitItem->update(circuitItem->circuit());
+
+ foreach (StreamItem *streamItem, circuitItem->streams()) {
+ streamItem->update(streamItem->stream());
+ }
+ }
+}
+
+/** Called when the user requests a context menu on a circuit or stream in the
+ * list and displays a context menu appropriate for whichever type of item is
+ * currently selected. */
+void
+CircuitListWidget::customContextMenuRequested(const QPoint &pos)
+{
+ QMenu menu(this);
+
+ /* Find out which item was right-clicked */
+ QTreeWidgetItem *item = itemAt(pos);
+ if (!item)
+ return;
+
+ if (!item->parent()) {
+ /* A circuit was selected */
+ CircuitItem *circuitItem = dynamic_cast<CircuitItem *>(item);
+ if (!circuitItem)
+ return;
+
+ /* Set up the circuit context menu */
+ QAction *zoomAct = new QAction(QIcon(IMG_ZOOM),
+ tr("Zoom to Circuit"), this);
+ QAction *closeAct = new QAction(QIcon(IMG_CLOSE),
+ tr("Close Circuit (Del)"), this);
+#if defined(USE_MARBLE)
+ zoomAct->setEnabled(circuitItem->circuit().status() == Circuit::Built);
+ menu.addAction(zoomAct);
+ menu.addSeparator();
+#endif
+ menu.addAction(closeAct);
+
+ /* Display the context menu and find out which (if any) action was
+ * selected */
+ QAction* action = menu.exec(mapToGlobal(pos));
+ if (action == closeAct)
+ emit closeCircuit(circuitItem->id());
+ else if (action == zoomAct)
+ emit zoomToCircuit(circuitItem->id());
+ } else {
+ /* A stream was selected */
+ StreamItem *streamItem = dynamic_cast<StreamItem *>(item);
+ if (!streamItem)
+ return;
+
+ /* Set up the stream context menu */
+ QAction *closeAct = new QAction(QIcon(IMG_CLOSE),
+ tr("Close Stream (Del)"), this);
+ menu.addAction(closeAct);
+
+ /* Display the context menu and find out which (if any) action was
+ * selected */
+ QAction* action = menu.exec(mapToGlobal(pos));
+ if (action == closeAct)
+ emit closeStream(streamItem->id());
+ }
+}
+
+/** Closes all selected circuits or streams. */
+void
+CircuitListWidget::closeSelectedConnections()
+{
+ QList<QTreeWidgetItem *> items = selectedItems();
+ foreach (QTreeWidgetItem *item, items) {
+ if (!item->parent()) {
+ CircuitItem *circuitItem = dynamic_cast<CircuitItem *>(item);
+ if (circuitItem)
+ emit closeCircuit(circuitItem->id());
+ } else {
+ StreamItem *streamItem = dynamic_cast<StreamItem *>(item);
+ if (streamItem)
+ emit closeStream(streamItem->id());
+ }
+ }
+}
+
+/** Adds a <b>circuit</b> to the list. If the circuit already exists in the
+ * list, the status and path will be updated. */
+void
+CircuitListWidget::addCircuit(const Circuit &circuit)
+{
+ /* Check to see if the circuit already exists in the tree */
+ CircuitItem *item = findCircuitItem(circuit.id());
+
+ if (!item) {
+ /* Add the new circuit */
+ item = new CircuitItem(circuit);
+ addTopLevelItem(item);
+ } else {
+ /* Circuit already exists, so update its status and path */
+ item->update(circuit);
+ }
+
+ /* If the circuit is closed or dead, schedule it for removal */
+ Circuit::Status status = circuit.status();
+ if (status == Circuit::Closed) {
+ scheduleCircuitRemoval(item, CLOSED_CIRCUIT_REMOVE_DELAY);
+ } else if (status == Circuit::Failed) {
+ scheduleCircuitRemoval(item, FAILED_CIRCUIT_REMOVE_DELAY);
+ }
+}
+
+/** Adds a stream to the list. If the stream already exists in the list, the
+ * status and path will be updated. */
+void
+CircuitListWidget::addStream(const Stream &stream)
+{
+ /* Check to see if the stream already exists in the tree */
+ StreamItem *item = findStreamItem(stream.id());
+
+ if (!item) {
+ CircuitItem *circuit = findCircuitItem(stream.circuitId());
+ /* New stream, so try to find its circuit and add it */
+ if (circuit) {
+ circuit->addStream(new StreamItem(stream));
+ expandItem(circuit);
+ }
+ } else {
+ /* Stream already exists, so just update its status */
+ item->update(stream);
+
+ /* If the stream is closed or dead, schedule it for removal */
+ Stream::Status status = stream.status();
+ if (status == Stream::Closed) {
+ scheduleStreamRemoval(item, CLOSED_STREAM_REMOVE_DELAY);
+ } else if (status == Stream::Failed) {
+ scheduleStreamRemoval(item, FAILED_STREAM_REMOVE_DELAY);
+ }
+ }
+}
+
+/** Schedules the given circuit to be removed after the specified timeout. */
+void
+CircuitListWidget::scheduleCircuitRemoval(CircuitItem *circuit, int delay)
+{
+ if (!_circuitRemovalList.contains(circuit)) {
+ _circuitRemovalList << circuit;
+ QTimer::singleShot(delay, this, SLOT(removeCircuit()));
+ }
+}
+
+/** Schedules the given stream to be removed after the specified timeout. */
+void
+CircuitListWidget::scheduleStreamRemoval(StreamItem *stream, int delay)
+{
+ if (!_streamRemovalList.contains(stream)) {
+ _streamRemovalList << stream;
+ QTimer::singleShot(delay, this, SLOT(removeStream()));
+ }
+}
+
+/** Removes the first circuit scheduled to be removed. */
+void
+CircuitListWidget::removeCircuit()
+{
+ if (!_circuitRemovalList.isEmpty()) {
+ CircuitItem *circuitItem = _circuitRemovalList.takeFirst();
+ Circuit circuit = circuitItem->circuit();
+ removeCircuit(circuitItem);
+ emit circuitRemoved(circuit.id());
+ }
+}
+
+/** Removes the given circuit item and all streams on that circuit. */
+void
+CircuitListWidget::removeCircuit(CircuitItem *circuit)
+{
+ if (circuit) {
+ /* Remove all streams (if any) on this circuit. */
+ QList<StreamItem *> streams = circuit->streams();
+ foreach (StreamItem *stream, streams) {
+ /* Check if this stream was scheduled for removal already */
+ if (_streamRemovalList.contains(stream)) {
+ /* If this stream was already scheduled for removal, replace its pointer
+ * with 0, so it doesn't get removed twice. */
+ int index = _streamRemovalList.indexOf(stream);
+ _streamRemovalList.replace(index, (StreamItem *)0);
+ }
+
+ /* Remove the stream item from the circuit */
+ circuit->removeStream(stream);
+ }
+ /* Remove the circuit item itself */
+ delete takeTopLevelItem(indexOfTopLevelItem(circuit));
+ }
+}
+
+/** Removes the first stream scheduled to be removed. */
+void
+CircuitListWidget::removeStream()
+{
+ if (!_streamRemovalList.isEmpty()) {
+ StreamItem *stream = _streamRemovalList.takeFirst();
+ removeStream(stream);
+ }
+}
+
+/** Removes the given stream item. */
+void
+CircuitListWidget::removeStream(StreamItem *stream)
+{
+ if (stream) {
+ /* Try to get the stream's parent (a circuit item) */
+ CircuitItem *circuit = (CircuitItem *)stream->parent();
+ if (circuit) {
+ /* Remove the stream from the circuit and delete the item */
+ circuit->removeStream(stream);
+ } else {
+ /* It isn't on a circuit, so just delete the stream */
+ delete stream;
+ }
+ }
+}
+
+/** Clears all circuits and streams from the list. */
+void
+CircuitListWidget::clearCircuits()
+{
+ QTreeWidget::clear();
+ _circuitRemovalList.clear();
+ _streamRemovalList.clear();
+}
+
+/** Finds the circuit with the given ID and returns a pointer to that
+ * circuit's item in the list. */
+CircuitItem*
+CircuitListWidget::findCircuitItem(const CircuitId &circid)
+{
+ int numCircs = topLevelItemCount();
+ for (int i = 0; i < numCircs; i++) {
+ CircuitItem *circuit = (CircuitItem *)topLevelItem(i);
+ if (circid == circuit->id()) {
+ return circuit;
+ }
+ }
+ return 0;
+}
+
+/** Finds the stream with the given ID and returns a pointer to that stream's
+ * item in the list. */
+StreamItem*
+CircuitListWidget::findStreamItem(const StreamId &streamid)
+{
+ int numCircs = topLevelItemCount();
+ int numStreams;
+
+ for (int i = 0; i < numCircs; i++) {
+ CircuitItem *circuit = (CircuitItem *)topLevelItem(i);
+ numStreams = circuit->childCount();
+
+ for (int j = 0; j < numStreams; j++) {
+ StreamItem *stream = (StreamItem *)circuit->child(j);
+ if (streamid == stream->id()) {
+ return stream;
+ }
+ }
+ }
+ return 0;
+}
+
+/** Called when the current item selection has changed. */
+void
+CircuitListWidget::onSelectionChanged(QTreeWidgetItem *cur,
+ QTreeWidgetItem *prev)
+{
+ Q_UNUSED(prev);
+
+ if (cur) {
+ Circuit circuit;
+
+ if (!cur->parent()) {
+ /* User selected a CircuitItem, so just grab the Circuit */
+ circuit = ((CircuitItem *)cur)->circuit();
+ } else {
+ /* User selected a StreamItem, so get its parent and then the Circuit */
+ CircuitItem *circItem = (CircuitItem *)cur->parent();
+ circuit = circItem->circuit();
+ }
+
+ /* If this circuit has a path, then emit it so we can highlight it */
+ emit circuitSelected(circuit);
+ }
+}
+
+/** Returns a list of circuits currently in the widget. */
+CircuitList
+CircuitListWidget::circuits()
+{
+ int numCircs = topLevelItemCount();
+ CircuitList circs;
+
+ for (int i = 0; i < numCircs; i++) {
+ CircuitItem *circ = (CircuitItem *)topLevelItem(i);
+ circs << circ->circuit();
+ }
+ return circs;
+}
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/CircuitListWidget.h (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/CircuitListWidget.h)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/CircuitListWidget.h (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/CircuitListWidget.h 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,106 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file CircuitListWidget.h
+** \version $Id$
+** \brief Collection of Tor circuits as CircuitItems
+*/
+
+#ifndef _CIRCUITLISTWIDGET_H
+#define _CIRCUITLISTWIDGET_H
+
+#include "CircuitItem.h"
+#include "StreamItem.h"
+
+#include <QTreeWidget>
+#include <QList>
+#include <QMenu>
+#include <QAction>
+#include <QMouseEvent>
+
+
+class CircuitListWidget : public QTreeWidget
+{
+ Q_OBJECT
+
+public:
+ /** Circuit list columns. */
+ enum Columns {
+ ConnectionColumn = 0, /**< Column for either the circuit or stream */
+ StatusColumn = 1 /**< Status of the connection. */
+ };
+
+ /** Default constructor */
+ CircuitListWidget(QWidget *parent = 0);
+
+ /** Adds a circuit to the list. If the circuit already exists in the list,
+ * the status and path will be updated. */
+ void addCircuit(const Circuit &circuit);
+ /** Adds a stream to the list. If the stream already exists in the list, the
+ * status and path will be updated. */
+ void addStream(const Stream &stream);
+ /** Returns a list of circuits currently in the widget. */
+ QList<Circuit> circuits();
+ /** Called when the user changes the UI translation. */
+ void retranslateUi();
+
+signals:
+ /** Emitted when a circuit item is selected. */
+ void circuitSelected(Circuit circuit);
+ /** Emitted when a circuit is removed from the list. */
+ void circuitRemoved(CircuitId circid);
+ /** Emitted when the user selects a circuit to be closed. */
+ void closeCircuit(CircuitId circid);
+ /** Emitted when the user selects a stream to be closed. */
+ void closeStream(StreamId streamid);
+ /** Emitted when the user selects a circuit to zoom to. */
+ void zoomToCircuit(CircuitId circid);
+
+public slots:
+ /** Clears all circuits and streams from the list. */
+ void clearCircuits();
+
+private slots:
+ /** Removes the first circuit scheduled to be removed.*/
+ void removeCircuit();
+ /** Removes the first stream scheduled to be removed. */
+ void removeStream();
+ /** Called when the current item selectio has changed. */
+ void onSelectionChanged(QTreeWidgetItem *cur, QTreeWidgetItem *prev);
+ /** Called when the user requests a context menu on a circuit or stream in
+ * the list and displays a context menu appropriate for whichever type of
+ * item is currently selected. */
+ void customContextMenuRequested(const QPoint &pos);
+ /** Closes all selected circuits or streams. */
+ void closeSelectedConnections();
+
+private:
+ /** Removes the given circuit item and all streams on that circuit. */
+ void removeCircuit(CircuitItem *circuit);
+ /** Removes the given stream item. */
+ void removeStream(StreamItem *stream);
+ /** Finds the circuit with the given ID. */
+ CircuitItem* findCircuitItem(const CircuitId &circid);
+ /** Finds the stream with the given ID. */
+ StreamItem* findStreamItem(const StreamId &streamid);
+ /** Schedules the given circuit item to be removed after the given timeout. */
+ void scheduleCircuitRemoval(CircuitItem *circuit, int delay);
+ /** Schedules a stream to be removed after the given timeout. */
+ void scheduleStreamRemoval(StreamItem *stream, int delay);
+
+ /** List of circuit items to be removed. */
+ QList<CircuitItem *> _circuitRemovalList;
+ /** List of stream items to be removed. */
+ QList<StreamItem *> _streamRemovalList;
+};
+
+#endif
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIp.cpp (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/GeoIp.cpp)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIp.cpp (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIp.cpp 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,75 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file GeoIp.cpp
+** \version $Id$
+** \brief Associates an IP with a geographic location
+*/
+
+#include "GeoIp.h"
+
+#include <QStringList>
+
+/** Verifies a latitude is between -90.0 and 90.0 degrees. */
+#define IS_VALID_LATITUDE(x) (((x) >= -90.0) && ((x) <= 90.0))
+/** Verifies a longitude is between -180.0 and 180.0 degrees. */
+#define IS_VALID_LONGITUDE(x) (((x) >= -180.0) && ((x) <= 180.0))
+
+
+GeoIp::GeoIp()
+{
+ _latitude = 0.0;
+ _longitude = 0.0;
+}
+
+GeoIp::GeoIp(const QHostAddress &ip, float latitude, float longitude,
+ const QString &city, const QString ®ion,
+ const QString &country, const QString &countryCode)
+{
+ _ip = ip;
+ _latitude = latitude;
+ _longitude = longitude;
+ _city = city;
+ _region = region;
+ _country = country;
+ _countryCode = countryCode;
+}
+
+bool
+GeoIp::isValid() const
+{
+ return (! _ip.isNull()
+ && IS_VALID_LATITUDE(_latitude)
+ && IS_VALID_LONGITUDE(_longitude));
+}
+
+QString
+GeoIp::toString() const
+{
+ QStringList location;
+
+ /* Add the city name (if present) */
+ if (!_city.isEmpty())
+ location << _city;
+
+ /* Add the full state or region name (if present) */
+ if (!_region.isEmpty() && _region != _city)
+ location << _region;
+
+ /* Add the country name or the country code (if present) */
+ if (!_country.isEmpty())
+ location << _country;
+ else if (!_countryCode.isEmpty())
+ location << _countryCode;
+
+ return location.join(", ");
+}
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIp.h (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/GeoIp.h)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIp.h (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIp.h 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,96 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file GeoIp.h
+** \version $Id$
+** \brief Associates an IP with a geographic location
+*/
+
+#ifndef _GEOIP_H
+#define _GEOIP_H
+
+#include <QHash>
+#include <QString>
+#include <QHostAddress>
+
+
+class GeoIp
+{
+public:
+ /** Default constructor. Creates an empty GeoIp object.
+ */
+ GeoIp();
+
+ GeoIp(const QHostAddress &ip, float latitude, float longitude,
+ const QString &city = QString(),
+ const QString ®ion = QString(),
+ const QString &country = QString(),
+ const QString &countryCode = QString());
+
+ /** Returns the IP address associated with this GeoIP object.
+ */
+ QHostAddress ip() const { return _ip; }
+
+ /** Returns the latitude portion of the geographic coordinates associated
+ * with this IP address or range of IP addresses.
+ */
+ float latitude() const { return _latitude; }
+
+ /** Returns the longitude portion of the geographic coordinates associated
+ * with this IP address or range of IP addresses.
+ */
+ float longitude() const { return _longitude; }
+
+ /** Returns the name of the city associated with this IP address, if known.
+ * Otherwise, returns an empty QString.
+ */
+ QString city() const { return _city; }
+
+ /** Returns the full region name (e.g., state) in which this IP address
+ * resides, if known. Otherwise, returns an empty QString.
+ */
+ QString region() const { return _region; }
+
+ /** Returns the full name of the country associated with this IP address
+ * or range of IP addresses, if known. Otherwise, returns an empty QString.
+ */
+ QString country() const { return _country; }
+
+ /** Returns the ISO 3166-1 alpha-2 two-letter country code of the country
+ * associated with this IP address or range of IP addresses, if known.
+ * Otherwise, returns an empty QString.
+ */
+ QString countryCode() const { return _countryCode; }
+
+ /** Returns a human-readable string of city, region(state), and country.
+ * Some fields may be absent if they are not known. If no fields are known,
+ * this will return an empty QString.
+ */
+ QString toString() const;
+
+ /** Returns true if the GeoIp object is valid. A valid GeoIp object must
+ * have valid IP address, valid latitude and longitude coordinates and a
+ * two-letter country code.
+ */
+ bool isValid() const;
+
+private:
+ QHostAddress _ip; /**< IP address for this location. */
+ float _latitude; /**< Latitudinal coordinate for this IP's location. */
+ float _longitude; /**< Longitudinal coordinate for this IP's location. */
+ QString _city; /**< City in which this IP lives. */
+ QString _region; /**< State or district in which this IP lives. */
+ QString _country; /**< Country in which this IP lives. */
+ QString _countryCode; /**< ISO-3166-1 alpha-2 country code. */
+};
+
+#endif
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpCache.cpp (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/GeoIpCache.cpp)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpCache.cpp (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpCache.cpp 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,160 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file GeoIpCache.cpp
+** \version $Id$
+** \brief Caches the results of previous GeoIP requests
+*/
+
+#include "GeoIpCache.h"
+#include "GeoIpCacheItem.h"
+#include "GeoIp.h"
+#include "Vidalia.h"
+
+#include "file.h"
+#include "stringutil.h"
+
+#include <QFile>
+#include <QDir>
+#include <QString>
+#include <QDateTime>
+#include <QTextStream>
+#include <QHostAddress>
+
+
+GeoIpCache::GeoIpCache(QObject *parent)
+ : QObject(parent)
+{
+ loadFromDisk();
+}
+
+QString
+GeoIpCache::cacheFileName() const
+{
+ return (Vidalia::dataDirectory() + "/geoip-cache");
+}
+
+bool
+GeoIpCache::saveToDisk(QString *errmsg)
+{
+ /* Make sure we have a data directory. */
+ if (!create_path(Vidalia::dataDirectory())) {
+ return false;
+ }
+
+ /* Try to open a temporary cache file for writing */
+ QFile tmpCacheFile(cacheFileName() + ".tmp");
+ if (!tmpCacheFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ return err(errmsg, tmpCacheFile.errorString());
+ }
+
+ /* Write the cache entries to the file. */
+ QTextStream cache(&tmpCacheFile);
+ foreach (GeoIpCacheItem cacheItem, _cache) {
+ /* Save the cache item if it's not too old. */
+ if (!cacheItem.isExpired()) {
+ cache << cacheItem.toCacheString() << endl;
+ }
+ }
+
+ QFile cacheFile(cacheFileName());
+ /* Check if an previous cache file exists. */
+ if (cacheFile.exists()) {
+ /* A previous cache file exists, so try to remove it */
+ if (!cacheFile.remove()) {
+ return err(errmsg, cacheFile.errorString());
+ }
+ }
+ /* Rename the temporary file into place. */
+ if (!tmpCacheFile.rename(cacheFile.fileName())) {
+ return err(errmsg, tmpCacheFile.errorString());
+ }
+ return true;
+}
+
+bool
+GeoIpCache::loadFromDisk(QString *errmsg)
+{
+ QFile cacheFile(cacheFileName());
+
+ if (cacheFile.exists()) {
+ /* Try to open the cache file */
+ if (!cacheFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ return err(errmsg, cacheFile.errorString());
+ }
+
+ /* Read the cached items from the cache file */
+ QTextStream cache(&cacheFile);
+ QString line = cache.readLine();
+ while (! line.isNull()) {
+ /* Create a GeoIpCacheItem from the line and save it */
+ GeoIpCacheItem item = GeoIpCacheItem::fromCacheString(line);
+ if (item.isValid() && ! item.isExpired())
+ addToCache(item);
+
+ line = cache.readLine();
+ }
+ vInfo("Parsed %1 GeoIP entries from '%2'").arg(_cache.size())
+ .arg(cacheFileName());
+ }
+ return true;
+}
+
+void
+GeoIpCache::addToCache(const GeoIp &geoip)
+{
+ /* Create a "range" consisting of only a single IP address. */
+ if (! contains(geoip.ip()))
+ addToCache(geoip.ip(), geoip.ip(), geoip);
+}
+
+void
+GeoIpCache::addToCache(const QHostAddress &from, const QHostAddress &to,
+ const GeoIp &geoip)
+{
+ /* New cache entries expire 30 days from the time they are first cached. */
+ QDateTime expires = QDateTime::currentDateTime().toUTC().addDays(30);
+
+ /* Create a new GeoIpCacheItem and add it to the cache */
+ addToCache(GeoIpCacheItem(from, to, geoip, expires));
+}
+
+void
+GeoIpCache::addToCache(const GeoIpCacheItem &ci)
+{
+ if (! ci.isValid() || ci.isExpired())
+ return;
+
+ /* The key for the cache is the last IP address in the range of IP addresses
+ * covered by the GeoIpCacheItem. We do this because QMap::upperBound() and
+ * QMap::lowerBound() return an iterator to the next item higher than the
+ * search value (an IP address) if an exact match is not found.
+ */
+ _cache.insert(ci.ipRangeEnd().toIPv4Address(), ci);
+}
+
+GeoIp
+GeoIpCache::geoIpForAddress(const QHostAddress &ip)
+{
+ GeoIpCacheMap::iterator i = _cache.upperBound(ip.toIPv4Address());
+ if (i != _cache.end() && (*i).contains(ip))
+ return (*i).toGeoIp(ip);
+ return GeoIp();
+}
+
+bool
+GeoIpCache::contains(const QHostAddress &ip)
+{
+ GeoIpCacheMap::iterator i = _cache.upperBound(ip.toIPv4Address());
+ return (i != _cache.end() && (*i).contains(ip));
+}
+
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpCache.h (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/GeoIpCache.h)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpCache.h (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpCache.h 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,87 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file GeoIpCache.h
+** \version $Id$
+** \brief Caches the results of previous GeoIP requests
+*/
+
+#ifndef _GEOIPCACHE_H
+#define _GEOIPCACHE_H
+
+#include "GeoIpCacheItem.h"
+
+#include <QObject>
+#include <QMap>
+
+class GeoIp;
+class QString;
+class QHostAddress;
+
+typedef QMap<quint32, GeoIpCacheItem> GeoIpCacheMap;
+
+
+class GeoIpCache : public QObject
+{
+ Q_OBJECT
+
+public:
+ /** Default constructor. */
+ GeoIpCache(QObject *parent = 0);
+
+ /** Writes the current cache to disk. Returns true if the cache file was
+ * successfully saved to disk. Otherwise, returns false and sets
+ * <b>errmsg</b> to a string describing the error encountered, if
+ * <b>errmsg</b> is not null.
+ */
+ bool saveToDisk(QString *errmsg = 0);
+
+ /** Reads the cache in from disk. Returns true if the cache file was
+ * successfully read. Otherwise, returns false and sets <b>errmsg</b> to
+ * a string describing the error encountered, if <b>errmsg</b> is not null.
+ */
+ bool loadFromDisk(QString *errmsg = 0);
+
+ /** Returns the location currently used for the cache file.
+ */
+ QString cacheFileName() const;
+
+ /** Caches the geographic information in <b>geoip</b> associated with a
+ * single IP address.
+ */
+ void addToCache(const GeoIp &geoip);
+
+ /** Caches the geographic information in <b>geoip</b> associated with a
+ * range of IP addresses, from <b>from</b> to <b>to</b> (inclusive).
+ */
+ void addToCache(const QHostAddress &from, const QHostAddress &to,
+ const GeoIp &geoip);
+
+ /** Returns a GeoIp object for the given <b>ip</b> from cache. If no cached
+ * information is known for <b>ip</b>, an empty GeoIp object is returned.
+ */
+ GeoIp geoIpForAddress(const QHostAddress &ip);
+
+ /** Returns true if the cache contains geographic location information for
+ * <b>ip</b>. Otherwise, returns false.
+ */
+ bool contains(const QHostAddress &ip);
+
+private:
+ /** Adds the GeoIpCacheItem <b>ci</b> to the cache. */
+ void addToCache(const GeoIpCacheItem &ci);
+
+ /**< List of cached GeoIp objects. */
+ GeoIpCacheMap _cache;
+};
+
+#endif
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpCacheItem.cpp (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/GeoIpCacheItem.cpp)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpCacheItem.cpp (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpCacheItem.cpp 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,183 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file GeoIpCacheItem.cpp
+** \version $Id$
+** \brief Cached result of a single IP-to-geolocation result
+*/
+
+#include "GeoIpCacheItem.h"
+#include "GeoIp.h"
+
+#include "stringutil.h"
+
+#include <QString>
+#include <QDateTime>
+#include <QStringList>
+
+#define CACHE_KEY_FROM_IP "FROM"
+#define CACHE_KEY_TO_IP "TO"
+#define CACHE_KEY_EXPIRES "EXPIRES"
+#define CACHE_KEY_LATITUDE "LAT"
+#define CACHE_KEY_LONGITUDE "LON"
+#define CACHE_KEY_CITY "CITY"
+#define CACHE_KEY_REGION "REGION"
+#define CACHE_KEY_COUNTRY "COUNTRY"
+#define CACHE_KEY_COUNTRY_CODE "CC"
+
+
+GeoIpCacheItem::GeoIpCacheItem()
+{
+ _fromIp = 0;
+ _toIp = 0;
+}
+
+GeoIpCacheItem::GeoIpCacheItem(const QHostAddress &from, const QHostAddress &to,
+ const GeoIp &geoip, const QDateTime &expires)
+{
+ _fromIp = from.toIPv4Address();
+ _toIp = to.toIPv4Address();
+ _expires = expires;
+
+ _fields.insert(CACHE_KEY_LATITUDE, geoip.latitude());
+ _fields.insert(CACHE_KEY_LONGITUDE, geoip.longitude());
+ if (! geoip.city().isEmpty())
+ _fields.insert(CACHE_KEY_CITY, geoip.city());
+ if (! geoip.region().isEmpty())
+ _fields.insert(CACHE_KEY_REGION, geoip.region());
+ if (! geoip.country().isEmpty())
+ _fields.insert(CACHE_KEY_COUNTRY, geoip.country());
+ if (! geoip.countryCode().isEmpty())
+ _fields.insert(CACHE_KEY_COUNTRY_CODE, geoip.countryCode());
+}
+
+QHostAddress
+GeoIpCacheItem::ipRangeStart() const
+{
+ return QHostAddress(_fromIp);
+}
+
+QHostAddress
+GeoIpCacheItem::ipRangeEnd() const
+{
+ return QHostAddress(_toIp);
+}
+
+bool
+GeoIpCacheItem::contains(const QHostAddress &ip) const
+{
+ quint32 ipv4 = ip.toIPv4Address();
+
+ return (ipv4 >= _fromIp && ipv4 <= _toIp);
+}
+
+bool
+GeoIpCacheItem::isValid() const
+{
+ return (_expires.isValid()
+ && ! QHostAddress(_fromIp).isNull()
+ && ! QHostAddress(_toIp).isNull()
+ && _fromIp <= _toIp
+ && _fields.contains(CACHE_KEY_LATITUDE)
+ && _fields.contains(CACHE_KEY_LONGITUDE));
+}
+
+bool
+GeoIpCacheItem::isExpired() const
+{
+ return (_expires < QDateTime::currentDateTime().toUTC());
+}
+
+GeoIp
+GeoIpCacheItem::toGeoIp(const QHostAddress &ip) const
+{
+ if (this->contains(ip))
+ return GeoIp(ip,
+ _fields.value(CACHE_KEY_LATITUDE).toDouble(),
+ _fields.value(CACHE_KEY_LONGITUDE).toDouble(),
+ _fields.value(CACHE_KEY_CITY).toString(),
+ _fields.value(CACHE_KEY_REGION).toString(),
+ _fields.value(CACHE_KEY_COUNTRY).toString(),
+ _fields.value(CACHE_KEY_COUNTRY_CODE).toString());
+ return GeoIp();
+}
+
+QString
+GeoIpCacheItem::toCacheString() const
+{
+ QStringList keyvals;
+
+ keyvals << QString(CACHE_KEY_FROM_IP"=%1").arg(QHostAddress(_fromIp).toString());
+ keyvals << QString(CACHE_KEY_TO_IP"=%1").arg(QHostAddress(_toIp).toString());
+ keyvals << QString(CACHE_KEY_EXPIRES"=\"%1\"").arg(_expires.toString(Qt::ISODate));
+
+ foreach (QString key, _fields.keys()) {
+ QString value = _fields.value(key).toString();
+ if (value.contains(" ")) {
+ value.replace("\\", "\\\\");
+ value.replace("\"", "\\\"");
+ value = "\"" + value + "\"";
+ }
+ keyvals << key + "=" + value;
+ }
+ return keyvals.join(" ");
+}
+
+GeoIpCacheItem
+GeoIpCacheItem::fromCacheString(const QString &line)
+{
+ GeoIpCacheItem ci;
+ bool ok;
+
+ QHash<QString,QString> keyvals = string_parse_keyvals(line, &ok);
+ if (! ok)
+ return GeoIpCacheItem();
+
+ /* Get the range of IP addresses associated with this cache entry */
+ QHostAddress fromIp(keyvals.value(CACHE_KEY_FROM_IP));
+ QHostAddress toIp(keyvals.value(CACHE_KEY_TO_IP));
+ if (fromIp.isNull() || toIp.isNull())
+ return GeoIpCacheItem();
+ ci._fromIp = fromIp.toIPv4Address();
+ ci._toIp = toIp.toIPv4Address();
+
+ /* Extract the expiration timestamp of this entry */
+ ci._expires = QDateTime::fromString(keyvals.value(CACHE_KEY_EXPIRES),
+ Qt::ISODate);
+ if (! ci._expires.isValid())
+ ci._expires = QDateTime::currentDateTime().toUTC().addDays(30);
+
+
+ /* Make sure we have valid geographic coordinates */
+ float latitude = keyvals.value(CACHE_KEY_LATITUDE).toFloat(&ok);
+ if (! ok)
+ return GeoIpCacheItem();
+ ci._fields.insert(CACHE_KEY_LATITUDE, latitude);
+
+ float longitude = keyvals.value(CACHE_KEY_LONGITUDE).toFloat(&ok);
+ if (! ok)
+ return GeoIpCacheItem();
+ ci._fields.insert(CACHE_KEY_LONGITUDE, longitude);
+
+ /* Each of these fields is optional */
+ if (keyvals.contains(CACHE_KEY_CITY))
+ ci._fields.insert(CACHE_KEY_CITY, keyvals.value(CACHE_KEY_CITY));
+ if (keyvals.contains(CACHE_KEY_REGION))
+ ci._fields.insert(CACHE_KEY_REGION, keyvals.value(CACHE_KEY_REGION));
+ if (keyvals.contains(CACHE_KEY_COUNTRY))
+ ci._fields.insert(CACHE_KEY_COUNTRY, keyvals.value(CACHE_KEY_COUNTRY));
+ if (keyvals.contains(CACHE_KEY_COUNTRY_CODE))
+ ci._fields.insert(CACHE_KEY_COUNTRY_CODE,
+ keyvals.value(CACHE_KEY_COUNTRY_CODE));
+
+ return ci;
+}
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpCacheItem.h (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/GeoIpCacheItem.h)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpCacheItem.h (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpCacheItem.h 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,97 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file GeoIpCacheItem.h
+** \version $Id$
+** \brief Cached result of a single IP-to-geolocation result
+*/
+
+#ifndef _GEOIPCACHEITEM_H
+#define _GEOIPCACHEITEM_H
+
+#include <QHash>
+#include <QString>
+#include <QVariant>
+#include <QDateTime>
+
+class GeoIp;
+class QHostAddress;
+
+
+class GeoIpCacheItem
+{
+public:
+ /** Default constructor
+ */
+ GeoIpCacheItem();
+
+ /** Constructor.
+ */
+ GeoIpCacheItem(const QHostAddress &from, const QHostAddress &to,
+ const GeoIp &geoIp, const QDateTime &expires);
+
+ /** Returns the first IP address in the range of IP addresses associated
+ * with this GeoIpCacheItem.
+ */
+ QHostAddress ipRangeStart() const;
+
+ /** Returns the last IP address in the range of IP addresses associated
+ * with this GeoIpCacheItem.
+ */
+ QHostAddress ipRangeEnd() const;
+
+ /** Returns true if <b>ip</b> is within the range of IP addresses associated
+ * with this GeoIpCacheItem.
+ * \sa ipRangeStart()
+ * \sa ipRangeEnd()
+ */
+ bool contains(const QHostAddress &ip) const;
+
+ /** Returns true if this GeoIpCacheItem object contains valid values for
+ * all required fields.
+ */
+ bool isValid() const;
+
+ /** Returns true if the cache item is too old to be considered accurate.
+ * Cached GeoIp responses are considered valid for thirty days after they
+ * are first added to the cache.
+ */
+ bool isExpired() const;
+
+ /** Returns a GeoIp object for <b>ip</b>, populated with the cached
+ * geographic information stored by this GeoIpCacheObject. If <b>ip</b>
+ * is not within the range of IP addresses associated with this object,
+ * an empty GeoIp object is returned.
+ * \sa contains
+ */
+ GeoIp toGeoIp(const QHostAddress &ip) const;
+
+ /** Formats the fields contained in this GeoIpCacheItem as a string
+ * suitable for writing to a cache file.
+ */
+ QString toCacheString() const;
+
+ /** Parses <b>cacheLine</b> and constructs a new GeoIpCacheItem object
+ * with the parsed values. The format of <b>cacheLine</b> must follow the
+ * format as produced by toCacheString().
+ * \sa toCacheString()
+ */
+ static GeoIpCacheItem fromCacheString(const QString &cacheLine);
+
+private:
+ quint32 _fromIp;
+ quint32 _toIp;
+ QDateTime _expires; /**< Time this item was cached. */
+ QHash<QString,QVariant> _fields;
+};
+
+#endif
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpRequest.cpp (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/GeoIpRequest.cpp)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpRequest.cpp (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpRequest.cpp 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,80 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file GeoIpRequest.cpp
+** \version $Id$
+** \brief A formatted request for GeoIP information for one or more IPs
+*/
+
+#include "GeoIpRequest.h"
+#include "ZlibByteArray.h"
+
+#include <QString>
+#include <QHostAddress>
+#include <QHttpRequestHeader>
+
+
+/** Creates an HTTP POST header for this request, based on the
+ * Host, Page, and content-length values. */
+QHttpRequestHeader
+GeoIpRequest::createHeader() const
+{
+ QHttpRequestHeader header("POST", _page, 1, 1);
+
+ if (!_host.isEmpty())
+ header.setValue("Host", _host);
+ header.setContentType("application/x-www-form-urlencoded");
+ header.setContentLength(_request.length());
+ header.setValue("Connection", "close");
+
+ if (ZlibByteArray::isZlibAvailable()) {
+ QString acceptEncodings = "deflate, x-deflate";
+ if (ZlibByteArray::isGzipSupported())
+ acceptEncodings += ", gzip, x-gzip";
+ header.setValue("Accept-Encoding", acceptEncodings);
+ }
+
+ return header;
+}
+
+/** Sets the list of IPs whose geo information we want to request. */
+void
+GeoIpRequest::setRequest(const QList<QHostAddress> &ips)
+{
+ _request = "format=long&ip=";
+ int ipcount = ips.size();
+
+ /* Add each IP to a comma-delimited list. */
+ for (int i = 0; i < ipcount; i++) {
+ _request.append(ips.at(i).toString());
+ if (i < ipcount-1) {
+ _request.append(",");
+ }
+ }
+ _ips = ips;
+}
+
+/** Formats the request as an HTTP POST request. */
+QByteArray
+GeoIpRequest::request() const
+{
+ /* Create the header and append the request content. */
+ QString request = createHeader().toString() + _request;
+ return request.toAscii();
+}
+
+/** Returns true if this request contains <b>ip</b>. */
+bool
+GeoIpRequest::contains(const QHostAddress &ip) const
+{
+ return _ips.contains(ip);
+}
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpRequest.h (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/GeoIpRequest.h)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpRequest.h (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpRequest.h 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,62 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file GeoIpRequest.h
+** \version $Id$
+** \brief A formatted request for GeoIP information for one or more IPs
+*/
+
+#ifndef _GEOIPREQUEST_H
+#define _GEOIPREQUEST_H
+
+#include <QList>
+#include <QString>
+#include <QHttpRequestHeader>
+
+class QHostAddress;
+class QByteArray;
+
+
+class GeoIpRequest
+{
+public:
+ /** Constructor */
+ GeoIpRequest(int id) : _id(id) {}
+
+ /** Sets the Host: field in this request's header. */
+ void setHost(const QString &host) { _host = host; }
+ /** Sets the page path in this request's header. */
+ void setPage(const QString &page) { _page = page; }
+ /** Sets the list of IPs whose geo information we want to request. */
+ void setRequest(const QList<QHostAddress> &ips);
+ /** Returns true if this request contains <b>ip</b>. */
+ bool contains(const QHostAddress &ip) const;
+
+ /** Returns the request's identifier. */
+ int id() const { return _id; }
+ /** Returns the number of IP addresses contained in this request. */
+ int size() const { return _ips.size(); }
+ /** Formats the request as an HTTP POST request */
+ QByteArray request() const;
+
+private:
+ /** Creates an HTTP header for this request. */
+ QHttpRequestHeader createHeader() const;
+
+ int _id; /**< Request identifier */
+ QString _host; /**< Host: field value. */
+ QString _page; /**< Page giving us the geo ip information. */
+ QString _request; /**< Formatted Geo IP request string. */
+ QList<QHostAddress> _ips; /**< List of IP addresses in this request. */
+};
+
+#endif
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpResolver.cpp (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/GeoIpResolver.cpp)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpResolver.cpp (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpResolver.cpp 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,283 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file GeoIpResolver.cpp
+** \version $Id$
+** \brief Requests GeoIP information and caches the result
+*/
+
+#include "GeoIpResolver.h"
+#include "GeoIpRequest.h"
+#include "GeoIpResponse.h"
+#include "GeoIp.h"
+#include "Vidalia.h"
+
+#include "stringutil.h"
+#include "TorSslSocket.h"
+
+/** Host for the GeoIP information. */
+#define GEOIP_HOST "geoips.vidalia-project.net"
+/** The SSL GeoIP service runs on port 443. */
+#define GEOIP_SSL_PORT 443
+/** Page that we request the GeoIP information from. */
+#define GEOIP_PAGE "/cgi-bin/geoip.py"
+
+
+/** Default constructor. */
+GeoIpResolver::GeoIpResolver(QObject *parent)
+ : QObject(parent)
+{
+ _socksAddr = QHostAddress::LocalHost;
+ _socksPort = 9050;
+ _cache = new GeoIpCache(this);
+}
+
+/** Sets the address and port of Tor, through which GeoIP requests will be
+ * made. */
+void
+GeoIpResolver::setSocksHost(const QHostAddress &addr, quint16 port)
+{
+ _socksAddr = addr;
+ _socksPort = port;
+}
+
+/** Resolves <b>ip</b> to geographic information if it is cached. A resolved()
+ * signal will be emitted and true returned if we have cached geographic
+ * information for <b>ip</b>. Otherwise, this returns false. */
+bool
+GeoIpResolver::resolveFromCache(const QHostAddress &ip)
+{
+ if (_cache->contains(ip)) {
+ emit resolved(-1, QList<GeoIp>() << _cache->geoIpForAddress(ip));
+ return true;
+ }
+ return false;
+}
+
+/** Resolves a list of IPs to a geographic location, but only those which are
+ * cached. Returns a list of IPs that were not in the cache. */
+QList<QHostAddress>
+GeoIpResolver::resolveFromCache(const QList<QHostAddress> &ips)
+{
+ QList<GeoIp> cached;
+
+ /* Build a list of which IPs have cached GeoIp information */
+ foreach (QHostAddress ip, ips) {
+ if (_cache->contains(ip))
+ cached << _cache->geoIpForAddress(ip);
+ }
+
+ /* If any were cached, emit their results now */
+ if (cached.size() > 0) {
+ vInfo("Resolved %1 GeoIP entries from cache.").arg(cached.size());
+ emit resolved(-1, cached);
+ }
+ return ips;
+}
+
+/** Resolves a single IP to a geographic location. */
+int
+GeoIpResolver::resolve(const QHostAddress &ip)
+{
+ return resolve(QList<QHostAddress>() << ip);
+}
+
+/** Called when the socket has connected to the Geo IP host. */
+void
+GeoIpResolver::connected()
+{
+ /* Find the socket and request for whoever called this slot */
+ QAbstractSocket *socket = dynamic_cast<QAbstractSocket *>(sender());
+ if (!_requestList.contains(socket)) {
+ return;
+ }
+ GeoIpRequest *req = static_cast<GeoIpRequest *>(_requestList.value(socket));
+
+ vInfo("Connected to the GeoIP host. Sending request for %1 uncached "
+ "GeoIP entries. (request id %2)").arg(req->size()).arg(req->id());
+
+ /* Send the request */
+ socket->write(req->request());
+}
+
+/** Called when the socket has disconnected from the Geo IP host. */
+void
+GeoIpResolver::disconnected()
+{
+ /* Find the socket and request for whoever called this slot */
+ QAbstractSocket *socket = dynamic_cast<QAbstractSocket *>(sender());
+ if (!_requestList.contains(socket)) {
+ return;
+ }
+ GeoIpRequest *req = static_cast<GeoIpRequest *>(_requestList.take(socket));
+
+ /* Read and parse the response header */
+ GeoIpResponse response = GeoIpResponse(socket->readAll());
+
+ /* Check the response code and see what we got */
+ if (response.statusCode() == 200) {
+ /* We got a 200 OK, so get the Geo IP information from the response body
+ * and cache the results. */
+ parseGeoIpResponse(response.content(), req);
+ } else {
+ /* We failed to get the Geo IP information, so emit resolveFailed and
+ * include the HTTP status message. */
+ vWarn("GeoIP resolution failed (request id %1): %2").arg(req->id())
+ .arg(response.statusMessage());
+ emit resolveFailed(req->id(), response.statusMessage());
+ }
+ /* Close the socket and clean up */
+ socket->close();
+ delete socket;
+ delete req;
+}
+
+void
+GeoIpResolver::parseGeoIpResponse(const QByteArray &response,
+ GeoIpRequest *request)
+{
+ QList<GeoIp> geoIpList;
+ QHash<QString,QString> keyvals;
+ QHostAddress ip, from, to;
+ QString city, region, country, cc;
+ float latitude, longitude;
+ GeoIp geoIp;
+ int numCached = 0;
+ bool ok;
+
+ QStringList lines = QString(response).split("\n", QString::SkipEmptyParts);
+ foreach (QString line, lines) {
+ /* Split the key=value formatted GeoIP record into keys and values */
+ QHash<QString,QString> keyvals = string_parse_keyvals(line.trimmed(), &ok);
+ if (! ok)
+ goto err;
+
+ /* Extract each of the required fields from the GeoIP record */
+ ip = QHostAddress(keyvals.value("IP"));
+ if (ip.isNull())
+ goto err;
+ latitude = keyvals.value("LAT").toFloat(&ok);
+ if (! ok)
+ goto err;
+ longitude = keyvals.value("LON").toFloat(&ok);
+ if (! ok)
+ goto err;
+
+ /* Each of these fields is optional */
+ city = keyvals.value("CITY");
+ region = keyvals.value("REGION");
+ country = keyvals.value("COUNTRY");
+ cc = keyvals.value("CC");
+
+ geoIp = GeoIp(ip, latitude, longitude, city, region, country, cc);
+ if (! geoIp.isValid())
+ goto err;
+
+ if (request->contains(ip)) {
+ if (! _cache->contains(ip)) {
+ from = QHostAddress(keyvals.value("FROM"));
+ to = QHostAddress(keyvals.value("TO"));
+ if (! from.isNull() && ! to.isNull())
+ _cache->addToCache(from, to, geoIp);
+ else
+ _cache->addToCache(geoIp);
+ numCached++;
+ }
+
+ geoIpList << geoIp;
+ continue;
+
+err:
+ vInfo("Ignored improperly formatted GeoIP record (request id %1): %2")
+ .arg(line).arg(request->id());
+ } else {
+ /* This item wasn't requested, so just log it and ignore. */
+ vWarn("Received a GeoIP entry for IP address %1 that was not included "
+ "in the initial request. (request id %2)").arg(ip)
+ .arg(request->id());
+ }
+ }
+ /* If new results were cached, save them to disk */
+ if (numCached > 0)
+ _cache->saveToDisk();
+
+ /* Emit the results */
+ vInfo("Parsed %1 entries from the GeoIP response. (request id %2)")
+ .arg(geoIpList.size()).arg(request->id());
+ emit resolved(request->id(), geoIpList);
+}
+
+/** Called when an error has occurred requesting Geo IP information. */
+void
+GeoIpResolver::socketError(const QString &errorString)
+{
+ /* Find the socket and request for whoever called this slot */
+ QAbstractSocket *socket = dynamic_cast<QAbstractSocket *>(sender());
+ if (!_requestList.contains(socket)) {
+ return;
+ }
+
+ /* We expect a remote host to close the socket, because that's how the HTTP
+ * server tells us he's done talking to us. */
+ if (socket->error() != QAbstractSocket::RemoteHostClosedError) {
+ /* Emit the failure and clean up */
+ GeoIpRequest *req = static_cast<GeoIpRequest *>(_requestList.take(socket));
+ emit resolveFailed(req->id(), errorString);
+ socket->abort();
+ vWarn("GeoIP request socket error (request id %1): %2").arg(req->id())
+ .arg(errorString);
+ delete socket;
+ delete req;
+ }
+}
+
+/** Creates an HTTP request for Geo IP information. */
+GeoIpRequest*
+GeoIpResolver::createRequest(const QList<QHostAddress> &ips)
+{
+ static int id = -1;
+ GeoIpRequest *request = new GeoIpRequest(++id);
+ request->setHost(GEOIP_HOST);
+ request->setPage(GEOIP_PAGE);
+ request->setRequest(ips);
+ return request;
+}
+
+/** Resolves a list of IPs to a geographic location. */
+int
+GeoIpResolver::resolve(const QList<QHostAddress> &ips)
+{
+ /* Resolve the cached IPs and get a list of IPs that still need to be
+ * resolved to a lat and long. */
+ QList<QHostAddress> uncached = resolveFromCache(ips);
+ if (! uncached.size())
+ return -1;
+
+ /* Create a socket used to request the geo ip information. */
+ TorSslSocket *socket = new TorSslSocket(_socksAddr, _socksPort);
+ connect(socket, SIGNAL(connectedToRemoteHost()), this, SLOT(connected()),
+ Qt::QueuedConnection);
+ connect(socket, SIGNAL(socketError(QString)),
+ this, SLOT(socketError(QString)),
+ Qt::QueuedConnection);
+ connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected()),
+ Qt::QueuedConnection);
+ GeoIpRequest *request = createRequest(uncached);
+ _requestList.insert(socket, request);
+
+ /* Connect so we can send our request and return the request ID. */
+ vInfo("Opening an SSL connection to the GeoIP host at %1:%2 (request id %3)")
+ .arg(GEOIP_HOST).arg(GEOIP_SSL_PORT).arg(request->id());
+ socket->connectToRemoteHost(GEOIP_HOST, GEOIP_SSL_PORT, true);
+
+ return request->id();
+}
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpResolver.h (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/GeoIpResolver.h)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpResolver.h (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpResolver.h 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,86 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+G** \file GeoIpResolver.h
+** \version $Id$
+** \brief Requests GeoIP information and caches the result
+*/
+
+#ifndef _GEOIPRESOLVER_H
+#define _GEOIPRESOLVER_H
+
+#include "GeoIpCache.h"
+
+#include <QObject>
+#include <QList>
+#include <QHash>
+#include <QHostAddress>
+
+class GeoIp;
+class GeoIpRequest;
+class GeoIpResponse;
+class QString;
+class QAbstractSocket;
+
+
+class GeoIpResolver : public QObject
+{
+ Q_OBJECT
+
+public:
+ /** Default constructor. */
+ GeoIpResolver(QObject *parent = 0);
+
+ /** Sets the address and port of Tor, through which GeoIP requests will be
+ * made. */
+ void setSocksHost(const QHostAddress &addr, quint16 port);
+ /** Resolves a single IP to a geographic location. */
+ int resolve(const QHostAddress &ip);
+ /** Resolves a list of IPs to a geographic location. */
+ int resolve(const QList<QHostAddress> &ips);
+ /** Resolves <b>ip</b> to geographic information only if it is cached. */
+ bool resolveFromCache(const QHostAddress &ip);
+ /** Resolves a list of IPs to a geographic location, but only those which
+ * are cached. Returns a list of which IPs were not cached. */
+ QList<QHostAddress> resolveFromCache(const QList<QHostAddress> &ips);
+
+signals:
+ /** Emitted when a list of IPs have been resolved to lat/long. */
+ void resolved(int id, const QList<GeoIp> &geoips);
+ /** Emitted when a resolve has failed. */
+ void resolveFailed(int id, const QString &errorString);
+
+private slots:
+ /** Called when the socket has connected to the Geo IP host. */
+ void connected();
+ /** Called when the socket has disconnected from the Geo IP host. */
+ void disconnected();
+ /** Called when an error has occurred getting the Geo IP information. */
+ void socketError(const QString &errorString);
+
+private:
+ /** Creates an HTTP request for Geo IP information. */
+ GeoIpRequest* createRequest(const QList<QHostAddress> &ips);
+
+ void parseGeoIpResponse(const QByteArray &response, GeoIpRequest *request);
+
+ /**< Cached GeoIp objects. */
+ GeoIpCache* _cache;
+ /**< List of sockets used for requests. */
+ QHash<QAbstractSocket *,GeoIpRequest*> _requestList;
+ /** Tor's SocksListenAddress. */
+ QHostAddress _socksAddr;
+ /** Tor's SocksPort. */
+ quint16 _socksPort;
+};
+
+#endif
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpResponse.cpp (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/GeoIpResponse.cpp)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpResponse.cpp (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpResponse.cpp 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,137 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file GeoIpResponse.cpp
+** \version $Id$
+** \brief Parses a response to a previous GeoIP request
+*/
+
+#include "GeoIpResponse.h"
+#include "GeoIp.h"
+#include "Vidalia.h"
+
+#include "ZlibByteArray.h"
+
+#include <QByteArray>
+#include <QStringList>
+#include <QHttpResponseHeader>
+
+/** Status code for a successful HTTP request. */
+#define STATUS_HTTP_OK 200
+/** Status code for content encoding errors. */
+#define STATUS_CONTENT_ENCODING_ERR 601
+/** Status code for transfer encoding errors. */
+#define STATUS_TRANSFER_ENCODING_ERR 602
+
+
+GeoIpResponse::GeoIpResponse(const QByteArray &response)
+{
+ QString errmsg;
+
+ /* Parse out the header */
+ int headerPos = response.indexOf("\r\n\r\n");
+ _header = QHttpResponseHeader(QString(response.mid(0, headerPos)));
+
+ /* Parse out the Geo IP information, if any was included. */
+ if (headerPos > 0 && _header.statusCode() == STATUS_HTTP_OK) {
+ _content = response.mid(headerPos+4);
+
+ if (_header.hasKey("Transfer-Encoding")) {
+ QString encoding = _header.value("Transfer-Encoding");
+ if (encoding == "chunked") {
+ _content = decodeChunked(_content);
+ if (_content.isEmpty()) {
+ _header.setStatusLine(STATUS_TRANSFER_ENCODING_ERR,
+ QString("Failed to decode chunked response"));
+ return;
+ }
+ } else {
+ _header.setStatusLine(STATUS_TRANSFER_ENCODING_ERR,
+ QString("Unknown transfer encoding '%1'").arg(encoding));
+ return;
+ }
+ }
+
+ if (_header.hasKey("Content-Encoding")) {
+ ZlibByteArray::CompressionMethod method;
+ QString encoding = _header.value("Content-Encoding");
+ if (encoding == "gzip" || encoding == "x-gzip") {
+ method = ZlibByteArray::Gzip;
+ } else if (encoding == "deflate" || encoding == "x-deflate") {
+ method = ZlibByteArray::Zlib;
+ } else if (encoding == "text/plain") {
+ method = ZlibByteArray::None;
+ } else {
+ _header.setStatusLine(STATUS_CONTENT_ENCODING_ERR,
+ QString("Unknown content encoding '%1'").arg(encoding));
+ return;
+ }
+
+ _content = ZlibByteArray::uncompress(_content, method, &errmsg);
+ if (_content.isEmpty()) {
+ _header.setStatusLine(STATUS_CONTENT_ENCODING_ERR,
+ QString("Content decoding using method '%1' failed: %2")
+ .arg(encoding).arg(errmsg));
+ return;
+ }
+ }
+ }
+}
+
+QByteArray
+GeoIpResponse::decodeChunked(const QByteArray &chunked)
+{
+ QByteArray unchunked;
+ QString sizeString;
+ int eol, chunkedlen, chunksize, offset = 0;
+ bool ok;
+
+ chunkedlen = chunked.length();
+ while (offset < chunkedlen) {
+ eol = chunked.indexOf("\r\n", offset);
+ if (eol < 0)
+ return QByteArray();
+ sizeString = QString::fromAscii(chunked.mid(offset, eol-offset));
+ offset = eol + 2; /* Skip past the CRLF */
+
+ if (sizeString.indexOf(";") >= 0)
+ sizeString.truncate(sizeString.indexOf(";"));
+ chunksize = sizeString.toInt(&ok, 16);
+ if (!ok || chunksize > chunkedlen - offset)
+ return QByteArray();
+ if (!chunksize)
+ break; /* Last chunk. Ignore the trailer. */
+
+ unchunked.append(chunked.mid(offset, chunksize));
+ offset += chunksize;
+ offset += 2; /* CRLF after each chunk */
+ }
+ return unchunked;
+}
+
+int
+GeoIpResponse::statusCode() const
+{
+ return _header.statusCode();
+}
+
+QString
+GeoIpResponse::statusMessage() const
+{
+ return _header.reasonPhrase();
+}
+
+QByteArray
+GeoIpResponse::content() const
+{
+ return _content;
+}
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpResponse.h (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/GeoIpResponse.h)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpResponse.h (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/GeoIpResponse.h 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,58 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file GeoIpResponse.h
+** \version $Id$
+** \brief Parses a response to a previous GeoIP request
+*/
+
+#ifndef _GEOIPRESPONSE_H
+#define _GEOIPRESPONSE_H
+
+#include <QList>
+#include <QByteArray>
+#include <QHttpResponseHeader>
+
+class GeoIp;
+class QString;
+class QStringList;
+
+
+class GeoIpResponse
+{
+public:
+ /** Constructor. Parses the response data for an HTTP header and Geo IP
+ * information. */
+ GeoIpResponse(const QByteArray &response);
+
+ /** Returns the HTTP status code for this response.
+ */
+ int statusCode() const;
+
+ /** Returns the HTTP status message for this response.
+ */
+ QString statusMessage() const;
+
+ /** Returns the Geo IP information contained in this response.
+ */
+ QByteArray content() const;
+
+private:
+ /** Decodes a <b>chunked</b> transfer encoding. Returns the unchunked
+ * result on success, or an empty QByteArray if decoding fails. */
+ QByteArray decodeChunked(const QByteArray &chunked);
+
+ QHttpResponseHeader _header; /**< HTTP response header. */
+ QByteArray _content; /**< Geo IP information in this response. */
+};
+
+#endif
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/NetViewer.cpp (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/NetViewer.cpp)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/NetViewer.cpp (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/NetViewer.cpp 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,593 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file NetViewer.cpp
+** \version $Id$
+** \brief Displays a map of the Tor network and the user's circuits
+*/
+
+#include "NetViewer.h"
+#include "RouterInfoDialog.h"
+#include "RouterListItem.h"
+#include "Vidalia.h"
+#include "VMessageBox.h"
+
+#include <QMessageBox>
+#include <QHeaderView>
+
+#define IMG_MOVE ":/images/22x22/move-map.png"
+#define IMG_ZOOMIN ":/images/22x22/zoom-in.png"
+#define IMG_ZOOMOUT ":/images/22x22/zoom-out.png"
+
+/** Number of milliseconds to wait after the arrival of the last descriptor whose
+ * IP needs to be resolved to geographic information, in case more descriptors
+ * arrive. Then we can simply lump the IPs into a single request. */
+#define MIN_RESOLVE_QUEUE_DELAY (10*1000)
+/** Maximum number of milliseconds to wait after the arrival of the first
+ * IP address into the resolve queue, before we flush the entire queue. */
+#define MAX_RESOLVE_QUEUE_DELAY (30*1000)
+
+
+/** Constructor. Loads settings from VidaliaSettings.
+ * \param parent The parent widget of this NetViewer object.\
+ */
+NetViewer::NetViewer()
+ : VidaliaPanel()
+{
+ /* Invoke Qt Designer generated QObject setup routine */
+ ui.setupUi(this);
+
+#if defined(Q_WS_MAC)
+ ui.actionHelp->setShortcut(QString("Ctrl+?"));
+#endif
+
+ /* Pressing 'Esc' or 'Ctrl+W' will close the window */
+ ui.actionClose->setShortcut(QString("Esc"));
+ Vidalia::createShortcut("Ctrl+W", this, ui.actionClose, SLOT(trigger()));
+
+ /* Get the TorControl object */
+ _torControl = Vidalia::torControl();
+ _torControl->setEvent(TorEvents::NewDescriptor, this, true);
+ _torControl->setEvent(TorEvents::CircuitStatus, this, true);
+ _torControl->setEvent(TorEvents::StreamStatus, this, true);
+ _torControl->setEvent(TorEvents::AddressMap, this, true);
+
+ /* Change the column widths of the tree widgets */
+ ui.treeRouterList->header()->
+ resizeSection(RouterListWidget::StatusColumn, 25);
+ ui.treeRouterList->header()->
+ resizeSection(RouterListWidget::CountryColumn, 25);
+ ui.treeCircuitList->header()->
+ resizeSection(CircuitListWidget::ConnectionColumn, 235);
+
+ /* Create the TorMapWidget and add it to the dialog */
+#if defined(USE_MARBLE)
+ _map = new TorMapWidget();
+ connect(_map, SIGNAL(displayRouterInfo(QString)),
+ this, SLOT(displayRouterInfo(QString)));
+ connect(ui.actionZoomFullScreen, SIGNAL(triggered()),
+ this, SLOT(toggleFullScreen()));
+ Vidalia::createShortcut("ESC", _map, this, SLOT(toggleFullScreen()));
+#else
+ _map = new TorMapImageView();
+ ui.actionZoomFullScreen->setVisible(false);
+#endif
+ ui.gridLayout->addWidget(_map);
+
+
+ /* Connect zoom buttons to TorMapWidget zoom slots */
+ connect(ui.actionZoomIn, SIGNAL(triggered()), this, SLOT(zoomIn()));
+ connect(ui.actionZoomOut, SIGNAL(triggered()), this, SLOT(zoomOut()));
+ connect(ui.actionZoomToFit, SIGNAL(triggered()), _map, SLOT(zoomToFit()));
+
+ /* Create the timer that will be used to update the router list once every
+ * hour. We still receive the NEWDESC event to get new descriptors, but this
+ * needs to be called to get rid of any descriptors that were removed. */
+ _refreshTimer.setInterval(60*60*1000);
+ connect(&_refreshTimer, SIGNAL(timeout()), this, SLOT(refresh()));
+
+ /* Set up the timers used to group together GeoIP requests for new
+ * descriptors arriving within MIN_RESOLVE_QUEUE_DELAY milliseconds, but no
+ * more than MAX_RESOLVE_QUEUE_DELAY milliseconds of each other. */
+ _minResolveQueueTimer.setSingleShot(true);
+ connect(&_minResolveQueueTimer, SIGNAL(timeout()), this, SLOT(resolve()));
+ _maxResolveQueueTimer.setSingleShot(true);
+ connect(&_maxResolveQueueTimer, SIGNAL(timeout()), this, SLOT(resolve()));
+
+ /* Connect the necessary slots and signals */
+ connect(ui.actionHelp, SIGNAL(triggered()), this, SLOT(help()));
+ connect(ui.actionRefresh, SIGNAL(triggered()), this, SLOT(refresh()));
+ connect(ui.treeRouterList, SIGNAL(routerSelected(QList<RouterDescriptor>)),
+ this, SLOT(routerSelected(QList<RouterDescriptor>)));
+ connect(ui.treeRouterList, SIGNAL(zoomToRouter(QString)),
+ _map, SLOT(zoomToRouter(QString)));
+ connect(ui.treeCircuitList, SIGNAL(circuitSelected(Circuit)),
+ this, SLOT(circuitSelected(Circuit)));
+ connect(ui.treeCircuitList, SIGNAL(circuitRemoved(CircuitId)),
+ _map, SLOT(removeCircuit(CircuitId)));
+ connect(ui.treeCircuitList, SIGNAL(zoomToCircuit(CircuitId)),
+ _map, SLOT(zoomToCircuit(CircuitId)));
+ connect(ui.treeCircuitList, SIGNAL(closeCircuit(CircuitId)),
+ _torControl, SLOT(closeCircuit(CircuitId)));
+ connect(ui.treeCircuitList, SIGNAL(closeStream(StreamId)),
+ _torControl, SLOT(closeStream(StreamId)));
+
+ /* Respond to changes in the status of the control connection */
+ connect(_torControl, SIGNAL(authenticated()), this, SLOT(onAuthenticated()));
+ connect(_torControl, SIGNAL(disconnected()), this, SLOT(onDisconnected()));
+
+ /* Connect the slot to find out when geoip information has arrived */
+ connect(&_geoip, SIGNAL(resolved(int, QList<GeoIp>)),
+ this, SLOT(resolved(int, QList<GeoIp>)));
+}
+
+/** Called when the user changes the UI translation. */
+void
+NetViewer::retranslateUi()
+{
+ ui.retranslateUi(this);
+ ui.treeRouterList->retranslateUi();
+ ui.treeCircuitList->retranslateUi();
+
+ if (ui.treeRouterList->selectedItems().size()) {
+ QList<RouterDescriptor> routers;
+ foreach (QTreeWidgetItem *item, ui.treeRouterList->selectedItems()) {
+ routers << dynamic_cast<RouterListItem *>(item)->descriptor();
+ }
+ ui.textRouterInfo->display(routers);
+ } else if (ui.treeCircuitList->selectedItems().size()) {
+ QList<RouterDescriptor> routers;
+ QTreeWidgetItem *item = ui.treeCircuitList->selectedItems()[0];
+ Circuit circuit = dynamic_cast<CircuitItem*>(item)->circuit();
+ foreach (QString id, circuit.routerIDs()) {
+ RouterListItem *item = ui.treeRouterList->findRouterById(id);
+ if (item)
+ routers.append(item->descriptor());
+ }
+ ui.textRouterInfo->display(routers);
+ }
+}
+
+/** Display the network map window. If there are geoip requests waiting in the
+ * queue, start the queue timers now. */
+void
+NetViewer::showWindow()
+{
+ if (!_resolveQueue.isEmpty()) {
+ _minResolveQueueTimer.start(MIN_RESOLVE_QUEUE_DELAY);
+ _maxResolveQueueTimer.start(MAX_RESOLVE_QUEUE_DELAY);
+ }
+ show();
+}
+
+/** Loads data into map, lists and starts timer when we get connected*/
+void
+NetViewer::onAuthenticated()
+{
+ _geoip.setSocksHost(_torControl->getSocksAddress(),
+ _torControl->getSocksPort());
+ refresh();
+ _refreshTimer.start();
+ ui.actionRefresh->setEnabled(true);
+}
+
+/** Clears map, lists and stops timer when we get disconnected */
+void
+NetViewer::onDisconnected()
+{
+ clear();
+ _refreshTimer.stop();
+ ui.actionRefresh->setEnabled(false);
+}
+
+/** Custom event handler. Catches the new descriptor events. */
+void
+NetViewer::customEvent(QEvent *event)
+{
+ int type = event->type();
+
+ if (type == CustomEventType::NewDescriptorEvent) {
+ /* New router descriptor, so load it and add it to the list */
+ NewDescriptorEvent *nde = (NewDescriptorEvent *)event;
+ newDescriptors(nde->descriptorIDs());
+ } else if (type == CustomEventType::CircuitEvent) {
+ /* New or updated circuit information */
+ CircuitEvent *ce = (CircuitEvent *)event;
+ addCircuit(ce->circuit());
+ } else if (type == CustomEventType::StreamEvent) {
+ /* New or updated stream information */
+ StreamEvent *se = (StreamEvent *)event;
+ addStream(se->stream());
+ } else if (type == CustomEventType::AddressMapEvent) {
+ /* New or updated address mapping. We store the reverse of the new
+ * mapping, so we can go from an IP address back to a hostname. */
+ AddressMapEvent *ae = (AddressMapEvent *)event;
+ _addressMap.add(ae->to(), ae->from(), ae->expires());
+ }
+
+ /* Update the world map */
+ _map->update();
+}
+
+/** Reloads the lists of routers, circuits that Tor knows about */
+void
+NetViewer::refresh()
+{
+ /* Don't let the user refresh while we're refreshing. */
+ ui.actionRefresh->setEnabled(false);
+
+ /* Clear the data */
+ clear();
+
+ /* Load router information */
+ loadNetworkStatus();
+ /* Load existing address mappings */
+ loadAddressMap();
+ /* Load Circuits and Streams information */
+ loadConnections();
+
+ /* Ok, they can refresh again. */
+ ui.actionRefresh->setEnabled(true);
+}
+
+/** Clears the lists and the map */
+void
+NetViewer::clear()
+{
+ /* Clear the resolve queue and map */
+ _resolveMap.clear();
+ _resolveQueue.clear();
+ /* Clear the network map */
+ _map->clear();
+ _map->update();
+ /* Clear the address map */
+ _addressMap.clear();
+ /* Clear the lists of routers, circuits, and streams */
+ ui.treeRouterList->clearRouters();
+ ui.treeCircuitList->clearCircuits();
+ ui.textRouterInfo->clear();
+}
+
+/** Loads a list of all current address mappings. */
+void
+NetViewer::loadAddressMap()
+{
+ /* We store the reverse address mappings, so we can go from a numeric value
+ * back to a likely more meaningful hostname to display for the user. */
+ _addressMap = _torControl->getAddressMap().reverse();
+}
+
+/** Loads a list of all current circuits and streams. */
+void
+NetViewer::loadConnections()
+{
+ /* Load all circuits */
+ CircuitList circuits = _torControl->getCircuits();
+ foreach (Circuit circuit, circuits) {
+ addCircuit(circuit);
+ }
+ /* Now load all streams */
+ StreamList streams = _torControl->getStreams();
+ foreach (Stream stream, streams) {
+ addStream(stream);
+ }
+
+ /* Update the map */
+ _map->update();
+}
+
+/** Adds <b>circuit</b> to the map and the list */
+void
+NetViewer::addCircuit(const Circuit &circuit)
+{
+ /* Add the circuit to the list of all current circuits */
+ ui.treeCircuitList->addCircuit(circuit);
+ /* Plot the circuit on the map */
+ _map->addCircuit(circuit.id(), circuit.routerIDs());
+}
+
+/** Adds <b>stream</b> to its associated circuit on the list of all circuits. */
+void
+NetViewer::addStream(const Stream &stream)
+{
+ QString target = stream.targetAddress();
+ QHostAddress addr(target);
+
+ /* If the stream's target has an IP address instead of a host name,
+ * check our cache for an existing reverse address mapping. */
+ if (!addr.isNull() && _addressMap.isMapped(target)) {
+ /* Replace the IP address in the stream event with the original
+ * hostname */
+ ui.treeCircuitList->addStream(
+ Stream(stream.id(), stream.status(), stream.circuitId(),
+ _addressMap.mappedTo(target), stream.targetPort()));
+ } else {
+ ui.treeCircuitList->addStream(stream);
+ }
+}
+
+/** Called when the user selects the "Help" action from the toolbar. */
+void
+NetViewer::help()
+{
+#if 0
+ emit helpRequested("netview");
+#endif
+}
+
+/** Retrieves a list of all running routers from Tor and their descriptors,
+ * and adds them to the RouterListWidget. */
+void
+NetViewer::loadNetworkStatus()
+{
+ NetworkStatus networkStatus = _torControl->getNetworkStatus();
+ foreach (RouterStatus rs, networkStatus) {
+ if (!rs.isRunning())
+ continue;
+
+ RouterDescriptor rd = _torControl->getRouterDescriptor(rs.id());
+ if (!rd.isEmpty())
+ addRouter(rd);
+ }
+}
+
+/** Adds a router to our list of servers and retrieves geographic location
+ * information for the server. */
+void
+NetViewer::addRouter(const RouterDescriptor &rd)
+{
+ /* Add the descriptor to the list of server */
+ ui.treeRouterList->addRouter(rd);
+
+ /* Add this IP to a list of addresses whose geographic location we'd like to
+ * find, but not for special purpose descriptors (e.g., bridges). This
+ * check is only valid for Tor >= 0.2.0.13-alpha. */
+ if (_torControl->getTorVersion() >= 0x020013) {
+ DescriptorAnnotations annotations =
+ _torControl->getDescriptorAnnotations(rd.id());
+ if (!annotations.contains("purpose"))
+ addToResolveQueue(rd.ip(), rd.id());
+ } else {
+ addToResolveQueue(rd.ip(), rd.id());
+ }
+}
+
+/** Called when a NEWDESC event arrives. Retrieves new router descriptors
+ * for the router identities given in <b>ids</b> and updates the router
+ * list and network map. */
+void
+NetViewer::newDescriptors(const QStringList &ids)
+{
+ foreach (QString id, ids) {
+ RouterDescriptor rd = _torControl->getRouterDescriptor(id);
+ if (!rd.isEmpty())
+ addRouter(rd); /* Updates the existing entry */
+ }
+}
+
+/** Adds an IP address to the resolve queue and updates the queue timers. */
+void
+NetViewer::addToResolveQueue(const QHostAddress &ip, const QString &id)
+{
+ QString ipstr = ip.toString();
+ if (!_resolveMap.values(ipstr).contains(id)) {
+ /* Remember which server ids belong to which IP addresses */
+ _resolveMap.insertMulti(ipstr, id);
+ }
+
+ if (!_resolveQueue.contains(ip) && !_geoip.resolveFromCache(ip)) {
+ /* Add the IP to the queue of IPs waiting for geographic information */
+ _resolveQueue << ip;
+
+ /* Wait MIN_RESOLVE_QUEUE_DELAY after the last item inserted into the
+ * queue, before sending the resolve request. */
+ _minResolveQueueTimer.start(MIN_RESOLVE_QUEUE_DELAY);
+
+ /* Do not wait longer than MAX_RESOLVE_QUEUE_DELAY from the time the first
+ * item is inserted into the queue, before flushing and resolving the
+ * queue. */
+ if (_resolveQueue.size() == 1) {
+ _maxResolveQueueTimer.start(MAX_RESOLVE_QUEUE_DELAY);
+ }
+ }
+}
+
+/** Called when the user selects a circuit from the circuit and streams
+ * list. */
+void
+NetViewer::circuitSelected(const Circuit &circuit)
+{
+ /* Clear any selected items. */
+ ui.treeRouterList->deselectAll();
+ ui.textRouterInfo->clear();
+ _map->deselectAll();
+
+ /* Select the items on the map and in the list */
+ _map->selectCircuit(circuit.id());
+
+ QList<RouterDescriptor> routers;
+
+ foreach (QString id, circuit.routerIDs()) {
+ /* Try to find and select each router in the path */
+ RouterListItem *item = ui.treeRouterList->findRouterById(id);
+ if (item)
+ routers.append(item->descriptor());
+ }
+
+ ui.textRouterInfo->display(routers);
+}
+
+/** Called when the user selects one or more routers from the router list. */
+void
+NetViewer::routerSelected(const QList<RouterDescriptor> &routers)
+{
+ _map->deselectAll();
+ ui.textRouterInfo->clear();
+ ui.textRouterInfo->display(routers);
+
+ /* XXX: Ideally we would also be able to select multiple pinpoints on the
+ * map. But our current map sucks and you can't even tell when one is
+ * selected anyway. Worry about this when we actually get to Marble.
+ */
+ if (routers.size() == 1)
+ _map->selectRouter(routers[0].id());
+}
+
+/** If there are any IPs in the resolve queue, do the request now. */
+void
+NetViewer::resolve()
+{
+ if (!_resolveQueue.isEmpty()) {
+ /* Send the request now if either the network map is visible, or the
+ * request is for more than a quarter of the servers in the list. */
+ if (isVisible() ||
+ (_resolveQueue.size() >= ui.treeRouterList->topLevelItemCount()/4)) {
+ vInfo("Sending GeoIP request for %1 IP addresses.")
+ .arg(_resolveQueue.size());
+ /* Flush the resolve queue and stop the timers */
+ _geoip.resolve(_resolveQueue);
+ _resolveQueue.clear();
+ }
+ }
+ /* Stop the queue timers. Only one should be active since the other is what
+ * called this slot, but calling stop() on a stopped timer does not hurt. */
+ _minResolveQueueTimer.stop();
+ _maxResolveQueueTimer.stop();
+}
+
+/** Called when a list of GeoIp information has been resolved. */
+void
+NetViewer::resolved(int id, const QList<GeoIp> &geoips)
+{
+ Q_UNUSED(id);
+
+ QString ip;
+ RouterListItem *router;
+
+ foreach (GeoIp geoip, geoips) {
+ /* Find all routers that are at this IP address */
+ ip = geoip.ip().toString();
+ QList<QString> ids = _resolveMap.values(ip);
+ _resolveMap.remove(ip);
+
+ /* Update their geographic location information with the results of this
+ * GeoIP query. */
+ foreach (QString id, ids) {
+ router = ui.treeRouterList->findRouterById(id);
+ if (router) {
+ /* Save the location information in the descriptor */
+ router->setLocation(geoip);
+ /* Plot the router on the map */
+ _map->addRouter(router->descriptor(), geoip);
+ }
+ }
+ }
+
+ /* Update the circuit lines */
+ foreach (Circuit circuit, ui.treeCircuitList->circuits()) {
+ _map->addCircuit(circuit.id(), circuit.routerIDs());
+ }
+
+ /* Repaint the map */
+ _map->update();
+}
+
+/** Called when the user selects a router on the network map. Displays a
+ * dialog with detailed information for the router specified by
+ * <b>id</b>.*/
+void
+NetViewer::displayRouterInfo(const QString &id)
+{
+ RouterInfoDialog dlg(_map->isFullScreen() ? static_cast<QWidget*>(_map)
+ : static_cast<QWidget*>(this));
+
+ /* Fetch the specified router's descriptor */
+ QStringList rd = _torControl->getRouterDescriptorText(id);
+ if (rd.isEmpty()) {
+ VMessageBox::warning(this, tr("Relay Not Found"),
+ tr("No details on the selected relay are available."),
+ VMessageBox::Ok);
+ return;
+ }
+
+ /* Fetch the router's network status information */
+ RouterStatus rs = _torControl->getRouterStatus(id);
+
+ dlg.setRouterInfo(rd, rs);
+
+ /* Populate the UI with information learned from a previous GeoIP request */
+ RouterListItem *item = ui.treeRouterList->findRouterById(id);
+ if (item)
+ dlg.setLocation(item->location());
+ else
+ dlg.setLocation(tr("Unknown"));
+
+ dlg.exec();
+}
+
+/* XXX: The following zoomIn() and zoomOut() slots are a hack. MarbleWidget
+ * does have zoomIn() and zoomOut() slots to which we could connect the
+ * buttons, but these slots currently don't force a repaint. So to see
+ * the zoom effect, the user has to click on the map after clicking one
+ * of the zoom buttons. Instead, we use the zoomViewBy() method, which
+ * DOES force a repaint.
+ */
+/** Called when the user clicks the "Zoom In" button. */
+void
+NetViewer::zoomIn()
+{
+#if defined(USE_MARBLE)
+ _map->zoomViewBy(40);
+#else
+ _map->zoomIn();
+#endif
+}
+
+/** Called when the user clicks the "Zoom Out" button. */
+void
+NetViewer::zoomOut()
+{
+#if defined(USE_MARBLE)
+ _map->zoomViewBy(-40);
+#else
+ _map->zoomOut();
+#endif
+}
+
+/** Called when the user clicks "Full Screen" or presses Escape on the map.
+ * Toggles the map between normal and a full screen viewing modes. */
+void
+NetViewer::toggleFullScreen()
+{
+ if (_map->isFullScreen()) {
+ /* Disabling full screen mode. Put the map back in its container. */
+ ui.gridLayout->addWidget(_map);
+ _map->setWindowState(_map->windowState() & ~Qt::WindowFullScreen);
+ } else {
+ /* Enabling full screen mode. Remove the map from the QGridLayout
+ * container and set its window state to full screen. */
+ _map->setParent(0);
+ _map->setWindowState(_map->windowState() | Qt::WindowFullScreen);
+ _map->show();
+ }
+}
+
+QString
+NetViewer::tabLabel() const
+{
+ return QString("Network Map");
+}
+
+QIcon
+NetViewer::tabIcon() const
+{
+ return QIcon();
+}
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/NetViewer.h (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/NetViewer.h)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/NetViewer.h (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/NetViewer.h 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,151 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file NetViewer.h
+** \version $Id$
+** \brief Displays a map of the Tor network and the user's circuits
+*/
+
+#ifndef _NETVIEWER_H
+#define _NETVIEWER_H
+
+#include "config.h"
+#include "ui_NetViewer.h"
+#include "VidaliaPanel.h"
+#include "GeoIpResolver.h"
+
+#if defined(USE_MARBLE)
+#include "TorMapWidget.h"
+#else
+#include "TorMapImageView.h"
+#endif
+
+#include "TorControl.h"
+
+#include <QIcon>
+#include <QMainWindow>
+#include <QString>
+#include <QStringList>
+#include <QEvent>
+#include <QTimer>
+#include <QHash>
+
+
+class NetViewer : public VidaliaPanel
+{
+ Q_OBJECT
+
+public:
+ /** Default constructor */
+ NetViewer();
+
+public slots:
+ /** Displays the network map window. */
+ void showWindow();
+ /** Loads a list of current circuits and streams. */
+ void loadConnections();
+ /** Adds <b>circuit</b> to the list and the map */
+ void addCircuit(const Circuit &circuit);
+ /** Adds <b>stream</b> to the list of circuits, under the appropriate
+ * circuit. */
+ void addStream(const Stream &stream);
+ /** Clears all known information */
+ void clear();
+
+protected:
+ /** Called to deliver a NEWDESC event from Tor. */
+ void customEvent(QEvent *event);
+ /** Called when the user changes the UI translation. */
+ void retranslateUi();
+
+private slots:
+ /** Called when the user selects the "Help" action on the toolbar. */
+ void help();
+ /** Called when the user selects the "Refresh" action on the toolbar */
+ void refresh();
+ /** Called when the user selects a circuit on the circuit list */
+ void circuitSelected(const Circuit &circuit);
+ /** Called when an IP has been resolved to geographic information. */
+ void resolved(int id, const QList<GeoIp> &geoips);
+ /** Called when the user selects one or more routers in the list. */
+ void routerSelected(const QList<RouterDescriptor> &routers);
+ /** Handles when we get connected to Tor network */
+ void onAuthenticated();
+ /** Handles when we get disconnected from Tor network */
+ void onDisconnected();
+ /** Resolves IP addresses in the resolve queue to geographic information. */
+ void resolve();
+ /** Called when the user selects a router on the network map. Displays a
+ * dialog with detailed information for the router specified by
+ * <b>id</b>.*/
+ void displayRouterInfo(const QString &id);
+ /** Called when the user clicks the "Zoom In" button. */
+ void zoomIn();
+ /** Called when the user clicks the "Zoom Out" button. */
+ void zoomOut();
+ /** Called when the user clicks "Full Screen" or presses Escape on the map.
+ * Toggles the map between normal and a full screen viewing modes. */
+ void toggleFullScreen();
+
+ /** Hook for displaying text in a panel's tab. */
+ QString tabLabel() const;
+ /** Hook for displaying an icon in a panel's tab. */
+ QIcon tabIcon() const;
+
+private:
+ /** Adds an IP address to the resolve queue and updates the queue timers. */
+ void addToResolveQueue(const QHostAddress &ip, const QString &id);
+ /** Retrieves a list of all running routers from Tor and their descriptors,
+ * and adds them to the RouterListWidget. */
+ void loadNetworkStatus();
+ /** Loads a list of address mappings from Tor. */
+ void loadAddressMap();
+ /** Adds a router to our list of servers and retrieves geographic location
+ * information for the server. */
+ void addRouter(const RouterDescriptor &rd);
+ /** Called when a NEWDESC event arrives. Retrieves new router descriptors
+ * for the router identities given in <b>ids</b> and updates the router list
+ * and network map. */
+ void newDescriptors(const QStringList &ids);
+
+ /** TorControl object used to talk to Tor. */
+ TorControl* _torControl;
+ /** Timer that fires once an hour to update the router list. */
+ QTimer _refreshTimer;
+ /** GeoIpResolver used to geolocate routers by IP address. */
+ GeoIpResolver _geoip;
+ /** Queue for IPs pending resolution to geographic information. */
+ QList<QHostAddress> _resolveQueue;
+ /** Maps pending GeoIP requests to server IDs. */
+ QHash<QString, QString> _resolveMap;
+ /** Stores a list of address mappings from Tor. */
+ AddressMap _addressMap;
+ /** Timer used to delay GeoIP requests for MIN_RESOLVE_QUEUE_DELAY
+ * milliseconds after we've inserted the last item into the queue. */
+ QTimer _minResolveQueueTimer;
+ /** Timer used to limit the delay of GeoIP requests to
+ * MAX_RESOLVE_QUEUE_DELAY milliseconds after inserting the first item
+ * into the queue. */
+ QTimer _maxResolveQueueTimer;
+
+ /** Widget that displays the Tor network map. */
+#if defined(USE_MARBLE)
+ TorMapWidget* _map;
+#else
+ TorMapImageView* _map;
+#endif
+
+ /** Qt Designer generated object **/
+ Ui::NetViewer ui;
+};
+
+#endif
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/NetViewer.ui (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/NetViewer.ui)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/NetViewer.ui (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/NetViewer.ui 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,393 @@
+<ui version="4.0" >
+ <class>NetViewer</class>
+ <widget class="QMainWindow" name="NetViewer" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>844</width>
+ <height>596</height>
+ </rect>
+ </property>
+ <property name="sizePolicy" >
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="contextMenuPolicy" >
+ <enum>Qt::NoContextMenu</enum>
+ </property>
+ <property name="windowTitle" >
+ <string>Tor Network Map</string>
+ </property>
+ <property name="windowIcon" >
+ <iconset resource="../res/vidalia.qrc" >:/images/32x32/applications-internet.png</iconset>
+ </property>
+ <property name="toolButtonStyle" >
+ <enum>Qt::ToolButtonTextUnderIcon</enum>
+ </property>
+ <widget class="QWidget" name="centralwidget" >
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QSplitter" name="splitter" >
+ <property name="contextMenuPolicy" >
+ <enum>Qt::NoContextMenu</enum>
+ </property>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="childrenCollapsible" >
+ <bool>false</bool>
+ </property>
+ <widget class="RouterListWidget" name="treeRouterList" >
+ <property name="sizePolicy" >
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize" >
+ <size>
+ <width>175</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="contextMenuPolicy" >
+ <enum>Qt::DefaultContextMenu</enum>
+ </property>
+ <property name="statusTip" >
+ <string/>
+ </property>
+ <property name="indentation" >
+ <number>0</number>
+ </property>
+ <property name="sortingEnabled" >
+ <bool>true</bool>
+ </property>
+ <property name="selectionMode" >
+ <enum>QAbstractItemView::ExtendedSelection</enum>
+ </property>
+ </widget>
+ <widget class="QSplitter" name="splitter3" >
+ <property name="sizePolicy" >
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>100</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="contextMenuPolicy" >
+ <enum>Qt::NoContextMenu</enum>
+ </property>
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="childrenCollapsible" >
+ <bool>false</bool>
+ </property>
+ <widget class="QFrame" name="frmMap" >
+ <property name="sizePolicy" >
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>100</horstretch>
+ <verstretch>100</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize" >
+ <size>
+ <width>400</width>
+ <height>300</height>
+ </size>
+ </property>
+ <property name="contextMenuPolicy" >
+ <enum>Qt::NoContextMenu</enum>
+ </property>
+ <property name="frameShape" >
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow" >
+ <enum>QFrame::Raised</enum>
+ </property>
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QSplitter" name="splitter2" >
+ <property name="sizePolicy" >
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>100</horstretch>
+ <verstretch>1</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="contextMenuPolicy" >
+ <enum>Qt::NoContextMenu</enum>
+ </property>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="childrenCollapsible" >
+ <bool>false</bool>
+ </property>
+ <widget class="CircuitListWidget" name="treeCircuitList" >
+ <property name="sizePolicy" >
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>100</horstretch>
+ <verstretch>100</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize" >
+ <size>
+ <width>300</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="contextMenuPolicy" >
+ <enum>Qt::CustomContextMenu</enum>
+ </property>
+ <property name="statusTip" >
+ <string/>
+ </property>
+ <property name="sortingEnabled" >
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="RouterDescriptorView" name="textRouterInfo" >
+ <property name="sizePolicy" >
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>100</horstretch>
+ <verstretch>100</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="contextMenuPolicy" >
+ <enum>Qt::DefaultContextMenu</enum>
+ </property>
+ <property name="readOnly" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </widget>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QStatusBar" name="statusbar" />
+ <widget class="QToolBar" name="toolBar" >
+ <property name="contextMenuPolicy" >
+ <enum>Qt::NoContextMenu</enum>
+ </property>
+ <property name="movable" >
+ <bool>false</bool>
+ </property>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <attribute name="toolBarArea" >
+ <number>4</number>
+ </attribute>
+ <addaction name="actionRefresh" />
+ <addaction name="separator" />
+ <addaction name="actionZoomIn" />
+ <addaction name="actionZoomOut" />
+ <addaction name="actionZoomToFit" />
+ <addaction name="actionZoomFullScreen" />
+ <addaction name="separator" />
+ <addaction name="actionHelp" />
+ <addaction name="actionClose" />
+ </widget>
+ <action name="actionRefresh" >
+ <property name="enabled" >
+ <bool>false</bool>
+ </property>
+ <property name="icon" >
+ <iconset resource="../res/vidalia.qrc" >:/images/32x32/view-refresh.png</iconset>
+ </property>
+ <property name="text" >
+ <string>Refresh</string>
+ </property>
+ <property name="toolTip" >
+ <string>Refresh the list of Tor relays and connections</string>
+ </property>
+ <property name="statusTip" >
+ <string>Refresh the list of Tor relays and connections</string>
+ </property>
+ <property name="shortcut" >
+ <string>Ctrl+R</string>
+ </property>
+ </action>
+ <action name="actionHelp" >
+ <property name="icon" >
+ <iconset resource="../res/vidalia.qrc" >:/images/32x32/system-help.png</iconset>
+ </property>
+ <property name="text" >
+ <string>Help</string>
+ </property>
+ <property name="toolTip" >
+ <string>Show the network map help</string>
+ </property>
+ <property name="statusTip" >
+ <string>Show network map help</string>
+ </property>
+ <property name="shortcut" >
+ <string>F1</string>
+ </property>
+ </action>
+ <action name="actionClose" >
+ <property name="icon" >
+ <iconset resource="../res/vidalia.qrc" >:/images/32x32/window-close.png</iconset>
+ </property>
+ <property name="text" >
+ <string>Close</string>
+ </property>
+ <property name="toolTip" >
+ <string>Close the network map</string>
+ </property>
+ <property name="statusTip" >
+ <string>Close the network map</string>
+ </property>
+ <property name="shortcut" >
+ <string>Esc</string>
+ </property>
+ </action>
+ <action name="actionZoomIn" >
+ <property name="icon" >
+ <iconset resource="../res/vidalia.qrc" >:/images/32x32/zoom-in.png</iconset>
+ </property>
+ <property name="text" >
+ <string>Zoom In</string>
+ </property>
+ <property name="toolTip" >
+ <string>Zoom in on the network map</string>
+ </property>
+ <property name="statusTip" >
+ <string>Zoom in on the network map</string>
+ </property>
+ <property name="shortcut" >
+ <string>+</string>
+ </property>
+ </action>
+ <action name="actionZoomOut" >
+ <property name="icon" >
+ <iconset resource="../res/vidalia.qrc" >:/images/32x32/zoom-out.png</iconset>
+ </property>
+ <property name="text" >
+ <string>Zoom Out</string>
+ </property>
+ <property name="toolTip" >
+ <string>Zoom out on the network map</string>
+ </property>
+ <property name="statusTip" >
+ <string>Zoom out on the network map</string>
+ </property>
+ <property name="shortcut" >
+ <string>-</string>
+ </property>
+ </action>
+ <action name="actionZoomToFit" >
+ <property name="icon" >
+ <iconset resource="../res/vidalia.qrc" >:/images/32x32/zoom-fit-best.png</iconset>
+ </property>
+ <property name="text" >
+ <string>Zoom To Fit</string>
+ </property>
+ <property name="toolTip" >
+ <string>Zooms to fit all currently displayed circuits</string>
+ </property>
+ <property name="statusTip" >
+ <string>Zooms to fit all currently displayed circuits</string>
+ </property>
+ <property name="shortcut" >
+ <string>Ctrl+Z</string>
+ </property>
+ </action>
+ <action name="actionZoomFullScreen" >
+ <property name="icon" >
+ <iconset resource="../res/vidalia.qrc" >:/images/32x32/view-fullscreen.png</iconset>
+ </property>
+ <property name="text" >
+ <string>Full Screen</string>
+ </property>
+ <property name="toolTip" >
+ <string>View the network map as a full screen window</string>
+ </property>
+ <property name="statusTip" >
+ <string>View the network map as a full screen window</string>
+ </property>
+ <property name="shortcut" >
+ <string>Ctrl+F</string>
+ </property>
+ </action>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>RouterDescriptorView</class>
+ <extends>QTextEdit</extends>
+ <header>network/RouterDescriptorView.h</header>
+ </customwidget>
+ <customwidget>
+ <class>RouterListWidget</class>
+ <extends>QTreeWidget</extends>
+ <header>network/RouterListWidget.h</header>
+ </customwidget>
+ <customwidget>
+ <class>CircuitListWidget</class>
+ <extends>QTreeWidget</extends>
+ <header>network/CircuitListWidget.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources>
+ <include location="../res/vidalia.qrc" />
+ </resources>
+ <connections>
+ <connection>
+ <sender>actionClose</sender>
+ <signal>triggered()</signal>
+ <receiver>NetViewer</receiver>
+ <slot>close()</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>-1</x>
+ <y>-1</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>403</x>
+ <y>322</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
Modified: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/NetworkMapPlugin.cpp
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/NetworkMapPlugin.cpp 2009-06-25 03:57:32 UTC (rev 3888)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/NetworkMapPlugin.cpp 2009-06-25 04:04:58 UTC (rev 3889)
@@ -18,14 +18,23 @@
NetworkMapPlugin::NetworkMapPlugin()
{
+ _netViewer = new NetViewer();
}
NetworkMapPlugin::~NetworkMapPlugin()
{
+ delete _netViewer;
}
-QString NetworkMapPlugin::pluginHumanName() const
+VidaliaPanel*
+NetworkMapPlugin::panel() const
{
+ return _netViewer;
+}
+
+QString
+NetworkMapPlugin::pluginHumanName() const
+{
return QString("Network Map");
}
Modified: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/NetworkMapPlugin.h
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/NetworkMapPlugin.h 2009-06-25 03:57:32 UTC (rev 3888)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/NetworkMapPlugin.h 2009-06-25 04:04:58 UTC (rev 3889)
@@ -17,10 +17,14 @@
#ifndef _NETWORKMAPPLUGIN_H
#define _NETWORKMAPPLUGIN_H
+#include "NetViewer.h"
+
#include <QObject>
#include <vidalia/VidaliaPluginInterface.h>
+/** Plugin for viewer the user's network panel, containing a visual display of
+ * the Tor network. */
class NetworkMapPlugin : public QObject, public VidaliaPluginInterface
{
Q_OBJECT
@@ -30,7 +34,13 @@
NetworkMapPlugin();
~NetworkMapPlugin();
+ /** Returns the vidalia panel to be displayed in the main window. */
+ VidaliaPanel* panel() const;
+ /** Returns the human readable plugin name for adding and removing plugins. */
QString pluginHumanName() const;
+private:
+ /** The network map object. */
+ NetViewer* _netViewer;
};
#endif
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterDescriptorView.cpp (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/RouterDescriptorView.cpp)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterDescriptorView.cpp (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterDescriptorView.cpp 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,143 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file RouterDescriptorView.cpp
+** \version $Id$
+** \brief Formats and displays a router descriptor as HTML
+*/
+
+#include "RouterDescriptorView.h"
+#include "Vidalia.h"
+
+#include "html.h"
+#include "stringutil.h"
+
+#include <QMenu>
+#include <QIcon>
+#include <QTextCursor>
+#include <QClipboard>
+#include <QShortcut>
+#include <QTextDocumentFragment>
+
+#define IMG_COPY ":/images/22x22/edit-copy.png"
+
+
+/** Default constructor. */
+RouterDescriptorView::RouterDescriptorView(QWidget *parent)
+: QTextEdit(parent)
+{
+ /* Steal QTextEdit's default "Copy" shortcut, since we want to do some
+ * tweaking of the selected text before putting it on the clipboard. */
+ QShortcut *shortcut = new QShortcut(QKeySequence::Copy, this,
+ SLOT(copySelectedText()));
+ Q_UNUSED(shortcut);
+}
+
+/** Displays a context menu for the user when they right-click on the
+ * widget. */
+void
+RouterDescriptorView::contextMenuEvent(QContextMenuEvent *event)
+{
+ QMenu *menu = new QMenu();
+
+ QAction *copyAction = new QAction(QIcon(IMG_COPY), tr("Copy"), menu);
+ copyAction->setShortcut(QKeySequence::Copy);
+ connect(copyAction, SIGNAL(triggered()), this, SLOT(copySelectedText()));
+
+ if (textCursor().selectedText().isEmpty())
+ copyAction->setEnabled(false);
+
+ menu->addAction(copyAction);
+ menu->exec(event->globalPos());
+ delete menu;
+}
+
+/** Copies any selected text to the clipboard. */
+void
+RouterDescriptorView::copySelectedText()
+{
+ QString selectedText = textCursor().selection().toPlainText();
+ selectedText.replace(":\n", ": ");
+ vApp->clipboard()->setText(selectedText);
+}
+
+/** Adjusts the displayed uptime to include time since the router's descriptor
+ * was last published. */
+quint64
+RouterDescriptorView::adjustUptime(quint64 uptime, QDateTime published)
+{
+ QDateTime now = QDateTime::currentDateTime().toUTC();
+
+ if (now < published) {
+ return uptime;
+ }
+ return (uptime + (now.toTime_t() - published.toTime_t()));
+}
+
+/** Displays all router descriptors in the given list. */
+void
+RouterDescriptorView::display(QList<RouterDescriptor> rdlist)
+{
+ RouterDescriptor rd;
+ QString html = "<html><body>";
+
+ for (int r = 0; r < rdlist.size(); r++) {
+ rd = rdlist.at(r);
+ if (rd.isEmpty())
+ continue;
+
+ /* Router name and status */
+ html.append(p(b(rd.name()) + " (" + i(rd.status()) + ")"));
+
+ /* IP and platform */
+ html.append("<table>");
+
+ /* If we have location information, show that first. */
+ if (!rd.location().isEmpty()) {
+ html.append(trow(tcol(b(tr("Location:"))) + tcol(rd.location())));
+ }
+
+ /* Add the IP address and router platform information */
+ html.append(trow(tcol(b(tr("IP Address:"))) + tcol(rd.ip().toString())));
+ html.append(trow(tcol(b(tr("Platform:"))) + tcol(rd.platform())));
+
+ /* If the router is online, then show the uptime and bandwidth stats. */
+ if (!rd.offline()) {
+ html.append(trow(tcol(b(tr("Bandwidth:"))) +
+ tcol(string_format_bandwidth(rd.observedBandwidth()))));
+ html.append(trow(tcol(b(tr("Uptime:"))) +
+ tcol(string_format_uptime(
+ adjustUptime(rd.uptime(), rd.published())))));
+ }
+
+ /* Date the router was published */
+ html.append(trow(tcol(b(tr("Last Updated:"))) +
+ tcol(string_format_datetime(rd.published()) + " GMT")));
+
+ html.append("</table>");
+
+ /* If there are multiple descriptors, and this isn't is the last one
+ * then separate them with a short horizontal line. */
+ if (r+1 != rdlist.size()) {
+ html.append("<center><hr width=\"50%\"/></center>");
+ }
+ }
+ html.append("</body></html>");
+ setHtml(html);
+}
+
+/** Displays the given router descriptor. */
+void
+RouterDescriptorView::display(RouterDescriptor rd)
+{
+ display(QList<RouterDescriptor>() << rd);
+}
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterDescriptorView.h (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/RouterDescriptorView.h)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterDescriptorView.h (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterDescriptorView.h 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,56 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file RouterDescriptorView.h
+** \version $Id$
+** \brief Formats and displays a router descriptor as HTML
+*/
+
+#ifndef _ROUTERDESCRIPTORVIEW_H
+#define _ROUTERDESCRIPTORVIEW_H
+
+#include "RouterDescriptor.h"
+
+#include <QObject>
+#include <QTextEdit>
+#include <QList>
+#include <QContextMenuEvent>
+
+
+class RouterDescriptorView : public QTextEdit
+{
+ Q_OBJECT
+
+public:
+ /** Default constructor. */
+ RouterDescriptorView(QWidget *parent = 0);
+
+public slots:
+ /** Shows the given router descriptor. */
+ void display(RouterDescriptor rd);
+ /** Shows all router descriptors in the given list. */
+ void display(QList<RouterDescriptor> rdlist);
+ /** Copies any selected text to the clipboard. */
+ void copySelectedText();
+
+protected:
+ /** Displays a context menu for the user when they right-click on the
+ * widget. */
+ virtual void contextMenuEvent(QContextMenuEvent *event);
+
+private:
+ /** Adjusts the displayed uptime to include time since the
+ * router's descriptor was last published. */
+ quint64 adjustUptime(quint64 uptime, QDateTime published);
+};
+
+#endif
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterInfoDialog.cpp (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/RouterInfoDialog.cpp)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterInfoDialog.cpp (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterInfoDialog.cpp 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,79 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file RouterInfoDialog.cpp
+** \version $Id$
+** \brief Displays detailed information about a particular router
+*/
+
+#include "RouterInfoDialog.h"
+
+#include "stringutil.h"
+
+
+RouterInfoDialog::RouterInfoDialog(QWidget *parent)
+ : QDialog(parent)
+{
+ ui.setupUi(this);
+}
+
+quint64
+RouterInfoDialog::adjustUptime(quint64 uptime, const QDateTime &published)
+{
+ QDateTime now = QDateTime::currentDateTime().toUTC();
+
+ if (now < published)
+ return uptime;
+
+ return (uptime + (now.toTime_t() - published.toTime_t()));
+}
+
+void
+RouterInfoDialog::setRouterInfo(const QStringList &desc,
+ const RouterStatus &status)
+{
+ RouterDescriptor rd(desc);
+
+ ui.lblName->setText(rd.name());
+ ui.lblIPAddress->setText(rd.ip().toString());
+ ui.lblPlatform->setText(rd.platform());
+ ui.lblBandwidth->setText(string_format_bandwidth(rd.observedBandwidth()));
+ ui.lblLastUpdated->setText(string_format_datetime(rd.published()) + " GMT");
+ ui.lblUptime->setText(string_format_uptime(adjustUptime(rd.uptime(),
+ rd.published())));
+
+ if (rd.hibernating()) {
+ ui.lblStatus->setText(tr("Hibernating"));
+ } else if (status.isValid()) {
+ if (status.flags() & RouterStatus::Running)
+ ui.lblStatus->setText(tr("Online"));
+ else
+ ui.lblStatus->setText(tr("Offline"));
+ } else {
+ ui.lblStatus->setText(tr("Unknown"));
+ }
+
+ if (! rd.contact().isEmpty()) {
+ ui.lblContact->setText(rd.contact());
+ } else {
+ ui.lblContact->setVisible(false);
+ ui.lblContactLabel->setVisible(false);
+ }
+
+ ui.textDescriptor->setPlainText(desc.join("\n"));
+}
+
+void
+RouterInfoDialog::setLocation(const QString &location)
+{
+ ui.lblLocation->setText(location);
+}
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterInfoDialog.h (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/RouterInfoDialog.h)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterInfoDialog.h (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterInfoDialog.h 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,59 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file RouterInfoDialog.h
+** \version $Id$
+** \brief Displays detailed information about a particular router
+*/
+
+
+#ifndef _ROUTERINFODIALOG_H
+#define _ROUTERINFODIALOG_H
+
+#include "ui_RouterInfoDialog.h"
+
+#include "RouterStatus.h"
+#include "RouterDescriptor.h"
+
+#include <QDialog>
+
+
+class RouterInfoDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ /** Default constructor.
+ */
+ RouterInfoDialog(QWidget *parent = 0);
+
+ /** Populates the dialog's UI with information parsed from the router
+ * descriptor <b>desc</b> and the router status information in
+ * <b>status</b>.
+ */
+ void setRouterInfo(const QStringList &desc, const RouterStatus &status);
+
+ /** Sets the geographic location information displayed in the dialog to
+ * <b>location</b>.
+ */
+ void setLocation(const QString &location);
+
+private:
+ /** Adjusts <b>uptime</b> to be the greater of either <b>published</b> or
+ * <b>uptime</b> plus the number of seconds elapsed since <b>published</b>.
+ */
+ quint64 adjustUptime(quint64 uptime, const QDateTime &published);
+
+ Ui::RouterInfoDialog ui;
+};
+
+#endif
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterInfoDialog.ui (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/RouterInfoDialog.ui)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterInfoDialog.ui (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterInfoDialog.ui 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,449 @@
+<ui version="4.0" >
+ <class>RouterInfoDialog</class>
+ <widget class="QDialog" name="RouterInfoDialog" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>427</width>
+ <height>382</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Relay Details</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout" >
+ <item>
+ <widget class="QTabWidget" name="tabWidget" >
+ <property name="tabShape" >
+ <enum>QTabWidget::Rounded</enum>
+ </property>
+ <property name="currentIndex" >
+ <number>0</number>
+ </property>
+ <property name="usesScrollButtons" >
+ <bool>false</bool>
+ </property>
+ <widget class="QWidget" name="tabSummary" >
+ <attribute name="title" >
+ <string>Summary</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_2" >
+ <item row="0" column="0" >
+ <widget class="QLabel" name="lblNameLabel" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font" >
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text" >
+ <string>Name:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" >
+ <widget class="QLabel" name="lblName" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Preferred" hsizetype="MinimumExpanding" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text" >
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="lblStatusLabel" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font" >
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text" >
+ <string>Status:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" >
+ <widget class="QLabel" name="lblStatus" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Preferred" hsizetype="MinimumExpanding" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text" >
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" >
+ <widget class="QLabel" name="lblLocationLabel" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font" >
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text" >
+ <string>Location:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1" >
+ <widget class="QLabel" name="lblLocation" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Preferred" hsizetype="MinimumExpanding" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text" >
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" >
+ <widget class="QLabel" name="lblIPAddressLabel" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font" >
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text" >
+ <string>IP Address:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1" >
+ <widget class="QLabel" name="lblIPAddress" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Preferred" hsizetype="MinimumExpanding" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text" >
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0" >
+ <widget class="QLabel" name="lblPlatformLabel" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font" >
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text" >
+ <string>Platform:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1" >
+ <widget class="QLabel" name="lblPlatform" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Preferred" hsizetype="MinimumExpanding" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize" >
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text" >
+ <string/>
+ </property>
+ <property name="wordWrap" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0" >
+ <widget class="QLabel" name="lblBandwidthLabel" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font" >
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text" >
+ <string>Bandwidth:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1" >
+ <widget class="QLabel" name="lblBandwidth" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Preferred" hsizetype="MinimumExpanding" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text" >
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0" >
+ <widget class="QLabel" name="lblUptimeLabel" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font" >
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text" >
+ <string>Uptime:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="1" >
+ <widget class="QLabel" name="lblUptime" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Preferred" hsizetype="MinimumExpanding" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text" >
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="0" >
+ <widget class="QLabel" name="lblContactLabel" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font" >
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text" >
+ <string>Contact:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="1" >
+ <widget class="QLabel" name="lblContact" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Preferred" hsizetype="MinimumExpanding" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text" >
+ <string/>
+ </property>
+ <property name="wordWrap" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="10" column="0" >
+ <spacer name="verticalSpacer" >
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="9" column="0" >
+ <widget class="QLabel" name="lblLastUpdatedLabel" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font" >
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text" >
+ <string>Last Updated:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="9" column="1" >
+ <widget class="QLabel" name="lblLastUpdated" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Preferred" hsizetype="MinimumExpanding" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text" >
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tabDescriptor" >
+ <attribute name="title" >
+ <string>Descriptor</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout" >
+ <item row="0" column="0" >
+ <widget class="QTextEdit" name="textDescriptor" >
+ <property name="font" >
+ <font>
+ <family>Courier New</family>
+ <pointsize>10</pointsize>
+ </font>
+ </property>
+ <property name="lineWrapMode" >
+ <enum>QTextEdit::NoWrap</enum>
+ </property>
+ <property name="acceptRichText" >
+ <bool>false</bool>
+ </property>
+ <property name="textInteractionFlags" >
+ <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons" >
+ <set>QDialogButtonBox::Close</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>RouterInfoDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>RouterInfoDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterListItem.cpp (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/RouterListItem.cpp)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterListItem.cpp (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterListItem.cpp 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,150 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file RouterListItem.cpp
+** \version $Id$
+** \brief Item representing a single router and status in a RouterListWidget
+*/
+
+#include "RouterListItem.h"
+#include "RouterListWidget.h"
+
+#include <QHeaderView>
+
+#define STATUS_COLUMN (RouterListWidget::StatusColumn)
+#define COUNTRY_COLUMN (RouterListWidget::CountryColumn)
+#define NAME_COLUMN (RouterListWidget::NameColumn)
+
+#define IMG_NODE_OFFLINE ":/images/icons/node-unresponsive.png"
+#define IMG_NODE_SLEEPING ":/images/icons/node-hibernating.png"
+#define IMG_NODE_NO_BW ":/images/icons/node-bw-none.png"
+#define IMG_NODE_LOW_BW ":/images/icons/node-bw-low.png"
+#define IMG_NODE_MED_BW ":/images/icons/node-bw-med.png"
+#define IMG_NODE_HIGH_BW ":/images/icons/node-bw-high.png"
+#define IMG_FLAG_UNKNOWN ":/images/flags/unknown.png"
+
+
+/** Default constructor. */
+RouterListItem::RouterListItem(RouterListWidget *list, RouterDescriptor rd)
+ : QTreeWidgetItem()
+{
+ _list = list;
+ _rd = 0;
+ _country = "~"; /* Force items with no country to the bottom */
+ setIcon(COUNTRY_COLUMN, QIcon(IMG_FLAG_UNKNOWN));
+ update(rd);
+}
+
+/** Destructor. */
+RouterListItem::~RouterListItem()
+{
+ if (_rd)
+ delete _rd;
+}
+
+/** Updates the router descriptor for this item. */
+void
+RouterListItem::update(const RouterDescriptor &rd)
+{
+ QIcon statusIcon;
+ if (_rd) {
+ delete _rd;
+ }
+ _rd = new RouterDescriptor(rd);
+
+ /* Determine the status value (used for sorting) and icon */
+ if (_rd->offline()) {
+ _statusValue = -1;
+ statusIcon = QIcon(IMG_NODE_OFFLINE);
+ setToolTip(STATUS_COLUMN, tr("Offline"));
+ } else if (_rd->hibernating()) {
+ _statusValue = 0;
+ statusIcon = QIcon(IMG_NODE_SLEEPING);
+ setToolTip(STATUS_COLUMN, tr("Hibernating"));
+ } else {
+ _statusValue = (qint64)_rd->observedBandwidth();
+ if (_statusValue >= 400*1024) {
+ statusIcon = QIcon(IMG_NODE_HIGH_BW);
+ } else if (_statusValue >= 60*1024) {
+ statusIcon = QIcon(IMG_NODE_MED_BW);
+ } else if (_statusValue >= 20*1024) {
+ statusIcon = QIcon(IMG_NODE_LOW_BW);
+ } else {
+ statusIcon = QIcon(IMG_NODE_NO_BW);
+ }
+ setToolTip(STATUS_COLUMN, tr("%1 KB/s").arg(_statusValue/1024));
+ }
+
+ /* Make the new information visible */
+ setIcon(STATUS_COLUMN, statusIcon);
+ setText(NAME_COLUMN, _rd->name());
+ setToolTip(NAME_COLUMN, QString(_rd->name() + "\r\n" + _rd->platform()));
+}
+
+/** Sets the location information for this item's router descriptor. */
+void
+RouterListItem::setLocation(const GeoIp &geoip)
+{
+ QPixmap flag(":/images/flags/" + geoip.countryCode().toLower() + ".png");
+ if (!flag.isNull()) {
+ setIcon(COUNTRY_COLUMN, QIcon(flag));
+ }
+ setToolTip(COUNTRY_COLUMN, geoip.toString());
+
+ if (_rd)
+ _rd->setLocation(geoip.toString());
+ _country = geoip.countryCode();
+}
+
+/** Overload the comparison operator. */
+bool
+RouterListItem::operator<(const QTreeWidgetItem &other) const
+{
+ const RouterListItem *a = this;
+ const RouterListItem *b = (RouterListItem *)&other;
+
+ if (_list) {
+ Qt::SortOrder order = _list->header()->sortIndicatorOrder();
+ switch (_list->sortColumn()) {
+ case RouterListWidget::StatusColumn:
+ /* Numeric comparison based on status and/or bandwidth */
+ if (a->_statusValue == b->_statusValue) {
+ if (order == Qt::AscendingOrder)
+ return (a->name().toLower() > b->name().toLower());
+ else
+ return (a->name().toLower() < b->name().toLower());
+ }
+ return (a->_statusValue < b->_statusValue);
+ case RouterListWidget::CountryColumn:
+ /* Compare based on country code */
+ if (a->_country == b->_country) {
+ if (order == Qt::AscendingOrder)
+ return (a->_statusValue > b->_statusValue);
+ else
+ return (a->_statusValue < b->_statusValue);
+ }
+ return (a->_country < b->_country);
+ case RouterListWidget::NameColumn:
+ /* Case-insensitive comparison based on router name */
+ if (a->name().toLower() == b->name().toLower()) {
+ if (order == Qt::AscendingOrder)
+ return (a->_statusValue > b->_statusValue);
+ else
+ return (a->_statusValue < b->_statusValue);
+ }
+ return (a->name().toLower() < b->name().toLower());
+ default:
+ break;
+ }
+ }
+ return QTreeWidgetItem::operator<(other);
+}
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterListItem.h (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/RouterListItem.h)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterListItem.h (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterListItem.h 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,66 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file RouterListItem.h
+** \version $Id$
+** \brief Item representing a single router and status in a RouterListWidget
+*/
+
+#ifndef _ROUTERLISTITEM_H
+#define _ROUTERLISTITEM_H
+
+#include "RouterDescriptor.h"
+#include "RouterListWidget.h"
+#include "GeoIp.h"
+
+#include <QCoreApplication>
+#include <QTreeWidgetItem>
+#include <QString>
+
+class RouterListWidget;
+
+
+class RouterListItem : public QTreeWidgetItem
+{
+ Q_DECLARE_TR_FUNCTIONS(RouterListItem)
+
+public:
+ /** Default constructor. */
+ RouterListItem(RouterListWidget *list, RouterDescriptor rd);
+ /** Destructor. */
+ ~RouterListItem();
+
+ /** Updates this router item using a new descriptor. */
+ void update(const RouterDescriptor &rd);
+ /** Returns the router's ID. */
+ QString id() const { return _rd->id(); }
+ /** Returns the router's name. */
+ QString name() const { return _rd->name(); }
+ /** Returns the descriptor for this router. */
+ RouterDescriptor descriptor() const { return *_rd; }
+ /** Sets the location information for this router item. */
+ void setLocation(const GeoIp &geoip);
+ /** Returns the location information set for this router item. */
+ QString location() const { return _rd->location(); }
+
+ /** Overload the comparison operator. */
+ virtual bool operator<(const QTreeWidgetItem &other) const;
+
+private:
+ RouterDescriptor* _rd; /**< Descriptor for this router item. */
+ RouterListWidget* _list; /**< The list for this list item. */
+ qint64 _statusValue; /**< Value used to sort items by status. */
+ QString _country; /**< Country in which this router is likely
+ located. */
+};
+
+#endif
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterListWidget.cpp (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/RouterListWidget.cpp)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterListWidget.cpp (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterListWidget.cpp 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,240 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file RouterListWidget.cpp
+** \version $Id$
+** \brief Displays a list of Tor servers and their status
+*/
+
+#include "RouterListWidget.h"
+#include "RouterListItem.h"
+#include "Vidalia.h"
+
+#include <QHeaderView>
+#include <QClipboard>
+
+#define IMG_ZOOM ":/images/22x22/page-zoom.png"
+#define IMG_COPY ":/images/22x22/edit-copy.png"
+
+
+RouterListWidget::RouterListWidget(QWidget *parent)
+ : QTreeWidget(parent)
+{
+ /* Create and initialize columns */
+ setHeaderLabels(QStringList() << QString("")
+ << QString("")
+ << tr("Relay"));
+
+ /* Sort by descending server bandwidth */
+ sortItems(StatusColumn, Qt::DescendingOrder);
+
+ /* Find out when the selected item has changed. */
+ connect(this, SIGNAL(itemSelectionChanged()),
+ this, SLOT(onSelectionChanged()));
+}
+
+/** Called when the user changes the UI translation. */
+void
+RouterListWidget::retranslateUi()
+{
+ setHeaderLabels(QStringList() << QString("")
+ << QString("")
+ << tr("Relay"));
+}
+
+/** Called when the user requests a context menu for a router in the list. A
+ * context menu will be displayed providing a list of actions, including
+ * zooming in on the server. */
+void
+RouterListWidget::contextMenuEvent(QContextMenuEvent *event)
+{
+ QAction *action;
+ QMenu *menu, *copyMenu;
+ QList<QTreeWidgetItem *> selected;
+
+ selected = selectedItems();
+ if (! selected.size())
+ return;
+
+ menu = new QMenu();
+ copyMenu = menu->addMenu(QIcon(IMG_COPY), tr("Copy"));
+ action = copyMenu->addAction(tr("Nickname"));
+ connect(action, SIGNAL(triggered()), this, SLOT(copySelectedNicknames()));
+
+ action = copyMenu->addAction(tr("Fingerprint"));
+ connect(action, SIGNAL(triggered()), this, SLOT(copySelectedFingerprints()));
+
+ action = menu->addAction(QIcon(IMG_ZOOM), tr("Zoom to Relay"));
+ if (selected.size() > 1)
+ action->setEnabled(false);
+ else
+ connect(action, SIGNAL(triggered()), this, SLOT(zoomToSelectedRelay()));
+
+ menu->exec(event->globalPos());
+ delete menu;
+}
+
+/** Copies the nicknames for all currently selected relays to the clipboard.
+ * Nicknames are formatted as a comma-delimited list, suitable for doing
+ * dumb things with your torrc. */
+void
+RouterListWidget::copySelectedNicknames()
+{
+ QString text;
+
+ foreach (QTreeWidgetItem *item, selectedItems()) {
+ RouterListItem *relay = dynamic_cast<RouterListItem *>(item);
+ if (relay)
+ text.append(relay->name() + ",");
+ }
+ if (text.length()) {
+ text.remove(text.length()-1, 1);
+ vApp->clipboard()->setText(text);
+ }
+}
+
+/** Copies the fingerprints for all currently selected relays to the
+ * clipboard. Fingerprints are formatted as a comma-delimited list, suitable
+ * for doing dumb things with your torrc. */
+void
+RouterListWidget::copySelectedFingerprints()
+{
+ QString text;
+
+ foreach (QTreeWidgetItem *item, selectedItems()) {
+ RouterListItem *relay = dynamic_cast<RouterListItem *>(item);
+ if (relay)
+ text.append("$" + relay->id() + ",");
+ }
+ if (text.length()) {
+ text.remove(text.length()-1, 1);
+ vApp->clipboard()->setText(text);
+ }
+}
+
+/** Emits a zoomToRouter() signal containing the fingerprint of the
+ * currently selected relay. */
+void
+RouterListWidget::zoomToSelectedRelay()
+{
+ QList<QTreeWidgetItem *> selected = selectedItems();
+ if (selected.size() != 1)
+ return;
+
+ RouterListItem *relay = dynamic_cast<RouterListItem *>(selected[0]);
+ if (relay)
+ emit zoomToRouter(relay->id());
+}
+
+/** Deselects all currently selected routers. */
+void
+RouterListWidget::deselectAll()
+{
+ QList<QTreeWidgetItem *> selected = selectedItems();
+ foreach (QTreeWidgetItem *item, selected) {
+ setItemSelected(item, false);
+ }
+}
+
+/** Clear the list of router items. */
+void
+RouterListWidget::clearRouters()
+{
+ _idmap.clear();
+ QTreeWidget::clear();
+ setStatusTip(tr("%1 relays online").arg(0));
+}
+
+/** Called when the user selects a router from the list. This will search the
+ * list for a router whose names starts with the key pressed. */
+void
+RouterListWidget::keyPressEvent(QKeyEvent *event)
+{
+ int index;
+
+ QString key = event->text();
+ if (!key.isEmpty() && key.at(0).isLetterOrNumber()) {
+ /* A text key was pressed, so search for routers that begin with that key. */
+ QList<QTreeWidgetItem *> list = findItems(QString("^[%1%2].*$")
+ .arg(key.toUpper())
+ .arg(key.toLower()),
+ Qt::MatchRegExp|Qt::MatchWrap,
+ NameColumn);
+ if (list.size() > 0) {
+ QList<QTreeWidgetItem *> s = selectedItems();
+
+ /* A match was found, so deselect any previously selected routers,
+ * select the new match, and make sure it's visible. If there was
+ * already a router selected that started with the search key, go to the
+ * next match in the list. */
+ deselectAll();
+ index = (!s.size() ? 0 : (list.indexOf(s.at(0)) + 1) % list.size());
+
+ /* Select the item and scroll to it */
+ setItemSelected(list.at(index), true);
+ scrollToItem(list.at(index));
+ }
+ event->accept();
+ } else {
+ /* It was something we don't understand, so hand it to the parent class */
+ QTreeWidget::keyPressEvent(event);
+ }
+}
+
+/** Finds the list item whose key ID matches <b>id</b>. Returns 0 if not
+ * found. */
+RouterListItem*
+RouterListWidget::findRouterById(QString id)
+{
+ if (_idmap.contains(id)) {
+ return _idmap.value(id);
+ }
+ return 0;
+}
+
+/** Adds a router descriptor to the list. */
+void
+RouterListWidget::addRouter(RouterDescriptor rd)
+{
+ QString id = rd.id();
+ if (id.isEmpty())
+ return;
+
+ RouterListItem *item = findRouterById(id);
+ if (item) {
+ item->update(rd);
+ } else {
+ item = new RouterListItem(this, rd);
+ addTopLevelItem(item);
+ _idmap.insert(id, item);
+ }
+
+ /* Set our status tip to the number of servers in the list */
+ setStatusTip(tr("%1 relays online").arg(topLevelItemCount()));
+}
+
+/** Called when the selected items have changed. This emits the
+ * routerSelected() signal with the descriptor for the selected router.
+ */
+void
+RouterListWidget::onSelectionChanged()
+{
+ QList<RouterDescriptor> descriptors;
+
+ foreach (QTreeWidgetItem *item, selectedItems()) {
+ RouterListItem *relay = dynamic_cast<RouterListItem *>(item);
+ if (relay)
+ descriptors << relay->descriptor();
+ }
+ if (descriptors.count() > 0)
+ emit routerSelected(descriptors);
+}
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterListWidget.h (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/RouterListWidget.h)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterListWidget.h (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/RouterListWidget.h 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,97 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file RouterListWidget.h
+** \version $Id$
+** \brief Displays a list of Tor servers and their status
+*/
+
+#ifndef _ROUTERLISTWIDGET_H
+#define _ROUTERLISTWIDGET_H
+
+#include "RouterDescriptor.h"
+
+#include <QHash>
+#include <QList>
+#include <QMenu>
+#include <QObject>
+#include <QAction>
+#include <QKeyEvent>
+#include <QTreeWidget>
+#include <QHostAddress>
+#include <QMouseEvent>
+
+class RouterListItem;
+
+class RouterListWidget : public QTreeWidget
+{
+ Q_OBJECT
+
+public:
+ /** Columns in the list. */
+ enum Columns {
+ StatusColumn = 0, /**< Status column, indicating bandwidth. */
+ CountryColumn = 1, /**< Router's country flag. */
+ NameColumn = 2, /**< Router's name. */
+ };
+
+ /** Default constructor. */
+ RouterListWidget(QWidget *parent = 0);
+
+ /** Adds a new descriptor the list. */
+ void addRouter(RouterDescriptor rd);
+ /** Finds the list item whose key ID matches <b>id</b>. Returns 0 if not
+ * found. */
+ RouterListItem* findRouterById(QString id);
+ /** Deselects all currently selected routers. */
+ void deselectAll();
+ /** Called when the user changes the UI translation. */
+ void retranslateUi();
+
+signals:
+ /** Emitted when the user selects a router from the list. */
+ void routerSelected(QList<RouterDescriptor> rd);
+ /** Emitted when the user selects a router to zoom in on. */
+ void zoomToRouter(QString id);
+
+public slots:
+ /** Clears the list of router items. */
+ void clearRouters();
+
+private slots:
+ /** Called when the user clicks on an item in the list. */
+ void onSelectionChanged();
+ /** Copies the nicknames for all currently selected relays to the clipboard.
+ * Nicknames are formatted as a comma-delimited list, suitable for doing
+ * dumb things with your torrc. */
+ void copySelectedNicknames();
+ /** Copies the fingerprints for all currently selected relays to the
+ * clipboard. Fingerprints are formatted as a comma-delimited list, suitable
+ * for doing dumb things with your torrc. */
+ void copySelectedFingerprints();
+ /** Emits a zoomToRouter() signal containing the fingerprint of the
+ * currently selected relay. */
+ void zoomToSelectedRelay();
+
+protected:
+ /** Called when the user presses a key while the list has focus. */
+ void keyPressEvent(QKeyEvent *event);
+ /** Displays a context menu for the user when they right-click on the
+ * widget. */
+ virtual void contextMenuEvent(QContextMenuEvent *event);
+
+private:
+ /** Maps a server ID to that server's list item. */
+ QHash<QString,RouterListItem*> _idmap;
+};
+
+#endif
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/StreamItem.cpp (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/StreamItem.cpp)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/StreamItem.cpp (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/StreamItem.cpp 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,38 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file StreamItem.cpp
+** \version $Id$
+** \brief Item representing a stream through Tor and its status
+*/
+
+#include "StreamItem.h"
+#include "CircuitListWidget.h"
+
+
+/** Constructor */
+StreamItem::StreamItem(const Stream &stream)
+{
+ /* Update the status and target */
+ update(stream);
+}
+
+/** Updates the status of this stream item. */
+void
+StreamItem::update(const Stream &stream)
+{
+ _stream = stream;
+ setText(CircuitListWidget::ConnectionColumn, stream.target());
+ setToolTip(CircuitListWidget::ConnectionColumn, stream.target());
+ setText(CircuitListWidget::StatusColumn, stream.statusString());
+ setToolTip(CircuitListWidget::StatusColumn, stream.statusString());
+}
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/StreamItem.h (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/StreamItem.h)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/StreamItem.h (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/StreamItem.h 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,42 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file StreamItem.h
+** \version $Id$
+** \brief Item representing a stream through Tor and its status
+*/
+
+#ifndef _STREAMITEM_H
+#define _STREAMITEM_H
+
+#include "Stream.h"
+
+#include <QTreeWidgetItem>
+
+
+class StreamItem : public QTreeWidgetItem
+{
+public:
+ /** Constructor */
+ StreamItem(const Stream &stream);
+
+ Stream stream() const { return _stream; }
+ /** Updates the status of this stream item. */
+ void update(const Stream &stream);
+ /** Returns the ID of the stream associated with this tree item. */
+ StreamId id() const { return _stream.id(); }
+
+private:
+ Stream _stream;
+};
+
+#endif
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/TorMapImageView.cpp (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/TorMapImageView.cpp)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/TorMapImageView.cpp (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/TorMapImageView.cpp 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,333 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file TorMapImageView.cpp
+** \version $Id$
+** \brief Displays Tor servers and circuits on a map of the world
+*/
+
+#include "config.h"
+#include "TorMapImageView.h"
+
+#include <QStringList>
+
+#if defined(__sgi) && defined(_COMPILER_VERSION) && _COMPILER_VERSION >= 730
+#include <math.h>
+#else
+#include <cmath>
+#endif
+
+#define IMG_WORLD_MAP ":/images/map/world-map.png"
+
+/** QPens to use for drawing different map elements */
+#define PEN_ROUTER QPen(QColor("#ff030d"), 1.0)
+#define PEN_CIRCUIT QPen(Qt::yellow, 0.5)
+#define PEN_SELECTED QPen(Qt::green, 2.0)
+
+/** Size of the map image */
+#define IMG_WIDTH 1000
+#define IMG_HEIGHT 507
+
+/** Border between the edge of the image and the actual map */
+#define MAP_TOP 2
+#define MAP_BOTTOM 2
+#define MAP_RIGHT 5
+#define MAP_LEFT 5
+#define MAP_WIDTH (IMG_WIDTH-MAP_LEFT-MAP_RIGHT)
+#define MAP_HEIGHT (IMG_HEIGHT-MAP_TOP-MAP_BOTTOM)
+
+/** Map offset from zero longitude */
+#define MAP_ORIGIN -10
+
+/** Minimum allowable size for this widget */
+#define MIN_SIZE QSize(512,256)
+
+/** Robinson projection table */
+/** Length of the parallel of latitude */
+static float plen[] = {
+ 1.0000, 0.9986, 0.9954, 0.9900,
+ 0.9822, 0.9730, 0.9600, 0.9427,
+ 0.9216, 0.8962, 0.8679, 0.8350,
+ 0.7986, 0.7597, 0.7186, 0.6732,
+ 0.6213, 0.5722, 0.5322
+ };
+
+/** Distance of corresponding parallel from equator */
+static float pdfe[] = {
+ 0.0000, 0.0620, 0.1240, 0.1860,
+ 0.2480, 0.3100, 0.3720, 0.4340,
+ 0.4958, 0.5571, 0.6176, 0.6769,
+ 0.7346, 0.7903, 0.8435, 0.8936,
+ 0.9394, 0.9761, 1.0000
+ };
+
+/** Default constructor */
+TorMapImageView::TorMapImageView(QWidget *parent)
+: ZImageView(parent)
+{
+ QImage map(IMG_WORLD_MAP);
+ setImage(map);
+}
+
+/** Destructor */
+TorMapImageView::~TorMapImageView()
+{
+ clear();
+}
+
+/** Adds a router to the map. */
+void
+TorMapImageView::addRouter(const RouterDescriptor &desc, const GeoIp &geoip)
+{
+ QString id = desc.id();
+ QPointF routerCoord = toMapSpace(geoip.latitude(), geoip.longitude());
+
+ /* Add data the hash of known routers, and plot the point on the map */
+ if (_routers.contains(id))
+ _routers.value(id)->first = routerCoord;
+ else
+ _routers.insert(id, new QPair<QPointF,bool>(routerCoord, false));
+}
+
+/** Adds a circuit to the map using the given ordered list of router IDs. */
+void
+TorMapImageView::addCircuit(const CircuitId &circid, const QStringList &path)
+{
+ QPainterPath *circPainterPath = new QPainterPath;
+
+ /* Build the new circuit */
+ for (int i = 0; i < path.size()-1; i++) {
+ QString fromNode = path.at(i);
+ QString toNode = path.at(i+1);
+
+ /* Add the coordinates of the hops to the circuit */
+ if (_routers.contains(fromNode) && _routers.contains(toNode)) {
+ /* Find the two endpoints for this path segment */
+ QPointF fromPos = _routers.value(fromNode)->first;
+ QPointF endPos = _routers.value(toNode)->first;
+
+ /* Draw the path segment */
+ circPainterPath->moveTo(fromPos);
+ circPainterPath->lineTo(endPos);
+ circPainterPath->moveTo(endPos);
+ }
+ }
+
+ /** Add the data to the hash of known circuits and plot the circuit on the map */
+ if (_circuits.contains(circid)) {
+ /* This circuit is being updated, so just update the path, making sure we
+ * free the memory allocated to the old one. */
+ QPair<QPainterPath*,bool> *circuitPair = _circuits.value(circid);
+ delete circuitPair->first;
+ circuitPair->first = circPainterPath;
+ } else {
+ /* This is a new path, so just add it to our list */
+ _circuits.insert(circid, new QPair<QPainterPath*,bool>(circPainterPath,false));
+ }
+}
+
+/** Removes a circuit from the map. */
+void
+TorMapImageView::removeCircuit(const CircuitId &circid)
+{
+ QPair<QPainterPath*,bool> *circ = _circuits.take(circid);
+ QPainterPath *circpath = circ->first;
+ if (circpath) {
+ delete circpath;
+ }
+ delete circ;
+}
+
+/** Selects and highlights the router on the map. */
+void
+TorMapImageView::selectRouter(const QString &id)
+{
+ if (_routers.contains(id)) {
+ QPair<QPointF, bool> *routerPair = _routers.value(id);
+ routerPair->second = true;
+ }
+ repaint();
+}
+
+/** Selects and highlights the circuit with the id <b>circid</b>
+ * on the map. */
+void
+TorMapImageView::selectCircuit(const CircuitId &circid)
+{
+ if (_circuits.contains(circid)) {
+ QPair<QPainterPath*, bool> *circuitPair = _circuits.value(circid);
+ circuitPair->second = true;
+ }
+ repaint();
+}
+
+/** Deselects any highlighted routers or circuits */
+void
+TorMapImageView::deselectAll()
+{
+ /* Deselect all router points */
+ foreach (QString router, _routers.keys()) {
+ QPair<QPointF,bool> *routerPair = _routers.value(router);
+ routerPair->second = false;
+ }
+ /* Deselect all circuit paths */
+ foreach (CircuitId circid, _circuits.keys()) {
+ QPair<QPainterPath*,bool> *circuitPair = _circuits.value(circid);
+ circuitPair->second = false;
+ }
+}
+
+/** Clears the list of routers and removes all the data on the map */
+void
+TorMapImageView::clear()
+{
+ /* Clear out all the router points and free their memory */
+ foreach (QString router, _routers.keys()) {
+ delete _routers.take(router);
+ }
+ /* Clear out all the circuit paths and free their memory */
+ foreach (CircuitId circid, _circuits.keys()) {
+ QPair<QPainterPath*,bool> *circuitPair = _circuits.take(circid);
+ delete circuitPair->first;
+ delete circuitPair;
+ }
+}
+
+/** Draws the routers and paths onto the map image. */
+void
+TorMapImageView::paintImage(QPainter *painter)
+{
+ painter->setRenderHint(QPainter::Antialiasing);
+
+ /* Draw the router points */
+ foreach(QString router, _routers.keys()) {
+ QPair<QPointF,bool> *routerPair = _routers.value(router);
+ painter->setPen((routerPair->second ? PEN_SELECTED : PEN_ROUTER));
+ painter->drawPoint(routerPair->first);
+ }
+ /* Draw the circuit paths */
+ foreach(CircuitId circid, _circuits.keys()) {
+ QPair<QPainterPath*,bool> *circuitPair = _circuits.value(circid);
+ painter->setPen((circuitPair->second ? PEN_SELECTED : PEN_CIRCUIT));
+ painter->drawPath(*(circuitPair->first));
+ }
+}
+
+/** Converts world space coordinates into map space coordinates */
+QPointF
+TorMapImageView::toMapSpace(float latitude, float longitude)
+{
+ float width = MAP_WIDTH;
+ float height = MAP_HEIGHT;
+ float deg = width / 360.0;
+ longitude += MAP_ORIGIN;
+
+ float lat;
+ float lon;
+
+ lat = floor(longitude * (deg * lerp(abs(int(latitude)), plen))
+ + width/2 + MAP_LEFT);
+
+ if (latitude < 0) {
+ lon = floor((height/2) + (lerp(abs(int(latitude)), pdfe) * (height/2))
+ + MAP_TOP);
+ } else {
+ lon = floor((height/2) - (lerp(abs(int(latitude)), pdfe) * (height/2))
+ + MAP_TOP);
+ }
+
+ return QPointF(lat, lon);
+}
+
+/** Linearly interpolates using the values in the Robinson projection table */
+float
+TorMapImageView::lerp(float input, float *table)
+{
+ int x = int(floor(input / 5));
+
+ return ((table[x+1] - table[x]) /
+ (((x+1)*5) - (x*5))) * (input - x*5) + table[x];
+}
+
+/** Returns the minimum size of the widget */
+QSize
+TorMapImageView::minimumSizeHint() const
+{
+ return MIN_SIZE;
+}
+
+/** Zooms to fit all currently displayed circuits on the map. If there are no
+ * circuits on the map, the viewport will be returned to its default position
+ * (zoomed all the way out and centered). */
+void
+TorMapImageView::zoomToFit()
+{
+ QRectF rect = circuitBoundingBox();
+
+ if (rect.isNull()) {
+ /* If there are no circuits, zoom all the way out */
+ resetZoomPoint();
+ zoom(0.0);
+ } else {
+ /* Zoom in on the displayed circuits */
+ float zoomLevel = 1.0 - qMax(rect.height()/float(MAP_HEIGHT),
+ rect.width()/float(MAP_WIDTH));
+
+ zoom(rect.center().toPoint(), zoomLevel+0.2);
+ }
+}
+
+/** Zoom to the circuit on the map with the given <b>circid</b>. */
+void
+TorMapImageView::zoomToCircuit(const CircuitId &circid)
+{
+ if (_circuits.contains(circid)) {
+ QPair<QPainterPath*,bool> *pair = _circuits.value(circid);
+ QRectF rect = ((QPainterPath *)pair->first)->boundingRect();
+ if (!rect.isNull()) {
+ float zoomLevel = 1.0 - qMax(rect.height()/float(MAP_HEIGHT),
+ rect.width()/float(MAP_WIDTH));
+
+ zoom(rect.center().toPoint(), zoomLevel+0.2);
+ }
+ }
+}
+
+/** Zooms in on the router with the given <b>id</b>. */
+void
+TorMapImageView::zoomToRouter(const QString &id)
+{
+ QPair<QPointF,bool> *routerPair;
+
+ if (_routers.contains(id)) {
+ deselectAll();
+ routerPair = _routers.value(id);
+ routerPair->second = true; /* Set the router point to "selected" */
+ zoom(routerPair->first.toPoint(), 1.0);
+ }
+}
+
+/** Computes a bounding box around all currently displayed circuit paths on
+ * the map. */
+QRectF
+TorMapImageView::circuitBoundingBox()
+{
+ QRectF rect;
+
+ /* Compute the union of bounding rectangles for all circuit paths */
+ foreach (CircuitId circid, _circuits.keys()) {
+ QPair<QPainterPath*,bool> *pair = _circuits.value(circid);
+ QPainterPath *circuit = pair->first;
+ rect = rect.unite(circuit->boundingRect());
+ }
+ return rect;
+}
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/TorMapImageView.h (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/TorMapImageView.h)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/TorMapImageView.h (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/TorMapImageView.h 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,87 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file TorMapImageView.h
+** \version $Id$
+** \brief Displays Tor servers and circuits on a map of the world
+*/
+
+#ifndef _TORMAPIMAGEVIEW_H
+#define _TORMAPIMAGEVIEW_H
+
+#include "ZImageView.h"
+#include "GeoIp.h"
+
+#include "RouterDescriptor.h"
+#include "Circuit.h"
+
+#include <QHash>
+#include <QPair>
+#include <QPainter>
+#include <QPainterPath>
+
+
+class TorMapImageView : public ZImageView
+{
+ Q_OBJECT
+
+public:
+ /** Default constructor. */
+ TorMapImageView(QWidget *parent = 0);
+ /** Destructor. */
+ ~TorMapImageView();
+
+ /** Plots the given router on the map using the given coordinates. */
+ void addRouter(const RouterDescriptor &desc, const GeoIp &geoip);
+ /** Plots the given circuit on the map. */
+ void addCircuit(const CircuitId &circid, const QStringList &path);
+ /** Selects and hightlights a router on the map. */
+ void selectRouter(const QString &id);
+ /** Selects and highlights a circuit on the map. */
+ void selectCircuit(const CircuitId &circid);
+ /** Returns the minimum size of the widget */
+ QSize minimumSizeHint() const;
+
+public slots:
+ /** Removes a circuit from the map. */
+ void removeCircuit(const CircuitId &circid);
+ /** Deselects all the highlighted circuits and routers */
+ void deselectAll();
+ /** Clears the known routers and removes all the data from the map */
+ void clear();
+ /** Zooms to fit all currently displayed circuits on the map. */
+ void zoomToFit();
+ /** Zoom to a particular router on the map. */
+ void zoomToRouter(const QString &id);
+ /** Zoom to the circuit on the map with the given <b>circid</b>. */
+ void zoomToCircuit(const CircuitId &circid);
+
+protected:
+ /** Paints the current circuits and streams on the image. */
+ virtual void paintImage(QPainter *painter);
+
+private:
+ /** Converts world space coordinates into map space coordinates */
+ QPointF toMapSpace(float latitude, float longitude);
+ /** Linearly interpolates using the values in the projection table */
+ float lerp(float input, float *table);
+ /** Computes a bounding box around all currently displayed circuit paths on
+ * the map. */
+ QRectF circuitBoundingBox();
+
+ /** Stores map locations for tor routers */
+ QHash<QString, QPair<QPointF,bool>* > _routers;
+ /** Stores circuit information */
+ QHash<CircuitId, QPair<QPainterPath *,bool>* > _circuits;
+};
+
+#endif
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/TorMapWidget.cpp (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/TorMapWidget.cpp)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/TorMapWidget.cpp (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/TorMapWidget.cpp 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,290 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file TorMapWidget.cpp
+** \version $Id$
+** \brief Displays Tor servers and circuits on a map of the world
+*/
+
+#include "TorMapWidget.h"
+#include "TorMapWidgetInputHandler.h"
+#include "TorMapWidgetPopupMenu.h"
+#include "Vidalia.h"
+
+#include <QStringList>
+
+using namespace Marble;
+
+/** QPens to use for drawing different map elements */
+#define CIRCUIT_NORMAL_PEN QPen(Qt::blue, 2.0)
+#define CIRCUIT_SELECTED_PEN QPen(Qt::green, 3.0)
+
+
+/** Default constructor */
+TorMapWidget::TorMapWidget(QWidget *parent)
+ : MarbleWidget(parent)
+{
+ setMapThemeId("earth/srtm/srtm.dgml");
+ setShowScaleBar(false);
+ setShowCrosshairs(false);
+ setAnimationsEnabled(true);
+ setCursor(Qt::OpenHandCursor);
+
+ TorMapWidgetInputHandler *handler = new TorMapWidgetInputHandler();
+ TorMapWidgetPopupMenu *popupMenu = new TorMapWidgetPopupMenu(this);
+
+ connect(handler, SIGNAL(featureClicked(QPoint,Qt::MouseButton)),
+ popupMenu, SLOT(featureClicked(QPoint,Qt::MouseButton)));
+ connect(popupMenu, SIGNAL(displayRouterInfo(QString)),
+ this, SIGNAL(displayRouterInfo(QString)));
+
+ /* We can't call setInputHandler() until MarbleWidget has called its
+ * internal _q_initGui() method, which doesn't happen until a
+ * QTimer::singleShot(0, this, SLOT(_q_initGui())) timer set in its
+ * constructor times out. So force that event to process now. */
+ vApp->processEvents(QEventLoop::ExcludeUserInputEvents
+ | QEventLoop::ExcludeSocketNotifiers);
+
+ setInputHandler(handler);
+}
+
+/** Destructor */
+TorMapWidget::~TorMapWidget()
+{
+ clear();
+}
+
+/** Adds a router to the map. */
+void
+TorMapWidget::addRouter(const RouterDescriptor &desc, const GeoIp &geoip)
+{
+ QString kml;
+ qreal lon = geoip.longitude();
+ qreal lat = geoip.latitude();
+ quint64 bw;
+
+ bw = qMin(desc.averageBandwidth(), desc.burstBandwidth());
+ bw = qMin(bw, desc.observedBandwidth());
+
+ kml.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<kml xmlns=\"http://earth.google.com/kml/2.0\">"
+ "<Document>"
+ " <Style id=\"normalPlacemark\">"
+ " <IconStyle><Icon><href>:/images/icons/placemark-relay.png</href></Icon></IconStyle>"
+ " </Style>"
+ );
+
+ kml.append("<Placemark>");
+ kml.append("<styleUrl>#normalPlacemark</styleUrl>");
+ kml.append(QString("<name>%1</name>").arg(desc.name()));
+ kml.append(QString("<description>%1</description>").arg(desc.id()));
+ kml.append(QString("<role>1</role>"));
+ kml.append(QString("<address>%1</address>").arg(geoip.toString()));
+ kml.append(QString("<CountryNameCode>%1</CountryNameCode>").arg(geoip.country()));
+ kml.append(QString("<pop>%1</pop>").arg(10 * bw));
+ kml.append(QString("<Point>"
+ " <coordinates>%1,%2</coordinates>"
+ "</Point>").arg(lon).arg(lat));
+ kml.append("</Placemark>");
+ kml.append("</Document></kml>");
+
+ QString id = desc.id();
+ addPlaceMarkData(kml, id);
+ _routers.insert(id, GeoDataCoordinates(lon, lat, 0.0,
+ GeoDataCoordinates::Degree));
+}
+
+/** Adds a circuit to the map using the given ordered list of router IDs. */
+void
+TorMapWidget::addCircuit(const CircuitId &circid, const QStringList &path)
+{
+ /* XXX: Is it better to do KML LineString-based circuit drawing here,
+ * instead of going with a QPainter-based approach? I gave it a brief
+ * try once but failed. It might be worth looking into harder if we
+ * want to make circuits selectable on the map too.
+ */
+
+ /* It doesn't make sense to draw a path of length less than two */
+ if (path.size() < 2)
+ return;
+
+ if (_circuits.contains(circid)) {
+ /* Extend an existing path */
+ CircuitGeoPath *geoPath = _circuits.value(circid);
+
+ QString router = path.at(path.size()-1);
+ if (_routers.contains(router)) {
+ GeoDataCoordinates coords = _routers.value(router);
+ geoPath->first.append(new GeoDataCoordinates(coords));
+ }
+ } else {
+ /* Construct a new path */
+ CircuitGeoPath *geoPath = new CircuitGeoPath();
+ geoPath->second = false; /* initially unselected */
+
+ foreach (QString router, path) {
+ if (_routers.contains(router)) {
+ GeoDataCoordinates coords = _routers.value(router);
+ geoPath->first.append(new GeoDataCoordinates(coords));
+ }
+ }
+ geoPath->first.setTessellationFlags(Tessellate | RespectLatitudeCircle);
+ _circuits.insert(circid, geoPath);
+ }
+
+ repaint();
+}
+
+/** Removes a circuit from the map. */
+void
+TorMapWidget::removeCircuit(const CircuitId &circid)
+{
+ CircuitGeoPath *path = _circuits.take(circid);
+ if (path) {
+ GeoDataLineString coords = path->first;
+ qDeleteAll(coords.begin(), coords.end());
+ delete path;
+ }
+
+ repaint();
+}
+
+/** Selects and highlights the router on the map. */
+void
+TorMapWidget::selectRouter(const QString &id)
+{
+#if 0
+ if (_routers.contains(id)) {
+ QPair<QPointF, bool> *routerPair = _routers.value(id);
+ routerPair->second = true;
+ }
+ repaint();
+#endif
+}
+
+/** Selects and highlights the circuit with the id <b>circid</b>
+ * on the map. */
+void
+TorMapWidget::selectCircuit(const CircuitId &circid)
+{
+ if (_circuits.contains(circid)) {
+ CircuitGeoPath *path = _circuits.value(circid);
+ path->second = true;
+ }
+
+ repaint();
+}
+
+/** Deselects any highlighted routers or circuits */
+void
+TorMapWidget::deselectAll()
+{
+#if 0
+ /* Deselect all router points */
+ foreach (QString router, _routers.keys()) {
+ QPair<QPointF,bool> *routerPair = _routers.value(router);
+ routerPair->second = false;
+ }
+#endif
+ /* Deselect all circuit paths */
+ foreach (CircuitGeoPath *path, _circuits.values()) {
+ path->second = false;
+ }
+
+ repaint();
+}
+
+/** Clears the list of routers and removes all the data on the map */
+void
+TorMapWidget::clear()
+{
+ foreach (QString id, _routers.keys()) {
+ removePlaceMarkKey(id);
+ }
+
+ foreach (CircuitId circid, _circuits.keys()) {
+ CircuitGeoPath *path = _circuits.take(circid);
+ GeoDataLineString coords = path->first;
+ qDeleteAll(coords.begin(), coords.end());
+ delete path;
+ }
+
+ repaint();
+}
+
+/** Zooms the map to fit entirely within the constraints of the current
+ * viewport size. */
+void
+TorMapWidget::zoomToFit()
+{
+ int width = size().width();
+ int height = size().height();
+
+ setRadius(qMin(width, height) / 2);
+
+ /* XXX: Calling setRadius() seems to cause Marble to no longer draw the
+ * atmosphere. So, re-enable it. */
+ setShowAtmosphere(true);
+}
+
+/** Zoom to the circuit on the map with the given <b>circid</b>. */
+void
+TorMapWidget::zoomToCircuit(const CircuitId &circid)
+{
+#if 0
+ if (_circuits.contains(circid)) {
+ QPair<QPainterPath*,bool> *pair = _circuits.value(circid);
+ QRectF rect = ((QPainterPath *)pair->first)->boundingRect();
+ if (!rect.isNull()) {
+ float zoomLevel = 1.0 - qMax(rect.height()/float(MAP_HEIGHT),
+ rect.width()/float(MAP_WIDTH));
+
+ zoom(rect.center().toPoint(), zoomLevel+0.2);
+ }
+ }
+#endif
+}
+
+/** Zooms in on the router with the given <b>id</b>. */
+void
+TorMapWidget::zoomToRouter(const QString &id)
+{
+ if (_routers.contains(id)) {
+ qreal lon, lat;
+ GeoDataCoordinates coords = _routers.value(id);
+ coords.geoCoordinates(lon, lat, GeoDataPoint::Degree);
+
+ zoomView(maximumZoom());
+ centerOn(lon, lat, true);
+ }
+}
+
+/** Paints the current circuits and streams on the image. */
+void
+TorMapWidget::customPaint(GeoPainter *painter)
+{
+ bool selected = false;
+
+ painter->autoMapQuality();
+ painter->setPen(CIRCUIT_NORMAL_PEN);
+
+ foreach (CircuitGeoPath *path, _circuits.values()) {
+ if (! path->second && selected) {
+ painter->setPen(CIRCUIT_NORMAL_PEN);
+ selected = false;
+ } else if (path->second && ! selected) {
+ painter->setPen(CIRCUIT_SELECTED_PEN);
+ selected = true;
+ }
+ painter->drawPolyline(path->first);
+ }
+}
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/TorMapWidget.h (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/TorMapWidget.h)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/TorMapWidget.h (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/TorMapWidget.h 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,89 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file TorMapWidget.h
+** \version $Id$
+** \brief Displays Tor servers and circuits on a map of the world
+*/
+
+#ifndef _TORMAPWIDGET_H
+#define _TORMAPWIDGET_H
+
+#include "RouterDescriptor.h"
+#include "GeoIp.h"
+
+#include "Circuit.h"
+#include "Stream.h"
+
+#include <MarbleWidget.h>
+#include <GeoPainter.h>
+#include <GeoDataCoordinates.h>
+#include <GeoDataLineString.h>
+
+#include <QHash>
+#include <QPair>
+#include <QPainterPath>
+
+typedef QPair<Marble::GeoDataLineString, bool> CircuitGeoPath;
+
+
+class TorMapWidget : public Marble::MarbleWidget
+{
+ Q_OBJECT
+
+public:
+ /** Default constructor. */
+ TorMapWidget(QWidget *parent = 0);
+ /** Destructor. */
+ ~TorMapWidget();
+
+ /** Plots the given router on the map using the given coordinates. */
+ void addRouter(const RouterDescriptor &desc, const GeoIp &geoip);
+ /** Plots the given circuit on the map. */
+ void addCircuit(const CircuitId &circid, const QStringList &path);
+ /** Selects and hightlights a router on the map. */
+ void selectRouter(const QString &id);
+ /** Selects and highlights a circuit on the map. */
+ void selectCircuit(const CircuitId &circid);
+
+public slots:
+ /** Removes a circuit from the map. */
+ void removeCircuit(const CircuitId &circid);
+ /** Deselects all the highlighted circuits and routers */
+ void deselectAll();
+ /** Clears the known routers and removes all the data from the map */
+ void clear();
+ /** Zooms the map to fit entirely within the constraints of the current
+ * viewport size. */
+ void zoomToFit();
+ /** Zoom to a particular router on the map. */
+ void zoomToRouter(const QString &id);
+ /** Zoom to the circuit on the map with the given <b>circid</b>. */
+ void zoomToCircuit(const CircuitId &circid);
+
+signals:
+ /** Emitted when the user selects a router placemark on the map. <b>id</b>
+ * contain's the selected router's fingerprint. */
+ void displayRouterInfo(const QString &id);
+
+protected:
+ /** Paints the current circuits and streams on the image. */
+ virtual void customPaint(Marble::GeoPainter *painter);
+
+private:
+ /** Stores placemark IDs for Tor routers. */
+ QHash<QString, Marble::GeoDataCoordinates> _routers;
+ /** Stores circuit information */
+ QHash<CircuitId, CircuitGeoPath*> _circuits;
+};
+
+#endif
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/TorMapWidgetInputHandler.cpp (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/TorMapWidgetInputHandler.cpp)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/TorMapWidgetInputHandler.cpp (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/TorMapWidgetInputHandler.cpp 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,138 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+#include "TorMapWidgetInputHandler.h"
+
+#include <MarbleWidget.h>
+#include <MarbleMap.h>
+#include <MarbleModel.h>
+#include <ViewParams.h>
+#include <ViewportParams.h>
+
+#include <QTimer>
+#include <QMouseEvent>
+#include <QWheelEvent>
+#include <QPersistentModelIndex>
+
+using namespace Marble;
+
+
+/** Amount to zoom in or out when responding to mouse double clicks. This
+ * value was taken from MarbleMap.cpp.
+ */
+#define MAP_ZOOM_STEP 40
+
+/** Number of units the mouse must be clicked and dragged before it will
+ * force a map rotation and repaint.
+*/
+#define MIN_DRAG_THRESHOLD 3
+
+
+TorMapWidgetInputHandler::TorMapWidgetInputHandler()
+ : MarbleWidgetInputHandler()
+{
+}
+
+bool
+TorMapWidgetInputHandler::eventFilter(QObject *obj, QEvent *e)
+{
+ Q_UNUSED(obj);
+
+ QWheelEvent *wheelEvent = 0;
+ QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(e);
+
+ switch (e->type()) {
+ case QEvent::MouseButtonPress:
+ _mousePressedX = mouseEvent->x();
+ _mousePressedY = mouseEvent->y();
+ _mousePressedLon = m_widget->centerLongitude();
+ _mousePressedLat = m_widget->centerLatitude();
+
+ if (pointHasFeatures(mouseEvent->pos()))
+ emit featureClicked(mouseEvent->pos(), mouseEvent->button());
+ else
+ m_widget->setCursor(Qt::ClosedHandCursor);
+ break;
+
+ case QEvent::MouseButtonRelease:
+ if (! pointHasFeatures(mouseEvent->pos()))
+ m_widget->setCursor(Qt::OpenHandCursor);
+ else
+ m_widget->setCursor(Qt::PointingHandCursor);
+ break;
+
+ case QEvent::MouseMove:
+ if (mouseEvent->buttons() & Qt::LeftButton) {
+ // Pan the map if the left button is pressed while dragging
+ int dx = mouseEvent->x() - _mousePressedX;
+ int dy = mouseEvent->y() - _mousePressedY;
+
+ if (abs(dx) <= MIN_DRAG_THRESHOLD && abs(dy) <= MIN_DRAG_THRESHOLD)
+ return true;
+ m_widget->setCursor(Qt::ClosedHandCursor);
+
+ qreal dir = 1;
+ if (m_widget->projection() == Spherical) {
+ if (m_widget->map()->viewParams()->viewport()->polarity() > 0) {
+ if (mouseEvent->y() < (-m_widget->northPoleY() + m_widget->height()/2))
+ dir = -1;
+ } else {
+ if (mouseEvent->y() > (+m_widget->northPoleY() + m_widget->height()/2))
+ dir = -1;
+ }
+ }
+
+ qreal radius = (qreal)(m_widget->radius());
+ qreal lon = (qreal)(_mousePressedLon) - 90.0 * dir * dx / radius;
+ qreal lat = (qreal)(_mousePressedLat) + 90.0 * dy / radius;
+ m_widget->centerOn(lon, lat, false);
+
+ return true;
+ } else {
+ // Change the mouse cursor if we're hovering over a relay placemark
+ if (pointHasFeatures(mouseEvent->pos()) > 0)
+ m_widget->setCursor(Qt::PointingHandCursor);
+ else
+ m_widget->setCursor(Qt::OpenHandCursor);
+ }
+ break;
+
+ case QEvent::MouseButtonDblClick:
+ // Adjust the zoom level on the map
+ if (mouseEvent->button() == Qt::LeftButton) {
+ m_widget->zoomViewBy(MAP_ZOOM_STEP);
+ return true;
+ } else if (mouseEvent->button() == Qt::RightButton) {
+ m_widget->zoomViewBy(-MAP_ZOOM_STEP);
+ return true;
+ }
+ break;
+
+ case QEvent::Wheel:
+ // Adjust the zoom level on the map
+ m_widget->setViewContext(Marble::Animation);
+
+ wheelEvent = static_cast<QWheelEvent*>(e);
+ m_widget->zoomViewBy((int)(wheelEvent->delta() / 3));
+ m_mouseWheelTimer->start(400);
+ return true;
+
+ default:
+ break;
+ }
+ return MarbleWidgetInputHandler::eventFilter(obj, e);
+}
+
+bool
+TorMapWidgetInputHandler::pointHasFeatures(const QPoint &point) const
+{
+ return (m_widget->model()->whichFeatureAt(point).size() > 0);
+}
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/TorMapWidgetInputHandler.h (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/TorMapWidgetInputHandler.h)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/TorMapWidgetInputHandler.h (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/TorMapWidgetInputHandler.h 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,55 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+#ifndef _TORMAPWIDGETINPUTHANDLER_H
+#define _TORMAPWIDGETINPUTHANDLER_H
+
+#include "MarbleWidgetInputHandler.h"
+
+#include <QEvent>
+#include <QObject>
+#include <QPoint>
+
+
+class TorMapWidgetInputHandler : public Marble::MarbleWidgetInputHandler
+{
+ Q_OBJECT
+
+public:
+ /** Default constructor.
+ */
+ TorMapWidgetInputHandler();
+
+signals:
+ /** Emitted when the user clicks on a map feature located at <b>point</b>.
+ * <b>button</b> indicates which mouse button was clicked.
+ */
+ void featureClicked(const QPoint &point, Qt::MouseButton button);
+
+protected:
+ /** Filter and handles event <b>e</b> that was sent to widget <b>obj</b>.
+ * <b>obj</b> is always a MarbleWidget object.
+ */
+ virtual bool eventFilter(QObject *obj, QEvent *e);
+
+private:
+ /** Returns true if the map has one or more features located at the screen
+ * position <b>point</b>.
+ */
+ bool pointHasFeatures(const QPoint &point) const;
+
+ int _mousePressedX;
+ int _mousePressedY;
+ qreal _mousePressedLon;
+ qreal _mousePressedLat;
+};
+
+#endif
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/TorMapWidgetPopupMenu.cpp (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/TorMapWidgetPopupMenu.cpp)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/TorMapWidgetPopupMenu.cpp (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/TorMapWidgetPopupMenu.cpp 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,90 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file TorMapWidgetPopupMenu.cpp
+** \version $Id$
+** \brief Popup menu displayed when the user mouse clicks on a map placemark
+*/
+
+#include "TorMapWidgetPopupMenu.h"
+#include "Vidalia.h"
+
+#include <MarbleModel.h>
+#include <MarblePlacemarkModel.h>
+
+#include <QChar>
+#include <QVector>
+#include <QModelIndex>
+
+using namespace Marble;
+
+
+TorMapWidgetPopupMenu::TorMapWidgetPopupMenu(TorMapWidget *widget)
+ : QObject(widget),
+ _widget(widget)
+{
+ _leftClickMenu = new QMenu(widget);
+ connect(_leftClickMenu, SIGNAL(triggered(QAction*)),
+ this, SLOT(relaySelected(QAction*)));
+}
+
+void
+TorMapWidgetPopupMenu::featureClicked(const QPoint &pos, Qt::MouseButton btn)
+{
+ switch (btn) {
+ case Qt::LeftButton:
+ featureLeftClicked(pos);
+ break;
+
+ case Qt::RightButton:
+ break;
+
+ default:
+ break;
+ }
+}
+
+void
+TorMapWidgetPopupMenu::featureLeftClicked(const QPoint &pos)
+{
+ QVector<QModelIndex>::const_iterator it;
+ QVector<QModelIndex> features = _widget->model()->whichFeatureAt(pos);
+ QString name, id;
+ int numRelays = 0;
+
+ _leftClickMenu->clear();
+ for (it = features.constBegin(); it != features.constEnd(); ++it) {
+ QChar role = (*it).data(MarblePlacemarkModel::GeoTypeRole).toChar();
+ if (role == '1') {
+ /* Normal Tor Relay */
+ name = (*it).data().toString();
+ id = (*it).data(MarblePlacemarkModel::DescriptionRole).toString();
+
+ QAction *action = _leftClickMenu->addAction(name);
+ action->setData(id);
+ numRelays++;
+ }
+ }
+
+ if (numRelays == 1)
+ emit displayRouterInfo(id);
+ else if (numRelays > 1)
+ _leftClickMenu->popup(_widget->mapToGlobal(pos));
+}
+
+void
+TorMapWidgetPopupMenu::relaySelected(QAction *action)
+{
+ QString id = action->data().toString();
+ if (! id.isEmpty())
+ emit displayRouterInfo(id);
+}
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/TorMapWidgetPopupMenu.h (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/TorMapWidgetPopupMenu.h)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/TorMapWidgetPopupMenu.h (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/TorMapWidgetPopupMenu.h 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,81 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file TorMapWidgetPopupMenu.h
+** \version $Id$
+** \brief Popup menu displayed when the user mouse clicks on a map placemark
+*/
+
+#ifndef _TORMAPWIDGETPOPUPMENU_H
+#define _TORMAPWIDGETPOPUPMENU_H
+
+#include "TorMapWidget.h"
+
+#include <QObject>
+#include <QPoint>
+#include <QString>
+#include <QMenu>
+
+
+class TorMapWidgetPopupMenu : public QObject
+{
+ Q_OBJECT
+
+public:
+ /** Constructor. <b>widget</b> is the parent map widget on which the popup
+ * menu will be displayed.
+ */
+ TorMapWidgetPopupMenu(TorMapWidget *widget);
+
+public slots:
+ /** Called when the user clicks on one or more map features located at mouse
+ * position <b>pos</b>. <b>button</b> specifies the mouse button clicked.
+ * A popup menu will be displayed depending on which mouse button was
+ * clicked.
+ *
+ * \sa featureLeftClicked
+ */
+ void featureClicked(const QPoint &pos, Qt::MouseButton button);
+
+signals:
+ /** Emitted when the user selects the router placemark whose fingerprint
+ * is <b>id</b>.
+ */
+ void displayRouterInfo(const QString &id);
+
+protected:
+ /** Called when the user left-clicks on one or more placemarks at mouse
+ * position <b>pos</b>. If only one relay placemark exists at <b>pos</b>,
+ * then the displayRouterInfo() signal will be emitted. Otherwise, a
+ * popup menu will be displayed listing all placemarks at this location.
+ *
+ * \sa featureLeftClicked
+ */
+ virtual void featureLeftClicked(const QPoint &pos);
+
+private slots:
+ /** Called when the user selects a relay from the popup menu used to
+ * disambiguate a location with multiple relay placemarks.
+ */
+ void relaySelected(QAction *action);
+
+private:
+ /** The parent map widget on which the popup menu is displayed.
+ */
+ TorMapWidget *_widget;
+
+ /** Menu displayed when the user left-clicks on one or more placemarks.
+ */
+ QMenu *_leftClickMenu;
+};
+
+#endif
+
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/ZImageView.cpp (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/ZImageView.cpp)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/ZImageView.cpp (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/ZImageView.cpp 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,374 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file ZImageView.cpp
+** \version $Id$
+** \brief Displays an image and allows zooming and panning
+*/
+
+#include "ZImageView.h"
+
+#include <QPainter>
+#include <QMouseEvent>
+
+#include <cmath>
+
+#if QT_VERSION >= 0x040200
+#define CURSOR_NORMAL QCursor(Qt::OpenHandCursor)
+#define CURSOR_MOUSE_PRESS QCursor(Qt::ClosedHandCursor)
+#else
+#define CURSOR_NORMAL QCursor(Qt::CrossCursor)
+#define CURSOR_MOUSE_PRESS QCursor(Qt::SizeAllCursor)
+#endif
+
+
+/** Constructor. */
+ZImageView::ZImageView(QWidget *parent)
+ : QWidget(parent)
+{
+ /* Initialize members */
+ _zoom = 0.0;
+ _desiredX = 0.0;
+ _desiredY = 0.0;
+ _maxZoomFactor = 2.0;
+ _padding = 60;
+
+ setCursor(CURSOR_NORMAL);
+ updateViewport();
+ resetZoomPoint();
+ repaint();
+}
+
+/** Sets the displayed image. */
+void
+ZImageView::setImage(QImage& img)
+{
+ _image = img.copy();
+ updateViewport();
+ resetZoomPoint();
+
+ if (isVisible()) {
+ repaint();
+ }
+}
+
+/** Draws the scaled image on the widget. */
+void
+ZImageView::drawScaledImage()
+{
+ if (!isVisible()) {
+ return;
+ }
+
+ QBrush background(QColor("#fdfdfd"));
+ if (_image.isNull()) {
+ QPainter p(this);
+ p.fillRect(rect(), background);
+ return;
+ }
+
+ QRect sRect = rect();
+ QRect iRect = _image.rect();
+ QRect r = _view;
+
+ // Think of the _view as being overlaid on the image. The _view has the same
+ // aspect ratio as the screen, so we cut the _view region out of the _image
+ // and scale it to the screen dimensions and paint it.
+
+ // There is a slight catch in that the _view may be larger than the image in
+ // one or both directions. In that case, we need to reduce the _view region
+ // to lie within the image, then paint the background around it. Copying
+ // a region from an image where the region is bigger than the image results
+ // in the parts outside the image being black, which is not what we want.
+
+ // The view has the same aspect ratio as the screen, so the vertical and
+ // horizontal scale factors will be equal.
+
+ double scaleFactor = double(sRect.width()) / double(_view.width());
+
+ // Constrain r to lie entirely within the image.
+ if (r.top() < 0) {
+ r.setTop(0);
+ }
+ if (iRect.bottom() < r.bottom()) {
+ r.setBottom(iRect.bottom());
+ }
+ if (r.left() < 0) {
+ r.setLeft(0);
+ }
+ if (iRect.right() < r.right()) {
+ r.setRight(iRect.right());
+ }
+
+ // Figure out the size that the 'r' region will be when drawn to the screen.
+ QSize scaleTo(int(double(r.width()) * scaleFactor),
+ int(double(r.height()) * scaleFactor));
+
+ /** Make a copy of the image so we don't ruin the original */
+ QImage i = _image.copy();
+
+ /** Create a QPainter that draws directly on the copied image and call the
+ * virtual function to draw whatever the subclasses need to on the image. */
+ QPainter painter;
+ painter.begin(&i);
+ paintImage(&painter);
+ painter.end();
+
+ /** Rescale the image copy */
+ i = i.copy(r).scaled(scaleTo,
+ Qt::KeepAspectRatioByExpanding,
+ Qt::SmoothTransformation);
+
+ int extraWidth = int(double(sRect.width() - i.width()) / 2.0);
+ int extraHeight = int(double(sRect.height() - i.height()) / 2.0);
+
+ // We don't want to paint the background
+ // because this isn't double buffered and that would flicker.
+ // We could double buffer it, but that would cost ~3 MB of memory.
+
+ QPainter p(this);
+ if (extraWidth > 0) {
+ p.fillRect(0, 0, extraWidth, sRect.height(), background);
+ p.fillRect(sRect.width() - extraWidth, 0,
+ sRect.width(), sRect.height(), background);
+ }
+
+ if (extraHeight > 0) {
+ p.fillRect(0, 0, sRect.width(), extraHeight, background);
+ p.fillRect(0, sRect.height() - extraHeight,
+ sRect.width(), sRect.height(), background);
+ }
+
+ // Finally, paint the image copy.
+ p.drawImage(extraWidth, extraHeight, i);
+}
+
+/** Updates the displayed viewport. */
+void
+ZImageView::updateViewport(int screendx, int screendy)
+{
+ /* The gist of this is to find the biggest and smallest possible viewports,
+ * then use the _zoom factor to interpolate between them. Also pan the
+ * viewport, but constrain each dimension to lie within the image or to be
+ * centered if the image is too small in that direction. */
+
+ QRect sRect = rect();
+ QRect iRect = _image.rect();
+
+ float sw = float(sRect.width());
+ float sh = float(sRect.height());
+ float iw = float(iRect.width());
+ float ih = float(iRect.height());
+
+ // Get the initial max and min sizes for the viewport. These won't have the
+ // correct aspect ratio. They will actually be the least upper bound and
+ // greatest lower bound of the set containing the screen and image rects.
+ float maxw = float(std::max<int>(sRect.width(), iRect.width())) + _padding;
+ float maxh = float(std::max<int>(sRect.height(), iRect.height())) + _padding;
+ float minw = std::ceil(float(sRect.width()) / _maxZoomFactor);
+ float minh = std::ceil(float(sRect.height()) / _maxZoomFactor);
+
+ // Now that we have the glb and the lub, we expand/shrink them until
+ // the aspect ratio is that of the screen.
+ float aspect = sw / sh;
+
+ // Fix the max rect.
+ float newmaxh = maxh;
+ float newmaxw = aspect * newmaxh;
+ if (newmaxw < maxw) {
+ newmaxw = maxw;
+ newmaxh = maxw / aspect;
+ }
+
+ // Fix the min rect.
+ float newminh = minh;
+ float newminw = aspect * newminh;
+ if (minw < newminw) {
+ newminw = minw;
+ newminh = newminw / aspect;
+ }
+
+ // Now interpolate between max and min.
+ float vw = (1.0f - _zoom) * (newmaxw - newminw) + newminw;
+ float vh = (1.0f - _zoom) * (newmaxh - newminh) + newminh;
+
+ _view.setWidth(int(vw));
+ _view.setHeight(int(vh));
+
+ // Now pan the view
+
+ // Convert the pan delta from screen coordinates to view coordinates.
+ float vdx = vw * (float(screendx) / sw);
+ float vdy = vh * (float(screendy) / sh);
+
+ // Constrain the center of the viewport to the image rect.
+ _desiredX = qBound(0.0f, _desiredX + vdx, iw);
+ _desiredY = qBound(0.0f, _desiredY + vdy, ih);
+ _view.moveCenter(QPoint(int(_desiredX), int(_desiredY)));
+
+ QPoint viewCenter = _view.center();
+ float vx = viewCenter.x();
+ float vy = viewCenter.y();
+
+ // The viewport may be wider than the height and/or width. In that case,
+ // center the view over the image in the appropriate directions.
+ //
+ // If the viewport is smaller than the image in either direction, then make
+ // sure the edge of the viewport isn't past the edge of the image.
+
+ vdx = 0;
+ vdy = 0;
+
+ if (iw <= vw) {
+ vdx = (iw / 2.0f) - vx; // Center horizontally.
+ } else {
+ // Check that the edge of the view isn't past the edge of the image.
+ float vl = float(_view.left());
+ float vr = float(_view.right());
+ if (vl < 0) {
+ vdx = -vl;
+ } else if (vr > iw) {
+ vdx = iw - vr;
+ }
+ }
+
+ if (ih <= vh) {
+ vdy = (ih / 2.0f) - vy; // Center vertically.
+ } else {
+ // Check that the edge of the view isn't past the edge of the image.
+ float vt = float(_view.top());
+ float vb = float(_view.bottom());
+ if (vt < 0) {
+ vdy = -vt;
+ } else if (vb > ih) {
+ vdy = ih - vb;
+ }
+ }
+
+ _view.translate(int(vdx), int(vdy));
+}
+
+/** Resets the zoom point back to the center of the viewport. */
+void
+ZImageView::resetZoomPoint()
+{
+ QPoint viewCenter = _view.center();
+ _desiredX = viewCenter.x();
+ _desiredY = viewCenter.y();
+}
+
+/** Handles repainting this widget by updating the viewport and drawing the
+ * scaled image. */
+void
+ZImageView::paintEvent(QPaintEvent*)
+{
+ updateViewport();
+ drawScaledImage();
+}
+
+/** Sets the current zoom percentage to the given value and scrolls the
+ * viewport to center the given point. */
+void
+ZImageView::zoom(QPoint zoomAt, float pct)
+{
+ _desiredX = zoomAt.x();
+ _desiredY = zoomAt.y();
+ zoom(pct);
+}
+
+/** Sets the current zoom percentage to the given value. */
+void
+ZImageView::zoom(float pct)
+{
+ _zoom = qBound(0.0f, pct, 1.0f);
+ repaint();
+}
+
+/** Zooms into the image by 10% */
+void
+ZImageView::zoomIn()
+{
+ zoom(_zoom + .1);
+}
+
+/** Zooms away from the image by 10% */
+void
+ZImageView::zoomOut()
+{
+ zoom(_zoom - .1);
+}
+
+/** Responds to the user pressing a mouse button. */
+void
+ZImageView::mousePressEvent(QMouseEvent *e)
+{
+ e->accept();
+ setCursor(CURSOR_MOUSE_PRESS);
+ _mouseX = e->x();
+ _mouseY = e->y();
+}
+
+/** Responds to the user releasing a mouse button. */
+void
+ZImageView::mouseReleaseEvent(QMouseEvent *e)
+{
+ e->accept();
+ setCursor(CURSOR_NORMAL);
+ updateViewport();
+ resetZoomPoint();
+}
+
+/** Responds to the user double-clicking a mouse button on the image. A left
+ * double-click zooms in on the image and a right double-click zooms out.
+ * Zooming is centered on the location of the double-click. */
+void
+ZImageView::mouseDoubleClickEvent(QMouseEvent *e)
+{
+ e->accept();
+
+ QPoint center = rect().center();
+ int dx = e->x() - center.x();
+ int dy = e->y() - center.y();
+ updateViewport(dx, dy);
+ resetZoomPoint();
+
+ Qt::MouseButton btn = e->button();
+ if (btn == Qt::LeftButton)
+ zoomIn();
+ else if (btn == Qt::RightButton)
+ zoomOut();
+}
+
+/** Responds to the user moving the mouse. */
+void
+ZImageView::mouseMoveEvent(QMouseEvent *e)
+{
+ e->accept();
+ int dx = _mouseX - e->x();
+ int dy = _mouseY - e->y();
+ _mouseX = e->x();
+ _mouseY = e->y();
+
+ updateViewport(dx, dy);
+ if (0.001 <= _zoom) {
+ repaint();
+ }
+}
+
+void
+ZImageView::wheelEvent(QWheelEvent *e)
+{
+ if (e->delta() > 0) {
+ zoomIn();
+ } else {
+ zoomOut();
+ }
+}
Copied: vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/ZImageView.h (from rev 3887, vidalia/branches/extension-api/src/vidalia/network/ZImageView.h)
===================================================================
--- vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/ZImageView.h (rev 0)
+++ vidalia/branches/extension-api/src/vidalia/NetworkMapPlugin/ZImageView.h 2009-06-25 04:04:58 UTC (rev 3889)
@@ -0,0 +1,88 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If you
+** did not receive the LICENSE file with this file, you may obtain it from the
+** Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to the
+** terms described in the LICENSE file.
+*/
+
+/*
+** \file ZImageView.h
+** \version $Id$
+** \brief Displays an image and allows zooming and panning
+*/
+
+#ifndef ZIMAGEVIEW_H
+#define ZIMAGEVIEW_H
+
+#include <QImage>
+#include <QPixmap>
+#include <QWidget>
+
+
+class ZImageView : public QWidget
+{
+ Q_OBJECT
+
+public:
+ /** Default constructor. */
+ ZImageView(QWidget *parent = 0);
+ /** Sets the displayed image. */
+ void setImage(QImage& pixmap);
+
+public slots:
+ /** Resets the center zoom point back to the center of the viewport. */
+ void resetZoomPoint();
+ /** Sets the current zoom level to the given percent. */
+ void zoom(float pct);
+ /** Sets the current zoom level to the given percent and scrolls the window
+ * to place the specified point in the middle. */
+ void zoom(QPoint zoomAt, float pct);
+ /** Zooms into the displayed image by 5% */
+ void zoomIn();
+ /** Zooms away from the displayed image by 5% */
+ void zoomOut();
+
+protected:
+ /** Virtual method to let subclasses paint on the image before it's scaled. */
+ virtual void paintImage(QPainter *painter) { Q_UNUSED(painter); }
+ /** Updates the viewport and repaints the displayed image. */
+ virtual void paintEvent(QPaintEvent*);
+ /** Handles the user pressing a mouse button. */
+ virtual void mousePressEvent(QMouseEvent* e);
+ /** Handles the user releasing a mouse button. */
+ virtual void mouseReleaseEvent(QMouseEvent* e);
+ /** Handles the user moving the mouse. */
+ virtual void mouseMoveEvent(QMouseEvent* e);
+ /** Handles the user double-clicking a mouse button. */
+ virtual void mouseDoubleClickEvent(QMouseEvent *e);
+ /** Handles the wheel events. */
+ virtual void wheelEvent(QWheelEvent *e);
+
+ /** Update the viewport. This will set _view to a region that,
+ * when copied from the image and scaled to the screen size, will
+ * show what is expected. The _view may be larger in one or more
+ * directions than the image, and you must deal with the
+ * non-overlapping regions. */
+ void updateViewport(int screendx=0, int screendy=0);
+ /** Redraws the scaled image in the viewport. */
+ void drawScaledImage();
+
+private:
+ float _zoom; /**< The current zoom level. */
+ QImage _image; /**< The displayed image. */
+ float _padding; /**< Amount of padding to use on the side of the image. */
+ float _maxZoomFactor; /**< Maximum amount to zoom into the image. */
+
+ int _mouseX; /**< The x-coordinate of the current mouse position. */
+ int _mouseY; /**< The y-coordinate of the current mouse position. */
+
+ QRect _view; /**< The displayed viewport. */
+ float _desiredX; /**< The X value we desire (???). */
+ float _desiredY; /**< The Y value we desire (???). */
+};
+
+#endif
+
Modified: vidalia/branches/extension-api/src/vidalia/PluginManager.cpp
===================================================================
--- vidalia/branches/extension-api/src/vidalia/PluginManager.cpp 2009-06-25 03:57:32 UTC (rev 3888)
+++ vidalia/branches/extension-api/src/vidalia/PluginManager.cpp 2009-06-25 04:04:58 UTC (rev 3889)
@@ -34,11 +34,12 @@
foreach (QObject *plugin, QPluginLoader::staticInstances()) {
VidaliaPluginInterface* pluginInstance =
qobject_cast<VidaliaPluginInterface*>(plugin);
- if (pluginInstance) {
- _pluginList.push_back(pluginInstance);
- } else {
- /* Invalid plugin type? */
- }
+ if (pluginInstance) {
+ vInfo(pluginInstance->pluginHumanName());
+ _pluginList.push_back(pluginInstance);
+ } else {
+ /* Invalid plugin type? */
+ }
}
#if 0
QDir dataPluginsDir = QDir(dataDirectory);
Modified: vidalia/branches/extension-api/src/vidalia/PluginManager.h
===================================================================
--- vidalia/branches/extension-api/src/vidalia/PluginManager.h 2009-06-25 03:57:32 UTC (rev 3888)
+++ vidalia/branches/extension-api/src/vidalia/PluginManager.h 2009-06-25 04:04:58 UTC (rev 3889)
@@ -14,6 +14,8 @@
** \brief Class for loading and managing plugins within Vidalia.
*/
+#include "Vidalia.h"
+
#include <QList>
#include <QObject>
#include <QString>
Modified: vidalia/branches/extension-api/src/vidalia/VidaliaPanel.h
===================================================================
--- vidalia/branches/extension-api/src/vidalia/VidaliaPanel.h 2009-06-25 03:57:32 UTC (rev 3888)
+++ vidalia/branches/extension-api/src/vidalia/VidaliaPanel.h 2009-06-25 04:04:58 UTC (rev 3889)
@@ -26,8 +26,10 @@
class VidaliaPanel : public QMainWindow
{
public:
+ /** Constructor for a Vidalia Panel. */
+ VidaliaPanel() {}
/** Destructor for a Vidalia Panel. */
- virtual ~VidaliaPanel() = 0;
+ virtual ~VidaliaPanel() {}
/** Returns the label to be displayed on the panel's tab. */
virtual QString tabLabel() const = 0;
/** Returns the icon to be displayed adjacent to the tabLabel. */