3 Commits

Author SHA1 Message Date
forust eabf420ac3 ci: fix GUI smoke binary name
ci-release / verify (push) Failing after 2m20s
ci-release / publish-linux-amd64 (push) Has been skipped
2026-07-26 19:03:54 +02:00
forust 9f7908b275 chore: release v0.3.0
ci-release / publish-linux-amd64 (push) Has been skipped
ci-release / verify (push) Failing after 1m46s
2026-07-26 16:46:25 +02:00
forust 010bd0f374 feat(ui): rename app and add input history
Import legacy SearchMyFiles settings once so the FolderScope identity change does not discard user profiles and preferences.
2026-07-26 16:46:08 +02:00
12 changed files with 173 additions and 54 deletions
+1 -1
View File
@@ -42,7 +42,7 @@ jobs:
shell: bash shell: bash
run: | run: |
set +e set +e
QT_QPA_PLATFORM=offscreen timeout 3s ./build/searchmyfiles QT_QPA_PLATFORM=offscreen timeout 3s ./build/folderscope
status="$?" status="$?"
test "$status" = 0 || test "$status" = 124 test "$status" = 0 || test "$status" = 124
+17
View File
@@ -5,6 +5,22 @@ All notable changes to FolderScope are documented here.
The project uses [Semantic Versioning](https://semver.org/). Release tags are The project uses [Semantic Versioning](https://semver.org/). Release tags are
formatted as `vMAJOR.MINOR.PATCH`. formatted as `vMAJOR.MINOR.PATCH`.
## [0.3.0] - 2026-07-26
### Added
- Added persistent input history for folder and wildcard fields in Search
Options.
### Changed
- Renamed the executable, desktop entry, icon, and application identity from
SearchMyFiles to FolderScope.
- Preserved existing settings and search profiles when migrating to the new
application identity.
- Kept the base-folder browser button compact so the input field uses the
available width.
## [0.2.5] - 2026-07-26 ## [0.2.5] - 2026-07-26
### Added ### Added
@@ -82,3 +98,4 @@ formatted as `vMAJOR.MINOR.PATCH`.
[0.2.3]: https://gitea.forust.xyz/forust/folderscope/releases/tag/v0.2.3 [0.2.3]: https://gitea.forust.xyz/forust/folderscope/releases/tag/v0.2.3
[0.2.4]: https://gitea.forust.xyz/forust/folderscope/releases/tag/v0.2.4 [0.2.4]: https://gitea.forust.xyz/forust/folderscope/releases/tag/v0.2.4
[0.2.5]: https://gitea.forust.xyz/forust/folderscope/releases/tag/v0.2.5 [0.2.5]: https://gitea.forust.xyz/forust/folderscope/releases/tag/v0.2.5
[0.3.0]: https://gitea.forust.xyz/forust/folderscope/releases/tag/v0.3.0
+7 -7
View File
@@ -22,7 +22,7 @@ target_link_libraries(folderscope_core PUBLIC Qt6::Core)
target_compile_options(folderscope_core PRIVATE -Wall -Wextra -Wpedantic) target_compile_options(folderscope_core PRIVATE -Wall -Wextra -Wpedantic)
target_include_directories(folderscope_core PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src") target_include_directories(folderscope_core PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src")
qt_add_executable(searchmyfiles qt_add_executable(folderscope
src/long_long_spin_box.cpp src/long_long_spin_box.cpp
src/long_long_spin_box.h src/long_long_spin_box.h
src/main.cpp src/main.cpp
@@ -38,14 +38,14 @@ qt_add_executable(searchmyfiles
src/options_dialog.cpp src/options_dialog.cpp
) )
target_link_libraries(searchmyfiles PRIVATE folderscope_core Qt6::Widgets Qt6::Concurrent Qt6::DBus) target_link_libraries(folderscope PRIVATE folderscope_core Qt6::Widgets Qt6::Concurrent Qt6::DBus)
target_compile_options(searchmyfiles PRIVATE -Wall -Wextra -Wpedantic) target_compile_options(folderscope PRIVATE -Wall -Wextra -Wpedantic)
target_compile_definitions(searchmyfiles PRIVATE FOLDERSCOPE_VERSION="${PROJECT_VERSION}") target_compile_definitions(folderscope PRIVATE FOLDERSCOPE_VERSION="${PROJECT_VERSION}")
install(TARGETS searchmyfiles RUNTIME DESTINATION bin) install(TARGETS folderscope RUNTIME DESTINATION bin)
install(FILES packaging/searchmyfiles.desktop install(FILES packaging/folderscope.desktop
DESTINATION share/applications) DESTINATION share/applications)
install(FILES packaging/searchmyfiles.svg install(FILES packaging/folderscope.svg
DESTINATION share/icons/hicolor/scalable/apps) DESTINATION share/icons/hicolor/scalable/apps)
include(CTest) include(CTest)
+1 -1
View File
@@ -34,7 +34,7 @@ Requirements:
```bash ```bash
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j cmake --build build -j
./build/searchmyfiles ./build/folderscope
``` ```
Run the automated tests: Run the automated tests:
+1 -1
View File
@@ -1 +1 @@
0.2.5 0.3.0
@@ -1,8 +1,8 @@
[Desktop Entry] [Desktop Entry]
Type=Application Type=Application
Name=SearchMyFiles Name=FolderScope
Comment=Fast advanced file search Comment=Fast advanced file search
Exec=searchmyfiles Exec=folderscope
Icon=searchmyfiles Icon=folderscope
Terminal=false Terminal=false
Categories=Utility;FileTools; Categories=Utility;FileTools;

Before

Width:  |  Height:  |  Size: 324 B

After

Width:  |  Height:  |  Size: 324 B

+25 -2
View File
@@ -1,15 +1,38 @@
#include <QApplication> #include <QApplication>
#include <QMetaType> #include <QMetaType>
#include <QSettings>
#include <QTimer> #include <QTimer>
#include "main_window.h" #include "main_window.h"
#include "search_core.h" #include "search_core.h"
namespace {
void migrateLegacySettings()
{
QSettings settings;
const QString migrationKey =
QStringLiteral("migration/searchMyFilesSettingsImported");
if (settings.value(migrationKey, false).toBool())
return;
QSettings legacy(QStringLiteral("SearchMyFilesLinux"),
QStringLiteral("SearchMyFiles"));
for (const QString &key : legacy.allKeys()) {
if (!settings.contains(key))
settings.setValue(key, legacy.value(key));
}
settings.setValue(migrationKey, true);
}
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
QApplication app(argc, argv); QApplication app(argc, argv);
QCoreApplication::setOrganizationName(QStringLiteral("SearchMyFilesLinux")); QCoreApplication::setOrganizationName(QStringLiteral("FolderScope"));
QCoreApplication::setApplicationName(QStringLiteral("SearchMyFiles")); QCoreApplication::setApplicationName(QStringLiteral("FolderScope"));
migrateLegacySettings();
QApplication::setStyle(QStringLiteral("Fusion")); QApplication::setStyle(QStringLiteral("Fusion"));
qRegisterMetaType<SearchOptions>(); qRegisterMetaType<SearchOptions>();
qRegisterMetaType<QVector<FileRecord>>(); qRegisterMetaType<QVector<FileRecord>>();
+2 -2
View File
@@ -23,7 +23,7 @@
MainWindow::MainWindow() MainWindow::MainWindow()
{ {
loadOptions(); loadOptions();
setWindowTitle(tr("SearchMyFiles for Linux")); setWindowTitle(tr("FolderScope"));
resize(1280, 760); resize(1280, 760);
model_ = new ResultsModel(this); model_ = new ResultsModel(this);
model_->setDisplayOptions(sizeUnit_, showGmt_, markDuplicates_, duplicateColorSet_); model_->setDisplayOptions(sizeUnit_, showGmt_, markDuplicates_, duplicateColorSet_);
@@ -507,7 +507,7 @@ void MainWindow::startCurrentSearch()
progress_->show(); progress_->show();
progress_->setRange(0, 0); progress_->setRange(0, 0);
progress_->setFormat(tr("Scanning…")); progress_->setFormat(tr("Scanning…"));
setWindowTitle(tr("%1 — %2 — SearchMyFiles for Linux") setWindowTitle(tr("%1 — %2 — FolderScope")
.arg(options_.roots, options_.mode)); .arg(options_.roots, options_.mode));
if (focusOnSearchStart_) if (focusOnSearchStart_)
table_->setFocus(); table_->setFocus();
+75 -30
View File
@@ -8,6 +8,7 @@
#include <QMessageBox> #include <QMessageBox>
#include <QMimeData> #include <QMimeData>
#include <QSettings> #include <QSettings>
#include <QSizePolicy>
#include "long_long_spin_box.h" #include "long_long_spin_box.h"
@@ -87,22 +88,29 @@ void OptionsDialog::setupUi(const SearchOptions &o)
auto *filesPage = new QWidget; auto *filesPage = new QWidget;
auto *filesForm = new QFormLayout(filesPage); auto *filesForm = new QFormLayout(filesPage);
roots_ = new QLineEdit(o.roots); roots_ = historyCombo(QStringLiteral("roots"), o.roots);
roots_->setAcceptDrops(true); roots_->lineEdit()->setAcceptDrops(true);
roots_->setToolTip(tr("Drop folders here from your file manager")); roots_->setToolTip(tr("Drop folders here from your file manager"));
roots_->installEventFilter(this); roots_->lineEdit()->installEventFilter(this);
auto *rootRow = new QWidget; auto *rootRow = new QWidget;
auto *rootLayout = new QHBoxLayout(rootRow); auto *rootLayout = new QHBoxLayout(rootRow);
rootLayout->setContentsMargins(0, 0, 0, 0); rootLayout->setContentsMargins(0, 0, 0, 0);
rootLayout->addWidget(roots_); rootLayout->addWidget(roots_);
auto *browse = new QPushButton(tr("Browse…")); auto *browse = new QPushButton(tr("Browse…"));
browse->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
rootLayout->addWidget(browse); rootLayout->addWidget(browse);
rootLayout->setStretch(0, 1);
filesForm->addRow(tr("Base folders (separate with ;):"), rootRow); filesForm->addRow(tr("Base folders (separate with ;):"), rootRow);
fileMasks_ = addLine(filesForm, tr("Files wildcard:"), o.fileWildcards); fileMasks_ = addHistoryCombo(filesForm, tr("Files wildcard:"),
subfolderMasks_ = addLine(filesForm, tr("Subfolders wildcard:"), o.subfolderWildcards); QStringLiteral("fileWildcards"), o.fileWildcards);
excludeFiles_ = addLine(filesForm, tr("Exclude files:"), o.excludeFiles); subfolderMasks_ = addHistoryCombo(filesForm, tr("Subfolders wildcard:"),
excludeFolders_ = addLine(filesForm, tr("Exclude folders:"), o.excludeFolders); QStringLiteral("subfolderWildcards"), o.subfolderWildcards);
includeFolders_ = addLine(filesForm, tr("Include only folders:"), o.includeFolders); excludeFiles_ = addHistoryCombo(filesForm, tr("Exclude files:"),
QStringLiteral("excludeFiles"), o.excludeFiles);
excludeFolders_ = addHistoryCombo(filesForm, tr("Exclude folders:"),
QStringLiteral("excludeFolders"), o.excludeFolders);
includeFolders_ = addHistoryCombo(filesForm, tr("Include only folders:"),
QStringLiteral("includeFolders"), o.includeFolders);
recursive_ = new QCheckBox(tr("Search subfolders")); recursive_->setChecked(o.recursive); recursive_ = new QCheckBox(tr("Search subfolders")); recursive_->setChecked(o.recursive);
findFiles_ = new QCheckBox(tr("Find files")); findFiles_->setChecked(o.findFiles); findFiles_ = new QCheckBox(tr("Find files")); findFiles_->setChecked(o.findFiles);
findFolders_ = new QCheckBox(tr("Find folders")); findFolders_->setChecked(o.findFolders); findFolders_ = new QCheckBox(tr("Find folders")); findFolders_->setChecked(o.findFolders);
@@ -183,7 +191,7 @@ void OptionsDialog::setupUi(const SearchOptions &o)
connect(buttons, &QDialogButtonBox::accepted, this, &OptionsDialog::accept); connect(buttons, &QDialogButtonBox::accepted, this, &OptionsDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
connect(browse, &QPushButton::clicked, this, [this] { connect(browse, &QPushButton::clicked, this, [this] {
const QStringList existingFolders = patterns(roots_->text()); const QStringList existingFolders = patterns(roots_->currentText());
const QString startFolder = existingFolders.isEmpty() const QString startFolder = existingFolders.isEmpty()
? QDir::homePath() : existingFolders.constLast(); ? QDir::homePath() : existingFolders.constLast();
const QString folder = QFileDialog::getExistingDirectory( const QString folder = QFileDialog::getExistingDirectory(
@@ -195,7 +203,7 @@ void OptionsDialog::setupUi(const SearchOptions &o)
const QString cleanPath = QDir::cleanPath(folder); const QString cleanPath = QDir::cleanPath(folder);
if (!folders.contains(cleanPath)) if (!folders.contains(cleanPath))
folders.push_back(cleanPath); folders.push_back(cleanPath);
roots_->setText(folders.join(u';')); roots_->setEditText(folders.join(u';'));
}); });
layout->addWidget(buttons); layout->addWidget(buttons);
} }
@@ -203,12 +211,12 @@ void OptionsDialog::setupUi(const SearchOptions &o)
SearchOptions OptionsDialog::options() const SearchOptions OptionsDialog::options() const
{ {
SearchOptions o; SearchOptions o;
o.roots = roots_->text(); o.roots = roots_->currentText();
o.fileWildcards = fileMasks_->text(); o.fileWildcards = fileMasks_->currentText();
o.subfolderWildcards = subfolderMasks_->text(); o.subfolderWildcards = subfolderMasks_->currentText();
o.excludeFiles = excludeFiles_->text(); o.excludeFiles = excludeFiles_->currentText();
o.excludeFolders = excludeFolders_->text(); o.excludeFolders = excludeFolders_->currentText();
o.includeFolders = includeFolders_->text(); o.includeFolders = includeFolders_->currentText();
o.contains = contains_->toPlainText(); o.contains = contains_->toPlainText();
o.binary = binary_->isChecked(); o.binary = binary_->isChecked();
o.multipleValues = multiple_->isChecked(); o.multipleValues = multiple_->isChecked();
@@ -244,14 +252,35 @@ void OptionsDialog::accept()
if (!validateDate(minTime_, tr("From time")) if (!validateDate(minTime_, tr("From time"))
|| !validateDate(maxTime_, tr("To time"))) || !validateDate(maxTime_, tr("To time")))
return; return;
rememberHistory(QStringLiteral("roots"), roots_->currentText());
rememberHistory(QStringLiteral("fileWildcards"), fileMasks_->currentText());
rememberHistory(QStringLiteral("subfolderWildcards"), subfolderMasks_->currentText());
rememberHistory(QStringLiteral("excludeFiles"), excludeFiles_->currentText());
rememberHistory(QStringLiteral("excludeFolders"), excludeFolders_->currentText());
rememberHistory(QStringLiteral("includeFolders"), includeFolders_->currentText());
QDialog::accept(); QDialog::accept();
} }
QLineEdit *OptionsDialog::addLine(QFormLayout *layout, const QString &label, const QString &value) QComboBox *OptionsDialog::historyCombo(const QString &key, const QString &value)
{ {
auto *edit = new QLineEdit(value); auto *combo = new QComboBox;
layout->addRow(label, edit); combo->setEditable(true);
return edit; combo->setInsertPolicy(QComboBox::NoInsert);
combo->setObjectName(QStringLiteral("history%1").arg(key));
QSettings settings;
const QStringList history = settings.value(QStringLiteral("inputHistory/%1").arg(key)).toStringList();
combo->addItems(history);
combo->setEditText(value);
return combo;
}
QComboBox *OptionsDialog::addHistoryCombo(QFormLayout *layout, const QString &label,
const QString &key, const QString &value)
{
auto *combo = historyCombo(key, value);
layout->addRow(label, combo);
return combo;
} }
LongLongSpinBox *OptionsDialog::sizeSpin(qint64 value) LongLongSpinBox *OptionsDialog::sizeSpin(qint64 value)
@@ -284,6 +313,22 @@ bool OptionsDialog::validateDate(QLineEdit *edit, const QString &label)
return false; return false;
} }
void OptionsDialog::rememberHistory(const QString &key, const QString &value)
{
if (value.isEmpty())
return;
QSettings settings;
const QString settingsKey = QStringLiteral("inputHistory/%1").arg(key);
QStringList history = settings.value(settingsKey).toStringList();
history.removeAll(value);
history.prepend(value);
constexpr qsizetype maxHistoryEntries = 20;
if (history.size() > maxHistoryEntries)
history.erase(history.begin() + maxHistoryEntries, history.end());
settings.setValue(settingsKey, history);
}
QComboBox *OptionsDialog::attrCombo(const QString &value) QComboBox *OptionsDialog::attrCombo(const QString &value)
{ {
auto *combo = new QComboBox; auto *combo = new QComboBox;
@@ -294,7 +339,7 @@ QComboBox *OptionsDialog::attrCombo(const QString &value)
bool OptionsDialog::eventFilter(QObject *obj, QEvent *event) bool OptionsDialog::eventFilter(QObject *obj, QEvent *event)
{ {
if (obj == roots_ && event->type() == QEvent::DragEnter) { if (obj == roots_->lineEdit() && event->type() == QEvent::DragEnter) {
auto *dragEnter = static_cast<QDragEnterEvent *>(event); auto *dragEnter = static_cast<QDragEnterEvent *>(event);
if (dragEnter->mimeData()->hasUrls()) { if (dragEnter->mimeData()->hasUrls()) {
dragEnter->acceptProposedAction(); dragEnter->acceptProposedAction();
@@ -302,11 +347,11 @@ bool OptionsDialog::eventFilter(QObject *obj, QEvent *event)
} }
return false; return false;
} }
if (obj == roots_ && event->type() == QEvent::Drop) { if (obj == roots_->lineEdit() && event->type() == QEvent::Drop) {
auto *drop = static_cast<QDropEvent *>(event); auto *drop = static_cast<QDropEvent *>(event);
if (!drop->mimeData()->hasUrls()) if (!drop->mimeData()->hasUrls())
return false; return false;
QStringList folders = patterns(roots_->text()); QStringList folders = patterns(roots_->currentText());
for (const QUrl &url : drop->mimeData()->urls()) { for (const QUrl &url : drop->mimeData()->urls()) {
if (!url.isLocalFile()) if (!url.isLocalFile())
continue; continue;
@@ -314,7 +359,7 @@ bool OptionsDialog::eventFilter(QObject *obj, QEvent *event)
if (QFileInfo::exists(cleanPath) && !folders.contains(cleanPath)) if (QFileInfo::exists(cleanPath) && !folders.contains(cleanPath))
folders.append(cleanPath); folders.append(cleanPath);
} }
roots_->setText(folders.join(u';')); roots_->setEditText(folders.join(u';'));
drop->acceptProposedAction(); drop->acceptProposedAction();
return true; return true;
} }
@@ -401,12 +446,12 @@ void OptionsDialog::loadProfile()
auto loadInt = [&](const char *key, int def) { return settings.value(QString::fromLatin1(key), def).toInt(); }; auto loadInt = [&](const char *key, int def) { return settings.value(QString::fromLatin1(key), def).toInt(); };
auto loadLong = [&](const char *key, qint64 def) { return settings.value(QString::fromLatin1(key), def).toLongLong(); }; auto loadLong = [&](const char *key, qint64 def) { return settings.value(QString::fromLatin1(key), def).toLongLong(); };
roots_->setText(load("roots")); roots_->setEditText(load("roots"));
fileMasks_->setText(load("fileWildcards")); fileMasks_->setEditText(load("fileWildcards"));
subfolderMasks_->setText(load("subfolderWildcards")); subfolderMasks_->setEditText(load("subfolderWildcards"));
excludeFiles_->setText(load("excludeFiles")); excludeFiles_->setEditText(load("excludeFiles"));
excludeFolders_->setText(load("excludeFolders")); excludeFolders_->setEditText(load("excludeFolders"));
includeFolders_->setText(load("includeFolders")); includeFolders_->setEditText(load("includeFolders"));
contains_->setText(load("contains")); contains_->setText(load("contains"));
binary_->setChecked(loadBool("binary", false)); binary_->setChecked(loadBool("binary", false));
multiple_->setChecked(loadBool("multipleValues", false)); multiple_->setChecked(loadBool("multipleValues", false));
+10 -7
View File
@@ -29,10 +29,13 @@ protected:
private: private:
bool eventFilter(QObject *obj, QEvent *event) override; bool eventFilter(QObject *obj, QEvent *event) override;
static QLineEdit *addLine(QFormLayout *layout, const QString &label, const QString &value); static QComboBox *historyCombo(const QString &key, const QString &value);
static QComboBox *addHistoryCombo(QFormLayout *layout, const QString &label,
const QString &key, const QString &value);
static LongLongSpinBox *sizeSpin(qint64 value); static LongLongSpinBox *sizeSpin(qint64 value);
static qint64 dateValue(const QLineEdit *edit); static qint64 dateValue(const QLineEdit *edit);
bool validateDate(QLineEdit *edit, const QString &label); bool validateDate(QLineEdit *edit, const QString &label);
void rememberHistory(const QString &key, const QString &value);
static QComboBox *attrCombo(const QString &value); static QComboBox *attrCombo(const QString &value);
void setupUi(const SearchOptions &o); void setupUi(const SearchOptions &o);
@@ -42,12 +45,12 @@ private:
QComboBox *mode_{}; QComboBox *mode_{};
QComboBox *duplicateMode_{}; QComboBox *duplicateMode_{};
QCheckBox *duplicateWithoutExtension_{}; QCheckBox *duplicateWithoutExtension_{};
QLineEdit *roots_{}; QComboBox *roots_{};
QLineEdit *fileMasks_{}; QComboBox *fileMasks_{};
QLineEdit *subfolderMasks_{}; QComboBox *subfolderMasks_{};
QLineEdit *excludeFiles_{}; QComboBox *excludeFiles_{};
QLineEdit *excludeFolders_{}; QComboBox *excludeFolders_{};
QLineEdit *includeFolders_{}; QComboBox *includeFolders_{};
QTextEdit *contains_{}; QTextEdit *contains_{};
QCheckBox *binary_{}; QCheckBox *binary_{};
QCheckBox *multiple_{}; QCheckBox *multiple_{};
+31
View File
@@ -1,6 +1,8 @@
#include "options_dialog.h" #include "options_dialog.h"
#include <QCheckBox> #include <QCheckBox>
#include <QComboBox>
#include <QSettings>
#include <QtTest> #include <QtTest>
class OptionsDialogTest final : public QObject { class OptionsDialogTest final : public QObject {
@@ -23,6 +25,35 @@ private slots:
strict->setChecked(false); strict->setChecked(false);
QVERIFY(!dialog.options().strictDuplicateComparison); QVERIFY(!dialog.options().strictDuplicateComparison);
} }
void remembersInputHistory()
{
QSettings settings;
settings.remove(QStringLiteral("inputHistory"));
SearchOptions options;
OptionsDialog first(options);
auto *roots = first.findChild<QComboBox *>(QStringLiteral("historyroots"));
QVERIFY(roots);
roots->setEditText(QStringLiteral("/tmp/first;/tmp/second"));
QVERIFY(QMetaObject::invokeMethod(&first, "accept"));
QCOMPARE(settings.value(QStringLiteral("inputHistory/roots")).toStringList(),
QStringList{QStringLiteral("/tmp/first;/tmp/second")});
OptionsDialog second(options);
auto *history = second.findChild<QComboBox *>(QStringLiteral("historyroots"));
QVERIFY(history);
QCOMPARE(history->itemText(0), QStringLiteral("/tmp/first;/tmp/second"));
history->setEditText(QStringLiteral("/tmp/new"));
QVERIFY(QMetaObject::invokeMethod(&second, "accept"));
QCOMPARE(settings.value(QStringLiteral("inputHistory/roots")).toStringList(),
(QStringList{QStringLiteral("/tmp/new"),
QStringLiteral("/tmp/first;/tmp/second")}));
settings.remove(QStringLiteral("inputHistory"));
}
}; };
QTEST_MAIN(OptionsDialogTest) QTEST_MAIN(OptionsDialogTest)