From 4c71c8571ceb10b29e6550cd0d8eb928049e6851 Mon Sep 17 00:00:00 2001 From: Aqua-sama Date: Tue, 14 Jan 2020 23:34:13 +0200 Subject: Move/rename files for readability - add BookmarkFormat <<|>> BookmarkModel operators --- lib/bookmarks/bookmarkmodel.cpp | 356 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 356 insertions(+) create mode 100644 lib/bookmarks/bookmarkmodel.cpp (limited to 'lib/bookmarks/bookmarkmodel.cpp') diff --git a/lib/bookmarks/bookmarkmodel.cpp b/lib/bookmarks/bookmarkmodel.cpp new file mode 100644 index 0000000..895b178 --- /dev/null +++ b/lib/bookmarks/bookmarkmodel.cpp @@ -0,0 +1,356 @@ +/* + * This file is part of smolbote. It's copyrighted by the contributors recorded + * in the version control history of the file, available from its original + * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote + * + * SPDX-License-Identifier: GPL-3.0 + */ + +#include "bookmarkmodel.h" +#include "bookmarkitem.h" +#include "formats/xbel.h" +#include +#include +#include + +BookmarkModel::BookmarkModel(QObject *parent) + : QAbstractItemModel(parent) +{ + rootItem = new BookmarkItem({ tr("Title"), tr("Address") }, BookmarkItem::Root, nullptr); +} + +BookmarkModel::~BookmarkModel() +{ + delete rootItem; +} + +QVariant BookmarkModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if(orientation == Qt::Horizontal && role == Qt::DisplayRole) + return rootItem->data(static_cast(section)); + + return QVariant(); +} + +QVariant BookmarkModel::data(const QModelIndex &index, int role) const +{ + if(!index.isValid()) + return QVariant(); + + if(role == Qt::DecorationRole && index.column() == 0) + return static_cast(index.internalPointer())->icon(); + + else if(role == Qt::ToolTipRole) + return static_cast(index.internalPointer())->tooltip(); + + else if(role == Qt::DisplayRole) + return static_cast(index.internalPointer())->data(static_cast(index.column())); + + else + return QVariant(); +} + +QVariant BookmarkModel::data(const QModelIndex &index, int column, int role) const +{ + if(!index.isValid()) + return QVariant(); + + if(role == Qt::DisplayRole) + return static_cast(index.internalPointer())->data(static_cast(column)); + + return QVariant(); +} + +bool BookmarkModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if(!index.isValid()) + return false; + + bool success = false; + + if(role == Qt::DisplayRole) { + success = static_cast(index.internalPointer())->setData(static_cast(index.column()), value); + } + + if(success) { + emit dataChanged(index, index, { role }); + m_isModified = true; + } + return success; +} + +bool BookmarkModel::setData(const QModelIndex &index, const QVariant &value, BookmarkItem::Fields column, int role) +{ + if(!index.isValid() || role != Qt::DisplayRole) + return false; + + bool success = static_cast(index.internalPointer())->setData(column, value); + if(success) { + emit dataChanged(index, index, { role }); + m_isModified = true; + } + return success; +} + +Qt::ItemFlags BookmarkModel::flags(const QModelIndex &index) const +{ + if(getItem(index)->type() == BookmarkItem::Folder) + return QAbstractItemModel::flags(index) | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; + else + return QAbstractItemModel::flags(index) | Qt::ItemIsDragEnabled | Qt::ItemNeverHasChildren; +} + +bool BookmarkModel::isItemExpanded(const QModelIndex &index) const +{ + if(!index.isValid()) + return false; + + return static_cast(index.internalPointer())->isExpanded(); +} + +void BookmarkModel::setItemExpanded(const QModelIndex &index, bool expanded) +{ + BookmarkItem *item = getItem(index); + if(item->type() == BookmarkItem::Folder) { + item->setExpanded(expanded); + m_isModified = true; + } +} + +int BookmarkModel::rowCount(const QModelIndex &index) const +{ + if(index.column() > 0) + return 0; + + return getItem(index)->childCount(); +} + +QModelIndex BookmarkModel::appendBookmark(const QString &title, const QString &url, const QModelIndex &parent) +{ + auto *parentItem = getItem(parent); + + int row = parentItem->childCount(); + beginInsertRows(parent, row, row); + auto *childItem = new BookmarkItem({ title, url }, BookmarkItem::Bookmark, parentItem); + parentItem->appendChild(childItem); + endInsertRows(); + + m_isModified = true; + return createIndex(row, 0, childItem); +} + +QModelIndex BookmarkModel::appendFolder(const QString &title, const QModelIndex &parent) +{ + auto *parentItem = getItem(parent); + const int row = parentItem->childCount(); + + beginInsertRows(parent, row, row); + auto *childItem = new BookmarkItem({ title }, BookmarkItem::Folder, parentItem); + parentItem->appendChild(childItem); + endInsertRows(); + + m_isModified = true; + return createIndex(row, 0, childItem); +} + +bool BookmarkModel::removeRows(int position, int rows, const QModelIndex &parent) +{ + auto *parentItem = getItem(parent); + + beginRemoveRows(parent, position, position + rows - 1); + bool success = parentItem->removeChildAt(position, rows); + endRemoveRows(); + + if(success) + m_isModified = true; + return success; +} + +int BookmarkModel::columnCount(const QModelIndex &index) const +{ + Q_UNUSED(index); + return 2; +} + +QModelIndex BookmarkModel::index(int row, int column, const QModelIndex &parent) const +{ + if(!this->hasIndex(row, column, parent)) + return QModelIndex(); + + BookmarkItem *parentItem = getItem(parent); + BookmarkItem *childItem = parentItem->child(row); + if(childItem) + return createIndex(row, column, childItem); + else + return QModelIndex(); +} + +QModelIndex BookmarkModel::parent(const QModelIndex &index) const +{ + if(!index.isValid()) + return QModelIndex(); + + auto *childItem = static_cast(index.internalPointer()); + auto *parentItem = childItem->parent(); + + if(parentItem == rootItem) + return QModelIndex(); + + return createIndex(parentItem->row(), 0, parentItem); +} + +QModelIndex BookmarkModel::parentFolder(const QModelIndex &index) const +{ + // invalid index is the root index -> return it back + if(!index.isValid()) + return QModelIndex(); + + if(getItem(index)->type() == BookmarkItem::Bookmark) { + return index.parent(); + } + + return index; +} + +inline bool has(const QStringList &terms, const QStringList &where) +{ + for(const QString &term : terms) { + if(where.contains(term)) + return true; + } + return false; +} + +inline QStringList searchThrough(const QString &term, const QStringList &tags, BookmarkItem *item) +{ + QStringList results; + + for(int i = 0; i < item->childCount(); ++i) { + auto *child = item->child(i); + + if(child->type() == BookmarkItem::Bookmark) { + if((!term.isEmpty() && child->data(BookmarkItem::Href).toString().contains(term)) || has(tags, child->data(BookmarkItem::Tags).toStringList())) + results.append(child->data(BookmarkItem::Href).toString()); + } + + else if(child->type() == BookmarkItem::Folder) { + if(has(tags, child->data(BookmarkItem::Tags).toStringList())) { + + // append all bookmarks + for(int i = 0; i < child->childCount(); ++i) { + auto *subChild = child->child(i); + if(subChild->type() == BookmarkItem::Bookmark) + results.append(subChild->data(BookmarkItem::Href).toString()); + } + } + results.append(searchThrough(term, tags, child)); + } + } + return results; +} + +QStringList BookmarkModel::search(const QString &term) const +{ + QString searchTerm = term; + QStringList tags; + + const QRegularExpression tagRE(":\\w+\\s?", QRegularExpression::CaseInsensitiveOption); + auto i = tagRE.globalMatch(term); + while(i.hasNext()) { + auto match = i.next(); + QString tag = match.captured(); + searchTerm.remove(tag); + tag = tag.remove(0, 1).trimmed(); + tags.append(tag); + } + + return searchThrough(searchTerm, tags, rootItem); +} + +BookmarkItem *BookmarkModel::getItem(const QModelIndex &index) const +{ + if(!index.isValid()) + return rootItem; + else + return static_cast(index.internalPointer()); +} + +/* + * Drag'n'Drop implementation + * How drag and drop actually works: the view encodes the data of the original + * item (through ::mimeData), and then uses ::dropMimeData to create the new + * item. If successful, the old item is removed (through ::removeRows). + * This means that the encoding and decoding needs to be provided. In this case, + * this is done through xbel. + */ + +Qt::DropActions BookmarkModel::supportedDropActions() const +{ + return Qt::MoveAction; +} + +QStringList BookmarkModel::mimeTypes() const +{ + return { mimeType }; +} + +QMimeData *BookmarkModel::mimeData(const QModelIndexList &indexes) const +{ + auto *mimeData = new QMimeData(); + QByteArray data; + + QDataStream stream(&data, QIODevice::WriteOnly); + for(const QModelIndex &index : indexes) { + if(index.isValid() && index.column() == 0) { + QByteArray encodedData; + QBuffer buffer(&encodedData); + buffer.open(QIODevice::WriteOnly); + + Xbel::write(&buffer, getItem(index)); + + stream << encodedData; + } + } + mimeData->setData(mimeType, data); + return mimeData; +} +bool BookmarkModel::dropMimeData(const QMimeData *mimeData, Qt::DropAction action, int row, int column, const QModelIndex &parent) +{ + if(action == Qt::IgnoreAction) + return true; + + if(action != Qt::MoveAction) + return false; + + if(!mimeData->hasFormat(mimeType) || column > 0) + return false; + + QByteArray data = mimeData->data(mimeType); + QDataStream stream(&data, QIODevice::ReadOnly); + if(stream.atEnd()) + return false; + + while(!stream.atEnd()) { + QByteArray encodedData; + stream >> encodedData; + + QBuffer buffer(&encodedData); + buffer.open(QIODevice::ReadOnly); + + auto *fakeRoot = new BookmarkItem({}, BookmarkItem::Folder, nullptr); + auto *parentItem = getItem(parent); + Xbel::read(&buffer, fakeRoot); + + beginInsertRows(parent, row, row + fakeRoot->childCount() - 1); + for(int i = 0; i < fakeRoot->childCount(); ++i) { + auto *child = fakeRoot->takeChild(0, parentItem); + parentItem->insertChild(row, child); + } + endInsertRows(); + + delete fakeRoot; + } + + m_isModified = true; + return true; +} -- cgit v1.2.1 From 5f708d4618d739d14442b85c466fdbac84a74cc8 Mon Sep 17 00:00:00 2001 From: Aqua-sama Date: Fri, 17 Jan 2020 10:25:27 +0200 Subject: BookmarkItem::DateAdded and LastModified fields - add read support in FFJson for DateAdded and LastModified fields - add read/write support in Xbel for DateAdded and LastModified fields - BookmarkModel: set DateAdded on appendBookmark and appendFolder - EditBookmarkDialog: set LastModified field when saving changes --- lib/bookmarks/bookmarkmodel.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib/bookmarks/bookmarkmodel.cpp') diff --git a/lib/bookmarks/bookmarkmodel.cpp b/lib/bookmarks/bookmarkmodel.cpp index 895b178..05df3d7 100644 --- a/lib/bookmarks/bookmarkmodel.cpp +++ b/lib/bookmarks/bookmarkmodel.cpp @@ -12,6 +12,7 @@ #include #include #include +#include BookmarkModel::BookmarkModel(QObject *parent) : QAbstractItemModel(parent) @@ -132,6 +133,7 @@ QModelIndex BookmarkModel::appendBookmark(const QString &title, const QString &u int row = parentItem->childCount(); beginInsertRows(parent, row, row); auto *childItem = new BookmarkItem({ title, url }, BookmarkItem::Bookmark, parentItem); + childItem->setData(BookmarkItem::DateAdded, QDateTime::currentDateTime()); parentItem->appendChild(childItem); endInsertRows(); @@ -146,6 +148,7 @@ QModelIndex BookmarkModel::appendFolder(const QString &title, const QModelIndex beginInsertRows(parent, row, row); auto *childItem = new BookmarkItem({ title }, BookmarkItem::Folder, parentItem); + childItem->setData(BookmarkItem::DateAdded, QDateTime::currentDateTime()); parentItem->appendChild(childItem); endInsertRows(); -- cgit v1.2.1