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"