Newer
Older
//-------------------------------------------
// Includes
//-------------------------------------------
#include "MantidQtWidgets/Common/MessageDisplay.h"
#include "MantidKernel/ConfigService.h"
#include "MantidKernel/Logger.h"
#include <QAction>
#include <QActionGroup>
#include <QHBoxLayout>
#include <QMenu>
#include <QPlainTextEdit>
#include <QPoint>
#include <QScrollBar>
#include <QSignalMapper>
#include <Poco/Logger.h>
#include <Poco/Message.h>
#include <Poco/SplitterChannel.h>
namespace MantidQt {
namespace MantidWidgets {
//-------------------------------------------
// Public member functions
//-------------------------------------------
/**
* Load settings from the persistent store. The client is expected to call
* this method with the QSettings object opened at the approriate group
* @param storage A pointer to an existing QSettings instance opened
* at the group containing the values
*/
void MessageDisplay::readSettings(const QSettings &storage) {
Mantid::Kernel::ConfigService::Instance().setFilterChannelLogLevel(
m_filterChannelName,
storage.value("MessageDisplayPriority", Message::Priority::PRIO_NOTICE)
.toInt(),
true);
}
/**
* Load settings from the persistent store. The client is expected to call
* this method with the QSettings object opened at the approriate group
* @param storage A pointer to an existing QSettings instance opened
* at the group where the values should be stored.
*/
void MessageDisplay::writeSettings(QSettings *storage) {
Q_ASSERT(storage);
storage->setValue("MessageDisplayPriority",
static_cast<int>(m_filterChannel->getPriority()));
}
/**
* Constructs a widget that does not allow control over the global log level
* @param parent An optional parent widget
*/
MessageDisplay::MessageDisplay(QWidget *parent)
: MessageDisplay(DisableLogLevelControl, parent) {}
/**
* @param logLevelControl Controls whether this display shows the right-click
* option to change
* the global log level
* @param parent An optional parent widget
*/
MessageDisplay::MessageDisplay(LogLevelControl logLevelControl, QWidget *parent)
: QWidget(parent), m_logLevelControl(logLevelControl),
m_logChannel(new QtSignalChannel),
m_filterChannel(new Poco::FilterChannel),
m_textDisplay(new QPlainTextEdit(this)), m_formats(),
m_loglevels(new QActionGroup(this)),
m_logLevelMapping(new QSignalMapper(this)),
m_error(new QAction(tr("&Error"), this)),
m_warning(new QAction(tr("&Warning"), this)),
m_notice(new QAction(tr("&Notice"), this)),
m_information(new QAction(tr("&Information"), this)),
m_debug(new QAction(tr("&Debug"), this)) {
initActions();
initFormats();
setupTextArea();
}
/**
*/
MessageDisplay::~MessageDisplay() {
// The Channel class is ref counted and will
// delete itself when required
m_filterChannel->release();
m_logChannel->release();
delete m_textDisplay;
}
/**
* Attaches the Mantid logging framework
* (Note the ConfigService must have already been started)
*/
void MessageDisplay::attachLoggingChannel() {
// Setup logging
auto &rootLogger = Poco::Logger::root();
auto *rootChannel = Poco::Logger::root().getChannel();
// The root channel might be a SplitterChannel
if (auto *splitChannel = dynamic_cast<Poco::SplitterChannel *>(rootChannel)) {
splitChannel->addChannel(m_filterChannel);
} else {
Poco::Logger::setChannel(rootLogger.name(), m_filterChannel);
}
m_filterChannel->addChannel(m_logChannel);
auto &configService = Mantid::Kernel::ConfigService::Instance();
configService.registerLoggingFilterChannel(m_FilterChannelName,
m_filterChannel);
configService.setFilterChannelLogLevel(m_FilterChannelName, priority);
Gigg, Martyn Anthony
committed
connect(m_logChannel, SIGNAL(messageReceived(const Message &)), this,
SLOT(append(const Message &)));
}
Gigg, Martyn Anthony
committed
/**
* @param source A string specifying the required source for messages
* that will be emitted
*/
void MessageDisplay::setSource(const QString &source) {
m_logChannel->setSource(source);
}
Gigg, Martyn Anthony
committed
//----------------------------------------------------------------------------------------
// Public slots
//----------------------------------------------------------------------------------------
/**
* @param text The text string to append at PRIO_FATAL level
*/
void MessageDisplay::appendFatal(const QString &text) {
this->append(Message(text, Message::Priority::PRIO_FATAL));
}
Gigg, Martyn Anthony
committed
/**
* @param text The text string to append at PRIO_ERROR level
*/
void MessageDisplay::appendError(const QString &text) {
this->append(Message(text, Message::Priority::PRIO_ERROR));
}
Gigg, Martyn Anthony
committed
/**
* @param text The text string to append at PRIO_WARNING level
*/
void MessageDisplay::appendWarning(const QString &text) {
this->append(Message(text, Message::Priority::PRIO_WARNING));
}
Gigg, Martyn Anthony
committed
/**
* @param text The text string to append at PRIO_NOTICE level
*/
void MessageDisplay::appendNotice(const QString &text) {
this->append(Message(text, Message::Priority::PRIO_NOTICE));
}
/**
* @param text The text string to append at PRIO_INFORMATION level
*/
void MessageDisplay::appendInformation(const QString &text) {
this->append(Message(text, Message::Priority::PRIO_INFORMATION));
}
/**
* @param text The text string to append at PRIO_DEBUG level
*/
void MessageDisplay::appendDebug(const QString &text) {
this->append(Message(text, Message::Priority::PRIO_DEBUG));
}
/**
* @param msg A message that is echoed to the display after the
* current text
*/
void MessageDisplay::append(const Message &msg) {
QTextCursor cursor = moveCursorToEnd();
cursor.insertText(msg.text(), format(msg.priority()));
moveCursorToEnd();
if (msg.priority() <= Message::Priority::PRIO_ERROR)
emit errorReceived(msg.text());
if (msg.priority() <= Message::Priority::PRIO_WARNING)
emit warningReceived(msg.text());
}
/**
* @param msg Replace the current contents with this message
*/
void MessageDisplay::replace(const Message &msg) {
clear();
append(msg.text());
}
/**
* Clear all of the text
*/
void MessageDisplay::clear() { m_textDisplay->clear(); }
/**
* @returns The cursor at the end of the text
*/
QTextCursor MessageDisplay::moveCursorToEnd() {
QTextCursor cursor(m_textDisplay->textCursor());
cursor.movePosition(QTextCursor::End);
m_textDisplay->setTextCursor(cursor);
return cursor;
}
/**
* @return True if scroll bar is at bottom, false otherwise
*/
bool MessageDisplay::isScrollbarAtBottom() const {
return m_textDisplay->verticalScrollBar()->value() ==
m_textDisplay->verticalScrollBar()->maximum();
}
/**
* Moves the cursor to the top of the document
*/
void MessageDisplay::scrollToTop() {
// Code taken from QtCreator source
m_textDisplay->verticalScrollBar()->setValue(
m_textDisplay->verticalScrollBar()->minimum());
// QPlainTextEdit destroys the first calls value in case of multiline
// text, so make sure that the scroll bar actually gets the value set.
// Is a noop if the first call succeeded.
m_textDisplay->verticalScrollBar()->setValue(
m_textDisplay->verticalScrollBar()->minimum());
}
/**
* Moves the cursor to the bottom of the document
*/
void MessageDisplay::scrollToBottom() {
// Code taken from QtCreator source
m_textDisplay->verticalScrollBar()->setValue(
m_textDisplay->verticalScrollBar()->maximum());
// QPlainTextEdit destroys the first calls value in case of multiline
// text, so make sure that the scroll bar actually gets the value set.
// Is a noop if the first call succeeded.
m_textDisplay->verticalScrollBar()->setValue(
m_textDisplay->verticalScrollBar()->maximum());
}
//------------------------------------------------------------------------------
// Protected members
//------------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Private slot member functions
//-----------------------------------------------------------------------------
void MessageDisplay::showContextMenu(const QPoint &mousePos) {
QMenu *menu = m_textDisplay->createStandardContextMenu();
if (!m_textDisplay->document()->isEmpty())
menu->addAction("Clear", m_textDisplay, SLOT(clear()));
if (m_logLevelControl == MessageDisplay::EnableLogLevelControl) {
menu->addSeparator();
QMenu *logLevelMenu = menu->addMenu("&Log Level");
logLevelMenu->addAction(m_error);
logLevelMenu->addAction(m_warning);
logLevelMenu->addAction(m_notice);
logLevelMenu->addAction(m_information);
logLevelMenu->addAction(m_debug);
// check the right level
int level = m_filterChannel->getPriority();
// get the root logger logging level
int rootLevel = Poco::Logger::root().getLevel();
if (rootLevel < level) {
level = rootLevel;
if (level == Poco::Message::PRIO_ERROR)
m_error->setChecked(true);
if (level == Poco::Message::PRIO_WARNING)
m_warning->setChecked(true);
if (level == Poco::Message::PRIO_NOTICE)
m_notice->setChecked(true);
if (level == Poco::Message::PRIO_INFORMATION)
m_information->setChecked(true);
if (level >= Poco::Message::PRIO_DEBUG)
m_debug->setChecked(true);
}
menu->exec(this->mapToGlobal(mousePos));
delete menu;
}
/*
* @param priority An integer that must match the Poco::Message priority
* enumeration
*/
void MessageDisplay::setGlobalLogLevel(int priority) {
// set Local filter level
Mantid::Kernel::ConfigService::Instance().setFilterChannelLogLevel(
m_FilterChannelName, priority);
m_filterChannel->setPriority(priority);
}
//-----------------------------------------------------------------------------
// Private non-slot member functions
//-----------------------------------------------------------------------------
/*
*/
void MessageDisplay::initActions() {
m_error->setCheckable(true);
m_warning->setCheckable(true);
m_notice->setCheckable(true);
m_information->setCheckable(true);
m_debug->setCheckable(true);
m_loglevels->addAction(m_error);
m_loglevels->addAction(m_warning);
m_loglevels->addAction(m_notice);
m_loglevels->addAction(m_information);
m_loglevels->addAction(m_debug);
m_logLevelMapping->setMapping(m_error, Poco::Message::PRIO_ERROR);
m_logLevelMapping->setMapping(m_warning, Poco::Message::PRIO_WARNING);
m_logLevelMapping->setMapping(m_notice, Poco::Message::PRIO_NOTICE);
m_logLevelMapping->setMapping(m_information, Poco::Message::PRIO_INFORMATION);
m_logLevelMapping->setMapping(m_debug, Poco::Message::PRIO_DEBUG);
connect(m_error, SIGNAL(triggered()), m_logLevelMapping, SLOT(map()));
connect(m_warning, SIGNAL(triggered()), m_logLevelMapping, SLOT(map()));
connect(m_notice, SIGNAL(triggered()), m_logLevelMapping, SLOT(map()));
connect(m_information, SIGNAL(triggered()), m_logLevelMapping, SLOT(map()));
connect(m_debug, SIGNAL(triggered()), m_logLevelMapping, SLOT(map()));
connect(m_logLevelMapping, SIGNAL(mapped(int)), this,
SLOT(setGlobalLogLevel(int)));
}
/**
* Sets up the internal map of text formatters for each log level
*/
void MessageDisplay::initFormats() {
m_formats.clear();
QTextCharFormat format;
format.setForeground(Qt::red);
m_formats[Message::Priority::PRIO_ERROR] = format;
format.setForeground(QColor::fromRgb(255, 100, 0));
m_formats[Message::Priority::PRIO_WARNING] = format;
format.setForeground(Qt::gray);
m_formats[Message::Priority::PRIO_INFORMATION] = format;
format.setForeground(Qt::darkBlue);
m_formats[Message::Priority::PRIO_NOTICE] = format;
}
/**
* Set the properties of the text display, i.e read-only
* and make it occupy the whole widget
*/
void MessageDisplay::setupTextArea() {
m_textDisplay->setReadOnly(true);
m_textDisplay->ensureCursorVisible();
m_textDisplay->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
m_textDisplay->setMouseTracking(true);
m_textDisplay->setUndoRedoEnabled(false);
auto layoutBox = new QHBoxLayout(this);
layoutBox->setContentsMargins(0, 0, 0, 0);
layoutBox->addWidget(m_textDisplay);
this->setFocusProxy(m_textDisplay);
m_textDisplay->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_textDisplay, SIGNAL(customContextMenuRequested(const QPoint &)),
this, SLOT(showContextMenu(const QPoint &)));
}
/**
* @param priority An enumeration for the log level
* @return format for given log level
*/
QTextCharFormat MessageDisplay::format(const Message::Priority priority) const {
return m_formats.value(priority, QTextCharFormat());
}
}