Vidalia  0.2.21
LogTreeWidget.cpp
Go to the documentation of this file.
1 /*
2 ** This file is part of Vidalia, and is subject to the license terms in the
3 ** LICENSE file, found in the top level directory of this distribution. If you
4 ** did not receive the LICENSE file with this file, you may obtain it from the
5 ** Vidalia source package distributed by the Vidalia Project at
6 ** http://www.torproject.org/projects/vidalia.html. No part of Vidalia,
7 ** including this file, may be copied, modified, propagated, or distributed
8 ** except according to the terms described in the LICENSE file.
9 */
10 
11 /*
12 ** \file LogTreeWidget.cpp
13 ** \brief Contains a collection of log messages as LogTreeItems
14 */
15 
16 #include "LogTreeWidget.h"
17 #include "LogHeaderView.h"
19 
20 #include <QScrollBar>
21 
22 
23 /** Default constructor. */
25  : QTreeWidget(parent)
26 {
27  setHeader(new LogHeaderView(this));
28 
29  /* Tor's log messages are always in English, so stop Qt from futzing with
30  * the message text if we're currently using a non-English RTL layout. */
31  if (layoutDirection() == Qt::RightToLeft) {
32  setItemDelegateForColumn(LogTreeWidget::MessageColumn,
33  new LogMessageColumnDelegate(this));
34  }
35 
36  /* Explicitly default to sorting messages chronologically */
37  sortItems(LogTreeWidget::TimeColumn, Qt::AscendingOrder);
38 
39  /* Default to always scrolling to the most recent item added */
40  _scrollOnNewItem = true;
41  setVerticalScrollMode(QAbstractItemView::ScrollPerItem);
42  connect(verticalScrollBar(), SIGNAL(sliderReleased()),
43  this, SLOT(verticalSliderReleased()));
44 }
45 
46 /** Called when the user moves the vertical scrollbar. If the user has the
47  * scrollbar at within one step of its maximum, then always scroll to new
48  * items when added. Otherwise, leave the scrollbar alone since they are
49  * probably looking at something in their history. */
50 void
52 {
53  QScrollBar *scrollBar = verticalScrollBar();
54  if (header()->sortIndicatorOrder() == Qt::AscendingOrder)
55  _scrollOnNewItem = (scrollBar->value() == scrollBar->maximum());
56  else
57  _scrollOnNewItem = (scrollBar->value() == scrollBar->minimum());
58 }
59 
60 /** Cast a QList of QTreeWidgetItem pointers to a list of LogTreeWidget
61  * pointers. There really must be a better way to do this. */
62 QList<LogTreeItem *>
63 LogTreeWidget::qlist_cast(QList<QTreeWidgetItem *> inlist)
64 {
65  QList<LogTreeItem *> outlist;
66  foreach (QTreeWidgetItem *item, inlist) {
67  outlist << (LogTreeItem *)item;
68  }
69  return outlist;
70 }
71 
72 /** Sorts the list of pointers to log tree items by timestamp. */
73 QList<LogTreeItem *>
74 LogTreeWidget::qlist_sort(QList<LogTreeItem *> inlist)
75 {
76  QMap<quint32, LogTreeItem *> outlist;
77  foreach (LogTreeItem *item, inlist) {
78  outlist.insert(item->id(), item);
79  }
80  return outlist.values();
81 }
82 
83 /** The first time the log tree is shown, we need to set the default column
84  * widths. */
85 void
86 LogTreeWidget::showEvent(QShowEvent *event)
87 {
88  static bool shown = false;
89  QTreeWidget::showEvent(event);
90  if (!shown) {
91  /* Set the default column widths the first time this is shown */
92  ((LogHeaderView *)header())->resetColumnWidths();
93  shown = true;
94  }
95 }
96 
97 /** Clears all items from the message log and resets the counter in the status
98  * bar. */
99 void
101 {
102  /* Clear the messages */
103  _itemHistory.clear();
104  clear();
105 }
106 
107 /** Returns a list of all currently selected items. */
108 QStringList
110 {
111  QStringList messages;
112 
113  /* Get all selected log items */
114  QList<LogTreeItem *> items =
115  qlist_cast(selectedItems());
116 
117  /* Format the message items as strings and put them in a list */
118  foreach (LogTreeItem *item, qlist_sort(items)) {
119  messages << item->toString();
120  }
121  return messages;
122 }
123 
124 /** Returns a list of all items in the tree. */
125 QStringList
127 {
128  QStringList messages;
129 
130  /* Format the message items as strings and put them in a list */
131  foreach (LogTreeItem *item, _itemHistory) {
132  messages << item->toString();
133  }
134  return messages;
135 }
136 
137 /** Returns the number of items currently shown. */
138 int
140 {
141  return topLevelItemCount();
142 }
143 
144 /** Sets the maximum number of items in the tree. */
145 void
147 {
148  while (max < messageCount() && _itemHistory.size() > 0) {
149  /* If the new max is less than the currently displayed number of
150  * items, then we'll get rid of some. */
151  int index = indexOfTopLevelItem(_itemHistory.takeFirst());
152  if (index != -1)
153  delete takeTopLevelItem(index);
154  }
155  _maxItemCount = max;
156 }
157 
158 /** Deselects all currently selected items. */
159 void
161 {
162  foreach(QTreeWidgetItem *item, selectedItems()) {
163  item->setSelected(false);
164  }
165 }
166 
167 /** Adds a log item to the tree and returns a pointer to the new item. */
169 LogTreeWidget::log(tc::Severity type, const QString &message)
170 {
171  int oldScrollValue;
172  QScrollBar *scrollBar = verticalScrollBar();
173  LogTreeItem *item = new LogTreeItem(type, message);
174 
175  /* Remember the current scrollbar position */
176  oldScrollValue = scrollBar->value();
177 
178  /* If we need to make room, then make some room */
179  if (messageCount() >= _maxItemCount && _itemHistory.size()) {
180  int index = indexOfTopLevelItem(_itemHistory.takeFirst());
181  if (index != -1)
182  delete takeTopLevelItem(index);
183  }
184 
185  /* Add the new message item.
186  * NOTE: We disable sorting, add the new item, and then re-enable sorting
187  * to force the result to be sorted immediately. Otherwise, the new
188  * message is not sorted until the message log has focus again. This
189  * is totally lame.
190  */
191  setSortingEnabled(false);
192  addLogTreeItem(item);
193  setSortingEnabled(true);
194 
195  /* The intended vertical scrolling behavior is as follows:
196  *
197  * 1) If the message log is sorted in chronological order, and the user
198  * previously had the vertical scroll bar at its maximum position, then
199  * reposition the vertical scroll bar to the new maximum value.
200  *
201  * 2) If the message log is sorted in reverse chronological order, and the
202  * user previously had the vertical scroll bar at its minimum position,
203  * then reposition the vertical scroll bar to the new minimum value
204  * (which is always just 0 anyway).
205  *
206  * 3) If the message log is sorted by severity level or lexicographically
207  * by log message, or if the user manually repositioned the scroll bar,
208  * then leave the vertical scroll bar at its previous position.
209  */
210  if (_scrollOnNewItem && sortColumn() == LogTreeWidget::TimeColumn) {
211  if (header()->sortIndicatorOrder() == Qt::AscendingOrder)
212  scrollBar->setValue(scrollBar->maximum());
213  else
214  scrollBar->setValue(scrollBar->minimum());
215  } else {
216  scrollBar->setValue(oldScrollValue);
217  }
218 
219  return item;
220 }
221 
222 /** Adds <b>item</b> as a top-level item in the tree. */
223 void
225 {
226  addTopLevelItem(item);
227  _itemHistory.append(item);
228 }
229 
230 /** Filters the message log based on the given filter. */
231 void
233 {
234  int itemsShown = 0;
235  for (int i = _itemHistory.size()-1; i >= 0; i--) {
236  LogTreeItem *item = _itemHistory.at(i);
237  if ((itemsShown < _maxItemCount) && (filter & item->severity())) {
238  itemsShown++;
239  } else {
240  int itemIndex = indexOfTopLevelItem(item);
241  if (itemIndex != -1)
242  delete takeTopLevelItem(itemIndex);
243  _itemHistory.removeAt(i);
244  }
245  }
246 }
247 
248 /** Searches the log for entries that contain the given text. */
249 QList<LogTreeItem *>
250 LogTreeWidget::find(QString text, bool highlight)
251 {
252  QList<LogTreeItem *> items =
253  qlist_cast(findItems(text, Qt::MatchContains|Qt::MatchWrap, MessageColumn));
254 
255  if (highlight) {
256  /* Deselect all items before highlighting our search results. */
257  deselectAll();
258  foreach (LogTreeItem *item, items) {
259  /* Highlight a matched item */
260  item->setSelected(true);
261  }
262  }
263 
264  /* Return the results, sorted by timestamp */
265  return qlist_sort(items);
266 }