Vidalia  0.2.15
RouterListWidget.cpp
Go to the documentation of this file.
00001 /*
00002 **  This file is part of Vidalia, and is subject to the license terms in the
00003 **  LICENSE file, found in the top level directory of this distribution. If you
00004 **  did not receive the LICENSE file with this file, you may obtain it from the
00005 **  Vidalia source package distributed by the Vidalia Project at
00006 **  http://www.torproject.org/projects/vidalia.html. No part of Vidalia, 
00007 **  including this file, may be copied, modified, propagated, or distributed 
00008 **  except according to the terms described in the LICENSE file.
00009 */
00010 
00011 /*
00012 ** \file RouterListWidget.cpp
00013 ** \brief Displays a list of Tor servers and their status
00014 */
00015 
00016 #include "RouterListWidget.h"
00017 #include "RouterListItem.h"
00018 #include "Vidalia.h"
00019 
00020 #include <QHeaderView>
00021 #include <QClipboard>
00022 
00023 #define IMG_ZOOM   ":/images/22x22/page-zoom.png"
00024 #define IMG_COPY   ":/images/22x22/edit-copy.png"
00025 
00026 
00027 RouterListWidget::RouterListWidget(QWidget *parent)
00028   : QTreeWidget(parent)
00029 {
00030   /* Create and initialize columns */
00031   setHeaderLabels(QStringList() << QString("")
00032                                 << QString("")
00033                                 << tr("Relay"));
00034 
00035   /* Sort by descending server bandwidth */
00036   sortItems(StatusColumn, Qt::DescendingOrder);
00037 
00038   /* Find out when the selected item has changed. */
00039   connect(this, SIGNAL(itemSelectionChanged()), 
00040           this, SLOT(onSelectionChanged()));
00041 }
00042 
00043 /** Called when the user changes the UI translation. */
00044 void
00045 RouterListWidget::retranslateUi()
00046 {
00047   setHeaderLabels(QStringList() << QString("")
00048                                 << QString("")
00049                                 << tr("Relay"));
00050 }
00051 
00052 /** Called when the user requests a context menu for a router in the list. A
00053  * context menu will be displayed providing a list of actions, including
00054  * zooming in on the server. */
00055 void
00056 RouterListWidget::contextMenuEvent(QContextMenuEvent *event)
00057 {
00058   QAction *action;
00059   QMenu *menu, *copyMenu;
00060   QList<QTreeWidgetItem *> selected;
00061 
00062   selected = selectedItems();
00063   if (! selected.size())
00064     return;
00065 
00066   menu = new QMenu();
00067   copyMenu = menu->addMenu(QIcon(IMG_COPY), tr("Copy"));
00068   action = copyMenu->addAction(tr("Nickname"));
00069   connect(action, SIGNAL(triggered()), this, SLOT(copySelectedNicknames()));
00070 
00071   action = copyMenu->addAction(tr("Fingerprint"));
00072   connect(action, SIGNAL(triggered()), this, SLOT(copySelectedFingerprints()));
00073 
00074   action = menu->addAction(QIcon(IMG_ZOOM), tr("Zoom to Relay"));
00075   if (selected.size() > 1)
00076     action->setEnabled(false);
00077   else
00078     connect(action, SIGNAL(triggered()), this, SLOT(zoomToSelectedRelay()));
00079 
00080   menu->exec(event->globalPos());
00081   delete menu;
00082 }
00083 
00084 /** Copies the nicknames for all currently selected relays to the clipboard.
00085  * Nicknames are formatted as a comma-delimited list, suitable for doing
00086  * dumb things with your torrc. */
00087 void
00088 RouterListWidget::copySelectedNicknames()
00089 {
00090   QString text;
00091 
00092   foreach (QTreeWidgetItem *item, selectedItems()) {
00093     RouterListItem *relay = dynamic_cast<RouterListItem *>(item);
00094     if (relay)
00095       text.append(relay->name() + ",");
00096   }
00097   if (text.length()) {
00098     text.remove(text.length()-1, 1);
00099     vApp->clipboard()->setText(text);
00100   }
00101 }
00102 
00103 /** Copies the fingerprints for all currently selected relays to the
00104  * clipboard. Fingerprints are formatted as a comma-delimited list, suitable
00105  * for doing dumb things with your torrc. */
00106 void
00107 RouterListWidget::copySelectedFingerprints()
00108 {
00109   QString text;
00110 
00111   foreach (QTreeWidgetItem *item, selectedItems()) {
00112     RouterListItem *relay = dynamic_cast<RouterListItem *>(item);
00113     if (relay)
00114       text.append("$" + relay->id() + ",");
00115   }
00116   if (text.length()) {
00117     text.remove(text.length()-1, 1);
00118     vApp->clipboard()->setText(text);
00119   }
00120 }
00121 
00122 /** Emits a zoomToRouter() signal containing the fingerprint of the
00123  * currently selected relay. */
00124 void
00125 RouterListWidget::zoomToSelectedRelay()
00126 {
00127   QList<QTreeWidgetItem *> selected = selectedItems();
00128   if (selected.size() != 1)
00129     return;
00130 
00131   RouterListItem *relay = dynamic_cast<RouterListItem *>(selected[0]);
00132   if (relay)
00133     emit zoomToRouter(relay->id());
00134 }
00135 
00136 /** Deselects all currently selected routers. */
00137 void
00138 RouterListWidget::deselectAll()
00139 {
00140   QList<QTreeWidgetItem *> selected = selectedItems();
00141   foreach (QTreeWidgetItem *item, selected) {
00142     setItemSelected(item, false);
00143   }
00144 }
00145 
00146 /** Clear the list of router items. */
00147 void
00148 RouterListWidget::clearRouters()
00149 {
00150   _idmap.clear();
00151   QTreeWidget::clear();
00152   setStatusTip(tr("%1 relays online").arg(0));
00153 }
00154 
00155 /** Called when the user selects a router from the list. This will search the
00156  * list for a router whose names starts with the key pressed. */
00157 void
00158 RouterListWidget::keyPressEvent(QKeyEvent *event)
00159 {
00160   int index;
00161   
00162   QString key = event->text();
00163   if (!key.isEmpty() && key.at(0).isLetterOrNumber()) {
00164     /* A text key was pressed, so search for routers that begin with that key. */
00165     QList<QTreeWidgetItem *> list = findItems(QString("^[%1%2].*$")
00166                                                   .arg(key.toUpper())
00167                                                   .arg(key.toLower()),
00168                                                Qt::MatchRegExp|Qt::MatchWrap,
00169                                                NameColumn);
00170     if (list.size() > 0) {
00171       QList<QTreeWidgetItem *> s = selectedItems();
00172       
00173       /* A match was found, so deselect any previously selected routers,
00174        * select the new match, and make sure it's visible. If there was
00175        * already a router selected that started with the search key, go to the
00176        * next match in the list. */
00177       deselectAll();
00178       index = (!s.size() ? 0 : (list.indexOf(s.at(0)) + 1) % list.size());
00179 
00180       /* Select the item and scroll to it */
00181       setItemSelected(list.at(index), true);
00182       scrollToItem(list.at(index));
00183     }
00184     event->accept();
00185   } else {
00186     /* It was something we don't understand, so hand it to the parent class */
00187     QTreeWidget::keyPressEvent(event);
00188   }
00189 }
00190 
00191 /** Finds the list item whose key ID matches <b>id</b>. Returns 0 if not 
00192  * found. */
00193 RouterListItem*
00194 RouterListWidget::findRouterById(QString id)
00195 {
00196   if (_idmap.contains(id)) {
00197     return _idmap.value(id);
00198   }
00199   return 0;
00200 }
00201 
00202 /** Adds a router descriptor to the list. */
00203 RouterListItem*
00204 RouterListWidget::addRouter(const RouterDescriptor &rd)
00205 {
00206   QString id = rd.id();
00207   if (id.isEmpty())
00208     return 0;
00209 
00210   RouterListItem *item = findRouterById(id);
00211   if (item) {
00212     item->update(rd);
00213   } else {
00214     item = new RouterListItem(this, rd);
00215     addTopLevelItem(item);
00216     _idmap.insert(id, item);
00217   }
00218 
00219   /* Set our status tip to the number of servers in the list */
00220   setStatusTip(tr("%1 relays online").arg(topLevelItemCount()));
00221 
00222   return item;
00223 }
00224 
00225 /** Called when the selected items have changed. This emits the 
00226  * routerSelected() signal with the descriptor for the selected router.
00227  */
00228 void
00229 RouterListWidget::onSelectionChanged()
00230 {
00231   QList<RouterDescriptor> descriptors;
00232 
00233   foreach (QTreeWidgetItem *item, selectedItems()) {
00234     RouterListItem *relay = dynamic_cast<RouterListItem *>(item);
00235     if (relay)
00236       descriptors << relay->descriptor();
00237   }
00238   if (descriptors.count() > 0)
00239     emit routerSelected(descriptors);
00240 }
00241