You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libfm-qt-packaging/src/proxyfoldermodel.cpp

328 lines
12 KiB

/*
* Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "proxyfoldermodel.h"
#include "foldermodel.h"
#include <QCollator>
namespace Fm {
ProxyFolderModel::ProxyFolderModel(QObject* parent):
QSortFilterProxyModel(parent),
showHidden_(false),
backupAsHidden_(true),
folderFirst_(true),
showThumbnails_(false),
thumbnailSize_(0) {
setDynamicSortFilter(true);
setSortCaseSensitivity(Qt::CaseInsensitive);
collator_.setNumericMode(true);
}
ProxyFolderModel::~ProxyFolderModel() {
if(showThumbnails_ && thumbnailSize_ != 0) {
FolderModel* srcModel = static_cast<FolderModel*>(sourceModel());
// tell the source model that we don't need the thumnails anymore
if(srcModel) {
srcModel->releaseThumbnails(thumbnailSize_);
disconnect(srcModel, SIGNAL(thumbnailLoaded(QModelIndex, int)));
}
}
}
void ProxyFolderModel::setSourceModel(QAbstractItemModel* model) {
if(model == sourceModel()) // avoid setting the same model twice
return;
FolderModel* oldSrcModel = static_cast<FolderModel*>(sourceModel());
#if (QT_VERSION == QT_VERSION_CHECK(5,11,0))
// workaround for Qt-5.11 bug https://bugreports.qt.io/browse/QTBUG-68581
if(oldSrcModel) {
disconnect(oldSrcModel, SIGNAL(destroyed()), this, SLOT(_q_sourceModelDestroyed()));
}
#endif
if(model) {
// we only support Fm::FolderModel
Q_ASSERT(model->inherits("Fm::FolderModel"));
if(showThumbnails_ && thumbnailSize_ != 0) { // if we're showing thumbnails
if(oldSrcModel) { // we need to release cached thumbnails for the old source model
oldSrcModel->releaseThumbnails(thumbnailSize_);
disconnect(oldSrcModel, SIGNAL(thumbnailLoaded(QModelIndex, int)));
}
FolderModel* newSrcModel = static_cast<FolderModel*>(model);
if(newSrcModel) { // tell the new source model that we want thumbnails of this size
newSrcModel->cacheThumbnails(thumbnailSize_);
connect(newSrcModel, &FolderModel::thumbnailLoaded, this, &ProxyFolderModel::onThumbnailLoaded);
}
}
}
QSortFilterProxyModel::setSourceModel(model);
}
void ProxyFolderModel::sort(int column, Qt::SortOrder order) {
int oldColumn = sortColumn();
Qt::SortOrder oldOrder = sortOrder();
QSortFilterProxyModel::sort(column, order);
if(column != oldColumn || order != oldOrder) {
Q_EMIT sortFilterChanged();
}
}
void ProxyFolderModel::setShowHidden(bool show) {
if(show != showHidden_) {
showHidden_ = show;
invalidateFilter();
Q_EMIT sortFilterChanged();
}
}
void ProxyFolderModel::setBackupAsHidden(bool backupAsHidden) {
if(backupAsHidden != backupAsHidden_) {
backupAsHidden_ = backupAsHidden;
invalidateFilter();
Q_EMIT sortFilterChanged();
}
}
// need to call invalidateFilter() manually.
void ProxyFolderModel::setFolderFirst(bool folderFirst) {
if(folderFirst != folderFirst_) {
folderFirst_ = folderFirst;
invalidate();
Q_EMIT sortFilterChanged();
}
}
void ProxyFolderModel::setSortCaseSensitivity(Qt::CaseSensitivity cs) {
collator_.setCaseSensitivity(cs);
QSortFilterProxyModel::setSortCaseSensitivity(cs);
invalidate();
Q_EMIT sortFilterChanged();
}
bool ProxyFolderModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const {
if(!showHidden_) {
if(FolderModel* srcModel = static_cast<FolderModel*>(sourceModel())) {
auto info = srcModel->fileInfoFromIndex(srcModel->index(source_row, 0, source_parent));
if(info && (info->isHidden() || (backupAsHidden_ && info->isBackup()))) {
return false;
}
}
}
// apply additional filters if there're any
for(ProxyFolderModelFilter* const filter : qAsConst(filters_)) {
if(FolderModel* srcModel = static_cast<FolderModel*>(sourceModel())){
auto fileInfo = srcModel->fileInfoFromIndex(srcModel->index(source_row, 0, source_parent));
if(!filter->filterAcceptsRow(this, fileInfo)) {
return false;
}
}
}
return true;
}
bool ProxyFolderModel::lessThan(const QModelIndex& left, const QModelIndex& right) const {
FolderModel* srcModel = static_cast<FolderModel*>(sourceModel());
// left and right are indexes of source model, not the proxy model.
if(srcModel) {
auto leftInfo = srcModel->fileInfoFromIndex(left);
auto rightInfo = srcModel->fileInfoFromIndex(right);
if(folderFirst_) {
bool leftIsFolder = leftInfo->isDir();
bool rightIsFolder = rightInfo->isDir();
if(leftIsFolder != rightIsFolder) {
return sortOrder() == Qt::AscendingOrder ? leftIsFolder : rightIsFolder;
}
}
int comp;
switch(sortColumn()) {
case FolderModel::ColumnFileMTime:
comp = leftInfo->mtime() - rightInfo->mtime();
break;
case FolderModel::ColumnFileSize:
comp = leftInfo->size() - rightInfo->size();
break;
default: {
QString leftText = left.data(Qt::DisplayRole).toString();
QString rightText = right.data(Qt::DisplayRole).toString();
comp = collator_.compare(leftText, rightText);
break;
}
}
// always sort files by their display names when they have the same property
if(comp == 0) {
return collator_.compare(leftInfo->displayName(), rightInfo->displayName()) < 0;
}
return comp < 0;
}
return QSortFilterProxyModel::lessThan(left, right);
}
std::shared_ptr<const Fm::FileInfo> ProxyFolderModel::fileInfoFromIndex(const QModelIndex& index) const {
if(index.isValid()) {
FolderModel* srcModel = static_cast<FolderModel*>(sourceModel());
if(srcModel) {
QModelIndex srcIndex = mapToSource(index);
return srcModel->fileInfoFromIndex(srcIndex);
}
}
return nullptr;
}
QModelIndex ProxyFolderModel::indexFromPath(const FilePath &path) const {
QModelIndex ret;
int n_rows = rowCount();
for(int row = 0; row < n_rows; ++row) {
auto idx = index(row, FolderModel::ColumnFileName, QModelIndex());
auto fi = fileInfoFromIndex(idx);
if(fi && fi->path() == path) { // found the item
ret = idx;
break;
}
}
return ret;
}
std::shared_ptr<const FileInfo> ProxyFolderModel::fileInfoFromPath(const FilePath &path) const {
return fileInfoFromIndex(indexFromPath(path));
}
void ProxyFolderModel::setCutFiles(const QItemSelection& selection) {
FolderModel* srcModel = static_cast<FolderModel*>(sourceModel());
if(srcModel) {
srcModel->setCutFiles(mapSelectionToSource(selection));
}
}
void ProxyFolderModel::setShowThumbnails(bool show) {
if(show != showThumbnails_) {
showThumbnails_ = show;
FolderModel* srcModel = static_cast<FolderModel*>(sourceModel());
if(srcModel && thumbnailSize_ != 0) {
if(show) {
// ask for cache of thumbnails of the new size in source model
srcModel->cacheThumbnails(thumbnailSize_);
// connect to the srcModel so we can be notified when a thumbnail is loaded.
connect(srcModel, &FolderModel::thumbnailLoaded, this, &ProxyFolderModel::onThumbnailLoaded);
}
else { // turn off thumbnails
// free cached old thumbnails in souce model
srcModel->releaseThumbnails(thumbnailSize_);
disconnect(srcModel, SIGNAL(thumbnailLoaded(QModelIndex, int)));
}
// reload all items, FIXME: can we only update items previously having thumbnails
Q_EMIT dataChanged(index(0, 0), index(rowCount() - 1, 0));
}
}
}
void ProxyFolderModel::setThumbnailSize(int size) {
if(size != thumbnailSize_) {
FolderModel* srcModel = static_cast<FolderModel*>(sourceModel());
if(showThumbnails_ && srcModel) {
// free cached thumbnails of the old size
if(thumbnailSize_ != 0) {
srcModel->releaseThumbnails(thumbnailSize_);
}
else {
// if the old thumbnail size is 0, we did not turn on thumbnail initially
connect(srcModel, &FolderModel::thumbnailLoaded, this, &ProxyFolderModel::onThumbnailLoaded);
}
// ask for cache of thumbnails of the new size in source model
srcModel->cacheThumbnails(size);
// reload all items, FIXME: can we only update items previously having thumbnails
Q_EMIT dataChanged(index(0, 0), index(rowCount() - 1, 0));
}
thumbnailSize_ = size;
}
}
QVariant ProxyFolderModel::data(const QModelIndex& index, int role) const {
if(index.column() == 0) { // only show the decoration role for the first column
if(role == Qt::DecorationRole && showThumbnails_ && thumbnailSize_) {
// we need to show thumbnails instead of icons
FolderModel* srcModel = static_cast<FolderModel*>(sourceModel());
QModelIndex srcIndex = mapToSource(index);
QImage image = srcModel->thumbnailFromIndex(srcIndex, thumbnailSize_);
if(!image.isNull()) { // if we got a thumbnail of the desired size, use it
return QVariant(image);
}
}
}
// fallback to icons if thumbnails are not available
return QSortFilterProxyModel::data(index, role);
}
void ProxyFolderModel::onThumbnailLoaded(const QModelIndex& srcIndex, int size) {
// FolderModel* srcModel = static_cast<FolderModel*>(sourceModel());
// FolderModelItem* item = srcModel->itemFromIndex(srcIndex);
// qDebug("ProxyFolderModel::onThumbnailLoaded: %d, %s", size, item->displayName.toUtf8().data());
if(size == thumbnailSize_ // if a thumbnail of the size we want is loaded
&& srcIndex.model() == sourceModel()) { // check if the sourse model contains the index item
QModelIndex index = mapFromSource(srcIndex);
Q_EMIT dataChanged(index, index);
}
}
void ProxyFolderModel::addFilter(ProxyFolderModelFilter* filter) {
filters_.append(filter);
invalidateFilter();
Q_EMIT sortFilterChanged();
}
void ProxyFolderModel::removeFilter(ProxyFolderModelFilter* filter) {
filters_.removeOne(filter);
invalidateFilter();
Q_EMIT sortFilterChanged();
}
void ProxyFolderModel::updateFilters() {
invalidate();
Q_EMIT sortFilterChanged();
}
#if 0
void ProxyFolderModel::reloadAllThumbnails() {
// reload all thumbnails and update UI
FolderModel* srcModel = static_cast<FolderModel*>(sourceModel());
if(srcModel) {
int rows = rowCount();
for(int row = 0; row < rows; ++row) {
QModelIndex index = this->index(row, 0);
QModelIndex srcIndex = mapToSource(index);
QImage image = srcModel->thumbnailFromIndex(srcIndex, size);
// tell the world that the item is changed to trigger a UI update
if(!image.isNull()) {
Q_EMIT dataChanged(index, index);
}
}
}
}
#endif
} // namespace Fm