Files
qt-creator/src/plugins/coreplugin/outputwindow.cpp
Robert Loehning 746c5d8863 Incremented year in copyright info
Change-Id: Ib5423fdd064e4546f848c0b640b0ed0514c26d3a
Reviewed-by: Leena Miettinen <riitta-leena.miettinen@digia.com>
Reviewed-by: Kai Koehne <kai.koehne@digia.com>
2014-01-08 08:29:47 +01:00

333 lines
11 KiB
C++

/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** 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.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "outputwindow.h"
#include "actionmanager/actionmanager.h"
#include "coreconstants.h"
#include "icore.h"
#include <utils/synchronousprocess.h>
#include <QAction>
#include <QScrollBar>
using namespace Utils;
namespace Core {
/*******************/
OutputWindow::OutputWindow(Core::Context context, QWidget *parent)
: QPlainTextEdit(parent)
, m_formatter(0)
, m_enforceNewline(false)
, m_scrollToBottom(false)
, m_linksActive(true)
, m_mousePressed(false)
, m_maxLineCount(100000)
{
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
//setCenterOnScroll(false);
setFrameShape(QFrame::NoFrame);
setMouseTracking(true);
setUndoRedoEnabled(false);
m_outputWindowContext = new Core::IContext;
m_outputWindowContext->setContext(context);
m_outputWindowContext->setWidget(this);
ICore::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);
ActionManager::registerAction(undoAction, Core::Constants::UNDO, context);
ActionManager::registerAction(redoAction, Core::Constants::REDO, context);
ActionManager::registerAction(cutAction, Core::Constants::CUT, context);
ActionManager::registerAction(copyAction, Core::Constants::COPY, context);
ActionManager::registerAction(pasteAction, Core::Constants::PASTE, context);
ActionManager::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::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::doNewlineEnforcement(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::setMaxLineCount(int count)
{
m_maxLineCount = count;
setMaximumBlockCount(m_maxLineCount);
}
void OutputWindow::appendMessage(const QString &output, OutputFormat format)
{
const QString out = Utils::SynchronousProcess::normalizeNewlines(output);
setMaximumBlockCount(m_maxLineCount);
const bool atBottom = isScrollbarAtBottom();
if (format == ErrorMessageFormat || format == NormalMessageFormat) {
m_formatter->appendMessage(doNewlineEnforcement(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(doNewlineEnforcement(out), format);
}
}
if (atBottom)
scrollToBottom();
enableUndoRedo();
}
// TODO rename
void OutputWindow::appendText(const QString &textIn, const QTextCharFormat &format)
{
const QString text = Utils::SynchronousProcess::normalizeNewlines(textIn);
if (m_maxLineCount > 0 && document()->blockCount() >= m_maxLineCount)
return;
const bool atBottom = isScrollbarAtBottom();
QTextCursor cursor = QTextCursor(document());
cursor.movePosition(QTextCursor::End);
cursor.beginEditBlock();
cursor.insertText(doNewlineEnforcement(text), format);
if (m_maxLineCount > 0 && document()->blockCount() >= m_maxLineCount) {
QTextCharFormat tmp;
tmp.setFontWeight(QFont::Bold);
cursor.insertText(doNewlineEnforcement(tr("Additional output omitted") + QLatin1Char('\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());
// 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.
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 Core