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

Generated on Sat Aug 16 17:38:35 2008 for Vidalia by  doxygen 1.5.6