Commit 53425816 authored by Erik Verbruggen's avatar Erik Verbruggen

Moved "Application Output" formatting to a specialized formatter.

By introducing the OutputFormatter, RunControls can influence the formatting
depening on the application started. A nice (and intended) side-effect is that
the QML runtime specific formatting will not interfere anymore with anything
else.
parent 890aca82
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include "outputformatter.h"
#include <QtGui/QPlainTextEdit>
using namespace ProjectExplorer;
OutputFormatter::OutputFormatter(QObject *parent)
: QObject(parent)
{
}
QPlainTextEdit *OutputFormatter::plainTextEdit() const
{
return m_plainTextEdit;
}
void OutputFormatter::setPlainTextEdit(QPlainTextEdit *plainText)
{
m_plainTextEdit = plainText;
setParent(m_plainTextEdit);
}
void OutputFormatter::appendOutput(const QString &text)
{
QTextCharFormat format;
format.setForeground(plainTextEdit()->palette().text().color());
plainTextEdit()->setCurrentCharFormat(format);
plainTextEdit()->insertPlainText(text);
}
void OutputFormatter::appendError(const QString &text)
{
appendOutput(text);
}
void OutputFormatter::mousePressEvent(QMouseEvent * /*e*/)
{}
void OutputFormatter::mouseReleaseEvent(QMouseEvent * /*e*/)
{}
void OutputFormatter::mouseMoveEvent(QMouseEvent * /*e*/)
{}
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#ifndef OUTPUTFORMATTER_H
#define OUTPUTFORMATTER_H
#include "projectexplorer_export.h"
#include <QtCore/QObject>
#include <QtCore/QString>
QT_FORWARD_DECLARE_CLASS(QPlainTextEdit);
QT_FORWARD_DECLARE_CLASS(QMouseEvent);
namespace ProjectExplorer {
class PROJECTEXPLORER_EXPORT OutputFormatter: public QObject
{
Q_OBJECT
public:
OutputFormatter(QObject *parent = 0);
QPlainTextEdit *plainTextEdit() const;
void setPlainTextEdit(QPlainTextEdit *plainText);
virtual void appendOutput(const QString &text);
virtual void appendError(const QString &text);
virtual void mousePressEvent(QMouseEvent *e);
virtual void mouseReleaseEvent(QMouseEvent *e);
virtual void mouseMoveEvent(QMouseEvent *e);
private:
QPlainTextEdit *m_plainTextEdit;
};
} // namespace ProjectExplorer
#endif // OUTPUTFORMATTER_H
......@@ -42,7 +42,6 @@
#include <coreplugin/icontext.h>
#include <find/basetextfind.h>
#include <aggregation/aggregate.h>
#include <texteditor/basetexteditor.h>
#include <QtGui/QIcon>
#include <QtGui/QScrollBar>
......@@ -176,11 +175,6 @@ void OutputPane::setFocus()
m_tabWidget->currentWidget()->setFocus();
}
void OutputPane::appendOutput(const QString &/*out*/)
{
// This function is in the interface, since we can't do anything sensible here, we don't do anything here.
}
void OutputPane::createNewOutputWindow(RunControl *rc)
{
connect(rc, SIGNAL(started()),
......@@ -199,6 +193,7 @@ void OutputPane::createNewOutputWindow(RunControl *rc)
OutputWindow *ow = static_cast<OutputWindow *>(m_tabWidget->widget(i));
ow->grayOutOldContent();
ow->verticalScrollBar()->setValue(ow->verticalScrollBar()->maximum());
ow->setFormatter(rc->createOutputFormatter(ow));
m_outputWindows.insert(rc, ow);
found = true;
break;
......@@ -206,6 +201,7 @@ void OutputPane::createNewOutputWindow(RunControl *rc)
}
if (!found) {
OutputWindow *ow = new OutputWindow(m_tabWidget);
ow->setFormatter(rc->createOutputFormatter(ow));
Aggregation::Aggregate *agg = new Aggregation::Aggregate;
agg->add(ow);
agg->add(new Find::BaseTextFind(ow));
......@@ -226,6 +222,12 @@ void OutputPane::appendOutputInline(RunControl *rc, const QString &out)
ow->appendOutputInline(out);
}
void OutputPane::appendError(RunControl *rc, const QString &out)
{
OutputWindow *ow = m_outputWindows.value(rc);
ow->appendError(out);
}
void OutputPane::showTabFor(RunControl *rc)
{
OutputWindow *ow = m_outputWindows.value(rc);
......@@ -346,11 +348,8 @@ bool OutputPane::canNavigate()
OutputWindow::OutputWindow(QWidget *parent)
: QPlainTextEdit(parent)
, m_qmlError(QLatin1String("(file:///[^:]+:\\d+:\\d+):"))
, m_enforceNewline(false)
, m_scrollToBottom(false)
, m_linksActive(true)
, m_mousePressed(false)
{
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
//setCenterOnScroll(false);
......@@ -405,6 +404,17 @@ OutputWindow::~OutputWindow()
delete m_outputWindowContext;
}
OutputFormatter *OutputWindow::formatter() const
{
return m_formatter;
}
void OutputWindow::setFormatter(OutputFormatter *formatter)
{
m_formatter = formatter;
m_formatter->setPlainTextEdit(this);
}
void OutputWindow::showEvent(QShowEvent *e)
{
QPlainTextEdit::showEvent(e);
......@@ -414,20 +424,28 @@ void OutputWindow::showEvent(QShowEvent *e)
m_scrollToBottom = false;
}
void OutputWindow::appendOutput(const QString &out)
QString OutputWindow::doNewlineMagic(const QString &out)
{
m_scrollToBottom = true;
QString s = out;
if (m_enforceNewline)
s.prepend(QLatin1Char('\n'));
m_enforceNewline = true; // make appendOutputInline put in a newline next time
if (s.endsWith(QLatin1Char('\n'))) {
if (s.endsWith(QLatin1Char('\n')))
s.chop(1);
}
setMaximumBlockCount(MaxBlockCount);
QTextCharFormat format;
format.setForeground(palette().text().color());
setCurrentCharFormat(format);
appendPlainText(out);
return s;
}
void OutputWindow::appendOutput(const QString &out)
{
setMaximumBlockCount(MaxBlockCount);
const bool atBottom = isScrollbarAtBottom();
m_formatter->appendOutput(doNewlineMagic(out));
if (atBottom)
scrollToBottom();
enableUndoRedo();
}
......@@ -439,15 +457,12 @@ void OutputWindow::appendOutputInline(const QString &out)
int newline = -1;
bool enforceNewline = m_enforceNewline;
m_enforceNewline = false;
const bool atBottom = isScrollbarAtBottom();
if (!enforceNewline) {
newline = out.indexOf(QLatin1Char('\n'));
moveCursor(QTextCursor::End);
bool atBottom = (blockBoundingRect(document()->lastBlock()).bottom() + contentOffset().y()
<= viewport()->rect().bottom());
insertPlainText(newline < 0 ? out : out.left(newline)); // doesn't enforce new paragraph like appendPlainText
if (atBottom)
verticalScrollBar()->setValue(verticalScrollBar()->maximum());
m_formatter->appendOutput(newline < 0 ? out : out.left(newline)); // doesn't enforce new paragraph like appendPlainText
}
QString s = out.mid(newline+1);
......@@ -458,28 +473,32 @@ void OutputWindow::appendOutputInline(const QString &out)
m_enforceNewline = true;
s.chop(1);
}
QTextCharFormat format;
format.setForeground(palette().text().color());
setCurrentCharFormat(format);
// (This feature depends on the availability of QPlainTextEdit::anchorAt)
// Convert to HTML, preserving newlines and whitespace
s = Qt::convertFromPlainText(s);
// Create links from QML errors (anything of the form "file:///...:[line]:[column]:")
int index = 0;
while ((index = m_qmlError.indexIn(s, index)) != -1) {
const QString captured = m_qmlError.cap(1);
const QString link = QString(QLatin1String("<a href=\"%1\">%2</a>")).arg(captured, captured);
s.replace(index, captured.length(), link);
index += link.length();
}
appendHtml(s);
m_formatter->appendOutput(QLatin1Char('\n') + s);
}
if (atBottom)
scrollToBottom();
enableUndoRedo();
}
void OutputWindow::appendError(const QString &out)
{
setMaximumBlockCount(MaxBlockCount);
m_formatter->appendError(doNewlineMagic(out));
enableUndoRedo();
}
bool OutputWindow::isScrollbarAtBottom() const
{
return blockBoundingRect(document()->lastBlock()).bottom() + contentOffset().y()
<= viewport()->rect().bottom();
}
void OutputWindow::scrollToBottom()
{
verticalScrollBar()->setValue(verticalScrollBar()->maximum());
}
void OutputWindow::grayOutOldContent()
{
QTextCursor cursor = textCursor();
......@@ -512,43 +531,17 @@ void OutputWindow::enableUndoRedo()
void OutputWindow::mousePressEvent(QMouseEvent *e)
{
QPlainTextEdit::mousePressEvent(e);
m_mousePressed = true;
m_formatter->mousePressEvent(e);
}
void OutputWindow::mouseReleaseEvent(QMouseEvent *e)
{
QPlainTextEdit::mouseReleaseEvent(e);
m_mousePressed = false;
if (!m_linksActive) {
// Mouse was released, activate links again
m_linksActive = true;
return;
}
const QString href = anchorAt(e->pos());
if (!href.isEmpty()) {
QRegExp qmlErrorLink(QLatin1String("^file://(/[^:]+):(\\d+):(\\d+)"));
if (qmlErrorLink.indexIn(href) != -1) {
const QString fileName = qmlErrorLink.cap(1);
const int line = qmlErrorLink.cap(2).toInt();
const int column = qmlErrorLink.cap(3).toInt();
TextEditor::BaseTextEditor::openEditorAt(fileName, line, column - 1);
}
}
m_formatter->mouseReleaseEvent(e);
}
void OutputWindow::mouseMoveEvent(QMouseEvent *e)
{
QPlainTextEdit::mouseMoveEvent(e);
// Cursor was dragged to make a selection, deactivate links
if (m_mousePressed && textCursor().hasSelection())
m_linksActive = false;
if (!m_linksActive || anchorAt(e->pos()).isEmpty())
viewport()->setCursor(Qt::IBeamCursor);
else
viewport()->setCursor(Qt::PointingHandCursor);
m_formatter->mouseMoveEvent(e);
}
......@@ -31,6 +31,7 @@
#define OUTPUTWINDOW_H
#include <coreplugin/ioutputpane.h>
#include <projectexplorer/outputformatter.h>
#include <QtCore/QObject>
#include <QtCore/QHash>
......@@ -83,18 +84,18 @@ public:
void goToPrev();
bool canNavigate();
void appendOutput(const QString &out);
// ApplicationOutputspecifics
void createNewOutputWindow(RunControl *rc);
void appendOutput(RunControl *rc, const QString &out);
void appendOutputInline(RunControl *rc, const QString &out);
void showTabFor(RunControl *rc);
public slots:
// ApplicationOutputspecifics
void createNewOutputWindow(RunControl *rc);
void projectRemoved();
void coreAboutToClose();
void appendOutput(RunControl *rc, const QString &out);
void appendOutputInline(RunControl *rc, const QString &out);
void appendError(RunControl *rc, const QString &out);
private slots:
void reRunRunControl();
void stopRunControl();
......@@ -123,8 +124,13 @@ public:
OutputWindow(QWidget *parent = 0);
~OutputWindow();
OutputFormatter* formatter() const;
void setFormatter(OutputFormatter *formatter);
void appendOutput(const QString &out);
void appendOutputInline(const QString &out);
void appendError(const QString &out);
void grayOutOldContent();
void showEvent(QShowEvent *);
......@@ -134,15 +140,19 @@ protected:
void mouseReleaseEvent(QMouseEvent *e);
void mouseMoveEvent(QMouseEvent *e);
bool isScrollbarAtBottom() const;
void scrollToBottom();
private:
Core::BaseContext *m_outputWindowContext;
void enableUndoRedo();
QString doNewlineMagic(const QString &out);
private:
Core::BaseContext *m_outputWindowContext;
OutputFormatter *m_formatter;
QRegExp m_qmlError;
bool m_enforceNewline;
bool m_scrollToBottom;
bool m_linksActive;
bool m_mousePressed;
};
} // namespace Internal
......
......@@ -1274,11 +1274,12 @@ void ProjectExplorerPlugin::startRunControl(RunControl *runControl, const QStrin
d->m_outputPane->clearContents();
connect(runControl, SIGNAL(addToOutputWindow(RunControl *, const QString &)),
this, SLOT(addToApplicationOutputWindow(RunControl *, const QString &)));
d->m_outputPane, SLOT(appendOutput(RunControl*,const QString &)));
connect(runControl, SIGNAL(addToOutputWindowInline(RunControl *, const QString &)),
this, SLOT(addToApplicationOutputWindowInline(RunControl *, const QString &)));
d->m_outputPane, SLOT(appendOutputInline(RunControl*,const QString &)));
connect(runControl, SIGNAL(error(RunControl *, const QString &)),
this, SLOT(addErrorToApplicationOutputWindow(RunControl *, const QString &)));
d->m_outputPane, SLOT(appendError(RunControl *, const QString &)));
connect(runControl, SIGNAL(finished()),
this, SLOT(runControlFinished()));
......@@ -1711,21 +1712,6 @@ bool ProjectExplorerPlugin::showBuildConfigDialog()
}
}
void ProjectExplorerPlugin::addToApplicationOutputWindow(RunControl *rc, const QString &line)
{
d->m_outputPane->appendOutput(rc, line);
}
void ProjectExplorerPlugin::addToApplicationOutputWindowInline(RunControl *rc, const QString &line)
{
d->m_outputPane->appendOutputInline(rc, line);
}
void ProjectExplorerPlugin::addErrorToApplicationOutputWindow(RunControl *rc, const QString &error)
{
d->m_outputPane->appendOutput(rc, error);
}
void ProjectExplorerPlugin::runControlFinished()
{
if (sender() == d->m_debuggingRunControl)
......
......@@ -182,10 +182,6 @@ private slots:
void startupProjectChanged(); // Calls updateRunAction
void updateRunActions();
void addToApplicationOutputWindow(RunControl *, const QString &line);
void addToApplicationOutputWindowInline(RunControl *, const QString &line);
void addErrorToApplicationOutputWindow(RunControl *, const QString &error);
void loadProject(const QString &project) { openProject(project); }
void currentModeChanged(Core::IMode *mode, Core::IMode *oldMode);
void updateActions();
......
......@@ -79,7 +79,8 @@ HEADERS += projectexplorer.h \
buildenvironmentwidget.h \
buildconfigdialog.h \
ldparser.h \
linuxiccparser.h
linuxiccparser.h \
outputformatter.h
SOURCES += projectexplorer.cpp \
projectwindow.cpp \
buildmanager.cpp \
......@@ -145,7 +146,8 @@ SOURCES += projectexplorer.cpp \
buildenvironmentwidget.cpp \
buildconfigdialog.cpp \
ldparser.cpp \
linuxiccparser.cpp
linuxiccparser.cpp \
outputformatter.cpp
FORMS += processstep.ui \
editorsettingspropertiespage.ui \
runsettingspropertiespage.ui \
......
......@@ -29,6 +29,7 @@
#include "runconfiguration.h"
#include "outputformatter.h"
#include "project.h"
#include "target.h"
#include "buildconfiguration.h"
......@@ -236,6 +237,11 @@ bool RunControl::sameRunConfiguration(RunControl *other)
return other->m_runConfiguration.data() == m_runConfiguration.data();
}
OutputFormatter *RunControl::createOutputFormatter(QObject *parent)
{
return new OutputFormatter(parent);
}
void RunControl::bringApplicationToForeground(qint64 pid)
{
#ifdef Q_OS_MAC
......
......@@ -47,6 +47,7 @@ class Target;
class RunControl;
class BuildConfiguration;
class OutputFormatter;
/**
* Base class for a run configuration. A run configuration specifies how a
......@@ -168,6 +169,8 @@ public:
bool sameRunConfiguration(RunControl *other);
virtual OutputFormatter *createOutputFormatter(QObject *parent = 0);
signals:
void addToOutputWindow(RunControl *, const QString &line);
void addToOutputWindowInline(RunControl *, const QString &line);
......
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include "qmloutputformatter.h"
#include <texteditor/basetexteditor.h>
#include <QtGui/QPlainTextEdit>
using namespace ProjectExplorer;
using namespace QmlProjectManager::Internal;
QmlOutputFormatter::QmlOutputFormatter(QObject *parent)
: OutputFormatter(parent)
, m_qmlError(QLatin1String("(file:///[^:]+:\\d+:\\d+):"))
, m_linksActive(true)
, m_mousePressed(false)
{
}
void QmlOutputFormatter::appendOutput(const QString &text)
{
QTextCharFormat normalFormat, linkFormat;
normalFormat.setForeground(plainTextEdit()->palette().text().color());
linkFormat.setForeground(plainTextEdit()->palette().link().color());
linkFormat.setUnderlineStyle(QTextCharFormat::SingleUnderline);
linkFormat.setAnchor(true);
plainTextEdit()->setCurrentCharFormat(normalFormat);
// Create links from QML errors (anything of the form "file:///...:[line]:[column]:")
int index = 0;