Removed old files

pull/41/head
Laurent Cozic 2017-06-24 18:43:13 +01:00
parent e521ca9427
commit 9f0f826a9f
206 changed files with 2 additions and 91882 deletions

3
.gitignore vendored
View File

@ -30,4 +30,5 @@ sparse_test.php
INFO.md
/web/env.php
sync_staging.sh
*.swp
*.swp
_vieux/

View File

@ -1,42 +0,0 @@
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.1
Item {
id: root
width: 120
height: 100
signal addNoteButtonClicked
signal addFolderButtonClicked
ColumnLayout {
anchors.fill: parent
spacing: 2
Button {
id: addNoteButton
text: "Add note"
Layout.fillWidth: true
Layout.fillHeight: true
onClicked: root.addNoteButtonClicked()
}
Button {
id: addFolderButton
text: "Add folder"
Layout.fillWidth: true
Layout.fillHeight: true
onClicked: root.addFolderButtonClicked()
}
Button {
text: "ADD"
Layout.fillWidth: true
Layout.fillHeight: true
}
}
}

View File

@ -1,80 +0,0 @@
<?xml version="1.0"?>
<manifest package="net.cozic.joplin" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0" android:versionCode="1" android:installLocation="auto">
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="Joplin">
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:label="Joplin" android:screenOrientation="unspecified" android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<!-- Application arguments -->
<!-- meta-data android:name="android.app.arguments" android:value="arg1 arg2 arg3"/ -->
<!-- Application arguments -->
<meta-data android:name="android.app.lib_name" android:value="Joplin"/>
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
<meta-data android:name="android.app.repository" android:value="default"/>
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
<!-- Deploy Qt libs as part of package -->
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="1"/>
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
<!-- Run with local libs -->
<meta-data android:name="android.app.use_local_qt_libs" android:value="1"/>
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
<meta-data android:name="android.app.load_local_libs" android:value="plugins/platforms/android/libqtforandroid.so:plugins/bearer/libqandroidbearer.so:lib/libQt5QuickParticles.so"/>
<meta-data android:name="android.app.load_local_jars" android:value="jar/QtAndroid.jar:jar/QtAndroid-bundled.jar:jar/QtAndroidBearer.jar:jar/QtAndroidBearer-bundled.jar"/>
<meta-data android:name="android.app.static_init_classes" android:value=""/>
<!-- Messages maps -->
<meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
<meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
<!-- Messages maps -->
<!-- Splash screen -->
<!-- meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/ -->
<!-- meta-data android:name="android.app.splash_screen_sticky" android:value="true"/ -->
<!-- Splash screen -->
<!-- Background running -->
<!-- Warning: changing this value to true may cause unexpected crashes if the
application still try to draw after
"applicationStateChanged(Qt::ApplicationSuspended)"
signal is sent! -->
<meta-data android:name="android.app.background_running" android:value="false"/>
<!-- Background running -->
<!-- auto screen scale factor -->
<meta-data android:name="android.app.auto_screen_scale_factor" android:value="false"/>
<!-- auto screen scale factor -->
<!-- extract android style -->
<!-- available android:values :
* full - useful QWidget & Quick Controls 1 apps
* minimal - useful for Quick Controls 2 apps, it is much faster than "full"
* none - useful for apps that don't use any of the above Qt modules
-->
<meta-data android:name="android.app.extract_android_style" android:value="full"/>
<!-- extract android style -->
</activity>
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
</application>
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="16"/>
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
Remove the comment if you do not require these default permissions. -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
Remove the comment if you do not require these default features. -->
<uses-feature android:glEsVersion="0x00020000" android:required="true"/>
</manifest>

View File

@ -1,39 +0,0 @@
import QtQuick 2.0
import QtQuick.Controls 2.0
Component {
Item {
width: parent.width
height: 25
Text {
id: label
text: display
anchors.fill: parent
MouseArea {
anchors.fill: parent
onClicked: {
listView.currentIndex = index
}
onDoubleClicked: {
label.visible = false
textField.visible = true
textField.focus = true
}
}
}
TextField {
id: textField
text: display
visible: false
width: parent.width
height: parent.height
onAccepted: {
}
onEditingFinished: {
label.visible = true
textField.visible = false
}
}
}
}

View File

@ -1,84 +0,0 @@
import QtQuick 2.0
import QtQuick.Controls 2.0
Item {
id: root
width: parent.width
height: 25
property int mouseAreaDefaultWidth
property Menu contextMenu
signal startedEditing;
signal stoppedEditing;
signal editingAccepted(int index, string text);
function makeEditable(editable) {
if (typeof editable === 'undefined') editable = true;
if (editable === isEditable()) return; // Nothing to do
if (editable) {
label.visible = false
mouseArea.anchors.rightMargin = 10000; // Hack because `mouseArea.visible = false` makes the MouseArea ignore the next click event
textField.visible = true
textField.focus = true
textField.text = display
root.ListView.view.focus = true;
textField.selectAll()
root.startedEditing();
} else {
mouseArea.anchors.rightMargin = 0;
label.visible = true
textField.visible = false
root.stoppedEditing();
}
}
function startEditing() {
makeEditable(true);
}
function stopEditing() {
makeEditable(false);
}
function isEditable() {
return textField.visible;
}
Text {
id: label
text: display
anchors.fill: parent
verticalAlignment: Text.AlignVCenter
}
TextField {
id: textField
visible: false
width: parent.width
height: parent.height
onAccepted: {
root.editingAccepted(index, text);
}
onEditingFinished: {
stopEditing();
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: {
root.ListView.view.currentIndex = index
if (mouse.button === Qt.RightButton) {
contextMenu.open();
}
}
onDoubleClicked: {
startEditing();
}
}
}

View File

@ -1,38 +0,0 @@
import QtQuick 2.0
import QtQuick.Controls 2.0
Item {
id: folderDelegateRoot
width: 100//parent.width
height: 25
Text {
id: label
text: display
anchors.fill: parent
MouseArea {
anchors.fill: parent
onClicked: {
listView.currentIndex = index
}
onDoubleClicked: {
label.visible = false
textField.visible = true
textField.focus = true
}
}
}
TextField {
id: textField
text: display
visible: false
width: parent.width
height: parent.height
onAccepted: {
}
onEditingFinished: {
label.visible = true
textField.visible = false
}
}
}

View File

@ -1,104 +0,0 @@
import QtQuick 2.0
import QtQuick.Controls 2.0
Item {
id: root
property alias model: listView.model
property alias currentIndex: listView.currentIndex
property alias currentItem: listView.currentItem
property string currentItemId
signal startedEditing;
signal stoppedEditing;
signal editingAccepted(int index, string text);
signal deleteButtonClicked(int index);
// While an item is being edited, this property hold the item ID.
// It is then used, once the model is updated, to restore the selection.
property string editedItemId;
function startEditing(index) {
root.editedItemId = listView.model.indexToId(index);
currentIndex = model.rowCount() - 1;
currentItem.startEditing();
print("Start editing", root.editedItemId);
}
function stopEditing() {
currentItem.stopEditing();
print("Stop editing", root.editedItemId);
//print(root.editedItemId, listView.model.idToIndex(root.editedItemId));
//currentIndex = listView.model.idToIndex(root.editedItemId);
}
function selectItemById(id) {
print("selectItemBy()", id);
currentItemId = id
var newIndex = listView.model.idToIndex(currentItemId);
print("newIndex", newIndex);
currentIndex = newIndex
if (newIndex < 0) currentItemId = "";
print("currentItemId", currentItemId);
}
Rectangle {
color: "#eeeeff"
border.color: "#ff0000"
anchors.fill: parent
}
ListView {
Connections {
target: model
onDataChanged: {
print("Connection.onDataChanged", root.editedItemId);
if (root.editedItemId !== "") {
selectItemById(root.editedItemId);
root.editedItemId = "";
}
}
}
// onCurrentItemChanged: {
// print("onCurrentItemChanged avant", currentItemId);
// currentItemId = model.indexToId(currentIndex);
// print("onCurrentItemChanged apres", currentItemId);
// }
id: listView
highlightMoveVelocity: -1
highlightMoveDuration: 100
anchors.fill: parent
delegate: itemListDelegate
ScrollBar.vertical: ScrollBar { }
highlight: Rectangle { color: "lightsteelblue"; radius: 5 }
focus: true
}
Component {
id: itemListDelegate
EditableListItem {
contextMenu:
Menu {
MenuItem {
text: "Delete"
onTriggered: deleteButtonClicked(currentIndex);
}
}
onStartedEditing: {
print("onStartedEditing()");
root.editedItemId = listView.model.indexToId(index);
root.startedEditing();
}
onStoppedEditing: {
print("onStoppedEditing()");
root.stoppedEditing();
}
onEditingAccepted: function(index, text) {
print("onEditingAccepted()");
root.editingAccepted(index, text);
}
}
}
}

View File

@ -1,142 +0,0 @@
import QtQuick 2.0
Item {
id: root
signal rowsRequested(int fromRowIndex, int toRowIndex)
property variant items: [];
property int itemCount_: 0;
property int itemHeight_: 0;
property bool needToRequestRows_: false;
function itemHeight() {
if (root.itemHeight_) return root.itemHeight_;
var item = itemComponent.createObject(root)
item.content = { title: "dummy", id: "" };
item.updateDisplay();
item.visible = false;
root.itemHeight_ = item.height;
return root.itemHeight_;
}
function itemCount() {
return itemCount_;
}
function setItem(index, itemContent) {
if (index < 0 || index >= itemCount) {
console.error("ItemList::setItem: index out of bounds:", index);
return;
}
var contentTitle = itemContent.title;
var item = itemComponent.createObject(scrollArea.contentItem)
item.content = {
id: itemContent.id,
title: itemContent.title
};
item.invalidateDisplay();
items[index] = item;
root.invalidateDisplay();
}
function setItems(fromIndex, itemContents) {
for (var i = 0; i < itemContents.length; i++) {
setItem(fromIndex + i, itemContents[i]);
}
}
function addItem(title) {
var item = itemComponent.createObject(scrollArea.contentItem)
item.title = title;
item.updateDisplay();
items.push(item);
if (!itemHeight) itemHeight = item.height;
root.invalidateDisplay();
}
function setItemCount(count) {
if (count === root.itemCount_) return;
root.itemCount_ = count;
root.needToRequestRows_ = true;
root.invalidateDisplay();
}
function invalidateDisplay() {
root.updateDisplay();
}
function updateDisplay() {
var itemY = 0;
for (var i = 0; i < items.length; i++) {
var item = items[i];
if (item) item.y = itemY;
itemY += itemHeight()
}
scrollArea.contentHeight = itemCount() * itemHeight();
if (root.needToRequestRows_) {
root.needToRequestRows_ = false;
var indexes = itemIndexesInView();
root.rowsRequested(indexes[0], indexes[1]);
}
}
function itemIndexesInView() {
var maxVisibleItems = Math.ceil(scrollArea.height / itemHeight());
var fromIndex = Math.max(0, Math.floor(scrollArea.contentY / itemHeight()));
var toIndex = fromIndex + maxVisibleItems;
var maxIndex = itemCount() - 1;
return [Math.min(fromIndex, maxIndex), Math.min(toIndex, maxIndex)];
}
Component {
id: itemComponent
Item {
id: container
//property alias title: label.text
property variant content;
function invalidateDisplay() {
container.updateDisplay();
}
function updateDisplay() {
label.text = content.title;
container.height = label.height
}
Text {
id: label
anchors.left: parent.left
anchors.right: parent.right
verticalAlignment: Text.AlignVCenter
}
}
}
Flickable {
id: scrollArea
anchors.fill: parent
contentWidth: 800
contentHeight: 5000
// Rectangle {
// id: background
// color: "#ffffff"
// border.color: "#0000ff"
// width: 800
// height: 500
// }
}
}

View File

@ -1,106 +0,0 @@
# 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 \
models/folder.cpp \
database.cpp \
models/foldermodel.cpp \
models/notemodel.cpp \
models/note.cpp \
webapi.cpp \
synchronizer.cpp \
settings.cpp \
uuid.cpp \
dispatcher.cpp \
models/change.cpp \
models/basemodel.cpp \
models/setting.cpp \
paths.cpp \
window.cpp \
filters.cpp \
models/abstractlistmodel.cpp \
cliapplication.cpp \
command.cpp \
qmlutils.cpp \
baseitemlistcontroller.cpp \
folderlistcontroller.cpp
RESOURCES += qml.qrc \
database.qrc
# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
HEADERS += \
stable.h \
models/folder.h \
models/item.h \
database.h \
models/foldermodel.h \
models/notemodel.h \
models/note.h \
sparsevector.hpp \
webapi.h \
synchronizer.h \
settings.h \
simpletypes.h \
uuid.h \
dispatcher.h \
models/change.h \
models/basemodel.h \
enum.h \
models/setting.h \
paths.h \
constants.h \
window.h \
filters.h \
models/abstractlistmodel.h \
cliapplication.h \
command.h \
qmlutils.h \
baseitemlistcontroller.h \
folderlistcontroller.h
defined(JOP_FRONT_END_GUI, var) {
SOURCES += application.cpp
HEADERS += application.h
}
DISTFILES += \
AndroidManifest.xml
PRECOMPILED_HEADER = stable.h
# INCLUDEPATH += "C:/Program Files (x86)/Windows Kits/10/Include/10.0.10240.0/ucrt"
# LIBS += -L"C:/Program Files (x86)/Windows Kits/10/Lib/10.0.10240.0/ucrt/x86"

View File

@ -1,336 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.2.0, 2017-01-27T17:22:29. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{13661a96-7123-4040-a8b0-364538c1219d}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="int">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap"/>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop Qt 5.7.1 GCC 64bit</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop Qt 5.7.1 GCC 64bit</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">qt.57.gcc_64_kit</value>
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/laurent/src/notes/QtClient/build-JoplinQtClient-Desktop_Qt_5_7_1_GCC_64bit-Debug</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">qmake</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibrary">true</value>
<value type="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments">&quot;JOP_FRONT_END_CLI=1&quot;</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.SeparateDebugInfo">false</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.UseQtQuickCompiler">false</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments">
<value type="QString">-w</value>
<value type="QString">-r</value>
</valuelist>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments">
<value type="QString">-w</value>
<value type="QString">-r</value>
</valuelist>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Debug</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">2</value>
<value type="bool" key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild">true</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/laurent/src/notes/QtClient/build-JoplinQtClient-Desktop_Qt_5_7_1_GCC_64bit-Release</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">qmake</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibrary">false</value>
<value type="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.SeparateDebugInfo">false</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.UseQtQuickCompiler">false</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments">
<value type="QString">-w</value>
<value type="QString">-r</value>
</valuelist>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments">
<value type="QString">-w</value>
<value type="QString">-r</value>
</valuelist>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Release</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value>
<value type="bool" key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild">true</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.2">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/laurent/src/notes/QtClient/build-JoplinQtClient-Desktop_Qt_5_7_1_GCC_64bit-Profile</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">qmake</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibrary">true</value>
<value type="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.SeparateDebugInfo">true</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.UseQtQuickCompiler">false</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments">
<value type="QString">-w</value>
<value type="QString">-r</value>
</valuelist>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments">
<value type="QString">-w</value>
<value type="QString">-r</value>
</valuelist>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Profile</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value>
<value type="bool" key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild">true</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">3</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy locally</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.PluginSettings"/>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="bool" key="Analyzer.QmlProfiler.AggregateTraces">false</value>
<value type="bool" key="Analyzer.QmlProfiler.FlushEnabled">false</value>
<value type="uint" key="Analyzer.QmlProfiler.FlushInterval">1000</value>
<value type="QString" key="Analyzer.QmlProfiler.LastTraceFile"></value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
<value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
<value type="int" key="Analyzer.Valgrind.LeakCheckOnFinish">1</value>
<value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
<value type="int" key="Analyzer.Valgrind.SelfModifyingCodeDetection">1</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.Valgrind.ShowReachable">false</value>
<value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
<value type="int">0</value>
<value type="int">1</value>
<value type="int">2</value>
<value type="int">3</value>
<value type="int">4</value>
<value type="int">5</value>
<value type="int">6</value>
<value type="int">7</value>
<value type="int">8</value>
<value type="int">9</value>
<value type="int">10</value>
<value type="int">11</value>
<value type="int">12</value>
<value type="int">13</value>
<value type="int">14</value>
</valuelist>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">JoplinQtClient</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4RunConfiguration:/home/laurent/src/notes/QtClient/JoplinQtClient/JoplinQtClient.pro</value>
<value type="bool" key="QmakeProjectManager.QmakeRunConfiguration.UseLibrarySearchPath">true</value>
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.CommandLineArguments"></value>
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.ProFile">JoplinQtClient.pro</value>
<value type="bool" key="Qt4ProjectManager.Qt4RunConfiguration.UseDyldImageSuffix">false</value>
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.UserWorkingDirectory"></value>
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.UserWorkingDirectory.default"></value>
<value type="uint" key="RunConfiguration.QmlDebugServerPort">3768</value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseMultiProcess">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="int">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">18</value>
</data>
<data>
<variable>Version</variable>
<value type="int">18</value>
</data>
</qtcreator>

View File

@ -1,35 +0,0 @@
import QtQuick 2.4
LoginPageForm {
property Item appRoot
id: root
function onShown() {
root.apiBaseUrl = settings.valueString("api.baseUrl");
root.email = settings.valueString("user.email");
root.password = "";
}
Connections {
target: root
onLoginButtonClicked: {
appRoot.emitLoginClicked(root.apiBaseUrl, root.email, root.password);
}
}
Connections {
target: appRoot
onLoginStarted: {
root.enabled = false;
}
onLoginFailed: {
root.enabled = true;
}
onLoginSuccess: {
root.enabled = true;
}
}
}

View File

@ -1,103 +0,0 @@
import QtQuick 2.4
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
Item {
id: root
width: 400
height: 400
signal loginButtonClicked()
property alias apiBaseUrl: apiBaseUrlTF.text
property alias email: emailTF.text
property alias password: passwordTF.text
Rectangle {
id: rectangle2
color: "#ffffff"
anchors.fill: parent
}
GridLayout {
id: gridLayout1
flow: GridLayout.LeftToRight
rows: 6
columns: 2
anchors.fill: parent
Label {
id: label1
text: qsTr("API base URL")
}
TextField {
id: apiBaseUrlTF
text: "http://joplin.local"
Layout.fillWidth: true
}
Label {
id: label2
text: qsTr("Email")
}
TextField {
id: emailTF
text: "laurent@cozic.net"
Layout.fillWidth: true
}
Label {
id: label3
text: qsTr("Password")
}
TextField {
id: passwordTF
text: "12345678"
Layout.fillWidth: true
}
Button {
id: loginButton
text: qsTr("Login")
Layout.fillWidth: true
Layout.columnSpan: 2
}
Rectangle {
id: rectangle1
width: 200
height: 200
color: "#ffffff"
Layout.columnSpan: 2
Layout.rowSpan: 1
Layout.fillHeight: true
Layout.fillWidth: true
}
}
Connections {
target: loginButton
onClicked: root.loginButtonClicked()
}
Connections {
target: apiBaseUrlTF
onAccepted: root.loginButtonClicked()
}
Connections {
target: emailTF
onAccepted: root.loginButtonClicked()
}
Connections {
target: passwordTF
onAccepted: root.loginButtonClicked()
}
}

View File

@ -1,264 +0,0 @@
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
Item {
property Item appRoot
property alias itemList: itemList
// Component {
// id: rectangleComponent
// Rectangle { width: 80; height: 50; color: "red" }
// }
// function createRectangle() {
// var rect = rectangleComponent.createObject(parent);
// rect.x = 200;
// //console.info("aAAAAAAAAAAAAAAAAAAAAAAAA");
// }
ItemList2 {
id: itemList
width: 800
height: 500
}
// RowLayout {
// id: layout
// anchors.fill: parent
// spacing: 0
// ItemList {
// id: folderList
// model: folderListModel
// Layout.fillWidth: true
// Layout.fillHeight: true
// Layout.minimumWidth: 50
// Layout.preferredWidth: 100
// Layout.maximumWidth: 200
// Layout.minimumHeight: 150
// onCurrentItemChanged: {
// appRoot.currentFolderChanged()
// }
// onEditingAccepted: function(index, text) {
// handleItemListEditingAccepted(folderList, index, text);
// }
// onStoppedEditing: {
// handleItemListStoppedEditing(folderList);
// }
// onDeleteButtonClicked: {
// handleItemListAction(folderList, "delete");
// }
// }
// ItemList {
// id: noteList
// model: noteListModel
// Layout.fillWidth: true
// Layout.fillHeight: true
// Layout.minimumWidth: 100
// Layout.maximumWidth: 200
// Layout.preferredWidth: 200
// Layout.preferredHeight: 100
// onCurrentItemChanged: {
// appRoot.currentNoteChanged()
// }
// onEditingAccepted: function(index, text) {
// handleItemListEditingAccepted(noteList, index, text);
// }
// onStoppedEditing: {
// handleItemListStoppedEditing(noteList);
// }
// onDeleteButtonClicked: {
// handleItemListAction(noteList, "delete");
// }
// }
// NoteEditor {
// id: noteEditor
// model: noteModel
// Layout.fillWidth: true
// Layout.fillHeight: true
// Layout.minimumWidth: 100
// Layout.preferredHeight: 100
// }
// }
// AddButton {
// id: addButton
// anchors.right: parent.right
// anchors.bottom: parent.bottom
// onAddFolderButtonClicked: handleAddItem(folderList)
// onAddNoteButtonClicked: handleAddItem(noteList)
// }
// Button {
// id: syncButton
// text: "Sync"
// anchors.right: parent.right
// anchors.top: parent.top
// onClicked: appRoot.syncButtonClicked()
// }
// Button {
// id: logoutButton
// text: "Logout"
// anchors.right: syncButton.left
// anchors.top: parent.top
// onClicked: appRoot.logoutClicked()
// }
}
//import QtQuick 2.7
//import QtQuick.Controls 2.0
//import QtQuick.Layouts 1.0
//Item {
// property Item appRoot
// property alias currentFolderIndex: folderList.currentIndex
// property alias currentNoteIndex: noteList.currentIndex
// function onShown() {}
// function handleAddItem(list) {
// list.model.showVirtualItem();
// list.startEditing(list.model.rowCount() - 1);
// }
// function handleItemListEditingAccepted(list, index, text) {
// if (list.model.virtualItemShown()) {
// list.model.hideVirtualItem();
// list.model.addData(text)
// print("handleItemListEditingAccepted");
// list.selectItemById(list.model.lastInsertId());
// } else {
// list.model.setData(index, text, "title")
// }
// }
// function handleItemListStoppedEditing(list) {
// if (list.model.virtualItemShown()) {
// list.model.hideVirtualItem();
// }
// }
// function handleItemListAction(list, action) {
// if (action === "delete") {
// if (list.currentIndex === undefined) return;
// list.model.deleteData(list.currentIndex)
// }
// }
// RowLayout {
// id: layout
// anchors.fill: parent
// spacing: 0
// ItemList {
// id: folderList
// model: folderListModel
// Layout.fillWidth: true
// Layout.fillHeight: true
// Layout.minimumWidth: 50
// Layout.preferredWidth: 100
// Layout.maximumWidth: 200
// Layout.minimumHeight: 150
// onCurrentItemChanged: {
// appRoot.currentFolderChanged()
// }
// onEditingAccepted: function(index, text) {
// handleItemListEditingAccepted(folderList, index, text);
// }
// onStoppedEditing: {
// handleItemListStoppedEditing(folderList);
// }
// onDeleteButtonClicked: {
// handleItemListAction(folderList, "delete");
// }
// }
// ItemList {
// id: noteList
// model: noteListModel
// Layout.fillWidth: true
// Layout.fillHeight: true
// Layout.minimumWidth: 100
// Layout.maximumWidth: 200
// Layout.preferredWidth: 200
// Layout.preferredHeight: 100
// onCurrentItemChanged: {
// appRoot.currentNoteChanged()
// }
// onEditingAccepted: function(index, text) {
// handleItemListEditingAccepted(noteList, index, text);
// }
// onStoppedEditing: {
// handleItemListStoppedEditing(noteList);
// }
// onDeleteButtonClicked: {
// handleItemListAction(noteList, "delete");
// }
// }
// NoteEditor {
// id: noteEditor
// model: noteModel
// Layout.fillWidth: true
// Layout.fillHeight: true
// Layout.minimumWidth: 100
// Layout.preferredHeight: 100
// }
// }
// AddButton {
// id: addButton
// anchors.right: parent.right
// anchors.bottom: parent.bottom
// onAddFolderButtonClicked: handleAddItem(folderList)
// onAddNoteButtonClicked: handleAddItem(noteList)
// }
// Button {
// id: syncButton
// text: "Sync"
// anchors.right: parent.right
// anchors.top: parent.top
// onClicked: appRoot.syncButtonClicked()
// }
// Button {
// id: logoutButton
// text: "Logout"
// anchors.right: syncButton.left
// anchors.top: parent.top
// onClicked: appRoot.logoutClicked()
// }
//}

View File

@ -1,51 +0,0 @@
import QtQuick 2.0
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.1
Item {
property QtObject model
Connections {
target: model
onChanged: {
if (!model) {
titleField.text = ""
bodyField.text = ""
} else {
titleField.text = model.title
bodyField.text = model.body
}
}
}
Rectangle {
color: "#eeeeee"
border.color: "#0000ff"
anchors.fill: parent
}
ColumnLayout {
anchors.fill: parent
spacing: 2
TextField {
id: titleField
Layout.fillWidth: true
Layout.minimumWidth: 50
Layout.preferredWidth: 100
}
TextArea {
id: bodyField
Layout.fillWidth: true
Layout.fillHeight: true
Layout.minimumWidth: 50
Layout.preferredWidth: 100
Layout.minimumHeight: 150
}
}
}

View File

@ -1,46 +0,0 @@
import QtQuick 2.0
import QtQuick.Controls 2.0
Item {
id: root
property alias model: listView.model
property alias currentIndex: listView.currentIndex
property alias currentItem: listView.currentItem
Rectangle {
color: "#ffeeee"
border.color: "#00ff00"
anchors.fill: parent
}
Component {
id: noteDelegate
Item {
width: parent.width
height: 25
Text {
text: display
}
MouseArea {
anchors.fill: parent
onClicked: {
listView.currentIndex = index
}
}
}
}
ListView {
id: listView
anchors.fill: parent
delegate: noteDelegate
highlightMoveVelocity: -1
highlightMoveDuration: 100
ScrollBar.vertical: ScrollBar { }
highlight: Rectangle { color: "lightsteelblue"; radius: 5 }
focus: true
onCurrentItemChanged: {
root.currentItemChanged()
}
}
}

View File

@ -1,7 +0,0 @@
import QtQuick 2.7
Page1Form {
button1.onClicked: {
console.log("Button Pressed. Entered text: " + textField1.text);
}
}

View File

@ -1,73 +0,0 @@
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
Item {
property alias textField1: textField1
property alias button1: button1
RowLayout {
anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 20
anchors.top: parent.top
TextField {
id: textField1
placeholderText: qsTr("Text Field")
}
Button {
id: button1
text: qsTr("Press Me")
}
}
ListView {
id: listView1
x: 62
y: 143
width: 410
height: 199
model: ListModel {
ListElement {
name: "Grey"
colorCode: "grey"
}
ListElement {
name: "Red"
colorCode: "red"
}
ListElement {
name: "Blue"
colorCode: "blue"
}
ListElement {
name: "Green"
colorCode: "green"
}
}
delegate: Item {
x: 5
width: 80
height: 40
Row {
id: row1
Rectangle {
width: 40
height: 40
color: colorCode
}
Text {
text: name
font.bold: true
anchors.verticalCenter: parent.verticalCenter
}
spacing: 10
}
}
}
}

View File

@ -1,4 +0,0 @@
import QtQuick 2.4
TestForm {
}

View File

@ -1,26 +0,0 @@
import QtQuick 2.4
Item {
id: item1
width: 400
height: 400
AddButton {
id: addButton1
x: 232
y: 294
width: 100
height: 50
anchors.rightMargin: 0
anchors.bottom: parent.bottom
anchors.right: parent.right
}
FolderList {
id: folderList1
width: 107
anchors.bottom: parent.bottom
anchors.top: parent.top
anchors.left: parent.left
}
}

View File

@ -1,15 +0,0 @@
import QtQuick.Controls 1.4
TreeView {
TableViewColumn {
title: "Name"
role: "fileName"
width: 300
}
TableViewColumn {
title: "Permissions"
role: "filePermissions"
width: 100
}
model: fileSystemModel
}

View File

@ -1,128 +0,0 @@
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.0
Item {
id: root
width: 800
height: 600
property alias itemList : mainPage.itemList
function testing() {
var itemList = mainPage.itemList;
itemList.setItemCount(100);
var items = [];
for (var i = 0; i < 100; i++) {
items.push({ title: "Item " + i });
}
itemList.setItems(0, items);
}
MainPage {
id: mainPage
anchors.fill: parent
appRoot: root
}
}
//import QtQuick 2.7
//import QtQuick.Controls 2.0
//import QtQuick.Controls 1.4
//import QtQuick.Layouts 1.0
//Item {
// id: root
// width: 800
// height: 600
// signal currentFolderChanged()
// signal currentNoteChanged()
// signal addNoteButtonClicked()
// signal addFolderButtonClicked()
// signal syncButtonClicked()
// signal loginButtonClicked()
// signal loginClicked(string apiBaseUrl, string email, string password)
// signal loginStarted()
// signal loginFailed()
// signal loginSuccess()
// signal logoutClicked()
// property alias currentFolderIndex: mainPage.currentFolderIndex
// property alias currentNoteIndex: mainPage.currentNoteIndex
// property var pages : ({})
// function pageByName(pageName) {
// if (root.pages[pageName]) return root.pages[pageName];
// var page = null;
// if (pageName === "main") {
// page = mainPage
// } else if (pageName === "login") {
// var s = '
// LoginPage {
// id: loginPage
// anchors.fill: parent
// visible: false
// appRoot: root
// }';
// page = Qt.createQmlObject(s, root);
// }
// root.pages[pageName] = page;
// return page;
// }
// function showPage(pageName) {
// for (var n in root.pages) {
// root.pages[n].visible = false;
// }
// print("Switching to page: " + pageName);
// var page = pageByName(pageName);
// page.visible = true;
// page.onShown();
// }
// function selectFolderbyId(id) {
// mainPage.folderList.selectItemById(id);
// }
// function selectNoteById(id) {
// mainPage.noteList.selectItemById(id);
// }
// function emitLoginStarted() {
// root.loginStarted();
// }
// function emitLoginFailed() {
// root.loginFailed();
// }
// function emitLoginSuccess() {
// root.loginSuccess();
// }
// function emitLoginClicked(apiBaseUrl, email, password) {
// root.loginClicked(apiBaseUrl, email, password);
// }
// function emitLogoutClicked() {
// root.logoutClicked();
// }
// MainPage {
// id: mainPage
// anchors.fill: parent
// appRoot: root
// visible: false
// }
//}

View File

@ -1,244 +0,0 @@
#include "application.h"
#include "models/folder.h"
#include "database.h"
#include "models/foldermodel.h"
#include "models/change.h"
#include "services/folderservice.h"
#include "settings.h"
#include "uuid.h"
#include "dispatcher.h"
#include "paths.h"
#include "constants.h"
#include "filters.h"
#include "qmlutils.h"
using namespace jop;
Application::Application(int &argc, char **argv) :
QGuiApplication(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());
}
Settings* qmlSettings = new Settings();
view_.setResizeMode(QQuickView::SizeRootObjectToView);
QQmlContext *ctxt = view_.rootContext();
ctxt->setContextProperty("folderListModel", &folderModel_);
ctxt->setContextProperty("noteListModel", &noteModel_);
//ctxt->setContextProperty("noteModel", &selectedQmlNote_);
ctxt->setContextProperty("settings", qmlSettings);
view_.setSource(QUrl("qrc:/app.qml"));
QObject* rootObject = (QObject*)view_.rootObject();
QObject* itemList = qmlUtils::childFromProperty(rootObject, "itemList");
itemListController_.setItemList(itemList);
itemListController_.setParentId(QString(""));
//qmlUtils::callQml(itemList, "testing");
//qDebug() << itemList;
// QObject* itemList = rootObject->findChild<QObject*>("itemList");
// qDebug() << "WWWWWWWWWW" << itemList;
//view_.callQml("testing");
// connect(rootObject, SIGNAL(currentFolderChanged()), this, SLOT(view_currentFolderChanged()));
// connect(rootObject, SIGNAL(currentNoteChanged()), this, SLOT(view_currentNoteChanged()));
// connect(rootObject, SIGNAL(addFolderButtonClicked()), this, SLOT(view_addFolderButtonClicked()));
// connect(rootObject, SIGNAL(addNoteButtonClicked()), this, SLOT(view_addNoteButtonClicked()));
// connect(rootObject, SIGNAL(syncButtonClicked()), this, SLOT(view_syncButtonClicked()));
// connect(rootObject, SIGNAL(loginClicked(QString,QString,QString)), this, SLOT(dispatcher_loginClicked(QString,QString,QString)));
// connect(rootObject, SIGNAL(logoutClicked()), this, SLOT(dispatcher_logoutClicked()));
view_.show();
synchronizerTimer_.setInterval(1000 * 120);
synchronizerTimer_.start();
connect(&synchronizerTimer_, SIGNAL(timeout()), this, SLOT(synchronizerTimer_timeout()));
connect(&api_, SIGNAL(requestDone(const QJsonObject&, const QString&)), this, SLOT(api_requestDone(const QJsonObject&, const QString&)));
if (!settings.contains("user.email") || !settings.contains("session.id") || !settings.contains("api.baseUrl")) {
synchronizer_.freeze();
view_.showPage("login");
} else {
afterSessionInitialization();
view_.showPage("main");
view_currentFolderChanged(); // Make sure the note list shows the right notes
}
}
Application::~Application() {
jop::db().close();
}
void Application::login(const QString &email, const QString &password) {
Settings settings;
QUrlQuery postData;
postData.addQueryItem("email", email);
postData.addQueryItem("password", password);
postData.addQueryItem("client_id", settings.value("clientId").toString());
api_.post("sessions", QUrlQuery(), postData, "getSession");
}
void Application::api_requestDone(const QJsonObject& response, const QString& tag) {
// TODO: handle errors
// Handle expired sessions
if (tag == "getSession") {
if (response.contains("error")) {
qWarning() << "Could not get session:" << response.value("error").toString();
view_.emitSignal("loginFailed");
view_.showPage("login");
} else {
QString sessionId = response.value("id").toString();
qInfo() << "Got session" << sessionId;
Settings settings;
settings.setValue("session.id", sessionId);
afterSessionInitialization();
view_.emitSignal("loginSuccess");
view_.showPage("main");
}
return;
}
}
void Application::dispatcher_loginClicked(const QString &apiBaseUrl, const QString &email, const QString &password) {
view_.emitSignal("loginStarted");
QString newBaseUrl = filters::apiBaseUrl(apiBaseUrl);
Settings settings;
if (newBaseUrl != settings.value("api.baseUrl").toString()) {
// TODO: add confirmation dialog
qDebug() << "Base URL has changed from" << settings.value("api.baseUrl").toString() << "to" << newBaseUrl;
BaseModel::deleteAll(jop::FoldersTable);
BaseModel::deleteAll(jop::ChangesTable);
settings.remove("lastRevId");
settings.setValue("clientId", uuid::createUuid());
}
settings.setValue("user.email", filters::email(email));
settings.setValue("api.baseUrl", newBaseUrl);
api_.setBaseUrl(apiBaseUrl);
login(email, password);
}
void Application::dispatcher_logoutClicked() {
api_.abortAll();
synchronizer_.abort();
synchronizer_.freeze();
Settings settings;
settings.remove("session.id");
api_.setSessionId("");
synchronizer_.setSessionId("");
view_.showPage("login");
}
void Application::synchronizerTimer_timeout() {
//synchronizerTimer_.start(1000 * 10);
synchronizer_.start();
}
QString Application::selectedFolderId() const {
QObject* rootObject = (QObject*)view_.rootObject();
if (!rootObject) {
qCritical() << "Calling selectedFolderId() when root is null";
return "";
}
int index = rootObject->property("currentFolderIndex").toInt();
QModelIndex modelIndex = folderModel_.index(index);
return folderModel_.data(modelIndex, FolderModel::IdRole).toString();
}
QString Application::selectedNoteId() const {
QObject* rootObject = (QObject*)view_.rootObject();
int index = rootObject->property("currentNoteIndex").toInt();
QModelIndex modelIndex = noteModel_.index(index);
return noteModel_.data(modelIndex, NoteModel::IdRole).toString();
}
void Application::afterSessionInitialization() {
Settings settings;
QString sessionId = settings.value("session.id").toString();
api_.setBaseUrl(settings.value("api.baseUrl").toString());
api_.setSessionId(sessionId);
synchronizer_.api().setBaseUrl(settings.value("api.baseUrl").toString());
synchronizer_.setSessionId(sessionId);
synchronizer_.unfreeze();
synchronizer_.start();
}
void Application::view_currentFolderChanged() {
QString folderId = selectedFolderId();
noteModel_.setFolderId(folderId);
}
void Application::view_currentNoteChanged() {
// QString noteId = selectedNoteId();
// Note note = noteCollection_.byId(noteId);
// selectedQmlNote_.setNote(note);
}
void Application::view_addNoteButtonClicked() {
qDebug() <<"ADDNOTE";
}
void Application::view_addFolderButtonClicked() {
// QStringList fields;
// fields << "id";
// VariantVector values;
// values << uuid::createUuid();
// QSqlQuery q = db_.buildSqlQuery(Database::Insert, "folders", fields, values);
// q.exec();
// emit jop::dispatcher().folderCreated("test");
}
void Application::view_syncButtonClicked() {
synchronizer_.start();
}

