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/bookmarkformat.cpp | 26 +++ lib/bookmarks/bookmarkformat.h | 67 ++++++ lib/bookmarks/bookmarkitem.cpp | 139 +++++++++++ lib/bookmarks/bookmarkitem.h | 69 ++++++ lib/bookmarks/bookmarkmodel.cpp | 356 +++++++++++++++++++++++++++++ lib/bookmarks/bookmarkmodel.h | 73 ++++++ lib/bookmarks/bookmarkswidget.cpp | 14 +- lib/bookmarks/formats/format.cpp | 26 --- lib/bookmarks/formats/format.h | 36 --- lib/bookmarks/formats/xbel.cpp | 2 +- lib/bookmarks/forms/editbookmarkdialog.cpp | 4 +- lib/bookmarks/meson.build | 6 +- lib/bookmarks/model/bookmarkitem.cpp | 139 ----------- lib/bookmarks/model/bookmarkitem.h | 69 ------ lib/bookmarks/model/bookmarkmodel.cpp | 356 ----------------------------- lib/bookmarks/model/bookmarkmodel.h | 73 ------ 16 files changed, 740 insertions(+), 715 deletions(-) create mode 100644 lib/bookmarks/bookmarkformat.cpp create mode 100644 lib/bookmarks/bookmarkformat.h create mode 100644 lib/bookmarks/bookmarkitem.cpp create mode 100644 lib/bookmarks/bookmarkitem.h create mode 100644 lib/bookmarks/bookmarkmodel.cpp create mode 100644 lib/bookmarks/bookmarkmodel.h delete mode 100644 lib/bookmarks/formats/format.cpp delete mode 100644 lib/bookmarks/formats/format.h delete mode 100644 lib/bookmarks/model/bookmarkitem.cpp delete mode 100644 lib/bookmarks/model/bookmarkitem.h delete mode 100644 lib/bookmarks/model/bookmarkmodel.cpp delete mode 100644 lib/bookmarks/model/bookmarkmodel.h diff --git a/lib/bookmarks/bookmarkformat.cpp b/lib/bookmarks/bookmarkformat.cpp new file mode 100644 index 0000000..e6149cd --- /dev/null +++ b/lib/bookmarks/bookmarkformat.cpp @@ -0,0 +1,26 @@ +/* + * 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 "bookmarkformat.h" +#include "formats/xbel.h" +#include + +template<> +void BookmarkFormat::read(BookmarkItem *root) const +{ + Q_CHECK_PTR(m_device); + Xbel::read(m_device, root); +} + +template<> +void BookmarkFormat::write(BookmarkItem *root) +{ + Q_CHECK_PTR(m_device); + Xbel::write(m_device, root); +} + diff --git a/lib/bookmarks/bookmarkformat.h b/lib/bookmarks/bookmarkformat.h new file mode 100644 index 0000000..673acbd --- /dev/null +++ b/lib/bookmarks/bookmarkformat.h @@ -0,0 +1,67 @@ +/* + * 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 + */ + +#ifndef BOOKMARKFORMAT_H +#define BOOKMARKFORMAT_H + +#include "bookmarkmodel.h" + +class QIODevice; + +enum BookmarkFormats { + XbelFormat +}; + +template +class BookmarkFormat +{ +public: + explicit BookmarkFormat(QIODevice *device) + { + m_device = device; + } + ~BookmarkFormat() + { + m_device->close(); + } + + void read(BookmarkItem *root) const; + void write(BookmarkItem *root); + +protected: + QIODevice *m_device; +}; + +template +void operator<<(BookmarkModel *model, const BookmarkFormat &format) +{ + format.read(model->root()); +} + +template +void operator>>(const BookmarkFormat &format, BookmarkModel *model) +{ + format.read(model->root()); +} + +template +void operator<<(BookmarkFormat &format, BookmarkModel *model) +{ + format.write(model->root()); + model->resetModified(); +} + +template +void operator>>(BookmarkModel *model, BookmarkFormat &format) +{ + format.write(model->root()); + model->resetModified(); +} + +#endif // BOOKMARKSFORMAT_H + diff --git a/lib/bookmarks/bookmarkitem.cpp b/lib/bookmarks/bookmarkitem.cpp new file mode 100644 index 0000000..21d034f --- /dev/null +++ b/lib/bookmarks/bookmarkitem.cpp @@ -0,0 +1,139 @@ +/* + * 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 "bookmarkitem.h" +#include +#include + +BookmarkItem::BookmarkItem(const QVector &data, Type type, BookmarkItem *parent) +{ + m_parentItem = parent; + + m_type = type; + if(m_type == Folder) { + m_icon.addPixmap(qApp->style()->standardPixmap(QStyle::SP_DirClosedIcon), QIcon::Normal, QIcon::Off); + m_icon.addPixmap(qApp->style()->standardPixmap(QStyle::SP_DirOpenIcon), QIcon::Normal, QIcon::On); + } else if(m_type == Bookmark) + m_icon.addPixmap(qApp->style()->standardPixmap(QStyle::SP_FileIcon)); + + m_data.resize(FieldCount); + for(int i = 0; i < FieldCount; ++i) { + m_data[i] = data.value(i, QVariant()); + } +} + +BookmarkItem::~BookmarkItem() +{ + qDeleteAll(m_children); +} + +BookmarkItem *BookmarkItem::parent() const +{ + return m_parentItem; +} + +bool BookmarkItem::appendChild(BookmarkItem *childItem) +{ + // Only folders can have children, so only append them on folders + // This way, we don't need to add checks to the other methods + if(m_type == Folder || m_type == Root) { + m_children.append(childItem); + return true; + } + + return false; +} + +bool BookmarkItem::insertChild(int position, BookmarkItem *childItem) +{ + // position is invalid (-1) when dropping an item onto the folder, which leads to crash + // make sure that position passed is >= 0 (insert item at first position) + + if(m_type == Folder || m_type == Root) { + m_children.insert(qMax(position, 0), childItem); + return true; + } + + return false; +} + +bool BookmarkItem::removeChildAt(int index, int count) +{ + if(index < 0 || index + count > m_children.size()) + return false; + + // delete the item at index count times + for(int i = 0; i < count; ++i) { + delete m_children.takeAt(index); + } + return true; +} + +BookmarkItem *BookmarkItem::takeChild(int index, BookmarkItem *newParent) +{ + m_children[index]->m_parentItem = newParent; + return m_children.takeAt(index); +} + +BookmarkItem *BookmarkItem::child(int index) const +{ + return m_children.value(index); +} + +int BookmarkItem::childCount() const +{ + return m_children.count(); +} + +QVariant BookmarkItem::data(Fields column) const +{ + return m_data.value(column); +} + +bool BookmarkItem::setData(Fields column, const QVariant &data) +{ + if(column >= FieldCount) + return false; + + m_data[column] = data; + return true; +} + +QIcon BookmarkItem::icon() const +{ + return m_icon; +} + +bool BookmarkItem::isExpanded() const +{ + return m_isExpanded; +} + +void BookmarkItem::setExpanded(bool expanded) +{ + if(m_type == BookmarkItem::Folder) + m_isExpanded = expanded; +} + +QString BookmarkItem::tooltip() const +{ + return m_data.value(Tags).toStringList().join(", "); +} + +BookmarkItem::Type BookmarkItem::type() const +{ + return m_type; +} + +int BookmarkItem::row() const +{ + if(m_parentItem) + return m_parentItem->m_children.indexOf(const_cast(this)); + + return 0; +} diff --git a/lib/bookmarks/bookmarkitem.h b/lib/bookmarks/bookmarkitem.h new file mode 100644 index 0000000..8c9463f --- /dev/null +++ b/lib/bookmarks/bookmarkitem.h @@ -0,0 +1,69 @@ +/* + * 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 + */ + +#ifndef SMOLBOTE_BOOKMARKITEM_H +#define SMOLBOTE_BOOKMARKITEM_H + +#include +#include +#include + +class BookmarkItem +{ +public: + enum Type { + Root, + Folder, + Bookmark, + }; + + enum Fields { + Title, + Href, + Tags, + Description, + FieldCount + }; + + explicit BookmarkItem(const QVector &data, Type type, BookmarkItem *parent = nullptr); + ~BookmarkItem(); + + BookmarkItem *parent() const; + + bool appendChild(BookmarkItem *childItem); + bool insertChild(int position, BookmarkItem *childItem); + bool removeChildAt(int index, int count = 1); + BookmarkItem *takeChild(int index, BookmarkItem *newParent); + + BookmarkItem *child(int index) const; + int childCount() const; + + QVariant data(Fields column) const; + bool setData(Fields column, const QVariant &data); + + QIcon icon() const; + bool isExpanded() const; + void setExpanded(bool expanded); + + QString tooltip() const; + Type type() const; + int row() const; + +private: + QVector m_children; + BookmarkItem *m_parentItem; + + Type m_type; + QIcon m_icon; + bool m_isExpanded = false; + + // fields + QVector m_data; +}; + +#endif // SMOLBOTE_BOOKMARKITEM_H 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; +} diff --git a/lib/bookmarks/bookmarkmodel.h b/lib/bookmarks/bookmarkmodel.h new file mode 100644 index 0000000..300b724 --- /dev/null +++ b/lib/bookmarks/bookmarkmodel.h @@ -0,0 +1,73 @@ +/* + * 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 + */ + +#ifndef SMOLBOTE_BOOKMARKMODEL_H +#define SMOLBOTE_BOOKMARKMODEL_H + +#include "bookmarkitem.h" +#include + +class BookmarkModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + explicit BookmarkModel(QObject *parent = nullptr); + ~BookmarkModel() override; + + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + QVariant data(const QModelIndex &index, int role) const override; + QVariant data(const QModelIndex &index, int column, int role) const; + bool setData(const QModelIndex &index, const QVariant &value, int role) override; + bool setData(const QModelIndex &index, const QVariant &value, BookmarkItem::Fields column, int role); + Qt::ItemFlags flags(const QModelIndex &index) const override; + + bool isItemExpanded(const QModelIndex &index) const; + void setItemExpanded(const QModelIndex &index, bool expanded); + + int rowCount(const QModelIndex &index) const override; + QModelIndex appendBookmark(const QString &title, const QString &url, const QModelIndex &parent); + QModelIndex appendFolder(const QString &title, const QModelIndex &parent); + bool removeRows(int position, int rows, const QModelIndex &parent) override; + int columnCount(const QModelIndex &index) const override; + + Qt::DropActions supportedDropActions() const override; + QStringList mimeTypes() const override; + QMimeData *mimeData(const QModelIndexList &indexes) const override; + bool dropMimeData(const QMimeData *mimeData, Qt::DropAction action, int row, int column, const QModelIndex &parent) override; + + QModelIndex index(int row, int column, const QModelIndex &parent) const override; + QModelIndex parent(const QModelIndex &index) const override; + QModelIndex parentFolder(const QModelIndex &index) const; + + BookmarkItem *root() + { + return rootItem; + } + + QStringList search(const QString &term) const; + + void resetModified() + { + m_isModified = false; + } + bool isModified() const + { + return m_isModified; + } + +private: + const QLatin1String mimeType = QLatin1String("application/xbel"); + + BookmarkItem *getItem(const QModelIndex &index) const; + BookmarkItem *rootItem; + + bool m_isModified = false; +}; + +#endif // SMOLBOTE_BOOKMARKMODEL_H diff --git a/lib/bookmarks/bookmarkswidget.cpp b/lib/bookmarks/bookmarkswidget.cpp index aed7e97..29e388c 100644 --- a/lib/bookmarks/bookmarkswidget.cpp +++ b/lib/bookmarks/bookmarkswidget.cpp @@ -8,10 +8,8 @@ #include "bookmarkswidget.h" #include "forms/editbookmarkdialog.h" -#include "model/bookmarkitem.h" -#include "model/bookmarkmodel.h" #include "ui_bookmarksform.h" -#include "formats/format.h" +#include "bookmarkformat.h" #include #include @@ -48,9 +46,7 @@ BookmarksWidget::BookmarksWidget(const QString &path, QWidget *parent) m_bookmarksPath = path; QFile bookmarksFile(m_bookmarksPath); if(bookmarksFile.open(QIODevice::ReadOnly | QIODevice::Text)) { - BookmarksFormat format(&bookmarksFile); - format.read(model->root()); - bookmarksFile.close(); + BookmarkFormat(&bookmarksFile) >> model; } model->resetModified(); @@ -124,11 +120,9 @@ void BookmarksWidget::save() QFile bookmarksFile(m_bookmarksPath); if(bookmarksFile.open(QIODevice::WriteOnly | QIODevice::Text)) { - BookmarksFormat format(&bookmarksFile); - format.write(model->root()); + BookmarkFormat f(&bookmarksFile); + model >> f; bookmarksFile.flush(); - bookmarksFile.close(); - model->resetModified(); } } diff --git a/lib/bookmarks/formats/format.cpp b/lib/bookmarks/formats/format.cpp deleted file mode 100644 index 551151c..0000000 --- a/lib/bookmarks/formats/format.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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 "format.h" -#include "xbel.h" -#include - -template<> -void BookmarksFormat::read(BookmarkItem *root) -{ - Q_CHECK_PTR(m_device); - Xbel::read(m_device, root); -} - -template<> -void BookmarksFormat::write(BookmarkItem *root) -{ - Q_CHECK_PTR(m_device); - Xbel::write(m_device, root); -} - diff --git a/lib/bookmarks/formats/format.h b/lib/bookmarks/formats/format.h deleted file mode 100644 index e96dfcc..0000000 --- a/lib/bookmarks/formats/format.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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 - */ - -#ifndef BOOKMARKSFORMAT_H -#define BOOKMARKSFORMAT_H - -class QIODevice; -class BookmarkItem; - -enum BookmarksFormats { - XbelFormat -}; - -template -class BookmarksFormat -{ -public: - explicit BookmarksFormat(QIODevice *device) - { - m_device = device; - } - - void read(BookmarkItem *root); - void write(BookmarkItem *root); - -protected: - QIODevice *m_device; -}; - -#endif // BOOKMARKSFORMAT_H - diff --git a/lib/bookmarks/formats/xbel.cpp b/lib/bookmarks/formats/xbel.cpp index 1cb5756..174995d 100644 --- a/lib/bookmarks/formats/xbel.cpp +++ b/lib/bookmarks/formats/xbel.cpp @@ -7,7 +7,7 @@ */ #include "xbel.h" -#include "model/bookmarkitem.h" +#include "bookmarkitem.h" #include #include diff --git a/lib/bookmarks/forms/editbookmarkdialog.cpp b/lib/bookmarks/forms/editbookmarkdialog.cpp index 9c6efa0..7df90b8 100644 --- a/lib/bookmarks/forms/editbookmarkdialog.cpp +++ b/lib/bookmarks/forms/editbookmarkdialog.cpp @@ -7,8 +7,8 @@ */ #include "editbookmarkdialog.h" -#include "model/bookmarkitem.h" -#include "model/bookmarkmodel.h" +#include "bookmarkitem.h" +#include "bookmarkmodel.h" #include "ui_editbookmarkdialog.h" EditBookmarkDialog::EditBookmarkDialog(BookmarkModel *model, const QModelIndex &index, QWidget *parent) diff --git a/lib/bookmarks/meson.build b/lib/bookmarks/meson.build index 3049e3c..78d0510 100644 --- a/lib/bookmarks/meson.build +++ b/lib/bookmarks/meson.build @@ -1,14 +1,14 @@ bookmarks_inc = include_directories('.') bookmarks_moc = mod_qt5.preprocess( - moc_headers: ['bookmarkswidget.h', 'model/bookmarkmodel.h', 'forms/editbookmarkdialog.h'], + moc_headers: ['bookmarkswidget.h', 'bookmarkmodel.h', 'forms/editbookmarkdialog.h'], ui_files: ['bookmarksform.ui', 'forms/editbookmarkdialog.ui'], dependencies: dep_qt5 ) bookmarks_lib = static_library('bookmarks', ['bookmarkswidget.cpp', bookmarks_moc, - 'formats/format.cpp', 'formats/xbel.cpp', - 'model/bookmarkitem.cpp', 'model/bookmarkmodel.cpp', + 'bookmarksloader.cpp', 'formats/xbel.cpp', + 'bookmarkitem.cpp', 'bookmarkmodel.cpp', 'forms/editbookmarkdialog.cpp'], dependencies: dep_qt5 ) diff --git a/lib/bookmarks/model/bookmarkitem.cpp b/lib/bookmarks/model/bookmarkitem.cpp deleted file mode 100644 index 21d034f..0000000 --- a/lib/bookmarks/model/bookmarkitem.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/* - * 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 "bookmarkitem.h" -#include -#include - -BookmarkItem::BookmarkItem(const QVector &data, Type type, BookmarkItem *parent) -{ - m_parentItem = parent; - - m_type = type; - if(m_type == Folder) { - m_icon.addPixmap(qApp->style()->standardPixmap(QStyle::SP_DirClosedIcon), QIcon::Normal, QIcon::Off); - m_icon.addPixmap(qApp->style()->standardPixmap(QStyle::SP_DirOpenIcon), QIcon::Normal, QIcon::On); - } else if(m_type == Bookmark) - m_icon.addPixmap(qApp->style()->standardPixmap(QStyle::SP_FileIcon)); - - m_data.resize(FieldCount); - for(int i = 0; i < FieldCount; ++i) { - m_data[i] = data.value(i, QVariant()); - } -} - -BookmarkItem::~BookmarkItem() -{ - qDeleteAll(m_children); -} - -BookmarkItem *BookmarkItem::parent() const -{ - return m_parentItem; -} - -bool BookmarkItem::appendChild(BookmarkItem *childItem) -{ - // Only folders can have children, so only append them on folders - // This way, we don't need to add checks to the other methods - if(m_type == Folder || m_type == Root) { - m_children.append(childItem); - return true; - } - - return false; -} - -bool BookmarkItem::insertChild(int position, BookmarkItem *childItem) -{ - // position is invalid (-1) when dropping an item onto the folder, which leads to crash - // make sure that position passed is >= 0 (insert item at first position) - - if(m_type == Folder || m_type == Root) { - m_children.insert(qMax(position, 0), childItem); - return true; - } - - return false; -} - -bool BookmarkItem::removeChildAt(int index, int count) -{ - if(index < 0 || index + count > m_children.size()) - return false; - - // delete the item at index count times - for(int i = 0; i < count; ++i) { - delete m_children.takeAt(index); - } - return true; -} - -BookmarkItem *BookmarkItem::takeChild(int index, BookmarkItem *newParent) -{ - m_children[index]->m_parentItem = newParent; - return m_children.takeAt(index); -} - -BookmarkItem *BookmarkItem::child(int index) const -{ - return m_children.value(index); -} - -int BookmarkItem::childCount() const -{ - return m_children.count(); -} - -QVariant BookmarkItem::data(Fields column) const -{ - return m_data.value(column); -} - -bool BookmarkItem::setData(Fields column, const QVariant &data) -{ - if(column >= FieldCount) - return false; - - m_data[column] = data; - return true; -} - -QIcon BookmarkItem::icon() const -{ - return m_icon; -} - -bool BookmarkItem::isExpanded() const -{ - return m_isExpanded; -} - -void BookmarkItem::setExpanded(bool expanded) -{ - if(m_type == BookmarkItem::Folder) - m_isExpanded = expanded; -} - -QString BookmarkItem::tooltip() const -{ - return m_data.value(Tags).toStringList().join(", "); -} - -BookmarkItem::Type BookmarkItem::type() const -{ - return m_type; -} - -int BookmarkItem::row() const -{ - if(m_parentItem) - return m_parentItem->m_children.indexOf(const_cast(this)); - - return 0; -} diff --git a/lib/bookmarks/model/bookmarkitem.h b/lib/bookmarks/model/bookmarkitem.h deleted file mode 100644 index 8c9463f..0000000 --- a/lib/bookmarks/model/bookmarkitem.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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 - */ - -#ifndef SMOLBOTE_BOOKMARKITEM_H -#define SMOLBOTE_BOOKMARKITEM_H - -#include -#include -#include - -class BookmarkItem -{ -public: - enum Type { - Root, - Folder, - Bookmark, - }; - - enum Fields { - Title, - Href, - Tags, - Description, - FieldCount - }; - - explicit BookmarkItem(const QVector &data, Type type, BookmarkItem *parent = nullptr); - ~BookmarkItem(); - - BookmarkItem *parent() const; - - bool appendChild(BookmarkItem *childItem); - bool insertChild(int position, BookmarkItem *childItem); - bool removeChildAt(int index, int count = 1); - BookmarkItem *takeChild(int index, BookmarkItem *newParent); - - BookmarkItem *child(int index) const; - int childCount() const; - - QVariant data(Fields column) const; - bool setData(Fields column, const QVariant &data); - - QIcon icon() const; - bool isExpanded() const; - void setExpanded(bool expanded); - - QString tooltip() const; - Type type() const; - int row() const; - -private: - QVector m_children; - BookmarkItem *m_parentItem; - - Type m_type; - QIcon m_icon; - bool m_isExpanded = false; - - // fields - QVector m_data; -}; - -#endif // SMOLBOTE_BOOKMARKITEM_H diff --git a/lib/bookmarks/model/bookmarkmodel.cpp b/lib/bookmarks/model/bookmarkmodel.cpp deleted file mode 100644 index 895b178..0000000 --- a/lib/bookmarks/model/bookmarkmodel.cpp +++ /dev/null @@ -1,356 +0,0 @@ -/* - * 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; -} diff --git a/lib/bookmarks/model/bookmarkmodel.h b/lib/bookmarks/model/bookmarkmodel.h deleted file mode 100644 index 300b724..0000000 --- a/lib/bookmarks/model/bookmarkmodel.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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 - */ - -#ifndef SMOLBOTE_BOOKMARKMODEL_H -#define SMOLBOTE_BOOKMARKMODEL_H - -#include "bookmarkitem.h" -#include - -class BookmarkModel : public QAbstractItemModel -{ - Q_OBJECT - -public: - explicit BookmarkModel(QObject *parent = nullptr); - ~BookmarkModel() override; - - QVariant headerData(int section, Qt::Orientation orientation, int role) const override; - QVariant data(const QModelIndex &index, int role) const override; - QVariant data(const QModelIndex &index, int column, int role) const; - bool setData(const QModelIndex &index, const QVariant &value, int role) override; - bool setData(const QModelIndex &index, const QVariant &value, BookmarkItem::Fields column, int role); - Qt::ItemFlags flags(const QModelIndex &index) const override; - - bool isItemExpanded(const QModelIndex &index) const; - void setItemExpanded(const QModelIndex &index, bool expanded); - - int rowCount(const QModelIndex &index) const override; - QModelIndex appendBookmark(const QString &title, const QString &url, const QModelIndex &parent); - QModelIndex appendFolder(const QString &title, const QModelIndex &parent); - bool removeRows(int position, int rows, const QModelIndex &parent) override; - int columnCount(const QModelIndex &index) const override; - - Qt::DropActions supportedDropActions() const override; - QStringList mimeTypes() const override; - QMimeData *mimeData(const QModelIndexList &indexes) const override; - bool dropMimeData(const QMimeData *mimeData, Qt::DropAction action, int row, int column, const QModelIndex &parent) override; - - QModelIndex index(int row, int column, const QModelIndex &parent) const override; - QModelIndex parent(const QModelIndex &index) const override; - QModelIndex parentFolder(const QModelIndex &index) const; - - BookmarkItem *root() - { - return rootItem; - } - - QStringList search(const QString &term) const; - - void resetModified() - { - m_isModified = false; - } - bool isModified() const - { - return m_isModified; - } - -private: - const QLatin1String mimeType = QLatin1String("application/xbel"); - - BookmarkItem *getItem(const QModelIndex &index) const; - BookmarkItem *rootItem; - - bool m_isModified = false; -}; - -#endif // SMOLBOTE_BOOKMARKMODEL_H -- cgit v1.2.1