mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-04 21:39:52 +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_userFilesFolder = "user_files";
|
||||
|
||||
const QString ConfigMgr::c_appFilesFolder = "vnotex_files";
|
||||
|
||||
const QJsonObject &ConfigMgr::Settings::getJson() const
|
||||
{
|
||||
return m_jobj;
|
||||
@ -68,17 +72,21 @@ ConfigMgr::ConfigMgr(bool p_isUnitTest, QObject *p_parent)
|
||||
qWarning() << "failed to init ConfigMgr for UnitTest";
|
||||
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));
|
||||
} else {
|
||||
locateConfigFolder();
|
||||
QDir dir(m_dirForUnitTest->path());
|
||||
dir.mkdir(c_appFilesFolder);
|
||||
dir.mkdir(c_userFilesFolder);
|
||||
|
||||
bool needUpdate = checkAppConfig();
|
||||
if (needUpdate) {
|
||||
checkUserConfig();
|
||||
}
|
||||
m_appConfigFolderPath = m_dirForUnitTest->filePath(c_appFilesFolder);
|
||||
m_userConfigFolderPath = m_dirForUnitTest->filePath(c_userFilesFolder);
|
||||
return;
|
||||
}
|
||||
|
||||
locateConfigFolder();
|
||||
|
||||
bool needUpdate = checkAppConfig();
|
||||
if (needUpdate) {
|
||||
checkUserConfig();
|
||||
}
|
||||
|
||||
m_config->init();
|
||||
@ -106,8 +114,7 @@ void ConfigMgr::locateConfigFolder()
|
||||
qInfo() << "app folder" << appDirPath;
|
||||
// Check app config.
|
||||
{
|
||||
const QString configFolderName("vnotex_files");
|
||||
QString folderPath(appDirPath + '/' + configFolderName);
|
||||
QString folderPath(appDirPath + '/' + c_appFilesFolder);
|
||||
if (QDir(folderPath).exists()) {
|
||||
// Config folder in app/.
|
||||
m_appConfigFolderPath = PathUtils::cleanPath(folderPath);
|
||||
@ -118,8 +125,7 @@ void ConfigMgr::locateConfigFolder()
|
||||
|
||||
// Check user config.
|
||||
{
|
||||
const QString configFolderName("user_files");
|
||||
QString folderPath(appDirPath + '/' + configFolderName);
|
||||
QString folderPath(appDirPath + '/' + c_userFilesFolder);
|
||||
if (QDir(folderPath).exists()) {
|
||||
// Config folder in app/.
|
||||
m_userConfigFolderPath = PathUtils::cleanPath(folderPath);
|
||||
@ -564,3 +570,14 @@ QString ConfigMgr::getApplicationVersion()
|
||||
|
||||
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;
|
||||
|
||||
// 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.
|
||||
static QString locateSessionConfigFilePathAtBootstrap();
|
||||
|
||||
@ -168,6 +171,10 @@ namespace vnotex
|
||||
|
||||
// Name of the session config file.
|
||||
static const QString c_sessionFileName;
|
||||
|
||||
static const QString c_userFilesFolder;
|
||||
|
||||
static const QString c_appFilesFolder;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
|
@ -29,11 +29,6 @@ SOURCES += \
|
||||
$$PWD/texteditorconfig.cpp \
|
||||
$$PWD/vnotex.cpp \
|
||||
$$PWD/thememgr.cpp \
|
||||
$$PWD/task.cpp \
|
||||
$$PWD/taskhelper.cpp \
|
||||
$$PWD/taskmgr.cpp \
|
||||
$$PWD/taskvariablemgr.cpp \
|
||||
$$PWD/shellexecution.cpp \
|
||||
$$PWD/notebookmgr.cpp \
|
||||
$$PWD/theme.cpp \
|
||||
$$PWD/sessionconfig.cpp \
|
||||
@ -65,11 +60,6 @@ HEADERS += \
|
||||
$$PWD/texteditorconfig.h \
|
||||
$$PWD/vnotex.h \
|
||||
$$PWD/thememgr.h \
|
||||
$$PWD/task.h \
|
||||
$$PWD/taskhelper.h \
|
||||
$$PWD/taskmgr.h \
|
||||
$$PWD/taskvariablemgr.h \
|
||||
$$PWD/shellexecution.h \
|
||||
$$PWD/global.h \
|
||||
$$PWD/namebasedserver.h \
|
||||
$$PWD/exception.h \
|
||||
|
@ -150,7 +150,11 @@ QJsonObject EditorConfig::toJson() const
|
||||
obj[m_markdownEditorConfig->getSessionName()] = m_markdownEditorConfig->toJson();
|
||||
obj[QStringLiteral("core")] = saveCore();
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -265,6 +265,11 @@ ID NotebookMgr::getCurrentNotebookId() const
|
||||
return m_currentNotebookId;
|
||||
}
|
||||
|
||||
QSharedPointer<Notebook> NotebookMgr::getCurrentNotebook() const
|
||||
{
|
||||
return findNotebookById(m_currentNotebookId);
|
||||
}
|
||||
|
||||
void NotebookMgr::setCurrentNotebook(ID p_notebookId)
|
||||
{
|
||||
auto lastId = m_currentNotebookId;
|
||||
|
@ -62,6 +62,8 @@ namespace vnotex
|
||||
|
||||
ID getCurrentNotebookId() const;
|
||||
|
||||
QSharedPointer<Notebook> getCurrentNotebook() const;
|
||||
|
||||
// Find the notebook with the same directory as root folder.
|
||||
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 <utils/docsutils.h>
|
||||
#include <task/taskmgr.h>
|
||||
|
||||
|
||||
using namespace vnotex;
|
||||
@ -63,6 +64,8 @@ void VNoteX::initTaskMgr()
|
||||
{
|
||||
Q_ASSERT(!m_taskMgr);
|
||||
m_taskMgr = new TaskMgr(this);
|
||||
connect(m_taskMgr, &TaskMgr::taskOutputRequested,
|
||||
this, &VNoteX::showOutputRequested);
|
||||
}
|
||||
|
||||
ThemeMgr &VNoteX::getThemeMgr() const
|
||||
|
@ -6,7 +6,6 @@
|
||||
|
||||
#include "noncopyable.h"
|
||||
#include "thememgr.h"
|
||||
#include "taskmgr.h"
|
||||
#include "global.h"
|
||||
|
||||
namespace vnotex
|
||||
@ -19,6 +18,7 @@ namespace vnotex
|
||||
class Event;
|
||||
class Notebook;
|
||||
struct ComplexLocation;
|
||||
class TaskMgr;
|
||||
|
||||
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",
|
||||
"type": "promptString",
|
||||
"description": {
|
||||
"en_US": "Please provide a commit message",
|
||||
"en_US": "Please input the commit message",
|
||||
"zh_CN": "请输入提交信息",
|
||||
"ja_JP": "コミットメッセージを提供してください"
|
||||
},
|
||||
"default": {
|
||||
"en_US": "Update note on ${magic:datetime}",
|
||||
"en_US": "Update note at ${magic:datetime}",
|
||||
"zh_CN": "更新笔记于 ${magic:datetime}",
|
||||
"ja_JP": "アップデート ${magic:datetime}"
|
||||
}
|
||||
@ -77,4 +77,4 @@
|
||||
"command": "git log -10 --graph --pretty=format:'%h -%d %s (%cr) <%an>' --abbrev-commit"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -192,13 +192,15 @@ bool WebViewExporter::writeHtmlFile(const QString &p_file,
|
||||
bool p_embedImages)
|
||||
{
|
||||
const auto baseName = QFileInfo(p_file).completeBaseName();
|
||||
auto title = QString("%1 - %2").arg(baseName, ConfigMgr::c_appName);
|
||||
|
||||
const QString resourceFolderName = baseName + "_files";
|
||||
auto resourceFolder = PathUtils::concatenateFilePath(PathUtils::parentDirPath(p_file), resourceFolderName);
|
||||
|
||||
qDebug() << "HTML files folder" << resourceFolder;
|
||||
|
||||
auto htmlContent = m_exportHtmlTemplate;
|
||||
|
||||
const auto title = QString("%1").arg(baseName);
|
||||
HtmlTemplateHelper::fillTitle(htmlContent, title);
|
||||
|
||||
if (!p_styleContent.isEmpty() && p_embedStyles) {
|
||||
|
@ -20,7 +20,9 @@
|
||||
|
||||
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()
|
||||
{
|
||||
@ -231,10 +233,11 @@ void SnippetMgr::applySnippet(const QString &p_name,
|
||||
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;
|
||||
return applySnippetBySymbol(p_content, QString(), offset);
|
||||
return applySnippetBySymbol(p_content, QString(), offset, p_overrides);
|
||||
}
|
||||
|
||||
QString SnippetMgr::applySnippetBySymbol(const QString &p_content,
|
||||
@ -244,11 +247,11 @@ QString SnippetMgr::applySnippetBySymbol(const QString &p_content,
|
||||
{
|
||||
QString content(p_content);
|
||||
|
||||
int maxTimes = 100;
|
||||
int maxTimesAtSamePos = 100;
|
||||
|
||||
QRegularExpression regExp(c_snippetSymbolRegExp);
|
||||
int pos = 0;
|
||||
while (pos < content.size() && maxTimes-- > 0) {
|
||||
while (pos < content.size()) {
|
||||
QRegularExpressionMatch match;
|
||||
int idx = content.indexOf(regExp, pos, &match);
|
||||
if (idx == -1) {
|
||||
@ -288,6 +291,13 @@ QString SnippetMgr::applySnippetBySymbol(const QString &p_content,
|
||||
}
|
||||
|
||||
// @afterText may still contains snippet symbol.
|
||||
if (pos == idx) {
|
||||
if (--maxTimesAtSamePos == 0) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
maxTimesAtSamePos = 100;
|
||||
}
|
||||
pos = idx;
|
||||
}
|
||||
|
||||
@ -426,8 +436,10 @@ void SnippetMgr::addDynamicSnippet(QVector<QSharedPointer<Snippet>> &p_snippets,
|
||||
SnippetMgr::OverrideMap SnippetMgr::generateOverrides(const Buffer *p_buffer)
|
||||
{
|
||||
OverrideMap overrides;
|
||||
overrides.insert(QStringLiteral("note"), p_buffer->getName());
|
||||
overrides.insert(QStringLiteral("no"), QFileInfo(p_buffer->getName()).completeBaseName());
|
||||
if (p_buffer) {
|
||||
overrides.insert(QStringLiteral("note"), p_buffer->getName());
|
||||
overrides.insert(QStringLiteral("no"), QFileInfo(p_buffer->getName()).completeBaseName());
|
||||
}
|
||||
return overrides;
|
||||
}
|
||||
|
||||
@ -438,3 +450,8 @@ SnippetMgr::OverrideMap SnippetMgr::generateOverrides(const QString &p_fileName)
|
||||
overrides.insert(QStringLiteral("no"), QFileInfo(p_fileName).completeBaseName());
|
||||
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,
|
||||
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.
|
||||
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 QString generateSnippetSymbol(const QString &p_snippetName);
|
||||
|
||||
static const QChar c_snippetSymbolGuard;
|
||||
|
||||
// %name%.
|
||||
// Captured texts:
|
||||
// 1 - The name of the snippet.
|
||||
|
@ -56,6 +56,8 @@ include($$PWD/snippet/snippet.pri)
|
||||
|
||||
include($$PWD/imagehost/imagehost.pri)
|
||||
|
||||
include($$PWD/task/task.pri)
|
||||
|
||||
include($$PWD/core/core.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,
|
||||
QDir::Filters p_filters)
|
||||
{
|
||||
QStringList entries;
|
||||
|
||||
QDir dir(p_dirPath);
|
||||
if (dir.isEmpty()) return {};
|
||||
QStringList entrys;
|
||||
const auto curEntrys = dir.entryList(p_nameFilters, p_filters | QDir::NoDotAndDotDot);
|
||||
for (const auto &e : curEntrys) {
|
||||
entrys.append(PathUtils::concatenateFilePath(p_dirPath, e));
|
||||
if (!dir.exists()) {
|
||||
return entries;
|
||||
}
|
||||
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) {
|
||||
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.
|
||||
static QStringList entryListRecursively(const QString &p_dirPath,
|
||||
const QStringList &p_nameFilters,
|
||||
QDir::Filters p_filters=QDir::NoFilter);
|
||||
QDir::Filters p_filters = QDir::NoFilter);
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
|
@ -16,7 +16,6 @@ QString PathUtils::parentDirPath(const QString &p_path)
|
||||
}
|
||||
|
||||
QFileInfo info(p_path);
|
||||
Q_ASSERT(info.isAbsolute());
|
||||
return cleanPath(info.absolutePath());
|
||||
}
|
||||
|
||||
|
@ -4,13 +4,15 @@
|
||||
#include <QDir>
|
||||
#include <QKeySequence>
|
||||
#include <QWidget>
|
||||
#include <QVariant>
|
||||
#include <QFontDatabase>
|
||||
#include <QRegularExpression>
|
||||
#include <QRegularExpressionMatch>
|
||||
#include <QSvgRenderer>
|
||||
#include <QPainter>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QDebug>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
@ -139,3 +141,54 @@ QJsonObject Utils::fromJsonString(const QByteArray &p_data)
|
||||
{
|
||||
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 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
|
||||
|
||||
|
@ -13,13 +13,8 @@ using namespace vnotex;
|
||||
const QChar SelectDialog::c_cancelShortcut = QLatin1Char('z');
|
||||
|
||||
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,
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "snippetpanel.h"
|
||||
#include "historypanel.h"
|
||||
#include "tagexplorer.h"
|
||||
#include "terminalviewer.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
@ -113,7 +114,7 @@ void DockWidgetHelper::setupDocks()
|
||||
|
||||
setupOutlineDock();
|
||||
|
||||
setupOutputDock();
|
||||
setupTerminalDock();
|
||||
|
||||
setupLocationListDock();
|
||||
|
||||
@ -148,17 +149,17 @@ void DockWidgetHelper::setupOutlineDock()
|
||||
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->setAllowedAreas(Qt::BottomDockWidgetArea);
|
||||
dock->setObjectName(QStringLiteral("TerminalDock.vnotex"));
|
||||
dock->setAllowedAreas(Qt::AllDockWidgetAreas);
|
||||
|
||||
dock->setWidget(m_mainWindow->m_outputViewer);
|
||||
dock->setFocusProxy(m_mainWindow->m_outputViewer);
|
||||
dock->hide();
|
||||
dock->setWidget(m_mainWindow->m_terminalViewer);
|
||||
dock->setFocusProxy(m_mainWindow->m_terminalViewer);
|
||||
m_mainWindow->addDockWidget(Qt::BottomDockWidgetArea, dock);
|
||||
dock->hide();
|
||||
}
|
||||
|
||||
void DockWidgetHelper::setupSearchDock()
|
||||
|
@ -29,7 +29,7 @@ namespace vnotex
|
||||
SearchDock,
|
||||
SnippetDock,
|
||||
OutlineDock,
|
||||
OutputDock,
|
||||
TerminalDock,
|
||||
LocationListDock,
|
||||
MaxDock
|
||||
};
|
||||
@ -98,7 +98,7 @@ namespace vnotex
|
||||
|
||||
void setupOutlineDock();
|
||||
|
||||
void setupOutputDock();
|
||||
void setupTerminalDock();
|
||||
|
||||
void setupSearchDock();
|
||||
|
||||
|
@ -230,10 +230,4 @@ void FramelessMainWindowLinux::showEvent(QShowEvent *p_event)
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
using namespace vnotex;
|
||||
|
||||
FramelessMainWindowLinuxDummy::FramelessMainWindowLinuxDummy()
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
@ -65,7 +65,7 @@ namespace vnotex
|
||||
class FramelessMainWindowLinuxDummy
|
||||
{
|
||||
public:
|
||||
FramelessMainWindowLinuxDummy();
|
||||
FramelessMainWindowLinuxDummy() = default;
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
@ -221,10 +221,4 @@ void FramelessMainWindowWin::forceRedraw()
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
using namespace vnotex;
|
||||
|
||||
FramelessMainWindowWinDummy::FramelessMainWindowWinDummy()
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
@ -41,7 +41,7 @@ namespace vnotex
|
||||
class FramelessMainWindowWinDummy
|
||||
{
|
||||
public:
|
||||
FramelessMainWindowWinDummy();
|
||||
FramelessMainWindowWinDummy() = default;
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
@ -56,6 +56,7 @@
|
||||
#include "tagexplorer.h"
|
||||
#include "toolbarhelper.h"
|
||||
#include "statusbarhelper.h"
|
||||
#include "terminalviewer.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
@ -257,7 +258,7 @@ void MainWindow::setupDocks()
|
||||
|
||||
setupOutlineViewer();
|
||||
|
||||
setupOutputViewer();
|
||||
setupTerminalViewer();
|
||||
|
||||
setupHistoryPanel();
|
||||
|
||||
@ -514,23 +515,15 @@ void MainWindow::setupOutlineViewer()
|
||||
this, &MainWindow::focusViewArea);
|
||||
}
|
||||
|
||||
void MainWindow::setupOutputViewer()
|
||||
void MainWindow::setupTerminalViewer()
|
||||
{
|
||||
m_outputViewer = new QTextEdit(this);
|
||||
m_outputViewer->setObjectName("OutputViewer.vnotex");
|
||||
m_outputViewer->setReadOnly(true);
|
||||
m_terminalViewer = new TerminalViewer(this);
|
||||
m_terminalViewer->setObjectName("TerminalViewer.vnotex");
|
||||
|
||||
connect(&VNoteX::getInst(), &VNoteX::showOutputRequested,
|
||||
m_outputViewer, [this](const QString &p_text) {
|
||||
auto cursor = m_outputViewer->textCursor();
|
||||
cursor.movePosition(QTextCursor::End);
|
||||
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();
|
||||
this, [this](const QString &p_text) {
|
||||
m_terminalViewer->append(p_text);
|
||||
m_dockWidgetHelper.activateDock(DockWidgetHelper::TerminalDock);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,7 @@ namespace vnotex
|
||||
class SnippetPanel;
|
||||
class HistoryPanel;
|
||||
class ExportDialog;
|
||||
class TerminalViewer;
|
||||
|
||||
enum { RESTART_EXIT_CODE = 1000 };
|
||||
|
||||
@ -106,7 +107,7 @@ namespace vnotex
|
||||
|
||||
void setupOutlineViewer();
|
||||
|
||||
void setupOutputViewer();
|
||||
void setupTerminalViewer();
|
||||
|
||||
void setupSearchPanel();
|
||||
|
||||
@ -167,7 +168,7 @@ namespace vnotex
|
||||
|
||||
OutlineViewer *m_outlineViewer = nullptr;
|
||||
|
||||
QTextEdit *m_outputViewer = nullptr;
|
||||
TerminalViewer *m_terminalViewer = nullptr;
|
||||
|
||||
LocationList *m_locationList = nullptr;
|
||||
|
||||
|
@ -838,7 +838,7 @@ QAction *NotebookNodeExplorer::createAction(Action p_act, QObject *p_parent)
|
||||
break;
|
||||
|
||||
case Action::OpenLocation:
|
||||
act = new QAction(tr("Open &Location"), p_parent);
|
||||
act = new QAction(tr("Open Locat&ion"), p_parent);
|
||||
connect(act, &QAction::triggered,
|
||||
this, [this]() {
|
||||
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/htmltemplatehelper.h>
|
||||
#include <core/exception.h>
|
||||
#include <task/taskmgr.h>
|
||||
#include "propertydefs.h"
|
||||
#include "dialogs/settings/settingsdialog.h"
|
||||
#include "dialogs/updater.h"
|
||||
@ -286,52 +287,80 @@ QToolBar *ToolBarHelper::setupQuickAccessToolBar(MainWindow *p_win, QToolBar *p_
|
||||
void ToolBarHelper::setupTaskMenu(QMenu *p_menu)
|
||||
{
|
||||
p_menu->clear();
|
||||
|
||||
setupTaskActionMenu(p_menu);
|
||||
|
||||
p_menu->addSeparator();
|
||||
|
||||
const auto &taskMgr = VNoteX::getInst().getTaskMgr();
|
||||
for (auto task : taskMgr.getAppTasks()) {
|
||||
addTaskMenu(p_menu, task);
|
||||
for (const auto &task : taskMgr.getAppTasks()) {
|
||||
addTaskMenu(p_menu, task.data());
|
||||
}
|
||||
|
||||
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();
|
||||
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)
|
||||
{
|
||||
MainWindow::connect(p_task, &Task::showOutput,
|
||||
&VNoteX::getInst(), &VNoteX::showOutputRequested);
|
||||
QAction *action = nullptr;
|
||||
const auto &tasks = p_task->getTasks();
|
||||
|
||||
const auto &children = p_task->getChildren();
|
||||
|
||||
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;
|
||||
try {
|
||||
auto taskIcon = p_task->getIcon();
|
||||
if (!taskIcon.isEmpty()) {
|
||||
icon = generateIcon(p_task->getIcon());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} catch (Exception &e) {
|
||||
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->setData(reinterpret_cast<qulonglong>(p_task));
|
||||
|
||||
WidgetUtils::addActionShortcut(action, p_task->getShortcut());
|
||||
MainWindow::connect(action, &QAction::triggered,
|
||||
p_task, &Task::run);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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]() {
|
||||
setupTaskMenu(taskMenu);
|
||||
});
|
||||
btn->setMenu(taskMenu);
|
||||
|
||||
return tb;
|
||||
}
|
||||
|
||||
@ -685,6 +722,12 @@ void ToolBarHelper::setupMenuButton(MainWindow *p_win, QToolBar *p_toolBar)
|
||||
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,
|
||||
[]() {
|
||||
|
@ -36,6 +36,8 @@ namespace vnotex
|
||||
|
||||
static void setupTaskMenu(QMenu *p_menu);
|
||||
|
||||
static void setupTaskActionMenu(QMenu *p_menu);
|
||||
|
||||
static void addTaskMenu(QMenu *p_menu, Task *p_task);
|
||||
|
||||
static QToolBar *setupTaskToolBar(MainWindow *p_win, QToolBar *p_toolBar);
|
||||
|
@ -97,6 +97,8 @@ namespace vnotex
|
||||
|
||||
bool isSessionEnabled() const;
|
||||
|
||||
virtual QString selectedText() const;
|
||||
|
||||
public slots:
|
||||
virtual void handleEditorConfigChange() = 0;
|
||||
|
||||
@ -226,8 +228,6 @@ namespace vnotex
|
||||
|
||||
virtual void zoom(bool p_zoomIn) = 0;
|
||||
|
||||
virtual QString selectedText() const;
|
||||
|
||||
void showZoomFactor(qreal p_factor);
|
||||
|
||||
void showZoomDelta(int p_delta);
|
||||
|
@ -89,6 +89,7 @@ SOURCES += \
|
||||
$$PWD/tagexplorer.cpp \
|
||||
$$PWD/tagpopup.cpp \
|
||||
$$PWD/tagviewer.cpp \
|
||||
$$PWD/terminalviewer.cpp \
|
||||
$$PWD/textviewwindow.cpp \
|
||||
$$PWD/toolbarhelper.cpp \
|
||||
$$PWD/treeview.cpp \
|
||||
@ -218,6 +219,7 @@ HEADERS += \
|
||||
$$PWD/tagexplorer.h \
|
||||
$$PWD/tagpopup.h \
|
||||
$$PWD/tagviewer.h \
|
||||
$$PWD/terminalviewer.h \
|
||||
$$PWD/textviewwindow.h \
|
||||
$$PWD/textviewwindowhelper.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)
|
||||
|
||||
QT += sql
|
||||
include($$PWD/../../commonfull.pri)
|
||||
|
||||
TARGET = test_notebook
|
||||
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 += \
|
||||
dummynode.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 <QTemporaryDir>
|
||||
#include <QJsonArray>
|
||||
|
||||
#include <utils/utils.h>
|
||||
#include <utils/pathutils.h>
|
||||
#include <utils/fileutils.h>
|
||||
|
||||
@ -10,6 +12,68 @@ using namespace tests;
|
||||
|
||||
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()
|
||||
{
|
||||
QTest::addColumn<QString>("path");
|
||||
|
@ -2,16 +2,25 @@
|
||||
#define TESTS_UTILS_TEST_UTILS_H
|
||||
|
||||
#include <QtTest>
|
||||
#include <QJsonObject>
|
||||
|
||||
namespace tests
|
||||
{
|
||||
class TestUtils : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TestUtils(QObject *p_parent = nullptr);
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
|
||||
// Define test cases here per slot.
|
||||
|
||||
// Utils tests.
|
||||
void testParseAndReadJson_data();
|
||||
void testParseAndReadJson();
|
||||
|
||||
// PathUtils Tests.
|
||||
void testParentDirPath_data();
|
||||
void testParentDirPath();
|
||||
@ -32,6 +41,9 @@ namespace tests
|
||||
void testRenameFile();
|
||||
|
||||
void testIsText();
|
||||
|
||||
private:
|
||||
QJsonObject m_obj;
|
||||
};
|
||||
} // ns tests
|
||||
|
||||
|
@ -10,10 +10,12 @@ INCLUDEPATH *= $$SRC_FOLDER
|
||||
|
||||
SOURCES += \
|
||||
test_utils.cpp \
|
||||
$$UTILS_FOLDER/utils.cpp \
|
||||
$$UTILS_FOLDER/pathutils.cpp \
|
||||
$$UTILS_FOLDER/fileutils.cpp
|
||||
|
||||
HEADERS += \
|
||||
test_utils.h \
|
||||
$$UTILS_FOLDER/utils.h \
|
||||
$$UTILS_FOLDER/pathutils.h \
|
||||
$$UTILS_FOLDER/fileutils.h
|
||||
|
@ -2,4 +2,5 @@ TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS = \
|
||||
test_utils \
|
||||
test_core
|
||||
test_core \
|
||||
test_task
|
||||
|
Loading…
x
Reference in New Issue
Block a user