From: con Date: Thu, 14 Apr 2011 08:39:09 +0000 (+0200) Subject: Make output window implementation reusable. X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=ef771552913401428d01dc0ee4b56d4d07fc522b;p=qt-creator-jp%2Fqt-creator-jp.git Make output window implementation reusable. Removes the dependencies to project explorer and text editor plugins and moves unrelated code to its own file. --- diff --git a/src/plugins/coreplugin/outputwindow.cpp b/src/plugins/coreplugin/outputwindow.cpp new file mode 100644 index 0000000000..e992df6546 --- /dev/null +++ b/src/plugins/coreplugin/outputwindow.cpp @@ -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 +#include +#include +#include + +#include +#include + +#include +#include + +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 diff --git a/src/plugins/coreplugin/outputwindow.h b/src/plugins/coreplugin/outputwindow.h new file mode 100644 index 0000000000..37bde29986 --- /dev/null +++ b/src/plugins/coreplugin/outputwindow.h @@ -0,0 +1,99 @@ +/************************************************************************** +** +** 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. +** +**************************************************************************/ + +#ifndef OUTPUTWINDOW_H +#define OUTPUTWINDOW_H + +#include +#include + +#include + +namespace Core { + class IContext; +} + +namespace ProjectExplorer { + +namespace Internal { + +class OutputWindow : public QPlainTextEdit +{ + Q_OBJECT + +public: + OutputWindow(Core::Context context, QWidget *parent = 0); + ~OutputWindow(); + + Utils::OutputFormatter* formatter() const; + void setFormatter(Utils::OutputFormatter *formatter); + + void appendMessage(const QString &out, Utils::OutputFormat format); + /// appends a \p text using \p format without using formater + void appendText(const QString &text, const QTextCharFormat &format, int maxLineCount); + + void grayOutOldContent(); + void clear(); + + void showEvent(QShowEvent *); + + void scrollToBottom(); + +public slots: + void setWordWrapEnabled(bool wrap); + +protected: + bool isScrollbarAtBottom() const; + + virtual void mousePressEvent(QMouseEvent *e); + virtual void mouseReleaseEvent(QMouseEvent *e); + virtual void mouseMoveEvent(QMouseEvent *e); + virtual void resizeEvent(QResizeEvent *e); + virtual void keyPressEvent(QKeyEvent *ev); + +private: + void enableUndoRedo(); + QString doNewlineEnfocement(const QString &out); + + Core::IContext *m_outputWindowContext; + Utils::OutputFormatter *m_formatter; + + bool m_enforceNewline; + bool m_scrollToBottom; + bool m_linksActive; + bool m_mousePressed; +}; + +} // namespace Internal +} // namespace ProjectExplorer + +#endif // OUTPUTWINDOW_H diff --git a/src/plugins/projectexplorer/appoutputpane.cpp b/src/plugins/projectexplorer/appoutputpane.cpp new file mode 100644 index 0000000000..1b7b9aaf20 --- /dev/null +++ b/src/plugins/projectexplorer/appoutputpane.cpp @@ -0,0 +1,483 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#include "appoutputpane.h" +#include "outputwindow.h" +#include "projectexplorerconstants.h" +#include "projectexplorer.h" +#include "projectexplorersettings.h" +#include "runconfiguration.h" +#include "session.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +enum { debug = 0 }; + +using namespace ProjectExplorer; +using namespace ProjectExplorer::Internal; + +AppOutputPane::RunControlTab::RunControlTab(RunControl *rc, OutputWindow *w) : + runControl(rc), window(w), asyncClosing(false) +{ +} + +AppOutputPane::AppOutputPane() : + m_mainWidget(new QWidget), + m_tabWidget(new QTabWidget), + m_stopAction(new QAction(QIcon(QLatin1String(Constants::ICON_STOP)), tr("Stop"), this)), + m_reRunButton(new QToolButton), + m_stopButton(new QToolButton) +{ + // Rerun + m_reRunButton->setIcon(QIcon(ProjectExplorer::Constants::ICON_RUN_SMALL)); + m_reRunButton->setToolTip(tr("Re-run this run-configuration")); + m_reRunButton->setAutoRaise(true); + m_reRunButton->setEnabled(false); + connect(m_reRunButton, SIGNAL(clicked()), + this, SLOT(reRunRunControl())); + + // Stop + Core::ActionManager *am = Core::ICore::instance()->actionManager(); + Core::Context globalcontext(Core::Constants::C_GLOBAL); + + m_stopAction->setToolTip(tr("Stop")); + m_stopAction->setEnabled(false); + + Core::Command *cmd = am->registerAction(m_stopAction, Constants::STOP, globalcontext); + + m_stopButton->setDefaultAction(cmd->action()); + m_stopButton->setAutoRaise(true); + + connect(m_stopAction, SIGNAL(triggered()), + this, SLOT(stopRunControl())); + + // Spacer (?) + + QVBoxLayout *layout = new QVBoxLayout; + layout->setMargin(0); + m_tabWidget->setDocumentMode(true); + m_tabWidget->setTabsClosable(true); + m_tabWidget->setMovable(true); + connect(m_tabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int))); + layout->addWidget(m_tabWidget); + + connect(m_tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int))); + + m_mainWidget->setLayout(layout); + + connect(ProjectExplorerPlugin::instance()->session(), SIGNAL(aboutToUnloadSession()), + this, SLOT(aboutToUnloadSession())); + connect(ProjectExplorerPlugin::instance(), SIGNAL(settingsChanged()), + this, SLOT(updateWordWrapMode())); +} + +AppOutputPane::~AppOutputPane() +{ + if (debug) + qDebug() << "OutputPane::~OutputPane: Entries left" << m_runControlTabs.size(); + + foreach(const RunControlTab &rt, m_runControlTabs) + delete rt.runControl; + delete m_mainWidget; +} + +int AppOutputPane::currentIndex() const +{ + if (const QWidget *w = m_tabWidget->currentWidget()) + return indexOf(w); + return -1; +} + +RunControl *AppOutputPane::currentRunControl() const +{ + const int index = currentIndex(); + if (index != -1) + return m_runControlTabs.at(index).runControl; + return 0; +} + +int AppOutputPane::indexOf(const RunControl *rc) const +{ + for (int i = m_runControlTabs.size() - 1; i >= 0; i--) + if (m_runControlTabs.at(i).runControl == rc) + return i; + return -1; +} + +int AppOutputPane::indexOf(const QWidget *outputWindow) const +{ + for (int i = m_runControlTabs.size() - 1; i >= 0; i--) + if (m_runControlTabs.at(i).window == outputWindow) + return i; + return -1; +} + +int AppOutputPane::tabWidgetIndexOf(int runControlIndex) const +{ + if (runControlIndex >= 0 && runControlIndex < m_runControlTabs.size()) + return m_tabWidget->indexOf(m_runControlTabs.at(runControlIndex).window); + return -1; +} + +bool AppOutputPane::aboutToClose() const +{ + foreach(const RunControlTab &rt, m_runControlTabs) + if (rt.runControl->isRunning() && !rt.runControl->promptToStop()) + return false; + return true; +} + +void AppOutputPane::aboutToUnloadSession() +{ + closeTabs(CloseTabWithPrompt); +} + +QWidget *AppOutputPane::outputWidget(QWidget *) +{ + return m_mainWidget; +} + +QList AppOutputPane::toolBarWidgets() const +{ + return QList() << m_reRunButton << m_stopButton; +} + +QString AppOutputPane::displayName() const +{ + return tr("Application Output"); +} + +int AppOutputPane::priorityInStatusBar() const +{ + return 60; +} + +void AppOutputPane::clearContents() +{ + OutputWindow *currentWindow = qobject_cast(m_tabWidget->currentWidget()); + if (currentWindow) + currentWindow->clear(); +} + +void AppOutputPane::visibilityChanged(bool /* b */) +{ +} + +bool AppOutputPane::hasFocus() +{ + return m_tabWidget->currentWidget() && m_tabWidget->currentWidget()->hasFocus(); +} + +bool AppOutputPane::canFocus() +{ + return m_tabWidget->currentWidget(); +} + +void AppOutputPane::setFocus() +{ + if (m_tabWidget->currentWidget()) + m_tabWidget->currentWidget()->setFocus(); +} + +void AppOutputPane::createNewOutputWindow(RunControl *rc) +{ + connect(rc, SIGNAL(started()), + this, SLOT(runControlStarted())); + connect(rc, SIGNAL(finished()), + this, SLOT(runControlFinished())); + connect(rc, SIGNAL(appendMessage(ProjectExplorer::RunControl*,QString,Utils::OutputFormat)), + this, SLOT(appendMessage(ProjectExplorer::RunControl*,QString,Utils::OutputFormat))); + + Utils::OutputFormatter *formatter = rc->outputFormatter(); + formatter->setFont(TextEditor::TextEditorSettings::instance()->fontSettings().font()); + + // First look if we can reuse a tab + const int size = m_runControlTabs.size(); + for (int i = 0; i < size; i++) { + RunControlTab &tab =m_runControlTabs[i]; + if (tab.runControl->sameRunConfiguration(rc) && !tab.runControl->isRunning()) { + // Reuse this tab + delete tab.runControl; + tab.runControl = rc; + handleOldOutput(tab.window); + tab.window->scrollToBottom(); + tab.window->setFormatter(formatter); + if (debug) + qDebug() << "OutputPane::createNewOutputWindow: Reusing tab" << i << " for " << rc; + return; + } + } + // Create new + static uint counter = 0; + Core::Context context(Constants::C_APP_OUTPUT, counter++); + OutputWindow *ow = new OutputWindow(context, m_tabWidget); + ow->setWindowTitle(tr("Application Output Window")); + // TODO the following is a hidden impossible dependency of projectexplorer on qt4projectmanager + ow->setWindowIcon(QIcon(QLatin1String(Qt4ProjectManager::Constants::ICON_WINDOW))); + ow->setFormatter(formatter); + ow->setWordWrapEnabled(ProjectExplorerPlugin::instance()->projectExplorerSettings().wrapAppOutput); + Aggregation::Aggregate *agg = new Aggregation::Aggregate; + agg->add(ow); + agg->add(new Find::BaseTextFind(ow)); + m_runControlTabs.push_back(RunControlTab(rc, ow)); + m_tabWidget->addTab(ow, rc->displayName()); + if (debug) + qDebug() << "OutputPane::createNewOutputWindow: Adding tab for " << rc; +} + +void AppOutputPane::handleOldOutput(OutputWindow *window) const +{ + if (ProjectExplorerPlugin::instance()->projectExplorerSettings().cleanOldAppOutput) + window->clear(); + else + window->grayOutOldContent(); +} + +void AppOutputPane::updateWordWrapMode() +{ + const int size = m_runControlTabs.size(); + for (int i = 0; i < size; i++) { + RunControlTab &tab =m_runControlTabs[i]; + tab.window->setWordWrapEnabled(ProjectExplorerPlugin::instance()->projectExplorerSettings().wrapAppOutput); + } +} + +void AppOutputPane::appendMessage(RunControl *rc, const QString &out, Utils::OutputFormat format) +{ + const int index = indexOf(rc); + if (index != -1) + m_runControlTabs.at(index).window->appendMessage(out, format); +} + +void AppOutputPane::showTabFor(RunControl *rc) +{ + m_tabWidget->setCurrentIndex(tabWidgetIndexOf(indexOf(rc))); +} + +void AppOutputPane::reRunRunControl() +{ + const int index = currentIndex(); + QTC_ASSERT(index != -1 && !m_runControlTabs.at(index).runControl->isRunning(), return;) + + RunControlTab &tab = m_runControlTabs[index]; + + handleOldOutput(tab.window); + tab.window->scrollToBottom(); + tab.runControl->start(); +} + +void AppOutputPane::stopRunControl() +{ + const int index = currentIndex(); + QTC_ASSERT(index != -1 && m_runControlTabs.at(index).runControl->isRunning(), return;) + + RunControl *rc = m_runControlTabs.at(index).runControl; + if (rc->isRunning() && optionallyPromptToStop(rc)) + rc->stop(); + + if (debug) + qDebug() << "OutputPane::stopRunControl " << rc; +} + +bool AppOutputPane::closeTabs(CloseTabMode mode) +{ + bool allClosed = true; + for (int t = m_tabWidget->count() - 1; t >= 0; t--) + if (!closeTab(t, mode)) + allClosed = false; + if (debug) + qDebug() << "OutputPane::closeTabs() returns " << allClosed; + return allClosed; +} + +bool AppOutputPane::closeTab(int index) +{ + return closeTab(index, CloseTabWithPrompt); +} + +bool AppOutputPane::closeTab(int tabIndex, CloseTabMode closeTabMode) +{ + const int index = indexOf(m_tabWidget->widget(tabIndex)); + QTC_ASSERT(index != -1, return true;) + + RunControlTab &tab = m_runControlTabs[index]; + + if (debug) + qDebug() << "OutputPane::closeTab tab " << tabIndex << tab.runControl + << tab.window << tab.asyncClosing; + // Prompt user to stop + if (tab.runControl->isRunning()) { + switch (closeTabMode) { + case CloseTabNoPrompt: + break; + case CloseTabWithPrompt: + if (!tab.runControl->promptToStop()) + return false; + break; + } + if (tab.runControl->stop() == RunControl::AsynchronousStop) { + tab.asyncClosing = true; + return false; + } + } + + m_tabWidget->removeTab(tabIndex); + if (tab.asyncClosing) { // We were invoked from its finished() signal. + tab.runControl->deleteLater(); + } else { + delete tab.runControl; + } + delete tab.window; + m_runControlTabs.removeAt(index); + return true; +} + +bool AppOutputPane::optionallyPromptToStop(RunControl *runControl) +{ + ProjectExplorerPlugin *pe = ProjectExplorerPlugin::instance(); + ProjectExplorerSettings settings = pe->projectExplorerSettings(); + if (!runControl->promptToStop(&settings.prompToStopRunControl)) + return false; + pe->setProjectExplorerSettings(settings); + return true; +} + +void AppOutputPane::projectRemoved() +{ + tabChanged(m_tabWidget->currentIndex()); +} + +void AppOutputPane::tabChanged(int i) +{ + if (i == -1) { + m_stopAction->setEnabled(false); + m_reRunButton->setEnabled(false); + } else { + const int index = indexOf(m_tabWidget->widget(i)); + QTC_ASSERT(index != -1, return; ) + + RunControl *rc = m_runControlTabs.at(index).runControl; + m_stopAction->setEnabled(rc->isRunning()); + m_reRunButton->setEnabled(!rc->isRunning()); + m_reRunButton->setIcon(rc->icon()); + } +} + +void AppOutputPane::runControlStarted() +{ + RunControl *current = currentRunControl(); + if (current && current == sender()) { + m_reRunButton->setEnabled(false); + m_stopAction->setEnabled(true); + m_reRunButton->setIcon(current->icon()); + } +} + +void AppOutputPane::runControlFinished() +{ + RunControl *senderRunControl = qobject_cast(sender()); + const int senderIndex = indexOf(senderRunControl); + + QTC_ASSERT(senderIndex != -1, return; ) + + // Enable buttons for current + RunControl *current = currentRunControl(); + + if (debug) + qDebug() << "OutputPane::runControlFinished" << senderRunControl << senderIndex + << " current " << current << m_runControlTabs.size(); + + if (current && current == sender()) { + m_reRunButton->setEnabled(true); + m_stopAction->setEnabled(false); + m_reRunButton->setIcon(current->icon()); + } + // Check for asynchronous close. Close the tab. + if (m_runControlTabs.at(senderIndex).asyncClosing) + closeTab(tabWidgetIndexOf(senderIndex), CloseTabNoPrompt); + + if (!isRunning()) + emit allRunControlsFinished(); +} + +bool AppOutputPane::isRunning() const +{ + foreach(const RunControlTab &rt, m_runControlTabs) + if (rt.runControl->isRunning()) + return true; + return false; +} + +bool AppOutputPane::canNext() +{ + return false; +} + +bool AppOutputPane::canPrevious() +{ + return false; +} + +void AppOutputPane::goToNext() +{ + +} + +void AppOutputPane::goToPrev() +{ + +} + +bool AppOutputPane::canNavigate() +{ + return false; +} diff --git a/src/plugins/projectexplorer/outputwindow.h b/src/plugins/projectexplorer/appoutputpane.h similarity index 55% rename from src/plugins/projectexplorer/outputwindow.h rename to src/plugins/projectexplorer/appoutputpane.h index 962dc8b2a0..75e6948991 100644 --- a/src/plugins/projectexplorer/outputwindow.h +++ b/src/plugins/projectexplorer/appoutputpane.h @@ -4,41 +4,39 @@ ** ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). ** -** Contact: Nokia Corporation (info@qt.nokia.com) +** Contact: Nokia Corporation (qt-info@nokia.com) ** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. ** ** 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. +** 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, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception +** 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. ** **************************************************************************/ -#ifndef OUTPUTWINDOW_H -#define OUTPUTWINDOW_H +#ifndef APPOUTPUTPANE_H +#define APPOUTPUTPANE_H -#include "outputformat.h" -#include -#include +#include "outputwindow.h" -#include -#include +#include QT_BEGIN_NAMESPACE class QTabWidget; @@ -46,25 +44,14 @@ class QToolButton; class QAction; QT_END_NAMESPACE -namespace Core { - class IContext; -} - namespace ProjectExplorer { + class RunControl; class Project; -namespace Constants { - const char * const C_APP_OUTPUT = "Application Output"; -} - namespace Internal { -class OutputWindow; - -struct OutputPanePrivate; - -class OutputPane : public Core::IOutputPane +class AppOutputPane : public Core::IOutputPane { Q_OBJECT @@ -74,8 +61,8 @@ public: CloseTabWithPrompt }; - OutputPane(); - virtual ~OutputPane(); + AppOutputPane(); + virtual ~AppOutputPane(); QWidget *outputWidget(QWidget *); QList toolBarWidgets() const; @@ -118,6 +105,7 @@ private slots: void runControlFinished(); void aboutToUnloadSession(); + void updateWordWrapMode(); private: struct RunControlTab { @@ -138,6 +126,7 @@ private: int currentIndex() const; RunControl *currentRunControl() const; int tabWidgetIndexOf(int runControlIndex) const; + void handleOldOutput(OutputWindow *window) const; QWidget *m_mainWidget; QTabWidget *m_tabWidget; @@ -147,57 +136,7 @@ private: QToolButton *m_stopButton; }; - -class OutputWindow : public QPlainTextEdit -{ - Q_OBJECT - -public: - OutputWindow(QWidget *parent = 0); - ~OutputWindow(); - - Utils::OutputFormatter* formatter() const; - void setFormatter(Utils::OutputFormatter *formatter); - - void appendMessage(const QString &out, Utils::OutputFormat format); - /// appends a \p text using \p format without using formater - void appendText(const QString &text, const QTextCharFormat &format, int maxLineCount); - - void grayOutOldContent(); - - void showEvent(QShowEvent *); - - void clear(); - void handleOldOutput(); - - void scrollToBottom(); - -protected: - bool isScrollbarAtBottom() const; - - virtual void mousePressEvent(QMouseEvent *e); - virtual void mouseReleaseEvent(QMouseEvent *e); - virtual void mouseMoveEvent(QMouseEvent *e); - virtual void resizeEvent(QResizeEvent *e); - virtual void keyPressEvent(QKeyEvent *ev); - -private slots: - void updateWordWrapMode(); - -private: - void enableUndoRedo(); - QString doNewlineEnfocement(const QString &out); - - Core::IContext *m_outputWindowContext; - Utils::OutputFormatter *m_formatter; - - bool m_enforceNewline; - bool m_scrollToBottom; - bool m_linksActive; - bool m_mousePressed; -}; - } // namespace Internal } // namespace ProjectExplorer -#endif // OUTPUTWINDOW_H +#endif // APPOUTPUTPANE_H diff --git a/src/plugins/projectexplorer/compileoutputwindow.cpp b/src/plugins/projectexplorer/compileoutputwindow.cpp index ecfecc57b2..21c6c837d5 100644 --- a/src/plugins/projectexplorer/compileoutputwindow.cpp +++ b/src/plugins/projectexplorer/compileoutputwindow.cpp @@ -34,7 +34,11 @@ #include "buildmanager.h" #include "showoutputtaskhandler.h" #include "task.h" +#include "projectexplorerconstants.h" +#include "projectexplorer.h" +#include "projectexplorersettings.h" +#include #include #include #include @@ -58,7 +62,8 @@ const int MAX_LINECOUNT = 50000; CompileOutputWindow::CompileOutputWindow(BuildManager * /*bm*/) { - m_outputWindow = new OutputWindow(); + Core::Context context(Constants::C_COMPILE_OUTPUT); + m_outputWindow = new OutputWindow(context); m_outputWindow->setWindowTitle(tr("Compile Output")); m_outputWindow->setWindowIcon(QIcon(QLatin1String(Qt4ProjectManager::Constants::ICON_WINDOW))); m_outputWindow->setReadOnly(true); @@ -72,6 +77,9 @@ CompileOutputWindow::CompileOutputWindow(BuildManager * /*bm*/) m_handler = new ShowOutputTaskHandler(this); ExtensionSystem::PluginManager::instance()->addObject(m_handler); + connect(ProjectExplorerPlugin::instance(), SIGNAL(settingsChanged()), + this, SLOT(updateWordWrapMode())); + updateWordWrapMode(); } CompileOutputWindow::~CompileOutputWindow() @@ -80,6 +88,11 @@ CompileOutputWindow::~CompileOutputWindow() delete m_handler; } +void CompileOutputWindow::updateWordWrapMode() +{ + m_outputWindow->setWordWrapEnabled(ProjectExplorerPlugin::instance()->projectExplorerSettings().wrapAppOutput); +} + bool CompileOutputWindow::hasFocus() { return m_outputWindow->hasFocus(); diff --git a/src/plugins/projectexplorer/compileoutputwindow.h b/src/plugins/projectexplorer/compileoutputwindow.h index 96d99fbc9c..86a812d145 100644 --- a/src/plugins/projectexplorer/compileoutputwindow.h +++ b/src/plugins/projectexplorer/compileoutputwindow.h @@ -82,6 +82,9 @@ public: bool knowsPositionOf(const Task &task); void showPositionOf(const Task &task); +private slots: + void updateWordWrapMode(); + private: OutputWindow *m_outputWindow; QHash m_taskPositions; diff --git a/src/plugins/projectexplorer/localapplicationruncontrol.cpp b/src/plugins/projectexplorer/localapplicationruncontrol.cpp index 66a4300676..5c60eb608c 100644 --- a/src/plugins/projectexplorer/localapplicationruncontrol.cpp +++ b/src/plugins/projectexplorer/localapplicationruncontrol.cpp @@ -33,8 +33,8 @@ #include "localapplicationruncontrol.h" #include "applicationrunconfiguration.h" #include "projectexplorerconstants.h" -#include "outputformat.h" +#include #include #include diff --git a/src/plugins/projectexplorer/outputwindow.cpp b/src/plugins/projectexplorer/outputwindow.cpp deleted file mode 100644 index 8c8d4730e1..0000000000 --- a/src/plugins/projectexplorer/outputwindow.cpp +++ /dev/null @@ -1,773 +0,0 @@ -/************************************************************************** -** -** 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 "projectexplorerconstants.h" -#include "projectexplorer.h" -#include "projectexplorersettings.h" -#include "runconfiguration.h" -#include "session.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -static const int MaxBlockCount = 100000; - -enum { debug = 0 }; - -using namespace Utils; - -namespace ProjectExplorer { -namespace Internal { - -OutputPane::RunControlTab::RunControlTab(RunControl *rc, OutputWindow *w) : - runControl(rc), window(w), asyncClosing(false) -{ -} - -OutputPane::OutputPane() : - m_mainWidget(new QWidget), - m_tabWidget(new QTabWidget), - m_stopAction(new QAction(QIcon(QLatin1String(Constants::ICON_STOP)), tr("Stop"), this)), - m_reRunButton(new QToolButton), - m_stopButton(new QToolButton) -{ - // Rerun - m_reRunButton->setIcon(QIcon(ProjectExplorer::Constants::ICON_RUN_SMALL)); - m_reRunButton->setToolTip(tr("Re-run this run-configuration")); - m_reRunButton->setAutoRaise(true); - m_reRunButton->setEnabled(false); - connect(m_reRunButton, SIGNAL(clicked()), - this, SLOT(reRunRunControl())); - - // Stop - Core::ActionManager *am = Core::ICore::instance()->actionManager(); - Core::Context globalcontext(Core::Constants::C_GLOBAL); - - m_stopAction->setToolTip(tr("Stop")); - m_stopAction->setEnabled(false); - - Core::Command *cmd = am->registerAction(m_stopAction, Constants::STOP, globalcontext); - - m_stopButton->setDefaultAction(cmd->action()); - m_stopButton->setAutoRaise(true); - - connect(m_stopAction, SIGNAL(triggered()), - this, SLOT(stopRunControl())); - - // Spacer (?) - - QVBoxLayout *layout = new QVBoxLayout; - layout->setMargin(0); - m_tabWidget->setDocumentMode(true); - m_tabWidget->setTabsClosable(true); - m_tabWidget->setMovable(true); - connect(m_tabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int))); - layout->addWidget(m_tabWidget); - - connect(m_tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int))); - - m_mainWidget->setLayout(layout); - - connect(ProjectExplorer::ProjectExplorerPlugin::instance()->session(), SIGNAL(aboutToUnloadSession()), - this, SLOT(aboutToUnloadSession())); -} - -OutputPane::~OutputPane() -{ - if (debug) - qDebug() << "OutputPane::~OutputPane: Entries left" << m_runControlTabs.size(); - - foreach(const RunControlTab &rt, m_runControlTabs) - delete rt.runControl; - delete m_mainWidget; -} - -int OutputPane::currentIndex() const -{ - if (const QWidget *w = m_tabWidget->currentWidget()) - return indexOf(w); - return -1; -} - -RunControl *OutputPane::currentRunControl() const -{ - const int index = currentIndex(); - if (index != -1) - return m_runControlTabs.at(index).runControl; - return 0; -} - -int OutputPane::indexOf(const RunControl *rc) const -{ - for (int i = m_runControlTabs.size() - 1; i >= 0; i--) - if (m_runControlTabs.at(i).runControl == rc) - return i; - return -1; -} - -int OutputPane::indexOf(const QWidget *outputWindow) const -{ - for (int i = m_runControlTabs.size() - 1; i >= 0; i--) - if (m_runControlTabs.at(i).window == outputWindow) - return i; - return -1; -} - -int OutputPane::tabWidgetIndexOf(int runControlIndex) const -{ - if (runControlIndex >= 0 && runControlIndex < m_runControlTabs.size()) - return m_tabWidget->indexOf(m_runControlTabs.at(runControlIndex).window); - return -1; -} - -bool OutputPane::aboutToClose() const -{ - foreach(const RunControlTab &rt, m_runControlTabs) - if (rt.runControl->isRunning() && !rt.runControl->promptToStop()) - return false; - return true; -} - -void OutputPane::aboutToUnloadSession() -{ - closeTabs(CloseTabWithPrompt); -} - -QWidget *OutputPane::outputWidget(QWidget *) -{ - return m_mainWidget; -} - -QList OutputPane::toolBarWidgets() const -{ - return QList() << m_reRunButton << m_stopButton; -} - -QString OutputPane::displayName() const -{ - return tr("Application Output"); -} - -int OutputPane::priorityInStatusBar() const -{ - return 60; -} - -void OutputPane::clearContents() -{ - OutputWindow *currentWindow = qobject_cast(m_tabWidget->currentWidget()); - if (currentWindow) - currentWindow->clear(); -} - -void OutputPane::visibilityChanged(bool /* b */) -{ -} - -bool OutputPane::hasFocus() -{ - return m_tabWidget->currentWidget() && m_tabWidget->currentWidget()->hasFocus(); -} - -bool OutputPane::canFocus() -{ - return m_tabWidget->currentWidget(); -} - -void OutputPane::setFocus() -{ - if (m_tabWidget->currentWidget()) - m_tabWidget->currentWidget()->setFocus(); -} - -void OutputPane::createNewOutputWindow(RunControl *rc) -{ - connect(rc, SIGNAL(started()), - this, SLOT(runControlStarted())); - connect(rc, SIGNAL(finished()), - this, SLOT(runControlFinished())); - connect(rc, SIGNAL(appendMessage(ProjectExplorer::RunControl*,QString,Utils::OutputFormat)), - this, SLOT(appendMessage(ProjectExplorer::RunControl*,QString,Utils::OutputFormat))); - - // First look if we can reuse a tab - const int size = m_runControlTabs.size(); - for (int i = 0; i < size; i++) { - RunControlTab &tab =m_runControlTabs[i]; - if (tab.runControl->sameRunConfiguration(rc) && !tab.runControl->isRunning()) { - // Reuse this tab - delete tab.runControl; - tab.runControl = rc; - tab.window->handleOldOutput(); - tab.window->scrollToBottom(); - tab.window->setFormatter(rc->outputFormatter()); - if (debug) - qDebug() << "OutputPane::createNewOutputWindow: Reusing tab" << i << " for " << rc; - return; - } - } - // Create new - OutputWindow *ow = new OutputWindow(m_tabWidget); - ow->setWindowTitle(tr("Application Output Window")); - ow->setWindowIcon(QIcon(QLatin1String(Qt4ProjectManager::Constants::ICON_WINDOW))); - ow->setFormatter(rc->outputFormatter()); - Aggregation::Aggregate *agg = new Aggregation::Aggregate; - agg->add(ow); - agg->add(new Find::BaseTextFind(ow)); - m_runControlTabs.push_back(RunControlTab(rc, ow)); - m_tabWidget->addTab(ow, rc->displayName()); - if (debug) - qDebug() << "OutputPane::createNewOutputWindow: Adding tab for " << rc; -} - -void OutputPane::appendMessage(RunControl *rc, const QString &out, OutputFormat format) -{ - const int index = indexOf(rc); - if (index != -1) - m_runControlTabs.at(index).window->appendMessage(out, format); -} - -void OutputPane::showTabFor(RunControl *rc) -{ - m_tabWidget->setCurrentIndex(tabWidgetIndexOf(indexOf(rc))); -} - -void OutputPane::reRunRunControl() -{ - const int index = currentIndex(); - QTC_ASSERT(index != -1 && !m_runControlTabs.at(index).runControl->isRunning(), return;) - - RunControlTab &tab = m_runControlTabs[index]; - - tab.window->handleOldOutput(); - tab.window->scrollToBottom(); - tab.runControl->start(); -} - -void OutputPane::stopRunControl() -{ - const int index = currentIndex(); - QTC_ASSERT(index != -1 && m_runControlTabs.at(index).runControl->isRunning(), return;) - - RunControl *rc = m_runControlTabs.at(index).runControl; - if (rc->isRunning() && optionallyPromptToStop(rc)) - rc->stop(); - - if (debug) - qDebug() << "OutputPane::stopRunControl " << rc; -} - -bool OutputPane::closeTabs(CloseTabMode mode) -{ - bool allClosed = true; - for (int t = m_tabWidget->count() - 1; t >= 0; t--) - if (!closeTab(t, mode)) - allClosed = false; - if (debug) - qDebug() << "OutputPane::closeTabs() returns " << allClosed; - return allClosed; -} - -bool OutputPane::closeTab(int index) -{ - return closeTab(index, CloseTabWithPrompt); -} - -bool OutputPane::closeTab(int tabIndex, CloseTabMode closeTabMode) -{ - const int index = indexOf(m_tabWidget->widget(tabIndex)); - QTC_ASSERT(index != -1, return true;) - - RunControlTab &tab = m_runControlTabs[index]; - - if (debug) - qDebug() << "OutputPane::closeTab tab " << tabIndex << tab.runControl - << tab.window << tab.asyncClosing; - // Prompt user to stop - if (tab.runControl->isRunning()) { - switch (closeTabMode) { - case CloseTabNoPrompt: - break; - case CloseTabWithPrompt: - if (!tab.runControl->promptToStop()) - return false; - break; - } - if (tab.runControl->stop() == RunControl::AsynchronousStop) { - tab.asyncClosing = true; - return false; - } - } - - m_tabWidget->removeTab(tabIndex); - if (tab.asyncClosing) { // We were invoked from its finished() signal. - tab.runControl->deleteLater(); - } else { - delete tab.runControl; - } - delete tab.window; - m_runControlTabs.removeAt(index); - return true; -} - -bool OutputPane::optionallyPromptToStop(RunControl *runControl) -{ - ProjectExplorerPlugin *pe = ProjectExplorerPlugin::instance(); - ProjectExplorerSettings settings = pe->projectExplorerSettings(); - if (!runControl->promptToStop(&settings.prompToStopRunControl)) - return false; - pe->setProjectExplorerSettings(settings); - return true; -} - -void OutputPane::projectRemoved() -{ - tabChanged(m_tabWidget->currentIndex()); -} - -void OutputPane::tabChanged(int i) -{ - if (i == -1) { - m_stopAction->setEnabled(false); - m_reRunButton->setEnabled(false); - } else { - const int index = indexOf(m_tabWidget->widget(i)); - QTC_ASSERT(index != -1, return; ) - - RunControl *rc = m_runControlTabs.at(index).runControl; - m_stopAction->setEnabled(rc->isRunning()); - m_reRunButton->setEnabled(!rc->isRunning()); - m_reRunButton->setIcon(rc->icon()); - } -} - -void OutputPane::runControlStarted() -{ - RunControl *current = currentRunControl(); - if (current && current == sender()) { - m_reRunButton->setEnabled(false); - m_stopAction->setEnabled(true); - m_reRunButton->setIcon(current->icon()); - } -} - -void OutputPane::runControlFinished() -{ - RunControl *senderRunControl = qobject_cast(sender()); - const int senderIndex = indexOf(senderRunControl); - - QTC_ASSERT(senderIndex != -1, return; ) - - // Enable buttons for current - RunControl *current = currentRunControl(); - - if (debug) - qDebug() << "OutputPane::runControlFinished" << senderRunControl << senderIndex - << " current " << current << m_runControlTabs.size(); - - if (current && current == sender()) { - m_reRunButton->setEnabled(true); - m_stopAction->setEnabled(false); - m_reRunButton->setIcon(current->icon()); - } - // Check for asynchronous close. Close the tab. - if (m_runControlTabs.at(senderIndex).asyncClosing) - closeTab(tabWidgetIndexOf(senderIndex), CloseTabNoPrompt); - - if (!isRunning()) - emit allRunControlsFinished(); -} - -bool OutputPane::isRunning() const -{ - foreach(const RunControlTab &rt, m_runControlTabs) - if (rt.runControl->isRunning()) - return true; - return false; -} - -bool OutputPane::canNext() -{ - return false; -} - -bool OutputPane::canPrevious() -{ - return false; -} - -void OutputPane::goToNext() -{ - -} - -void OutputPane::goToPrev() -{ - -} - -bool OutputPane::canNavigate() -{ - return false; -} - -/*******************/ - -OutputWindow::OutputWindow(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); - if (!ProjectExplorerPlugin::instance()->projectExplorerSettings().wrapAppOutput) - setWordWrapMode(QTextOption::NoWrap); - - static uint usedIds = 0; - Core::Context context(Constants::C_APP_OUTPUT, usedIds++); - 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); - - connect(ProjectExplorerPlugin::instance(), SIGNAL(settingsChanged()), - this, SLOT(updateWordWrapMode())); -} - -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->setFont(TextEditor::TextEditorSettings::instance()->fontSettings().font()); - 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::handleOldOutput() -{ - if (ProjectExplorerPlugin::instance()->projectExplorerSettings().cleanOldAppOutput) - clear(); - else - grayOutOldContent(); -} - -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::updateWordWrapMode() -{ - if (ProjectExplorerPlugin::instance()->projectExplorerSettings().wrapAppOutput) - setWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); - else - setWordWrapMode(QTextOption::NoWrap); -} - -} // namespace Internal -} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index 459e9a3625..6d4421649a 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -59,6 +59,7 @@ #include "metatypedeclarations.h" #include "nodesvisitor.h" #include "outputwindow.h" +#include "appoutputpane.h" #include "persistentsettings.h" #include "pluginfilefactory.h" #include "processstep.h" @@ -221,7 +222,7 @@ struct ProjectExplorerPluginPrivate { QList m_fileFactories; QStringList m_profileMimeTypes; - Internal::OutputPane *m_outputPane; + Internal::AppOutputPane *m_outputPane; QList > m_recentProjects; // pair of filename, displayname static const int m_maxRecentProjects = 7; @@ -375,7 +376,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er addAutoReleasedObject(new CoreListener); - d->m_outputPane = new OutputPane; + d->m_outputPane = new AppOutputPane; addAutoReleasedObject(d->m_outputPane); connect(d->m_session, SIGNAL(projectRemoved(ProjectExplorer::Project *)), d->m_outputPane, SLOT(projectRemoved())); @@ -1085,7 +1086,7 @@ ExtensionSystem::IPlugin::ShutdownFlag ProjectExplorerPlugin::aboutToShutdown() // Attempt to synchronously shutdown all run controls. // If that fails, fall back to asynchronous shutdown (Debugger run controls // might shutdown asynchronously). - if (d->m_outputPane->closeTabs(OutputPane::CloseTabNoPrompt /* No prompt any more */)) + if (d->m_outputPane->closeTabs(AppOutputPane::CloseTabNoPrompt /* No prompt any more */)) return SynchronousShutdown; connect(d->m_outputPane, SIGNAL(allRunControlsFinished()), this, SIGNAL(asynchronousShutdownFinished())); diff --git a/src/plugins/projectexplorer/projectexplorer.pro b/src/plugins/projectexplorer/projectexplorer.pro index 524532f8f9..488a213b9a 100644 --- a/src/plugins/projectexplorer/projectexplorer.pro +++ b/src/plugins/projectexplorer/projectexplorer.pro @@ -101,7 +101,8 @@ HEADERS += projectexplorer.h \ publishing/publishingwizardselectiondialog.h \ publishing/ipublishingwizardfactory.h \ headerpath.h \ - gcctoolchainfactories.h + gcctoolchainfactories.h \ + appoutputpane.h SOURCES += projectexplorer.cpp \ abi.cpp \ @@ -185,7 +186,8 @@ SOURCES += projectexplorer.cpp \ localapplicationruncontrol.cpp \ customexecutableconfigurationwidget.cpp \ sessionnodeimpl.cpp \ - publishing/publishingwizardselectiondialog.cpp + publishing/publishingwizardselectiondialog.cpp \ + appoutputpane.cpp FORMS += processstep.ui \ toolchainoptionspage.ui \ diff --git a/src/plugins/projectexplorer/projectexplorerconstants.h b/src/plugins/projectexplorer/projectexplorerconstants.h index 574f16a80d..0f0e943303 100644 --- a/src/plugins/projectexplorer/projectexplorerconstants.h +++ b/src/plugins/projectexplorer/projectexplorerconstants.h @@ -98,6 +98,8 @@ const int P_ACTION_BUILDPROJECT = 80; // context const char * const C_PROJECTEXPLORER = "Project Explorer"; const char * const C_PROJECT_TREE = "ProjectExplorer.ProjectTreeContext"; +const char * const C_APP_OUTPUT = "ProjectExplorer.ApplicationOutput"; +const char * const C_COMPILE_OUTPUT = "ProjectExplorer.CompileOutput"; // languages const char * const LANG_CXX = "CXX";