File indexing completed on 2024-05-19 12:06:06
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"