Vidalia 0.2.12

LogTreeWidget.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 LogTreeWidget.cpp
00013 ** \brief Contains a collection of log messages as LogTreeItems
00014 */
00015 
00016 #include "LogTreeWidget.h"
00017 #include "LogHeaderView.h"
00018 #include "LogMessageColumnDelegate.h"
00019 
00020 #include <QScrollBar>
00021 
00022 
00023 /** Default constructor. */
00024 LogTreeWidget::LogTreeWidget(QWidget *parent)
00025   : QTreeWidget(parent)
00026 {
00027   setHeader(new LogHeaderView(this));
00028 
00029   /* Tor's log messages are always in English, so stop Qt from futzing with
00030    * the message text if we're currently using a non-English RTL layout. */
00031   if (layoutDirection() == Qt::RightToLeft) {
00032     setItemDelegateForColumn(LogTreeWidget::MessageColumn,
00033                              new LogMessageColumnDelegate(this));
00034   }
00035 
00036   /* Explicitly default to sorting messages chronologically */
00037   sortItems(LogTreeWidget::TimeColumn, Qt::AscendingOrder);
00038 
00039   /* Default to always scrolling to the most recent item added */
00040   _scrollOnNewItem = true;
00041   setVerticalScrollMode(QAbstractItemView::ScrollPerItem);
00042   connect(verticalScrollBar(), SIGNAL(sliderReleased()),
00043           this, SLOT(verticalSliderReleased()));
00044 }
00045 
00046 /** Called when the user moves the vertical scrollbar. If the user has the
00047  * scrollbar at within one step of its maximum, then always scroll to new
00048  * items when added. Otherwise, leave the scrollbar alone since they are
00049  * probably looking at something in their history. */
00050 void
00051 LogTreeWidget::verticalSliderReleased()
00052 {
00053   QScrollBar *scrollBar = verticalScrollBar();
00054   if (header()->sortIndicatorOrder() == Qt::AscendingOrder)
00055     _scrollOnNewItem = (scrollBar->value() == scrollBar->maximum());
00056   else
00057     _scrollOnNewItem = (scrollBar->value() == scrollBar->minimum());
00058 }
00059 
00060 /** Cast a QList of QTreeWidgetItem pointers to a list of LogTreeWidget
00061  * pointers. There really must be a better way to do this. */
00062 QList<LogTreeItem *>
00063 LogTreeWidget::qlist_cast(QList<QTreeWidgetItem *> inlist)
00064 {
00065   QList<LogTreeItem *> outlist;
00066   foreach (QTreeWidgetItem *item, inlist) {
00067     outlist << (LogTreeItem *)item;
00068   }
00069   return outlist;
00070 }
00071 
00072 /** Sorts the list of pointers to log tree items by timestamp. */
00073 QList<LogTreeItem *>
00074 LogTreeWidget::qlist_sort(QList<LogTreeItem *> inlist)
00075 {
00076   QMap<quint32, LogTreeItem *> outlist;
00077   foreach (LogTreeItem *item, inlist) {
00078     outlist.insert(item->id(), item);
00079   }
00080   return outlist.values();
00081 }
00082 
00083 /** The first time the log tree is shown, we need to set the default column
00084  * widths. */
00085 void
00086 LogTreeWidget::showEvent(QShowEvent *event)
00087 {
00088   static bool shown = false;
00089   QTreeWidget::showEvent(event);
00090   if (!shown) {
00091     /* Set the default column widths the first time this is shown */
00092     ((LogHeaderView *)header())->resetColumnWidths();
00093     shown = true;
00094   }
00095 }
00096 
00097 /** Clears all items from the message log and resets the counter in the status
00098  * bar. */
00099 void
00100 LogTreeWidget::clearMessages()
00101 {
00102   /* Clear the messages */
00103   _itemHistory.clear();
00104   clear();
00105 }
00106 
00107 /** Returns a list of all currently selected items. */
00108 QStringList
00109 LogTreeWidget::selectedMessages()
00110 {
00111   QStringList messages;
00112   
00113   /* Get all selected log items */
00114   QList<LogTreeItem *> items = 
00115     qlist_cast(selectedItems());
00116   
00117   /* Format the message items as strings and put them in a list */
00118   foreach (LogTreeItem *item, qlist_sort(items)) {
00119     messages << item->toString();
00120   }
00121   return messages;
00122 }
00123 
00124 /** Returns a list of all items in the tree. */
00125 QStringList
00126 LogTreeWidget::allMessages()
00127 {
00128   QStringList messages;
00129 
00130   /* Format the message items as strings and put them in a list */
00131   foreach (LogTreeItem *item, _itemHistory) {
00132     messages << item->toString();
00133   }
00134   return messages;
00135 }
00136 
00137 /** Returns the number of items currently shown. */
00138 int
00139 LogTreeWidget::messageCount()
00140 {
00141   return topLevelItemCount();
00142 }
00143 
00144 /** Sets the maximum number of items in the tree. */
00145 void
00146 LogTreeWidget::setMaximumMessageCount(int max)
00147 {
00148   while (max < messageCount() && _itemHistory.size() > 0) {
00149     /* If the new max is less than the currently displayed number of 
00150      * items, then we'll get rid of some. */
00151     int index = indexOfTopLevelItem(_itemHistory.takeFirst());
00152     if (index != -1)
00153       delete takeTopLevelItem(index);
00154   }
00155   _maxItemCount = max;
00156 }
00157 
00158 /** Deselects all currently selected items. */
00159 void
00160 LogTreeWidget::deselectAll()
00161 {
00162   foreach(QTreeWidgetItem *item, selectedItems()) {
00163     item->setSelected(false);
00164   }
00165 }
00166 
00167 /** Adds a log item to the tree and returns a pointer to the new item. */
00168 LogTreeItem*
00169 LogTreeWidget::log(tc::Severity type, const QString &message)
00170 {
00171   int oldScrollValue;
00172   QScrollBar *scrollBar = verticalScrollBar();
00173   LogTreeItem *item = new LogTreeItem(type, message);
00174 
00175   /* Remember the current scrollbar position */
00176   oldScrollValue = scrollBar->value();
00177 
00178   /* If we need to make room, then make some room */
00179   if (messageCount() >= _maxItemCount && _itemHistory.size()) {
00180     int index = indexOfTopLevelItem(_itemHistory.takeFirst());
00181     if (index != -1)
00182       delete takeTopLevelItem(index);
00183   }
00184 
00185   /* Add the new message item.
00186    * NOTE: We disable sorting, add the new item, and then re-enable sorting
00187    *       to force the result to be sorted immediately. Otherwise, the new
00188    *       message is not sorted until the message log has focus again. This
00189    *       is totally lame.
00190    */
00191   setSortingEnabled(false);
00192   addLogTreeItem(item);
00193   setSortingEnabled(true);
00194 
00195   /* The intended vertical scrolling behavior is as follows:
00196    *
00197    *   1) If the message log is sorted in chronological order, and the user
00198    *      previously had the vertical scroll bar at its maximum position, then
00199    *      reposition the vertical scroll bar to the new maximum value.
00200    *
00201    *   2) If the message log is sorted in reverse chronological order, and the
00202    *      user previously had the vertical scroll bar at its minimum position,
00203    *      then reposition the vertical scroll bar to the new minimum value
00204    *      (which is always just 0 anyway).
00205    *
00206    *   3) If the message log is sorted by severity level or lexicographically
00207    *      by log message, or if the user manually repositioned the scroll bar,
00208    *      then leave the vertical scroll bar at its previous position.
00209    */
00210   if (_scrollOnNewItem && sortColumn() == LogTreeWidget::TimeColumn) {
00211     if (header()->sortIndicatorOrder() == Qt::AscendingOrder)
00212       scrollBar->setValue(scrollBar->maximum());
00213     else
00214       scrollBar->setValue(scrollBar->minimum());
00215   } else {
00216     scrollBar->setValue(oldScrollValue);
00217   }
00218 
00219   return item;
00220 }
00221 
00222 /** Adds <b>item</b> as a top-level item in the tree. */
00223 void
00224 LogTreeWidget::addLogTreeItem(LogTreeItem *item)
00225 {
00226   addTopLevelItem(item);
00227   _itemHistory.append(item);
00228 }
00229 
00230 /** Filters the message log based on the given filter. */
00231 void
00232 LogTreeWidget::filter(uint filter)
00233 {
00234   int itemsShown = 0;
00235   for (int i = _itemHistory.size()-1; i >= 0; i--) {
00236     LogTreeItem *item = _itemHistory.at(i);
00237     if ((itemsShown < _maxItemCount) && (filter & item->severity())) {
00238       itemsShown++;
00239     } else {
00240       int itemIndex = indexOfTopLevelItem(item);
00241       if (itemIndex != -1)
00242         delete takeTopLevelItem(itemIndex);
00243       _itemHistory.removeAt(i);
00244     }
00245   }
00246 }
00247 
00248 /** Searches the log for entries that contain the given text. */
00249 QList<LogTreeItem *>
00250 LogTreeWidget::find(QString text, bool highlight)
00251 {
00252   QList<LogTreeItem *> items = 
00253     qlist_cast(findItems(text, Qt::MatchContains|Qt::MatchWrap, MessageColumn));
00254   
00255   if (highlight) {
00256     /* Deselect all items before highlighting our search results. */
00257     deselectAll();
00258     foreach (LogTreeItem *item, items) {
00259       /* Highlight a matched item */
00260       item->setSelected(true);
00261     }
00262   }
00263 
00264   /* Return the results, sorted by timestamp */
00265   return qlist_sort(items);
00266 }