[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[vidalia-svn] r3485: Merge the Marble branch to trunk. It's still optional and di (in vidalia/trunk: . cmake debian pkg/win32 src/common src/torcontrol src/vidalia src/vidalia/network src/vidalia/res src/vidalia/res/icons)



Author: edmanm
Date: 2009-01-30 23:22:30 -0500 (Fri, 30 Jan 2009)
New Revision: 3485

Added:
   vidalia/trunk/cmake/FindMarble.cmake
   vidalia/trunk/src/vidalia/network/routerinfodialog.cpp
   vidalia/trunk/src/vidalia/network/routerinfodialog.h
   vidalia/trunk/src/vidalia/network/routerinfodialog.ui
   vidalia/trunk/src/vidalia/network/tormapimageview.cpp
   vidalia/trunk/src/vidalia/network/tormapimageview.h
   vidalia/trunk/src/vidalia/network/tormapwidgetinputhandler.cpp
   vidalia/trunk/src/vidalia/network/tormapwidgetinputhandler.h
   vidalia/trunk/src/vidalia/network/tormapwidgetpopupmenu.cpp
   vidalia/trunk/src/vidalia/network/tormapwidgetpopupmenu.h
   vidalia/trunk/src/vidalia/res/icons/placemark-relay.png
Modified:
   vidalia/trunk/
   vidalia/trunk/CMakeLists.txt
   vidalia/trunk/INSTALL
   vidalia/trunk/config.h.in
   vidalia/trunk/debian/faq
   vidalia/trunk/pkg/win32/vidalia.wxs.in
   vidalia/trunk/src/common/stringutil.cpp
   vidalia/trunk/src/common/stringutil.h
   vidalia/trunk/src/torcontrol/torcontrol.cpp
   vidalia/trunk/src/torcontrol/torcontrol.h
   vidalia/trunk/src/vidalia/CMakeLists.txt
   vidalia/trunk/src/vidalia/network/circuitlistwidget.cpp
   vidalia/trunk/src/vidalia/network/netviewer.cpp
   vidalia/trunk/src/vidalia/network/netviewer.h
   vidalia/trunk/src/vidalia/network/routerdescriptorview.cpp
   vidalia/trunk/src/vidalia/network/routerdescriptorview.h
   vidalia/trunk/src/vidalia/network/routerlistitem.h
   vidalia/trunk/src/vidalia/network/tormapwidget.cpp
   vidalia/trunk/src/vidalia/network/tormapwidget.h
   vidalia/trunk/src/vidalia/res/vidalia.qrc
   vidalia/trunk/src/vidalia/vidalia.cpp
Log:
Merge the Marble branch to trunk. It's still optional and disabled by
default.



Property changes on: vidalia/trunk
___________________________________________________________________
Added: svn:mergeinfo
   + /vidalia/branches/marble:3435-3484

Modified: vidalia/trunk/CMakeLists.txt
===================================================================
--- vidalia/trunk/CMakeLists.txt	2009-01-31 03:39:39 UTC (rev 3484)
+++ vidalia/trunk/CMakeLists.txt	2009-01-31 04:22:30 UTC (rev 3485)
@@ -25,20 +25,28 @@
   cmake_policy(SET CMP0003 OLD)
 endif(COMMAND cmake_policy)
 
-## Require Qt >= 4.2.0
-set(QT_MIN_VERSION    "4.2.0")
+## We declare this option here, because it determines the minimum
+## required Qt version
+option(USE_MARBLE "Enable the KDE Marble-based map widget." OFF)
 
+## Specify the minimum version of Qt required
+if (USE_MARBLE)
+  set(QT_MIN_VERSION    "4.4.0")
+else(USE_MARBLE)
+  set(QT_MIN_VERSION    "4.2.0")
+endif(USE_MARBLE)
+
 ## Specify the Qt libraries used
 include(FindQt4)
 find_package(Qt4 REQUIRED)
 set(QT_USE_QTNETWORK  true)
 set(QT_USE_QTXML      true)
+set(QT_USE_QTSVG      true)
 include(${QT_USE_FILE})
 include(${CMAKE_SOURCE_DIR}/cmake/VidaliaMacros.cmake)
 include(CheckIncludeFile)
 include(CheckIncludeFileCXX)
 include(CheckTypeSize)
-include(CPack)
 if (WIN32)
   include(${CMAKE_SOURCE_DIR}/cmake/FindWiX.cmake)
 endif(WIN32)
@@ -83,6 +91,11 @@
   option(USE_AUTOUPDATE "Enable automatic software update support." OFF)
 endif(WIN32)
 
+## Find the KDE Marble library
+if (USE_MARBLE)
+  include(${CMAKE_SOURCE_DIR}/cmake/FindMarble.cmake)
+endif(USE_MARBLE)
+
 ## Check for system header files
 check_include_file("limits.h" HAVE_LIMITS_H)
 check_include_file("sys/limits.h" HAVE_SYS_LIMITS_H)

Modified: vidalia/trunk/INSTALL
===================================================================
--- vidalia/trunk/INSTALL	2009-01-31 03:39:39 UTC (rev 3484)
+++ vidalia/trunk/INSTALL	2009-01-31 04:22:30 UTC (rev 3485)
@@ -198,6 +198,8 @@
 
   -DCMAKE_BUILD_TYPE=<buildtype>          Available build types include:
                                             "release", "debug", "minsizerel"
+  -DUSE_MARBLE=1                          Replace the flat map with a
+                                          3-D sphere using the Marble libraries.
 
 For example, to configure CMake to look for Qt in "/usr/local/Qt-4.3.2/bin",
 you would run:

Copied: vidalia/trunk/cmake/FindMarble.cmake (from rev 3484, vidalia/branches/marble/cmake/FindMarble.cmake)
===================================================================
--- vidalia/trunk/cmake/FindMarble.cmake	                        (rev 0)
+++ vidalia/trunk/cmake/FindMarble.cmake	2009-01-31 04:22:30 UTC (rev 3485)
@@ -0,0 +1,111 @@
+##
+##  $Id$
+## 
+##  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.
+##
+##  This file incorporates work from the KDE Marble project, and is covered by 
+##  the following copyright and permission notice:
+##
+##  Redistribution and use in source and binary forms, with or without
+##  modification, are permitted provided that the following conditions are
+##  met:
+##
+##   * Redistributions of source code must retain the above copyright notice,
+##     this list of conditions and the following disclaimer.
+##
+##   * Redistributions in binary form must reproduce the above copyright 
+##     notice, this list of conditions and the following disclaimer in the 
+##     documentation and/or other materials provided with the distribution.
+##
+##   * Neither the names of the copyright owners nor the names of its
+##     contributors may be used to endorse or promote products derived from 
+##     this software without specific prior written permission.
+##
+##  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
+##  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
+##  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
+##  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
+##  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
+##  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
+##  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
+##  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
+##  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
+##  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+##  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+##
+
+message(STATUS "Looking for Marble header files")
+find_path(MARBLE_INCLUDE_DIR
+  NAMES MarbleWidget.h
+  PATH_SUFFIXES marble
+)
+if (MARBLE_INCLUDE_DIR)
+  message(STATUS "Looking for Marble header files - found")
+else(MARBLE_INCLUDE_DIR)
+  message(FATAL_ERROR "Could not find Marble header files. If Marble is installed, you can run CMake again and specify its location with -DMARBLE_INCLUDE_DIR=<path>")
+endif(MARBLE_INCLUDE_DIR)
+
+
+message(STATUS "Looking for Marble libraries")
+find_library(MARBLEWIDGET_LIBRARY
+  NAMES marblewidget
+  PATHS ${MARBLE_LIBRARY_DIR}
+)
+if (MARBLEWIDGET_LIBRARY)
+  message(STATUS "Looking for Marble libraries - found")
+  set(MARBLE_LIBRARIES ${MARBLEWIDGET_LIBRARY})
+  get_filename_component(MARBLE_LIBRARY_DIR ${MARBLEWIDGET_LIBRARY} PATH)
+else(MARBLEWIDGET_LIBRARY)
+  message(FATAL_ERROR "Could not find Marble libraries. If Marble is installed, you can run CMake again and specify its location with -DMARBLE_LIBRARY_DIR=<path>")
+endif(MARBLEWIDGET_LIBRARY)
+
+
+if (APPLE OR WIN32)
+  message(STATUS "Looking for Marble data files")
+  find_path(MARBLE_DATA_DIR
+    NAMES srtm.dgml
+    PATH_SUFFIXES maps/earth/srtm
+  )
+  if (MARBLE_DATA_DIR)
+    message(STATUS "Looking for Marble data files - ${MARBLE_DATA_DIR}")
+  else (MARBLE_DATA_DIR)
+    message(FATAL_ERROR "Could not find Marble libraries. If Marble is installed, you can run CMake again and specify its location with -DMARBLE_DATA_DIR=<path>")
+  endif(MARBLE_DATA_DIR)
+
+
+  message(STATUS "Looking for Marble plugin widgets")
+  find_library(MARBLE_OVERVIEWMAP_PLUGIN
+    NAMES MarbleOverviewMap
+    PATHS ${MARBLE_PLUGIN_DIR}
+    PATH_SUFFIXES render/overviewmap
+  )
+  if (MARBLE_OVERVIEWMAP_PLUGIN)
+    message(STATUS "Looking for Marble plugin widgets - found overview map plugin")
+    set(MARBLE_PLUGINS ${MARBLE_PLUGINS}
+      ${MARBLE_OVERVIEWMAP_PLUGIN}
+    )
+  endif(MARBLE_OVERVIEWMAP_PLUGIN)
+
+  find_library(MARBLE_STARS_PLUGIN
+    NAMES MarbleStarsPlugin
+    PATHS ${MARBLE_PLUGIN_DIR}
+    PATH_SUFFIXES render/stars
+  )
+  if (MARBLE_STARS_PLUGIN)
+    message(STATUS "Looking for Marble plugin widgets - found stars plugin")
+    set(MARBLE_PLUGINS ${MARBLE_PLUGINS}
+      ${MARBLE_STARS_PLUGIN}
+    )
+  endif(MARBLE_STARS_PLUGIN)
+
+  if (NOT MARBLE_PLUGINS)
+    message(STATUS "Looking for Marble plugin widgets - none found")
+  endif(NOT MARBLE_PLUGINS)
+endif(APPLE OR WIN32)
+

Modified: vidalia/trunk/config.h.in
===================================================================
--- vidalia/trunk/config.h.in	2009-01-31 03:39:39 UTC (rev 3484)
+++ vidalia/trunk/config.h.in	2009-01-31 04:22:30 UTC (rev 3485)
@@ -30,5 +30,7 @@
 
 #cmakedefine USE_AUTOUPDATE
 
+#cmakedefine USE_MARBLE
+
 #endif
 


Property changes on: vidalia/trunk/debian/faq
___________________________________________________________________
Deleted: svn:mergeinfo
   - 

Modified: vidalia/trunk/pkg/win32/vidalia.wxs.in
===================================================================
--- vidalia/trunk/pkg/win32/vidalia.wxs.in	2009-01-31 03:39:39 UTC (rev 3484)
+++ vidalia/trunk/pkg/win32/vidalia.wxs.in	2009-01-31 04:22:30 UTC (rev 3485)
@@ -19,62 +19,192 @@
            EmbedCab="yes" DiskPrompt="CD-ROM #1" />
     <Property Id="DiskPrompt" Value="Vidalia @VERSION@ Installation [1]" />
 
+    <!-- Define the target installation directory hierarchy -->
     <Directory Id="TARGETDIR" Name="SourceDir">
       <Directory Id="ProgramFilesFolder" Name="ProgramFilesDir">
         <Directory Id="INSTALLDIR" Name="Vidalia">
+          <Directory Id="MarblePluginsDir" Name="plugins" />
+          <Directory Id="MarbleDataDir" Name="data">
+            <Directory Id="MarbleMapsDir" Name="maps">
+              <Directory Id="MarbleEarthMapsDir" Name="earth">
+                <Directory Id="MarbleBlueMarbleDataDir" Name="bluemarble" />
+                <Directory Id="MarbleCityLightsDataDir" Name="citylights" />
+                <Directory Id="MarbleSrtmDataDir" Name="srtm" />
+              </Directory>
+            </Directory>
+            <Directory Id="MarbleMwdbiiDir" Name="mwdbii" />
+            <Directory Id="MarblePlacemarkDataDir" Name="placemarks" />
+            <Directory Id="MarbleStarsDataDir" Name="stars" />
+            <Directory Id="MarbleSvgDataDir" Name="svg" />
+          </Directory>
+        </Directory>
+      </Directory>
+    </Directory>
 
-          <!-- Main Vidalia application files -->
-          <Component Id="VidaliaExecutable" Guid="*">
-            <File Id="VidaliaExe" DiskId="1"
-                  Name="vidalia.exe" Source="@Vidalia_BINARY_DIR@\src\vidalia\vidalia.exe" />
-          </Component>
+    <DirectoryRef Id="INSTALLDIR">
+      <!-- Main Vidalia application files -->
+      <Component Id="VidaliaExecutable" Guid="*">
+        <File Id="VidaliaExe" DiskId="1"
+              Name="vidalia.exe" Source="@Vidalia_BINARY_DIR@\src\vidalia\vidalia.exe" />
+      </Component>
 
-          <!-- Vidalia-related documents -->
-          <Component Id="VidaliaDocuments" Guid="6A51C86C-A7D4-407f-9B84-7ADCE016E939">
-            <File Id="VidaliaReadme" DiskId="1"
-                  Name="README" Source="@Vidalia_SOURCE_DIR@\README" />
-            <File Id="VidaliaCredits" DiskId="1"
-                  Name="CREDITS" Source="@Vidalia_SOURCE_DIR@\CREDITS" />
-            <File Id="VidaliaChangeLog" DiskId="1"
-                  Name="CHANGELOG" Source="@Vidalia_SOURCE_DIR@\CHANGELOG" />
-            <File Id="VidaliaLicense" DiskId="1"
-                  Name="LICENSE" Source="@Vidalia_SOURCE_DIR@\LICENSE" />
-            <File Id="VidaliaLicenseGplV2" DiskId="1"
-                  Name="LICENSE-GPLV2" Source="@Vidalia_SOURCE_DIR@\LICENSE-GPLV2" />
-            <File Id="VidaliaLicenseGplV3" DiskId="1"
-                  Name="LICENSE-GPLV3" Source="@Vidalia_SOURCE_DIR@\LICENSE-GPLV3" />
-            <File Id="VidaliaLicenseLgplV3" DiskId="1"
-                  Name="LICENSE-LGPLV3" Source="@Vidalia_SOURCE_DIR@\LICENSE-LGPLV3" />
-            <File Id="VidaliaLicenseOpenSSL" DiskId="1"
-                  Name="LICENSE-OPENSSL" Source="@Vidalia_SOURCE_DIR@\LICENSE-OPENSSL" />
-          </Component>
+      <!-- Vidalia-related documents -->
+      <Component Id="VidaliaDocuments" Guid="6A51C86C-A7D4-407f-9B84-7ADCE016E939">
+        <File Id="VidaliaReadme" DiskId="1"
+              Name="README" Source="@Vidalia_SOURCE_DIR@\README" />
+        <File Id="VidaliaCredits" DiskId="1"
+              Name="CREDITS" Source="@Vidalia_SOURCE_DIR@\CREDITS" />
+        <File Id="VidaliaChangeLog" DiskId="1"
+              Name="CHANGELOG" Source="@Vidalia_SOURCE_DIR@\CHANGELOG" />
+        <File Id="VidaliaLicense" DiskId="1"
+              Name="LICENSE" Source="@Vidalia_SOURCE_DIR@\LICENSE" />
+        <File Id="VidaliaLicenseGplV2" DiskId="1"
+              Name="LICENSE-GPLV2" Source="@Vidalia_SOURCE_DIR@\LICENSE-GPLV2" />
+        <File Id="VidaliaLicenseGplV3" DiskId="1"
+              Name="LICENSE-GPLV3" Source="@Vidalia_SOURCE_DIR@\LICENSE-GPLV3" />
+        <File Id="VidaliaLicenseLgplV3" DiskId="1"
+              Name="LICENSE-LGPLV3" Source="@Vidalia_SOURCE_DIR@\LICENSE-LGPLV3" />
+        <File Id="VidaliaLicenseOpenSSL" DiskId="1"
+              Name="LICENSE-OPENSSL" Source="@Vidalia_SOURCE_DIR@\LICENSE-OPENSSL" />
+      </Component>
 
-          <!-- Qt-related library files -->
-          <Component Id="QtLibrary" Guid="00F354CB-B313-4100-8900-11293A66B385">
-            <File Id="QtCore4Dll" DiskId="1"
-                  Name="QtCore4.dll" Source="@QT_BINARY_DIR@\QtCore4.dll" />
-            <File Id="QtGui4Dll" DiskId="1"
-                  Name="QtGui4.dll" Source="@QT_BINARY_DIR@\QtGui4.dll" />
-            <File Id="QtNetwork4Dll" DiskId="1"
-                  Name="QtNetwork4.dll" Source="@QT_BINARY_DIR@\QtNetwork4.dll" />
-            <File Id="QtXml4Dll" DiskId="1"
-                  Name="QtXml4.dll" Source="@QT_BINARY_DIR@\QtXml4.dll" />
-          </Component>
+      <!-- Qt-related library files -->
+      <Component Id="QtLibrary" Guid="00F354CB-B313-4100-8900-11293A66B385">
+        <File Id="QtCore4Dll" DiskId="1"
+              Name="QtCore4.dll" Source="@QT_BINARY_DIR@\QtCore4.dll" />
+        <File Id="QtGui4Dll" DiskId="1"
+              Name="QtGui4.dll" Source="@QT_BINARY_DIR@\QtGui4.dll" />
+        <File Id="QtNetwork4Dll" DiskId="1"
+              Name="QtNetwork4.dll" Source="@QT_BINARY_DIR@\QtNetwork4.dll" />
+        <File Id="QtXml4Dll" DiskId="1"
+              Name="QtXml4.dll" Source="@QT_BINARY_DIR@\QtXml4.dll" />
+      </Component>
 
-          <!-- MinGW-related library files -->
-          <Component Id="MinGWLibrary" Guid="2287E844-F9CD-4129-8BD0-50D071698194">
-            <File Id="MinGWDll" DiskId="1"
-                  Name="mingwm10.dll" Source="@MINGW_BINARY_DIR@\mingwm10.dll" />
-          </Component>
+      <!-- MinGW-related library files -->
+      <Component Id="MinGWLibrary" Guid="2287E844-F9CD-4129-8BD0-50D071698194">
+        <File Id="MinGWDll" DiskId="1"
+              Name="mingwm10.dll" Source="@MINGW_BINARY_DIR@\mingwm10.dll" />
+      </Component>
 
-          <!-- OpenSSL-related library files -->
-          <Component Id="OpenSSLLibrary" Guid="C3EDC2EC-D0B1-452a-83A6-85B0BC94735B">
-            <File Id="ssleay32dll" DiskId="1"
-                  Name="ssleay32.dll" Source="@OPENSSL_BINARY_DIR@\ssleay32.dll" />
-          </Component>
-        </Directory>
-      </Directory>
+      <!-- OpenSSL-related library files -->
+      <Component Id="OpenSSLLibrary" Guid="C3EDC2EC-D0B1-452a-83A6-85B0BC94735B">
+        <File Id="ssleay32dll" DiskId="1"
+              Name="ssleay32.dll" Source="@OPENSSL_BINARY_DIR@\ssleay32.dll" />
+      </Component>
+      
+      <!-- Marble-related library files -->
+      <Component Id="MarbleWidgetLibrary" Guid="A2968B8D-0E23-4649-B8F3-A988A04F823C">
+        <File Id="MarbleWidgetDll" DiskId="1"
+              Name="libmarblewidget.dll" Source="@MARBLEWIDGET_LIBRARY@" />
+      </Component>
+    </DirectoryRef>
+    
+    <!-- Marble-related plugin files -->
+    <DirectoryRef Id="MarblePluginsDir">
+      <Component Id="MarblePlugins" Guid="B4665EC1-3082-4fca-AA7E-60512ED62EF6">
+        <File Id="MarbleOverviewMapPlugin" DiskId="1"
+              Name="libMarbleOverviewMap.dll" Source="@MARBLE_OVERVIEWMAP_PLUGIN@" />
+        <File Id="MarbleStarsPlugin" DiskId="1"
+              Name="libMarbleStarsPlugin.dll" Source="@MARBLE_STARS_PLUGIN@" />
+      </Component>
+    </DirectoryRef>
+          
+    <!-- Marble-related data files -->
+    <DirectoryRef Id="MarbleDataDir">
+      <Component Id="MarbleLegendsData" Guid="FA6F733F-8249-4b2b-B978-2AA04E06F5D5">
+        <File Id="LandColorsLegend" DiskId="1"
+              Name="landcolors.leg" Source="@MARBLE_DATA_DIR@\landcolors.leg" />
+        <File Id="SeaColorsLegend" DiskId="1"
+              Name="seacolors.leg" Source="@MARBLE_DATA_DIR@\seacolors.leg" />
+      </Component>
+    </DirectoryRef>
+    <DirectoryRef Id="MarbleBlueMarbleDataDir">
+      <Component Id="MarbleBlueMarbleData" Guid="392FEE6B-374A-447a-AECC-0422915305DB">
+        <File Id="MarbleBlueMarbleDgml" DiskId="1"
+              Name="bluemarble.dgml" Source="@MARBLE_DATA_DIR@\maps\earth\bluemarble\bluemarble.dgml" />
+      </Component>
+    </DirectoryRef>
+    <DirectoryRef Id="MarbleCityLightsDataDir">
+      <Component Id="MarbleCityLightsData" Guid="DBF9FBE7-012D-4457-ABB4-9085F7E784F8">
+        <File Id="MarbleCityLightsDgml" DiskId="1"
+              Name="citylights.dgml" Source="@MARBLE_DATA_DIR@\maps\earth\citylights\citylights.dgml" />
+      </Component>
+    </DirectoryRef>
+    <DirectoryRef Id="MarbleSrtmDataDir">
+      <Component Id="MarbleSrtmData" Guid="2C3A9C67-92CB-4fc6-A2F6-AD9118AFD58C">
+        <File Id="MarbleSrtmDgml" DiskId="1"
+              Name="srtm.dgml" Source="@MARBLE_DATA_DIR@\maps\earth\srtm\srtm.dgml" />
+        <File Id="MarbleSrtmJpg" DiskId="1"
+              Name="srtm.jpg" Source="@MARBLE_DATA_DIR@\maps\earth\srtm\srtm.jpg" />
+      </Component>
+    </DirectoryRef>
+    <DirectoryRef Id="MarbleMwdbiiDir">
+      <Component Id="MarbleMwdbiiData" Guid="56ACE658-6892-408a-91E5-7B5BD23D8829">
+        <File Id="DatelinePnt" DiskId="1"
+              Name="DATELINE.PNT" Source="@MARBLE_DATA_DIR@\mwdbii\DATELINE.PNT" />
+        <File Id="PAustPnt" DiskId="1"
+              Name="PAUST.PNT" Source="@MARBLE_DATA_DIR@\mwdbii\PAUST.PNT" />
+        <File Id="PBorderPnt" DiskId="1"
+              Name="PBORDER.PNT" Source="@MARBLE_DATA_DIR@\mwdbii\PBORDER.PNT" />
+        <File Id="PCanProvPnt" DiskId="1"
+              Name="PCANPROV.PNT" Source="@MARBLE_DATA_DIR@\mwdbii\PCANPROV.PNT" />
+        <File Id="PCoastPnt" DiskId="1"
+              Name="PCOAST.PNT" Source="@MARBLE_DATA_DIR@\mwdbii\PCOAST.PNT" />
+        <File Id="PDiffBorderPnt" DiskId="1"
+              Name="PDIFFBORDER.PNT" Source="@MARBLE_DATA_DIR@\mwdbii\PDIFFBORDER.PNT" />
+        <File Id="PGlacierPnt" DiskId="1"
+              Name="PGLACIER.PNT" Source="@MARBLE_DATA_DIR@\mwdbii\PGLACIER.PNT" />
+        <File Id="PIslandPnt" DiskId="1"
+              Name="PISLAND.PNT" Source="@MARBLE_DATA_DIR@\mwdbii\PISLAND.PNT" />
+        <File Id="PLakePnt" DiskId="1"
+              Name="PLAKE.PNT" Source="@MARBLE_DATA_DIR@\mwdbii\PLAKE.PNT" />
+        <File Id="PLakeIslandPnt" DiskId="1"
+              Name="PLAKEISLAND.PNT" Source="@MARBLE_DATA_DIR@\mwdbii\PLAKEISLAND.PNT" />
+        <File Id="PMexicoPnt" DiskId="1"
+              Name="PMEXICO.PNT" Source="@MARBLE_DATA_DIR@\mwdbii\PMEXICO.PNT" />
+        <File Id="PUsa48DiffPnt" DiskId="1"
+              Name="PUSA48.DIFF.PNT" Source="@MARBLE_DATA_DIR@\mwdbii\PUSA48.DIFF.PNT" />
+        <File Id="PUsa48Pnt" DiskId="1"
+              Name="PUSA48.PNT" Source="@MARBLE_DATA_DIR@\mwdbii\PUSA48.PNT" />
+        <File Id="RiverPnt" DiskId="1"
+              Name="RIVER.PNT" Source="@MARBLE_DATA_DIR@\mwdbii\RIVER.PNT" />
+      </Component>
+    </DirectoryRef>
+    <DirectoryRef Id="MarblePlacemarkDataDir">
+      <Component Id="MarblePlacemarkData" Guid="27D54547-A819-435b-9A0C-13284169A9D9">
+        <File Id="BasePlacemarksCache" DiskId="1"
+              Name="baseplacemarks.cache" Source="@MARBLE_DATA_DIR@\placemarks\baseplacemarks.cache" />
+        <File Id="BoundaryPlacemarksCache" DiskId="1"
+              Name="boundaryplacemarks.cache" Source="@MARBLE_DATA_DIR@\placemarks\boundaryplacemarks.cache" />
+        <File Id="ElevPlacemarksCache" DiskId="1"
+              Name="elevplacemarks.cache" Source="@MARBLE_DATA_DIR@\placemarks\elevplacemarks.cache" />
+      </Component>
+    </DirectoryRef>
+    <DirectoryRef Id="MarbleStarsDataDir">
+      <Component Id="MarbleStarsData" Guid="8C5C3F32-67E6-4a4a-9B6C-6D67A6D44D67">
+        <File Id="StarsDat" DiskId="1"
+              Name="stars.dat" Source="@MARBLE_DATA_DIR@\stars\stars.dat" />
+      </Component>
+    </DirectoryRef>
+    <DirectoryRef Id="MarbleSvgDataDir">
+      <Component Id="MarbleSvgData" Guid="03E25BCA-4C58-4571-9F17-061D52877714">
+        <File Id="WorldMapSvg" DiskId="1"
+              Name="worldmap.svg" Source="@MARBLE_DATA_DIR@\svg\worldmap.svg" />
+      </Component>
+    </DirectoryRef>
+    <ComponentGroup Id="MarbleData">
+      <ComponentRef Id="MarbleLegendsData" />
+      <ComponentRef Id="MarbleBlueMarbleData" />
+      <ComponentRef Id="MarbleCityLightsData" />
+      <ComponentRef Id="MarbleSrtmData" />
+      <ComponentRef Id="MarbleMwdbiiData" />
+      <ComponentRef Id="MarblePlacemarkData" />
+      <ComponentRef Id="MarbleStarsData" />
+      <ComponentRef Id="MarbleSvgData" />
+    </ComponentGroup>
 
+    <!-- Application shortcuts and menu items -->
+    <DirectoryRef Id="TARGETDIR">
       <Directory Id="ProgramMenuFolder" Name="Programs">
         <Directory Id="ShortcutFolder" Name="Vidalia">
           <Component Id="AddVidaliaToStartMenu" Guid="0F2CEE2C-8730-432e-8A8F-E49AF78AF28C">
@@ -109,7 +239,7 @@
           <RegistryValue Name="Vidalia" Value='"[INSTALLDIR]vidalia.exe"' Type="string" />
         </RegistryKey>
       </Component>
-    </Directory>
+    </DirectoryRef>
 
     <!-- Build up the feature hierarchy -->
     <Feature Id="Complete" Title="Vidalia"
@@ -123,6 +253,9 @@
         <ComponentRef Id="QtLibrary" />
         <ComponentRef Id="MinGWLibrary" />
         <ComponentRef Id="OpenSSLLibrary" />
+        <ComponentRef Id="MarbleWidgetLibrary" />
+        <ComponentRef Id="MarblePlugins" />
+        <ComponentGroupRef Id="MarbleData"/>
       </Feature>
       <Feature Id="Shortcuts" Title="Shortcuts"
                AllowAdvertise="no" Absent="allow" Level="1"

Modified: vidalia/trunk/src/common/stringutil.cpp
===================================================================
--- vidalia/trunk/src/common/stringutil.cpp	2009-01-31 03:39:39 UTC (rev 3484)
+++ vidalia/trunk/src/common/stringutil.cpp	2009-01-31 04:22:30 UTC (rev 3485)
@@ -14,6 +14,8 @@
 ** \brief Common string manipulation functions
 */
 
+#include <QCoreApplication>
+
 #include "stringutil.h"
 
 
@@ -305,3 +307,49 @@
   return true;
 }
 
