diff options
Diffstat (limited to 'src/bookmarks/bookmarkstreemodel.cpp')
-rw-r--r-- | src/bookmarks/bookmarkstreemodel.cpp | 518 |
1 files changed, 184 insertions, 334 deletions
diff --git a/src/bookmarks/bookmarkstreemodel.cpp b/src/bookmarks/bookmarkstreemodel.cpp index 45b50253..444d2e7b 100644 --- a/src/bookmarks/bookmarkstreemodel.cpp +++ b/src/bookmarks/bookmarkstreemodel.cpp @@ -1,406 +1,256 @@ /* ============================================================ -* -* This file is a part of the rekonq project -* -* Copyright (C) 2009 by Nils Weigel <nehlsen at gmail dot com> -* Copyright (C) 2010-2013 by Andrea Diamantini <adjam7 at gmail dot com> -* -* -* This program is free software; you can redistribute it and/or -* modify it under the terms of the GNU General Public License as -* published by the Free Software Foundation; either version 2 of -* the License or (at your option) version 3 or any later version -* accepted by the membership of KDE e.V. (or its successor approved -* by the membership of KDE e.V.), which shall act as a proxy -* defined in Section 14 of version 3 of the license. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see <http://www.gnu.org/licenses/>. -* -* ============================================================ */ - - -// Self Includes -#include "bookmarkstreemodel.h" -#include "bookmarkstreemodel.moc" - -// Local Includes -#include "bookmarkmanager.h" -#include "iconmanager.h" - -// KDE Includes -#include <KBookmarkManager> -#include <KLocalizedString> -#include <KIcon> - -// Qt Includes -#include <QtCore/QMimeData> - - -BtmItem::BtmItem(const KBookmark &bm) - : m_parent(0) - , m_kbm(bm) + * rekonq + * ============================================================ + * SPDX-License-Identifier: GPL-3.0-only + * Copyright (C) 2022 aqua <aqua@iserlohn-fortress.net> + * ============================================================ + * Description: rekonq bookmarks model + * ============================================================ */ + +#include "bookmarkstreemodel.hpp" +#include "bookmarkstreeformats.hpp" +#include <QBuffer> +#include <QDateTime> +#include <QFile> +#include <QMimeData> + +BookmarkModel::BookmarkModel(const QString &path, QObject *parent) + : QAbstractItemModel(parent), bookmarksFile(new QFile(path)) { -} - - -BtmItem::~BtmItem() -{ - qDeleteAll(m_children); -} - + rootItem = new BookmarksTreeItem(BookmarksTreeItem::Root, {.title = tr("Title"), .href = tr("Address")}, nullptr); -QVariant BtmItem::data(int role) const -{ - if (m_kbm.isNull()) - return QVariant(); // should only happen for root item - - if (role == Qt::DisplayRole) - return m_kbm.text(); - - if (role == Qt::DecorationRole) - { - // NOTE - // this should be: - // return KIcon(m_kbm.icon()); - // but I cannot let it work :( - // I really cannot understand how let this work properly... - if (m_kbm.isGroup() || m_kbm.isSeparator()) - return KIcon(m_kbm.icon()); - else - return IconManager::self()->iconForUrl(KUrl(m_kbm.url())); - } - - if (role == Qt::UserRole) - return m_kbm.url(); - - if (role == Qt::ToolTipRole) - { - QString tooltip = m_kbm.fullText(); - if (m_kbm.isGroup()) - tooltip += i18ncp("%1=Number of items in bookmark folder", " (1 item)", " (%1 items)", childCount()); - - QString url = m_kbm.url().url(); - if (!url.isEmpty()) - { - if (!tooltip.isEmpty()) - tooltip += '\n'; - tooltip += url; - } - - if (!m_kbm.description().isEmpty()) - { - if (!tooltip.isEmpty()) - tooltip += '\n'; - tooltip += m_kbm.description(); - } - - return tooltip; - } - - return QVariant(); -} - - -int BtmItem::row() const -{ - if (m_parent) - return m_parent->m_children.indexOf(const_cast< BtmItem* >(this)); - return 0; -} - - -int BtmItem::childCount() const -{ - return m_children.count(); + if (bookmarksFile->open(QIODevice::ReadOnly | QIODevice::Text)) { + if (path.endsWith(".xbel")) { readFns[Formats::FormatXbel](bookmarksFile, rootItem); } + bookmarksFile->close(); + } } - -BtmItem* BtmItem::child(int n) +BookmarkModel::~BookmarkModel() { - Q_ASSERT(n >= 0); - Q_ASSERT(n < childCount()); + save(); // save on exit + bookmarksFile->flush(); - return m_children.at(n); + delete rootItem; + delete bookmarksFile; } - -BtmItem* BtmItem::parent() const +void BookmarkModel::load(const QIODevice *buffer) { - return m_parent; + if (buffer->isOpen() && buffer->isReadable()) { readFns[Formats::FormatXbel](buffer, rootItem); } } - -void BtmItem::appendChild(BtmItem *child) +QVariant BookmarkModel::headerData(int section, Qt::Orientation, int role) const { - if (!child) - return; - - child->m_parent = this; - m_children << child; + if (role != Qt::DisplayRole) return {}; + return rootItem->data(static_cast<BookmarksTreeItem::Attributes>(section)); } - -void BtmItem::clear() +QVariant BookmarkModel::data(const QModelIndex &index, int role) const { - qDeleteAll(m_children); - m_children.clear(); + if (!index.isValid()) return {}; + + auto *it = item(index); + + // find item Alias points to + if (it->type() == BookmarksTreeItem::Alias) { + // find the item this is an alias of + auto *child = rootItem->findChild(it->data(BookmarksTreeItem::Href).toString()); + if (child == nullptr) return {}; + it = child; + } + + switch (role) { + case Qt::DecorationRole: + if (index.column() == 0) return it->icon(); + break; + case Qt::ToolTipRole: + return it->tooltip(); + case Qt::DisplayRole: + case Qt::EditRole: + if (index.column() == 0) return it->data(BookmarksTreeItem::Title); + if (index.column() == 1) return it->data(BookmarksTreeItem::Href); + break; + case CompletionMatchingRole: + if (it->type() == BookmarksTreeItem::Bookmark) return it->data(BookmarksTreeItem::Href); + return it->data(BookmarksTreeItem::Title); + default: + break; + } + + return {}; } -KBookmark BtmItem::getBkm() const +bool BookmarkModel::setData(const QModelIndex &index, const QVariant &value, int role) { - return m_kbm; -} + if (!index.isValid()) return false; + bool success = false; -// ------------------------------------------------------------------------------------- + if (role == Qt::DisplayRole || role == Qt::EditRole) { + auto *item = static_cast<BookmarksTreeItem *>(index.internalPointer()); + success = item->setData(static_cast<BookmarksTreeItem::Attributes>(index.column()), value); + } + if (success) { + emit dataChanged(index, index, {role}); + m_isModified = true; + } -BookmarksTreeModel::BookmarksTreeModel(QObject *parent) - : QAbstractItemModel(parent) - , m_root(0) -{ - resetModel(); - connect(BookmarkManager::self()->manager(), SIGNAL(changed(QString,QString)), - this, SLOT(bookmarksChanged(QString))); + return success; } - -BookmarksTreeModel::~BookmarksTreeModel() +Qt::ItemFlags BookmarkModel::flags(const QModelIndex &index) const { - delete m_root; + switch (item(index)->type()) { + case BookmarksTreeItem::Root: + return QAbstractItemModel::flags(index) /*| Qt::ItemIsDragEnabled*/ | Qt::ItemIsDropEnabled; + case BookmarksTreeItem::Folder: + return QAbstractItemModel::flags(index) | (index.column() == 0 ? Qt::ItemIsEditable : Qt::NoItemFlags) | + Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; + case BookmarksTreeItem::Bookmark: + return QAbstractItemModel::flags(index) | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemNeverHasChildren; + case BookmarksTreeItem::Separator: + return QAbstractItemModel::flags(index) | Qt::ItemIsDragEnabled | Qt::ItemNeverHasChildren; + case BookmarksTreeItem::Alias: + // TODO find aliased item and return its flags + return QAbstractItemModel::flags(index); + } + + __builtin_unreachable(); } - -int BookmarksTreeModel::rowCount(const QModelIndex &parent) const +int BookmarkModel::rowCount(const QModelIndex &index) const { - BtmItem *parentItem = 0; - if (!parent.isValid()) - { - parentItem = m_root; - } - else - { - parentItem = static_cast<BtmItem*>(parent.internalPointer()); - } - - return parentItem->childCount(); + if (index.column() > 0) return 0; + return static_cast<int>(item(index)->childCount()); } - -int BookmarksTreeModel::columnCount(const QModelIndex& /*parent*/) const +QModelIndex BookmarkModel::appendItem(BookmarksTreeItem::Types type, BookmarksTreeItem::Attributes_t data, + const QModelIndex &parent) { - return 1; + auto *parentItem = item(parent); + const auto row = rowCount(parent); + + beginInsertRows(parent, row, row); + auto *child = new BookmarksTreeItem(type, std::move(data), parentItem); + child->setData(BookmarksTreeItem::Added, QDateTime::currentDateTime()); + bool success = parentItem->appendChild(child); + endInsertRows(); + + if (success) { + m_isModified = true; + return createIndex(row, 0, child); + } + else + return {}; } - -Qt::ItemFlags BookmarksTreeModel::flags(const QModelIndex &index) const +bool BookmarkModel::removeRows(int position, int rows, const QModelIndex &parent) { - Qt::ItemFlags flags = QAbstractItemModel::flags(index); - - if (!index.isValid()) - return flags | Qt::ItemIsDropEnabled; + auto *parentItem = item(parent); - flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled; + beginRemoveRows(parent, position, position + rows - 1); + bool success = parentItem->removeChildAt(position, rows); + endRemoveRows(); - if (bookmarkForIndex(index).isGroup()) - flags |= Qt::ItemIsDropEnabled; - - return flags; + if (success) m_isModified = true; + return success; } - -QModelIndex BookmarksTreeModel::index(int row, int column, const QModelIndex &parent) const +QModelIndex BookmarkModel::index(int row, int column, const QModelIndex &parent) const { - if (!hasIndex(row, column, parent)) - return QModelIndex(); - - BtmItem *parentItem; + if (!this->hasIndex(row, column, parent)) return {}; - if (!parent.isValid()) - parentItem = m_root; - else - parentItem = static_cast<BtmItem*>(parent.internalPointer()); - - BtmItem *childItem = parentItem->child(row); - if (childItem) - return createIndex(row, column, childItem); - - return QModelIndex(); + BookmarksTreeItem *parentItem = item(parent); + BookmarksTreeItem *childItem = parentItem->child(row); + if (childItem) return createIndex(row, column, childItem); + return {}; } - -QModelIndex BookmarksTreeModel::parent(const QModelIndex &index) const +QModelIndex BookmarkModel::parent(const QModelIndex &index) const { - if (!index.isValid()) - return QModelIndex(); - - BtmItem *childItem = static_cast<BtmItem*>(index.internalPointer()); - BtmItem *parentItem = childItem->parent(); + if (!index.isValid()) return {}; - if (parentItem == m_root) - return QModelIndex(); - - return createIndex(parentItem->row(), 0, parentItem); -} + auto *childItem = static_cast<BookmarksTreeItem *>(index.internalPointer()); + auto *parentItem = childItem->parent(); + if (parentItem == rootItem) return {}; -QVariant BookmarksTreeModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - BtmItem *node = static_cast<BtmItem*>(index.internalPointer()); - if (node && node == m_root) - { - if (role == Qt::DisplayRole) - return i18n("Bookmarks"); - if (role == Qt::DecorationRole) - return KIcon("bookmarks"); - } - else - { - if (node) - return node->data(role); - } - - return QVariant(); + return createIndex(parentItem->row(), 0, parentItem); } - -QStringList BookmarksTreeModel::mimeTypes() const +QModelIndex BookmarkModel::parentFolder(const QModelIndex &index) const { - return QStringList(BookmarkManager::bookmark_mime_type()); -} - - -bool BookmarksTreeModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) -{ - if (action != Qt::MoveAction || !data->hasFormat(BookmarkManager::bookmark_mime_type())) - return false; - - QByteArray addresses = data->data(BookmarkManager::bookmark_mime_type()); - KBookmark bookmark = BookmarkManager::self()->findByAddress(QString::fromLatin1(addresses.data())); - - KBookmarkGroup root; - if (parent.isValid()) - root = bookmarkForIndex(parent).toGroup(); - else - root = BookmarkManager::self()->rootGroup(); - - QModelIndex destIndex = index(row, column, parent); + // invalid index is the root index -> return it back + if (!index.isValid()) return {}; - if (destIndex.isValid() && row != -1) - { - root.moveBookmark(bookmark, root.previous(bookmarkForIndex(destIndex))); - } - else - { - root.deleteBookmark(bookmark); - root.addBookmark(bookmark); - } + if (item(index)->type() == BookmarksTreeItem::Bookmark) { return index.parent(); } - BookmarkManager::self()->emitChanged(); - - return true; + return index; } - -Qt::DropActions BookmarksTreeModel::supportedDropActions() const +BookmarksTreeItem *BookmarkModel::item(const QModelIndex &index) const { - return Qt::MoveAction; + if (!index.isValid()) return rootItem; + return static_cast<BookmarksTreeItem *>(index.internalPointer()); } +/* + * Drag'n'Drop implementation + * How drag and drop actually works: the view encodes the data of the original item (using BookmarkModel::mimeData), and + * then uses BookmarkModel::dropMimeData to create the new item. If successful, the old item is removed (through + * BookmarkModel::removeRows). + */ -QMimeData* BookmarksTreeModel::mimeData(const QModelIndexList &indexes) const +QMimeData *BookmarkModel::mimeData(const QModelIndexList &indexes) const { - QMimeData *mimeData = new QMimeData; - - QByteArray address = bookmarkForIndex(indexes.first()).address().toLatin1(); - mimeData->setData(BookmarkManager::bookmark_mime_type(), address); - bookmarkForIndex(indexes.first()).populateMimeData(mimeData); - - return mimeData; + QByteArray data; + QBuffer buffer(&data); + buffer.open(QIODevice::WriteOnly | QIODevice::Text); + + QVector<const BookmarksTreeItem *> items; + for (const QModelIndex &index : indexes) { + if (index.isValid() && index.column() == 0) items.append(item(index)); + } + xbel::write(&buffer, items); + + auto *mimeData = new QMimeData; + mimeData->setData(mimeType, data); + return mimeData; } - -void BookmarksTreeModel::bookmarksChanged(const QString &groupAddress) +bool BookmarkModel::dropMimeData(const QMimeData *mimeData, Qt::DropAction action, int row, int column, + const QModelIndex &parent) { - if (groupAddress.isEmpty()) - { - resetModel(); - } - else - { - beginResetModel(); - BtmItem *node = m_root; - QModelIndex nodeIndex; - - QStringList indexChain(groupAddress.split('/', QString::SkipEmptyParts)); - bool ok; - int i; - Q_FOREACH(const QString & sIndex, indexChain) - { - i = sIndex.toInt(&ok); - if (!ok) - break; - - if (i < 0 || i >= node->childCount()) - break; - - node = node->child(i); - nodeIndex = index(i, 0, nodeIndex); - } - populate(node, BookmarkManager::self()->findByAddress(groupAddress).toGroup()); - endResetModel(); - } - - emit bookmarksUpdated(); -} + if (action == Qt::IgnoreAction) return true; + if (action != Qt::MoveAction) return false; // we only implement MoveAction's + if (!mimeData->hasFormat(mimeType) || column > 0) return false; + auto data = mimeData->data(mimeType); + QBuffer buffer(&data); + buffer.open(QIODevice::ReadOnly | QIODevice::Text); -void BookmarksTreeModel::resetModel() -{ - setRoot(BookmarkManager::self()->rootGroup()); -} + auto *fake_root = new BookmarksTreeItem(BookmarksTreeItem::Root, {}, nullptr); + xbel::read(&buffer, fake_root); + const auto childCount = static_cast<int>(fake_root->childCount()); + auto *parentItem = item(parent); -void BookmarksTreeModel::setRoot(KBookmarkGroup bmg) -{ - beginResetModel(); - delete m_root; - m_root = new BtmItem(KBookmark()); - populate(m_root, bmg); - endResetModel(); -} + beginInsertRows(parent, row, row + childCount - 1); + for (int i = 0; i < childCount; ++i) parentItem->insertChild(row + i, fake_root->takeChild(i)); + endInsertRows(); + delete fake_root; -void BookmarksTreeModel::populate(BtmItem *node, KBookmarkGroup bmg) -{ - node->clear(); - - if (bmg.isNull()) - return; - - KBookmark bm = bmg.first(); - while (!bm.isNull()) - { - BtmItem *newChild = new BtmItem(bm); - if (bm.isGroup()) - populate(newChild, bm.toGroup()); - - node->appendChild(newChild); - bm = bmg.next(bm); - } + m_isModified = true; + return true; } - -KBookmark BookmarksTreeModel::bookmarkForIndex(const QModelIndex &index) const +void BookmarkModel::save() { - return static_cast<BtmItem*>(index.internalPointer())->getBkm(); + if (m_isModified) { + bookmarksFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate); + m_isModified = !writeFns[Formats::FormatXbel](bookmarksFile, {rootItem}); + bookmarksFile->close(); + } } |