| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782 | /*=========================================================================  Library:   CTK  Copyright (c) Kitware Inc.  Licensed under the Apache License, Version 2.0 (the "License");  you may not use this file except in compliance with the License.  You may obtain a copy of the License at      http://www.apache.org/licenses/LICENSE-2.0.txt  Unless required by applicable law or agreed to in writing, software  distributed under the License is distributed on an "AS IS" BASIS,  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the specific language governing permissions and  limitations under the License.=========================================================================*/// Qt includes#include <QApplication>#include <QDateTime>#include <QDebug>#include <QFile>#include <QMainWindow>#include <QMetaEnum>#include <QMetaType>#include <QMutexLocker>#include <QPointer>#include <QStandardItem>#include <QStatusBar>// CTK includes#include "ctkErrorLogModel.h"#include <ctkPimpl.h>// STD includes#include <cstdio> // For _fileno or fileno#ifdef _MSC_VER# include <io.h> // For _write()#else# include <unistd.h>#endif// --------------------------------------------------------------------------// ctkErrorLogLevel methods// --------------------------------------------------------------------------ctkErrorLogLevel::ctkErrorLogLevel(){  qRegisterMetaType<ctkErrorLogLevel::LogLevel>("ctkErrorLogLevel::LogLevel");}// --------------------------------------------------------------------------QString ctkErrorLogLevel::operator()(ctkErrorLogLevel::LogLevel logLevel){  return this->logLevelAsString(logLevel);}// --------------------------------------------------------------------------QString ctkErrorLogLevel::logLevelAsString(ctkErrorLogLevel::LogLevel logLevel)const{  QMetaEnum logLevelEnum = this->metaObject()->enumerator(0);  Q_ASSERT(QString("LogLevel").compare(logLevelEnum.name()) == 0);  return QLatin1String(logLevelEnum.valueToKey(logLevel));}// --------------------------------------------------------------------------// ctkErrorLogTerminalOutputPrivate// --------------------------------------------------------------------------class ctkErrorLogTerminalOutputPrivate{public:  ctkErrorLogTerminalOutputPrivate();  ~ctkErrorLogTerminalOutputPrivate();  bool Enabled;  mutable QMutex EnableMutex;  int FD;  mutable QMutex OutputMutex;};// --------------------------------------------------------------------------ctkErrorLogTerminalOutputPrivate::ctkErrorLogTerminalOutputPrivate()  : Enabled(false){#ifdef Q_OS_WIN32  this->FD = _fileno(stdout);#else  this->FD = fileno(stdout);#endif}// --------------------------------------------------------------------------ctkErrorLogTerminalOutputPrivate::~ctkErrorLogTerminalOutputPrivate(){}// --------------------------------------------------------------------------// ctkErrorLogTerminalOutput methods// --------------------------------------------------------------------------ctkErrorLogTerminalOutput::ctkErrorLogTerminalOutput()  : d_ptr(new ctkErrorLogTerminalOutputPrivate){}// --------------------------------------------------------------------------ctkErrorLogTerminalOutput::~ctkErrorLogTerminalOutput(){}// --------------------------------------------------------------------------bool ctkErrorLogTerminalOutput::enabled()const{  Q_D(const ctkErrorLogTerminalOutput);  QMutexLocker locker(&d->EnableMutex);  return d->Enabled;}// --------------------------------------------------------------------------void ctkErrorLogTerminalOutput::setEnabled(bool value){  Q_D(ctkErrorLogTerminalOutput);  QMutexLocker locker(&d->EnableMutex);  d->Enabled = value;}// --------------------------------------------------------------------------int ctkErrorLogTerminalOutput::fileDescriptor()const{  Q_D(const ctkErrorLogTerminalOutput);  QMutexLocker locker(&d->OutputMutex);  return d->FD;}// --------------------------------------------------------------------------void ctkErrorLogTerminalOutput::setFileDescriptor(int fd){  Q_D(ctkErrorLogTerminalOutput);  QMutexLocker locker(&d->OutputMutex);  d->FD = fd;}// --------------------------------------------------------------------------void ctkErrorLogTerminalOutput::output(const QString& text){  Q_D(ctkErrorLogTerminalOutput);  {    QMutexLocker locker(&d->EnableMutex);    if (!d->Enabled)      {      return;      }  }  {    QMutexLocker locker(&d->OutputMutex);    QString textWithNewLine = text + "\n";#ifdef _MSC_VER    int res = _write(d->FD, qPrintable(textWithNewLine), textWithNewLine.size());#else    ssize_t res = write(d->FD, qPrintable(textWithNewLine), textWithNewLine.size());#endif    if (res == -1)      {      return;      }  }}// --------------------------------------------------------------------------// ctkErrorLogModelPrivate// --------------------------------------------------------------------------class ctkErrorLogModelPrivate{  Q_DECLARE_PUBLIC(ctkErrorLogModel);protected:  ctkErrorLogModel* const q_ptr;public:  ctkErrorLogModelPrivate(ctkErrorLogModel& object);  ~ctkErrorLogModelPrivate();  void init();  /// Convenient method that could be used for debugging purposes.  void appendToFile(const QString& fileName, const QString& text);  void setMessageHandlerConnection(ctkErrorLogAbstractMessageHandler * msgHandler, bool asynchronous);  QStandardItemModel StandardItemModel;  QHash<QString, ctkErrorLogAbstractMessageHandler*> RegisteredHandlers;  ctkErrorLogLevel::LogLevels CurrentLogLevelFilter;  bool LogEntryGrouping;  bool AsynchronousLogging;  bool AddingEntry;  ctkErrorLogLevel ErrorLogLevel;  ctkErrorLogTerminalOutput StdErrTerminalOutput;  ctkErrorLogTerminalOutput StdOutTerminalOutput;  QMutex AppendToFileMutex;};// --------------------------------------------------------------------------// ctkErrorLogModelPrivate methods// --------------------------------------------------------------------------ctkErrorLogModelPrivate::ctkErrorLogModelPrivate(ctkErrorLogModel& object)  : q_ptr(&object){  this->LogEntryGrouping = false;  this->AsynchronousLogging = true;  this->AddingEntry = false;}// --------------------------------------------------------------------------ctkErrorLogModelPrivate::~ctkErrorLogModelPrivate(){  foreach(const QString& handlerName, this->RegisteredHandlers.keys())    {    ctkErrorLogAbstractMessageHandler * msgHandler =        this->RegisteredHandlers.value(handlerName);    Q_ASSERT(msgHandler);    msgHandler->setEnabled(false);    delete msgHandler;    }}// --------------------------------------------------------------------------void ctkErrorLogModelPrivate::init(){  Q_Q(ctkErrorLogModel);  q->setDynamicSortFilter(true);  //  // WARNING - Using a QSortFilterProxyModel slows down the insertion of rows by a factor 10  //  q->setSourceModel(&this->StandardItemModel);  q->setFilterKeyColumn(ctkErrorLogModel::LogLevelColumn);}// --------------------------------------------------------------------------void ctkErrorLogModelPrivate::appendToFile(const QString& fileName, const QString& text){  QMutexLocker locker(&this->AppendToFileMutex);  QFile f(fileName);  f.open(QFile::Append);  QTextStream s(&f);  s << QDateTime::currentDateTime().toString() << " - " << text << "\n";  f.close();}// --------------------------------------------------------------------------void ctkErrorLogModelPrivate::setMessageHandlerConnection(    ctkErrorLogAbstractMessageHandler * msgHandler, bool asynchronous){  Q_Q(ctkErrorLogModel);  msgHandler->disconnect();  QObject::connect(msgHandler,        SIGNAL(messageHandled(QDateTime,QString,ctkErrorLogLevel::LogLevel,QString,QString)),        q, SLOT(addEntry(QDateTime,QString,ctkErrorLogLevel::LogLevel,QString,QString)),        asynchronous ? Qt::QueuedConnection : Qt::BlockingQueuedConnection);}// --------------------------------------------------------------------------// ctkErrorLogModel methods//------------------------------------------------------------------------------ctkErrorLogModel::ctkErrorLogModel(QObject * parentObject)  : Superclass(parentObject)  , d_ptr(new ctkErrorLogModelPrivate(*this)){  Q_D(ctkErrorLogModel);  d->init();}//------------------------------------------------------------------------------ctkErrorLogModel::~ctkErrorLogModel(){}//------------------------------------------------------------------------------bool ctkErrorLogModel::registerMsgHandler(ctkErrorLogAbstractMessageHandler * msgHandler){  Q_D(ctkErrorLogModel);  if (!msgHandler)    {    return false;    }  if (d->RegisteredHandlers.keys().contains(msgHandler->handlerName()))    {    return false;    }  d->setMessageHandlerConnection(msgHandler, d->AsynchronousLogging);  msgHandler->setTerminalOutput(Self::StandardError, &d->StdErrTerminalOutput);  msgHandler->setTerminalOutput(Self::StandardOutput, &d->StdOutTerminalOutput);  d->RegisteredHandlers.insert(msgHandler->handlerName(), msgHandler);  return true;}//------------------------------------------------------------------------------QStringList ctkErrorLogModel::msgHandlerNames()const{  Q_D(const ctkErrorLogModel);  return d->RegisteredHandlers.keys();}//------------------------------------------------------------------------------bool ctkErrorLogModel::msgHandlerEnabled(const QString& handlerName) const{  Q_D(const ctkErrorLogModel);  if (!d->RegisteredHandlers.keys().contains(handlerName))    {    return false;    }  return d->RegisteredHandlers.value(handlerName)->enabled();}//------------------------------------------------------------------------------void ctkErrorLogModel::setMsgHandlerEnabled(const QString& handlerName, bool enabled){  Q_D(ctkErrorLogModel);  if (!d->RegisteredHandlers.keys().contains(handlerName))    {//    qCritical() << "Failed to enable/disable message handler " << handlerName//                << "-  Handler not registered !";    return;    }  d->RegisteredHandlers.value(handlerName)->setEnabled(enabled);}//------------------------------------------------------------------------------QStringList ctkErrorLogModel::msgHandlerEnabled() const{  Q_D(const ctkErrorLogModel);  QStringList msgHandlers;  foreach(const QString& handlerName, d->RegisteredHandlers.keys())    {    if (d->RegisteredHandlers.value(handlerName)->enabled())      {      msgHandlers << handlerName;      }    }  return msgHandlers;}//------------------------------------------------------------------------------void ctkErrorLogModel::setMsgHandlerEnabled(const QStringList& handlerNames){  foreach(const QString& handlerName, handlerNames)    {    this->setMsgHandlerEnabled(handlerName, true);    }}//------------------------------------------------------------------------------void ctkErrorLogModel::enableAllMsgHandler(){  this->setAllMsgHandlerEnabled(true);}//------------------------------------------------------------------------------void ctkErrorLogModel::disableAllMsgHandler(){  this->setAllMsgHandlerEnabled(false);}//------------------------------------------------------------------------------void ctkErrorLogModel::setAllMsgHandlerEnabled(bool enabled){  Q_D(ctkErrorLogModel);  foreach(const QString& msgHandlerName, d->RegisteredHandlers.keys())    {    this->setMsgHandlerEnabled(msgHandlerName, enabled);    }}//------------------------------------------------------------------------------ctkErrorLogModel::TerminalOutputs ctkErrorLogModel::terminalOutputs()const{  Q_D(const ctkErrorLogModel);  ctkErrorLogModel::TerminalOutputs currentTerminalOutputs;  currentTerminalOutputs |= d->StdErrTerminalOutput.enabled() ? Self::StandardError : Self::None;  currentTerminalOutputs |= d->StdOutTerminalOutput.enabled() ? Self::StandardOutput : Self::None;  return currentTerminalOutputs;}//------------------------------------------------------------------------------void ctkErrorLogModel::setTerminalOutputs(    const ctkErrorLogModel::TerminalOutputs& terminalOutput){  Q_D(ctkErrorLogModel);  d->StdErrTerminalOutput.setEnabled(terminalOutput & ctkErrorLogModel::StandardOutput);  d->StdOutTerminalOutput.setEnabled(terminalOutput & ctkErrorLogModel::StandardError);}//------------------------------------------------------------------------------void ctkErrorLogModel::addEntry(const QDateTime& currentDateTime, const QString& threadId,                                ctkErrorLogLevel::LogLevel logLevel,                                const QString& origin, const QString& text){  Q_D(ctkErrorLogModel);//  d->appendToFile("/tmp/ctkErrorLogModel-appendToFile.txt",//                  QString("addEntry: %1").arg(QThread::currentThreadId()));  if (d->AddingEntry)    {//    QString str;//    QTextStream s(&str);//    s << "----------------------------------\n";//    s << "text=>" << text << "\n";//    s << "\tlogLevel:" << qPrintable(d->ErrorLogLevel(logLevel)) << "\n";//    s << "\torigin:" << qPrintable(origin) << "\n";//    d->appendToFile("/tmp/ctkErrorLogModel-AddingEntry-true.txt", str);    return;    }  d->AddingEntry = true;  QString timeFormat("dd.MM.yyyy hh:mm:ss");  bool groupEntry = false;  if (d->LogEntryGrouping)    {    // Retrieve threadId associated with last row    QModelIndex lastRowThreadIdIndex =        d->StandardItemModel.index(d->StandardItemModel.rowCount() - 1, ctkErrorLogModel::ThreadIdColumn);    bool threadIdMatched = threadId == lastRowThreadIdIndex.data().toString();    // Retrieve logLevel associated with last row    QModelIndex lastRowLogLevelIndex =        d->StandardItemModel.index(d->StandardItemModel.rowCount() - 1, ctkErrorLogModel::LogLevelColumn);    bool logLevelMatched = d->ErrorLogLevel(logLevel) == lastRowLogLevelIndex.data().toString();    // Retrieve origin associated with last row    QModelIndex lastRowOriginIndex =        d->StandardItemModel.index(d->StandardItemModel.rowCount() - 1, ctkErrorLogModel::OriginColumn);    bool originMatched = origin == lastRowOriginIndex.data().toString();    // Retrieve time associated with last row    QModelIndex lastRowTimeIndex =            d->StandardItemModel.index(d->StandardItemModel.rowCount() - 1, ctkErrorLogModel::TimeColumn);    QDateTime lastRowDateTime = QDateTime::fromString(lastRowTimeIndex.data().toString(), timeFormat);    int groupingIntervalInMsecs = 1000;    bool withinGroupingInterval = lastRowDateTime.time().msecsTo(currentDateTime.time()) <= groupingIntervalInMsecs;    groupEntry = threadIdMatched && logLevelMatched && originMatched && withinGroupingInterval;    }  if (!groupEntry)    {    QList<QStandardItem*> itemList;    // Time item    QStandardItem * timeItem = new QStandardItem(currentDateTime.toString(timeFormat));    timeItem->setEditable(false);    itemList << timeItem;    // ThreadId item    QStandardItem * threadIdItem = new QStandardItem(threadId);    threadIdItem->setEditable(false);    itemList << threadIdItem;    // LogLevel item    QStandardItem * logLevelItem = new QStandardItem(d->ErrorLogLevel(logLevel));    logLevelItem->setEditable(false);    itemList << logLevelItem;    // Origin item    QStandardItem * originItem = new QStandardItem(origin);    originItem->setEditable(false);    itemList << originItem;    // Description item    QStandardItem * descriptionItem = new QStandardItem();    QString descriptionText(text);    descriptionItem->setData(descriptionText.left(160).append((descriptionText.size() > 160) ? "..." : ""), Qt::DisplayRole);    descriptionItem->setData(descriptionText, ctkErrorLogModel::DescriptionTextRole);    descriptionItem->setEditable(false);    itemList << descriptionItem;    d->StandardItemModel.invisibleRootItem()->appendRow(itemList);    }  else    {    // Retrieve description associated with last row    QModelIndex lastRowDescriptionIndex =        d->StandardItemModel.index(d->StandardItemModel.rowCount() - 1, ctkErrorLogModel::DescriptionColumn);    QStringList updatedDescription;    updatedDescription << lastRowDescriptionIndex.data(ctkErrorLogModel::DescriptionTextRole).toString();    updatedDescription << text;    d->StandardItemModel.setData(lastRowDescriptionIndex, updatedDescription.join("\n"),                                 ctkErrorLogModel::DescriptionTextRole);    // Append '...' to displayText if needed    QString displayText = lastRowDescriptionIndex.data().toString();    if (!displayText.endsWith("..."))      {      d->StandardItemModel.setData(lastRowDescriptionIndex, displayText.append("..."), Qt::DisplayRole);      }    }  d->AddingEntry = false;}//------------------------------------------------------------------------------void ctkErrorLogModel::clear(){  Q_D(ctkErrorLogModel);  d->StandardItemModel.invisibleRootItem()->removeRows(0, d->StandardItemModel.rowCount());}//------------------------------------------------------------------------------void ctkErrorLogModel::filterEntry(const ctkErrorLogLevel::LogLevels& logLevel,                                   bool disableFilter){  Q_D(ctkErrorLogModel);  QStringList patterns;  if (!this->filterRegExp().pattern().isEmpty())    {    patterns << this->filterRegExp().pattern().split("|");    }  patterns.removeAll(d->ErrorLogLevel(ctkErrorLogLevel::None));//  foreach(QString s, patterns)//    {//    std::cout << "pattern:" << qPrintable(s) << std::endl;//    }  QMetaEnum logLevelEnum = d->ErrorLogLevel.metaObject()->enumerator(0);  Q_ASSERT(QString("LogLevel").compare(logLevelEnum.name()) == 0);  // Loop over enum values and append associated name to 'patterns' if  // it has been specified within 'logLevel'  for (int i = 1; i < logLevelEnum.keyCount(); ++i)    {    int aLogLevel = logLevelEnum.value(i);    if (logLevel & aLogLevel)      {      QString logLevelAsString = d->ErrorLogLevel(static_cast<ctkErrorLogLevel::LogLevel>(aLogLevel));      if (!disableFilter)        {        patterns << logLevelAsString;        d->CurrentLogLevelFilter |= static_cast<ctkErrorLogLevel::LogLevels>(aLogLevel);        }      else        {        patterns.removeAll(logLevelAsString);        d->CurrentLogLevelFilter ^= static_cast<ctkErrorLogLevel::LogLevels>(aLogLevel);        }      }    }  if (patterns.isEmpty())    {    // If there are no patterns, let's filter with the None level so that    // all entries are filtered out.    patterns << d->ErrorLogLevel(ctkErrorLogLevel::None);    }  bool filterChanged = true;  QStringList currentPatterns = this->filterRegExp().pattern().split("|");  if (currentPatterns.count() == patterns.count())    {    foreach(const QString& p, patterns)      {      currentPatterns.removeAll(p);      }    filterChanged = currentPatterns.count() > 0;    }  this->setFilterRegExp(patterns.join("|"));  if (filterChanged)    {    emit this->logLevelFilterChanged();    }}//------------------------------------------------------------------------------ctkErrorLogLevel::LogLevels ctkErrorLogModel::logLevelFilter()const{  Q_D(const ctkErrorLogModel);  QMetaEnum logLevelEnum = d->ErrorLogLevel.metaObject()->enumerator(0);  Q_ASSERT(QString("LogLevel").compare(logLevelEnum.name()) == 0);  ctkErrorLogLevel::LogLevels filter = ctkErrorLogLevel::Unknown;  foreach(const QString& filterAsString, this->filterRegExp().pattern().split("|"))    {    filter |= static_cast<ctkErrorLogLevel::LogLevels>(logLevelEnum.keyToValue(filterAsString.toLatin1()));    }  return filter;}//------------------------------------------------------------------------------bool ctkErrorLogModel::logEntryGrouping()const{  Q_D(const ctkErrorLogModel);  return d->LogEntryGrouping;}//------------------------------------------------------------------------------void ctkErrorLogModel::setLogEntryGrouping(bool value){  Q_D(ctkErrorLogModel);  d->LogEntryGrouping = value;}//------------------------------------------------------------------------------bool ctkErrorLogModel::asynchronousLogging()const{  Q_D(const ctkErrorLogModel);  return d->AsynchronousLogging;}//------------------------------------------------------------------------------void ctkErrorLogModel::setAsynchronousLogging(bool value){  Q_D(ctkErrorLogModel);  if (d->AsynchronousLogging == value)    {    return;    }  foreach(const QString& handlerName, d->RegisteredHandlers.keys())    {    d->setMessageHandlerConnection(          d->RegisteredHandlers.value(handlerName), value);    }  d->AsynchronousLogging = value;}// --------------------------------------------------------------------------// ctkErrorLogAbstractMessageHandlerPrivate// --------------------------------------------------------------------------class ctkErrorLogAbstractMessageHandlerPrivate{public:  ctkErrorLogAbstractMessageHandlerPrivate();  ~ctkErrorLogAbstractMessageHandlerPrivate();  bool                        Enabled;  QString                     HandlerPrettyName;  // Use "int" instead of "ctkErrorLogModel::TerminalOutput" to avoid compilation warning ...  // qhash.h:879: warning: passing ‘ctkErrorLogModel::TerminalOutput’ chooses ‘int’ over ‘uint’ [-Wsign-promo]  QHash<int, ctkErrorLogTerminalOutput*> TerminalOutputs;};// --------------------------------------------------------------------------ctkErrorLogAbstractMessageHandlerPrivate::ctkErrorLogAbstractMessageHandlerPrivate()  : Enabled(false){}// --------------------------------------------------------------------------ctkErrorLogAbstractMessageHandlerPrivate::~ctkErrorLogAbstractMessageHandlerPrivate(){}// --------------------------------------------------------------------------// ctkErrorLogAbstractMessageHandlerPrivate methods// --------------------------------------------------------------------------ctkErrorLogAbstractMessageHandler::ctkErrorLogAbstractMessageHandler()  : Superclass(), d_ptr(new ctkErrorLogAbstractMessageHandlerPrivate){}// --------------------------------------------------------------------------ctkErrorLogAbstractMessageHandler::~ctkErrorLogAbstractMessageHandler(){}// --------------------------------------------------------------------------QString ctkErrorLogAbstractMessageHandler::handlerPrettyName()const{  Q_D(const ctkErrorLogAbstractMessageHandler);  if (d->HandlerPrettyName.isEmpty())    {    return this->handlerName();    }  else    {    return d->HandlerPrettyName;    }}// --------------------------------------------------------------------------void ctkErrorLogAbstractMessageHandler::setHandlerPrettyName(const QString& newHandlerPrettyName){  Q_D(ctkErrorLogAbstractMessageHandler);  d->HandlerPrettyName = newHandlerPrettyName;}// --------------------------------------------------------------------------bool ctkErrorLogAbstractMessageHandler::enabled()const{  Q_D(const ctkErrorLogAbstractMessageHandler);  return d->Enabled;}// --------------------------------------------------------------------------void ctkErrorLogAbstractMessageHandler::setEnabled(bool value){  Q_D(ctkErrorLogAbstractMessageHandler);  if (value == d->Enabled)    {    return;    }  this->setEnabledInternal(value);  d->Enabled = value;}// --------------------------------------------------------------------------void ctkErrorLogAbstractMessageHandler::handleMessage(const QString& threadId,                                                      ctkErrorLogLevel::LogLevel logLevel,                                                      const QString& origin, const QString& text){  Q_D(ctkErrorLogAbstractMessageHandler);  if (logLevel <= ctkErrorLogLevel::Info)    {    if(d->TerminalOutputs.contains(ctkErrorLogModel::StandardOutput))      {      d->TerminalOutputs.value(ctkErrorLogModel::StandardOutput)->output(text);      }    }  else    {    if(d->TerminalOutputs.contains(ctkErrorLogModel::StandardError))      {      d->TerminalOutputs.value(ctkErrorLogModel::StandardError)->output(text);      }    }  emit this->messageHandled(QDateTime::currentDateTime(), threadId, logLevel, origin, text);}// --------------------------------------------------------------------------ctkErrorLogTerminalOutput* ctkErrorLogAbstractMessageHandler::terminalOutput(    ctkErrorLogModel::TerminalOutput terminalOutputType)const{  Q_D(const ctkErrorLogAbstractMessageHandler);  if(d->TerminalOutputs.contains(terminalOutputType))    {    return d->TerminalOutputs.value(terminalOutputType);    }  return 0;}// --------------------------------------------------------------------------void ctkErrorLogAbstractMessageHandler::setTerminalOutput(    ctkErrorLogModel::TerminalOutput terminalOutputType, ctkErrorLogTerminalOutput* terminalOutput){  Q_D(ctkErrorLogAbstractMessageHandler);  d->TerminalOutputs.insert(terminalOutputType, terminalOutput);}
 |