+/** Returns a human-readable description of the time elapsed given by
+ * <b>seconds</b>, broken down into days, hours, minutes and seconds. */
+QString
+string_format_uptime(quint64 seconds)
+{
+  QString uptime;
+  int secs  = (seconds % 60);
+  int mins  = (seconds / 60 % 60);
+  int hours = (seconds / 3600 % 24);
+  int days  = (seconds / 86400);
+
+  if (days)
+    uptime += qApp->translate("stringutil.h", "%1 days ").arg(days);
+  if (hours)
+    uptime += qApp->translate("stringutil.h", "%1 hours ").arg(hours);
+  if (mins)
+    uptime += qApp->translate("stringutil.h", "%1 mins ").arg(mins);
+  if (secs)
+    uptime += qApp->translate("stringutil.h", "%1 secs").arg(secs);
+
+  return uptime;
+}
+
+/** Returns a string representation of <b>date</b> formatted according to
+ * "yyyy-MM-dd HH:mm:ss". */
+QString
+string_format_datetime(const QDateTime &date)
+{
+  return date.toString("yyyy-MM-dd HH:mm:ss");
+}
+
+/** Returns a string representation of <b>bytes</b> with the appropriate
+ * suffix of either "B/s", "KB/s", "MB/s" or "GB/s". */
+QString
+string_format_bandwidth(quint64 bytes)
+{
+  if (bytes < 1024)
+    return qApp->translate("stringutil.h", "%1 B/s").arg(bytes);
+  if (bytes < 1048576)
+    return qApp->translate("stringutil.h", "%1 KB/s").arg(bytes/1024.0, 0, 'f', 2);
+  if (bytes < 1073741824)
+    return qApp->translate("stringutil.h", "%1 MB/s").arg(bytes/1048576.0, 0, 'f', 2);
+
+  return qApp->translate("stringutil.h", "%1 GB/s").arg(bytes/1073741824.0, 0, 'f', 2);
+}
+

