Vidalia 0.2.15
|
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.torproject.org/projects/vidalia.html. No part of Vidalia, 00007 ** including this file, may be copied, modified, propagated, or distributed 00008 ** except according to the 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 }