From 7939806f2a12281e6b5a28620c2ef9a4532cf05d Mon Sep 17 00:00:00 2001 From: "J.D. Purcell" Date: Thu, 23 Nov 2023 22:59:21 -0500 Subject: [PATCH] Ability to disable loading certain file extensions. --- src/mainwindow.cpp | 2 -- src/qvapplication.cpp | 52 ++++++++++++++++++++-------- src/qvapplication.h | 18 ++++++---- src/qvimagecore.cpp | 18 ++++------ src/qvoptionsdialog.cpp | 77 +++++++++++++++++++++++++++++++++++------ src/qvoptionsdialog.h | 8 +++++ src/qvoptionsdialog.ui | 65 +++++++++++++++++++++++++++++++++- src/settingsmanager.cpp | 2 ++ 8 files changed, 196 insertions(+), 46 deletions(-) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index f70de01c..102ca2ea 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -350,8 +350,6 @@ void MainWindow::settingsUpdated() #ifdef COCOA_LOADED // titlebaralwaysdark QVCocoaFunctions::setVibrancy(settingsManager.getBoolean("titlebaralwaysdark"), windowHandle()); - // quitonlastwindow - qvApp->setQuitOnLastWindowClosed(settingsManager.getBoolean("quitonlastwindow")); #endif //slideshow timer diff --git a/src/qvapplication.cpp b/src/qvapplication.cpp index 35840291..071c49a5 100644 --- a/src/qvapplication.cpp +++ b/src/qvapplication.cpp @@ -17,6 +17,7 @@ QVApplication::QVApplication(int &argc, char **argv) : QApplication(argc, argv) // Connections connect(this, &QGuiApplication::commitDataRequest, this, &QVApplication::onCommitDataRequest, Qt::DirectConnection); connect(this, &QCoreApplication::aboutToQuit, this, &QVApplication::onAboutToQuit); + connect(&settingsManager, &SettingsManager::settingsUpdated, this, &QVApplication::settingsUpdated); connect(&actionManager, &ActionManager::recentsMenuUpdated, this, &QVApplication::recentsMenuUpdated); connect(&updateChecker, &UpdateChecker::checkedUpdates, this, &QVApplication::checkedUpdates); @@ -25,7 +26,7 @@ QVApplication::QVApplication(int &argc, char **argv) : QApplication(argc, argv) QIcon::setFallbackSearchPaths(QIcon::fallbackSearchPaths() << "/usr/share/pixmaps"); #endif - defineFilterLists(); + settingsUpdated(); // Check for updates // TODO: move this to after first window show event @@ -58,9 +59,6 @@ QVApplication::QVApplication(int &argc, char **argv) : QApplication(argc, argv) #ifdef COCOA_LOADED QVCocoaFunctions::setUserDefaults(); #endif -#ifdef Q_OS_MACOS - setQuitOnLastWindowClosed(getSettingsManager().getBoolean("quitonlastwindow")); -#endif // Block any erroneous icons from showing up on mac and windows // (this is overridden in some cases) @@ -318,15 +316,41 @@ void QVApplication::hideIncompatibleActions() #endif } +void QVApplication::settingsUpdated() +{ + auto &settingsManager = qvApp->getSettingsManager(); + + QStringList disabledFileExtensionsList = settingsManager.getString("disabledfileextensions").split(';'); + disabledFileExtensions = QSet(disabledFileExtensionsList.begin(), disabledFileExtensionsList.end()); + +#ifdef Q_OS_MACOS + setQuitOnLastWindowClosed(settingsManager.getBoolean("quitonlastwindow")); +#endif + + defineFilterLists(); +} + void QVApplication::defineFilterLists() { + allFileExtensionList.clear(); + nameFilterList.clear(); + fileExtensionSet.clear(); + mimeTypeNameSet.clear(); + const auto &byteArrayFormats = QImageReader::supportedImageFormats(); auto filterString = tr("Supported Images") + " ("; - filterList.reserve(byteArrayFormats.size()-1); - fileExtensionList.reserve(byteArrayFormats.size()-1); + fileExtensionSet.reserve(byteArrayFormats.size()-1); + + const auto addExtension = [&](const QString &extension) { + allFileExtensionList << extension; + if (disabledFileExtensions.contains(extension)) + return; + filterString += "*" + extension + " "; + fileExtensionSet << extension; + }; - // Build the filterlist, filterstring, and filterregexplist in one loop + // Build extension and filter lists for (const auto &byteArray : byteArrayFormats) { const auto fileExtension = "." + QString::fromUtf8(byteArray); @@ -334,16 +358,14 @@ void QVApplication::defineFilterLists() if (fileExtension == ".pdf") continue; - filterList << "*" + fileExtension; - filterString += "*" + fileExtension + " "; - fileExtensionList << fileExtension; + addExtension(fileExtension); // If we support jpg, we actually support the jfif, jfi, and jpe file extensions too almost certainly. if (fileExtension == ".jpg") { - filterList << "*.jpe" << "*.jfi" << "*.jfif"; - filterString += "*.jpe *.jfi *.jfif "; - fileExtensionList << ".jpe" << ".jfi" << ".jfif"; + addExtension(".jpe"); + addExtension(".jfi"); + addExtension(".jfif"); } } filterString.chop(1); @@ -351,7 +373,7 @@ void QVApplication::defineFilterLists() // Build mime type list const auto &byteArrayMimeTypes = QImageReader::supportedMimeTypes(); - mimeTypeNameList.reserve(byteArrayMimeTypes.size()-1); + mimeTypeNameSet.reserve(byteArrayMimeTypes.size()-1); for (const auto &byteArray : byteArrayMimeTypes) { // Qt 5.15 seems to have added pdf support for QImageReader but it is super broken in qView @@ -359,7 +381,7 @@ void QVApplication::defineFilterLists() if (mime == "application/pdf") continue; - mimeTypeNameList << mime; + mimeTypeNameSet << mime; } // Build name filter list for file dialogs diff --git a/src/qvapplication.h b/src/qvapplication.h index 3e1db991..18bc08ad 100644 --- a/src/qvapplication.h +++ b/src/qvapplication.h @@ -65,17 +65,21 @@ class QVApplication : public QApplication void hideIncompatibleActions(); + void settingsUpdated(); + void defineFilterLists(); QMenuBar *getMenuBar() const { return menuBar; } - const QStringList &getFilterList() const { return filterList; } + const QSet &getDisabledFileExtensions() const { return disabledFileExtensions; } + + const QStringList &getAllFileExtensionList() const { return allFileExtensionList; } const QStringList &getNameFilterList() const { return nameFilterList; } - const QStringList &getFileExtensionList() const { return fileExtensionList; } + const QSet &getFileExtensionSet() const { return fileExtensionSet; } - const QStringList &getMimeTypeNameList() const { return mimeTypeNameList; } + const QSet &getMimeTypeNameSet() const { return mimeTypeNameSet; } const SettingsManager &getSettingsManager() const { return settingsManager; } SettingsManager &getSettingsManager() { return settingsManager; } @@ -120,10 +124,12 @@ protected slots: QMenuBar *menuBar; - QStringList filterList; + QSet disabledFileExtensions; + + QStringList allFileExtensionList; QStringList nameFilterList; - QStringList fileExtensionList; - QStringList mimeTypeNameList; + QSet fileExtensionSet; + QSet mimeTypeNameSet; // This order is very important SettingsManager settingsManager; diff --git a/src/qvimagecore.cpp b/src/qvimagecore.cpp index 481a65b5..658f9dee 100644 --- a/src/qvimagecore.cpp +++ b/src/qvimagecore.cpp @@ -248,30 +248,24 @@ QList QVImageCore::getCompatibleFiles(const QString QList fileList; QMimeDatabase mimeDb; - const auto &extensions = qvApp->getFileExtensionList(); - const auto &mimeTypes = qvApp->getMimeTypeNameList(); + const auto &extensions = qvApp->getFileExtensionSet(); + const auto &disabledExtensions = qvApp->getDisabledFileExtensions(); + const auto &mimeTypes = qvApp->getMimeTypeNameSet(); QMimeDatabase::MatchMode mimeMatchMode = allowMimeContentDetection ? QMimeDatabase::MatchDefault : QMimeDatabase::MatchExtension; const QFileInfoList currentFolder = QDir(dirPath).entryInfoList(QDir::Files | QDir::Hidden, QDir::Unsorted); for (const QFileInfo &fileInfo : currentFolder) { - bool matched = false; const QString absoluteFilePath = fileInfo.absoluteFilePath(); const QString fileName = fileInfo.fileName(); - for (const QString &extension : extensions) - { - if (fileName.endsWith(extension, Qt::CaseInsensitive)) - { - matched = true; - break; - } - } + const QString suffix = fileInfo.suffix().toLower(); + bool matched = !suffix.isEmpty() && extensions.contains("." + suffix); QString mimeType; if (!matched || sortMode == Qv::SortMode::Type) { mimeType = mimeDb.mimeTypeForFile(absoluteFilePath, mimeMatchMode).name(); - matched |= mimeTypes.contains(mimeType); + matched |= mimeTypes.contains(mimeType) && (suffix.isEmpty() || !disabledExtensions.contains("." + suffix)); } if (matched) { diff --git a/src/qvoptionsdialog.cpp b/src/qvoptionsdialog.cpp index f3bf45a8..1e84db92 100644 --- a/src/qvoptionsdialog.cpp +++ b/src/qvoptionsdialog.cpp @@ -35,6 +35,7 @@ QVOptionsDialog::QVOptionsDialog(QWidget *parent) : connect(ui->titlebarComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &QVOptionsDialog::titlebarComboBoxCurrentIndexChanged); connect(ui->windowResizeComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &QVOptionsDialog::windowResizeComboBoxCurrentIndexChanged); connect(ui->langComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &QVOptionsDialog::languageComboBoxCurrentIndexChanged); + connect(ui->formatsTable, &QTableWidget::itemChanged, this, &QVOptionsDialog::formatsItemChanged); populateCategories(); populateComboBoxes(); @@ -92,6 +93,7 @@ QVOptionsDialog::QVOptionsDialog(QWidget *parent) : syncSettings(false, true); syncShortcuts(); + syncFormats(); updateButtonBox(); isInitialLoad = false; @@ -121,6 +123,7 @@ void QVOptionsDialog::modifySetting(QString key, QVariant value) void QVOptionsDialog::saveSettings() { QSettings settings; + settings.beginGroup("options"); const auto keys = transientSettings.keys(); @@ -130,7 +133,10 @@ void QVOptionsDialog::saveSettings() settings.setValue(key, value); } + settings.setValue("disabledfileextensions", QStringList(transientDisabledFileExtensions.begin(), transientDisabledFileExtensions.end()).join(';')); + settings.endGroup(); + settings.beginGroup("shortcuts"); const auto &shortcutsList = qvApp->getShortcutManager().getShortcutsList(); @@ -406,6 +412,38 @@ void QVOptionsDialog::shortcutCellDoubleClicked(int row, int column) shortcutDialog->open(); } +void QVOptionsDialog::syncFormats(bool defaults) +{ + if (defaults) + transientDisabledFileExtensions.clear(); + else + transientDisabledFileExtensions = qvApp->getDisabledFileExtensions(); + + QStringList extensions = qvApp->getAllFileExtensionList(); + extensions.sort(Qt::CaseInsensitive); + + isLoadingFormats = true; + + ui->formatsTable->setRowCount(extensions.length()); + + for (int i = 0; i < extensions.length(); i++) + { + const QString &extension = extensions.value(i); + const bool isDisabled = transientDisabledFileExtensions.contains(extension); + + auto *extensionItem = new QTableWidgetItem(); + extensionItem->setText(extension); + ui->formatsTable->setItem(i, 0, extensionItem); + + auto *enabledItem = new QTableWidgetItem(); + enabledItem->setCheckState(isDisabled ? Qt::Unchecked : Qt::Checked); + enabledItem->setData(Qt::UserRole, extension); + ui->formatsTable->setItem(i, 1, enabledItem); + } + + isLoadingFormats = false; +} + void QVOptionsDialog::buttonBoxClicked(QAbstractButton *button) { auto role = ui->buttonBox->buttonRole(button); @@ -419,6 +457,7 @@ void QVOptionsDialog::buttonBoxClicked(QAbstractButton *button) { syncSettings(true); syncShortcuts(true); + syncFormats(true); } } @@ -426,8 +465,8 @@ void QVOptionsDialog::updateButtonBox() { QPushButton *defaultsButton = ui->buttonBox->button(QDialogButtonBox::RestoreDefaults); QPushButton *applyButton = ui->buttonBox->button(QDialogButtonBox::Apply); - defaultsButton->setEnabled(false); - applyButton->setEnabled(false); + bool anyUnsaved = false; + bool anyNonDefault = false; // settings const QList settingKeys = transientSettings.keys(); @@ -437,10 +476,8 @@ void QVOptionsDialog::updateButtonBox() const auto &savedValue = qvApp->getSettingsManager().getSetting(key); const auto &defaultValue = qvApp->getSettingsManager().getSetting(key, true); - if (transientValue != savedValue) - applyButton->setEnabled(true); - if (transientValue != defaultValue) - defaultsButton->setEnabled(true); + anyUnsaved |= transientValue != savedValue; + anyNonDefault |= transientValue != defaultValue; } // shortcuts @@ -451,11 +488,16 @@ void QVOptionsDialog::updateButtonBox() QStringList savedValue = shortcutsList.value(i).shortcuts; QStringList defaultValue = shortcutsList.value(i).defaultShortcuts; - if (transientValue != savedValue) - applyButton->setEnabled(true); - if (transientValue != defaultValue) - defaultsButton->setEnabled(true); + anyUnsaved |= transientValue != savedValue; + anyNonDefault |= transientValue != defaultValue; } + + // formats + anyUnsaved |= transientDisabledFileExtensions != qvApp->getDisabledFileExtensions(); + anyNonDefault |= !transientDisabledFileExtensions.isEmpty(); + + applyButton->setEnabled(anyUnsaved); + defaultsButton->setEnabled(anyNonDefault); } void QVOptionsDialog::bgColorButtonClicked() @@ -538,6 +580,7 @@ void QVOptionsDialog::populateCategories() addItem(u'\ue3f4', tr("Image")); addItem(u'\ue429', tr("Miscellaneous")); addItem(u'\ue312', tr("Shortcuts")); + addItem(u'\ue87b', tr("Formats")); addItem(u'\ue323', tr("Mouse")); ui->categoryList->setFixedWidth(ui->categoryList->sizeHintForColumn(0) + ui->categoryList->frameWidth() + listRightPadding); } @@ -574,6 +617,20 @@ void QVOptionsDialog::languageComboBoxCurrentIndexChanged(int index) } } +void QVOptionsDialog::formatsItemChanged(QTableWidgetItem *item) +{ + if (isLoadingFormats) + return; + + const QString extension = item->data(Qt::UserRole).toString(); + if (item->checkState() == Qt::Unchecked) + transientDisabledFileExtensions.insert(extension); + else + transientDisabledFileExtensions.remove(extension); + + updateButtonBox(); +} + void QVOptionsDialog::middleButtonModeChanged() { const bool isClick = ui->middleButtonModeClickRadioButton->isChecked(); diff --git a/src/qvoptionsdialog.h b/src/qvoptionsdialog.h index 4180a615..cbe6eb17 100644 --- a/src/qvoptionsdialog.h +++ b/src/qvoptionsdialog.h @@ -9,6 +9,7 @@ #include #include #include +#include namespace Ui { class QVOptionsDialog; @@ -40,6 +41,7 @@ class QVOptionsDialog : public QDialog void syncLineEdit(QLineEdit *lineEdit, const QString &key, bool defaults = false, bool makeConnection = false); void syncShortcuts(bool defaults = false); void updateShortcutsTable(); + void syncFormats(bool defaults = false); void updateButtonBox(); void bgColorButtonClicked(); void updateBgColorButton(); @@ -78,6 +80,8 @@ private slots: void languageComboBoxCurrentIndexChanged(int index); + void formatsItemChanged(QTableWidgetItem *item); + void middleButtonModeChanged(); private: @@ -87,9 +91,13 @@ private slots: QList transientShortcuts; + QSet transientDisabledFileExtensions; + bool isInitialLoad {true}; bool languageRestartMessageShown {false}; + + bool isLoadingFormats {false}; }; diff --git a/src/qvoptionsdialog.ui b/src/qvoptionsdialog.ui index 50723808..4b221efd 100644 --- a/src/qvoptionsdialog.ui +++ b/src/qvoptionsdialog.ui @@ -927,6 +927,69 @@ + + + + 10 + + + 0 + + + 0 + + + 0 + + + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + false + + + 0 + + + 2 + + + true + + + 120 + + + false + + + false + + + + Extension + + + + + Enabled + + + + + + true @@ -936,7 +999,7 @@ 0 0 - 482 + 386 609 diff --git a/src/settingsmanager.cpp b/src/settingsmanager.cpp index ccf6dd47..fb940ec8 100644 --- a/src/settingsmanager.cpp +++ b/src/settingsmanager.cpp @@ -216,6 +216,8 @@ void SettingsManager::initializeSettingsLibrary() settingsLibrary.insert("allowmimecontentdetection", {false, {}}); settingsLibrary.insert("saverecents", {true, {}}); settingsLibrary.insert("updatenotifications", {false, {}}); + // Formats + settingsLibrary.insert("disabledfileextensions", {"", {}}); // Mouse settingsLibrary.insert("viewportdoubleclickaction", {static_cast(Qv::ViewportClickAction::ToggleFullScreen), {}}); settingsLibrary.insert("viewportaltdoubleclickaction", {static_cast(Qv::ViewportClickAction::ToggleTitlebarHidden), {}});