diff --git a/core/app/items/views/digikamitemview.cpp b/core/app/items/views/digikamitemview.cpp index 3171c800f0..cbb64468de 100644 --- a/core/app/items/views/digikamitemview.cpp +++ b/core/app/items/views/digikamitemview.cpp @@ -1,611 +1,611 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-04-24 * Description : Qt model-view for items * * Copyright (C) 2009-2011 by Marcel Wiesweg * Copyright (C) 2009-2019 by Gilles Caulier * Copyright (C) 2011 by Andi Clemens * Copyright (C) 2013 by Michael G. Hansen * Copyright (C) 2014 by Mohamed_Anwer * Copyright (C) 2017 by Simon Frei * * This program is free software you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "digikamitemview_p.h" // Qt includes #include #include #include #include #include #include // Local includes #include "digikam_debug.h" #include "albummanager.h" #include "coredb.h" #include "coredboperationgroup.h" #include "advancedrenamedialog.h" #include "advancedrenameprocessdialog.h" #include "applicationsettings.h" #include "assignnameoverlay.h" #include "contextmenuhelper.h" #include "coredbaccess.h" #include "ddragobjects.h" #include "digikamapp.h" #include "digikamitemdelegate.h" #include "itemfacedelegate.h" #include "dio.h" #include "groupindicatoroverlay.h" #include "itemalbumfiltermodel.h" #include "itemalbummodel.h" #include "itemdragdrop.h" #include "itemratingoverlay.h" #include "itemfullscreenoverlay.h" #include "itemcoordinatesoverlay.h" #include "tagslineeditoverlay.h" #include "itemviewutilities.h" #include "imagewindow.h" #include "fileactionmngr.h" #include "fileactionprogress.h" #include "thumbnailloadthread.h" #include "tagregion.h" #include "addtagslineedit.h" #include "facerejectionoverlay.h" #include "facetagsiface.h" namespace Digikam { DigikamItemView::DigikamItemView(QWidget* const parent) : ItemCategorizedView(parent), d(new Private(this)) { installDefaultModels(); d->editPipeline.plugDatabaseEditor(); d->editPipeline.plugTrainer(); d->editPipeline.construct(); connect(&d->editPipeline, SIGNAL(scheduled()), this, SLOT(slotInitProgressIndicator())); d->normalDelegate = new DigikamItemDelegate(this); d->faceDelegate = new ItemFaceDelegate(this); setItemDelegate(d->normalDelegate); setSpacing(10); ApplicationSettings* const settings = ApplicationSettings::instance(); imageFilterModel()->setCategorizationMode(ItemSortSettings::CategoryByAlbum); imageAlbumModel()->setThumbnailLoadThread(ThumbnailLoadThread::defaultIconViewThread()); setThumbnailSize(ThumbnailSize(settings->getDefaultIconSize())); imageAlbumModel()->setPreloadThumbnails(true); imageModel()->setDragDropHandler(new ItemDragDropHandler(imageModel())); setDragEnabled(true); setAcceptDrops(true); setDropIndicatorShown(false); setToolTipEnabled(settings->showToolTipsIsValid()); imageFilterModel()->setStringTypeNatural(settings->isStringTypeNatural()); imageFilterModel()->setSortRole((ItemSortSettings::SortRole)settings->getImageSortOrder()); imageFilterModel()->setSortOrder((ItemSortSettings::SortOrder)settings->getImageSorting()); imageFilterModel()->setCategorizationMode((ItemSortSettings::CategorizationMode)settings->getImageSeparationMode()); imageFilterModel()->setCategorizationSortOrder((ItemSortSettings::SortOrder) settings->getImageSeparationSortOrder()); // selection overlay addSelectionOverlay(d->normalDelegate); addSelectionOverlay(d->faceDelegate); // rotation overlays d->rotateLeftOverlay = ItemRotateOverlay::left(this); d->rotateRightOverlay = ItemRotateOverlay::right(this); d->fullscreenOverlay = ItemFullScreenOverlay::instance(this); d->updateOverlays(); // rating overlay ItemRatingOverlay* const ratingOverlay = new ItemRatingOverlay(this); addOverlay(ratingOverlay); // face overlays // NOTE: order to plug this overlay is important, else rejection cant be suitable (see bug #324759). addAssignNameOverlay(d->faceDelegate); addRejectionOverlay(d->faceDelegate); GroupIndicatorOverlay* const groupOverlay = new GroupIndicatorOverlay(this); addOverlay(groupOverlay); addOverlay(new ItemCoordinatesOverlay(this)); connect(ratingOverlay, SIGNAL(ratingEdited(QList,int)), this, SLOT(assignRating(QList,int))); connect(groupOverlay, SIGNAL(toggleGroupOpen(QModelIndex)), this, SLOT(groupIndicatorClicked(QModelIndex))); connect(groupOverlay, SIGNAL(showButtonContextMenu(QModelIndex,QContextMenuEvent*)), this, SLOT(showGroupContextMenu(QModelIndex,QContextMenuEvent*))); d->utilities = new ItemViewUtilities(this); connect(imageModel()->dragDropHandler(), SIGNAL(assignTags(QList,QList)), FileActionMngr::instance(), SLOT(assignTags(QList,QList))); connect(imageModel()->dragDropHandler(), SIGNAL(addToGroup(ItemInfo,QList)), FileActionMngr::instance(), SLOT(addToGroup(ItemInfo,QList))); connect(imageModel()->dragDropHandler(), SIGNAL(dragDropSort(ItemInfo,QList)), this, SLOT(dragDropSort(ItemInfo,QList))); connect(d->utilities, SIGNAL(editorCurrentUrlChanged(QUrl)), this, SLOT(setCurrentUrlWhenAvailable(QUrl))); - // --- NOTE: use dynamic binding as slotSetupChenged() is a virtual method which can be re-implemented in derived classes. + // --- NOTE: use dynamic binding as slotSetupChanged() is a virtual method which can be re-implemented in derived classes. connect(settings, &ApplicationSettings::setupChanged, this, &DigikamItemView::slotSetupChanged); this->slotSetupChanged(); // --- } DigikamItemView::~DigikamItemView() { delete d; } ItemViewUtilities* DigikamItemView::utilities() const { return d->utilities; } void DigikamItemView::setThumbnailSize(const ThumbnailSize& size) { imageThumbnailModel()->setPreloadThumbnailSize(size); ItemCategorizedView::setThumbnailSize(size); } ItemInfoList DigikamItemView::allItemInfos(bool grouping) const { if (grouping) { return resolveGrouping(ItemCategorizedView::allItemInfos()); } return ItemCategorizedView::allItemInfos(); } ItemInfoList DigikamItemView::selectedItemInfos(bool grouping) const { if (grouping) { return resolveGrouping(ItemCategorizedView::selectedItemInfos()); } return ItemCategorizedView::selectedItemInfos(); } ItemInfoList DigikamItemView::selectedItemInfosCurrentFirst(bool grouping) const { if (grouping) { return resolveGrouping(ItemCategorizedView::selectedItemInfosCurrentFirst()); } return ItemCategorizedView::selectedItemInfosCurrentFirst(); } void DigikamItemView::dragDropSort(const ItemInfo& pick, const QList& infos) { if (pick.isNull() || infos.isEmpty()) { return; } ItemInfoList infoList = allItemInfos(false); qlonglong counter = pick.manualOrder(); bool order = (ApplicationSettings::instance()-> getImageSorting() == Qt::AscendingOrder); bool found = false; QApplication::setOverrideCursor(Qt::WaitCursor); CoreDbOperationGroup group; group.setMaximumTime(200); foreach (ItemInfo info, infoList) { if (!found && info.id() == pick.id()) { foreach (ItemInfo dropInfo, infos) { dropInfo.setManualOrder(counter); counter += (order ? 1 : -1); } info.setManualOrder(counter); found = true; } else if (found && !infos.contains(info)) { if (( order && info.manualOrder() > counter) || (!order && info.manualOrder() < counter)) { break; } counter += (order ? 100 : -100); info.setManualOrder(counter); } group.allowLift(); } QApplication::restoreOverrideCursor(); imageFilterModel()->invalidate(); } bool DigikamItemView::allNeedGroupResolving(const ApplicationSettings::OperationType type) const { return needGroupResolving(type, allItemInfos()); } bool DigikamItemView::selectedNeedGroupResolving(const ApplicationSettings::OperationType type) const { return needGroupResolving(type, selectedItemInfos()); } int DigikamItemView::fitToWidthIcons() { return delegate()->calculatethumbSizeToFit(viewport()->size().width()); } void DigikamItemView::slotSetupChanged() { imageFilterModel()->setStringTypeNatural(ApplicationSettings::instance()->isStringTypeNatural()); setToolTipEnabled(ApplicationSettings::instance()->showToolTipsIsValid()); setFont(ApplicationSettings::instance()->getIconViewFont()); d->updateOverlays(); ItemCategorizedView::slotSetupChanged(); } bool DigikamItemView::hasHiddenGroupedImages(const ItemInfo& info) const { return (info.hasGroupedImages() && !imageFilterModel()->isAllGroupsOpen() && !imageFilterModel()->isGroupOpen(info.id())); } ItemInfoList DigikamItemView::imageInfos(const QList& indexes, ApplicationSettings::OperationType type) const { ItemInfoList infos = ItemCategorizedView::imageInfos(indexes); if (needGroupResolving(type, infos)) { return resolveGrouping(infos); } return infos; } bool DigikamItemView::getFaceMode() const { return d->faceMode; } void DigikamItemView::setFaceMode(bool on) { d->faceMode = on; if (on) { // See ItemLister, which creates a search the implements listing tag in the ioslave imageAlbumModel()->setSpecialTagListing(QLatin1String("faces")); setItemDelegate(d->faceDelegate); // grouping is not very much compatible with faces imageFilterModel()->setAllGroupsOpen(true); } else { imageAlbumModel()->setSpecialTagListing(QString()); setItemDelegate(d->normalDelegate); bool open = ApplicationSettings::instance()->getAllGroupsOpen(); imageFilterModel()->setAllGroupsOpen(open); } } void DigikamItemView::addRejectionOverlay(ItemDelegate* delegate) { FaceRejectionOverlay* const rejectionOverlay = new FaceRejectionOverlay(this); connect(rejectionOverlay, SIGNAL(rejectFaces(QList)), this, SLOT(removeFaces(QList))); addOverlay(rejectionOverlay, delegate); } /* void DigikamItemView::addTagEditOverlay(ItemDelegate* delegate) { TagsLineEditOverlay* tagOverlay = new TagsLineEditOverlay(this); connect(tagOverlay, SIGNAL(tagEdited(QModelIndex,QString)), this, SLOT(assignTag(QModelIndex,QString))); addOverlay(tagOverlay, delegate); } */ void DigikamItemView::addAssignNameOverlay(ItemDelegate* delegate) { AssignNameOverlay* const nameOverlay = new AssignNameOverlay(this); addOverlay(nameOverlay, delegate); connect(nameOverlay, SIGNAL(confirmFaces(QList,int)), this, SLOT(confirmFaces(QList,int))); connect(nameOverlay, SIGNAL(removeFaces(QList)), this, SLOT(removeFaces(QList))); } void DigikamItemView::confirmFaces(const QList& indexes, int tagId) { QList infos; QList faces; QList sourceIndexes; // fast-remove in the "unknown person" view Album* const album = currentAlbum(); bool needFastRemove = false; if (album) { needFastRemove = (d->faceMode && (tagId != album->id())); } foreach (const QModelIndex& index, indexes) { infos << ItemModel::retrieveItemInfo(index); faces << d->faceDelegate->face(index); if (needFastRemove) { sourceIndexes << imageSortFilterModel()->mapToSourceItemModel(index); } } imageAlbumModel()->removeIndexes(sourceIndexes); for (int i = 0 ; i < infos.size() ; ++i) { d->editPipeline.confirm(infos[i], faces[i], tagId); } } void DigikamItemView::removeFaces(const QList& indexes) { QList infos; QList faces; QList sourceIndexes; foreach (const QModelIndex& index, indexes) { infos << ItemModel::retrieveItemInfo(index); faces << d->faceDelegate->face(index); sourceIndexes << imageSortFilterModel()->mapToSourceItemModel(index); } imageAlbumModel()->removeIndexes(sourceIndexes); for (int i = 0 ; i < infos.size() ; ++i) { d->editPipeline.remove(infos[i], faces[i]); } } void DigikamItemView::activated(const ItemInfo& info, Qt::KeyboardModifiers modifiers) { if (info.isNull()) { return; } if (modifiers != Qt::MetaModifier) { if (ApplicationSettings::instance()->getItemLeftClickAction() == ApplicationSettings::ShowPreview) { emit previewRequested(info); } else { openFile(info); } } else { d->utilities->openInfosWithDefaultApplication(QList() << info); } } void DigikamItemView::showContextMenuOnInfo(QContextMenuEvent* event, const ItemInfo& info) { emit signalShowContextMenuOnInfo(event, info, QList(), imageFilterModel()); } void DigikamItemView::showGroupContextMenu(const QModelIndex& index, QContextMenuEvent* event) { Q_UNUSED(index); emit signalShowGroupContextMenu(event, selectedItemInfosCurrentFirst(), imageFilterModel()); } void DigikamItemView::showContextMenu(QContextMenuEvent* event) { emit signalShowContextMenu(event); } void DigikamItemView::openFile(const ItemInfo& info) { d->utilities->openInfos(info, allItemInfos(), currentAlbum()); } void DigikamItemView::deleteSelected(const ItemViewUtilities::DeleteMode deleteMode) { ItemInfoList imageInfoList = selectedItemInfos(true); if (d->utilities->deleteImages(imageInfoList, deleteMode)) { awayFromSelection(); } } void DigikamItemView::deleteSelectedDirectly(const ItemViewUtilities::DeleteMode deleteMode) { ItemInfoList imageInfoList = selectedItemInfos(true); d->utilities->deleteImagesDirectly(imageInfoList, deleteMode); awayFromSelection(); } void DigikamItemView::assignRating(const QList& indexes, int rating) { ItemInfoList infos = imageInfos(indexes, ApplicationSettings::Metadata); FileActionMngr::instance()->assignRating(infos, rating); } void DigikamItemView::groupIndicatorClicked(const QModelIndex& index) { ItemInfo info = imageFilterModel()->imageInfo(index); if (info.isNull()) { return; } setCurrentIndex(index); imageFilterModel()->toggleGroupOpen(info.id()); imageAlbumModel()->ensureHasGroupedImages(info); } void DigikamItemView::rename() { ItemInfoList infos = selectedItemInfos(); if (needGroupResolving(ApplicationSettings::Rename, infos)) { infos = resolveGrouping(infos); } QList urls = infos.toImageUrlList(); bool loop = false; NewNamesList newNamesList; do { qCDebug(DIGIKAM_GENERAL_LOG) << "Selected URLs to rename: " << urls; QPointer dlg = new AdvancedRenameDialog(this); dlg->slotAddImages(urls); if (dlg->exec() != QDialog::Accepted) { delete dlg; break; } if (!loop) { QUrl nextUrl = nextInOrder(infos.last(), 1).fileUrl(); setCurrentUrl(nextUrl); loop = true; } newNamesList = dlg->newNames(); delete dlg; setFocus(); qApp->processEvents(); if (!newNamesList.isEmpty()) { QPointer dlg2 = new AdvancedRenameProcessDialog(newNamesList, this); dlg2->exec(); imageFilterModel()->invalidate(); urls = dlg2->failedUrls(); delete dlg2; } } while (!urls.isEmpty() && !newNamesList.isEmpty()); } void DigikamItemView::slotRotateLeft(const QList& indexes) { ItemInfoList infos = imageInfos(indexes, ApplicationSettings::Metadata); FileActionMngr::instance()->transform(infos, MetaEngineRotation::Rotate270); } void DigikamItemView::slotRotateRight(const QList& indexes) { ItemInfoList infos = imageInfos(indexes, ApplicationSettings::Metadata); FileActionMngr::instance()->transform(infos, MetaEngineRotation::Rotate90); } void DigikamItemView::slotFullscreen(const QList& indexes) { QList infos = imageInfos(indexes, ApplicationSettings::Slideshow); if (infos.isEmpty()) { return; } // Just fullscreen the first. const ItemInfo& info = infos.at(0); emit fullscreenRequested(info); } void DigikamItemView::slotInitProgressIndicator() { if (!ProgressManager::instance()->findItembyId(QLatin1String("FaceActionProgress"))) { FileActionProgress* const item = new FileActionProgress(QLatin1String("FaceActionProgress")); connect(&d->editPipeline, SIGNAL(started(QString)), item, SLOT(slotProgressStatus(QString))); connect(&d->editPipeline, SIGNAL(progressValueChanged(float)), item, SLOT(slotProgressValue(float))); connect(&d->editPipeline, SIGNAL(finished()), item, SLOT(slotCompleted())); } } } // namespace Digikam diff --git a/core/libs/models/abstractalbummodel.cpp b/core/libs/models/abstractalbummodel.cpp index e694e7b832..9587764bd7 100644 --- a/core/libs/models/abstractalbummodel.cpp +++ b/core/libs/models/abstractalbummodel.cpp @@ -1,1221 +1,1226 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-03-23 * Description : Qt Model for Albums * * Copyright (C) 2008-2011 by Marcel Wiesweg * Copyright (C) 2010 by Andi Clemens + * Copyright (C) 2012-2019 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "abstractalbummodel.h" // Qt includes #include #include #include // KDE includes #include // Local includes #include "digikam_debug.h" #include "albummanager.h" #include "albummodeldragdrophandler.h" #include "albumthumbnailloader.h" namespace Digikam { class Q_DECL_HIDDEN AbstractAlbumModel::Private { public: explicit Private() : rootAlbum(nullptr), addingAlbum(nullptr), type(Album::PHYSICAL), dragDropHandler(nullptr), rootBehavior(AbstractAlbumModel::IncludeRootAlbum), removingAlbum(0), itemDrag(true), itemDrop(true) { } Album* rootAlbum; Album* addingAlbum; Album::Type type; AlbumModelDragDropHandler* dragDropHandler; AbstractAlbumModel::RootAlbumBehavior rootBehavior; quintptr removingAlbum; bool itemDrag; bool itemDrop; }; AbstractAlbumModel::AbstractAlbumModel(Album::Type albumType, Album* const rootAlbum, RootAlbumBehavior rootBehavior, QObject* const parent) : QAbstractItemModel(parent), d(new Private) { d->type = albumType; d->rootAlbum = rootAlbum; d->rootBehavior = rootBehavior; - connect(AlbumManager::instance(), SIGNAL(signalAlbumAboutToBeAdded(Album*,Album*,Album*)), - this, SLOT(slotAlbumAboutToBeAdded(Album*,Album*,Album*))); + // --- NOTE: use dynamic binding as all slots above are virtual methods which can be re-implemented in derived classes. - connect(AlbumManager::instance(), SIGNAL(signalAlbumAdded(Album*)), - this, SLOT(slotAlbumAdded(Album*))); + connect(AlbumManager::instance(), &AlbumManager::signalAlbumAboutToBeAdded, + this, &AbstractAlbumModel::slotAlbumAboutToBeAdded); - connect(AlbumManager::instance(), SIGNAL(signalAlbumAboutToBeDeleted(Album*)), - this, SLOT(slotAlbumAboutToBeDeleted(Album*))); + connect(AlbumManager::instance(), &AlbumManager::signalAlbumAdded, + this, &AbstractAlbumModel::slotAlbumAdded); + + connect(AlbumManager::instance(), &AlbumManager::signalAlbumAboutToBeDeleted, + this, &AbstractAlbumModel::slotAlbumAboutToBeDeleted); connect(AlbumManager::instance(), &AlbumManager::signalAlbumHasBeenDeleted, this, &AbstractAlbumModel::slotAlbumHasBeenDeleted); - connect(AlbumManager::instance(), SIGNAL(signalAlbumsCleared()), - this, SLOT(slotAlbumsCleared())); + connect(AlbumManager::instance(), &AlbumManager::signalAlbumsCleared, + this, &AbstractAlbumModel::slotAlbumsCleared); + + connect(AlbumManager::instance(), &AlbumManager::signalAlbumIconChanged, + this, &AbstractAlbumModel::slotAlbumIconChanged); - connect(AlbumManager::instance(), SIGNAL(signalAlbumIconChanged(Album*)), - this, SLOT(slotAlbumIconChanged(Album*))); + connect(AlbumManager::instance(), &AlbumManager::signalAlbumRenamed, + this, &AbstractAlbumModel::slotAlbumRenamed); - connect(AlbumManager::instance(), SIGNAL(signalAlbumRenamed(Album*)), - this, SLOT(slotAlbumRenamed(Album*))); + // --- } AbstractAlbumModel::~AbstractAlbumModel() { delete d; } QVariant AbstractAlbumModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) { return QVariant(); } Album* const a = static_cast(index.internalPointer()); return albumData(a, role); } QVariant AbstractAlbumModel::albumData(Album* a, int role) const { switch (role) { case Qt::DisplayRole: return a->title(); case Qt::ToolTipRole: return a->title(); case Qt::DecorationRole: // reimplement in subclasses return decorationRoleData(a); case AlbumTitleRole: return a->title(); case AlbumTypeRole: return a->type(); case AlbumPointerRole: return QVariant::fromValue(a); case AlbumIdRole: return a->id(); case AlbumGlobalIdRole: return a->globalID(); case AlbumSortRole: // reimplement in subclass return sortRoleData(a); default: return QVariant(); } } QVariant AbstractAlbumModel::headerData(int section, Qt::Orientation orientation, int role) const { Q_UNUSED(orientation) if (section == 0 && role == Qt::DisplayRole) { return columnHeader(); } return QVariant(); } int AbstractAlbumModel::rowCount(const QModelIndex& parent) const { if (parent.isValid()) { Album* const a = static_cast(parent.internalPointer()); return a->childCount(); } else { if (!d->rootAlbum) { return 0; } if (d->rootBehavior == IncludeRootAlbum) { return 1; } else { return d->rootAlbum->childCount(); } } } int AbstractAlbumModel::columnCount(const QModelIndex& /*parent*/) const { return 1; } Qt::ItemFlags AbstractAlbumModel::flags(const QModelIndex& index) const { if (!index.isValid()) { return nullptr; } Album* const a = static_cast(index.internalPointer()); return itemFlags(a); } bool AbstractAlbumModel::hasChildren(const QModelIndex& parent) const { if (parent.isValid()) { Album* const a = static_cast(parent.internalPointer()); return a->firstChild(); } else { if (!d->rootAlbum) { return false; } if (d->rootBehavior == IncludeRootAlbum) { return 1; } else { return d->rootAlbum->firstChild(); } } } QModelIndex AbstractAlbumModel::index(int row, int column, const QModelIndex& parent) const { if (column != 0 || row < 0) { return QModelIndex(); } if (parent.isValid()) { Album* const parentAlbum = static_cast(parent.internalPointer()); Album* const a = parentAlbum->childAtRow(row); if (a) { return createIndex(row, column, a); } } else { if (!d->rootAlbum) { return QModelIndex(); } if (d->rootBehavior == IncludeRootAlbum) { if (row == 0) { return createIndex(0, 0, d->rootAlbum); } } else { Album* const a = d->rootAlbum->childAtRow(row); if (a) { return createIndex(row, column, a); } } } return QModelIndex(); } QModelIndex AbstractAlbumModel::parent(const QModelIndex& index) const { if (index.isValid()) { Album* const a = static_cast(index.internalPointer()); return indexForAlbum(a->parent()); } return QModelIndex(); } Qt::DropActions AbstractAlbumModel::supportedDropActions() const { return Qt::CopyAction|Qt::MoveAction; } QStringList AbstractAlbumModel::mimeTypes() const { if (d->dragDropHandler) { return d->dragDropHandler->mimeTypes(); } return QStringList(); } bool AbstractAlbumModel::dropMimeData(const QMimeData*, Qt::DropAction, int, int, const QModelIndex&) { // we require custom solutions return false; } QMimeData* AbstractAlbumModel::mimeData(const QModelIndexList& indexes) const { if (!d->dragDropHandler) { return nullptr; } QList albums; foreach (const QModelIndex& index, indexes) { Album* const a = albumForIndex(index); if (a) { albums << a; } } return d->dragDropHandler->createMimeData(albums); } void AbstractAlbumModel::setEnableDrag(bool enable) { d->itemDrag = enable; } void AbstractAlbumModel::setEnableDrop(bool enable) { d->itemDrop = enable; } void AbstractAlbumModel::setDragDropHandler(AlbumModelDragDropHandler* handler) { d->dragDropHandler = handler; } AlbumModelDragDropHandler* AbstractAlbumModel::dragDropHandler() const { return d->dragDropHandler; } QModelIndex AbstractAlbumModel::indexForAlbum(Album* a) const { if (!a) { return QModelIndex(); } if (!filterAlbum(a)) { return QModelIndex(); } // a is root album? Decide on root behavior if (a == d->rootAlbum) { if (d->rootBehavior == IncludeRootAlbum) { // create top-level indexes return createIndex(0, 0, a); } else { // with this behavior, root album has no valid index return QModelIndex(); } } // Normal album. Get its row. return createIndex(a->rowFromAlbum(), 0, a); } Album* AbstractAlbumModel::albumForIndex(const QModelIndex& index) const { return (static_cast(index.internalPointer())); } Album* AbstractAlbumModel::retrieveAlbum(const QModelIndex& index) { return (index.data(AbstractAlbumModel::AlbumPointerRole).value()); } Album* AbstractAlbumModel::rootAlbum() const { return d->rootAlbum; } QModelIndex AbstractAlbumModel::rootAlbumIndex() const { return indexForAlbum(d->rootAlbum); } AbstractAlbumModel::RootAlbumBehavior AbstractAlbumModel::rootAlbumBehavior() const { return d->rootBehavior; } Album::Type AbstractAlbumModel::albumType() const { return d->type; } QVariant AbstractAlbumModel::decorationRoleData(Album*) const { return QVariant(); } QVariant AbstractAlbumModel::sortRoleData(Album* a) const { return a->title(); } QString AbstractAlbumModel::columnHeader() const { return i18n("Album"); } Qt::ItemFlags AbstractAlbumModel::itemFlags(Album*) const { Qt::ItemFlags f = Qt::ItemIsSelectable | Qt::ItemIsEnabled; if (d->itemDrag) { f |= Qt::ItemIsDragEnabled; } if (d->itemDrop) { f |= Qt::ItemIsDropEnabled; } return f; } bool AbstractAlbumModel::filterAlbum(Album* album) const { return album && album->type() == d->type; } void AbstractAlbumModel::slotAlbumAboutToBeAdded(Album* album, Album* parent, Album* prev) { if (!filterAlbum(album)) { return; } if (album->isRoot() && d->rootBehavior == IgnoreRootAlbum) { d->rootAlbum = album; return; } // start inserting operation int row = prev ? prev->rowFromAlbum()+1 : 0; QModelIndex parentIndex = indexForAlbum(parent); beginInsertRows(parentIndex, row, row); // The root album will become available in time // when the model is instantiated before albums are initialized. // Set d->rootAlbum only after if (album->isRoot() && !d->rootAlbum) { d->rootAlbum = album; } // store album for slotAlbumAdded d->addingAlbum = album; } void AbstractAlbumModel::slotAlbumAdded(Album* album) { if (d->addingAlbum == album) { bool isRoot = (d->addingAlbum == d->rootAlbum); d->addingAlbum = nullptr; endInsertRows(); if (isRoot) { emit rootAlbumAvailable(); } } } void AbstractAlbumModel::slotAlbumAboutToBeDeleted(Album* album) { if (!filterAlbum(album)) { return; } if (album->isRoot() && d->rootBehavior == IgnoreRootAlbum) { albumCleared(album); d->rootAlbum = nullptr; return; } // begin removing operation int row = album->rowFromAlbum(); QModelIndex parent = indexForAlbum(album->parent()); beginRemoveRows(parent, row, row); albumCleared(album); // store album for slotAlbumHasBeenDeleted d->removingAlbum = reinterpret_cast(album); } void AbstractAlbumModel::slotAlbumHasBeenDeleted(quintptr p) { if (d->removingAlbum == p) { d->removingAlbum = 0; endRemoveRows(); } } void AbstractAlbumModel::slotAlbumsCleared() { d->rootAlbum = nullptr; beginResetModel(); allAlbumsCleared(); endResetModel(); } void AbstractAlbumModel::slotAlbumIconChanged(Album* album) { if (!filterAlbum(album)) { return; } QModelIndex index = indexForAlbum(album); emit dataChanged(index, index); } void AbstractAlbumModel::slotAlbumRenamed(Album* album) { if (!filterAlbum(album)) { return; } QModelIndex index = indexForAlbum(album); emit dataChanged(index, index); } // ------------------------------------------------------------------ AbstractSpecificAlbumModel::AbstractSpecificAlbumModel(Album::Type albumType, Album* const rootAlbum, RootAlbumBehavior rootBehavior, QObject* const parent) : AbstractAlbumModel(albumType, rootAlbum, rootBehavior, parent) { } void AbstractSpecificAlbumModel::setupThumbnailLoading() { AlbumThumbnailLoader* const loader = AlbumThumbnailLoader::instance(); connect(loader, SIGNAL(signalThumbnail(Album*,QPixmap)), this, SLOT(slotGotThumbnailFromIcon(Album*,QPixmap))); connect(loader, SIGNAL(signalFailed(Album*)), this, SLOT(slotThumbnailLost(Album*))); connect(loader, SIGNAL(signalReloadThumbnails()), this, SLOT(slotReloadThumbnails())); } QString AbstractSpecificAlbumModel::columnHeader() const { return m_columnHeader; } void AbstractSpecificAlbumModel::setColumnHeader(const QString& header) { m_columnHeader = header; emit headerDataChanged(Qt::Horizontal, 0, 0); } void AbstractSpecificAlbumModel::slotGotThumbnailFromIcon(Album* album, const QPixmap&) { // see decorationRole() method of subclasses if (!filterAlbum(album)) { return; } QModelIndex index = indexForAlbum(album); emit dataChanged(index, index); } void AbstractSpecificAlbumModel::slotThumbnailLost(Album*) { // ignore, use default thumbnail } void AbstractSpecificAlbumModel::slotReloadThumbnails() { // emit dataChanged() for all albums emitDataChangedForChildren(rootAlbum()); } void AbstractSpecificAlbumModel::emitDataChangedForChildren(Album* album) { if (!album) { return; } for (Album* child = album->firstChild(); child; child = child->next()) { if (filterAlbum(child)) { // recurse to children of children emitDataChangedForChildren(child); // emit signal for child QModelIndex index = indexForAlbum(child); emit dataChanged(index, index); } } } // ------------------------------------------------------------------ class Q_DECL_HIDDEN AbstractCountingAlbumModel::Private { public: explicit Private() { showCount = false; } bool showCount; QMap countMap; QHash countHashReady; QSet includeChildrenAlbums; }; AbstractCountingAlbumModel::AbstractCountingAlbumModel(Album::Type albumType, Album* const rootAlbum, RootAlbumBehavior rootBehavior, QObject* const parent) : AbstractSpecificAlbumModel(albumType, rootAlbum, rootBehavior, parent), d(new Private) { } void AbstractCountingAlbumModel::setup() { connect(AlbumManager::instance(), SIGNAL(signalAlbumMoved(Album*)), this, SLOT(slotAlbumMoved(Album*))); } AbstractCountingAlbumModel::~AbstractCountingAlbumModel() { delete d; } void AbstractCountingAlbumModel::setShowCount(bool show) { if (d->showCount != show) { d->showCount = show; emitDataChangedForChildren(rootAlbum()); } } bool AbstractCountingAlbumModel::showCount() const { return d->showCount; } void AbstractCountingAlbumModel::excludeChildrenCount(const QModelIndex& index) { Album* const album = albumForIndex(index); if (!album) { return; } d->includeChildrenAlbums.remove(album->id()); updateCount(album); } void AbstractCountingAlbumModel::includeChildrenCount(const QModelIndex& index) { Album* const album = albumForIndex(index); if (!album) { return; } d->includeChildrenAlbums << album->id(); updateCount(album); } void AbstractCountingAlbumModel::setCountMap(const QMap& idCountMap) { d->countMap = idCountMap; QMap::const_iterator it = d->countMap.constBegin(); for (; it != d->countMap.constEnd(); ++it) { updateCount(albumForId(it.key())); } } void AbstractCountingAlbumModel::updateCount(Album* album) { if (!album) { return; } // if the model does not contain the album, do nothing. QModelIndex index = indexForAlbum(album); if (!index.isValid()) { return; } QHash::iterator includeIt = d->countHashReady.find(album->id()); bool changed = false; // get count for album without children int count = d->countMap.value(album->id()); // if wanted, add up children's counts if (d->includeChildrenAlbums.contains(album->id())) { AlbumIterator it(album); while ( it.current() ) { count += d->countMap.value((*it)->id()); ++it; } } // insert or update if (includeIt == d->countHashReady.end()) { changed = true; d->countHashReady[album->id()] = count; } else { changed = (includeIt.value() != count); includeIt.value() = count; } // notify views if (changed) { emit dataChanged(index, index); } } void AbstractCountingAlbumModel::setCount(Album* album, int count) { if (!album) { return; } // if the model does not contain the album, do nothing. QModelIndex index = indexForAlbum(album); if (!index.isValid()) { return; } QHash::iterator includeIt = d->countHashReady.find(album->id()); bool changed = false; // insert or update if (includeIt == d->countHashReady.end()) { changed = true; d->countHashReady[album->id()] = count; } else { changed = (includeIt.value() != count); includeIt.value() = count; } // notify views if (changed) { emit dataChanged(index, index); } } QVariant AbstractCountingAlbumModel::albumData(Album* album, int role) const { if (role == Qt::DisplayRole && d->showCount && !album->isRoot()) { if (album->isTrashAlbum()) { PAlbum* const palbum = AlbumManager::instance()->findPAlbum(album->parent()->id()); if (palbum) { QString path = palbum->folderPath(); path.append(QLatin1String(".dtrash/files")); QDir dir(path, QLatin1String(""), QDir::Unsorted, QDir::Files); return QString::fromUtf8("%1 (%2)").arg(albumName(album)).arg(dir.count()); } } else { QHash::const_iterator it = d->countHashReady.constFind(album->id()); if (it != d->countHashReady.constEnd()) { return QString::fromUtf8("%1 (%2)").arg(albumName(album)).arg(it.value()); } } } return AbstractSpecificAlbumModel::albumData(album, role); } int AbstractCountingAlbumModel::albumCount(Album* album) const { QHash::const_iterator it = d->countHashReady.constFind(album->id()); if (it != d->countHashReady.constEnd()) { return it.value(); } return -1; } QString AbstractCountingAlbumModel::albumName(Album* album) const { return album->title(); } void AbstractCountingAlbumModel::albumCleared(Album* album) { if (!AlbumManager::instance()->isMovingAlbum(album)) { d->countMap.remove(album->id()); d->countHashReady.remove(album->id()); d->includeChildrenAlbums.remove(album->id()); } } void AbstractCountingAlbumModel::allAlbumsCleared() { d->countMap.clear(); d->countHashReady.clear(); d->includeChildrenAlbums.clear(); } void AbstractCountingAlbumModel::slotAlbumMoved(Album*) { // need to update counts of all parents setCountMap(d->countMap); } // ------------------------------------------------------------------ class Q_DECL_HIDDEN AbstractCheckableAlbumModel::Private { public: explicit Private() : staticVectorContainingCheckStateRole(1, Qt::CheckStateRole) { extraFlags = nullptr; rootIsCheckable = true; addExcludeTristate = false; } Qt::ItemFlags extraFlags; bool rootIsCheckable; bool addExcludeTristate; QHash checkedAlbums; QVector staticVectorContainingCheckStateRole; }; AbstractCheckableAlbumModel::AbstractCheckableAlbumModel(Album::Type albumType, Album* const rootAlbum, RootAlbumBehavior rootBehavior, QObject* const parent) : AbstractCountingAlbumModel(albumType, rootAlbum, rootBehavior, parent), d(new Private) { setup(); } AbstractCheckableAlbumModel::~AbstractCheckableAlbumModel() { delete d; } void AbstractCheckableAlbumModel::setCheckable(bool isCheckable) { if (isCheckable) { d->extraFlags |= Qt::ItemIsUserCheckable; } else { d->extraFlags &= ~Qt::ItemIsUserCheckable; resetCheckedAlbums(); } } bool AbstractCheckableAlbumModel::isCheckable() const { return d->extraFlags & Qt::ItemIsUserCheckable; } void AbstractCheckableAlbumModel::setRootCheckable(bool isCheckable) { d->rootIsCheckable = isCheckable; Album* const root = rootAlbum(); if (!d->rootIsCheckable && root) { setChecked(root, false); } } bool AbstractCheckableAlbumModel::rootIsCheckable() const { return d->rootIsCheckable && isCheckable(); } void AbstractCheckableAlbumModel::setTristate(bool isTristate) { if (isTristate) { d->extraFlags |= Qt::ItemIsTristate; } else { d->extraFlags &= ~Qt::ItemIsTristate; } } bool AbstractCheckableAlbumModel::isTristate() const { return d->extraFlags & Qt::ItemIsTristate; } void AbstractCheckableAlbumModel::setAddExcludeTristate(bool b) { d->addExcludeTristate = b; setCheckable(true); setTristate(b); } bool AbstractCheckableAlbumModel::isAddExcludeTristate() const { return d->addExcludeTristate && isTristate(); } bool AbstractCheckableAlbumModel::isChecked(Album* album) const { return d->checkedAlbums.value(album, Qt::Unchecked) == Qt::Checked; } Qt::CheckState AbstractCheckableAlbumModel::checkState(Album* album) const { return d->checkedAlbums.value(album, Qt::Unchecked); } void AbstractCheckableAlbumModel::setChecked(Album* album, bool isChecked) { setData(indexForAlbum(album), isChecked ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole); } void AbstractCheckableAlbumModel::setCheckState(Album* album, Qt::CheckState state) { setData(indexForAlbum(album), state, Qt::CheckStateRole); } void AbstractCheckableAlbumModel::toggleChecked(Album* album) { if (checkState(album) != Qt::PartiallyChecked) { setChecked(album, !isChecked(album)); } } QList AbstractCheckableAlbumModel::checkedAlbums() const { // return a list with all keys with value Qt::Checked return d->checkedAlbums.keys(Qt::Checked); } QList AbstractCheckableAlbumModel::partiallyCheckedAlbums() const { // return a list with all keys with value Qt::PartiallyChecked return d->checkedAlbums.keys(Qt::PartiallyChecked); } void AbstractCheckableAlbumModel::resetAllCheckedAlbums() { const QHash oldChecked = d->checkedAlbums; d->checkedAlbums.clear(); for (QHash::const_iterator it = oldChecked.begin() ; it != oldChecked.end() ; ++it) { if (it.value() != Qt::Unchecked) { QModelIndex index = indexForAlbum(it.key()); emit dataChanged(index, index, d->staticVectorContainingCheckStateRole); emit checkStateChanged(it.key(), Qt::Unchecked); } } } void AbstractCheckableAlbumModel::setDataForChildren(const QModelIndex& parent, const QVariant& value, int role) { setData(parent, value, role); for (int row = 0 ; row < rowCount(parent) ; ++row) { QModelIndex childIndex = index(row, 0, parent); setDataForChildren(childIndex, value, role); } } void AbstractCheckableAlbumModel::resetCheckedAlbums(const QModelIndex& parent) { if (parent == rootAlbumIndex()) { resetAllCheckedAlbums(); return; } setDataForChildren(parent, Qt::Unchecked, Qt::CheckStateRole); } void AbstractCheckableAlbumModel::setDataForParents(const QModelIndex& child, const QVariant& value, int role) { QModelIndex current = child; while (current.isValid() && current != rootAlbumIndex()) { setData(current, value, role); current = parent(current); } } void AbstractCheckableAlbumModel::resetCheckedParentAlbums(const QModelIndex& child) { setDataForParents(child, Qt::Unchecked, Qt::CheckStateRole); } void AbstractCheckableAlbumModel::checkAllParentAlbums(const QModelIndex& child) { setDataForParents(child, Qt::Checked, Qt::CheckStateRole); } void AbstractCheckableAlbumModel::checkAllAlbums(const QModelIndex& parent) { setDataForChildren(parent, Qt::Checked, Qt::CheckStateRole); } void AbstractCheckableAlbumModel::invertCheckedAlbums(const QModelIndex& parent) { Album* const album = albumForIndex(parent); if (album) { toggleChecked(album); } for (int row = 0 ; row < rowCount(parent) ; ++row) { invertCheckedAlbums(index(row, 0, parent)); } } void AbstractCheckableAlbumModel::setCheckStateForChildren(Album* album, Qt::CheckState state) { QModelIndex index = indexForAlbum(album); setDataForChildren(index, state, Qt::CheckStateRole); } void AbstractCheckableAlbumModel::setCheckStateForParents(Album* album, Qt::CheckState state) { QModelIndex index = indexForAlbum(album); setDataForParents(index, state, Qt::CheckStateRole); } QVariant AbstractCheckableAlbumModel::albumData(Album* a, int role) const { if (role == Qt::CheckStateRole) { if ((d->extraFlags & Qt::ItemIsUserCheckable) && (!a->isRoot() || d->rootIsCheckable)) { // with Qt::Unchecked as default, albums not in the hash (initially all) // are simply regarded as unchecked Qt::CheckState state = d->checkedAlbums.value(a, Qt::Unchecked); if (d->addExcludeTristate) { // Use Qt::PartiallyChecked only internally, do not expose it to the TreeView return (state == Qt::Unchecked) ? Qt::Unchecked : Qt::Checked; } return state; } } return AbstractCountingAlbumModel::albumData(a, role); } void AbstractCheckableAlbumModel::prepareAddExcludeDecoration(Album* a, QPixmap& icon) const { if (!d->addExcludeTristate) { return; } Qt::CheckState state = checkState(a); if (state != Qt::Unchecked) { int iconSize = qMax(icon.width(), icon.height()); int overlay_size = qMin(iconSize, qMax(16, iconSize * 2 / 3)); QPainter p(&icon); p.drawPixmap((icon.width() - overlay_size) / 2, (icon.height() - overlay_size) / 2, QIcon::fromTheme(state == Qt::PartiallyChecked ? QLatin1String("list-remove") : QLatin1String("list-add")).pixmap(overlay_size, overlay_size)); } } Qt::ItemFlags AbstractCheckableAlbumModel::flags(const QModelIndex& index) const { Qt::ItemFlags extraFlags = d->extraFlags; if (!d->rootIsCheckable) { QModelIndex root = rootAlbumIndex(); if (root.isValid() && index == root) { extraFlags &= ~Qt::ItemIsUserCheckable; } } return AbstractCountingAlbumModel::flags(index) | extraFlags; } bool AbstractCheckableAlbumModel::setData(const QModelIndex& index, const QVariant& value, int role) { if (role == Qt::CheckStateRole) { Qt::CheckState state = (Qt::CheckState)value.toInt(); Album* const album = albumForIndex(index); if (!album) { return false; } //qCDebug(DIGIKAM_GENERAL_LOG) << "Updating check state for album" << album->title() << "to" << value; d->checkedAlbums.insert(album, state); emit dataChanged(index, index); emit checkStateChanged(album, state); return true; } else { return AbstractCountingAlbumModel::setData(index, value, role); } } void AbstractCheckableAlbumModel::albumCleared(Album* album) { // preserve check state if album is only being moved if (!AlbumManager::instance()->isMovingAlbum(album)) { d->checkedAlbums.remove(album); } AbstractCountingAlbumModel::albumCleared(album); } void AbstractCheckableAlbumModel::allAlbumsCleared() { d->checkedAlbums.clear(); AbstractCountingAlbumModel::allAlbumsCleared(); } } // namespace Digikam diff --git a/core/libs/models/abstractalbummodel.h b/core/libs/models/abstractalbummodel.h index ddf397f39c..c5f5b5a474 100644 --- a/core/libs/models/abstractalbummodel.h +++ b/core/libs/models/abstractalbummodel.h @@ -1,450 +1,470 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-03-23 * Description : Qt Model for Albums * * Copyright (C) 2008-2011 by Marcel Wiesweg * Copyright (C) 2010 by Andi Clemens + * Copyright (C) 2012-2019 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_ABSTRACT_ALBUM_MODEL_H #define DIGIKAM_ABSTRACT_ALBUM_MODEL_H // Qt includes #include #include #include #include // Local includes #include "album.h" #include "digikam_export.h" namespace Digikam { class Album; class AlbumManager; class AlbumModelDragDropHandler; class DIGIKAM_EXPORT AbstractAlbumModel : public QAbstractItemModel { Q_OBJECT public: /** * AbstractAlbumModel is the abstract base class for all models that * present Album objects as managed by AlbumManager. * You will want to create an instance of the base classes. */ enum RootAlbumBehavior { - /** The root album will be included as a single parent item - * with all top-level album as children + /** + * The root album will be included as a single parent item + * with all top-level album as children */ IncludeRootAlbum, - /** The root album will not be included, but all top-level album - * are represented as top-level items in this view + /** + * The root album will not be included, but all top-level album + * are represented as top-level items in this view */ IgnoreRootAlbum }; enum AlbumDataRole { /// Returns the album title. Principally the same as display role, but without any additions. AlbumTitleRole = Qt::UserRole, /// Returns the Album::Type of the associated album AlbumTypeRole = Qt::UserRole + 1, /// Returns a pointer to the associated Album object AlbumPointerRole = Qt::UserRole + 2, /// Returns the id of the associated Album object AlbumIdRole = Qt::UserRole + 3, /// Returns the global id (unique across all album types) AlbumGlobalIdRole = Qt::UserRole + 4, /// Returns the data to sort on AlbumSortRole = Qt::UserRole + 5 }; public: /** * Create an AbstractAlbumModel object for albums with the given type. * Pass the root album if it is already available. * Do not use this class directly, but one of the subclasses. */ explicit AbstractAlbumModel(Album::Type albumType, Album* const rootAlbum, RootAlbumBehavior rootBehavior = IncludeRootAlbum, QObject* const parent = nullptr); ~AbstractAlbumModel(); - /** Set a drag drop handler + /** + * Set a drag drop handler */ void setDragDropHandler(AlbumModelDragDropHandler* handler); - /** Returns the drag drop handler, or 0 if none is installed + /** + * Returns the drag drop handler, or 0 if none is installed */ - AlbumModelDragDropHandler* dragDropHandler() const; + AlbumModelDragDropHandler* dragDropHandler() const; - /** Returns the album object associated with the given model index + /** + * Returns the album object associated with the given model index */ - Album* albumForIndex(const QModelIndex& index) const; + Album* albumForIndex(const QModelIndex& index) const; - /** Return the QModelIndex for the given album, or an invalid index if - * the album is not contained in this model. + /** + * Return the QModelIndex for the given album, or an invalid index if + * the album is not contained in this model. */ - QModelIndex indexForAlbum(Album* album) const; + QModelIndex indexForAlbum(Album* album) const; - /** Returns the album represented by the index. In contrast to albumForIndex(), - * the index can be from any proxy model, as long as an AbstractAlbumModel is at the end. + /** + * Returns the album represented by the index. In contrast to albumForIndex(), + * the index can be from any proxy model, as long as an AbstractAlbumModel is at the end. */ static Album* retrieveAlbum(const QModelIndex& index); - Album* rootAlbum() const; + Album* rootAlbum() const; - /** Return the index corresponding to the root album. If the policy is IgnoreRootAlbum, this is an invalid index. + /** + * Return the index corresponding to the root album. If the policy is IgnoreRootAlbum, this is an invalid index. */ - QModelIndex rootAlbumIndex() const; + QModelIndex rootAlbumIndex() const; - /** Returns the root album behavior set for this model + /** + * Returns the root album behavior set for this model */ - RootAlbumBehavior rootAlbumBehavior() const; + RootAlbumBehavior rootAlbumBehavior() const; - /** Returns the Album::Type of the contained albums + /** + * Returns the Album::Type of the contained albums */ - Album::Type albumType() const; - - virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; - virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; - virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; - virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override; - virtual Qt::ItemFlags flags(const QModelIndex& index) const override; - virtual bool hasChildren(const QModelIndex& parent = QModelIndex()) const override; - virtual QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; - virtual QModelIndex parent(const QModelIndex& index) const override; - - virtual Qt::DropActions supportedDropActions() const override; - virtual QStringList mimeTypes() const override; - virtual bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) override; - virtual QMimeData* mimeData(const QModelIndexList& indexes) const override; + Album::Type albumType() const; + + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; + virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override; + virtual Qt::ItemFlags flags(const QModelIndex& index) const override; + virtual bool hasChildren(const QModelIndex& parent = QModelIndex()) const override; + virtual QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; + virtual QModelIndex parent(const QModelIndex& index) const override; + + virtual Qt::DropActions supportedDropActions() const override; + virtual QStringList mimeTypes() const override; + virtual bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) override; + virtual QMimeData* mimeData(const QModelIndexList& indexes) const override; Q_SIGNALS: - /** This is initialized once after creation, if the root album becomes available, - * if it was not already available at time of construction. - * This is emitted regardless of root album policy. + /** + * This is initialized once after creation, if the root album becomes available, + * if it was not already available at time of construction. + * This is emitted regardless of root album policy. */ void rootAlbumAvailable(); protected: - /** Switch on drag and drop globally for all items. Default is true. - * For per-item cases reimplement itemFlags(). + /** + * Switch on drag and drop globally for all items. Default is true. + * For per-item cases reimplement itemFlags(). */ void setEnableDrag(bool enable); void setEnableDrop(bool enable); // these can be reimplemented in a subclass /// For subclassing convenience: A part of the implementation of data() - virtual QVariant albumData(Album* a, int role) const; + virtual QVariant albumData(Album* a, int role) const; /// For subclassing convenience: A part of the implementation of data() - virtual QVariant decorationRoleData(Album* a) const; + virtual QVariant decorationRoleData(Album* a) const; /// For subclassing convenience: A port of the implementation of data() - virtual QVariant sortRoleData(Album* a) const; + virtual QVariant sortRoleData(Album* a) const; /// For subclassing convenience: A part of the implementation of headerData() - virtual QString columnHeader() const; + virtual QString columnHeader() const; /// For subclassing convenience: A part of the implementation of itemFlags() - virtual Qt::ItemFlags itemFlags(Album* album) const; + virtual Qt::ItemFlags itemFlags(Album* album) const; - /** Returns true for those and only those albums that shall be contained in this model. - * They must have a common root album, which is set in the constructor. + /** + * Returns true for those and only those albums that shall be contained in this model. + * They must have a common root album, which is set in the constructor. */ - virtual bool filterAlbum(Album* album) const; + virtual bool filterAlbum(Album* album) const; /// Notification when an entry is removed virtual void albumCleared(Album* /*album*/) {}; /// Notification when all entries are removed - virtual void allAlbumsCleared() {}; + virtual void allAlbumsCleared() {}; protected Q_SLOTS: void slotAlbumAboutToBeAdded(Album* album, Album* parent, Album* prev); void slotAlbumAdded(Album*); void slotAlbumAboutToBeDeleted(Album* album); void slotAlbumHasBeenDeleted(quintptr); void slotAlbumsCleared(); void slotAlbumIconChanged(Album* album); void slotAlbumRenamed(Album* album); private: class Private; Private* const d; }; // ------------------------------------------------------------------ class DIGIKAM_EXPORT AbstractSpecificAlbumModel : public AbstractAlbumModel { Q_OBJECT public: /// Abstract base class, do not instantiate. explicit AbstractSpecificAlbumModel(Album::Type albumType, Album* const rootAlbum, RootAlbumBehavior rootBehavior = IncludeRootAlbum, QObject* const parent = nullptr); protected: virtual QString columnHeader() const override; void setColumnHeader(const QString& header); /// You need to call this from your constructor if you intend to load the thumbnail facilities of this class void setupThumbnailLoading(); void emitDataChangedForChildren(Album* album); protected Q_SLOTS: void slotGotThumbnailFromIcon(Album* album, const QPixmap& thumbnail); void slotThumbnailLost(Album* album); void slotReloadThumbnails(); protected: QString m_columnHeader; }; // ------------------------------------------------------------------ class DIGIKAM_EXPORT AbstractCountingAlbumModel : public AbstractSpecificAlbumModel { Q_OBJECT public: /// Supports displaying a count alongside the album name in DisplayRole explicit AbstractCountingAlbumModel(Album::Type albumType, Album* const rootAlbum, RootAlbumBehavior rootBehavior = IncludeRootAlbum, QObject* const parent = nullptr); ~AbstractCountingAlbumModel(); protected: /** * Call this method in children class contructors to init signal/slots connections. */ void setup(); public Q_SLOTS: /// Call to enable or disable showing the count. Default is false. void setShowCount(bool show); bool showCount() const; - /** Enable displaying the count. Set a map of album id -> count (excluding children). - * If an album is not contained, no count is displayed. To display a count of 0, - * there must be an entry album id -> 0. + /** + * Enable displaying the count. Set a map of album id -> count (excluding children). + * If an album is not contained, no count is displayed. To display a count of 0, + * there must be an entry album id -> 0. */ void setCountMap(const QMap& idCountMap); - /** Displays only the count of the album, without adding child albums' counts. - * This is the default. - * Can connect to QTreeView's expanded() signal. + /** + * Displays only the count of the album, without adding child albums' counts. + * This is the default. + * Can connect to QTreeView's expanded() signal. */ void excludeChildrenCount(const QModelIndex& index); - /** Displays sum of the count of the album and child albums' counts. - * Can connect to QTreeView's collapsed() signal. + /** + * Displays sum of the count of the album and child albums' counts. + * Can connect to QTreeView's collapsed() signal. */ void includeChildrenCount(const QModelIndex& index); /** * Returns the number of included items for this album. * * @return positive value or -1 if unknown */ virtual int albumCount(Album* album) const; protected: /// If you do not use setCountMap, excludeChildrenCount and includeChildrenCount, you can set a count here. void setCount(Album* album, int count); /// need to implement in subclass - virtual Album* albumForId(int id) const = 0; + virtual Album* albumForId(int id) const = 0; /// Can reimplement in subclass - virtual QString albumName(Album* a) const; + virtual QString albumName(Album* a) const; // Reimplemented from parent classes virtual QVariant albumData(Album* a, int role) const override; virtual void albumCleared(Album* album) override; virtual void allAlbumsCleared() override; protected Q_SLOTS: void slotAlbumMoved(Album* album); private: void updateCount(Album* album); private: class Private; Private* const d; }; // ------------------------------------------------------------------ class DIGIKAM_EXPORT AbstractCheckableAlbumModel : public AbstractCountingAlbumModel { Q_OBJECT public: /// Abstract base class that manages the check state of Albums. /// Call setCheckable(true) to enable checkable albums. explicit AbstractCheckableAlbumModel(Album::Type albumType, Album* const rootAlbum, RootAlbumBehavior rootBehavior = IncludeRootAlbum, QObject* const parent = nullptr); ~AbstractCheckableAlbumModel(); /// Triggers if the albums in this model are checkable void setCheckable(bool isCheckable); - bool isCheckable() const; + bool isCheckable() const; - /** Triggers if the root album is checkable. - * Only applicable if the root album is contained at all, and if isCheckable() is true. + /** + * Triggers if the root album is checkable. + * Only applicable if the root album is contained at all, and if isCheckable() is true. */ void setRootCheckable(bool rootIsCheckable); - bool rootIsCheckable() const; + bool rootIsCheckable() const; - /** Triggers if the albums in this model are tristate. - * Used to allow the user to actively set a third state, - * don't use if you only want to display a third state. - * Note that you want to set setCheckable(true) before. + /** + * Triggers if the albums in this model are tristate. + * Used to allow the user to actively set a third state, + * don't use if you only want to display a third state. + * Note that you want to set setCheckable(true) before. */ void setTristate(bool isTristate); - bool isTristate() const; + bool isTristate() const; /** * Sets a special tristate mode, which offers the * three modes "unchecked", "added" and "excluded", * where "excluded" corresponds to partially checked internally, * but is reflected in the treeview through the decoration only. */ void setAddExcludeTristate(bool b); - bool isAddExcludeTristate() const; + bool isAddExcludeTristate() const; /// Returns if the given album has the check state Checked - bool isChecked(Album* album) const; + bool isChecked(Album* album) const; /// Returns the check state of the album Qt::CheckState checkState(Album* album) const; /// Returns a list of album with check state Checked QList checkedAlbums() const; /// Returns a list of album with partially check state Checked QList partiallyCheckedAlbums() const; public Q_SLOTS: /// Sets the check state of album to Checked or Unchecked void setChecked(Album* album, bool isChecked); /// Sets the check state of the album void setCheckState(Album* album, Qt::CheckState state); /// Toggles the check state of album between Checked or Unchecked void toggleChecked(Album* album); /// Resets the checked state of all albums to Qt::Unchecked void resetAllCheckedAlbums(); /// Resets the checked state of all albums under the given parent void resetCheckedAlbums(const QModelIndex& parent = QModelIndex()); /// Resets the checked state of all parents of the child including it. void resetCheckedParentAlbums(const QModelIndex& child); /// Checks all albums beneath the given parent void checkAllAlbums(const QModelIndex& parent = QModelIndex()); /// Checks all parent albums starting at the child, including it. void checkAllParentAlbums(const QModelIndex& child); /// Inverts the checked state of all albums under the given parent. void invertCheckedAlbums(const QModelIndex& parent = QModelIndex()); /// Sets the checked state recursively for all children of and the given album. void setCheckStateForChildren(Album* album, Qt::CheckState state); /// Sets the checked state recursively for all parents of and the given album. void setCheckStateForParents(Album* album, Qt::CheckState state); Q_SIGNALS: - /** Emitted when the check state of an album changes. - * checkState contains the new Qt::CheckState of album + /** + * Emitted when the check state of an album changes. + * checkState contains the new Qt::CheckState of album */ void checkStateChanged(Album* album, Qt::CheckState checkState); protected: /** * If in AddExcludeTristate mode, changes the icon as to indicate the state. */ - void prepareAddExcludeDecoration(Album* a, QPixmap& icon) const; + void prepareAddExcludeDecoration(Album* a, QPixmap& icon) const; - virtual QVariant albumData(Album* a, int role) const override; - virtual Qt::ItemFlags flags(const QModelIndex& index) const override; - virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; + virtual QVariant albumData(Album* a, int role) const override; + virtual Qt::ItemFlags flags(const QModelIndex& index) const override; + virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; - virtual void albumCleared(Album* album) override; - virtual void allAlbumsCleared() override; + virtual void albumCleared(Album* album) override; + virtual void allAlbumsCleared() override; private: void setDataForParents(const QModelIndex& child, const QVariant& value, int role = Qt::EditRole); void setDataForChildren(const QModelIndex& parent, const QVariant& value, int role = Qt::EditRole); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_ABSTRACT_ALBUM_MODEL_H