Commit 26d85fae authored by Urs Fleisch's avatar Urs Fleisch
Browse files

QML: Move QML script code to QmlCommand plugin.

This will make Kid3 independent of QML, only the plugin
depends on libQt5Qml, libQt5Quick (or in the case of Qt 4 on
libQtDeclarative, libQtScript, libQtSql, libQtXmlPatterns).
parent 9e8697c4
......@@ -289,10 +289,6 @@ if (NOT Qt4_FOUND AND NOT QT4_FOUND AND NOT Qt5Core_FOUND)
message(FATAL_ERROR "Neither Qt4 nor Qt5 found.")
endif (NOT Qt4_FOUND AND NOT QT4_FOUND AND NOT Qt5Core_FOUND)
if (WITH_QML AND (Qt5Qml_FOUND OR QT_QTDECLARATIVE_FOUND))
set(HAVE_QML 1)
endif ()
# Check that QT_NO_DEBUG is defined for release configurations
foreach (_buildType RELWITHDEBINFO RELEASE MINSIZEREL)
......
......@@ -14,9 +14,6 @@
/* Define if mntent.h is available */
#cmakedefine HAVE_MNTENT_H 1
/* Define if build with QML support */
#cmakedefine HAVE_QML 1
#cmakedefine CFG_DATAROOTDIR "@CFG_DATAROOTDIR@"
#cmakedefine CFG_DOCDIR "@CFG_DOCDIR@"
#cmakedefine CFG_TRANSLATIONSDIR "@CFG_TRANSLATIONSDIR@"
......
......@@ -31,6 +31,7 @@ set(model_SRCS
model/pixmapprovider.cpp
model/frameeditorobject.cpp
model/frameobjectmodel.cpp
model/iusercommandprocessor.cpp
)
set(model_MOC_HDRS
......@@ -59,8 +60,3 @@ if (HAVE_QTDBUS)
set(model_SRCS ${model_SRCS} model/scriptinterface.cpp)
set(model_MOC_HDRS ${model_MOC_HDRS} model/scriptinterface.h)
endif (HAVE_QTDBUS)
if (HAVE_QML)
set(model_SRCS ${model_SRCS} model/qmlprocess.cpp)
set(model_MOC_HDRS ${model_MOC_HDRS} model/qmlprocess.h)
endif (HAVE_QML)
......@@ -33,10 +33,8 @@
#include <QStringList>
#include <QTextCursor>
#include "taggedfile.h"
#include "config.h"
#ifdef HAVE_QML
#include "qmlprocess.h"
#endif
#include "iusercommandprocessor.h"
#include "kid3application.h"
/**
* Constructor.
......@@ -132,10 +130,15 @@ void ExternalProcess::OutputViewer::scrollToBottom()
* @param parent parent object
*/
ExternalProcess::ExternalProcess(Kid3Application* app, QWidget* parent) :
QObject(parent), m_app(app), m_parent(parent), m_process(0),
m_outputViewer(0), m_qmlProcess(0)
QObject(parent), m_app(app), m_parent(parent), m_process(0), m_outputViewer(0)
{
setObjectName(QLatin1String("ExternalProcess"));
foreach (IUserCommandProcessor* userCommandProcessor,
m_app->getUserCommandProcessors()) {
userCommandProcessor->initialize(m_app);
connect(userCommandProcessor->qobject(), SIGNAL(commandOutput(QString)),
this, SLOT(showOutputLine(QString)));
}
}
/**
......@@ -143,6 +146,13 @@ ExternalProcess::ExternalProcess(Kid3Application* app, QWidget* parent) :
*/
ExternalProcess::~ExternalProcess()
{
foreach (IUserCommandProcessor* userCommandProcessor,
m_app->getUserCommandProcessors()) {
userCommandProcessor->cleanup();
}
if (m_outputViewer) {
m_outputViewer->close();
}
delete m_outputViewer;
}
......@@ -191,16 +201,12 @@ void ExternalProcess::launchCommand(const QString& name, const QStringList& args
QStringList arguments = args;
QString program = arguments.takeFirst();
#ifdef HAVE_QML
if (!m_qmlProcess) {
m_qmlProcess = new QmlProcess(m_app, this);
connect(m_qmlProcess, SIGNAL(qmlOutput(QString)),
this, SLOT(showOutputLine(QString)));
}
if (m_qmlProcess->startQml(program, arguments, showOutput)) {
return;
foreach (IUserCommandProcessor* userCommandProcessor,
m_app->getUserCommandProcessors()) {
if (userCommandProcessor->userCommandKeys().contains(program) &&
userCommandProcessor->startUserCommand(program, arguments, showOutput))
return;
}
#endif
m_process->start(program, arguments);
if (!m_process->waitForStarted(10000)) {
QMessageBox::warning(
......
......@@ -36,7 +36,6 @@ class QString;
class QStringList;
class Kid3Application;
class TaggedFile;
class QmlProcess;
/**
* Handler for external process.
......@@ -117,7 +116,6 @@ private:
QWidget* m_parent;
QProcess* m_process;
OutputViewer* m_outputViewer;
QmlProcess* m_qmlProcess;
};
#endif // EXTERNALPROCESS_H
/**
* \file iusercommandprocessor.cpp
* Interface for user command processor.
*
* \b Project: Kid3
* \author Urs Fleisch
* \date 21 Feb 2015
*
* Copyright (C) 2015 Urs Fleisch
*
* This file is part of Kid3.
*
* Kid3 is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Kid3 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "iusercommandprocessor.h"
/**
* Destructor.
*/
IUserCommandProcessor::~IUserCommandProcessor()
{
// Just defining "virtual ~IServerImporterFactory() {}" in the header file
// will lead to unresolved symbols when building with shared libraries on
// Windows and a class from another library inherits from this class.
}
/**
* Initialize processor.
* This method can be used to initialize the processor before it is used.
* @param app application context
*/
void IUserCommandProcessor::initialize(Kid3Application*)
{
}
/**
* Cleanup processor.
* This method can be used to clean up resources for which the plugin
* destruction time is too late, e.g. GUI widgets.
*/
void IUserCommandProcessor::cleanup()
{
}
/**
* \file iusercommandprocessor.h
* Interface for user command processor.
*
* \b Project: Kid3
* \author Urs Fleisch
* \date 21 Feb 2015
*
* Copyright (C) 2015 Urs Fleisch
*
* This file is part of Kid3.
*
* Kid3 is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Kid3 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef IUSERCOMMANDPROCESSOR_H
#define IUSERCOMMANDPROCESSOR_H
#include <QtPlugin>
#include "kid3api.h"
class QString;
class QStringList;
class Kid3Application;
/**
* Interface for user command processor.
*/
class KID3_CORE_EXPORT IUserCommandProcessor {
public:
/**
* Destructor.
*/
virtual ~IUserCommandProcessor();
/**
* Get keys of available user commands.
* @return list of keys.
*/
virtual QStringList userCommandKeys() const = 0;
/**
* Initialize processor.
* This method can be used to initialize the processor before it is used.
* @param app application context
*/
virtual void initialize(Kid3Application* app);
/**
* Cleanup processor.
* This method can be used to clean up resources for which the plugin
* destruction time is too late.
*/
virtual void cleanup();
/**
* Start user command.
* @param key user command name
* @param arguments arguments to pass to command
* @param showOutput true to enable output in output viewer
* @return true if command is started.
*
* @remarks If @a showOutput is true, command output is emitted using a signal
* "void commandOutput(QString)". Objects implementing this interface have to
* be QObjects providing such a signal.
* @see qobject()
*/
virtual bool startUserCommand(
const QString& key, const QStringList& arguments, bool showOutput) = 0;
/**
* Return object which emits commandOutput() signal.
*
* @return object which emits signals.
*/
virtual QObject* qobject() = 0;
};
Q_DECLARE_INTERFACE(IUserCommandProcessor,
"net.sourceforge.kid3.IUserCommandProcessor")
#endif // IUSERCOMMANDPROCESSOR_H
......@@ -76,6 +76,7 @@
#include "iserverimporterfactory.h"
#include "iservertrackimporterfactory.h"
#include "itaggedfilefactory.h"
#include "iusercommandprocessor.h"
#if defined HAVE_PHONON || QT_VERSION >= 0x050000
#include "audioplayer.h"
#endif
......@@ -421,6 +422,16 @@ void Kid3Application::checkPlugin(QObject* plugin)
FileProxyModel::taggedFileFactories().append(taggedFileFactory);
}
}
if (IUserCommandProcessor* userCommandProcessor =
qobject_cast<IUserCommandProcessor*>(plugin)) {
ImportConfig& importCfg = ImportConfig::instance();
QStringList availablePlugins = importCfg.availablePlugins();
availablePlugins.append(plugin->objectName());
importCfg.setAvailablePlugins(availablePlugins);
if (!importCfg.disabledPlugins().contains(plugin->objectName())) {
m_userCommandProcessors.append(userCommandProcessor);
}
}
}
#if defined HAVE_PHONON || QT_VERSION >= 0x050000
......
......@@ -64,6 +64,7 @@ class BatchImportProfile;
class BatchImporter;
class IAbortable;
class ICorePlatformTools;
class IUserCommandProcessor;
#if defined HAVE_PHONON || QT_VERSION >= 0x050000
class AudioPlayer;
#endif
......@@ -278,6 +279,14 @@ public:
return m_trackImporters;
}
/**
* Get available user command processors.
* @return list of user command processors.
*/
QList<IUserCommandProcessor*> getUserCommandProcessors() {
return m_userCommandProcessors;
}
/**
* Get tag searcher.
* @return tag searcher.
......@@ -1297,6 +1306,8 @@ private:
QList<ServerImporter*> m_importers;
/** Importer for MusicBrainz fingerprints */
QList<ServerTrackImporter*> m_trackImporters;
/** Processors for user commands */
QList<IUserCommandProcessor*> m_userCommandProcessors;
/** Current directory */
QString m_dirName;
/** Stored current selection with the list of all selected items */
......
......@@ -38,6 +38,7 @@ add_subdirectory(taglibmetadata)
add_subdirectory(oggflacmetadata)
add_subdirectory(mp4v2metadata)
if (WITH_QML)
add_subdirectory(qmlcommand)
add_subdirectory(kid3appqml)
endif (WITH_QML)
......
set(plugin_SRCS
qmlcommandplugin.cpp
)
set(plugin_MOC_HDRS
qmlcommandplugin.h
)
set(plugin_NAME QmlCommand)
if (WITH_GCC_PCH)
add_definitions(${GCC_PCH_COMPILE_FLAGS})
endif (WITH_GCC_PCH)
string(TOLOWER ${plugin_NAME} plugin_TARGET)
qt4_wrap_cpp(plugin_GEN_MOC_SRCS ${plugin_MOC_HDRS})
add_library(${plugin_TARGET} ${plugin_SRCS} ${plugin_GEN_MOC_SRCS})
target_link_libraries(${plugin_TARGET} kid3-core ${BASIC_LIBRARIES})
INSTALL_KID3_PLUGIN(${plugin_TARGET} ${plugin_NAME})
/**
* \file qmlprocess.cpp
* \file qmlcommandplugin.cpp
* Starter for QML scripts.
*
* \b Project: Kid3
......@@ -24,7 +24,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "qmlprocess.h"
#include "qmlcommandplugin.h"
#include <QDir>
#if QT_VERSION >= 0x050000
#include <QQuickView>
......@@ -51,6 +51,10 @@
//! @endcond
#endif
#if QT_VERSION < 0x050000
Q_EXPORT_PLUGIN2(QmlCommandPlugin, QmlCommandPlugin)
#endif
namespace {
/**
......@@ -73,23 +77,54 @@ void setupQmlEngine(QQmlEngine* engine, Kid3Application* app)
/**
* Constructor.
*
* @param app application context
* @param parent parent object
*/
QmlProcess::QmlProcess(Kid3Application* app, QObject* parent) : QObject(parent),
m_app(app), m_qmlView(0), m_qmlEngine(0), m_showOutput(false)
QmlCommandPlugin::QmlCommandPlugin(QObject* parent) : QObject(parent),
m_app(0), m_qmlView(0), m_qmlEngine(0), m_showOutput(false)
{
setObjectName(QLatin1String("QmlCommand"));
}
/**
* Destructor.
*/
QmlProcess::~QmlProcess()
QmlCommandPlugin::~QmlCommandPlugin()
{
}
/**
* Get keys of available user commands.
* @return list of keys, ["qml", "qmlview"].
*/
QStringList QmlCommandPlugin::userCommandKeys() const
{
return QStringList() << QLatin1String("qml") << QLatin1String("qmlview");
}
/**
* Initialize processor.
* This method must be invoked before the first call to startUserCommand()
* to set the application context.
* @param app application context
*/
void QmlCommandPlugin::initialize(Kid3Application* app)
{
m_app = app;
}
/**
* Cleanup processor.
* This method must be invoked to close and delete the GUI resources.
*/
void QmlCommandPlugin::cleanup()
{
if (m_qmlView) {
m_qmlView->close();
}
delete m_qmlView;
m_qmlView = 0;
delete m_qmlEngine;
m_qmlEngine = 0;
if (s_messageHandlerInstance == this) {
s_messageHandlerInstance = 0;
}
......@@ -97,17 +132,17 @@ QmlProcess::~QmlProcess()
/**
* Start a QML script.
*
* @param program virtual program, e.g. "qmlview"
* @param key user command name, "qml" or "qmlview"
* @param arguments arguments to pass to script
* @param showOutput true to enable output in output viewer
* @return true if program and arguments are suitable for QML script.
* @param showOutput true to enable output in output viewer, using signal
* commandOutput().
* @return true if command is started.
*/
bool QmlProcess::startQml(const QString& program,
const QStringList& arguments, bool showOutput)
bool QmlCommandPlugin::startUserCommand(
const QString& key, const QStringList& arguments, bool showOutput)
{
if (!arguments.isEmpty()) {
if (program == QLatin1String("qmlview")) {
if (key == QLatin1String("qmlview")) {
m_showOutput = showOutput;
if (!m_qmlView) {
m_qmlView = new QQuickView;
......@@ -128,17 +163,17 @@ bool QmlProcess::startQml(const QString& program,
// Probably an error.
if (m_showOutput && m_qmlView->status() == QQuickView::Error) {
foreach (const QQmlError& err, m_qmlView->errors()) {
emit qmlOutput(err.toString());
emit commandOutput(err.toString());
}
}
m_qmlView->engine()->clearComponentCache();
onEngineFinished();
}
return true;
} else if (program == QLatin1String("qml")) {
} else if (key == QLatin1String("qml")) {
m_showOutput = showOutput;
if (!m_qmlEngine) {
m_qmlEngine = new QQmlEngine(this);
m_qmlEngine = new QQmlEngine;
connect(m_qmlEngine, SIGNAL(quit()), this, SLOT(onQmlEngineQuit()));
setupQmlEngine(m_qmlEngine, m_app);
}
......@@ -152,7 +187,7 @@ bool QmlProcess::startQml(const QString& program,
// Probably an error.
if (m_showOutput && component.isError()) {
foreach (const QQmlError& err, component.errors()) {
emit qmlOutput(err.toString());
emit commandOutput(err.toString());
}
}
m_qmlEngine->clearComponentCache();
......@@ -163,10 +198,19 @@ bool QmlProcess::startQml(const QString& program,
return false;
}
/**
* Return object which emits commandOutput() signal.
* @return this.
*/
QObject* QmlCommandPlugin::qobject()
{
return this;
}
/**
* Called when the QML view is closing.
*/
void QmlProcess::onQmlViewClosing()
void QmlCommandPlugin::onQmlViewClosing()
{
if (QQuickView* view = qobject_cast<QQuickView*>(sender())) {
// This will invoke destruction of the currently loaded QML code.
......@@ -179,7 +223,7 @@ void QmlProcess::onQmlViewClosing()
/**
* Called when Qt.quit() is called from the QML code in the QQuickView.
*/
void QmlProcess::onQmlViewFinished()
void QmlCommandPlugin::onQmlViewFinished()
{
if (m_qmlView) {
#if QT_VERSION >= 0x050000
......@@ -198,7 +242,7 @@ void QmlProcess::onQmlViewFinished()
/**
* Called when Qt.quit() is called from the QML code in the core engine.
*/
void QmlProcess::onQmlEngineQuit()
void QmlCommandPlugin::onQmlEngineQuit()
{
if (m_qmlEngine) {
m_qmlEngine->clearComponentCache();
......@@ -209,7 +253,7 @@ void QmlProcess::onQmlEngineQuit()
/**
* Restore default message handler after QML code is terminated.
*/
void QmlProcess::onEngineFinished()
void QmlCommandPlugin::onEngineFinished()
{
if (m_showOutput) {
#if QT_VERSION >= 0x050000
......@@ -224,7 +268,7 @@ void QmlProcess::onEngineFinished()
/**
* Forward console output to output viewer while QML code is executed.
*/
void QmlProcess::onEngineReady()
void QmlCommandPlugin::onEngineReady()
{
if (m_showOutput) {
s_messageHandlerInstance = this;
......@@ -236,24 +280,24 @@ void QmlProcess::onEngineReady()
}
}
/** Instance of QmlProcess running and generating messages. */
QmlProcess* QmlProcess::s_messageHandlerInstance = 0;
/** Instance of QmlCommandPlugin running and generating messages. */
QmlCommandPlugin* QmlCommandPlugin::s_messageHandlerInstance = 0;
/**
* Message handler emitting qmlOutput().
* Message handler emitting commandOutput().
*/
#if QT_VERSION >= 0x050000
void QmlProcess::messageHandler(QtMsgType, const QMessageLogContext&, const QString& msg)
void QmlCommandPlugin::messageHandler(QtMsgType, const QMessageLogContext&, const QString& msg)
{
if (s_messageHandlerInstance) {
emit s_messageHandlerInstance->qmlOutput(msg);
emit s_messageHandlerInstance->commandOutput(msg);
}
}
#else
void QmlProcess::messageHandler(QtMsgType, const char* msg)
void QmlCommandPlugin::messageHandler(QtMsgType, const char* msg)
{
if (s_messageHandlerInstance) {
emit s_messageHandlerInstance->qmlOutput(QString::fromUtf8(msg));
emit s_messageHandlerInstance->commandOutput(QString::fromUtf8(msg));
}
}
#endif
......
/**
* \file qmlprocess.h
* \file qmlcommandplugin.h
* Starter for QML scripts.
*
* \b Project: Kid3
......@@ -24,14 +24,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef QMLPROCESS_H
#define QMLPROCESS_H
#ifndef QMLCOMMANDPLUGIN_H
#define QMLCOMMANDPLUGIN_H
#include <QObject>
#include "kid3api.h"
#if QT_VERSION < 0x050000
#include <QDeclarativeView>
#endif
#include "iusercommandprocessor.h"
class Kid3Application;
#if QT_VERSION >= 0x050000
......@@ -45,39 +45,69 @@ class QQuickCloseEvent;
/**
* Starter for QML scripts.
*/
class KID3_CORE_EXPORT QmlProcess : public QObject {
class KID3_PLUGIN_EXPORT QmlCommandPlugin :
public QObject, public IUserCommandProcessor {
Q_OBJECT
#if QT_VERSION >= 0x050000
Q_PLUGIN_METADATA(IID "net.sourceforge.kid3.IUserCommandProcessor")
#endif
Q_INTERFACES(IUserCommandProcessor)
public:
/**
* Constructor.
*
* @param app application context
* @param parent parent object
*/
explicit QmlProcess