Download src/Mod/Start/App/DisplayedFilesModel.cpp from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 10.8 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Start/App/DisplayedFilesModel.cpp
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/Start/App/DisplayedFilesModel.cpp
-
curl -L -o DisplayedFilesModel.cpp https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Start/App/DisplayedFilesModel.cpp
10.8 kB
| // SPDX-License-Identifier: LGPL-2.1-or-later | |
| /**************************************************************************** | |
| * * | |
| * Copyright (c) 2024 The FreeCAD Project Association AISBL * | |
| * * | |
| * This file is part of FreeCAD. * | |
| * * | |
| * FreeCAD 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. * | |
| * * | |
| * FreeCAD 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 FreeCAD. If not, see * | |
| * <https://www.gnu.org/licenses/>. * | |
| * * | |
| ***************************************************************************/ | |
| using namespace Start; | |
| FileStats fileInfoFromFreeCADFile(const std::string& path) | |
| { | |
| App::ProjectFile proj(path); | |
| proj.loadDocument(); | |
| auto metadata = proj.getMetadata(); | |
| FileStats result; | |
| result.insert(std::make_pair(DisplayedFilesModelRoles::author, metadata.createdBy)); | |
| result.insert(std::make_pair(DisplayedFilesModelRoles::modifiedTime, metadata.lastModifiedDate)); | |
| result.insert(std::make_pair(DisplayedFilesModelRoles::creationTime, metadata.creationDate)); | |
| result.insert(std::make_pair(DisplayedFilesModelRoles::company, metadata.company)); | |
| result.insert(std::make_pair(DisplayedFilesModelRoles::license, metadata.license)); | |
| result.insert(std::make_pair(DisplayedFilesModelRoles::description, metadata.comment)); | |
| return result; | |
| } | |
| /// Load the thumbnail image data (if any) that is stored in an FCStd file. | |
| /// \returns The image bytes, or an empty QByteArray (if no thumbnail was stored) | |
| QByteArray loadFCStdThumbnail(const QString& pathToFCStdFile) | |
| { | |
| if (App::ProjectFile proj(pathToFCStdFile.toStdString()); proj.loadDocument()) { | |
| try { | |
| const QString pathToCachedThumbnail = getPathToCachedThumbnail(pathToFCStdFile); | |
| if (!useCachedThumbnail(pathToCachedThumbnail, pathToFCStdFile)) { | |
| static const QString pathToThumbnail = defaultThumbnailPath; | |
| if (proj.containsFile(pathToThumbnail.toStdString())) { | |
| createThumbnailsDir(); | |
| const Base::FileInfo fi(pathToCachedThumbnail.toStdString()); | |
| Base::ofstream stream(fi, std::ios::out | std::ios::binary); | |
| proj.readInputFileDirect(pathToThumbnail.toStdString(), stream); | |
| stream.close(); | |
| } | |
| } | |
| if (auto inputFile = QFile(pathToCachedThumbnail); inputFile.exists()) { | |
| inputFile.open(QIODevice::OpenModeFlag::ReadOnly); | |
| return inputFile.readAll(); | |
| } | |
| } | |
| catch (...) { | |
| Base::Console().log("Failed to load thumbnail for %s\n", pathToFCStdFile.toStdString()); | |
| } | |
| } | |
| return {}; | |
| } | |
| FileStats getFileInfo(const std::string& path) | |
| { | |
| FileStats result; | |
| const Base::FileInfo file(path); | |
| if (file.hasExtension("FCStd")) { | |
| result = fileInfoFromFreeCADFile(path); | |
| } | |
| else { | |
| result.insert( | |
| std::make_pair(DisplayedFilesModelRoles::modifiedTime, getLastModifiedAsString(file)) | |
| ); | |
| } | |
| result.insert(std::make_pair(DisplayedFilesModelRoles::path, path)); | |
| result.insert(std::make_pair(DisplayedFilesModelRoles::size, humanReadableSize(file.size()))); | |
| result.insert(std::make_pair(DisplayedFilesModelRoles::baseName, file.fileName())); | |
| return result; | |
| } | |
| bool freecadCanOpen(const QString& extension) | |
| { | |
| std::string ext = extension.toStdString(); | |
| auto importTypes = App::GetApplication().getImportTypes(); | |
| return std::ranges::find_if( | |
| importTypes, | |
| [&ext](const auto& item) { return boost::iequals(item, ext); } | |
| ) | |
| != importTypes.end(); | |
| } | |
| DisplayedFilesModel::DisplayedFilesModel(QObject* parent) | |
| : QAbstractListModel(parent) | |
| {} | |
| int DisplayedFilesModel::rowCount(const QModelIndex& parent) const | |
| { | |
| Q_UNUSED(parent); | |
| return static_cast<int>(_fileInfoCache.size()); | |
| } | |
| QVariant DisplayedFilesModel::data(const QModelIndex& index, int role) const | |
| { | |
| const int row = index.row(); | |
| if (row < 0 || row >= static_cast<int>(_fileInfoCache.size())) { | |
| return {}; | |
| } | |
| const auto mapEntry = _fileInfoCache.at(row); | |
| switch (const auto roleAsType = static_cast<DisplayedFilesModelRoles>(role)) { | |
| case DisplayedFilesModelRoles::author: // NOLINT(bugprone-branch-clone) | |
| [[fallthrough]]; | |
| case DisplayedFilesModelRoles::baseName: | |
| [[fallthrough]]; | |
| case DisplayedFilesModelRoles::company: | |
| [[fallthrough]]; | |
| case DisplayedFilesModelRoles::creationTime: | |
| [[fallthrough]]; | |
| case DisplayedFilesModelRoles::description: | |
| [[fallthrough]]; | |
| case DisplayedFilesModelRoles::license: | |
| [[fallthrough]]; | |
| case DisplayedFilesModelRoles::modifiedTime: | |
| [[fallthrough]]; | |
| case DisplayedFilesModelRoles::path: | |
| [[fallthrough]]; | |
| case DisplayedFilesModelRoles::size: | |
| if (mapEntry.contains(roleAsType)) { | |
| return QString::fromStdString(mapEntry.at(roleAsType)); | |
| } | |
| break; | |
| case DisplayedFilesModelRoles::image: { | |
| if (const auto path = QString::fromStdString(mapEntry.at(DisplayedFilesModelRoles::path)); | |
| _imageCache.contains(path)) { | |
| return _imageCache[path]; | |
| } | |
| break; | |
| } | |
| default: | |
| break; | |
| } | |
| switch (role) { | |
| case Qt::ItemDataRole::ToolTipRole: | |
| return QString::fromStdString(mapEntry.at(DisplayedFilesModelRoles::path)); | |
| default: | |
| // No other role gets handled | |
| break; | |
| } | |
| return {}; | |
| } | |
| void DisplayedFilesModel::addFile(const QString& filePath) | |
| { | |
| const QFileInfo qfi(filePath); | |
| if (!qfi.isReadable()) { | |
| return; | |
| } | |
| if (!freecadCanOpen(qfi.suffix())) { | |
| return; | |
| } | |
| _fileInfoCache.emplace_back(getFileInfo(filePath.toStdString())); | |
| const auto lowercaseExtension = qfi.suffix().toLower(); | |
| const QStringList ignoredExtensions { | |
| QLatin1String("fcmacro"), | |
| QLatin1String("py"), | |
| QLatin1String("pyi"), | |
| QLatin1String("csv"), | |
| QLatin1String("txt") | |
| }; | |
| if (lowercaseExtension == QLatin1String("fcstd")) { | |
| if (const auto thumbnail = loadFCStdThumbnail(filePath); !thumbnail.isEmpty()) { | |
| _imageCache.insert(filePath, thumbnail); | |
| } | |
| } | |
| else if (ignoredExtensions.contains(lowercaseExtension)) { | |
| // Don't try to generate a thumbnail for things like this: FreeCAD can read them, but | |
| // there's not much point in showing anything besides a generic icon | |
| } | |
| else { | |
| const auto runner = new ThumbnailSource(filePath); | |
| connect( | |
| runner->signals(), | |
| &ThumbnailSourceSignals::thumbnailAvailable, | |
| this, | |
| &DisplayedFilesModel::processNewThumbnail | |
| ); | |
| QThreadPool::globalInstance()->start(runner); | |
| } | |
| } | |
| void DisplayedFilesModel::clear() | |
| { | |
| _fileInfoCache.clear(); | |
| } | |
| QHash<int, QByteArray> DisplayedFilesModel::roleNames() const | |
| { | |
| static QHash<int, QByteArray> nameMap { | |
| std::make_pair(static_cast<int>(DisplayedFilesModelRoles::author), "author"), | |
| std::make_pair(static_cast<int>(DisplayedFilesModelRoles::baseName), "baseName"), | |
| std::make_pair(static_cast<int>(DisplayedFilesModelRoles::company), "company"), | |
| std::make_pair(static_cast<int>(DisplayedFilesModelRoles::creationTime), "creationTime"), | |
| std::make_pair(static_cast<int>(DisplayedFilesModelRoles::description), "description"), | |
| std::make_pair(static_cast<int>(DisplayedFilesModelRoles::image), "image"), | |
| std::make_pair(static_cast<int>(DisplayedFilesModelRoles::license), "license"), | |
| std::make_pair(static_cast<int>(DisplayedFilesModelRoles::modifiedTime), "modifiedTime"), | |
| std::make_pair(static_cast<int>(DisplayedFilesModelRoles::path), "path"), | |
| std::make_pair(static_cast<int>(DisplayedFilesModelRoles::size), "size"), | |
| }; | |
| return nameMap; | |
| } | |
| void DisplayedFilesModel::processNewThumbnail(const QString& file, const QByteArray& thumbnail) | |
| { | |
| if (!thumbnail.isEmpty()) { | |
| _imageCache.insert(file, thumbnail); | |
| // Figure out the index of this file... | |
| auto it = std::ranges::find_if(_fileInfoCache, [file](const FileStats& row) { | |
| auto pathIt = row.find(DisplayedFilesModelRoles::path); | |
| return pathIt != row.end() && pathIt->second == file.toStdString(); | |
| }); | |
| if (it != _fileInfoCache.end()) { | |
| std::size_t index = std::distance(_fileInfoCache.begin(), it); | |
| QModelIndex qmi = createIndex(index, 0); | |
| Q_EMIT(dataChanged(qmi, qmi, {static_cast<int>(DisplayedFilesModelRoles::image)})); | |
| } | |
| else { | |
| Base::Console().log("Unrecognized path %s\n", file.toStdString()); | |
| } | |
| } | |
| } | |