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

Generated on Mon Aug 30 19:14:02 2010 for Vidalia by  doxygen 1.5.9