Vidalia  0.3.1
MessageLog.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 MessageLog.cpp
13 ** \brief Displays log messages and message log settings
14 */
15 
16 #include "MessageLog.h"
17 #include "StatusEventItem.h"
18 #include "Vidalia.h"
19 #include "VMessageBox.h"
20 
21 #include "html.h"
22 
23 #include <QMessageBox>
24 #include <QFileDialog>
25 #include <QInputDialog>
26 #include <QMessageBox>
27 #include <QClipboard>
28 
29 #include <QToolBar>
30 
31 /* Message log settings */
32 #define SETTING_MSG_FILTER "MessageFilter"
33 #define SETTING_MAX_MSG_COUNT "MaxMsgCount"
34 #define SETTING_ENABLE_LOGFILE "EnableLogFile"
35 #define SETTING_LOGFILE "LogFile"
36 #define DEFAULT_MSG_FILTER \
37  (tc::ErrorSeverity|tc::WarnSeverity|tc::NoticeSeverity)
38 #define DEFAULT_MAX_MSG_COUNT 50
39 #define DEFAULT_ENABLE_LOGFILE false
40 #if defined(Q_OS_WIN32)
41 
42 /** Default location of the log file to which log messages will be written. */
43 #define DEFAULT_LOGFILE \
44  (win32_program_files_folder()+"\\Tor\\tor-log.txt")
45 #else
46 #define DEFAULT_LOGFILE (QDir::homePath() + "/.tor/tor-log.txt")
47 #endif
48 
49 #define ADD_TO_FILTER(f,v,b) (f = ((b) ? ((f) | (v)) : ((f) & ~(v))))
50 
51 
52 /** Constructor. The constructor will load the message log's settings from
53  * VidaliSettings and register for log events according to the most recently
54  * set severity filter.
55  * \param torControl A TorControl object used to register for log events.
56  * \param parent The parent widget of this MessageLog object.
57  * \param flags Any desired window creation flags.
58  */
59 MessageLog::MessageLog(QStatusBar *st, QWidget *parent)
60 : VidaliaTab(tr("Message Log"), "MessageLog", parent),
61  _statusBar(st)
62 {
63  /* Invoke Qt Designer generated QObject setup routine */
64  ui.setupUi(this);
65 
66  /* Create necessary Message Log QObjects */
68  connect(_torControl, SIGNAL(logMessage(tc::Severity, QString)),
69  this, SLOT(log(tc::Severity, QString)));
70 
71  /* Bind events to actions */
72  createActions();
73 
74  /* Set tooltips for necessary widgets */
75  setToolTips();
76 
77  /* Load the message log's stored settings */
78  loadSettings();
79 
80  /* Sort in ascending chronological order */
81  ui.listMessages->sortItems(LogTreeWidget::TimeColumn,
82  Qt::AscendingOrder);
83 
84  ui.frmSettings->setVisible(false);
85 }
86 
87 /** Default Destructor. Simply frees up any memory allocated for member
88  * variables. */
90 {
91  _logFile.close();
92 }
93 
94 /** Binds events (signals) to actions (slots). */
95 void
97 {
98  connect(ui.actionSave_Selected, SIGNAL(triggered()),
99  this, SLOT(saveSelected()));
100 
101  connect(ui.actionSave_All, SIGNAL(triggered()),
102  this, SLOT(saveAll()));
103 
104  connect(ui.actionSelect_All, SIGNAL(triggered()),
105  this, SLOT(selectAll()));
106 
107  connect(ui.actionCopy, SIGNAL(triggered()),
108  this, SLOT(copy()));
109 
110  connect(ui.actionFind, SIGNAL(triggered()),
111  this, SLOT(find()));
112 
113  connect(ui.actionClear, SIGNAL(triggered()),
114  this, SLOT(clear()));
115 
116  connect(ui.actionHelp, SIGNAL(triggered()),
117  this, SLOT(help()));
118 
119  connect(ui.btnSaveSettings, SIGNAL(clicked()),
120  this, SLOT(saveSettings()));
121 
122  connect(ui.btnCancelSettings, SIGNAL(clicked()),
123  this, SLOT(cancelChanges()));
124 
125  connect(ui.btnBrowse, SIGNAL(clicked()),
126  this, SLOT(browse()));
127 
128  QToolBar *tb = new QToolBar(tr("toolbar"));
129  tb->addAction(ui.actionSave_All);
130  tb->addAction(ui.actionSave_Selected);
131  tb->addAction(ui.actionCopy);
132  tb->addAction(ui.actionSelect_All);
133  tb->addAction(ui.actionFind);
134  tb->addAction(ui.actionClear);
135  tb->addAction(ui.actionSettings);
136  tb->addAction(ui.actionHelp);
137 
138  tb->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
139  ui.verticalLayout->insertWidget(0, tb);
140 
141 #if defined(Q_WS_MAC)
142  ui.actionHelp->setShortcut(QString("Ctrl+?"));
143 #endif
144  ui.actionClose->setShortcut(QString("Esc"));
145  Vidalia::createShortcut("Ctrl+W", this, ui.actionClose, SLOT(trigger()));
146 }
147 
148 /** Set tooltips for Message Filter checkboxes in code because they are long
149  * and Designer wouldn't let us insert newlines into the text. */
150 void
152 {
153  ui.chkTorErr->setToolTip(tr("Messages that appear when something has \n"
154  "gone very wrong and Tor cannot proceed."));
155  ui.chkTorWarn->setToolTip(tr("Messages that only appear when \n"
156  "something has gone wrong with Tor."));
157  ui.chkTorNote->setToolTip(tr("Messages that appear infrequently \n"
158  "during normal Tor operation and are \n"
159  "not considered errors, but you may \n"
160  "care about."));
161  ui.chkTorInfo->setToolTip(tr("Messages that appear frequently \n"
162  "during normal Tor operation."));
163  ui.chkTorDebug->setToolTip(tr("Hyper-verbose messages primarily of \n"
164  "interest to Tor developers."));
165 }
166 
167 /** Called when the user changes the UI translation. */
168 void
170 {
171  ui.retranslateUi(this);
172  setTitle(tr("Message Log"));
173  setToolTips();
174 }
175 
176 /** Loads the saved Message Log settings */
177 void
179 {
180  /* Set Max Count widget */
181  uint maxMsgCount = getSetting(SETTING_MAX_MSG_COUNT,
182  DEFAULT_MAX_MSG_COUNT).toUInt();
183  ui.spnbxMaxCount->setValue(maxMsgCount);
184  ui.listMessages->setMaximumMessageCount(maxMsgCount);
185 
186  /* Set whether or not logging to file is enabled */
188  DEFAULT_ENABLE_LOGFILE).toBool();
189  QString logfile = getSetting(SETTING_LOGFILE,
190  DEFAULT_LOGFILE).toString();
191  ui.lineFile->setText(QDir::convertSeparators(logfile));
192  rotateLogFile(logfile);
193  ui.chkEnableLogFile->setChecked(_logFile.isOpen());
194 
195  /* Set the checkboxes accordingly */
197  ui.chkTorErr->setChecked(_filter & tc::ErrorSeverity);
198  ui.chkTorWarn->setChecked(_filter & tc::WarnSeverity);
199  ui.chkTorNote->setChecked(_filter & tc::NoticeSeverity);
200  ui.chkTorInfo->setChecked(_filter & tc::InfoSeverity);
201  ui.chkTorDebug->setChecked(_filter & tc::DebugSeverity);
203 
204  /* Filter the message log */
205  QApplication::setOverrideCursor(Qt::WaitCursor);
206  ui.listMessages->filter(_filter);
207  QApplication::restoreOverrideCursor();
208 }
209 
210 /** Attempts to register the selected message filter with Tor and displays an
211  * error if setting the events fails. */
212 void
214 {
217  _filter & tc::DebugSeverity, false);
219  _filter & tc::InfoSeverity, false);
221  _filter & tc::NoticeSeverity, false);
223  _filter & tc::WarnSeverity, false);
225  _filter & tc::ErrorSeverity, false);
226 
227  QString errmsg;
228  if (_torControl->isConnected() && !_torControl->setEvents(&errmsg)) {
229  VMessageBox::warning(this, tr("Error Setting Filter"),
230  p(tr("Vidalia was unable to register for Tor's log events.")) + p(errmsg),
232  }
233 }
234 
235 /** Opens a log file if necessary, or closes it if logging is disabled. If a
236  * log file is already opened and a new filename is specified, then the log
237  * file will be rotated to the new filename. In the case that the new filename
238  * can not be openend, the old file will remain open and writable. */
239 bool
240 MessageLog::rotateLogFile(const QString &filename)
241 {
242  QString errmsg;
243  if (_enableLogging) {
244  if (!_logFile.open(filename, &errmsg)) {
245  VMessageBox::warning(this, tr("Error Opening Log File"),
246  p(tr("Vidalia was unable to open the specified log file."))+p(errmsg),
248  return false;
249  }
250  } else {
251  /* Close the log file. */
252  _logFile.close();
253  }
254  return true;
255 }
256 
257 /** Saves the Message Log settings, adjusts the message list if required, and
258  * then hides the settings frame. */
259 void
261 {
262  /* Update the logging status */
263  _enableLogging = ui.chkEnableLogFile->isChecked();
264  if (_enableLogging && ui.lineFile->text().isEmpty()) {
265  /* The user chose to enable logging messages to a file, but didn't specify
266  * a log filename. */
267  VMessageBox::warning(this, tr("Log Filename Required"),
268  p(tr("You must enter a filename to be able to save log "
269  "messages to a file.")), VMessageBox::Ok);
270  return;
271  }
272  if (rotateLogFile(ui.lineFile->text())) {
273  saveSetting(SETTING_LOGFILE, ui.lineFile->text());
275  }
276  ui.lineFile->setText(QDir::convertSeparators(ui.lineFile->text()));
277  ui.chkEnableLogFile->setChecked(_logFile.isOpen());
278 
279  /* Update the maximum displayed item count */
280  saveSetting(SETTING_MAX_MSG_COUNT, ui.spnbxMaxCount->value());
281  ui.listMessages->setMaximumMessageCount(ui.spnbxMaxCount->value());
282 
283  /* Save message filter and refilter the list */
284  uint filter = 0;
285  ADD_TO_FILTER(filter, tc::ErrorSeverity, ui.chkTorErr->isChecked());
286  ADD_TO_FILTER(filter, tc::WarnSeverity, ui.chkTorWarn->isChecked());
287  ADD_TO_FILTER(filter, tc::NoticeSeverity, ui.chkTorNote->isChecked());
288  ADD_TO_FILTER(filter, tc::InfoSeverity, ui.chkTorInfo->isChecked());
289  ADD_TO_FILTER(filter, tc::DebugSeverity, ui.chkTorDebug->isChecked());
292 
293  /* Filter the message log */
294  QApplication::setOverrideCursor(Qt::WaitCursor);
295  ui.listMessages->filter(_filter);
296  QApplication::restoreOverrideCursor();
297 
298  /* Hide the settings frame and reset toggle button*/
299  ui.actionSettings->toggle();
300 }
301 
302 /** Simply restores the previously saved settings and hides the settings
303  * frame. */
304 void
306 {
307  /* Hide the settings frame and reset toggle button */
308  ui.actionSettings->toggle();
309  /* Reload the settings */
310  loadSettings();
311 }
312 
313 /** Called when the user clicks "Browse" to select a new log file. */
314 void
316 {
317  /* Strangely, QFileDialog returns a non seperator converted path. */
318  QString filename = QDir::convertSeparators(
319  QFileDialog::getSaveFileName(this,
320  tr("Select Log File"), "tor-log.txt"));
321  if (!filename.isEmpty()) {
322  ui.lineFile->setText(filename);
323  }
324 }
325 
326 /** Saves the given list of items to a file.
327  * \param items A list of log message items to save.
328  */
329 void
330 MessageLog::save(const QStringList &messages)
331 {
332  if (!messages.size()) {
333  return;
334  }
335 
336  QString fileName = QFileDialog::getSaveFileName(this,
337  tr("Save Log Messages"),
338  "VidaliaLog-" +
339  QDateTime::currentDateTime().toString("MM.dd.yyyy")
340  + ".txt", tr("Text Files (*.txt)"));
341 
342  /* If the choose to save */
343  if (!fileName.isEmpty()) {
344  LogFile logFile;
345  QString errmsg;
346 
347  /* If can't write to file, show error message */
348  if (!logFile.open(fileName, &errmsg)) {
349  VMessageBox::warning(this, tr("Vidalia"),
350  p(tr("Cannot write file %1\n\n%2."))
351  .arg(fileName)
352  .arg(errmsg),
354  return;
355  }
356 
357  /* Write out the message log to the file */
358  QApplication::setOverrideCursor(Qt::WaitCursor);
359  foreach (QString msg, messages) {
360  logFile << msg << "\n";
361  }
362  QApplication::restoreOverrideCursor();
363  }
364 }
365 
366 /** Saves currently selected messages to a file. */
367 void
369 {
370  save(ui.listMessages->selectedMessages());
371 }
372 
373 /** Saves all shown messages to a file. */
374 void
376 {
377  save(ui.listMessages->allMessages());
378 }
379 
380 void
382 {
383  ui.listMessages->selectAll();
384 }
385 
386 /** Copies contents of currently selected messages to the 'clipboard'. */
387 void
389 {
390  QString contents;
391 
392  contents = ui.listMessages->selectedMessages().join("\n");
393 
394  if (!contents.isEmpty()) {
395  /* Copy the selected messages to the clipboard */
396  QApplication::clipboard()->setText(contents);
397  }
398 }
399 
400 /** Clears all log messages or status notifications, depending on which tab
401  * is currently visible. */
402 void
404 {
405  ui.listMessages->clearMessages();
406 }
407 
408 /** Prompts the user for a search string. If the search string is not found in
409  * any of the currently displayed log entires, then a message will be
410  * displayed for the user informing them that no matches were found.
411  * \sa search()
412  */
413 void
415 {
416  bool ok;
417  QString text = QInputDialog::getText(this, tr("Find in Message Log"),
418  tr("Find:"), QLineEdit::Normal, QString(), &ok);
419 
420  if (ok && !text.isEmpty()) {
421  QTreeWidget *tree;
422  QTreeWidgetItem *firstItem = 0;
423 
424  /* Pick the right tree widget to search based on the current tab */
425  QList<LogTreeItem *> results = ui.listMessages->find(text, true);
426  if (results.size() > 0) {
427  tree = ui.listMessages;
428  firstItem = dynamic_cast<QTreeWidgetItem *>(results.at(0));
429  }
430 
431  if (! firstItem) {
432  VMessageBox::information(this, tr("Not Found"),
433  p(tr("Search found 0 matches.")),
435  } else {
436  tree->scrollToItem(firstItem);
437  }
438  }
439 }
440 
441 /** Writes a message to the Message History and tags it with
442  * the proper date, time and type.
443  * \param type The message's severity type.
444  * \param message The log message to be added.
445  */
446 void
447 MessageLog::log(tc::Severity type, const QString &message)
448 {
449  setUpdatesEnabled(false);
450  /* Only add the message if it's not being filtered out */
451  if (_filter & (uint)type) {
452  /* Add the message to the list and scroll to it if necessary. */
453  LogTreeItem *item = ui.listMessages->log(type, message);
454 
455  /* This is a workaround to force Qt to update the statusbar text (if any
456  * is currently displayed) to reflect the new message added. */
457  QString currStatusTip;
458  if(_statusBar && _onTop)
459  currStatusTip = _statusBar->currentMessage();
460  if (!currStatusTip.isEmpty()) {
461  currStatusTip = ui.listMessages->statusTip();
462  _statusBar->showMessage(currStatusTip);
463  }
464 
465  /* If we're saving log messages to a file, go ahead and do that now */
466  if (_enableLogging) {
467  _logFile << item->toString() << "\n";
468  }
469  }
470  setUpdatesEnabled(true);
471 }
472 
473 /** Displays help information about the message log. */
474 void
476 {
477  emit helpRequested("log");
478 }
479 
QString p(QString str)
Definition: html.cpp:22
void registerLogEvents()
Definition: MessageLog.cpp:213
void saveSetting(QString name, QVariant value)
Definition: VidaliaTab.cpp:28
bool open(QString filename, QString *errmsg=0)
Definition: LogFile.cpp:50
void loadSettings()
Definition: MessageLog.cpp:178
TorControl * _torControl
Definition: MessageLog.h:83
#define SETTING_MSG_FILTER
Definition: MessageLog.cpp:32
uint _filter
Definition: MessageLog.h:87
void saveSelected()
Definition: MessageLog.cpp:368
QString toString() const
Definition: LogTreeItem.cpp:50
#define DEFAULT_MAX_MSG_COUNT
Definition: MessageLog.cpp:38
void log(tc::Severity severity, const QString &msg)
Definition: MessageLog.cpp:447
Ui::MessageLog ui
Definition: MessageLog.h:96
void save(const QStringList &messages)
Definition: MessageLog.cpp:330
#define SETTING_LOGFILE
Definition: MessageLog.cpp:35
#define SETTING_MAX_MSG_COUNT
Definition: MessageLog.cpp:33
bool setEvent(TorEvents::Event e, bool add=true, bool set=true, QString *errmsg=0)
Definition: TorControl.cpp:697
void browse()
Definition: MessageLog.cpp:315
bool setEvents(QString *errmsg=0)
Definition: TorControl.cpp:707
void clear()
Definition: MessageLog.cpp:403
void find()
Definition: MessageLog.cpp:414
void copy()
Definition: MessageLog.cpp:388
bool rotateLogFile(const QString &filename)
Definition: MessageLog.cpp:240
static int information(QWidget *parent, QString caption, QString text, int button0, int button1=NoButton, int button2=NoButton)
void saveAll()
Definition: MessageLog.cpp:375
bool isOpen()
Definition: LogFile.cpp:94
bool _enableLogging
Definition: MessageLog.h:89
void help()
Definition: MessageLog.cpp:475
static TorControl * torControl()
Definition: Vidalia.h:76
QVariant getSetting(QString name, QVariant defaultValue)
Definition: VidaliaTab.cpp:21
stop errmsg connect(const QHostAddress &address, quint16 port)
#define DEFAULT_MSG_FILTER
Definition: MessageLog.cpp:36
bool _onTop
Definition: VidaliaTab.h:66
#define DEFAULT_ENABLE_LOGFILE
Definition: MessageLog.cpp:39
void saveSettings()
Definition: MessageLog.cpp:260
#define ADD_TO_FILTER(f, v, b)
Definition: MessageLog.cpp:49
void selectAll()
Definition: MessageLog.cpp:381
Severity
Definition: tcglobal.h:69
void cancelChanges()
Definition: MessageLog.cpp:305
static int warning(QWidget *parent, QString caption, QString text, int button0, int button1=NoButton, int button2=NoButton)
QStatusBar * _statusBar
Definition: MessageLog.h:93
MessageLog(QStatusBar *st=0, QWidget *parent=0)
Definition: MessageLog.cpp:59
void setToolTips()
Definition: MessageLog.cpp:151
void close()
Definition: LogFile.cpp:84
bool isConnected()
Definition: TorControl.cpp:262
LogFile _logFile
Definition: MessageLog.h:91
virtual void retranslateUi()
Definition: MessageLog.cpp:169
#define DEFAULT_LOGFILE
Definition: MessageLog.cpp:46
void setTitle(const QString &title)
Definition: VidaliaTab.h:36
void helpRequested(const QString &topic)
#define SETTING_ENABLE_LOGFILE
Definition: MessageLog.cpp:34
static void createShortcut(const QKeySequence &key, QWidget *sender, QObject *receiver, const char *slot)
Definition: Vidalia.cpp:402
void createActions()
Definition: MessageLog.cpp:96