diff --git a/core/utilities/import/models/camitemsortsettings.cpp b/core/utilities/import/models/camitemsortsettings.cpp index 26b6e9292e..0462be3458 100644 --- a/core/utilities/import/models/camitemsortsettings.cpp +++ b/core/utilities/import/models/camitemsortsettings.cpp @@ -1,299 +1,376 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2012-23-06 * Description : Sort settings for camera item infos * * Copyright (C) 2012 by Islam Wazery * * 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 "camitemsortsettings.h" // Qt includes #include namespace Digikam { CamItemSortSettings::CamItemSortSettings() + : categorizationMode(NoCategories), + categorizationSortOrder(DefaultOrder), + currentCategorizationSortOrder(Qt::AscendingOrder), + categorizationCaseSensitivity(Qt::CaseSensitive), + sortOrder(DefaultOrder), + sortRole(SortByFileName), + strTypeNatural(true), + currentSortOrder(Qt::AscendingOrder), + sortCaseSensitivity(Qt::CaseSensitive) { - categorizationMode = NoCategories; - categorizationSortOrder = DefaultOrder; - categorizationCaseSensitivity = Qt::CaseSensitive; - sortRole = SortByFileName; - sortOrder = DefaultOrder; - strTypeNatural = true; - sortCaseSensitivity = Qt::CaseSensitive; - currentCategorizationSortOrder = Qt::AscendingOrder; - currentSortOrder = Qt::AscendingOrder; } CamItemSortSettings::~CamItemSortSettings() { } bool CamItemSortSettings::operator ==(const CamItemSortSettings& other) const { - return (categorizationMode == other.categorizationMode && - categorizationSortOrder == other.categorizationSortOrder && - categorizationCaseSensitivity == other.categorizationCaseSensitivity && - sortRole == other.sortRole && - sortOrder == other.sortOrder && - sortCaseSensitivity == other.sortCaseSensitivity); + return ( + (categorizationMode == other.categorizationMode) && + (categorizationSortOrder == other.categorizationSortOrder) && + (categorizationCaseSensitivity == other.categorizationCaseSensitivity) && + (sortRole == other.sortRole) && + (sortOrder == other.sortOrder) && + (sortCaseSensitivity == other.sortCaseSensitivity) + ); } void CamItemSortSettings::setCategorizationMode(CategorizationMode mode) { categorizationMode = mode; if (categorizationSortOrder == DefaultOrder) { currentCategorizationSortOrder = defaultSortOrderForCategorizationMode(categorizationMode); } } void CamItemSortSettings::setCategorizationSortOrder(SortOrder order) { categorizationSortOrder = order; if (categorizationSortOrder == DefaultOrder) { currentCategorizationSortOrder = defaultSortOrderForCategorizationMode(categorizationMode); } else { currentCategorizationSortOrder = (Qt::SortOrder)categorizationSortOrder; } } void CamItemSortSettings::setSortRole(SortRole role) { sortRole = role; if (sortOrder == DefaultOrder) { currentSortOrder = defaultSortOrderForSortRole(sortRole); } } void CamItemSortSettings::setSortOrder(SortOrder order) { sortOrder = order; if (sortOrder == DefaultOrder) { currentSortOrder = defaultSortOrderForSortRole(sortRole); } else { currentSortOrder = (Qt::SortOrder)order; } } void CamItemSortSettings::setStringTypeNatural(bool natural) { strTypeNatural = natural; } Qt::SortOrder CamItemSortSettings::defaultSortOrderForCategorizationMode(CategorizationMode mode) { switch (mode) { case NoCategories: case CategoryByFolder: case CategoryByFormat: case CategoryByDate: default: + { return Qt::AscendingOrder; + } } } Qt::SortOrder CamItemSortSettings::defaultSortOrderForSortRole(SortRole role) { switch (role) { case SortByFileName: case SortByFilePath: + { return Qt::AscendingOrder; + } + case SortByFileSize: + { return Qt::DescendingOrder; + } + case SortByCreationDate: + { return Qt::AscendingOrder; + } + case SortByDownloadState: + { return Qt::AscendingOrder; + } + case SortByRating: + { return Qt::DescendingOrder; + } + default: + { return Qt::AscendingOrder; + } } } int CamItemSortSettings::compareCategories(const CamItemInfo& left, const CamItemInfo& right) const { switch (categorizationMode) { case NoCategories: case CategoryByFolder: + { return naturalCompare(left.folder, right.folder, currentCategorizationSortOrder, categorizationCaseSensitivity, strTypeNatural); + } + case CategoryByFormat: + { return naturalCompare(left.mime, right.mime, currentCategorizationSortOrder, categorizationCaseSensitivity, strTypeNatural); + } + case CategoryByDate: + { return compareByOrder(left.ctime.date(), right.ctime.date(), currentCategorizationSortOrder); + } + default: + { return 0; + } } } bool CamItemSortSettings::lessThan(const CamItemInfo& left, const CamItemInfo& right) const { int result = compare(left, right, sortRole); if (result != 0) { - return result < 0; + return (result < 0); } if (left == right) { return false; } if ((result = compare(left, right, SortByFileName)) != 0) { - return result < 0; + return (result < 0); } if ((result = compare(left, right, SortByCreationDate)) != 0) { - return result < 0; + return (result < 0); } if ((result = compare(left, right, SortByFilePath)) != 0) { - return result < 0; + return (result < 0); } if ((result = compare(left, right, SortByFileSize)) != 0) { - return result < 0; + return (result < 0); } if ((result = compare(left, right, SortByRating)) != 0) { - return result < 0; + return (result < 0); } if ((result = compare(left, right, SortByDownloadState)) != 0) { - return result < 0; + return (result < 0); } return false; } int CamItemSortSettings::compare(const CamItemInfo& left, const CamItemInfo& right) const { return compare(left, right, sortRole); } int CamItemSortSettings::compare(const CamItemInfo& left, const CamItemInfo& right, SortRole role) const { switch (role) { case SortByFileName: { return naturalCompare(left.name, right.name, currentSortOrder, sortCaseSensitivity, strTypeNatural); } + case SortByFilePath: + { return naturalCompare(left.url().toLocalFile(), right.url().toLocalFile(), currentSortOrder, sortCaseSensitivity, strTypeNatural); + } + case SortByFileSize: + { return compareByOrder(left.size, right.size, currentSortOrder); //FIXME: Change it to creation date instead of modification date. + } + case SortByCreationDate: + { return compareByOrder(left.ctime, right.ctime, currentSortOrder); + } + case SortByRating: + { return compareByOrder(left.rating, right.rating, currentSortOrder); + } + case SortByDownloadState: + { return compareByOrder(left.downloaded, right.downloaded, currentSortOrder); + } + default: + { return 1; + } } } bool CamItemSortSettings::lessThan(const QVariant& left, const QVariant& right) const { if (left.type() != right.type()) { return false; } switch (left.type()) { case QVariant::Int: + { return compareByOrder(left.toInt(), right.toInt(), currentSortOrder); + } + case QVariant::UInt: + { return compareByOrder(left.toUInt(), right.toUInt(), currentSortOrder); + } + case QVariant::LongLong: + { return compareByOrder(left.toLongLong(), right.toLongLong(), currentSortOrder); + } + case QVariant::ULongLong: + { return compareByOrder(left.toULongLong(), right.toULongLong(), currentSortOrder); + } + case QVariant::Double: + { return compareByOrder(left.toDouble(), right.toDouble(), currentSortOrder); + } + case QVariant::Date: + { return compareByOrder(left.toDate(), right.toDate(), currentSortOrder); + } + case QVariant::DateTime: + { return compareByOrder(left.toDateTime(), right.toDateTime(), currentSortOrder); + } + case QVariant::Time: + { return compareByOrder(left.toTime(), right.toTime(), currentSortOrder); + } + case QVariant::Rect: case QVariant::RectF: { QRectF rectLeft = left.toRectF(); QRectF rectRight = right.toRectF(); int result; if ((result = compareByOrder(rectLeft.top(), rectRight.top(), currentSortOrder)) != 0) { return result < 0; } if ((result = compareByOrder(rectLeft.left(), rectRight.left(), currentSortOrder)) != 0) { return result < 0; } QSizeF sizeLeft = rectLeft.size(); QSizeF sizeRight = rectRight.size(); if ((result = compareByOrder(sizeLeft.width()*sizeLeft.height(), sizeRight.width()*sizeRight.height(), currentSortOrder)) != 0) { return result < 0; } #if __GNUC__ >= 7 // krazy:exclude=cpp [[fallthrough]]; #endif } + default: + { return naturalCompare(left.toString(), right.toString(), currentSortOrder, sortCaseSensitivity, strTypeNatural); + } } } } // namespace Digikam diff --git a/core/utilities/import/models/camitemsortsettings.h b/core/utilities/import/models/camitemsortsettings.h index 0fd6704f83..292fe5fdc6 100644 --- a/core/utilities/import/models/camitemsortsettings.h +++ b/core/utilities/import/models/camitemsortsettings.h @@ -1,206 +1,214 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2012-23-06 * Description : Sort settings for camera item infos * * Copyright (C) 2012 by Islam Wazery * * 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_CAMITEM_SORT_SETTINGS_H #define DIGIKAM_CAMITEM_SORT_SETTINGS_H // Qt includes #include // Local includes #include "camiteminfo.h" namespace Digikam { class CamItemSortSettings { public: - CamItemSortSettings(); - ~CamItemSortSettings(); - - bool operator==(const CamItemSortSettings& other) const; - - /** Compares the categories of left and right camItemInfos. - * It returns -1 if the left camItemInfo is less than right, and 0 if both fall - * in the same category, and 1 if the left camItemInfo is greater than right. - * Adheres to set categorization mode and current category sort order. - */ - int compareCategories(const CamItemInfo& left, const CamItemInfo& right) const; - - - /** Returns true if left is less than right. - * Adheres to current sort role and sort order. - */ - bool lessThan(const CamItemInfo& left, const CamItemInfo& right) const; - - /** Returns true if left QVariant is less than right. - * Adheres to current sort role and sort order. - */ - bool lessThan(const QVariant& left, const QVariant& right) const; - - /** Compares the camItemInfos left and right. - * Return -1 if left is less than right, 1 if left is greater than right, - * and 0 if left equals right comparing the current sort role's value. - * Adheres to set sort role and sort order. - */ - int compare(const CamItemInfo& left, const CamItemInfo& right) const; - enum SortOrder { AscendingOrder = Qt::AscendingOrder, DescendingOrder = Qt::DescendingOrder, - DefaultOrder /// sort order depends on the chosen sort role + DefaultOrder ///< sort order depends on the chosen sort role }; - /// --- Categories --------------- - enum CategorizationMode { NoCategories, CategoryByFolder, CategoryByFormat, CategoryByDate }; - CategorizationMode categorizationMode; - SortOrder categorizationSortOrder; - - void setCategorizationMode(CategorizationMode mode); - void setCategorizationSortOrder(SortOrder order); - - /// Only Ascending or Descending, never be DefaultOrder - Qt::SortOrder currentCategorizationSortOrder; - Qt::CaseSensitivity categorizationCaseSensitivity; - - bool isCategorized() const { return categorizationMode >= CategoryByFolder; } - - /// --- Camera Items Sorting --------------- - enum SortRole { SortByFileName, SortByFilePath, SortByCreationDate, SortByFileSize, SortByDownloadState, SortByRating }; - SortOrder sortOrder; - SortRole sortRole; - bool strTypeNatural; +public: + + CamItemSortSettings(); + ~CamItemSortSettings(); + + bool operator==(const CamItemSortSettings& other) const; + + /** + * Compares the categories of left and right camItemInfos. + * It returns -1 if the left camItemInfo is less than right, and 0 if both fall + * in the same category, and 1 if the left camItemInfo is greater than right. + * Adheres to set categorization mode and current category sort order. + */ + int compareCategories(const CamItemInfo& left, const CamItemInfo& right) const; + + /** + * Returns true if left is less than right. + * Adheres to current sort role and sort order. + */ + bool lessThan(const CamItemInfo& left, const CamItemInfo& right) const; + + /** + * Returns true if left QVariant is less than right. + * Adheres to current sort role and sort order. + */ + bool lessThan(const QVariant& left, const QVariant& right) const; + + /** + * Compares the camItemInfos left and right. + * Return -1 if left is less than right, 1 if left is greater than right, + * and 0 if left equals right comparing the current sort role's value. + * Adheres to set sort role and sort order. + */ + int compare(const CamItemInfo& left, const CamItemInfo& right) const; + + void setCategorizationMode(CategorizationMode mode); + void setCategorizationSortOrder(SortOrder order); + + bool isCategorized() const { return categorizationMode >= CategoryByFolder; } void setSortRole(SortRole role); void setSortOrder(SortOrder order); void setStringTypeNatural(bool natural); - Qt::SortOrder currentSortOrder; - Qt::CaseSensitivity sortCaseSensitivity; - int compare(const CamItemInfo& left, const CamItemInfo& right, SortRole sortRole) const; static Qt::SortOrder defaultSortOrderForCategorizationMode(CategorizationMode mode); static Qt::SortOrder defaultSortOrderForSortRole(SortRole role); - /** Returns a < b if sortOrder is Ascending, or b < a if order is descending + /** + * Returns a < b if sortOrder is Ascending, or b < a if order is descending */ template static inline bool lessThanByOrder(const T& a, const T& b, Qt::SortOrder sortOrder) { if (sortOrder == Qt::AscendingOrder) { - return a < b; + return (a < b); } - return b < a; + return (b < a); } - /** Returns the usual compare result of -1, 0, or 1 for lessThan, equals and greaterThan. + /** + * Returns the usual compare result of -1, 0, or 1 for lessThan, equals and greaterThan. */ template static inline int compareValue(const T& a, const T &b) { if (a == b) { return 0; } if (a > b) { return 1; } - return -1; + return (-1); } - /** Takes a typical result from a compare method (0 is equal, -1 is less than, 1 is greater than) - * and applies the given sort order to it. + /** + * Takes a typical result from a compare method (0 is equal, -1 is less than, 1 is greater than) + * and applies the given sort order to it. */ static inline int compareByOrder(int compareResult, Qt::SortOrder sortOrder) { if (sortOrder == Qt::AscendingOrder) { return compareResult; } - return - compareResult; + return (- compareResult); } template static inline int compareByOrder(const T& a, const T& b, Qt::SortOrder sortOrder) { return compareByOrder(compareValue(a, b), sortOrder); } - /** Compares the two string by natural comparison and adheres to given sort order + /** + * Compares the two string by natural comparison and adheres to given sort order */ static inline int naturalCompare(const QString& a, const QString& b, Qt::SortOrder sortOrder, Qt::CaseSensitivity caseSensitive = Qt::CaseSensitive, bool natural = true) { QCollator collator; collator.setNumericMode(natural); collator.setIgnorePunctuation(false); collator.setCaseSensitivity(caseSensitive); if (a.contains(QLatin1String("_v"), Qt::CaseInsensitive)) { collator.setIgnorePunctuation(true); } if (sortOrder == Qt::AscendingOrder) { return collator.compare(a, b); } return - collator.compare(a, b); } + +public: + + CategorizationMode categorizationMode; + SortOrder categorizationSortOrder; + + /// Only Ascending or Descending, never be DefaultOrder + Qt::SortOrder currentCategorizationSortOrder; + Qt::CaseSensitivity categorizationCaseSensitivity; + + /// Camera Items Sorting + SortOrder sortOrder; + SortRole sortRole; + bool strTypeNatural; + + Qt::SortOrder currentSortOrder; + Qt::CaseSensitivity sortCaseSensitivity; }; } // namespace Digikam #endif // DIGIKAM_CAMITEM_SORT_SETTINGS_H diff --git a/core/utilities/import/models/importfiltermodel.cpp b/core/utilities/import/models/importfiltermodel.cpp index 0861c7d75e..8102835fe5 100644 --- a/core/utilities/import/models/importfiltermodel.cpp +++ b/core/utilities/import/models/importfiltermodel.cpp @@ -1,586 +1,596 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2012-21-06 * Description : Qt filter model for import items * * Copyright (C) 2012 by Islam Wazery * * 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 "importfiltermodel.h" // Local includes #include "camiteminfo.h" #include "filter.h" #include "importimagemodel.h" namespace Digikam { ImportSortFilterModel::ImportSortFilterModel(QObject* const parent) : DCategorizedSortFilterProxyModel(parent), m_chainedModel(nullptr) { } ImportSortFilterModel::~ImportSortFilterModel() { } void ImportSortFilterModel::setSourceImportModel(ImportItemModel* const sourceModel) { if (m_chainedModel) { m_chainedModel->setSourceImportModel(sourceModel); } else { setDirectSourceImportModel(sourceModel); } } ImportItemModel* ImportSortFilterModel::sourceImportModel() const { if (m_chainedModel) { return m_chainedModel->sourceImportModel(); } return static_cast(sourceModel()); } void ImportSortFilterModel::setSourceFilterModel(ImportSortFilterModel* const sourceModel) { if (sourceModel) { ImportItemModel* const model = sourceImportModel(); if (model) { sourceModel->setSourceImportModel(model); } } m_chainedModel = sourceModel; setSourceModel(sourceModel); } ImportSortFilterModel* ImportSortFilterModel::sourceFilterModel() const { return m_chainedModel; } QModelIndex ImportSortFilterModel::mapToSourceImportModel(const QModelIndex& proxyIndex) const { if (!proxyIndex.isValid()) { return QModelIndex(); } if (m_chainedModel) { return m_chainedModel->mapToSourceImportModel(mapToSource(proxyIndex)); } return mapToSource(proxyIndex); } QModelIndex ImportSortFilterModel::mapFromSourceImportModel(const QModelIndex& importModelIndex) const { if (!importModelIndex.isValid()) { return QModelIndex(); } if (m_chainedModel) { return mapFromSource(m_chainedModel->mapFromSourceImportModel(importModelIndex)); } return mapFromSource(importModelIndex); } QModelIndex ImportSortFilterModel::mapFromDirectSourceToSourceImportModel(const QModelIndex& sourceModelIndex) const { if (!sourceModelIndex.isValid()) { return QModelIndex(); } if (m_chainedModel) { return m_chainedModel->mapToSourceImportModel(sourceModelIndex); } return sourceModelIndex; } QList ImportSortFilterModel::mapListToSource(const QList& indexes) const { QList sourceIndexes; foreach (const QModelIndex& index, indexes) { sourceIndexes << mapToSourceImportModel(index); } return sourceIndexes; } QList ImportSortFilterModel::mapListFromSource(const QList& sourceIndexes) const { QList indexes; foreach (const QModelIndex& index, sourceIndexes) { indexes << mapFromSourceImportModel(index); } return indexes; } CamItemInfo ImportSortFilterModel::camItemInfo(const QModelIndex& index) const { return sourceImportModel()->camItemInfo(mapToSourceImportModel(index)); } qlonglong ImportSortFilterModel::camItemId(const QModelIndex& index) const { return sourceImportModel()->camItemId(mapToSourceImportModel(index)); } QList ImportSortFilterModel::camItemInfos(const QList& indexes) const { QList infos; foreach (const QModelIndex& index, indexes) { infos << camItemInfo(index); } return infos; } QList ImportSortFilterModel::camItemIds(const QList& indexes) const { QList ids; foreach (const QModelIndex& index, indexes) { ids << camItemId(index); } return ids; } QModelIndex ImportSortFilterModel::indexForPath(const QString& filePath) const { QUrl fileUrl = QUrl::fromLocalFile(filePath); + return mapFromSourceImportModel(sourceImportModel()->indexForUrl(fileUrl)); } QModelIndex ImportSortFilterModel::indexForCamItemInfo(const CamItemInfo& info) const { return mapFromSourceImportModel(sourceImportModel()->indexForCamItemInfo(info)); } QModelIndex ImportSortFilterModel::indexForCamItemId(qlonglong id) const { return mapFromSourceImportModel(sourceImportModel()->indexForCamItemId(id)); } QList ImportSortFilterModel::camItemInfosSorted() const { QList infos; const int size = rowCount(); for (int i = 0 ; i < size ; ++i) { infos << camItemInfo(index(i, 0)); } return infos; } ImportFilterModel* ImportSortFilterModel::importFilterModel() const { if (m_chainedModel) { return m_chainedModel->importFilterModel(); } return nullptr; } void ImportSortFilterModel::setSourceModel(QAbstractItemModel* sourceModel) { DCategorizedSortFilterProxyModel::setSourceModel(sourceModel); } void ImportSortFilterModel::setDirectSourceImportModel(ImportItemModel* const sourceModel) { setSourceModel(sourceModel); } //--- ImportFilterModel methods --------------------------------- class Q_DECL_HIDDEN ImportFilterModel::ImportFilterModelPrivate : public QObject { public: ImportFilterModelPrivate() : q(nullptr), importItemModel(nullptr), filter(nullptr) { } void init(ImportFilterModel* const _q); Q_SIGNALS: void reAddCamItemInfos(const QList&); void reAddingFinished(); public: ImportFilterModel* q; ImportItemModel* importItemModel; CamItemSortSettings sorter; Filter* filter; }; void ImportFilterModel::ImportFilterModelPrivate::init(ImportFilterModel* const _q) { q = _q; } ImportFilterModel::ImportFilterModel(QObject* const parent) : ImportSortFilterModel(parent), d_ptr(new ImportFilterModelPrivate) { d_ptr->init(this); } ImportFilterModel::~ImportFilterModel() { Q_D(ImportFilterModel); delete d; } QVariant ImportFilterModel::data(const QModelIndex& index, int role) const { Q_D(const ImportFilterModel); if (!index.isValid()) { return QVariant(); } switch (role) { case DCategorizedSortFilterProxyModel::CategoryDisplayRole: return categoryIdentifier(d->importItemModel->camItemInfoRef(mapToSource(index))); case CategorizationModeRole: return d->sorter.categorizationMode; case SortOrderRole: return d->sorter.sortRole; case CategoryFormatRole: return d->importItemModel->camItemInfoRef(mapToSource(index)).mime; case CategoryDateRole: return d->importItemModel->camItemInfoRef(mapToSource(index)).ctime; case ImportFilterModelPointerRole: return QVariant::fromValue(const_cast(this)); } return DCategorizedSortFilterProxyModel::data(index, role); } ImportFilterModel* ImportFilterModel::importFilterModel() const { return const_cast(this); } // --- Sorting and Categorization ---------------------------------------------- void ImportFilterModel::setCamItemSortSettings(const CamItemSortSettings& sorter) { Q_D(ImportFilterModel); d->sorter = sorter; setCategorizedModel(d->sorter.categorizationMode != CamItemSortSettings::NoCategories); invalidate(); } void ImportFilterModel::setCategorizationMode(CamItemSortSettings::CategorizationMode mode) { Q_D(ImportFilterModel); d->sorter.setCategorizationMode(mode); setCamItemSortSettings(d->sorter); } void ImportFilterModel::setSortRole(CamItemSortSettings::SortRole role) { Q_D(ImportFilterModel); d->sorter.setSortRole(role); setCamItemSortSettings(d->sorter); } void ImportFilterModel::setSortOrder(CamItemSortSettings::SortOrder order) { Q_D(ImportFilterModel); d->sorter.setSortOrder(order); setCamItemSortSettings(d->sorter); } void ImportFilterModel::setStringTypeNatural(bool natural) { Q_D(ImportFilterModel); d->sorter.setStringTypeNatural(natural); setCamItemSortSettings(d->sorter); } void ImportFilterModel::setFilter(Digikam::Filter* filter) { Q_D(ImportFilterModel); d->filter = filter; invalidateFilter(); } void ImportFilterModel::setCameraThumbsController(CameraThumbsCtrl* const thumbsCtrl) { Q_D(ImportFilterModel); d->importItemModel->setCameraThumbsController(thumbsCtrl); } void ImportFilterModel::setSendCamItemInfoSignals(bool sendSignals) { if (sendSignals) { connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(slotRowsInserted(QModelIndex,int,int))); connect(this, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int))); } else { disconnect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(slotRowsInserted(QModelIndex,int,int))); disconnect(this, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int))); } } void ImportFilterModel::slotRowsInserted(const QModelIndex& /*parent*/, int start, int end) { QList infos; for (int i = start ; i < end ; ++i) { infos << camItemInfo(index(i, 0)); } emit camItemInfosAdded(infos); } void ImportFilterModel::slotRowsAboutToBeRemoved(const QModelIndex& /*parent*/, int start, int end) { QList infos; for (int i = start ; i < end ; ++i) { infos << camItemInfo(index(i, 0)); } emit camItemInfosAboutToBeRemoved(infos); } void ImportFilterModel::setDirectSourceImportModel(ImportItemModel* const sourceModel) { Q_D(ImportFilterModel); if (d->importItemModel) { - //disconnect(d->importItemModel, SIGNAL(modelReset()), - // this, SLOT(slotModelReset())); - - //TODO: slotModelReset(); will be added when implementing filtering options +/* + disconnect(d->importItemModel, SIGNAL(modelReset()), + this, SLOT(slotModelReset())); + // TODO: slotModelReset(); will be added when implementing filtering options +*/ disconnect(d->importItemModel, SIGNAL(processAdded(QList)), this, SLOT(slotProcessAdded(QList))); } // TODO do we need to delete the old one? + d->importItemModel = sourceModel; if (d->importItemModel) { - //connect(d, SIGNAL(reAddCamItemInfos(QList)), - // d->importItemModel, SLOT(reAddCamItemInfos(QList))); +/* + connect(d, SIGNAL(reAddCamItemInfos(QList)), + d->importItemModel, SLOT(reAddCamItemInfos(QList))); - //connect(d, SIGNAL(reAddingFinished()), - // d->importItemModel, SLOT(reAddingFinished())); - - //TODO: connect(d->importItemModel, SIGNAL(modelReset()), - // this, SLOT(slotModelReset())); + connect(d, SIGNAL(reAddingFinished()), + d->importItemModel, SLOT(reAddingFinished())); + connect(d->importItemModel, SIGNAL(modelReset()), + this, SLOT(slotModelReset())); +*/ connect(d->importItemModel, SIGNAL(processAdded(QList)), this, SLOT(slotProcessAdded(QList))); } setSourceModel(d->importItemModel); } void ImportFilterModel::slotProcessAdded(const QList&) { invalidate(); } int ImportFilterModel::compareCategories(const QModelIndex& left, const QModelIndex& right) const { Q_D(const ImportFilterModel); if (!d->sorter.isCategorized()) { return 0; } if (!left.isValid() || !right.isValid()) { return -1; } return compareInfosCategories(d->importItemModel->camItemInfoRef(left), d->importItemModel->camItemInfoRef(right)); } bool ImportFilterModel::subSortLessThan(const QModelIndex& left, const QModelIndex& right) const { Q_D(const ImportFilterModel); if (!left.isValid() || !right.isValid()) { return true; } if (left == right) { return false; } const CamItemInfo& leftInfo = d->importItemModel->camItemInfoRef(left); const CamItemInfo& rightInfo = d->importItemModel->camItemInfoRef(right); if (leftInfo == rightInfo) { return d->sorter.lessThan(left.data(ImportItemModel::ExtraDataRole), right.data(ImportItemModel::ExtraDataRole)); } return infosLessThan(leftInfo, rightInfo); } int ImportFilterModel::compareInfosCategories(const CamItemInfo& left, const CamItemInfo& right) const { Q_D(const ImportFilterModel); + return d->sorter.compareCategories(left, right); } bool ImportFilterModel::infosLessThan(const CamItemInfo& left, const CamItemInfo& right) const { Q_D(const ImportFilterModel); + return d->sorter.lessThan(left, right); } QString ImportFilterModel::categoryIdentifier(const CamItemInfo& info) const { Q_D(const ImportFilterModel); switch (d->sorter.categorizationMode) { case CamItemSortSettings::NoCategories: return QString(); + case CamItemSortSettings::CategoryByFolder: return info.folder; + case CamItemSortSettings::CategoryByFormat: return info.mime; + case CamItemSortSettings::CategoryByDate: return info.ctime.date().toString(Qt::ISODate); + default: return QString(); } } bool ImportFilterModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const { Q_D(const ImportFilterModel); if (!d->filter) { return true; } QModelIndex idx = sourceModel()->index(source_row, 0, source_parent); const CamItemInfo &info = d->importItemModel->camItemInfo(idx); if (d->filter->matchesCurrentFilter(info)) { return true; } return false; } // ------------------------------------------------------------------------------------------------------- NoDuplicatesImportFilterModel::NoDuplicatesImportFilterModel(QObject* const parent) : ImportSortFilterModel(parent) { } bool NoDuplicatesImportFilterModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const { QModelIndex index = sourceModel()->index(source_row, 0, source_parent); if (index.data(ImportItemModel::ExtraDataDuplicateCount).toInt() <= 1) { return true; } QModelIndex previousIndex = sourceModel()->index(source_row - 1, 0, source_parent); if (!previousIndex.isValid()) { return true; } if (sourceImportModel()->camItemId(mapFromDirectSourceToSourceImportModel(index)) == sourceImportModel()->camItemId(mapFromDirectSourceToSourceImportModel(previousIndex))) { return false; } return true; } } // namespace Digikam diff --git a/core/utilities/import/models/importfiltermodel.h b/core/utilities/import/models/importfiltermodel.h index 037c0a6fa3..28fbfc8267 100644 --- a/core/utilities/import/models/importfiltermodel.h +++ b/core/utilities/import/models/importfiltermodel.h @@ -1,231 +1,249 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2012-21-06 * Description : Qt filter model for import items * * Copyright (C) 2012 by Islam Wazery * * 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_IMPORT_FILTER_MODEL_H #define DIGIKAM_IMPORT_FILTER_MODEL_H // Qt includes #include // Local includes #include "dcategorizedsortfilterproxymodel.h" #include "camitemsortsettings.h" #include "importimagemodel.h" namespace Digikam { class Filter; class ImportFilterModel; class ImportSortFilterModel : public DCategorizedSortFilterProxyModel { Q_OBJECT public: explicit ImportSortFilterModel(QObject* const parent = nullptr); ~ImportSortFilterModel(); void setSourceImportModel(ImportItemModel* const sourceModel); ImportItemModel* sourceImportModel() const; void setSourceFilterModel(ImportSortFilterModel* const sourceModel); ImportSortFilterModel* sourceFilterModel() const; - /// Convenience methods mapped to ImportItemModel. - /// Mentioned indexes returned come from the source import image model. + /** + * Convenience methods mapped to ImportItemModel. + * Mentioned indexes returned come from the source import image model. + */ QModelIndex mapToSourceImportModel(const QModelIndex& proxyIndex) const; QModelIndex mapFromSourceImportModel(const QModelIndex& importModelIndex) const; QModelIndex mapFromDirectSourceToSourceImportModel(const QModelIndex& sourceModelIndex) const; QList mapListToSource(const QList& indexes) const; QList mapListFromSource(const QList& sourceIndexes) const; CamItemInfo camItemInfo(const QModelIndex& index) const; qlonglong camItemId(const QModelIndex& index) const; QList camItemInfos(const QList& indexes) const; QList camItemIds(const QList& indexes) const; QModelIndex indexForPath(const QString& filePath) const; QModelIndex indexForCamItemInfo(const CamItemInfo& info) const; QModelIndex indexForCamItemId(qlonglong id) const; - /** Returns a list of all camera infos, sorted according to this model. - * If you do not need a sorted list, use ImportItemModel's camItemInfo() method. + /** + * Returns a list of all camera infos, sorted according to this model. + * If you do not need a sorted list, use ImportItemModel's camItemInfo() method. */ QList camItemInfosSorted() const; /// Returns this, any chained ImportFilterModel, or 0. virtual ImportFilterModel* importFilterModel() const; protected: virtual void setSourceModel(QAbstractItemModel* sourceModel); /// Reimplement if needed. Called only when model shall be set as (direct) sourceModel. virtual void setDirectSourceImportModel(ImportItemModel* const sourceModel); protected: ImportSortFilterModel* m_chainedModel; }; // ------------------------------------------------------------------------------------------ class ImportFilterModel : public ImportSortFilterModel { Q_OBJECT public: enum ImportFilterModelRoles { /// Returns the current categorization mode. CategorizationModeRole = ImportItemModel::FilterModelRoles + 1, /// Returns the current sort order. SortOrderRole = ImportItemModel::FilterModelRoles + 2, /// Returns the format of the index which is used for category. CategoryFormatRole = ImportItemModel::FilterModelRoles + 3, /// Returns the date of the index which is used for category. CategoryDateRole = ImportItemModel::FilterModelRoles + 4, /// Returns true if the given camera item is a group leader, and the group is opened. - //TODO: GroupIsOpenRole = ImportItemModel::FilterModelRoles + 5 + // TODO: GroupIsOpenRole = ImportItemModel::FilterModelRoles + 5 ImportFilterModelPointerRole = ImportItemModel::FilterModelRoles + 50 }; public: explicit ImportFilterModel(QObject* const parent = nullptr); ~ImportFilterModel(); CamItemSortSettings camItemSortSettings() const; void setCamItemSortSettings(const CamItemSortSettings& sorter); /// Enables sending camItemInfosAdded and camItemInfosAboutToBeRemoved. void setSendCamItemInfoSignals(bool sendSignals); - //TODO: Implement grouping in import tool. - //bool isGroupOpen(qlonglong group) const; - //bool isAllGroupsOpen() const; - + // TODO: Implement grouping in import tool. +/* + bool isGroupOpen(qlonglong group) const; + bool isAllGroupsOpen() const; +*/ virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; virtual ImportFilterModel* importFilterModel() const override; public Q_SLOTS: void setCategorizationMode(CamItemSortSettings::CategorizationMode mode); void setSortRole(CamItemSortSettings::SortRole role); void setSortOrder(CamItemSortSettings::SortOrder order); void setStringTypeNatural(bool natural); void setFilter(Filter*); void setCameraThumbsController(CameraThumbsCtrl* const thumbsCtrl); - //TODO: Implement grouping in import tool. - //void setGroupOpen(qlonglong group, bool open); - //void toggleGroupOpen(qlonglong group); - //void setAllGroupsOpen(bool open); + // TODO: Implement grouping in import tool. +/* + void setGroupOpen(qlonglong group, bool open); + void toggleGroupOpen(qlonglong group); + void setAllGroupsOpen(bool open); +*/ + /** + * Changes the current image filter settings and refilters. + */ - /** Changes the current image filter settings and refilters. */ - //TODO: Implement filtering in import tool. - //virtual void setItemFilterSettings(const ItemFilterSettings& settings); + // TODO: Implement filtering in import tool. +/* + virtual void setItemFilterSettings(const ItemFilterSettings& settings); +*/ + /** + * Changes the current image sort settings and resorts. + */ - /** Changes the current image sort settings and resorts. */ - //TODO: virtual void setItemSortSettings(const ItemSortSettings& settings); + // TODO: Implement filtering in import tool. +/* + virtual void setItemSortSettings(const ItemSortSettings& settings); +*/ Q_SIGNALS: /** These signals need to be explicitly enabled with setSendItemInfoSignals(). */ void camItemInfosAdded(const QList& infos); void camItemInfosAboutToBeRemoved(const QList& infos); protected Q_SLOTS: void slotRowsInserted(const QModelIndex& parent, int start, int end); void slotRowsAboutToBeRemoved(const QModelIndex& parent, int start, int end); void slotProcessAdded(const QList&); public: // Declared as public because of use in sub-classes. class ImportFilterModelPrivate; protected: ImportFilterModelPrivate* const d_ptr; protected: virtual void setDirectSourceImportModel(ImportItemModel* const sourceModel) override; virtual bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override; virtual int compareCategories(const QModelIndex& left, const QModelIndex& right) const override; virtual bool subSortLessThan(const QModelIndex& left, const QModelIndex& right) const override; - /** Reimplement to customize category sorting, - * Return negative if category of left < category right, - * Return 0 if left and right are in the same category, else return positive. + /** + * Reimplement to customize category sorting, + * Return negative if category of left < category right, + * Return 0 if left and right are in the same category, else return positive. */ virtual int compareInfosCategories(const CamItemInfo& left, const CamItemInfo& right) const; - /** Reimplement to customize sorting. Do not take categories into account here. + /** + * Reimplement to customize sorting. Do not take categories into account here. */ virtual bool infosLessThan(const CamItemInfo& left, const CamItemInfo& right) const; - /** Returns a unique identifier for the category if info. The string need not be for user display. + /** + * Returns a unique identifier for the category if info. The string need not be for user display. */ virtual QString categoryIdentifier(const CamItemInfo& info) const; private: Q_DECLARE_PRIVATE(ImportFilterModel) }; // ----------------------------------------------------------------------------------------------------- class NoDuplicatesImportFilterModel : public ImportSortFilterModel { Q_OBJECT public: explicit NoDuplicatesImportFilterModel(QObject* const parent = nullptr); protected: virtual bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const; }; } // namespace Digikam Q_DECLARE_METATYPE(Digikam::ImportFilterModel*) #endif // DIGIKAM_IMPORT_FILTER_MODEL_H diff --git a/core/utilities/import/models/importimagemodel.cpp b/core/utilities/import/models/importimagemodel.cpp index eac2da1152..03233f5e18 100644 --- a/core/utilities/import/models/importimagemodel.cpp +++ b/core/utilities/import/models/importimagemodel.cpp @@ -1,1083 +1,1100 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2012-05-22 * Description : Qt item model for camera entries * * Copyright (C) 2012 by Islam Wazery * * 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 "importimagemodel.h" // Qt includes #include // Local includes #include "digikam_debug.h" #include "coredbdownloadhistory.h" #include "cameracontroller.h" namespace Digikam { class Q_DECL_HIDDEN ImportItemModel::Private { public: explicit Private() : controller(nullptr), keepFileUrlCache(false), refreshing(false), reAdding(false), incrementalRefreshRequested(false), sendRemovalSignals(false), incrementalUpdater(nullptr) { } inline bool isValid(const QModelIndex& index) { - return (index.isValid() && + return ( + index.isValid() && (index.row() >= 0) && (index.row() < infos.size()) ); } public: CameraController* controller; CamItemInfoList infos; CamItemInfo camItemInfo; QHash idHash; QHash fileUrlHash; bool keepFileUrlCache; bool refreshing; bool reAdding; bool incrementalRefreshRequested; bool sendRemovalSignals; class ImportItemModelIncrementalUpdater* incrementalUpdater; }; // ---------------------------------------------------------------------------------------------------- typedef QPair IntPair; typedef QList IntPairList; class Q_DECL_HIDDEN ImportItemModelIncrementalUpdater { public: explicit ImportItemModelIncrementalUpdater(ImportItemModel::Private* const d); void appendInfos(const QList& infos); void aboutToBeRemovedInModel(const IntPairList& aboutToBeRemoved); QList oldIndexes(); static QList toContiguousPairs(const QList& ids); public: QHash oldIds; QList newInfos; QList modelRemovals; }; // ---------------------------------------------------------------------------------------------------- ImportItemModel::ImportItemModel(QObject* const parent) : QAbstractListModel(parent), d(new Private) { } ImportItemModel::~ImportItemModel() { delete d; } void ImportItemModel::setCameraThumbsController(CameraThumbsCtrl* const thumbsCtrl) { d->controller = thumbsCtrl->cameraController(); connect(d->controller, SIGNAL(signalFileList(CamItemInfoList)), SLOT(addCamItemInfos(CamItemInfoList))); connect(d->controller, SIGNAL(signalDeleted(QString,QString,bool)), SLOT(slotFileDeleted(QString,QString,bool))); connect(d->controller, SIGNAL(signalUploaded(CamItemInfo)), SLOT(slotFileUploaded(CamItemInfo))); } void ImportItemModel::setKeepsFileUrlCache(bool keepCache) { d->keepFileUrlCache = keepCache; } bool ImportItemModel::keepsFileUrlCache() const { return d->keepFileUrlCache; } bool ImportItemModel::isEmpty() const { return d->infos.isEmpty(); } CamItemInfo ImportItemModel::camItemInfo(const QModelIndex& index) const { if (!d->isValid(index)) { return CamItemInfo(); } return d->infos.at(index.row()); } CamItemInfo& ImportItemModel::camItemInfoRef(const QModelIndex& index) const { if (!d->isValid(index)) { return d->camItemInfo; } return d->infos[index.row()]; } qlonglong ImportItemModel::camItemId(const QModelIndex& index) const { if (!d->isValid(index)) { return -1; } return d->infos.at(index.row()).id; } QList ImportItemModel::camItemInfos(const QList& indexes) const { QList infos; foreach (const QModelIndex& index, indexes) { infos << camItemInfo(index); } return infos; } QList ImportItemModel::camItemIds(const QList& indexes) const { QList ids; foreach (const QModelIndex& index, indexes) { ids << camItemId(index); } return ids; } CamItemInfo ImportItemModel::camItemInfo(int row) const { - if (row < 0 || row >= d->infos.size()) + if ((row < 0) || (row >= d->infos.size())) { return CamItemInfo(); } return d->infos.at(row); } CamItemInfo& ImportItemModel::camItemInfoRef(int row) const { - if (row < 0 || row >= d->infos.size()) + if ((row < 0) || (row >= d->infos.size())) { return d->camItemInfo; } return d->infos[row]; } qlonglong ImportItemModel::camItemId(int row) const { - if (row < 0 || row >= d->infos.size()) + if ((row < 0) || (row >= d->infos.size())) { return -1; } return d->infos.at(row).id; } QModelIndex ImportItemModel::indexForCamItemInfo(const CamItemInfo& info) const { return indexForCamItemId(info.id); } QList ImportItemModel::indexesForCamItemInfo(const CamItemInfo& info) const { return indexesForCamItemId(info.id); } QModelIndex ImportItemModel::indexForCamItemId(qlonglong id) const { int index = d->idHash.value(id, -1); if (index == -1) { return QModelIndex(); } return createIndex(index, 0); } QList ImportItemModel::indexesForCamItemId(qlonglong id) const { QList indexes; QHash::const_iterator it; for (it = d->idHash.constFind(id) ; it != d->idHash.constEnd() && it.key() == id ; ++it) { indexes << createIndex(it.value(), 0); } return indexes; } int ImportItemModel::numberOfIndexesForCamItemInfo(const CamItemInfo& info) const { return numberOfIndexesForCamItemId(info.id); } int ImportItemModel::numberOfIndexesForCamItemId(qlonglong id) const { int count = 0; QHash::const_iterator it; for (it = d->idHash.constFind(id) ; it != d->idHash.constEnd() && it.key() == id ; ++it) { ++count; } return count; } // static method CamItemInfo ImportItemModel::retrieveCamItemInfo(const QModelIndex& index) { if (!index.isValid()) { return CamItemInfo(); } ImportItemModel* const model = index.data(ImportItemModelPointerRole).value(); int row = index.data(ImportItemModelInternalId).toInt(); if (!model) { return CamItemInfo(); } return model->camItemInfo(row); } // static method qlonglong ImportItemModel::retrieveCamItemId(const QModelIndex& index) { if (!index.isValid()) { return -1; } ImportItemModel* const model = index.data(ImportItemModelPointerRole).value(); int row = index.data(ImportItemModelInternalId).toInt(); if (!model) { return -1; } return model->camItemId(row); } QModelIndex ImportItemModel::indexForUrl(const QUrl& fileUrl) const { if (d->keepFileUrlCache) { return indexForCamItemId(d->fileUrlHash.value(fileUrl.toLocalFile())); } else { const int size = d->infos.size(); for (int i = 0 ; i < size ; ++i) { if (d->infos.at(i).url() == fileUrl) { return createIndex(i, 0); } } } return QModelIndex(); } QList ImportItemModel::indexesForUrl(const QUrl& fileUrl) const { if (d->keepFileUrlCache) { return indexesForCamItemId(d->fileUrlHash.value(fileUrl.toLocalFile())); } else { QList indexes; const int size = d->infos.size(); for (int i = 0 ; i < size ; ++i) { if (d->infos.at(i).url() == fileUrl) { indexes << createIndex(i, 0); } } return indexes; } } CamItemInfo ImportItemModel::camItemInfo(const QUrl& fileUrl) const { if (d->keepFileUrlCache) { qlonglong id = d->fileUrlHash.value(fileUrl.toLocalFile()); if (id) { int index = d->idHash.value(id, -1); if (index != -1) { return d->infos.at(index); } } } else { foreach (const CamItemInfo& info, d->infos) { if (info.url() == fileUrl) { return info; } } } return CamItemInfo(); } QList ImportItemModel::camItemInfos(const QUrl& fileUrl) const { QList infos; if (d->keepFileUrlCache) { qlonglong id = d->fileUrlHash.value(fileUrl.toLocalFile()); if (id) { foreach (int index, d->idHash.values(id)) { infos << d->infos.at(index); } } } else { foreach (const CamItemInfo& info, d->infos) { if (info.url() == fileUrl) { infos << info; } } } return infos; } void ImportItemModel::addCamItemInfo(const CamItemInfo& info) { addCamItemInfos(QList() << info); } void ImportItemModel::addCamItemInfos(const CamItemInfoList& infos) { if (infos.isEmpty()) { return; } if (d->incrementalUpdater) { d->incrementalUpdater->appendInfos(infos); } else { appendInfos(infos); } } void ImportItemModel::addCamItemInfoSynchronously(const CamItemInfo& info) { addCamItemInfosSynchronously(QList() << info); } void ImportItemModel::addCamItemInfosSynchronously(const CamItemInfoList& infos) { if (infos.isEmpty()) { return; } publiciseInfos(infos); emit processAdded(infos); } void ImportItemModel::clearCamItemInfos() { beginResetModel(); d->infos.clear(); d->idHash.clear(); d->fileUrlHash.clear(); delete d->incrementalUpdater; d->incrementalUpdater = nullptr; d->reAdding = false; d->refreshing = false; d->incrementalRefreshRequested = false; camItemInfosCleared(); endResetModel(); } // TODO unused void ImportItemModel::setCamItemInfos(const CamItemInfoList& infos) { clearCamItemInfos(); addCamItemInfos(infos); } QList ImportItemModel::camItemInfos() const { return d->infos; } QList ImportItemModel::camItemIds() const { return d->idHash.keys(); } QList ImportItemModel::uniqueCamItemInfos() const { QList uniqueInfos; const int size = d->infos.size(); for (int i = 0 ; i < size ; ++i) { const CamItemInfo& info = d->infos.at(i); if (d->idHash.value(info.id) == i) { uniqueInfos << info; } } return uniqueInfos; } bool ImportItemModel::hasImage(qlonglong id) const { return d->idHash.contains(id); } bool ImportItemModel::hasImage(const CamItemInfo& info) const { return d->fileUrlHash.contains(info.url().toLocalFile()); } void ImportItemModel::emitDataChangedForAll() { if (d->infos.isEmpty()) { return; } QModelIndex first = createIndex(0, 0); QModelIndex last = createIndex(d->infos.size() - 1, 0); emit dataChanged(first, last); } void ImportItemModel::emitDataChangedForSelections(const QItemSelection& selection) { if (!selection.isEmpty()) { foreach (const QItemSelectionRange& range, selection) { emit dataChanged(range.topLeft(), range.bottomRight()); } } } void ImportItemModel::appendInfos(const CamItemInfoList& infos) { if (infos.isEmpty()) { return; } publiciseInfos(infos); } void ImportItemModel::reAddCamItemInfos(const CamItemInfoList& infos) { publiciseInfos(infos); } void ImportItemModel::reAddingFinished() { d->reAdding = false; cleanSituationChecks(); } void ImportItemModel::slotFileDeleted(const QString& folder, const QString& file, bool status) { Q_UNUSED(status) QUrl url = QUrl::fromLocalFile(folder); url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + QLatin1Char('/') + file); CamItemInfo info = camItemInfo(url); removeCamItemInfo(info); } void ImportItemModel::slotFileUploaded(const CamItemInfo& info) { addCamItemInfo(info); } void ImportItemModel::startRefresh() { d->refreshing = true; } void ImportItemModel::finishRefresh() { d->refreshing = false; cleanSituationChecks(); } bool ImportItemModel::isRefreshing() const { return d->refreshing; } void ImportItemModel::cleanSituationChecks() { // For starting an incremental refresh we want a clear situation: // Any remaining batches from non-incremental refreshing subclasses have been received in appendInfos(), // any batches sent to preprocessor for re-adding have been re-added. + if (d->refreshing || d->reAdding) { return; } if (d->incrementalRefreshRequested) { d->incrementalRefreshRequested = false; emit readyForIncrementalRefresh(); } else { emit allRefreshingFinished(); } } void ImportItemModel::publiciseInfos(const CamItemInfoList& infos) { if (infos.isEmpty()) { return; } emit itemInfosAboutToBeAdded(infos); const int firstNewIndex = d->infos.size(); const int lastNewIndex = d->infos.size() + infos.size() -1; beginInsertRows(QModelIndex(), firstNewIndex, lastNewIndex); d->infos << infos; for (int i = firstNewIndex ; i <= lastNewIndex ; ++i) { CamItemInfo& info = d->infos[i]; // TODO move this to a separate thread, see CameraHistoryUpdater // TODO can we/do we want to differentiate at all between whether the status is unknown and not downloaded? + info.downloaded = CoreDbDownloadHistory::status(QString::fromUtf8(d->controller->cameraMD5ID()), info.name, info.size, info.ctime); + // TODO is this safe? if so, is there a need to store this inside idHash separately? info.id = i; qlonglong id = info.id; d->idHash.insertMulti(id, i); if (d->keepFileUrlCache) { d->fileUrlHash[info.url().toLocalFile()] = id; } } endInsertRows(); emit processAdded(infos); emit itemInfosAdded(infos); } void ImportItemModel::requestIncrementalRefresh() { if (d->reAdding) { d->incrementalRefreshRequested = true; } else { emit readyForIncrementalRefresh(); } } bool ImportItemModel::hasIncrementalRefreshPending() const { return d->incrementalRefreshRequested; } void ImportItemModel::startIncrementalRefresh() { delete d->incrementalUpdater; d->incrementalUpdater = new ImportItemModelIncrementalUpdater(d); } void ImportItemModel::finishIncrementalRefresh() { if (!d->incrementalUpdater) { return; } // remove old entries + QList > pairs = d->incrementalUpdater->oldIndexes(); removeRowPairs(pairs); // add new indexes + appendInfos(d->incrementalUpdater->newInfos); delete d->incrementalUpdater; d->incrementalUpdater = nullptr; } template static bool pairsContain(const List& list, T value) { typename List::const_iterator middle; typename List::const_iterator begin = list.begin(); typename List::const_iterator end = list.end(); int n = int(end - begin); while (n > 0) { - int half = n >> 1; + int half = (n >> 1); middle = begin + half; if ((middle->first <= value) && (middle->second >= value)) { return true; } else if (middle->second < value) { begin = middle + 1; n -= half + 1; } else { n = half; } } return false; } void ImportItemModel::removeIndex(const QModelIndex& index) { removeIndexs(QList() << index); } void ImportItemModel::removeIndexs(const QList& indexes) { QList indexesList; foreach (const QModelIndex& index, indexes) { if (d->isValid(index)) { indexesList << index.row(); } } if (indexesList.isEmpty()) { return; } removeRowPairsWithCheck(ImportItemModelIncrementalUpdater::toContiguousPairs(indexesList)); } void ImportItemModel::removeCamItemInfo(const CamItemInfo& info) { removeCamItemInfos(QList() << info); } void ImportItemModel::removeCamItemInfos(const QList& infos) { QList indexesList; foreach (const CamItemInfo& info, infos) { QModelIndex index = indexForCamItemId(info.id); if (index.isValid()) { indexesList << index.row(); } } removeRowPairsWithCheck(ImportItemModelIncrementalUpdater::toContiguousPairs(indexesList)); } void ImportItemModel::setSendRemovalSignals(bool send) { d->sendRemovalSignals = send; } void ImportItemModel::removeRowPairsWithCheck(const QList >& toRemove) { if (d->incrementalUpdater) { d->incrementalUpdater->aboutToBeRemovedInModel(toRemove); } removeRowPairs(toRemove); } void ImportItemModel::removeRowPairs(const QList >& toRemove) { if (toRemove.isEmpty()) { return; } // Remove old indexes // Keep in mind that when calling beginRemoveRows all structures announced to be removed // must still be valid, and this includes our hashes as well, which limits what we can optimize int removedRows = 0; int offset = 0; foreach (const IntPair& pair, toRemove) { const int begin = pair.first - offset; const int end = pair.second - offset; removedRows = end - begin + 1; // when removing from the list, all subsequent indexes are affected + offset += removedRows; QList removedInfos; if (d->sendRemovalSignals) { std::copy(d->infos.begin() + begin, d->infos.begin() + end, removedInfos.begin()); emit itemInfosAboutToBeRemoved(removedInfos); } itemInfosAboutToBeRemoved(begin, end); beginRemoveRows(QModelIndex(), begin, end); // update idHash - which points to indexes of d->infos + QHash::iterator it; for (it = d->idHash.begin() ; it != d->idHash.end() ; ) { if (it.value() >= begin) { if (it.value() > end) { // after the removed interval, adjust index + it.value() -= removedRows; } else { // in the removed interval + it = d->idHash.erase(it); continue; } } ++it; } // remove from list + d->infos.erase(d->infos.begin() + begin, d->infos.begin() + (end + 1)); endRemoveRows(); if (d->sendRemovalSignals) { emit itemInfosRemoved(removedInfos); } } // tidy up: remove old indexes from file path hash now + if (d->keepFileUrlCache) { QHash::iterator it; for (it = d->fileUrlHash.begin() ; it!= d->fileUrlHash.end() ; ) { if (pairsContain(toRemove, it.value())) { it = d->fileUrlHash.erase(it); } else { ++it; } } } } // ------------ ImportItemModelIncrementalUpdater ------------ ImportItemModelIncrementalUpdater::ImportItemModelIncrementalUpdater(ImportItemModel::Private* const d) { oldIds = d->idHash; } void ImportItemModelIncrementalUpdater::aboutToBeRemovedInModel(const IntPairList& toRemove) { modelRemovals << toRemove; } void ImportItemModelIncrementalUpdater::appendInfos(const QList& infos) { for (int i = 0 ; i < infos.size() ; ++i) { const CamItemInfo& info = infos.at(i); bool found = false; QHash::iterator it; for (it = oldIds.find(info.id) ; it != oldIds.end() ; ++it) { if (it.key() == info.id) { found = true; break; } } if (found) { oldIds.erase(it); } else { newInfos << info; } } } QList > ImportItemModelIncrementalUpdater::toContiguousPairs(const QList& unsorted) { // Take the given indices and return them as contiguous pairs [begin, end] QList > pairs; if (unsorted.isEmpty()) { return pairs; } QList indices(unsorted); std::sort(indices.begin(), indices.end()); QPair pair(indices.first(), indices.first()); for (int i = 1 ; i < indices.size() ; ++i) { const int &index = indices.at(i); if (index == pair.second + 1) { pair.second = index; continue; } pairs << pair; // insert last pair pair.first = index; pair.second = index; } pairs << pair; return pairs; } QList > ImportItemModelIncrementalUpdater::oldIndexes() { // first, apply all changes to indexes by direct removal in model // while the updater was active + foreach (const IntPairList& list, modelRemovals) { int removedRows = 0; int offset = 0; foreach (const IntPair& pair, list) { const int begin = pair.first - offset; const int end = pair.second - offset; // inclusive removedRows = end - begin + 1; // when removing from the list, all subsequent indexes are affected + offset += removedRows; // update idHash - which points to indexes of d->infos, and these change now! + QHash::iterator it; for (it = oldIds.begin() ; it != oldIds.end() ; ) { if (it.value() >= begin) { if (it.value() > end) { // after the removed interval: adjust index + it.value() -= removedRows; } else { // in the removed interval + it = oldIds.erase(it); continue; } } ++it; } } } modelRemovals.clear(); return toContiguousPairs(oldIds.values()); } // ------------ QAbstractItemModel implementation ------------- QVariant ImportItemModel::data(const QModelIndex& index, int role) const { if (!d->isValid(index)) { return QVariant(); } switch(role) { case Qt::DisplayRole: case Qt::ToolTipRole: return d->infos.at(index.row()).name; break; case ImportItemModelPointerRole: return QVariant::fromValue(const_cast(this)); break; case ImportItemModelInternalId: return index.row(); break; } return QVariant(); } QVariant ImportItemModel::headerData(int section, Qt::Orientation orientation, int role) const { Q_UNUSED(section) Q_UNUSED(orientation) Q_UNUSED(role) return QVariant(); } int ImportItemModel::rowCount(const QModelIndex& parent) const { if (parent.isValid()) { return 0; } return d->infos.size(); } Qt::ItemFlags ImportItemModel::flags(const QModelIndex& index) const { if (!d->isValid(index)) { return nullptr; } Qt::ItemFlags f = Qt::ItemIsSelectable | Qt::ItemIsEnabled; f |= dragDropFlags(index); return f; } QModelIndex ImportItemModel::index(int row, int column, const QModelIndex& parent) const { - if (column != 0 || row < 0 || parent.isValid() || row >= d->infos.size()) + if ((column != 0) || (row < 0) || parent.isValid() || (row >= d->infos.size())) { return QModelIndex(); } return createIndex(row, 0); } } // namespace Digikam diff --git a/core/utilities/import/models/importimagemodel.h b/core/utilities/import/models/importimagemodel.h index 4c05ed96e6..d141d16253 100644 --- a/core/utilities/import/models/importimagemodel.h +++ b/core/utilities/import/models/importimagemodel.h @@ -1,302 +1,306 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2012-05-22 * Description : Qt model for camera entries * * Copyright (C) 2012 by Islam Wazery * * 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_IMPORT_IMAGE_MODEL_H #define DIGIKAM_IMPORT_IMAGE_MODEL_H // Qt includes #include #include // Local includes #include "dragdropimplementations.h" #include "camerathumbsctrl.h" #include "camiteminfo.h" namespace Digikam { class AbstractItemDragDropHandler; class ImportItemModel : public QAbstractListModel, public DragDropModelImplementation { Q_OBJECT public: enum ImportItemModelRoles { /// An ImportItemModel* pointer to this model ImportItemModelPointerRole = Qt::UserRole, ImportItemModelInternalId = Qt::UserRole + 1, - /// Returns a thumbnail pixmap. May be implemented by subclasses. - /// Returns either a valid pixmap or a null QVariant. + /** + * Returns a thumbnail pixmap. May be implemented by subclasses. + * Returns either a valid pixmap or a null QVariant. + */ ThumbnailRole = Qt::UserRole + 2, /// Return (optional) extraData field ExtraDataRole = Qt::UserRole + 3, /// Returns the number of duplicate indexes for the same image id ExtraDataDuplicateCount = Qt::UserRole + 6, FilterModelRoles = Qt::UserRole + 100 }; public: explicit ImportItemModel(QObject* const parent = nullptr); ~ImportItemModel(); - // Used to set the camera controller, and connect with it. + /// Used to set the camera controller, and connect with it. virtual void setCameraThumbsController(CameraThumbsCtrl* const controller); - /** If a cache is kept, lookup by file path is fast, - * without a cache it is O(n). Default is false. */ + /** + * If a cache is kept, lookup by file path is fast, + * without a cache it is O(n). Default is false. + */ void setKeepsFileUrlCache(bool keepCache); bool keepsFileUrlCache() const; /** - * Returns the CamItemInfo object, reference from the underlying data pointed to by the index. - * For camItemInfo and camItemInfoId If the index is not valid they will return a null CamItemInfo, and 0 - * respectively, camItemInfoRef must not be called with an invalid index as it will crash. + * Returns the CamItemInfo object, reference from the underlying data pointed to by the index. + * For camItemInfo and camItemInfoId If the index is not valid they will return a null CamItemInfo, and 0 + * respectively, camItemInfoRef must not be called with an invalid index as it will crash. */ CamItemInfo camItemInfo(const QModelIndex& index) const; CamItemInfo& camItemInfoRef(const QModelIndex& index) const; qlonglong camItemId(const QModelIndex& index) const; CamItemInfoList camItemInfos(const QList& indexes) const; QList camItemIds(const QList& indexes) const; /** * Returns the CamItemInfo object, reference from the underlying data of * the given row (parent is the invalid QModelIndex, column is 0). * Note that camItemInfoRef must not be called with an invalid index as it will crash. */ CamItemInfo camItemInfo(int row) const; CamItemInfo& camItemInfoRef(int row) const; qlonglong camItemId(int row) const; /** * Return the index of a given CamItemInfo, if it exists in the model. */ QModelIndex indexForCamItemInfo(const CamItemInfo& info) const; QList indexesForCamItemInfo(const CamItemInfo& info) const; QModelIndex indexForCamItemId(qlonglong id) const; QList indexesForCamItemId(qlonglong id) const; /** * Returns the index or CamItemInfo object from the underlying data for * the given file url. In case of multiple occurrences of the same file, the simpler * overrides returns any one found first, use the QList methods to retrieve all occurrences. */ QModelIndex indexForUrl(const QUrl& fileUrl) const; QList indexesForUrl(const QUrl& fileUrl) const; CamItemInfo camItemInfo(const QUrl& fileUrl) const; QList camItemInfos(const QUrl& fileUrl) const; /** * Clears the CamItemInfos and resets the model. */ void clearCamItemInfos(); /** * addCamItemInfo() is asynchronous if a prepocessor is set. * This method first adds the info, synchronously. * Only afterwards, the preprocessor will have the opportunity to process it. * This method also bypasses any incremental updates. */ void addCamItemInfoSynchronously(const CamItemInfo& info); void addCamItemInfosSynchronously(const Digikam::CamItemInfoList& infos); /** * Clears and adds infos. */ void setCamItemInfos(const CamItemInfoList& infos); QList camItemInfos() const; QList camItemIds() const; QList uniqueCamItemInfos() const; bool hasImage(qlonglong id) const; bool hasImage(const CamItemInfo& info) const; bool isEmpty() const; /** * Remove the given infos or indexes directly from the model. */ void removeIndex(const QModelIndex& index); void removeIndexs(const QList& indexes); void removeCamItemInfo(const CamItemInfo& info); void removeCamItemInfos(const QList& infos); int numberOfIndexesForCamItemInfo(const CamItemInfo& info) const; int numberOfIndexesForCamItemId(qlonglong id) const; /** * Retrieve the CamItemInfo object from the data() function of the given index * The index may be from a QSortFilterProxyModel as long as an ImportItemModel is at the end. */ static CamItemInfo retrieveCamItemInfo(const QModelIndex& index); static qlonglong retrieveCamItemId(const QModelIndex& index); - // QAbstractListModel implementation + /// QAbstractListModel implementation virtual int rowCount(const QModelIndex& parent) const override; virtual QVariant data(const QModelIndex& index, int role) const override; virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override; virtual Qt::ItemFlags flags(const QModelIndex& index) const override; virtual QModelIndex index(int row, int column, const QModelIndex& parent) const override; - // DragDrop methods + /// DragDrop methods DECLARE_MODEL_DRAG_DROP_METHODS /** * Enable sending of itemInfosAboutToBeRemoved and itemsInfosRemoved signals. * Default: false */ void setSendRemovalSignals(bool send); /** * Returns true if this model is currently refreshing. * For a preprocessor this means that, although the preprocessor may currently have * processed all it got, more batches are to be expected. */ bool isRefreshing() const; Q_SIGNALS: /** * Informs that ItemInfos will be added to the model. * This signal is sent before the model data is changed and views are informed. */ void itemInfosAboutToBeAdded(const QList& infos); /** * Informs that ItemInfos have been added to the model. * This signal is sent after the model data is changed and views are informed. */ void itemInfosAdded(const QList& infos); /** * Informs that CamItemInfos will be removed from the model. * This signal is sent before the model data is changed and views are informed. * Note: You need to explicitly enable sending of this signal. It is not sent * in clearCamItemInfos(). */ void itemInfosAboutToBeRemoved(const QList& infos); /** * Informs that CamItemInfos have been removed from the model. * This signal is sent after the model data is changed and views are informed. * Note: You need to explicitly enable sending of this signal. It is not sent * in clearCamItemInfos(). */ void itemInfosRemoved(const QList& infos); /** * Connect to this signal only if you are the current preprocessor. */ void preprocess(const QList& infos); void processAdded(const QList& infos); /** * Signals that the model is right now ready to start an incremental refresh. * This is guaranteed only for the scope of emitting this signal. */ void readyForIncrementalRefresh(); /** * Signals that the model has finished currently with all scheduled * refreshing, full or incremental, and all preprocessing. * The model is in polished, clean situation right now. */ void allRefreshingFinished(); public Q_SLOTS: void reAddCamItemInfos(const CamItemInfoList& infos); void reAddingFinished(); void slotFileDeleted(const QString& folder, const QString& file, bool status); void slotFileUploaded(const CamItemInfo& info); void addCamItemInfo(const CamItemInfo& info); void addCamItemInfos(const CamItemInfoList& infos); protected: /** * Subclasses that add CamItemInfos in batches shall call startRefresh() * when they start sending batches and finishRefresh() when they have finished. * No incremental refreshes will be started while listing. * A clearCamItemInfos() always stops listing, calling finishRefresh() is then not necessary. */ void startRefresh(); void finishRefresh(); /** * As soon as the model is ready to start an incremental refresh, the signal * readyForIncrementalRefresh() will be emitted. The signal will be emitted inline * if the model is ready right now. */ void requestIncrementalRefresh(); bool hasIncrementalRefreshPending() const; /** * Starts an incremental refresh operation. You shall only call this method from a slot * connected to readyForIncrementalRefresh(). To initiate an incremental refresh, * call requestIncrementalRefresh(). */ void startIncrementalRefresh(); void finishIncrementalRefresh(); void emitDataChangedForAll(); void emitDataChangedForSelections(const QItemSelection& selection); - // Called when the internal storage is cleared. + /// Called when the internal storage is cleared. virtual void camItemInfosCleared() {}; - // Called before rowsAboutToBeRemoved + /// Called before rowsAboutToBeRemoved virtual void itemInfosAboutToBeRemoved(int /*begin*/, int /*end*/) {}; private: void appendInfos(const CamItemInfoList& infos); void publiciseInfos(const CamItemInfoList& infos); void cleanSituationChecks(); void removeRowPairs(const QList >& toRemove); void removeRowPairsWithCheck(const QList >& toRemove); public: // NOTE: Declared public because it's used in ItemModelIncrementalUpdater class class Private; private: Private* const d; }; } // namespace Digikam Q_DECLARE_METATYPE(Digikam::ImportItemModel*) #endif // DIGIKAM_IMPORT_IMAGE_MODEL_H diff --git a/core/utilities/import/models/importthumbnailmodel.cpp b/core/utilities/import/models/importthumbnailmodel.cpp index 3f2f9279bc..d3ce66b58c 100644 --- a/core/utilities/import/models/importthumbnailmodel.cpp +++ b/core/utilities/import/models/importthumbnailmodel.cpp @@ -1,172 +1,174 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2012-06-13 * Description : Qt item model for camera thumbnails entries * * Copyright (C) 2012 by Islam Wazery * * 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 "importthumbnailmodel.h" // Qt includes #include #include // Local includes #include "digikam_debug.h" #include "cameracontroller.h" namespace Digikam { class Q_DECL_HIDDEN ImportThumbnailModel::Private { public: explicit Private() : thumbsCtrl(nullptr), thumbSize(0), lastGlobalThumbSize(0), emitDataChanged(true) { } CameraThumbsCtrl* thumbsCtrl; ThumbnailSize thumbSize; ThumbnailSize lastGlobalThumbSize; bool emitDataChanged; }; ImportThumbnailModel::ImportThumbnailModel(QObject* const parent) : ImportItemModel(parent), d(new Private) { setKeepsFileUrlCache(true); } ImportThumbnailModel::~ImportThumbnailModel() { delete d; } void ImportThumbnailModel::setCameraThumbsController(CameraThumbsCtrl* const thumbsCtrl) { d->thumbsCtrl = thumbsCtrl; connect(d->thumbsCtrl, SIGNAL(signalThumbInfoReady(CamItemInfo)), this, SLOT(slotThumbInfoReady(CamItemInfo))); ImportItemModel::setCameraThumbsController(d->thumbsCtrl); } ThumbnailSize ImportThumbnailModel::thumbnailSize() const { return d->thumbSize; } void ImportThumbnailModel::setEmitDataChanged(bool emitSignal) { d->emitDataChanged = emitSignal; } QVariant ImportThumbnailModel::data(const QModelIndex& index, int role) const { if (role == ThumbnailRole && d->thumbsCtrl && index.isValid()) { CamItemInfo info = camItemInfo(index); QString path = info.url().toLocalFile(); CachedItem item; // use mimetype thumbnail also if the mime is set to something else than to image // this is to avoid querying the device for previews with unsupported file formats // at least gphoto2 doesn't really like it and will error a lot and slow down + if (info.isNull() || path.isEmpty() || !info.previewPossible) { return QVariant(d->thumbsCtrl->cameraController()->mimeTypeThumbnail(path).pixmap(d->thumbSize.size())); } if (d->thumbsCtrl->getThumbInfo(info, item)) { return QVariant(item.second.scaled(d->thumbSize.size(), d->thumbSize.size(), Qt::KeepAspectRatio)); } return QVariant(d->thumbsCtrl->cameraController()->mimeTypeThumbnail(path).pixmap(d->thumbSize.size())); } return ImportItemModel::data(index, role); } bool ImportThumbnailModel::setData(const QModelIndex& index, const QVariant& value, int role) { if (role == ThumbnailRole) { switch (value.type()) { case QVariant::Invalid: d->thumbSize = d->lastGlobalThumbSize; break; case QVariant::Int: if (value.isNull()) { d->thumbSize = d->lastGlobalThumbSize; } else { d->lastGlobalThumbSize = d->thumbSize; d->thumbSize = ThumbnailSize(value.toInt()); } break; default: break; } } return ImportItemModel::setData(index, value, role); } void ImportThumbnailModel::slotThumbInfoReady(const CamItemInfo& info) { CachedItem item; d->thumbsCtrl->getThumbInfo(info, item); // In case of multiple occurrence, we currently do not know which thumbnail is this. Signal change on all. + foreach (const QModelIndex& index, indexesForUrl(info.url())) { if (item.second.isNull()) { emit thumbnailFailed(index, d->thumbSize.size()); } else { emit thumbnailAvailable(index, d->thumbSize.size()); if (d->emitDataChanged) { emit dataChanged(index, index); } } } } } // namespace Digikam diff --git a/project/bundles/mxe/config.sh b/project/bundles/mxe/config.sh index c97a14d9d9..fc531bedbb 100644 --- a/project/bundles/mxe/config.sh +++ b/project/bundles/mxe/config.sh @@ -1,77 +1,77 @@ #!/bin/bash # Copyright (c) 2013-2020 by Gilles Caulier # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. ######################################################################## # Absolute path where are downloaded all tarballs to compile. DOWNLOAD_DIR="`pwd`/temp.dwnld" # Absolute path where are compiled all tarballs BUILDING_DIR="`pwd`/temp.build" #------------------------------------------------------------------------------------------- # MXE configuration #------------ # IMPORTANT: Target Windows architecture to build installer. Possible values: 32 or 64 bits. -MXE_ARCHBITS=64 +MXE_ARCHBITS=32 #------------ if [[ $MXE_ARCHBITS == 32 ]]; then # Windows 32 bits shared MXE_BUILD_TARGETS="i686-w64-mingw32.shared" MXE_BUILDROOT="`pwd`/build.win32" elif [[ $MXE_ARCHBITS == 64 ]]; then # Windows 64 bits shared MXE_BUILD_TARGETS="x86_64-w64-mingw32.shared" MXE_BUILDROOT="`pwd`/build.win64" else echo "Unsupported or wrong target Windows architecture: $MXE_ARCHBITS bits." exit -1 fi echo "Target Windows architecture: $MXE_ARCHBITS bits." MXE_GIT_URL="https://github.com/mxe/mxe.git" MXE_GIT_REVISION=master MXE_INSTALL_PREFIX=${MXE_BUILDROOT}/usr/${MXE_BUILD_TARGETS}/ MXE_TOOLCHAIN=${MXE_INSTALL_PREFIX}/share/cmake/mxe-conf.cmake #------------------------------------------------------------------------------------------- # URL to git repository to checkout digiKam source code DK_GITURL="git@invent.kde.org:kde/digikam.git" # digiKam tarball information. DK_URL="http://download.kde.org/stable/digikam" # Location to build source code. DK_BUILDTEMP=~/dktemp # digiKam tag version from git. Official tarball do not include extra shared libraries. # The list of tags can be listed with this url: https://quickgit.kde.org/?p=digikam.git&a=tags # If you want to package current implemntation from git, use "master" as tag. #DK_VERSION=v6.4.0 DK_VERSION=master #DK_VERSION=development/dplugins # Installer sub version to differentiates newer updates of the installer itself, even if the underlying application hasn’t changed. #DK_EPOCH="-01" # Epoch with time-stamp for pre-release bundle in ISO format DK_EPOCH="-`date "+%Y%m%dT%H%M%S"`" # Installer will include or not digiKam debug symbols DK_DEBUG=0 # Sign bundles with GPG. Passphrase must be hosted in ~/.gnupg/dkorg-gpg-pwd.txt DK_SIGN=0 # Upload automatically bundle to files.kde.org (pre-release only). DK_UPLOAD=1 DK_UPLOADURL="digikam@milonia.kde.org" DK_UPLOADDIR="/srv/archives/files/digikam/"