View File

@ -1,59 +0,0 @@
#ifndef APPLICATION_H
#define APPLICATION_H
#include <stable.h>
#include "database.h"
#include "models/foldermodel.h"
#include "models/notemodel.h"
#include "webapi.h"
#include "synchronizer.h"
#include "window.h"
#include "folderlistcontroller.h"
namespace jop {
class Application : public QGuiApplication {
Q_OBJECT
public:
Application(int &argc, char **argv);
~Application();
void login(const QString& email, const QString& password);
private:
Window view_;
FolderModel folderModel_;
NoteModel noteModel_;
QString selectedFolderId() const;
QString selectedNoteId() const;
WebApi api_;
Synchronizer synchronizer_;
QTimer synchronizerTimer_;
FolderListController itemListController_;
void afterSessionInitialization();
public slots:
void view_currentFolderChanged();
void view_currentNoteChanged();
void view_addNoteButtonClicked();
void view_addFolderButtonClicked();
void view_syncButtonClicked();
void api_requestDone(const QJsonObject& response, const QString& tag);
void dispatcher_loginClicked(const QString &domain, const QString &email, const QString &password);
void dispatcher_logoutClicked();
void synchronizerTimer_timeout();
};
}
#endif // APPLICATION_H

View File

@ -1,73 +0,0 @@
#include "baseitemlistcontroller.h"
namespace jop {
BaseItemListController::BaseItemListController() :
parentId_(QString("")),
itemList_(NULL),
orderBy_("title") {
}
void BaseItemListController::setItemList(QObject *itemList) {
if (itemList_) {
qFatal("Cannot reset itemList - create a new ItemListController instead");
return;
}
itemList_ = itemList;
connect(itemList, SIGNAL(rowsRequested(int,int)), this, SLOT(itemList_rowsRequested(int,int)));
}
void BaseItemListController::setParentId(const QString &parentId) {
parentId_= parentId;
updateItemCount();
}
QString BaseItemListController::parentId() const {
return parentId_;
}
QObject *BaseItemListController::itemList() const {
return itemList_;
}
void BaseItemListController::setOrderBy(const QString &v) {
orderBy_ = v;
}
QString BaseItemListController::orderBy() const {
return orderBy_;
}
void BaseItemListController::updateItemCount() {
qFatal("BaseItemListController::updateItemCount() must be implemented by child class");
}
void BaseItemListController::itemList_rowsRequested(int fromIndex, int toIndex) {
Q_UNUSED(fromIndex); Q_UNUSED(toIndex);
qFatal("BaseItemListController::itemList_rowsRequested() must be implemented by child class");
}
const BaseModel *BaseItemListController::cacheGet(int index) const {
Q_UNUSED(index);
qFatal("BaseItemListController::cacheGet() not implemented");
return NULL;
}
void BaseItemListController::cacheSet(int index, BaseModel* baseModel) const {
Q_UNUSED(index); Q_UNUSED(baseModel);
qFatal("BaseItemListController::cacheSet() not implemented");
}
bool BaseItemListController::cacheIsset(int index) const {
Q_UNUSED(index);
qFatal("BaseItemListController::cacheIsset() not implemented");
return false;
}
void BaseItemListController::cacheClear() const {
qFatal("BaseItemListController::cacheClear() not implemented");
}
}

View File

@ -1,49 +0,0 @@
#ifndef BASEITEMLISTCONTROLLER_H
#define BASEITEMLISTCONTROLLER_H
#include <stable.h>
#include "models/basemodel.h"
namespace jop {
class BaseItemListController : public QObject {
Q_OBJECT
public:
BaseItemListController();
void setItemList(QObject* itemList);
void setParentId(const QString& parentId);
QString parentId() const;
QObject* itemList() const;
void setOrderBy(const QString& v);
QString orderBy() const;
private:
QString parentId_;
QObject* itemList_;
QString orderBy_;
protected:
virtual void updateItemCount();
// All these methods are const because we want to be able to clear the
// cache or set values from any method including const ones.
// http://stackoverflow.com/a/4248661/561309
virtual const BaseModel* cacheGet(int index) const;
virtual void cacheSet(int index, BaseModel* baseModel) const;
virtual bool cacheIsset(int index) const;
virtual void cacheClear() const;
public slots:
virtual void itemList_rowsRequested(int fromIndex, int toIndex);
};
}
#endif // BASEITEMLISTCONTROLLER_H

View File

@ -1,11 +0,0 @@
@echo off
D:
mkdir "D:\Web\www\joplin\QtClient\build-JoplinQtClient-Visual_C_32_bits-Debug\"
cd "D:\Web\www\joplin\QtClient\build-JoplinQtClient-Visual_C_32_bits-Debug\"
"C:\Qt\5.7\msvc2015\bin\qmake.exe" D:\Web\www\joplin\QtClient\JoplinQtClient\JoplinQtClient.pro -spec win32-msvc2015 "CONFIG+=debug" "CONFIG+=qml_debug" "JOP_FRONT_END_GUI=1"
"C:\Qt\Tools\QtCreator\bin\jom.exe" qmake_all
"C:\Qt\Tools\QtCreator\bin\jom.exe"
rem "C:\Qt\5.7\msvc2015\bin\qmake.exe" D:\Web\www\joplin\QtClient\JoplinQtClient\JoplinQtClient.pro -spec win32-msvc2015 "CONFIG+=debug" "CONFIG+=qml_debug"
rem "C:\Qt\Tools\QtCreator\bin\jom.exe" qmake_all
rem "C:\Qt\Tools\QtCreator\bin\jom.exe"

