messagelog.cpp

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

Generated on Wed Sep 5 15:49:27 2007 for Vidalia by  doxygen 1.5.3