Modified: vidalia/trunk/src/common/stringutil.h
===================================================================
--- vidalia/trunk/src/common/stringutil.h	2009-01-31 03:39:39 UTC (rev 3484)
+++ vidalia/trunk/src/common/stringutil.h	2009-01-31 04:22:30 UTC (rev 3485)
@@ -19,6 +19,7 @@
 
 #include <QStringList>
 #include <QHash>
+#include <QDateTime>
 
 
 /** Creates a QStringList from the array of C strings. */
@@ -73,5 +74,17 @@
  * otherwise. */
 bool string_is_hex(const QString &str);
 
+/** Returns a human-readable description of the time elapsed given by
+ * <b>seconds</b>, broken down into days, hours, minutes and seconds. */
+QString string_format_uptime(quint64 seconds);
+
+/** Returns a string representation of <b>date</b> formatted according to
+ * "yyyy-MM-dd HH:mm:ss". */
+QString string_format_datetime(const QDateTime &date);
+
+/** Returns a string representation of <b>bytes</b> with the appropriate
+ * (localized) suffix of either "B/s", "KB/s", "MB/s" or "GB/s". */
+QString string_format_bandwidth(quint64 bytes);
+
 #endif
 

Modified: vidalia/trunk/src/torcontrol/torcontrol.cpp
===================================================================
--- vidalia/trunk/src/torcontrol/torcontrol.cpp	2009-01-31 03:39:39 UTC (rev 3484)
+++ vidalia/trunk/src/torcontrol/torcontrol.cpp	2009-01-31 04:22:30 UTC (rev 3485)
@@ -848,14 +848,23 @@
   return resetConf(QStringList() << key, errmsg);
 }
 
