Vidalia 0.2.10
|
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 }