View File

@ -1,24 +0,0 @@
#!/bin/bash
set -e
# mkdir -p /cygdrive/d/Web/www/joplin/QtClient/build-JoplinQtClient-Visual_C_32_bits-Debug
# cd /cygdrive/d/Web/www/joplin/QtClient/build-JoplinQtClient-Visual_C_32_bits-Debug
# rm -rf debug/ release/ Makefile*
# export PATH="/cygdrive/c/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin":$PATH
# export PATH=$PATH:"/cygdrive/c/Program Files (x86)/Windows Kits/8.1/bin/x86"
# export PATH=$PATH:"/cygdrive/c/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include"
# "/cygdrive/c/Qt/5.7/msvc2015/bin/qmake.exe" D:\\Web\\www\\joplin\\QtClient\\JoplinQtClient\\JoplinQtClient.pro -spec win32-msvc2015 "CONFIG+=debug" "CONFIG+=qml_debug" "JOP_FRONT_END_GUI=1"
# "/cygdrive/c/Qt/Tools/QtCreator/bin/jom.exe" qmake_all
# "/cygdrive/c/Qt/Tools/QtCreator/bin/jom.exe"
# rsync -a /cygdrive/d/Web/www/joplin/QtClient/dependencies/dll-debug/ /cygdrive/d/Web/www/joplin/QtClient/build-JoplinQtClient-Visual_C_32_bits-Debug/debug
# cd -
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

View File

@ -1,486 +0,0 @@
#include <stable.h>
#include "cliapplication.h"
#include "constants.h"
#include "database.h"
#include "paths.h"
#include "uuid.h"
#include "settings.h"
#include "models/folder.h"
#include <signal.h>
namespace jop {
StdoutHandler::StdoutHandler() : QTextStream(stdout) {}
StderrHandler::StderrHandler() : QTextStream(stderr) {}
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());
}
connect(&api_, SIGNAL(requestDone(const QJsonObject&, const QString&)), this, SLOT(api_requestDone(const QJsonObject&, const QString&)));
connect(&synchronizer_, SIGNAL(started()), this, SLOT(synchronizer_started()));
connect(&synchronizer_, SIGNAL(finished()), this, SLOT(synchronizer_finished()));
}
CliApplication::~CliApplication() {
jop::db().close();
}
void CliApplication::api_requestDone(const QJsonObject& response, const QString& tag) {
// TODO: handle errors
// Handle expired sessions
if (tag == "getSession") {
if (response.contains("error")) {
qStderr() << "Could not login: " << response.value("error").toString() << endl;
emit synchronizationDone();
} else {
QString sessionId = response.value("id").toString();
Settings settings;
settings.setValue("session.id", sessionId);
startSynchronization();
}
}
}
// Call this only once the API base URL has been defined and the session has been set.
void CliApplication::startSynchronization() {
Settings settings;
synchronizer_.api().setBaseUrl(api_.baseUrl());
synchronizer_.setSessionId(settings.value("session.id").toString());
synchronizer_.unfreeze();
synchronizer_.start();
}
void CliApplication::synchronizer_started() {
qDebug() << "Synchronization started...";
}
void CliApplication::synchronizer_finished() {
qDebug() << "Synchronization finished...";
emit synchronizationDone();
}
bool CliApplication::filePutContents(const QString& filePath, const QString& content) const {
QFile file(filePath);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) return false;
QTextStream out(&file);
out << content;
out.flush();
return true;
}
QString CliApplication::fileGetContents(const QString& filePath) const {
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return QString("");
QTextStream in(&file);
return in.readAll();
}
void CliApplication::saveNoteIfFileChanged(Note& note, const QDateTime& originalLastModified, const QString& noteFilePath) {
if (originalLastModified == QFileInfo(noteFilePath).lastModified()) return;
QString content = fileGetContents(noteFilePath);
if (content.isEmpty()) return;
note.patchFriendlyString(content);
note.save();
}
// int CliApplication::execCommandConfig(QCommandLineParser& parser) {
// parser.addPositionalArgument("key", "Key of the config property.");
// parser.addPositionalArgument("value", "Value of the config property.");
// QCommandLineOption unsetOption(QStringList() << "unset", "Unset the given <key>.", "key");
// parser.addOption(unsetOption);
// QStringList args = parser.positionalArguments();
// Settings settings;
// QString propKey = args.size() >= 1 ? args[0] : "";
// QString propValue = args.size() >= 2 ? args[1] : "";
// if (propKey.isEmpty()) {
// QStringList propKeys = settings.allKeys();
// for (int i = 0; i < propKeys.size(); i++) {
// qStdout() << settings.keyValueserialize(propKeys[i]) << endl;
// }
// return 0;
// }
// if (propValue.isEmpty()) {
// qStdout() << settings.keyValueserialize(propKey) << endl;
// return 0;
// }
// settings.setValue(propKey, propValue);
// return 0;
// }
QStringList CliApplication::parseCommandLinePath(const QString& commandLine) const {
QStringList output;
int state = 0; // 0 = "outside quotes", 1 = "inside quotes"
QString current("");
for (int i = 0; i < commandLine.length(); i++) {
QChar c = commandLine[i];
// End quote
if (c == '"' && state == 1) {
output << current;
current = "";
state = 0;
continue;
}
// Start quote
if (c == '"' && state == 0) {
state = 1;
current = current.trimmed();
if (current != "") output << current;
current = "";
state = 1;
continue;
}
// A space when not inside a quoted string
if (c == ' ' && state == 0) {
current = current.trimmed();
if (current != "") output << current;
current = "";
continue;
}
current += c;
}
if (state == 0) current = current.trimmed();
if (current != "") output << current;
return output;
}
QString CliApplication::commandLineArgsToString(const QStringList& args) const {
QString output;
for (int i = 0; i < args.size(); i++) {
if (output != "") output += " ";
QString arg = args[i];
if (arg.contains(' ')) {
output += QString("\"%1\"").arg(arg);
} else {
output += arg;
}
}
return output;
}
int CliApplication::exec() {
qDebug() << "===========================================";
Settings settings;
QString command = "help";
QStringList args = arguments();
if (args.size() >= 2) {
command = args[1];
args.erase(args.begin() + 1);
}
QCommandLineParser parser;
QCommandLineOption helpOption(QStringList() << "h" << "help", "Display usage information.");
parser.addOption(helpOption);
parser.addVersionOption();
// mkdir "new_folder"
// rm "new_folder"
// ls
// ls new_folder
// touch new_folder/new_note
// edit new_folder/new_note
// config editor "subl -w %1"
// sync
// TODO: implement mv "new_folder"
if (command == "mkdir") {
parser.addPositionalArgument("path", "Folder path.");
} else if (command == "rm") {
parser.addPositionalArgument("path", "Folder path.");
} else if (command == "ls") {
parser.addPositionalArgument("path", "Folder path.");
} else if (command == "touch") {
parser.addPositionalArgument("path", "Note path.");
} else if (command == "edit") {
parser.addPositionalArgument("path", "Note path.");
} else if (command == "config") {
parser.addPositionalArgument("key", "Key of the config property.");
parser.addPositionalArgument("value", "Value of the config property.");
parser.addOption(QCommandLineOption(QStringList() << "unset", "Unset the given <key>.", "key"));
} else if (command == "sync") {
} else if (command == "help") {
} else {
qStderr() << parser.helpText() << endl;
return 1;
}
parser.process(args);
if (parser.isSet(helpOption) || command == "help") {
qStdout() << parser.helpText();
return 0;
}
args = parser.positionalArguments();
int errorCode = 0;
if (command == "mkdir") {
QString path = args.size() ? args[0] : QString();
if (path.isEmpty()) {
qStderr() << "Please provide a path or name for the folder.";
return 1;
}
std::vector<std::unique_ptr<Folder>> folders = Folder::pathToFolders(path, false, errorCode);
if (errorCode) {
qStderr() << "Invalid path: " << path << endl;
return 1;
}
Folder folder;
folder.setValue("parent_id", folders.size() ? folders[folders.size() - 1]->idString() : "");
folder.setValue("title", Folder::pathBaseName(path));
folder.save();
}
if (command == "rm") {
QString path = args.size() ? args[0] : QString();
if (path.isEmpty()) {
qStderr() << "Please provide a path or name for the folder.";
return 1;
}
std::vector<std::unique_ptr<Folder>> folders = Folder::pathToFolders(path, true, errorCode);
if (errorCode || !folders.size()) {
qStderr() << "Invalid path: " << path << endl;
return 1;
}
folders[folders.size() - 1]->dispose();
}
if (command == "ls") {
QString path = args.size() ? args[0] : QString();
std::vector<std::unique_ptr<Folder>> folders = Folder::pathToFolders(path, true, errorCode);
if (errorCode) {
qStderr() << "Invalid path: " << path << endl;
return 1;
}
std::vector<std::unique_ptr<BaseModel>> children;
if (folders.size()) {
children = folders[folders.size() - 1]->children();
} else {
std::unique_ptr<Folder> root = Folder::root();
children = root->children();
}
qStdout() << QString("Total: %1 items").arg(children.size()) << endl;
for (size_t i = 0; i < children.size(); i++) {
qStdout() << children[i]->displayTitle() << endl;
}
}
if (command == "touch") {
QString path = args.size() ? args[0] : QString();
if (path.isEmpty()) {
qStderr() << "Please provide a path or name for the note.";
return 1;
}
std::vector<std::unique_ptr<Folder>> folders = Folder::pathToFolders(path, false, errorCode);
if (errorCode) {
qStderr() << "Invalid path: " << path << endl;
} else {
QString noteTitle = Folder::pathBaseName(path);
Note note;
note.setValue("parent_id", folders.size() ? folders[folders.size() - 1]->idString() : "");
note.setValue("title", noteTitle);
note.save();
}
}
if (command == "edit") {
QString path = args.size() ? args[0] : QString();
if (path.isEmpty()) {
qStderr() << "Please provide a path or name for the note.";
return 1;
}
std::vector<std::unique_ptr<Folder>> folders = Folder::pathToFolders(path, false, errorCode);
if (errorCode) {
qStderr() << "Invalid path: " << path << endl;
} else {
// TODO: handle case where two notes with the same title exist
QString editorCommandString = settings.value("editor").toString().trimmed();
if (editorCommandString.isEmpty()) {
qStderr() << "No editor is defined. Please define one using the \"config editor\" command." << endl;
return 1;
}
QStringList editorCommand = parseCommandLinePath(editorCommandString);
QString parentId = folders.size() ? folders[folders.size() - 1]->idString() : QString("");
QString noteTitle = Folder::pathBaseName(path);
Note note;
if (!note.loadByField(parentId, QString("title"), noteTitle)) {
note.setValue("parent_id", folders.size() ? folders[folders.size() - 1]->idString() : "");
note.setValue("title", noteTitle);
note.save();
note.reload(); // To ensure that all fields are populated with the default values
}
QString noteFilePath = QString("%1/%2.txt").arg(paths::noteDraftsDir()).arg(note.idString());
if (!filePutContents(noteFilePath, note.serialize())) {
qStderr() << QString("Cannot open %1 for writing").arg(noteFilePath) << endl;
return 1;
}
QFileInfo fileInfo(noteFilePath);
QDateTime originalLastModified = fileInfo.lastModified();
qStdout() << QString("Editing note \"%1\" (Either close the editor or press Ctrl+C when done)").arg(path) << endl;
qDebug() << "File:" << noteFilePath;
QProcess* process = new QProcess();
qint64 processId = 0;
QString editorCommandPath = editorCommand.takeFirst();
editorCommand << noteFilePath;
if (!process->startDetached(editorCommandPath, editorCommand, QString(), &processId)) {
qStderr() << QString("Could not start command: %1").arg(editorCommandPath + " " + commandLineArgsToString(editorCommand)) << endl;
return 1;
}
while (kill(processId, 0) == 0) { // While the process still exist
QThread::sleep(2);
saveNoteIfFileChanged(note, originalLastModified, noteFilePath);
}
saveNoteIfFileChanged(note, originalLastModified, noteFilePath);
delete process; process = NULL;
QFile::remove(noteFilePath);
}
}
if (command == "config") {
if (parser.isSet("unset")) {
QString key = parser.value("unset").trimmed();
settings.remove(key);
return 0;
}
QString propKey = args.size() >= 1 ? args[0] : "";
QString propValue = args.size() >= 2 ? args[1] : "";
if (propKey.isEmpty()) {
QStringList propKeys = settings.allKeys();
for (int i = 0; i < propKeys.size(); i++) {
qStdout() << settings.keyValueserialize(propKeys[i]) << endl;
}
return 0;
}
if (propValue.isEmpty()) {
qStdout() << settings.keyValueserialize(propKey) << endl;
return 0;
}
settings.setValue(propKey, propValue);
}
if (command == "sync") {
QString sessionId = settings.value("session.id").toString();
qDebug() << "Session ID:" << sessionId;
// TODO: ask user
api_.setBaseUrl("http://127.0.0.1:8000");
QEventLoop loop;
connect(this, SIGNAL(synchronizationDone()), &loop, SLOT(quit()));
if (sessionId == "") {
QTextStream qtin(stdin);
qStdout() << "Enter email:" << endl;
QString email = qtin.readLine();
qStdout() << "Enter password:" << endl;
QString password = qtin.readLine();
qDebug() << email << password;
Settings settings;
QUrlQuery postData;
postData.addQueryItem("email", email);
postData.addQueryItem("password", password);
postData.addQueryItem("client_id", settings.value("clientId").toString());
api_.post("sessions", QUrlQuery(), postData, "getSession");
} else {
startSynchronization();
}
loop.exec();
qDebug() << "Synchronization done";
}
qDebug() << "=========================================== END";
return 0;
}
}

View File

@ -1,75 +0,0 @@
#ifndef CLIAPPLICATION_H
#define CLIAPPLICATION_H
#include <stable.h>
#include "command.h"
#include "models/note.h"
#include "webapi.h"
#include "synchronizer.h"
namespace jop {
class StdoutHandler : public QTextStream {
public:
StdoutHandler();
};
class StderrHandler : public QTextStream {
public:
StderrHandler();
};
inline StdoutHandler& qStdout() {
static StdoutHandler r;
return r;
}
inline StderrHandler& qStderr() {
static StderrHandler r;
return r;
}
class CliApplication : public QCoreApplication {
Q_OBJECT
public:
CliApplication(int &argc, char **argv);
~CliApplication();
void processCommand(const Command &command);
int exec();
public slots:
void api_requestDone(const QJsonObject& response, const QString& tag);
void synchronizer_started();
void synchronizer_finished();
signals:
void synchronizationDone();
private:
bool filePutContents(const QString& filePath, const QString& content) const;
void startSynchronization();
QString fileGetContents(const QString& filePath) const;
void saveNoteIfFileChanged(Note& note, const QDateTime& originalLastModified, const QString& noteFilePath);
QStringList parseCommandLinePath(const QString& commandLine) const;
QString commandLineArgsToString(const QStringList& args) const;
WebApi api_;
Synchronizer synchronizer_;
};
}
#endif // CLIAPPLICATION_H

View File

@ -1,24 +0,0 @@
#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<QString, QString> Command::flags() const {
return flags_;
}
QStringList Command::args() const {
return args_;
}
}

View File

@ -1,27 +0,0 @@
#ifndef COMMAND_H
#define COMMAND_H
#include <stable.h>
namespace jop {
class Command {
public:
Command(const QStringList& arguments);
QString name() const;
std::map<QString, QString> flags() const;
QStringList args() const;
private:
QString name_;
std::map<QString, QString> flags_;
QStringList args_;
};
}
#endif // COMMAND_H

View File

@ -1,26 +0,0 @@
#ifndef CONSTANTS_H
#define CONSTANTS_H
#include <stable.h>
namespace jop {
const QString ORG_NAME = "Cozic";
const QString ORG_DOMAIN = "cozic.net";
const QString APP_NAME = "Joplin";
#ifdef Q_WS_WIN
const QString NEW_LINE = "\r\n";
#else // Q_WS_WIN
const QString NEW_LINE = "\n";
#endif // Q_WS_WIN
#if defined(JOP_FRONT_END_CLI)
const QString FRONT_END = "cli";
#elif defined(JOP_FRONT_END_GUI)
const QString FRONT_END = "gui";
#endif // JOP_FRONT_END_GUI
}
#endif // CONSTANTS_H

View File

@ -1,270 +0,0 @@
#include "database.h"
using namespace jop;
Database::Database() : db_(NULL), isClosed_(true) {}
Database::~Database() {
if (!isClosed()) qWarning() << "Database::close() should be called explicitely";
}
void Database::initialize(const QString &path) {
version_ = -1;
transactionCount_ = 0;
logQueries_ = true;
// QFile::remove(path);
db_ = new QSqlDatabase(QSqlDatabase::addDatabase("QSQLITE"));
db_->setDatabaseName(path);
if (!db_->open()) {
qFatal("Error: connection with database fail");
} else {
qInfo() << "Database: connection ok";
isClosed_ = false;
}
upgrade();
}
// See https://bugreports.qt.io/browse/QTBUG-35977 for the reason why it's necessary
// to manually destroy the QSqlDatabase instance (i.e. it cannot be done in the
// Database::~Database).
void Database::close() {
if (db_ && db_->open()) db_->close();
delete db_;
db_ = NULL;
isClosed_ = true;
}
bool Database::isClosed() const {
return isClosed_;
}
QSqlDatabase* Database::database() const {
if (isClosed_) qFatal("Database::database: Database is closed");
return db_;
}
QSqlQuery Database::buildSqlQuery(Database::QueryType type, const QString &tableName, const QStringList &fields, const VariantVector &values, const QString &whereCondition) {
QString sql;
if (type == Insert) {
QString fieldString = "";
QString valueString = "";
for (int i = 0; i < fields.length(); i++) {
QString f = fields[i];
if (fieldString != "") fieldString += ", ";
if (valueString != "") valueString += ", ";
fieldString += QString("`%1`").arg(f);
valueString += ":" + f;
}
sql = QString("INSERT INTO `%1` (%2) VALUES (%3)").arg(tableName).arg(fieldString).arg(valueString);
} else if (type == Update) {
QString fieldString = "";
for (int i = 0; i < fields.length(); i++) {
QString f = fields[i];
if (fieldString != "") fieldString += ", ";
fieldString += QString("`%1`=:%1").arg(f);
}
sql = QString("UPDATE `%1` SET %2").arg(tableName).arg(fieldString);
if (whereCondition != "") sql += " WHERE " + whereCondition;
}
QSqlQuery query(*db_);
bool ok = query.prepare(sql);
if (!ok) {
printError(query);
return query;
}
for (int i = 0; i < values.size(); i++) {
QVariant v = values[i];
QString fieldName = ":" + fields[i];
if (v.type() == QVariant::String) {
query.bindValue(fieldName, v.toString());
} else if (v.type() == QVariant::Int) {
query.bindValue(fieldName, v.toInt());
} else if (v.isNull()) {
query.bindValue(fieldName, (int)NULL);
} else if (v.type() == QVariant::Double) {
query.bindValue(fieldName, v.toDouble());
} else if (v.type() == (QVariant::Type)QMetaType::Float) {
query.bindValue(fieldName, v.toFloat());
} else if (v.type() == QVariant::LongLong) {
query.bindValue(fieldName, v.toLongLong());
} else if (v.type() == QVariant::UInt) {
query.bindValue(fieldName, v.toUInt());
} else if (v.type() == QVariant::Char) {
query.bindValue(fieldName, v.toChar());
} else {
qWarning() << Q_FUNC_INFO << "Unsupported variant type:" << v.type();
}
}
return query;
}
QSqlQuery Database::buildSqlQuery(Database::QueryType type, const QString &tableName, const QMap<QString, QVariant> &values, const QString &whereCondition) {
QStringList fields;
VariantVector fieldValues;
for (QMap<QString, QVariant>::const_iterator it = values.begin(); it != values.end(); ++it) {
fields.push_back(it.key());
fieldValues.push_back(it.value());
}
return buildSqlQuery(type, tableName, fields, fieldValues, whereCondition);
}
void Database::printError(const QSqlQuery& query) const {
if (query.lastError().isValid()) {
qCritical().noquote() << "SQL error: " << query.lastError().text().trimmed() << ". Query was: " << query.lastQuery();
QMapIterator<QString, QVariant> i(query.boundValues());
while (i.hasNext()) {
i.next();
qCritical() << i.key() << "=" << i.value().toString();
}
}
}
bool Database::errorCheck(const QSqlQuery& query) {
if (query.lastError().isValid()) {
printError(query);
return false;
}
return true;
}
bool Database::transaction() {
transactionCount_++;
if (transactionCount_ > 1) return true;
return db_->transaction();
}
bool Database::commit() {
transactionCount_--;
if (transactionCount_ < 0) {
transactionCount_ = 0;
qCritical() << "Attempting commit on a database that is not in transaction mode";
return false;
}
if (transactionCount_ <= 0) {
return db_->commit();
}
return true;
}
bool Database::execQuery(QSqlQuery &query) {
if (logQueries_) {
QString sql = query.lastQuery();
qDebug().noquote() << "SQL:" << sql;
QMapIterator<QString, QVariant> i(query.boundValues());
while (i.hasNext()) {
i.next();
qDebug().noquote() << "SQL:" << i.key() << "=" << i.value().toString();
}
}
return query.exec();
}
bool Database::execQuery(const QString &sql) {
QSqlQuery query(sql, *db_);
return execQuery(query);
}
QSqlQuery Database::prepare(const QString &sql) {
QSqlQuery query(*db_);
query.prepare(sql);
return query;
}
int Database::version() const {
if (version_ >= 0) return version_;
QSqlQuery query = db_->exec("SELECT * FROM version");
bool result = query.next();
if (!result) return 0;
QSqlRecord r = query.record();
int i_version = r.indexOf("version");
version_ = query.value(i_version).toInt();
return version_;
}
QStringList Database::sqlStringToLines(const QString& sql) {
QStringList statements;
QStringList lines = sql.split("\n");
QString statement;
foreach (QString line, lines) {
line = line.trimmed();
if (line == "") continue;
if (line.left(2) == "--") continue;
statement += line;
if (line[line.length() - 1] == ';') {
statements.append(statement);
statement = "";
}
}
return statements;
}
void Database::upgrade() {
// INSTRUCTIONS TO UPGRADE THE DATABASE:
//
// 1. Add the new version number to the existingDatabaseVersions array
// 2. Add the upgrade logic to the "switch (targetVersion)" statement below
QList<int> existingVersions;
existingVersions << 1;
int versionIndex = existingVersions.indexOf(version());
if (versionIndex == existingVersions.length() - 1) return;
while (versionIndex < existingVersions.length() - 1) {
int targetVersion = existingVersions[versionIndex + 1];
qDebug() << "Upgrading database to version " << targetVersion;
db_->transaction();
switch (targetVersion) {
case 1:
QFile f(":/schema.sql");
if (!f.open(QFile::ReadOnly | QFile::Text)) {
qFatal("Cannot open database schema file");
return;
}
QTextStream in(&f);
QString schemaSql = in.readAll();
QStringList lines = sqlStringToLines(schemaSql);
foreach (const QString& line, lines) {
db_->exec(line);
}
break;
}
db_->exec(QString("UPDATE version SET version = %1").arg(targetVersion));
db_->commit();
versionIndex++;
}
}
Database databaseInstance_;
Database& jop::db() {
return databaseInstance_;
}

