File indexing completed on 2024-04-28 05:48:37
0001 // SPDX-FileCopyrightText: 2022 Kåre Särs <kare.sars@iki.fi> 0002 // 0003 // SPDX-License-Identifier: LGPL-2.0-only 0004 0005 #include "AppOutput.h" 0006 0007 #include "hostprocess.h" 0008 0009 #include <QDebug> 0010 #include <QFontDatabase> 0011 #include <QScrollBar> 0012 #include <QTextCursor> 0013 #include <QTextEdit> 0014 #include <QVBoxLayout> 0015 0016 #include <KColorScheme> 0017 #include <KPluginFactory> 0018 #include <KProcess> 0019 0020 #include <KParts/ReadOnlyPart> 0021 #include <kde_terminal_interface.h> 0022 0023 struct AppOutput::Private { 0024 explicit Private(AppOutput *pub) 0025 : q(pub) 0026 { 0027 } 0028 KParts::ReadOnlyPart *part = nullptr; 0029 KProcess process; 0030 QTextEdit *outputArea = nullptr; 0031 QString terminalProcess; 0032 AppOutput *q = nullptr; 0033 0034 void addOutputText(QString const &text) 0035 { 0036 qDebug() << text; 0037 if (!outputArea) { 0038 qWarning() << "Can't output text to nullptr"; 0039 return; 0040 } 0041 0042 QScrollBar *scrollb = outputArea->verticalScrollBar(); 0043 if (!scrollb) { 0044 return; 0045 } 0046 bool atEnd = (scrollb->value() == scrollb->maximum()); 0047 0048 QTextCursor cursor = outputArea->textCursor(); 0049 if (!cursor.atEnd()) { 0050 cursor.movePosition(QTextCursor::End); 0051 } 0052 cursor.insertText(text); 0053 0054 if (atEnd) { 0055 scrollb->setValue(scrollb->maximum()); 0056 } 0057 } 0058 0059 void updateTerminalProcessInfo() 0060 { 0061 TerminalInterface *t = qobject_cast<TerminalInterface *>(part); 0062 if (!t) { 0063 return; 0064 } 0065 0066 if (terminalProcess != t->foregroundProcessName()) { 0067 terminalProcess = t->foregroundProcessName(); 0068 Q_EMIT q->runningChanged(); 0069 } 0070 } 0071 }; 0072 0073 AppOutput::AppOutput(QWidget *parent) 0074 : QWidget(parent) 0075 , d(std::make_unique<AppOutput::Private>(this)) 0076 { 0077 const QString konsolePart = QStringLiteral("kf6/parts/konsolepart"); 0078 KPluginFactory *factory = KPluginFactory::loadFactory(konsolePart).plugin; 0079 if (!factory) { 0080 qDebug() << "could not load the konsolepart factory"; 0081 } else { 0082 d->part = factory->create<KParts::ReadOnlyPart>(this); 0083 } 0084 0085 TerminalInterface *t = nullptr; 0086 if (!d->part) { 0087 qDebug() << "could not create a konsole part"; 0088 } else { 0089 t = qobject_cast<TerminalInterface *>(d->part); 0090 } 0091 if (!t) { 0092 qDebug() << "Failed to cast the TerminalInterface"; 0093 } 0094 0095 QVBoxLayout *layout = new QVBoxLayout(this); 0096 layout->setContentsMargins(0, 0, 0, 0); 0097 if (d->part) { 0098 layout->addWidget(d->part->widget()); 0099 connect(d->part->widget(), &QObject::destroyed, this, &AppOutput::deleteLater); 0100 setFocusProxy(d->part->widget()); 0101 0102 connect(d->part, &KParts::ReadOnlyPart::setWindowCaption, this, [this]() { 0103 d->updateTerminalProcessInfo(); 0104 }); 0105 0106 } else { 0107 d->outputArea = new QTextEdit(this); 0108 layout->addWidget(d->outputArea); 0109 d->outputArea->setAcceptRichText(false); 0110 d->outputArea->setReadOnly(true); 0111 d->outputArea->setUndoRedoEnabled(false); 0112 // fixed wide font, like konsole 0113 d->outputArea->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); 0114 // alternate color scheme, like konsole 0115 KColorScheme schemeView(QPalette::Active, KColorScheme::View); 0116 d->outputArea->setTextBackgroundColor(schemeView.foreground().color()); 0117 d->outputArea->setTextColor(schemeView.background().color()); 0118 QPalette p = d->outputArea->palette(); 0119 p.setColor(QPalette::Base, schemeView.foreground().color()); 0120 d->outputArea->setPalette(p); 0121 0122 d->process.setOutputChannelMode(KProcess::SeparateChannels); 0123 connect(&d->process, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, &AppOutput::runningChanged); 0124 connect(&d->process, &KProcess::readyReadStandardError, this, [this]() { 0125 d->addOutputText(QString::fromUtf8(d->process.readAllStandardError())); 0126 }); 0127 connect(&d->process, &KProcess::readyReadStandardOutput, this, [this]() { 0128 d->addOutputText(QString::fromUtf8(d->process.readAllStandardOutput())); 0129 }); 0130 } 0131 } 0132 0133 AppOutput::~AppOutput() 0134 { 0135 d->process.kill(); 0136 } 0137 0138 void AppOutput::setWorkingDir(const QString &path) 0139 { 0140 TerminalInterface *t = qobject_cast<TerminalInterface *>(d->part); 0141 if (t) { 0142 t->showShellInDir(path); 0143 } else { 0144 d->process.setWorkingDirectory(path); 0145 } 0146 } 0147 0148 void AppOutput::runCommand(const QString &cmd) 0149 { 0150 TerminalInterface *t = qobject_cast<TerminalInterface *>(d->part); 0151 if (t) { 0152 t->sendInput(cmd + QLatin1Char('\n')); 0153 d->terminalProcess = cmd; 0154 } else { 0155 d->outputArea->clear(); 0156 d->process.setShellCommand(cmd); 0157 startHostProcess(d->process); 0158 d->process.waitForStarted(300); 0159 } 0160 Q_EMIT runningChanged(); 0161 } 0162 0163 QString AppOutput::runningProcess() 0164 { 0165 TerminalInterface *t = qobject_cast<TerminalInterface *>(d->part); 0166 if (t) { 0167 return d->terminalProcess; 0168 } 0169 0170 QString program = d->process.program().isEmpty() ? QString() : d->process.program().first(); 0171 return d->process.state() == QProcess::NotRunning ? QString() : program; 0172 } 0173 0174 #include "moc_AppOutput.cpp"