mirror of https://github.com/laurent22/joplin.git
Removed old files
parent
e521ca9427
commit
9f0f826a9f
|
@ -30,4 +30,5 @@ sparse_test.php
|
|||
INFO.md
|
||||
/web/env.php
|
||||
sync_staging.sh
|
||||
*.swp
|
||||
*.swp
|
||||
_vieux/
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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>
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
|
@ -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"
|
|
@ -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">"JOP_FRONT_END_CLI=1"</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>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
|
||||
}
|
|
@ -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()
|
||||
// }
|
||||
|
||||
//}
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
import QtQuick 2.7
|
||||
|
||||
Page1Form {
|
||||
button1.onClicked: {
|
||||
console.log("Button Pressed. Entered text: " + textField1.text);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
import QtQuick 2.4
|
||||
|
||||
TestForm {
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
// }
|
||||
|
||||
//}
|
|
@ -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", ¬eModel_);
|
||||
//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();
|
||||
}
|
|
@ -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
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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"
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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_;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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_;
|
||||
}
|
|
@ -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
|
|
@ -1,5 +0,0 @@
|
|||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>schema.sql</file>
|
||||
</qresource>
|
||||
</RCC>
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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 ¬eId) {
|
||||
emit noteCreated(noteId);
|
||||
}
|
||||
|
||||
void Dispatcher::emitNoteUpdated(const QString ¬eId) {
|
||||
emit noteUpdated(noteId);
|
||||
}
|
||||
|
||||
void Dispatcher::emitNoteDeleted(const QString ¬eId) {
|
||||
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_;
|
||||
}
|
|
@ -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
|
|
@ -1,6 +0,0 @@
|
|||
#include "enum.h"
|
||||
|
||||
enum::enum()
|
||||
{
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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_;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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) {
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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 ¬eId) {
|
||||
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 ¬eId) {
|
||||
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 ¬eId) {
|
||||
qDebug() << "NoteModel note deleted" << noteId;
|
||||
|
||||
int index = idToIndex(noteId);
|
||||
qDebug() << "index" << index;
|
||||
if (index < 0) return;
|
||||
|
||||
cacheClear();
|
||||
|
||||
beginRemoveRows(QModelIndex(), index, index);
|
||||
endRemoveRows();
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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>
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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);
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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());
|
||||
}
|
|
@ -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
|
|
@ -1,12 +0,0 @@
|
|||
#ifndef SIMPLETYPES_H
|
||||
#define SIMPLETYPES_H
|
||||
|
||||
#include <stable.h>
|
||||
|
||||
namespace jop {
|
||||
|
||||
typedef QVector<QVariant> VariantVector;
|
||||
|
||||
}
|
||||
|
||||
#endif // SIMPLETYPES_H
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
#ifndef UUID_H
|
||||
#define UUID_H
|
||||
|
||||
#include <stable.h>
|
||||
|
||||
namespace jop {
|
||||
namespace uuid {
|
||||
|
||||
QString createUuid(QString s = "");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // UUID_H
|
|
@ -1,7 +0,0 @@
|
|||
#include "uuid_utils.h"
|
||||
|
||||
|
||||
QString testtest()
|
||||
{
|
||||
return "con";
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
#ifndef UUID_UTILS_H
|
||||
#define UUID_UTILS_H
|
||||
|
||||
#include <stable.h>
|
||||
|
||||
QString testtest();
|
||||
|
||||
#endif // UUID_UTILS_H
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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!");
|
||||
// }
|
||||
}
|
|
@ -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
|
|
@ -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);
|
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue