From d6636e41800dcd07f04e6052a63a7c6fea75d91b Mon Sep 17 00:00:00 2001
From: Lasse Holmstedt <lasse.holmstedt@nokia.com>
Date: Wed, 21 Apr 2010 16:22:30 +0200
Subject: [PATCH] Debugger mode for simultaneous QML and C++ debugging (with
 plugins) If you have plugins that extend qml, you probably want to debug them
 too. This is now possible with the "Start debugging QML and C++
 Simultaneously" debug menu entry. Just select a QML project which uses c++
 plugins and start debugging in this mode.

---
 .../components/objectpropertiesview.cpp       |   4 -
 .../qmlinspector/components/objecttree.cpp    |   2 -
 src/plugins/qmlinspector/qmlinspector.cpp     | 284 ++++++++++++------
 src/plugins/qmlinspector/qmlinspector.h       |  27 +-
 .../qmlinspector/qmlinspectorconstants.h      |   2 +-
 .../qmlinspector/startexternalqmldialog.cpp   |  42 ++-
 .../qmlinspector/startexternalqmldialog.h     |  14 +
 .../qmlinspector/startexternalqmldialog.ui    |  30 +-
 8 files changed, 299 insertions(+), 106 deletions(-)

diff --git a/src/plugins/qmlinspector/components/objectpropertiesview.cpp b/src/plugins/qmlinspector/components/objectpropertiesview.cpp
index 1e380efbf5d..677330f078b 100644
--- a/src/plugins/qmlinspector/components/objectpropertiesview.cpp
+++ b/src/plugins/qmlinspector/components/objectpropertiesview.cpp
@@ -229,8 +229,6 @@ QString ObjectPropertiesView::propertyBaseClass(const QDeclarativeDebugObjectRef
     QmlJSEditor::ModelManagerInterface *modelManager = pluginManager->getObject<QmlJSEditor::ModelManagerInterface>();
     QmlJS::Snapshot snapshot = modelManager->snapshot();
 
-    //qDebug() << property.name() << object.source().url().path();
-
     QmlJS::Document::Ptr document = snapshot.document(object.source().url().path());
     if (document.isNull()) {
 
@@ -241,7 +239,6 @@ QString ObjectPropertiesView::propertyBaseClass(const QDeclarativeDebugObjectRef
             contents = ins.readAll();
             inFile.close();
         }
-        //qDebug() << contents;
 
         document = QmlJS::Document::create(object.source().url().path());
         document->setSource(contents);
@@ -301,7 +298,6 @@ void ObjectPropertiesView::setObject(const QDeclarativeDebugObjectReference &obj
 
                     baseClassItems.insert(baseClassName, baseClassItem);
 
-                    qDebug() << "Baseclass" << baseClassName;
                 }
                 currentParentItem = baseClassItems.value(baseClassName);
                 item = new PropertiesViewItem(currentParentItem);
diff --git a/src/plugins/qmlinspector/components/objecttree.cpp b/src/plugins/qmlinspector/components/objecttree.cpp
index 970113e79b5..cb709e96c43 100644
--- a/src/plugins/qmlinspector/components/objecttree.cpp
+++ b/src/plugins/qmlinspector/components/objecttree.cpp
@@ -184,8 +184,6 @@ void ObjectTree::buildTree(const QDeclarativeDebugObjectReference &obj, QTreeWid
     if (!parent)
         clear();
 
-    qDebug() << obj.className();
-
     if (obj.contextDebugId() < 0 && !m_showUninspectableItems)
         return;
 
diff --git a/src/plugins/qmlinspector/qmlinspector.cpp b/src/plugins/qmlinspector/qmlinspector.cpp
index c12b5aee756..23475d05dfa 100644
--- a/src/plugins/qmlinspector/qmlinspector.cpp
+++ b/src/plugins/qmlinspector/qmlinspector.cpp
@@ -162,7 +162,8 @@ QmlInspector::QmlInspector(QObject *parent)
     m_inspectorOutputDock(0),
     m_connectionTimer(new QTimer(this)),
     m_connectionAttempts(0),
-    m_simultaneousCppAndQmlDebugMode(false)
+    m_simultaneousCppAndQmlDebugMode(false),
+    m_debugMode(StandaloneMode)
 {
     m_instance = this;
     m_watchTableModel = new Internal::WatchTableModel(0, this);
@@ -174,6 +175,8 @@ QmlInspector::QmlInspector(QObject *parent)
 //    m_frameRateWidget = new Internal::CanvasFrameRate;
 //    m_frameRateWidget->setObjectName(QLatin1String("QmlDebugFrameRate"));
 
+    Debugger::DebuggerManager *debugManager = Debugger::DebuggerManager::instance();
+    connect(debugManager, SIGNAL(stateChanged(int)), this, SLOT(debuggerStateChanged(int)));
 
     m_editablePropertyTypes = QStringList() << "qreal" << "bool" << "QString"
                                             << "int" << "QVariant" << "QUrl" << "QColor";
@@ -202,6 +205,7 @@ void QmlInspector::pollInspector()
                               tr("Failed to connect to debugger"),
                               tr("Could not connect to debugger server.") );
     }
+    updateMenuActions();
 }
 
 bool QmlInspector::setDebugConfigurationDataFromProject(ProjectExplorer::Project *projectToDebug)
@@ -227,11 +231,6 @@ bool QmlInspector::setDebugConfigurationDataFromProject(ProjectExplorer::Project
 void QmlInspector::startQmlProjectDebugger()
 {
     m_simultaneousCppAndQmlDebugMode = false;
-    startConnectionTimer();
-}
-
-void QmlInspector::startConnectionTimer()
-{
     m_connectionTimer->start();
 }
 
@@ -269,6 +268,7 @@ bool QmlInspector::connectToViewer()
 void QmlInspector::disconnectFromViewer()
 {
     m_conn->disconnectFromHost();
+    updateMenuActions();
 }
 
 void QmlInspector::connectionStateChanged()
@@ -285,6 +285,8 @@ void QmlInspector::connectionStateChanged()
 
             resetViews();
 
+            updateMenuActions();
+
             break;
         }
         case QAbstractSocket::HostLookupState:
@@ -445,14 +447,14 @@ void QmlInspector::createDockWidgets()
     core->addContextObject(m_propWatcherContext);
     core->addContextObject(m_context);
 
-    QAction *attachToExternalAction = new QAction(this);
-    attachToExternalAction->setText(tr("Start Debugging C++ and QML Simultaneously..."));
-    connect(attachToExternalAction, SIGNAL(triggered()),
-        this, SLOT(attachToExternalQmlApplication()));
+    m_simultaneousDebugAction = new QAction(this);
+    m_simultaneousDebugAction->setText(tr("Start Debugging C++ and QML Simultaneously..."));
+    connect(m_simultaneousDebugAction, SIGNAL(triggered()),
+        this, SLOT(simultaneouslyDebugQmlCppApplication()));
 
     Core::ActionManager *am = core->actionManager();
     Core::ActionContainer *mstart = am->actionContainer(ProjectExplorer::Constants::M_DEBUG_STARTDEBUGGING);
-    Core::Command *cmd = am->registerAction(attachToExternalAction, Constants::M_ATTACH_TO_EXTERNAL,
+    Core::Command *cmd = am->registerAction(m_simultaneousDebugAction, Constants::M_DEBUG_SIMULTANEOUSLY,
                                             QList<int>() << m_context->context());
     cmd->setAttribute(Core::Command::CA_Hide);
     mstart->addAction(cmd, Core::Constants::G_DEFAULT_ONE);
@@ -471,128 +473,222 @@ void QmlInspector::createDockWidgets()
             SLOT(setContextHelpId(QString)));
 }
 
