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 */
59MessageLog::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 */
73
74 /* Set tooltips for necessary widgets */
76
77 /* Load the message log's stored settings */
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{
92}
93
94/** Binds events (signals) to actions (slots). */
95void
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. */
150void
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. */
168void
170{
171 ui.retranslateUi(this);
172 setTitle(tr("Message Log"));
173 setToolTips();
174}
175
176/** Loads the saved Message Log settings */
177void
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. */
212void
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. */
239bool
240MessageLog::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. */
259void
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. */
304void
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. */
314void
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 */
329void
330MessageLog::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. */
367void
369{
370 save(ui.listMessages->selectedMessages());
371}
372
373/** Saves all shown messages to a file. */
374void
376{
377 save(ui.listMessages->allMessages());
378}
379
380void
382{
383 ui.listMessages->selectAll();
384}
385
386/** Copies contents of currently selected messages to the 'clipboard'. */
387void
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. */
402void
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 */
413void
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 */
446void
447MessageLog::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. */
474void
476{
477 emit helpRequested("log");
478}
479
#define DEFAULT_MAX_MSG_COUNT
Definition: MessageLog.cpp:38
#define DEFAULT_MSG_FILTER
Definition: MessageLog.cpp:36
#define DEFAULT_LOGFILE
Definition: MessageLog.cpp:46
#define SETTING_LOGFILE
Definition: MessageLog.cpp:35
#define SETTING_ENABLE_LOGFILE
Definition: MessageLog.cpp:34
#define DEFAULT_ENABLE_LOGFILE
Definition: MessageLog.cpp:39
#define SETTING_MAX_MSG_COUNT
Definition: MessageLog.cpp:33
#define SETTING_MSG_FILTER
Definition: MessageLog.cpp:32
#define ADD_TO_FILTER(f, v, b)
Definition: MessageLog.cpp:49
stop errmsg connect(const QHostAddress &address, quint16 port)
bool open(QString filename, QString *errmsg=0)
Definition: LogFile.cpp:50
void close()
Definition: LogFile.cpp:84
bool isOpen()
Definition: LogFile.cpp:94
QString toString() const
Definition: LogTreeItem.cpp:50
Ui::MessageLog ui
Definition: MessageLog.h:96
void saveSettings()
Definition: MessageLog.cpp:260
virtual void retranslateUi()
Definition: MessageLog.cpp:169
TorControl * _torControl
Definition: MessageLog.h:83
QStatusBar * _statusBar
Definition: MessageLog.h:93
void copy()
Definition: MessageLog.cpp:388
void clear()
Definition: MessageLog.cpp:403
bool _enableLogging
Definition: MessageLog.h:89
void selectAll()
Definition: MessageLog.cpp:381
void find()
Definition: MessageLog.cpp:414
void cancelChanges()
Definition: MessageLog.cpp:305
void setToolTips()
Definition: MessageLog.cpp:151
void help()
Definition: MessageLog.cpp:475
void saveAll()
Definition: MessageLog.cpp:375
void log(tc::Severity severity, const QString &msg)
Definition: MessageLog.cpp:447
LogFile _logFile
Definition: MessageLog.h:91
void createActions()
Definition: MessageLog.cpp:96
void save(const QStringList &messages)
Definition: MessageLog.cpp:330
void saveSelected()
Definition: MessageLog.cpp:368
uint _filter
Definition: MessageLog.h:87
MessageLog(QStatusBar *st=0, QWidget *parent=0)
Definition: MessageLog.cpp:59
bool rotateLogFile(const QString &filename)
Definition: MessageLog.cpp:240
void browse()
Definition: MessageLog.cpp:315
void registerLogEvents()
Definition: MessageLog.cpp:213
void loadSettings()
Definition: MessageLog.cpp:178
bool setEvents(QString *errmsg=0)
Definition: TorControl.cpp:707
bool isConnected()
Definition: TorControl.cpp:262
bool setEvent(TorEvents::Event e, bool add=true, bool set=true, QString *errmsg=0)
Definition: TorControl.cpp:697
@ LogError
Definition: TorEvents.h:50
@ LogDebug
Definition: TorEvents.h:46
@ LogNotice
Definition: TorEvents.h:48
static int information(QWidget *parent, QString caption, QString text, int button0, int button1=NoButton, int button2=NoButton)
static int warning(QWidget *parent, QString caption, QString text, int button0, int button1=NoButton, int button2=NoButton)
static void createShortcut(const QKeySequence &key, QWidget *sender, QObject *receiver, const char *slot)
Definition: Vidalia.cpp:402
static TorControl * torControl()
Definition: Vidalia.h:76
QVariant getSetting(QString name, QVariant defaultValue)
Definition: VidaliaTab.cpp:21
void saveSetting(QString name, QVariant value)
Definition: VidaliaTab.cpp:28
void helpRequested(const QString &topic)
void setTitle(const QString &title)
Definition: VidaliaTab.h:36
bool _onTop
Definition: VidaliaTab.h:66
QString p(QString str)
Definition: html.cpp:22
Severity
Definition: tcglobal.h:69
@ DebugSeverity
Definition: tcglobal.h:71
@ NoticeSeverity
Definition: tcglobal.h:73
@ ErrorSeverity
Definition: tcglobal.h:75
@ WarnSeverity
Definition: tcglobal.h:74
@ InfoSeverity
Definition: tcglobal.h:72