+/** Returns an unparsed router descriptor for the router whose fingerprint
+ * matches <b>id</b>. The returned text can later be parsed by the
+ * RouterDescriptor class. If <b>id</b> is invalid, then an empty
+ * QStringList is returned. */
+QStringList
+TorControl::getRouterDescriptorText(const QString &id, QString *errmsg)
+{
+  return getInfo("desc/id/" + id, errmsg).toStringList();
+}
+
 /** Returns the descriptor for the router whose fingerprint matches
  * <b>id</b>. If <b>id</b> is invalid or the router's descriptor cannot
  * be parsed, then an invalid RouterDescriptor is returned. */
 RouterDescriptor
 TorControl::getRouterDescriptor(const QString &id, QString *errmsg)
 {
-  QStringList descriptor = getInfo("desc/id/" + id, errmsg).toStringList();
-  return RouterDescriptor(descriptor);
+  return RouterDescriptor(getRouterDescriptorText(id, errmsg));
 }
 
 /** Returns the status of the router whose fingerprint matches <b>id</b>. If

Modified: vidalia/trunk/src/torcontrol/torcontrol.h
===================================================================
--- vidalia/trunk/src/torcontrol/torcontrol.h	2009-01-31 03:39:39 UTC (rev 3484)
+++ vidalia/trunk/src/torcontrol/torcontrol.h	2009-01-31 04:22:30 UTC (rev 3485)
@@ -165,6 +165,11 @@
   /** Tells Tor to reset a configuration key back to its default value. */
   bool resetConf(QString key, QString *errmsg = 0);
 