View File

@ -1,50 +0,0 @@
#ifndef DATABASE_H
#define DATABASE_H
#include <stable.h>
#include "enum.h"
#include "simpletypes.h"
namespace jop {
class Database {
public:
enum QueryType { Select, Insert, Update, Delete };
Database();
~Database();
void initialize(const QString& path);
void close();
bool isClosed() const;
QSqlDatabase* database() const;
QSqlQuery buildSqlQuery(Database::QueryType type, const QString& tableName, const QStringList& fields, const VariantVector& values, const QString& whereCondition = "");
QSqlQuery buildSqlQuery(Database::QueryType type, const QString& tableName, const QMap<QString, QVariant>& values, const QString& whereCondition = "");
bool errorCheck(const QSqlQuery& query);
bool transaction();
bool commit();
bool execQuery(QSqlQuery &query);
bool execQuery(const QString &query);
QSqlQuery prepare(const QString& sql);
private:
QSqlDatabase* db_;
void upgrade();
int version() const;
mutable int version_;
QStringList sqlStringToLines(const QString& sql);
void printError(const QSqlQuery& query) const;
int transactionCount_;
bool logQueries_;
bool isClosed_;
};
Database& db();
}
#endif // DATABASE_H

View File

@ -1,5 +0,0 @@
<RCC>
<qresource prefix="/">
<file>schema.sql</file>
</qresource>
</RCC>

View File

@ -1,69 +0,0 @@
#include <stable.h>
#include "databaseutils.h"
using namespace jop;
QSqlQuery dbUtils::buildSqlQuery(QSqlDatabase* db, const QString& type, const QString& tableName, const QStringList& fields, const VariantVector& values, const QString& whereCondition) {
QString sql;
if (type.toLower() == "insert") {
QString fieldString = "";
QString valueString = "";
for (int i = 0; i < fields.length(); i++) {
QString f = fields[i];
if (fieldString != "") fieldString += ", ";
if (valueString != "") valueString += ", ";
fieldString += f;
valueString += ":" + f;
}
sql = QString("INSERT INTO %1 (%2) VALUES (%3)").arg(tableName).arg(fieldString).arg(valueString);
} else if (type.toLower() == "update") {
QString fieldString = "";
for (int i = 0; i < fields.length(); i++) {
QString f = fields[i];
if (fieldString != "") fieldString += ", ";
fieldString += f + " = :" + f;
}
sql = QString("UPDATE %1 SET %2").arg(tableName).arg(fieldString);
if (whereCondition != "") sql += " WHERE " + whereCondition;
}
QSqlQuery query(*db);
query.prepare(sql);
for (int i = 0; i < values.size(); i++) {
QVariant v = values[i];
QString fieldName = ":" + fields[i];
if (v.type() == QVariant::String) {
query.bindValue(fieldName, v.toString());
} else if (v.type() == QVariant::Int) {
query.bindValue(fieldName, v.toInt());
} else if (v.isNull()) {
query.bindValue(fieldName, (int)NULL);
} else if (v.type() == QVariant::Double) {
query.bindValue(fieldName, v.toDouble());
} else if (v.type() == (QVariant::Type)QMetaType::Float) {
query.bindValue(fieldName, v.toFloat());
} else if (v.type() == QVariant::LongLong) {
query.bindValue(fieldName, v.toLongLong());
} else if (v.type() == QVariant::UInt) {
query.bindValue(fieldName, v.toUInt());
} else if (v.type() == QVariant::Char) {
query.bindValue(fieldName, v.toChar());
} else {
qWarning() << Q_FUNC_INFO << "Unsupported variant type:" << v.type();
}
}
qDebug() <<"SQL:"<<sql;
QMapIterator<QString, QVariant> i(query.boundValues());
while (i.hasNext()) {
i.next();
qDebug() << i.key() << ":" << i.value().toString();
}
return query;
}

View File

@ -1,15 +0,0 @@
#ifndef DATABASEUTILS_H
#define DATABASEUTILS_H
#include <stable.h>
#include "simpletypes.h"
namespace jop {
namespace dbUtils {
QSqlQuery buildSqlQuery(QSqlDatabase* db, const QString& type, const QString& tableName, const QStringList& fields, const VariantVector& values, const QString& whereCondition = "");
}
}
#endif // DATABASEUTILS_H

View File

@ -1,59 +0,0 @@
#include "dispatcher.h"
using namespace jop;
Dispatcher::Dispatcher() {}
void Dispatcher::emitFolderCreated(const QString &folderId) {
emit folderCreated(folderId);
}
void Dispatcher::emitFolderUpdated(const QString &folderId) {
emit folderUpdated(folderId);
}
void Dispatcher::emitFolderDeleted(const QString &folderId) {
emit folderDeleted(folderId);
}
void Dispatcher::emitAllFoldersDeleted() {
emit allFoldersDeleted();
}
void Dispatcher::emitNoteCreated(const QString &noteId) {
emit noteCreated(noteId);
}
void Dispatcher::emitNoteUpdated(const QString &noteId) {
emit noteUpdated(noteId);
}
void Dispatcher::emitNoteDeleted(const QString &noteId) {
emit noteDeleted(noteId);
}
void Dispatcher::emitLoginClicked(const QString &apiBaseUrl, const QString &email, const QString &password) {
emit loginClicked(apiBaseUrl, email, password);
}
void Dispatcher::emitLogoutClicked() {
emit logoutClicked();
}
void Dispatcher::emitLoginStarted() {
emit loginStarted();
}
void Dispatcher::emitLoginFailed() {
emit loginFailed();
}
void Dispatcher::emitLoginSuccess() {
emit loginSuccess();
}
Dispatcher dispatcherInstance_;
Dispatcher& jop::dispatcher() {
return dispatcherInstance_;
}

View File

@ -1,52 +0,0 @@
#ifndef DISPATCHER_H
#define DISPATCHER_H
#include <stable.h>
namespace jop {
class Dispatcher : public QObject {
Q_OBJECT
public:
Dispatcher();
public slots:
void emitFolderCreated(const QString& folderId);
void emitFolderUpdated(const QString& folderId);
void emitFolderDeleted(const QString& folderId);
void emitAllFoldersDeleted();
void emitNoteCreated(const QString& noteId);
void emitNoteUpdated(const QString& noteId);
void emitNoteDeleted(const QString& noteId);
void emitLoginClicked(const QString& domain, const QString& email, const QString &password);
void emitLogoutClicked();
void emitLoginStarted();
void emitLoginFailed();
void emitLoginSuccess();
signals:
void folderCreated(const QString& folderId);
void folderUpdated(const QString& folderId);
void folderDeleted(const QString& folderId);
void allFoldersDeleted();
void noteCreated(const QString& noteId);
void noteUpdated(const QString& noteId);
void noteDeleted(const QString& noteId);
void loginClicked(const QString& domain, const QString& email, const QString& password);
void logoutClicked();
void loginStarted();
void loginFailed();
void loginSuccess();
};
Dispatcher& dispatcher();
}
#endif // DISPATCHER_H

View File

@ -1,6 +0,0 @@
#include "enum.h"
enum::enum()
{
}

View File

@ -1,15 +0,0 @@
#ifndef ENUM_H
#define ENUM_H
#include <stable.h>
namespace jop {
enum Table { UndefinedTable, FoldersTable, NotesTable, ChangesTable };
// Note "DELETE" is a reserved keyword so we need to use "DEL"
enum HttpMethod { UndefinedMethod, HEAD, GET, PUT, POST, DEL, PATCH };
}
#endif // ENUM_H

View File

@ -1,19 +0,0 @@
#include "filters.h"
using namespace jop;
QString filters::apiBaseUrl(const QString &baseUrl) {
QString output(baseUrl.trimmed());
if (!output.startsWith("http://") && !output.startsWith("https://")) {
output = "http://" + output;
}
while (output.endsWith("/")) {
output = output.left(output.length() - 1);
}
return output;
}
QString filters::email(const QString &email) {
QString output(email.trimmed());
return output;
}

View File

@ -1,15 +0,0 @@
#ifndef FILTERS_H
#define FILTERS_H
#include <stable.h>
namespace jop {
namespace filters {
QString apiBaseUrl(const QString& apiBaseUrl);
QString email(const QString& email);
}
}
#endif // FILTERS_H

View File

@ -1,62 +0,0 @@
#include "folderlistcontroller.h"
#include "qmlutils.h"
#include "models/folder.h"
namespace jop {
FolderListController::FolderListController() : BaseItemListController() {}
void FolderListController::updateItemCount() {
int itemCount = Folder::count(parentId());
qmlUtils::callQml(itemList(), "setItemCount", QVariantList() << itemCount);
}
const BaseModel *FolderListController::cacheGet(int index) const {
return cache_[index].get();
}
void FolderListController::cacheSet(int index, BaseModel *baseModel) const {
Folder* folder = static_cast<Folder*>(baseModel);
cache_[index] = std::unique_ptr<Folder>(folder);
}
bool FolderListController::cacheIsset(int index) const {
return index > 0 && cache_.size() > (size_t)index;
}
void FolderListController::cacheClear() const {
cache_.clear();
}
void FolderListController::itemList_rowsRequested(int fromIndex, int toIndex) {
if (!cache_.size()) {
qFatal("TODO: replace with root::children()");
//cache_ = Folder::all(parentId(), orderBy());
}
//qDebug() << cache_.size();
if (fromIndex < 0 || (size_t)toIndex >= cache_.size() || !cache_.size()) {
qWarning() << "Invalid folder indexes" << fromIndex << toIndex;
return;
}
QVariantList output;
for (int i = fromIndex; i <= toIndex; i++) {
const BaseModel* model = cacheGet(i);
//qDebug() << model;
//QVariant v(cacheGet(i));
QVariant v = QVariant::fromValue((QObject*)model);
//qDebug() << v;
output.push_back(v);
}
QVariantList args;
args.push_back(fromIndex);
args.push_back(output);
qmlUtils::callQml(itemList(), "setItems", args);
}
}

View File

@ -1,39 +0,0 @@
#ifndef ITEMLISTCONTROLLER_H
#define ITEMLISTCONTROLLER_H
#include <stable.h>
#include "models/folder.h"
#include "baseitemlistcontroller.h"
namespace jop {
class FolderListController : public BaseItemListController {
Q_OBJECT
public:
FolderListController();
protected:
void updateItemCount();
const BaseModel* cacheGet(int index) const;
void cacheSet(int index, BaseModel* baseModel) const;
bool cacheIsset(int index) const;
void cacheClear() const;
private:
mutable std::vector<std::unique_ptr<Folder>> cache_;
public slots:
void itemList_rowsRequested(int fromIndex, int toIndex);
};
}
#endif // ITEMLISTCONTROLLER_H

View File

@ -1,46 +0,0 @@
#include "itemlist2.h"
ItemList2::ItemList2(QObject *parent)
: QAbstractItemModel(parent)
{
}
QVariant ItemList2::headerData(int section, Qt::Orientation orientation, int role) const
{
// FIXME: Implement me!
}
QModelIndex ItemList2::index(int row, int column, const QModelIndex &parent) const
{
// FIXME: Implement me!
}
QModelIndex ItemList2::parent(const QModelIndex &index) const
{
// FIXME: Implement me!
}
int ItemList2::rowCount(const QModelIndex &parent) const
{
if (!parent.isValid())
return 0;
// FIXME: Implement me!
}
int ItemList2::columnCount(const QModelIndex &parent) const
{
if (!parent.isValid())
return 0;
// FIXME: Implement me!
}
QVariant ItemList2::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
// FIXME: Implement me!
return QVariant();
}

View File

@ -1,29 +0,0 @@
#ifndef ITEMLIST2_H
#define ITEMLIST2_H
#include <QAbstractItemModel>
class ItemList2 : public QAbstractItemModel
{
Q_OBJECT
public:
explicit ItemList2(QObject *parent = 0);
// Header:
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
// Basic functionality:
QModelIndex index(int row, int column,
const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
private:
};
#endif // ITEMLIST2_H

View File

@ -1,43 +0,0 @@
#include <stable.h>
#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"
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);
jop::Application* app = new jop::Application(argc, argv);
#endif
#ifdef JOP_FRONT_END_CLI
qDebug() << "Front end: CLI";
jop::CliApplication* app = new jop::CliApplication(argc, argv);
#endif
int errorCode = app->exec();
delete app;
app = NULL;
return errorCode;
}

View File

@ -1,17 +0,0 @@
SET PATH=%PATH%;"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin"
cd "D:\Web\www\joplin\QtClient\build-evernote-import-qt-Visual_C_32_bites-Debug"
if %errorlevel% neq 0 exit /b %errorlevel%
"C:\Qt\5.7\msvc2015\bin\qmake.exe" D:\Web\www\joplin\QtClient\evernote-import\evernote-import-qt.pro -spec win32-msvc2015 "CONFIG+=debug" "CONFIG+=qml_debug"
if %errorlevel% neq 0 exit /b %errorlevel%
"C:\Qt\Tools\QtCreator\bin\jom.exe" qmake_all
if %errorlevel% neq 0 exit /b %errorlevel%
"C:\Qt\Tools\QtCreator\bin\jom.exe"
if %errorlevel% neq 0 exit /b %errorlevel%
/cygdrive/c/Qt/Tools/QtCreator/bin/jom.exe

View File

@ -1,140 +0,0 @@
#include "abstractlistmodel.h"
using namespace jop;
AbstractListModel::AbstractListModel() : QAbstractListModel() {
virtualItemShown_ = false;
}
int AbstractListModel::rowCount(const QModelIndex & parent) const { Q_UNUSED(parent);
return baseModelCount() + (virtualItemShown() ? 1 : 0);
}
QVariant AbstractListModel::data(const QModelIndex & index, int role) const {
const BaseModel* model = NULL;
if (virtualItemShown() && index.row() == rowCount() - 1) {
if (role == Qt::DisplayRole) return "Untitled";
return "";
} else {
model = atIndex(index.row());
}
if (role == Qt::DisplayRole) {
return model->value("title").toQVariant();
}
if (role == IdRole) {
return model->id().toQVariant();
}
return QVariant();
}
const BaseModel *AbstractListModel::atIndex(int index) const {
Q_UNUSED(index);
qFatal("AbstractListModel::atIndex() not implemented");
return NULL;
}
const BaseModel* AbstractListModel::atIndex(const QModelIndex &index) const {
return atIndex(index.row());
}
bool AbstractListModel::setData(const QModelIndex &index, const QVariant &value, int role) {
const BaseModel* model = atIndex(index.row());
if (!model) return false;
if (role == TitleRole) {
BaseModel temp;
temp.clone(*model);
temp.setValue("title", value.toString());
if (!temp.save()) return false;
cacheClear();
return true;
// model->setValue("title", value.toString());
// if (!model->save()) return false;
// cacheClear();
// return true;
}
qWarning() << "Unsupported role" << role;
return false;
}
bool AbstractListModel::setData(int index, const QVariant &value, const QString& role) {
return setData(this->index(index), value, roleNameToId(role));
}
int AbstractListModel::baseModelCount() const {
qFatal("AbstractListModel::baseModelCount() not implemented");
return 0;
}
const BaseModel *AbstractListModel::cacheGet(int index) const {
Q_UNUSED(index);
qFatal("AbstractListModel::cacheGet() not implemented");
return NULL;
}
void AbstractListModel::cacheSet(int index, BaseModel* baseModel) const {
Q_UNUSED(index); Q_UNUSED(baseModel);
qFatal("AbstractListModel::cacheSet() not implemented");
}
bool AbstractListModel::cacheIsset(int index) const {
Q_UNUSED(index);
qFatal("AbstractListModel::cacheIsset() not implemented");
return false;
}
void AbstractListModel::cacheClear() const {
qFatal("AbstractListModel::cacheClear() not implemented");
}
void AbstractListModel::showVirtualItem() {
virtualItemShown_ = true;
beginInsertRows(QModelIndex(), this->rowCount() - 1, this->rowCount() - 1);
endInsertRows();
}
void AbstractListModel::hideVirtualItem() {
beginRemoveRows(QModelIndex(), this->rowCount() - 1, this->rowCount() - 1);
virtualItemShown_ = false;
endRemoveRows();
}
bool AbstractListModel::virtualItemShown() const {
return virtualItemShown_;
}
QHash<int, QByteArray> AbstractListModel::roleNames() const {
QHash<int, QByteArray> roles = QAbstractItemModel::roleNames();
roles[TitleRole] = "title";
roles[IdRole] = "id";
return roles;
}
int AbstractListModel::roleNameToId(const QString &name) const {
QHash<int, QByteArray> roles = roleNames();
for (QHash<int, QByteArray>::const_iterator it = roles.begin(); it != roles.end(); ++it) {
if (it.value() == name) return it.key();
}
qCritical() << "Unknown role" << name;
return 0;
}
QString AbstractListModel::indexToId(int index) const {
return data(this->index(index), IdRole).toString();
}
int AbstractListModel::idToIndex(const QString &id) const {
Q_UNUSED(id);
qFatal("AbstractListModel::idToIndex() not implemented");
return -1;
}
QString AbstractListModel::lastInsertId() const {
return lastInsertId_;
}

View File

@ -1,61 +0,0 @@
#ifndef ABSTRACTLISTMODEL_H
#define ABSTRACTLISTMODEL_H
#include <stable.h>
#include "models/basemodel.h"
namespace jop {
class AbstractListModel : public QAbstractListModel {
Q_OBJECT
public:
enum ModelRoles {
IdRole = Qt::UserRole + 1,
TitleRole
};
AbstractListModel();
int rowCount(const QModelIndex & parent = QModelIndex()) const;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
virtual const BaseModel* atIndex(int index) const;
const BaseModel* atIndex(const QModelIndex &index) const;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
protected:
QString lastInsertId_;
virtual int baseModelCount() const;
// All these methods are const because we want to be able to clear the
// cache or set values from any method including const ones.
// http://stackoverflow.com/a/4248661/561309
virtual const BaseModel* cacheGet(int index) const;
virtual void cacheSet(int index, BaseModel* baseModel) const;
virtual bool cacheIsset(int index) const;
virtual void cacheClear() const;
private:
bool virtualItemShown_;
public slots:
void showVirtualItem();
bool virtualItemShown() const;
void hideVirtualItem();
QHash<int, QByteArray> roleNames() const;
int roleNameToId(const QString& name) const;
QString indexToId(int index) const;
virtual int idToIndex(const QString& id) const;
QString lastInsertId() const;
bool setData(int index, const QVariant &value, const QString& role = "edit");
};
}
#endif // ABSTRACTLISTMODEL_H

View File

