Vidalia 0.2.12

CircuitListWidget.cpp

Go to the documentation of this file.
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 CircuitListWidget.cpp
00013 ** \brief Collection of Tor circuits as CircuitItems
00014 */
00015 
00016 #include "config.h"
00017 #include "CircuitListWidget.h"
00018 #include "Vidalia.h"
00019 
00020 #include <QPoint>
00021 #include <QTimer>
00022 
00023 #define IMG_CLOSE   ":/images/22x22/edit-delete.png"
00024 #define IMG_ZOOM    ":/images/22x22/page-zoom.png"
00025 
00026 #define CLOSED_CIRCUIT_REMOVE_DELAY     3000
00027 #define FAILED_CIRCUIT_REMOVE_DELAY     5000
00028 #define CLOSED_STREAM_REMOVE_DELAY      3000
00029 #define FAILED_STREAM_REMOVE_DELAY      4000
00030 
00031 
00032 /** Default constructor. */
00033 CircuitListWidget::CircuitListWidget(QWidget *parent)
00034 : QTreeWidget(parent)
00035 {
00036   /* Create and initialize columns */
00037   setHeaderLabels(QStringList() << tr("Connection") << tr("Status"));
00038 
00039   /* Find out when a circuit has been selected */
00040   connect(this, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
00041           this, SLOT(onSelectionChanged(QTreeWidgetItem*,QTreeWidgetItem*)));
00042   connect(this, SIGNAL(customContextMenuRequested(QPoint)),
00043           this, SLOT(customContextMenuRequested(QPoint)));
00044 
00045   /* Respond to the Delete key by closing whatever circuits or streams are
00046    * selected. */
00047   vApp->createShortcut(QKeySequence::Delete, this, this,
00048                        SLOT(closeSelectedConnections()));
00049 }
00050 
00051 /** Called when the user changes the UI translation. */
00052 void
00053 CircuitListWidget::retranslateUi()
00054 {
00055   setHeaderLabels(QStringList() << tr("Connection") << tr("Status"));
00056   for (int i = 0; i < topLevelItemCount(); i++) {
00057     CircuitItem *circuitItem = dynamic_cast<CircuitItem *>(topLevelItem(i));
00058     circuitItem->update(circuitItem->circuit());
00059 
00060     foreach (StreamItem *streamItem, circuitItem->streams()) {
00061       streamItem->update(streamItem->stream());
00062     }
00063   }
00064 }
00065 
00066 /** Called when the user requests a context menu on a circuit or stream in the
00067  * list and displays a context menu appropriate for whichever type of item is
00068  * currently selected. */
00069 void
00070 CircuitListWidget::customContextMenuRequested(const QPoint &pos)
00071 {
00072   QMenu menu(this);
00073 
00074   /* Find out which item was right-clicked */
00075   QTreeWidgetItem *item = itemAt(pos);
00076   if (!item)
00077     return;
00078     
00079   if (!item->parent()) {
00080     /* A circuit was selected */
00081     CircuitItem *circuitItem = dynamic_cast<CircuitItem *>(item);
00082     if (!circuitItem)
00083       return;
00084 
00085     /* Set up the circuit context menu */
00086     QAction *zoomAct  = new QAction(QIcon(IMG_ZOOM),
00087                                     tr("Zoom to Circuit"), this);
00088     QAction *closeAct = new QAction(QIcon(IMG_CLOSE),
00089                                     tr("Close Circuit (Del)"), this);
00090 #if defined(USE_MARBLE)
00091     zoomAct->setEnabled(circuitItem->circuit().status() == Circuit::Built);
00092     menu.addAction(zoomAct);
00093     menu.addSeparator();
00094 #endif
00095     menu.addAction(closeAct);
00096       
00097     /* Display the context menu and find out which (if any) action was
00098      * selected */
00099     QAction* action = menu.exec(mapToGlobal(pos));
00100     if (action == closeAct)
00101       emit closeCircuit(circuitItem->id());
00102     else if (action == zoomAct)
00103       emit zoomToCircuit(circuitItem->id());
00104   } else {
00105     /* A stream was selected */
00106     StreamItem *streamItem = dynamic_cast<StreamItem *>(item);
00107     if (!streamItem)
00108       return;
00109  
00110     /* Set up the stream context menu */
00111     QAction *closeAct = new QAction(QIcon(IMG_CLOSE),
00112                                     tr("Close Stream (Del)"), this);
00113     menu.addAction(closeAct);
00114 
00115     /* Display the context menu and find out which (if any) action was
00116      * selected */
00117     QAction* action = menu.exec(mapToGlobal(pos));
00118     if (action == closeAct)
00119       emit closeStream(streamItem->id());
00120   }
00121 }
00122 
00123 /** Closes all selected circuits or streams. */
00124 void
00125 CircuitListWidget::closeSelectedConnections()
00126 {
00127   QList<QTreeWidgetItem *> items = selectedItems();
00128   foreach (QTreeWidgetItem *item, items) {
00129     if (!item->parent()) {
00130       CircuitItem *circuitItem = dynamic_cast<CircuitItem *>(item);
00131       if (circuitItem)
00132         emit closeCircuit(circuitItem->id());
00133     } else {
00134       StreamItem *streamItem = dynamic_cast<StreamItem *>(item);
00135       if (streamItem)
00136         emit closeStream(streamItem->id());
00137     }
00138   }
00139 }
00140 
00141 /** Adds a <b>circuit</b> to the list. If the circuit already exists in the
00142  * list, the status and path will be updated. */
00143 void
00144 CircuitListWidget::addCircuit(const Circuit &circuit)
00145 {
00146   /* Check to see if the circuit already exists in the tree */
00147   CircuitItem *item = findCircuitItem(circuit.id());
00148   
00149   if (!item) {
00150     /* Add the new circuit */
00151     item = new CircuitItem(circuit);
00152     addTopLevelItem(item);
00153   } else {
00154     /* Circuit already exists, so update its status and path */
00155     item->update(circuit);
00156   }
00157 
00158   /* If the circuit is closed or dead, schedule it for removal */
00159   Circuit::Status status = circuit.status();
00160   if (status == Circuit::Closed) {
00161     scheduleCircuitRemoval(item, CLOSED_CIRCUIT_REMOVE_DELAY);
00162   } else if (status == Circuit::Failed) {
00163     scheduleCircuitRemoval(item, FAILED_CIRCUIT_REMOVE_DELAY);
00164   }
00165 }
00166 
00167 /** Adds a stream to the list. If the stream already exists in the list, the
00168  * status and path will be updated. */
00169 void
00170 CircuitListWidget::addStream(const Stream &stream)
00171 {
00172   /* Check to see if the stream already exists in the tree */
00173   StreamItem *item = findStreamItem(stream.id());
00174 
00175   if (!item) {
00176     CircuitItem *circuit = findCircuitItem(stream.circuitId());
00177     /* New stream, so try to find its circuit and add it */
00178     if (circuit) {
00179       circuit->addStream(new StreamItem(stream));
00180       expandItem(circuit);
00181     }
00182   } else {
00183     /* Stream already exists, so just update its status */
00184     item->update(stream);
00185 
00186     /* If the stream is closed or dead, schedule it for removal */
00187     Stream::Status status = stream.status();
00188     if (status == Stream::Closed) {
00189       scheduleStreamRemoval(item, CLOSED_STREAM_REMOVE_DELAY);
00190     } else if (status == Stream::Failed) {
00191       scheduleStreamRemoval(item, FAILED_STREAM_REMOVE_DELAY);
00192     }
00193   }
00194 }
00195 
00196 /** Schedules the given circuit to be removed after the specified timeout. */
00197 void
00198 CircuitListWidget::scheduleCircuitRemoval(CircuitItem *circuit, int delay)
00199 {
00200   if (!_circuitRemovalList.contains(circuit)) {
00201     _circuitRemovalList << circuit;
00202     QTimer::singleShot(delay, this, SLOT(removeCircuit()));
00203   } 
00204 }
00205 
00206 /** Schedules the given stream to be removed after the specified timeout. */
00207 void
00208 CircuitListWidget::scheduleStreamRemoval(StreamItem *stream, int delay)
00209 {
00210   if (!_streamRemovalList.contains(stream)) {
00211     _streamRemovalList << stream;
00212     QTimer::singleShot(delay, this, SLOT(removeStream()));
00213   } 
00214 }
00215 
00216 /** Removes the first circuit scheduled to be removed. */
00217 void
00218 CircuitListWidget::removeCircuit()
00219 {
00220   if (!_circuitRemovalList.isEmpty()) {
00221     CircuitItem *circuitItem = _circuitRemovalList.takeFirst();
00222     Circuit circuit = circuitItem->circuit();
00223     removeCircuit(circuitItem);
00224     emit circuitRemoved(circuit.id());
00225   }
00226 }
00227 
00228 /** Removes the given circuit item and all streams on that circuit. */
00229 void
00230 CircuitListWidget::removeCircuit(CircuitItem *circuit)
00231 {
00232   if (circuit) {
00233     /* Remove all streams (if any) on this circuit. */
00234     QList<StreamItem *> streams = circuit->streams();
00235     foreach (StreamItem *stream, streams) {
00236       /* Check if this stream was scheduled for removal already */
00237       if (_streamRemovalList.contains(stream)) {
00238         /* If this stream was already scheduled for removal, replace its pointer
00239          * with 0, so it doesn't get removed twice. */
00240         int index = _streamRemovalList.indexOf(stream);
00241         _streamRemovalList.replace(index, (StreamItem *)0);
00242       }
00243       
00244       /* Remove the stream item from the circuit */
00245       circuit->removeStream(stream);
00246     }
00247     /* Remove the circuit item itself */
00248     delete takeTopLevelItem(indexOfTopLevelItem(circuit));
00249   }
00250 }
00251 
00252 /** Removes the first stream scheduled to be removed. */
00253 void
00254 CircuitListWidget::removeStream()
00255 {
00256   if (!_streamRemovalList.isEmpty()) {
00257     StreamItem *stream = _streamRemovalList.takeFirst();
00258     removeStream(stream);
00259   }
00260 }
00261 
00262 /** Removes the given stream item. */
00263 void
00264 CircuitListWidget::removeStream(StreamItem *stream)
00265 {
00266   if (stream) {
00267     /* Try to get the stream's parent (a circuit item) */ 
00268     CircuitItem *circuit = (CircuitItem *)stream->parent();
00269     if (circuit) {
00270       /* Remove the stream from the circuit and delete the item */
00271       circuit->removeStream(stream);
00272     } else {
00273       /* It isn't on a circuit, so just delete the stream */
00274       delete stream;
00275     }
00276   }
00277 }
00278 
00279 /** Clears all circuits and streams from the list. */
00280 void
00281 CircuitListWidget::clearCircuits()
00282 {
00283   QTreeWidget::clear();
00284   _circuitRemovalList.clear();
00285   _streamRemovalList.clear();
00286 }
00287 
00288 /** Finds the circuit with the given ID and returns a pointer to that
00289  * circuit's item in the list. */
00290 CircuitItem*
00291 CircuitListWidget::findCircuitItem(const CircuitId &circid)
00292 {
00293   int numCircs = topLevelItemCount();
00294   for (int i = 0; i < numCircs; i++) {
00295     CircuitItem *circuit = (CircuitItem *)topLevelItem(i);
00296     if (circid == circuit->id()) {
00297       return circuit;
00298     }
00299   }
00300   return 0;
00301 }
00302 
00303 /** Finds the stream with the given ID and returns a pointer to that stream's
00304  * item in the list. */
00305 StreamItem*
00306 CircuitListWidget::findStreamItem(const StreamId &streamid)
00307 {
00308   int numCircs = topLevelItemCount();
00309   int numStreams;
00310   
00311   for (int i = 0; i < numCircs; i++) {
00312     CircuitItem *circuit = (CircuitItem *)topLevelItem(i);
00313     numStreams = circuit->childCount();
00314   
00315     for (int j = 0; j < numStreams; j++) {
00316       StreamItem *stream = (StreamItem *)circuit->child(j);
00317       if (streamid == stream->id()) {
00318         return stream;
00319       }
00320     }
00321   }
00322   return 0;
00323 }
00324 
00325 /** Called when the current item selection has changed. */
00326 void
00327 CircuitListWidget::onSelectionChanged(QTreeWidgetItem *cur, 
00328                                       QTreeWidgetItem *prev)
00329 {
00330   Q_UNUSED(prev);
00331 
00332   if (cur) {
00333     Circuit circuit;
00334     
00335     if (!cur->parent()) {
00336       /* User selected a CircuitItem, so just grab the Circuit */
00337       circuit = ((CircuitItem *)cur)->circuit();
00338     } else {
00339       /* User selected a StreamItem, so get its parent and then the Circuit */
00340       CircuitItem *circItem = (CircuitItem *)cur->parent();
00341       circuit = circItem->circuit();
00342     }
00343 
00344     /* If this circuit has a path, then emit it so we can highlight it */
00345     emit circuitSelected(circuit);
00346   }
00347 }
00348 
00349 /** Returns a list of circuits currently in the widget. */
00350 CircuitList
00351 CircuitListWidget::circuits()
00352 {
00353   int numCircs = topLevelItemCount();
00354   CircuitList circs;
00355   
00356   for (int i = 0; i < numCircs; i++) {
00357     CircuitItem *circ = (CircuitItem *)topLevelItem(i);
00358     circs << circ->circuit();
00359   }
00360   return circs;
00361 }
00362