// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "compileoutputwindow.h" #include "buildmanager.h" #include "projectexplorerconstants.h" #include "projectexplorericons.h" #include "projectexplorertr.h" #include "showoutputtaskhandler.h" #include "taskhub.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 namespace ProjectExplorer::Internal { const char SETTINGS_KEY[] = "ProjectExplorer/CompileOutput/Zoom"; const char C_COMPILE_OUTPUT[] = "ProjectExplorer.CompileOutput"; const char OPTIONS_PAGE_ID[] = "C.ProjectExplorer.CompileOutputOptions"; CompileOutputWindow::CompileOutputWindow(QAction *cancelBuildAction) : m_cancelBuildButton(new QToolButton), m_settingsButton(new QToolButton) { setId("CompileOutput"); setDisplayName(QCoreApplication::translate("QtC::ProjectExplorer", "Compile Output")); setPriorityInStatusBar(40); Core::Context context(C_COMPILE_OUTPUT); m_outputWindow = new Core::OutputWindow(context, SETTINGS_KEY); m_outputWindow->setWindowTitle(displayName()); m_outputWindow->setWindowIcon(Icons::WINDOW.icon()); m_outputWindow->setReadOnly(true); m_outputWindow->setUndoRedoEnabled(false); m_outputWindow->setMaxCharCount(Core::Constants::DEFAULT_MAX_CHAR_COUNT); //: file name suggested for saving compile output m_outputWindow->setOutputFileNameHint(Tr::tr("compile-output.txt")); Utils::ProxyAction *cancelBuildProxyButton = Utils::ProxyAction::proxyActionWithIcon(cancelBuildAction, Utils::Icons::STOP_SMALL_TOOLBAR.icon()); m_cancelBuildButton->setDefaultAction(cancelBuildProxyButton); m_settingsButton->setToolTip(Core::ICore::msgShowOptionsDialog()); m_settingsButton->setIcon(Utils::Icons::SETTINGS_TOOLBAR.icon()); auto updateFontSettings = [this] { m_outputWindow->setBaseFont(TextEditor::TextEditorSettings::fontSettings().font()); }; auto updateZoomEnabled = [this] { m_outputWindow->setWheelZoomEnabled( TextEditor::globalBehaviorSettings().m_scrollWheelZooming); }; updateFontSettings(); updateZoomEnabled(); setupFilterUi("CompileOutputPane.Filter", "ProjectExplorer::Internal::CompileOutputPane"); setFilteringEnabled(true); connect(this, &IOutputPane::zoomInRequested, m_outputWindow, &Core::OutputWindow::zoomIn); connect(this, &IOutputPane::zoomOutRequested, m_outputWindow, &Core::OutputWindow::zoomOut); connect(this, &IOutputPane::resetZoomRequested, m_outputWindow, &Core::OutputWindow::resetZoom); connect(TextEditor::TextEditorSettings::instance(), &TextEditor::TextEditorSettings::fontSettingsChanged, this, updateFontSettings); connect(TextEditor::TextEditorSettings::instance(), &TextEditor::TextEditorSettings::behaviorSettingsChanged, this, updateZoomEnabled); connect(m_settingsButton, &QToolButton::clicked, this, [] { Core::ICore::showOptionsDialog(OPTIONS_PAGE_ID); }); qRegisterMetaType("QTextCharFormat"); m_handler = new ShowOutputTaskHandler(this, Tr::tr("Show Compile &Output"), Tr::tr("Show the output that generated this issue in Compile Output."), Tr::tr("O")); ExtensionSystem::PluginManager::addObject(m_handler); setupContext(C_COMPILE_OUTPUT, m_outputWindow); updateFromSettings(); CompileOutputSettings &s = compileOutputSettings(); m_outputWindow->setWordWrapEnabled(s.wrapOutput()); m_outputWindow->setDiscardExcessiveOutput(s.discardOutput()); m_outputWindow->setMaxCharCount(s.maxCharCount()); connect(&s.wrapOutput, &Utils::BaseAspect::changed, m_outputWindow, [this] { m_outputWindow->setWordWrapEnabled(compileOutputSettings().wrapOutput()); }); connect(&s.discardOutput, &Utils::BaseAspect::changed, m_outputWindow, [this] { m_outputWindow->setDiscardExcessiveOutput(compileOutputSettings().discardOutput()); }); connect(&s.maxCharCount, &Utils::BaseAspect::changed, m_outputWindow, [this] { m_outputWindow->setMaxCharCount(compileOutputSettings().maxCharCount()); }); connect(m_outputWindow, &Core::OutputWindow::outputDiscarded, this, [] { TaskHub::addTask( Task::Warning, Tr::tr("Discarded excessive compile output."), Constants::TASK_CATEGORY_COMPILE); }); connect(&s.overwriteColor, &Utils::BaseAspect::changed, this, &CompileOutputWindow::updateFromSettings); connect(&s.backgroundColor, &Utils::BaseAspect::changed, this, &CompileOutputWindow::updateFromSettings); } CompileOutputWindow::~CompileOutputWindow() { ExtensionSystem::PluginManager::removeObject(m_handler); delete m_handler; delete m_cancelBuildButton; delete m_settingsButton; } void CompileOutputWindow::updateFromSettings() { QColor background; if (compileOutputSettings().overwriteColor()) background = compileOutputSettings().backgroundColor(); if (!background.isValid()) background = Utils::creatorColor(Utils::Theme::PaletteBase); m_outputWindow->outputFormatter()->setExplicitBackgroundColor(background); Utils::StyleHelper::modifyPaletteBase(m_outputWindow, background); } bool CompileOutputWindow::hasFocus() const { return m_outputWindow->window()->focusWidget() == m_outputWindow; } bool CompileOutputWindow::canFocus() const { return true; } void CompileOutputWindow::setFocus() { m_outputWindow->setFocus(); } QWidget *CompileOutputWindow::outputWidget(QWidget *) { return m_outputWindow; } QList CompileOutputWindow::toolBarWidgets() const { return QList{m_cancelBuildButton, m_settingsButton} + IOutputPane::toolBarWidgets(); } void CompileOutputWindow::appendText(const QString &text, BuildStep::OutputFormat format) { Utils::OutputFormat fmt = Utils::NormalMessageFormat; switch (format) { case BuildStep::OutputFormat::Stdout: fmt = Utils::StdOutFormat; break; case BuildStep::OutputFormat::Stderr: fmt = Utils::StdErrFormat; break; case BuildStep::OutputFormat::NormalMessage: fmt = Utils::NormalMessageFormat; break; case BuildStep::OutputFormat::ErrorMessage: fmt = Utils::ErrorMessageFormat; break; } m_outputWindow->appendMessage(text, fmt); } void CompileOutputWindow::clearContents() { m_outputWindow->clear(); } bool CompileOutputWindow::canNext() const { return false; } bool CompileOutputWindow::canPrevious() const { return false; } void CompileOutputWindow::goToNext() { } void CompileOutputWindow::goToPrev() { } bool CompileOutputWindow::canNavigate() const { return false; } bool CompileOutputWindow::hasFilterContext() const { return true; } void CompileOutputWindow::registerPositionOf(const Task &task, int linkedOutputLines, int skipLines, int offset) { m_outputWindow->registerPositionOf( task.id(), linkedOutputLines, skipLines, offset, Core::OutputWindow::TaskSource::Direct); } void CompileOutputWindow::flush() { m_outputWindow->flush(); } void CompileOutputWindow::reset() { m_outputWindow->reset(); } Utils::OutputFormatter *CompileOutputWindow::outputFormatter() const { return m_outputWindow->outputFormatter(); } void CompileOutputWindow::updateFilter() { m_outputWindow->updateFilterProperties(filterText(), filterCaseSensitivity(), filterUsesRegexp(), filterIsInverted(), beforeContext(), afterContext()); } // CompileOutputSettings CompileOutputSettings &compileOutputSettings() { static CompileOutputSettings theSettings; return theSettings; } CompileOutputSettings::CompileOutputSettings() { setAutoApply(false); wrapOutput.setSettingsKey("ProjectExplorer/Settings/WrapBuildOutput"); wrapOutput.setDefaultValue(true); wrapOutput.setLabelText(Tr::tr("Word-wrap output")); popUp.setSettingsKey("ProjectExplorer/Settings/ShowCompilerOutput"); popUp.setLabelText(Tr::tr("Open Compile Output when building")); discardOutput.setSettingsKey("ProjectExplorer/Settings/DiscardCompilerOutput"); discardOutput.setLabelText(Tr::tr("Discard excessive output")); discardOutput.setToolTip( Tr::tr( "Discards compile output that continuously comes in faster than " "it can be handled.")); maxCharCount.setSettingsKey("ProjectExplorer/Settings/MaxBuildOutputLines"); maxCharCount.setRange(1, Core::Constants::DEFAULT_MAX_CHAR_COUNT); maxCharCount.setDefaultValue(Core::Constants::DEFAULT_MAX_CHAR_COUNT); maxCharCount.setToSettingsTransformation([](const QVariant &v) { return v.toInt() / 100; }); maxCharCount.setFromSettingsTransformation([](const QVariant &v) { return v.toInt() * 100; }); overwriteColor.setSettingsKey("ProjectExplorer/CompileOutput/OverwriteBackground"); overwriteColor.setLabelText(Tr::tr("Overwrite background color")); overwriteColor.setToolTip("Customize background color of the compile output.\n" "Note: existing output will not get recolored."); backgroundColor.setSettingsKey("ProjectExplorer/CompileOutput/BackgroundColor"); backgroundColor.setDefaultValue(QColor{}); backgroundColor.setMinimumSize({64, 0}); backgroundColor.setFromSettingsTransformation([](const QVariant &var) { const QColor color = var.value(); return color.isValid() ? color : Utils::creatorColor(Utils::Theme::PaletteBase); }); backgroundColor.setEnabler(&overwriteColor); setLayouter([this] { using namespace Layouting; const QString msg = Tr::tr("Limit output to %1 characters"); const QStringList parts = msg.split("%1") << QString() << QString(); auto resetColorButton = new QPushButton(Tr::tr("Reset")); resetColorButton->setToolTip(Tr::tr("Reset to default.", "Color")); connect(resetColorButton, &QPushButton::clicked, this, [this] { backgroundColor.setVolatileValue(QColor{}); }); connect(&overwriteColor, &Utils::BoolAspect::volatileValueChanged, this, [this, resetColorButton] { resetColorButton->setEnabled(overwriteColor.volatileValue()); }); resetColorButton->setEnabled(overwriteColor()); return Column { wrapOutput, popUp, discardOutput, Row { parts.at(0), maxCharCount, parts.at(1), st }, Row { overwriteColor, backgroundColor, resetColorButton, st }, st }; }); readSettings(); } // CompileOutputSettingsPage class CompileOutputSettingsPage final : public Core::IOptionsPage { public: CompileOutputSettingsPage() { setId(OPTIONS_PAGE_ID); setDisplayName(Tr::tr("Compile Output")); setCategory(Constants::BUILD_AND_RUN_SETTINGS_CATEGORY); setSettingsProvider([] { return &compileOutputSettings(); }); } }; const CompileOutputSettingsPage settingsPage; } // ProjectExplorer::Internal