@ -1,538 +0,0 @@
#include "basemodel.h"
#include "dispatcher.h"
#include "models/change.h"
#include "database.h"
#include "uuid.h"
using namespace jop;
QMap<int, QVector<BaseModel::Field>> BaseModel::tableFields_;
QHash<QString, QVariant> BaseModel::cache_;
BaseModel::BaseModel() : isNew_(-1), table_(jop::UndefinedTable) {}
QStringList BaseModel::changedFields() const {
QStringList output;
for (QHash<QString, bool>::const_iterator it = changedFields_.begin(); it != changedFields_.end(); ++it) {
output.push_back(it.key());
}
return output;
}
int BaseModel::count(Table table, const QString &parentId) {
QString t = BaseModel::tableName(table);
QString k = QString("%1:count").arg(t);
QVariant r = BaseModel::cacheGet(k);
if (r.isValid()) return r.toInt();
QSqlQuery q = jop::db().prepare("SELECT count(*) AS row_count FROM " + t + " WHERE parent_id = :parent_id");
q.bindValue(":parent_id", parentId);
jop::db().execQuery(q);
q.next();
if (!jop::db().errorCheck(q)) return 0;
int output = q.value(0).toInt();
BaseModel::cacheSet(k, QVariant(output));
return output;
}
bool BaseModel::load(const QString &id) {
QSqlQuery q(*jop::db().database());
q.prepare("SELECT " + BaseModel::sqlTableFields(table()) + " FROM " + BaseModel::tableName(table()) + " WHERE id = :id");
q.bindValue(":id", id);
jop::db().execQuery(q);
q.next();
if (!jop::db().errorCheck(q)) return false;
if (!q.isValid()) return false;
loadSqlQuery(q);
return true;
}
bool BaseModel::reload() {
return load(idString());
}
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::sqlTableFields(table()))
.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();
if (!changedFields_.size() && !isNew) return true;
QStringList fields = changedFields();
QMap<QString, QVariant> values;
foreach (QString field, fields) {
values[field] = value(field).toQVariant();
}
// If it's a new entry and the ID is a UUID, we need to create this
// ID now. If the ID is an INT, it will be automatically set by
// SQLite.
if (isNew && primaryKeyIsUuid() && !valueIsSet(primaryKey())) {
values[primaryKey()] = uuid::createUuid();
}
// Update created_time and updated_time if needed. If updated_time
// has already been updated (maybe manually by the user), don't
// automatically update it.
if (isNew) {
if (BaseModel::hasField(table(), "created_time")) {
values["created_time"] = (int)(QDateTime::currentMSecsSinceEpoch() / 1000);
}
} else {
if (!values.contains("updated_time")) {
if (BaseModel::hasField(table(), "updated_time")) {
values["updated_time"] = (int)(QDateTime::currentMSecsSinceEpoch() / 1000);
}
}
}
changedFields_.clear();
const QString& tableName = BaseModel::tableName(table());
if (isNew) {
cacheDelete(QString("%1:count").arg(tableName));
}
bool isSaved = false;
jop::db().transaction();
if (isNew) {
QSqlQuery q = jop::db().buildSqlQuery(Database::Insert, tableName, values);
jop::db().execQuery(q);
isSaved = jop::db().errorCheck(q);
if (isSaved) setValue("id", values["id"]);
} else {
QSqlQuery q = jop::db().buildSqlQuery(Database::Update, tableName, values, QString("%1 = '%2'").arg(primaryKey()).arg(value("id").toString()));
jop::db().execQuery(q);
isSaved = jop::db().errorCheck(q);
}
if (isSaved && this->trackChanges() && trackChanges) {
if (isNew) {
Change change;
change.setValue("item_id", id());
change.setValue("item_type", table());
change.setValue("type", Change::Create);
change.save();
} else {
for (QMap<QString, QVariant>::const_iterator it = values.begin(); it != values.end(); ++it) {
Change change;
change.setValue("item_id", id());
change.setValue("item_type", table());
change.setValue("type", Change::Update);
change.setValue("item_field", it.key());
change.save();
}
}
}
jop::db().commit();
if (isSaved) {
if (table() == jop::FoldersTable) {
if (isNew) {
dispatcher().emitFolderCreated(idString());
} else {
dispatcher().emitFolderUpdated(idString());
}
}
if (table() == jop::NotesTable) {
if (isNew) {
dispatcher().emitNoteCreated(idString());
} else {
dispatcher().emitNoteUpdated(idString());
}
}
}
isNew_ = -1;
return isSaved;
}
bool BaseModel::dispose() {
const QString& tableName = BaseModel::tableName(table());
QSqlQuery q(*jop::db().database());
q.prepare("DELETE FROM " + tableName + " WHERE " + primaryKey() + " = :id");
q.bindValue(":id", id().toString());
jop::db().execQuery(q);
bool isDeleted = jop::db().errorCheck(q);
if (isDeleted) cacheDelete(QString("%1:count").arg(tableName));
if (isDeleted && trackChanges()) {
Change change;
change.setValue("item_id", id());
change.setValue("item_type", table());
change.setValue("type", Change::Delete);
change.save();
}
if (isDeleted) {
if (table() == jop::FoldersTable) dispatcher().emitFolderDeleted(idString());
if (table() == jop::NotesTable) dispatcher().emitNoteDeleted(idString());
}
return isDeleted;
}
Table BaseModel::table() const {
return table_;
}
QString BaseModel::primaryKey() const {
return "id";
}
bool BaseModel::primaryKeyIsUuid() const {
return false;
}
bool BaseModel::trackChanges() const {
return false;
}
QString BaseModel::displayTitle() const {
return value("title").toString();
}
bool BaseModel::isNew() const {
if (isNew_ == 0) return false;
if (isNew_ == 1) return true;
return !valueIsSet(primaryKey());
}
BaseModel::Field createField(const QString& name, QMetaType::Type type) {
BaseModel::Field c;
c.name = name;
c.type = type;
return c;
}
QVector<BaseModel::Field> BaseModel::tableFields(jop::Table table) {
if (BaseModel::tableFields_.contains(table)) return BaseModel::tableFields_[table];
QVector<BaseModel::Field> output;
// TODO: ideally that should be auto-generated based on schema.sql
if (table == jop::FoldersTable) {
output.push_back(createField("id", QMetaType::QString ));
output.push_back(createField("title", QMetaType::QString ));
output.push_back(createField("created_time", QMetaType::Int ));
output.push_back(createField("updated_time", QMetaType::Int ));
} else if (table == jop::NotesTable) {
output.push_back(createField("id", QMetaType::QString ));
output.push_back(createField("title", QMetaType::QString ));
output.push_back(createField("body", QMetaType::QString ));
output.push_back(createField("parent_id", QMetaType::QString ));
output.push_back(createField("created_time", QMetaType::Int ));
output.push_back(createField("updated_time", QMetaType::Int ));
output.push_back(createField("latitude", QMetaType::QString ));
output.push_back(createField("longitude", QMetaType::QString ));
output.push_back(createField("altitude", QMetaType::QString ));
output.push_back(createField("source", QMetaType::QString ));
output.push_back(createField("author", QMetaType::QString ));
output.push_back(createField("source_url", QMetaType::QString ));
output.push_back(createField("is_todo", QMetaType::Int ));
output.push_back(createField("todo_due", QMetaType::Int ));
output.push_back(createField("todo_completed", QMetaType::Int ));
output.push_back(createField("source_application", QMetaType::QString ));
output.push_back(createField("application_data", QMetaType::QString ));
output.push_back(createField("order", QMetaType::Int ));
} else if (table == jop::ChangesTable) {
output.push_back(createField("id", QMetaType::Int ));
output.push_back(createField("type", QMetaType::Int ));
output.push_back(createField("item_id", QMetaType::QString ));
output.push_back(createField("item_type", QMetaType::Int ));
output.push_back(createField("item_field", QMetaType::QString ));
} else {
qFatal("Field not defined for table %d", table);
}
BaseModel::tableFields_[table] = output;
return output;
}
bool BaseModel::hasField(jop::Table table, const QString &name) {
QVector<BaseModel::Field> fields = tableFields(table);
foreach (Field field, fields) {
if (field.name == name) return true;
}
return false;
}
QStringList BaseModel::tableFieldNames(Table table) {
QVector<BaseModel::Field> fields = BaseModel::tableFields(table);
QStringList output;
foreach (BaseModel::Field field, fields) {
output.push_back(field.name);
}
return output;
}
QString BaseModel::sqlTableFields(Table table) {
QString output = "";
QStringList fields = BaseModel::tableFieldNames(table);
for (int i = 0; i < fields.size(); i++) {
if (output != "") output += ",";
output += QString("`%1`").arg(fields[i]);
}
return output;
}
bool BaseModel::isValidFieldName(Table table, const QString &name) {
QVector<BaseModel::Field> fields = BaseModel::tableFields(table);
foreach (BaseModel::Field col, fields) {
if (col.name == name) return true;
}
return false;
}
void BaseModel::deleteAll(Table table) {
QString tableName = BaseModel::tableName(table);
jop::db().execQuery("DELETE FROM " + tableName);
BaseModel::cache_.clear();
if (table == jop::FoldersTable) {
dispatcher().emitAllFoldersDeleted();
}
}
// When loading a QSqlQuery, all the values are cleared and replaced by those
// from the QSqlQuery. All the fields are marked as NOT changed as it's assumed
// the object is already in the database (since loaded from there).
void BaseModel::loadSqlQuery(const QSqlQuery &query) {
values_.clear();
QSqlRecord record = query.record();
QVector<BaseModel::Field> fields = BaseModel::tableFields(table());
foreach (BaseModel::Field field, fields) {
int idx = record.indexOf(field.name);
if (idx < 0) {
qCritical() << "Cannot find field" << field.name;
continue;
}
if (field.type == QMetaType::QString) {
setValue(field.name, query.value(idx).toString());
} else if (field.type == QMetaType::Int) {
setValue(field.name, query.value(idx).toInt());
} else {
qCritical() << "Unsupported value type" << field.name;
}
}
isNew_ = -1;
changedFields_.clear();
}
// When loading a QJsonObject, all the values are cleared and replaced by those
// from the QJsonObject. All the fields are marked as changed since it's
// assumed that the object comes from the web service.
void BaseModel::loadJsonObject(const QJsonObject &jsonObject) {
values_.clear();
changedFields_.clear();
QVector<BaseModel::Field> fields = BaseModel::tableFields(table());
foreach (BaseModel::Field field, fields) {
setValue(field.name, jsonObject[field.name], field.type);
}
isNew_ = 1;
}
void BaseModel::patchJsonObject(const QJsonObject &jsonObject) {
QVector<BaseModel::Field> fields = BaseModel::tableFields(table());
foreach (BaseModel::Field field, fields) {
if (!jsonObject.contains(field.name)) continue;
setValue(field.name, jsonObject[field.name], field.type);
}
}
QHash<QString, BaseModel::Value> BaseModel::values() const {
return values_;
}
BaseModel::Value BaseModel::value(const QString &name) const {
if (!valueIsSet(name)) {
qCritical() << "Value does not exist" << name;
return Value();
}
return values_[name];
}
bool BaseModel::valueIsSet(const QString &name) const {
return values_.contains(name);
}
void BaseModel::setValue(const QString &name, const BaseModel::Value &value) {
if (!values_.contains(name)) {
values_.insert(name, value);
changedFields_.insert(name, true);
} else {
Value& v = values_[name];
if (v.isEqual(value)) return;
values_.insert(name, value);
changedFields_.insert(name, true);
}
}
void BaseModel::setValue(const QString &name, int value) {
setValue(name, Value(value));
}
void BaseModel::setValue(const QString &name, const QJsonValue &value, QMetaType::Type type) {
if (type == QMetaType::QString) {
setValue(name, value.toString());
} else if (type == QMetaType::Int) {
setValue(name, value.toInt());
} else {
qFatal("Unsupported value type %s %d", name.toStdString().c_str(), type);
}
}
//void BaseModel::setValues(const QHash<QString, BaseModel::Value> values) {
// values_ = values;
//}
BaseModel::Value BaseModel::id() const {
if (!valueIsSet(primaryKey())) return QVariant();
return value(primaryKey());
}
QString BaseModel::idString() const {
return id().toString();
}
QString BaseModel::valuesToString() const {
QString s;
for (QHash<QString, Value>::const_iterator it = values_.begin(); it != values_.end(); ++it) {
if (s != "") s += "\n";
s += it.key() + " = " + it.value().toString();
}
return s;
}
void BaseModel::clone(const BaseModel &baseModel) {
values_ = baseModel.values_;
changedFields_.clear();
isNew_ = false;
table_ = baseModel.table_;
}
QString BaseModel::tableName(Table t) {
if (t == jop::FoldersTable) return "folders";
if (t == jop::NotesTable) return "notes";
if (t == jop::ChangesTable) return "changes";
qFatal("Unknown table %d", t);
}
QVariant BaseModel::cacheGet(const QString &key) {
if (!BaseModel::cache_.contains(key)) return QVariant();
return cache_[key];
}
void BaseModel::cacheSet(const QString &key, const QVariant &value) {
BaseModel::cache_[key] = value;
}
void BaseModel::cacheDelete(const QString &key) {
BaseModel::cache_.remove(key);
}
QString BaseModel::title() const {
return value("title").toString();
}
void BaseModel::setValue(const QString &name, const QString &value) {
setValue(name, Value(value));
}
void BaseModel::setValue(const QString& name, const QVariant& value) {
setValue(name, Value(value));
}
BaseModel::Value::Value() {}
BaseModel::Value::Value(const QString &v) {
type_ = QMetaType::QString;
stringValue_ = v;
}
BaseModel::Value::Value(int v) {
type_ = QMetaType::Int;
intValue_ = v;
}
BaseModel::Value::Value(const QVariant &v) {
type_ = (QMetaType::Type)v.type();
if (type_ == QMetaType::QString) {
stringValue_ = v.toString();
} else if (type_ == QMetaType::Int) {
intValue_ = v.toInt();
} else {
// Creates an invalid Value, which is what we want
}
}
int BaseModel::Value::toInt() const {
return intValue_;
}
QString BaseModel::Value::toString() const {
if (type_ == QMetaType::QString) return stringValue_;
if (type_ == QMetaType::Int) return QString::number(intValue_);
return QString("");
}
QVariant BaseModel::Value::toQVariant() const {
QMetaType::Type t = type();
if (t == QMetaType::QString) return QVariant(toString());
if (t == QMetaType::Int) return QVariant(toInt());
return QVariant();
}
QMetaType::Type BaseModel::Value::type() const {
return type_;
}
bool BaseModel::Value::isValid() const {
return type_ > 0;
}
bool BaseModel::Value::isEqual(const BaseModel::Value &v) const {
QMetaType::Type type = v.type();
if (this->type() != type) return false;
if (type == QMetaType::QString) return toString() == v.toString();
if (type == QMetaType::Int) return toInt() == v.toInt();
qCritical() << "Unreachable";
return false;
}

View File

@ -1,112 +0,0 @@
#ifndef BASEMODEL_H
#define BASEMODEL_H
#include <stable.h>
#include "enum.h"
namespace jop {
class BaseModel : public QObject {
Q_OBJECT
Q_PROPERTY(QString title READ title)
Q_PROPERTY(QString id READ idString)
public:
struct Field {
QString name;
QMetaType::Type type;
};
class Value {
public:
Value();
Value(const QString& v);
Value(int v);
Value(const QVariant& v);
int toInt() const;
QString toString() const;
QVariant toQVariant() const;
QMetaType::Type type() const;
bool isValid() const;
bool isEqual(const Value& v) const;
private:
QMetaType::Type type_;
QString stringValue_;
int intValue_;
};
BaseModel();
QStringList changedFields() const;
static int count(jop::Table table, const QString &parentId);
bool load(const QString& id);
bool loadByField(const QString& parentId, const QString& field, const QString& fieldValue);
bool reload();
virtual bool save(bool trackChanges = true);
virtual bool dispose();
Table table() const;
virtual QString primaryKey() const;
virtual bool primaryKeyIsUuid() const;
virtual bool trackChanges() const;
virtual QString displayTitle() const;
bool isNew() const;
static QVector<BaseModel::Field> tableFields(Table table);
static bool hasField(jop::Table table, const QString& name);
static QStringList tableFieldNames(Table table);
static QString sqlTableFields(Table table);
static bool isValidFieldName(Table table, const QString& name);
static void deleteAll(Table table);
void loadSqlQuery(const QSqlQuery& query);
void loadJsonObject(const QJsonObject& jsonObject);
void patchJsonObject(const QJsonObject& jsonObject);
QHash<QString, Value> values() const;
Value value(const QString& name) const;
bool valueIsSet(const QString& name) const;
void setValue(const QString& name, const Value& value);
void setValue(const QString& name, const QVariant& value);
void setValue(const QString& name, const QString& value);
void setValue(const QString& name, int value);
void setValue(const QString& name, const QJsonValue& value, QMetaType::Type type);
//void setValues(const QHash<QString, Value> values);
Value id() const;
QString valuesToString() const;
void clone(const BaseModel& baseModel);
static QString tableName(Table t);
protected:
QHash<QString, bool> changedFields_;
QHash<QString, Value> values_;
int isNew_;
jop::Table table_;
static QVariant cacheGet(const QString& key);
static void cacheSet(const QString& key, const QVariant& value);
static void cacheDelete(const QString& key);
static QMap<int, QVector<BaseModel::Field>> tableFields_;
static QHash<QString, QVariant> cache_;
public slots:
QString title() const;
QString idString() const;
};
}
#endif // BASEMODEL_H

View File

@ -1,120 +0,0 @@
#include "change.h"
#include "database.h"
using namespace jop;
//Table Change::table() const {
// return jop::ChangesTable;
//}
Change::Change() : BaseModel() {
table_ = jop::ChangesTable;
}
std::vector<Change*> Change::all(int limit) {
QString sql = QString("SELECT %1 FROM %2 ORDER BY id ASC LIMIT %3")
.arg(BaseModel::tableFieldNames(jop::ChangesTable).join(","))
.arg(BaseModel::tableName(jop::ChangesTable))
.arg(QString::number(limit));
QSqlQuery q(sql);
jop::db().execQuery(q);
std::vector<Change*> output;
while (q.next()) {
Change* change(new Change());
change->loadSqlQuery(q);
output.push_back(change);
}
return output;
}
void Change::mergedChanges(std::vector<Change*>& changes) {
QStringList createdItems;
QStringList deletedItems;
QHash<QString, Change*> itemChanges;
for (size_t i = 0; i < changes.size(); i++) {
Change* change = changes[i];
QString itemId = change->value("item_id").toString();
Change::Type type = (Change::Type)change->value("type").toInt();
if (type == Change::Create) {
createdItems.push_back(itemId);
} else if (type == Change::Delete) {
deletedItems.push_back(itemId);
}
if (itemChanges.contains(itemId) && type == Change::Update) {
// Merge all the "Update" event into one.
Change* existingChange = itemChanges[itemId];
existingChange->addMergedField(change->value("item_field").toString());
} else {
itemChanges[itemId] = change;
}
}
std::vector<Change*> output;
for (QHash<QString, Change*>::iterator it = itemChanges.begin(); it != itemChanges.end(); ++it) {
QString itemId = it.key();
Change* change = it.value();
if (createdItems.contains(itemId) && deletedItems.contains(itemId)) {
// Item both created then deleted - skip
continue;
}
if (deletedItems.contains(itemId)) {
// Item was deleted at some point - just return one 'delete' event
change->setValue("type", Change::Delete);
} else if (createdItems.contains(itemId)) {
// Item was created then updated - just return one 'create' event with the latest changes
change->setValue("type", Change::Create);
}
output.push_back(change);
}
// Delete the changes that are now longer needed (have been merged)
for (size_t i = 0; i < changes.size(); i++) {
Change* c1 = changes[i];
bool found = false;
for (size_t j = 0; j < output.size(); j++) {
Change* c2 = output[i];
if (c1 == c2) {
found = true;
break;
}
}
if (!found) {
delete c1; c1 = NULL;
}
}
changes = output;
}
void Change::addMergedField(const QString &name) {
if (mergedFields_.contains(name)) return;
mergedFields_.push_back(name);
}
QStringList Change::mergedFields() const {
QStringList output(mergedFields_);
QString itemField = value("item_field").toString();
if (!mergedFields_.contains(itemField)) {
output.push_back(itemField);
}
return output;
}
void Change::disposeByItemId(const QString &itemId) {
QString sql = QString("DELETE FROM %1 WHERE item_id = :item_id").arg(BaseModel::tableName(jop::ChangesTable));
QSqlQuery q = jop::db().prepare(sql);
q.bindValue(":item_id", itemId);
jop::db().execQuery(q);
}

View File

@ -1,32 +0,0 @@
#ifndef CHANGE_H
#define CHANGE_H
#include <stable.h>
#include "models/basemodel.h"
namespace jop {
class Change : public BaseModel {
public:
enum Type { Undefined, Create, Update, Delete };
Change();
static std::vector<Change*> all(int limit = 100);
static void mergedChanges(std::vector<Change*> &changes);
static void disposeByItemId(const QString& itemId);
void addMergedField(const QString& name);
QStringList mergedFields() const;
private:
QStringList mergedFields_;
};
}
#endif // CHANGE_H

View File

