forked from qt-creator/qt-creator
		
	Change-Id: Ib5423fdd064e4546f848c0b640b0ed0514c26d3a Reviewed-by: Leena Miettinen <riitta-leena.miettinen@digia.com> Reviewed-by: Kai Koehne <kai.koehne@digia.com>
		
			
				
	
	
		
			333 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			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
 |