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

Generated on Mon Oct 23 20:08:16 2006 for Vidalia by  doxygen 1.5.0