Vidalia  0.3.1
CircuitListWidget.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 CircuitListWidget.cpp
13 ** \brief Collection of Tor circuits as CircuitItems
14 */
15 
16 #include "config.h"
17 #include "CircuitListWidget.h"
18 #include "Vidalia.h"
19 
20 #include <QPoint>
21 #include <QTimer>
22 
23 #define IMG_CLOSE ":/images/22x22/edit-delete.png"
24 #define IMG_ZOOM ":/images/22x22/page-zoom.png"
25 
26 #define CLOSED_CIRCUIT_REMOVE_DELAY 3000
27 #define FAILED_CIRCUIT_REMOVE_DELAY 5000
28 #define CLOSED_STREAM_REMOVE_DELAY 3000
29 #define FAILED_STREAM_REMOVE_DELAY 4000
30 
31 
32 /** Default constructor. */
34 : QTreeWidget(parent)
35 {
36  /* Create and initialize columns */
37  setHeaderLabels(QStringList() << tr("Connection") << tr("Status"));
38 
39  /* Find out when a circuit has been selected */
40  connect(this, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
41  this, SLOT(onSelectionChanged(QTreeWidgetItem*,QTreeWidgetItem*)));
42  connect(this, SIGNAL(customContextMenuRequested(QPoint)),
43  this, SLOT(customContextMenuRequested(QPoint)));
44 
45  /* Respond to the Delete key by closing whatever circuits or streams are
46  * selected. */
47  vApp->createShortcut(QKeySequence::Delete, this, this,
48  SLOT(closeSelectedConnections()));
49 }
50 
51 /** Called when the user changes the UI translation. */
52 void
54 {
55  setHeaderLabels(QStringList() << tr("Connection") << tr("Status"));
56  for (int i = 0; i < topLevelItemCount(); i++) {
57  CircuitItem *circuitItem = dynamic_cast<CircuitItem *>(topLevelItem(i));
58  circuitItem->update(circuitItem->circuit());
59 
60  foreach (StreamItem *streamItem, circuitItem->streams()) {
61  streamItem->update(streamItem->stream());
62  }
63  }
64 }
65 
66 /** Called when the user requests a context menu on a circuit or stream in the
67  * list and displays a context menu appropriate for whichever type of item is
68  * currently selected. */
69 void
71 {
72  QMenu menu(this);
73 
74  /* Find out which item was right-clicked */
75  QTreeWidgetItem *item = itemAt(pos);
76  if (!item)
77  return;
78 
79  if (!item->parent()) {
80  /* A circuit was selected */
81  CircuitItem *circuitItem = dynamic_cast<CircuitItem *>(item);
82  if (!circuitItem)
83  return;
84 
85  /* Set up the circuit context menu */
86  QAction *zoomAct = new QAction(QIcon(IMG_ZOOM),
87  tr("Zoom to Circuit"), this);
88  QAction *closeAct = new QAction(QIcon(IMG_CLOSE),
89  tr("Close Circuit (Del)"), this);
90 #if defined(USE_MARBLE)
91  zoomAct->setEnabled(circuitItem->circuit().status() == Circuit::Built);
92  menu.addAction(zoomAct);
93  menu.addSeparator();
94 #endif
95  menu.addAction(closeAct);
96 
97  /* Display the context menu and find out which (if any) action was
98  * selected */
99  QAction* action = menu.exec(mapToGlobal(pos));
100  if (action == closeAct)
101  emit closeCircuit(circuitItem->id());
102  else if (action == zoomAct)
103  emit zoomToCircuit(circuitItem->id());
104  } else {
105  /* A stream was selected */
106  StreamItem *streamItem = dynamic_cast<StreamItem *>(item);
107  if (!streamItem)
108  return;
109 
110  /* Set up the stream context menu */
111  QAction *closeAct = new QAction(QIcon(IMG_CLOSE),
112  tr("Close Stream (Del)"), this);
113  menu.addAction(closeAct);
114 
115  /* Display the context menu and find out which (if any) action was
116  * selected */
117  QAction* action = menu.exec(mapToGlobal(pos));
118  if (action == closeAct)
119  emit closeStream(streamItem->id());
120  }
121 }
122 
123 /** Closes all selected circuits or streams. */
124 void
126 {
127  QList<QTreeWidgetItem *> items = selectedItems();
128  foreach (QTreeWidgetItem *item, items) {
129  if (!item->parent()) {
130  CircuitItem *circuitItem = dynamic_cast<CircuitItem *>(item);
131  if (circuitItem)
132  emit closeCircuit(circuitItem->id());
133  } else {
134  StreamItem *streamItem = dynamic_cast<StreamItem *>(item);
135  if (streamItem)
136  emit closeStream(streamItem->id());
137  }
138  }
139 }
140 
141 /** Adds a <b>circuit</b> to the list. If the circuit already exists in the
142  * list, the status and path will be updated. */
143 void
145 {
146  /* Check to see if the circuit already exists in the tree */
147  CircuitItem *item = findCircuitItem(circuit.id());
148 
149  if (!item) {
150  /* Add the new circuit */
151  item = new CircuitItem(circuit);
152  addTopLevelItem(item);
153  } else {
154  /* Circuit already exists, so update its status and path */
155  item->update(circuit);
156  }
157 
158  /* If the circuit is closed or dead, schedule it for removal */
159  Circuit::Status status = circuit.status();
160  if (status == Circuit::Closed) {
162  } else if (status == Circuit::Failed) {
164  }
165 }
166 
167 /** Adds a stream to the list. If the stream already exists in the list, the
168  * status and path will be updated. */
169 void
171 {
172  /* Check to see if the stream already exists in the tree */
173  StreamItem *item = findStreamItem(stream.id());
174 
175  if (!item) {
176  CircuitItem *circuit = findCircuitItem(stream.circuitId());
177  /* New stream, so try to find its circuit and add it */
178  if (circuit) {
179  circuit->addStream(new StreamItem(stream));
180  expandItem(circuit);
181  }
182  } else {
183  /* Stream already exists, so just update its status */
184  item->update(stream);
185 
186  /* If the stream is closed or dead, schedule it for removal */
187  Stream::Status status = stream.status();
188  if (status == Stream::Closed) {
190  } else if (status == Stream::Failed) {
192  }
193  }
194 }
195 
196 /** Schedules the given circuit to be removed after the specified timeout. */
197 void
199 {
200  if (!_circuitRemovalList.contains(circuit)) {
201  _circuitRemovalList << circuit;
202  QTimer::singleShot(delay, this, SLOT(removeCircuit()));
203  }
204 }
205 
206 /** Schedules the given stream to be removed after the specified timeout. */
207 void
209 {
210  if (!_streamRemovalList.contains(stream)) {
211  _streamRemovalList << stream;
212  QTimer::singleShot(delay, this, SLOT(removeStream()));
213  }
214 }
215 
216 /** Removes the first circuit scheduled to be removed. */
217 void
219 {
220  if (!_circuitRemovalList.isEmpty()) {
221  CircuitItem *circuitItem = _circuitRemovalList.takeFirst();
222  Circuit circuit = circuitItem->circuit();
223  removeCircuit(circuitItem);
224  emit circuitRemoved(circuit.id());
225  }
226 }
227 
228 /** Removes the given circuit item and all streams on that circuit. */
229 void
231 {
232  if (circuit) {
233  /* Remove all streams (if any) on this circuit. */
234  QList<StreamItem *> streams = circuit->streams();
235  foreach (StreamItem *stream, streams) {
236  /* Check if this stream was scheduled for removal already */
237  if (_streamRemovalList.contains(stream)) {
238  /* If this stream was already scheduled for removal, replace its pointer
239  * with 0, so it doesn't get removed twice. */
240  int index = _streamRemovalList.indexOf(stream);
241  _streamRemovalList.replace(index, (StreamItem *)0);
242  }
243 
244  /* Remove the stream item from the circuit */
245  circuit->removeStream(stream);
246  }
247  /* Remove the circuit item itself */
248  delete takeTopLevelItem(indexOfTopLevelItem(circuit));
249  }
250 }
251 
252 /** Removes the first stream scheduled to be removed. */
253 void
255 {
256  if (!_streamRemovalList.isEmpty()) {
257  StreamItem *stream = _streamRemovalList.takeFirst();
258  removeStream(stream);
259  }
260 }
261 
262 /** Removes the given stream item. */
263 void
265 {
266  if (stream) {
267  /* Try to get the stream's parent (a circuit item) */
268  CircuitItem *circuit = (CircuitItem *)stream->parent();
269  if (circuit) {
270  /* Remove the stream from the circuit and delete the item */
271  circuit->removeStream(stream);
272  } else {
273  /* It isn't on a circuit, so just delete the stream */
274  delete stream;
275  }
276  }
277 }
278 
279 /** Clears all circuits and streams from the list. */
280 void
282 {
283  QTreeWidget::clear();
284  _circuitRemovalList.clear();
285  _streamRemovalList.clear();
286 }
287 
288 /** Finds the circuit with the given ID and returns a pointer to that
289  * circuit's item in the list. */
292 {
293  int numCircs = topLevelItemCount();
294  for (int i = 0; i < numCircs; i++) {
295  CircuitItem *circuit = (CircuitItem *)topLevelItem(i);
296  if (circid == circuit->id()) {
297  return circuit;
298  }
299  }
300  return 0;
301 }
302 
303 /** Finds the stream with the given ID and returns a pointer to that stream's
304  * item in the list. */
305 StreamItem*
307 {
308  int numCircs = topLevelItemCount();
309  int numStreams;
310 
311  for (int i = 0; i < numCircs; i++) {
312  CircuitItem *circuit = (CircuitItem *)topLevelItem(i);
313  numStreams = circuit->childCount();
314 
315  for (int j = 0; j < numStreams; j++) {
316  StreamItem *stream = (StreamItem *)circuit->child(j);
317  if (streamid == stream->id()) {
318  return stream;
319  }
320  }
321  }
322  return 0;
323 }
324 
325 /** Called when the current item selection has changed. */
326 void
328  QTreeWidgetItem *prev)
329 {
330  Q_UNUSED(prev);
331 
332  if (cur) {
333  Circuit circuit;
334 
335  if (!cur->parent()) {
336  /* User selected a CircuitItem, so just grab the Circuit */
337  circuit = ((CircuitItem *)cur)->circuit();
338  } else {
339  /* User selected a StreamItem, so get its parent and then the Circuit */
340  CircuitItem *circItem = (CircuitItem *)cur->parent();
341  circuit = circItem->circuit();
342  }
343 
344  /* If this circuit has a path, then emit it so we can highlight it */
345  emit circuitSelected(circuit);
346  }
347 }
348 
349 /** Returns a list of circuits currently in the widget. */
352 {
353  int numCircs = topLevelItemCount();
354  CircuitList circs;
355 
356  for (int i = 0; i < numCircs; i++) {
357  CircuitItem *circ = (CircuitItem *)topLevelItem(i);
358  circs << circ->circuit();
359  }
360  return circs;
361 }
362 
void circuitSelected(Circuit circuit)
StreamId id() const
Definition: Stream.h:68
QList< StreamItem * > _streamRemovalList
Definition: Stream.h:31
void customContextMenuRequested(const QPoint &pos)
Circuit circuit() const
Definition: CircuitItem.h:45
void addCircuit(const Circuit &circuit)
#define CLOSED_STREAM_REMOVE_DELAY
Stream stream() const
Definition: StreamItem.h:30
void removeStream(StreamItem *stream)
Definition: CircuitItem.cpp:56
Status
Definition: Circuit.h:33
void onSelectionChanged(QTreeWidgetItem *cur, QTreeWidgetItem *prev)
void update(const Circuit &circuit)
Definition: CircuitItem.cpp:29
CircuitListWidget(QWidget *parent=0)
QList< Circuit > circuits()
void addStream(const Stream &stream)
void addStream(StreamItem *stream)
Definition: CircuitItem.cpp:49
CircuitId id() const
Definition: CircuitItem.h:43
void zoomToCircuit(CircuitId circid)
CircuitItem * findCircuitItem(const CircuitId &circid)
CircuitId circuitId() const
Definition: Stream.h:74
QString StreamId
Definition: Stream.h:28
Status status() const
Definition: Circuit.h:53
QString i(QString str)
Definition: html.cpp:32
stop errmsg connect(const QHostAddress &address, quint16 port)
void closeStream(StreamId streamid)
CircuitId id() const
Definition: Circuit.h:51
void circuitRemoved(CircuitId circid)
#define FAILED_CIRCUIT_REMOVE_DELAY
void scheduleCircuitRemoval(CircuitItem *circuit, int delay)
QList< CircuitItem * > _circuitRemovalList
Status status() const
Definition: Stream.h:70
void closeCircuit(CircuitId circid)
QList< Circuit > CircuitList
Definition: Circuit.h:81
#define CLOSED_CIRCUIT_REMOVE_DELAY
QList< StreamItem * > streams() const
Definition: CircuitItem.cpp:66
void update(const Stream &stream)
Definition: StreamItem.cpp:33
QString CircuitId
Definition: Circuit.h:24
#define vApp
Definition: Vidalia.h:37
StreamItem * findStreamItem(const StreamId &streamid)
void scheduleStreamRemoval(StreamItem *stream, int delay)
Status
Definition: Stream.h:37
#define FAILED_STREAM_REMOVE_DELAY
#define IMG_CLOSE
#define IMG_ZOOM
StreamId id() const
Definition: StreamItem.h:34