diff --git a/QtClient/JoplinQtClient/JoplinQtClient.pro b/QtClient/JoplinQtClient/JoplinQtClient.pro
index 327a7be6e..882d9e70f 100755
--- a/QtClient/JoplinQtClient/JoplinQtClient.pro
+++ b/QtClient/JoplinQtClient/JoplinQtClient.pro
@@ -1,7 +1,29 @@
+# To enable CLI or GUI, add either of these:
+# "JOP_FRONT_END_CLI=1"
+# "JOP_FRONT_END_GUI=1"
+# to the qmake command. So that it looks like this:
+# qmake JoplinQtClient.pro -spec linux-g++ CONFIG+=debug CONFIG+=qml_debug "JOP_FRONT_END_CLI=1" && /usr/bin/make qmake_all
+
QT += qml quick sql quickcontrols2 network
CONFIG += c++11
+defined(JOP_FRONT_END_CLI, var) {
+ message(Building CLI client)
+ DEFINES += "JOP_FRONT_END_CLI=$$JOP_FRONT_END_CLI"
+}
+
+defined(JOP_FRONT_END_GUI, var) {
+ message(Building GUI client)
+ DEFINES += "JOP_FRONT_END_GUI=$$JOP_FRONT_END_GUI"
+}
+
+defined(JOP_FRONT_END_CLI, var) {
+ QT -= gui
+ CONFIG += console
+ CONFIG -= app_bundle
+}
+
SOURCES += \
main.cpp \
models/item.cpp \
@@ -10,7 +32,6 @@ SOURCES += \
models/foldermodel.cpp \
models/notemodel.cpp \
models/note.cpp \
- application.cpp \
models/qmlnote.cpp \
webapi.cpp \
synchronizer.cpp \
@@ -23,7 +44,9 @@ SOURCES += \
paths.cpp \
window.cpp \
filters.cpp \
- models/abstractlistmodel.cpp
+ models/abstractlistmodel.cpp \
+ cliapplication.cpp \
+ command.cpp
RESOURCES += qml.qrc \
database.qrc
@@ -44,7 +67,6 @@ HEADERS += \
models/foldermodel.h \
models/notemodel.h \
models/note.h \
- application.h \
sparsevector.hpp \
models/qmlnote.h \
webapi.h \
@@ -61,7 +83,14 @@ HEADERS += \
constants.h \
window.h \
filters.h \
- models/abstractlistmodel.h
+ models/abstractlistmodel.h \
+ cliapplication.h \
+ command.h
+
+defined(JOP_FRONT_END_GUI, var) {
+ SOURCES += application.cpp
+ HEADERS += application.h
+}
DISTFILES += \
AndroidManifest.xml
diff --git a/QtClient/JoplinQtClient/JoplinQtClient.pro.user.13661a9 b/QtClient/JoplinQtClient/JoplinQtClient.pro.user.13661a9
new file mode 100644
index 000000000..0cc2f29be
--- /dev/null
+++ b/QtClient/JoplinQtClient/JoplinQtClient.pro.user.13661a9
@@ -0,0 +1,336 @@
+
+
+
+
+
+ EnvironmentId
+ {13661a96-7123-4040-a8b0-364538c1219d}
+
+
+ ProjectExplorer.Project.ActiveTarget
+ 0
+
+
+ ProjectExplorer.Project.EditorSettings
+
+ true
+ false
+ true
+
+ Cpp
+
+ CppGlobal
+
+
+
+ QmlJS
+
+ QmlJSGlobal
+
+
+ 2
+ UTF-8
+ false
+ 4
+ false
+ 80
+ true
+ true
+ 1
+ true
+ false
+ 0
+ true
+ true
+ 0
+ 8
+ true
+ 1
+ true
+ true
+ true
+ false
+
+
+
+ ProjectExplorer.Project.PluginSettings
+
+
+
+ ProjectExplorer.Project.Target.0
+
+ Desktop Qt 5.7.1 GCC 64bit
+ Desktop Qt 5.7.1 GCC 64bit
+ qt.57.gcc_64_kit
+ 0
+ 0
+ 0
+
+ /home/laurent/src/notes/QtClient/build-JoplinQtClient-Desktop_Qt_5_7_1_GCC_64bit-Debug
+
+
+ true
+ qmake
+
+ QtProjectManager.QMakeBuildStep
+ true
+ "JOP_FRONT_END_CLI=1"
+ false
+ false
+ false
+
+
+ true
+ Make
+
+ Qt4ProjectManager.MakeStep
+
+ -w
+ -r
+
+ false
+
+
+
+ 2
+ Build
+
+ ProjectExplorer.BuildSteps.Build
+
+
+
+ true
+ Make
+
+ Qt4ProjectManager.MakeStep
+
+ -w
+ -r
+
+ true
+ clean
+
+
+ 1
+ Clean
+
+ ProjectExplorer.BuildSteps.Clean
+
+ 2
+ false
+
+ Debug
+
+ Qt4ProjectManager.Qt4BuildConfiguration
+ 2
+ true
+
+
+ /home/laurent/src/notes/QtClient/build-JoplinQtClient-Desktop_Qt_5_7_1_GCC_64bit-Release
+
+
+ true
+ qmake
+
+ QtProjectManager.QMakeBuildStep
+ false
+
+ false
+ false
+ false
+
+
+ true
+ Make
+
+ Qt4ProjectManager.MakeStep
+
+ -w
+ -r
+
+ false
+
+
+
+ 2
+ Build
+
+ ProjectExplorer.BuildSteps.Build
+
+
+
+ true
+ Make
+
+ Qt4ProjectManager.MakeStep
+
+ -w
+ -r
+
+ true
+ clean
+
+
+ 1
+ Clean
+
+ ProjectExplorer.BuildSteps.Clean
+
+ 2
+ false
+
+ Release
+
+ Qt4ProjectManager.Qt4BuildConfiguration
+ 0
+ true
+
+
+ /home/laurent/src/notes/QtClient/build-JoplinQtClient-Desktop_Qt_5_7_1_GCC_64bit-Profile
+
+
+ true
+ qmake
+
+ QtProjectManager.QMakeBuildStep
+ true
+
+ false
+ true
+ false
+
+
+ true
+ Make
+
+ Qt4ProjectManager.MakeStep
+
+ -w
+ -r
+
+ false
+
+
+
+ 2
+ Build
+
+ ProjectExplorer.BuildSteps.Build
+
+
+
+ true
+ Make
+
+ Qt4ProjectManager.MakeStep
+
+ -w
+ -r
+
+ true
+ clean
+
+
+ 1
+ Clean
+
+ ProjectExplorer.BuildSteps.Clean
+
+ 2
+ false
+
+ Profile
+
+ Qt4ProjectManager.Qt4BuildConfiguration
+ 0
+ true
+
+ 3
+
+
+ 0
+ Deploy
+
+ ProjectExplorer.BuildSteps.Deploy
+
+ 1
+ Deploy locally
+
+ ProjectExplorer.DefaultDeployConfiguration
+
+ 1
+
+
+ false
+ false
+ 1000
+
+ true
+
+ false
+ false
+ false
+ false
+ true
+ 0.01
+ 10
+ true
+ 1
+ 25
+
+ 1
+ true
+ false
+ true
+ valgrind
+
+ 0
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+ 11
+ 12
+ 13
+ 14
+
+ 2
+
+ JoplinQtClient
+
+ Qt4ProjectManager.Qt4RunConfiguration:/home/laurent/src/notes/QtClient/JoplinQtClient/JoplinQtClient.pro
+ true
+
+ JoplinQtClient.pro
+ false
+
+
+ 3768
+ false
+ true
+ false
+ false
+ true
+
+ 1
+
+
+
+ ProjectExplorer.Project.TargetCount
+ 1
+
+
+ ProjectExplorer.Project.Updater.FileVersion
+ 18
+
+
+ Version
+ 18
+
+
diff --git a/QtClient/JoplinQtClient/build.sh b/QtClient/JoplinQtClient/build.sh
new file mode 100755
index 000000000..71a286bee
--- /dev/null
+++ b/QtClient/JoplinQtClient/build.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+BUILD_DIR=/home/laurent/src/notes/QtClient/build-JoplinQtClient-Desktop_Qt_5_7_1_GCC_64bit-Debug
+mkdir -p "$BUILD_DIR"
+cd "$BUILD_DIR"
+/opt/Qt/5.7/gcc_64/bin/qmake /home/laurent/src/notes/QtClient/JoplinQtClient/JoplinQtClient.pro -spec linux-g++ CONFIG+=debug CONFIG+=qml_debug JOP_FRONT_END_CLI=1
+/usr/bin/make qmake_all
+/usr/bin/make
\ No newline at end of file
diff --git a/QtClient/JoplinQtClient/cliapplication.cpp b/QtClient/JoplinQtClient/cliapplication.cpp
new file mode 100644
index 000000000..3a906076f
--- /dev/null
+++ b/QtClient/JoplinQtClient/cliapplication.cpp
@@ -0,0 +1,151 @@
+#include
+
+#include "cliapplication.h"
+#include "constants.h"
+#include "database.h"
+#include "paths.h"
+#include "uuid.h"
+#include "settings.h"
+#include "models/folder.h"
+
+
+
+
+
+
+
+
+
+#include
+
+
+
+namespace jop {
+
+StdoutHandler::StdoutHandler() : QTextStream(stdout) {}
+
+CliApplication::CliApplication(int &argc, char **argv) : QCoreApplication(argc, argv) {
+ // This is linked to where the QSettings will be saved. In other words,
+ // if these values are changed, the settings will be reset and saved
+ // somewhere else.
+ QCoreApplication::setOrganizationName(jop::ORG_NAME);
+ QCoreApplication::setOrganizationDomain(jop::ORG_DOMAIN);
+ QCoreApplication::setApplicationName(jop::APP_NAME);
+
+ qInfo() << "Config dir:" << paths::configDir();
+ qInfo() << "Database file:" << paths::databaseFile();
+ qInfo() << "SSL:" << QSslSocket::sslLibraryBuildVersionString() << QSslSocket::sslLibraryVersionNumber();
+
+ jop::db().initialize(paths::databaseFile());
+
+ Settings::initialize();
+
+ Settings settings;
+
+ if (!settings.contains("clientId")) {
+ // Client ID should be unique per instance of a program
+ settings.setValue("clientId", uuid::createUuid());
+ }
+}
+
+// void CliApplication::processCommand(const Command& command) {
+// qDebug() << "Command" << command.name();
+// qDebug() << "Flags" << command.flags();
+// qDebug() << "Args" << command.args();
+
+// // if (command == "mkdir") {
+
+// // //Folder folder;
+// // //folder.setValue("title", args[
+
+// // }
+// }
+
+int CliApplication::exec() {
+ qDebug() << "===========================================";
+
+
+
+
+ // QProcess* process = new QProcess();
+ // qint64* processId = new qint64();
+ // process->startDetached("/usr/bin/vim", QStringList(), QString(), processId);
+ // while (kill(*processId, 0) == 0) {}
+ // delete processId;
+
+
+ QString command = "help";
+ QStringList args = arguments();
+
+ if (args.size() >= 2) {
+ command = args[1];
+ args.erase(args.begin() + 1);
+ }
+
+ QCommandLineParser parser;
+ parser.addHelpOption();
+ parser.addVersionOption();
+
+ // mkdir "new_folder"
+ // ls
+ // ls new_folder
+ // touch new_folder/new_note
+
+ if (command == "mkdir") {
+ parser.addPositionalArgument("path", "Folder path.");
+ } else if (command == "ls") {
+ parser.addPositionalArgument("path", "Folder path.");
+ } else if (command == "touch") {
+ parser.addPositionalArgument("path", "Note path.");
+ } else {
+ qDebug().noquote() << parser.helpText();
+ return 1;
+ }
+
+ parser.process(args);
+
+ args = parser.positionalArguments();
+
+ if (command == "mkdir") {
+ QString title = args.size() ? args[0] : QString();
+ Folder folder;
+ folder.setValue("title", title);
+ folder.save();
+ }
+
+ if (command == "ls") {
+ QString path = args.size() ? args[0] : QString();
+ if (path == "") {
+ std::vector> folders = Folder::all();
+ for (size_t i = 0; i < folders.size(); i++) {
+ qDebug().noquote() << folders[i].get()->value("title").toString();
+ }
+ } else {
+ Folder folder;
+ bool found = folder.loadByField("", "title", path);
+ if (found) {
+ qStdout() << "Found" << path << endl;
+ } else {
+ qWarning() << "Not found:" << path;
+ }
+ }
+ }
+
+ if (command == "touch") {
+ // QString path = args.size() ? args[0] : QString();
+ // if (path == "") {
+ // std::vector> folders = Folder::all();
+ // for (size_t i = 0; i < folders.size(); i++) {
+ // qDebug().noquote() << folders[i].get()->value("title").toString();
+ // }
+ // } else {
+ // // TODO: Get folder by name
+ // }
+ }
+
+ qDebug() << "===========================================";
+
+ return 0;
+}
+
+}
diff --git a/QtClient/JoplinQtClient/cliapplication.h b/QtClient/JoplinQtClient/cliapplication.h
new file mode 100644
index 000000000..27ca61582
--- /dev/null
+++ b/QtClient/JoplinQtClient/cliapplication.h
@@ -0,0 +1,35 @@
+#ifndef CLIAPPLICATION_H
+#define CLIAPPLICATION_H
+
+#include
+
+#include "command.h"
+
+namespace jop {
+
+class StdoutHandler : public QTextStream {
+
+public:
+
+ StdoutHandler();
+
+};
+
+inline StdoutHandler& qStdout() {
+ static StdoutHandler r;
+ return r;
+}
+
+class CliApplication : public QCoreApplication {
+
+public:
+
+ CliApplication(int &argc, char **argv);
+ void processCommand(const Command &command);
+ int exec();
+
+};
+
+}
+
+#endif // CLIAPPLICATION_H
diff --git a/QtClient/JoplinQtClient/command.cpp b/QtClient/JoplinQtClient/command.cpp
new file mode 100644
index 000000000..372dd1d6d
--- /dev/null
+++ b/QtClient/JoplinQtClient/command.cpp
@@ -0,0 +1,24 @@
+#include "command.h"
+
+namespace jop {
+
+Command::Command(const QStringList &arguments) : name_("help"), args_(arguments) {
+ args_.removeFirst();
+ if (args_.size() >= 1) {
+ name_ = args_.takeFirst();
+ }
+}
+
+QString Command::name() const {
+ return name_;
+}
+
+std::map Command::flags() const {
+ return flags_;
+}
+
+QStringList Command::args() const {
+ return args_;
+}
+
+}
diff --git a/QtClient/JoplinQtClient/command.h b/QtClient/JoplinQtClient/command.h
new file mode 100644
index 000000000..378a7340e
--- /dev/null
+++ b/QtClient/JoplinQtClient/command.h
@@ -0,0 +1,27 @@
+#ifndef COMMAND_H
+#define COMMAND_H
+
+#include
+
+namespace jop {
+
+class Command {
+
+public:
+
+ Command(const QStringList& arguments);
+ QString name() const;
+ std::map flags() const;
+ QStringList args() const;
+
+private:
+
+ QString name_;
+ std::map flags_;
+ QStringList args_;
+
+};
+
+}
+
+#endif // COMMAND_H
diff --git a/QtClient/JoplinQtClient/constants.h b/QtClient/JoplinQtClient/constants.h
index 4cb9a485a..4a222dc04 100755
--- a/QtClient/JoplinQtClient/constants.h
+++ b/QtClient/JoplinQtClient/constants.h
@@ -9,6 +9,12 @@ const QString ORG_NAME = "Cozic";
const QString ORG_DOMAIN = "cozic.net";
const QString APP_NAME = "Joplin";
+#if defined(JOP_FRONT_END_CLI)
+const QString FRONT_END = "cli";
+#elif defined(JOP_FRONT_END_GUI)
+const QString FRONT_END = "gui";
+#endif
+
}
#endif // CONSTANTS_H
diff --git a/QtClient/JoplinQtClient/database.cpp b/QtClient/JoplinQtClient/database.cpp
index 4eb5d754b..8649731be 100755
--- a/QtClient/JoplinQtClient/database.cpp
+++ b/QtClient/JoplinQtClient/database.cpp
@@ -4,6 +4,12 @@ using namespace jop;
Database::Database() {}
+Database::~Database() {
+ if (db_.open()) {
+ db_.close();
+ }
+}
+
void Database::initialize(const QString &path) {
version_ = -1;
transactionCount_ = 0;
diff --git a/QtClient/JoplinQtClient/database.h b/QtClient/JoplinQtClient/database.h
index 6cd6b2ab1..148a14aed 100755
--- a/QtClient/JoplinQtClient/database.h
+++ b/QtClient/JoplinQtClient/database.h
@@ -14,6 +14,7 @@ public:
enum QueryType { Select, Insert, Update, Delete };
Database();
+ ~Database();
void initialize(const QString& path);
QSqlDatabase& database();
QSqlQuery buildSqlQuery(Database::QueryType type, const QString& tableName, const QStringList& fields, const VariantVector& values, const QString& whereCondition = "");
diff --git a/QtClient/JoplinQtClient/main.cpp b/QtClient/JoplinQtClient/main.cpp
index 32377c468..b47c974cb 100755
--- a/QtClient/JoplinQtClient/main.cpp
+++ b/QtClient/JoplinQtClient/main.cpp
@@ -1,15 +1,38 @@
#include
+#if defined(JOP_FRONT_END_CLI)
+#include "cliapplication.h"
+#elif defined(JOP_FRONT_END_GUI)
#include "application.h"
+#endif
+
#include "models/folder.h"
#include "database.h"
#include "models/foldermodel.h"
#include "services/folderservice.h"
-using namespace jop;
+int main(int argc, char *argv[]) {
-int main(int argc, char *argv[]) {
+#if (!defined(JOP_FRONT_END_GUI) && !defined(JOP_FRONT_END_CLI))
+ qFatal("Either JOP_FRONT_END_GUI or JOP_FRONT_END_CLI must be defined!");
+ return 1;
+#endif
+
+#if (defined(JOP_FRONT_END_GUI) && defined(JOP_FRONT_END_CLI))
+ qFatal("JOP_FRONT_END_GUI and JOP_FRONT_END_CLI cannot both be defined!");
+ return 1;
+#endif
+
+#ifdef JOP_FRONT_END_GUI
+ qDebug() << "Front end: GUI";
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
- Application app(argc, argv);
- return app.exec();
-}
+ jop::Application app(argc, argv);
+#endif
+
+#ifdef JOP_FRONT_END_CLI
+ qDebug() << "Front end: CLI";
+ jop::CliApplication app(argc, argv);
+#endif
+
+ return app.exec();
+}
\ No newline at end of file
diff --git a/QtClient/JoplinQtClient/models/basemodel.cpp b/QtClient/JoplinQtClient/models/basemodel.cpp
index f3372bca8..6ce97b835 100755
--- a/QtClient/JoplinQtClient/models/basemodel.cpp
+++ b/QtClient/JoplinQtClient/models/basemodel.cpp
@@ -47,6 +47,24 @@ bool BaseModel::load(const QString &id) {
return true;
}
+bool BaseModel::loadByField(const QString& parentId, const QString& field, const QString& fieldValue) {
+ QSqlQuery q(jop::db().database());
+ QString sql = QString("SELECT %1 FROM %2 WHERE %3 = :field_value AND parent_id = :parent_id LIMIT 1")
+ .arg(BaseModel::tableFieldNames(table()).join(","))
+ .arg(BaseModel::tableName(table()))
+ .arg(field);
+ q.prepare(sql);
+ q.bindValue(":parent_id", parentId);
+ q.bindValue(":field_value", fieldValue);
+ jop::db().execQuery(q);
+ q.next();
+ if (!jop::db().errorCheck(q)) return false;
+ if (!q.isValid()) return false;
+
+ loadSqlQuery(q);
+ return true;
+}
+
bool BaseModel::save(bool trackChanges) {
bool isNew = this->isNew();
diff --git a/QtClient/JoplinQtClient/models/basemodel.h b/QtClient/JoplinQtClient/models/basemodel.h
index d4b4a0dc1..401f31526 100755
--- a/QtClient/JoplinQtClient/models/basemodel.h
+++ b/QtClient/JoplinQtClient/models/basemodel.h
@@ -43,6 +43,7 @@ public:
QStringList changedFields() const;
static int count(jop::Table table);
bool load(const QString& id);
+ bool loadByField(const QString& parentId, const QString& field, const QString& fieldValue);
virtual bool save(bool trackChanges = true);
virtual bool dispose();
diff --git a/QtClient/JoplinQtClient/models/folder.h b/QtClient/JoplinQtClient/models/folder.h
index 611ccf6dc..be6883cb6 100755
--- a/QtClient/JoplinQtClient/models/folder.h
+++ b/QtClient/JoplinQtClient/models/folder.h
@@ -14,7 +14,7 @@ public:
Folder();
static int count();
- static std::vector> all(const QString& orderBy);
+ static std::vector> all(const QString& orderBy = "title");
//Table table() const;
bool primaryKeyIsUuid() const;
diff --git a/QtClient/JoplinQtClient/schema.sql b/QtClient/JoplinQtClient/schema.sql
index 995d46277..95a548930 100755
--- a/QtClient/JoplinQtClient/schema.sql
+++ b/QtClient/JoplinQtClient/schema.sql
@@ -1,29 +1,30 @@
CREATE TABLE folders (
id TEXT PRIMARY KEY,
- title TEXT,
- created_time INT,
- updated_time INT
+ parent_id TEXT NOT NULL DEFAULT "",
+ title TEXT NOT NULL DEFAULT "",
+ created_time INT NOT NULL DEFAULT 0,
+ updated_time INT NOT NULL DEFAULT 0
);
CREATE TABLE notes (
id TEXT PRIMARY KEY,
- title TEXT,
- body TEXT,
- parent_id TEXT,
- created_time INT,
- updated_time INT,
- latitude NUMERIC,
- longitude NUMERIC,
- altitude NUMERIC,
- source TEXT,
- author TEXT,
- source_url TEXT,
- is_todo BOOLEAN,
- todo_due INT,
- todo_completed INT,
- source_application TEXT,
- application_data TEXT,
- `order` INT
+ parent_id TEXT NOT NULL DEFAULT "",
+ title TEXT NOT NULL DEFAULT "",
+ body TEXT NOT NULL DEFAULT "",
+ created_time INT NOT NULL DEFAULT 0,
+ updated_time INT NOT NULL DEFAULT 0,
+ latitude NUMERIC NOT NULL DEFAULT 0,
+ longitude NUMERIC NOT NULL DEFAULT 0,
+ altitude NUMERIC NOT NULL DEFAULT 0,
+ source TEXT NOT NULL DEFAULT "",
+ author TEXT NOT NULL DEFAULT "",
+ source_url TEXT NOT NULL DEFAULT "",
+ is_todo BOOLEAN NOT NULL DEFAULT 0,
+ todo_due INT NOT NULL DEFAULT 0,
+ todo_completed INT NOT NULL DEFAULT 0,
+ source_application TEXT NOT NULL DEFAULT "",
+ application_data TEXT NOT NULL DEFAULT "",
+ `order` INT NOT NULL DEFAULT 0
);
CREATE TABLE tags (
diff --git a/QtClient/JoplinQtClient/stable.h b/QtClient/JoplinQtClient/stable.h
index 0bb43aa44..c9cf9e04a 100755
--- a/QtClient/JoplinQtClient/stable.h
+++ b/QtClient/JoplinQtClient/stable.h
@@ -6,6 +6,7 @@
#include
#include
+#include
#include
#include
#include
@@ -32,6 +33,8 @@
#include
#include
#include
+#include
+#include
#endif // __cplusplus
diff --git a/joplin.sublime-project b/joplin.sublime-project
index 2bc6fcdb5..eb3efb241 100755
--- a/joplin.sublime-project
+++ b/joplin.sublime-project
@@ -13,6 +13,10 @@
{
"name": "Build evernote-import",
"shell_cmd": "D:\\Programmes\\cygwin\\bin\\bash.exe --login D:\\Web\\www\\joplin\\QtClient\\evernote-import\\build.sh"
+ },
+ {
+ "name": "Build QtClient CLI - Linux",
+ "shell_cmd": "/home/laurent/src/notes/QtClient/JoplinQtClient/build.sh"
}
]
}