Vidalia  0.2.21
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. */
44 void
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. */
55 void
56 RouterListWidget::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. */
87 void
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. */
106 void
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. */
124 void
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. */
137 void
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. */
147 void
149 {
150  _idmap.clear();
151  QTreeWidget::clear();
152  setStatusTip(tr("%1 relays online").arg(0));
153 }
154 
155 /** Called when the user selects a router from the list. This will search the
156  * list for a router whose names starts with the key pressed. */
157 void
159 {
160  int index;
161 
162  QString key = event->text();
163  if (!key.isEmpty() && key.at(0).isLetterOrNumber()) {
164  /* A text key was pressed, so search for routers that begin with that key. */
165  QList<QTreeWidgetItem *> list = findItems(QString("^[%1%2].*$")
166  .arg(key.toUpper())
167  .arg(key.toLower()),
168  Qt::MatchRegExp|Qt::MatchWrap,
169  NameColumn);
170  if (list.size() > 0) {
171  QList<QTreeWidgetItem *> s = selectedItems();
172 
173  /* A match was found, so deselect any previously selected routers,
174  * select the new match, and make sure it's visible. If there was
175  * already a router selected that started with the search key, go to the
176  * next match in the list. */
177  deselectAll();
178  index = (!s.size() ? 0 : (list.indexOf(s.at(0)) + 1) % list.size());
179 
180  /* Select the item and scroll to it */
181  setItemSelected(list.at(index), true);
182  scrollToItem(list.at(index));
183  }
184  event->accept();
185  } else {
186  /* It was something we don't understand, so hand it to the parent class */
187  QTreeWidget::keyPressEvent(event);
188  }
189 }
190 
191 /** Finds the list item whose key ID matches <b>id</b>. Returns 0 if not
192  * found. */
195 {
196  if (_idmap.contains(id)) {
197  return _idmap.value(id);
198  }
199  return 0;
200 }
201 
202 /** Adds a router descriptor to the list. */
205 {
206  QString id = rd.id();
207  if (id.isEmpty())
208  return 0;
209 
210  RouterListItem *item = findRouterById(id);
211  if (item) {
212  item->update(rd);
213  } else {
214  item = new RouterListItem(this, rd);
215  addTopLevelItem(item);
216  _idmap.insert(id, item);
217  }
218 
219  /* Set our status tip to the number of servers in the list */
220  setStatusTip(tr("%1 relays online").arg(topLevelItemCount()));
221 
222  return item;
223 }
224 
225 /** Called when the selected items have changed. This emits the
226  * routerSelected() signal with the descriptor for the selected router.
227  */
228 void
230 {
231  QList<RouterDescriptor> descriptors;
232 
233  foreach (QTreeWidgetItem *item, selectedItems()) {
234  RouterListItem *relay = dynamic_cast<RouterListItem *>(item);
235  if (relay)
236  descriptors << relay->descriptor();
237  }
238  if (descriptors.count() > 0)
239  emit routerSelected(descriptors);
240 }
241