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 2983 2008-08-17 05:59:43Z 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       50
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   ui.actionClose->setShortcut(QString("Esc"));
00117   Vidalia::createShortcut("Ctrl+W", this, ui.actionClose, SLOT(trigger()));
00118 }
00119 
00120 /** Set tooltips for Message Filter checkboxes in code because they are long
00121  * and Designer wouldn't let us insert newlines into the text. */
00122 void
00123 MessageLog::setToolTips()
00124 {
00125   ui.chkTorErr->setToolTip(tr("Messages that appear when something has \n"
00126                               "gone very wrong and Tor cannot proceed."));
00127   ui.chkTorWarn->setToolTip(tr("Messages that only appear when \n"
00128                                "something has gone wrong with Tor."));
00129   ui.chkTorNote->setToolTip(tr("Messages that appear infrequently \n"
00130                                "during normal Tor operation and are \n"
00131                                "not considered errors, but you may \n"
00132                                "care about."));
00133   ui.chkTorInfo->setToolTip(tr("Messages that appear frequently \n"
00134                                "during normal Tor operation."));
00135   ui.chkTorDebug->setToolTip(tr("Hyper-verbose messages primarily of \n"
00136                                 "interest to Tor developers.")); 
00137 }
00138 
00139 /** Loads the saved Message Log settings */
00140 void
00141 MessageLog::loadSettings()
00142 {
00143   /* Set Max Count widget */
00144   uint maxMsgCount = getSetting(SETTING_MAX_MSG_COUNT,
00145                                 DEFAULT_MAX_MSG_COUNT).toUInt();
00146   ui.spnbxMaxCount->setValue(maxMsgCount);
00147   ui.lstMessages->setMaximumMessageCount(maxMsgCount);
00148 
00149   /* Set whether or not logging to file is enabled */
00150   _enableLogging = getSetting(SETTING_ENABLE_LOGFILE,
00151                               DEFAULT_ENABLE_LOGFILE).toBool();
00152   QString logfile = getSetting(SETTING_LOGFILE,
00153                                DEFAULT_LOGFILE).toString();
00154   ui.lineFile->setText(QDir::convertSeparators(logfile));
00155   rotateLogFile(logfile);
00156   ui.chkEnableLogFile->setChecked(_logFile.isOpen());
00157 
00158   /* Set the checkboxes accordingly */
00159   _filter = getSetting(SETTING_MSG_FILTER, DEFAULT_MSG_FILTER).toUInt();
00160   ui.chkTorErr->setChecked(_filter & LogEvent::Error);
00161   ui.chkTorWarn->setChecked(_filter & LogEvent::Warn);
00162   ui.chkTorNote->setChecked(_filter & LogEvent::Notice);
00163   ui.chkTorInfo->setChecked(_filter & LogEvent::Info);
00164   ui.chkTorDebug->setChecked(_filter & LogEvent::Debug);
00165   registerLogEvents();
00166  
00167   /* Filter the message log */
00168   QApplication::setOverrideCursor(Qt::WaitCursor);
00169   ui.lstMessages->filter(_filter);
00170   QApplication::restoreOverrideCursor();
00171 }
00172 
00173 /** Attempts to register the selected message filter with Tor and displays an
00174  * error if setting the events fails. */
00175 void
00176 MessageLog::registerLogEvents()
00177 {
00178   QString errmsg;
00179   _filter = getSetting(SETTING_MSG_FILTER, DEFAULT_MSG_FILTER).toUInt();
00180   if (!_torControl->setLogEvents(_filter, this, &errmsg)) {
00181     VMessageBox::warning(this, tr("Error Setting Filter"),
00182       p(tr("Vidalia was unable to register for Tor's log events.")) + p(errmsg),
00183       VMessageBox::Ok);
00184   }
00185 }
00186 
00187 /** Opens a log file if necessary, or closes it if logging is disabled. If a
00188  * log file is already opened and a new filename is specified, then the log
00189  * file will be rotated to the new filename. In the case that the new filename
00190  * can not be openend, the old file will remain open and writable. */
00191 bool
00192 MessageLog::rotateLogFile(QString filename)
00193 {
00194   QString errmsg;
00195   if (_enableLogging) {
00196     if (!_logFile.open(filename, &errmsg)) {
00197       VMessageBox::warning(this, tr("Error Opening Log File"),
00198         p(tr("Vidalia was unable to open the specified log file."))+p(errmsg),
00199         VMessageBox::Ok);
00200       return false;
00201     }
00202   } else {
00203     /* Close the log file. */
00204     _logFile.close();
00205   }
00206   return true;
00207 }
00208 
00209 /** Saves the Message Log settings, adjusts the message list if required, and
00210  * then hides the settings frame. */
00211 void
00212 MessageLog::saveSettings()
00213 {
00214   /* Update the logging status */
00215   _enableLogging = ui.chkEnableLogFile->isChecked();
00216   if (_enableLogging && ui.lineFile->text().isEmpty()) {
00217     /* The user chose to enable logging messages to a file, but didn't specify
00218      * a log filename. */
00219     VMessageBox::warning(this, tr("Log Filename Required"),
00220       p(tr("You must enter a filename to be able to save log "
00221            "messages to a file.")), VMessageBox::Ok);
00222     return;
00223   }
00224   if (rotateLogFile(ui.lineFile->text())) {
00225     saveSetting(SETTING_LOGFILE, ui.lineFile->text());
00226     saveSetting(SETTING_ENABLE_LOGFILE, _logFile.isOpen());
00227   }
00228   ui.lineFile->setText(QDir::convertSeparators(ui.lineFile->text()));
00229   ui.chkEnableLogFile->setChecked(_logFile.isOpen());
00230 
00231   /* Update the maximum displayed item count */
00232   saveSetting(SETTING_MAX_MSG_COUNT, ui.spnbxMaxCount->value());
00233   ui.lstMessages->setMaximumMessageCount(ui.spnbxMaxCount->value());
00234   
00235   /* Save message filter and refilter the list */
00236   uint filter = 0;
00237   ADD_TO_FILTER(filter, LogEvent::Error, ui.chkTorErr->isChecked());
00238   ADD_TO_FILTER(filter, LogEvent::Warn, ui.chkTorWarn->isChecked());
00239   ADD_TO_FILTER(filter, LogEvent::Notice, ui.chkTorNote->isChecked());
00240   ADD_TO_FILTER(filter, LogEvent::Info, ui.chkTorInfo->isChecked());
00241   ADD_TO_FILTER(filter, LogEvent::Debug, ui.chkTorDebug->isChecked());
00242   saveSetting(SETTING_MSG_FILTER, filter);
00243   registerLogEvents();
00244   
00245   /* Filter the message log */
00246   QApplication::setOverrideCursor(Qt::WaitCursor);
00247   ui.lstMessages->filter(_filter);
00248   QApplication::restoreOverrideCursor();
00249    
00250   /* Hide the settings frame and reset toggle button*/
00251   ui.actionSettings->toggle(); 
00252 }
00253 
00254 /** Simply restores the previously saved settings and hides the settings
00255  * frame. */
00256 void 
00257 MessageLog::cancelChanges()
00258 {
00259   /* Hide the settings frame and reset toggle button */
00260   ui.actionSettings->toggle();
00261   /* Reload the settings */
00262   loadSettings();
00263 }
00264 
00265 /** Called when the user clicks "Browse" to select a new log file. */
00266 void
00267 MessageLog::browse()
00268 {
00269   /* Strangely, QFileDialog returns a non seperator converted path. */
00270   QString filename = QDir::convertSeparators(
00271                           QFileDialog::getSaveFileName(this,
00272                               tr("Select Log File"), "tor-log.txt"));
00273   if (!filename.isEmpty()) {
00274     ui.lineFile->setText(filename);
00275   }
00276 }
00277 
00278 /** Saves the given list of items to a file.
00279  * \param items A list of log message items to save. 
00280  */
00281 void
00282 MessageLog::save(QStringList messages)
00283 {
00284   if (!messages.size()) {
00285     return;
00286   }
00287 
00288   QString fileName = QFileDialog::getSaveFileName(this,
00289                           tr("Save Log Messages"),
00290                           "VidaliaLog-" + 
00291                           QDateTime::currentDateTime().toString("MM.dd.yyyy")
00292                           + ".txt", tr("Text Files (*.txt)"));
00293   
00294   /* If the choose to save */
00295   if (!fileName.isEmpty()) {
00296     LogFile logFile;
00297     QString errmsg;
00298     
00299     /* If can't write to file, show error message */
00300     if (!logFile.open(fileName, &errmsg)) {
00301       VMessageBox::warning(this, tr("Vidalia"),
00302                            p(tr("Cannot write file %1\n\n%2."))
00303                                                 .arg(fileName)
00304                                                 .arg(errmsg),
00305                            VMessageBox::Ok);
00306       return;
00307     }
00308    
00309     /* Write out the message log to the file */
00310     QApplication::setOverrideCursor(Qt::WaitCursor);
00311     foreach (QString msg, messages) {
00312       logFile << msg;
00313     }
00314     QApplication::restoreOverrideCursor();
00315   }
00316 }
00317 
00318 /** Saves currently selected messages to a file. */
00319 void
00320 MessageLog::saveSelected()
00321 {
00322   save(ui.lstMessages->selectedMessages());
00323 }
00324 
00325 /** Saves all shown messages to a file. */
00326 void
00327 MessageLog::saveAll()
00328 {
00329   save(ui.lstMessages->allMessages());
00330 }
00331 
00332 /** Copies contents of currently selected messages to the 'clipboard'. */
00333 void
00334 MessageLog::copy()
00335 {
00336   QString contents = ui.lstMessages->selectedMessages().join("");
00337   if (!contents.isEmpty()) {
00338     /* Copy the selected messages to the clipboard */
00339     QApplication::clipboard()->setText(contents);
00340   }
00341 }
00342 
00343 /** Prompts the user for a search string. If the search string is not found in
00344  * any of the currently displayed log entires, then a message will be
00345  * displayed for the user informing them that no matches were found. 
00346  * \sa search()
00347  */
00348 void
00349 MessageLog::find()
00350 {
00351   bool ok;
00352   QString text = QInputDialog::getText(this, tr("Find in Message Log"),
00353                   tr("Find:"), QLineEdit::Normal, QString(), &ok);
00354   
00355   if (ok && !text.isEmpty()) {
00356     /* Search for the user-specified text */
00357     QList<LogTreeItem *> results = ui.lstMessages->find(text);
00358     if (!results.size()) {
00359       VMessageBox::information(this, tr("Not Found"), 
00360                                p(tr("Search found 0 matches.")), 
00361                                VMessageBox::Ok);
00362     } else {
00363       /* Set the focus to the first match */
00364       ui.lstMessages->scrollToItem(results.at(0));
00365     }
00366   }
00367 }
00368 
00369 /** Writes a message to the Message History and tags it with
00370  * the proper date, time and type.
00371  * \param type The message's severity type.
00372  * \param message The log message to be added.
00373  */
00374 void 
00375 MessageLog::log(LogEvent::Severity type, QString message)
00376 {
00377   /* Only add the message if it's not being filtered out */
00378   if (_filter & (uint)type) {
00379     /* Add the message to the list and scroll to it if necessary. */
00380     LogTreeItem *item = ui.lstMessages->log(type, message); 
00381    
00382     /* This is a workaround to force Qt to update the statusbar text (if any
00383      * is currently displayed) to reflect the new message added. */
00384     QString currStatusTip = ui.statusbar->currentMessage();
00385     if (!currStatusTip.isEmpty()) {
00386       currStatusTip = ui.lstMessages->statusTip();
00387       ui.statusbar->showMessage(currStatusTip);
00388     }
00389     
00390     /* If we're saving log messages to a file, go ahead and do that now */
00391     if (_enableLogging) {
00392       _logFile << item->toString();
00393     }
00394   }
00395 }
00396 
00397 /** Custom event handler. Checks if the event is a log event. If it is, then
00398  * it will write the message to the message log. 
00399  * \param event The custom log event. 
00400  */
00401 void
00402 MessageLog::customEvent(QEvent *event)
00403 {
00404   if (event->type() == CustomEventType::LogEvent) {
00405     LogEvent *e = (LogEvent *)event;
00406     log(e->severity(), e->message());
00407     e->accept();
00408   }
00409 }
00410 
00411 /** Displays help information about the message log. */
00412 void
00413 MessageLog::help()
00414 {
00415   emit helpRequested("log");
00416 }
00417 

Generated on Wed Nov 26 21:02:42 2008 for Vidalia by  doxygen 1.5.7.1