diff --git a/src/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp index 069bb899691..cc985f312f8 100644 --- a/src/libs/utils/fileutils.cpp +++ b/src/libs/utils/fileutils.cpp @@ -481,15 +481,29 @@ FilePath firstOrEmpty(const FilePaths &filePaths) return filePaths.isEmpty() ? FilePath() : filePaths.first(); } +bool FileUtils::hasNativeFileDialog() +{ + static std::optional hasNative; + if (!hasNative.has_value()) { + // Checking QFileDialog::itemDelegate() seems to be the only way to determine + // whether the dialog is native or not. + QFileDialog dialog; + hasNative = dialog.itemDelegate() == nullptr; + } + + return *hasNative; +} + FilePath FileUtils::getOpenFilePath(QWidget *parent, const QString &caption, const FilePath &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options, - bool fromDeviceIfShiftIsPressed) + bool fromDeviceIfShiftIsPressed, + bool forceNonNativeDialog) { - bool forceNonNativeDialog = dir.needsDevice(); + forceNonNativeDialog = forceNonNativeDialog || dir.needsDevice(); #ifdef QT_GUI_LIB if (fromDeviceIfShiftIsPressed && qApp->queryKeyboardModifiers() & Qt::ShiftModifier) { forceNonNativeDialog = true; @@ -514,9 +528,10 @@ FilePath FileUtils::getSaveFilePath(QWidget *parent, const FilePath &dir, const QString &filter, QString *selectedFilter, - QFileDialog::Options options) + QFileDialog::Options options, + bool forceNonNativeDialog) { - bool forceNonNativeDialog = dir.needsDevice(); + forceNonNativeDialog = forceNonNativeDialog || dir.needsDevice(); const QStringList schemes = QStringList(QStringLiteral("file")); return firstOrEmpty(getFilePaths(dialogParent(parent), @@ -535,9 +550,10 @@ FilePath FileUtils::getExistingDirectory(QWidget *parent, const QString &caption, const FilePath &dir, QFileDialog::Options options, - bool fromDeviceIfShiftIsPressed) + bool fromDeviceIfShiftIsPressed, + bool forceNonNativeDialog) { - bool forceNonNativeDialog = dir.needsDevice(); + forceNonNativeDialog = forceNonNativeDialog || dir.needsDevice(); #ifdef QT_GUI_LIB if (fromDeviceIfShiftIsPressed && qApp->queryKeyboardModifiers() & Qt::ShiftModifier) { diff --git a/src/libs/utils/fileutils.h b/src/libs/utils/fileutils.h index f70c3fe5744..a5408c2e28f 100644 --- a/src/libs/utils/fileutils.h +++ b/src/libs/utils/fileutils.h @@ -90,26 +90,31 @@ public: #ifdef QT_WIDGETS_LIB static void setDialogParentGetter(const std::function &getter); + static bool hasNativeFileDialog(); + static FilePath getOpenFilePath(QWidget *parent, const QString &caption, const FilePath &dir = {}, const QString &filter = {}, QString *selectedFilter = nullptr, QFileDialog::Options options = {}, - bool fromDeviceIfShiftIsPressed = false); + bool fromDeviceIfShiftIsPressed = false, + bool forceNonNativeDialog = false); static FilePath getSaveFilePath(QWidget *parent, const QString &caption, const FilePath &dir = {}, const QString &filter = {}, QString *selectedFilter = nullptr, - QFileDialog::Options options = {}); + QFileDialog::Options options = {}, + bool forceNonNativeDialog = false); static FilePath getExistingDirectory(QWidget *parent, const QString &caption, const FilePath &dir = {}, QFileDialog::Options options = QFileDialog::ShowDirsOnly, - bool fromDeviceIfShiftIsPressed = false); + bool fromDeviceIfShiftIsPressed = false, + bool forceNonNativeDialog = false); static FilePaths getOpenFilePaths(QWidget *parent, const QString &caption, diff --git a/src/libs/utils/pathchooser.cpp b/src/libs/utils/pathchooser.cpp index ef30703c9b7..b396081f189 100644 --- a/src/libs/utils/pathchooser.cpp +++ b/src/libs/utils/pathchooser.cpp @@ -8,11 +8,13 @@ #include "fileutils.h" #include "hostosinfo.h" #include "macroexpander.h" +#include "optionpushbutton.h" #include "qtcassert.h" #include "qtcprocess.h" +#include "utilstr.h" -#include #include +#include #include #include #include @@ -174,6 +176,9 @@ public: const MacroExpander *m_macroExpander = globalMacroExpander(); std::function m_openTerminal; bool m_allowPathFromDevice = false; + + QMenu *m_contextMenu = nullptr; + OptionPushButton *m_browseButton = nullptr; }; PathChooserPrivate::PathChooserPrivate() @@ -254,7 +259,15 @@ PathChooser::PathChooser(QWidget *parent) : d->m_hLayout->addWidget(d->m_lineEdit); d->m_hLayout->setSizeConstraint(QLayout::SetMinimumSize); - addButton(browseButtonLabel(), this, [this] { slotBrowse(); }); + d->m_contextMenu = new QMenu(d->m_browseButton); + d->m_contextMenu->addAction(Tr::tr("Local"), this, [this] { slotBrowse(false); }); + d->m_contextMenu->addAction(Tr::tr("Remote"), this, [this] { slotBrowse(true); }); + + d->m_browseButton = new OptionPushButton(); + d->m_browseButton->setText(browseButtonLabel()); + connect(d->m_browseButton, &OptionPushButton::clicked, this, [this] { slotBrowse(false); }); + + insertButton(d->m_buttons.count(), d->m_browseButton); setLayout(d->m_hLayout); setFocusProxy(d->m_lineEdit); @@ -277,13 +290,18 @@ void PathChooser::addButton(const QString &text, QObject *context, const std::fu insertButton(d->m_buttons.count(), text, context, callback); } +void PathChooser::insertButton(int index, QAbstractButton *button) +{ + d->m_hLayout->insertWidget(index + 1 /*line edit*/, button); + d->m_buttons.insert(index, button); +} + void PathChooser::insertButton(int index, const QString &text, QObject *context, const std::function &callback) { auto button = new QPushButton; button->setText(text); connect(button, &QAbstractButton::clicked, context, callback); - d->m_hLayout->insertWidget(index + 1/*line edit*/, button); - d->m_buttons.insert(index, button); + insertButton(index, button); } QString PathChooser::browseButtonLabel() @@ -371,7 +389,7 @@ void PathChooser::setReadOnly(bool b) button->setEnabled(!b); } -void PathChooser::slotBrowse() +void PathChooser::slotBrowse(bool remote) { emit beforeBrowsing(); @@ -387,6 +405,8 @@ void PathChooser::slotBrowse() predefined.clear(); } + remote = remote || filePath().needsDevice(); + // Prompt for a file/dir FilePath newPath; switch (d->m_acceptingKind) { @@ -394,7 +414,10 @@ void PathChooser::slotBrowse() case PathChooser::ExistingDirectory: newPath = FileUtils::getExistingDirectory(this, makeDialogTitle(tr("Choose Directory")), - predefined, {}, d->m_allowPathFromDevice); + predefined, + {}, + d->m_allowPathFromDevice, + remote); break; case PathChooser::ExistingCommand: case PathChooser::Command: @@ -404,7 +427,8 @@ void PathChooser::slotBrowse() d->m_dialogFilter, nullptr, {}, - d->m_allowPathFromDevice); + d->m_allowPathFromDevice, + remote); newPath = appBundleExpandedPath(newPath); break; case PathChooser::File: // fall through @@ -414,14 +438,18 @@ void PathChooser::slotBrowse() d->m_dialogFilter, nullptr, {}, - d->m_allowPathFromDevice); + d->m_allowPathFromDevice, + remote); newPath = appBundleExpandedPath(newPath); break; case PathChooser::SaveFile: newPath = FileUtils::getSaveFilePath(this, makeDialogTitle(tr("Choose File")), predefined, - d->m_dialogFilter); + d->m_dialogFilter, + nullptr, + {}, + remote); break; case PathChooser::Any: { newPath = FileUtils::getOpenFilePath(this, @@ -430,7 +458,8 @@ void PathChooser::slotBrowse() d->m_dialogFilter, nullptr, {}, - d->m_allowPathFromDevice); + d->m_allowPathFromDevice, + remote); break; } default: @@ -740,6 +769,11 @@ void PathChooser::setCommandVersionArguments(const QStringList &arguments) void PathChooser::setAllowPathFromDevice(bool allow) { d->m_allowPathFromDevice = allow; + + if (allow && FileUtils::hasNativeFileDialog()) + d->m_browseButton->setOptionalMenu(d->m_contextMenu); + else + d->m_browseButton->setOptionalMenu(nullptr); } bool PathChooser::allowPathFromDevice() const diff --git a/src/libs/utils/pathchooser.h b/src/libs/utils/pathchooser.h index 4fb31599a16..d87918db380 100644 --- a/src/libs/utils/pathchooser.h +++ b/src/libs/utils/pathchooser.h @@ -91,6 +91,7 @@ public: static FilePath homePath(); void addButton(const QString &text, QObject *context, const std::function &callback); + void insertButton(int index, QAbstractButton *button); void insertButton(int index, const QString &text, QObject *context, const std::function &callback); QAbstractButton *buttonAtIndex(int index) const; @@ -157,7 +158,7 @@ private: bool validatePath(FancyLineEdit *edit, QString *errorMessage) const; // Returns overridden title or the one from QString makeDialogTitle(const QString &title); - void slotBrowse(); + void slotBrowse(bool remote); void contextMenuRequested(const QPoint &pos); PathChooserPrivate *d = nullptr; diff --git a/src/plugins/cmakeprojectmanager/cmakesettingspage.cpp b/src/plugins/cmakeprojectmanager/cmakesettingspage.cpp index 99f74562dde..4e9bb6a24a2 100644 --- a/src/plugins/cmakeprojectmanager/cmakesettingspage.cpp +++ b/src/plugins/cmakeprojectmanager/cmakesettingspage.cpp @@ -417,6 +417,7 @@ CMakeToolItemConfigWidget::CMakeToolItemConfigWidget(CMakeToolItemModel *model) m_binaryChooser->setMinimumWidth(400); m_binaryChooser->setHistoryCompleter(QLatin1String("Cmake.Command.History")); m_binaryChooser->setCommandVersionArguments({"--version"}); + m_binaryChooser->setAllowPathFromDevice(true); m_qchFileChooser = new PathChooser(this); m_qchFileChooser->setExpectedKind(PathChooser::File);