File indexing completed on 2025-01-19 03:50:50
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2013-02-18 0007 * Description : Sync QItemSelectionModel of ItemFilterModel and TableViewModel 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_selection_model_syncer.h" 0017 0018 // Qt includes 0019 0020 #include <QTimer> 0021 0022 // Local includes 0023 0024 #include "digikam_debug.h" 0025 #include "itemfiltermodel.h" 0026 #include "tableview_model.h" 0027 #include "tableview_shared.h" 0028 0029 namespace Digikam 0030 { 0031 0032 class Q_DECL_HIDDEN TableViewSelectionModelSyncer::Private 0033 { 0034 public: 0035 0036 explicit Private() 0037 : syncing(false) 0038 { 0039 } 0040 0041 bool syncing; 0042 }; 0043 0044 TableViewSelectionModelSyncer::TableViewSelectionModelSyncer(TableViewShared* const sharedObject, QObject* const parent) 0045 : QObject(parent), 0046 d (new Private()), 0047 s (sharedObject) 0048 { 0049 connect(s->imageFilterSelectionModel, SIGNAL(currentChanged(QModelIndex,QModelIndex)), 0050 this, SLOT(slotSourceCurrentChanged(QModelIndex,QModelIndex))); 0051 0052 connect(s->imageFilterSelectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), 0053 this, SLOT(slotSourceSelectionChanged(QItemSelection,QItemSelection))); 0054 0055 connect(s->tableViewSelectionModel, SIGNAL(currentChanged(QModelIndex,QModelIndex)), 0056 this, SLOT(slotTargetCurrentChanged(QModelIndex,QModelIndex))); 0057 0058 connect(s->tableViewSelectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), 0059 this, SLOT(slotTargetSelectionChanged(QItemSelection,QItemSelection))); 0060 0061 connect(s->tableViewModel, SIGNAL(columnsInserted(QModelIndex,int,int)), 0062 this, SLOT(slotTargetColumnsInserted(QModelIndex,int,int))); 0063 0064 connect(s->tableViewModel, SIGNAL(modelReset()), 0065 this, SLOT(slotTargetModelReset())); 0066 0067 connect(s->tableViewModel, SIGNAL(rowsInserted(QModelIndex,int,int)), 0068 this, SLOT(slotTargetModelRowsInserted(QModelIndex,int,int))); 0069 0070 /// @todo This is necessary to re-sync the selection when tags are added to images. 0071 /// Check whether both are necessary or whether we need more. 0072 0073 connect(s->imageFilterModel, SIGNAL(layoutChanged()), 0074 this, SLOT(slotSourceModelReset())); 0075 0076 connect(s->imageFilterModel, SIGNAL(modelReset()), 0077 this, SLOT(slotSourceModelReset())); 0078 0079 slotDoInitialSync(); 0080 } 0081 0082 TableViewSelectionModelSyncer::~TableViewSelectionModelSyncer() 0083 { 0084 } 0085 0086 QModelIndex TableViewSelectionModelSyncer::toSource(const QModelIndex& tableViewIndex) const 0087 { 0088 return s->tableViewModel->toItemFilterModelIndex(tableViewIndex); 0089 } 0090 0091 QModelIndex TableViewSelectionModelSyncer::toTarget(const QModelIndex& sourceIndex) const 0092 { 0093 return s->tableViewModel->fromItemFilterModelIndex(sourceIndex); 0094 } 0095 0096 int TableViewSelectionModelSyncer::targetModelColumnCount() const 0097 { 0098 return s->tableViewModel->columnCount(QModelIndex()); 0099 } 0100 0101 QItemSelection TableViewSelectionModelSyncer::targetIndexToRowItemSelection(const QModelIndex& targetIndex) const 0102 { 0103 const int row = targetIndex.row(); 0104 const QModelIndex topLeft = s->tableViewModel->index(row, 0, targetIndex.parent()); 0105 const QModelIndex bottomRight = s->tableViewModel->index(row, targetModelColumnCount()-1, targetIndex.parent()); 0106 const QItemSelection mySelection(topLeft, bottomRight); 0107 0108 return mySelection; 0109 } 0110 0111 void TableViewSelectionModelSyncer::slotDoInitialSync() 0112 { 0113 if (!s->isActive) 0114 { 0115 return; 0116 } 0117 0118 d->syncing = true; 0119 0120 s->tableViewSelectionModel->clearSelection(); 0121 0122 const QItemSelection sourceSelection = s->imageFilterSelectionModel->selection(); 0123 const QItemSelection targetSelection = itemSelectionToTarget(sourceSelection); 0124 s->tableViewSelectionModel->select(targetSelection, QItemSelectionModel::Select); 0125 0126 const QModelIndex targetIndexCurrent = toTarget(s->imageFilterSelectionModel->currentIndex()); 0127 s->tableViewSelectionModel->setCurrentIndex(targetIndexCurrent, QItemSelectionModel::NoUpdate); 0128 0129 d->syncing = false; 0130 } 0131 0132 void TableViewSelectionModelSyncer::slotSourceCurrentChanged(const QModelIndex& current, const QModelIndex& previous) 0133 { 0134 if (!s->isActive) 0135 { 0136 return; 0137 } 0138 0139 Q_UNUSED(previous) 0140 0141 if (d->syncing) 0142 { 0143 return; 0144 } 0145 0146 d->syncing = true; 0147 0148 // we have to select the whole row of the target index 0149 0150 const QModelIndex targetIndexCurrent = toTarget(current); 0151 0152 s->tableViewSelectionModel->setCurrentIndex(targetIndexCurrent, QItemSelectionModel::Select); 0153 0154 d->syncing = false; 0155 } 0156 0157 QItemSelection TableViewSelectionModelSyncer::itemSelectionToSource(const QItemSelection& selection) const 0158 { 0159 QItemSelection sourceSelection; 0160 0161 Q_FOREACH (const QItemSelectionRange& range, selection) 0162 { 0163 const int firstRow = range.top(); 0164 const int lastRow = range.bottom(); 0165 0166 for (int row = firstRow ; row <= lastRow ; ++row) 0167 { 0168 const QModelIndex tableViewIndex = s->tableViewModel->index(row, 0, range.parent()); 0169 const QModelIndex sourceIndex = s->tableViewModel->toItemFilterModelIndex(tableViewIndex); 0170 0171 if (sourceIndex.isValid()) 0172 { 0173 sourceSelection.select(sourceIndex, sourceIndex); 0174 } 0175 } 0176 } 0177 0178 return sourceSelection; 0179 } 0180 0181 QItemSelection TableViewSelectionModelSyncer::itemSelectionToTarget(const QItemSelection& selection) const 0182 { 0183 const int targetColumnCount = targetModelColumnCount(); 0184 QItemSelection targetSelection; 0185 0186 Q_FOREACH (const QItemSelectionRange& range, selection) 0187 { 0188 const int firstRow = range.top(); 0189 const int lastRow = range.bottom(); 0190 0191 for (int row = firstRow ; row <= lastRow ; ++row) 0192 { 0193 const QModelIndex sourceIndex = s->imageFilterModel->index(row, 0, range.parent()); 0194 const QModelIndex tableViewIndexTopLeft = s->tableViewModel->fromItemFilterModelIndex(sourceIndex); 0195 const QModelIndex tableViewIndexBottomRight = s->tableViewModel->index(tableViewIndexTopLeft.row(), 0196 targetColumnCount-1, 0197 tableViewIndexTopLeft.parent()); 0198 0199 targetSelection.select(tableViewIndexTopLeft, tableViewIndexBottomRight); 0200 } 0201 } 0202 0203 return targetSelection; 0204 } 0205 0206 void TableViewSelectionModelSyncer::slotSourceSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected) 0207 { 0208 if (!s->isActive) 0209 { 0210 return; 0211 } 0212 0213 if (d->syncing) 0214 { 0215 return; 0216 } 0217 0218 d->syncing = true; 0219 0220 const QItemSelection targetSelection = itemSelectionToTarget(selected); 0221 s->tableViewSelectionModel->select(targetSelection, QItemSelectionModel::Select); 0222 0223 const QItemSelection targetDeselection = itemSelectionToTarget(deselected); 0224 s->tableViewSelectionModel->select(targetDeselection, QItemSelectionModel::Deselect); 0225 0226 d->syncing = false; 0227 } 0228 0229 void TableViewSelectionModelSyncer::slotTargetCurrentChanged(const QModelIndex& current, const QModelIndex& previous) 0230 { 0231 if (!s->isActive) 0232 { 0233 return; 0234 } 0235 0236 Q_UNUSED(previous) 0237 0238 if (d->syncing) 0239 { 0240 return; 0241 } 0242 0243 d->syncing = true; 0244 0245 const QModelIndex sourceIndexCurrent = toSource(current); 0246 s->imageFilterSelectionModel->setCurrentIndex(sourceIndexCurrent, QItemSelectionModel::Select); 0247 0248 d->syncing = false; 0249 } 0250 0251 void TableViewSelectionModelSyncer::slotTargetSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected) 0252 { 0253 if (!s->isActive) 0254 { 0255 return; 0256 } 0257 0258 if (d->syncing) 0259 { 0260 return; 0261 } 0262 0263 d->syncing = true; 0264 0265 const QItemSelection sourceSelection = itemSelectionToSource(selected); 0266 s->imageFilterSelectionModel->select(sourceSelection, QItemSelectionModel::Select); 0267 0268 const QItemSelection sourceDeselection = itemSelectionToSource(deselected); 0269 s->imageFilterSelectionModel->select(sourceDeselection, QItemSelectionModel::Deselect); 0270 0271 d->syncing = false; 0272 } 0273 0274 void TableViewSelectionModelSyncer::slotSourceModelReset() 0275 { 0276 // QAbstractItemModel will also react to the modelReset signal 0277 // make sure we transfer the update after that. 0278 0279 QTimer::singleShot(0, this, SLOT(slotDoInitialSync())); 0280 } 0281 0282 void TableViewSelectionModelSyncer::slotTargetColumnsInserted(const QModelIndex& parent, int start, int end) 0283 { 0284 if (!s->isActive) 0285 { 0286 return; 0287 } 0288 0289 Q_UNUSED(parent) 0290 Q_UNUSED(start) 0291 Q_UNUSED(end) 0292 0293 if (d->syncing) 0294 { 0295 return; 0296 } 0297 0298 // New columns were inserted. We have to make sure that all selected rows include the new columns. 0299 // We just re-perform the initial synchronization. 0300 /// @todo There may be more efficient ways. 0301 0302 slotDoInitialSync(); 0303 } 0304 0305 void Digikam::TableViewSelectionModelSyncer::slotSetActive(const bool isActive) 0306 { 0307 if (isActive) 0308 { 0309 slotSourceModelReset(); 0310 } 0311 } 0312 0313 void Digikam::TableViewSelectionModelSyncer::slotTargetModelReset() 0314 { 0315 slotDoInitialSync(); 0316 } 0317 0318 void Digikam::TableViewSelectionModelSyncer::slotTargetModelRowsInserted(const QModelIndex& parent, int start, int end) 0319 { 0320 if (!s->isActive) 0321 { 0322 return; 0323 } 0324 0325 // look up the state of the source indexes and transfer them here 0326 0327 for (int i = start ; i <= end ; ++i) 0328 { 0329 const QModelIndex iTarget = s->tableViewModel->index(i, 0, parent); 0330 0331 if (!iTarget.isValid()) 0332 { 0333 continue; 0334 } 0335 0336 const QModelIndex iSource = toSource(iTarget); 0337 0338 if (!iSource.isValid()) 0339 { 0340 continue; 0341 } 0342 0343 if (s->imageFilterSelectionModel->isSelected(iSource)) 0344 { 0345 const QItemSelection targetSelection = targetIndexToRowItemSelection(iTarget); 0346 s->tableViewSelectionModel->select(targetSelection, QItemSelectionModel::Select); 0347 } 0348 } 0349 0350 // also transfer the current index if necessary 0351 0352 const QModelIndex sourceCurrentIndex = s->imageFilterSelectionModel->currentIndex(); 0353 const QModelIndex targetIndexCurrent = toTarget(sourceCurrentIndex); 0354 s->tableViewSelectionModel->setCurrentIndex(targetIndexCurrent, QItemSelectionModel::NoUpdate); 0355 } 0356 0357 } // namespace Digikam 0358 0359 #include "moc_tableview_selection_model_syncer.cpp"