From cac53ea41f98e5cf0b81ca3eea696940b98caeb3 Mon Sep 17 00:00:00 2001
From: Daniel Teske <daniel.teske@theqtcompany.com>
Date: Mon, 3 Aug 2015 12:44:48 +0200
Subject: [PATCH] Utils::PathChooser: Allow the core plugin to extend the
 context menu

And then use this to add the "Open in Graphical Shell" and "Open in
Terminal" actions.

Those actions cannot be implemented in Utils directly since the Core::FileUtils
depends on the Options dialog.

This affects all PathChoosers, and there's currently no way for a PathChooser
to opt out or have a different context menu. That can be added at a later
point.

Change-Id: I22121c19d66f08785381c7e0bca5317628eb6342
Task-number: QTCREATORBUG-14736
Reviewed-by: Eike Ziller <eike.ziller@theqtcompany.com>
---
 src/libs/utils/pathchooser.cpp        | 23 +++++++++++++++++++++++
 src/libs/utils/pathchooser.h          |  7 +++++++
 src/plugins/coreplugin/coreplugin.cpp | 26 ++++++++++++++++++++++++++
 src/plugins/coreplugin/coreplugin.h   | 10 +++++++++-
 4 files changed, 65 insertions(+), 1 deletion(-)

diff --git a/src/libs/utils/pathchooser.cpp b/src/libs/utils/pathchooser.cpp
index 59e1242f5d4..4d8685b009f 100644
--- a/src/libs/utils/pathchooser.cpp
+++ b/src/libs/utils/pathchooser.cpp
@@ -39,6 +39,7 @@
 #include <QDebug>
 #include <QFileDialog>
 #include <QHBoxLayout>
+#include <QMenu>
 #include <QPushButton>
 #include <QStandardPaths>
 
@@ -66,6 +67,8 @@ static QString appBundleExpandedPath(const QString &path)
     return path;
 }
 
+Utils::PathChooser::AboutToShowContextMenuHandler Utils::PathChooser::s_aboutToShowContextMenuHandler;
+
 namespace Utils {
 
 // ------------------ BinaryVersionToolTipEventFilter
@@ -221,6 +224,9 @@ PathChooser::PathChooser(QWidget *parent) :
 {
     d->m_hLayout->setContentsMargins(0, 0, 0, 0);
 
+    d->m_lineEdit->setContextMenuPolicy(Qt::CustomContextMenu);
+
+    connect(d->m_lineEdit, &FancyLineEdit::customContextMenuRequested, this, &PathChooser::contextMenuRequested);
     connect(d->m_lineEdit, &FancyLineEdit::validReturnPressed, this, &PathChooser::returnPressed);
     connect(d->m_lineEdit, &QLineEdit::textChanged, this, [this] { emit changed(rawPath()); });
     connect(d->m_lineEdit, &FancyLineEdit::validChanged, this, &PathChooser::validChanged);
@@ -422,6 +428,18 @@ void PathChooser::slotBrowse()
     triggerChanged();
 }
 
+void PathChooser::contextMenuRequested(const QPoint &pos)
+{
+    if (QMenu *menu = d->m_lineEdit->createStandardContextMenu()) {
+        menu->setAttribute(Qt::WA_DeleteOnClose);
+
+        if (s_aboutToShowContextMenuHandler)
+            s_aboutToShowContextMenuHandler(this, menu);
+
+        menu->popup(d->m_lineEdit->mapToGlobal(pos));
+    }
+}
+
 bool PathChooser::isValid() const
 {
     return d->m_lineEdit->isValid();
@@ -437,6 +455,11 @@ void PathChooser::triggerChanged()
     d->m_lineEdit->triggerChanged();
 }
 
+void PathChooser::setAboutToShowContextMenuHandler(PathChooser::AboutToShowContextMenuHandler handler)
+{
+    s_aboutToShowContextMenuHandler = handler;
+}
+
 FancyLineEdit::ValidationFunction PathChooser::defaultValidationFunction() const
 {
     return std::bind(&PathChooser::validatePath, this, std::placeholders::_1, std::placeholders::_2);
diff --git a/src/libs/utils/pathchooser.h b/src/libs/utils/pathchooser.h
index 64beffa91a6..5e701a9e93d 100644
--- a/src/libs/utils/pathchooser.h
+++ b/src/libs/utils/pathchooser.h
@@ -140,11 +140,17 @@ public:
 
     void triggerChanged();
 
+    // global handler for adding context menus to ALL pathchooser
+    // used by the coreplugin to add "Open in Terminal" and "Open in Explorer" context menu actions
+    using AboutToShowContextMenuHandler = std::function<void (Utils::PathChooser *, QMenu *)>;
+    static void setAboutToShowContextMenuHandler(AboutToShowContextMenuHandler handler);
+
 private:
     bool validatePath(FancyLineEdit *edit, QString *errorMessage) const;
     // Returns overridden title or the one from <title>
     QString makeDialogTitle(const QString &title);
     void slotBrowse();
+    void contextMenuRequested(const QPoint &pos);
 
 signals:
     void validChanged(bool validState);
@@ -161,6 +167,7 @@ public slots:
 
 private:
     PathChooserPrivate *d;
+    static AboutToShowContextMenuHandler s_aboutToShowContextMenuHandler;
 };
 
 } // namespace Utils
