MessageLog.cpp

Go to the documentation of this file.
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 

Generated on Mon Aug 30 19:09:59 2010 for Vidalia by  doxygen 1.5.9