+  /** Returns an unparsed router descriptor for the router whose fingerprint
+   * matches <b>id</b>. The returned text can later be parsed by the
+   * RouterDescriptor class. If <b>id</b> is invalid, then an empty
+   * QStringList is returned. */
+  QStringList getRouterDescriptorText(const QString &id, QString *errmsg = 0);
   /** Returns the descriptor for the router whose fingerprint matches
    * <b>id</b>. If <b>id</b> is invalid or the router's descriptor cannot be
    * parsed, then an invalid RouterDescriptor is returned. */

Modified: vidalia/trunk/src/vidalia/CMakeLists.txt
===================================================================
--- vidalia/trunk/src/vidalia/CMakeLists.txt	2009-01-31 03:39:39 UTC (rev 3484)
+++ vidalia/trunk/src/vidalia/CMakeLists.txt	2009-01-31 04:22:30 UTC (rev 3485)
@@ -16,7 +16,25 @@
   ${CMAKE_CURRENT_SOURCE_DIR}
   ${CMAKE_CURRENT_SOURCE_DIR}/config
   ${CMAKE_CURRENT_SOURCE_DIR}/help/browser
+  ${MARBLE_INCLUDE_DIR}
 )
+if (APPLE)
+##
+## XXX: The Marble build system on OS X currently doesn't actually install the
+## required include files to /usr/local/include or similar. Instead, we have
+## to pluck them directly from the source tree. Ick.
+##
+  include_directories(
+    ${MARBLE_INCLUDE_DIR}/AbstractLayer
+    ${MARBLE_INCLUDE_DIR}/geodata
+    ${MARBLE_INCLUDE_DIR}/geodata/data
+    ${MARBLE_INCLUDE_DIR}/geodata/handlers
+    ${MARBLE_INCLUDE_DIR}/geodata/parser
+    ${MARBLE_INCLUDE_DIR}/geodata/scene
+    ${MARBLE_INCLUDE_DIR}/Projections
+  )
+endif(APPLE)
+
 configure_file(
   ${CMAKE_CURRENT_SOURCE_DIR}/res/vidalia_win.rc.in
   ${CMAKE_CURRENT_SOURCE_DIR}/res/vidalia_win.rc
@@ -147,21 +165,40 @@
   network/geoipresponse.cpp
   network/netviewer.cpp
   network/routerdescriptorview.cpp
+  network/routerinfodialog.cpp
   network/routerlistitem.cpp
   network/routerlistwidget.cpp
   network/streamitem.cpp
-  network/tormapwidget.cpp
-  network/zimageview.cpp
 )
 qt4_wrap_cpp(vidalia_SRCS
   network/circuitlistwidget.h
   network/geoipresolver.h
   network/netviewer.h
   network/routerdescriptorview.h
+  network/routerinfodialog.h
   network/routerlistwidget.h
-  network/tormapwidget.h
-  network/zimageview.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)
@@ -214,6 +251,7 @@
   help/browser/helpbrowser.ui
   log/messagelog.ui
   network/netviewer.ui
+  network/routerinfodialog.ui
 )
 
 if (USE_MINIUPNPC)
@@ -245,6 +283,35 @@
   ${CMAKE_CURRENT_BINARY_DIR}/i18n/vidalia_i18n.qrc
 )
 
+## Specify the map data Marble will need
+set(marble_DATA
+  landcolors.leg
+  seacolors.leg
+  maps/earth/bluemarble/bluemarble.dgml
+  maps/earth/citylights/citylights.dgml
+  maps/earth/srtm/srtm.dgml
+  maps/earth/srtm/srtm.jpg
+  mwdbii/DATELINE.PNT
+  mwdbii/PAUST.PNT
+  mwdbii/PBORDER.PNT
+  mwdbii/PCANPROV.PNT
+  mwdbii/PCOAST.PNT
+  mwdbii/PDIFFBORDER.PNT
+  mwdbii/PGLACIER.PNT
+  mwdbii/PISLAND.PNT
+  mwdbii/PLAKE.PNT
+  mwdbii/PLAKEISLAND.PNT
+  mwdbii/PMEXICO.PNT
+  mwdbii/PUSA48.DIFF.PNT
+  mwdbii/PUSA48.PNT
+  mwdbii/RIVER.PNT
+  placemarks/baseplacemarks.cache
+  placemarks/boundaryplacemarks.cache
+  placemarks/elevplacemarks.cache
+  stars/stars.dat
+  svg/worldmap.svg
+)
+
 ## Set the appropriate executable target for the current platform
 if (APPLE)
   ## Set the output file name (make it uppercase on OS X)
