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 MessageLog.cpp 00013 ** \version $Id: MessageLog.cpp 4091 2009-08-30 03:10:07Z edmanm $ 00014 ** \brief Displays log messages and message log settings 00015 */ 00016 00017 #include "MessageLog.h" 00018 #include "StatusEventItem.h" 00019 #include "Vidalia.h" 00020 #include "VMessageBox.h" 00021 00022 #include "html.h" 00023 00024 #include <QMessageBox> 00025 #include <QFileDialog> 00026 #include <QInputDialog> 00027 #include <QMessageBox> 00028 #include <QClipboard> 00029 00030 /* Message log settings */ 00031 #define SETTING_MSG_FILTER "MessageFilter" 00032 #define SETTING_MAX_MSG_COUNT "MaxMsgCount" 00033 #define SETTING_ENABLE_LOGFILE "EnableLogFile" 00034 #define SETTING_LOGFILE "LogFile" 00035 #define DEFAULT_MSG_FILTER \ 00036 (tc::ErrorSeverity|tc::WarnSeverity|tc::NoticeSeverity) 00037 #define DEFAULT_MAX_MSG_COUNT 50 00038 #define DEFAULT_ENABLE_LOGFILE false 00039 #if defined(Q_OS_WIN32) 00040 00041 /** Default location of the log file to which log messages will be written. */ 00042 #define DEFAULT_LOGFILE \ 00043 (win32_program_files_folder()+"\\Tor\\tor-log.txt") 00044 #else 00045 #define DEFAULT_LOGFILE ("/var/log/tor/tor.log") 00046 #endif 00047 00048 #define ADD_TO_FILTER(f,v,b) (f = ((b) ? ((f) | (v)) : ((f) & ~(v)))) 00049 00050 00051 /** Constructor. The constructor will load the message log's settings from 00052 * VidaliSettings and register for log events according to the most recently 00053 * set severity filter. 00054 * \param torControl A TorControl object used to register for log events. 00055 * \param parent The parent widget of this MessageLog object. 00056 * \param flags Any desired window creation flags. 00057 */ 00058 MessageLog::MessageLog(QWidget *parent, Qt::WFlags flags) 00059 : VidaliaWindow("MessageLog", parent, flags) 00060 { 00061 /* Invoke Qt Designer generated QObject setup routine */ 00062 ui.setupUi(this); 00063 00064 /* Create necessary Message Log QObjects */ 00065 _torControl = Vidalia::torControl(); 00066 connect(_torControl, SIGNAL(logMessage(tc::Severity, QString)), 00067 this, SLOT(log(tc::Severity, QString))); 00068 00069 /* Bind events to actions */ 00070 createActions(); 00071 00072 /* Set tooltips for necessary widgets */ 00073 setToolTips(); 00074 00075 /* Load the message log's stored settings */ 00076 loadSettings(); 00077 00078 /* Sort in ascending chronological order */ 00079 ui.listMessages->sortItems(LogTreeWidget::TimeColumn, 00080 Qt::AscendingOrder); 00081 ui.listNotifications->sortItems(0, Qt::AscendingOrder); 00082 } 00083 00084 /** Default Destructor. Simply frees up any memory allocated for member 00085 * variables. */ 00086 MessageLog::~MessageLog() 00087 { 00088 _logFile.close(); 00089 } 00090 00091 /** Binds events (signals) to actions (slots). */ 00092 void 00093 MessageLog::createActions() 00094 { 00095 connect(ui.actionSave_Selected, SIGNAL(triggered()), 00096 this, SLOT(saveSelected())); 00097 00098 connect(ui.actionSave_All, SIGNAL(triggered()), 00099 this, SLOT(saveAll())); 00100 00101 connect(ui.actionSelect_All, SIGNAL(triggered()), 00102 this, SLOT(selectAll())); 00103 00104 connect(ui.actionCopy, SIGNAL(triggered()), 00105 this, SLOT(copy())); 00106 00107 connect(ui.actionFind, SIGNAL(triggered()), 00108 this, SLOT(find())); 00109 00110 connect(ui.actionClear, SIGNAL(triggered()), 00111 this, SLOT(clear())); 00112 00113 connect(ui.actionHelp, SIGNAL(triggered()), 00114 this, SLOT(help())); 00115 00116 connect(ui.btnSaveSettings, SIGNAL(clicked()), 00117 this, SLOT(saveSettings())); 00118 00119 connect(ui.btnCancelSettings, SIGNAL(clicked()), 00120 this, SLOT(cancelChanges())); 00121 00122 connect(ui.btnBrowse, SIGNAL(clicked()), 00123 this, SLOT(browse())); 00124 00125 #if defined(Q_WS_MAC) 00126 ui.actionHelp->setShortcut(QString("Ctrl+?")); 00127 #endif 00128 ui.actionClose->setShortcut(QString("Esc")); 00129 Vidalia::createShortcut("Ctrl+W", this, ui.actionClose, SLOT(trigger())); 00130 } 00131 00132 /** Set tooltips for Message Filter checkboxes in code because they are long 00133 * and Designer wouldn't let us insert newlines into the text. */ 00134 void 00135 MessageLog::setToolTips() 00136 { 00137 ui.chkTorErr->setToolTip(tr("Messages that appear when something has \n" 00138 "gone very wrong and Tor cannot proceed.")); 00139 ui.chkTorWarn->setToolTip(tr("Messages that only appear when \n" 00140 "something has gone wrong with Tor.")); 00141 ui.chkTorNote->setToolTip(tr("Messages that appear infrequently \n" 00142 "during normal Tor operation and are \n" 00143 "not considered errors, but you may \n" 00144 "care about.")); 00145 ui.chkTorInfo->setToolTip(tr("Messages that appear frequently \n" 00146 "during normal Tor operation.")); 00147 ui.chkTorDebug->setToolTip(tr("Hyper-verbose messages primarily of \n" 00148 "interest to Tor developers.")); 00149 } 00150 00151 /** Called when the user changes the UI translation. */ 00152 void 00153 MessageLog::retranslateUi() 00154 { 00155 ui.retranslateUi(this); 00156 setToolTips(); 00157 } 00158 00159 /** Loads the saved Message Log settings */ 00160 void 00161 MessageLog::loadSettings() 00162 { 00163 /* Set Max Count widget */ 00164 uint maxMsgCount = getSetting(SETTING_MAX_MSG_COUNT, 00165 DEFAULT_MAX_MSG_COUNT).toUInt(); 00166 ui.spnbxMaxCount->setValue(maxMsgCount); 00167 ui.listMessages->setMaximumMessageCount(maxMsgCount); 00168 ui.listNotifications->setMaximumItemCount(maxMsgCount); 00169 00170 /* Set whether or not logging to file is enabled */ 00171 _enableLogging = getSetting(SETTING_ENABLE_LOGFILE, 00172 DEFAULT_ENABLE_LOGFILE).toBool(); 00173 QString logfile = getSetting(SETTING_LOGFILE, 00174 DEFAULT_LOGFILE).toString(); 00175 ui.lineFile->setText(QDir::convertSeparators(logfile)); 00176 rotateLogFile(logfile); 00177 ui.chkEnableLogFile->setChecked(_logFile.isOpen()); 00178 00179 /* Set the checkboxes accordingly */ 00180 _filter = getSetting(SETTING_MSG_FILTER, DEFAULT_MSG_FILTER).toUInt(); 00181 ui.chkTorErr->setChecked(_filter & tc::ErrorSeverity); 00182 ui.chkTorWarn->setChecked(_filter & tc::WarnSeverity); 00183 ui.chkTorNote->setChecked(_filter & tc::NoticeSeverity); 00184 ui.chkTorInfo->setChecked(_filter & tc::InfoSeverity); 00185 ui.chkTorDebug->setChecked(_filter & tc::DebugSeverity); 00186 registerLogEvents(); 00187 00188 /* Filter the message log */ 00189 QApplication::setOverrideCursor(Qt::WaitCursor); 00190 ui.listMessages->filter(_filter); 00191 QApplication::restoreOverrideCursor(); 00192 } 00193 00194 /** Attempts to register the selected message filter with Tor and displays an 00195 * error if setting the events fails. */ 00196 void 00197 MessageLog::registerLogEvents() 00198 { 00199 _filter = getSetting(SETTING_MSG_FILTER, DEFAULT_MSG_FILTER).toUInt(); 00200 _torControl->setEvent(TorEvents::LogDebug, 00201 _filter & tc::DebugSeverity, false); 00202 _torControl->setEvent(TorEvents::LogInfo, 00203 _filter & tc::InfoSeverity, false); 00204 _torControl->setEvent(TorEvents::LogNotice, 00205 _filter & tc::NoticeSeverity, false); 00206 _torControl->setEvent(TorEvents::LogWarn, 00207 _filter & tc::WarnSeverity, false); 00208 _torControl->setEvent(TorEvents::LogError, 00209 _filter & tc::ErrorSeverity, false); 00210 00211 QString errmsg; 00212 if (_torControl->isConnected() && !_torControl->setEvents(&errmsg)) { 00213 VMessageBox::warning(this, tr("Error Setting Filter"), 00214 p(tr("Vidalia was unable to register for Tor's log events.")) + p(errmsg), 00215 VMessageBox::Ok); 00216 } 00217 } 00218 00219 /** Opens a log file if necessary, or closes it if logging is disabled. If a 00220 * log file is already opened and a new filename is specified, then the log 00221 * file will be rotated to the new filename. In the case that the new filename 00222 * can not be openend, the old file will remain open and writable. */ 00223 bool 00224 MessageLog::rotateLogFile(const QString &filename) 00225 { 00226 QString errmsg; 00227 if (_enableLogging) { 00228 if (!_logFile.open(filename, &errmsg)) { 00229 VMessageBox::warning(this, tr("Error Opening Log File"), 00230 p(tr("Vidalia was unable to open the specified log file."))+p(errmsg), 00231 VMessageBox::Ok); 00232 return false; 00233 } 00234 } else { 00235 /* Close the log file. */ 00236 _logFile.close(); 00237 } 00238 return true; 00239 } 00240 00241 /** Saves the Message Log settings, adjusts the message list if required, and 00242 * then hides the settings frame. */ 00243 void 00244 MessageLog::saveSettings() 00245 { 00246 /* Update the logging status */ 00247 _enableLogging = ui.chkEnableLogFile->isChecked(); 00248 if (_enableLogging && ui.lineFile->text().isEmpty()) { 00249 /* The user chose to enable logging messages to a file, but didn't specify 00250 * a log filename. */ 00251 VMessageBox::warning(this, tr("Log Filename Required"), 00252 p(tr("You must enter a filename to be able to save log " 00253 "messages to a file.")), VMessageBox::Ok); 00254 return; 00255 } 00256 if (rotateLogFile(ui.lineFile->text())) { 00257 saveSetting(SETTING_LOGFILE, ui.lineFile->text()); 00258 saveSetting(SETTING_ENABLE_LOGFILE, _logFile.isOpen()); 00259 } 00260 ui.lineFile->setText(QDir::convertSeparators(ui.lineFile->text())); 00261 ui.chkEnableLogFile->setChecked(_logFile.isOpen()); 00262 00263 /* Update the maximum displayed item count */ 00264 saveSetting(SETTING_MAX_MSG_COUNT, ui.spnbxMaxCount->value()); 00265 ui.listMessages->setMaximumMessageCount(ui.spnbxMaxCount->value()); 00266 ui.listNotifications->setMaximumItemCount(ui.spnbxMaxCount->value()); 00267 00268 /* Save message filter and refilter the list */ 00269 uint filter = 0; 00270 ADD_TO_FILTER(filter, tc::ErrorSeverity, ui.chkTorErr->isChecked()); 00271 ADD_TO_FILTER(filter, tc::WarnSeverity, ui.chkTorWarn->isChecked()); 00272 ADD_TO_FILTER(filter, tc::NoticeSeverity, ui.chkTorNote->isChecked()); 00273 ADD_TO_FILTER(filter, tc::InfoSeverity, ui.chkTorInfo->isChecked()); 00274 ADD_TO_FILTER(filter, tc::DebugSeverity, ui.chkTorDebug->isChecked()); 00275 saveSetting(SETTING_MSG_FILTER, filter); 00276 registerLogEvents(); 00277 00278 /* Filter the message log */ 00279 QApplication::setOverrideCursor(Qt::WaitCursor); 00280 ui.listMessages->filter(_filter); 00281 QApplication::restoreOverrideCursor(); 00282 00283 /* Hide the settings frame and reset toggle button*/ 00284 ui.actionSettings->toggle(); 00285 } 00286 00287 /** Simply restores the previously saved settings and hides the settings 00288 * frame. */ 00289 void 00290 MessageLog::cancelChanges() 00291 { 00292 /* Hide the settings frame and reset toggle button */ 00293 ui.actionSettings->toggle(); 00294 /* Reload the settings */ 00295 loadSettings(); 00296 } 00297 00298 /** Called when the user clicks "Browse" to select a new log file. */ 00299 void 00300 MessageLog::browse() 00301 { 00302 /* Strangely, QFileDialog returns a non seperator converted path. */ 00303 QString filename = QDir::convertSeparators( 00304 QFileDialog::getSaveFileName(this, 00305 tr("Select Log File"), "tor-log.txt")); 00306 if (!filename.isEmpty()) { 00307 ui.lineFile->setText(filename); 00308 } 00309 } 00310 00311 /** Saves the given list of items to a file. 00312 * \param items A list of log message items to save. 00313 */ 00314 void 00315 MessageLog::save(const QStringList &messages) 00316 { 00317 if (!messages.size()) { 00318 return; 00319 } 00320 00321 QString fileName = QFileDialog::getSaveFileName(this, 00322 tr("Save Log Messages"), 00323 "VidaliaLog-" + 00324 QDateTime::currentDateTime().toString("MM.dd.yyyy") 00325 + ".txt", tr("Text Files (*.txt)")); 00326 00327 /* If the choose to save */ 00328 if (!fileName.isEmpty()) { 00329 LogFile logFile; 00330 QString errmsg; 00331 00332 /* If can't write to file, show error message */ 00333 if (!logFile.open(fileName, &errmsg)) { 00334 VMessageBox::warning(this, tr("Vidalia"), 00335 p(tr("Cannot write file %1\n\n%2.")) 00336 .arg(fileName) 00337 .arg(errmsg), 00338 VMessageBox::Ok); 00339 return; 00340 } 00341 00342 /* Write out the message log to the file */ 00343 QApplication::setOverrideCursor(Qt::WaitCursor); 00344 foreach (QString msg, messages) { 00345 logFile << msg << "\n"; 00346 } 00347 QApplication::restoreOverrideCursor(); 00348 } 00349 } 00350 00351 /** Saves currently selected messages to a file. */ 00352 void 00353 MessageLog::saveSelected() 00354 { 00355 if (ui.tabWidget->currentIndex() == 0) 00356 save(ui.listNotifications->selectedEvents()); 00357 else 00358 save(ui.listMessages->selectedMessages()); 00359 } 00360 00361 /** Saves all shown messages to a file. */ 00362 void 00363 MessageLog::saveAll() 00364 { 00365 if (ui.tabWidget->currentIndex() == 0) 00366 save(ui.listNotifications->allEvents()); 00367 else 00368 save(ui.listMessages->allMessages()); 00369 } 00370 00371 void 00372 MessageLog::selectAll() 00373 { 00374 if (ui.tabWidget->currentIndex() == 0) 00375 ui.listNotifications->selectAll(); 00376 else 00377 ui.listMessages->selectAll(); 00378 } 00379 00380 /** Copies contents of currently selected messages to the 'clipboard'. */ 00381 void 00382 MessageLog::copy() 00383 { 00384 QString contents; 00385 00386 if (ui.tabWidget->currentIndex() == 0) 00387 contents = ui.listNotifications->selectedEvents().join("\n"); 00388 else 00389 contents = ui.listMessages->selectedMessages().join("\n"); 00390 00391 if (!contents.isEmpty()) { 00392 /* Copy the selected messages to the clipboard */ 00393 QApplication::clipboard()->setText(contents); 00394 } 00395 } 00396 00397 /** Clears all log messages or status notifications, depending on which tab 00398 * is currently visible. */ 00399 void 00400 MessageLog::clear() 00401 { 00402 if (ui.tabWidget->currentIndex() == 0) 00403 ui.listNotifications->clear(); 00404 else 00405 ui.listMessages->clearMessages(); 00406 } 00407 00408 /** Prompts the user for a search string. If the search string is not found in 00409 * any of the currently displayed log entires, then a message will be 00410 * displayed for the user informing them that no matches were found. 00411 * \sa search() 00412 */ 00413 void 00414 MessageLog::find() 00415 { 00416 bool ok; 00417 QString text = QInputDialog::getText(this, tr("Find in Message Log"), 00418 tr("Find:"), QLineEdit::Normal, QString(), &ok); 00419 00420 if (ok && !text.isEmpty()) { 00421 QTreeWidget *tree; 00422 QTreeWidgetItem *firstItem = 0; 00423 00424 /* Pick the right tree widget to search based on the current tab */ 00425 if (ui.tabWidget->currentIndex() == 0) { 00426 QList<StatusEventItem *> results = ui.listNotifications->find(text, true); 00427 if (results.size() > 0) { 00428 tree = ui.listNotifications; 00429 firstItem = dynamic_cast<QTreeWidgetItem *>(results.at(0)); 00430 } 00431 } else { 00432 QList<LogTreeItem *> results = ui.listMessages->find(text, true); 00433 if (results.size() > 0) { 00434 tree = ui.listMessages; 00435 firstItem = dynamic_cast<QTreeWidgetItem *>(results.at(0)); 00436 } 00437 } 00438 00439 if (! firstItem) { 00440 VMessageBox::information(this, tr("Not Found"), 00441 p(tr("Search found 0 matches.")), 00442 VMessageBox::Ok); 00443 } else { 00444 tree->scrollToItem(firstItem); 00445 } 00446 } 00447 } 00448 00449 /** Writes a message to the Message History and tags it with 00450 * the proper date, time and type. 00451 * \param type The message's severity type. 00452 * \param message The log message to be added. 00453 */ 00454 void 00455 MessageLog::log(tc::Severity type, const QString &message) 00456 { 00457 /* Only add the message if it's not being filtered out */ 00458 if (_filter & (uint)type) { 00459 /* Add the message to the list and scroll to it if necessary. */ 00460 LogTreeItem *item = ui.listMessages->log(type, message); 00461 00462 /* This is a workaround to force Qt to update the statusbar text (if any 00463 * is currently displayed) to reflect the new message added. */ 00464 QString currStatusTip = ui.statusbar->currentMessage(); 00465 if (!currStatusTip.isEmpty()) { 00466 currStatusTip = ui.listMessages->statusTip(); 00467 ui.statusbar->showMessage(currStatusTip); 00468 } 00469 00470 /* If we're saving log messages to a file, go ahead and do that now */ 00471 if (_enableLogging) { 00472 _logFile << item->toString() << "\n"; 00473 } 00474 } 00475 } 00476 00477 /** Displays help information about the message log. */ 00478 void 00479 MessageLog::help() 00480 { 00481 emit helpRequested("log"); 00482 } 00483