@ -1,171 +0,0 @@
#include "models/folder.h"
#include "database.h"
namespace jop {
Folder::Folder() : Item() {
table_ = jop::FoldersTable;
}
//Table Folder::table() const {
// return jop::FoldersTable;
//}
bool Folder::primaryKeyIsUuid() const {
return true;
}
bool Folder::trackChanges() const {
return true;
}
int Folder::noteCount() const {
QSqlQuery q = jop::db().prepare(QString("SELECT count(*) AS row_count FROM %1 WHERE parent_id = :parent_id").arg(BaseModel::tableName(jop::NotesTable)));
q.bindValue(":parent_id", id().toString());
jop::db().execQuery(q);
q.next();
return q.value(0).toInt();
}
std::unique_ptr<Folder> Folder::root() {
std::unique_ptr<Folder> folder(new Folder());
return std::move(folder);
}
std::vector<std::unique_ptr<BaseModel>> Folder::children(const QString &orderBy, int limit, int offset) const {
std::vector<std::unique_ptr<BaseModel>> output;
std::vector<jop::Table> tables;
tables.push_back(jop::FoldersTable);
tables.push_back(jop::NotesTable);
for (size_t tableIndex = 0; tableIndex < tables.size(); tableIndex++) {
jop::Table table = tables[tableIndex];
QString sql = QString("SELECT %1 FROM %2 WHERE parent_id = :parent_id ORDER BY %3 %4 %5")
.arg(BaseModel::sqlTableFields(table))
.arg(BaseModel::tableName(table))
.arg(orderBy)
.arg(limit ? QString("LIMIT %1").arg(limit) : "")
.arg(limit && offset ? QString("OFFSET %1").arg(offset) : "");
QSqlQuery q = jop::db().prepare(sql);
q.bindValue(":parent_id", idString());
jop::db().execQuery(q);
if (!jop::db().errorCheck(q)) return output;
while (q.next()) {
if (table == jop::FoldersTable) {
std::unique_ptr<BaseModel> folder(new Folder());
folder->loadSqlQuery(q);
output.push_back(std::move(folder));
} else if (table == jop::NotesTable) {
std::unique_ptr<BaseModel> note(new Note());
note->loadSqlQuery(q);
output.push_back(std::move(note));
}
}
}
return output;
}
std::vector<std::unique_ptr<Note>> Folder::notes(const QString &orderBy, int limit, int offset) const {
std::vector<std::unique_ptr<Note>> output;
QSqlQuery q = jop::db().prepare(QString("SELECT %1 FROM %2 WHERE parent_id = :parent_id ORDER BY %3 LIMIT %4 OFFSET %5")
.arg(BaseModel::sqlTableFields(jop::NotesTable))
.arg(BaseModel::tableName(jop::NotesTable))
.arg(orderBy)
.arg(limit)
.arg(offset));
q.bindValue(":parent_id", id().toString());
jop::db().execQuery(q);
if (!jop::db().errorCheck(q)) return output;
while (q.next()) {
std::unique_ptr<Note> note(new Note());
note->loadSqlQuery(q);
output.push_back(std::move(note));
}
return output;
}
std::vector<std::unique_ptr<Folder>> Folder::pathToFolders(const QString& path, bool returnLast, int& errorCode) {
std::vector<std::unique_ptr<Folder>> output;
if (!path.length()) return output;
QStringList parts = path.split('/');
QString parentId("");
int toIndex = returnLast ? parts.size() : parts.size() - 1;
for (int i = 0; i < toIndex; i++) {
std::unique_ptr<Folder> folder(new Folder());
bool ok = folder->loadByField(parentId, "title", parts[i]);
if (!ok) {
// qWarning() << "Folder does not exist" << parts[i];
errorCode = 1;
return output;
}
output.push_back(std::move(folder));
}
return output;
}
QString Folder::pathBaseName(const QString& path) {
QStringList parts = path.split('/');
return parts[parts.size() - 1];
}
int Folder::noteIndexById(const QString &orderBy, const QString& id) const {
qDebug() << "Folder::noteIndexById" << orderBy << id;
QSqlQuery q = jop::db().prepare(QString("SELECT id, %2 FROM %1 WHERE parent_id = :parent_id ORDER BY %2")
.arg(BaseModel::tableName(jop::NotesTable))
.arg(orderBy));
q.bindValue(":parent_id", idString());
jop::db().execQuery(q);
if (!jop::db().errorCheck(q)) return -1;
int index = 0;
while (q.next()) {
QString qId = q.value(0).toString();
QString qTitle = q.value(1).toString();
qDebug() << "CURRENT" << qId << qTitle;
if (qId == id) return index;
index++;
}
return -1;
}
int Folder::count(const QString &parentId) {
return BaseModel::count(jop::FoldersTable, parentId);
}
// std::vector<std::unique_ptr<Folder>> Folder::all(const QString& parentId, const QString &orderBy) {
// QSqlQuery q = jop::db().prepare(QString("SELECT %1 FROM %2 WHERE parent_id = :parent_id ORDER BY %3")
// .arg(BaseModel::tableFieldNames(jop::FoldersTable).join(","))
// .arg(BaseModel::tableName(jop::FoldersTable))
// .arg(orderBy));
// q.bindValue(":parent_id", parentId);
// jop::db().execQuery(q);
// std::vector<std::unique_ptr<Folder>> output;
// //if (!jop::db().errorCheck(q)) return output;
// while (q.next()) {
// std::unique_ptr<Folder> folder(new Folder());
// folder->loadSqlQuery(q);
// output.push_back(std::move(folder));
// }
// return output;
// }
QString Folder::displayTitle() const {
return QString("%1/").arg(value("title").toString());
}
}

View File

@ -1,35 +0,0 @@
#ifndef FOLDER_H
#define FOLDER_H
#include <stable.h>
#include "models/item.h"
#include "models/note.h"
namespace jop {
class Folder : public Item {
Q_OBJECT
public:
Folder();
static int count(const QString& parentId);
static std::vector<std::unique_ptr<Folder>> pathToFolders(const QString& path, bool returnLast, int& errorCode);
static QString pathBaseName(const QString& path);
static std::unique_ptr<Folder> root();
bool primaryKeyIsUuid() const;
bool trackChanges() const;
int noteCount() const;
std::vector<std::unique_ptr<Note>> notes(const QString& orderBy, int limit, int offset = 0) const;
std::vector<std::unique_ptr<BaseModel>> children(const QString &orderBy = QString("title"), int limit = 0, int offset = 0) const;
int noteIndexById(const QString& orderBy, const QString &id) const;
QString displayTitle() const;
};
}
#endif // FOLDER_H

View File

@ -1,106 +0,0 @@
#include "foldercollection.h"
#include "databaseutils.h"
#include "dispatcher.h"
#include "uuid.h"
using namespace jop;
// Note: although parentId is supplied, it is currently not being used.
FolderCollection::FolderCollection(Database& db, const QString& parentId, const QString& orderBy) {
db_ = db;
parentId_ = parentId;
orderBy_ = orderBy;
connect(&jop::dispatcher(), SIGNAL(folderCreated(const QString&)), this, SLOT(dispatcher_folderCreated(QString)));
}
Folder FolderCollection::at(int index) const {
if (cache_.size()) {
if (index < 0 || index >= cache_.size()) {
qWarning() << "Invalid folder index:" << index;
return Folder();
}
return cache_[index];
}
QSqlQuery q = db_.query("SELECT " + Folder::dbFields().join(",") + " FROM folders ORDER BY " + orderBy_);
q.exec();
while (q.next()) {
Folder folder;
folder.fromSqlQuery(q);
cache_.push_back(folder);
}
if (!cache_.size()) {
qWarning() << "Invalid folder index:" << index;
return Folder();
} else {
return at(index);
}
}
// TODO: cache result
int FolderCollection::count() const {
QSqlQuery q = db_.query("SELECT count(*) as row_count FROM folders");
q.exec();
q.next();
return q.value(0).toInt();
}
Folder FolderCollection::byId(const QString& id) const {
int index = idToIndex(id);
return at(index);
}
int FolderCollection::idToIndex(const QString &id) const {
int count = this->count();
for (int i = 0; i < count; i++) {
Folder folder = at(i);
if (folder.id() == id) return i;
}
return -1;
}
QString FolderCollection::indexToId(int index) const {
Folder folder = at(index);
return folder.id();
}
void FolderCollection::update(const QString &id, QStringList fields, VariantVector values) {
if (!fields.contains("synced")) {
fields.push_back("synced");
values.push_back(QVariant(0));
}
QSqlQuery q = db_.buildSqlQuery(Database::Update, "folders", fields, values, "id = \"" + id + "\"");
q.exec();
cache_.clear();
emit changed(0, count() - 1, fields);
}
void FolderCollection::add(QStringList fields, VariantVector values) {
fields.push_back("synced");
values.push_back(QVariant(0));
fields.push_back("id");
values.push_back(uuid::createUuid());
QSqlQuery q = db_.buildSqlQuery(Database::Insert, "folders", fields, values);
q.exec();
cache_.clear();
emit changed(0, count() - 1, fields);
}
void FolderCollection::remove(const QString& id) {
QSqlQuery q(db_.database());
q.prepare("DELETE FROM folders WHERE id = :id");
q.bindValue(":id", id);
q.exec();
cache_.clear();
emit changed(0, count(), QStringList());
}
void FolderCollection::dispatcher_folderCreated(const QString &id) {
}

View File

@ -1,50 +0,0 @@
#ifndef FOLDERCOLLECTION_H
#define FOLDERCOLLECTION_H
#include <stable.h>
#include "database.h"
#include "models/note.h"
#include "models/folder.h"
#include "sparsevector.hpp"
#include "simpletypes.h"
namespace jop {
class FolderCollection : public QObject {
Q_OBJECT
public:
//FolderCollection();
FolderCollection(Database& db, const QString &parentId, const QString& orderBy);
Folder at(int index) const;
int count() const;
Folder byId(const QString &id) const;
int idToIndex(const QString& id) const;
QString indexToId(int index) const;
void update(const QString& id, QStringList fields, VariantVector values);
void add(QStringList fields, VariantVector values);
void remove(const QString &id);
private:
QString parentId_;
QString orderBy_;
Database db_;
mutable QVector<Folder> cache_;
signals:
void changed(int from, int to, const QStringList& fields);
public slots:
void dispatcher_folderCreated(const QString& id);
};
}
#endif // FOLDERCOLLECTION_H

View File

@ -1,139 +0,0 @@
#include "foldermodel.h"
#include "uuid.h"
#include "dispatcher.h"
using namespace jop;
FolderModel::FolderModel() : AbstractListModel(), orderBy_("title") {
connect(&dispatcher(), SIGNAL(folderCreated(QString)), this, SLOT(dispatcher_folderCreated(QString)));
connect(&dispatcher(), SIGNAL(folderUpdated(QString)), this, SLOT(dispatcher_folderUpdated(QString)));
connect(&dispatcher(), SIGNAL(folderDeleted(QString)), this, SLOT(dispatcher_folderDeleted(QString)));
connect(&dispatcher(), SIGNAL(allFoldersDeleted()), this, SLOT(dispatcher_allFoldersDeleted()));
}
const BaseModel *FolderModel::atIndex(int index) const {
if (cache_.size()) {
if (index < 0 || index >= (int)cache_.size()) {
qWarning() << "Invalid folder index:" << index;
return NULL;
}
return cacheGet(index);
}
cacheClear();
qFatal("TODO: replace with root::children()");
//cache_ = Folder::all(orderBy_);
if (!cache_.size()) {
qWarning() << "Invalid folder index:" << index;
return NULL;
} else {
return atIndex(index);
}
}
int FolderModel::idToIndex(const QString &id) const {
int count = this->rowCount();
for (int i = 0; i < count; i++) {
Folder* folder = (Folder*)atIndex(i);
if (!folder) return -1;
if (folder->idString() == id) return i;
}
return -1;
}
//bool FolderModel::setTitle(int index, const QVariant &value, int role) {
// return setData(this->index(index), value, role);
//}
//bool FolderModel::setData(int index, const QVariant &value, int role) {
// return BaseModel::setData(this->index(index), value, role);
//}
void FolderModel::addData(const QString &title) {
Folder folder;
folder.setValue("title", title);
if (!folder.save()) return;
lastInsertId_ = folder.id().toString();
}
void FolderModel::deleteData(const int index) {
Folder* folder = (Folder*)atIndex(index);
if (!folder) return;
folder->dispose();
}
int FolderModel::baseModelCount() const {
return Folder::count("");
}
const BaseModel *FolderModel::cacheGet(int index) const {
return cache_[index].get();
}
void FolderModel::cacheSet(int index, BaseModel* baseModel) const {
Folder* folder = static_cast<Folder*>(baseModel);
cache_[index] = std::unique_ptr<Folder>(folder);
}
bool FolderModel::cacheIsset(int index) const {
return index > 0 && (int)cache_.size() > index;
}
void FolderModel::cacheClear() const {
cache_.clear();
}
// TODO: instead of clearing the whole cache every time, the individual items
// could be created/updated/deleted
void FolderModel::dispatcher_folderCreated(const QString &folderId) {
qDebug() << "FolderModel Folder created" << folderId;
cacheClear();
int from = 0;
int to = rowCount() - 1;
QVector<int> roles;
roles << Qt::DisplayRole;
// Necessary to make sure a new item is added to the view, even
// though it might not be positioned there due to sorting
beginInsertRows(QModelIndex(), to, to);
endInsertRows();
emit dataChanged(this->index(from), this->index(to), roles);
}
void FolderModel::dispatcher_folderUpdated(const QString &folderId) {
qDebug() << "FolderModel Folder udpated" << folderId;
cacheClear();
QVector<int> roles;
roles << Qt::DisplayRole;
emit dataChanged(this->index(0), this->index(rowCount() - 1), roles);
}
void FolderModel::dispatcher_folderDeleted(const QString &folderId) {
qDebug() << "FolderModel Folder deleted" << folderId;
int index = idToIndex(folderId);
if (index < 0) return;
cacheClear();
beginRemoveRows(QModelIndex(), index, index);
endRemoveRows();
}
void FolderModel::dispatcher_allFoldersDeleted() {
qDebug() << "FolderModel All folders deleted";
cacheClear();
beginResetModel();
endResetModel();
}

View File

@ -1,52 +0,0 @@
#ifndef FOLDERMODEL_H
#define FOLDERMODEL_H
#include <stable.h>
#include "models/folder.h"
#include "models/abstractlistmodel.h"
#include "database.h"
namespace jop {
class FolderModel : public AbstractListModel {
Q_OBJECT
public:
FolderModel();
void addFolder(Folder* folder);
const BaseModel* atIndex(int index) const;
protected:
int baseModelCount() const;
const BaseModel *cacheGet(int index) const;
void cacheSet(int index, BaseModel *baseModel) const;
bool cacheIsset(int index) const;
void cacheClear() const;
int cacheSize() const;
private:
QList<Folder> folders_;
QString orderBy_;
mutable std::vector<std::unique_ptr<Folder>> cache_;
public slots:
void addData(const QString& title);
void deleteData(const int index);
int idToIndex(const QString& id) const;
void dispatcher_folderCreated(const QString& folderId);
void dispatcher_folderUpdated(const QString& folderId);
void dispatcher_folderDeleted(const QString& folderId);
void dispatcher_allFoldersDeleted();
};
}
#endif // FOLDERMODEL_H

View File

@ -1,61 +0,0 @@
#include "models/item.h"
#include "constants.h"
namespace jop {
Item::Item() {}
QString Item::serialize() const {
QStringList shownKeys;
shownKeys << "author" << "longitude" << "latitude" << "is_todo" << "todo_due" << "todo_completed";
QStringList output;
output << value("title").toString();
output << "";
output << value("body").toString();
output << "================================================================================";
QHash<QString, Value> values = this->values();
for (int i = 0; i < shownKeys.size(); i++) {
QString key = shownKeys[i];
if (!values.contains(key)) continue;
output << QString("%1: %2").arg(key).arg(values[key].toString());
}
return output.join(NEW_LINE);
}
void Item::patchFriendlyString(const QString& patch) {
QStringList lines = patch.split(jop::NEW_LINE);
QString title("");
if (lines.size() >= 1) {
title = lines[0];
}
bool foundDelimiter = false;
QString body("");
for (int i = 1; i < lines.size(); i++) {
QString line = lines[i];
if (line.indexOf("================================================================================") == 0) {
foundDelimiter = true;
continue;
}
if (!foundDelimiter && line.trimmed() == "" && i == 1) continue; // Skip the first \n
if (!foundDelimiter) {
if (!body.isEmpty()) body += "\n";
body += line;
} else {
int colonIndex = line.indexOf(':');
QString propName = line.left(colonIndex).trimmed();
QString propValue = line.right(line.length() - colonIndex - 1).trimmed();
setValue(propName, propValue);
}
}
setValue("title", title);
setValue("body", body);
}
}

View File

@ -1,24 +0,0 @@
#ifndef ITEM_H
#define ITEM_H
#include <stable.h>
#include "models/basemodel.h"
namespace jop {
class Item : public BaseModel {
Q_OBJECT
public:
Item();
QString serialize() const;
void patchFriendlyString(const QString& patch);
};
}
#endif // ITEM_H

View File

@ -1,19 +0,0 @@
#include "note.h"
using namespace jop;
Note::Note() : Item() {
table_ = jop::NotesTable;
}
//Table Note::table() const {
// return jop::NotesTable;
//}
bool Note::primaryKeyIsUuid() const {
return true;
}
bool Note::trackChanges() const {
return true;
}

View File

@ -1,21 +0,0 @@
#ifndef NOTE_H
#define NOTE_H
#include <stable.h>
#include "models/item.h"
namespace jop {
class Note : public Item {
public:
Note();
bool primaryKeyIsUuid() const;
bool trackChanges() const;
};
}
#endif // NOTE_H

View File

@ -1,83 +0,0 @@
#include "notecollection.h"
using namespace jop;
NoteCollection::NoteCollection() {}
NoteCollection::NoteCollection(Database& db, const QString& parentId, const QString& orderBy) {
db_ = db;
parentId_ = parentId;
orderBy_ = orderBy;
}
Note NoteCollection::at(int index) const {
return Note();
// if (parentId_ == "") return Note();
// if (cache_.isset(index)) return cache_.get(index);
// std::vector<int> indexes = cache_.availableBufferAround(index, 32);
// if (!indexes.size()) {
// qWarning() << "Couldn't acquire buffer"; // "Cannot happen"
// return Note();
// }
// int from = indexes[0];
// int to = indexes[indexes.size() - 1];
// QSqlQuery q = db_.query("SELECT id, title, body FROM notes WHERE parent_id = :parent_id ORDER BY " + orderBy_ + " LIMIT " + QString::number(to - from + 1) + " OFFSET " + QString::number(from));
// q.bindValue(":parent_id", parentId_);
// q.exec();
// int noteIndex = from;
// while (q.next()) {
// Note note;
// note.setId(q.value(0).toString());
// note.setTitle(q.value(1).toString());
// note.setBody(q.value(2).toString());
// cache_.set(noteIndex, note);
// noteIndex++;
// }
// return cache_.get(index);
}
// TODO: cache result
int NoteCollection::count() const {
return 0;
// if (parentId_ == "") return 0;
// QSqlQuery q = db_.query("SELECT count(*) as row_count FROM notes WHERE parent_id = :parent_id");
// q.bindValue(":parent_id", parentId_);
// q.exec();
// q.next();
// return q.value(0).toInt();
}
Note NoteCollection::byId(const QString& id) const {
return Note();
// std::vector<int> indexes = cache_.indexes();
// for (size_t i = 0; i < indexes.size(); i++) {
// Note note = cache_.get(indexes[i]);
// if (note.id() == id) return note;
// }
// QSqlQuery q = db_.query("SELECT id, title, body FROM notes WHERE id = :id");
// q.bindValue(":id", id);
// q.exec();
// q.next();
// if (!q.isValid()) {
// qWarning() << "Invalid note ID:" << id;
// return Note();
// }
// // TODO: refactor creation of note from SQL query object
// Note note;
// note.setId(q.value(0).toString());
// note.setTitle(q.value(1).toString());
// note.setBody(q.value(2).toString());
// return note;
}

View File

@ -1,33 +0,0 @@
#ifndef NOTECOLLECTION_H
#define NOTECOLLECTION_H
#include <stable.h>
#include "database.h"
#include "models/note.h"
#include "sparsevector.hpp"
namespace jop {
class NoteCollection {
public:
NoteCollection();
NoteCollection(Database& db, const QString &parentId, const QString& orderBy);
Note at(int index) const;
int count() const;
Note byId(const QString &id) const;
private:
QString parentId_;
QString orderBy_;
Database db_;
mutable SparseVector<Note> cache_;
};
}
#endif // NOTECOLLECTION_H

View File

