File indexing completed on 2024-05-12 05:09:51
0001 /*************************************************************************** 0002 Copyright (C) 2008-2009 Robby Stephenson <robby@periapsis.org> 0003 ***************************************************************************/ 0004 0005 /*************************************************************************** 0006 * * 0007 * This program is free software; you can redistribute it and/or * 0008 * modify it under the terms of the GNU General Public License as * 0009 * published by the Free Software Foundation; either version 2 of * 0010 * the License or (at your option) version 3 or any later version * 0011 * accepted by the membership of KDE e.V. (or its successor approved * 0012 * by the membership of KDE e.V.), which shall act as a proxy * 0013 * defined in Section 14 of version 3 of the license. * 0014 * * 0015 * This program is distributed in the hope that it will be useful, * 0016 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0018 * GNU General Public License for more details. * 0019 * * 0020 * You should have received a copy of the GNU General Public License * 0021 * along with this program. If not, see <http://www.gnu.org/licenses/>. * 0022 * * 0023 ***************************************************************************/ 0024 0025 #include "entrygroupmodel.h" 0026 #include "models.h" 0027 #include "../entrygroup.h" 0028 #include "../collectionfactory.h" 0029 #include "../tellico_debug.h" 0030 0031 #include <KLocalizedString> 0032 #include <QIcon> 0033 0034 using Tellico::EntryGroupModel; 0035 0036 class EntryGroupModel::Node { 0037 public: 0038 Node(Node* parent_) : m_parent(parent_), m_row(-1) { } 0039 ~Node() { qDeleteAll(m_children); } 0040 0041 Node* parent() const { return m_parent; } 0042 Node* child(int row) const { return row < m_children.count() ? m_children.at(row) : nullptr; } 0043 int row() const { return m_row; } 0044 int childCount() const { return m_children.count(); }; 0045 0046 void addChild(Node* child) { 0047 child->m_row = m_children.count(); 0048 m_children.append(child); 0049 } 0050 void removeChild(int i) { 0051 delete m_children.takeAt(i); 0052 // all subsequent children move up a row 0053 for(int j = i; j < m_children.count(); ++j) { --m_children.at(j)->m_row; } 0054 } 0055 void removeAll() { 0056 qDeleteAll(m_children); 0057 m_children.clear(); 0058 } 0059 0060 private: 0061 Node* m_parent; 0062 QList<Node*> m_children; 0063 int m_row; 0064 }; 0065 0066 EntryGroupModel::EntryGroupModel(QObject* parent) : QAbstractItemModel(parent), m_rootNode(new Node(nullptr)) { 0067 m_groupHeader = i18nc("Group Name Header", "Group"); 0068 } 0069 0070 EntryGroupModel::~EntryGroupModel() { 0071 delete m_rootNode; 0072 m_rootNode = nullptr; 0073 } 0074 0075 int EntryGroupModel::rowCount(const QModelIndex& index_) const { 0076 if(!index_.isValid()) { 0077 return m_rootNode->childCount(); 0078 } 0079 0080 Node* node = static_cast<Node*>(index_.internalPointer()); 0081 Q_ASSERT(node); 0082 return node->childCount(); 0083 } 0084 0085 int EntryGroupModel::columnCount(const QModelIndex&) const { 0086 return 1; 0087 } 0088 0089 QVariant EntryGroupModel::headerData(int section_, Qt::Orientation orientation_, int role_) const { 0090 if(section_ < 0 || section_ >= columnCount() || orientation_ != Qt::Horizontal) { 0091 return QVariant(); 0092 } 0093 if(role_ == Qt::DisplayRole) { 0094 return m_groupHeader; 0095 } 0096 return QVariant(); 0097 } 0098 0099 bool EntryGroupModel::setHeaderData(int section_, Qt::Orientation orientation_, 0100 const QVariant& value_, int role_) { 0101 if(section_ < 0 || section_ >= columnCount() || orientation_ != Qt::Horizontal || role_ != Qt::EditRole) { 0102 return false; 0103 } 0104 m_groupHeader = value_.toString(); 0105 emit headerDataChanged(orientation_, section_, section_); 0106 return true; 0107 } 0108 0109 QVariant EntryGroupModel::data(const QModelIndex& index_, int role_) const { 0110 if(!index_.isValid()) { 0111 return QVariant(); 0112 } 0113 0114 switch(role_) { 0115 case Qt::DisplayRole: 0116 if(hasValidParent(index_)) { 0117 // it probably points to an entry 0118 Tellico::Data::EntryPtr e = entry(index_); 0119 if(e) { 0120 return e->title(); 0121 } 0122 } else { 0123 // it probably points to a group 0124 Tellico::Data::EntryGroup* g = group(index_); 0125 if(g) { 0126 return g->groupName(); 0127 } 0128 } 0129 return QString(); // DisplayRole should get an empty string, supposedly... 0130 case Qt::DecorationRole: 0131 if(hasValidParent(index_)) { 0132 // assume all the entries have the same icon 0133 // so no need to lookup the entry(index_), just use first one we find 0134 foreach(Data::EntryGroup* group, m_groups) { 0135 if(!group->isEmpty()) { 0136 return QIcon(QLatin1String(":/icons/") + CollectionFactory::typeName(group->first()->collection())); 0137 } 0138 } 0139 } 0140 // for groups, check the icon name list 0141 return QIcon::fromTheme(m_groupIconNames.at(index_.row())); 0142 case RowCountRole: 0143 return rowCount(index_); 0144 case EntryPtrRole: 0145 return QVariant::fromValue(entry(index_)); 0146 case GroupPtrRole: 0147 return QVariant::fromValue(group(index_)); 0148 case ValidParentRole: 0149 return hasValidParent(index_); 0150 } 0151 0152 return QVariant(); 0153 } 0154 0155 bool EntryGroupModel::setData(const QModelIndex& index_, const QVariant& value_, int role_) { 0156 // if the index has a parent, then it's not a group 0157 if(!index_.isValid() || hasValidParent(index_) || index_.row() >= rowCount() || role_ != Qt::DecorationRole) { 0158 return false; 0159 } 0160 m_groupIconNames.replace(index_.row(), value_.toString()); 0161 return true; 0162 } 0163 0164 QModelIndex EntryGroupModel::index(int row_, int column_, const QModelIndex& parent_) const { 0165 if(!hasIndex(row_, column_, parent_)) { 0166 return QModelIndex(); 0167 } 0168 0169 Node* parentNode; 0170 if(parent_.isValid()) { 0171 parentNode = static_cast<Node*>(parent_.internalPointer()); 0172 } else { 0173 parentNode = m_rootNode; 0174 } 0175 0176 Node* child = parentNode->child(row_); 0177 Q_ASSERT(child); 0178 /* 0179 if(!child) { 0180 myWarning() << "no child at row" << row_; 0181 return QModelIndex(); 0182 } 0183 */ 0184 return createIndex(row_, column_, child); 0185 } 0186 0187 QModelIndex EntryGroupModel::parent(const QModelIndex& index_) const { 0188 if(!index_.isValid()) { 0189 return QModelIndex(); 0190 } 0191 0192 Node* node = static_cast<Node*>(index_.internalPointer()); 0193 Q_ASSERT(node); 0194 Node* parentNode = node->parent(); 0195 Q_ASSERT(parentNode); 0196 0197 // if it's top-level, it has no parent 0198 if(parentNode == m_rootNode) { 0199 return QModelIndex(); 0200 } 0201 return createIndex(parentNode->row(), 0, parentNode); 0202 } 0203 0204 void EntryGroupModel::clear() { 0205 beginResetModel(); 0206 m_groups.clear(); 0207 delete m_rootNode; 0208 m_rootNode = new Node(nullptr); 0209 m_groupIconNames.clear(); 0210 endResetModel(); 0211 } 0212 0213 void EntryGroupModel::addGroups(const QList<Tellico::Data::EntryGroup*>& groups_, const QString& iconName_) { 0214 if(groups_.isEmpty()) { // shouldn't ever happen 0215 myWarning() << "adding empty group list!"; 0216 return; 0217 } 0218 beginInsertRows(QModelIndex(), rowCount(), rowCount()+groups_.count()-1); 0219 m_groups += groups_; 0220 foreach(Tellico::Data::EntryGroup* group, groups_) { 0221 Node* groupNode = new Node(m_rootNode); 0222 m_rootNode->addChild(groupNode); 0223 for(int i = 0; i < group->count(); ++i) { 0224 Node* childNode = new Node(groupNode); 0225 groupNode->addChild(childNode); 0226 } 0227 m_groupIconNames.append(iconName_); 0228 } 0229 endInsertRows(); 0230 } 0231 0232 QModelIndex EntryGroupModel::addGroup(Tellico::Data::EntryGroup* group_) { 0233 Q_ASSERT(group_); 0234 addGroups(QList<Tellico::Data::EntryGroup*>() << group_, QString()); 0235 // rowCount() has increased now 0236 return index(rowCount()-1, 0); 0237 } 0238 0239 QModelIndex EntryGroupModel::modifyGroup(Tellico::Data::EntryGroup* group_) { 0240 Q_ASSERT(group_); 0241 const int idx = m_groups.indexOf(group_); 0242 if(idx < 0) { 0243 myWarning() << "no group named" << group_->groupName(); 0244 return QModelIndex(); 0245 } 0246 0247 QModelIndex groupIndex = index(idx, 0); 0248 Node* groupNode = m_rootNode->child(idx); 0249 const int oldCount = groupNode->childCount(); 0250 0251 beginRemoveRows(groupIndex, 0, groupNode->childCount() - 1); 0252 groupNode->removeAll(); 0253 endRemoveRows(); 0254 0255 beginInsertRows(groupIndex, 0, group_->count() - 1); 0256 for(int i = 0; i < group_->count(); ++i) { 0257 Node* childNode = new Node(groupNode); 0258 groupNode->addChild(childNode); 0259 } 0260 endInsertRows(); 0261 0262 // the only data that might have changed is the count 0263 if(oldCount != groupNode->childCount()) { 0264 emit dataChanged(groupIndex, groupIndex); 0265 } 0266 return groupIndex; 0267 } 0268 0269 void EntryGroupModel::removeGroup(Tellico::Data::EntryGroup* group_) { 0270 Q_ASSERT(group_); 0271 int idx = m_groups.indexOf(group_); 0272 if(idx < 0) { 0273 myWarning() << "no group named" << group_->groupName(); 0274 return; 0275 } 0276 0277 beginRemoveRows(QModelIndex(), idx, idx); 0278 m_groups.removeAt(idx); 0279 m_rootNode->removeChild(idx); 0280 m_groupIconNames.removeAt(idx); 0281 endRemoveRows(); 0282 } 0283 0284 Tellico::Data::EntryGroup* EntryGroupModel::group(const QModelIndex& index_) const { 0285 // if the parent isn't invalid, then it's not a top-level group 0286 if(!index_.isValid() || hasValidParent(index_) || index_.row() >= m_groups.count()) { 0287 return nullptr; 0288 } 0289 return m_groups.at(index_.row()); 0290 } 0291 0292 Tellico::Data::EntryPtr EntryGroupModel::entry(const QModelIndex& index_) const { 0293 // if there's not a parent, then it's a top-level item, no entry 0294 if(!hasValidParent(index_)) { 0295 return Tellico::Data::EntryPtr(); 0296 } 0297 Tellico::Data::EntryPtr entry; 0298 Tellico::Data::EntryGroup* group = this->group(index_.parent()); 0299 // it's possible to have an inconsistent model where the number of rows != number of entries in group 0300 // like when an entry is removed. modeltest catches this 0301 if(group && index_.row() < group->count()) { 0302 entry = group->at(index_.row()); 0303 } 0304 return entry; 0305 } 0306 0307 QModelIndex EntryGroupModel::indexFromGroup(Tellico::Data::EntryGroup* group_) const { 0308 if(!group_) { 0309 return QModelIndex(); 0310 } 0311 int idx = m_groups.indexOf(group_); 0312 return idx < 0 ? QModelIndex() : index(idx, 0); 0313 } 0314 0315 // quick parent check for when we don't actually need to know the parent 0316 // just that the parent is valid 0317 // since calling parent() does the node indexOf(), which turns out to be slightly expensive 0318 bool EntryGroupModel::hasValidParent(const QModelIndex& index_) const { 0319 if(!index_.isValid()) { 0320 return false; 0321 } 0322 0323 Node* node = static_cast<Node*>(index_.internalPointer()); 0324 Q_ASSERT(node); 0325 Node* parentNode = node->parent(); 0326 Q_ASSERT(parentNode); 0327 0328 // if it's top-level, it has no parent 0329 return parentNode && parentNode != m_rootNode; 0330 }