@@ -271,14 +338,80 @@
       ARGS ${CMAKE_CURRENT_SOURCE_DIR}/res/icons/*.icns 
            ${CMAKE_CURRENT_BINARY_DIR}/Vidalia.app/Contents/Resources/
   )
+
+  ## Copy the Marble data into the bundle
+  if (USE_MARBLE)
+    foreach(it ${marble_DATA})
+      get_filename_component(outdir  ${it} PATH)
+      get_filename_component(outfile ${it} NAME)
+      set(outdir Vidalia.app/Contents/Resources/data/${outdir})
+
+      add_custom_command(TARGET ${vidalia_BIN} POST_BUILD
+        COMMAND ${CMAKE_COMMAND}
+          ARGS -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/${outdir}
+        COMMAND ${CMAKE_COMMAND}
+          ARGS -E copy_if_different 
+                ${MARBLE_DATA_DIR}/${it} 
+                ${CMAKE_CURRENT_BINARY_DIR}/${outdir}/${outfile}
+      )
+    endforeach(it)
+
+    ## Create the Marble plugins directory and copy in any wanted plugins
+    if (MARBLE_PLUGINS)
+      add_custom_command(TARGET ${vidalia_BIN} POST_BUILD
+        COMMAND ${CMAKE_COMMAND}
+          ARGS -E make_directory 
+           ${CMAKE_CURRENT_BINARY_DIR}/Vidalia.app/Contents/Resources/plugins
+      )
+      foreach(it ${MARBLE_PLUGINS})
+        add_custom_command(TARGET ${vidalia_BIN} POST_BUILD
+          COMMAND ${CMAKE_COMMAND}
+            ARGS -E copy_if_different ${it}
+             ${CMAKE_CURRENT_BINARY_DIR}/Vidalia.app/Contents/Resources/plugins/
+        )
+      endforeach(it)
+    endif(MARBLE_PLUGINS)
+  endif(USE_MARBLE)
 else(APPLE)
   ## Set the output file name
   set(vidalia_BIN vidalia)
-  
+
   if (WIN32)
     ## Create a Windows binary
     win32_wrap_rc(vidalia_SRCS res/vidalia_win.rc)
     add_executable(${vidalia_BIN} WIN32 ${vidalia_SRCS})
+
+    ## Copy the Marble data into the data/ directory under the binary
+    if (USE_MARBLE)
+      foreach(it ${marble_DATA})
+        get_filename_component(outdir  ${it} PATH)
+        get_filename_component(outfile ${it} NAME)
+
+        add_custom_command(TARGET ${vidalia_BIN} POST_BUILD
+          COMMAND ${CMAKE_COMMAND}
+            ARGS -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/${outdir}
+          COMMAND ${CMAKE_COMMAND}
+            ARGS -E copy_if_different ${MARBLE_DATA_DIR}/${it} 
+                                      ${CMAKE_CURRENT_BINARY_DIR}/data/${it}
+        )
+      endforeach(it)
+
+      ## Create the Marble plugins directory and copy in any wanted plugins
+      if (MARBLE_PLUGINS)
+        add_custom_command(TARGET ${vidalia_BIN} POST_BUILD
+          COMMAND ${CMAKE_COMMAND}
+            ARGS -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/plugins
+        )
+        foreach(it ${MARBLE_PLUGINS})
+          add_custom_command(TARGET ${vidalia_BIN} POST_BUILD
+            COMMAND ${CMAKE_COMMAND}
+              ARGS -E copy_if_different ${it} 
+                    ${CMAKE_CURRENT_BINARY_DIR}/plugins/
+          )
+        endforeach(it)
+      endif(MARBLE_PLUGINS)
+    endif(USE_MARBLE)
+
   else (WIN32)
     ## Non-Windows, non-Mac
     add_executable(${vidalia_BIN} ${vidalia_SRCS})
@@ -290,12 +423,16 @@
 ## Link to the Qt libraries and other libraries built as a part of Vidalia
 target_link_libraries(${vidalia_BIN}
   ${QT_LIBRARIES}
+  ${MARBLE_LIBRARIES}
   common
   torcontrol
 )
 if (USE_MINIUPNPC)
   target_link_libraries(${vidalia_BIN} miniupnpc)
 endif(USE_MINIUPNPC)
+if (USE_MARBLE)
+  target_link_libraries(${vidalia_BIN} ${MARBLE_LIBRARIES})
+endif(USE_MARBLE)
 
 if (WIN32)
   target_link_libraries(${vidalia_BIN}
@@ -320,4 +457,3 @@
           DESTINATION share/icons/hicolor/22x22/apps)
 endif(NOT WIN32 AND NOT APPLE)
 
-

Modified: vidalia/trunk/src/vidalia/network/circuitlistwidget.cpp
===================================================================
--- vidalia/trunk/src/vidalia/network/circuitlistwidget.cpp	2009-01-31 03:39:39 UTC (rev 3484)
+++ vidalia/trunk/src/vidalia/network/circuitlistwidget.cpp	2009-01-31 04:22:30 UTC (rev 3485)
@@ -18,6 +18,7 @@
 #include <QTimer>
 #include <vidalia.h>
 
+#include "config.h"
 #include "circuitlistwidget.h"
 
 #define IMG_CLOSE   ":/images/22x22/edit-delete.png"
@@ -87,9 +88,11 @@
                                     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

Modified: vidalia/trunk/src/vidalia/network/netviewer.cpp
===================================================================
--- vidalia/trunk/src/vidalia/network/netviewer.cpp	2009-01-31 03:39:39 UTC (rev 3484)
+++ vidalia/trunk/src/vidalia/network/netviewer.cpp	2009-01-31 04:22:30 UTC (rev 3485)
@@ -17,8 +17,10 @@
 #include <QMessageBox>
 #include <QHeaderView>
 #include <vidalia.h>
+#include <vmessagebox.h>
 
 #include "netviewer.h"
+#include "routerinfodialog.h"
 
 #define IMG_MOVE    ":/images/22x22/move-map.png"
 #define IMG_ZOOMIN  ":/images/22x22/zoom-in.png"
@@ -65,12 +67,19 @@
     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)));
+#else
+  _map = new TorMapImageView();
+#endif
   ui.gridLayout->addWidget(_map);
 
-  /* Connect zoom buttons to ZImageView zoom slots */
-  connect(ui.actionZoomIn, SIGNAL(triggered()), _map, SLOT(zoomIn()));
-  connect(ui.actionZoomOut, SIGNAL(triggered()), _map, SLOT(zoomOut()));
+
+  /* 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
@@ -470,7 +479,7 @@
         /* Save the location information in the descriptor */
         router->setLocation(geoip);
         /* Plot the router on the map */
-        _map->addRouter(router->id(), geoip.latitude(), geoip.longitude());
+        _map->addRouter(router->descriptor(), geoip);
       }
     }
   }
@@ -484,3 +493,64 @@
   _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(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
+}
+

Modified: vidalia/trunk/src/vidalia/network/netviewer.h
===================================================================
--- vidalia/trunk/src/vidalia/network/netviewer.h	2009-01-31 03:39:39 UTC (rev 3484)
+++ vidalia/trunk/src/vidalia/network/netviewer.h	2009-01-31 04:22:30 UTC (rev 3485)
@@ -26,9 +26,13 @@
 #include <vidaliawindow.h>
 
 #include "geoipresolver.h"
-#include "tormapwidget.h"
 #include "ui_netviewer.h"
 
+#if defined(USE_MARBLE)
+#include "tormapwidget.h"
+#else
+#include "tormapimageview.h"
+#endif
 
 class NetViewer : public VidaliaWindow
 {
@@ -74,7 +78,15 @@
   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();
+
 private:
   /** Adds an IP address to the resolve queue and updates the queue timers. */
   void addToResolveQueue(const QHostAddress &ip, const QString &id);
@@ -95,8 +107,6 @@
   TorControl* _torControl;
   /** Timer that fires once an hour to update the router list. */
   QTimer _refreshTimer;
-  /** TorMapWidget that displays the map. */
-  TorMapWidget* _map;
   /** GeoIpResolver used to geolocate routers by IP address. */
   GeoIpResolver _geoip;
   /** Queue for IPs pending resolution to geographic information. */
@@ -112,7 +122,14 @@
    * 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;
 };

Modified: vidalia/trunk/src/vidalia/network/routerdescriptorview.cpp
===================================================================
--- vidalia/trunk/src/vidalia/network/routerdescriptorview.cpp	2009-01-31 03:39:39 UTC (rev 3484)
+++ vidalia/trunk/src/vidalia/network/routerdescriptorview.cpp	2009-01-31 04:22:30 UTC (rev 3485)
@@ -22,9 +22,10 @@
 #include <QTextDocumentFragment>
 #include <html.h>
 #include <vidalia.h>
+#include <stringutil.h>
+
 #include "routerdescriptorview.h"
 
-#define DATE_FORMAT   "yyyy-MM-dd HH:mm:ss"
 #define IMG_COPY      ":/images/22x22/edit-copy.png"
 
 
@@ -66,13 +67,6 @@
   vApp->clipboard()->setText(selectedText);
 }
 
-/** Format the date the descriptor was published. */
-QString
-RouterDescriptorView::formatPublished(QDateTime date)
-{
-  return date.toString(DATE_FORMAT) + " GMT";
-}
-
 /** Adjusts the displayed uptime to include time since the router's descriptor
  * was last published. */
 quint64
@@ -86,38 +80,6 @@
   return (uptime + (now.toTime_t() - published.toTime_t()));
 }
 
-/** Format the uptime for this router in a readable format. */
-QString
-RouterDescriptorView::formatUptime(quint64 seconds)
-{
-  QString uptime;
-  int secs  = (seconds % 60);
-  int mins  = (seconds / 60 % 60);
-  int hours = (seconds / 3600 % 24);
-  int days  = (seconds / 86400);
-
-  if (days) {
-    uptime += tr("%1 days ").arg(days);
-  }
-  if (hours) {
-    uptime += tr("%1 hours ").arg(hours);
-  }
-  if (mins) {
-    uptime += tr("%1 mins ").arg(mins);
-  }
-  if (secs) {
-    uptime += tr("%1 secs").arg(secs);
-  }
-  return uptime;
-}
-
-/** Format the bandwidth into KB/s. */
-QString
-RouterDescriptorView::formatBandwidth(quint64 bandwidth)
-{
-  return QString::number(bandwidth/1024);
-}
-
 /** Displays all router descriptors in the given list. */
 void
 RouterDescriptorView::display(QList<RouterDescriptor> rdlist)
@@ -146,18 +108,18 @@
     /* If the router is online, then show the uptime and bandwidth stats. */
     if (!rd.offline()) {
       html.append(trow(tcol(b(tr("Bandwidth:")))  + 
-                       tcol(formatBandwidth(rd.observedBandwidth()) + " KB/s")));
+                       tcol(string_format_bandwidth(rd.observedBandwidth()))));
       html.append(trow(tcol(b(tr("Uptime:")))   + 
-                       tcol(formatUptime(
+                       tcol(string_format_uptime(
                               adjustUptime(rd.uptime(), rd.published())))));
     }
-    
+
     /* Date the router was published */
     html.append(trow(tcol(b(tr("Last Updated:")))  +
-                     tcol(formatPublished(rd.published()))));
-    
+                     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()) {

Modified: vidalia/trunk/src/vidalia/network/routerdescriptorview.h
===================================================================
--- vidalia/trunk/src/vidalia/network/routerdescriptorview.h	2009-01-31 03:39:39 UTC (rev 3484)
+++ vidalia/trunk/src/vidalia/network/routerdescriptorview.h	2009-01-31 04:22:30 UTC (rev 3485)
@@ -50,12 +50,6 @@
   /** Adjusts the displayed uptime to include time since the
    * router's descriptor was last published. */
   quint64 adjustUptime(quint64 uptime, QDateTime published);
-  /** Formats the descriptor's published date. */
-  QString formatPublished(QDateTime date);
-  /** Formats the router's uptime. */
-  QString formatUptime(quint64 seconds);
-  /** Formats the observed bandwidth into KB/s. */
-  QString formatBandwidth(quint64 bandwidth);
 };
 
 #endif

