Vidalia 0.3.1
RouterListWidget.cpp
Go to the documentation of this file.
1/*
2** This file is part of Vidalia, and is subject to the license terms in the
3** LICENSE file, found in the top level directory of this distribution. If you
4** did not receive the LICENSE file with this file, you may obtain it from the
5** Vidalia source package distributed by the Vidalia Project at
6** http://www.torproject.org/projects/vidalia.html. No part of Vidalia,
7** including this file, may be copied, modified, propagated, or distributed
8** except according to the terms described in the LICENSE file.
9*/
10
11/*
12** \file RouterListWidget.cpp
13** \brief Displays a list of Tor servers and their status
14*/
15
16#include "RouterListWidget.h"
17#include "RouterListItem.h"
18#include "Vidalia.h"
19
20#include <QHeaderView>
21#include <QClipboard>
22
23#define IMG_ZOOM ":/images/22x22/page-zoom.png"
24#define IMG_COPY ":/images/22x22/edit-copy.png"
25
26
28 : QTreeWidget(parent)
29{
30 /* Create and initialize columns */
31 setHeaderLabels(QStringList() << QString("")
32 << QString("")
33 << tr("Relay"));
34
35 /* Sort by descending server bandwidth */
36 sortItems(StatusColumn, Qt::DescendingOrder);
37
38 /* Find out when the selected item has changed. */
39 connect(this, SIGNAL(itemSelectionChanged()),
40 this, SLOT(onSelectionChanged()));
41}
42
43/** Called when the user changes the UI translation. */
44void
46{
47 setHeaderLabels(QStringList() << QString("")
48 << QString("")
49 << tr("Relay"));
50}
51
52/** Called when the user requests a context menu for a router in the list. A
53 * context menu will be displayed providing a list of actions, including
54 * zooming in on the server. */
55void
56RouterListWidget::contextMenuEvent(QContextMenuEvent *event)
57{
58 QAction *action;
59 QMenu *menu, *copyMenu;
60 QList<QTreeWidgetItem *> selected;
61
62 selected = selectedItems();
63 if (! selected.size())
64 return;
65
66 menu = new QMenu();
67 copyMenu = menu->addMenu(QIcon(IMG_COPY), tr("Copy"));
68 action = copyMenu->addAction(tr("Nickname"));
69 connect(action, SIGNAL(triggered()), this, SLOT(copySelectedNicknames()));
70
71 action = copyMenu->addAction(tr("Fingerprint"));
72 connect(action, SIGNAL(triggered()), this, SLOT(copySelectedFingerprints()));
73
74 action = menu->addAction(QIcon(IMG_ZOOM), tr("Zoom to Relay"));
75 if (selected.size() > 1)
76 action->setEnabled(false);
77 else
78 connect(action, SIGNAL(triggered()), this, SLOT(zoomToSelectedRelay()));
79
80 menu->exec(event->globalPos());
81 delete menu;
82}
83
84/** Copies the nicknames for all currently selected relays to the clipboard.
85 * Nicknames are formatted as a comma-delimited list, suitable for doing
86 * dumb things with your torrc. */
87void
89{
90 QString text;
91
92 foreach (QTreeWidgetItem *item, selectedItems()) {
93 RouterListItem *relay = dynamic_cast<RouterListItem *>(item);
94 if (relay)
95 text.append(relay->name() + ",");
96 }
97 if (text.length()) {
98 text.remove(text.length()-1, 1);
99 vApp->clipboard()->setText(text);
100 }
101}
102
103/** Copies the fingerprints for all currently selected relays to the
104 * clipboard. Fingerprints are formatted as a comma-delimited list, suitable
105 * for doing dumb things with your torrc. */
106void
108{
109 QString text;
110
111 foreach (QTreeWidgetItem *item, selectedItems()) {
112 RouterListItem *relay = dynamic_cast<RouterListItem *>(item);
113 if (relay)
114 text.append("$" + relay->id() + ",");
115 }
116 if (text.length()) {
117 text.remove(text.length()-1, 1);
118 vApp->clipboard()->setText(text);
119 }
120}
121
122/** Emits a zoomToRouter() signal containing the fingerprint of the
123 * currently selected relay. */
124void
126{
127 QList<QTreeWidgetItem *> selected = selectedItems();
128 if (selected.size() != 1)
129 return;
130
131 RouterListItem *relay = dynamic_cast<RouterListItem *>(selected[0]);
132 if (relay)
133 emit zoomToRouter(relay->id());
134}
135
136/** Deselects all currently selected routers. */
137void
139{
140 QList<QTreeWidgetItem *> selected = selectedItems();
141 foreach (QTreeWidgetItem *item, selected) {
142 setItemSelected(item, false);
143 }
144}
145
146/** Clear the list of router items. */
147void
149{
150 _idmap.clear();
151 QTreeWidget::clear();
152 setStatusTip(tr("%1 relays online").arg(0));
153}
154
155/** Called when the search of a router is triggered by the signal
156 * textChanged from the search field. */
157void
158RouterListWidget::onRouterSearch(const QString routerNickname)
159{
160 if (!currentIndex().data().toString().toLower().startsWith(routerNickname.toLower()))
161 {
162 searchNextRouter(routerNickname);
163 } else {
164 /* If item at currentIndex() isn't visible, make it visible. */
165 scrollToItem(itemFromIndex(currentIndex()));
166 }
167}
168
169/** Selects the following router whose name starts by routerNickname. */
170void
171RouterListWidget::searchNextRouter(const QString routerNickname)
172{
173 /* currentIndex().row() = -1 if no item is selected. */
174 int startIndex = currentIndex().row() + 1;
175 /* Search for a router whose name start with routerNickname. Case-insensitive search. */
176 QModelIndexList qmIndList = model()->match(model()->index(startIndex, NameColumn),
177 Qt::DisplayRole,
178 QVariant(routerNickname),
179 1,
180 (Qt::MatchStartsWith | Qt::MatchWrap));
181 if (qmIndList.count() > 0) {
182 setCurrentIndex(qmIndList.at(0));
183 /* If item at currentIndex() was already selected but not visible,
184 * make it visible. */
185 scrollToItem(itemFromIndex(currentIndex()));
186 }
187}
188
189/** Called when the user selects a router from the list. This will search the
190 * list for a router whose names starts with the key pressed. */
191void
193{
194 int index;
195
196 QString key = event->text();
197 if (!key.isEmpty() && key.at(0).isLetterOrNumber()) {
198 /* A text key was pressed, so search for routers that begin with that key. */
199 QList<QTreeWidgetItem *> list = findItems(QString("^[%1%2].*$")
200 .arg(key.toUpper())
201 .arg(key.toLower()),
202 Qt::MatchRegExp|Qt::MatchWrap,
203 NameColumn);
204 if (list.size() > 0) {
205 QList<QTreeWidgetItem *> s = selectedItems();
206
207 /* A match was found, so deselect any previously selected routers,
208 * select the new match, and make sure it's visible. If there was
209 * already a router selected that started with the search key, go to the
210 * next match in the list. */
211 deselectAll();
212 index = (!s.size() ? 0 : (list.indexOf(s.at(0)) + 1) % list.size());
213
214 /* Select the item and scroll to it */
215 setItemSelected(list.at(index), true);
216 scrollToItem(list.at(index));
217 }
218 event->accept();
219 } else {
220 /* It was something we don't understand, so hand it to the parent class */
221 QTreeWidget::keyPressEvent(event);
222 }
223}
224
225/** Finds the list item whose key ID matches <b>id</b>. Returns 0 if not
226 * found. */
229{
230 if (_idmap.contains(id)) {
231 return _idmap.value(id);
232 }
233 return 0;
234}
235
236/** Adds a router descriptor to the list. */
239{
240 QString id = rd.id();
241 if (id.isEmpty())
242 return 0;
243
244 RouterListItem *item = findRouterById(id);
245 if (item) {
246 item->update(rd);
247 } else {
248 item = new RouterListItem(this, rd);
249 addTopLevelItem(item);
250 _idmap.insert(id, item);
251 }
252
253 /* Set our status tip to the number of servers in the list */
254 setStatusTip(tr("%1 relays online").arg(topLevelItemCount()));
255
256 return item;
257}
258
259/** Called when the selected items have changed. This emits the
260 * routerSelected() signal with the descriptor for the selected router.
261 */
262void
264{
265 QList<RouterDescriptor> descriptors;
266
267 foreach (QTreeWidgetItem *item, selectedItems()) {
268 RouterListItem *relay = dynamic_cast<RouterListItem *>(item);
269 if (relay)
270 descriptors << relay->descriptor();
271 }
272 if (descriptors.count() > 0)
273 emit routerSelected(descriptors);
274}
275
#define IMG_ZOOM
#define IMG_COPY
stop errmsg connect(const QHostAddress &address, quint16 port)
stop errmsg QVariant
#define vApp
Definition: Vidalia.h:37
QString id() const
QString id() const
RouterDescriptor descriptor() const
QString name() const
void update(const RouterDescriptor &rd)
void onRouterSearch(const QString routerNickname)
void routerSelected(QList< RouterDescriptor > rd)
void keyPressEvent(QKeyEvent *event)
QHash< QString, RouterListItem * > _idmap
RouterListItem * addRouter(const RouterDescriptor &rd)
RouterListItem * findRouterById(QString id)
virtual void contextMenuEvent(QContextMenuEvent *event)
RouterListWidget(QWidget *parent=0)
void searchNextRouter(const QString routerNickname)
void zoomToRouter(QString id)