-void QmlInspector::attachToExternalQmlApplication()
+void QmlInspector::simultaneouslyDebugQmlCppApplication()
 {
+    QString errorMessage;
     ProjectExplorer::ProjectExplorerPlugin *pex = ProjectExplorer::ProjectExplorerPlugin::instance();
     ProjectExplorer::Project *project = pex->startupProject();
+
+    if (!project)
+         errorMessage = QString(tr("No project was found."));
+    else {
+        if (project->id() == "QmlProjectManager.QmlProject")
+            errorMessage = attachToQmlViewerAsExternalApp(project);
+        else {
+            errorMessage = attachToExternalCppAppWithQml(project);
+        }
+    }
+
+    if (!errorMessage.isEmpty())
+        QMessageBox::warning(Core::ICore::instance()->mainWindow(), "Failed to debug C++ and QML", errorMessage);
+}
+
+QString QmlInspector::attachToQmlViewerAsExternalApp(ProjectExplorer::Project *project)
+{
+    m_debugMode = QmlProjectWithCppPlugins;
+
+    QmlProjectManager::QmlProjectRunConfiguration* runConfig =
+                qobject_cast<QmlProjectManager::QmlProjectRunConfiguration*>(project->activeTarget()->activeRunConfiguration());
+
+    if (!runConfig)
+        return QString(tr("No run configurations were found for the project '%1'.").arg(project->displayName()));
+
+    Internal::StartExternalQmlDialog dlg(Debugger::DebuggerUISwitcher::instance()->mainWindow());
+
+    QString importPathArgument = "-I";
+    QString execArgs;
+    if (runConfig->viewerArguments().contains(importPathArgument))
+        execArgs = runConfig->viewerArguments().join(" ");
+    else {
+        QFileInfo qmlFileInfo(runConfig->viewerArguments().last());
+        importPathArgument.append(" " + qmlFileInfo.absolutePath() + " ");
+        execArgs = importPathArgument + runConfig->viewerArguments().join(" ");
+    }
+
+
+    dlg.setPort(runConfig->debugServerPort());
+    dlg.setDebuggerUrl(runConfig->debugServerAddress());
+    dlg.setProjectDisplayName(project->displayName());
+    dlg.setDebugMode(Internal::StartExternalQmlDialog::QmlProjectWithCppPlugins);
+    dlg.setQmlViewerArguments(execArgs);
+    dlg.setQmlViewerPath(runConfig->viewerPath());
+
+    if (dlg.exec() != QDialog::Accepted)
+        return QString();
+
+    m_runConfigurationDebugData.serverAddress = dlg.debuggerUrl();
+    m_runConfigurationDebugData.serverPort = dlg.port();
+    m_settings.setExternalPort(dlg.port());
+    m_settings.setExternalUrl(dlg.debuggerUrl());
+
+    ProjectExplorer::Environment customEnv = ProjectExplorer::Environment::systemEnvironment(); // empty env by default
+    customEnv.set(QmlProjectManager::Constants::E_QML_DEBUG_SERVER_PORT, QString::number(m_settings.externalPort()));
+
+    Debugger::Internal::DebuggerRunControl *debuggableRunControl = createDebuggerRunControl(runConfig,
+                                                                                            dlg.qmlViewerPath(), dlg.qmlViewerArguments());
+
+    return executeDebuggerRunControl(debuggableRunControl, &customEnv);
+}
+
+QString QmlInspector::attachToExternalCppAppWithQml(ProjectExplorer::Project *project)
+{
+    m_debugMode = CppProjectWithQmlEngines;
+
     ProjectExplorer::LocalApplicationRunConfiguration* runConfig =
                 qobject_cast<ProjectExplorer::LocalApplicationRunConfiguration*>(project->activeTarget()->activeRunConfiguration());
 
-    QString errorMessage;
-
-    if (!project)
-        errorMessage = QString(tr("No project was found."));
-    else if (!project->activeTarget() || !project->activeTarget()->activeRunConfiguration())
-        errorMessage = QString(tr("No run configurations were found for the project '%1'.").arg(project->displayName()));
+    if (!project->activeTarget() || !project->activeTarget()->activeRunConfiguration())
+        return QString(tr("No run configurations were found for the project '%1'.").arg(project->displayName()));
     else if (!runConfig)
-        errorMessage = QString(tr("No valid run configuration was found for the project %1. "
+        return QString(tr("No valid run configuration was found for the project %1. "
                                   "Only locally runnable configurations are supported.\n"
                                   "Please check your project settings.").arg(project->displayName()));
 
+    Internal::StartExternalQmlDialog dlg(Debugger::DebuggerUISwitcher::instance()->mainWindow());
 
-    if (errorMessage.isEmpty()) {
+    dlg.setPort(m_settings.externalPort());
+    dlg.setDebuggerUrl(m_settings.externalUrl());
+    dlg.setProjectDisplayName(project->displayName());
+    dlg.setDebugMode(Internal::StartExternalQmlDialog::CppProjectWithQmlEngine);
+    if (dlg.exec() != QDialog::Accepted)
+        return QString();
 
-        Internal::StartExternalQmlDialog dlg(Debugger::DebuggerUISwitcher::instance()->mainWindow());
+    m_runConfigurationDebugData.serverAddress = dlg.debuggerUrl();
+    m_runConfigurationDebugData.serverPort = dlg.port();
+    m_settings.setExternalPort(dlg.port());
+    m_settings.setExternalUrl(dlg.debuggerUrl());
 
-        dlg.setPort(m_settings.externalPort());
-        dlg.setDebuggerUrl(m_settings.externalUrl());
-        dlg.setProjectDisplayName(project->displayName());
-        if (dlg.exec() != QDialog::Accepted)
-            return;
+    ProjectExplorer::Environment customEnv = runConfig->environment();
+    customEnv.set(QmlProjectManager::Constants::E_QML_DEBUG_SERVER_PORT, QString::number(m_settings.externalPort()));
+    Debugger::Internal::DebuggerRunControl *debuggableRunControl = createDebuggerRunControl(runConfig);
+    return executeDebuggerRunControl(debuggableRunControl, &customEnv);
+}
 
-        m_runConfigurationDebugData.serverAddress = dlg.debuggerUrl();
-        m_runConfigurationDebugData.serverPort = dlg.port();
-        m_settings.setExternalPort(dlg.port());
-        m_settings.setExternalUrl(dlg.debuggerUrl());
+QString QmlInspector::executeDebuggerRunControl(Debugger::Internal::DebuggerRunControl *debuggableRunControl, ProjectExplorer::Environment *environment)
+{
+    ProjectExplorer::ProjectExplorerPlugin *pex = ProjectExplorer::ProjectExplorerPlugin::instance();
 
-        ProjectExplorer::Environment customEnv = runConfig->environment();
-        customEnv.set(QmlProjectManager::Constants::E_QML_DEBUG_SERVER_PORT, QString::number(m_settings.externalPort()));
+    // to make sure we have a valid, debuggable run control, find the correct factory for it
+    if (debuggableRunControl) {
 
-        ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
-        const QList<Debugger::Internal::DebuggerRunControlFactory *> factories = pm->getObjects<Debugger::Internal::DebuggerRunControlFactory>();
-        // to make sure we have a valid, debuggable run control, find the correct factory for it
-        if (factories.length() && factories.first()->canRun(runConfig, ProjectExplorer::Constants::DEBUGMODE)) {
+        // modify the env
+        debuggableRunControl->setCustomEnvironment(*environment);
 
+        pex->startRunControl(debuggableRunControl, ProjectExplorer::Constants::DEBUGMODE);
+        m_simultaneousCppAndQmlDebugMode = true;
 
-            ProjectExplorer::RunControl *runControl = factories.first()->create(runConfig, ProjectExplorer::Constants::DEBUGMODE);
-            Debugger::Internal::DebuggerRunControl *debuggableRunControl = qobject_cast<Debugger::Internal::DebuggerRunControl *>(runControl);
-            // modify the env
-            debuggableRunControl->setCustomEnvironment(customEnv);
+        return QString();
+    }
+    return QString(tr("A valid run control was not registered in Qt Creator for this project run configuration."));;
+}
 
-            Debugger::DebuggerManager *debugManager = Debugger::DebuggerManager::instance();
-            connect(debugManager, SIGNAL(stateChanged(int)), this, SLOT(debuggerStateChanged(int)));
+Debugger::Internal::DebuggerRunControl *QmlInspector::createDebuggerRunControl(ProjectExplorer::RunConfiguration *runConfig,
+                                                                               const QString &executableFile, const QString &executableArguments)
+{
+    ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
+    const QList<Debugger::Internal::DebuggerRunControlFactory *> factories = pm->getObjects<Debugger::Internal::DebuggerRunControlFactory>();
+    ProjectExplorer::RunControl *runControl = 0;
+    if (m_debugMode == QmlProjectWithCppPlugins) {
 
-            pex->startRunControl(debuggableRunControl, ProjectExplorer::Constants::DEBUGMODE);
-            m_simultaneousCppAndQmlDebugMode = true;
+        const Debugger::DebuggerStartParametersPtr sp(new Debugger::DebuggerStartParameters);
+        sp->startMode = Debugger::StartExternal;
+        sp->executable = executableFile;
+        sp->processArgs = executableArguments.split(QLatin1Char(' '));
 
-        } else {
-            errorMessage = QString(tr("A valid run control was not registered in Qt Creator for this project run configuration."));
+        runControl = factories.first()->create(sp);
+        return qobject_cast<Debugger::Internal::DebuggerRunControl *>(runControl);
+
+    } else if (m_debugMode == CppProjectWithQmlEngines) {
+        if (factories.length() && factories.first()->canRun(runConfig, ProjectExplorer::Constants::DEBUGMODE)) {
+            runControl = factories.first()->create(runConfig, ProjectExplorer::Constants::DEBUGMODE);
+            return qobject_cast<Debugger::Internal::DebuggerRunControl *>(runControl);
         }
+    }
 
+    return 0;
+}
 
-    }
+void QmlInspector::updateMenuActions()
+{
 
-    if (!errorMessage.isEmpty())
-        QMessageBox::warning(Core::ICore::instance()->mainWindow(), "Failed to debug C++ and QML", errorMessage);
+    bool enabled = true;
+    if (m_simultaneousCppAndQmlDebugMode)
+        enabled = (m_cppDebuggerState == Debugger::DebuggerNotReady && (!m_conn || m_conn->state() == QAbstractSocket::UnconnectedState));
+    else
+        enabled = (!m_conn || m_conn->state() == QAbstractSocket::UnconnectedState);
+
+    m_simultaneousDebugAction->setEnabled(enabled);
 }
 
+
 void QmlInspector::debuggerStateChanged(int newState)
 {
-    if (!m_simultaneousCppAndQmlDebugMode)
-        return;
+    if (m_simultaneousCppAndQmlDebugMode) {
 
-    switch(newState) {
-    case Debugger::EngineStarting:
-        {
-            m_connectionInitialized = false;
+        switch(newState) {
+        case Debugger::EngineStarting:
+            {
+                m_connectionInitialized = false;
+                break;
+            }
+        case Debugger::AdapterStartFailed:
+        case Debugger::InferiorStartFailed:
+            emit statusMessage(QString(tr("Debugging failed: could not start C++ debugger.")));
             break;
-        }
-    case Debugger::AdapterStartFailed:
-    case Debugger::InferiorStartFailed:
-        disconnect(Debugger::DebuggerManager::instance(), SIGNAL(stateChanged(int)), this, SLOT(debuggerStateChanged(int)));
-        emit statusMessage(QString(tr("Debugging failed: could not start C++ debugger.")));
-        break;
-    case Debugger::InferiorRunningRequested:
-        {
-            if (m_cppDebuggerState == Debugger::InferiorStopped) {
-                // re-enable UI again
+        case Debugger::InferiorRunningRequested:
+            {
+                if (m_cppDebuggerState == Debugger::InferiorStopped) {
+                    // re-enable UI again
+                    m_objectTreeWidget->setEnabled(true);
+                    m_propertiesWidget->setEnabled(true);
+                    m_expressionWidget->setEnabled(true);
+                }
+                break;
+            }
+        case Debugger::InferiorRunning:
+            {
+                if (!m_connectionInitialized) {
+                    m_connectionInitialized = true;
+                    m_connectionTimer->setInterval(ConnectionAttemptSimultaneousInterval);
+                    m_connectionTimer->start();
+                }
+                break;
+            }
+        case Debugger::InferiorStopped:
+            {
+                m_objectTreeWidget->setEnabled(false);
+                m_propertiesWidget->setEnabled(false);
+                m_expressionWidget->setEnabled(false);
+                break;
+            }
+        case Debugger::EngineShuttingDown:
+            {
+                m_connectionInitialized = false;
+                // here it's safe to enable the debugger windows again -
+                // disabled ones look ugly.
                 m_objectTreeWidget->setEnabled(true);
                 m_propertiesWidget->setEnabled(true);
                 m_expressionWidget->setEnabled(true);
+                m_simultaneousCppAndQmlDebugMode = false;
+                break;
             }
+        default:
             break;
         }
-    case Debugger::InferiorRunning:
-        {
-            if (!m_connectionInitialized) {
-                m_connectionInitialized = true;
-                m_connectionTimer->setInterval(ConnectionAttemptSimultaneousInterval);
-                startConnectionTimer();
-            }
-            break;
-        }
-    case Debugger::InferiorStopped:
-        {
-            m_objectTreeWidget->setEnabled(false);
-            m_propertiesWidget->setEnabled(false);
-            m_expressionWidget->setEnabled(false);
-            break;
-        }
-    case Debugger::EngineShuttingDown:
-        {
-            m_connectionInitialized = false;
-            // here it's safe to enable the debugger windows again -
-            // disabled ones look ugly.
-            m_objectTreeWidget->setEnabled(true);
-            m_propertiesWidget->setEnabled(true);
-            m_expressionWidget->setEnabled(true);
-            m_simultaneousCppAndQmlDebugMode = false;
-            break;
-        }
-    default:
-        break;
     }
+
     m_cppDebuggerState = newState;
+    updateMenuActions();
 }
 
 
diff --git a/src/plugins/qmlinspector/qmlinspector.h b/src/plugins/qmlinspector/qmlinspector.h
index a7bbdff6fcc..691917b07f8 100644
--- a/src/plugins/qmlinspector/qmlinspector.h
+++ b/src/plugins/qmlinspector/qmlinspector.h
@@ -56,11 +56,17 @@ QT_END_NAMESPACE
 
 namespace ProjectExplorer {
     class Project;
+    class Environment;
 }
 
 namespace Core {
     class IContext;
 }
+namespace Debugger {
+namespace Internal {
+    class DebuggerRunControl;
+} // Internal
+} // Debugger
 
 namespace Qml {
 
@@ -85,6 +91,12 @@ class QMLINSPECTOR_EXPORT QmlInspector : public QObject
     Q_OBJECT
 
 public:
+    enum DebugMode {
+        StandaloneMode,
+        CppProjectWithQmlEngines,
+        QmlProjectWithCppPlugins
+    };
+
     QmlInspector(QObject *parent = 0);
     ~QmlInspector();
 
@@ -109,7 +121,6 @@ public slots:
     void setSimpleDockWidgetArrangement();
 
 private slots:
-    void startConnectionTimer();
     void connectionStateChanged();
     void connectionError();
     void reloadEngines();
@@ -117,12 +128,20 @@ private slots:
     void queryEngineContext(int);
     void contextChanged();
     void treeObjectActivated(const QDeclarativeDebugObjectReference &obj);
-    void attachToExternalQmlApplication();
+    void simultaneouslyDebugQmlCppApplication();
 
     void debuggerStateChanged(int newState);
     void pollInspector();
 
 private:
+    void updateMenuActions();
+    Debugger::Internal::DebuggerRunControl *createDebuggerRunControl(ProjectExplorer::RunConfiguration *runConfig,
+                                                                     const QString &executableFile = QString(),
+                                                                     const QString &executableArguments = QString());
+    QString executeDebuggerRunControl(Debugger::Internal::DebuggerRunControl *debuggableRunControl, ProjectExplorer::Environment *environment);
+    QString attachToQmlViewerAsExternalApp(ProjectExplorer::Project *project);
+    QString attachToExternalCppAppWithQml(ProjectExplorer::Project *project);
+
     bool addQuotesForData(const QVariant &value) const;
     void resetViews();
 
@@ -151,6 +170,8 @@ private:
     Internal::InspectorContext *m_context;
     Internal::InspectorContext *m_propWatcherContext;
 
+    QAction *m_simultaneousDebugAction;
+
     QTimer *m_connectionTimer;
     int m_connectionAttempts;
 
@@ -159,9 +180,11 @@ private:
 
     QStringList m_editablePropertyTypes;
 
+    // simultaneous debug mode stuff
     int m_cppDebuggerState;
     bool m_connectionInitialized;
     bool m_simultaneousCppAndQmlDebugMode;
+    DebugMode m_debugMode;
 
     static QmlInspector *m_instance;
 };
diff --git a/src/plugins/qmlinspector/qmlinspectorconstants.h b/src/plugins/qmlinspector/qmlinspectorconstants.h
index f5b23f0ce34..a8a3af98981 100644
--- a/src/plugins/qmlinspector/qmlinspectorconstants.h
+++ b/src/plugins/qmlinspector/qmlinspectorconstants.h
@@ -39,7 +39,7 @@ namespace Qml {
         const char * const C_INSPECTOR = "QmlInspector";
         const char * const COMPLETE_THIS = "QmlInspector.CompleteThis";
 
-        const char * const M_ATTACH_TO_EXTERNAL = "QmlInspector.Menu.AttachToExternal";
+        const char * const M_DEBUG_SIMULTANEOUSLY = "QmlInspector.Menu.SimultaneousDebug";
 
         const char * const LANG_QML = "QML";
 
diff --git a/src/plugins/qmlinspector/startexternalqmldialog.cpp b/src/plugins/qmlinspector/startexternalqmldialog.cpp
index a06d15c4b22..b21fdd066b3 100644
--- a/src/plugins/qmlinspector/startexternalqmldialog.cpp
+++ b/src/plugins/qmlinspector/startexternalqmldialog.cpp
@@ -10,7 +10,7 @@ namespace Internal {
 
 
 StartExternalQmlDialog::StartExternalQmlDialog(QWidget *parent)
-  : QDialog(parent), m_ui(new Ui::StartExternalQmlDialog)
+  : QDialog(parent), m_ui(new Ui::StartExternalQmlDialog), m_debugMode(QmlProjectWithCppPlugins)
 {
     m_ui->setupUi(this);
     m_ui->buttonBox->button(QDialogButtonBox::Ok)->setDefault(true);
@@ -50,5 +50,45 @@ void StartExternalQmlDialog::setProjectDisplayName(const QString &projectName)
     m_ui->projectDisplayName->setText(projectName);
 }
 
+void StartExternalQmlDialog::setQmlViewerPath(const QString &path)
+{
+    m_ui->viewerPathLineEdit->setText(path);
+}
+
+QString StartExternalQmlDialog::qmlViewerPath() const
+{
+    return m_ui->viewerPathLineEdit->text();
+}
+
+void StartExternalQmlDialog::setQmlViewerArguments(const QString &arguments)
+{
+    m_ui->viewerArgumentsLineEdit->setText(arguments);
+}
+
+QString StartExternalQmlDialog::qmlViewerArguments() const
+{
+    return m_ui->viewerArgumentsLineEdit->text();
+}
+
+void StartExternalQmlDialog::setDebugMode(DebugMode mode)
+{
+    m_debugMode = mode;
+    if (m_debugMode == QmlProjectWithCppPlugins) {
+        m_ui->labelViewerPath->show();
+        m_ui->viewerPathLineEdit->show();
+        m_ui->labelViewerArguments->show();
+        m_ui->viewerArgumentsLineEdit->show();
+        setMinimumHeight(270);
+        resize(width(), 270);
+    } else {
+        m_ui->labelViewerPath->hide();
+        m_ui->viewerPathLineEdit->hide();
+        m_ui->labelViewerArguments->hide();
+        m_ui->viewerArgumentsLineEdit->hide();
+        setMinimumHeight(200);
+        resize(width(), 200);
+    }
+}
+
 } // Internal
 } // Qml
diff --git a/src/plugins/qmlinspector/startexternalqmldialog.h b/src/plugins/qmlinspector/startexternalqmldialog.h
index 225fc06f2e7..2675d905ef9 100644
--- a/src/plugins/qmlinspector/startexternalqmldialog.h
+++ b/src/plugins/qmlinspector/startexternalqmldialog.h
@@ -21,6 +21,19 @@ public:
     explicit StartExternalQmlDialog(QWidget *parent);
     ~StartExternalQmlDialog();
 
+    enum DebugMode {
+        QmlProjectWithCppPlugins,
+        CppProjectWithQmlEngine
+    };
+
+    void setDebugMode(DebugMode mode);
+
+    void setQmlViewerPath(const QString &path);
+    QString qmlViewerPath() const;
+
+    void setQmlViewerArguments(const QString &arguments);
+    QString qmlViewerArguments() const;
+
     void setDebuggerUrl(const QString &url);
     QString debuggerUrl() const;
 
@@ -33,6 +46,7 @@ private slots:
 
 private:
     Ui::StartExternalQmlDialog *m_ui;
+    DebugMode m_debugMode;
 };
 
 } // Internal
diff --git a/src/plugins/qmlinspector/startexternalqmldialog.ui b/src/plugins/qmlinspector/startexternalqmldialog.ui
index a846d8e7b5e..89f0fb95914 100644
--- a/src/plugins/qmlinspector/startexternalqmldialog.ui
+++ b/src/plugins/qmlinspector/startexternalqmldialog.ui
@@ -7,11 +7,17 @@
     <x>0</x>
     <y>0</y>
     <width>425</width>
-    <height>198</height>
+    <height>278</height>
    </rect>
   </property>
+  <property name="minimumSize">
+   <size>
+    <width>425</width>
+    <height>278</height>
+   </size>
+  </property>
   <property name="windowTitle">
-   <string>Start Debugging QML and C++ simultaneously</string>
+   <string>Start Simultaneous QML and C++ Debugging </string>
   </property>
   <layout class="QVBoxLayout">
    <property name="spacing">
@@ -73,6 +79,26 @@
        </property>
       </widget>
      </item>
+     <item row="4" column="0">
+      <widget class="QLabel" name="labelViewerPath">
+       <property name="text">
+        <string>Viewer path:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="4" column="1">
+      <widget class="QLineEdit" name="viewerPathLineEdit"/>
+     </item>
+     <item row="5" column="0">
+      <widget class="QLabel" name="labelViewerArguments">
+       <property name="text">
+        <string>Viewer arguments:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="5" column="1">
+      <widget class="QLineEdit" name="viewerArgumentsLineEdit"/>
+     </item>
     </layout>
    </item>
    <item>
-- 
GitLab