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.
282 lines
10 KiB
282 lines
10 KiB
9 years ago
|
/*
|
||
|
* 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),
|
||
|
thumbnailSize_(0),
|
||
|
showHidden_(false),
|
||
|
showThumbnails_(false),
|
||
|
folderFirst_(true) {
|
||
|
setDynamicSortFilter(true);
|
||
|
setSortCaseSensitivity(Qt::CaseInsensitive);
|
||
|
}
|
||
|
|
||
|
ProxyFolderModel::~ProxyFolderModel() {
|
||
|
qDebug("delete 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) {
|
||
|
// we only support Fm::FolderModel
|
||
|
Q_ASSERT(model->inherits("Fm::FolderModel"));
|
||
|
|
||
|
if(showThumbnails_ && thumbnailSize_ != 0) { // if we're showing thumbnails
|
||
|
FolderModel* oldSrcModel = static_cast<FolderModel*>(sourceModel());
|
||
|
FolderModel* newSrcModel = static_cast<FolderModel*>(model);
|
||
|
if(oldSrcModel) { // we need to release cached thumbnails for the old source model
|
||
|
oldSrcModel->releaseThumbnails(thumbnailSize_);
|
||
|
disconnect(oldSrcModel, SIGNAL(thumbnailLoaded(QModelIndex,int)));
|
||
|
}
|
||
|
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();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// need to call invalidateFilter() manually.
|
||
|
void ProxyFolderModel::setFolderFirst(bool folderFirst) {
|
||
|
if(folderFirst != folderFirst_) {
|
||
|
folderFirst_ = folderFirst;
|
||
|
invalidate();
|
||
|
Q_EMIT sortFilterChanged();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool ProxyFolderModel::filterAcceptsRow(int source_row, const QModelIndex & source_parent) const {
|
||
|
if(!showHidden_) {
|
||
|
QAbstractItemModel* srcModel = sourceModel();
|
||
|
QString name = srcModel->data(srcModel->index(source_row, 0, source_parent)).toString();
|
||
|
if(name.startsWith(".") || name.endsWith("~"))
|
||
|
return false;
|
||
|
}
|
||
|
// apply additional filters if there're any
|
||
|
Q_FOREACH(ProxyFolderModelFilter* filter, filters_) {
|
||
|
FolderModel* srcModel = static_cast<FolderModel*>(sourceModel());
|
||
|
FmFileInfo* 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) {
|
||
|
FmFileInfo* leftInfo = srcModel->fileInfoFromIndex(left);
|
||
|
FmFileInfo* rightInfo = srcModel->fileInfoFromIndex(right);
|
||
|
|
||
|
if(Q_UNLIKELY(!leftInfo || !rightInfo)) {
|
||
|
// In theory, this should not happen, but it's safer to add the null check.
|
||
|
// This is reported in https://github.com/lxde/pcmanfm-qt/issues/205
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if(folderFirst_) {
|
||
|
bool leftIsFolder = (bool)fm_file_info_is_dir(leftInfo);
|
||
|
bool rightIsFolder = (bool)fm_file_info_is_dir(rightInfo);
|
||
|
if(leftIsFolder != rightIsFolder)
|
||
|
return sortOrder() == Qt::AscendingOrder ? leftIsFolder : rightIsFolder;
|
||
|
}
|
||
|
|
||
|
switch(sortColumn()) {
|
||
|
case FolderModel::ColumnFileName:
|
||
|
if(sortCaseSensitivity() == Qt::CaseSensitive) {
|
||
|
// fm_file_info_get_collate_key_nocasefold() uses g_utf8_casefold() from glib internally, which
|
||
|
// is only an approximation not working correctly in some locales.
|
||
|
// FIXME: we may use QCollator (since Qt 5.2) for this, but the performance impact is unknown
|
||
|
return strcmp(fm_file_info_get_collate_key_nocasefold(leftInfo), fm_file_info_get_collate_key_nocasefold(rightInfo)) < 0;
|
||
|
/*
|
||
|
QCollator coll;
|
||
|
coll.setCaseSensitivity(Qt::CaseSensitive);
|
||
|
coll.setIgnorePunctuation(false);
|
||
|
coll.setNumericMode(true);
|
||
|
return coll.compare(QString::fromUtf8(fm_file_info_get_disp_name(leftInfo)), QString::fromUtf8(fm_file_info_get_disp_name(rightInfo))) < 0;
|
||
|
*/
|
||
|
}
|
||
|
else {
|
||
|
// linguistic case insensitive ordering
|
||
|
return strcmp(fm_file_info_get_collate_key(leftInfo), fm_file_info_get_collate_key(rightInfo)) < 0;
|
||
|
}
|
||
|
case FolderModel::ColumnFileMTime:
|
||
|
return fm_file_info_get_mtime(leftInfo) < fm_file_info_get_mtime(rightInfo);
|
||
|
case FolderModel::ColumnFileSize:
|
||
|
return fm_file_info_get_size(leftInfo) < fm_file_info_get_size(rightInfo);
|
||
|
case FolderModel::ColumnFileOwner:
|
||
|
// TODO: sort by owner
|
||
|
break;
|
||
|
case FolderModel::ColumnFileType:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return QSortFilterProxyModel::lessThan(left, right);
|
||
|
}
|
||
|
|
||
|
FmFileInfo* ProxyFolderModel::fileInfoFromIndex(const QModelIndex& index) const {
|
||
|
FolderModel* srcModel = static_cast<FolderModel*>(sourceModel());
|
||
|
if(srcModel) {
|
||
|
QModelIndex srcIndex = mapToSource(index);
|
||
|
return srcModel->fileInfoFromIndex(srcIndex);
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
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
|
||
|
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
|