mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 05:49:53 +08:00
Task: refine task
This commit is contained in:
parent
87ed9250ef
commit
70c984353c
@ -36,6 +36,10 @@ const QString ConfigMgr::c_configFileName = "vnotex.json";
|
|||||||
|
|
||||||
const QString ConfigMgr::c_sessionFileName = "session.json";
|
const QString ConfigMgr::c_sessionFileName = "session.json";
|
||||||
|
|
||||||
|
const QString ConfigMgr::c_userFilesFolder = "user_files";
|
||||||
|
|
||||||
|
const QString ConfigMgr::c_appFilesFolder = "vnotex_files";
|
||||||
|
|
||||||
const QJsonObject &ConfigMgr::Settings::getJson() const
|
const QJsonObject &ConfigMgr::Settings::getJson() const
|
||||||
{
|
{
|
||||||
return m_jobj;
|
return m_jobj;
|
||||||
@ -68,17 +72,21 @@ ConfigMgr::ConfigMgr(bool p_isUnitTest, QObject *p_parent)
|
|||||||
qWarning() << "failed to init ConfigMgr for UnitTest";
|
qWarning() << "failed to init ConfigMgr for UnitTest";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_appConfigFolderPath = m_dirForUnitTest->filePath("vnotex_files");
|
|
||||||
m_userConfigFolderPath = m_dirForUnitTest->filePath("user_files");
|
|
||||||
|
|
||||||
FileUtils::copyFile(getConfigFilePath(Source::Default), PathUtils::concatenateFilePath(m_appConfigFolderPath, c_configFileName));
|
QDir dir(m_dirForUnitTest->path());
|
||||||
} else {
|
dir.mkdir(c_appFilesFolder);
|
||||||
locateConfigFolder();
|
dir.mkdir(c_userFilesFolder);
|
||||||
|
|
||||||
bool needUpdate = checkAppConfig();
|
m_appConfigFolderPath = m_dirForUnitTest->filePath(c_appFilesFolder);
|
||||||
if (needUpdate) {
|
m_userConfigFolderPath = m_dirForUnitTest->filePath(c_userFilesFolder);
|
||||||
checkUserConfig();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
locateConfigFolder();
|
||||||
|
|
||||||
|
bool needUpdate = checkAppConfig();
|
||||||
|
if (needUpdate) {
|
||||||
|
checkUserConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_config->init();
|
m_config->init();
|
||||||
@ -106,8 +114,7 @@ void ConfigMgr::locateConfigFolder()
|
|||||||
qInfo() << "app folder" << appDirPath;
|
qInfo() << "app folder" << appDirPath;
|
||||||
// Check app config.
|
// Check app config.
|
||||||
{
|
{
|
||||||
const QString configFolderName("vnotex_files");
|
QString folderPath(appDirPath + '/' + c_appFilesFolder);
|
||||||
QString folderPath(appDirPath + '/' + configFolderName);
|
|
||||||
if (QDir(folderPath).exists()) {
|
if (QDir(folderPath).exists()) {
|
||||||
// Config folder in app/.
|
// Config folder in app/.
|
||||||
m_appConfigFolderPath = PathUtils::cleanPath(folderPath);
|
m_appConfigFolderPath = PathUtils::cleanPath(folderPath);
|
||||||
@ -118,8 +125,7 @@ void ConfigMgr::locateConfigFolder()
|
|||||||
|
|
||||||
// Check user config.
|
// Check user config.
|
||||||
{
|
{
|
||||||
const QString configFolderName("user_files");
|
QString folderPath(appDirPath + '/' + c_userFilesFolder);
|
||||||
QString folderPath(appDirPath + '/' + configFolderName);
|
|
||||||
if (QDir(folderPath).exists()) {
|
if (QDir(folderPath).exists()) {
|
||||||
// Config folder in app/.
|
// Config folder in app/.
|
||||||
m_userConfigFolderPath = PathUtils::cleanPath(folderPath);
|
m_userConfigFolderPath = PathUtils::cleanPath(folderPath);
|
||||||
@ -564,3 +570,14 @@ QString ConfigMgr::getApplicationVersion()
|
|||||||
|
|
||||||
return appVersion;
|
return appVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QJsonValue ConfigMgr::parseAndReadConfig(const QString &p_exp) const
|
||||||
|
{
|
||||||
|
if (p_exp.startsWith(QStringLiteral("main."))) {
|
||||||
|
return Utils::parseAndReadJson(m_config->toJson(), p_exp.mid(5));
|
||||||
|
} else if (p_exp.startsWith(QStringLiteral("session."))) {
|
||||||
|
return Utils::parseAndReadJson(m_sessionConfig->toJson(), p_exp.mid(8));
|
||||||
|
} else {
|
||||||
|
return QJsonValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -106,6 +106,9 @@ namespace vnotex
|
|||||||
|
|
||||||
QString getConfigFilePath(Source p_src) const;
|
QString getConfigFilePath(Source p_src) const;
|
||||||
|
|
||||||
|
// Parse exp like "[main|session].core.shortcuts.FullScreen" and return the config value.
|
||||||
|
QJsonValue parseAndReadConfig(const QString &p_exp) const;
|
||||||
|
|
||||||
// Called at boostrap without QApplication instance.
|
// Called at boostrap without QApplication instance.
|
||||||
static QString locateSessionConfigFilePathAtBootstrap();
|
static QString locateSessionConfigFilePathAtBootstrap();
|
||||||
|
|
||||||
@ -168,6 +171,10 @@ namespace vnotex
|
|||||||
|
|
||||||
// Name of the session config file.
|
// Name of the session config file.
|
||||||
static const QString c_sessionFileName;
|
static const QString c_sessionFileName;
|
||||||
|
|
||||||
|
static const QString c_userFilesFolder;
|
||||||
|
|
||||||
|
static const QString c_appFilesFolder;
|
||||||
};
|
};
|
||||||
} // ns vnotex
|
} // ns vnotex
|
||||||
|
|
||||||
|
@ -29,11 +29,6 @@ SOURCES += \
|
|||||||
$$PWD/texteditorconfig.cpp \
|
$$PWD/texteditorconfig.cpp \
|
||||||
$$PWD/vnotex.cpp \
|
$$PWD/vnotex.cpp \
|
||||||
$$PWD/thememgr.cpp \
|
$$PWD/thememgr.cpp \
|
||||||
$$PWD/task.cpp \
|
|
||||||
$$PWD/taskhelper.cpp \
|
|
||||||
$$PWD/taskmgr.cpp \
|
|
||||||
$$PWD/taskvariablemgr.cpp \
|
|
||||||
$$PWD/shellexecution.cpp \
|
|
||||||
$$PWD/notebookmgr.cpp \
|
$$PWD/notebookmgr.cpp \
|
||||||
$$PWD/theme.cpp \
|
$$PWD/theme.cpp \
|
||||||
$$PWD/sessionconfig.cpp \
|
$$PWD/sessionconfig.cpp \
|
||||||
@ -65,11 +60,6 @@ HEADERS += \
|
|||||||
$$PWD/texteditorconfig.h \
|
$$PWD/texteditorconfig.h \
|
||||||
$$PWD/vnotex.h \
|
$$PWD/vnotex.h \
|
||||||
$$PWD/thememgr.h \
|
$$PWD/thememgr.h \
|
||||||
$$PWD/task.h \
|
|
||||||
$$PWD/taskhelper.h \
|
|
||||||
$$PWD/taskmgr.h \
|
|
||||||
$$PWD/taskvariablemgr.h \
|
|
||||||
$$PWD/shellexecution.h \
|
|
||||||
$$PWD/global.h \
|
$$PWD/global.h \
|
||||||
$$PWD/namebasedserver.h \
|
$$PWD/namebasedserver.h \
|
||||||
$$PWD/exception.h \
|
$$PWD/exception.h \
|
||||||
|
@ -150,7 +150,11 @@ QJsonObject EditorConfig::toJson() const
|
|||||||
obj[m_markdownEditorConfig->getSessionName()] = m_markdownEditorConfig->toJson();
|
obj[m_markdownEditorConfig->getSessionName()] = m_markdownEditorConfig->toJson();
|
||||||
obj[QStringLiteral("core")] = saveCore();
|
obj[QStringLiteral("core")] = saveCore();
|
||||||
obj[QStringLiteral("image_host")] = saveImageHost();
|
obj[QStringLiteral("image_host")] = saveImageHost();
|
||||||
obj[QStringLiteral("vi")] = m_viConfig->toJson();
|
|
||||||
|
// In UT, it may be nullptr.
|
||||||
|
if (m_viConfig) {
|
||||||
|
obj[QStringLiteral("vi")] = m_viConfig->toJson();
|
||||||
|
}
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,6 +265,11 @@ ID NotebookMgr::getCurrentNotebookId() const
|
|||||||
return m_currentNotebookId;
|
return m_currentNotebookId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QSharedPointer<Notebook> NotebookMgr::getCurrentNotebook() const
|
||||||
|
{
|
||||||
|
return findNotebookById(m_currentNotebookId);
|
||||||
|
}
|
||||||
|
|
||||||
void NotebookMgr::setCurrentNotebook(ID p_notebookId)
|
void NotebookMgr::setCurrentNotebook(ID p_notebookId)
|
||||||
{
|
{
|
||||||
auto lastId = m_currentNotebookId;
|
auto lastId = m_currentNotebookId;
|
||||||
|
@ -62,6 +62,8 @@ namespace vnotex
|
|||||||
|
|
||||||
ID getCurrentNotebookId() const;
|
ID getCurrentNotebookId() const;
|
||||||
|
|
||||||
|
QSharedPointer<Notebook> getCurrentNotebook() const;
|
||||||
|
|
||||||
// Find the notebook with the same directory as root folder.
|
// Find the notebook with the same directory as root folder.
|
||||||
QSharedPointer<Notebook> findNotebookByRootFolderPath(const QString &p_rootFolderPath) const;
|
QSharedPointer<Notebook> findNotebookByRootFolderPath(const QString &p_rootFolderPath) const;
|
||||||
|
|
||||||
|
@ -1,102 +0,0 @@
|
|||||||
#include "shellexecution.h"
|
|
||||||
|
|
||||||
#include <QFileInfo>
|
|
||||||
|
|
||||||
using namespace vnotex;
|
|
||||||
|
|
||||||
ShellExecution::ShellExecution()
|
|
||||||
{
|
|
||||||
auto shell = defaultShell();
|
|
||||||
setShellExecutable(shell);
|
|
||||||
setShellArguments(defaultShellArguments(shell));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShellExecution::setProgram(const QString &p_prog)
|
|
||||||
{
|
|
||||||
m_program = p_prog;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShellExecution::setArguments(const QStringList &p_args)
|
|
||||||
{
|
|
||||||
m_args = p_args;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShellExecution::setShellExecutable(const QString &p_exec)
|
|
||||||
{
|
|
||||||
m_shell_executable = p_exec;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShellExecution::setShellArguments(const QStringList &p_args)
|
|
||||||
{
|
|
||||||
m_shell_args = p_args;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShellExecution::setupProcess(QProcess *p_process,
|
|
||||||
const QString &p_program,
|
|
||||||
const QStringList &p_args,
|
|
||||||
const QString &p_shell_exec,
|
|
||||||
const QStringList &p_shell_args)
|
|
||||||
{
|
|
||||||
auto shell_exec = p_shell_exec.isNull() ? defaultShell() : p_shell_exec;
|
|
||||||
auto shell_args = p_shell_args.isEmpty() ? defaultShellArguments(shell_exec)
|
|
||||||
: p_shell_args;
|
|
||||||
p_process->setProgram(shell_exec);
|
|
||||||
auto args = p_args;
|
|
||||||
|
|
||||||
auto shell = shellBasename(shell_exec);
|
|
||||||
if (!p_program.isEmpty() && !p_args.isEmpty()) {
|
|
||||||
args = shellQuote(args, shell_exec);
|
|
||||||
}
|
|
||||||
QStringList allArgs(shell_args);
|
|
||||||
if (shell == "bash") {
|
|
||||||
allArgs << (QStringList() << p_program << args).join(' ');
|
|
||||||
} else {
|
|
||||||
allArgs << p_program << args;
|
|
||||||
}
|
|
||||||
p_process->setArguments(allArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString ShellExecution::shellBasename(const QString &p_shell)
|
|
||||||
{
|
|
||||||
return QFileInfo(p_shell).baseName().toLower();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString ShellExecution::defaultShell()
|
|
||||||
{
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
return "PowerShell.exe";
|
|
||||||
#else
|
|
||||||
return "/bin/bash";
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList ShellExecution::defaultShellArguments(const QString &p_shell)
|
|
||||||
{
|
|
||||||
auto shell = shellBasename(p_shell);
|
|
||||||
if (shell == "cmd") {
|
|
||||||
return {"/C"};
|
|
||||||
} else if (shell == "powershell" || p_shell == "pwsh") {
|
|
||||||
return {"-Command"};
|
|
||||||
} else if (shell == "bash") {
|
|
||||||
return {"-c"};
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
QString ShellExecution::shellQuote(const QString &p_text, const QString &)
|
|
||||||
{
|
|
||||||
if (p_text.contains(' ')) {
|
|
||||||
return QString("\"%1\"").arg(p_text);
|
|
||||||
}
|
|
||||||
return p_text;
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList ShellExecution::shellQuote(const QStringList &p_list, const QString &p_shell)
|
|
||||||
{
|
|
||||||
auto shell = shellBasename(p_shell);
|
|
||||||
QStringList list;
|
|
||||||
for (const auto &s : p_list) {
|
|
||||||
list << shellQuote(s, shell);
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
#ifndef SHELLEXECUTION_H
|
|
||||||
#define SHELLEXECUTION_H
|
|
||||||
|
|
||||||
#include <QProcess>
|
|
||||||
|
|
||||||
namespace vnotex {
|
|
||||||
|
|
||||||
|
|
||||||
class ShellExecution : public QProcess
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
ShellExecution();
|
|
||||||
void setProgram(const QString &p_prog);
|
|
||||||
void setArguments(const QStringList &p_args);
|
|
||||||
void setShellExecutable(const QString &p_exec);
|
|
||||||
void setShellArguments(const QStringList &p_args);
|
|
||||||
|
|
||||||
static void setupProcess(QProcess *p_process,
|
|
||||||
const QString &p_program,
|
|
||||||
const QStringList &p_args = QStringList(),
|
|
||||||
const QString &p_shell_exec = QString(),
|
|
||||||
const QStringList &p_shell_args = QStringList());
|
|
||||||
static QString defaultShell();
|
|
||||||
static QStringList defaultShellArguments(const QString &p_shell);
|
|
||||||
static QString shellQuote(const QString &p_text,
|
|
||||||
const QString &p_shell);
|
|
||||||
static QStringList shellQuote(const QStringList &p_list,
|
|
||||||
const QString &p_shell);
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString m_shell_executable;
|
|
||||||
QStringList m_shell_args;
|
|
||||||
QString m_program;
|
|
||||||
QStringList m_args;
|
|
||||||
|
|
||||||
static QString shellBasename(const QString &p_shell);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // SHELLEXECUTION_H
|
|
@ -1,557 +0,0 @@
|
|||||||
#include "task.h"
|
|
||||||
|
|
||||||
#include <QJsonDocument>
|
|
||||||
#include <QVersionNumber>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QJsonValue>
|
|
||||||
#include <QJsonArray>
|
|
||||||
#include <QAction>
|
|
||||||
#include <QRegularExpression>
|
|
||||||
#include <QInputDialog>
|
|
||||||
#include <QTextCodec>
|
|
||||||
#include <QRandomGenerator>
|
|
||||||
|
|
||||||
#include "utils/fileutils.h"
|
|
||||||
#include "utils/pathutils.h"
|
|
||||||
#include "vnotex.h"
|
|
||||||
#include "exception.h"
|
|
||||||
#include "taskhelper.h"
|
|
||||||
#include "notebook/notebook.h"
|
|
||||||
#include "shellexecution.h"
|
|
||||||
|
|
||||||
|
|
||||||
using namespace vnotex;
|
|
||||||
|
|
||||||
QString Task::s_latestVersion = "0.1.3";
|
|
||||||
TaskVariableMgr Task::s_vars;
|
|
||||||
|
|
||||||
Task *Task::fromFile(const QString &p_file,
|
|
||||||
const QJsonDocument &p_json,
|
|
||||||
const QString &p_locale,
|
|
||||||
QObject *p_parent)
|
|
||||||
{
|
|
||||||
auto task = new Task(p_locale, p_file, p_parent);
|
|
||||||
return fromJson(task, p_json.object());
|
|
||||||
}
|
|
||||||
|
|
||||||
Task* Task::fromJson(Task *p_task,
|
|
||||||
const QJsonObject &p_obj)
|
|
||||||
{
|
|
||||||
if (p_obj.contains("version")) {
|
|
||||||
p_task->dto.version = p_obj["version"].toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto version = QVersionNumber::fromString(p_task->getVersion());
|
|
||||||
if (version < QVersionNumber(1, 0, 0)) {
|
|
||||||
return fromJsonV0(p_task, p_obj);
|
|
||||||
}
|
|
||||||
qWarning() << "Unknow Task Version" << version << p_task->dto._source;
|
|
||||||
return p_task;
|
|
||||||
}
|
|
||||||
|
|
||||||
Task *Task::fromJsonV0(Task *p_task,
|
|
||||||
const QJsonObject &p_obj,
|
|
||||||
bool mergeTasks)
|
|
||||||
{
|
|
||||||
if (p_obj.contains("type")) {
|
|
||||||
p_task->dto.type = p_obj["type"].toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p_obj.contains("icon")) {
|
|
||||||
QString path = p_obj["icon"].toString();
|
|
||||||
QDir iconPath(path);
|
|
||||||
if (iconPath.isRelative()) {
|
|
||||||
QDir taskDir(p_task->dto._source);
|
|
||||||
taskDir.cdUp();
|
|
||||||
path = QDir(taskDir.filePath(path)).absolutePath();
|
|
||||||
}
|
|
||||||
if (QFile::exists(path)) {
|
|
||||||
p_task->dto.icon = path;
|
|
||||||
} else {
|
|
||||||
qWarning() << "task icon not exists" << path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p_obj.contains("shortcut")) {
|
|
||||||
p_task->dto.shortcut = p_obj["shortcut"].toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p_obj.contains("type")) {
|
|
||||||
p_task->dto.type = p_obj["type"].toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p_obj.contains("command")) {
|
|
||||||
p_task->dto.command = getLocaleString(p_obj["command"], p_task->m_locale);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p_obj.contains("args")) {
|
|
||||||
p_task->dto.args = getLocaleStringList(p_obj["args"], p_task->m_locale);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p_obj.contains("label")) {
|
|
||||||
p_task->dto.label = getLocaleString(p_obj["label"], p_task->m_locale);
|
|
||||||
} else if (p_task->dto.label.isNull() && !p_task->dto.command.isNull()) {
|
|
||||||
p_task->dto.label = p_task->dto.command;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p_obj.contains("options")) {
|
|
||||||
auto options = p_obj["options"].toObject();
|
|
||||||
|
|
||||||
if (options.contains("cwd")) {
|
|
||||||
p_task->dto.options.cwd = options["cwd"].toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.contains("env")) {
|
|
||||||
p_task->dto.options.env.clear();
|
|
||||||
auto env = options["env"].toObject();
|
|
||||||
for (auto i = env.begin(); i != env.end(); i++) {
|
|
||||||
auto key = i.key();
|
|
||||||
auto value = getLocaleString(i.value(), p_task->m_locale);
|
|
||||||
p_task->dto.options.env.insert(key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.contains("shell") && p_task->getType() == "shell") {
|
|
||||||
auto shell = options["shell"].toObject();
|
|
||||||
|
|
||||||
if (shell.contains("executable")) {
|
|
||||||
p_task->dto.options.shell.executable = shell["executable"].toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shell.contains("args")) {
|
|
||||||
p_task->dto.options.shell.args.clear();
|
|
||||||
|
|
||||||
for (auto arg : shell["args"].toArray()) {
|
|
||||||
p_task->dto.options.shell.args << arg.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p_obj.contains("tasks")) {
|
|
||||||
if (!mergeTasks) p_task->m_tasks.clear();
|
|
||||||
auto tasks = p_obj["tasks"].toArray();
|
|
||||||
|
|
||||||
for (const auto &task : tasks) {
|
|
||||||
auto t = new Task(p_task->m_locale,
|
|
||||||
p_task->getFile(),
|
|
||||||
p_task);
|
|
||||||
p_task->m_tasks.append(fromJson(t, task.toObject()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p_obj.contains("inputs")) {
|
|
||||||
p_task->dto.inputs.clear();
|
|
||||||
auto inputs = p_obj["inputs"].toArray();
|
|
||||||
|
|
||||||
for (const auto &input : inputs) {
|
|
||||||
auto in = input.toObject();
|
|
||||||
InputDTO i;
|
|
||||||
if (in.contains("id")) {
|
|
||||||
i.id = in["id"].toString();
|
|
||||||
} else {
|
|
||||||
qWarning() << "Input configuration not contain id";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (in.contains("type")) {
|
|
||||||
i.type = in["type"].toString();
|
|
||||||
} else {
|
|
||||||
i.type = "promptString";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (in.contains("description")) {
|
|
||||||
i.description = getLocaleString(in["description"], p_task->m_locale);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (in.contains("default")) {
|
|
||||||
i.default_ = getLocaleString(in["default"], p_task->m_locale);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i.type == "promptString" && in.contains("password")) {
|
|
||||||
i.password = in["password"].toBool();
|
|
||||||
} else {
|
|
||||||
i.password = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i.type == "pickString" && in.contains("options")) {
|
|
||||||
i.options = getLocaleStringList(in["options"], p_task->m_locale);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i.type == "pickString" && !i.default_.isNull() && !i.options.contains(i.default_)) {
|
|
||||||
qWarning() << "default must be one of the option values";
|
|
||||||
}
|
|
||||||
|
|
||||||
p_task->dto.inputs << i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p_obj.contains("messages")) {
|
|
||||||
p_task->dto.messages.clear();
|
|
||||||
auto messages = p_obj["messages"].toArray();
|
|
||||||
|
|
||||||
for (const auto &message : messages) {
|
|
||||||
auto msg = message.toObject();
|
|
||||||
MessageDTO m;
|
|
||||||
if (msg.contains("id")) {
|
|
||||||
m.id = msg["id"].toString();
|
|
||||||
} else {
|
|
||||||
qWarning() << "Message configuration not contain id";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (msg.contains("type")) {
|
|
||||||
m.type = msg["type"].toString();
|
|
||||||
} else {
|
|
||||||
m.type = "information";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (msg.contains("title")) {
|
|
||||||
m.title = getLocaleString(msg["title"], p_task->m_locale);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (msg.contains("text")) {
|
|
||||||
m.text = getLocaleString(msg["text"], p_task->m_locale);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (msg.contains("detailedText")) {
|
|
||||||
m.detailedText = getLocaleString(msg["detailedText"], p_task->m_locale);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (msg.contains("buttons")) {
|
|
||||||
auto buttons = msg["buttons"].toArray();
|
|
||||||
for (auto button : buttons) {
|
|
||||||
auto btn = button.toObject();
|
|
||||||
ButtonDTO b;
|
|
||||||
b.text = getLocaleString(btn["text"], p_task->m_locale);
|
|
||||||
m.buttons << b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p_task->dto.messages << m;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// OS-specific task configuration
|
|
||||||
#if defined (Q_OS_WIN)
|
|
||||||
#define OS_SPEC "windows"
|
|
||||||
#endif
|
|
||||||
#if defined (Q_OS_MACOS)
|
|
||||||
#define OS_SPEC "osx"
|
|
||||||
#endif
|
|
||||||
#if defined (Q_OS_LINUX)
|
|
||||||
#define OS_SPEC "linux"
|
|
||||||
#endif
|
|
||||||
if (p_obj.contains(OS_SPEC)) {
|
|
||||||
auto os = p_obj[OS_SPEC].toObject();
|
|
||||||
fromJsonV0(p_task, os, true);
|
|
||||||
}
|
|
||||||
#undef OS_SPEC
|
|
||||||
|
|
||||||
return p_task;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Task::getVersion() const
|
|
||||||
{
|
|
||||||
return dto.version;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Task::getType() const
|
|
||||||
{
|
|
||||||
return dto.type;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Task::getCommand() const
|
|
||||||
{
|
|
||||||
return s_vars.evaluate(dto.command, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList Task::getArgs() const
|
|
||||||
{
|
|
||||||
return s_vars.evaluate(dto.args, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Task::getLabel() const
|
|
||||||
{
|
|
||||||
return dto.label;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Task::getIcon() const
|
|
||||||
{
|
|
||||||
return dto.icon;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Task::getShortcut() const
|
|
||||||
{
|
|
||||||
return dto.shortcut;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Task::getOptionsCwd() const
|
|
||||||
{
|
|
||||||
auto cwd = dto.options.cwd;
|
|
||||||
if (!cwd.isNull()) {
|
|
||||||
return s_vars.evaluate(cwd, this);
|
|
||||||
}
|
|
||||||
auto notebook = TaskHelper::getCurrentNotebook();
|
|
||||||
if (notebook) cwd = notebook->getRootFolderAbsolutePath();
|
|
||||||
if (!cwd.isNull()) {
|
|
||||||
return cwd;
|
|
||||||
}
|
|
||||||
cwd = TaskHelper::getCurrentFile();
|
|
||||||
if (!cwd.isNull()) {
|
|
||||||
return QFileInfo(cwd).dir().absolutePath();
|
|
||||||
}
|
|
||||||
return QFileInfo(dto._source).dir().absolutePath();
|
|
||||||
}
|
|
||||||
|
|
||||||
const QMap<QString, QString> &Task::getOptionsEnv() const
|
|
||||||
{
|
|
||||||
return dto.options.env;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Task::getOptionsShellExecutable() const
|
|
||||||
{
|
|
||||||
return dto.options.shell.executable;
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList Task::getOptionsShellArgs() const
|
|
||||||
{
|
|
||||||
if (dto.options.shell.args.isEmpty()) {
|
|
||||||
return ShellExecution::defaultShellArguments(dto.options.shell.executable);
|
|
||||||
} else {
|
|
||||||
return s_vars.evaluate(dto.options.shell.args, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const QVector<Task *> &Task::getTasks() const
|
|
||||||
{
|
|
||||||
return m_tasks;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QVector<InputDTO> &Task::getInputs() const
|
|
||||||
{
|
|
||||||
return dto.inputs;
|
|
||||||
}
|
|
||||||
|
|
||||||
InputDTO Task::getInput(const QString &p_id) const
|
|
||||||
{
|
|
||||||
for (auto i : dto.inputs) {
|
|
||||||
if (i.id == p_id) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
qDebug() << getLabel();
|
|
||||||
qWarning() << "input" << p_id << "not found";
|
|
||||||
throw "Input variable can not found";
|
|
||||||
}
|
|
||||||
|
|
||||||
MessageDTO Task::getMessage(const QString &p_id) const
|
|
||||||
{
|
|
||||||
for (auto msg : dto.messages) {
|
|
||||||
if (msg.id == p_id) {
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
qDebug() << getLabel();
|
|
||||||
qWarning() << "message" << p_id << "not found";
|
|
||||||
throw "Message can not found";
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Task::getFile() const
|
|
||||||
{
|
|
||||||
return dto._source;
|
|
||||||
}
|
|
||||||
|
|
||||||
Task::Task(const QString &p_locale,
|
|
||||||
const QString &p_file,
|
|
||||||
QObject *p_parent)
|
|
||||||
: QObject(p_parent)
|
|
||||||
{
|
|
||||||
dto._source = p_file;
|
|
||||||
dto.version = s_latestVersion;
|
|
||||||
dto.type = "shell";
|
|
||||||
dto.options.shell.executable = ShellExecution::defaultShell();
|
|
||||||
|
|
||||||
// inherit configuration
|
|
||||||
m_parent = qobject_cast<Task*>(p_parent);
|
|
||||||
if (m_parent) {
|
|
||||||
dto.version = m_parent->dto.version;
|
|
||||||
dto.type = m_parent->dto.type;
|
|
||||||
dto.command = m_parent->dto.command;
|
|
||||||
dto.args = m_parent->dto.args;
|
|
||||||
dto.options.cwd = m_parent->dto.options.cwd;
|
|
||||||
dto.options.env = m_parent->dto.options.env;
|
|
||||||
dto.options.shell.executable = m_parent->dto.options.shell.executable;
|
|
||||||
dto.options.shell.args = m_parent->dto.options.shell.args;
|
|
||||||
// not inherit label/inputs/tasks
|
|
||||||
} else {
|
|
||||||
dto.label = QFileInfo(p_file).baseName();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!p_locale.isNull()) {
|
|
||||||
m_locale = p_locale;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QProcess *Task::setupProcess() const
|
|
||||||
{
|
|
||||||
// Set process property
|
|
||||||
auto command = getCommand();
|
|
||||||
if (command.isEmpty()) return nullptr;
|
|
||||||
auto process = new QProcess(this->parent());
|
|
||||||
process->setWorkingDirectory(getOptionsCwd());
|
|
||||||
|
|
||||||
auto options_env = getOptionsEnv();
|
|
||||||
if (!options_env.isEmpty()) {
|
|
||||||
auto env = QProcessEnvironment::systemEnvironment();
|
|
||||||
for (auto i = options_env.begin(); i != options_env.end(); i++) {
|
|
||||||
env.insert(i.key(), i.value());
|
|
||||||
}
|
|
||||||
process->setProcessEnvironment(env);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto args = getArgs();
|
|
||||||
auto type = getType();
|
|
||||||
|
|
||||||
// set program and args
|
|
||||||
if (type == "shell") {
|
|
||||||
ShellExecution::setupProcess(process,
|
|
||||||
command,
|
|
||||||
args,
|
|
||||||
getOptionsShellExecutable(),
|
|
||||||
getOptionsShellArgs());
|
|
||||||
} else if (getType() == "process") {
|
|
||||||
process->setProgram(command);
|
|
||||||
process->setArguments(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
// connect signal and slot
|
|
||||||
connect(process, &QProcess::started,
|
|
||||||
this, [this]() {
|
|
||||||
emit showOutput(tr("[Task %1 started]\n").arg(getLabel()));
|
|
||||||
});
|
|
||||||
connect(process, &QProcess::readyReadStandardOutput,
|
|
||||||
this, [process, this]() {
|
|
||||||
auto text = textDecode(process->readAllStandardOutput());
|
|
||||||
text = TaskHelper::handleCommand(text, process, this);
|
|
||||||
emit showOutput(text);
|
|
||||||
});
|
|
||||||
connect(process, &QProcess::readyReadStandardError,
|
|
||||||
this, [process, this]() {
|
|
||||||
auto text = process->readAllStandardError();
|
|
||||||
emit showOutput(textDecode(text));
|
|
||||||
});
|
|
||||||
connect(process, &QProcess::errorOccurred,
|
|
||||||
this, [this](QProcess::ProcessError error) {
|
|
||||||
emit showOutput(tr("[Task %1 error occurred with code %2]\n").arg(getLabel(), QString::number(error)));
|
|
||||||
});
|
|
||||||
connect(process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
|
|
||||||
this, [this, process](int exitCode) {
|
|
||||||
emit showOutput(tr("\n[Task %1 finished with exit code %2]\n")
|
|
||||||
.arg(getLabel(), QString::number(exitCode)));
|
|
||||||
process->deleteLater();
|
|
||||||
});
|
|
||||||
return process;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Task::run() const
|
|
||||||
{
|
|
||||||
QProcess *process;
|
|
||||||
try {
|
|
||||||
process = setupProcess();
|
|
||||||
} catch (const char *msg) {
|
|
||||||
qDebug() << msg;
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
if (process) {
|
|
||||||
// start process
|
|
||||||
qInfo() << "run task" << process->program() << process->arguments();
|
|
||||||
process->start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TaskDTO Task::getDTO() const
|
|
||||||
{
|
|
||||||
return dto;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Task::textDecode(const QByteArray &p_text)
|
|
||||||
{
|
|
||||||
static QByteArrayList codecNames = {
|
|
||||||
"UTF-8",
|
|
||||||
"System",
|
|
||||||
"UTF-16",
|
|
||||||
"GB18030"
|
|
||||||
};
|
|
||||||
for (auto name : codecNames) {
|
|
||||||
auto text = textDecode(p_text, name);
|
|
||||||
if (!text.isNull()) return text;
|
|
||||||
}
|
|
||||||
return p_text;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Task::textDecode(const QByteArray &p_text, const QByteArray &name)
|
|
||||||
{
|
|
||||||
auto codec = QTextCodec::codecForName(name);
|
|
||||||
if (codec) {
|
|
||||||
QTextCodec::ConverterState state;
|
|
||||||
auto text = codec->toUnicode(p_text.data(), p_text.size(), &state);
|
|
||||||
if (state.invalidChars > 0) return QString();
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool Task::isValidTaskFile(const QString &p_file,
|
|
||||||
QJsonDocument &p_json)
|
|
||||||
{
|
|
||||||
QFile file(p_file);
|
|
||||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
QJsonParseError error;
|
|
||||||
p_json = QJsonDocument::fromJson(file.readAll(), &error);
|
|
||||||
file.close();
|
|
||||||
if (p_json.isNull()) {
|
|
||||||
qDebug() << "load task" << p_file << "failed: " << error.errorString()
|
|
||||||
<< "at offset" << error.offset;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Task::getLocaleString(const QJsonValue &p_value, const QString &p_locale)
|
|
||||||
{
|
|
||||||
if (p_value.isObject()) {
|
|
||||||
auto obj = p_value.toObject();
|
|
||||||
if (obj.contains(p_locale)) {
|
|
||||||
return obj.value(p_locale).toString();
|
|
||||||
} else {
|
|
||||||
qWarning() << "current locale" << p_locale << "not found";
|
|
||||||
if (!obj.isEmpty()){
|
|
||||||
return obj.begin().value().toString();
|
|
||||||
} else {
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return p_value.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList Task::getLocaleStringList(const QJsonValue &p_value, const QString &p_locale)
|
|
||||||
{
|
|
||||||
QStringList list;
|
|
||||||
for (auto value : p_value.toArray()) {
|
|
||||||
list << getLocaleString(value, p_locale);
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList Task::getStringList(const QJsonValue &p_value)
|
|
||||||
{
|
|
||||||
QStringList list;
|
|
||||||
for (auto value : p_value.toArray()) {
|
|
||||||
list << value.toString();
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
185
src/core/task.h
185
src/core/task.h
@ -1,185 +0,0 @@
|
|||||||
#ifndef TASK_H
|
|
||||||
#define TASK_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QVector>
|
|
||||||
#include <QMap>
|
|
||||||
#include <QProcess>
|
|
||||||
|
|
||||||
#include "taskvariablemgr.h"
|
|
||||||
|
|
||||||
class QAction;
|
|
||||||
|
|
||||||
namespace vnotex {
|
|
||||||
|
|
||||||
struct ButtonDTO {
|
|
||||||
QString text;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct InputDTO {
|
|
||||||
QString id;
|
|
||||||
|
|
||||||
QString type;
|
|
||||||
|
|
||||||
QString description;
|
|
||||||
|
|
||||||
QString default_;
|
|
||||||
|
|
||||||
bool password;
|
|
||||||
|
|
||||||
QStringList options;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MessageDTO {
|
|
||||||
QString id;
|
|
||||||
|
|
||||||
QString type;
|
|
||||||
|
|
||||||
QString title;
|
|
||||||
|
|
||||||
QString text;
|
|
||||||
|
|
||||||
QString detailedText;
|
|
||||||
|
|
||||||
QVector<ButtonDTO> buttons;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ShellOptionsDTO {
|
|
||||||
QString executable;
|
|
||||||
|
|
||||||
QStringList args;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TaskOptionsDTO {
|
|
||||||
QString cwd;
|
|
||||||
|
|
||||||
QMap<QString, QString> env;
|
|
||||||
|
|
||||||
ShellOptionsDTO shell;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TaskDTO {
|
|
||||||
QString version;
|
|
||||||
|
|
||||||
QString type;
|
|
||||||
|
|
||||||
QString command;
|
|
||||||
|
|
||||||
QStringList args;
|
|
||||||
|
|
||||||
QString label;
|
|
||||||
|
|
||||||
QString icon;
|
|
||||||
|
|
||||||
QString shortcut;
|
|
||||||
|
|
||||||
QVector<InputDTO> inputs;
|
|
||||||
|
|
||||||
QVector<MessageDTO> messages;
|
|
||||||
|
|
||||||
TaskOptionsDTO options;
|
|
||||||
|
|
||||||
QString _scope;
|
|
||||||
|
|
||||||
QString _source;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Notebook;
|
|
||||||
|
|
||||||
class Task : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
|
|
||||||
|
|
||||||
static Task* fromFile(const QString &p_file,
|
|
||||||
const QJsonDocument &p_json,
|
|
||||||
const QString &p_locale,
|
|
||||||
QObject *p_parent = nullptr);
|
|
||||||
|
|
||||||
void run() const;
|
|
||||||
|
|
||||||
TaskDTO getDTO() const;
|
|
||||||
|
|
||||||
QString getVersion() const;
|
|
||||||
|
|
||||||
QString getType() const;
|
|
||||||
|
|
||||||
QString getCommand() const;
|
|
||||||
|
|
||||||
QStringList getArgs() const;
|
|
||||||
|
|
||||||
QString getLabel() const;
|
|
||||||
|
|
||||||
QString getIcon() const;
|
|
||||||
|
|
||||||
QString getShortcut() const;
|
|
||||||
|
|
||||||
QString getOptionsCwd() const;
|
|
||||||
|
|
||||||
const QMap<QString, QString> &getOptionsEnv() const;
|
|
||||||
|
|
||||||
QString getOptionsShellExecutable() const;
|
|
||||||
|
|
||||||
QStringList getOptionsShellArgs() const;
|
|
||||||
|
|
||||||
const QVector<Task*> &getTasks() const;
|
|
||||||
|
|
||||||
const QVector<InputDTO> &getInputs() const;
|
|
||||||
|
|
||||||
InputDTO getInput(const QString &p_id) const;
|
|
||||||
|
|
||||||
MessageDTO getMessage(const QString &p_id) const;
|
|
||||||
|
|
||||||
QString getFile() const;
|
|
||||||
|
|
||||||
static QString s_latestVersion;
|
|
||||||
|
|
||||||
static bool isValidTaskFile(const QString &p_file,
|
|
||||||
QJsonDocument &p_json);
|
|
||||||
|
|
||||||
static QString getLocaleString(const QJsonValue &p_value,
|
|
||||||
const QString &p_locale);
|
|
||||||
|
|
||||||
static QStringList getLocaleStringList(const QJsonValue &p_value,
|
|
||||||
const QString &p_locale);
|
|
||||||
|
|
||||||
static QStringList getStringList(const QJsonValue &p_value);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void showOutput(const QString &p_text) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
static Task* fromJson(Task *p_task,
|
|
||||||
const QJsonObject &p_obj);
|
|
||||||
|
|
||||||
static Task* fromJsonV0(Task *p_task,
|
|
||||||
const QJsonObject &p_obj,
|
|
||||||
bool mergeTasks = false);
|
|
||||||
|
|
||||||
explicit Task(const QString &p_locale,
|
|
||||||
const QString &p_file = QString(),
|
|
||||||
QObject *p_parent = nullptr);
|
|
||||||
|
|
||||||
QProcess *setupProcess() const;
|
|
||||||
|
|
||||||
static QString textDecode(const QByteArray &p_text);
|
|
||||||
|
|
||||||
static QString textDecode(const QByteArray &p_text, const QByteArray &name);
|
|
||||||
|
|
||||||
TaskDTO dto;
|
|
||||||
|
|
||||||
Task *m_parent = nullptr;
|
|
||||||
|
|
||||||
QVector<Task*> m_tasks;
|
|
||||||
|
|
||||||
QString m_locale;
|
|
||||||
|
|
||||||
static TaskVariableMgr s_vars;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
} // ns vnotex
|
|
||||||
|
|
||||||
#endif // TASK_H
|
|
@ -1,233 +0,0 @@
|
|||||||
#include "taskhelper.h"
|
|
||||||
|
|
||||||
#include <QRegularExpression>
|
|
||||||
#include <QJsonArray>
|
|
||||||
#include <QMessageBox>
|
|
||||||
#include <QPushButton>
|
|
||||||
#include <QInputDialog>
|
|
||||||
|
|
||||||
#include "vnotex.h"
|
|
||||||
#include "notebookmgr.h"
|
|
||||||
#include <widgets/mainwindow.h>
|
|
||||||
#include <widgets/viewarea.h>
|
|
||||||
#include <widgets/viewwindow.h>
|
|
||||||
#include <widgets/dialogs/selectdialog.h>
|
|
||||||
#include <widgets/markdownviewwindow.h>
|
|
||||||
#include <widgets/textviewwindow.h>
|
|
||||||
#include <utils/pathutils.h>
|
|
||||||
|
|
||||||
using namespace vnotex;
|
|
||||||
|
|
||||||
QString TaskHelper::normalPath(const QString &p_text)
|
|
||||||
{
|
|
||||||
#if defined (Q_OS_WIN)
|
|
||||||
return QString(p_text).replace('/', '\\');
|
|
||||||
#endif
|
|
||||||
return p_text;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TaskHelper::getCurrentFile()
|
|
||||||
{
|
|
||||||
auto win = VNoteX::getInst().getMainWindow()->getViewArea()->getCurrentViewWindow();
|
|
||||||
QString file;
|
|
||||||
if (win && win->getBuffer()) {
|
|
||||||
file = win->getBuffer()->getPath();
|
|
||||||
}
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
QSharedPointer<Notebook> TaskHelper::getCurrentNotebook()
|
|
||||||
{
|
|
||||||
const auto ¬ebookMgr = VNoteX::getInst().getNotebookMgr();
|
|
||||||
auto id = notebookMgr.getCurrentNotebookId();
|
|
||||||
if (id == Notebook::InvalidId) return nullptr;
|
|
||||||
return notebookMgr.findNotebookById(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TaskHelper::getFileNotebookFolder(const QString p_currentFile)
|
|
||||||
{
|
|
||||||
const auto ¬ebookMgr = VNoteX::getInst().getNotebookMgr();
|
|
||||||
const auto ¬ebooks = notebookMgr.getNotebooks();
|
|
||||||
for (auto notebook : notebooks) {
|
|
||||||
auto rootPath = notebook->getRootFolderAbsolutePath();
|
|
||||||
if (PathUtils::pathContains(rootPath, p_currentFile)) {
|
|
||||||
return rootPath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TaskHelper::getSelectedText()
|
|
||||||
{
|
|
||||||
auto window = VNoteX::getInst().getMainWindow()->getViewArea()->getCurrentViewWindow();
|
|
||||||
{
|
|
||||||
auto win = dynamic_cast<MarkdownViewWindow*>(window);
|
|
||||||
if (win) {
|
|
||||||
return win->selectedText();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto win = dynamic_cast<TextViewWindow*>(window);
|
|
||||||
if (win) {
|
|
||||||
return win->selectedText();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList TaskHelper::getAllSpecialVariables(const QString &p_name, const QString &p_text)
|
|
||||||
{
|
|
||||||
QStringList list;
|
|
||||||
QRegularExpression re(QString(R"(\$\{[\t ]*%1[\t ]*:[\t ]*(.*?)[\t ]*\})").arg(p_name));
|
|
||||||
auto it = re.globalMatch(p_text);
|
|
||||||
while (it.hasNext()) {
|
|
||||||
list << it.next().captured(1);
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TaskHelper::replaceAllSepcialVariables(const QString &p_name,
|
|
||||||
const QString &p_text,
|
|
||||||
const QMap<QString, QString> &p_map)
|
|
||||||
{
|
|
||||||
auto text = p_text;
|
|
||||||
for (auto i = p_map.begin(); i != p_map.end(); i++) {
|
|
||||||
auto key = QString(i.key()).replace(".", "\\.").replace("[", "\\[").replace("]", "\\]");
|
|
||||||
auto pattern = QRegularExpression(QString(R"(\$\{[\t ]*%1[\t ]*:[\t ]*%2[\t ]*\})").arg(p_name, key));
|
|
||||||
text = text.replace(pattern, i.value());
|
|
||||||
}
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TaskHelper::evaluateJsonExpr(const QJsonObject &p_obj, const QString &p_expr)
|
|
||||||
{
|
|
||||||
QJsonValue value = p_obj;
|
|
||||||
for (auto token : p_expr.split('.')) {
|
|
||||||
auto pos = token.indexOf('[');
|
|
||||||
auto name = token.mid(0, pos);
|
|
||||||
value = value.toObject().value(name);
|
|
||||||
if (pos == -1) continue;
|
|
||||||
if (token.back() == ']') {
|
|
||||||
for (auto idx : token.mid(pos+1, token.length()-pos-2).split("][")) {
|
|
||||||
bool ok;
|
|
||||||
auto index = idx.toInt(&ok);
|
|
||||||
if (!ok) throw "Config variable syntax error!";
|
|
||||||
value = value.toArray().at(index);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw "Config variable syntax error!";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (value.isBool()) {
|
|
||||||
if (value.toBool()) return "true";
|
|
||||||
else return "false";
|
|
||||||
} else if (value.isDouble()) {
|
|
||||||
return QString::number(value.toDouble());
|
|
||||||
} else if (value.isNull()) {
|
|
||||||
return "null";
|
|
||||||
} else if (value.isUndefined()) {
|
|
||||||
return "undefined";
|
|
||||||
}
|
|
||||||
return value.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TaskHelper::getPathSeparator()
|
|
||||||
{
|
|
||||||
#if defined (Q_OS_WIN)
|
|
||||||
return "\\";
|
|
||||||
#else
|
|
||||||
return "/";
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TaskHelper::handleCommand(const QString &p_text,
|
|
||||||
QProcess *p_process,
|
|
||||||
const Task *p_task)
|
|
||||||
{
|
|
||||||
QRegularExpression re(R"(^::([a-zA-Z-]+)(.*?)?::(.*?)$)",
|
|
||||||
QRegularExpression::MultilineOption);
|
|
||||||
auto i = re.globalMatch(p_text);
|
|
||||||
while (i.hasNext()) {
|
|
||||||
auto match = i.next();
|
|
||||||
auto cmd = match.captured(1).toLower();
|
|
||||||
auto args = match.captured(2).trimmed().split(',');
|
|
||||||
auto value = match.captured(3);
|
|
||||||
|
|
||||||
QMap<QString, QString> arg;
|
|
||||||
for (const auto &i : args) {
|
|
||||||
auto s = i.trimmed();
|
|
||||||
auto p = s.indexOf('=');
|
|
||||||
auto name = s.mid(0, p);
|
|
||||||
QString val;
|
|
||||||
if (p != -1) {
|
|
||||||
val = s.mid(p+1);
|
|
||||||
}
|
|
||||||
arg.insert(name, val);
|
|
||||||
}
|
|
||||||
if (cmd == "show-message") {
|
|
||||||
QMessageBox box;
|
|
||||||
// fill message dto
|
|
||||||
auto msgId = arg.value("id");
|
|
||||||
QVector<QPushButton*> buttons;
|
|
||||||
if (!msgId.isEmpty()) {
|
|
||||||
MessageDTO msgd = p_task->getMessage(msgId);
|
|
||||||
box.setWindowTitle(msgd.title);
|
|
||||||
box.setText(msgd.text);
|
|
||||||
box.setDetailedText(msgd.detailedText);
|
|
||||||
for (auto button : msgd.buttons) {
|
|
||||||
buttons.append(box.addButton(button.text, QMessageBox::ActionRole));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// fill args
|
|
||||||
if (arg.contains("title")) box.setWindowTitle(arg["title"]);
|
|
||||||
if (arg.contains("text")) box.setText(arg["text"]);
|
|
||||||
if (arg.contains("detailedText")) box.setWindowTitle(arg["detailedText"]);
|
|
||||||
if (arg.contains("buttons")) {
|
|
||||||
buttons.clear();
|
|
||||||
for (auto button : arg["buttons"].split('|')) {
|
|
||||||
buttons.append(box.addButton(button, QMessageBox::ActionRole));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
box.exec();
|
|
||||||
int clickedBtnId;
|
|
||||||
for (clickedBtnId = 0; clickedBtnId < buttons.size(); clickedBtnId++) {
|
|
||||||
if (box.clickedButton() == buttons.at(clickedBtnId)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (p_process) {
|
|
||||||
if (p_process->state() == QProcess::Running) {
|
|
||||||
p_process->write(QByteArray::number(clickedBtnId)+"\n");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
qWarning() << "process finished!";
|
|
||||||
}
|
|
||||||
} else if (cmd == "show-inputdialog") {
|
|
||||||
QInputDialog dialog;
|
|
||||||
dialog.setWindowTitle(arg.value("title"));
|
|
||||||
|
|
||||||
} else if (cmd == "show-info") {
|
|
||||||
QMessageBox::information(VNoteX::getInst().getMainWindow(),
|
|
||||||
arg.value("title"),
|
|
||||||
value);
|
|
||||||
} else if (cmd == "show-question") {
|
|
||||||
auto ret = QMessageBox::question(VNoteX::getInst().getMainWindow(),
|
|
||||||
arg.value("title"),
|
|
||||||
value);
|
|
||||||
if (p_process) {
|
|
||||||
if (p_process->state() == QProcess::Running) {
|
|
||||||
p_process->write(QByteArray::number(ret)+"\n");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
qWarning() << "process finished!";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto text = p_text;
|
|
||||||
return text.replace(re, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
TaskHelper::TaskHelper()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
#ifndef TASKHELPER_H
|
|
||||||
#define TASKHELPER_H
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
#include <QSharedPointer>
|
|
||||||
|
|
||||||
class QProcess;
|
|
||||||
|
|
||||||
namespace vnotex {
|
|
||||||
|
|
||||||
class Notebook;
|
|
||||||
class Task;
|
|
||||||
|
|
||||||
class TaskHelper
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// helper functions
|
|
||||||
|
|
||||||
static QString normalPath(const QString &p_text);
|
|
||||||
|
|
||||||
static QString getCurrentFile();
|
|
||||||
|
|
||||||
static QSharedPointer<Notebook> getCurrentNotebook();
|
|
||||||
|
|
||||||
static QString getFileNotebookFolder(const QString p_currentFile);
|
|
||||||
|
|
||||||
static QString getSelectedText();
|
|
||||||
|
|
||||||
static QStringList getAllSpecialVariables(const QString &p_name, const QString &p_text);
|
|
||||||
|
|
||||||
static QString replaceAllSepcialVariables(const QString &p_name,
|
|
||||||
const QString &p_text,
|
|
||||||
const QMap<QString, QString> &p_map);
|
|
||||||
|
|
||||||
static QString evaluateJsonExpr(const QJsonObject &p_obj,
|
|
||||||
const QString &p_expr);
|
|
||||||
|
|
||||||
static QString getPathSeparator();
|
|
||||||
|
|
||||||
static QString handleCommand(const QString &p_text,
|
|
||||||
QProcess *p_process,
|
|
||||||
const Task *p_task);
|
|
||||||
|
|
||||||
private:
|
|
||||||
TaskHelper();
|
|
||||||
};
|
|
||||||
|
|
||||||
} // ns vnotex
|
|
||||||
|
|
||||||
#endif // TASKHELPER_H
|
|
@ -1,192 +0,0 @@
|
|||||||
#include "taskmgr.h"
|
|
||||||
|
|
||||||
#include <QDir>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QFileSystemWatcher>
|
|
||||||
#include <QJsonDocument>
|
|
||||||
|
|
||||||
#include "configmgr.h"
|
|
||||||
#include "coreconfig.h"
|
|
||||||
#include "utils/pathutils.h"
|
|
||||||
#include "utils/fileutils.h"
|
|
||||||
#include "vnotex.h"
|
|
||||||
#include "notebookmgr.h"
|
|
||||||
#include "notebookconfigmgr/bundlenotebookconfigmgr.h"
|
|
||||||
|
|
||||||
using namespace vnotex;
|
|
||||||
|
|
||||||
QStringList TaskMgr::s_searchPaths;
|
|
||||||
|
|
||||||
TaskMgr::TaskMgr(QObject *parent)
|
|
||||||
: QObject(parent)
|
|
||||||
{
|
|
||||||
m_watcher = new QFileSystemWatcher(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TaskMgr::init()
|
|
||||||
{
|
|
||||||
// load all tasks and watch them
|
|
||||||
loadAllTask();
|
|
||||||
|
|
||||||
connect(&VNoteX::getInst().getNotebookMgr(), &NotebookMgr::currentNotebookChanged,
|
|
||||||
this, &TaskMgr::loadAllTask);
|
|
||||||
connect(m_watcher, &QFileSystemWatcher::directoryChanged,
|
|
||||||
this, &TaskMgr::loadAllTask);
|
|
||||||
connect(m_watcher, &QFileSystemWatcher::fileChanged,
|
|
||||||
this, &TaskMgr::loadAllTask);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TaskMgr::refresh()
|
|
||||||
{
|
|
||||||
loadAvailableTasks();
|
|
||||||
}
|
|
||||||
|
|
||||||
QVector<Task*> TaskMgr::getAllTasks() const
|
|
||||||
{
|
|
||||||
QVector<Task*> tasks;
|
|
||||||
tasks.append(m_appTasks);
|
|
||||||
tasks.append(m_userTasks);
|
|
||||||
tasks.append(m_notebookTasks);
|
|
||||||
return tasks;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QVector<Task *> &TaskMgr::getAppTasks() const
|
|
||||||
{
|
|
||||||
return m_appTasks;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QVector<Task *> &TaskMgr::getUserTasks() const
|
|
||||||
{
|
|
||||||
return m_userTasks;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QVector<Task *> &TaskMgr::getNotebookTasks() const
|
|
||||||
{
|
|
||||||
return m_notebookTasks;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TaskMgr::addSearchPath(const QString &p_path)
|
|
||||||
{
|
|
||||||
s_searchPaths << p_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TaskMgr::getNotebookTaskFolder()
|
|
||||||
{
|
|
||||||
const auto ¬ebookMgr = VNoteX::getInst().getNotebookMgr();
|
|
||||||
auto id = notebookMgr.getCurrentNotebookId();
|
|
||||||
if (id == Notebook::InvalidId) return QString();
|
|
||||||
auto notebook = notebookMgr.findNotebookById(id);
|
|
||||||
if (!notebook) return QString();
|
|
||||||
auto configMgr = notebook->getConfigMgr();
|
|
||||||
if (!configMgr) return QString();
|
|
||||||
auto configMgrName = configMgr->getName();
|
|
||||||
if (configMgrName == "vx.vnotex") {
|
|
||||||
QDir dir(notebook->getRootFolderAbsolutePath());
|
|
||||||
dir.cd(BundleNotebookConfigMgr::getConfigFolderName());
|
|
||||||
if (!dir.cd("tasks")) return QString();
|
|
||||||
return dir.absolutePath();
|
|
||||||
} else {
|
|
||||||
qWarning() << "Unknow notebook config type"<< configMgrName <<"task will not be load.";
|
|
||||||
}
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TaskMgr::addWatchPaths(const QStringList &list)
|
|
||||||
{
|
|
||||||
if (list.isEmpty()) return ;
|
|
||||||
qDebug() << "addWatchPaths" << list;
|
|
||||||
m_watcher->addPaths(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TaskMgr::clearWatchPaths()
|
|
||||||
{
|
|
||||||
auto entrys = m_watcher->files();
|
|
||||||
if (!entrys.isEmpty()) {
|
|
||||||
m_watcher->removePaths(entrys);
|
|
||||||
}
|
|
||||||
entrys = m_watcher->directories();
|
|
||||||
if (!entrys.isEmpty()) {
|
|
||||||
m_watcher->removePaths(entrys);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TaskMgr::clearTasks()
|
|
||||||
{
|
|
||||||
m_appTasks.clear();
|
|
||||||
m_userTasks.clear();
|
|
||||||
m_notebookTasks.clear();
|
|
||||||
m_files.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TaskMgr::addAllTaskFolder()
|
|
||||||
{
|
|
||||||
s_searchPaths.clear();
|
|
||||||
auto &configMgr = ConfigMgr::getInst();
|
|
||||||
// App scope task folder
|
|
||||||
addSearchPath(configMgr.getAppTaskFolder());
|
|
||||||
// User scope task folder
|
|
||||||
addSearchPath(configMgr.getUserTaskFolder());
|
|
||||||
// Notebook scope task folder
|
|
||||||
auto path = getNotebookTaskFolder();
|
|
||||||
if (!path.isNull()) addSearchPath(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TaskMgr::loadAllTask()
|
|
||||||
{
|
|
||||||
addAllTaskFolder();
|
|
||||||
loadAvailableTasks();
|
|
||||||
watchTaskEntrys();
|
|
||||||
emit taskChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TaskMgr::watchTaskEntrys()
|
|
||||||
{
|
|
||||||
clearWatchPaths();
|
|
||||||
addWatchPaths(s_searchPaths);
|
|
||||||
for (const auto &pa : s_searchPaths) {
|
|
||||||
addWatchPaths(FileUtils::entryListRecursively(pa, QStringList(), QDir::AllDirs));
|
|
||||||
}
|
|
||||||
addWatchPaths(m_files);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TaskMgr::loadAvailableTasks()
|
|
||||||
{
|
|
||||||
m_files.clear();
|
|
||||||
auto &configMgr = ConfigMgr::getInst();
|
|
||||||
loadAvailableTasks(m_appTasks, configMgr.getAppTaskFolder());
|
|
||||||
loadAvailableTasks(m_userTasks, configMgr.getUserTaskFolder());
|
|
||||||
loadAvailableTasks(m_notebookTasks, getNotebookTaskFolder());
|
|
||||||
}
|
|
||||||
|
|
||||||
void TaskMgr::loadAvailableTasks(QVector<Task *> &p_tasks, const QString &p_searchPath)
|
|
||||||
{
|
|
||||||
p_tasks.clear();
|
|
||||||
if (p_searchPath.isEmpty()) return ;
|
|
||||||
const auto taskFiles = FileUtils::entryListRecursively(p_searchPath, {"*.json"}, QDir::Files);
|
|
||||||
for (auto &file : taskFiles) {
|
|
||||||
m_files << file;
|
|
||||||
checkAndAddTaskFile(p_tasks, file);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
QStringList list;
|
|
||||||
for (auto task : p_tasks) {
|
|
||||||
list << QFileInfo(task->getFile()).fileName();
|
|
||||||
}
|
|
||||||
if (!p_tasks.isEmpty()) qDebug() << "load tasks" << list;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TaskMgr::checkAndAddTaskFile(QVector<Task *> &p_tasks, const QString &p_file)
|
|
||||||
{
|
|
||||||
QJsonDocument json;
|
|
||||||
if (Task::isValidTaskFile(p_file, json)) {
|
|
||||||
const auto localeStr = ConfigMgr::getInst().getCoreConfig().getLocaleToUse();
|
|
||||||
p_tasks.push_back(Task::fromFile(p_file, json, localeStr, this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TaskMgr::deleteTask(Task *p_task)
|
|
||||||
{
|
|
||||||
Q_UNUSED(p_task);
|
|
||||||
}
|
|
@ -1,79 +0,0 @@
|
|||||||
#ifndef TASKMGR_H
|
|
||||||
#define TASKMGR_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
#include <QVector>
|
|
||||||
|
|
||||||
#include "task.h"
|
|
||||||
|
|
||||||
class QFileSystemWatcher;
|
|
||||||
|
|
||||||
namespace vnotex
|
|
||||||
{
|
|
||||||
class TaskMgr : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
|
|
||||||
explicit TaskMgr(QObject *parent = nullptr);
|
|
||||||
|
|
||||||
// It will be invoked after MainWindow show
|
|
||||||
void init();
|
|
||||||
|
|
||||||
void refresh();
|
|
||||||
|
|
||||||
QVector<Task*> getAllTasks() const;
|
|
||||||
|
|
||||||
const QVector<Task*> &getAppTasks() const;
|
|
||||||
|
|
||||||
const QVector<Task*> &getUserTasks() const;
|
|
||||||
|
|
||||||
const QVector<Task*> &getNotebookTasks() const;
|
|
||||||
|
|
||||||
void deleteTask(Task *p_task);
|
|
||||||
|
|
||||||
static void addSearchPath(const QString &p_path);
|
|
||||||
|
|
||||||
static QString getNotebookTaskFolder();
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void taskChanged();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void addWatchPaths(const QStringList &list);
|
|
||||||
|
|
||||||
void clearWatchPaths();
|
|
||||||
|
|
||||||
void clearTasks();
|
|
||||||
|
|
||||||
void addAllTaskFolder();
|
|
||||||
|
|
||||||
void loadAllTask();
|
|
||||||
|
|
||||||
void watchTaskEntrys();
|
|
||||||
|
|
||||||
void loadAvailableTasks();
|
|
||||||
|
|
||||||
void loadAvailableTasks(QVector<Task*> &p_tasks, const QString &p_searchPath);
|
|
||||||
|
|
||||||
void loadTasks(const QString &p_path);
|
|
||||||
|
|
||||||
void checkAndAddTaskFile(QVector<Task*> &p_tasks, const QString &p_file);
|
|
||||||
|
|
||||||
QVector<Task*> m_appTasks;
|
|
||||||
QVector<Task*> m_userTasks;
|
|
||||||
QVector<Task*> m_notebookTasks;
|
|
||||||
|
|
||||||
// all json files in task folder
|
|
||||||
// maybe invalid
|
|
||||||
QStringList m_files;
|
|
||||||
|
|
||||||
QFileSystemWatcher *m_watcher;
|
|
||||||
|
|
||||||
// List of path to search for tasks.
|
|
||||||
static QStringList s_searchPaths;
|
|
||||||
};
|
|
||||||
} // ns vnotex
|
|
||||||
|
|
||||||
#endif // TASKMGR_H
|
|
@ -1,334 +0,0 @@
|
|||||||
#include "taskvariablemgr.h"
|
|
||||||
|
|
||||||
#include <QRegularExpression>
|
|
||||||
#include <QInputDialog>
|
|
||||||
#include <QApplication>
|
|
||||||
#include <QRandomGenerator>
|
|
||||||
#include <QTimeZone>
|
|
||||||
|
|
||||||
#include "vnotex.h"
|
|
||||||
#include "task.h"
|
|
||||||
#include "taskhelper.h"
|
|
||||||
#include "shellexecution.h"
|
|
||||||
#include "configmgr.h"
|
|
||||||
#include "mainconfig.h"
|
|
||||||
#include "notebook/notebook.h"
|
|
||||||
#include <widgets/mainwindow.h>
|
|
||||||
#include <widgets/dialogs/selectdialog.h>
|
|
||||||
|
|
||||||
using namespace vnotex;
|
|
||||||
|
|
||||||
|
|
||||||
TaskVariable::TaskVariable(TaskVariable::Type p_type,
|
|
||||||
const QString &p_name,
|
|
||||||
TaskVariable::Func p_func)
|
|
||||||
: m_type(p_type), m_name(p_name), m_func(p_func)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TaskVariableMgr::TaskVariableMgr()
|
|
||||||
: m_initialized(false)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void TaskVariableMgr::refresh()
|
|
||||||
{
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TaskVariableMgr::evaluate(const QString &p_text,
|
|
||||||
const Task *p_task) const
|
|
||||||
{
|
|
||||||
auto text = p_text;
|
|
||||||
auto eval = [&text](const QString &p_name, std::function<QString()> p_func) {
|
|
||||||
auto reg = QRegularExpression(QString(R"(\$\{[\t ]*%1[\t ]*\})").arg(p_name));
|
|
||||||
if (text.contains(reg)) {
|
|
||||||
text.replace(reg, p_func());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// current notebook variables
|
|
||||||
{
|
|
||||||
eval("notebookFolder", []() {
|
|
||||||
auto notebook = TaskHelper::getCurrentNotebook();
|
|
||||||
if (notebook) {
|
|
||||||
return TaskHelper::normalPath(notebook->getRootFolderAbsolutePath());
|
|
||||||
} else return QString();
|
|
||||||
});
|
|
||||||
eval("notebookFolderBasename", []() {
|
|
||||||
auto notebook = TaskHelper::getCurrentNotebook();
|
|
||||||
if (notebook) {
|
|
||||||
auto folder = notebook->getRootFolderAbsolutePath();
|
|
||||||
return QDir(folder).dirName();
|
|
||||||
} else return QString();
|
|
||||||
});
|
|
||||||
eval("notebookName", []() {
|
|
||||||
auto notebook = TaskHelper::getCurrentNotebook();
|
|
||||||
if (notebook) {
|
|
||||||
return notebook->getName();
|
|
||||||
} else return QString();
|
|
||||||
});
|
|
||||||
eval("notebookName", []() {
|
|
||||||
auto notebook = TaskHelper::getCurrentNotebook();
|
|
||||||
if (notebook) {
|
|
||||||
return notebook->getDescription();
|
|
||||||
} else return QString();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// current file variables
|
|
||||||
{
|
|
||||||
eval("file", []() {
|
|
||||||
return TaskHelper::normalPath(TaskHelper::getCurrentFile());
|
|
||||||
});
|
|
||||||
eval("fileNotebookFolder", []() {
|
|
||||||
auto file = TaskHelper::getCurrentFile();
|
|
||||||
return TaskHelper::normalPath(TaskHelper::getFileNotebookFolder(file));
|
|
||||||
});
|
|
||||||
eval("relativeFile", []() {
|
|
||||||
auto file = TaskHelper::getCurrentFile();
|
|
||||||
auto folder = TaskHelper::getFileNotebookFolder(file);
|
|
||||||
return QDir(folder).relativeFilePath(file);
|
|
||||||
});
|
|
||||||
eval("fileBasename", []() {
|
|
||||||
auto file = TaskHelper::getCurrentFile();
|
|
||||||
return QFileInfo(file).fileName();
|
|
||||||
});
|
|
||||||
eval("fileBasename", []() {
|
|
||||||
auto file = TaskHelper::getCurrentFile();
|
|
||||||
return QFileInfo(file).completeBaseName();
|
|
||||||
});
|
|
||||||
eval("fileDirname", []() {
|
|
||||||
auto file = TaskHelper::getCurrentFile();
|
|
||||||
return TaskHelper::normalPath(QFileInfo(file).dir().absolutePath());
|
|
||||||
});
|
|
||||||
eval("fileExtname", []() {
|
|
||||||
auto file = TaskHelper::getCurrentFile();
|
|
||||||
return QFileInfo(file).suffix();
|
|
||||||
});
|
|
||||||
eval("selectedText", []() {
|
|
||||||
return TaskHelper::getSelectedText();
|
|
||||||
});
|
|
||||||
eval("cwd", [p_task]() {
|
|
||||||
return TaskHelper::normalPath(p_task->getOptionsCwd());
|
|
||||||
});
|
|
||||||
eval("taskFile", [p_task]() {
|
|
||||||
return TaskHelper::normalPath(p_task->getFile());
|
|
||||||
});
|
|
||||||
eval("taskDirname", [p_task]() {
|
|
||||||
return TaskHelper::normalPath(QFileInfo(p_task->getFile()).dir().absolutePath());
|
|
||||||
});
|
|
||||||
eval("execPath", []() {
|
|
||||||
return TaskHelper::normalPath(qApp->applicationFilePath());
|
|
||||||
});
|
|
||||||
eval("pathSeparator", []() {
|
|
||||||
return TaskHelper::getPathSeparator();
|
|
||||||
});
|
|
||||||
eval("notebookTaskFolder", []() {
|
|
||||||
return TaskHelper::normalPath(TaskMgr::getNotebookTaskFolder());
|
|
||||||
});
|
|
||||||
eval("userTaskFolder", []() {
|
|
||||||
return TaskHelper::normalPath(ConfigMgr::getInst().getUserTaskFolder());
|
|
||||||
});
|
|
||||||
eval("appTaskFolder", []() {
|
|
||||||
return TaskHelper::normalPath(ConfigMgr::getInst().getAppTaskFolder());
|
|
||||||
});
|
|
||||||
eval("userThemeFolder", []() {
|
|
||||||
return TaskHelper::normalPath(ConfigMgr::getInst().getUserThemeFolder());
|
|
||||||
});
|
|
||||||
eval("appThemeFolder", []() {
|
|
||||||
return TaskHelper::normalPath(ConfigMgr::getInst().getAppThemeFolder());
|
|
||||||
});
|
|
||||||
eval("userDocsFolder", []() {
|
|
||||||
return TaskHelper::normalPath(ConfigMgr::getInst().getUserDocsFolder());
|
|
||||||
});
|
|
||||||
eval("appDocsFolder", []() {
|
|
||||||
return TaskHelper::normalPath(ConfigMgr::getInst().getAppDocsFolder());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Magic variables
|
|
||||||
{
|
|
||||||
auto cDT = QDateTime::currentDateTime();
|
|
||||||
for(auto s : {
|
|
||||||
"d", "dd", "ddd", "dddd", "M", "MM", "MMM", "MMMM",
|
|
||||||
"yy", "yyyy", "h", "hh", "H", "HH", "m", "mm",
|
|
||||||
"s", "ss", "z", "zzz", "AP", "A", "ap", "a"
|
|
||||||
}) eval(QString("magic:%1").arg(s), [s]() {
|
|
||||||
return QDateTime::currentDateTime().toString(s);
|
|
||||||
});
|
|
||||||
eval("magic:random", []() {
|
|
||||||
return QString::number(QRandomGenerator::global()->generate());
|
|
||||||
});
|
|
||||||
eval("magic:random_d", []() {
|
|
||||||
return QString::number(QRandomGenerator::global()->generate());
|
|
||||||
});
|
|
||||||
eval("magic:date", []() {
|
|
||||||
return QDate::currentDate().toString("yyyy-MM-dd");
|
|
||||||
});
|
|
||||||
eval("magic:da", []() {
|
|
||||||
return QDate::currentDate().toString("yyyyMMdd");
|
|
||||||
});
|
|
||||||
eval("magic:time", []() {
|
|
||||||
return QTime::currentTime().toString("hh:mm:ss");
|
|
||||||
});
|
|
||||||
eval("magic:datetime", []() {
|
|
||||||
return QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
|
|
||||||
});
|
|
||||||
eval("magic:dt", []() {
|
|
||||||
return QDateTime::currentDateTime().toString("yyyyMMdd hh:mm:ss");
|
|
||||||
});
|
|
||||||
eval("magic:note", []() {
|
|
||||||
auto file = TaskHelper::getCurrentFile();
|
|
||||||
return QFileInfo(file).fileName();
|
|
||||||
});
|
|
||||||
eval("magic:no", []() {
|
|
||||||
auto file = TaskHelper::getCurrentFile();
|
|
||||||
return QFileInfo(file).completeBaseName();
|
|
||||||
});
|
|
||||||
eval("magic:t", []() {
|
|
||||||
auto dt = QDateTime::currentDateTime();
|
|
||||||
return dt.timeZone().displayName(dt, QTimeZone::ShortName);
|
|
||||||
});
|
|
||||||
eval("magic:w", []() {
|
|
||||||
return QString::number(QDate::currentDate().weekNumber());
|
|
||||||
});
|
|
||||||
eval("magic:att", []() {
|
|
||||||
// TODO
|
|
||||||
return QString();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// environment variables
|
|
||||||
do {
|
|
||||||
QMap<QString, QString> map;
|
|
||||||
auto list = TaskHelper::getAllSpecialVariables("env", p_text);
|
|
||||||
list.erase(std::unique(list.begin(), list.end()), list.end());
|
|
||||||
if (list.isEmpty()) break;
|
|
||||||
for (const auto &name : list) {
|
|
||||||
auto value = QProcessEnvironment::systemEnvironment().value(name);
|
|
||||||
map.insert(name, value);
|
|
||||||
}
|
|
||||||
text = TaskHelper::replaceAllSepcialVariables("env", text, map);
|
|
||||||
} while(0);
|
|
||||||
|
|
||||||
// config variables
|
|
||||||
do {
|
|
||||||
const auto config_obj = ConfigMgr::getInst().getConfig().toJson();
|
|
||||||
QMap<QString, QString> map;
|
|
||||||
auto list = TaskHelper::getAllSpecialVariables("config", p_text);
|
|
||||||
if (list.isEmpty()) break;
|
|
||||||
list.erase(std::unique(list.begin(), list.end()), list.end());
|
|
||||||
for (const auto &name : list) {
|
|
||||||
auto value = TaskHelper::evaluateJsonExpr(config_obj, name);
|
|
||||||
qDebug() << "insert" << name << value;
|
|
||||||
map.insert(name, value);
|
|
||||||
}
|
|
||||||
text = TaskHelper::replaceAllSepcialVariables("config", text, map);
|
|
||||||
} while(0);
|
|
||||||
|
|
||||||
// input variables
|
|
||||||
text = evaluateInputVariables(text, p_task);
|
|
||||||
// shell variables
|
|
||||||
text = evaluateShellVariables(text, p_task);
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList TaskVariableMgr::evaluate(const QStringList &p_list,
|
|
||||||
const Task *p_task) const
|
|
||||||
{
|
|
||||||
QStringList list;
|
|
||||||
for (const auto &s : p_list) {
|
|
||||||
list << evaluate(s, p_task);
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TaskVariableMgr::init()
|
|
||||||
{
|
|
||||||
if (m_initialized) return ;
|
|
||||||
m_initialized = true;
|
|
||||||
|
|
||||||
add(TaskVariable::FunctionBased,
|
|
||||||
"file",
|
|
||||||
[](const TaskVariable *, const Task *) {
|
|
||||||
return QString();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void TaskVariableMgr::add(TaskVariable::Type p_type,
|
|
||||||
const QString &p_name,
|
|
||||||
TaskVariable::Func p_func)
|
|
||||||
{
|
|
||||||
m_predefs.insert(p_name, TaskVariable(p_type, p_name, p_func));
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TaskVariableMgr::evaluateInputVariables(const QString &p_text,
|
|
||||||
const Task *p_task) const
|
|
||||||
{
|
|
||||||
QMap<QString, QString> map;
|
|
||||||
auto list = TaskHelper::getAllSpecialVariables("input", p_text);
|
|
||||||
list.erase(std::unique(list.begin(), list.end()), list.end());
|
|
||||||
if (list.isEmpty()) return p_text;
|
|
||||||
for (const auto &id : list) {
|
|
||||||
auto input = p_task->getInput(id);
|
|
||||||
QString text;
|
|
||||||
auto mainwin = VNoteX::getInst().getMainWindow();
|
|
||||||
if (input.type == "promptString") {
|
|
||||||
auto desc = evaluate(input.description, p_task);
|
|
||||||
auto defaultText = evaluate(input.default_, p_task);
|
|
||||||
QInputDialog dialog(mainwin);
|
|
||||||
dialog.setInputMode(QInputDialog::TextInput);
|
|
||||||
if (input.password) dialog.setTextEchoMode(QLineEdit::Password);
|
|
||||||
else dialog.setTextEchoMode(QLineEdit::Normal);
|
|
||||||
dialog.setWindowTitle(p_task->getLabel());
|
|
||||||
dialog.setLabelText(desc);
|
|
||||||
dialog.setTextValue(defaultText);
|
|
||||||
if (dialog.exec() == QDialog::Accepted) {
|
|
||||||
text = dialog.textValue();
|
|
||||||
} else {
|
|
||||||
throw "TaskCancle";
|
|
||||||
}
|
|
||||||
} else if (input.type == "pickString") {
|
|
||||||
// TODO: select description
|
|
||||||
SelectDialog dialog(p_task->getLabel(), input.description, mainwin);
|
|
||||||
for (int i = 0; i < input.options.size(); i++) {
|
|
||||||
dialog.addSelection(input.options.at(i), i);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dialog.exec() == QDialog::Accepted) {
|
|
||||||
int selection = dialog.getSelection();
|
|
||||||
text = input.options.at(selection);
|
|
||||||
} else {
|
|
||||||
throw "TaskCancle";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
map.insert(input.id, text);
|
|
||||||
}
|
|
||||||
return TaskHelper::replaceAllSepcialVariables("input", p_text, map);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TaskVariableMgr::evaluateShellVariables(const QString &p_text,
|
|
||||||
const Task *p_task) const
|
|
||||||
{
|
|
||||||
QMap<QString, QString> map;
|
|
||||||
auto list = TaskHelper::getAllSpecialVariables("shell", p_text);
|
|
||||||
list.erase(std::unique(list.begin(), list.end()), list.end());
|
|
||||||
if (list.isEmpty()) return p_text;
|
|
||||||
for (const auto &cmd : list) {
|
|
||||||
QProcess process;
|
|
||||||
process.setWorkingDirectory(p_task->getOptionsCwd());
|
|
||||||
ShellExecution::setupProcess(&process, cmd);
|
|
||||||
process.start();
|
|
||||||
if (!process.waitForStarted(1000) || !process.waitForFinished(1000)) {
|
|
||||||
throw "Shell variable execution timeout";
|
|
||||||
}
|
|
||||||
auto res = process.readAllStandardOutput().trimmed();
|
|
||||||
map.insert(cmd, res);
|
|
||||||
}
|
|
||||||
return TaskHelper::replaceAllSepcialVariables("shell", p_text, map);
|
|
||||||
}
|
|
||||||
|
|
@ -1,73 +0,0 @@
|
|||||||
#ifndef TASKVARIABLEMGR_H
|
|
||||||
#define TASKVARIABLEMGR_H
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
#include <QHash>
|
|
||||||
#include <QString>
|
|
||||||
#include <QSharedPointer>
|
|
||||||
|
|
||||||
namespace vnotex {
|
|
||||||
|
|
||||||
class Task;
|
|
||||||
class TaskVariable;
|
|
||||||
class TaskVariableMgr;
|
|
||||||
class Notebook;
|
|
||||||
|
|
||||||
class TaskVariable {
|
|
||||||
public:
|
|
||||||
enum Type
|
|
||||||
{
|
|
||||||
// Definition is plain text.
|
|
||||||
Simple = 0,
|
|
||||||
|
|
||||||
// Definition is a function call to get the value.
|
|
||||||
FunctionBased,
|
|
||||||
|
|
||||||
// Like FunctionBased, but should re-evaluate the value for each occurence.
|
|
||||||
Dynamic,
|
|
||||||
|
|
||||||
Invalid
|
|
||||||
};
|
|
||||||
typedef std::function<QString(const TaskVariable *,
|
|
||||||
const Task*)> Func;
|
|
||||||
TaskVariable(Type p_type,
|
|
||||||
const QString &p_name,
|
|
||||||
Func p_func = nullptr);
|
|
||||||
private:
|
|
||||||
Type m_type;
|
|
||||||
QString m_name;
|
|
||||||
Func m_func;
|
|
||||||
};
|
|
||||||
|
|
||||||
class TaskVariableMgr
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
TaskVariableMgr();
|
|
||||||
void refresh();
|
|
||||||
QString evaluate(const QString &p_text,
|
|
||||||
const Task *p_task) const;
|
|
||||||
|
|
||||||
QStringList evaluate(const QStringList &p_list,
|
|
||||||
const Task *p_task) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void init();
|
|
||||||
|
|
||||||
void add(TaskVariable::Type p_type,
|
|
||||||
const QString &p_name,
|
|
||||||
TaskVariable::Func p_func = nullptr);
|
|
||||||
|
|
||||||
QString evaluateInputVariables(const QString &p_text,
|
|
||||||
const Task *p_task) const;
|
|
||||||
|
|
||||||
QString evaluateShellVariables(const QString &p_text,
|
|
||||||
const Task *p_task) const;
|
|
||||||
|
|
||||||
QHash<QString, TaskVariable> m_predefs;
|
|
||||||
bool m_initialized;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // ns vnotex
|
|
||||||
|
|
||||||
#endif // TASKVARIABLEMGR_H
|
|
@ -14,6 +14,7 @@
|
|||||||
#include "quickaccesshelper.h"
|
#include "quickaccesshelper.h"
|
||||||
|
|
||||||
#include <utils/docsutils.h>
|
#include <utils/docsutils.h>
|
||||||
|
#include <task/taskmgr.h>
|
||||||
|
|
||||||
|
|
||||||
using namespace vnotex;
|
using namespace vnotex;
|
||||||
@ -63,6 +64,8 @@ void VNoteX::initTaskMgr()
|
|||||||
{
|
{
|
||||||
Q_ASSERT(!m_taskMgr);
|
Q_ASSERT(!m_taskMgr);
|
||||||
m_taskMgr = new TaskMgr(this);
|
m_taskMgr = new TaskMgr(this);
|
||||||
|
connect(m_taskMgr, &TaskMgr::taskOutputRequested,
|
||||||
|
this, &VNoteX::showOutputRequested);
|
||||||
}
|
}
|
||||||
|
|
||||||
ThemeMgr &VNoteX::getThemeMgr() const
|
ThemeMgr &VNoteX::getThemeMgr() const
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
#include "noncopyable.h"
|
#include "noncopyable.h"
|
||||||
#include "thememgr.h"
|
#include "thememgr.h"
|
||||||
#include "taskmgr.h"
|
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
|
|
||||||
namespace vnotex
|
namespace vnotex
|
||||||
@ -19,6 +18,7 @@ namespace vnotex
|
|||||||
class Event;
|
class Event;
|
||||||
class Notebook;
|
class Notebook;
|
||||||
struct ComplexLocation;
|
struct ComplexLocation;
|
||||||
|
class TaskMgr;
|
||||||
|
|
||||||
class VNoteX : public QObject, private Noncopyable
|
class VNoteX : public QObject, private Noncopyable
|
||||||
{
|
{
|
||||||
|
@ -1 +1 @@
|
|||||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1611462360764" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2562" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M585.142857 804.571429h365.714286v-73.142858H585.142857v73.142858z m-219.428571-292.571429h585.142857v-73.142857H365.714286v73.142857z m365.714285-292.571429h219.428572V146.285714h-219.428572v73.142857z m292.571429 475.428572v146.285714c0 20.004571-16.566857 36.571429-36.571429 36.571429H36.571429c-20.004571 0-36.571429-16.566857-36.571429-36.571429v-146.285714c0-20.004571 16.566857-36.571429 36.571429-36.571429h950.857142c20.004571 0 36.571429 16.566857 36.571429 36.571429z m0-292.571429v146.285715c0 20.004571-16.566857 36.571429-36.571429 36.571428H36.571429c-20.004571 0-36.571429-16.566857-36.571429-36.571428v-146.285715c0-20.004571 16.566857-36.571429 36.571429-36.571428h950.857142c20.004571 0 36.571429 16.566857 36.571429 36.571428z m0-292.571428v146.285714c0 20.004571-16.566857 36.571429-36.571429 36.571429H36.571429c-20.004571 0-36.571429-16.566857-36.571429-36.571429V109.714286c0-20.004571 16.566857-36.571429 36.571429-36.571429h950.857142c20.004571 0 36.571429 16.566857 36.571429 36.571429z" fill="" p-id="2563"></path></svg>
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1639102667660" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="17408" width="512" height="512" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"></style></defs><path d="M967.857329 886.684588 67.056232 886.684588c-27.567869 0-49.995671-22.427802-49.995671-49.995671L17.060561 698.103582c0-27.567869 22.427802-49.995671 49.995671-49.995671L967.857329 648.107911c27.567869 0 49.995671 22.427802 49.995671 49.995671L1017.853 836.688917C1017.854023 864.256786 995.426221 886.684588 967.857329 886.684588zM67.056232 678.807097c-10.460236 0-19.296485 8.837272-19.296485 19.296485L47.759747 836.688917c0 10.460236 8.836249 19.296485 19.296485 19.296485L967.857329 855.985402c10.460236 0 19.296485-8.837272 19.296485-19.296485L987.153814 698.103582c0-10.460236-8.837272-19.296485-19.296485-19.296485L67.056232 678.807097zM948.561867 817.392432 571.399855 817.392432l0-99.991342 377.162012 0L948.561867 817.392432zM602.099041 786.693246l315.76364 0 0-38.59297L602.099041 748.100276 602.099041 786.693246zM967.857329 609.514941 67.056232 609.514941c-27.567869 0-49.995671-22.427802-49.995671-49.995671L17.060561 420.933935c0-27.567869 22.427802-49.995671 49.995671-49.995671L967.857329 370.938264c27.567869 0 49.995671 22.427802 49.995671 49.995671l0 138.584312C1017.854023 587.087139 995.426221 609.514941 967.857329 609.514941zM67.056232 401.63745c-10.460236 0-19.296485 8.837272-19.296485 19.296485l0 138.584312c0 10.460236 8.836249 19.296485 19.296485 19.296485L967.857329 578.814732c10.460236 0 19.296485-8.837272 19.296485-19.296485L987.153814 420.933935c0-10.460236-8.837272-19.296485-19.296485-19.296485L67.056232 401.63745zM948.561867 540.221762 363.522364 540.221762l0-99.991342 585.039503 0L948.561867 540.221762zM394.22155 509.523599l523.641131 0 0-38.59297L394.22155 470.930629 394.22155 509.523599zM967.857329 332.345294 67.056232 332.345294c-27.567869 0-49.995671-22.427802-49.995671-49.995671L17.060561 143.764288c0-27.567869 22.427802-49.995671 49.995671-49.995671L967.857329 93.768617c27.567869 0 49.995671 22.427802 49.995671 49.995671l0 138.585335C1017.854023 309.917492 995.426221 332.345294 967.857329 332.345294zM67.056232 124.467803c-10.460236 0-19.296485 8.836249-19.296485 19.296485l0 138.585335c0 10.460236 8.836249 19.296485 19.296485 19.296485L967.857329 301.646108c10.460236 0 19.296485-8.837272 19.296485-19.296485L987.153814 143.764288c0-10.460236-8.837272-19.296485-19.296485-19.296485L67.056232 124.467803zM948.561867 263.053138 709.984167 263.053138l0-99.991342 238.5777 0L948.561867 263.053138zM740.683353 232.353952l177.179328 0 0-38.59297L740.683353 193.760982 740.683353 232.353952z" p-id="17409" fill="#000000"></path></svg>
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 2.8 KiB |
@ -37,12 +37,12 @@
|
|||||||
"id": "msg",
|
"id": "msg",
|
||||||
"type": "promptString",
|
"type": "promptString",
|
||||||
"description": {
|
"description": {
|
||||||
"en_US": "Please provide a commit message",
|
"en_US": "Please input the commit message",
|
||||||
"zh_CN": "请输入提交信息",
|
"zh_CN": "请输入提交信息",
|
||||||
"ja_JP": "コミットメッセージを提供してください"
|
"ja_JP": "コミットメッセージを提供してください"
|
||||||
},
|
},
|
||||||
"default": {
|
"default": {
|
||||||
"en_US": "Update note on ${magic:datetime}",
|
"en_US": "Update note at ${magic:datetime}",
|
||||||
"zh_CN": "更新笔记于 ${magic:datetime}",
|
"zh_CN": "更新笔记于 ${magic:datetime}",
|
||||||
"ja_JP": "アップデート ${magic:datetime}"
|
"ja_JP": "アップデート ${magic:datetime}"
|
||||||
}
|
}
|
||||||
|
@ -192,13 +192,15 @@ bool WebViewExporter::writeHtmlFile(const QString &p_file,
|
|||||||
bool p_embedImages)
|
bool p_embedImages)
|
||||||
{
|
{
|
||||||
const auto baseName = QFileInfo(p_file).completeBaseName();
|
const auto baseName = QFileInfo(p_file).completeBaseName();
|
||||||
auto title = QString("%1 - %2").arg(baseName, ConfigMgr::c_appName);
|
|
||||||
const QString resourceFolderName = baseName + "_files";
|
const QString resourceFolderName = baseName + "_files";
|
||||||
auto resourceFolder = PathUtils::concatenateFilePath(PathUtils::parentDirPath(p_file), resourceFolderName);
|
auto resourceFolder = PathUtils::concatenateFilePath(PathUtils::parentDirPath(p_file), resourceFolderName);
|
||||||
|
|
||||||
qDebug() << "HTML files folder" << resourceFolder;
|
qDebug() << "HTML files folder" << resourceFolder;
|
||||||
|
|
||||||
auto htmlContent = m_exportHtmlTemplate;
|
auto htmlContent = m_exportHtmlTemplate;
|
||||||
|
|
||||||
|
const auto title = QString("%1").arg(baseName);
|
||||||
HtmlTemplateHelper::fillTitle(htmlContent, title);
|
HtmlTemplateHelper::fillTitle(htmlContent, title);
|
||||||
|
|
||||||
if (!p_styleContent.isEmpty() && p_embedStyles) {
|
if (!p_styleContent.isEmpty() && p_embedStyles) {
|
||||||
|
@ -20,7 +20,9 @@
|
|||||||
|
|
||||||
using namespace vnotex;
|
using namespace vnotex;
|
||||||
|
|
||||||
const QString SnippetMgr::c_snippetSymbolRegExp = QString("%([^%]+)%");
|
const QChar SnippetMgr::c_snippetSymbolGuard = QLatin1Char('%');
|
||||||
|
|
||||||
|
const QString SnippetMgr::c_snippetSymbolRegExp = QString("%1([^%]+)%1").arg(c_snippetSymbolGuard);
|
||||||
|
|
||||||
SnippetMgr::SnippetMgr()
|
SnippetMgr::SnippetMgr()
|
||||||
{
|
{
|
||||||
@ -231,10 +233,11 @@ void SnippetMgr::applySnippet(const QString &p_name,
|
|||||||
p_textEdit->setTextCursor(cursor);
|
p_textEdit->setTextCursor(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString SnippetMgr::applySnippetBySymbol(const QString &p_content) const
|
QString SnippetMgr::applySnippetBySymbol(const QString &p_content,
|
||||||
|
const OverrideMap &p_overrides) const
|
||||||
{
|
{
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
return applySnippetBySymbol(p_content, QString(), offset);
|
return applySnippetBySymbol(p_content, QString(), offset, p_overrides);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString SnippetMgr::applySnippetBySymbol(const QString &p_content,
|
QString SnippetMgr::applySnippetBySymbol(const QString &p_content,
|
||||||
@ -244,11 +247,11 @@ QString SnippetMgr::applySnippetBySymbol(const QString &p_content,
|
|||||||
{
|
{
|
||||||
QString content(p_content);
|
QString content(p_content);
|
||||||
|
|
||||||
int maxTimes = 100;
|
int maxTimesAtSamePos = 100;
|
||||||
|
|
||||||
QRegularExpression regExp(c_snippetSymbolRegExp);
|
QRegularExpression regExp(c_snippetSymbolRegExp);
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
while (pos < content.size() && maxTimes-- > 0) {
|
while (pos < content.size()) {
|
||||||
QRegularExpressionMatch match;
|
QRegularExpressionMatch match;
|
||||||
int idx = content.indexOf(regExp, pos, &match);
|
int idx = content.indexOf(regExp, pos, &match);
|
||||||
if (idx == -1) {
|
if (idx == -1) {
|
||||||
@ -288,6 +291,13 @@ QString SnippetMgr::applySnippetBySymbol(const QString &p_content,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// @afterText may still contains snippet symbol.
|
// @afterText may still contains snippet symbol.
|
||||||
|
if (pos == idx) {
|
||||||
|
if (--maxTimesAtSamePos == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
maxTimesAtSamePos = 100;
|
||||||
|
}
|
||||||
pos = idx;
|
pos = idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -426,8 +436,10 @@ void SnippetMgr::addDynamicSnippet(QVector<QSharedPointer<Snippet>> &p_snippets,
|
|||||||
SnippetMgr::OverrideMap SnippetMgr::generateOverrides(const Buffer *p_buffer)
|
SnippetMgr::OverrideMap SnippetMgr::generateOverrides(const Buffer *p_buffer)
|
||||||
{
|
{
|
||||||
OverrideMap overrides;
|
OverrideMap overrides;
|
||||||
overrides.insert(QStringLiteral("note"), p_buffer->getName());
|
if (p_buffer) {
|
||||||
overrides.insert(QStringLiteral("no"), QFileInfo(p_buffer->getName()).completeBaseName());
|
overrides.insert(QStringLiteral("note"), p_buffer->getName());
|
||||||
|
overrides.insert(QStringLiteral("no"), QFileInfo(p_buffer->getName()).completeBaseName());
|
||||||
|
}
|
||||||
return overrides;
|
return overrides;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -438,3 +450,8 @@ SnippetMgr::OverrideMap SnippetMgr::generateOverrides(const QString &p_fileName)
|
|||||||
overrides.insert(QStringLiteral("no"), QFileInfo(p_fileName).completeBaseName());
|
overrides.insert(QStringLiteral("no"), QFileInfo(p_fileName).completeBaseName());
|
||||||
return overrides;
|
return overrides;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString SnippetMgr::generateSnippetSymbol(const QString &p_snippetName)
|
||||||
|
{
|
||||||
|
return c_snippetSymbolGuard + p_snippetName + c_snippetSymbolGuard;
|
||||||
|
}
|
||||||
|
@ -60,14 +60,19 @@ namespace vnotex
|
|||||||
int &p_cursorOffset,
|
int &p_cursorOffset,
|
||||||
const OverrideMap &p_overrides = OverrideMap()) const;
|
const OverrideMap &p_overrides = OverrideMap()) const;
|
||||||
|
|
||||||
QString applySnippetBySymbol(const QString &p_content) const;
|
QString applySnippetBySymbol(const QString &p_content,
|
||||||
|
const OverrideMap &p_overrides = OverrideMap()) const;
|
||||||
|
|
||||||
// Generate standard overrides for given buffer.
|
// Generate standard overrides for given buffer.
|
||||||
static OverrideMap generateOverrides(const Buffer *p_buffer);
|
static OverrideMap generateOverrides(const Buffer *p_buffer);
|
||||||
|
|
||||||
// Generate standard overrides.
|
// Generate standard overrides for given file name.
|
||||||
static OverrideMap generateOverrides(const QString &p_fileName);
|
static OverrideMap generateOverrides(const QString &p_fileName);
|
||||||
|
|
||||||
|
static QString generateSnippetSymbol(const QString &p_snippetName);
|
||||||
|
|
||||||
|
static const QChar c_snippetSymbolGuard;
|
||||||
|
|
||||||
// %name%.
|
// %name%.
|
||||||
// Captured texts:
|
// Captured texts:
|
||||||
// 1 - The name of the snippet.
|
// 1 - The name of the snippet.
|
||||||
|
@ -56,6 +56,8 @@ include($$PWD/snippet/snippet.pri)
|
|||||||
|
|
||||||
include($$PWD/imagehost/imagehost.pri)
|
include($$PWD/imagehost/imagehost.pri)
|
||||||
|
|
||||||
|
include($$PWD/task/task.pri)
|
||||||
|
|
||||||
include($$PWD/core/core.pri)
|
include($$PWD/core/core.pri)
|
||||||
|
|
||||||
include($$PWD/widgets/widgets.pri)
|
include($$PWD/widgets/widgets.pri)
|
||||||
|
72
src/task/shellexecution.cpp
Normal file
72
src/task/shellexecution.cpp
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
#include "shellexecution.h"
|
||||||
|
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QProcess>
|
||||||
|
|
||||||
|
using namespace vnotex;
|
||||||
|
|
||||||
|
void ShellExecution::setupProcess(QProcess *p_process,
|
||||||
|
const QString &p_program,
|
||||||
|
const QStringList &p_args,
|
||||||
|
const QString &p_shellExec,
|
||||||
|
const QStringList &p_shellArgs)
|
||||||
|
{
|
||||||
|
auto shellExec = p_shellExec.isNull() ? defaultShell() : p_shellExec;
|
||||||
|
auto shellArgs = p_shellArgs.isEmpty() ? defaultShellArguments(shellExec) : p_shellArgs;
|
||||||
|
|
||||||
|
p_process->setProgram(shellExec);
|
||||||
|
|
||||||
|
const auto shell = shellBasename(p_shellExec);
|
||||||
|
QStringList allArgs(shellArgs);
|
||||||
|
if (shell == "bash") {
|
||||||
|
allArgs << (QStringList() << p_program << quoteSpaces(p_args)).join(' ');
|
||||||
|
} else {
|
||||||
|
allArgs << p_program << p_args;
|
||||||
|
}
|
||||||
|
p_process->setArguments(allArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ShellExecution::shellBasename(const QString &p_shell)
|
||||||
|
{
|
||||||
|
return QFileInfo(p_shell).baseName().toLower();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ShellExecution::defaultShell()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
return QStringLiteral("PowerShell.exe");
|
||||||
|
#else
|
||||||
|
return QStringLiteral("/bin/bash");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList ShellExecution::defaultShellArguments(const QString &p_shell)
|
||||||
|
{
|
||||||
|
auto shell = shellBasename(p_shell);
|
||||||
|
if (shell == "cmd") {
|
||||||
|
return {"/C"};
|
||||||
|
} else if (shell == "powershell" || p_shell == "pwsh") {
|
||||||
|
return {"-Command"};
|
||||||
|
} else if (shell == "bash") {
|
||||||
|
return {"-c"};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ShellExecution::quoteSpace(const QString &p_arg)
|
||||||
|
{
|
||||||
|
if (p_arg.contains(QLatin1Char(' '))) {
|
||||||
|
return QLatin1Char('"') + p_arg + QLatin1Char('"');
|
||||||
|
} else {
|
||||||
|
return p_arg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList ShellExecution::quoteSpaces(const QStringList &p_args)
|
||||||
|
{
|
||||||
|
QStringList args;
|
||||||
|
for (const auto &arg : p_args) {
|
||||||
|
args << quoteSpace(arg);
|
||||||
|
}
|
||||||
|
return args;
|
||||||
|
}
|
34
src/task/shellexecution.h
Normal file
34
src/task/shellexecution.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#ifndef SHELLEXECUTION_H
|
||||||
|
#define SHELLEXECUTION_H
|
||||||
|
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
class QProcess;
|
||||||
|
|
||||||
|
namespace vnotex
|
||||||
|
{
|
||||||
|
class ShellExecution
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ShellExecution() = delete;
|
||||||
|
|
||||||
|
static void setupProcess(QProcess *p_process,
|
||||||
|
const QString &p_program,
|
||||||
|
const QStringList &p_args = QStringList(),
|
||||||
|
const QString &p_shellExec = QString(),
|
||||||
|
const QStringList &p_shellArgs = QStringList());
|
||||||
|
|
||||||
|
static QString defaultShell();
|
||||||
|
|
||||||
|
static QStringList defaultShellArguments(const QString &p_shell);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static QString shellBasename(const QString &p_shell);
|
||||||
|
|
||||||
|
static QString quoteSpace(const QString &p_arg);
|
||||||
|
|
||||||
|
static QStringList quoteSpaces(const QStringList &p_args);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SHELLEXECUTION_H
|
588
src/task/task.cpp
Normal file
588
src/task/task.cpp
Normal file
@ -0,0 +1,588 @@
|
|||||||
|
#include "task.h"
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QVersionNumber>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QJsonValue>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QAction>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QInputDialog>
|
||||||
|
#include <QTextCodec>
|
||||||
|
#include <QRandomGenerator>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QScopedPointer>
|
||||||
|
|
||||||
|
#include <utils/fileutils.h>
|
||||||
|
#include <utils/pathutils.h>
|
||||||
|
#include <core/vnotex.h>
|
||||||
|
#include <core/exception.h>
|
||||||
|
#include <notebook/notebook.h>
|
||||||
|
#include <buffer/buffer.h>
|
||||||
|
|
||||||
|
#include "shellexecution.h"
|
||||||
|
#include "taskmgr.h"
|
||||||
|
|
||||||
|
using namespace vnotex;
|
||||||
|
|
||||||
|
QString Task::s_latestVersion = "0.1.3";
|
||||||
|
|
||||||
|
QSharedPointer<Task> Task::fromFile(const QString &p_file, const QString &p_locale, TaskMgr *p_taskMgr)
|
||||||
|
{
|
||||||
|
QSharedPointer<Task> task(new Task(p_locale, p_file, p_taskMgr, nullptr));
|
||||||
|
const auto obj = FileUtils::readJsonFile(p_file);
|
||||||
|
if (fromJson(task.data(), obj)) {
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Task::fromJson(Task *p_task, const QJsonObject &p_obj)
|
||||||
|
{
|
||||||
|
// For child task, it will inherit the version from parent.
|
||||||
|
if (p_obj.contains("version")) {
|
||||||
|
p_task->m_dto.version = p_obj["version"].toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto version = QVersionNumber::fromString(p_task->getVersion());
|
||||||
|
if (version.isNull()) {
|
||||||
|
qWarning() << "invalid task" << p_task->m_dto._source;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version < QVersionNumber(1, 0, 0)) {
|
||||||
|
return fromJsonV0(p_task, p_obj);
|
||||||
|
} else {
|
||||||
|
qWarning() << "unknown task version" << version << p_task->m_dto._source;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Task::fromJsonV0(Task *p_task, const QJsonObject &p_obj, bool p_mergeTasks)
|
||||||
|
{
|
||||||
|
if (p_obj.contains("type")) {
|
||||||
|
p_task->m_dto.type = p_obj["type"].toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_obj.contains("icon")) {
|
||||||
|
QString iconPath = p_obj["icon"].toString();
|
||||||
|
if (!iconPath.isEmpty()) {
|
||||||
|
if (QDir::isRelativePath(iconPath)) {
|
||||||
|
iconPath = QFileInfo(p_task->m_dto._source).dir().absoluteFilePath(iconPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (QFileInfo::exists(iconPath)) {
|
||||||
|
p_task->m_dto.icon = iconPath;
|
||||||
|
} else {
|
||||||
|
qWarning() << "task icon does not exist" << p_task->getLabel() << iconPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_obj.contains("shortcut")) {
|
||||||
|
p_task->m_dto.shortcut = p_obj["shortcut"].toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_obj.contains("type")) {
|
||||||
|
p_task->m_dto.type = p_obj["type"].toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_obj.contains("command")) {
|
||||||
|
p_task->m_dto.command = getLocaleString(p_obj["command"], p_task->m_locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_obj.contains("args")) {
|
||||||
|
p_task->m_dto.args = getLocaleStringList(p_obj["args"], p_task->m_locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_obj.contains("label")) {
|
||||||
|
p_task->m_dto.label = getLocaleString(p_obj["label"], p_task->m_locale);
|
||||||
|
} else if (p_task->m_dto.label.isNull() && !p_task->m_dto.command.isNull()) {
|
||||||
|
p_task->m_dto.label = p_task->m_dto.command;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_obj.contains("options")) {
|
||||||
|
auto options = p_obj["options"].toObject();
|
||||||
|
|
||||||
|
if (options.contains("cwd")) {
|
||||||
|
p_task->m_dto.options.cwd = options["cwd"].toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.contains("env")) {
|
||||||
|
p_task->m_dto.options.env.clear();
|
||||||
|
auto env = options["env"].toObject();
|
||||||
|
for (auto it = env.begin(); it != env.end(); it++) {
|
||||||
|
auto value = getLocaleString(it.value(), p_task->m_locale);
|
||||||
|
p_task->m_dto.options.env.insert(it.key(), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.contains("shell") && p_task->getType() == "shell") {
|
||||||
|
auto shell = options["shell"].toObject();
|
||||||
|
|
||||||
|
if (shell.contains("executable")) {
|
||||||
|
p_task->m_dto.options.shell.executable = shell["executable"].toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shell.contains("args")) {
|
||||||
|
p_task->m_dto.options.shell.args.clear();
|
||||||
|
|
||||||
|
const auto arr = shell["args"].toArray();
|
||||||
|
for (int i = 0; i < arr.size(); ++i) {
|
||||||
|
p_task->m_dto.options.shell.args << arr[i].toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_obj.contains("tasks")) {
|
||||||
|
if (!p_mergeTasks) {
|
||||||
|
p_task->m_children.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto arr = p_obj["tasks"].toArray();
|
||||||
|
for (int i = 0; i < arr.size(); ++i) {
|
||||||
|
QScopedPointer<Task> childTask(new Task(p_task->m_locale, p_task->getFile(), p_task->m_taskMgr, p_task));
|
||||||
|
if (fromJson(childTask.data(), arr[i].toObject())) {
|
||||||
|
connect(childTask.data(), &Task::outputRequested,
|
||||||
|
p_task, &Task::outputRequested);
|
||||||
|
p_task->m_children.append(childTask.take());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_obj.contains("inputs")) {
|
||||||
|
p_task->m_dto.inputs.clear();
|
||||||
|
auto arr = p_obj["inputs"].toArray();
|
||||||
|
for (int i = 0; i < arr.size(); ++i) {
|
||||||
|
const auto inputObj = arr[i].toObject();
|
||||||
|
InputDTO input;
|
||||||
|
if (inputObj.contains("id")) {
|
||||||
|
input.id = inputObj["id"].toString();
|
||||||
|
} else {
|
||||||
|
qWarning() << "Input configuration not contains id";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputObj.contains("type")) {
|
||||||
|
input.type = inputObj["type"].toString();
|
||||||
|
} else {
|
||||||
|
input.type = "promptString";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputObj.contains("description")) {
|
||||||
|
input.description = getLocaleString(inputObj["description"], p_task->m_locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputObj.contains("default")) {
|
||||||
|
input.default_ = getLocaleString(inputObj["default"], p_task->m_locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.type == "promptString" && inputObj.contains("password")) {
|
||||||
|
input.password = inputObj["password"].toBool();
|
||||||
|
} else {
|
||||||
|
input.password = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.type == "pickString") {
|
||||||
|
if (inputObj.contains("options")) {
|
||||||
|
input.options = getLocaleStringList(inputObj["options"], p_task->m_locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!input.default_.isNull() && !input.options.contains(input.default_)) {
|
||||||
|
qWarning() << "default of input must be one of the option values";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p_task->m_dto.inputs << input;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_obj.contains("messages")) {
|
||||||
|
p_task->m_dto.messages.clear();
|
||||||
|
auto arr = p_obj["messages"].toArray();
|
||||||
|
for (int i = 0; i < arr.size(); ++i) {
|
||||||
|
const auto msgObj = arr[i].toObject();
|
||||||
|
MessageDTO msg;
|
||||||
|
if (msgObj.contains("id")) {
|
||||||
|
msg.id = msgObj["id"].toString();
|
||||||
|
} else {
|
||||||
|
qWarning() << "Message configuration not contain id";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msgObj.contains("type")) {
|
||||||
|
msg.type = msgObj["type"].toString();
|
||||||
|
} else {
|
||||||
|
msg.type = "information";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msgObj.contains("title")) {
|
||||||
|
msg.title = getLocaleString(msgObj["title"], p_task->m_locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msgObj.contains("text")) {
|
||||||
|
msg.text = getLocaleString(msgObj["text"], p_task->m_locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msgObj.contains("detailedText")) {
|
||||||
|
msg.detailedText = getLocaleString(msgObj["detailedText"], p_task->m_locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msgObj.contains("buttons")) {
|
||||||
|
auto buttonsArr = msgObj["buttons"].toArray();
|
||||||
|
for (int j = 0; j < buttonsArr.size(); ++j) {
|
||||||
|
const auto btnObj = buttonsArr[j].toObject();
|
||||||
|
ButtonDTO btn;
|
||||||
|
btn.text = getLocaleString(btnObj["text"], p_task->m_locale);
|
||||||
|
msg.buttons << btn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p_task->m_dto.messages << msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OS-specific task configuration
|
||||||
|
#if defined (Q_OS_WIN)
|
||||||
|
#define OS_SPEC "windows"
|
||||||
|
#elif defined (Q_OS_MACOS)
|
||||||
|
#define OS_SPEC "osx"
|
||||||
|
#else
|
||||||
|
#define OS_SPEC "linux"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (p_obj.contains(OS_SPEC)) {
|
||||||
|
const auto osObj = p_obj[OS_SPEC].toObject();
|
||||||
|
fromJsonV0(p_task, osObj, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef OS_SPEC
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &Task::getVersion() const
|
||||||
|
{
|
||||||
|
return m_dto.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &Task::getType() const
|
||||||
|
{
|
||||||
|
return m_dto.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Task::getCommand()
|
||||||
|
{
|
||||||
|
return variableMgr().evaluate(this, m_dto.command);
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList Task::getArgs()
|
||||||
|
{
|
||||||
|
return variableMgr().evaluate(this, m_dto.args);
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &Task::getLabel() const
|
||||||
|
{
|
||||||
|
return m_dto.label;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &Task::getIcon() const
|
||||||
|
{
|
||||||
|
return m_dto.icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &Task::getShortcut() const
|
||||||
|
{
|
||||||
|
return m_dto.shortcut;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Task::getOptionsCwd()
|
||||||
|
{
|
||||||
|
auto cwd = m_dto.options.cwd;
|
||||||
|
if (!cwd.isNull()) {
|
||||||
|
return variableMgr().evaluate(this, cwd);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto notebook = TaskVariableMgr::getCurrentNotebook();
|
||||||
|
if (notebook) {
|
||||||
|
cwd = notebook->getRootFolderAbsolutePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cwd.isNull()) {
|
||||||
|
return cwd;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto buffer = TaskVariableMgr::getCurrentBuffer();
|
||||||
|
if (buffer) {
|
||||||
|
return QFileInfo(buffer->getPath()).dir().absolutePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
return QFileInfo(m_dto._source).dir().absolutePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
const QMap<QString, QString> &Task::getOptionsEnv() const
|
||||||
|
{
|
||||||
|
return m_dto.options.env;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &Task::getOptionsShellExecutable() const
|
||||||
|
{
|
||||||
|
return m_dto.options.shell.executable;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList Task::getOptionsShellArgs()
|
||||||
|
{
|
||||||
|
if (m_dto.options.shell.args.isEmpty()) {
|
||||||
|
return ShellExecution::defaultShellArguments(m_dto.options.shell.executable);
|
||||||
|
} else {
|
||||||
|
return variableMgr().evaluate(this, m_dto.options.shell.args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const QVector<Task *> &Task::getChildren() const
|
||||||
|
{
|
||||||
|
return m_children;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QVector<InputDTO> &Task::getInputs() const
|
||||||
|
{
|
||||||
|
return m_dto.inputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
const InputDTO *Task::findInput(const QString &p_id) const
|
||||||
|
{
|
||||||
|
for (const auto &input : m_dto.inputs) {
|
||||||
|
if (input.id == p_id) {
|
||||||
|
return &input;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qWarning() << "input" << p_id << "not found for task" << getLabel();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MessageDTO *Task::findMessage(const QString &p_id) const
|
||||||
|
{
|
||||||
|
for (const auto &msg : m_dto.messages) {
|
||||||
|
if (msg.id == p_id) {
|
||||||
|
return &msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qWarning() << "message" << p_id << "not found for task" << getLabel();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &Task::getFile() const
|
||||||
|
{
|
||||||
|
return m_dto._source;
|
||||||
|
}
|
||||||
|
|
||||||
|
Task::Task(const QString &p_locale,
|
||||||
|
const QString &p_file,
|
||||||
|
TaskMgr *p_taskMgr,
|
||||||
|
QObject *p_parent)
|
||||||
|
: QObject(p_parent),
|
||||||
|
m_taskMgr(p_taskMgr),
|
||||||
|
m_locale(p_locale)
|
||||||
|
{
|
||||||
|
m_dto._source = p_file;
|
||||||
|
m_dto.version = s_latestVersion;
|
||||||
|
m_dto.type = "shell";
|
||||||
|
m_dto.options.shell.executable = ShellExecution::defaultShell();
|
||||||
|
|
||||||
|
// Inherit configuration.
|
||||||
|
m_parent = qobject_cast<Task *>(p_parent);
|
||||||
|
if (m_parent) {
|
||||||
|
m_dto.version = m_parent->m_dto.version;
|
||||||
|
m_dto.type = m_parent->m_dto.type;
|
||||||
|
m_dto.command = m_parent->m_dto.command;
|
||||||
|
m_dto.args = m_parent->m_dto.args;
|
||||||
|
m_dto.options.cwd = m_parent->m_dto.options.cwd;
|
||||||
|
m_dto.options.env = m_parent->m_dto.options.env;
|
||||||
|
m_dto.options.shell.executable = m_parent->m_dto.options.shell.executable;
|
||||||
|
m_dto.options.shell.args = m_parent->m_dto.options.shell.args;
|
||||||
|
// Do not inherit label/inputs/tasks.
|
||||||
|
} else {
|
||||||
|
m_dto.label = QFileInfo(p_file).baseName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QProcess *Task::setupProcess()
|
||||||
|
{
|
||||||
|
setCancelled(false);
|
||||||
|
|
||||||
|
auto command = getCommand();
|
||||||
|
if (command.isEmpty()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
QScopedPointer<QProcess> scopedProcess(new QProcess(this));
|
||||||
|
|
||||||
|
auto process = scopedProcess.data();
|
||||||
|
process->setWorkingDirectory(getOptionsCwd());
|
||||||
|
|
||||||
|
const auto &optionsEnv = getOptionsEnv();
|
||||||
|
if (!optionsEnv.isEmpty()) {
|
||||||
|
auto env = QProcessEnvironment::systemEnvironment();
|
||||||
|
for (auto it = optionsEnv.begin(); it != optionsEnv.end(); it++) {
|
||||||
|
env.insert(it.key(), it.value());
|
||||||
|
}
|
||||||
|
process->setProcessEnvironment(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto args = getArgs();
|
||||||
|
const auto &type = getType();
|
||||||
|
|
||||||
|
if (type == "shell") {
|
||||||
|
ShellExecution::setupProcess(process,
|
||||||
|
command,
|
||||||
|
args,
|
||||||
|
getOptionsShellExecutable(),
|
||||||
|
getOptionsShellArgs());
|
||||||
|
} else if (getType() == "process") {
|
||||||
|
process->setProgram(command);
|
||||||
|
process->setArguments(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCancelled()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
scopedProcess.take();
|
||||||
|
|
||||||
|
connect(process, &QProcess::started,
|
||||||
|
this, [this]() {
|
||||||
|
emit outputRequested(tr("[Task (%1) started]\n").arg(getLabel()));
|
||||||
|
});
|
||||||
|
connect(process, &QProcess::readyReadStandardOutput,
|
||||||
|
this, [this, process]() {
|
||||||
|
auto text = decodeText(process->readAllStandardOutput());
|
||||||
|
// TODO: interaction with process.
|
||||||
|
emit outputRequested(text);
|
||||||
|
});
|
||||||
|
connect(process, &QProcess::readyReadStandardError,
|
||||||
|
this, [this, process]() {
|
||||||
|
auto text = process->readAllStandardError();
|
||||||
|
emit outputRequested(decodeText(text));
|
||||||
|
});
|
||||||
|
connect(process, &QProcess::errorOccurred,
|
||||||
|
this, [this](QProcess::ProcessError error) {
|
||||||
|
emit outputRequested(tr("[Task (%1) error occurred (%2)]\n").arg(getLabel()).arg(error));
|
||||||
|
});
|
||||||
|
connect(process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
|
||||||
|
this, [this, process](int exitCode) {
|
||||||
|
emit outputRequested(tr("\n[Task (%1) finished (%2)]\n").arg(getLabel()).arg(exitCode));
|
||||||
|
process->deleteLater();
|
||||||
|
});
|
||||||
|
|
||||||
|
return process;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Task::run()
|
||||||
|
{
|
||||||
|
QProcess *process;
|
||||||
|
try {
|
||||||
|
process = setupProcess();
|
||||||
|
} catch (const char *msg) {
|
||||||
|
qWarning() << "exception while setup process" << msg;
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process) {
|
||||||
|
qDebug() << "run task" << process->program() << process->arguments();
|
||||||
|
process->start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const TaskDTO &Task::getDTO() const
|
||||||
|
{
|
||||||
|
return m_dto;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Task::decodeText(const QByteArray &p_text)
|
||||||
|
{
|
||||||
|
static QByteArrayList codecNames = {
|
||||||
|
"UTF-8",
|
||||||
|
"System",
|
||||||
|
"UTF-16",
|
||||||
|
"GB18030"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto &name : codecNames) {
|
||||||
|
auto text = decodeText(p_text, name);
|
||||||
|
if (!text.isNull()) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QString::fromLocal8Bit(p_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Task::decodeText(const QByteArray &p_text, const QByteArray &p_name)
|
||||||
|
{
|
||||||
|
auto codec = QTextCodec::codecForName(p_name);
|
||||||
|
if (codec) {
|
||||||
|
QTextCodec::ConverterState state;
|
||||||
|
auto text = codec->toUnicode(p_text.data(), p_text.size(), &state);
|
||||||
|
if (state.invalidChars > 0) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Task::getLocaleString(const QJsonValue &p_value, const QString &p_locale)
|
||||||
|
{
|
||||||
|
if (p_value.isObject()) {
|
||||||
|
auto obj = p_value.toObject();
|
||||||
|
if (obj.contains(p_locale)) {
|
||||||
|
return obj.value(p_locale).toString();
|
||||||
|
} else {
|
||||||
|
qWarning() << "value of locale not found" << p_locale;
|
||||||
|
if (!obj.isEmpty()){
|
||||||
|
return obj.begin().value().toString();
|
||||||
|
} else {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return p_value.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList Task::getLocaleStringList(const QJsonValue &p_value, const QString &p_locale)
|
||||||
|
{
|
||||||
|
QStringList strs;
|
||||||
|
const auto arr = p_value.toArray();
|
||||||
|
for (int i = 0; i < arr.size(); ++i) {
|
||||||
|
strs << getLocaleString(arr[i], p_locale);
|
||||||
|
}
|
||||||
|
return strs;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList Task::getStringList(const QJsonValue &p_value)
|
||||||
|
{
|
||||||
|
QStringList strs;
|
||||||
|
const auto arr = p_value.toArray();
|
||||||
|
for (int i = 0; i < arr.size(); ++i) {
|
||||||
|
strs << arr[i].toString();
|
||||||
|
}
|
||||||
|
return strs;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TaskVariableMgr &Task::variableMgr() const
|
||||||
|
{
|
||||||
|
return m_taskMgr->getVariableMgr();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Task::isCancelled() const
|
||||||
|
{
|
||||||
|
return m_cancelled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Task::setCancelled(bool p_cancelled)
|
||||||
|
{
|
||||||
|
m_cancelled = p_cancelled;
|
||||||
|
}
|
197
src/task/task.h
Normal file
197
src/task/task.h
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
#ifndef TASK_H
|
||||||
|
#define TASK_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include <QVector>
|
||||||
|
#include <QMap>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
|
class QAction;
|
||||||
|
class QProcess;
|
||||||
|
class QJsonObject;
|
||||||
|
|
||||||
|
namespace tests
|
||||||
|
{
|
||||||
|
class TestTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace vnotex
|
||||||
|
{
|
||||||
|
struct ButtonDTO
|
||||||
|
{
|
||||||
|
QString text;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct InputDTO
|
||||||
|
{
|
||||||
|
QString id;
|
||||||
|
|
||||||
|
QString type;
|
||||||
|
|
||||||
|
QString description;
|
||||||
|
|
||||||
|
QString default_;
|
||||||
|
|
||||||
|
bool password;
|
||||||
|
|
||||||
|
QStringList options;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MessageDTO
|
||||||
|
{
|
||||||
|
QString id;
|
||||||
|
|
||||||
|
QString type;
|
||||||
|
|
||||||
|
QString title;
|
||||||
|
|
||||||
|
QString text;
|
||||||
|
|
||||||
|
QString detailedText;
|
||||||
|
|
||||||
|
QVector<ButtonDTO> buttons;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ShellOptionsDTO
|
||||||
|
{
|
||||||
|
QString executable;
|
||||||
|
|
||||||
|
QStringList args;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TaskOptionsDTO
|
||||||
|
{
|
||||||
|
QString cwd;
|
||||||
|
|
||||||
|
QMap<QString, QString> env;
|
||||||
|
|
||||||
|
ShellOptionsDTO shell;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TaskDTO
|
||||||
|
{
|
||||||
|
QString version;
|
||||||
|
|
||||||
|
QString type;
|
||||||
|
|
||||||
|
QString command;
|
||||||
|
|
||||||
|
QStringList args;
|
||||||
|
|
||||||
|
QString label;
|
||||||
|
|
||||||
|
QString icon;
|
||||||
|
|
||||||
|
QString shortcut;
|
||||||
|
|
||||||
|
QVector<InputDTO> inputs;
|
||||||
|
|
||||||
|
QVector<MessageDTO> messages;
|
||||||
|
|
||||||
|
TaskOptionsDTO options;
|
||||||
|
|
||||||
|
QString _scope;
|
||||||
|
|
||||||
|
QString _source;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TaskMgr;
|
||||||
|
class TaskVariableMgr;
|
||||||
|
|
||||||
|
class Task : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
friend class tests::TestTask;
|
||||||
|
|
||||||
|
// For top level Task, use QSharedPointer instead of QObject to manage ownership.
|
||||||
|
static QSharedPointer<Task> fromFile(const QString &p_file, const QString &p_locale, TaskMgr *p_taskMgr);
|
||||||
|
|
||||||
|
void run();
|
||||||
|
|
||||||
|
const TaskDTO &getDTO() const;
|
||||||
|
|
||||||
|
const QString &getVersion() const;
|
||||||
|
|
||||||
|
const QString &getType() const;
|
||||||
|
|
||||||
|
QString getCommand();
|
||||||
|
|
||||||
|
QStringList getArgs();
|
||||||
|
|
||||||
|
const QString &getLabel() const;
|
||||||
|
|
||||||
|
const QString &getIcon() const;
|
||||||
|
|
||||||
|
const QString &getShortcut() const;
|
||||||
|
|
||||||
|
QString getOptionsCwd();
|
||||||
|
|
||||||
|
const QMap<QString, QString> &getOptionsEnv() const;
|
||||||
|
|
||||||
|
const QString &getOptionsShellExecutable() const;
|
||||||
|
|
||||||
|
QStringList getOptionsShellArgs();
|
||||||
|
|
||||||
|
const QVector<Task *> &getChildren() const;
|
||||||
|
|
||||||
|
const QVector<InputDTO> &getInputs() const;
|
||||||
|
|
||||||
|
const InputDTO *findInput(const QString &p_id) const;
|
||||||
|
|
||||||
|
const MessageDTO *findMessage(const QString &p_id) const;
|
||||||
|
|
||||||
|
const QString &getFile() const;
|
||||||
|
|
||||||
|
bool isCancelled() const;
|
||||||
|
|
||||||
|
void setCancelled(bool p_cancelled);
|
||||||
|
|
||||||
|
static QString s_latestVersion;
|
||||||
|
|
||||||
|
static QString getLocaleString(const QJsonValue &p_value,
|
||||||
|
const QString &p_locale);
|
||||||
|
|
||||||
|
static QStringList getLocaleStringList(const QJsonValue &p_value,
|
||||||
|
const QString &p_locale);
|
||||||
|
|
||||||
|
static QStringList getStringList(const QJsonValue &p_value);
|
||||||
|
|
||||||
|
static QString decodeText(const QByteArray &p_text);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void outputRequested(const QString &p_text) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Task(const QString &p_locale,
|
||||||
|
const QString &p_file,
|
||||||
|
TaskMgr *p_taskMgr,
|
||||||
|
QObject *p_parent = nullptr);
|
||||||
|
|
||||||
|
// Must call start() or delete the returned QProcess.
|
||||||
|
QProcess *setupProcess();
|
||||||
|
|
||||||
|
const TaskVariableMgr &variableMgr() const;
|
||||||
|
|
||||||
|
static bool fromJson(Task *p_task, const QJsonObject &p_obj);
|
||||||
|
|
||||||
|
static bool fromJsonV0(Task *p_task, const QJsonObject &p_obj, bool p_mergeTasks = false);
|
||||||
|
|
||||||
|
static QString decodeText(const QByteArray &p_text, const QByteArray &p_name);
|
||||||
|
|
||||||
|
Task *m_parent = nullptr;
|
||||||
|
|
||||||
|
QVector<Task *> m_children;
|
||||||
|
|
||||||
|
TaskMgr *m_taskMgr = nullptr;
|
||||||
|
|
||||||
|
TaskDTO m_dto;
|
||||||
|
|
||||||
|
QString m_locale;
|
||||||
|
|
||||||
|
bool m_cancelled = false;
|
||||||
|
};
|
||||||
|
} // ns vnotex
|
||||||
|
|
||||||
|
#endif // TASK_H
|
13
src/task/task.pri
Normal file
13
src/task/task.pri
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
QT += widgets
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
$$PWD/task.cpp \
|
||||||
|
$$PWD/taskmgr.cpp \
|
||||||
|
$$PWD/taskvariablemgr.cpp \
|
||||||
|
$$PWD/shellexecution.cpp
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
$$PWD/task.h \
|
||||||
|
$$PWD/taskmgr.h \
|
||||||
|
$$PWD/taskvariablemgr.h \
|
||||||
|
$$PWD/shellexecution.h
|
111
src/task/taskmgr.cpp
Normal file
111
src/task/taskmgr.cpp
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
#include "taskmgr.h"
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
|
||||||
|
#include <core/configmgr.h>
|
||||||
|
#include <core/coreconfig.h>
|
||||||
|
#include <core/vnotex.h>
|
||||||
|
#include <core/notebookmgr.h>
|
||||||
|
#include <utils/pathutils.h>
|
||||||
|
#include <utils/fileutils.h>
|
||||||
|
|
||||||
|
using namespace vnotex;
|
||||||
|
|
||||||
|
TaskMgr::TaskMgr(QObject *p_parent)
|
||||||
|
: QObject(p_parent),
|
||||||
|
m_variableMgr(this)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskMgr::init()
|
||||||
|
{
|
||||||
|
m_variableMgr.init();
|
||||||
|
|
||||||
|
// Load all tasks and watch the location.
|
||||||
|
loadAllTasks();
|
||||||
|
|
||||||
|
connect(&VNoteX::getInst().getNotebookMgr(), &NotebookMgr::currentNotebookChanged,
|
||||||
|
this, [this]() {
|
||||||
|
loadNotebookTasks();
|
||||||
|
emit tasksUpdated();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskMgr::reload()
|
||||||
|
{
|
||||||
|
loadAllTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
const QVector<QSharedPointer<Task>> &TaskMgr::getAppTasks() const
|
||||||
|
{
|
||||||
|
return m_appTasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QVector<QSharedPointer<Task>> &TaskMgr::getUserTasks() const
|
||||||
|
{
|
||||||
|
return m_userTasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QVector<QSharedPointer<Task>> &TaskMgr::getNotebookTasks() const
|
||||||
|
{
|
||||||
|
return m_notebookTasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TaskMgr::getNotebookTaskFolder()
|
||||||
|
{
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskMgr::loadAllTasks()
|
||||||
|
{
|
||||||
|
loadGlobalTasks();
|
||||||
|
|
||||||
|
loadNotebookTasks();
|
||||||
|
|
||||||
|
emit tasksUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskMgr::loadNotebookTasks()
|
||||||
|
{
|
||||||
|
loadTasksFromFolder(m_notebookTasks, getNotebookTaskFolder());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskMgr::loadGlobalTasks()
|
||||||
|
{
|
||||||
|
loadTasksFromFolder(m_appTasks, ConfigMgr::getInst().getAppTaskFolder());
|
||||||
|
loadTasksFromFolder(m_userTasks, ConfigMgr::getInst().getUserTaskFolder());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskMgr::loadTasksFromFolder(QVector<QSharedPointer<Task>> &p_tasks, const QString &p_folder)
|
||||||
|
{
|
||||||
|
p_tasks.clear();
|
||||||
|
|
||||||
|
if (p_folder.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto taskFiles = FileUtils::entryListRecursively(p_folder, {"*.json"}, QDir::Files);
|
||||||
|
for (const auto &file : taskFiles) {
|
||||||
|
auto task = loadTask(file);
|
||||||
|
if (task) {
|
||||||
|
qDebug() << "loaded task" << task->getLabel();
|
||||||
|
connect(task.data(), &Task::outputRequested,
|
||||||
|
this, &TaskMgr::taskOutputRequested);
|
||||||
|
p_tasks.append(task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QSharedPointer<Task> TaskMgr::loadTask(const QString &p_taskFile)
|
||||||
|
{
|
||||||
|
const auto localeStr = ConfigMgr::getInst().getCoreConfig().getLocaleToUse();
|
||||||
|
auto task = Task::fromFile(p_taskFile, localeStr, this);
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TaskVariableMgr &TaskMgr::getVariableMgr() const
|
||||||
|
{
|
||||||
|
return m_variableMgr;
|
||||||
|
}
|
63
src/task/taskmgr.h
Normal file
63
src/task/taskmgr.h
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#ifndef TASKMGR_H
|
||||||
|
#define TASKMGR_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <core/noncopyable.h>
|
||||||
|
|
||||||
|
#include <QVector>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
|
#include "task.h"
|
||||||
|
#include "taskvariablemgr.h"
|
||||||
|
|
||||||
|
namespace vnotex
|
||||||
|
{
|
||||||
|
class TaskMgr : public QObject, private Noncopyable
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit TaskMgr(QObject *p_parent = nullptr);
|
||||||
|
|
||||||
|
// It will be invoked after MainWindow show.
|
||||||
|
void init();
|
||||||
|
|
||||||
|
void reload();
|
||||||
|
|
||||||
|
const QVector<QSharedPointer<Task>> &getAppTasks() const;
|
||||||
|
|
||||||
|
const QVector<QSharedPointer<Task>> &getUserTasks() const;
|
||||||
|
|
||||||
|
const QVector<QSharedPointer<Task>> &getNotebookTasks() const;
|
||||||
|
|
||||||
|
static QString getNotebookTaskFolder();
|
||||||
|
|
||||||
|
const TaskVariableMgr &getVariableMgr() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void tasksUpdated();
|
||||||
|
|
||||||
|
void taskOutputRequested(const QString &p_text) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void loadAllTasks();
|
||||||
|
|
||||||
|
void loadNotebookTasks();
|
||||||
|
|
||||||
|
void loadGlobalTasks();
|
||||||
|
|
||||||
|
void loadTasksFromFolder(QVector<QSharedPointer<Task>> &p_tasks, const QString &p_folder);
|
||||||
|
|
||||||
|
// Return nullptr if not a valid task.
|
||||||
|
QSharedPointer<Task> loadTask(const QString &p_taskFile);
|
||||||
|
|
||||||
|
QVector<QSharedPointer<Task>> m_appTasks;
|
||||||
|
|
||||||
|
QVector<QSharedPointer<Task>> m_userTasks;
|
||||||
|
|
||||||
|
QVector<QSharedPointer<Task>> m_notebookTasks;
|
||||||
|
|
||||||
|
TaskVariableMgr m_variableMgr;
|
||||||
|
};
|
||||||
|
} // ns vnotex
|
||||||
|
|
||||||
|
#endif // TASKMGR_H
|
435
src/task/taskvariablemgr.cpp
Normal file
435
src/task/taskvariablemgr.cpp
Normal file
@ -0,0 +1,435 @@
|
|||||||
|
#include "taskvariablemgr.h"
|
||||||
|
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QInputDialog>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QRandomGenerator>
|
||||||
|
#include <QTimeZone>
|
||||||
|
#include <QProcess>
|
||||||
|
|
||||||
|
#include <core/vnotex.h>
|
||||||
|
#include <core/notebookmgr.h>
|
||||||
|
#include <core/configmgr.h>
|
||||||
|
#include <core/mainconfig.h>
|
||||||
|
#include <core/exception.h>
|
||||||
|
#include <widgets/mainwindow.h>
|
||||||
|
#include <widgets/dialogs/selectdialog.h>
|
||||||
|
#include <notebook/notebook.h>
|
||||||
|
#include <notebook/node.h>
|
||||||
|
#include <utils/pathutils.h>
|
||||||
|
#include <buffer/buffer.h>
|
||||||
|
#include <widgets/viewwindow.h>
|
||||||
|
#include <widgets/viewarea.h>
|
||||||
|
#include <snippet/snippetmgr.h>
|
||||||
|
|
||||||
|
#include "task.h"
|
||||||
|
#include "taskmgr.h"
|
||||||
|
#include "shellexecution.h"
|
||||||
|
|
||||||
|
using namespace vnotex;
|
||||||
|
|
||||||
|
|
||||||
|
TaskVariable::TaskVariable(const QString &p_name, const Func &p_func)
|
||||||
|
: m_name(p_name),
|
||||||
|
m_func(p_func)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TaskVariable::evaluate(Task *p_task, const QString &p_value) const
|
||||||
|
{
|
||||||
|
return m_func(p_task, p_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const QString TaskVariableMgr::c_variableSymbolRegExp = QString(R"(\$\{([^${}:]+)(?::([^${}:]+))?\})");
|
||||||
|
|
||||||
|
TaskVariableMgr::TaskVariableMgr(TaskMgr *p_taskMgr)
|
||||||
|
: m_taskMgr(p_taskMgr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskVariableMgr::init()
|
||||||
|
{
|
||||||
|
initVariables();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskVariableMgr::initVariables()
|
||||||
|
{
|
||||||
|
m_variables.clear();
|
||||||
|
|
||||||
|
m_needUpdateSystemEnvironment = true;
|
||||||
|
|
||||||
|
initNotebookVariables();
|
||||||
|
|
||||||
|
initBufferVariables();
|
||||||
|
|
||||||
|
initTaskVariables();
|
||||||
|
|
||||||
|
initMagicVariables();
|
||||||
|
|
||||||
|
initEnvironmentVariables();
|
||||||
|
|
||||||
|
initConfigVariables();
|
||||||
|
|
||||||
|
initInputVariables();
|
||||||
|
|
||||||
|
initShellVariables();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskVariableMgr::initNotebookVariables()
|
||||||
|
{
|
||||||
|
addVariable("notebookFolder", [](Task *, const QString &) {
|
||||||
|
auto notebook = TaskVariableMgr::getCurrentNotebook();
|
||||||
|
if (notebook) {
|
||||||
|
return PathUtils::cleanPath(notebook->getRootFolderAbsolutePath());
|
||||||
|
} else {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
addVariable("notebookFolderName", [](Task *, const QString &) {
|
||||||
|
auto notebook = TaskVariableMgr::getCurrentNotebook();
|
||||||
|
if (notebook) {
|
||||||
|
return PathUtils::dirName(notebook->getRootFolderPath());
|
||||||
|
} else {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
addVariable("notebookName", [](Task *, const QString &) {
|
||||||
|
auto notebook = TaskVariableMgr::getCurrentNotebook();
|
||||||
|
if (notebook) {
|
||||||
|
return notebook->getName();
|
||||||
|
} else {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
addVariable("notebookDescription", [](Task *, const QString &) {
|
||||||
|
auto notebook = TaskVariableMgr::getCurrentNotebook();
|
||||||
|
if (notebook) {
|
||||||
|
return notebook->getDescription();
|
||||||
|
} else {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskVariableMgr::initBufferVariables()
|
||||||
|
{
|
||||||
|
addVariable("buffer", [](Task *, const QString &) {
|
||||||
|
auto buffer = getCurrentBuffer();
|
||||||
|
if (buffer) {
|
||||||
|
return PathUtils::cleanPath(buffer->getPath());
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
});
|
||||||
|
addVariable("bufferNotebookFolder", [](Task *, const QString &) {
|
||||||
|
auto buffer = getCurrentBuffer();
|
||||||
|
if (buffer) {
|
||||||
|
auto node = buffer->getNode();
|
||||||
|
if (node) {
|
||||||
|
return PathUtils::cleanPath(node->getNotebook()->getRootFolderAbsolutePath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
});
|
||||||
|
addVariable("bufferRelativePath", [](Task *, const QString &) {
|
||||||
|
auto buffer = getCurrentBuffer();
|
||||||
|
if (buffer) {
|
||||||
|
auto node = buffer->getNode();
|
||||||
|
if (node) {
|
||||||
|
return PathUtils::cleanPath(node->fetchPath());
|
||||||
|
} else {
|
||||||
|
return PathUtils::cleanPath(buffer->getPath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
});
|
||||||
|
addVariable("bufferName", [](Task *, const QString &) {
|
||||||
|
auto buffer = getCurrentBuffer();
|
||||||
|
if (buffer) {
|
||||||
|
return PathUtils::fileName(buffer->getPath());
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
});
|
||||||
|
addVariable("bufferBaseName", [](Task *, const QString &) {
|
||||||
|
auto buffer = getCurrentBuffer();
|
||||||
|
if (buffer) {
|
||||||
|
return QFileInfo(buffer->getPath()).completeBaseName();
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
});
|
||||||
|
addVariable("bufferDir", [](Task *, const QString &) {
|
||||||
|
auto buffer = getCurrentBuffer();
|
||||||
|
if (buffer) {
|
||||||
|
return PathUtils::parentDirPath(buffer->getPath());
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
});
|
||||||
|
addVariable("bufferExt", [](Task *, const QString &) {
|
||||||
|
auto buffer = getCurrentBuffer();
|
||||||
|
if (buffer) {
|
||||||
|
return QFileInfo(buffer->getPath()).suffix();
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
});
|
||||||
|
addVariable("selectedText", [](Task *, const QString &) {
|
||||||
|
auto win = getCurrentViewWindow();
|
||||||
|
if (win) {
|
||||||
|
return win->selectedText();
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskVariableMgr::initTaskVariables()
|
||||||
|
{
|
||||||
|
addVariable("cwd", [](Task *task, const QString &) {
|
||||||
|
return PathUtils::cleanPath(task->getOptionsCwd());
|
||||||
|
});
|
||||||
|
addVariable("taskFile", [](Task *task, const QString &) {
|
||||||
|
return PathUtils::cleanPath(task->getFile());
|
||||||
|
});
|
||||||
|
addVariable("taskDir", [](Task *task, const QString &) {
|
||||||
|
return PathUtils::parentDirPath(task->getFile());
|
||||||
|
});
|
||||||
|
addVariable("exeFile", [](Task *, const QString &) {
|
||||||
|
return PathUtils::cleanPath(qApp->applicationFilePath());
|
||||||
|
});
|
||||||
|
addVariable("pathSeparator", [](Task *, const QString &) {
|
||||||
|
return QDir::separator();
|
||||||
|
});
|
||||||
|
addVariable("notebookTaskFolder", [this](Task *, const QString &) {
|
||||||
|
return PathUtils::cleanPath(m_taskMgr->getNotebookTaskFolder());
|
||||||
|
});
|
||||||
|
addVariable("userTaskFolder", [](Task *, const QString &) {
|
||||||
|
return PathUtils::cleanPath(ConfigMgr::getInst().getUserTaskFolder());
|
||||||
|
});
|
||||||
|
addVariable("appTaskFolder", [](Task *, const QString &) {
|
||||||
|
return PathUtils::cleanPath(ConfigMgr::getInst().getAppTaskFolder());
|
||||||
|
});
|
||||||
|
addVariable("userThemeFolder", [](Task *, const QString &) {
|
||||||
|
return PathUtils::cleanPath(ConfigMgr::getInst().getUserThemeFolder());
|
||||||
|
});
|
||||||
|
addVariable("appThemeFolder", [](Task *, const QString &) {
|
||||||
|
return PathUtils::cleanPath(ConfigMgr::getInst().getAppThemeFolder());
|
||||||
|
});
|
||||||
|
addVariable("userDocsFolder", [](Task *, const QString &) {
|
||||||
|
return PathUtils::cleanPath(ConfigMgr::getInst().getUserDocsFolder());
|
||||||
|
});
|
||||||
|
addVariable("appDocsFolder", [](Task *, const QString &) {
|
||||||
|
return PathUtils::cleanPath(ConfigMgr::getInst().getAppDocsFolder());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskVariableMgr::initMagicVariables()
|
||||||
|
{
|
||||||
|
addVariable("magic", [](Task *, const QString &val) {
|
||||||
|
if (val.isEmpty()) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto overrides = SnippetMgr::generateOverrides(getCurrentBuffer());
|
||||||
|
return SnippetMgr::getInst().applySnippetBySymbol(SnippetMgr::generateSnippetSymbol(val), overrides);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskVariableMgr::initEnvironmentVariables()
|
||||||
|
{
|
||||||
|
addVariable("env", [this](Task *, const QString &val) {
|
||||||
|
if (val.isEmpty()) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
if (m_needUpdateSystemEnvironment) {
|
||||||
|
m_needUpdateSystemEnvironment = false;
|
||||||
|
m_systemEnv = QProcessEnvironment::systemEnvironment();
|
||||||
|
}
|
||||||
|
return m_systemEnv.value(val);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskVariableMgr::initConfigVariables()
|
||||||
|
{
|
||||||
|
// ${config:main.core.shortcuts.FullScreen}.
|
||||||
|
addVariable("config", [](Task *, const QString &val) {
|
||||||
|
if (val.isEmpty()) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
auto jsonVal = ConfigMgr::getInst().parseAndReadConfig(val);
|
||||||
|
switch (jsonVal.type()) {
|
||||||
|
case QJsonValue::Bool:
|
||||||
|
return jsonVal.toBool() ? QStringLiteral("1") : QStringLiteral("0");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QJsonValue::Double:
|
||||||
|
return QString::number(jsonVal.toDouble());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QJsonValue::String:
|
||||||
|
return jsonVal.toString();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskVariableMgr::initInputVariables()
|
||||||
|
{
|
||||||
|
// ${input:inputId}.
|
||||||
|
addVariable("input", [this](Task *task, const QString &val) {
|
||||||
|
if (val.isEmpty()) {
|
||||||
|
Exception::throwOne(Exception::Type::InvalidArgument,
|
||||||
|
QString("task (%1) with empty input id").arg(task->getLabel()));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto input = task->findInput(val);
|
||||||
|
if (!input) {
|
||||||
|
Exception::throwOne(Exception::Type::InvalidArgument,
|
||||||
|
QString("task (%1) with invalid input id (%2)").arg(task->getLabel(), val));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input->type == "promptString") {
|
||||||
|
const auto desc = evaluate(task, input->description);
|
||||||
|
const auto defaultText = evaluate(task, input->default_);
|
||||||
|
QInputDialog dialog(VNoteX::getInst().getMainWindow());
|
||||||
|
dialog.setInputMode(QInputDialog::TextInput);
|
||||||
|
dialog.setTextEchoMode(input->password ? QLineEdit::Password : QLineEdit::Normal);
|
||||||
|
dialog.setWindowTitle(task->getLabel());
|
||||||
|
dialog.setLabelText(desc);
|
||||||
|
dialog.setTextValue(defaultText);
|
||||||
|
if (dialog.exec() == QDialog::Accepted) {
|
||||||
|
return dialog.textValue();
|
||||||
|
} else {
|
||||||
|
task->setCancelled(true);
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
} else if (input->type == "pickString") {
|
||||||
|
const auto desc = evaluate(task, input->description);
|
||||||
|
SelectDialog dialog(task->getLabel(), desc, VNoteX::getInst().getMainWindow());
|
||||||
|
for (int i = 0; i < input->options.size(); i++) {
|
||||||
|
dialog.addSelection(input->options.at(i), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dialog.exec() == QDialog::Accepted) {
|
||||||
|
int selection = dialog.getSelection();
|
||||||
|
return input->options.at(selection);
|
||||||
|
} else {
|
||||||
|
task->setCancelled(true);
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Exception::throwOne(Exception::Type::InvalidArgument,
|
||||||
|
QString("task (%1) with invalid input type (%2)(%3)").arg(task->getLabel(), input->id, input->type));
|
||||||
|
}
|
||||||
|
|
||||||
|
return QString();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskVariableMgr::initShellVariables()
|
||||||
|
{
|
||||||
|
// ${shell:command}.
|
||||||
|
addVariable("shell", [this](Task *task, const QString &val) {
|
||||||
|
QProcess process;
|
||||||
|
process.setWorkingDirectory(task->getOptionsCwd());
|
||||||
|
ShellExecution::setupProcess(&process, val);
|
||||||
|
process.start();
|
||||||
|
const int timeout = 1000;
|
||||||
|
if (!process.waitForStarted(timeout) || !process.waitForFinished(timeout)) {
|
||||||
|
Exception::throwOne(Exception::Type::InvalidArgument,
|
||||||
|
QString("task (%1) failed to fetch shell variable (%2)").arg(task->getLabel(), val));
|
||||||
|
}
|
||||||
|
return Task::decodeText(process.readAllStandardOutput());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskVariableMgr::addVariable(const QString &p_name, const TaskVariable::Func &p_func)
|
||||||
|
{
|
||||||
|
Q_ASSERT(!m_variables.contains(p_name));
|
||||||
|
|
||||||
|
m_variables.insert(p_name, TaskVariable(p_name, p_func));
|
||||||
|
}
|
||||||
|
|
||||||
|
const ViewWindow *TaskVariableMgr::getCurrentViewWindow()
|
||||||
|
{
|
||||||
|
return VNoteX::getInst().getMainWindow()->getViewArea()->getCurrentViewWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer *TaskVariableMgr::getCurrentBuffer()
|
||||||
|
{
|
||||||
|
auto win = getCurrentViewWindow();
|
||||||
|
if (win) {
|
||||||
|
return win->getBuffer();
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSharedPointer<Notebook> TaskVariableMgr::getCurrentNotebook()
|
||||||
|
{
|
||||||
|
return VNoteX::getInst().getNotebookMgr().getCurrentNotebook();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TaskVariableMgr::evaluate(Task *p_task, const QString &p_text) const
|
||||||
|
{
|
||||||
|
QString content(p_text);
|
||||||
|
|
||||||
|
int maxTimesAtSamePos = 100;
|
||||||
|
|
||||||
|
QRegularExpression regExp(c_variableSymbolRegExp);
|
||||||
|
int pos = 0;
|
||||||
|
while (pos < content.size()) {
|
||||||
|
QRegularExpressionMatch match;
|
||||||
|
int idx = content.indexOf(regExp, pos, &match);
|
||||||
|
if (idx == -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto varName = match.captured(1).trimmed();
|
||||||
|
const auto varValue = match.captured(2).trimmed();
|
||||||
|
auto var = findVariable(varName);
|
||||||
|
if (!var) {
|
||||||
|
// Skip it.
|
||||||
|
pos = idx + match.capturedLength(0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto afterText = var->evaluate(p_task, varValue);
|
||||||
|
content.replace(idx, match.capturedLength(0), afterText);
|
||||||
|
|
||||||
|
// @afterText may still contains variable symbol.
|
||||||
|
if (pos == idx) {
|
||||||
|
if (--maxTimesAtSamePos == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
maxTimesAtSamePos = 100;
|
||||||
|
}
|
||||||
|
pos = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList TaskVariableMgr::evaluate(Task *p_task, const QStringList &p_texts) const
|
||||||
|
{
|
||||||
|
QStringList strs;
|
||||||
|
for (const auto &str : p_texts) {
|
||||||
|
strs << evaluate(p_task, str);
|
||||||
|
}
|
||||||
|
return strs;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TaskVariable *TaskVariableMgr::findVariable(const QString &p_name) const
|
||||||
|
{
|
||||||
|
auto it = m_variables.find(p_name);
|
||||||
|
if (it != m_variables.end()) {
|
||||||
|
return &(it.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskVariableMgr::overrideVariable(const QString &p_name, const TaskVariable::Func &p_func)
|
||||||
|
{
|
||||||
|
m_variables.insert(p_name, TaskVariable(p_name, p_func));
|
||||||
|
}
|
105
src/task/taskvariablemgr.h
Normal file
105
src/task/taskvariablemgr.h
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
#ifndef TASKVARIABLEMGR_H
|
||||||
|
#define TASKVARIABLEMGR_H
|
||||||
|
|
||||||
|
#include <core/noncopyable.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include <QHash>
|
||||||
|
#include <QString>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
#include <QScopedPointer>
|
||||||
|
#include <QProcessEnvironment>
|
||||||
|
|
||||||
|
namespace vnotex
|
||||||
|
{
|
||||||
|
class Task;
|
||||||
|
class Notebook;
|
||||||
|
class Buffer;
|
||||||
|
class ViewWindow;
|
||||||
|
class TaskMgr;
|
||||||
|
|
||||||
|
class TaskVariable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef std::function<QString(Task *, const QString &)> Func;
|
||||||
|
|
||||||
|
TaskVariable(const QString &p_name, const Func &p_func);
|
||||||
|
|
||||||
|
QString evaluate(Task *p_task, const QString &p_value) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_name;
|
||||||
|
|
||||||
|
Func m_func;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class TaskVariableMgr : private Noncopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit TaskVariableMgr(TaskMgr *p_taskMgr);
|
||||||
|
|
||||||
|
void init();
|
||||||
|
|
||||||
|
QString evaluate(Task *p_task, const QString &p_text) const;
|
||||||
|
|
||||||
|
QStringList evaluate(Task *p_task, const QStringList &p_texts) const;
|
||||||
|
|
||||||
|
// Used for UT.
|
||||||
|
void overrideVariable(const QString &p_name, const TaskVariable::Func &p_func);
|
||||||
|
|
||||||
|
static Buffer *getCurrentBuffer();
|
||||||
|
|
||||||
|
static QSharedPointer<Notebook> getCurrentNotebook();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void initVariables();
|
||||||
|
|
||||||
|
void initNotebookVariables();
|
||||||
|
|
||||||
|
void initBufferVariables();
|
||||||
|
|
||||||
|
void initTaskVariables();
|
||||||
|
|
||||||
|
void initMagicVariables();
|
||||||
|
|
||||||
|
void initEnvironmentVariables();
|
||||||
|
|
||||||
|
void initConfigVariables();
|
||||||
|
|
||||||
|
void initInputVariables();
|
||||||
|
|
||||||
|
void initShellVariables();
|
||||||
|
|
||||||
|
void addVariable(const QString &p_name, const TaskVariable::Func &p_func);
|
||||||
|
|
||||||
|
const TaskVariable *findVariable(const QString &p_name) const;
|
||||||
|
|
||||||
|
/*
|
||||||
|
QString evaluateInputVariables(const QString &p_text,
|
||||||
|
const Task *p_task) const;
|
||||||
|
|
||||||
|
QString evaluateShellVariables(const QString &p_text,
|
||||||
|
const Task *p_task) const;
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const ViewWindow *getCurrentViewWindow();
|
||||||
|
|
||||||
|
TaskMgr *m_taskMgr = nullptr;
|
||||||
|
|
||||||
|
QHash<QString, TaskVariable> m_variables;
|
||||||
|
|
||||||
|
bool m_needUpdateSystemEnvironment = true;
|
||||||
|
|
||||||
|
QProcessEnvironment m_systemEnv;
|
||||||
|
|
||||||
|
// %{name[:value]}%.
|
||||||
|
// Captured texts:
|
||||||
|
// 1 - The name of the variable (trim needed).
|
||||||
|
// 2 - The value option of the variable if available (trim needed).
|
||||||
|
static const QString c_variableSymbolRegExp;
|
||||||
|
};
|
||||||
|
} // ns vnotex
|
||||||
|
|
||||||
|
#endif // TASKVARIABLEMGR_H
|
@ -362,17 +362,23 @@ QStringList FileUtils::entryListRecursively(const QString &p_dirPath,
|
|||||||
const QStringList &p_nameFilters,
|
const QStringList &p_nameFilters,
|
||||||
QDir::Filters p_filters)
|
QDir::Filters p_filters)
|
||||||
{
|
{
|
||||||
|
QStringList entries;
|
||||||
|
|
||||||
QDir dir(p_dirPath);
|
QDir dir(p_dirPath);
|
||||||
if (dir.isEmpty()) return {};
|
if (!dir.exists()) {
|
||||||
QStringList entrys;
|
return entries;
|
||||||
const auto curEntrys = dir.entryList(p_nameFilters, p_filters | QDir::NoDotAndDotDot);
|
|
||||||
for (const auto &e : curEntrys) {
|
|
||||||
entrys.append(PathUtils::concatenateFilePath(p_dirPath, e));
|
|
||||||
}
|
}
|
||||||
auto subdirs = dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
|
|
||||||
|
const auto curEntries = dir.entryList(p_nameFilters, p_filters | QDir::NoDotAndDotDot);
|
||||||
|
for (const auto &e : curEntries) {
|
||||||
|
entries.append(PathUtils::concatenateFilePath(p_dirPath, e));
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto subdirs = dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
|
||||||
for (const auto &subdir : subdirs) {
|
for (const auto &subdir : subdirs) {
|
||||||
const auto dirPath = PathUtils::concatenateFilePath(p_dirPath, subdir);
|
const auto dirPath = PathUtils::concatenateFilePath(p_dirPath, subdir);
|
||||||
entrys.append(entryListRecursively(dirPath, p_nameFilters, p_filters));
|
entries.append(entryListRecursively(dirPath, p_nameFilters, p_filters));
|
||||||
}
|
}
|
||||||
return entrys;
|
|
||||||
|
return entries;
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ namespace vnotex
|
|||||||
// @p_nameFilters is for each dir, not for all.
|
// @p_nameFilters is for each dir, not for all.
|
||||||
static QStringList entryListRecursively(const QString &p_dirPath,
|
static QStringList entryListRecursively(const QString &p_dirPath,
|
||||||
const QStringList &p_nameFilters,
|
const QStringList &p_nameFilters,
|
||||||
QDir::Filters p_filters=QDir::NoFilter);
|
QDir::Filters p_filters = QDir::NoFilter);
|
||||||
};
|
};
|
||||||
} // ns vnotex
|
} // ns vnotex
|
||||||
|
|
||||||
|
@ -16,7 +16,6 @@ QString PathUtils::parentDirPath(const QString &p_path)
|
|||||||
}
|
}
|
||||||
|
|
||||||
QFileInfo info(p_path);
|
QFileInfo info(p_path);
|
||||||
Q_ASSERT(info.isAbsolute());
|
|
||||||
return cleanPath(info.absolutePath());
|
return cleanPath(info.absolutePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,13 +4,15 @@
|
|||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QKeySequence>
|
#include <QKeySequence>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <QVariant>
|
|
||||||
#include <QFontDatabase>
|
#include <QFontDatabase>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
#include <QRegularExpressionMatch>
|
||||||
#include <QSvgRenderer>
|
#include <QSvgRenderer>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
|
#include <QJsonArray>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
@ -139,3 +141,54 @@ QJsonObject Utils::fromJsonString(const QByteArray &p_data)
|
|||||||
{
|
{
|
||||||
return QJsonDocument::fromJson(p_data).object();
|
return QJsonDocument::fromJson(p_data).object();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QJsonValue Utils::parseAndReadJson(const QJsonObject &p_obj, const QString &p_exp)
|
||||||
|
{
|
||||||
|
// abc[0] or abc.
|
||||||
|
QRegularExpression regExp(R"(^([^\[\]\s]+)(?:\[(\d+)\])?$)");
|
||||||
|
|
||||||
|
QJsonValue val(p_obj);
|
||||||
|
|
||||||
|
bool valid = true;
|
||||||
|
const auto tokens = p_exp.split(QLatin1Char('.'));
|
||||||
|
for (int i = 0; i < tokens.size(); ++i) {
|
||||||
|
const auto &token = tokens[i];
|
||||||
|
if (token.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto match = regExp.match(token);
|
||||||
|
if (!match.hasMatch()) {
|
||||||
|
valid = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto key = match.captured(1);
|
||||||
|
const auto obj = val.toObject();
|
||||||
|
if (obj.contains(key)) {
|
||||||
|
val = obj.value(key);
|
||||||
|
} else {
|
||||||
|
valid = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!match.captured(2).isEmpty()) {
|
||||||
|
// Array.
|
||||||
|
const auto arr = val.toArray();
|
||||||
|
int idx = match.captured(2).toInt();
|
||||||
|
if (idx < 0 || idx >= arr.size()) {
|
||||||
|
valid = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = arr[idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!valid) {
|
||||||
|
qWarning() << "invalid expression to parse for JSON" << p_exp;
|
||||||
|
return QJsonValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
@ -57,6 +57,10 @@ namespace vnotex
|
|||||||
static QByteArray toJsonString(const QJsonObject &p_obj);
|
static QByteArray toJsonString(const QJsonObject &p_obj);
|
||||||
|
|
||||||
static QJsonObject fromJsonString(const QByteArray &p_data);
|
static QJsonObject fromJsonString(const QByteArray &p_data);
|
||||||
|
|
||||||
|
// Parse @p_exp into tokens and read the target value from @p_obj.
|
||||||
|
// Format: obj1.obj2.arr[2].obj3.
|
||||||
|
static QJsonValue parseAndReadJson(const QJsonObject &p_obj, const QString &p_exp);
|
||||||
};
|
};
|
||||||
} // ns vnotex
|
} // ns vnotex
|
||||||
|
|
||||||
|
@ -13,13 +13,8 @@ using namespace vnotex;
|
|||||||
const QChar SelectDialog::c_cancelShortcut = QLatin1Char('z');
|
const QChar SelectDialog::c_cancelShortcut = QLatin1Char('z');
|
||||||
|
|
||||||
SelectDialog::SelectDialog(const QString &p_title, QWidget *p_parent)
|
SelectDialog::SelectDialog(const QString &p_title, QWidget *p_parent)
|
||||||
: QDialog(p_parent)
|
: SelectDialog(p_title, QString(), p_parent)
|
||||||
{
|
{
|
||||||
const auto &themeMgr = VNoteX::getInst().getThemeMgr();
|
|
||||||
m_shortcutIconForeground = themeMgr.paletteColor(QStringLiteral("widgets#quickselector#item_icon#fg"));
|
|
||||||
m_shortcutIconBorder = themeMgr.paletteColor(QStringLiteral("widgets#quickselector#item_icon#border"));
|
|
||||||
|
|
||||||
setupUI(p_title);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectDialog::SelectDialog(const QString &p_title,
|
SelectDialog::SelectDialog(const QString &p_title,
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include "snippetpanel.h"
|
#include "snippetpanel.h"
|
||||||
#include "historypanel.h"
|
#include "historypanel.h"
|
||||||
#include "tagexplorer.h"
|
#include "tagexplorer.h"
|
||||||
|
#include "terminalviewer.h"
|
||||||
|
|
||||||
using namespace vnotex;
|
using namespace vnotex;
|
||||||
|
|
||||||
@ -113,7 +114,7 @@ void DockWidgetHelper::setupDocks()
|
|||||||
|
|
||||||
setupOutlineDock();
|
setupOutlineDock();
|
||||||
|
|
||||||
setupOutputDock();
|
setupTerminalDock();
|
||||||
|
|
||||||
setupLocationListDock();
|
setupLocationListDock();
|
||||||
|
|
||||||
@ -148,17 +149,17 @@ void DockWidgetHelper::setupOutlineDock()
|
|||||||
m_mainWindow->addDockWidget(Qt::RightDockWidgetArea, dock);
|
m_mainWindow->addDockWidget(Qt::RightDockWidgetArea, dock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DockWidgetHelper::setupOutputDock()
|
void DockWidgetHelper::setupTerminalDock()
|
||||||
{
|
{
|
||||||
auto dock = createDockWidget(DockIndex::OutputDock, tr("Output"), m_mainWindow);
|
auto dock = createDockWidget(DockIndex::TerminalDock, tr("Terminal"), m_mainWindow);
|
||||||
|
|
||||||
dock->setObjectName(QStringLiteral("OutputDock.vnotex"));
|
dock->setObjectName(QStringLiteral("TerminalDock.vnotex"));
|
||||||
dock->setAllowedAreas(Qt::BottomDockWidgetArea);
|
dock->setAllowedAreas(Qt::AllDockWidgetAreas);
|
||||||
|
|
||||||
dock->setWidget(m_mainWindow->m_outputViewer);
|
dock->setWidget(m_mainWindow->m_terminalViewer);
|
||||||
dock->setFocusProxy(m_mainWindow->m_outputViewer);
|
dock->setFocusProxy(m_mainWindow->m_terminalViewer);
|
||||||
dock->hide();
|
|
||||||
m_mainWindow->addDockWidget(Qt::BottomDockWidgetArea, dock);
|
m_mainWindow->addDockWidget(Qt::BottomDockWidgetArea, dock);
|
||||||
|
dock->hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DockWidgetHelper::setupSearchDock()
|
void DockWidgetHelper::setupSearchDock()
|
||||||
|
@ -29,7 +29,7 @@ namespace vnotex
|
|||||||
SearchDock,
|
SearchDock,
|
||||||
SnippetDock,
|
SnippetDock,
|
||||||
OutlineDock,
|
OutlineDock,
|
||||||
OutputDock,
|
TerminalDock,
|
||||||
LocationListDock,
|
LocationListDock,
|
||||||
MaxDock
|
MaxDock
|
||||||
};
|
};
|
||||||
@ -98,7 +98,7 @@ namespace vnotex
|
|||||||
|
|
||||||
void setupOutlineDock();
|
void setupOutlineDock();
|
||||||
|
|
||||||
void setupOutputDock();
|
void setupTerminalDock();
|
||||||
|
|
||||||
void setupSearchDock();
|
void setupSearchDock();
|
||||||
|
|
||||||
|
@ -230,10 +230,4 @@ void FramelessMainWindowLinux::showEvent(QShowEvent *p_event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
|
||||||
using namespace vnotex;
|
|
||||||
|
|
||||||
FramelessMainWindowLinuxDummy::FramelessMainWindowLinuxDummy()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -65,7 +65,7 @@ namespace vnotex
|
|||||||
class FramelessMainWindowLinuxDummy
|
class FramelessMainWindowLinuxDummy
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FramelessMainWindowLinuxDummy();
|
FramelessMainWindowLinuxDummy() = default;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -221,10 +221,4 @@ void FramelessMainWindowWin::forceRedraw()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
|
||||||
using namespace vnotex;
|
|
||||||
|
|
||||||
FramelessMainWindowWinDummy::FramelessMainWindowWinDummy()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -41,7 +41,7 @@ namespace vnotex
|
|||||||
class FramelessMainWindowWinDummy
|
class FramelessMainWindowWinDummy
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FramelessMainWindowWinDummy();
|
FramelessMainWindowWinDummy() = default;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,7 @@
|
|||||||
#include "tagexplorer.h"
|
#include "tagexplorer.h"
|
||||||
#include "toolbarhelper.h"
|
#include "toolbarhelper.h"
|
||||||
#include "statusbarhelper.h"
|
#include "statusbarhelper.h"
|
||||||
|
#include "terminalviewer.h"
|
||||||
|
|
||||||
using namespace vnotex;
|
using namespace vnotex;
|
||||||
|
|
||||||
@ -257,7 +258,7 @@ void MainWindow::setupDocks()
|
|||||||
|
|
||||||
setupOutlineViewer();
|
setupOutlineViewer();
|
||||||
|
|
||||||
setupOutputViewer();
|
setupTerminalViewer();
|
||||||
|
|
||||||
setupHistoryPanel();
|
setupHistoryPanel();
|
||||||
|
|
||||||
@ -514,23 +515,15 @@ void MainWindow::setupOutlineViewer()
|
|||||||
this, &MainWindow::focusViewArea);
|
this, &MainWindow::focusViewArea);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::setupOutputViewer()
|
void MainWindow::setupTerminalViewer()
|
||||||
{
|
{
|
||||||
m_outputViewer = new QTextEdit(this);
|
m_terminalViewer = new TerminalViewer(this);
|
||||||
m_outputViewer->setObjectName("OutputViewer.vnotex");
|
m_terminalViewer->setObjectName("TerminalViewer.vnotex");
|
||||||
m_outputViewer->setReadOnly(true);
|
|
||||||
|
|
||||||
connect(&VNoteX::getInst(), &VNoteX::showOutputRequested,
|
connect(&VNoteX::getInst(), &VNoteX::showOutputRequested,
|
||||||
m_outputViewer, [this](const QString &p_text) {
|
this, [this](const QString &p_text) {
|
||||||
auto cursor = m_outputViewer->textCursor();
|
m_terminalViewer->append(p_text);
|
||||||
cursor.movePosition(QTextCursor::End);
|
m_dockWidgetHelper.activateDock(DockWidgetHelper::TerminalDock);
|
||||||
m_outputViewer->setTextCursor(cursor);
|
|
||||||
m_outputViewer->insertPlainText(p_text);
|
|
||||||
auto scrollBar = m_outputViewer->verticalScrollBar();
|
|
||||||
if (scrollBar) {
|
|
||||||
scrollBar->setSliderPosition(scrollBar->maximum());
|
|
||||||
}
|
|
||||||
m_dockWidgetHelper.getDock(DockWidgetHelper::OutputDock)->show();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ namespace vnotex
|
|||||||
class SnippetPanel;
|
class SnippetPanel;
|
||||||
class HistoryPanel;
|
class HistoryPanel;
|
||||||
class ExportDialog;
|
class ExportDialog;
|
||||||
|
class TerminalViewer;
|
||||||
|
|
||||||
enum { RESTART_EXIT_CODE = 1000 };
|
enum { RESTART_EXIT_CODE = 1000 };
|
||||||
|
|
||||||
@ -106,7 +107,7 @@ namespace vnotex
|
|||||||
|
|
||||||
void setupOutlineViewer();
|
void setupOutlineViewer();
|
||||||
|
|
||||||
void setupOutputViewer();
|
void setupTerminalViewer();
|
||||||
|
|
||||||
void setupSearchPanel();
|
void setupSearchPanel();
|
||||||
|
|
||||||
@ -167,7 +168,7 @@ namespace vnotex
|
|||||||
|
|
||||||
OutlineViewer *m_outlineViewer = nullptr;
|
OutlineViewer *m_outlineViewer = nullptr;
|
||||||
|
|
||||||
QTextEdit *m_outputViewer = nullptr;
|
TerminalViewer *m_terminalViewer = nullptr;
|
||||||
|
|
||||||
LocationList *m_locationList = nullptr;
|
LocationList *m_locationList = nullptr;
|
||||||
|
|
||||||
|
@ -838,7 +838,7 @@ QAction *NotebookNodeExplorer::createAction(Action p_act, QObject *p_parent)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case Action::OpenLocation:
|
case Action::OpenLocation:
|
||||||
act = new QAction(tr("Open &Location"), p_parent);
|
act = new QAction(tr("Open Locat&ion"), p_parent);
|
||||||
connect(act, &QAction::triggered,
|
connect(act, &QAction::triggered,
|
||||||
this, [this]() {
|
this, [this]() {
|
||||||
auto item = m_masterExplorer->currentItem();
|
auto item = m_masterExplorer->currentItem();
|
||||||
|
62
src/widgets/terminalviewer.cpp
Normal file
62
src/widgets/terminalviewer.cpp
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#include "terminalviewer.h"
|
||||||
|
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QPlainTextEdit>
|
||||||
|
#include <QScrollBar>
|
||||||
|
#include <QToolButton>
|
||||||
|
|
||||||
|
#include <utils/widgetutils.h>
|
||||||
|
|
||||||
|
#include "widgetsfactory.h"
|
||||||
|
#include "titlebar.h"
|
||||||
|
|
||||||
|
using namespace vnotex;
|
||||||
|
|
||||||
|
TerminalViewer::TerminalViewer(QWidget *p_parent)
|
||||||
|
: QFrame(p_parent)
|
||||||
|
{
|
||||||
|
setupUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerminalViewer::setupUI()
|
||||||
|
{
|
||||||
|
auto mainLayout = new QVBoxLayout(this);
|
||||||
|
WidgetUtils::setContentsMargins(mainLayout);
|
||||||
|
|
||||||
|
{
|
||||||
|
setupTitleBar(QString(), this);
|
||||||
|
mainLayout->addWidget(m_titleBar);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_consoleEdit = new QPlainTextEdit(this);
|
||||||
|
m_consoleEdit->setReadOnly(true);
|
||||||
|
mainLayout->addWidget(m_consoleEdit);
|
||||||
|
|
||||||
|
setFocusProxy(m_consoleEdit);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerminalViewer::setupTitleBar(const QString &p_title, QWidget *p_parent)
|
||||||
|
{
|
||||||
|
m_titleBar = new TitleBar(p_title, true, TitleBar::Action::None, p_parent);
|
||||||
|
m_titleBar->setActionButtonsAlwaysShown(true);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto clearBtn = m_titleBar->addActionButton(QStringLiteral("clear.svg"), tr("Clear"));
|
||||||
|
connect(clearBtn, &QToolButton::triggered,
|
||||||
|
this, &TerminalViewer::clear);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerminalViewer::append(const QString &p_text)
|
||||||
|
{
|
||||||
|
m_consoleEdit->appendPlainText(p_text);
|
||||||
|
auto scrollBar = m_consoleEdit->verticalScrollBar();
|
||||||
|
if (scrollBar) {
|
||||||
|
scrollBar->setValue(scrollBar->maximum());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerminalViewer::clear()
|
||||||
|
{
|
||||||
|
m_consoleEdit->clear();
|
||||||
|
}
|
33
src/widgets/terminalviewer.h
Normal file
33
src/widgets/terminalviewer.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#ifndef TERMINALVIEWER_H
|
||||||
|
#define TERMINALVIEWER_H
|
||||||
|
|
||||||
|
#include <QFrame>
|
||||||
|
|
||||||
|
class QPlainTextEdit;
|
||||||
|
|
||||||
|
namespace vnotex
|
||||||
|
{
|
||||||
|
class TitleBar;
|
||||||
|
|
||||||
|
class TerminalViewer : public QFrame
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit TerminalViewer(QWidget *p_parent = nullptr);
|
||||||
|
|
||||||
|
void append(const QString &p_text);
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setupUI();
|
||||||
|
|
||||||
|
void setupTitleBar(const QString &p_title, QWidget *p_parent = nullptr);
|
||||||
|
|
||||||
|
TitleBar *m_titleBar = nullptr;
|
||||||
|
|
||||||
|
QPlainTextEdit *m_consoleEdit = nullptr;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // TERMINALVIEWER_H
|
@ -27,6 +27,7 @@
|
|||||||
#include <core/fileopenparameters.h>
|
#include <core/fileopenparameters.h>
|
||||||
#include <core/htmltemplatehelper.h>
|
#include <core/htmltemplatehelper.h>
|
||||||
#include <core/exception.h>
|
#include <core/exception.h>
|
||||||
|
#include <task/taskmgr.h>
|
||||||
#include "propertydefs.h"
|
#include "propertydefs.h"
|
||||||
#include "dialogs/settings/settingsdialog.h"
|
#include "dialogs/settings/settingsdialog.h"
|
||||||
#include "dialogs/updater.h"
|
#include "dialogs/updater.h"
|
||||||
@ -286,52 +287,80 @@ QToolBar *ToolBarHelper::setupQuickAccessToolBar(MainWindow *p_win, QToolBar *p_
|
|||||||
void ToolBarHelper::setupTaskMenu(QMenu *p_menu)
|
void ToolBarHelper::setupTaskMenu(QMenu *p_menu)
|
||||||
{
|
{
|
||||||
p_menu->clear();
|
p_menu->clear();
|
||||||
|
|
||||||
|
setupTaskActionMenu(p_menu);
|
||||||
|
|
||||||
|
p_menu->addSeparator();
|
||||||
|
|
||||||
const auto &taskMgr = VNoteX::getInst().getTaskMgr();
|
const auto &taskMgr = VNoteX::getInst().getTaskMgr();
|
||||||
for (auto task : taskMgr.getAppTasks()) {
|
for (const auto &task : taskMgr.getAppTasks()) {
|
||||||
addTaskMenu(p_menu, task);
|
addTaskMenu(p_menu, task.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
p_menu->addSeparator();
|
p_menu->addSeparator();
|
||||||
for (auto task : taskMgr.getUserTasks()) {
|
|
||||||
addTaskMenu(p_menu, task);
|
for (const auto &task : taskMgr.getUserTasks()) {
|
||||||
|
addTaskMenu(p_menu, task.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
p_menu->addSeparator();
|
p_menu->addSeparator();
|
||||||
for (auto task : taskMgr.getNotebookTasks()) {
|
|
||||||
addTaskMenu(p_menu, task);
|
for (const auto &task : taskMgr.getNotebookTasks()) {
|
||||||
|
addTaskMenu(p_menu, task.data());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ToolBarHelper::setupTaskActionMenu(QMenu *p_menu)
|
||||||
|
{
|
||||||
|
p_menu->addAction(MainWindow::tr("Add Task"),
|
||||||
|
p_menu,
|
||||||
|
[]() {
|
||||||
|
WidgetUtils::openUrlByDesktop(QUrl::fromLocalFile(ConfigMgr::getInst().getUserTaskFolder()));
|
||||||
|
});
|
||||||
|
|
||||||
|
p_menu->addAction(MainWindow::tr("Reload"),
|
||||||
|
p_menu,
|
||||||
|
[]() {
|
||||||
|
VNoteX::getInst().getTaskMgr().reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void ToolBarHelper::addTaskMenu(QMenu *p_menu, Task *p_task)
|
void ToolBarHelper::addTaskMenu(QMenu *p_menu, Task *p_task)
|
||||||
{
|
{
|
||||||
MainWindow::connect(p_task, &Task::showOutput,
|
|
||||||
&VNoteX::getInst(), &VNoteX::showOutputRequested);
|
|
||||||
QAction *action = nullptr;
|
QAction *action = nullptr;
|
||||||
const auto &tasks = p_task->getTasks();
|
|
||||||
|
const auto &children = p_task->getChildren();
|
||||||
|
|
||||||
auto label = p_task->getLabel();
|
auto label = p_task->getLabel();
|
||||||
label = label.replace("&", "&&");
|
// '&' will be considered shortuct symbol in QAction.
|
||||||
|
label.replace("&", "&&");
|
||||||
|
|
||||||
|
if (children.isEmpty()) {
|
||||||
|
action = p_menu->addAction(label);
|
||||||
|
} else {
|
||||||
|
auto subMenu = p_menu->addMenu(label);
|
||||||
|
for (auto task : children) {
|
||||||
|
addTaskMenu(subMenu, task);
|
||||||
|
}
|
||||||
|
action = subMenu->menuAction();
|
||||||
|
}
|
||||||
|
|
||||||
QIcon icon;
|
QIcon icon;
|
||||||
try {
|
try {
|
||||||
auto taskIcon = p_task->getIcon();
|
auto taskIcon = p_task->getIcon();
|
||||||
if (!taskIcon.isEmpty()) {
|
if (!taskIcon.isEmpty()) {
|
||||||
icon = generateIcon(p_task->getIcon());
|
icon = generateIcon(p_task->getIcon());
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception &e) {
|
||||||
if (e.m_type != Exception::Type::FailToReadFile) {
|
if (e.m_type != Exception::Type::FailToReadFile) {
|
||||||
throw;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tasks.isEmpty()) {
|
|
||||||
action = p_menu->addAction(label);
|
|
||||||
} else {
|
|
||||||
auto menu = p_menu->addMenu(label);
|
|
||||||
for (auto task : tasks) {
|
|
||||||
addTaskMenu(menu, task);
|
|
||||||
}
|
|
||||||
action = menu->menuAction();
|
|
||||||
}
|
|
||||||
action->setIcon(icon);
|
action->setIcon(icon);
|
||||||
|
|
||||||
|
action->setData(reinterpret_cast<qulonglong>(p_task));
|
||||||
|
|
||||||
WidgetUtils::addActionShortcut(action, p_task->getShortcut());
|
WidgetUtils::addActionShortcut(action, p_task->getShortcut());
|
||||||
MainWindow::connect(action, &QAction::triggered,
|
|
||||||
p_task, &Task::run);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QToolBar *ToolBarHelper::setupTaskToolBar(MainWindow *p_win, QToolBar *p_toolBar)
|
QToolBar *ToolBarHelper::setupTaskToolBar(MainWindow *p_win, QToolBar *p_toolBar)
|
||||||
@ -347,11 +376,19 @@ QToolBar *ToolBarHelper::setupTaskToolBar(MainWindow *p_win, QToolBar *p_toolBar
|
|||||||
btn->setProperty(PropertyDefs::c_toolButtonWithoutMenuIndicator, true);
|
btn->setProperty(PropertyDefs::c_toolButtonWithoutMenuIndicator, true);
|
||||||
|
|
||||||
auto taskMenu = WidgetsFactory::createMenu(tb);
|
auto taskMenu = WidgetsFactory::createMenu(tb);
|
||||||
MainWindow::connect(&VNoteX::getInst().getTaskMgr(), &TaskMgr::taskChanged,
|
setupTaskActionMenu(taskMenu);
|
||||||
|
btn->setMenu(taskMenu);
|
||||||
|
MainWindow::connect(taskMenu, &QMenu::triggered,
|
||||||
|
taskMenu, [](QAction *act) {
|
||||||
|
auto task = reinterpret_cast<Task *>(act->data().toULongLong());
|
||||||
|
Q_ASSERT(task);
|
||||||
|
task->run();
|
||||||
|
});
|
||||||
|
MainWindow::connect(&VNoteX::getInst().getTaskMgr(), &TaskMgr::tasksUpdated,
|
||||||
taskMenu, [taskMenu]() {
|
taskMenu, [taskMenu]() {
|
||||||
setupTaskMenu(taskMenu);
|
setupTaskMenu(taskMenu);
|
||||||
});
|
});
|
||||||
btn->setMenu(taskMenu);
|
|
||||||
return tb;
|
return tb;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -685,6 +722,12 @@ void ToolBarHelper::setupMenuButton(MainWindow *p_win, QToolBar *p_toolBar)
|
|||||||
WidgetUtils::openUrlByDesktop(QUrl("https://vnotex.github.io/vnote"));
|
WidgetUtils::openUrlByDesktop(QUrl("https://vnotex.github.io/vnote"));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
helpMenu->addAction(MainWindow::tr("Documentation"),
|
||||||
|
helpMenu,
|
||||||
|
[]() {
|
||||||
|
WidgetUtils::openUrlByDesktop(QUrl("https://vnotex.github.io/vnote/en_us/#!docs/vx.json"));
|
||||||
|
});
|
||||||
|
|
||||||
helpMenu->addAction(MainWindow::tr("Feedback and Discussions"),
|
helpMenu->addAction(MainWindow::tr("Feedback and Discussions"),
|
||||||
helpMenu,
|
helpMenu,
|
||||||
[]() {
|
[]() {
|
||||||
|
@ -36,6 +36,8 @@ namespace vnotex
|
|||||||
|
|
||||||
static void setupTaskMenu(QMenu *p_menu);
|
static void setupTaskMenu(QMenu *p_menu);
|
||||||
|
|
||||||
|
static void setupTaskActionMenu(QMenu *p_menu);
|
||||||
|
|
||||||
static void addTaskMenu(QMenu *p_menu, Task *p_task);
|
static void addTaskMenu(QMenu *p_menu, Task *p_task);
|
||||||
|
|
||||||
static QToolBar *setupTaskToolBar(MainWindow *p_win, QToolBar *p_toolBar);
|
static QToolBar *setupTaskToolBar(MainWindow *p_win, QToolBar *p_toolBar);
|
||||||
|
@ -97,6 +97,8 @@ namespace vnotex
|
|||||||
|
|
||||||
bool isSessionEnabled() const;
|
bool isSessionEnabled() const;
|
||||||
|
|
||||||
|
virtual QString selectedText() const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
virtual void handleEditorConfigChange() = 0;
|
virtual void handleEditorConfigChange() = 0;
|
||||||
|
|
||||||
@ -226,8 +228,6 @@ namespace vnotex
|
|||||||
|
|
||||||
virtual void zoom(bool p_zoomIn) = 0;
|
virtual void zoom(bool p_zoomIn) = 0;
|
||||||
|
|
||||||
virtual QString selectedText() const;
|
|
||||||
|
|
||||||
void showZoomFactor(qreal p_factor);
|
void showZoomFactor(qreal p_factor);
|
||||||
|
|
||||||
void showZoomDelta(int p_delta);
|
void showZoomDelta(int p_delta);
|
||||||
|
@ -89,6 +89,7 @@ SOURCES += \
|
|||||||
$$PWD/tagexplorer.cpp \
|
$$PWD/tagexplorer.cpp \
|
||||||
$$PWD/tagpopup.cpp \
|
$$PWD/tagpopup.cpp \
|
||||||
$$PWD/tagviewer.cpp \
|
$$PWD/tagviewer.cpp \
|
||||||
|
$$PWD/terminalviewer.cpp \
|
||||||
$$PWD/textviewwindow.cpp \
|
$$PWD/textviewwindow.cpp \
|
||||||
$$PWD/toolbarhelper.cpp \
|
$$PWD/toolbarhelper.cpp \
|
||||||
$$PWD/treeview.cpp \
|
$$PWD/treeview.cpp \
|
||||||
@ -218,6 +219,7 @@ HEADERS += \
|
|||||||
$$PWD/tagexplorer.h \
|
$$PWD/tagexplorer.h \
|
||||||
$$PWD/tagpopup.h \
|
$$PWD/tagpopup.h \
|
||||||
$$PWD/tagviewer.h \
|
$$PWD/tagviewer.h \
|
||||||
|
$$PWD/terminalviewer.h \
|
||||||
$$PWD/textviewwindow.h \
|
$$PWD/textviewwindow.h \
|
||||||
$$PWD/textviewwindowhelper.h \
|
$$PWD/textviewwindowhelper.h \
|
||||||
$$PWD/toolbarhelper.h \
|
$$PWD/toolbarhelper.h \
|
||||||
|
29
tests/commonfull.pri
Normal file
29
tests/commonfull.pri
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
include($$PWD/common.pri)
|
||||||
|
|
||||||
|
QT += sql
|
||||||
|
|
||||||
|
SRC_FOLDER = $$PWD/../src
|
||||||
|
|
||||||
|
LIBS_FOLDER = $$PWD/../libs
|
||||||
|
|
||||||
|
INCLUDEPATH *= $$SRC_FOLDER
|
||||||
|
|
||||||
|
include($$LIBS_FOLDER/vtextedit/src/editor/editor_export.pri)
|
||||||
|
|
||||||
|
include($$LIBS_FOLDER/vtextedit/src/libs/syntax-highlighting/syntax-highlighting_export.pri)
|
||||||
|
|
||||||
|
include($$SRC_FOLDER/utils/utils.pri)
|
||||||
|
|
||||||
|
include($$SRC_FOLDER/export/export.pri)
|
||||||
|
|
||||||
|
include($$SRC_FOLDER/search/search.pri)
|
||||||
|
|
||||||
|
include($$SRC_FOLDER/snippet/snippet.pri)
|
||||||
|
|
||||||
|
include($$SRC_FOLDER/imagehost/imagehost.pri)
|
||||||
|
|
||||||
|
include($$SRC_FOLDER/task/task.pri)
|
||||||
|
|
||||||
|
include($$SRC_FOLDER/core/core.pri)
|
||||||
|
|
||||||
|
include($$SRC_FOLDER/widgets/widgets.pri)
|
@ -1,29 +1,8 @@
|
|||||||
include($$PWD/../../common.pri)
|
include($$PWD/../../commonfull.pri)
|
||||||
|
|
||||||
QT += sql
|
|
||||||
|
|
||||||
TARGET = test_notebook
|
TARGET = test_notebook
|
||||||
TEMPLATE = app
|
TEMPLATE = app
|
||||||
|
|
||||||
SRC_FOLDER = $$PWD/../../../src
|
|
||||||
CORE_FOLDER = $$SRC_FOLDER/core
|
|
||||||
|
|
||||||
INCLUDEPATH *= $$SRC_FOLDER
|
|
||||||
|
|
||||||
LIBS_FOLDER = $$PWD/../../../libs
|
|
||||||
|
|
||||||
include($$LIBS_FOLDER/vtextedit/src/editor/editor_export.pri)
|
|
||||||
|
|
||||||
include($$LIBS_FOLDER/vtextedit/src/libs/syntax-highlighting/syntax-highlighting_export.pri)
|
|
||||||
|
|
||||||
include($$CORE_FOLDER/core.pri)
|
|
||||||
include($$SRC_FOLDER/widgets/widgets.pri)
|
|
||||||
include($$SRC_FOLDER/utils/utils.pri)
|
|
||||||
include($$SRC_FOLDER/export/export.pri)
|
|
||||||
include($$SRC_FOLDER/search/search.pri)
|
|
||||||
include($$SRC_FOLDER/snippet/snippet.pri)
|
|
||||||
include($$SRC_FOLDER/imagehost/imagehost.pri)
|
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
dummynode.cpp \
|
dummynode.cpp \
|
||||||
dummynotebook.cpp \
|
dummynotebook.cpp \
|
||||||
|
77
tests/test_task/test_task.cpp
Normal file
77
tests/test_task/test_task.cpp
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#include "test_task.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QProcessEnvironment>
|
||||||
|
|
||||||
|
#include <task/taskvariablemgr.h>
|
||||||
|
#include <task/task.h>
|
||||||
|
#include <core/configmgr.h>
|
||||||
|
#include <core/coreconfig.h>
|
||||||
|
#include <core/sessionconfig.h>
|
||||||
|
|
||||||
|
using namespace tests;
|
||||||
|
|
||||||
|
using namespace vnotex;
|
||||||
|
|
||||||
|
TestTask::TestTask(QObject *p_parent)
|
||||||
|
: QObject(p_parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestTask::initTestCase()
|
||||||
|
{
|
||||||
|
ConfigMgr::initForUnitTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestTask::TestTaskVariableMgr()
|
||||||
|
{
|
||||||
|
TaskVariableMgr mgr(nullptr);
|
||||||
|
mgr.init();
|
||||||
|
|
||||||
|
mgr.overrideVariable("notebookFolder", [](const Task *, const QString &val) {
|
||||||
|
Q_ASSERT(val.isEmpty());
|
||||||
|
return "/home/vnotex/vnote";
|
||||||
|
});
|
||||||
|
|
||||||
|
mgr.overrideVariable("notebookFolderName", [](const Task *, const QString &val) {
|
||||||
|
Q_ASSERT(val.isEmpty());
|
||||||
|
return "vnote";
|
||||||
|
});
|
||||||
|
|
||||||
|
mgr.overrideVariable("magic", [](const Task *, const QString &val) {
|
||||||
|
if (val.isEmpty()) {
|
||||||
|
return QString();
|
||||||
|
} else {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
auto task = createTask();
|
||||||
|
|
||||||
|
auto result = mgr.evaluate(task.data(), "start ${notebookFolder} end");
|
||||||
|
QCOMPARE(result, "start /home/vnotex/vnote end");
|
||||||
|
|
||||||
|
result = mgr.evaluate(task.data(), "start ${notebookFolder} mid ${notebookFolderName} end");
|
||||||
|
QCOMPARE(result, "start /home/vnotex/vnote mid vnote end");
|
||||||
|
|
||||||
|
result = mgr.evaluate(task.data(), "${magic:yyyy} ${magic:MM} ${magic:dd}");
|
||||||
|
QCOMPARE("yyyy MM dd", result);
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto env = QProcessEnvironment::systemEnvironment();
|
||||||
|
result = mgr.evaluate(task.data(), "${env:PATH} ${env:QT_PATH} ${env:nonexist}");
|
||||||
|
QCOMPARE(result, QString("%1 %2 %3").arg(env.value("PATH"), env.value("QT_PATH"), env.value("nonexist")));
|
||||||
|
}
|
||||||
|
|
||||||
|
result = mgr.evaluate(task.data(), "${config:main.core.toolbar_icon_size} ${config:main.core.nonexists} ${config:session.core.system_title_bar}");
|
||||||
|
QCOMPARE(result, QString("%1 %2").arg(ConfigMgr::getInst().getCoreConfig().getToolBarIconSize())
|
||||||
|
.arg(ConfigMgr::getInst().getSessionConfig().getSystemTitleBarEnabled()));
|
||||||
|
}
|
||||||
|
|
||||||
|
QSharedPointer<vnotex::Task> TestTask::createTask() const
|
||||||
|
{
|
||||||
|
return QSharedPointer<Task>(new Task("en_US", "dummy_file", nullptr, nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
QTEST_MAIN(tests::TestTask)
|
||||||
|
|
31
tests/test_task/test_task.h
Normal file
31
tests/test_task/test_task.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#ifndef TESTS_TASK_TEST_TASK_H
|
||||||
|
#define TESTS_TASK_TEST_TASK_H
|
||||||
|
|
||||||
|
#include <QtTest>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
|
namespace vnotex
|
||||||
|
{
|
||||||
|
class Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace tests
|
||||||
|
{
|
||||||
|
class TestTask : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit TestTask(QObject *p_parent = nullptr);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void initTestCase();
|
||||||
|
|
||||||
|
// Define test cases here per slot.
|
||||||
|
void TestTaskVariableMgr();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSharedPointer<vnotex::Task> createTask() const;
|
||||||
|
};
|
||||||
|
} // ns tests
|
||||||
|
|
||||||
|
#endif // TESTS_UTILS_TEST_UTILS_H
|
10
tests/test_task/test_task.pro
Normal file
10
tests/test_task/test_task.pro
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
include($$PWD/../commonfull.pri)
|
||||||
|
|
||||||
|
TARGET = test_task
|
||||||
|
TEMPLATE = app
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
test_task.cpp
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
test_task.h
|
@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QTemporaryDir>
|
#include <QTemporaryDir>
|
||||||
|
#include <QJsonArray>
|
||||||
|
|
||||||
|
#include <utils/utils.h>
|
||||||
#include <utils/pathutils.h>
|
#include <utils/pathutils.h>
|
||||||
#include <utils/fileutils.h>
|
#include <utils/fileutils.h>
|
||||||
|
|
||||||
@ -10,6 +12,68 @@ using namespace tests;
|
|||||||
|
|
||||||
using namespace vnotex;
|
using namespace vnotex;
|
||||||
|
|
||||||
|
TestUtils::TestUtils(QObject *p_parent)
|
||||||
|
: QObject(p_parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestUtils::initTestCase()
|
||||||
|
{
|
||||||
|
m_obj["a"] = "a";
|
||||||
|
|
||||||
|
{
|
||||||
|
QJsonObject objb;
|
||||||
|
objb["a"] = "ba";
|
||||||
|
objb["b"] = 2;
|
||||||
|
|
||||||
|
{
|
||||||
|
QJsonObject objbc;
|
||||||
|
objbc["a"] = "bca";
|
||||||
|
|
||||||
|
objb["c"] = objbc;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_obj["b"] = objb;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
QJsonObject objc;
|
||||||
|
objc["a"] = "ca";
|
||||||
|
|
||||||
|
QJsonArray arr;
|
||||||
|
arr.append("cb0");
|
||||||
|
arr.append("cb1");
|
||||||
|
arr.append("cb2");
|
||||||
|
objc["b"] = arr;
|
||||||
|
|
||||||
|
m_obj["c"] = objc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestUtils::testParseAndReadJson_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>("exp");
|
||||||
|
QTest::addColumn<QJsonValue>("result");
|
||||||
|
|
||||||
|
QTest::newRow("empty") << "" << QJsonValue(m_obj);
|
||||||
|
QTest::newRow("a") << "a" << QJsonValue("a");
|
||||||
|
QTest::newRow("ba") << "b.a" << QJsonValue("ba");
|
||||||
|
QTest::newRow("bb") << "b.b" << QJsonValue(2);
|
||||||
|
QTest::newRow("bca") << "b.c.a" << QJsonValue("bca");
|
||||||
|
QTest::newRow("ca") << "c.a" << QJsonValue("ca");
|
||||||
|
QTest::newRow("cb0") << "c.b[0]" << QJsonValue("cb0");
|
||||||
|
QTest::newRow("cb1") << "c.b[1]" << QJsonValue("cb1");
|
||||||
|
QTest::newRow("cb2") << "c.b[2]" << QJsonValue("cb2");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestUtils::testParseAndReadJson()
|
||||||
|
{
|
||||||
|
QFETCH(QString, exp);
|
||||||
|
QFETCH(QJsonValue, result);
|
||||||
|
|
||||||
|
QCOMPARE(Utils::parseAndReadJson(m_obj, exp), result);
|
||||||
|
}
|
||||||
|
|
||||||
void TestUtils::testParentDirPath_data()
|
void TestUtils::testParentDirPath_data()
|
||||||
{
|
{
|
||||||
QTest::addColumn<QString>("path");
|
QTest::addColumn<QString>("path");
|
||||||
|
@ -2,16 +2,25 @@
|
|||||||
#define TESTS_UTILS_TEST_UTILS_H
|
#define TESTS_UTILS_TEST_UTILS_H
|
||||||
|
|
||||||
#include <QtTest>
|
#include <QtTest>
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
namespace tests
|
namespace tests
|
||||||
{
|
{
|
||||||
class TestUtils : public QObject
|
class TestUtils : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit TestUtils(QObject *p_parent = nullptr);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
void initTestCase();
|
||||||
|
|
||||||
// Define test cases here per slot.
|
// Define test cases here per slot.
|
||||||
|
|
||||||
|
// Utils tests.
|
||||||
|
void testParseAndReadJson_data();
|
||||||
|
void testParseAndReadJson();
|
||||||
|
|
||||||
// PathUtils Tests.
|
// PathUtils Tests.
|
||||||
void testParentDirPath_data();
|
void testParentDirPath_data();
|
||||||
void testParentDirPath();
|
void testParentDirPath();
|
||||||
@ -32,6 +41,9 @@ namespace tests
|
|||||||
void testRenameFile();
|
void testRenameFile();
|
||||||
|
|
||||||
void testIsText();
|
void testIsText();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QJsonObject m_obj;
|
||||||
};
|
};
|
||||||
} // ns tests
|
} // ns tests
|
||||||
|
|
||||||
|
@ -10,10 +10,12 @@ INCLUDEPATH *= $$SRC_FOLDER
|
|||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
test_utils.cpp \
|
test_utils.cpp \
|
||||||
|
$$UTILS_FOLDER/utils.cpp \
|
||||||
$$UTILS_FOLDER/pathutils.cpp \
|
$$UTILS_FOLDER/pathutils.cpp \
|
||||||
$$UTILS_FOLDER/fileutils.cpp
|
$$UTILS_FOLDER/fileutils.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
test_utils.h \
|
test_utils.h \
|
||||||
|
$$UTILS_FOLDER/utils.h \
|
||||||
$$UTILS_FOLDER/pathutils.h \
|
$$UTILS_FOLDER/pathutils.h \
|
||||||
$$UTILS_FOLDER/fileutils.h
|
$$UTILS_FOLDER/fileutils.h
|
||||||
|
@ -2,4 +2,5 @@ TEMPLATE = subdirs
|
|||||||
|
|
||||||
SUBDIRS = \
|
SUBDIRS = \
|
||||||
test_utils \
|
test_utils \
|
||||||
test_core
|
test_core \
|
||||||
|
test_task
|
||||||
|
Loading…
x
Reference in New Issue
Block a user