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