Worked on C++ implementation of the Model. It seems to work somehow.

This commit is contained in:
Scharel Clemens 2019-01-28 23:15:53 +01:00
parent ddb778795b
commit c20d7408d2
12 changed files with 501 additions and 201 deletions

View file

@ -18,11 +18,13 @@ DEFINES += APP_VERSION=\\\"$$VERSION\\\"
HEADERS += \ HEADERS += \
src/sslconfiguration.h \ src/sslconfiguration.h \
src/notesmodel.h src/notesmodel.h \
src/note.h
SOURCES += src/harbour-nextcloudnotes.cpp \ SOURCES += src/harbour-nextcloudnotes.cpp \
src/sslconfiguration.cpp \ src/sslconfiguration.cpp \
src/notesmodel.cpp src/notesmodel.cpp \
src/note.cpp
DISTFILES += qml/harbour-nextcloudnotes.qml \ DISTFILES += qml/harbour-nextcloudnotes.qml \
qml/cover/CoverPage.qml \ qml/cover/CoverPage.qml \

View file

@ -109,23 +109,23 @@ ApplicationWindow
NotesApi { NotesApi {
id: api id: api
uuid: appSettings.currentAccount uuid: appSettings.currentAccount
//onResponseChanged: noteListModel.applyJSON(response) onResponseChanged: noteListModel.applyJSON(response)
} }
/*NotesModel { NotesModel {
id: noteListModel id: noteListModel
sortBy: 0 sortBy: 0 // TODO
favoritesOnTop: appSettings.favoritesOnTop favoritesOnTop: appSettings.favoritesOnTop
}*/ }
NoteDelegateModel { /*NoteDelegateModel {
id: noteListModel id: noteListModel
model: api.model model: api.model
favoritesOnTop: appSettings.favoritesOnTop favoritesOnTop: appSettings.favoritesOnTop
sortBy: appSettings.sortBy sortBy: appSettings.sortBy
showSeparator: appSettings.showSeparator showSeparator: appSettings.showSeparator
previewLineCount: appSettings.previewLineCount previewLineCount: appSettings.previewLineCount
} }*/
initialPage: Component { NotesPage { } } initialPage: Component { NotesPage { } }
cover: Qt.resolvedUrl("cover/CoverPage.qml") cover: Qt.resolvedUrl("cover/CoverPage.qml")

View file

@ -1,5 +1,6 @@
import QtQuick 2.5 import QtQuick 2.5
import Sailfish.Silica 1.0 import Sailfish.Silica 1.0
import harbour.nextcloudnotes.note 1.0
import "../js/showdown-1.9.0/dist/showdown.js" as ShowDown import "../js/showdown-1.9.0/dist/showdown.js" as ShowDown
Dialog { Dialog {

View file

@ -1,5 +1,6 @@
import QtQuick 2.0 import QtQuick 2.0
import Sailfish.Silica 1.0 import Sailfish.Silica 1.0
import harbour.nextcloudnotes.note 1.0
import "../components" import "../components"
Page { Page {
@ -84,6 +85,118 @@ Page {
model: noteListModel model: noteListModel
delegate: BackgroundItem {
id: note
contentHeight: titleLabel.height + previewLabel.height + 2*Theme.paddingSmall
height: contentHeight + menu.height
width: parent.width
highlighted: down || menu.active
ListView.onAdd: AddAnimation {
target: note //searchText !== "" ? null : note
}
ListView.onRemove: RemoveAnimation {
target: note //searchText !== "" ? null : note
}
RemorseItem {
id: remorse
}
onClicked: pageStack.push(Qt.resolvedUrl("../pages/NotePage.qml"),
{ note: notesList.model.get(index) })
onPressAndHold: menu.open(note)
Separator {
width: parent.width
color: Theme.primaryColor
anchors.top: titleLabel.top
visible: appSettings.showSeparator && index !== 0
}
IconButton {
id: isFavoriteIcon
anchors.left: parent.left
anchors.top: parent.top
icon.source: (favorite ? "image://theme/icon-m-favorite-selected?" : "image://theme/icon-m-favorite?") +
(note.highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor)
onClicked: {
api.updateNote(id, {'favorite': !favorite} )
}
}
Label {
id: titleLabel
anchors.left: isFavoriteIcon.right
anchors.leftMargin: Theme.paddingSmall
anchors.right: categoryRectangle.visible ? categoryRectangle.left : parent.right
anchors.top: parent.top
text: title
truncationMode: TruncationMode.Fade
color: note.highlighted ? Theme.highlightColor : Theme.primaryColor
}
Rectangle {
id: categoryRectangle
anchors.right: parent.right
anchors.rightMargin: Theme.horizontalPageMargin
anchors.top: parent.top
anchors.topMargin: Theme.paddingSmall
width: categoryLabel.width + Theme.paddingLarge
height: categoryLabel.height + Theme.paddingSmall
color: "transparent"
border.color: Theme.highlightColor
radius: height / 4
visible: appSettings.sortBy !== "category" && categoryLabel.text.length > 0
Label {
id: categoryLabel
anchors.centerIn: parent
text: category
color: note.highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor
font.pixelSize: Theme.fontSizeExtraSmall
}
}
Label {
id: previewLabel
anchors.left: isFavoriteIcon.right
anchors.leftMargin: Theme.paddingSmall
anchors.right: parent.right
anchors.rightMargin: Theme.horizontalPageMargin
anchors.top: titleLabel.bottom
text: parseText(content)
font.pixelSize: Theme.fontSizeExtraSmall
textFormat: Text.PlainText
wrapMode: Text.Wrap
elide: Text.ElideRight
maximumLineCount: appSettings.previewLineCount > 0 ? appSettings.previewLineCount : 1
visible: appSettings.previewLineCount > 0
color: note.highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor
function parseText (preText) {
var lines = preText.split('\n')
lines.splice(0,1);
var newText = lines.join('\n');
return newText.replace(/^\s*$(?:\r\n?|\n)/gm, "")
}
}
ContextMenu {
id: menu
MenuLabel {
id: modifiedLabel
anchors.horizontalCenter: parent.horizontalCenter
text: qsTr("Modified") + ": " + new Date(modified * 1000).toLocaleString(Qt.locale(), Locale.ShortFormat)
}
MenuItem {
text: qsTr("Delete")
onClicked: {
remorse.execute(note, qsTr("Deleting note"), function() {
api.deleteNote(id)
})
}
}
}
}
section.property: appSettings.sortBy section.property: appSettings.sortBy
section.criteria: appSettings.sortBy === "title" ? ViewSection.FirstCharacter : ViewSection.FullString section.criteria: appSettings.sortBy === "title" ? ViewSection.FirstCharacter : ViewSection.FullString
section.labelPositioning: appSettings.sortBy === "title" ? ViewSection.CurrentLabelAtStart | ViewSection.NextLabelAtEnd : ViewSection.InlineLabels section.labelPositioning: appSettings.sortBy === "title" ? ViewSection.CurrentLabelAtStart | ViewSection.NextLabelAtEnd : ViewSection.InlineLabels

View file

@ -2,6 +2,7 @@
#include <sailfishapp.h> #include <sailfishapp.h>
#include <QtQml> #include <QtQml>
#include <QObject> #include <QObject>
#include "note.h"
#include "notesmodel.h" #include "notesmodel.h"
#include "sslconfiguration.h" #include "sslconfiguration.h"
@ -15,6 +16,7 @@ int main(int argc, char *argv[])
app->setOrganizationName("harbour-nextcloudnotes"); app->setOrganizationName("harbour-nextcloudnotes");
qDebug() << app->applicationDisplayName() << app->applicationVersion(); qDebug() << app->applicationDisplayName() << app->applicationVersion();
qmlRegisterType<Note>("harbour.nextcloudnotes.note", 1, 0, "Note");
qmlRegisterType<NotesModel>("harbour.nextcloudnotes.notesmodel", 1, 0, "NotesModel"); qmlRegisterType<NotesModel>("harbour.nextcloudnotes.notesmodel", 1, 0, "NotesModel");
qmlRegisterType<SslConfiguration>("harbour.nextcloudnotes.sslconfiguration", 1, 0, "SslConfiguration"); qmlRegisterType<SslConfiguration>("harbour.nextcloudnotes.sslconfiguration", 1, 0, "SslConfiguration");

44
src/note.cpp Normal file
View file

@ -0,0 +1,44 @@
#include "note.h"
Note::Note(QObject *parent) : QObject(parent) {
m_id = -1;
m_modified = 0;
m_error = true;
}
Note::Note(const Note& note, QObject *parent) : QObject(parent) {
m_id = note.id();
m_modified = note.modified();
m_title = note.title();
m_category = note.category();
m_content = note.content();
m_favorite = note.favorite();
m_etag = note.etag();
m_error = note.error();
m_errorMessage = note.errorMessage();
}
Note& Note::operator=(const Note& note) {
m_id = note.id();
m_modified = note.modified();
m_title = note.title();
m_category = note.category();
m_content = note.content();
m_favorite = note.favorite();
m_etag = note.etag();
m_error = note.error();
m_errorMessage = note.errorMessage();
return *this;
}
bool Note::equal(const Note& n) const {
return m_id == n.id() &&
m_modified == n.modified() &&
m_title == n.title() &&
m_category == n.category() &&
m_content == n.content() &&
m_favorite == n.favorite() &&
m_etag == n.etag() &&
m_error == n.error() &&
m_errorMessage == n.errorMessage();
}

108
src/note.h Normal file
View file

@ -0,0 +1,108 @@
#ifndef NOTE_H
#define NOTE_H
#include <QObject>
#include <QJsonObject>
class Note : public QObject {
Q_OBJECT
Q_PROPERTY(int id READ id WRITE setId NOTIFY idChanged)
Q_PROPERTY(uint modified READ modified WRITE setModified NOTIFY modifiedChanged)
Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged)
Q_PROPERTY(QString category READ category WRITE setCategory NOTIFY categoryChanged)
Q_PROPERTY(QString content READ content WRITE setContent NOTIFY contentChanged)
Q_PROPERTY(bool favorite READ favorite WRITE setFavorite NOTIFY favoriteChanged)
Q_PROPERTY(QString etag READ etag WRITE setEtag NOTIFY etagChanged)
Q_PROPERTY(bool error READ error WRITE setError NOTIFY errorChanged)
Q_PROPERTY(QString errorMessage READ errorMessage WRITE setErrorMessage NOTIFY errorMessageChanged)
public:
Note(QObject *parent = NULL);
Note(const Note& note, QObject *parent = NULL);
int id() const { return m_id; }
uint modified() const { return m_modified; }
QString title() const { return m_title; }
QString category() const { return m_category; }
QString content() const { return m_content; }
bool favorite() const { return m_favorite; }
QString etag() const { return m_etag; }
bool error() const { return m_error; }
QString errorMessage() const { return m_errorMessage; }
void setId(int id) { if (id != m_id) { m_id = id; emit idChanged(id); } }
void setModified(uint modified) { if (modified != m_modified) { m_modified = modified; emit modifiedChanged(modified); } }
void setTitle(QString title) { if (title != m_title) { m_title = title; emit titleChanged(title); } }
void setCategory(QString category) { if (category != m_category) { m_category = category; emit categoryChanged(category); } }
void setContent(QString content) { if (content != m_content) { m_content = content; emit contentChanged(content); } }
void setFavorite(bool favorite) { if (favorite != m_favorite) { m_favorite = favorite; emit favoriteChanged(favorite); } }
void setEtag(QString etag) { if (etag != m_etag) { m_etag = etag; emit etagChanged(etag); } }
void setError(bool error) { if (error != m_error) { m_error = error; emit errorChanged(error); } }
void setErrorMessage(QString errorMessage) { if (errorMessage != m_errorMessage) { m_errorMessage = errorMessage; emit errorMessageChanged(errorMessage); } }
Note& operator=(const Note& note);
bool operator==(const Note& note) const {
return m_id == note.id();
}
bool equal(const Note& n) const;
enum SearchAttribute {
NoSearchAttribute = 0x0,
SearchInTitle = 0x1,
SearchInCategory = 0x2,
SearchInContent = 0x4,
SearchAll = 0x7
};
Q_DECLARE_FLAGS(SearchAttributes, SearchAttribute)
static Note *fromjson(const QJsonObject& jobj) {
Note *note = new Note;
note->setId(jobj.value("id").toInt());
note->setModified(jobj.value("modified").toInt());
note->setTitle(jobj.value("title").toString());
note->setCategory(jobj.value("category").toString());
note->setContent(jobj.value("content").toString());
note->setFavorite(jobj.value("favorite").toBool());
note->setEtag(jobj.value("etag").toString());
note->setError(jobj.value("error").toBool(true));
note->setErrorMessage(jobj.value("errorMessage").toString());
return note;
}
static bool searchInNote(const QString &query, const Note *note, SearchAttributes criteria = QFlag(SearchAll), Qt::CaseSensitivity cs = Qt::CaseInsensitive) {
bool queryFound = false;
if (criteria.testFlag(SearchInTitle)) {
queryFound |= note->title().contains(query, cs);
}
if (criteria.testFlag(SearchInContent)) {
queryFound |= note->content().contains(query, cs);
}
if (criteria.testFlag(SearchInCategory)) {
queryFound |= note->category().contains(query, cs);
}
return queryFound;
}
signals:
void idChanged(int id);
void modifiedChanged(uint modified);
void titleChanged(QString title);
void categoryChanged(QString category);
void contentChanged(QString content);
void favoriteChanged(bool favorite);
void etagChanged(QString etag);
void errorChanged(bool error);
void errorMessageChanged(QString errorMessage);
private:
int m_id;
uint m_modified;
QString m_title;
QString m_category;
QString m_content;
bool m_favorite;
QString m_etag;
bool m_error;
QString m_errorMessage;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(Note::SearchAttributes)
#endif // NOTE_H

View file

@ -1,83 +1,10 @@
#include "notesmodel.h" #include "notesmodel.h"
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray> #include <QJsonArray>
#include <QtMath> #include <QtMath>
#include <QDebug> #include <QDebug>
const QHash<int, QByteArray> noteRoles = QHash<int, QByteArray>{
{NotesModel::visible, "visible"},
{NotesModel::idRole, "id"},
{NotesModel::modifiedRole, "modified"},
{NotesModel::titleRole, "title"},
{NotesModel::categoryRole, "category"},
{NotesModel::contentRole, "content"},
{NotesModel::favoriteRole, "favorite"},
{NotesModel::etagRole, "etag"},
{NotesModel::errorRole, "error"},
{NotesModel::errorMessageRole, "errorMessage"}
};
struct Note {
int id;
uint modified;
QString title;
QString category;
QString content;
bool favorite;
QString etag;
bool error;
QString errorMessage;
bool operator==(const Note& n) const {
return id == n.id;
}
bool equal(const Note& n) const {
return id == n.id &&
modified == n.modified &&
title == n.title &&
category == n.category &&
content == n.content &&
favorite == n.favorite &&
etag == n.etag &&
error == n.error &&
errorMessage == n.errorMessage;
}
enum SearchAttribute {
NoSearchAttribute = 0x0,
SearchInTitle = 0x1,
SearchInCategory = 0x2,
SearchInContent = 0x4,
SearchAll = 0x7
};
Q_DECLARE_FLAGS(SearchAttributes, SearchAttribute)
static const Note fromjson(const QJsonObject& jobj) {
Note note;
note.id = jobj.value(noteRoles[NotesModel::idRole]).toInt();
note.modified = jobj.value(noteRoles[NotesModel::modifiedRole]).toInt();
note.title = jobj.value(noteRoles[NotesModel::titleRole]).toString();
note.category = jobj.value(noteRoles[NotesModel::categoryRole]).toString();
note.content = jobj.value(noteRoles[NotesModel::contentRole]).toString();
note.favorite = jobj.value(noteRoles[NotesModel::favoriteRole]).toBool();
note.etag = jobj.value(noteRoles[NotesModel::etagRole]).toString();
note.error = jobj.value(noteRoles[NotesModel::errorRole]).toBool(true);
note.errorMessage = jobj.value(noteRoles[NotesModel::errorMessageRole]).toString();
return note;
}
static bool searchInNote(const QString &query, const Note &note, SearchAttributes criteria = QFlag(SearchAll), Qt::CaseSensitivity cs = Qt::CaseInsensitive) {
bool queryFound = false;
if (criteria.testFlag(SearchInTitle)) {
queryFound |= note.title.contains(query, cs);
}
if (criteria.testFlag(SearchInContent)) {
queryFound |= note.content.contains(query, cs);
}
if (criteria.testFlag(SearchInCategory)) {
queryFound |= note.category.contains(query, cs);
}
return queryFound;
}
};
Q_DECLARE_OPERATORS_FOR_FLAGS(Note::SearchAttributes)
NotesModel::NotesModel(QObject *parent) : QAbstractListModel(parent) NotesModel::NotesModel(QObject *parent) : QAbstractListModel(parent)
{ {
m_sortBy = noSorting; m_sortBy = noSorting;
@ -85,7 +12,12 @@ NotesModel::NotesModel(QObject *parent) : QAbstractListModel(parent)
} }
NotesModel::~NotesModel() { NotesModel::~NotesModel() {
beginRemoveRows(QModelIndex(), 0, rowCount());
for (int i = 0; i < m_notes.size(); i++) {
delete m_notes[i].note;
}
m_notes.clear(); m_notes.clear();
endRemoveRows();
} }
void NotesModel::setSortBy(int sortBy) { void NotesModel::setSortBy(int sortBy) {
@ -104,43 +36,49 @@ void NotesModel::setFavoritesOnTop(bool favoritesOnTop) {
} }
} }
void NotesModel::setSearchText(QString searchText) {
if (searchText != m_searchText) {
search(searchText);
}
}
bool NotesModel::applyJSON(QString json, bool replaceIfArray) { bool NotesModel::applyJSON(QString json, bool replaceIfArray) {
qDebug() << "Applying JSON...";// << json; //qDebug() << "Applying JSON...";// << json;
QJsonDocument jdoc = QJsonDocument::fromJson(json.toUtf8()); QJsonDocument jdoc = QJsonDocument::fromJson(json.toUtf8());
int notesModified = 0; int notesModified = 0;
if (!jdoc.isNull()) { if (!jdoc.isNull()) {
if (jdoc.isArray()) { if (jdoc.isArray()) {
qDebug() << "It's an array..."; //qDebug() << "It's an array...";
QJsonArray jarr = jdoc.array(); QJsonArray jarr = jdoc.array();
QList<int> notesToRemove; QList<int> notesToRemove;
QList<ModelNote<Note, int> > notesToAdd;
for (int i = 0; i < m_notes.size(); i++) for (int i = 0; i < m_notes.size(); i++)
notesToRemove << i; notesToRemove << i;
while (!jarr.empty()) { while (!jarr.empty()) {
qDebug() << jarr.count() << "JSON Objects to handle..."; //qDebug() << jarr.count() << "JSON Objects to handle...";
QJsonValue jval = jarr.first(); QJsonValue jval = jarr.first();
if (jval.isObject()) { if (jval.isObject()) {
qDebug() << "It's an object, all fine..."; //qDebug() << "It's an object, all fine...";
QJsonObject jobj = jval.toObject(); QJsonObject jobj = jval.toObject();
if (!jobj.isEmpty() && !jobj.value(noteRoles[errorRole]).toBool(true)) { if (!jobj.isEmpty() && !jobj.value(roleNames()[ErrorRole]).toBool(true)) {
qDebug() << "Adding it to the model..."; //qDebug() << "Adding it to the model...";
Note note = Note::fromjson(jobj); Note* note = Note::fromjson(jobj);
int position = indexOf(note.id); int position = indexOf(note->id());
if (position >= 0 && replaceIfArray) { if (position >= 0 && replaceIfArray) {
qDebug() << "Replacing note" << note.title << "on position" << position; //qDebug() << "Replacing note" << note.title << "on position" << position;
m_notes[position].note = note; m_notes[position].note = note;
emit dataChanged(index(position), index(position)); emit dataChanged(index(position), index(position));
notesToRemove.removeAt(position); notesToRemove.removeAt(position);
delete note;
} }
else { else {
qDebug() << "New note" << note.title << "adding it to the notes to add..."; //qDebug() << "New note" << note.title << "adding it to the notes to add...";
position = insertPosition(note); position = insertPosition(*note);
//beginInsertRows(QModelIndex(), position, position); ModelNote<Note*, bool> noteToInsert;
ModelNote<Note, int> noteToAdd; noteToInsert.note = note; noteToInsert.param = true;
noteToAdd.note = note; noteToAdd.param = position; //qDebug() << "Adding note"<< note.title << "on position" << position;
notesToAdd << noteToAdd; beginInsertRows(QModelIndex(), position, position);
//m_notes[position].note = note; m_notes.insert(position, noteToInsert);
//endInsertRows(); endInsertRows();
} }
notesModified++; notesModified++;
} }
@ -157,29 +95,31 @@ bool NotesModel::applyJSON(QString json, bool replaceIfArray) {
m_notes.removeAt(notesToRemove[i]); m_notes.removeAt(notesToRemove[i]);
endRemoveRows(); endRemoveRows();
}*/ }*/
for (int i = 0; i < notesToAdd.size(); i++) { /*for (int i = 0; i < notesToAdd.size(); i++) {
beginInsertRows(QModelIndex(), notesToAdd[i].param, notesToAdd[i].param); beginInsertRows(QModelIndex(), notesToAdd[i].param, notesToAdd[i].param);
ModelNote<Note, bool> note; ModelNote<Note, bool> note;
note.note = notesToAdd[i].note; note.note = notesToAdd[i].note;
qDebug() << "Adding note"<< note.note.title; qDebug() << "Adding note"<< note.note.title << "on position" << notesToAdd[i].param;
m_notes.insert(notesToAdd[i].param, note); m_notes.insert(notesToAdd[i].param, note);
endInsertRows(); endInsertRows();
} }*/
} }
else if (jdoc.isObject()) { else if (jdoc.isObject()) {
qDebug() << "It's a single object..."; //qDebug() << "It's a single object...";
QJsonObject jobj = jdoc.object(); QJsonObject jobj = jdoc.object();
if (!jobj.isEmpty() && !jobj.value(noteRoles[errorRole]).toBool(true)) { if (!jobj.isEmpty() && !jobj.value(roleNames()[ErrorRole]).toBool(true)) {
Note note; Note* note = Note::fromjson(jobj);
note.fromjson(jobj); int position = indexOf(note->id());
int position = indexOf(note.id);
if (position >= 0 && replaceIfArray) { if (position >= 0 && replaceIfArray) {
m_notes[position].note = note; m_notes[position].note = note;
delete note;
} }
else { else {
position = insertPosition(note); position = insertPosition(*note);
ModelNote<Note*, bool> noteToInsert;
noteToInsert.note = note; noteToInsert.param = true;
beginInsertRows(index(position), position, position); beginInsertRows(index(position), position, position);
m_notes[position].note = note; m_notes.insert(position, noteToInsert);
endInsertRows(); endInsertRows();
} }
notesModified++; notesModified++;
@ -187,7 +127,7 @@ bool NotesModel::applyJSON(QString json, bool replaceIfArray) {
} }
if (notesModified > 0) { if (notesModified > 0) {
sort(); // TODO react to signal connect() sort(); // TODO react to signal connect()
search(m_searchQuery); search(m_searchText);
} }
return true; return true;
} }
@ -207,14 +147,17 @@ bool NotesModel::removeNote(int id) {
return noteRemoved; return noteRemoved;
} }
void NotesModel::search(QString query) { void NotesModel::search(QString searchText) {
m_searchQuery = query; if (m_searchText != searchText) {
m_searchText = searchText;
emit searchTextChanged(m_searchText);
}
for (int i = 0; i < m_notes.size(); i++) { for (int i = 0; i < m_notes.size(); i++) {
if (m_searchQuery.isEmpty()) { if (m_searchText.isEmpty()) {
m_notes[i].param = true; m_notes[i].param = true;
} }
else { else {
m_notes[i].param = Note::searchInNote(m_searchQuery, m_notes[i].note); m_notes[i].param = Note::searchInNote(m_searchText, m_notes[i].note);
} }
} }
} }
@ -225,12 +168,20 @@ void NotesModel::clearSearch() {
int NotesModel::indexOf(int id) const { int NotesModel::indexOf(int id) const {
for (int i = 0; i < m_notes.size(); i++) { for (int i = 0; i < m_notes.size(); i++) {
if (m_notes[i].note.id == id) if (m_notes[i].note->id() == id)
return i; return i;
} }
return -1; return -1;
} }
Note* NotesModel::get(int index) const {
Note* note = NULL;
if (index >= 0 && index < m_notes.size()) {
note = m_notes[index].note;
}
return note;
}
/* /*
bool NotesModel::addNote(Note &note) { bool NotesModel::addNote(Note &note) {
m_notes.append(note); m_notes.append(note);
@ -246,7 +197,18 @@ bool NotesModel::addNotes(QList<Note> &notes) {
*/ */
QHash<int, QByteArray> NotesModel::roleNames() const { QHash<int, QByteArray> NotesModel::roleNames() const {
return noteRoles; return QHash<int, QByteArray> {
{NotesModel::VisibleRole, "visible"},
{NotesModel::IdRole, "id"},
{NotesModel::ModifiedRole, "modified"},
{NotesModel::TitleRole, "title"},
{NotesModel::CategoryRole, "category"},
{NotesModel::ContentRole, "content"},
{NotesModel::FavoriteRole, "favorite"},
{NotesModel::EtagRole, "etag"},
{NotesModel::ErrorRole, "error"},
{NotesModel::ErrorMessageRole, "errorMessage"}
};
} }
QHash<int, QByteArray> NotesModel::sortingNames() const { QHash<int, QByteArray> NotesModel::sortingNames() const {
@ -278,60 +240,83 @@ int NotesModel::rowCount(const QModelIndex &parent) const {
QVariant NotesModel::data(const QModelIndex &index, int role) const { QVariant NotesModel::data(const QModelIndex &index, int role) const {
if (!index.isValid()) return QVariant(); if (!index.isValid()) return QVariant();
else if (role == visible) return m_notes[index.row()].param; else if (role == VisibleRole) return m_notes[index.row()].param;
else if (role == idRole) return m_notes[index.row()].note.id; else if (role == IdRole) return m_notes[index.row()].note->id();
else if (role == modifiedRole) return m_notes[index.row()].note.modified; else if (role == ModifiedRole) return m_notes[index.row()].note->modified();
else if (role == titleRole) return m_notes[index.row()].note.title; else if (role == TitleRole) return m_notes[index.row()].note->title();
else if (role == categoryRole) return m_notes[index.row()].note.category; else if (role == CategoryRole) return m_notes[index.row()].note->category();
else if (role == contentRole) return m_notes[index.row()].note.content; else if (role == ContentRole) return m_notes[index.row()].note->content();
else if (role == favoriteRole) return m_notes[index.row()].note.favorite; else if (role == FavoriteRole) return m_notes[index.row()].note->favorite();
else if (role == etagRole) return m_notes[index.row()].note.etag; else if (role == EtagRole) return m_notes[index.row()].note->etag();
else if (role == errorRole) return m_notes[index.row()].note.error; else if (role == ErrorRole) return m_notes[index.row()].note->error();
else if (role == errorMessageRole) return m_notes[index.row()].note.errorMessage; else if (role == ErrorMessageRole) return m_notes[index.row()].note->errorMessage();
return QVariant(); return QVariant();
} }
QMap<int, QVariant> NotesModel::itemData(const QModelIndex &index) const {
QMap<int, QVariant> map;
if (!index.isValid()) return map;
else {
for (int role = VisibleRole; role <= ErrorMessageRole; role++) {
map.insert(role, data(index, role));
}
}
return map;
}
bool NotesModel::setData(const QModelIndex &index, const QVariant &value, int role) { bool NotesModel::setData(const QModelIndex &index, const QVariant &value, int role) {
if (!index.isValid()) return false; if (!index.isValid()) return false;
else if (role == modifiedRole) { else if (role == ModifiedRole && m_notes[index.row()].note->modified() != value.toUInt()) {
m_notes[index.row()].note.modified = value.toInt(); m_notes[index.row()].note->setModified(value.toInt());
emit dataChanged(this->index(index.row()), this->index(index.row()), QVector<int> { 1, role } ); emit dataChanged(this->index(index.row()), this->index(index.row()), QVector<int> { 1, role } );
sort(); sort();
return true; return true;
} }
else if (role == categoryRole) { else if (role == CategoryRole && m_notes[index.row()].note->category() != value.toString()) {
m_notes[index.row()].note.category = value.toString(); m_notes[index.row()].note->setCategory(value.toString());
emit dataChanged(this->index(index.row()), this->index(index.row()), QVector<int> { 1, role } ); emit dataChanged(this->index(index.row()), this->index(index.row()), QVector<int> { 1, role } );
sort(); sort();
return true; return true;
} }
else if (role == contentRole) { else if (role == ContentRole && m_notes[index.row()].note->content() != value.toString()) {
m_notes[index.row()].note.content = value.toString(); m_notes[index.row()].note->setContent(value.toString());
emit dataChanged(this->index(index.row()), this->index(index.row()), QVector<int> { 1, role } ); emit dataChanged(this->index(index.row()), this->index(index.row()), QVector<int> { 1, role } );
sort(); sort();
return true; return true;
} }
else if (role == favoriteRole) { else if (role == FavoriteRole && m_notes[index.row()].note->favorite() != value.toBool()) {
m_notes[index.row()].note.favorite = value.toBool(); m_notes[index.row()].note->setFavorite(value.toBool());
emit dataChanged(this->index(index.row()), this->index(index.row()), QVector<int> { 1, role } ); emit dataChanged(this->index(index.row()), this->index(index.row()), QVector<int> { 1, role } );
sort(); sort();
return true; return true;
} }
return false; return false;
} }
bool NotesModel::setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles) {
if (!index.isValid()) return false;
else if (roles.contains(ModifiedRole) || roles.contains(CategoryRole) || roles.contains(ContentRole) || roles.contains(FavoriteRole)) {
QMap<int, QVariant>::const_iterator i = roles.constBegin();
while (i != roles.constEnd()) {
setData(index, i.value(), i.key());
i++;
}
}
return false;
}
void NotesModel::sort() { void NotesModel::sort() {
QList<ModelNote<Note, bool> > notes; QList<ModelNote<Note*, bool> > notes;
QMap<QString, ModelNote<Note, bool> > map; QMap<QString, ModelNote<Note*, bool> > map;
QMap<QString, ModelNote<Note, bool> > favorites; QMap<QString, ModelNote<Note*, bool> > favorites;
switch (m_sortBy) { switch (m_sortBy) {
case sortByDate: case sortByDate:
emit layoutAboutToBeChanged(QList<QPersistentModelIndex> (), VerticalSortHint); emit layoutAboutToBeChanged(QList<QPersistentModelIndex> (), VerticalSortHint);
for (int i = 0; i < m_notes.size(); i++) { for (int i = 0; i < m_notes.size(); i++) {
if (m_favoritesOnTop && m_notes[i].note.favorite) if (m_favoritesOnTop && m_notes[i].note->favorite())
favorites.insert(QString::number(m_notes[i].note.modified), m_notes[i]); favorites.insert(QString::number(m_notes[i].note->modified()), m_notes[i]);
else else
map.insert(QString::number(m_notes[i].note.modified), m_notes[i]); map.insert(QString::number(m_notes[i].note->modified()), m_notes[i]);
} }
notes = favorites.values(); notes = favorites.values();
notes.append(map.values()); notes.append(map.values());
@ -341,10 +326,10 @@ void NotesModel::sort() {
case sortByCategory: case sortByCategory:
emit layoutAboutToBeChanged(QList<QPersistentModelIndex> (), VerticalSortHint); emit layoutAboutToBeChanged(QList<QPersistentModelIndex> (), VerticalSortHint);
for (int i = 0; i < m_notes.size(); i++) { for (int i = 0; i < m_notes.size(); i++) {
if (m_favoritesOnTop && m_notes[i].note.favorite) if (m_favoritesOnTop && m_notes[i].note->favorite())
favorites.insert(m_notes[i].note.category, m_notes[i]); favorites.insert(m_notes[i].note->category(), m_notes[i]);
else else
map.insert(m_notes[i].note.category, m_notes[i]); map.insert(m_notes[i].note->category(), m_notes[i]);
} }
notes = favorites.values(); notes = favorites.values();
notes.append(map.values()); notes.append(map.values());
@ -354,10 +339,10 @@ void NotesModel::sort() {
case sortByTitle: case sortByTitle:
emit layoutAboutToBeChanged(QList<QPersistentModelIndex> (), VerticalSortHint); emit layoutAboutToBeChanged(QList<QPersistentModelIndex> (), VerticalSortHint);
for (int i = 0; i < m_notes.size(); i++) { for (int i = 0; i < m_notes.size(); i++) {
if (m_favoritesOnTop && m_notes[i].note.favorite) if (m_favoritesOnTop && m_notes[i].note->favorite())
favorites.insert(m_notes[i].note.title, m_notes[i]); favorites.insert(m_notes[i].note->title(), m_notes[i]);
else else
map.insert(m_notes[i].note.title, m_notes[i]); map.insert(m_notes[i].note->title(), m_notes[i]);
} }
notes = favorites.values(); notes = favorites.values();
notes.append(map.values()); notes.append(map.values());
@ -372,22 +357,22 @@ void NotesModel::sort() {
bool NotesModel::noteLessThan(const Note &n1, const Note &n2) const { bool NotesModel::noteLessThan(const Note &n1, const Note &n2) const {
switch (m_sortBy) { switch (m_sortBy) {
case sortByDate: case sortByDate:
if (m_favoritesOnTop && n1.favorite != n2.favorite) if (m_favoritesOnTop && n1.favorite() != n2.favorite())
return n1.favorite; return n1.favorite();
else else
return n1.modified > n2.modified; return n1.modified() > n2.modified();
break; break;
case sortByCategory: case sortByCategory:
if (m_favoritesOnTop && n1.favorite != n2.favorite) if (m_favoritesOnTop && n1.favorite() != n2.favorite())
return n1.favorite; return n1.favorite();
else else
return n1.category < n2.category; return n1.category() < n2.category();
break; break;
case sortByTitle: case sortByTitle:
if (m_favoritesOnTop && n1.favorite != n2.favorite) if (m_favoritesOnTop && n1.favorite() != n2.favorite())
return n1.favorite; return n1.favorite();
else else
return n1.title < n2.title; return n1.title() < n2.title();
break; break;
default: default:
break; break;

View file

@ -3,9 +3,7 @@
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QDateTime> #include <QDateTime>
#include <QJsonObject> #include "note.h"
struct Note;
template <typename N, typename P> template <typename N, typename P>
struct ModelNote { struct ModelNote {
@ -27,25 +25,30 @@ public:
bool favoritesOnTop() const { return m_favoritesOnTop; } bool favoritesOnTop() const { return m_favoritesOnTop; }
void setFavoritesOnTop(bool favoritesOnTop); void setFavoritesOnTop(bool favoritesOnTop);
Q_PROPERTY(QString searchText READ searchText WRITE setSearchText NOTIFY searchTextChanged)
QString searchText() const { return m_searchText; }
void setSearchText(QString searchText);
Q_INVOKABLE bool applyJSON(QString json, bool replaceIfArray = true); Q_INVOKABLE bool applyJSON(QString json, bool replaceIfArray = true);
Q_INVOKABLE bool removeNote(int id); Q_INVOKABLE bool removeNote(int id);
Q_INVOKABLE void search(QString query); Q_INVOKABLE void search(QString searchText);
Q_INVOKABLE void clearSearch(); Q_INVOKABLE void clearSearch();
Q_INVOKABLE int indexOf(int id) const; Q_INVOKABLE int indexOf(int id) const;
Q_INVOKABLE Note *get(int index) const;
enum NoteRoles { enum NoteRoles {
visible = Qt::UserRole, VisibleRole = Qt::UserRole,
idRole = Qt::UserRole + 1, IdRole = Qt::UserRole + 1,
modifiedRole = Qt::UserRole + 2, ModifiedRole = Qt::UserRole + 2,
titleRole = Qt::UserRole + 3, TitleRole = Qt::UserRole + 3,
categoryRole = Qt::UserRole + 4, CategoryRole = Qt::UserRole + 4,
contentRole = Qt::UserRole + 5, ContentRole = Qt::UserRole + 5,
favoriteRole = Qt::UserRole + 6, FavoriteRole = Qt::UserRole + 6,
etagRole = Qt::UserRole + 7, EtagRole = Qt::UserRole + 7,
errorRole = Qt::UserRole + 8, ErrorRole = Qt::UserRole + 8,
errorMessageRole = Qt::UserRole + 9 ErrorMessageRole = Qt::UserRole + 9
}; };
QHash<int, QByteArray> roleNames() const; QHash<int, QByteArray> roleNames() const;
@ -60,8 +63,10 @@ public:
Qt::ItemFlags flags(const QModelIndex &index) const; Qt::ItemFlags flags(const QModelIndex &index) const;
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
virtual QVariant data(const QModelIndex &index, int role) const; virtual QVariant data(const QModelIndex &index, int role) const;
QMap<int, QVariant> itemData(const QModelIndex &index) const;
//virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; //virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
virtual bool setData(const QModelIndex &index, const QVariant &value, int role); virtual bool setData(const QModelIndex &index, const QVariant &value, int role);
virtual bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles);
//bool insertRow(int row, const QModelIndex &parent); //bool insertRow(int row, const QModelIndex &parent);
//bool insertRows(int row, int count, const QModelIndex &parent); //bool insertRows(int row, int count, const QModelIndex &parent);
@ -74,12 +79,13 @@ signals:
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles = QVector<int> ()); void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles = QVector<int> ());
void sortByChanged(int sortBy); void sortByChanged(int sortBy);
void favoritesOnTopChanged(bool favoritesOnTop); void favoritesOnTopChanged(bool favoritesOnTop);
void searchTextChanged(QString searchText);
private: private:
QList<ModelNote<Note, bool> > m_notes; QList<ModelNote<Note*, bool> > m_notes;
int m_sortBy; int m_sortBy;
bool m_favoritesOnTop; bool m_favoritesOnTop;
QString m_searchQuery; QString m_searchText;
void sort(); void sort();
void update(); void update();

View file

@ -298,6 +298,18 @@
<source>Nextcloud Notes</source> <source>Nextcloud Notes</source>
<translation type="unfinished">Nextcloud Notizen</translation> <translation type="unfinished">Nextcloud Notizen</translation>
</message> </message>
<message>
<source>Modified</source>
<translation type="unfinished">Geändert</translation>
</message>
<message>
<source>Delete</source>
<translation type="unfinished">Löschen</translation>
</message>
<message>
<source>Deleting note</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SettingsPage</name> <name>SettingsPage</name>

View file

@ -298,6 +298,18 @@
<source>Nextcloud Notes</source> <source>Nextcloud Notes</source>
<translation type="unfinished">Nextcloud Notes</translation> <translation type="unfinished">Nextcloud Notes</translation>
</message> </message>
<message>
<source>Modified</source>
<translation type="unfinished">Ändrad</translation>
</message>
<message>
<source>Delete</source>
<translation type="unfinished">Ta bort</translation>
</message>
<message>
<source>Deleting note</source>
<translation type="unfinished">Tar bort anteckning</translation>
</message>
</context> </context>
<context> <context>
<name>SettingsPage</name> <name>SettingsPage</name>

View file

@ -219,52 +219,52 @@
<context> <context>
<name>NotePage</name> <name>NotePage</name>
<message> <message>
<location filename="../qml/pages/NotePage.qml" line="104"/> <location filename="../qml/pages/NotePage.qml" line="105"/>
<source>Delete</source> <source>Delete</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotePage.qml" line="108"/> <location filename="../qml/pages/NotePage.qml" line="109"/>
<source>Reload</source> <source>Reload</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotePage.qml" line="108"/> <location filename="../qml/pages/NotePage.qml" line="109"/>
<source>Updating...</source> <source>Updating...</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotePage.qml" line="114"/> <location filename="../qml/pages/NotePage.qml" line="115"/>
<source>Last update</source> <source>Last update</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotePage.qml" line="117"/> <location filename="../qml/pages/NotePage.qml" line="118"/>
<source>never</source> <source>never</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotePage.qml" line="124"/> <location filename="../qml/pages/NotePage.qml" line="125"/>
<source>Edit</source> <source>Edit</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotePage.qml" line="125"/> <location filename="../qml/pages/NotePage.qml" line="126"/>
<source>Notes</source> <source>Notes</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotePage.qml" line="245"/> <location filename="../qml/pages/NotePage.qml" line="246"/>
<source>No category</source> <source>No category</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotePage.qml" line="246"/> <location filename="../qml/pages/NotePage.qml" line="247"/>
<source>Category</source> <source>Category</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotePage.qml" line="261"/> <location filename="../qml/pages/NotePage.qml" line="262"/>
<source>Modified</source> <source>Modified</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -290,77 +290,92 @@
<context> <context>
<name>NotesPage</name> <name>NotesPage</name>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="30"/> <location filename="../qml/pages/NotesPage.qml" line="31"/>
<source>Settings</source> <source>Settings</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="34"/> <location filename="../qml/pages/NotesPage.qml" line="35"/>
<source>Add note</source> <source>Add note</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="39"/> <location filename="../qml/pages/NotesPage.qml" line="40"/>
<source>Reload</source> <source>Reload</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="39"/> <location filename="../qml/pages/NotesPage.qml" line="40"/>
<source>Updating...</source> <source>Updating...</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="45"/> <location filename="../qml/pages/NotesPage.qml" line="46"/>
<source>Last update</source> <source>Last update</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="48"/> <location filename="../qml/pages/NotesPage.qml" line="49"/>
<source>never</source> <source>never</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="58"/> <location filename="../qml/pages/NotesPage.qml" line="59"/>
<source>Nextcloud Notes</source> <source>Nextcloud Notes</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="104"/> <location filename="../qml/pages/NotesPage.qml" line="187"/>
<source>Modified</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="190"/>
<source>Delete</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="192"/>
<source>Deleting note</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="217"/>
<source>No account yet</source> <source>No account yet</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="105"/> <location filename="../qml/pages/NotesPage.qml" line="218"/>
<source>Got to the settings to add an account</source> <source>Got to the settings to add an account</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="111"/> <location filename="../qml/pages/NotesPage.qml" line="224"/>
<source>No notes yet</source> <source>No notes yet</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="112"/> <location filename="../qml/pages/NotesPage.qml" line="225"/>
<source>Pull down to add a note</source> <source>Pull down to add a note</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="118"/> <location filename="../qml/pages/NotesPage.qml" line="231"/>
<source>No result</source> <source>No result</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="119"/> <location filename="../qml/pages/NotesPage.qml" line="232"/>
<source>Try another query</source> <source>Try another query</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="125"/> <location filename="../qml/pages/NotesPage.qml" line="238"/>
<source>An error occurred</source> <source>An error occurred</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="136"/> <location filename="../qml/pages/NotesPage.qml" line="249"/>
<source>Open the settings to configure your Nextcloud accounts</source> <source>Open the settings to configure your Nextcloud accounts</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>