diff --git a/src/plugins/coreplugin/coreplugin.cpp b/src/plugins/coreplugin/coreplugin.cpp
index 7d75bb79ebe..f52fd828492 100644
--- a/src/plugins/coreplugin/coreplugin.cpp
+++ b/src/plugins/coreplugin/coreplugin.cpp
@@ -44,9 +44,11 @@
 #include <coreplugin/find/findplugin.h>
 #include <coreplugin/locator/locator.h>
 #include <coreplugin/coreconstants.h>
+#include <coreplugin/fileutils.h>
 
 #include <extensionsystem/pluginerroroverview.h>
 #include <extensionsystem/pluginmanager.h>
+#include <utils/pathchooser.h>
 #include <utils/macroexpander.h>
 #include <utils/savefile.h>
 #include <utils/stringutils.h>
@@ -57,6 +59,7 @@
 #include <QDebug>
 #include <QDateTime>
 #include <QDir>
+#include <QMenu>
 
 using namespace Core;
 using namespace Core::Internal;
@@ -229,6 +232,8 @@ bool CorePlugin::initialize(const QStringList &arguments, QString *errorMessage)
     // Make sure all wizards are there when the user might access the keyboard shortcuts:
     connect(ICore::instance(), &ICore::optionsDialogRequested, []() { IWizardFactory::allWizardFactories(); });
 
+    Utils::PathChooser::setAboutToShowContextMenuHandler(&CorePlugin::addToPathChooserContextMenu);
+
     return success;
 }
 
@@ -271,6 +276,27 @@ void CorePlugin::fileOpenRequest(const QString &f)
     remoteCommand(QStringList(), QString(), QStringList(f));
 }
 
+void CorePlugin::addToPathChooserContextMenu(Utils::PathChooser *pathChooser, QMenu *menu)
+{
+    QList<QAction*> actions = menu->actions();
+    QAction *firstAction = actions.isEmpty() ? nullptr : actions.first();
+
+    auto *showInGraphicalShell = new QAction(Core::FileUtils::msgGraphicalShellAction(), menu);
+    connect(showInGraphicalShell, &QAction::triggered, pathChooser, [pathChooser]() {
+        Core::FileUtils::showInGraphicalShell(pathChooser, pathChooser->path());
+    });
+    menu->insertAction(firstAction, showInGraphicalShell);
+
+    auto *showInTerminal = new QAction(Core::FileUtils::msgTerminalAction(), menu);
+    connect(showInTerminal, &QAction::triggered, pathChooser, [pathChooser]() {
+        Core::FileUtils::openTerminal(pathChooser->path());
+    });
+    menu->insertAction(firstAction, showInTerminal);
+
+    if (firstAction)
+        menu->insertSeparator(firstAction);
+}
+
 ExtensionSystem::IPlugin::ShutdownFlag CorePlugin::aboutToShutdown()
 {
     m_findPlugin->aboutToShutdown();
diff --git a/src/plugins/coreplugin/coreplugin.h b/src/plugins/coreplugin/coreplugin.h
index fd82e709d61..6401b721568 100644
--- a/src/plugins/coreplugin/coreplugin.h
+++ b/src/plugins/coreplugin/coreplugin.h
@@ -33,7 +33,14 @@
 
 #include <extensionsystem/iplugin.h>
 
-namespace Utils { class Theme; }
+QT_BEGIN_NAMESPACE
+class QMenu;
+QT_END_NAMESPACE
+
+namespace Utils {
+class PathChooser;
+class Theme;
+}
 
 namespace Core {
 
@@ -77,6 +84,7 @@ private slots:
 
 private:
     void parseArguments(const QStringList & arguments);
+    static void addToPathChooserContextMenu(Utils::PathChooser *pathChooser, QMenu *menu);
 
     MainWindow *m_mainWindow;
     EditMode *m_editMode;
-- 
GitLab