File indexing completed on 2024-05-19 04:56:01

0001 /**
0002  * \file externalprocess.cpp
0003  * Handler for external process.
0004  *
0005  * \b Project: Kid3
0006  * \author Urs Fleisch
0007  * \date 22 Feb 2007
0008  *
0009  * Copyright (C) 2007-2018  Urs Fleisch
0010  *
0011  * This file is part of Kid3.
0012  *
0013  * Kid3 is free software; you can redistribute it and/or modify
0014  * it under the terms of the GNU General Public License as published by
0015  * the Free Software Foundation; either version 2 of the License, or
0016  * (at your option) any later version.
0017  *
0018  * Kid3 is distributed in the hope that it will be useful,
0019  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0020  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0021  * GNU General Public License for more details.
0022  *
0023  * You should have received a copy of the GNU General Public License
0024  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0025  */
0026 
0027 #include "externalprocess.h"
0028 #include <QProcess>
0029 #include <QString>
0030 #include <QStringList>
0031 #include "taggedfile.h"
0032 #include "iusercommandprocessor.h"
0033 #include "kid3application.h"
0034 
0035 /**
0036  * Destructor.
0037  */
0038 ExternalProcess::IOutputViewer::~IOutputViewer()
0039 {
0040 }
0041 
0042 
0043 /**
0044  * Constructor.
0045  *
0046  * @param app application context
0047  * @param parent parent object
0048  */
0049 ExternalProcess::ExternalProcess(Kid3Application* app, QObject* parent)
0050   : QObject(parent),
0051     m_app(app), m_process(nullptr), m_outputViewer(nullptr)
0052 {
0053   setObjectName(QLatin1String("ExternalProcess"));
0054   const auto userCommandProcessors = m_app->getUserCommandProcessors();
0055   for (IUserCommandProcessor* userCommandProcessor : userCommandProcessors) {
0056     userCommandProcessor->initialize(m_app);
0057     connect(userCommandProcessor->qobject(), SIGNAL(commandOutput(QString)), // clazy:exclude=old-style-connect
0058             this, SLOT(showOutputLine(QString)));
0059   }
0060 }
0061 
0062 /**
0063  * Destructor.
0064  */
0065 ExternalProcess::~ExternalProcess()
0066 {
0067   const auto userCommandProcessors = m_app->getUserCommandProcessors();
0068   for (IUserCommandProcessor* userCommandProcessor : userCommandProcessors) {
0069     userCommandProcessor->cleanup();
0070   }
0071 }
0072 
0073 /**
0074  * Launch a command.
0075  *
0076  * @param name       display name
0077  * @param args       command and arguments
0078  * @param showOutput true to show output of process
0079  * @return false if process could not be executed.
0080  */
0081 bool ExternalProcess::launchCommand(const QString& name, const QStringList& args,
0082                                     bool showOutput)
0083 {
0084   if (args.isEmpty())
0085     return true;
0086 
0087   if (!m_process) {
0088     m_process = new QProcess(parent());
0089   }
0090   if (m_process->state() != QProcess::NotRunning) {
0091     m_process = new QProcess(parent());
0092   }
0093   connect(m_process, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(
0094             &QProcess::finished),
0095           this, &ExternalProcess::finished, Qt::UniqueConnection);
0096 
0097   if (showOutput && m_outputViewer) {
0098     m_process->setProcessChannelMode(QProcess::MergedChannels);
0099     connect(m_process, &QProcess::readyReadStandardOutput,
0100             this, &ExternalProcess::readFromStdout);
0101     m_outputViewer->setCaption(name);
0102     m_outputViewer->scrollToBottom();
0103   } else {
0104     disconnect(m_process, &QProcess::readyReadStandardOutput,
0105                this, &ExternalProcess::readFromStdout);
0106   }
0107 
0108   QStringList arguments = args;
0109   QString program = arguments.takeFirst();
0110   if (program.startsWith(QLatin1Char('@'))) {
0111     program = program.mid(1);
0112     const auto userCommandProcessors = m_app->getUserCommandProcessors();
0113     for (IUserCommandProcessor* userCommandProcessor : userCommandProcessors) {
0114       if (userCommandProcessor->userCommandKeys().contains(program)) {
0115         connect(userCommandProcessor->qobject(), SIGNAL(finished(int)),
0116                 this, SIGNAL(finished(int)), Qt::UniqueConnection);
0117         if (userCommandProcessor->startUserCommand(program, arguments,
0118                                                    showOutput)) {
0119           return true;
0120         }
0121       }
0122     }
0123   }
0124   m_process->start(program, arguments);
0125   if (!m_process->waitForStarted(10000)) {
0126     return false;
0127   }
0128   return true;
0129 }
0130 
0131 /**
0132  * Read data from standard output and display it in the output viewer.
0133  */
0134 void ExternalProcess::readFromStdout()
0135 {
0136   if (m_outputViewer) {
0137     m_outputViewer->append(QString::fromLocal8Bit(
0138                              m_process->readAllStandardOutput()));
0139   }
0140 }
0141 
0142 /**
0143  * Show a line in the output viewer.
0144  * @param msg message to be displayed
0145  */
0146 void ExternalProcess::showOutputLine(const QString& msg)
0147 {
0148   if (m_outputViewer) {
0149     m_outputViewer->append(msg + QLatin1Char('\n'));
0150   }
0151 }