@ -1,149 +0,0 @@
#include "notemodel.h"
#include "dispatcher.h"
namespace jop {
NoteModel::NoteModel() : AbstractListModel() {
folderId_ = "";
orderBy_ = "title";
connect(&dispatcher(), SIGNAL(noteCreated(QString)), this, SLOT(dispatcher_noteCreated(QString)), Qt::QueuedConnection);
connect(&dispatcher(), SIGNAL(noteUpdated(QString)), this, SLOT(dispatcher_noteUpdated(QString)), Qt::QueuedConnection);
connect(&dispatcher(), SIGNAL(noteDeleted(QString)), this, SLOT(dispatcher_noteDeleted(QString)), Qt::QueuedConnection);
}
const Note *NoteModel::atIndex(int index) const {
if (folderId_ == "") return NULL;
if (index < 0 || index >= rowCount()) return NULL;
if (cache_.isset(index)) return cache_.get(index);
std::vector<int> indexes = cache_.availableBufferAround(index, 32);
if (!indexes.size()) {
qCritical() << "Couldn't acquire buffer"; // "Cannot happen"
return NULL;
}
int from = indexes[0];
int to = indexes[indexes.size() - 1];
// Folder folder = this->folder();
// qDebug() << "NoteModel: cache recreated";
// std::vector<std::unique_ptr<Note>> notes = folder.notes(orderBy_, to - from + 1, from);
// int noteIndex = from;
// for (int i = 0; i < notes.size(); i++) {
// cache_.set(noteIndex, notes[i].release());
// noteIndex++;
// }
return cache_.get(index);
}
void NoteModel::setFolderId(const QString &v) {
if (v == folderId_) return;
beginResetModel();
cache_.clear();
folderId_ = v;
endResetModel();
}
//Folder NoteModel::folder() const {
// Folder folder;
// //if (folderId_ == "") return folder;
// folder.load(folderId_);
// return folder;
//}
int NoteModel::idToIndex(const QString &id) const {
std::vector<int> indexes = cache_.indexes();
for (size_t i = 0; i < indexes.size(); i++) {
Note* note = cache_.get(indexes[i]);
if (note->idString() == id) return indexes[i];
}
return 0;
//Folder f = this->folder();
//return f.noteIndexById(orderBy_, id);
}
void NoteModel::addData(const QString &title) {
Note note;
note.setValue("title", title);
note.setValue("parent_id", folderId_);
if (!note.save()) return;
lastInsertId_ = note.idString();
}
void NoteModel::deleteData(int index) {
Note* note = (Note*)atIndex(index);
if (!note) return;
note->dispose();
}
int NoteModel::baseModelCount() const {
return 0;
//return folder().noteCount();
}
const BaseModel *NoteModel::cacheGet(int index) const {
return static_cast<BaseModel*>(cache_.get(index));
}
void NoteModel::cacheSet(int index, BaseModel *baseModel) const {
cache_.set(index, static_cast<Note*>(baseModel));
}
bool NoteModel::cacheIsset(int index) const {
return cache_.isset(index);
}
void NoteModel::cacheClear() const {
qDebug() << "NoteModel::cacheClear()";
cache_.clear();
}
void NoteModel::dispatcher_noteCreated(const QString &noteId) {
qDebug() << "NoteModel note created" << noteId;
cacheClear();
int from = 0;
int to = rowCount() - 1;
QVector<int> roles;
roles << Qt::DisplayRole;
// Necessary to make sure a new item is added to the view, even
// though it might not be positioned there due to sorting
beginInsertRows(QModelIndex(), to, to);
endInsertRows();
emit dataChanged(this->index(from), this->index(to), roles);
}
void NoteModel::dispatcher_noteUpdated(const QString &noteId) {
qDebug() << "NoteModel note udpated" << noteId;
cacheClear();
QVector<int> roles;
roles << Qt::DisplayRole;
emit dataChanged(this->index(0), this->index(rowCount() - 1), roles);
}
void NoteModel::dispatcher_noteDeleted(const QString &noteId) {
qDebug() << "NoteModel note deleted" << noteId;
int index = idToIndex(noteId);
qDebug() << "index" << index;
if (index < 0) return;
cacheClear();
beginRemoveRows(QModelIndex(), index, index);
endRemoveRows();
}
}

View File

@ -1,51 +0,0 @@
#ifndef NOTEMODEL_H
#define NOTEMODEL_H
#include <stable.h>
#include "models/folder.h"
#include "sparsevector.hpp"
#include "models/abstractlistmodel.h"
namespace jop {
class NoteModel : public AbstractListModel {
Q_OBJECT
public:
NoteModel();
const Note* atIndex(int index) const;
void setFolderId(const QString& v);
//Folder folder() const;
public slots:
int idToIndex(const QString& id) const;
void addData(const QString& title);
void deleteData(int index);
void dispatcher_noteCreated(const QString& noteId);
void dispatcher_noteUpdated(const QString& noteId);
void dispatcher_noteDeleted(const QString& noteId);
protected:
int baseModelCount() const;
const BaseModel* cacheGet(int index) const;
void cacheSet(int index, BaseModel *baseModel) const;
bool cacheIsset(int index) const;
void cacheClear() const;
private:
QList<Note> notes_;
QString folderId_;
QString orderBy_;
mutable SparseVector<Note> cache_;
};
}
#endif // NOTEMODEL_H

View File

@ -1,38 +0,0 @@
#include "setting.h"
#include "database.h"
using namespace jop;
void Setting::setSettings(const QSettings::SettingsMap &map) {
jop::db().transaction();
jop::db().execQuery("DELETE FROM settings");
QString sql = "INSERT INTO settings (`key`, `value`, `type`) VALUES (:key, :value, :type)";
QSqlQuery query = jop::db().prepare(sql);
for (QSettings::SettingsMap::const_iterator it = map.begin(); it != map.end(); ++it) {
query.bindValue(":key", it.key());
query.bindValue(":value", it.value());
query.bindValue(":type", (int)it.value().type());
jop::db().execQuery(query);
}
jop::db().commit();
}
QSettings::SettingsMap Setting::settings() {
QSettings::SettingsMap output;
QSqlQuery query("SELECT key, value, type FROM settings");
jop::db().execQuery(query);
while (query.next()) {
QString key = query.value(0).toString();
QVariant val = query.value(1);
QMetaType::Type type = (QMetaType::Type)query.value(2).toInt();
if (type == QMetaType::Int) {
output[key] = QVariant(val.toInt());
} else if (type == QMetaType::QString) {
output[key] = QVariant(val.toString());
} else {
qCritical() << "Unsupported setting type" << key << val << type;
}
}
return output;
}

View File

@ -1,21 +0,0 @@
#ifndef SETTING_H
#define SETTING_H
#include <stable.h>
#include "models/basemodel.h"
namespace jop {
class Setting : public BaseModel {
public:
static void setSettings(const QSettings::SettingsMap &map);
static QSettings::SettingsMap settings();
};
}
#endif // SETTING_H

View File

@ -1,31 +0,0 @@
#include "paths.h"
using namespace jop;
QString configDir_ = "";
QString paths::configDir() {
if (configDir_ != "") return configDir_;
configDir_ = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/" + QCoreApplication::applicationName();
QDir d(configDir_);
if (!d.exists()) {
bool dirCreated = d.mkpath(".");
if (!dirCreated) qFatal("Cannot create config directory: %s", configDir_.toStdString().c_str());
}
return configDir_;
}
QString paths::databaseFile() {
return QString("%1/%2.sqlite").arg(configDir()).arg(QCoreApplication::applicationName());
}
QString paths::noteDraftsDir() {
QString output = QString("%1/note_drafts").arg(paths::configDir());
QDir d(output);
if (!d.exists()) {
bool dirCreated = d.mkpath(".");
if (!dirCreated) qFatal("Cannot create note draft directory: %s", output.toStdString().c_str());
}
return output;
}

View File

@ -1,16 +0,0 @@
#ifndef PATHS_H
#define PATHS_H
#include <stable.h>
namespace jop {
namespace paths {
QString configDir();
QString databaseFile();
QString noteDraftsDir();
}
}
#endif // PATHS_H

View File

@ -1,13 +0,0 @@
<RCC>
<qresource prefix="/">
<file>app.qml</file>
<file>ItemList.qml</file>
<file>NoteEditor.qml</file>
<file>AddButton.qml</file>
<file>EditableListItem.qml</file>
<file>LoginPage.qml</file>
<file>LoginPageForm.ui.qml</file>
<file>MainPage.qml</file>
<file>ItemList2.qml</file>
</qresource>
</RCC>

View File

@ -1,33 +0,0 @@
#include "qmlutils.h"
namespace jop {
namespace qmlUtils {
QVariant callQml(QObject* o, const QString &name, const QVariantList &args) {
QVariant returnedValue;
//qDebug() << "Going to call QML:" << name << args;
if (args.size() == 0) {
QMetaObject::invokeMethod(o, name.toStdString().c_str(), Q_RETURN_ARG(QVariant, returnedValue));
} else if (args.size() == 1) {
QMetaObject::invokeMethod(o, name.toStdString().c_str(), Q_RETURN_ARG(QVariant, returnedValue), Q_ARG(QVariant, args[0]));
} else if (args.size() == 2) {
QMetaObject::invokeMethod(o, name.toStdString().c_str(), Q_RETURN_ARG(QVariant, returnedValue), Q_ARG(QVariant, args[0]), Q_ARG(QVariant, args[1]));
} else if (args.size() == 3) {
QMetaObject::invokeMethod(o, name.toStdString().c_str(), Q_RETURN_ARG(QVariant, returnedValue), Q_ARG(QVariant, args[0]), Q_ARG(QVariant, args[1]), Q_ARG(QVariant, args[2]));
} else {
qFatal("qmlUtils::callQml: add support for more args!");
}
return returnedValue;
}
QObject* childFromProperty(QObject *o, const QString &propertyName) {
QVariant p = QQmlProperty(o, propertyName).read();
if (!p.isValid()) {
qCritical() << "Invalid QML property" << propertyName;
return NULL;
}
return qvariant_cast<QObject*>(p);
}
}
}

View File

@ -1,15 +0,0 @@
#ifndef QMLUTILS_H
#define QMLUTILS_H
#include <stable.h>
namespace jop {
namespace qmlUtils {
QVariant callQml(QObject* o, const QString &name, const QVariantList &args = QVariantList());
QObject* childFromProperty(QObject* o, const QString& propertyName);
}
}
#endif // QMLUTILS_H

View File

@ -1,87 +0,0 @@
CREATE TABLE folders (
id TEXT PRIMARY KEY,
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,
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 "",
todo_completed INT NOT NULL DEFAULT "",
source_application TEXT NOT NULL DEFAULT "",
application_data TEXT NOT NULL DEFAULT "",
`order` INT NOT NULL DEFAULT 0
);
CREATE TABLE tags (
id TEXT PRIMARY KEY,
title TEXT,
created_time INT,
updated_time INT
);
CREATE TABLE note_tags (
id INTEGER PRIMARY KEY,
note_id TEXT,
tag_id TEXT
);
CREATE TABLE resources (
id TEXT PRIMARY KEY,
title TEXT,
mime TEXT,
filename TEXT,
created_time INT,
updated_time INT
);
CREATE TABLE note_resources (
id INTEGER PRIMARY KEY,
note_id TEXT,
resource_id TEXT
);
CREATE TABLE version (
version INT
);
CREATE TABLE changes (
id INTEGER PRIMARY KEY,
`type` INT,
item_id TEXT,
item_type INT,
item_field TEXT
);
CREATE TABLE settings (
`key` TEXT PRIMARY KEY,
`value` TEXT,
`type` INT
);
--CREATE TABLE mimetypes (
-- id INT,
-- mime TEXT
--);
--CREATE TABLE mimetype_extensions (
-- id INTEGER PRIMARY KEY,
-- mimetype_id,
-- extension TEXT
--);
INSERT INTO version (version) VALUES (1);

View File

@ -1,53 +0,0 @@
#include "folderservice.h"
#include "uuid.h"
using namespace jop;
FolderService::FolderService() {}
FolderService::FolderService(Database &database) {
database_ = database;
}
int FolderService::count() const {
QSqlQuery q = database_.query("SELECT count(*) as row_count FROM folders");
q.exec();
q.next();
return q.value(0).toInt();
}
Folder FolderService::byId(const QString& id) const {
QSqlQuery q = database_.query("SELECT title, created_time FROM folders WHERE id = :id");
q.bindValue(":id", id);
q.exec();
q.next();
Folder output;
output.setId(id);
output.setTitle(q.value(0).toString());
output.setCreatedTime(q.value(1).toInt());
return output;
}
const QList<Folder> FolderService::overviewList() const {
if (cache_.size()) return cache_;
QList<Folder> output;
QSqlQuery q = database_.query("SELECT id, title FROM folders ORDER BY created_time DESC");
q.exec();
while (q.next()) {
Folder f;
f.setId(q.value(0).toString());
f.setTitle(q.value(1).toString());
f.setIsPartial(true);
output << f;
}
cache_ = output;
return cache_;
}
void FolderService::clearCache() {
cache_.clear();
}

View File

@ -1,31 +0,0 @@
#ifndef FOLDERSERVICE_H
#define FOLDERSERVICE_H
#include <stable.h>
#include "database.h"
#include "models/folder.h"
namespace jop {
class FolderService {
public:
FolderService();
FolderService(Database& database);
int count() const;
Folder byId(const QString &id) const;
//Folder partialAt(int index) const;
const QList<Folder> overviewList() const;
void clearCache();
private:
Database database_;
mutable QList<Folder> cache_;
};
}
#endif // FOLDERSERVICE_H

View File

@ -1,18 +0,0 @@
#include "notecache.h"
using namespace jop;
NoteCache::NoteCache() {
}
void NoteCache::add(QList<Note> notes) {
foreach (Note note, notes) {
//cache_[note.id()] = note;
}
}
std::pair<Note, bool> NoteCache::get(int id) const {
if (cache_.contains(id)) return std::make_pair(cache_[id], true);
return std::make_pair(Note(), true);
}

View File

@ -1,25 +0,0 @@
#ifndef NOTECACHE_H
#define NOTECACHE_H
#include <stable.h>
#include "models/note.h"
namespace jop {
class NoteCache {
public:
NoteCache();
void add(QList<Note> notes);
std::pair<Note, bool> get(int id) const;
private:
QMap<int, Note> cache_;
};
}
#endif // NOTECACHE_H

View File

@ -1,54 +0,0 @@
#include "noteservice.h"
using namespace jop;
NoteService::NoteService() {}
NoteService::NoteService(jop::Database &database) {
database_ = database;
}
int NoteService::count(const QString &parentFolderId) const {
QSqlQuery q = database_.query("SELECT count(*) as row_count FROM notes WHERE parent_id = :parent_id");
q.bindValue(":parent_id", parentFolderId);
q.exec();
q.next();
return q.value(0).toInt();
}
Note NoteService::byId(const QString &id) const {
Note n;
return n;
}
const QList<Note> NoteService::overviewList(const QString& folderId, int from, int to, const QString &orderBy) const {
QList<Note> output;
QSqlQuery q = database_.query("SELECT id, title FROM notes WHERE parent_id = :parent_id ORDER BY " + orderBy + " LIMIT " + QString::number(to - from) + " OFFSET " + QString::number(from));
q.bindValue(":parent_id", folderId);
q.exec();
while (q.next()) {
Note f;
f.setId(q.value(0).toString());
f.setTitle(q.value(1).toString());
f.setIsPartial(true);
output << f;
}
return output;
}
std::pair<const Note &, bool> NoteService::overviewAt(const QString &folderId, int index, const QString &orderBy) const {
QSqlQuery q = database_.query("SELECT id, title FROM notes WHERE parent_id = :parent_id ORDER BY " + orderBy + " LIMIT 1 OFFSET " + QString::number(index));
q.bindValue(":parent_id", folderId);
q.exec();
q.next();
if (!q.isValid()) return std::make_pair(Note(), false);
Note f;
f.setId(q.value(0).toString());
f.setTitle(q.value(1).toString());
f.setIsPartial(true);
return std::make_pair(f, true);
}

View File

@ -1,30 +0,0 @@
#ifndef NOTESERVICE_H
#define NOTESERVICE_H
#include <stable.h>
#include "database.h"
#include "models/note.h"
namespace jop {
class NoteService {
public:
NoteService();
NoteService(Database& database);
int count(const QString& parentFolderId) const;
Note byId(const QString& id) const;
const QList<Note> overviewList(const QString &folderId, int from, int to, const QString& orderBy) const;
std::pair<const Note&, bool> overviewAt(const QString& folderId, int index, const QString& orderBy) const;
private:
Database database_;
mutable QList<Note> cache_;
};
}
#endif // NOTESERVICE_H

View File

@ -1,40 +0,0 @@
#include "settings.h"
#include "models/setting.h"
using namespace jop;
Settings::Settings() : QSettings() {}
bool readSqlite(QIODevice &device, QSettings::SettingsMap &map) {
Q_UNUSED(device);
map = Setting::settings();
return true;
}
bool writeSqlite(QIODevice &device, const QSettings::SettingsMap &map) {
// HACK: QSettings requires a readable/writable file to be present
// for the custom handler to work. However, we don't need such a
// file since we write to the db. So to simulate it, we write once
// to that file. Without this, readSqlite in particular will never
// get called.
device.write("X", 1);
Setting::setSettings(map);
return true;
}
void Settings::initialize() {
const QSettings::Format SqliteFormat = QSettings::registerFormat("sqlite", &readSqlite, &writeSqlite);
QSettings::setDefaultFormat(SqliteFormat);
}
QString Settings::valueString(const QString &name, const QString &defaultValue) {
return value(name, defaultValue).toString();
}
int Settings::valueInt(const QString &name, int defaultValue) {
return value(name, defaultValue).toInt();
}
QString Settings::keyValueserialize(const QString& key) const {
return QString("%1 = %2").arg(key).arg(value(key).toString());
}

View File

@ -1,29 +0,0 @@
#ifndef SETTINGS_H
#define SETTINGS_H
#include <stable.h>
#include "database.h"
namespace jop {
class Settings : public QSettings {
Q_OBJECT
public:
Settings();
static void initialize();
QString keyValueserialize(const QString& key) const;
public slots:
QString valueString(const QString& name, const QString& defaultValue = "");
int valueInt(const QString& name, int defaultValue = 0);
};
}
#endif // SETTINGS_H

View File

@ -1,12 +0,0 @@
#ifndef SIMPLETYPES_H
#define SIMPLETYPES_H
#include <stable.h>
namespace jop {
typedef QVector<QVariant> VariantVector;
}
#endif // SIMPLETYPES_H

View File

@ -1,215 +0,0 @@
#ifndef SPARSEARRAY_HPP
#define SPARSEARRAY_HPP
#include <algorithm>
#include <memory>
#include <iostream>
#include <map>
#include <vector>
#include <math.h>
template <class ClassType> class SparseVector {
public:
SparseVector() {
counter_ = -1;
count_ = -1;
}
ClassType* get(int index) const {
if (index > count()) return NULL;
if (!isset(index)) return NULL;
typename IndexMap::const_iterator pos = indexes_.find(index);
int valueIndex = pos->second.valueIndex;
typename std::map<int, std::unique_ptr<ClassType>>::const_iterator pos2 = values_.find(valueIndex);
return pos2->second.get();
}
void set(int index, ClassType* value) {
unset(index);
int valueIndex = ++counter_;
std::unique_ptr<ClassType> ptr(value);
values_[valueIndex] = std::move(ptr);
IndexRecord r;
r.valueIndex = valueIndex;
r.time = 0; // Disabled / not needed
//r.time = time(0);
indexes_[index] = r;
count_ = -1;
}
void push(const ClassType& value) {
set(count(), value);
}
bool isset(int index) const {
return indexes_.find(index) != indexes_.end();
}
std::vector<int> indexes() const {
std::vector<int> output;
for (typename IndexMap::const_iterator it = indexes_.begin(); it != indexes_.end(); ++it) {
output.push_back(it->first);
}
return output;
}
// Unsets that particular index, but without shifting the following indexes
void unset(int index) {
if (!isset(index)) return;
IndexRecord r = indexes_[index];
values_.erase(r.valueIndex);
indexes_.erase(index);
}
void insert(int index, const ClassType& value) {
IndexMap newIndexes;
for (typename IndexMap::const_iterator it = indexes_.begin(); it != indexes_.end(); ++it) {
int key = it->first;
if (key > index) key++;
newIndexes[key] = it->second;
}
indexes_ = newIndexes;
set(index, value);
}
// Removes the element at that particular index, and shift all the following elements
void remove(int index) {
if (index > count()) return;
if (isset(index)) {
int valueIndex = indexes_[index].valueIndex;
values_.erase(valueIndex);
}
IndexMap newIndexes;
for (typename IndexMap::const_iterator it = indexes_.begin(); it != indexes_.end(); ++it) {
int key = it->first;
if (key == index) continue;
if (key > index) key--;
newIndexes[key] = it->second;
}
indexes_ = newIndexes;
count_ = -1;
}
// Returns a vector containing the indexes that are not currently set around
// the given index, up to bufferSize indexes.
std::vector<int> availableBufferAround(int index, size_t bufferSize) const {
std::vector<int> temp;
// Doesn't make sense to search for an empty buffer around
// an index that is already set.
if (isset(index)) return temp;
temp.push_back(index);
// Probably not the most efficient algorithm but it works:
// First search 1 position to the left, then 1 position to the right,
// then 2 to the left, etc. If encountering an unavailable index on one
// of the side, the path is "blocked" and searching is now done in only
// one direction. If both sides are blocked, the algorithm exit.
int inc = 1;
int sign = -1;
bool leftBlocked = false;
bool rightBlocked = false;
while (temp.size() < bufferSize) {
int bufferIndex = index + (inc * sign);
bool blocked = isset(bufferIndex) || bufferIndex < 0;
if (blocked) {
if (sign < 0) {
leftBlocked = true;
} else {
rightBlocked = true;
}
}
if (leftBlocked && rightBlocked) break;
if (!blocked) temp.push_back(bufferIndex);
sign = -sign;
if (sign < 0) inc++;
if (leftBlocked && sign < 0) sign = 1;
if (rightBlocked && sign > 0) sign = -1;
}
std::sort(temp.begin(), temp.end());
return temp;
}
int count() const {
if (count_ >= 0) return count_;
int maxKey = -1;
for (typename IndexMap::const_iterator it = indexes_.begin(); it != indexes_.end(); ++it) {
const int& key = it->first;
if (key > maxKey) maxKey = key;
}
count_ = maxKey + 1;
return count_;
}
void clearOlderThan(time_t time) {
IndexMap newIndexes;
for (typename IndexMap::const_iterator it = indexes_.begin(); it != indexes_.end(); ++it) {
const IndexRecord& r = it->second;
if (r.time > time) {
newIndexes[it->first] = r;
} else {
values_.erase(r.valueIndex);
}
}
indexes_ = newIndexes;
count_ = -1;
}
// Unset all values outside of this interval
void unsetAllButInterval(int intervalFrom, int intervalTo) {
int count = this->count();
for (int i = 0; i < count; i++) {
if (i >= intervalFrom && i <= intervalTo) continue;
unset(i);
}
}
void clear() {
indexes_.clear();
values_.clear();
counter_ = 0;
count_ = -1;
}
void print() const {
for (int i = 0; i < count(); i++) {
std::cout << "|";
std::cout << " " << get(i) << " ";
}
}
private:
struct IndexRecord {
int valueIndex;
time_t time;
};
typedef std::map<int, IndexRecord> IndexMap;
int counter_;
IndexMap indexes_;
std::map<int, std::unique_ptr<ClassType>> values_;
// This is used to cache the result of ::count().
// Don't forget to set it to -1 whenever the list
// size changes, so that it can be recalculated.
mutable int count_;
};
#endif // SPARSEARRAY_HPP