Copied: vidalia/trunk/src/vidalia/network/routerinfodialog.cpp (from rev 3484, vidalia/branches/marble/src/vidalia/network/routerinfodialog.cpp)
===================================================================
--- vidalia/trunk/src/vidalia/network/routerinfodialog.cpp	                        (rev 0)
+++ vidalia/trunk/src/vidalia/network/routerinfodialog.cpp	2009-01-31 04:22:30 UTC (rev 3485)
@@ -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 <stringutil.h>
+
+#include "routerinfodialog.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/trunk/src/vidalia/network/routerinfodialog.h (from rev 3484, vidalia/branches/marble/src/vidalia/network/routerinfodialog.h)
===================================================================
--- vidalia/trunk/src/vidalia/network/routerinfodialog.h	                        (rev 0)
+++ vidalia/trunk/src/vidalia/network/routerinfodialog.h	2009-01-31 04:22:30 UTC (rev 3485)
@@ -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 routerinfodialog.h
+** \version $Id$
+** \brief Displays detailed information about a particular router
+*/
+
+
+#ifndef _ROUTERINFODIALOG_H
+#define _ROUTERINFODIALOG_H
+
+#include <QDialog>
+#include <routerdescriptor.h>
+#include <routerstatus.h>
+
+#include "ui_routerinfodialog.h"
+
+
+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/trunk/src/vidalia/network/routerinfodialog.ui (from rev 3484, vidalia/branches/marble/src/vidalia/network/routerinfodialog.ui)
===================================================================
--- vidalia/trunk/src/vidalia/network/routerinfodialog.ui	                        (rev 0)
+++ vidalia/trunk/src/vidalia/network/routerinfodialog.ui	2009-01-31 04:22:30 UTC (rev 3485)
@@ -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>

Modified: vidalia/trunk/src/vidalia/network/routerlistitem.h
===================================================================
--- vidalia/trunk/src/vidalia/network/routerlistitem.h	2009-01-31 03:39:39 UTC (rev 3484)
+++ vidalia/trunk/src/vidalia/network/routerlistitem.h	2009-01-31 04:22:30 UTC (rev 3485)
@@ -48,6 +48,8 @@
   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;

Copied: vidalia/trunk/src/vidalia/network/tormapimageview.cpp (from rev 3484, vidalia/branches/marble/src/vidalia/network/tormapimageview.cpp)
===================================================================
--- vidalia/trunk/src/vidalia/network/tormapimageview.cpp	                        (rev 0)
+++ vidalia/trunk/src/vidalia/network/tormapimageview.cpp	2009-01-31 04:22:30 UTC (rev 3485)
@@ -0,0 +1,326 @@
+/*
+**  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 <QStringList>
+#include <cmath>
+#include "tormapimageview.h"
+
+#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/trunk/src/vidalia/network/tormapimageview.h (from rev 3484, vidalia/branches/marble/src/vidalia/network/tormapimageview.h)
===================================================================
--- vidalia/trunk/src/vidalia/network/tormapimageview.h	                        (rev 0)
+++ vidalia/trunk/src/vidalia/network/tormapimageview.h	2009-01-31 04:22:30 UTC (rev 3485)
@@ -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.
+*/
+
+/*
+** \file tormapimageview.h
+** \version $Id$
+** \brief Displays Tor servers and circuits on a map of the world
+*/
+
+#ifndef _TORMAPIMAGEVIEW_H
+#define _TORMAPIMAGEVIEW_H
+
+#include <QHash>
+#include <QPair>
+#include <QPainter>
+#include <QPainterPath>
+#include <circuit.h>
+#include <routerdescriptor.h>
+#include <network/geoip.h>
+
+#include "zimageview.h"
+
+
+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
+

Modified: vidalia/trunk/src/vidalia/network/tormapwidget.cpp
===================================================================
--- vidalia/trunk/src/vidalia/network/tormapwidget.cpp	2009-01-31 03:39:39 UTC (rev 3484)
+++ vidalia/trunk/src/vidalia/network/tormapwidget.cpp	2009-01-31 04:22:30 UTC (rev 3485)
@@ -15,59 +15,38 @@
 */
 
 #include <QStringList>
-#include <cmath>
+#include <vidalia.h>
+
+#include "tormapwidgetinputhandler.h"
+#include "tormapwidgetpopupmenu.h"
 #include "tormapwidget.h"
 
-#define IMG_WORLD_MAP   ":/images/map/world-map.png"
+using namespace Marble;
 
 /** 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)
+#define CIRCUIT_NORMAL_PEN      QPen(Qt::green,  2.0)
+#define CIRCUIT_SELECTED_PEN    QPen(Qt::yellow, 3.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 */
 TorMapWidget::TorMapWidget(QWidget *parent)
-: ZImageView(parent)
+  : MarbleWidget(parent)
 {
-  QImage map(IMG_WORLD_MAP);
-  setImage(map);
+  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)));
+
+  setInputHandler(handler);
 }
 
 /** Destructor */
@@ -78,75 +57,105 @@
 
 /** Adds a router to the map. */
 void
-TorMapWidget::addRouter(const QString &id, float latitude, float longitude)
+TorMapWidget::addRouter(const RouterDescriptor &desc, const GeoIp &geoip)
 {
-  QPointF routerCoord = toMapSpace(latitude, 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));
+  QString kml;
+  qreal lon = geoip.longitude();
+  qreal lat = geoip.latitude();
+
+  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(desc.observedBandwidth()));
+  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)
 {
-  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 */
+  /* 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)) {
-    /* 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;
+    /* 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 {
-    /* This is a new path, so just add it to our list */
-    _circuits.insert(circid, new QPair<QPainterPath*,bool>(circPainterPath,false));
+    /* 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));
+      }      
+    }
+    _circuits.insert(circid, geoPath);
   }
+
+  repaint();
 }
 
 /** Removes a circuit from the map. */
 void
 TorMapWidget::removeCircuit(const CircuitId &circid)
 {
-  QPair<QPainterPath*,bool> *circ = _circuits.take(circid);
-  QPainterPath *circpath = circ->first;
-  if (circpath) {
-    delete circpath;
+  CircuitGeoPath *path = _circuits.take(circid);
+  if (path) {
+    GeoDataLineString coords = path->first;
+    qDeleteAll(coords.begin(), coords.end());
+    delete path;
   }
-  delete circ;
+
+  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> 
@@ -155,9 +164,10 @@
 TorMapWidget::selectCircuit(const CircuitId &circid)
 {
   if (_circuits.contains(circid)) {
-    QPair<QPainterPath*, bool> *circuitPair = _circuits.value(circid);
-    circuitPair->second = true;
+    CircuitGeoPath *path = _circuits.value(circid);
+    path->second = true;
   }
+
   repaint();
 }
 
@@ -165,122 +175,59 @@
 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 (CircuitId circid, _circuits.keys()) {
-    QPair<QPainterPath*,bool> *circuitPair = _circuits.value(circid);
-    circuitPair->second = false;
+  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()
 {
-  /* Clear out all the router points and free their memory */
-  foreach (QString router, _routers.keys()) {
-    delete _routers.take(router);
+  foreach (QString id, _routers.keys()) {
+    removePlaceMarkKey(id);
   }
-  /* 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;
+    CircuitGeoPath *path = _circuits.take(circid);
+    GeoDataLineString coords = path->first;
+    qDeleteAll(coords.begin(), coords.end());
+    delete path;
   }
+
+  repaint();
 }
-  
-/** Draws the routers and paths onto the map image. */
+ 
+/** Zooms the map to fit entirely within the constraints of the current
+ * viewport size. */
 void
-TorMapWidget::paintImage(QPainter *painter)
+TorMapWidget::zoomToFit()
 {
-  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));
-  }
-}
+  int width  = size().width();
+  int height = size().height();
 
-/** Converts world space coordinates into map space coordinates */
-QPointF
-TorMapWidget::toMapSpace(float latitude, float longitude)
-{
-  float width  = MAP_WIDTH;
-  float height = MAP_HEIGHT;
-  float deg = width / 360.0;
-  longitude += MAP_ORIGIN;
+  setRadius(qMin(width, height) / 2);
 
-  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);
+  /* XXX: Calling setRadius() seems to cause Marble to no longer draw the
+   *      atmosphere. So, re-enable it. */
+  setShowAtmosphere(true);
 }
-  
-/** Linearly interpolates using the values in the Robinson projection table */
-float
-TorMapWidget::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
-TorMapWidget::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
-TorMapWidget::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
 TorMapWidget::zoomToCircuit(const CircuitId &circid)
 {
+#if 0
   if (_circuits.contains(circid)) {
     QPair<QPainterPath*,bool> *pair = _circuits.value(circid);
     QRectF rect = ((QPainterPath *)pair->first)->boundingRect();
@@ -291,35 +238,30 @@
       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)
 {
-  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); 
+    qreal lon, lat;
+    GeoDataCoordinates coords = _routers.value(id);
+    coords.geoCoordinates(lon, lat, GeoDataPoint::Degree);
+
+    zoomView(maximumZoom());
+    centerOn(lon, lat, true);
   }
 }
 
-/** Computes a bounding box around all currently displayed circuit paths on
- * the map. */
-QRectF
-TorMapWidget::circuitBoundingBox()
+/** Paints the current circuits and streams on the image. */
+void
+TorMapWidget::customPaint(GeoPainter *painter)
 {
-  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());
+  foreach (CircuitGeoPath *path, _circuits.values()) {
+    painter->setPen(path->second ? CIRCUIT_SELECTED_PEN : CIRCUIT_NORMAL_PEN);
+    painter->drawPolyline(path->first);
   }
-  return rect;
 }
 

