File indexing completed on 2025-01-26 03:51:08
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2013-02-12 0007 * Description : Wrapper model for table view 0008 * 0009 * SPDX-FileCopyrightText: 2017-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0010 * SPDX-FileCopyrightText: 2013 by Michael G. Hansen <mike at mghansen dot de> 0011 * 0012 * SPDX-License-Identifier: GPL-2.0-or-later 0013 * 0014 * ============================================================ */ 0015 0016 #include "tableview_model.h" 0017 0018 // C++ includes 0019 0020 #include <functional> 0021 #include <valarray> 0022 0023 // Qt includes 0024 0025 #include <QTimer> 0026 0027 // Local includes 0028 0029 #include "digikam_debug.h" 0030 #include "coredb.h" 0031 #include "coredbaccess.h" 0032 #include "coredbfields.h" 0033 #include "coredbwatch.h" 0034 #include "itemfiltermodel.h" 0035 #include "itemfiltersettings.h" 0036 #include "applicationsettings.h" 0037 #include "iteminfo.h" 0038 #include "tableview_columnfactory.h" 0039 #include "tableview_selection_model_syncer.h" 0040 0041 #ifdef DIGIKAM_ENABLE_MODELTEST 0042 # include "../../../tests/modeltest/modeltest.h" 0043 #endif // DIGIKAM_ENABLE_MODELTEST 0044 0045 #define ASSERT_MODEL(index, modelPointer) if (index.isValid()) { Q_ASSERT(index.model() == modelPointer); } 0046 0047 namespace Digikam 0048 { 0049 0050 TableViewModel::Item::Item() 0051 : imageId (0), 0052 parent (nullptr), 0053 children() 0054 { 0055 } 0056 0057 TableViewModel::Item::~Item() 0058 { 0059 qDeleteAll(children); 0060 } 0061 0062 void TableViewModel::Item::addChild(TableViewModel::Item* const newChild) 0063 { 0064 newChild->parent = this; 0065 0066 children << newChild; 0067 } 0068 0069 void TableViewModel::Item::insertChild(const int pos, TableViewModel::Item* const newChild) 0070 { 0071 newChild->parent = this; 0072 0073 children.insert(pos, newChild); 0074 } 0075 0076 void TableViewModel::Item::takeChild(TableViewModel::Item* const oldChild) 0077 { 0078 children.removeOne(oldChild); 0079 } 0080 0081 TableViewModel::Item* TableViewModel::Item::findChildWithImageId(const qlonglong searchImageId) 0082 { 0083 if (imageId == searchImageId) 0084 { 0085 return this; 0086 } 0087 0088 Q_FOREACH (Item* const item, children) 0089 { 0090 Item* const iItem = item->findChildWithImageId(searchImageId); 0091 0092 if (iItem) 0093 { 0094 return iItem; 0095 } 0096 } 0097 0098 return nullptr; 0099 } 0100 0101 // ---------------------------------------------------------------------------------------------- 0102 0103 class Q_DECL_HIDDEN TableViewModel::Private 0104 { 0105 public: 0106 0107 explicit Private() 0108 : columnObjects (), 0109 rootItem (nullptr), 0110 imageFilterSettings(), 0111 sortColumn (0), 0112 sortOrder (Qt::AscendingOrder), 0113 sortRequired (false), 0114 groupingMode (GroupingShowSubItems), 0115 outdated (true) 0116 { 0117 } 0118 0119 QList<TableViewColumn*> columnObjects; 0120 TableViewModel::Item* rootItem; 0121 ItemFilterSettings imageFilterSettings; 0122 int sortColumn; 0123 Qt::SortOrder sortOrder; 0124 bool sortRequired; 0125 GroupingMode groupingMode; 0126 bool outdated; 0127 }; 0128 0129 TableViewModel::TableViewModel(TableViewShared* const sharedObject, QObject* const parent) 0130 : QAbstractItemModel(parent), 0131 s (sharedObject), 0132 d (new Private()) 0133 { 0134 d->rootItem = new Item(); 0135 d->imageFilterSettings = s->imageFilterModel->imageFilterSettings(); 0136 0137 connect(s->imageModel, SIGNAL(modelAboutToBeReset()), 0138 this, SLOT(slotSourceModelAboutToBeReset())); 0139 0140 connect(s->imageModel, SIGNAL(modelReset()), 0141 this, SLOT(slotSourceModelReset())); 0142 0143 connect(s->imageModel, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), 0144 this, SLOT(slotSourceRowsAboutToBeInserted(QModelIndex,int,int))); 0145 0146 connect(s->imageModel, SIGNAL(rowsInserted(QModelIndex,int,int)), 0147 this, SLOT(slotSourceRowsInserted(QModelIndex,int,int))); 0148 0149 connect(s->imageModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), 0150 this, SLOT(slotSourceRowsAboutToBeRemoved(QModelIndex,int,int))); 0151 0152 connect(s->imageModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), 0153 this, SLOT(slotSourceRowsRemoved(QModelIndex,int,int))); 0154 0155 connect(s->imageModel, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), 0156 this, SLOT(slotSourceRowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int))); 0157 0158 connect(s->imageModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), 0159 this, SLOT(slotSourceRowsMoved(QModelIndex,int,int,QModelIndex,int))); 0160 0161 connect(s->imageModel, SIGNAL(layoutAboutToBeChanged()), 0162 this, SLOT(slotSourceLayoutAboutToBeChanged())); 0163 0164 connect(s->imageModel, SIGNAL(layoutChanged()), 0165 this, SLOT(slotSourceLayoutChanged())); 0166 0167 connect(s->imageFilterModel, SIGNAL(filterSettingsChanged(ItemFilterSettings)), 0168 this, SLOT(slotFilterSettingsChanged(ItemFilterSettings))); 0169 0170 // We do not connect to ItemFilterModel::dataChanged, because we monitor changes directly from the database. 0171 0172 connect(CoreDbAccess::databaseWatch(), SIGNAL(imageChange(ImageChangeset)), 0173 this, SLOT(slotDatabaseImageChanged(ImageChangeset)), Qt::QueuedConnection); 0174 0175 #ifdef DIGIKAM_ENABLE_MODELTEST 0176 0177 new ModelTest(this, this); 0178 0179 #endif // DIGIKAM_ENABLE_MODELTEST 0180 0181 // We only have to trigger population of the model if data is in the source model, 0182 // otherwise the source model will tell us about any new data. 0183 0184 const int itemsInItemModel = s->imageModel->rowCount(); 0185 0186 if (itemsInItemModel > 0) 0187 { 0188 // populate the model once later, not now 0189 0190 QTimer::singleShot(0, this, SLOT(slotPopulateModelWithNotifications())); 0191 } 0192 } 0193 0194 TableViewModel::~TableViewModel() 0195 { 0196 delete d->rootItem; 0197 } 0198 0199 int TableViewModel::columnCount(const QModelIndex& i) const 0200 { 0201 if (i.column() > 0) 0202 { 0203 return 0; 0204 } 0205 0206 return d->columnObjects.count(); 0207 } 0208 0209 QModelIndex TableViewModel::toItemFilterModelIndex(const QModelIndex& i) const 0210 { 0211 Item* const item = itemFromIndex(i); 0212 0213 if (!item) 0214 { 0215 return QModelIndex(); 0216 } 0217 0218 return s->imageFilterModel->indexForImageId(item->imageId); 0219 } 0220 0221 QModelIndex TableViewModel::toItemModelIndex(const QModelIndex& i) const 0222 { 0223 Item* const item = itemFromIndex(i); 0224 0225 if (!item) 0226 { 0227 return QModelIndex(); 0228 } 0229 0230 return s->imageModel->indexForImageId(item->imageId); 0231 } 0232 0233 QVariant TableViewModel::data(const QModelIndex& i, int role) const 0234 { 0235 Item* const item = itemFromIndex(i); 0236 0237 if (!item) 0238 { 0239 return QVariant(); 0240 } 0241 0242 const int columnNumber = i.column(); 0243 TableViewColumn* const myColumn = d->columnObjects.at(columnNumber); 0244 0245 return myColumn->data(item, role); 0246 } 0247 0248 QModelIndex TableViewModel::index(int row, int column, const QModelIndex& parent) const 0249 { 0250 Item* parentItem = d->rootItem; 0251 0252 if (parent.isValid()) 0253 { 0254 if (parent.column() > 0) 0255 { 0256 // only column 0 can have children, the other columns can not 0257 0258 return QModelIndex(); 0259 } 0260 0261 parentItem = itemFromIndex(parent); 0262 } 0263 0264 // test for valid row/column values 0265 0266 if ( 0267 (row < 0) || 0268 (column < 0) || 0269 (column >= d->columnObjects.count()) || 0270 (row >= parentItem->children.count()) 0271 ) 0272 { 0273 return QModelIndex(); 0274 } 0275 0276 Item* const itemPointer = parentItem->children.at(row); 0277 0278 return createIndex(row, column, itemPointer); 0279 } 0280 0281 QModelIndex TableViewModel::parent(const QModelIndex& childIndex) const 0282 { 0283 if (!childIndex.isValid()) 0284 { 0285 return QModelIndex(); 0286 } 0287 0288 Item* const childItem = itemFromIndex(childIndex); 0289 Item* const parentItem = childItem->parent; 0290 0291 if (parentItem == d->rootItem) 0292 { 0293 return QModelIndex(); 0294 } 0295 0296 Item* const grandParentItem = parentItem->parent; 0297 const int rowIndex = grandParentItem->children.indexOf(parentItem); 0298 0299 /// @todo What should be the column number? 0300 0301 return createIndex(rowIndex, 0, parentItem); 0302 } 0303 0304 int TableViewModel::rowCount(const QModelIndex& parent) const 0305 { 0306 if (parent.column() > 0) 0307 { 0308 return 0; 0309 } 0310 0311 Item* parentItem = d->rootItem; 0312 0313 if (parent.isValid()) 0314 { 0315 parentItem = itemFromIndex(parent); 0316 } 0317 0318 return parentItem->children.count(); 0319 } 0320 0321 QVariant TableViewModel::headerData(int section, Qt::Orientation orientation, int role) const 0322 { 0323 // test for valid input ranges 0324 0325 if ((section < 0) || (section >= d->columnObjects.count())) 0326 { 0327 return QVariant(); 0328 } 0329 0330 if ((orientation != Qt::Horizontal) || (role != Qt::DisplayRole)) 0331 { 0332 return QVariant(); 0333 } 0334 0335 TableViewColumn* const myColumn = d->columnObjects.at(section); 0336 0337 return myColumn->getTitle(); 0338 } 0339 0340 void TableViewModel::addColumnAt(const TableViewColumnDescription& description, const int targetColumn) 0341 { 0342 /// @todo take additional configuration data of the column into account 0343 0344 TableViewColumnConfiguration newConfiguration = description.toConfiguration(); 0345 0346 addColumnAt(newConfiguration, targetColumn); 0347 } 0348 0349 void TableViewModel::addColumnAt(const TableViewColumnConfiguration& configuration, const int targetColumn) 0350 { 0351 TableViewColumn* const newColumn = s->columnFactory->getColumn(configuration); 0352 0353 if (!newColumn) 0354 { 0355 return; 0356 } 0357 0358 int newColumnIndex = targetColumn; 0359 0360 if (targetColumn < 0) 0361 { 0362 // a negative column index means "append after last column" 0363 0364 newColumnIndex = d->columnObjects.count(); 0365 } 0366 0367 beginInsertColumns(QModelIndex(), newColumnIndex, newColumnIndex); 0368 0369 if (newColumnIndex >= d->columnObjects.count()) 0370 { 0371 d->columnObjects.append(newColumn); 0372 } 0373 else 0374 { 0375 d->columnObjects.insert(newColumnIndex, newColumn); 0376 } 0377 0378 endInsertColumns(); 0379 0380 connect(newColumn, SIGNAL(signalDataChanged(qlonglong)), 0381 this, SLOT(slotColumnDataChanged(qlonglong))); 0382 0383 connect(newColumn, SIGNAL(signalAllDataChanged()), 0384 this, SLOT(slotColumnAllDataChanged())); 0385 0386 const QModelIndex changedIndexTopLeft = index(0, 0, QModelIndex()); 0387 const QModelIndex changedIndexBottomRight = index(rowCount(QModelIndex())-1, 0388 columnCount(QModelIndex())-1, QModelIndex()); 0389 0390 Q_EMIT dataChanged(changedIndexTopLeft, changedIndexBottomRight); 0391 } 0392 0393 void TableViewModel::slotColumnDataChanged(const qlonglong imageId) 0394 { 0395 Item* const item = itemFromImageId(imageId); 0396 0397 if (!item) 0398 { 0399 return; 0400 } 0401 0402 Item* const parentItem = item->parent; 0403 0404 /// @todo This is a waste of time because itemFromImageId already did this search. 0405 /// We should modify it to also give the row index. 0406 0407 const int rowIndex = parentItem->children.indexOf(item); 0408 const int columnIndex = columnCount(QModelIndex())-1; 0409 const QModelIndex changedIndexLeft = index(rowIndex, 0, QModelIndex()); 0410 const QModelIndex changedIndexRight = index(rowIndex, columnIndex, QModelIndex()); 0411 0412 Q_EMIT dataChanged(changedIndexLeft, changedIndexRight); 0413 } 0414 0415 void TableViewModel::slotColumnAllDataChanged() 0416 { 0417 TableViewColumn* const senderColumn = qobject_cast<TableViewColumn*>(sender()); 0418 0419 /// @todo find a faster way to find the column number 0420 0421 const int iColumn = d->columnObjects.indexOf(senderColumn); 0422 0423 if (iColumn < 0) 0424 { 0425 return; 0426 } 0427 0428 const QModelIndex changedIndexTopLeft = index(0, iColumn, QModelIndex()); 0429 const QModelIndex changedIndexBottomRight = index(rowCount(QModelIndex())-1, iColumn, QModelIndex()); 0430 0431 Q_EMIT dataChanged(changedIndexTopLeft, changedIndexBottomRight); 0432 } 0433 0434 void TableViewModel::removeColumnAt(const int columnIndex) 0435 { 0436 beginRemoveColumns(QModelIndex(), columnIndex, columnIndex); 0437 TableViewColumn* const removedColumn = d->columnObjects.takeAt(columnIndex); 0438 endRemoveColumns(); 0439 0440 delete removedColumn; 0441 0442 const QModelIndex changedIndexTopLeft = index(0, 0, QModelIndex()); 0443 const QModelIndex changedIndexBottomRight = index(rowCount(QModelIndex())-1, 0444 columnCount(QModelIndex())-1, QModelIndex()); 0445 0446 Q_EMIT dataChanged(changedIndexTopLeft, changedIndexBottomRight); 0447 } 0448 0449 TableViewColumn* TableViewModel::getColumnObject(const int columnIndex) 0450 { 0451 return d->columnObjects.at(columnIndex); 0452 } 0453 0454 TableViewColumnProfile TableViewModel::getColumnProfile() const 0455 { 0456 TableViewColumnProfile profile; 0457 0458 for (int i = 0 ; i < d->columnObjects.count() ; ++i) 0459 { 0460 TableViewColumnConfiguration ic = d->columnObjects.at(i)->getConfiguration(); 0461 profile.columnConfigurationList << ic; 0462 } 0463 0464 return profile; 0465 } 0466 0467 void TableViewModel::loadColumnProfile(const TableViewColumnProfile& columnProfile) 0468 { 0469 while (!d->columnObjects.isEmpty()) 0470 { 0471 removeColumnAt(0); 0472 } 0473 0474 /// @todo disable updating of the model while this happens 0475 0476 for (int i = 0 ; i < columnProfile.columnConfigurationList.count() ; ++i) 0477 { 0478 addColumnAt(columnProfile.columnConfigurationList.at(i), -1); 0479 } 0480 } 0481 0482 void TableViewModel::slotSourceModelAboutToBeReset() 0483 { 0484 if (!s->isActive) 0485 { 0486 slotClearModel(true); 0487 return; 0488 } 0489 0490 // the source model is about to be reset. Propagate that change: 0491 0492 beginResetModel(); 0493 } 0494 0495 void TableViewModel::slotSourceModelReset() 0496 { 0497 if (!s->isActive) 0498 { 0499 return; 0500 } 0501 0502 // the source model is done resetting. 0503 0504 slotPopulateModel(false); 0505 endResetModel(); 0506 } 0507 0508 void TableViewModel::slotSourceRowsAboutToBeInserted(const QModelIndex& parent, int start, int end) 0509 { 0510 Q_UNUSED(parent) 0511 Q_UNUSED(start) 0512 Q_UNUSED(end) 0513 } 0514 0515 void TableViewModel::slotSourceRowsInserted(const QModelIndex& parent, int start, int end) 0516 { 0517 if (!s->isActive) 0518 { 0519 slotClearModel(true); 0520 return; 0521 } 0522 0523 for (int i = start ; i <= end ; ++i) 0524 { 0525 const QModelIndex sourceIndex = s->imageModel->index(i, 0, parent); 0526 0527 addSourceModelIndex(sourceIndex, true); 0528 } 0529 } 0530 0531 void TableViewModel::slotSourceRowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) 0532 { 0533 if (!s->isActive) 0534 { 0535 slotClearModel(true); 0536 return; 0537 } 0538 0539 bool needToResort = false; 0540 0541 for (int i = start ; i <= end ; ++i) 0542 { 0543 const QModelIndex imageModelIndex = s->imageModel->index(i, 0, parent); 0544 const qlonglong imageId = s->imageModel->imageId(imageModelIndex); 0545 const QModelIndex tableViewIndex = indexFromImageId(imageId, 0); 0546 0547 if (!tableViewIndex.isValid()) 0548 { 0549 continue; 0550 } 0551 0552 Item* const item = itemFromIndex(tableViewIndex); 0553 0554 if (!item) 0555 { 0556 continue; 0557 } 0558 0559 beginRemoveRows(tableViewIndex.parent(), tableViewIndex.row(), tableViewIndex.row()); 0560 item->parent->takeChild(item); 0561 0562 // move child items to parent item 0563 0564 while (!item->children.isEmpty()) 0565 { 0566 item->parent->addChild(item->children.takeFirst()); 0567 needToResort = true; 0568 } 0569 0570 // child items will be deleted when item is deleted 0571 0572 delete item; 0573 endRemoveRows(); 0574 } 0575 0576 if (needToResort) 0577 { 0578 scheduleResort(); 0579 } 0580 } 0581 0582 void TableViewModel::slotSourceRowsRemoved(const QModelIndex& parent, int start, int end) 0583 { 0584 /// @todo Do we need to do anything here? 0585 0586 Q_UNUSED(parent) 0587 Q_UNUSED(start) 0588 Q_UNUSED(end) 0589 } 0590 0591 void TableViewModel::slotSourceRowsAboutToBeMoved(const QModelIndex& sourceParent, int sourceStart, int sourceEnd, 0592 const QModelIndex& destinationParent, int destinationRow) 0593 { 0594 /* 0595 beginMoveRows(sourceParent, sourceStart, sourceEnd, destinationParent, destinationRow); 0596 */ 0597 /// @todo For our items, moving stuff around does not matter --> remove this slot 0598 0599 Q_UNUSED(sourceParent) 0600 Q_UNUSED(sourceStart) 0601 Q_UNUSED(sourceEnd) 0602 Q_UNUSED(destinationParent) 0603 Q_UNUSED(destinationRow) 0604 } 0605 0606 void TableViewModel::slotSourceRowsMoved(const QModelIndex& sourceParent, int sourceStart, int sourceEnd, 0607 const QModelIndex& destinationParent, int destinationRow) 0608 { 0609 /// @todo For our items, moving stuff around does not matter --> remove this slot 0610 0611 Q_UNUSED(sourceParent) 0612 Q_UNUSED(sourceStart) 0613 Q_UNUSED(sourceEnd) 0614 Q_UNUSED(destinationParent) 0615 Q_UNUSED(destinationRow) 0616 /* 0617 endMoveRows(); 0618 */ 0619 } 0620 0621 void TableViewModel::slotSourceLayoutAboutToBeChanged() 0622 { 0623 if (!s->isActive) 0624 { 0625 slotClearModel(true); 0626 return; 0627 } 0628 0629 /// @todo Emitting layoutAboutToBeChanged and layoutChanged is tricky, 0630 /// because we do not know what will change. 0631 /// It looks like ItemFilterModel emits layoutAboutToBeChanged and layoutChanged 0632 /// even when the resulting dataset will be empty, and ModelTest does not like that. 0633 /// For now, the easiest workaround is resetting the model 0634 /* 0635 Q_EMIT layoutAboutToBeChanged(); 0636 */ 0637 beginResetModel(); 0638 } 0639 0640 void TableViewModel::slotSourceLayoutChanged() 0641 { 0642 if (!s->isActive) 0643 { 0644 return; 0645 } 0646 0647 /// @todo See note in TableViewModel#slotSourceLayoutAboutToBeChanged 0648 0649 slotPopulateModel(false); 0650 0651 endResetModel(); 0652 } 0653 0654 void TableViewModel::slotDatabaseImageChanged(const ImageChangeset& imageChangeset) 0655 { 0656 if (!s->isActive) 0657 { 0658 slotClearModel(true); 0659 return; 0660 } 0661 /* 0662 const DatabaseFields::Set changes = imageChangeset.changes(); 0663 */ 0664 /// @todo Decide which changes are relevant here or 0665 /// let the TableViewColumn object decide which are relevant 0666 /// @todo Re-population of the model is also triggered, thus making this 0667 /// check irrelevant. Needs to be fixed. 0668 /// @todo If the user has never set which column should define the sorting, 0669 /// the sortColumn is invalid. We should set a useful default. 0670 0671 bool needToResort = false; 0672 0673 if ((d->sortColumn >= 0) && (d->sortColumn < d->columnObjects.count())) 0674 { 0675 TableViewColumn* const sortColumnObject = d->columnObjects.at(d->sortColumn); 0676 needToResort = sortColumnObject->columnAffectedByChangeset(imageChangeset); 0677 } 0678 0679 Q_FOREACH (const qlonglong& id, imageChangeset.ids()) 0680 { 0681 // first clear the item's cached values 0682 /// @todo Clear only the fields which were changed 0683 0684 Item* const item = itemFromImageId(id); 0685 0686 if (!item) 0687 { 0688 // Item is not in this model. If it is in the ItemModel, 0689 // it has been filtered out and we have to re-check the filtering. 0690 0691 const QModelIndex& imageModelIndex = s->imageModel->indexForImageId(id); 0692 0693 if (!imageModelIndex.isValid()) 0694 { 0695 continue; 0696 } 0697 0698 const ItemInfo imageInfo = s->imageModel->imageInfo(imageModelIndex); 0699 0700 if (d->imageFilterSettings.matches(imageInfo)) 0701 { 0702 // need to add the item 0703 0704 addSourceModelIndex(imageModelIndex, true); 0705 } 0706 0707 continue; 0708 } 0709 0710 // Re-check filtering for this item. 0711 0712 const QModelIndex changedIndexTopLeft = indexFromImageId(id, 0); 0713 0714 if (!changedIndexTopLeft.isValid()) 0715 { 0716 continue; 0717 } 0718 0719 const ItemInfo myItemInfo = imageInfo(changedIndexTopLeft); 0720 0721 if (!d->imageFilterSettings.matches(myItemInfo)) 0722 { 0723 // Filter does not match, remove the item. 0724 0725 beginRemoveRows(changedIndexTopLeft.parent(), changedIndexTopLeft.row(), changedIndexTopLeft.row()); 0726 item->parent->takeChild(item); 0727 0728 delete item; 0729 endRemoveRows(); 0730 0731 continue; 0732 } 0733 0734 /// @todo Re-check grouping for this item 0735 0736 // only update now if we do not resort later anyway 0737 0738 if (!needToResort) 0739 { 0740 const QModelIndex changedIndexBottomRight = index(changedIndexTopLeft.row(), 0741 columnCount(changedIndexTopLeft.parent())-1, 0742 changedIndexTopLeft.parent()); 0743 0744 if (changedIndexBottomRight.isValid()) 0745 { 0746 Q_EMIT dataChanged(changedIndexTopLeft, changedIndexBottomRight); 0747 } 0748 } 0749 } 0750 0751 if (needToResort) 0752 { 0753 scheduleResort(); 0754 } 0755 } 0756 0757 Qt::ItemFlags TableViewModel::flags(const QModelIndex& index) const 0758 { 0759 const Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index); 0760 0761 /// @todo Handle read-only files etc. which can not be moved 0762 0763 if (index.isValid()) 0764 { 0765 return (Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags); 0766 } 0767 0768 return (Qt::ItemIsDropEnabled | defaultFlags); 0769 } 0770 0771 QList<TableViewColumn*> TableViewModel::getColumnObjects() 0772 { 0773 return d->columnObjects; 0774 } 0775 0776 void TableViewModel::slotPopulateModelWithNotifications() 0777 { 0778 slotPopulateModel(true); 0779 } 0780 0781 void TableViewModel::slotClearModel(const bool sendNotifications) 0782 { 0783 if (d->outdated) 0784 { 0785 return; 0786 } 0787 0788 d->outdated = true; 0789 0790 if (sendNotifications) 0791 { 0792 beginResetModel(); 0793 } 0794 0795 if (d->rootItem) 0796 { 0797 delete d->rootItem; 0798 } 0799 0800 d->rootItem = new Item(); 0801 0802 if (sendNotifications) 0803 { 0804 endResetModel(); 0805 } 0806 } 0807 0808 void TableViewModel::slotPopulateModel(const bool sendNotifications) 0809 { 0810 if (!s->isActive) 0811 { 0812 slotClearModel(sendNotifications); 0813 return; 0814 } 0815 0816 if (sendNotifications) 0817 { 0818 beginResetModel(); 0819 } 0820 0821 if (d->rootItem) 0822 { 0823 delete d->rootItem; 0824 } 0825 0826 d->rootItem = new Item(); 0827 d->outdated = false; 0828 d->sortRequired = false; 0829 0830 const int sourceRowCount = s->imageModel->rowCount(QModelIndex()); 0831 0832 for (int i = 0 ; i < sourceRowCount ; ++i) 0833 { 0834 const QModelIndex sourceModelIndex = s->imageModel->index(i, 0); 0835 0836 // do not send notifications in addSourceModelIndex, because this function here 0837 // already started a model reset 0838 0839 addSourceModelIndex(sourceModelIndex, false); 0840 } 0841 0842 if (sendNotifications) 0843 { 0844 endResetModel(); 0845 } 0846 } 0847 0848 TableViewModel::Item* TableViewModel::createItemFromSourceIndex(const QModelIndex& imageModelIndex) 0849 { 0850 ASSERT_MODEL(imageModelIndex, s->imageModel); 0851 0852 Item* const item = new Item(); 0853 item->imageId = s->imageModel->imageId(imageModelIndex); 0854 0855 return item; 0856 } 0857 0858 void TableViewModel::addSourceModelIndex(const QModelIndex& imageModelIndex, const bool sendNotifications) 0859 { 0860 ASSERT_MODEL(imageModelIndex, s->imageModel); 0861 0862 const ItemInfo imageInfo = s->imageModel->imageInfo(imageModelIndex); 0863 const bool passedFilter = d->imageFilterSettings.matches(imageInfo); 0864 const bool allGroupsOpen = ApplicationSettings::instance()->getAllGroupsOpen(); 0865 0866 if (!passedFilter) 0867 { 0868 return; 0869 } 0870 0871 /// @todo Implement Grouping and sorting 0872 0873 Item* const parentItem = d->rootItem; 0874 0875 if (!allGroupsOpen && imageInfo.isGrouped()) 0876 { 0877 switch (d->groupingMode) 0878 { 0879 case GroupingHideGrouped: 0880 0881 // we do not show grouped items at all 0882 0883 return; 0884 0885 case GroupingIgnoreGrouping: 0886 0887 // nothing to do, we just add it to the root item 0888 0889 break; 0890 0891 case GroupingShowSubItems: 0892 0893 // we do not add this subitem, because it has been automatically added to the group leader 0894 0895 return; 0896 } 0897 } 0898 0899 Item* const item = createItemFromSourceIndex(imageModelIndex); 0900 0901 // Normally we do the sorting of items here on insertion. 0902 // However, if the sorting is currently outdated, we just 0903 // append the items because the model will be resorted later. 0904 0905 int newRowIndex = parentItem->children.count(); 0906 0907 if (!d->sortRequired) 0908 { 0909 newRowIndex = findChildSortedPosition(parentItem, item); 0910 } 0911 0912 if (sendNotifications) 0913 { 0914 const QModelIndex parentIndex = itemIndex(parentItem); 0915 beginInsertRows(parentIndex, newRowIndex, newRowIndex); 0916 } 0917 0918 parentItem->insertChild(newRowIndex, item); 0919 0920 if (sendNotifications) 0921 { 0922 endInsertRows(); 0923 } 0924 0925 if (!allGroupsOpen && (d->groupingMode == GroupingShowSubItems) && imageInfo.hasGroupedImages()) 0926 { 0927 // the item was a group leader, add its subitems 0928 0929 const QList<ItemInfo> groupedImages = imageInfo.groupedImages(); 0930 0931 if (sendNotifications) 0932 { 0933 const QModelIndex groupLeaderIndex = itemIndex(item); 0934 beginInsertRows(groupLeaderIndex, 0, groupedImages.count()-1); 0935 } 0936 0937 Q_FOREACH (const ItemInfo& groupedInfo, groupedImages) 0938 { 0939 /// @todo Grouped items are currently not filtered. Should they? 0940 0941 Item* const groupedItem = new Item(); 0942 groupedItem->imageId = groupedInfo.id(); 0943 0944 // Normally we do the sorting of items here on insertion. 0945 // However, if the sorting is currently outdated, we just 0946 // append the items because the model will be resorted later. 0947 0948 int index = item->children.count(); 0949 0950 if (!d->sortRequired) 0951 { 0952 index = findChildSortedPosition(item, groupedItem); 0953 } 0954 0955 item->insertChild(index, groupedItem); 0956 } 0957 0958 if (sendNotifications) 0959 { 0960 endInsertRows(); 0961 } 0962 } 0963 } 0964 0965 TableViewModel::Item* TableViewModel::itemFromImageId(const qlonglong imageId) const 0966 { 0967 return d->rootItem->findChildWithImageId(imageId); 0968 } 0969 0970 TableViewModel::Item* TableViewModel::itemFromIndex(const QModelIndex& i) const 0971 { 0972 if (!i.isValid()) 0973 { 0974 return nullptr; 0975 } 0976 0977 Q_ASSERT(i.model() == this); 0978 0979 Item* const item = static_cast<Item*>(i.internalPointer()); 0980 0981 return item; 0982 } 0983 0984 QModelIndex TableViewModel::fromItemFilterModelIndex(const QModelIndex& imageFilterModelIndex) 0985 { 0986 ASSERT_MODEL(imageFilterModelIndex, s->imageFilterModel); 0987 0988 const qlonglong imageId = s->imageFilterModel->imageId(imageFilterModelIndex); 0989 0990 if (!imageId) 0991 { 0992 return QModelIndex(); 0993 } 0994 0995 return indexFromImageId(imageId, 0); 0996 } 0997 0998 QModelIndex TableViewModel::fromItemModelIndex(const QModelIndex& imageModelIndex) 0999 { 1000 ASSERT_MODEL(imageModelIndex, s->imageModel); 1001 1002 const qlonglong imageId = s->imageModel->imageId(imageModelIndex); 1003 1004 if (!imageId) 1005 { 1006 return QModelIndex(); 1007 } 1008 1009 return indexFromImageId(imageId, 0); 1010 } 1011 1012 ItemInfo TableViewModel::infoFromItem(TableViewModel::Item* const item) const 1013 { 1014 /// @todo Is there a way to do it without first looking up the index in the ItemModel? 1015 1016 const QModelIndex imageModelIndex = s->imageModel->indexForImageId(item->imageId); 1017 const ItemInfo info = s->imageModel->imageInfo(imageModelIndex); 1018 1019 return info; 1020 } 1021 1022 ItemInfoList TableViewModel::infosFromItems(const QList<TableViewModel::Item*>& items) const 1023 { 1024 ItemInfoList infos; 1025 1026 Q_FOREACH (TableViewModel::Item* const item, items) 1027 { 1028 infos << infoFromItem(item); 1029 } 1030 1031 return infos; 1032 } 1033 1034 TableViewModel::DatabaseFieldsHashRaw TableViewModel::itemDatabaseFieldsRaw(TableViewModel::Item* const item, 1035 const DatabaseFields::Set& requestedSet) 1036 { 1037 const ItemInfo itemItemInfo = infoFromItem(item); 1038 1039 return itemItemInfo.getDatabaseFieldsRaw(requestedSet); 1040 } 1041 1042 QVariant TableViewModel::itemDatabaseFieldRaw(TableViewModel::Item* const item, 1043 const DatabaseFields::Set& requestedField) 1044 { 1045 const TableViewModel::DatabaseFieldsHashRaw rawHash = itemDatabaseFieldsRaw(item, requestedField); 1046 1047 if (requestedField.hasFieldsFromImageMetadata()) 1048 { 1049 const DatabaseFields::ImageMetadata requestedFieldFlag = requestedField; 1050 const QVariant value = rawHash.value(requestedFieldFlag); 1051 1052 return value; 1053 } 1054 1055 if (requestedField.hasFieldsFromVideoMetadata()) 1056 { 1057 const DatabaseFields::VideoMetadata requestedFieldFlag = requestedField; 1058 const QVariant value = rawHash.value(requestedFieldFlag); 1059 1060 return value; 1061 } 1062 1063 return QVariant(); 1064 } 1065 1066 QModelIndex TableViewModel::indexFromImageId(const qlonglong imageId, const int columnIndex) const 1067 { 1068 Item* const item = itemFromImageId(imageId); 1069 1070 if (!item) 1071 { 1072 return QModelIndex(); 1073 } 1074 1075 Item* const parentItem = item->parent; 1076 1077 /// @todo This is a waste of time because itemFromImageId already did this search. 1078 /// We should modify it to also give the row index. 1079 1080 const int rowIndex = parentItem->children.indexOf(item); 1081 1082 return createIndex(rowIndex, columnIndex, item); 1083 } 1084 1085 QList<qlonglong> TableViewModel::imageIds(const QModelIndexList& indexList) const 1086 { 1087 QList<qlonglong> idList; 1088 1089 Q_FOREACH (const QModelIndex& index, indexList) 1090 { 1091 ASSERT_MODEL(index, this); 1092 1093 if (index.column() > 0) 1094 { 1095 continue; 1096 } 1097 1098 const Item* const item = itemFromIndex(index); 1099 1100 if (!item) 1101 { 1102 continue; 1103 } 1104 1105 idList << item->imageId; 1106 } 1107 1108 return idList; 1109 } 1110 1111 QList<ItemInfo> TableViewModel::imageInfos(const QModelIndexList& indexList) const 1112 { 1113 QList<ItemInfo> infoList; 1114 1115 Q_FOREACH (const QModelIndex& index, indexList) 1116 { 1117 ASSERT_MODEL(index, this); 1118 1119 if (index.column() > 0) 1120 { 1121 continue; 1122 } 1123 1124 Item* const item = itemFromIndex(index); 1125 1126 if (!item) 1127 { 1128 continue; 1129 } 1130 1131 infoList << infoFromItem(item); 1132 } 1133 1134 return infoList; 1135 } 1136 1137 ItemInfo TableViewModel::imageInfo(const QModelIndex& index) const 1138 { 1139 ASSERT_MODEL(index, this); 1140 1141 Item* const item = itemFromIndex(index); 1142 1143 if (!item) 1144 { 1145 return ItemInfo(); 1146 } 1147 1148 return infoFromItem(item); 1149 } 1150 1151 void TableViewModel::slotFilterSettingsChanged(const ItemFilterSettings& settings) 1152 { 1153 d->imageFilterSettings = settings; 1154 1155 slotPopulateModel(true); 1156 } 1157 1158 class Q_DECL_HIDDEN TableViewModel::LessThan 1159 { 1160 public: 1161 1162 explicit LessThan(TableViewModel* const model) 1163 : m(model) 1164 { 1165 } 1166 1167 bool operator()(const TableViewModel::Item* const itemA, const TableViewModel::Item* const itemB) 1168 { 1169 const bool compareResult = m->lessThan(const_cast<Item*>(itemA), const_cast<Item*>(itemB)); 1170 1171 if (m->d->sortOrder == Qt::DescendingOrder) 1172 { 1173 return !compareResult; 1174 } 1175 1176 return compareResult; 1177 } 1178 1179 public: 1180 1181 TableViewModel* m; 1182 }; 1183 1184 QList<TableViewModel::Item*> TableViewModel::sortItems(const QList<TableViewModel::Item*>& itemList) 1185 { 1186 QList<Item*> sortedList = itemList; 1187 1188 std::sort(sortedList.begin(), 1189 sortedList.end(), 1190 LessThan(this)); 1191 1192 return sortedList; 1193 } 1194 1195 void TableViewModel::sort(int column, Qt::SortOrder order) 1196 { 1197 d->sortColumn = column; 1198 d->sortOrder = order; 1199 1200 /// @todo re-sort items 1201 1202 QList<Item*> itemsRequiringSorting; 1203 itemsRequiringSorting << d->rootItem; 1204 1205 beginResetModel(); 1206 1207 while (!itemsRequiringSorting.isEmpty()) 1208 { 1209 Item* const itemToSort = itemsRequiringSorting.takeFirst(); 1210 1211 Q_FOREACH (Item* const itemToCheck, itemToSort->children) 1212 { 1213 if (!itemToCheck->children.isEmpty()) 1214 { 1215 itemsRequiringSorting << itemToCheck; 1216 } 1217 } 1218 1219 itemToSort->children = sortItems(itemToSort->children); 1220 } 1221 1222 endResetModel(); 1223 } 1224 1225 bool TableViewModel::lessThan(TableViewModel::Item* const itemA, TableViewModel::Item* const itemB) 1226 { 1227 if ((d->sortColumn < 0) || (d->sortColumn >= d->columnObjects.count())) 1228 { 1229 return (itemA->imageId < itemB->imageId); 1230 } 1231 1232 const TableViewColumn* columnObject = s->tableViewModel->getColumnObject(d->sortColumn); 1233 1234 if (!columnObject->getColumnFlags().testFlag(TableViewColumn::ColumnCustomSorting)) 1235 { 1236 const QString stringA = columnObject->data(itemA, Qt::DisplayRole).toString(); 1237 const QString stringB = columnObject->data(itemB, Qt::DisplayRole).toString(); 1238 1239 if ((stringA == stringB) || (stringA.isEmpty() && stringB.isEmpty())) 1240 { 1241 return (itemA->imageId < itemB->imageId); 1242 } 1243 1244 return (stringA < stringB); 1245 } 1246 1247 TableViewColumn::ColumnCompareResult cmpResult = columnObject->compare(itemA, itemB); 1248 1249 if (cmpResult == TableViewColumn::CmpEqual) 1250 { 1251 // compared items are equal, use the image id to enforce a repeatable sorting 1252 1253 const qlonglong imageIdA = itemA->imageId; 1254 const qlonglong imageIdB = itemB->imageId; 1255 1256 return (imageIdA < imageIdB); 1257 } 1258 1259 return (cmpResult == TableViewColumn::CmpALessB); 1260 } 1261 1262 QMimeData* TableViewModel::mimeData(const QModelIndexList& indexes) const 1263 { 1264 // we pack the mime data via ItemModel's drag-drop handler 1265 1266 AbstractItemDragDropHandler* const ddHandler = s->imageModel->dragDropHandler(); 1267 1268 QModelIndexList imageModelIndexList; 1269 1270 Q_FOREACH (const QModelIndex& i, indexes) 1271 { 1272 if (i.column() > 0) 1273 { 1274 continue; 1275 } 1276 1277 const QModelIndex imageModelIndex = toItemModelIndex(i); 1278 1279 if (imageModelIndex.isValid()) 1280 { 1281 imageModelIndexList << imageModelIndex; 1282 } 1283 } 1284 1285 QMimeData* const imageModelMimeData = ddHandler->createMimeData(imageModelIndexList); 1286 1287 return imageModelMimeData; 1288 } 1289 1290 Qt::DropActions TableViewModel::supportedDropActions() const 1291 { 1292 return (Qt::CopyAction | Qt::MoveAction); 1293 } 1294 1295 QStringList TableViewModel::mimeTypes() const 1296 { 1297 AbstractItemDragDropHandler* const ddHandler = s->imageModel->dragDropHandler(); 1298 1299 if (ddHandler) 1300 { 1301 return ddHandler->mimeTypes(); 1302 } 1303 1304 return QStringList(); 1305 } 1306 1307 bool TableViewModel::dropMimeData(const QMimeData* data, 1308 Qt::DropAction action, 1309 int row, int column, 1310 const QModelIndex& parent) 1311 { 1312 Q_UNUSED(data) 1313 Q_UNUSED(action) 1314 Q_UNUSED(row) 1315 Q_UNUSED(column) 1316 Q_UNUSED(parent) 1317 1318 // the drop is handled by the drag-drop handler, therefore we return false here 1319 1320 return false; 1321 } 1322 1323 void TableViewModel::slotResortModel() 1324 { 1325 if (!d->sortRequired) 1326 { 1327 return; 1328 } 1329 1330 beginResetModel(); 1331 sort(d->sortColumn, d->sortOrder); 1332 endResetModel(); 1333 1334 d->sortRequired = false; 1335 } 1336 1337 void TableViewModel::scheduleResort() 1338 { 1339 if (d->sortRequired) 1340 { 1341 return; 1342 } 1343 1344 d->sortRequired = true; 1345 1346 QTimer::singleShot(100, this, SLOT(slotResortModel())); 1347 } 1348 1349 QModelIndex TableViewModel::itemIndex(TableViewModel::Item* const item) const 1350 { 1351 if ((!item) || (item==d->rootItem)) 1352 { 1353 return QModelIndex(); 1354 } 1355 1356 const int rowIndex = item->parent->children.indexOf(item); 1357 1358 return createIndex(rowIndex, 0, item); 1359 } 1360 1361 bool TableViewModel::hasChildren(const QModelIndex& parent) const 1362 { 1363 Item* parentItem = d->rootItem; 1364 1365 if (parent.isValid()) 1366 { 1367 if (parent.column() > 0) 1368 { 1369 // only column 0 can have children 1370 1371 return false; 1372 } 1373 1374 parentItem = itemFromIndex(parent); 1375 } 1376 1377 return !parentItem->children.isEmpty(); 1378 } 1379 1380 qlonglong TableViewModel::imageId(const QModelIndex& anIndex) const 1381 { 1382 const Item* const anItem = itemFromIndex(anIndex); 1383 1384 if (!anItem) 1385 { 1386 return (-1); 1387 } 1388 1389 return anItem->imageId; 1390 } 1391 1392 QList<ItemInfo> TableViewModel::allItemInfo() const 1393 { 1394 return infosFromItems(d->rootItem->children); 1395 } 1396 1397 TableViewModel::GroupingMode TableViewModel::groupingMode() const 1398 { 1399 return d->groupingMode; 1400 } 1401 1402 void TableViewModel::setGroupingMode(const TableViewModel::GroupingMode newGroupingMode) 1403 { 1404 if (d->groupingMode != newGroupingMode) 1405 { 1406 d->groupingMode = newGroupingMode; 1407 QTimer::singleShot(100, this, SLOT(slotPopulateModelWithNotifications())); 1408 1409 Q_EMIT signalGroupingModeChanged(); 1410 } 1411 } 1412 1413 QModelIndex TableViewModel::deepRowIndex(const int rowNumber) const 1414 { 1415 int targetRowNumber = rowNumber; 1416 1417 if (rowNumber < 0) 1418 { 1419 targetRowNumber += deepRowCount(); 1420 } 1421 1422 QModelIndex cIndex = index(0, 0); 1423 1424 for (int i = 0 ; i < targetRowNumber ; ++i) 1425 { 1426 if (hasChildren(cIndex)) 1427 { 1428 cIndex = index(0, 0, cIndex); 1429 } 1430 else 1431 { 1432 QModelIndex candidateIndex = cIndex.sibling(cIndex.row() + 1, 0); 1433 1434 if (!candidateIndex.isValid()) 1435 { 1436 QModelIndex parentIndex = cIndex.parent(); 1437 1438 if (!parentIndex.isValid()) 1439 { 1440 return QModelIndex(); 1441 } 1442 1443 candidateIndex = parentIndex.sibling(parentIndex.row() + 1, 0); 1444 } 1445 1446 cIndex = candidateIndex; 1447 } 1448 } 1449 1450 return cIndex; 1451 } 1452 1453 int TableViewModel::indexToDeepRowNumber(const QModelIndex& rowIndex) const 1454 { 1455 const QModelIndex column0Index = toCol0(rowIndex); 1456 int deepRowNumber = 0; 1457 QModelIndex cIndex = index(0, 0); 1458 1459 while (cIndex.isValid()) 1460 { 1461 if (cIndex == column0Index) 1462 { 1463 break; 1464 } 1465 1466 ++deepRowNumber; 1467 1468 if (hasChildren(cIndex)) 1469 { 1470 cIndex = index(0, 0, cIndex); 1471 } 1472 else 1473 { 1474 QModelIndex candidateIndex = cIndex.sibling(cIndex.row() + 1, 0); 1475 1476 if (!candidateIndex.isValid()) 1477 { 1478 QModelIndex parentIndex = cIndex.parent(); 1479 1480 if (!parentIndex.isValid()) 1481 { 1482 break; 1483 } 1484 1485 candidateIndex = parentIndex.sibling(parentIndex.row() + 1, 0); 1486 } 1487 1488 cIndex = candidateIndex; 1489 } 1490 } 1491 1492 if (!cIndex.isValid()) 1493 { 1494 return (-1); 1495 } 1496 1497 return deepRowNumber; 1498 } 1499 1500 int TableViewModel::deepRowCount() const 1501 { 1502 int deepRowNumber = 0; 1503 QModelIndex cIndex = index(0, 0); 1504 1505 while (cIndex.isValid()) 1506 { 1507 ++deepRowNumber; 1508 1509 if (hasChildren(cIndex)) 1510 { 1511 cIndex = index(0, 0, cIndex); 1512 } 1513 else 1514 { 1515 QModelIndex candidateIndex = cIndex.sibling(cIndex.row() + 1, 0); 1516 1517 if (!candidateIndex.isValid()) 1518 { 1519 QModelIndex parentIndex = cIndex.parent(); 1520 1521 if (!parentIndex.isValid()) 1522 { 1523 break; 1524 } 1525 1526 candidateIndex = parentIndex.sibling(parentIndex.row() + 1, 0); 1527 } 1528 1529 cIndex = candidateIndex; 1530 } 1531 } 1532 1533 return deepRowNumber; 1534 } 1535 1536 QModelIndex TableViewModel::toCol0(const QModelIndex& anIndex) const 1537 { 1538 return anIndex.sibling(anIndex.row(), 0); 1539 } 1540 1541 int TableViewModel::firstDeepRowNotInList(const QList<QModelIndex>& needleList) 1542 { 1543 int currentNeedlePos = 0; 1544 QModelIndex currentNeedleIndex = toCol0(needleList.first()); 1545 int deepRowNumber = 0; 1546 QModelIndex cIndex = index(0, 0); 1547 1548 while (cIndex.isValid()) 1549 { 1550 if (cIndex != currentNeedleIndex) 1551 { 1552 return deepRowNumber; 1553 } 1554 1555 if (hasChildren(cIndex)) 1556 { 1557 cIndex = index(0, 0, cIndex); 1558 } 1559 else 1560 { 1561 QModelIndex candidateIndex = cIndex.sibling(cIndex.row() + 1, 0); 1562 1563 if (!candidateIndex.isValid()) 1564 { 1565 QModelIndex parentIndex = cIndex.parent(); 1566 1567 if (!parentIndex.isValid()) 1568 { 1569 break; 1570 } 1571 1572 candidateIndex = parentIndex.sibling(parentIndex.row() + 1, 0); 1573 } 1574 1575 cIndex = candidateIndex; 1576 } 1577 1578 if (cIndex.isValid()) 1579 { 1580 ++deepRowNumber; 1581 ++currentNeedlePos; 1582 1583 if (currentNeedlePos >= needleList.count()) 1584 { 1585 return deepRowNumber; 1586 } 1587 1588 currentNeedleIndex = toCol0(needleList.at(currentNeedlePos)); 1589 } 1590 } 1591 1592 return -1; 1593 } 1594 1595 void TableViewModel::slotSetActive(const bool isActive) 1596 { 1597 if (isActive) 1598 { 1599 if (d->outdated) 1600 { 1601 // populate the model once later, not now 1602 1603 QTimer::singleShot(0, this, SLOT(slotPopulateModelWithNotifications())); 1604 } 1605 } 1606 } 1607 1608 int TableViewModel::findChildSortedPosition(TableViewModel::Item* const parentItem, TableViewModel::Item* const childItem) 1609 { 1610 if (parentItem->children.isEmpty()) 1611 { 1612 return 0; 1613 } 1614 1615 // nChildren is guaranteed to be >=1 1616 1617 const int nChildren = parentItem->children.count(); 1618 int stepSize = nChildren / 2; 1619 1620 // make sure pos is at least 0 if there is only one item 1621 1622 int pos = qMin(nChildren - 1, stepSize); 1623 1624 while (true) 1625 { 1626 stepSize = stepSize / 2; 1627 1628 if (stepSize == 0) 1629 { 1630 stepSize = 1; 1631 } 1632 1633 bool isLessThanUpper = lessThan(childItem, parentItem->children.at(pos)); 1634 1635 if (d->sortOrder == Qt::DescendingOrder) 1636 { 1637 isLessThanUpper = !isLessThanUpper; 1638 } 1639 1640 if (!isLessThanUpper) 1641 { 1642 // need to jump up, quit if we can not jump up by 1 1643 1644 if ((pos + 1) >= nChildren) 1645 { 1646 pos = nChildren; 1647 break; 1648 } 1649 1650 // jump up by stepSize and make sure we do not jump over the end 1651 1652 pos += stepSize; 1653 1654 if (pos >= nChildren) 1655 { 1656 pos = nChildren - 1; 1657 } 1658 1659 continue; 1660 } 1661 1662 // can we go lower? 1663 const bool lowerThere = (pos > 0); 1664 1665 if (!lowerThere) 1666 { 1667 // no, stop 1668 1669 pos = 0; 1670 break; 1671 } 1672 1673 bool isLessThanLower = lessThan(childItem, parentItem->children.at(pos-1)); 1674 1675 if (d->sortOrder == Qt::DescendingOrder) 1676 { 1677 isLessThanLower = !isLessThanLower; 1678 } 1679 1680 if (isLessThanLower) 1681 { 1682 // go lower and make sure we do not jump too low 1683 1684 pos -= stepSize; 1685 1686 if (pos < 0) 1687 { 1688 pos = 0; 1689 } 1690 1691 continue; 1692 } 1693 1694 break; 1695 } 1696 1697 return pos; 1698 } 1699 1700 } // namespace Digikam 1701 1702 #include "moc_tableview_model.cpp"