File indexing completed on 2024-05-12 05:09:52
0001 /*************************************************************************** 0002 Copyright (C) 2008-2020 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 "groupsortmodel.h" 0026 #include "models.h" 0027 #include "stringcomparison.h" 0028 #include "fieldcomparison.h" 0029 #include "../field.h" 0030 #include "../entrygroup.h" 0031 #include "../tellico_debug.h" 0032 0033 using namespace Tellico; 0034 using Tellico::GroupSortModel; 0035 0036 GroupSortModel::GroupSortModel(QObject* parent) : AbstractSortModel(parent) 0037 , m_entryComparison(nullptr) 0038 , m_groupComparison(nullptr) { 0039 } 0040 0041 GroupSortModel::~GroupSortModel() { 0042 clearComparisons(); 0043 } 0044 0045 void GroupSortModel::setSourceModel(QAbstractItemModel* sourceModel_) { 0046 AbstractSortModel::setSourceModel(sourceModel_); 0047 if(sourceModel_) { 0048 clearComparisons(); 0049 connect(sourceModel_, &QAbstractItemModel::modelReset, 0050 this, &GroupSortModel::clearComparisons); 0051 } 0052 } 0053 0054 QString GroupSortModel::entrySortField() const { 0055 return m_entryComparison ? m_entryComparison->field()->name() : QString(); 0056 } 0057 0058 void GroupSortModel::setEntrySortField(const QString& fieldName_) { 0059 // only have to update if the field name is different than existing 0060 if(m_entryComparison && m_entryComparison->field() && 0061 m_entryComparison->field()->name() == fieldName_) { 0062 return; 0063 } 0064 // can only update the sort field if the model is not empty 0065 QModelIndex groupIndex = index(0, 0); 0066 if(!groupIndex.isValid()) { 0067 return; 0068 } 0069 QModelIndex entryIndex = index(0, 0, groupIndex); 0070 if(!entryIndex.isValid()) { 0071 return; 0072 } 0073 // possible that field name does not exist in this collection 0074 Tellico::FieldComparison* newComp = getEntryComparison(entryIndex, fieldName_); 0075 if(!newComp) { 0076 return; 0077 } 0078 emit layoutAboutToBeChanged(QList<QPersistentModelIndex>(), QAbstractItemModel::VerticalSortHint); 0079 delete m_entryComparison; 0080 m_entryComparison = newComp; 0081 emit layoutChanged(QList<QPersistentModelIndex>(), QAbstractItemModel::VerticalSortHint); 0082 // emitting layoutChanged does not cause the sorting to be refreshed. I can't figure out why 0083 // but calling invalidate() does. <shrug> 0084 invalidate(); 0085 } 0086 0087 bool GroupSortModel::lessThan(const QModelIndex& left_, const QModelIndex& right_) const { 0088 // if the index have parents, then they represent entries, compare by title 0089 // calling index.parent() is expensive for the EntryGroupModel 0090 /// all we really need to know is whether the parent is valid 0091 const bool leftParentValid = sourceModel()->data(left_, ValidParentRole).toBool(); 0092 const bool rightParentValid = sourceModel()->data(right_, ValidParentRole).toBool(); 0093 const bool reverseOrder = (sortOrder() == Qt::DescendingOrder); 0094 if(!leftParentValid || !rightParentValid) { 0095 // now if we're using count, just pass up the line 0096 if(sortRole() == RowCountRole) { 0097 return AbstractSortModel::lessThan(left_, right_); 0098 } 0099 0100 // we're dealing with groups 0101 Data::EntryGroup* leftGroup = sourceModel()->data(left_, GroupPtrRole).value<Data::EntryGroup*>(); 0102 Data::EntryGroup* rightGroup = sourceModel()->data(right_, GroupPtrRole).value<Data::EntryGroup*>(); 0103 0104 // no matter what the sortRole is, the empty group is always first 0105 const bool emptyLeft = (!leftGroup || leftGroup->hasEmptyGroupName()); 0106 const bool emptyRight = (!rightGroup || rightGroup->hasEmptyGroupName()); 0107 0108 // yeah, I should figure out some bit-wise operations...whatever 0109 if(emptyLeft && !emptyRight) { 0110 return reverseOrder ? false : true; 0111 } else if(!emptyLeft && emptyRight) { 0112 return reverseOrder ? true : false; 0113 } else if(emptyLeft && emptyRight) { 0114 return reverseOrder ? true : false; 0115 } 0116 0117 // if we can get the fields' type, then for certain non-text-only 0118 // types use the sort defined for that type. 0119 if(!m_groupComparison) { 0120 m_groupComparison = getGroupComparison(leftGroup); 0121 } 0122 if(m_groupComparison) { 0123 return m_groupComparison->compare(leftGroup->groupName(), rightGroup->groupName()) < 0; 0124 } 0125 // couldn't determine the type or it's a type we want to sort 0126 // alphabetically, so sort by locale 0127 return left_.data().toString().localeAwareCompare(right_.data().toString()) < 0; 0128 } 0129 0130 if(!m_entryComparison) { 0131 // default to using the title field for sorting 0132 m_entryComparison = getEntryComparison(left_, QStringLiteral("title")); 0133 } 0134 Q_ASSERT(m_entryComparison); 0135 if(!m_entryComparison) { 0136 return left_.data().toString().localeAwareCompare(right_.data().toString()) < 0; 0137 } 0138 // entries always sort ascending, despite whatever the group order is 0139 const bool res = m_entryComparison->compare( left_.data(EntryPtrRole).value<Data::EntryPtr>(), 0140 right_.data(EntryPtrRole).value<Data::EntryPtr>()) < 0; 0141 return reverseOrder ? !res : res; 0142 } 0143 0144 void GroupSortModel::clearComparisons() { 0145 delete m_entryComparison; 0146 m_entryComparison = nullptr; 0147 delete m_groupComparison; 0148 m_groupComparison = nullptr; 0149 } 0150 0151 Tellico::FieldComparison* GroupSortModel::getEntryComparison(const QModelIndex& index_, const QString& fieldName_) const { 0152 FieldComparison* comp = nullptr; 0153 // depend on the index pointing to an entry from which we can get a collection 0154 Data::EntryPtr entry = index_.data(EntryPtrRole).value<Data::EntryPtr>(); 0155 if(entry) { 0156 Data::CollPtr coll = entry->collection(); 0157 // by default, always sort by title 0158 if(coll) { 0159 comp = FieldComparison::create(coll->fieldByName(fieldName_)); 0160 } 0161 } 0162 return comp; 0163 } 0164 0165 // if 'group_' contains a type of field that merits a non-alphabetic 0166 // sort, return a pointer to the proper sort function. 0167 Tellico::StringComparison* GroupSortModel::getGroupComparison(Data::EntryGroup* group_) const { 0168 StringComparison* comp = nullptr; 0169 if(group_ && !group_->isEmpty()) { 0170 // depend on the group NOT being empty which allows access to the first entry 0171 Data::CollPtr coll = group_->first()->collection(); 0172 if(coll && coll->hasField(group_->fieldName())) { 0173 comp = StringComparison::create(coll->fieldByName(group_->fieldName())); 0174 } 0175 } 0176 return comp; 0177 }