File indexing completed on 2024-05-05 16:27:57

0001 // SPDX-FileCopyrightText: 2009 Jan Kundrát <jkt@flaska.net>
0002 // SPDX-FileCopyrightText: 2009-2022 Jesper K. Pedersen <jesper.pedersen@kdab.com>
0003 // SPDX-FileCopyrightText: 2013-2023 Johannes Zarl-Zierl <johannes@zarl-zierl.at>
0004 // SPDX-FileCopyrightText: 2015-2022 Tobias Leupold <tl@stonemx.de>
0005 //
0006 // SPDX-License-Identifier: GPL-2.0-or-later
0007 
0008 // Qt includes
0009 #include <QMimeData>
0010 
0011 // Local includes
0012 #include "TreeCategoryModel.h"
0013 
0014 #include <DB/Category.h>
0015 #include <DB/CategoryItem.h>
0016 #include <DB/ImageDB.h>
0017 #include <MainWindow/DirtyIndicator.h>
0018 
0019 struct Browser::TreeCategoryModel::Data {
0020     Data(const QString &name)
0021         : name(name)
0022         , parent(nullptr)
0023     {
0024     }
0025 
0026     ~Data()
0027     {
0028         qDeleteAll(children);
0029     }
0030 
0031     void addChild(Data *child)
0032     {
0033         child->parent = this;
0034         children.append(child);
0035     }
0036 
0037     QString name;
0038     QList<Data *> children;
0039     Data *parent;
0040 };
0041 
0042 Browser::TreeCategoryModel::TreeCategoryModel(const DB::CategoryPtr &category,
0043                                               const DB::ImageSearchInfo &info)
0044     : AbstractCategoryModel(category, info)
0045 {
0046     m_data = new Data(QString());
0047     createData(m_category->itemsCategories().data(), nullptr);
0048 
0049     if (hasNoneEntry()) {
0050         Data *data = new Data(DB::ImageDB::NONE());
0051         data->parent = m_data;
0052         m_data->children.prepend(data);
0053     }
0054 
0055     m_memberMap = DB::ImageDB::instance()->memberMap();
0056 }
0057 
0058 int Browser::TreeCategoryModel::rowCount(const QModelIndex &index) const
0059 {
0060     return indexToData(index)->children.count();
0061 }
0062 
0063 int Browser::TreeCategoryModel::columnCount(const QModelIndex &) const
0064 {
0065     return 5;
0066 }
0067 
0068 QModelIndex Browser::TreeCategoryModel::index(int row, int column, const QModelIndex &parent) const
0069 {
0070     const Data *data = indexToData(parent);
0071     QList<Data *> children = data->children;
0072     int size = children.count();
0073     if (row >= size || row < 0 || column >= columnCount(parent) || column < 0) {
0074         // Invalid index
0075         return QModelIndex();
0076     } else {
0077         return createIndex(row, column, children[row]);
0078     }
0079 }
0080 
0081 QModelIndex Browser::TreeCategoryModel::parent(const QModelIndex &index) const
0082 {
0083     Data *me = indexToData(index);
0084     if (me == m_data) {
0085         return QModelIndex();
0086     }
0087 
0088     Data *parent = me->parent;
0089     if (parent == m_data) {
0090         return QModelIndex();
0091     }
0092 
0093     Data *grandParent = parent->parent;
0094 
0095     return createIndex(grandParent->children.indexOf(parent), 0, parent);
0096 }
0097 
0098 Browser::TreeCategoryModel::~TreeCategoryModel()
0099 {
0100     delete m_data;
0101 }
0102 
0103 bool Browser::TreeCategoryModel::createData(DB::CategoryItem *parentCategoryItem, Data *parent)
0104 {
0105     const QString name = parentCategoryItem->mp_name;
0106     const int imageCount = m_images.contains(name) ? m_images[name].count : 0;
0107     const int videoCount = m_videos.contains(name) ? m_videos[name].count : 0;
0108 
0109     Data *myData = new Data(name);
0110     bool anyItems = imageCount != 0 || videoCount != 0;
0111 
0112     for (QList<DB::CategoryItem *>::ConstIterator subCategoryIt = parentCategoryItem->mp_subcategories.constBegin();
0113          subCategoryIt != parentCategoryItem->mp_subcategories.constEnd(); ++subCategoryIt) {
0114         anyItems = createData(*subCategoryIt, myData) || anyItems;
0115     }
0116 
0117     if (parent) {
0118         if (anyItems) {
0119             parent->addChild(myData);
0120         } else {
0121             delete myData;
0122         }
0123     } else {
0124         m_data = myData;
0125     }
0126 
0127     return anyItems;
0128 }
0129 
0130 Browser::TreeCategoryModel::Data *Browser::TreeCategoryModel::indexToData(const QModelIndex &index) const
0131 {
0132     if (!index.isValid()) {
0133         return m_data;
0134     } else {
0135         return static_cast<Browser::TreeCategoryModel::Data *>(index.internalPointer());
0136     }
0137 }
0138 
0139 QString Browser::TreeCategoryModel::indexToName(const QModelIndex &index) const
0140 {
0141     const Browser::TreeCategoryModel::Data *data = indexToData(index);
0142     return data->name;
0143 }
0144 
0145 Qt::DropActions Browser::TreeCategoryModel::supportedDropActions() const
0146 {
0147     return Qt::CopyAction | Qt::MoveAction;
0148 }
0149 
0150 Qt::ItemFlags Browser::TreeCategoryModel::flags(const QModelIndex &index) const
0151 {
0152     Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index);
0153 
0154     if (m_category->isSpecialCategory() || indexToName(index) == QString::fromUtf8("**NONE**")) {
0155         return defaultFlags;
0156     }
0157 
0158     if (indexToData(index)->parent != nullptr) {
0159         return defaultFlags | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
0160     } else if (index.column() == -1) {
0161         return defaultFlags | Qt::ItemIsDropEnabled;
0162     } else {
0163         return defaultFlags | Qt::ItemIsDragEnabled;
0164     }
0165 }
0166 
0167 QStringList Browser::TreeCategoryModel::mimeTypes() const
0168 {
0169     return QStringList() << QString::fromUtf8("x-kphotoalbum/x-browser-tag-drag");
0170 }
0171 
0172 QMimeData *Browser::TreeCategoryModel::mimeData(const QModelIndexList &indexes) const
0173 {
0174     QMimeData *mimeData = new QMimeData();
0175     QByteArray encodedData;
0176     QDataStream stream(&encodedData, QIODevice::WriteOnly);
0177 
0178     // only use the first index, even if more than one are selected:
0179     stream << indexToName(indexes[0]);
0180     if (!indexes[0].parent().isValid()) {
0181         stream << QString();
0182     } else {
0183         stream << indexToName(indexes[0].parent());
0184     }
0185 
0186     mimeData->setData(QString::fromUtf8("x-kphotoalbum/x-browser-tag-drag"), encodedData);
0187     return mimeData;
0188 }
0189 
0190 Browser::TreeCategoryModel::tagData Browser::TreeCategoryModel::getDroppedTagData(QByteArray &encodedData)
0191 {
0192     QDataStream stream(&encodedData, QIODevice::ReadOnly);
0193     Browser::TreeCategoryModel::tagData droppedTagData;
0194     stream >> droppedTagData.tagName;
0195     stream >> droppedTagData.tagGroup;
0196     return droppedTagData;
0197 }
0198 
0199 bool Browser::TreeCategoryModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
0200                                               int, int, const QModelIndex &parent)
0201 {
0202     if (action == Qt::IgnoreAction) {
0203         return true;
0204     }
0205 
0206     const QString thisCategory = indexToName(parent);
0207     QByteArray encodedData = data->data(QString::fromUtf8("x-kphotoalbum/x-browser-tag-drag"));
0208     Browser::TreeCategoryModel::tagData droppedTagData = getDroppedTagData(encodedData);
0209 
0210     // the difference between a CopyAction and a MoveAction is that with the MoveAction,
0211     // we have to remove the tag from its current group first
0212     if (action == Qt::MoveAction) {
0213         // Remove the tag from its group and remove the group if it's empty now
0214         m_memberMap.removeMemberFromGroup(m_category->name(), droppedTagData.tagGroup, droppedTagData.tagName);
0215         if (m_memberMap.members(m_category->name(), droppedTagData.tagGroup, true).isEmpty()) {
0216             m_memberMap.deleteGroup(m_category->name(), droppedTagData.tagGroup);
0217         }
0218     }
0219     if (parent.isValid()) {
0220         // Check if the tag is dropped onto a copy of itself
0221         const DB::CategoryItemPtr categoryInfo = m_category->itemsCategories();
0222         if (thisCategory == droppedTagData.tagName
0223             || categoryInfo->isDescendentOf(thisCategory, droppedTagData.tagName)) {
0224             return true;
0225         }
0226 
0227         // Add the tag to a group, create it if we don't have it yet
0228         if (!m_memberMap.groups(m_category->name()).contains(thisCategory)) {
0229             m_memberMap.addGroup(m_category->name(), thisCategory);
0230             DB::ImageDB::instance()->memberMap() = m_memberMap;
0231         }
0232         m_memberMap.addMemberToGroup(m_category->name(), thisCategory, droppedTagData.tagName);
0233     }
0234 
0235     DB::ImageDB::instance()->memberMap() = m_memberMap;
0236     MainWindow::DirtyIndicator::markDirty();
0237     Q_EMIT dataChanged();
0238 
0239     return true;
0240 }
0241 
0242 // vi:expandtab:tabstop=4 shiftwidth=4:
0243 
0244 #include "moc_TreeCategoryModel.cpp"