forked from qt-creator/qt-creator
Make output window implementation reusable.
Removes the dependencies to project explorer and text editor plugins and moves unrelated code to its own file.
This commit is contained in:
335
src/plugins/coreplugin/outputwindow.cpp
Normal file
335
src/plugins/coreplugin/outputwindow.cpp
Normal file
@@ -0,0 +1,335 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (info@qt.nokia.com)
|
||||
**
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** Other Usage
|
||||
**
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "outputwindow.h"
|
||||
|
||||
#include <coreplugin/actionmanager/actionmanager.h>
|
||||
#include <coreplugin/actionmanager/command.h>
|
||||
#include <coreplugin/coreconstants.h>
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/outputformatter.h>
|
||||
|
||||
#include <QtGui/QAction>
|
||||
#include <QtGui/QScrollBar>
|
||||
|
||||
static const int MaxBlockCount = 100000;
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
namespace ProjectExplorer {
|
||||
namespace Internal {
|
||||
|
||||
/*******************/
|
||||
|
||||
OutputWindow::OutputWindow(Core::Context context, QWidget *parent)
|
||||
: QPlainTextEdit(parent)
|
||||
, m_formatter(0)
|
||||
, m_enforceNewline(false)
|
||||
, m_scrollToBottom(false)
|
||||
, m_linksActive(true)
|
||||
, m_mousePressed(false)
|
||||
{
|
||||
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
|
||||
//setCenterOnScroll(false);
|
||||
setFrameShape(QFrame::NoFrame);
|
||||
setMouseTracking(true);
|
||||
|
||||
Core::ICore *core = Core::ICore::instance();
|
||||
|
||||
m_outputWindowContext = new Core::IContext;
|
||||
m_outputWindowContext->setContext(context);
|
||||
m_outputWindowContext->setWidget(this);
|
||||
core->addContextObject(m_outputWindowContext);
|
||||
|
||||
QAction *undoAction = new QAction(this);
|
||||
QAction *redoAction = new QAction(this);
|
||||
QAction *cutAction = new QAction(this);
|
||||
QAction *copyAction = new QAction(this);
|
||||
QAction *pasteAction = new QAction(this);
|
||||
QAction *selectAllAction = new QAction(this);
|
||||
|
||||
Core::ActionManager *am = core->actionManager();
|
||||
am->registerAction(undoAction, Core::Constants::UNDO, context);
|
||||
am->registerAction(redoAction, Core::Constants::REDO, context);
|
||||
am->registerAction(cutAction, Core::Constants::CUT, context);
|
||||
am->registerAction(copyAction, Core::Constants::COPY, context);
|
||||
am->registerAction(pasteAction, Core::Constants::PASTE, context);
|
||||
am->registerAction(selectAllAction, Core::Constants::SELECTALL, context);
|
||||
|
||||
connect(undoAction, SIGNAL(triggered()), this, SLOT(undo()));
|
||||
connect(redoAction, SIGNAL(triggered()), this, SLOT(redo()));
|
||||
connect(cutAction, SIGNAL(triggered()), this, SLOT(cut()));
|
||||
connect(copyAction, SIGNAL(triggered()), this, SLOT(copy()));
|
||||
connect(pasteAction, SIGNAL(triggered()), this, SLOT(paste()));
|
||||
connect(selectAllAction, SIGNAL(triggered()), this, SLOT(selectAll()));
|
||||
|
||||
connect(this, SIGNAL(undoAvailable(bool)), undoAction, SLOT(setEnabled(bool)));
|
||||
connect(this, SIGNAL(redoAvailable(bool)), redoAction, SLOT(setEnabled(bool)));
|
||||
connect(this, SIGNAL(copyAvailable(bool)), cutAction, SLOT(setEnabled(bool))); // OutputWindow never read-only
|
||||
connect(this, SIGNAL(copyAvailable(bool)), copyAction, SLOT(setEnabled(bool)));
|
||||
|
||||
undoAction->setEnabled(false);
|
||||
redoAction->setEnabled(false);
|
||||
cutAction->setEnabled(false);
|
||||
copyAction->setEnabled(false);
|
||||
}
|
||||
|
||||
OutputWindow::~OutputWindow()
|
||||
{
|
||||
Core::ICore::instance()->removeContextObject(m_outputWindowContext);
|
||||
delete m_outputWindowContext;
|
||||
}
|
||||
|
||||
void OutputWindow::mousePressEvent(QMouseEvent * e)
|
||||
{
|
||||
m_mousePressed = true;
|
||||
QPlainTextEdit::mousePressEvent(e);
|
||||
}
|
||||
|
||||
void OutputWindow::mouseReleaseEvent(QMouseEvent *e)
|
||||
{
|
||||
m_mousePressed = false;
|
||||
|
||||
if (m_linksActive) {
|
||||
const QString href = anchorAt(e->pos());
|
||||
if (m_formatter)
|
||||
m_formatter->handleLink(href);
|
||||
}
|
||||
|
||||
// Mouse was released, activate links again
|
||||
m_linksActive = true;
|
||||
|
||||
QPlainTextEdit::mouseReleaseEvent(e);
|
||||
}
|
||||
|
||||
void OutputWindow::mouseMoveEvent(QMouseEvent *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);
|
||||
QPlainTextEdit::mouseMoveEvent(e);
|
||||
}
|
||||
|
||||
void OutputWindow::resizeEvent(QResizeEvent *e)
|
||||
{
|
||||
//Keep scrollbar at bottom of window while resizing, to ensure we keep scrolling
|
||||
//This can happen if window is resized while building, or if the horizontal scrollbar appears
|
||||
bool atBottom = isScrollbarAtBottom();
|
||||
QPlainTextEdit::resizeEvent(e);
|
||||
if (atBottom)
|
||||
scrollToBottom();
|
||||
}
|
||||
|
||||
void OutputWindow::keyPressEvent(QKeyEvent *ev)
|
||||
{
|
||||
QPlainTextEdit::keyPressEvent(ev);
|
||||
|
||||
//Ensure we scroll also on Ctrl+Home or Ctrl+End
|
||||
if (ev->matches(QKeySequence::MoveToStartOfDocument))
|
||||
verticalScrollBar()->triggerAction(QAbstractSlider::SliderToMinimum);
|
||||
else if (ev->matches(QKeySequence::MoveToEndOfDocument))
|
||||
verticalScrollBar()->triggerAction(QAbstractSlider::SliderToMaximum);
|
||||
}
|
||||
|
||||
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);
|
||||
if (m_scrollToBottom) {
|
||||
verticalScrollBar()->setValue(verticalScrollBar()->maximum());
|
||||
}
|
||||
m_scrollToBottom = false;
|
||||
}
|
||||
|
||||
QString OutputWindow::doNewlineEnfocement(const QString &out)
|
||||
{
|
||||
m_scrollToBottom = true;
|
||||
QString s = out;
|
||||
if (m_enforceNewline) {
|
||||
s.prepend(QLatin1Char('\n'));
|
||||
m_enforceNewline = false;
|
||||
}
|
||||
|
||||
if (s.endsWith(QLatin1Char('\n'))) {
|
||||
m_enforceNewline = true; // make appendOutputInline put in a newline next time
|
||||
s.chop(1);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void OutputWindow::appendMessage(const QString &output, OutputFormat format)
|
||||
{
|
||||
QString out = output;
|
||||
out.remove(QLatin1Char('\r'));
|
||||
setMaximumBlockCount(MaxBlockCount);
|
||||
const bool atBottom = isScrollbarAtBottom();
|
||||
|
||||
if (format == ErrorMessageFormat || format == NormalMessageFormat) {
|
||||
|
||||
m_formatter->appendMessage(doNewlineEnfocement(out), format);
|
||||
|
||||
} else {
|
||||
|
||||
bool sameLine = format == StdOutFormatSameLine
|
||||
|| format == StdErrFormatSameLine;
|
||||
|
||||
if (sameLine) {
|
||||
m_scrollToBottom = true;
|
||||
|
||||
int newline = -1;
|
||||
bool enforceNewline = m_enforceNewline;
|
||||
m_enforceNewline = false;
|
||||
|
||||
if (!enforceNewline) {
|
||||
newline = out.indexOf(QLatin1Char('\n'));
|
||||
moveCursor(QTextCursor::End);
|
||||
if (newline != -1)
|
||||
m_formatter->appendMessage(out.left(newline), format);// doesn't enforce new paragraph like appendPlainText
|
||||
}
|
||||
|
||||
QString s = out.mid(newline+1);
|
||||
if (s.isEmpty()) {
|
||||
m_enforceNewline = true;
|
||||
} else {
|
||||
if (s.endsWith(QLatin1Char('\n'))) {
|
||||
m_enforceNewline = true;
|
||||
s.chop(1);
|
||||
}
|
||||
m_formatter->appendMessage(QLatin1Char('\n') + s, format);
|
||||
}
|
||||
} else {
|
||||
m_formatter->appendMessage(doNewlineEnfocement(out), format);
|
||||
}
|
||||
}
|
||||
|
||||
if (atBottom)
|
||||
scrollToBottom();
|
||||
enableUndoRedo();
|
||||
}
|
||||
|
||||
// TODO rename
|
||||
void OutputWindow::appendText(const QString &textIn, const QTextCharFormat &format, int maxLineCount)
|
||||
{
|
||||
QString text = textIn;
|
||||
text.remove(QLatin1Char('\r'));
|
||||
if (document()->blockCount() > maxLineCount)
|
||||
return;
|
||||
const bool atBottom = isScrollbarAtBottom();
|
||||
QTextCursor cursor = QTextCursor(document());
|
||||
cursor.movePosition(QTextCursor::End);
|
||||
cursor.beginEditBlock();
|
||||
cursor.insertText(doNewlineEnfocement(text), format);
|
||||
|
||||
if (document()->blockCount() > maxLineCount) {
|
||||
QTextCharFormat tmp;
|
||||
tmp.setFontWeight(QFont::Bold);
|
||||
cursor.insertText(tr("Additional output omitted\n"), tmp);
|
||||
}
|
||||
|
||||
cursor.endEditBlock();
|
||||
if (atBottom)
|
||||
scrollToBottom();
|
||||
}
|
||||
|
||||
bool OutputWindow::isScrollbarAtBottom() const
|
||||
{
|
||||
return verticalScrollBar()->value() == verticalScrollBar()->maximum();
|
||||
}
|
||||
|
||||
void OutputWindow::clear()
|
||||
{
|
||||
m_enforceNewline = false;
|
||||
QPlainTextEdit::clear();
|
||||
}
|
||||
|
||||
void OutputWindow::scrollToBottom()
|
||||
{
|
||||
verticalScrollBar()->setValue(verticalScrollBar()->maximum());
|
||||
}
|
||||
|
||||
void OutputWindow::grayOutOldContent()
|
||||
{
|
||||
QTextCursor cursor = textCursor();
|
||||
cursor.movePosition(QTextCursor::End);
|
||||
QTextCharFormat endFormat = cursor.charFormat();
|
||||
|
||||
cursor.select(QTextCursor::Document);
|
||||
|
||||
QTextCharFormat format;
|
||||
const QColor bkgColor = palette().base().color();
|
||||
const QColor fgdColor = palette().text().color();
|
||||
double bkgFactor = 0.50;
|
||||
double fgdFactor = 1.-bkgFactor;
|
||||
format.setForeground(QColor((bkgFactor * bkgColor.red() + fgdFactor * fgdColor.red()),
|
||||
(bkgFactor * bkgColor.green() + fgdFactor * fgdColor.green()),
|
||||
(bkgFactor * bkgColor.blue() + fgdFactor * fgdColor.blue()) ));
|
||||
cursor.mergeCharFormat(format);
|
||||
|
||||
cursor.movePosition(QTextCursor::End);
|
||||
cursor.setCharFormat(endFormat);
|
||||
cursor.insertBlock(QTextBlockFormat());
|
||||
}
|
||||
|
||||
void OutputWindow::enableUndoRedo()
|
||||
{
|
||||
setMaximumBlockCount(0);
|
||||
setUndoRedoEnabled(true);
|
||||
}
|
||||
|
||||
void OutputWindow::setWordWrapEnabled(bool wrap)
|
||||
{
|
||||
if (wrap)
|
||||
setWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
|
||||
else
|
||||
setWordWrapMode(QTextOption::NoWrap);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ProjectExplorer
|
||||
Reference in New Issue
Block a user