File indexing completed on 2025-01-19 03:56:05
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2009-03-23 0007 * Description : Qt Model for Albums 0008 * 0009 * SPDX-FileCopyrightText: 2008-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de> 0010 * SPDX-FileCopyrightText: 2010 by Andi Clemens <andi dot clemens at gmail dot com> 0011 * SPDX-FileCopyrightText: 2012-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0012 * 0013 * SPDX-License-Identifier: GPL-2.0-or-later 0014 * 0015 * ============================================================ */ 0016 0017 #include "abstractalbummodel_p.h" 0018 0019 namespace Digikam 0020 { 0021 0022 class Q_DECL_HIDDEN AbstractCheckableAlbumModel::Private 0023 { 0024 public: 0025 0026 #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) 0027 0028 explicit Private() 0029 : extraFlags (), 0030 rootIsCheckable (true), 0031 addExcludeTristate (false), 0032 recursive (false), 0033 staticVectorContainingCheckStateRole(1, Qt::CheckStateRole) 0034 0035 { 0036 } 0037 0038 #else 0039 0040 explicit Private() 0041 : extraFlags (nullptr), 0042 rootIsCheckable (true), 0043 addExcludeTristate (false), 0044 recursive (false), 0045 staticVectorContainingCheckStateRole(1, Qt::CheckStateRole) 0046 0047 { 0048 } 0049 0050 #endif 0051 0052 Qt::ItemFlags extraFlags; 0053 bool rootIsCheckable; 0054 bool addExcludeTristate; 0055 bool recursive; 0056 QHash<Album*, Qt::CheckState> checkedAlbums; 0057 0058 QVector<int> staticVectorContainingCheckStateRole; 0059 }; 0060 0061 AbstractCheckableAlbumModel::AbstractCheckableAlbumModel(Album::Type albumType, 0062 Album* const rootAlbum, 0063 RootAlbumBehavior rootBehavior, 0064 QObject* const parent) 0065 : AbstractCountingAlbumModel(albumType, rootAlbum, rootBehavior, parent), 0066 d(new Private) 0067 { 0068 setup(); 0069 } 0070 0071 AbstractCheckableAlbumModel::~AbstractCheckableAlbumModel() 0072 { 0073 delete d; 0074 } 0075 0076 void AbstractCheckableAlbumModel::setCheckable(bool isCheckable) 0077 { 0078 if (isCheckable) 0079 { 0080 d->extraFlags |= Qt::ItemIsUserCheckable; 0081 } 0082 else 0083 { 0084 d->extraFlags &= ~Qt::ItemIsUserCheckable; 0085 resetCheckedAlbums(); 0086 } 0087 } 0088 0089 bool AbstractCheckableAlbumModel::isCheckable() const 0090 { 0091 return d->extraFlags & Qt::ItemIsUserCheckable; 0092 } 0093 0094 void AbstractCheckableAlbumModel::setRootCheckable(bool isCheckable) 0095 { 0096 d->rootIsCheckable = isCheckable; 0097 Album* const root = rootAlbum(); 0098 0099 if (!d->rootIsCheckable && root) 0100 { 0101 setChecked(root, false); 0102 } 0103 } 0104 0105 bool AbstractCheckableAlbumModel::rootIsCheckable() const 0106 { 0107 return (d->rootIsCheckable && isCheckable()); 0108 } 0109 0110 void AbstractCheckableAlbumModel::setTristate(bool isTristate) 0111 { 0112 if (isTristate) 0113 { 0114 d->extraFlags |= Qt::ItemIsAutoTristate; 0115 } 0116 else 0117 { 0118 d->extraFlags &= ~Qt::ItemIsAutoTristate; 0119 } 0120 } 0121 0122 bool AbstractCheckableAlbumModel::isTristate() const 0123 { 0124 return (d->extraFlags & Qt::ItemIsAutoTristate); 0125 } 0126 0127 void AbstractCheckableAlbumModel::setAddExcludeTristate(bool b) 0128 { 0129 d->addExcludeTristate = b; 0130 setCheckable(true); 0131 setTristate(b); 0132 } 0133 0134 bool AbstractCheckableAlbumModel::isAddExcludeTristate() const 0135 { 0136 return (d->addExcludeTristate && isTristate()); 0137 } 0138 0139 bool AbstractCheckableAlbumModel::isChecked(Album* album) const 0140 { 0141 return (d->checkedAlbums.value(album, Qt::Unchecked) == Qt::Checked); 0142 } 0143 0144 Qt::CheckState AbstractCheckableAlbumModel::checkState(Album* album) const 0145 { 0146 return (d->checkedAlbums.value(album, Qt::Unchecked)); 0147 } 0148 0149 void AbstractCheckableAlbumModel::setChecked(Album* album, bool isChecked) 0150 { 0151 setData(indexForAlbum(album), isChecked ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole); 0152 } 0153 0154 void AbstractCheckableAlbumModel::setCheckState(Album* album, Qt::CheckState state) 0155 { 0156 setData(indexForAlbum(album), state, Qt::CheckStateRole); 0157 } 0158 0159 void AbstractCheckableAlbumModel::toggleChecked(Album* album) 0160 { 0161 if (checkState(album) != Qt::PartiallyChecked) 0162 { 0163 setChecked(album, !isChecked(album)); 0164 } 0165 } 0166 0167 QList<Album*> AbstractCheckableAlbumModel::checkedAlbums() const 0168 { 0169 // return a list with all keys with value Qt::Checked 0170 0171 return d->checkedAlbums.keys(Qt::Checked); 0172 } 0173 0174 QList<Album*> AbstractCheckableAlbumModel::partiallyCheckedAlbums() const 0175 { 0176 // return a list with all keys with value Qt::PartiallyChecked 0177 0178 return d->checkedAlbums.keys(Qt::PartiallyChecked); 0179 } 0180 0181 void AbstractCheckableAlbumModel::setRecursive(bool recursive) 0182 { 0183 d->recursive = recursive; 0184 } 0185 0186 void AbstractCheckableAlbumModel::resetAllCheckedAlbums() 0187 { 0188 const QHash<Album*, Qt::CheckState> oldChecked = d->checkedAlbums; 0189 d->checkedAlbums.clear(); 0190 0191 for (QHash<Album*, Qt::CheckState>::const_iterator it = oldChecked.begin() ; 0192 it != oldChecked.end() ; ++it) 0193 { 0194 if (it.value() != Qt::Unchecked) 0195 { 0196 QModelIndex index = indexForAlbum(it.key()); 0197 Q_EMIT dataChanged(index, index, d->staticVectorContainingCheckStateRole); 0198 Q_EMIT checkStateChanged(it.key(), Qt::Unchecked); 0199 } 0200 } 0201 } 0202 0203 void AbstractCheckableAlbumModel::setDataForChildren(const QModelIndex& parent, const QVariant& value, int role) 0204 { 0205 for (int row = 0 ; row < rowCount(parent) ; ++row) 0206 { 0207 QModelIndex childIndex = index(row, 0, parent); 0208 setData(childIndex, value, role, true); 0209 } 0210 } 0211 0212 void AbstractCheckableAlbumModel::resetCheckedAlbums(const QModelIndex& parent) 0213 { 0214 if (parent == rootAlbumIndex()) 0215 { 0216 resetAllCheckedAlbums(); 0217 return; 0218 } 0219 0220 setDataForChildren(parent, Qt::Unchecked, Qt::CheckStateRole); 0221 } 0222 0223 void AbstractCheckableAlbumModel::setDataForParents(const QModelIndex& child, const QVariant& value, int role) 0224 { 0225 if (!child.isValid()) 0226 { 0227 return; 0228 } 0229 0230 QModelIndex current = parent(child); 0231 0232 while (current.isValid() && (current != rootAlbumIndex())) 0233 { 0234 setData(current, value, role, false); 0235 current = parent(current); 0236 } 0237 } 0238 0239 void AbstractCheckableAlbumModel::resetCheckedParentAlbums(const QModelIndex& child) 0240 { 0241 setDataForParents(child, Qt::Unchecked, Qt::CheckStateRole); 0242 } 0243 0244 void AbstractCheckableAlbumModel::checkAllParentAlbums(const QModelIndex& child) 0245 { 0246 setDataForParents(child, Qt::Checked, Qt::CheckStateRole); 0247 } 0248 0249 void AbstractCheckableAlbumModel::checkAllAlbums(const QModelIndex& parent) 0250 { 0251 setDataForChildren(parent, Qt::Checked, Qt::CheckStateRole); 0252 } 0253 0254 void AbstractCheckableAlbumModel::invertCheckedAlbums(const QModelIndex& parent) 0255 { 0256 Album* const album = albumForIndex(parent); 0257 0258 if (album) 0259 { 0260 toggleChecked(album); 0261 } 0262 0263 for (int row = 0 ; row < rowCount(parent) ; ++row) 0264 { 0265 invertCheckedAlbums(index(row, 0, parent)); 0266 } 0267 } 0268 0269 void AbstractCheckableAlbumModel::setCheckStateForChildren(Album* album, Qt::CheckState state) 0270 { 0271 QModelIndex index = indexForAlbum(album); 0272 setDataForChildren(index, state, Qt::CheckStateRole); 0273 } 0274 0275 void AbstractCheckableAlbumModel::setCheckStateForParents(Album* album, Qt::CheckState state) 0276 { 0277 QModelIndex index = indexForAlbum(album); 0278 setDataForParents(index, state, Qt::CheckStateRole); 0279 } 0280 0281 QVariant AbstractCheckableAlbumModel::albumData(Album* a, int role) const 0282 { 0283 if (role == Qt::CheckStateRole) 0284 { 0285 if ((d->extraFlags & Qt::ItemIsUserCheckable) && 0286 (!a->isRoot() || d->rootIsCheckable)) 0287 { 0288 // with Qt::Unchecked as default, albums not in the hash (initially all) 0289 // are simply regarded as unchecked 0290 0291 Qt::CheckState state = d->checkedAlbums.value(a, Qt::Unchecked); 0292 0293 if (d->addExcludeTristate) 0294 { 0295 // Use Qt::PartiallyChecked only internally, do not expose it to the TreeView 0296 0297 return ((state == Qt::Unchecked) ? Qt::Unchecked : Qt::Checked); 0298 } 0299 0300 return state; 0301 } 0302 } 0303 0304 return AbstractCountingAlbumModel::albumData(a, role); 0305 } 0306 0307 void AbstractCheckableAlbumModel::prepareAddExcludeDecoration(Album* a, QPixmap& icon) const 0308 { 0309 if (!d->addExcludeTristate) 0310 { 0311 return; 0312 } 0313 0314 Qt::CheckState state = checkState(a); 0315 0316 if (state != Qt::Unchecked) 0317 { 0318 int iconSize = qMax(icon.width(), icon.height()); 0319 int overlay_size = qMin(iconSize, qMax(16, iconSize * 2 / 3)); 0320 QPainter p(&icon); 0321 p.drawPixmap((icon.width() - overlay_size) / 2, 0322 (icon.height() - overlay_size) / 2, 0323 QIcon::fromTheme(state == Qt::PartiallyChecked ? QLatin1String("list-remove") 0324 : QLatin1String("list-add")).pixmap(overlay_size, overlay_size)); 0325 } 0326 } 0327 0328 Qt::ItemFlags AbstractCheckableAlbumModel::flags(const QModelIndex& index) const 0329 { 0330 Qt::ItemFlags extraFlags = d->extraFlags; 0331 0332 if (!d->rootIsCheckable) 0333 { 0334 QModelIndex root = rootAlbumIndex(); 0335 0336 if (root.isValid() && (index == root)) 0337 { 0338 extraFlags &= ~Qt::ItemIsUserCheckable; 0339 } 0340 } 0341 0342 return AbstractCountingAlbumModel::flags(index) | extraFlags; 0343 } 0344 0345 bool AbstractCheckableAlbumModel::setData(const QModelIndex& index, const QVariant& value, int role) 0346 { 0347 return setData(index, value, role, d->recursive); 0348 } 0349 0350 bool AbstractCheckableAlbumModel::setData(const QModelIndex& index, const QVariant& value, int role, bool recursive) 0351 { 0352 if (role == Qt::CheckStateRole) 0353 { 0354 Qt::CheckState state = (Qt::CheckState)value.toInt(); 0355 Album* const album = albumForIndex(index); 0356 0357 if (!album) 0358 { 0359 return false; 0360 } 0361 /* 0362 qCDebug(DIGIKAM_GENERAL_LOG) << "Updating check state for album" << album->title() << "to" << value; 0363 */ 0364 d->checkedAlbums.insert(album, state); 0365 Q_EMIT dataChanged(index, index); 0366 Q_EMIT checkStateChanged(album, state); 0367 0368 if (recursive) 0369 { 0370 setCheckStateForChildren(album, state); 0371 } 0372 0373 return true; 0374 } 0375 else 0376 { 0377 return AbstractCountingAlbumModel::setData(index, value, role); 0378 } 0379 } 0380 0381 void AbstractCheckableAlbumModel::albumCleared(Album* album) 0382 { 0383 // preserve check state if album is only being moved 0384 0385 if (!AlbumManager::instance()->isMovingAlbum(album)) 0386 { 0387 d->checkedAlbums.remove(album); 0388 } 0389 0390 AbstractCountingAlbumModel::albumCleared(album); 0391 } 0392 0393 void AbstractCheckableAlbumModel::allAlbumsCleared() 0394 { 0395 d->checkedAlbums.clear(); 0396 AbstractCountingAlbumModel::allAlbumsCleared(); 0397 } 0398 0399 } // namespace Digikam