View File

@ -1,44 +0,0 @@
#ifndef STABLE_H
#define STABLE_H
#if defined __cplusplus
#include <memory>
#include <limits>
#include <QAbstractListModel>
#include <QCoreApplication>
#include <QGuiApplication>
#include <QDebug>
#include <QFileInfo>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlRecord>
#include <QList>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QSqlDatabase>
#include <QQuickView>
#include <QQmlContext>
#include <QQmlProperty>
#include <QSqlError>
#include <QNetworkAccessManager>
#include <QUrlQuery>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QSettings>
#include <QJsonObject>
#include <QJsonParseError>
#include <QBuffer>
#include <QJsonArray>
#include <QTimer>
#include <QStandardPaths>
#include <QDir>
#include <QCommandLineParser>
#include <QProcess>
#include <QQmlProperty>
#include <QThread>
#endif // __cplusplus
#endif // STABLE_H

View File

@ -1,321 +0,0 @@
#include "synchronizer.h"
#include "models/folder.h"
#include "models/note.h"
#include "settings.h"
using namespace jop;
Synchronizer::Synchronizer() {
state_ = Idle;
uploadsRemaining_ = 0;
connect(&api_, SIGNAL(requestDone(QJsonObject,QString)), this, SLOT(api_requestDone(QJsonObject,QString)));
}
void Synchronizer::start() {
if (state_ == Frozen) {
qWarning() << "Cannot start synchronizer while frozen";
return;
}
if (state_ != Idle) {
qWarning() << "Cannot start synchronizer because synchronization already in progress. State: " << state_;
return;
}
emit started();
qInfo() << "Starting synchronizer...";
switchState(UploadingChanges);
}
void Synchronizer::setSessionId(const QString &v) {
api_.setSessionId(v);
}
void Synchronizer::abort() {
switchState(Aborting);
}
void Synchronizer::freeze() {
switchState(Frozen);
}
void Synchronizer::unfreeze() {
switchState(Idle);
}
WebApi &Synchronizer::api() {
return api_;
}
QUrlQuery Synchronizer::valuesToUrlQuery(const QHash<QString, Change::Value>& values) const {
QUrlQuery query;
for (QHash<QString, Change::Value>::const_iterator it = values.begin(); it != values.end(); ++it) {
if (it.key() == "id") continue;
query.addQueryItem(it.key(), it.value().toString());
}
return query;
}
void Synchronizer::checkNextState() {
qDebug() << "Synchronizer::checkNextState from state" << state_;
switch (state_) {
case UploadingChanges:
if (uploadsRemaining_ < 0) qCritical() << "Mismatch on upload operations done" << uploadsRemaining_;
if (uploadsRemaining_ <= 0) {
uploadsRemaining_ = 0;
switchState(DownloadingChanges);
}
break;
case DownloadingChanges:
switchState(Idle);
emit finished();
break;
case Idle:
break;
case Aborting:
switchState(Idle);
emit finished();
break;
case Frozen:
break;
default:
qCritical() << "Synchronizer has invalid state" << state_;
break;
}
}
void Synchronizer::switchState(Synchronizer::SynchronizationState state) {
if (state_ == state) {
qCritical() << "Trying to switch synchronizer to its current state" << state;
return;
}
state_ = state;
qInfo() << "Switching synchronizer state to" << state;
if (state == Idle) {
// =============================================================================================
// IDLE STATE
// =============================================================================================
} else if (state == UploadingChanges) {
// =============================================================================================
// UPLOADING STATE
// =============================================================================================
std::vector<Change*> changes = Change::all();
Change::mergedChanges(changes);
uploadsRemaining_ = changes.size();
for (size_t i = 0; i < changes.size(); i++) {
Change* change = changes[i];
jop::Table itemType = (jop::Table)change->value("item_type").toInt();
QString itemId = change->value("item_id").toString();
Change::Type type = (Change::Type)change->value("type").toInt();
qDebug() << "Change" << change->idString() << itemId << itemType;
if (itemType == jop::FoldersTable) {
if (type == Change::Create) {
Folder folder;
folder.load(itemId);
QUrlQuery data = valuesToUrlQuery(folder.values());
api_.put("folders/" + folder.idString(), QUrlQuery(), data, "upload:putFolder:" + folder.idString());
} else if (type == Change::Update) {
Folder folder;
folder.load(itemId);
QStringList mergedFields = change->mergedFields();
QUrlQuery data;
foreach (QString field, mergedFields) {
data.addQueryItem(field, folder.value(field).toString());
}
api_.patch("folders/" + folder.idString(), QUrlQuery(), data, "upload:patchFolder:" + folder.idString());
} else if (type == Change::Delete) {
api_.del("folders/" + itemId, QUrlQuery(), QUrlQuery(), "upload:deleteFolder:" + itemId);
}
} else {
qFatal("Unsupported item type: %d", itemType);
}
}
for (size_t i = 0; i < changes.size(); i++) {
delete changes[i];
}
changes.clear();
checkNextState();
} else if (state_ == DownloadingChanges) {
// =============================================================================================
// DOWNLOADING STATE
// =============================================================================================
Settings settings;
QString lastRevId = settings.value("lastRevId", "0").toString();
QUrlQuery query;
query.addQueryItem("rev_id", lastRevId);
api_.get("synchronizer", query, QUrlQuery(), "download:getSynchronizer");
} else if (state == Aborting) {
// =============================================================================================
// ABORTING STATE
// =============================================================================================
uploadsRemaining_ = 0;
api_.abortAll();
checkNextState();
} else if (state == Frozen) {
// =============================================================================================
// FROZEN STATE
// =============================================================================================
}
}
void Synchronizer::api_requestDone(const QJsonObject& response, const QString& tag) {
if (state_ == Frozen) {
qWarning() << "Receiving response while synchronizer is frozen";
return;
}
QStringList parts = tag.split(':');
QString category = parts[0];
QString action = parts[1];
QString arg1 = "";
QString arg2 = "";
if (parts.size() == 3) arg1 = parts[2];
if (parts.size() == 4) arg2 = parts[3];
qInfo() << "WebApi: done" << category << action << arg1 << arg2;
QString error = "";
if (response.contains("error")) {
error = response.value("error").toString();
qCritical().noquote() << "Sync error:" << error;
// Each action might handle errors differently so let it proceed below
}
// =============================================================================================
// HANDLE UPLOAD RESPONSE
// =============================================================================================
if (state_ == UploadingChanges) {
uploadsRemaining_--;
if (error == "") {
qInfo() << "Synced folder" << arg1;
if (action == "putFolder") {
Change::disposeByItemId(arg1);
}
if (action == "patchFolder") {
Change::disposeByItemId(arg1);
}
if (action == "deleteFolder") {
Change::disposeByItemId(arg1);
}
if (uploadsRemaining_ < 0) {
qWarning() << "Mismatch on operations done:" << uploadsRemaining_;
}
}
checkNextState();
// =============================================================================================
// HANDLE DOWNLOAD RESPONSE
// =============================================================================================
} else if (state_ == DownloadingChanges) {
if (error != "") {
checkNextState();
} else {
if (action == "getSynchronizer") {
QJsonArray items = response["items"].toArray();
QString maxRevId = "";
foreach (QJsonValue it, items) {
QJsonObject obj = it.toObject();
QString itemId = obj["item_id"].toString();
QString itemType = obj["item_type"].toString();
QString operationType = obj["type"].toString();
QString revId = obj["id"].toString();
QJsonObject item = obj["item"].toObject();
if (itemType == "folder") {
if (operationType == "create") {
Folder folder;
folder.loadJsonObject(item);
folder.save(false);
}
if (operationType == "update") {
Folder folder;
folder.load(itemId);
folder.patchJsonObject(item);
folder.save(false);
}
if (operationType == "delete") {
Folder folder;
folder.load(itemId);
folder.dispose();
}
}
if (revId > maxRevId) maxRevId = revId;
}
if (maxRevId != "") {
Settings settings;
settings.setValue("lastRevId", maxRevId);
}
checkNextState();
}
}
} else {
qCritical() << "Invalid category" << category;
}
}

View File

@ -1,49 +0,0 @@
#ifndef SYNCHRONIZER_H
#define SYNCHRONIZER_H
#include <stable.h>
#include "webapi.h"
#include "database.h"
#include "models/change.h"
namespace jop {
class Synchronizer : public QObject {
Q_OBJECT
public:
enum SynchronizationState { Idle, UploadingChanges, DownloadingChanges, Aborting, Frozen };
Synchronizer();
void start();
void setSessionId(const QString& v);
void abort();
void freeze();
void unfreeze();
WebApi& api();
private:
QUrlQuery valuesToUrlQuery(const QHash<QString, BaseModel::Value> &values) const;
WebApi api_;
SynchronizationState state_;
int uploadsRemaining_;
void checkNextState();
void switchState(SynchronizationState state);
public slots:
void api_requestDone(const QJsonObject& response, const QString& tag);
signals:
void started();
void finished();
};
}
#endif // SYNCHRONIZER_H

View File

@ -1,14 +0,0 @@
#include <stable.h>
#include "uuid.h"
namespace jop {
namespace uuid {
QString createUuid(QString s) {
if (s == "") s = QString("%1%2").arg(qrand()).arg(QDateTime::currentMSecsSinceEpoch());
QString hash = QString(QCryptographicHash::hash(s.toUtf8(), QCryptographicHash::Sha256).toHex());
return hash.left(32);
}
}
}

View File

@ -1,14 +0,0 @@
#ifndef UUID_H
#define UUID_H
#include <stable.h>
namespace jop {
namespace uuid {
QString createUuid(QString s = "");
}
}
#endif // UUID_H

View File

@ -1,7 +0,0 @@
#include "uuid_utils.h"
QString testtest()
{
return "con";
}

View File

@ -1,8 +0,0 @@
#ifndef UUID_UTILS_H
#define UUID_UTILS_H
#include <stable.h>
QString testtest();
#endif // UUID_UTILS_H

View File

@ -1,171 +0,0 @@
#include <stable.h>
#include "webapi.h"
using namespace jop;
WebApi::WebApi() {
baseUrl_ = "";
sessionId_ = "";
connect(&manager_, SIGNAL(finished(QNetworkReply*)), this, SLOT(request_finished(QNetworkReply*)));
}
QString WebApi::baseUrl() const {
return baseUrl_;
}
void WebApi::execRequest(HttpMethod method, const QString &path, const QUrlQuery &query, const QUrlQuery &data, const QString& tag) {
if (baseUrl() == "") {
qCritical() << "Trying to execute request before base URL has been set";
QJsonObject obj;
obj["error"] = "Trying to execute request before base URL has been set";
emit requestDone(obj, tag);
return;
}
QueuedRequest r;
r.method = method;
r.path = path;
r.query = query;
r.data = data;
r.tag = tag;
r.buffer = NULL;
r.reply = NULL;
queuedRequests_ << r;
processQueue();
}
void WebApi::post(const QString& path,const QUrlQuery& query, const QUrlQuery& data, const QString& tag) { execRequest(HttpMethod::POST, path, query, data, tag); }
void WebApi::get(const QString& path,const QUrlQuery& query, const QUrlQuery& data, const QString& tag) { execRequest(HttpMethod::GET, path, query, data, tag); }
void WebApi::put(const QString& path,const QUrlQuery& query, const QUrlQuery& data, const QString& tag) { execRequest(HttpMethod::PUT, path, query, data, tag); }
void WebApi::del(const QString &path, const QUrlQuery &query, const QUrlQuery &data, const QString &tag) { execRequest(HttpMethod::DEL, path, query, data, tag); }
void WebApi::patch(const QString &path, const QUrlQuery &query, const QUrlQuery &data, const QString &tag) { execRequest(HttpMethod::PATCH, path, query, data, tag); }
void WebApi::setSessionId(const QString &v) {
sessionId_ = v;
}
void WebApi::abortAll() {
for (int i = 0; i < inProgressRequests_.size(); i++) {
QueuedRequest r = inProgressRequests_[i];
if (r.reply) {
r.reply->abort();
// TODO: Delete r.reply?
}
}
for (int i = 0; i < queuedRequests_.size(); i++) {
QueuedRequest r = queuedRequests_[i];
if (r.reply) {
r.reply->abort();
// TODO: Delete r.reply?
}
}
queuedRequests_.size();
}
void WebApi::processQueue() {
if (!queuedRequests_.size() || inProgressRequests_.size() >= 50) return;
QueuedRequest r = queuedRequests_.takeFirst();
QString url = baseUrl_ + "/" + r.path;
QUrlQuery query = r.query;
if (sessionId_ != "") {
query.addQueryItem("session", sessionId_);
}
url += "?" + query.toString(QUrl::FullyEncoded);
QNetworkRequest* request = new QNetworkRequest(url);
request->setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
QNetworkReply* reply = NULL;
if (r.method == jop::PATCH) {
// TODO: Delete buffer when done
QBuffer* buffer = new QBuffer();
buffer->open(QBuffer::ReadWrite);
buffer->write(r.data.toString(QUrl::FullyEncoded).toUtf8());
buffer->seek(0);
r.buffer = buffer;
reply = manager_.sendCustomRequest(*request, "PATCH", buffer);
}
if (r.method == jop::GET) {
reply = manager_.get(*request);
}
if (r.method == jop::POST) {
reply = manager_.post(*request, r.data.toString(QUrl::FullyEncoded).toUtf8());
}
if (r.method == jop::PUT) {
reply = manager_.put(*request, r.data.toString(QUrl::FullyEncoded).toUtf8());
}
if (r.method == jop::DEL) {
reply = manager_.deleteResource(*request);
}
if (!reply) {
qWarning() << "WebApi::processQueue(): reply object was not created - invalid request method";
return;
}
r.reply = reply;
r.request = request;
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(request_error(QNetworkReply::NetworkError)));
QStringList cmd;
cmd << "curl";
if (r.method == jop::PUT) cmd << "-X" << "PUT";
if (r.method == jop::PATCH) cmd << "-X" << "PATCH";
if (r.method == jop::DEL) cmd << "-X" << "DELETE";
if (r.method != jop::GET && r.method != jop::DEL) {
cmd << "--data" << "'" + r.data.toString(QUrl::FullyEncoded) + "'";
}
cmd << "'" + url + "'";
qDebug().noquote() << cmd.join(" ");
inProgressRequests_.push_back(r);
}
void WebApi::request_finished(QNetworkReply *reply) {
QByteArray responseBodyBA = reply->readAll();
QJsonObject response;
QJsonParseError err;
QJsonDocument doc = QJsonDocument::fromJson(responseBodyBA, &err);
if (err.error != QJsonParseError::NoError) {
QString errorMessage = "Could not parse JSON: " + err.errorString() + "\n" + QString(responseBodyBA);
qWarning().noquote() << errorMessage;
response["error"] = errorMessage;
} else {
response = doc.object();
if (response.contains("error") && !response["error"].isNull()) {
qWarning().noquote() << "API error:" << QString(responseBodyBA);
}
}
for (int i = 0; i < inProgressRequests_.size(); i++) {
QueuedRequest r = inProgressRequests_[i];
if (r.reply == reply) {
inProgressRequests_.erase(inProgressRequests_.begin() + i);
emit requestDone(response, r.tag);
break;
}
}
processQueue();
}
void WebApi::request_error(QNetworkReply::NetworkError e) {
qWarning() << "Network error" << e;
}
void jop::WebApi::setBaseUrl(const QString &v) {
baseUrl_ = v;
}

View File

@ -1,60 +0,0 @@
#ifndef WEBAPI_H
#define WEBAPI_H
#include <stable.h>
#include "enum.h"
namespace jop {
class WebApi : public QObject {
Q_OBJECT
public:
struct QueuedRequest {
HttpMethod method;
QString path;
QUrlQuery query;
QUrlQuery data;
QNetworkReply* reply;
QNetworkRequest* request;
QString tag;
QBuffer* buffer;
};
WebApi();
void setBaseUrl(const QString& v);
QString baseUrl() const;
void execRequest(HttpMethod method, const QString& path,const QUrlQuery& query = QUrlQuery(), const QUrlQuery& data = QUrlQuery(), const QString& tag = "");
void post(const QString& path,const QUrlQuery& query = QUrlQuery(), const QUrlQuery& data = QUrlQuery(), const QString& tag = "");
void get(const QString& path,const QUrlQuery& query = QUrlQuery(), const QUrlQuery& data = QUrlQuery(), const QString& tag = "");
void put(const QString& path,const QUrlQuery& query = QUrlQuery(), const QUrlQuery& data = QUrlQuery(), const QString& tag = "");
void del(const QString& path,const QUrlQuery& query = QUrlQuery(), const QUrlQuery& data = QUrlQuery(), const QString& tag = "");
void patch(const QString& path,const QUrlQuery& query = QUrlQuery(), const QUrlQuery& data = QUrlQuery(), const QString& tag = "");
void setSessionId(const QString& v);
void abortAll();
private:
QString baseUrl_;
QList<QueuedRequest> queuedRequests_;
QList<QueuedRequest> inProgressRequests_;
void processQueue();
QString sessionId_;
QNetworkAccessManager manager_;
public slots:
void request_finished(QNetworkReply* reply);
void request_error(QNetworkReply::NetworkError e);
signals:
void requestDone(const QJsonObject& response, const QString& tag);
};
}
#endif // WEBAPI_H

View File

@ -1,52 +0,0 @@
#include "window.h"
using namespace jop;
Window::Window() : QQuickView() {}
void Window::showPage(const QString &pageName) {
qWarning() << "Window::showPage() disabled";
return;
QVariant pageNameV(pageName);
QVariant returnedValue;
QMetaObject::invokeMethod((QObject*)rootObject(), "showPage", Q_RETURN_ARG(QVariant, returnedValue), Q_ARG(QVariant, pageNameV));
}
QVariant Window::callQml(const QString &name, const QVariantList &args) {
QVariant returnedValue;
qDebug() << "Going to call QML:" << name;
QObject* o = (QObject*)rootObject();
if (args.size() == 0) {
QMetaObject::invokeMethod(o, name.toStdString().c_str(), Q_RETURN_ARG(QVariant, returnedValue));
} else if (args.size() == 1) {
QMetaObject::invokeMethod(o, name.toStdString().c_str(), Q_RETURN_ARG(QVariant, returnedValue), Q_ARG(QVariant, args[0]));
} else if (args.size() == 2) {
QMetaObject::invokeMethod(o, name.toStdString().c_str(), Q_RETURN_ARG(QVariant, returnedValue), Q_ARG(QVariant, args[0]), Q_ARG(QVariant, args[1]));
} else if (args.size() == 3) {
QMetaObject::invokeMethod(o, name.toStdString().c_str(), Q_RETURN_ARG(QVariant, returnedValue), Q_ARG(QVariant, args[0]), Q_ARG(QVariant, args[1]), Q_ARG(QVariant, args[2]));
} else {
qFatal("Window::emitSignal: add support for more args!");
}
return returnedValue;
}
void Window::emitSignal(const QString &name, const QVariantList &args) {
QString nameCopy(name);
nameCopy = nameCopy.left(1).toUpper() + nameCopy.right(nameCopy.length() - 1);
nameCopy = "emit" + nameCopy;
callQml(nameCopy, args);
// qDebug() << "Going to call QML:" << nameCopy;
// QObject* o = (QObject*)rootObject();
// if (args.size() == 0) {
// QMetaObject::invokeMethod(o, nameCopy.toStdString().c_str(), Q_RETURN_ARG(QVariant, returnedValue));
// } else if (args.size() == 1) {
// QMetaObject::invokeMethod(o, nameCopy.toStdString().c_str(), Q_RETURN_ARG(QVariant, returnedValue), Q_ARG(QVariant, args[0]));
// } else if (args.size() == 2) {
// QMetaObject::invokeMethod(o, nameCopy.toStdString().c_str(), Q_RETURN_ARG(QVariant, returnedValue), Q_ARG(QVariant, args[0]), Q_ARG(QVariant, args[1]));
// } else if (args.size() == 3) {
// QMetaObject::invokeMethod(o, nameCopy.toStdString().c_str(), Q_RETURN_ARG(QVariant, returnedValue), Q_ARG(QVariant, args[0]), Q_ARG(QVariant, args[1]), Q_ARG(QVariant, args[2]));
// } else {
// qFatal("Window::emitSignal: add support for more args!");
// }
}

View File

@ -1,23 +0,0 @@
#ifndef WINDOW_H
#define WINDOW_H
#include <stable.h>
namespace jop {
class Window : public QQuickView {
Q_OBJECT
public:
Window();
void showPage(const QString& pageName);
QVariant callQml(const QString& name, const QVariantList& args = QVariantList());
void emitSignal(const QString& name, const QVariantList& args = QVariantList());
};
}
#endif // WINDOW_H

View File

@ -1,23 +0,0 @@
CREATE TABLE folders (
id INTEGER PRIMARY KEY,
title TEXT,
created_time INT,
updated_time INT,
remote_id TEXT
);
CREATE TABLE notes (
id INTEGER PRIMARY KEY,
title TEXT,
body TEXT,
parent_id INT,
created_time INT,
updated_time INT,
remote_id TEXT
);
CREATE TABLE version (
version INT
);
INSERT INTO version (version) VALUES (1);

Some files were not shown because too many files have changed in this diff Show More