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