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

Generated on Wed Dec 23 21:06:54 2009 for Vidalia by  doxygen 1.6.1