diff --git a/harbour-nextcloudnotes.pro b/harbour-nextcloudnotes.pro index 0213fce..a2dae15 100644 --- a/harbour-nextcloudnotes.pro +++ b/harbour-nextcloudnotes.pro @@ -18,11 +18,13 @@ DEFINES += APP_VERSION=\\\"$$VERSION\\\" HEADERS += \ src/sslconfiguration.h \ - src/notesmodel.h + src/notesmodel.h \ + src/note.h SOURCES += src/harbour-nextcloudnotes.cpp \ src/sslconfiguration.cpp \ - src/notesmodel.cpp + src/notesmodel.cpp \ + src/note.cpp DISTFILES += qml/harbour-nextcloudnotes.qml \ qml/cover/CoverPage.qml \ diff --git a/qml/harbour-nextcloudnotes.qml b/qml/harbour-nextcloudnotes.qml index a828145..ac48857 100644 --- a/qml/harbour-nextcloudnotes.qml +++ b/qml/harbour-nextcloudnotes.qml @@ -109,23 +109,23 @@ ApplicationWindow NotesApi { id: api uuid: appSettings.currentAccount - //onResponseChanged: noteListModel.applyJSON(response) + onResponseChanged: noteListModel.applyJSON(response) } - /*NotesModel { + NotesModel { id: noteListModel - sortBy: 0 + sortBy: 0 // TODO favoritesOnTop: appSettings.favoritesOnTop - }*/ + } - NoteDelegateModel { + /*NoteDelegateModel { id: noteListModel model: api.model favoritesOnTop: appSettings.favoritesOnTop sortBy: appSettings.sortBy showSeparator: appSettings.showSeparator previewLineCount: appSettings.previewLineCount - } + }*/ initialPage: Component { NotesPage { } } cover: Qt.resolvedUrl("cover/CoverPage.qml") diff --git a/qml/pages/NotePage.qml b/qml/pages/NotePage.qml index 013351b..3a2c66a 100644 --- a/qml/pages/NotePage.qml +++ b/qml/pages/NotePage.qml @@ -1,5 +1,6 @@ import QtQuick 2.5 import Sailfish.Silica 1.0 +import harbour.nextcloudnotes.note 1.0 import "../js/showdown-1.9.0/dist/showdown.js" as ShowDown Dialog { diff --git a/qml/pages/NotesPage.qml b/qml/pages/NotesPage.qml index b51f77f..a6c983f 100644 --- a/qml/pages/NotesPage.qml +++ b/qml/pages/NotesPage.qml @@ -1,5 +1,6 @@ import QtQuick 2.0 import Sailfish.Silica 1.0 +import harbour.nextcloudnotes.note 1.0 import "../components" Page { @@ -84,6 +85,118 @@ Page { 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.criteria: appSettings.sortBy === "title" ? ViewSection.FirstCharacter : ViewSection.FullString section.labelPositioning: appSettings.sortBy === "title" ? ViewSection.CurrentLabelAtStart | ViewSection.NextLabelAtEnd : ViewSection.InlineLabels diff --git a/src/harbour-nextcloudnotes.cpp b/src/harbour-nextcloudnotes.cpp index 9005401..34bbfa7 100644 --- a/src/harbour-nextcloudnotes.cpp +++ b/src/harbour-nextcloudnotes.cpp @@ -2,6 +2,7 @@ #include #include #include +#include "note.h" #include "notesmodel.h" #include "sslconfiguration.h" @@ -15,6 +16,7 @@ int main(int argc, char *argv[]) app->setOrganizationName("harbour-nextcloudnotes"); qDebug() << app->applicationDisplayName() << app->applicationVersion(); + qmlRegisterType("harbour.nextcloudnotes.note", 1, 0, "Note"); qmlRegisterType("harbour.nextcloudnotes.notesmodel", 1, 0, "NotesModel"); qmlRegisterType("harbour.nextcloudnotes.sslconfiguration", 1, 0, "SslConfiguration"); diff --git a/src/note.cpp b/src/note.cpp new file mode 100644 index 0000000..1c30e06 --- /dev/null +++ b/src/note.cpp @@ -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(); +} diff --git a/src/note.h b/src/note.h new file mode 100644 index 0000000..15e6286 --- /dev/null +++ b/src/note.h @@ -0,0 +1,108 @@ +#ifndef NOTE_H +#define NOTE_H + +#include +#include + +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 diff --git a/src/notesmodel.cpp b/src/notesmodel.cpp index e0e393e..c619574 100644 --- a/src/notesmodel.cpp +++ b/src/notesmodel.cpp @@ -1,83 +1,10 @@ #include "notesmodel.h" #include +#include #include #include #include -const QHash noteRoles = QHash{ - {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 ¬e, 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) { m_sortBy = noSorting; @@ -85,7 +12,12 @@ NotesModel::NotesModel(QObject *parent) : QAbstractListModel(parent) } NotesModel::~NotesModel() { + beginRemoveRows(QModelIndex(), 0, rowCount()); + for (int i = 0; i < m_notes.size(); i++) { + delete m_notes[i].note; + } m_notes.clear(); + endRemoveRows(); } 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) { - qDebug() << "Applying JSON...";// << json; + //qDebug() << "Applying JSON...";// << json; QJsonDocument jdoc = QJsonDocument::fromJson(json.toUtf8()); int notesModified = 0; if (!jdoc.isNull()) { if (jdoc.isArray()) { - qDebug() << "It's an array..."; + //qDebug() << "It's an array..."; QJsonArray jarr = jdoc.array(); QList notesToRemove; - QList > notesToAdd; for (int i = 0; i < m_notes.size(); i++) notesToRemove << i; while (!jarr.empty()) { - qDebug() << jarr.count() << "JSON Objects to handle..."; + //qDebug() << jarr.count() << "JSON Objects to handle..."; QJsonValue jval = jarr.first(); if (jval.isObject()) { - qDebug() << "It's an object, all fine..."; + //qDebug() << "It's an object, all fine..."; QJsonObject jobj = jval.toObject(); - if (!jobj.isEmpty() && !jobj.value(noteRoles[errorRole]).toBool(true)) { - qDebug() << "Adding it to the model..."; - Note note = Note::fromjson(jobj); - int position = indexOf(note.id); + if (!jobj.isEmpty() && !jobj.value(roleNames()[ErrorRole]).toBool(true)) { + //qDebug() << "Adding it to the model..."; + Note* note = Note::fromjson(jobj); + int position = indexOf(note->id()); 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; emit dataChanged(index(position), index(position)); notesToRemove.removeAt(position); + delete note; } else { - qDebug() << "New note" << note.title << "adding it to the notes to add..."; - position = insertPosition(note); - //beginInsertRows(QModelIndex(), position, position); - ModelNote noteToAdd; - noteToAdd.note = note; noteToAdd.param = position; - notesToAdd << noteToAdd; - //m_notes[position].note = note; - //endInsertRows(); + //qDebug() << "New note" << note.title << "adding it to the notes to add..."; + position = insertPosition(*note); + ModelNote noteToInsert; + noteToInsert.note = note; noteToInsert.param = true; + //qDebug() << "Adding note"<< note.title << "on position" << position; + beginInsertRows(QModelIndex(), position, position); + m_notes.insert(position, noteToInsert); + endInsertRows(); } notesModified++; } @@ -157,29 +95,31 @@ bool NotesModel::applyJSON(QString json, bool replaceIfArray) { m_notes.removeAt(notesToRemove[i]); 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); ModelNote 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); endInsertRows(); - } + }*/ } else if (jdoc.isObject()) { - qDebug() << "It's a single object..."; + //qDebug() << "It's a single object..."; QJsonObject jobj = jdoc.object(); - if (!jobj.isEmpty() && !jobj.value(noteRoles[errorRole]).toBool(true)) { - Note note; - note.fromjson(jobj); - int position = indexOf(note.id); + if (!jobj.isEmpty() && !jobj.value(roleNames()[ErrorRole]).toBool(true)) { + Note* note = Note::fromjson(jobj); + int position = indexOf(note->id()); if (position >= 0 && replaceIfArray) { m_notes[position].note = note; + delete note; } else { - position = insertPosition(note); + position = insertPosition(*note); + ModelNote noteToInsert; + noteToInsert.note = note; noteToInsert.param = true; beginInsertRows(index(position), position, position); - m_notes[position].note = note; + m_notes.insert(position, noteToInsert); endInsertRows(); } notesModified++; @@ -187,7 +127,7 @@ bool NotesModel::applyJSON(QString json, bool replaceIfArray) { } if (notesModified > 0) { sort(); // TODO react to signal connect() - search(m_searchQuery); + search(m_searchText); } return true; } @@ -207,14 +147,17 @@ bool NotesModel::removeNote(int id) { return noteRemoved; } -void NotesModel::search(QString query) { - m_searchQuery = query; +void NotesModel::search(QString searchText) { + if (m_searchText != searchText) { + m_searchText = searchText; + emit searchTextChanged(m_searchText); + } for (int i = 0; i < m_notes.size(); i++) { - if (m_searchQuery.isEmpty()) { + if (m_searchText.isEmpty()) { m_notes[i].param = true; } 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 { 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 -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 ¬e) { m_notes.append(note); @@ -246,7 +197,18 @@ bool NotesModel::addNotes(QList ¬es) { */ QHash NotesModel::roleNames() const { - return noteRoles; + return QHash { + {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 NotesModel::sortingNames() const { @@ -278,60 +240,83 @@ int NotesModel::rowCount(const QModelIndex &parent) const { QVariant NotesModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); - else if (role == visible) return m_notes[index.row()].param; - 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 == titleRole) return m_notes[index.row()].note.title; - 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 == favoriteRole) return m_notes[index.row()].note.favorite; - 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 == errorMessageRole) return m_notes[index.row()].note.errorMessage; + else if (role == VisibleRole) return m_notes[index.row()].param; + 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 == TitleRole) return m_notes[index.row()].note->title(); + 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 == FavoriteRole) return m_notes[index.row()].note->favorite(); + 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 == ErrorMessageRole) return m_notes[index.row()].note->errorMessage(); return QVariant(); } +QMap NotesModel::itemData(const QModelIndex &index) const { + QMap 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) { if (!index.isValid()) return false; - else if (role == modifiedRole) { - m_notes[index.row()].note.modified = value.toInt(); - emit dataChanged(this->index(index.row()), this->index(index.row()), QVector { 1, role } ); + else if (role == ModifiedRole && m_notes[index.row()].note->modified() != value.toUInt()) { + m_notes[index.row()].note->setModified(value.toInt()); + emit dataChanged(this->index(index.row()), this->index(index.row()), QVector { 1, role } ); sort(); return true; } - else if (role == categoryRole) { - m_notes[index.row()].note.category = value.toString(); - emit dataChanged(this->index(index.row()), this->index(index.row()), QVector { 1, role } ); + else if (role == CategoryRole && 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 { 1, role } ); sort(); return true; } - else if (role == contentRole) { - m_notes[index.row()].note.content = value.toString(); - emit dataChanged(this->index(index.row()), this->index(index.row()), QVector { 1, role } ); + else if (role == ContentRole && 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 { 1, role } ); sort(); return true; } - else if (role == favoriteRole) { - m_notes[index.row()].note.favorite = value.toBool(); - emit dataChanged(this->index(index.row()), this->index(index.row()), QVector { 1, role } ); + else if (role == FavoriteRole && 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 { 1, role } ); sort(); return true; } return false; } +bool NotesModel::setItemData(const QModelIndex &index, const QMap &roles) { + if (!index.isValid()) return false; + else if (roles.contains(ModifiedRole) || roles.contains(CategoryRole) || roles.contains(ContentRole) || roles.contains(FavoriteRole)) { + QMap::const_iterator i = roles.constBegin(); + while (i != roles.constEnd()) { + setData(index, i.value(), i.key()); + i++; + } + } + return false; +} + void NotesModel::sort() { - QList > notes; - QMap > map; - QMap > favorites; + QList > notes; + QMap > map; + QMap > favorites; switch (m_sortBy) { case sortByDate: emit layoutAboutToBeChanged(QList (), VerticalSortHint); for (int i = 0; i < m_notes.size(); i++) { - if (m_favoritesOnTop && m_notes[i].note.favorite) - favorites.insert(QString::number(m_notes[i].note.modified), m_notes[i]); + if (m_favoritesOnTop && m_notes[i].note->favorite()) + favorites.insert(QString::number(m_notes[i].note->modified()), m_notes[i]); 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.append(map.values()); @@ -341,10 +326,10 @@ void NotesModel::sort() { case sortByCategory: emit layoutAboutToBeChanged(QList (), VerticalSortHint); for (int i = 0; i < m_notes.size(); i++) { - if (m_favoritesOnTop && m_notes[i].note.favorite) - favorites.insert(m_notes[i].note.category, m_notes[i]); + if (m_favoritesOnTop && m_notes[i].note->favorite()) + favorites.insert(m_notes[i].note->category(), m_notes[i]); 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.append(map.values()); @@ -354,10 +339,10 @@ void NotesModel::sort() { case sortByTitle: emit layoutAboutToBeChanged(QList (), VerticalSortHint); for (int i = 0; i < m_notes.size(); i++) { - if (m_favoritesOnTop && m_notes[i].note.favorite) - favorites.insert(m_notes[i].note.title, m_notes[i]); + if (m_favoritesOnTop && m_notes[i].note->favorite()) + favorites.insert(m_notes[i].note->title(), m_notes[i]); 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.append(map.values()); @@ -372,22 +357,22 @@ void NotesModel::sort() { bool NotesModel::noteLessThan(const Note &n1, const Note &n2) const { switch (m_sortBy) { case sortByDate: - if (m_favoritesOnTop && n1.favorite != n2.favorite) - return n1.favorite; + if (m_favoritesOnTop && n1.favorite() != n2.favorite()) + return n1.favorite(); else - return n1.modified > n2.modified; + return n1.modified() > n2.modified(); break; case sortByCategory: - if (m_favoritesOnTop && n1.favorite != n2.favorite) - return n1.favorite; + if (m_favoritesOnTop && n1.favorite() != n2.favorite()) + return n1.favorite(); else - return n1.category < n2.category; + return n1.category() < n2.category(); break; case sortByTitle: - if (m_favoritesOnTop && n1.favorite != n2.favorite) - return n1.favorite; + if (m_favoritesOnTop && n1.favorite() != n2.favorite()) + return n1.favorite(); else - return n1.title < n2.title; + return n1.title() < n2.title(); break; default: break; diff --git a/src/notesmodel.h b/src/notesmodel.h index 0ec0042..f3da322 100644 --- a/src/notesmodel.h +++ b/src/notesmodel.h @@ -3,9 +3,7 @@ #include #include -#include - -struct Note; +#include "note.h" template struct ModelNote { @@ -27,25 +25,30 @@ public: bool favoritesOnTop() const { return m_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 removeNote(int id); - Q_INVOKABLE void search(QString query); + Q_INVOKABLE void search(QString searchText); Q_INVOKABLE void clearSearch(); Q_INVOKABLE int indexOf(int id) const; + Q_INVOKABLE Note *get(int index) const; enum NoteRoles { - visible = Qt::UserRole, - idRole = Qt::UserRole + 1, - modifiedRole = Qt::UserRole + 2, - titleRole = Qt::UserRole + 3, - categoryRole = Qt::UserRole + 4, - contentRole = Qt::UserRole + 5, - favoriteRole = Qt::UserRole + 6, - etagRole = Qt::UserRole + 7, - errorRole = Qt::UserRole + 8, - errorMessageRole = Qt::UserRole + 9 + VisibleRole = Qt::UserRole, + IdRole = Qt::UserRole + 1, + ModifiedRole = Qt::UserRole + 2, + TitleRole = Qt::UserRole + 3, + CategoryRole = Qt::UserRole + 4, + ContentRole = Qt::UserRole + 5, + FavoriteRole = Qt::UserRole + 6, + EtagRole = Qt::UserRole + 7, + ErrorRole = Qt::UserRole + 8, + ErrorMessageRole = Qt::UserRole + 9 }; QHash roleNames() const; @@ -60,8 +63,10 @@ public: Qt::ItemFlags flags(const QModelIndex &index) const; virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; virtual QVariant data(const QModelIndex &index, int role) const; + QMap itemData(const QModelIndex &index) 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 setItemData(const QModelIndex &index, const QMap &roles); //bool insertRow(int row, 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 &roles = QVector ()); void sortByChanged(int sortBy); void favoritesOnTopChanged(bool favoritesOnTop); + void searchTextChanged(QString searchText); private: - QList > m_notes; + QList > m_notes; int m_sortBy; bool m_favoritesOnTop; - QString m_searchQuery; + QString m_searchText; void sort(); void update(); diff --git a/translations/harbour-nextcloudnotes-de.ts b/translations/harbour-nextcloudnotes-de.ts index d952d80..4681a53 100644 --- a/translations/harbour-nextcloudnotes-de.ts +++ b/translations/harbour-nextcloudnotes-de.ts @@ -298,6 +298,18 @@ Nextcloud Notes Nextcloud Notizen + + Modified + Geändert + + + Delete + Löschen + + + Deleting note + + SettingsPage diff --git a/translations/harbour-nextcloudnotes-sv.ts b/translations/harbour-nextcloudnotes-sv.ts index ed3a97a..fa76b20 100644 --- a/translations/harbour-nextcloudnotes-sv.ts +++ b/translations/harbour-nextcloudnotes-sv.ts @@ -298,6 +298,18 @@ Nextcloud Notes Nextcloud Notes + + Modified + Ändrad + + + Delete + Ta bort + + + Deleting note + Tar bort anteckning + SettingsPage diff --git a/translations/harbour-nextcloudnotes.ts b/translations/harbour-nextcloudnotes.ts index 58b9687..44f0278 100644 --- a/translations/harbour-nextcloudnotes.ts +++ b/translations/harbour-nextcloudnotes.ts @@ -219,52 +219,52 @@ NotePage - + Delete - + Reload - + Updating... - + Last update - + never - + Edit - + Notes - + No category - + Category - + Modified @@ -290,77 +290,92 @@ NotesPage - + Settings - + Add note - + Reload - + Updating... - + Last update - + never - + Nextcloud Notes - + + Modified + + + + + Delete + + + + + Deleting note + + + + No account yet - + Got to the settings to add an account - + No notes yet - + Pull down to add a note - + No result - + Try another query - + An error occurred - + Open the settings to configure your Nextcloud accounts