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"