Modified: vidalia/trunk/src/vidalia/network/tormapwidget.h
===================================================================
--- vidalia/trunk/src/vidalia/network/tormapwidget.h	2009-01-31 03:39:39 UTC (rev 3484)
+++ vidalia/trunk/src/vidalia/network/tormapwidget.h	2009-01-31 04:22:30 UTC (rev 3485)
@@ -19,15 +19,21 @@
 
 #include <QHash>
 #include <QPair>
-#include <QPainter>
 #include <QPainterPath>
+#include <routerdescriptor.h>
 #include <circuit.h>
 #include <stream.h>
+#include <network/geoip.h>
 
-#include "zimageview.h"
+#include <MarbleWidget.h>
+#include <GeoPainter.h>
+#include <GeoDataCoordinates.h>
+#include <GeoDataLineString.h>
 
+typedef QPair<Marble::GeoDataLineString, bool> CircuitGeoPath;
 
-class TorMapWidget : public ZImageView
+
+class TorMapWidget : public Marble::MarbleWidget
 {
   Q_OBJECT
 
@@ -38,15 +44,13 @@
   ~TorMapWidget();
 
   /** Plots the given router on the map using the given coordinates. */
-  void addRouter(const QString &id, float latitude, float longitude);
+  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. */
@@ -55,30 +59,28 @@
   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. */
+  /** 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 paintImage(QPainter *painter);
+  virtual void customPaint(Marble::GeoPainter *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 placemark IDs for Tor routers. */
+  QHash<QString, Marble::GeoDataCoordinates> _routers;
   /** Stores circuit information */
-  QHash<CircuitId, QPair<QPainterPath *,bool>* > _circuits;
+  QHash<CircuitId, CircuitGeoPath*> _circuits;
 };
 
 #endif

Copied: vidalia/trunk/src/vidalia/network/tormapwidgetinputhandler.cpp (from rev 3484, vidalia/branches/marble/src/vidalia/network/tormapwidgetinputhandler.cpp)
===================================================================
--- vidalia/trunk/src/vidalia/network/tormapwidgetinputhandler.cpp	                        (rev 0)
+++ vidalia/trunk/src/vidalia/network/tormapwidgetinputhandler.cpp	2009-01-31 04:22:30 UTC (rev 3485)
@@ -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 <QTimer>
+#include <QMouseEvent>
+#include <QWheelEvent>
+#include <QPersistentModelIndex>
+
+#include <MarbleWidget.h>
+#include <MarbleMap.h>
+#include <MarbleModel.h>
+#include <ViewParams.h>
+#include <ViewportParams.h>
+
+#include "tormapwidgetinputhandler.h"
+
+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/trunk/src/vidalia/network/tormapwidgetinputhandler.h (from rev 3484, vidalia/branches/marble/src/vidalia/network/tormapwidgetinputhandler.h)
===================================================================
--- vidalia/trunk/src/vidalia/network/tormapwidgetinputhandler.h	                        (rev 0)
+++ vidalia/trunk/src/vidalia/network/tormapwidgetinputhandler.h	2009-01-31 04:22:30 UTC (rev 3485)
@@ -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 <QEvent>
+#include <QObject>
+#include <QPoint>
+
+#include <MarbleWidgetInputHandler.h>
+
+
+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/trunk/src/vidalia/network/tormapwidgetpopupmenu.cpp (from rev 3484, vidalia/branches/marble/src/vidalia/network/tormapwidgetpopupmenu.cpp)
===================================================================
--- vidalia/trunk/src/vidalia/network/tormapwidgetpopupmenu.cpp	                        (rev 0)
+++ vidalia/trunk/src/vidalia/network/tormapwidgetpopupmenu.cpp	2009-01-31 04:22:30 UTC (rev 3485)
@@ -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 <QChar>
+#include <QVector>
+#include <QPersistentModelIndex>
+#include <vidalia.h>
+
+#include <MarbleModel.h>
+#include <MarblePlacemarkModel.h>
+
+#include "tormapwidgetpopupmenu.h"
+
+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<QPersistentModelIndex>::const_iterator it;
+  QVector<QPersistentModelIndex> 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/trunk/src/vidalia/network/tormapwidgetpopupmenu.h (from rev 3484, vidalia/branches/marble/src/vidalia/network/tormapwidgetpopupmenu.h)
===================================================================
--- vidalia/trunk/src/vidalia/network/tormapwidgetpopupmenu.h	                        (rev 0)
+++ vidalia/trunk/src/vidalia/network/tormapwidgetpopupmenu.h	2009-01-31 04:22:30 UTC (rev 3485)
@@ -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 <QObject>
+#include <QPoint>
+#include <QString>
+#include <QMenu>
+
+#include "tormapwidget.h"
+
+
+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/trunk/src/vidalia/res/icons/placemark-relay.png (from rev 3484, vidalia/branches/marble/src/vidalia/res/icons/placemark-relay.png)
===================================================================
(Binary files differ)

Modified: vidalia/trunk/src/vidalia/res/vidalia.qrc
===================================================================
--- vidalia/trunk/src/vidalia/res/vidalia.qrc	2009-01-31 03:39:39 UTC (rev 3484)
+++ vidalia/trunk/src/vidalia/res/vidalia.qrc	2009-01-31 04:22:30 UTC (rev 3485)
@@ -106,6 +106,7 @@
         <file>icons/node-bw-low.png</file>
         <file>icons/node-bw-med.png</file>
         <file>icons/node-bw-high.png</file>
+        <file>icons/placemark-relay.png</file>
     </qresource>
     <qresource prefix="/images">
         <file>flags/ae.png</file>

Modified: vidalia/trunk/src/vidalia/vidalia.cpp
===================================================================
--- vidalia/trunk/src/vidalia/vidalia.cpp	2009-01-31 03:39:39 UTC (rev 3484)
+++ vidalia/trunk/src/vidalia/vidalia.cpp	2009-01-31 04:22:30 UTC (rev 3485)
@@ -26,13 +26,16 @@
 #include <stringutil.h>
 #include <html.h>
 #include <stdlib.h>
+#include "config.h"
+#include "vidalia.h"
 
 #ifdef Q_OS_MACX
 #include <Carbon/Carbon.h>
 #endif
+#ifdef USE_MARBLE
+#include <MarbleDirs.h>
+#endif
 
-#include "vidalia.h"
-
 /* Available command-line arguments. */
 #define ARG_LANGUAGE   "lang"     /**< Argument specifying language.    */
 #define ARG_GUISTYLE   "style"    /**< Argument specfying GUI style.    */
@@ -119,6 +122,11 @@
 
   /* Creates a TorControl object, used to talk to Tor. */
   _torControl = new TorControl();
+
+#ifdef USE_MARBLE
+  /* Tell Marble where to stash its generated data */
+  Marble::MarbleDirs::setMarbleDataPath(dataDirectory());
+#endif
 }
 
 /** Destructor */