File indexing completed on 2025-01-05 03:58:32

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2010-03-21
0007  * Description : A model to hold GPS information about items.
0008  *
0009  * SPDX-FileCopyrightText: 2010-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0010  * SPDX-FileCopyrightText: 2010      by Michael G. Hansen <mike at mghansen dot de>
0011  *
0012  * SPDX-License-Identifier: GPL-2.0-or-later
0013  *
0014  * ============================================================ */
0015 
0016 #include "gpsitemmodel.h"
0017 
0018 // Local includes
0019 
0020 #include "digikam_debug.h"
0021 
0022 namespace Digikam
0023 {
0024 
0025 class Q_DECL_HIDDEN GPSItemModel::Private
0026 {
0027 public:
0028 
0029     explicit Private()
0030       : items              (),
0031         columnCount        (0),
0032         thumbnailLoadThread(nullptr)
0033     {
0034     }
0035 
0036     QList<GPSItemContainer*>        items;
0037     int                             columnCount;
0038     QMap<QPair<int, int>, QVariant> headerData;
0039     ThumbnailLoadThread*            thumbnailLoadThread;
0040 };
0041 
0042 GPSItemModel::GPSItemModel(QObject* const parent)
0043     : QAbstractItemModel(parent),
0044       d                 (new Private)
0045 {
0046     d->thumbnailLoadThread = new ThumbnailLoadThread(this);
0047 
0048     connect(d->thumbnailLoadThread, SIGNAL(signalThumbnailLoaded(LoadingDescription,QPixmap)),
0049             this, SLOT(slotThumbnailLoaded(LoadingDescription,QPixmap)));
0050 }
0051 
0052 GPSItemModel::~GPSItemModel()
0053 {
0054     // TODO: send a signal before deleting the items?
0055 
0056     qDeleteAll(d->items);
0057     delete d;
0058 }
0059 
0060 int GPSItemModel::columnCount(const QModelIndex& /*parent*/) const
0061 {
0062     return d->columnCount;
0063 }
0064 
0065 QVariant GPSItemModel::data(const QModelIndex& index, int role) const
0066 {
0067     if (index.isValid())
0068     {
0069         Q_ASSERT(index.model() == this);
0070     }
0071 
0072     const int rowNumber = index.row();
0073 
0074     if ((rowNumber < 0) || (rowNumber >= d->items.count()))
0075     {
0076         return QVariant();
0077     }
0078 
0079     return d->items.at(rowNumber)->data(index.column(), role);
0080 }
0081 
0082 QModelIndex GPSItemModel::index(int row, int column, const QModelIndex& parent) const
0083 {
0084 /*
0085     qCDebug(DIGIKAM_GENERAL_LOG)<<row<<column<<parent;
0086 */
0087     if (parent.isValid())
0088     {
0089         // there are no child items, only top level items
0090 
0091         return QModelIndex();
0092     }
0093 
0094     if ( (column < 0)               ||
0095          (column >= d->columnCount) ||
0096          (row < 0)                  ||
0097          (row >= d->items.count())
0098        )
0099     {
0100         return QModelIndex();
0101     }
0102 
0103     return createIndex(row, column, (void*)nullptr);
0104 }
0105 
0106 QModelIndex GPSItemModel::parent(const QModelIndex& /*index*/) const
0107 {
0108     // we have only top level items
0109 
0110     return QModelIndex();
0111 }
0112 
0113 void GPSItemModel::addItem(GPSItemContainer* const newItem)
0114 {
0115     beginInsertRows(QModelIndex(), d->items.count(), d->items.count());
0116     newItem->setModel(this);
0117     d->items << newItem;
0118     endInsertRows();
0119 }
0120 
0121 void GPSItemModel::setColumnCount(const int nColumns)
0122 {
0123     Q_EMIT layoutAboutToBeChanged();
0124 
0125     d->columnCount = nColumns;
0126 
0127     Q_EMIT layoutChanged();
0128 }
0129 
0130 void GPSItemModel::itemChanged(GPSItemContainer* const changedItem)
0131 {
0132     const int itemIndex = d->items.indexOf(changedItem);
0133 
0134     if (itemIndex < 0)
0135     {
0136         return;
0137     }
0138 
0139     const QModelIndex itemModelIndexStart = createIndex(itemIndex, 0, (void*)nullptr);
0140     const QModelIndex itemModelIndexEnd   = createIndex(itemIndex, d->columnCount - 1, (void*)nullptr);
0141 
0142     Q_EMIT dataChanged(itemModelIndexStart, itemModelIndexEnd);
0143 }
0144 
0145 GPSItemContainer* GPSItemModel::itemFromIndex(const QModelIndex& index) const
0146 {
0147     if (index.isValid())
0148     {
0149         Q_ASSERT(index.model() == this);
0150     }
0151 
0152     if (!index.isValid())
0153     {
0154         return nullptr;
0155     }
0156 
0157     const int row = index.row();
0158 
0159     if ((row < 0) || (row >= d->items.count()))
0160     {
0161         return nullptr;
0162     }
0163 
0164     return d->items.at(row);
0165 }
0166 
0167 int GPSItemModel::rowCount(const QModelIndex& parent) const
0168 {
0169     if (parent.isValid())
0170     {
0171         return 0;
0172     }
0173 
0174     return d->items.count();
0175 }
0176 
0177 bool GPSItemModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant& value, int role)
0178 {
0179     if ((section >= d->columnCount) || (orientation != Qt::Horizontal))
0180     {
0181         return false;
0182     }
0183 
0184     const QPair<int, int> headerIndex = QPair<int, int>(section, role);
0185     d->headerData[headerIndex]        = value;
0186 
0187     return true;
0188 }
0189 
0190 QVariant GPSItemModel::headerData(int section, Qt::Orientation orientation, int role) const
0191 {
0192     if ((section >= d->columnCount) || (orientation != Qt::Horizontal))
0193     {
0194         return false;
0195     }
0196 
0197     const QPair<int, int> headerIndex = QPair<int, int>(section, role);
0198 
0199     return d->headerData.value(headerIndex);
0200 }
0201 
0202 bool GPSItemModel::setData(const QModelIndex& index, const QVariant& value, int role)
0203 {
0204     Q_UNUSED(index);
0205     Q_UNUSED(value);
0206     Q_UNUSED(role);
0207 
0208     return false;
0209 }
0210 
0211 Qt::ItemFlags GPSItemModel::flags(const QModelIndex& index) const
0212 {
0213     if (index.isValid())
0214     {
0215         Q_ASSERT(index.model() == this);
0216     }
0217 
0218     if (!index.isValid())
0219     {
0220         return Qt::NoItemFlags;
0221     }
0222 
0223     return (QAbstractItemModel::flags(index) | Qt::ItemIsDragEnabled);
0224 }
0225 
0226 GPSItemContainer* GPSItemModel::itemFromUrl(const QUrl& url) const
0227 {
0228     for (int i = 0 ; i < d->items.count() ; ++i)
0229     {
0230         if (d->items.at(i)->url() == url)
0231         {
0232             return d->items.at(i);
0233         }
0234     }
0235 
0236     return nullptr;
0237 }
0238 
0239 QModelIndex GPSItemModel::indexFromUrl(const QUrl& url) const
0240 {
0241     for (int i = 0 ; i < d->items.count() ; ++i)
0242     {
0243         if (d->items.at(i)->url() == url)
0244         {
0245             return index(i, 0, QModelIndex());
0246         }
0247     }
0248 
0249     return QModelIndex();
0250 }
0251 
0252 QPixmap GPSItemModel::getPixmapForIndex(const QPersistentModelIndex& itemIndex, const int size)
0253 {
0254     if (itemIndex.isValid())
0255     {
0256         Q_ASSERT(itemIndex.model() == this);
0257     }
0258 
0259     // TODO: should we cache the pixmap on our own here or does the interface usually cache it for us?
0260     // TODO: do we need to make sure we do not request the same pixmap twice in a row?
0261     // construct the key under which we stored the pixmap in the cache
0262 
0263     GPSItemContainer* const imageItem = itemFromIndex(itemIndex);
0264 
0265     if (!imageItem)
0266     {
0267         return QPixmap();
0268     }
0269 
0270     QPixmap thumbnail;
0271 
0272     if (d->thumbnailLoadThread->find(ThumbnailIdentifier(imageItem->url().toLocalFile()), thumbnail, size))
0273     {
0274         return thumbnail.copy(1, 1, thumbnail.size().width()-2, thumbnail.size().height()-2);
0275     }
0276 
0277     return QPixmap();
0278 }
0279 
0280 void GPSItemModel::slotThumbnailLoaded(const LoadingDescription& loadingDescription, const QPixmap& thumb)
0281 {
0282     if (thumb.isNull())
0283     {
0284         return;
0285     }
0286 
0287     const QModelIndex currentIndex = indexFromUrl(QUrl::fromLocalFile(loadingDescription.filePath));
0288 
0289     if (currentIndex.isValid())
0290     {
0291         QPersistentModelIndex goodIndex(currentIndex);
0292         Q_EMIT signalThumbnailForIndexAvailable(goodIndex, thumb.copy(1, 1, thumb.size().width()-2, thumb.size().height()-2));
0293     }
0294 }
0295 
0296 Qt::DropActions GPSItemModel::supportedDragActions() const
0297 {
0298     return Qt::CopyAction;
0299 }
0300 
0301 } // namespace Digikam
0302 
0303 #include "moc_gpsitemmodel.cpp"