Vidalia 0.2.12
|
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 ** \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