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"