From b557c58eacfaf6634be6187ee58fd42f48c30d90 Mon Sep 17 00:00:00 2001 From: Lasse Holmstedt <lasse.holmstedt@nokia.com> Date: Wed, 18 Aug 2010 13:54:12 +0200 Subject: [PATCH] Qml Debugger: Enable debugging qml+cpp standalone applications A new debugger engine, QmlCppEngine, is introduced, which wraps gdb and qml engines into one. Alternatively, if using Windows, Cdb is used instead of Gdb. Most of the debugger ui switcher is now rewritten, and it is tailored for the QML and CPP layout case, the only one supported anyway. Reviewed-by: hjk --- .../include/qdeclarativedesignview.h | 1 + .../qmljsdebugger/qdeclarativedesignview.cpp | 12 + .../qmljsdebugger/qdeclarativedesignview_p.h | 4 + src/plugins/debugger/debuggerconstants.h | 37 +- src/plugins/debugger/debuggerengine.cpp | 45 +- src/plugins/debugger/debuggerengine.h | 32 +- src/plugins/debugger/debuggermainwindow.cpp | 4 +- src/plugins/debugger/debuggermainwindow.h | 4 +- src/plugins/debugger/debuggerplugin.cpp | 227 +++--- src/plugins/debugger/debuggerplugin.h | 3 + src/plugins/debugger/debuggerrunner.cpp | 83 ++- src/plugins/debugger/debuggerrunner.h | 6 + src/plugins/debugger/debuggeruiswitcher.cpp | 538 ++++++++++----- src/plugins/debugger/debuggeruiswitcher.h | 82 ++- src/plugins/debugger/qml/qml.pri | 7 +- src/plugins/debugger/qml/qmladapter.cpp | 17 + src/plugins/debugger/qml/qmladapter.h | 2 + src/plugins/debugger/qml/qmlcppengine.cpp | 649 ++++++++++++++++++ src/plugins/debugger/qml/qmlcppengine.h | 124 ++++ src/plugins/debugger/qml/qmlengine.cpp | 97 ++- src/plugins/debugger/qml/qmlengine.h | 10 +- .../qmljsinspector/qmlinspectortoolbar.cpp | 33 +- .../qmljsinspector/qmlinspectortoolbar.h | 9 + src/plugins/qmljsinspector/qmljsinspector.cpp | 194 +++++- src/plugins/qmljsinspector/qmljsinspector.h | 20 +- src/plugins/qmljsinspector/qmljsinspector.pro | 6 +- .../qmljsinspector/qmljsinspectorconstants.h | 2 - .../qmljsinspector/qmljsinspectorplugin.cpp | 11 +- .../qmljsinspector/qmljsinspectorplugin.h | 4 +- .../qmljsinspector/qmljslivetextpreview.cpp | 26 +- .../qmljsinspector/qmljsobjecttree.cpp | 238 +++++++ src/plugins/qmljsinspector/qmljsobjecttree.h | 84 +++ .../qmlprojectruncontrol.cpp | 7 +- src/tools/qml/qmlobserver/main.cpp | 3 + src/tools/qml/qmlobserver/qmlruntime.cpp | 8 +- 35 files changed, 2212 insertions(+), 417 deletions(-) create mode 100644 src/plugins/debugger/qml/qmlcppengine.cpp create mode 100644 src/plugins/debugger/qml/qmlcppengine.h create mode 100644 src/plugins/qmljsinspector/qmljsobjecttree.cpp create mode 100644 src/plugins/qmljsinspector/qmljsobjecttree.h diff --git a/share/qtcreator/qmljsdebugger/include/qdeclarativedesignview.h b/share/qtcreator/qmljsdebugger/include/qdeclarativedesignview.h index 7b8241e242b..543aaada671 100644 --- a/share/qtcreator/qmljsdebugger/include/qdeclarativedesignview.h +++ b/share/qtcreator/qmljsdebugger/include/qdeclarativedesignview.h @@ -58,6 +58,7 @@ public: QToolBar *toolbar() const; static QString idStringForObject(QObject *obj); QRectF adjustToScreenBoundaries(const QRectF &boundingRectInSceneSpace); + void setDebugMode(bool isDebugMode); public Q_SLOTS: void setDesignModeBehavior(bool value); diff --git a/share/qtcreator/qmljsdebugger/qdeclarativedesignview.cpp b/share/qtcreator/qmljsdebugger/qdeclarativedesignview.cpp index de0d21f86bf..704711f9da8 100644 --- a/share/qtcreator/qmljsdebugger/qdeclarativedesignview.cpp +++ b/share/qtcreator/qmljsdebugger/qdeclarativedesignview.cpp @@ -37,6 +37,7 @@ #include "boundingrecthighlighter.h" #include "subcomponenteditortool.h" #include "qmltoolbar.h" +#include "jsdebuggeragent.h" #include <QDeclarativeItem> #include <QDeclarativeEngine> @@ -48,6 +49,7 @@ #include <QApplication> #include <QAbstractAnimation> +#include <private/qdeclarativeengine_p.h> #include <private/qabstractanimation_p.h> namespace QmlViewer { @@ -61,6 +63,7 @@ QDeclarativeDesignViewPrivate::QDeclarativeDesignViewPrivate(QDeclarativeDesignV designModeBehavior(false), executionPaused(false), slowdownFactor(1.0f), + jsDebuggerAgent(0), toolbar(0) { sceneChangedTimer.setInterval(SceneChangeUpdateInterval); @@ -116,6 +119,9 @@ QDeclarativeDesignView::QDeclarativeDesignView(QWidget *parent) : data->createToolbar(); data->_q_changeToSingleSelectTool(); + + // always start debug mode - that's what this design view is for. + setDebugMode(true); } QDeclarativeDesignView::~QDeclarativeDesignView() @@ -763,6 +769,12 @@ void QDeclarativeDesignViewPrivate::createToolbar() QObject::connect(q, SIGNAL(marqueeSelectToolActivated()), toolbar, SLOT(activateMarqueeSelectTool())); } +void QDeclarativeDesignView::setDebugMode(bool isDebugMode) +{ + if (isDebugMode && !data->jsDebuggerAgent) + data->jsDebuggerAgent = new JSDebuggerAgent(QDeclarativeEnginePrivate::getScriptEngine(engine())); +} + } //namespace QmlViewer #include <moc_qdeclarativedesignview.cpp> diff --git a/share/qtcreator/qmljsdebugger/qdeclarativedesignview_p.h b/share/qtcreator/qmljsdebugger/qdeclarativedesignview_p.h index 54df7cf36b6..d83f46aca9c 100644 --- a/share/qtcreator/qmljsdebugger/qdeclarativedesignview_p.h +++ b/share/qtcreator/qmljsdebugger/qdeclarativedesignview_p.h @@ -36,6 +36,8 @@ #include "qdeclarativedesignview.h" +QT_FORWARD_DECLARE_CLASS(JSDebuggerAgent) + namespace QmlViewer { class QDeclarativeDesignView; @@ -82,6 +84,8 @@ public: bool executionPaused; qreal slowdownFactor; + JSDebuggerAgent *jsDebuggerAgent; + QmlToolbar *toolbar; QTimer sceneChangedTimer; QSet<QGraphicsObject *> sceneGraphicsObjects; diff --git a/src/plugins/debugger/debuggerconstants.h b/src/plugins/debugger/debuggerconstants.h index 34d31bfc45d..c048bb47c8d 100644 --- a/src/plugins/debugger/debuggerconstants.h +++ b/src/plugins/debugger/debuggerconstants.h @@ -38,19 +38,19 @@ namespace Constants { // modes and their priorities const char * const MODE_DEBUG = "Debugger.Mode.Debug"; const int P_MODE_DEBUG = 85; -const char * const LANG_CPP = "C++"; + // common actions -const char * const STOP = "Debugger.Interrupt"; +const char * const STOP = "Debugger.Interrupt"; const char * const RESET = "Debugger.Reset"; const char * const STEP = "Debugger.StepLine"; const char * const STEPOUT = "Debugger.StepOut"; const char * const NEXT = "Debugger.NextLine"; const char * const REVERSE = "Debugger.ReverseDirection"; -const char * const M_DEBUG_LANGUAGES = "Debugger.Menu.View.Languages"; -const char * const M_DEBUG_VIEWS = "Debugger.Menu.View.Debug"; +const char * const M_DEBUG_DEBUGGING_LANGUAGES = "Debugger.Menu.View.DebugLanguages"; +const char * const M_DEBUG_VIEWS = "Debugger.Menu.View.Debug"; -const char * const C_DEBUGMODE = "Debugger.DebugMode"; +const char * const C_DEBUGMODE = "Debugger.DebugMode"; const char * const C_CPPDEBUGGER = "Gdb Debugger"; const char * const DEBUGGER_COMMON_SETTINGS_ID = "A.Common"; @@ -62,6 +62,22 @@ const char * const DEBUGGER_SETTINGS_TR_CATEGORY = const char * const DEBUGGER_COMMON_SETTINGS_CATEGORY_ICON = ":/core/images/category_debug.png"; +const int QML_DEFAULT_DEBUG_SERVER_PORT = 3768; +const char * const E_QML_DEBUG_SERVER_PORT = "QML_DEBUG_SERVER_PORT"; + +// dock widget names +const char * const DW_BREAK = "Debugger.Docks.Break"; +const char * const DW_MODULES = "Debugger.Docks.Modules"; +const char * const DW_REGISTER = "Debugger.Docks.Register"; +const char * const DW_OUTPUT = "Debugger.Docks.Output"; +const char * const DW_SNAPSHOTS = "Debugger.Docks.Snapshots"; +const char * const DW_STACK = "Debugger.Docks.Stack"; +const char * const DW_SOURCE_FILES = "Debugger.Docks.SourceFiles"; +const char * const DW_THREADS = "Debugger.Docks.Threads"; +const char * const DW_WATCHERS = "Debugger.Docks.LocalsAndWatchers"; + +const char * const DW_QML_INSPECTOR = "Debugger.Docks.QmlInspector"; + namespace Internal { enum { debug = 0 }; #ifdef Q_OS_MAC @@ -71,6 +87,7 @@ namespace Internal { #endif } // namespace Internal + } // namespace Constants @@ -267,13 +284,23 @@ enum DebuggerEngineType PdbEngineType = 0x08, TcfEngineType = 0x10, QmlEngineType = 0x20, + QmlCppEngineType = 0x40, AllEngineTypes = GdbEngineType | ScriptEngineType | CdbEngineType | PdbEngineType | TcfEngineType | QmlEngineType + | QmlCppEngineType +}; + +enum DebuggerLanguage +{ + Lang_None = 0x0, + Lang_Cpp = 0x1, + Lang_Qml = 0x2 }; +Q_DECLARE_FLAGS(DebuggerLanguages, DebuggerLanguage) } // namespace Debugger diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp index 1cfd45bf4c7..6c68b25990e 100644 --- a/src/plugins/debugger/debuggerengine.cpp +++ b/src/plugins/debugger/debuggerengine.cpp @@ -106,12 +106,14 @@ using namespace TextEditor; /////////////////////////////////////////////////////////////////////// DebuggerStartParameters::DebuggerStartParameters() - : attachPID(-1), - useTerminal(false), - breakAtMain(false), - toolChainType(ToolChain::UNKNOWN), - startMode(NoStartMode), - executableUid(0) + : attachPID(-1) + , useTerminal(false) + , breakAtMain(false) + , qmlServerAddress("127.0.0.1") + , qmlServerPort(0) + , toolChainType(ToolChain::UNKNOWN) + , executableUid(0) + , startMode(NoStartMode) {} void DebuggerStartParameters::clear() @@ -233,7 +235,8 @@ public: m_stackHandler(engine), m_threadsHandler(engine), m_watchHandler(engine), - m_disassemblerViewAgent(engine) + m_disassemblerViewAgent(engine), + m_runInWrapperEngine(false) {} ~DebuggerEnginePrivate() {} @@ -312,6 +315,8 @@ public: WatchHandler m_watchHandler; DisassemblerViewAgent m_disassemblerViewAgent; QFutureInterface<void> m_progress; + + bool m_runInWrapperEngine; }; void DebuggerEnginePrivate::breakpointSetRemoveMarginActionTriggered() @@ -1329,7 +1334,11 @@ void DebuggerEngine::notifyEngineShutdownOk() showMessage(_("NOTE: ENGINE SHUTDOWN OK")); QTC_ASSERT(state() == EngineShutdownRequested, qDebug() << state()); setState(EngineShutdownOk); - d->queueFinishDebugger(); + if (!d->m_runInWrapperEngine) { + d->queueFinishDebugger(); + } else { + setState(DebuggerFinished); + } } void DebuggerEngine::notifyEngineShutdownFailed() @@ -1337,7 +1346,11 @@ void DebuggerEngine::notifyEngineShutdownFailed() showMessage(_("NOTE: ENGINE SHUTDOWN FAILED")); QTC_ASSERT(state() == EngineShutdownRequested, qDebug() << state()); setState(EngineShutdownFailed); - d->queueFinishDebugger(); + if (!d->m_runInWrapperEngine) { + d->queueFinishDebugger(); + } else { + setState(DebuggerFinished); + } } void DebuggerEnginePrivate::doFinishDebugger() @@ -1395,7 +1408,7 @@ void DebuggerEngine::notifyInferiorExited() void DebuggerEngine::setState(DebuggerState state, bool forced) { //qDebug() << "STATUS CHANGE: FROM " << stateName(d->m_state) - // << " TO " << stateName(state); + // << " TO " << stateName(state); DebuggerState oldState = d->m_state; d->m_state = state; @@ -1412,6 +1425,13 @@ void DebuggerEngine::setState(DebuggerState state, bool forced) showMessage(msg, LogDebug); plugin()->updateState(this); + + emit stateChanged(d->m_state); +} + +void DebuggerEngine::setRunInWrapperEngine(bool value) +{ + d->m_runInWrapperEngine = value; } bool DebuggerEngine::debuggerActionsEnabled() const @@ -1528,6 +1548,11 @@ QMessageBox *DebuggerEngine::showMessageBox(int icon, const QString &title, return plugin()->showMessageBox(icon, title, text, buttons); } +DebuggerRunControl *DebuggerEngine::runControl() const +{ + return d->m_runControl; +} + } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/debuggerengine.h b/src/plugins/debugger/debuggerengine.h index a4c0a7c3e5a..378905b96fb 100644 --- a/src/plugins/debugger/debuggerengine.h +++ b/src/plugins/debugger/debuggerengine.h @@ -79,6 +79,7 @@ public: // for qml debugging QString qmlServerAddress; quint16 qmlServerPort; + DebuggerEngineType cppEngineType; // for cpp+qml debugging // for remote debugging QString remoteChannel; @@ -218,17 +219,17 @@ public: WatchHandler *watchHandler() const; SourceFilesHandler *sourceFilesHandler() const; - QAbstractItemModel *commandModel() const; - QAbstractItemModel *modulesModel() const; - QAbstractItemModel *breakModel() const; - QAbstractItemModel *registerModel() const; - QAbstractItemModel *stackModel() const; - QAbstractItemModel *threadsModel() const; - QAbstractItemModel *localsModel() const; - QAbstractItemModel *watchersModel() const; - QAbstractItemModel *returnModel() const; + virtual QAbstractItemModel *commandModel() const; + virtual QAbstractItemModel *modulesModel() const; + virtual QAbstractItemModel *breakModel() const; + virtual QAbstractItemModel *registerModel() const; + virtual QAbstractItemModel *stackModel() const; + virtual QAbstractItemModel *threadsModel() const; + virtual QAbstractItemModel *localsModel() const; + virtual QAbstractItemModel *watchersModel() const; + virtual QAbstractItemModel *returnModel() const; //QAbstractItemModel *snapshotModel() const; - QAbstractItemModel *sourceFilesModel() const; + virtual QAbstractItemModel *sourceFilesModel() const; void progressPing(); void handleFinished(); @@ -275,6 +276,9 @@ public: void gotoLocation(const StackFrame &frame, bool setMarker); virtual void quitDebugger(); // called by DebuggerRunControl +signals: + void stateChanged(const DebuggerState &state); + protected: // The base notify*() function implementation should be sufficient // in most cases, but engines are free to override them to do some @@ -318,8 +322,11 @@ protected: virtual void shutdownInferior() = 0; virtual void shutdownEngine() = 0; -private: void setState(DebuggerState state, bool forced = false); + void setRunInWrapperEngine(bool value); + +private: + DebuggerRunControl *runControl() const; private: void executeRunToLine(); @@ -327,6 +334,9 @@ private: void executeJumpToLine(); void addToWatchWindow(); + // wrapper engine needs access to state of its subengines + friend class QmlCppEngine; + friend class DebuggerEnginePrivate; DebuggerEnginePrivate *d; }; diff --git a/src/plugins/debugger/debuggermainwindow.cpp b/src/plugins/debugger/debuggermainwindow.cpp index ba4709813d1..913e7218bff 100644 --- a/src/plugins/debugger/debuggermainwindow.cpp +++ b/src/plugins/debugger/debuggermainwindow.cpp @@ -71,9 +71,7 @@ QMenu* DebuggerMainWindow::createPopupMenu() for (int i = 0; i < dockwidgets.size(); ++i) { QDockWidget *dockWidget = dockwidgets.at(i)->m_dockWidget; - if (dockWidget->parentWidget() == this && - dockwidgets.at(i)->m_languageId == m_uiSwitcher->activeLanguageId()) { - + if (dockWidget->parentWidget() == this) { menu->addAction(dockWidget->toggleViewAction()); } } diff --git a/src/plugins/debugger/debuggermainwindow.h b/src/plugins/debugger/debuggermainwindow.h index 536e6082313..de70ff01f54 100644 --- a/src/plugins/debugger/debuggermainwindow.h +++ b/src/plugins/debugger/debuggermainwindow.h @@ -31,6 +31,7 @@ #define DEBUGGERMAINWINDOW_H #include <utils/fancymainwindow.h> +#include "debuggerconstants.h" QT_FORWARD_DECLARE_CLASS(QMenu); @@ -42,10 +43,9 @@ namespace Internal { class DebugToolWindow { public: - DebugToolWindow() : m_dockWidget(0), m_languageId(-1), m_visible(false) {} + DebugToolWindow() : m_dockWidget(0), m_visible(false) {} QDockWidget *m_dockWidget; - int m_languageId; bool m_visible; }; diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index 1d381ee8fa3..33fc79bcc89 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -75,6 +75,7 @@ #include <coreplugin/findplaceholder.h> #include <coreplugin/icontext.h> #include <coreplugin/icore.h> +#include <coreplugin/imode.h> #include <coreplugin/icorelistener.h> #include <coreplugin/manhattanstyle.h> #include <coreplugin/messagemanager.h> @@ -791,18 +792,6 @@ static TextEditor::ITextEditor *currentTextEditor() return qobject_cast<ITextEditor*>(editor); } -static bool isCurrentProjectCppBased() -{ - Project *startupProject = ProjectExplorerPlugin::instance()->startupProject(); - if (!startupProject) - return false; - const QString id = startupProject->id(); - return id == _("GenericProjectManager.GenericProject") - || id == _("CMakeProjectManager.CMakeProject") - || id == _("Qt4ProjectManager.Qt4Project"); -} - - /////////////////////////////////////////////////////////////////////// // // DebuggerPluginPrivate @@ -868,7 +857,7 @@ public slots: { if (on) notifyCurrentEngine(RequestReloadRegistersRole); } void onAction(); - void setSimpleDockWidgetArrangement(const QString &activeLanguage); + void setSimpleDockWidgetArrangement(const Debugger::DebuggerLanguages &activeLanguages); void editorOpened(Core::IEditor *editor); void editorAboutToClose(Core::IEditor *editor); @@ -900,7 +889,7 @@ public slots: void exitDebugger(); void enableReverseDebuggingTriggered(const QVariant &value); - void languageChanged(const QString &debuggerLanguage); + void languagesChanged(const Debugger::DebuggerLanguages &languages); void showStatusMessage(const QString &msg, int timeout = -1); DebuggerMainWindow *mainWindow() @@ -915,7 +904,7 @@ public slots: DebuggerRunControl *createDebugger(const DebuggerStartParameters &sp, ProjectExplorer::RunConfiguration *rc = 0); void startDebugger(ProjectExplorer::RunControl *runControl); - void displayDebugger(ProjectExplorer::RunControl *runControl); + void displayDebugger(DebuggerEngine *engine, bool updateEngine = true); void dumpLog(); void cleanupViews(); @@ -1234,36 +1223,40 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments, QString *er m_uiSwitcher = new DebuggerUISwitcher(m_debugMode, this); ExtensionSystem::PluginManager::instance()->addObject(m_uiSwitcher); theDebuggerAction(SwitchLanguageAutomatically)->setChecked(true); - m_uiSwitcher->addLanguage(LANG_CPP, cppDebuggercontext); - m_uiSwitcher->setActiveLanguage(LANG_CPP); - + m_uiSwitcher->addLanguage(Lang_Cpp, tr("C++"), cppDebuggercontext); // Dock widgets - m_breakDock = m_uiSwitcher->createDockWidget(LANG_CPP, m_breakWindow); - - m_modulesDock = m_uiSwitcher->createDockWidget(LANG_CPP, m_modulesWindow, + m_breakDock = m_uiSwitcher->createDockWidget(Lang_Cpp, m_breakWindow); + m_breakDock->setObjectName(QString(DW_BREAK)); + m_modulesDock = m_uiSwitcher->createDockWidget(Lang_Cpp, m_modulesWindow, Qt::TopDockWidgetArea, false); + m_modulesDock->setObjectName(QString(DW_MODULES)); connect(m_modulesDock->toggleViewAction(), SIGNAL(toggled(bool)), SLOT(modulesDockToggled(bool)), Qt::QueuedConnection); - m_registerDock = m_uiSwitcher->createDockWidget(LANG_CPP, m_registerWindow, + m_registerDock = m_uiSwitcher->createDockWidget(Lang_Cpp, m_registerWindow, Qt::TopDockWidgetArea, false); + m_registerDock->setObjectName(QString(DW_REGISTER)); connect(m_registerDock->toggleViewAction(), SIGNAL(toggled(bool)), SLOT(registerDockToggled(bool)), Qt::QueuedConnection); - m_outputDock = m_uiSwitcher->createDockWidget(QString(), m_outputWindow, + m_outputDock = m_uiSwitcher->createDockWidget(Lang_None, m_outputWindow, Qt::TopDockWidgetArea, false); + m_outputDock->setObjectName(QString(DW_OUTPUT)); + m_snapshotDock = m_uiSwitcher->createDockWidget(Lang_Cpp, m_snapshotWindow); + m_snapshotDock->setObjectName(QString(DW_SNAPSHOTS)); - m_snapshotDock = m_uiSwitcher->createDockWidget(LANG_CPP, m_snapshotWindow); - - m_stackDock = m_uiSwitcher->createDockWidget(LANG_CPP, m_stackWindow); + m_stackDock = m_uiSwitcher->createDockWidget(Lang_Cpp, m_stackWindow); + m_stackDock->setObjectName(QString(DW_STACK)); - m_sourceFilesDock = m_uiSwitcher->createDockWidget(LANG_CPP, + m_sourceFilesDock = m_uiSwitcher->createDockWidget(Lang_Cpp, m_sourceFilesWindow, Qt::TopDockWidgetArea, false); + m_sourceFilesDock->setObjectName(QString(DW_SOURCE_FILES)); connect(m_sourceFilesDock->toggleViewAction(), SIGNAL(toggled(bool)), SLOT(sourceFilesDockToggled(bool)), Qt::QueuedConnection); - m_threadsDock = m_uiSwitcher->createDockWidget(LANG_CPP, m_threadsWindow); + m_threadsDock = m_uiSwitcher->createDockWidget(Lang_Cpp, m_threadsWindow); + m_threadsDock->setObjectName(QString(DW_THREADS)); QSplitter *localsAndWatchers = new Core::MiniSplitter(Qt::Vertical); localsAndWatchers->setObjectName(QLatin1String("CppDebugLocalsAndWatchers")); @@ -1275,10 +1268,12 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments, QString *er localsAndWatchers->setStretchFactor(1, 1); localsAndWatchers->setStretchFactor(2, 1); - m_watchDock = m_uiSwitcher->createDockWidget(LANG_CPP, localsAndWatchers); + m_watchDock = m_uiSwitcher->createDockWidget(Lang_Cpp, localsAndWatchers); + m_watchDock->setObjectName(QString(DW_WATCHERS)); + m_dockWidgets << m_breakDock << m_modulesDock << m_registerDock - << m_outputDock << m_stackDock << m_sourceFilesDock - << m_threadsDock << m_watchDock; + << m_outputDock << m_snapshotDock << m_stackDock + << m_sourceFilesDock << m_threadsDock << m_watchDock; // Do not fail the whole plugin if something goes wrong here. uint cmdLineEnabledEngines = AllEngineTypes; @@ -1369,7 +1364,7 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments, QString *er cmd = am->registerAction(m_detachAction, Constants::DETACH, globalcontext); cmd->setAttribute(Command::CA_Hide); - m_uiSwitcher->addMenuAction(cmd, CC::G_DEFAULT_ONE); + m_uiSwitcher->addMenuAction(cmd, Lang_None, CC::G_DEFAULT_ONE); cmd = am->registerAction(m_actions.stopAction, Constants::STOP, globalcontext); @@ -1377,7 +1372,7 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments, QString *er cmd->setAttribute(Command::CA_UpdateIcon); //cmd->setDefaultKeySequence(QKeySequence(Constants::STOP_KEY)); cmd->setDefaultText(tr("Stop Debugger")); - m_uiSwitcher->addMenuAction(cmd, CC::G_DEFAULT_ONE); + m_uiSwitcher->addMenuAction(cmd, Lang_None, CC::G_DEFAULT_ONE); cmd = am->registerAction(m_actions.interruptAction, PE::DEBUG, m_interruptibleContext); @@ -1391,77 +1386,77 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments, QString *er cmd->setAttribute(Core::Command::CA_UpdateText); //cmd->setDefaultKeySequence(QKeySequence(Constants::RESET_KEY)); cmd->setDefaultText(tr("Reset Debugger")); - m_uiSwitcher->addMenuAction(cmd, CC::G_DEFAULT_ONE); + m_uiSwitcher->addMenuAction(cmd, Lang_None, CC::G_DEFAULT_ONE); QAction *sep = new QAction(this); sep->setSeparator(true); cmd = am->registerAction(sep, _("Debugger.Sep.Step"), globalcontext); - m_uiSwitcher->addMenuAction(cmd, Constants::LANG_CPP); + m_uiSwitcher->addMenuAction(cmd, Lang_Cpp); cmd = am->registerAction(m_actions.nextAction, Constants::NEXT, cppDebuggercontext); cmd->setDefaultKeySequence(QKeySequence(Constants::NEXT_KEY)); cmd->setAttribute(Command::CA_Hide); - m_uiSwitcher->addMenuAction(cmd, Constants::LANG_CPP); + m_uiSwitcher->addMenuAction(cmd, Lang_Cpp); cmd = am->registerAction(m_actions.stepAction, Constants::STEP, cppDebuggercontext); cmd->setDefaultKeySequence(QKeySequence(Constants::STEP_KEY)); cmd->setAttribute(Command::CA_Hide); - m_uiSwitcher->addMenuAction(cmd, Constants::LANG_CPP); + m_uiSwitcher->addMenuAction(cmd, Lang_Cpp); cmd = am->registerAction(m_actions.stepOutAction, Constants::STEPOUT, cppDebuggercontext); cmd->setDefaultKeySequence(QKeySequence(Constants::STEPOUT_KEY)); cmd->setAttribute(Command::CA_Hide); - m_uiSwitcher->addMenuAction(cmd, Constants::LANG_CPP); + m_uiSwitcher->addMenuAction(cmd, Lang_Cpp); cmd = am->registerAction(m_actions.runToLineAction1, Constants::RUN_TO_LINE1, cppDebuggercontext); cmd->setDefaultKeySequence(QKeySequence(Constants::RUN_TO_LINE_KEY)); cmd->setAttribute(Command::CA_Hide); - m_uiSwitcher->addMenuAction(cmd, Constants::LANG_CPP); + m_uiSwitcher->addMenuAction(cmd, Lang_Cpp); cmd = am->registerAction(m_actions.runToFunctionAction, Constants::RUN_TO_FUNCTION, cppDebuggercontext); cmd->setDefaultKeySequence(QKeySequence(Constants::RUN_TO_FUNCTION_KEY)); cmd->setAttribute(Command::CA_Hide); - m_uiSwitcher->addMenuAction(cmd, Constants::LANG_CPP); + m_uiSwitcher->addMenuAction(cmd, Lang_Cpp); cmd = am->registerAction(m_actions.jumpToLineAction1, Constants::JUMP_TO_LINE1, cppDebuggercontext); cmd->setAttribute(Command::CA_Hide); - m_uiSwitcher->addMenuAction(cmd, Constants::LANG_CPP); + m_uiSwitcher->addMenuAction(cmd, Lang_Cpp); cmd = am->registerAction(m_actions.returnFromFunctionAction, Constants::RETURN_FROM_FUNCTION, cppDebuggercontext); cmd->setAttribute(Command::CA_Hide); - m_uiSwitcher->addMenuAction(cmd, Constants::LANG_CPP); + m_uiSwitcher->addMenuAction(cmd, Lang_Cpp); cmd = am->registerAction(m_actions.reverseDirectionAction, Constants::REVERSE, cppDebuggercontext); cmd->setDefaultKeySequence(QKeySequence(Constants::REVERSE_KEY)); cmd->setAttribute(Command::CA_Hide); - m_uiSwitcher->addMenuAction(cmd, Constants::LANG_CPP); + m_uiSwitcher->addMenuAction(cmd, Lang_Cpp); sep = new QAction(this); sep->setSeparator(true); cmd = am->registerAction(sep, _("Debugger.Sep.Break"), globalcontext); - m_uiSwitcher->addMenuAction(cmd, Constants::LANG_CPP); + m_uiSwitcher->addMenuAction(cmd, Lang_Cpp); cmd = am->registerAction(m_actions.snapshotAction, Constants::SNAPSHOT, cppDebuggercontext); cmd->setDefaultKeySequence(QKeySequence(Constants::SNAPSHOT_KEY)); cmd->setAttribute(Command::CA_Hide); - m_uiSwitcher->addMenuAction(cmd, Constants::LANG_CPP); + m_uiSwitcher->addMenuAction(cmd, Lang_Cpp); cmd = am->registerAction(m_actions.frameDownAction, Constants::FRAME_DOWN, cppDebuggercontext); @@ -1472,13 +1467,13 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments, QString *er cmd = am->registerAction(theDebuggerAction(OperateByInstruction), Constants::OPERATE_BY_INSTRUCTION, cppDebuggercontext); cmd->setAttribute(Command::CA_Hide); - m_uiSwitcher->addMenuAction(cmd, Constants::LANG_CPP); + m_uiSwitcher->addMenuAction(cmd, Lang_Cpp); cmd = am->registerAction(m_actions.breakAction, Constants::TOGGLE_BREAK, globalcontext); cmd->setDefaultKeySequence(QKeySequence(Constants::TOGGLE_BREAK_KEY)); - m_uiSwitcher->addMenuAction(cmd, Constants::LANG_CPP); + m_uiSwitcher->addMenuAction(cmd, Lang_Cpp); connect(m_actions.breakAction, SIGNAL(triggered()), this, SLOT(toggleBreakpoint())); @@ -1487,14 +1482,14 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments, QString *er sep = new QAction(this); sep->setSeparator(true); cmd = am->registerAction(sep, _("Debugger.Sep.Watch"), globalcontext); - m_uiSwitcher->addMenuAction(cmd, Constants::LANG_CPP); + m_uiSwitcher->addMenuAction(cmd, Lang_Cpp); cmd = am->registerAction(m_actions.watchAction1, Constants::ADD_TO_WATCH1, cppeditorcontext); cmd->action()->setEnabled(true); //cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+D,Ctrl+W"))); - m_uiSwitcher->addMenuAction(cmd, Constants::LANG_CPP); + m_uiSwitcher->addMenuAction(cmd, Lang_Cpp); // Editor context menu @@ -1541,7 +1536,7 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments, QString *er m_locationMark = 0; - //setSimpleDockWidgetArrangement(LANG_CPP); + //setSimpleDockWidgetArrangement(Lang_Cpp); connect(ModeManager::instance(), SIGNAL(currentModeChanged(Core::IMode*)), this, SLOT(onModeChanged(Core::IMode*))); @@ -1606,16 +1601,16 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments, QString *er hbox->addSpacerItem(new QSpacerItem(4, 0)); hbox->addWidget(m_statusLabel, 10); - m_uiSwitcher->setToolbar(LANG_CPP, toolbarContainer); - connect(m_uiSwitcher, SIGNAL(dockArranged(QString)), - this, SLOT(setSimpleDockWidgetArrangement(QString))); + m_uiSwitcher->setToolbar(Lang_Cpp, toolbarContainer); + connect(m_uiSwitcher, SIGNAL(dockResetRequested(Debugger::DebuggerLanguages)), + this, SLOT(setSimpleDockWidgetArrangement(Debugger::DebuggerLanguages))); connect(theDebuggerAction(EnableReverseDebugging), SIGNAL(valueChanged(QVariant)), this, SLOT(enableReverseDebuggingTriggered(QVariant))); // UI Switcher - connect(m_uiSwitcher, SIGNAL(languageChanged(QString)), - this, SLOT(languageChanged(QString))); + connect(m_uiSwitcher, SIGNAL(activeLanguagesChanged(Debugger::DebuggerLanguages)), + this, SLOT(languagesChanged(Debugger::DebuggerLanguages))); setInitialState(); connectEngine(m_sessionEngine, false); @@ -1662,9 +1657,9 @@ void DebuggerPluginPrivate::onAction() notifyCurrentEngine(role); } -void DebuggerPluginPrivate::languageChanged(const QString &language) +void DebuggerPluginPrivate::languagesChanged(const Debugger::DebuggerLanguages &languages) { - const bool debuggerIsCPP = (language == Constants::LANG_CPP); + const bool debuggerIsCPP = (languages & Lang_Cpp); //qDebug() << "DEBUGGER IS CPP: " << debuggerIsCPP; m_startExternalAction->setVisible(debuggerIsCPP); @@ -1970,14 +1965,14 @@ DebuggerPluginPrivate::createDebugger(const DebuggerStartParameters &sp, return m_debuggerRunControlFactory->create(sp, rc); } -void DebuggerPluginPrivate::displayDebugger(ProjectExplorer::RunControl *rc) +void DebuggerPluginPrivate::displayDebugger(DebuggerEngine *engine, bool updateEngine) { - DebuggerRunControl *runControl = qobject_cast<DebuggerRunControl *>(rc); - QTC_ASSERT(runControl, return); + QTC_ASSERT(engine, return); disconnectEngine(); - connectEngine(runControl->engine()); - runControl->engine()->updateAll(); - updateState(runControl->engine()); + connectEngine(engine); + if (updateEngine) + engine->updateAll(); + updateState(engine); } void DebuggerPluginPrivate::startDebugger(ProjectExplorer::RunControl *rc) @@ -2079,48 +2074,58 @@ void DebuggerPluginPrivate::setBusyCursor(bool busy) m_snapshotWindow->setCursor(cursor); } -void DebuggerPluginPrivate::setSimpleDockWidgetArrangement(const QString &activeLanguage) +void DebuggerPluginPrivate::setSimpleDockWidgetArrangement(const Debugger::DebuggerLanguages &activeLanguages) { + Debugger::DebuggerUISwitcher *uiSwitcher = DebuggerUISwitcher::instance(); DebuggerMainWindow *mw = mainWindow(); - if (activeLanguage == LANG_CPP || activeLanguage.isEmpty()) { - //qDebug() << "SETTING SIMPLE CPP ARRANGEMENT" << activeLanguage; - mw->setTrackingEnabled(false); - QList<QDockWidget *> dockWidgets = mw->dockWidgets(); - foreach (QDockWidget *dockWidget, dockWidgets) { - if (m_dockWidgets.contains(dockWidget)) { - dockWidget->setFloating(false); - mw->removeDockWidget(dockWidget); - } - } + mw->setTrackingEnabled(false); - foreach (QDockWidget *dockWidget, dockWidgets) { - if (m_dockWidgets.contains(dockWidget)) { - if (dockWidget == m_outputDock) - mw->addDockWidget(Qt::TopDockWidgetArea, dockWidget); - else - mw->addDockWidget(Qt::BottomDockWidgetArea, dockWidget); - dockWidget->show(); - } + QList<QDockWidget *> dockWidgets = mw->dockWidgets(); + foreach (QDockWidget *dockWidget, dockWidgets) { + dockWidget->setFloating(false); + mw->removeDockWidget(dockWidget); + } + + foreach (QDockWidget *dockWidget, dockWidgets) { + if (dockWidget == m_outputDock) { + mw->addDockWidget(Qt::TopDockWidgetArea, dockWidget); + } else { + mw->addDockWidget(Qt::BottomDockWidgetArea, dockWidget); } + dockWidget->hide(); + } - mw->splitDockWidget(mw->toolBarDockWidget(), m_stackDock, Qt::Vertical); - mw->splitDockWidget(m_stackDock, m_watchDock, Qt::Horizontal); - mw->tabifyDockWidget(m_watchDock, m_breakDock); - mw->tabifyDockWidget(m_watchDock, m_modulesDock); - mw->tabifyDockWidget(m_watchDock, m_registerDock); - mw->tabifyDockWidget(m_watchDock, m_threadsDock); - mw->tabifyDockWidget(m_watchDock, m_sourceFilesDock); - mw->tabifyDockWidget(m_watchDock, m_snapshotDock); - // They following views are rarely used in ordinary debugging. Hiding them - // saves cycles since the corresponding information won't be retrieved. - m_sourceFilesDock->hide(); - m_registerDock->hide(); - m_modulesDock->hide(); - m_outputDock->hide(); - mw->setTrackingEnabled(true); + if ((activeLanguages.testFlag(Lang_Cpp) && !activeLanguages.testFlag(Lang_Qml)) + || activeLanguages == Lang_None + || !uiSwitcher->qmlInspectorWindow()) + { + m_stackDock->show(); + m_breakDock->show(); + m_watchDock->show(); + m_threadsDock->show(); + m_snapshotDock->show(); } else { - //qDebug() << "SETTING SIMPLE QML ARRANGEMENT" << activeLanguage; + m_stackDock->show(); + m_breakDock->show(); + m_watchDock->show(); + m_threadsDock->show(); + m_snapshotDock->show(); + uiSwitcher->qmlInspectorWindow()->show(); } + + mw->splitDockWidget(mw->toolBarDockWidget(), m_stackDock, Qt::Vertical); + mw->splitDockWidget(m_stackDock, m_watchDock, Qt::Horizontal); + mw->tabifyDockWidget(m_watchDock, m_breakDock); + mw->tabifyDockWidget(m_watchDock, m_modulesDock); + mw->tabifyDockWidget(m_watchDock, m_registerDock); + mw->tabifyDockWidget(m_watchDock, m_threadsDock); + mw->tabifyDockWidget(m_watchDock, m_sourceFilesDock); + mw->tabifyDockWidget(m_watchDock, m_snapshotDock); + mw->tabifyDockWidget(m_watchDock, m_snapshotDock); + if (uiSwitcher->qmlInspectorWindow()) + mw->tabifyDockWidget(m_watchDock, uiSwitcher->qmlInspectorWindow()); + + mw->setTrackingEnabled(true); } void DebuggerPluginPrivate::setInitialState() @@ -2324,12 +2329,8 @@ void DebuggerPluginPrivate::onModeChanged(IMode *mode) return; EditorManager *editorManager = EditorManager::instance(); - if (editorManager->currentEditor()) { + if (editorManager->currentEditor()) editorManager->currentEditor()->widget()->setFocus(); - - if (isCurrentProjectCppBased()) - m_uiSwitcher->setActiveLanguage(LANG_CPP); - } } void DebuggerPluginPrivate::showSettingsDialog() @@ -2373,7 +2374,8 @@ void DebuggerPluginPrivate::activatePreviousMode() void DebuggerPluginPrivate::activateDebugMode() { - //qDebug() << "ACTIVATING DEBUG MODE"; + // ### FIXME m_capabilities is not updated yet when this method is called + // from startDebugger() const bool canReverse = (m_capabilities & ReverseSteppingCapability) && theDebuggerBoolSetting(EnableReverseDebugging); m_actions.reverseDirectionAction->setChecked(false); @@ -2680,7 +2682,15 @@ void DebuggerPlugin::startDebugger(ProjectExplorer::RunControl *runControl) void DebuggerPlugin::displayDebugger(ProjectExplorer::RunControl *runControl) { - instance()->d->displayDebugger(runControl); + DebuggerRunControl *rc = qobject_cast<DebuggerRunControl *>(runControl); + QTC_ASSERT(rc, return); + instance()->d->displayDebugger(rc->engine()); +} + +// if updateEngine is set, the engine will update its threads/modules and so forth. +void DebuggerPlugin::displayDebugger(DebuggerEngine *engine, bool updateEngine) +{ + instance()->d->displayDebugger(engine, updateEngine); } void DebuggerPlugin::updateState(DebuggerEngine *engine) @@ -2701,7 +2711,7 @@ void DebuggerPlugin::activateDebugMode() void DebuggerPlugin::createNewDock(QWidget *widget) { QDockWidget *dockWidget = - DebuggerUISwitcher::instance()->createDockWidget(LANG_CPP, widget); + DebuggerUISwitcher::instance()->createDockWidget(Lang_Cpp, widget); dockWidget->setWindowTitle(widget->windowTitle()); dockWidget->setObjectName(widget->windowTitle()); dockWidget->setFeatures(QDockWidget::DockWidgetClosable); @@ -2723,6 +2733,11 @@ void DebuggerPlugin::runControlFinished(DebuggerRunControl *runControl) d->disconnectEngine(); } +DebuggerLanguages DebuggerPlugin::activeLanguages() const +{ + return DebuggerUISwitcher::instance()->activeDebugLanguages(); +} + DebuggerEngine *DebuggerPlugin::sessionTemplate() { return d->m_sessionEngine; diff --git a/src/plugins/debugger/debuggerplugin.h b/src/plugins/debugger/debuggerplugin.h index 181617bf338..b1bbc0e3289 100644 --- a/src/plugins/debugger/debuggerplugin.h +++ b/src/plugins/debugger/debuggerplugin.h @@ -31,6 +31,7 @@ #define DEBUGGERPLUGIN_H #include "debugger_global.h" +#include "debuggerconstants.h" #include <extensionsystem/iplugin.h> @@ -73,6 +74,7 @@ public: ProjectExplorer::RunConfiguration *rc = 0); static void startDebugger(ProjectExplorer::RunControl *runControl); static void displayDebugger(ProjectExplorer::RunControl *runControl); + static void displayDebugger(Internal::DebuggerEngine *engine, bool updateEngine = true); QVariant sessionValue(const QString &name); void setSessionValue(const QString &name, const QVariant &value); @@ -110,6 +112,7 @@ private: void createNewDock(QWidget *widget); void runControlStarted(DebuggerRunControl *runControl); void runControlFinished(DebuggerRunControl *runControl); + DebuggerLanguages activeLanguages() const; // This contains per-session data like breakpoints and watched // expression. It serves as a template for new engine instantiations. diff --git a/src/plugins/debugger/debuggerrunner.cpp b/src/plugins/debugger/debuggerrunner.cpp index 7bd3107bfde..5c658d7f1a1 100644 --- a/src/plugins/debugger/debuggerrunner.cpp +++ b/src/plugins/debugger/debuggerrunner.cpp @@ -43,6 +43,7 @@ #include <projectexplorer/debugginghelper.h> #include <projectexplorer/environment.h> #include <projectexplorer/project.h> +#include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/target.h> #include <projectexplorer/buildconfiguration.h> @@ -74,6 +75,7 @@ DebuggerEngine *createScriptEngine(const DebuggerStartParameters &); DebuggerEngine *createPdbEngine(const DebuggerStartParameters &); DebuggerEngine *createTcfEngine(const DebuggerStartParameters &); DebuggerEngine *createQmlEngine(const DebuggerStartParameters &); +DebuggerEngine *createQmlCppEngine(const DebuggerStartParameters &); bool checkGdbConfiguration(int toolChain, QString *errorMsg, QString *settingsPage); @@ -148,6 +150,17 @@ static DebuggerStartParameters localStartParameters(RunConfiguration *runConfigu sp.useTerminal = rc->runMode() == LocalApplicationRunConfiguration::Console; sp.dumperLibrary = rc->dumperLibrary(); sp.dumperLibraryLocations = rc->dumperLibraryLocations(); + + if (DebuggerRunControl::isQmlProject(runConfiguration)) { + sp.qmlServerAddress = QLatin1String("127.0.0.1"); + sp.qmlServerPort = rc->environment().value("QML_DEBUG_SERVER_PORT").toUInt(); + if (sp.qmlServerPort == 0) + sp.qmlServerPort = Constants::QML_DEFAULT_DEBUG_SERVER_PORT; + + sp.environment << QString(Constants::E_QML_DEBUG_SERVER_PORT) + + QLatin1Char('=') + QString::number(sp.qmlServerPort); + } + // FIXME: If it's not yet build this will be empty and not filled // when rebuild as the runConfiguration is not stored and therefore // cannot be used to retrieve the dumper location. @@ -208,14 +221,15 @@ QWidget *DebuggerRunControlFactory::createConfigurationWidget //////////////////////////////////////////////////////////////////////// DebuggerRunControl::DebuggerRunControl(RunConfiguration *runConfiguration, - DebuggerEngineType enabledEngines, const DebuggerStartParameters &sp) : - RunControl(runConfiguration, ProjectExplorer::Constants::DEBUGMODE), - m_engine(0), - m_myRunConfiguration(runConfiguration), - m_running(false), - m_enabledEngines(enabledEngines) + DebuggerEngineType enabledEngines, const DebuggerStartParameters &sp) + : RunControl(runConfiguration, ProjectExplorer::Constants::DEBUGMODE) + , m_myRunConfiguration(runConfiguration) + , m_running(false) + , m_started(false) + { connect(this, SIGNAL(finished()), this, SLOT(handleFinished())); + m_isQmlProject = isQmlProject(runConfiguration); createEngine(sp); } @@ -332,10 +346,13 @@ DebuggerEngineType DebuggerRunControl::engineForMode(DebuggerStartMode startMode #endif } -void DebuggerRunControl::createEngine(const DebuggerStartParameters &sp) +void DebuggerRunControl::createEngine(const DebuggerStartParameters &startParams) { + DebuggerStartParameters sp = startParams; + // Figure out engine according to toolchain, executable, attach or default. DebuggerEngineType engineType = NoEngineType; + DebuggerLanguages activeLangs = DebuggerPlugin::instance()->activeLanguages(); bool isQmlExecutable = sp.executable.endsWith(_("qmlviewer")) || sp.executable.endsWith(_("qmlobserver")); #ifdef Q_OS_MAC isQmlExecutable = sp.executable.endsWith(_("QMLViewer.app")) || sp.executable.endsWith(_("QMLObserver.app")); @@ -361,6 +378,15 @@ void DebuggerRunControl::createEngine(const DebuggerStartParameters &sp) if (!engineType) engineType = engineForMode(sp.startMode); + if (engineType != QmlEngineType && m_isQmlProject && (activeLangs & Lang_Qml)) { + if (activeLangs & Lang_Cpp) { + sp.cppEngineType = engineType; + engineType = QmlCppEngineType; + } else { + engineType = QmlEngineType; + } + } + // qDebug() << "USING ENGINE : " << engineType; switch (engineType) { @@ -382,6 +408,9 @@ void DebuggerRunControl::createEngine(const DebuggerStartParameters &sp) case QmlEngineType: m_engine = createQmlEngine(sp); break; + case QmlCppEngineType: + m_engine = createQmlCppEngine(sp); + break; default: { // Could not find anything suitable. debuggingFinished(); @@ -553,5 +582,45 @@ Internal::DebuggerEngine *DebuggerRunControl::engine() return m_engine; } +bool DebuggerRunControl::isQmlProject(RunConfiguration *config) +{ + if (!config || !config->target() || !config->target()->project()) + return false; + + QStringList projectFiles = config->target()->project()->files(ProjectExplorer::Project::ExcludeGeneratedFiles); + foreach(const QString &filename, projectFiles) { + if (filename.endsWith(".qml")) + return true; + } + + return false; +} + +bool DebuggerRunControl::isCurrentProjectQmlCppBased() +{ + Project *startupProject = ProjectExplorerPlugin::instance()->startupProject(); + if (!startupProject) + return false; + + if (!startupProject->activeTarget()) + return false; + + RunConfiguration *rc = startupProject->activeTarget()->activeRunConfiguration(); + + return isQmlProject(rc); +} + +bool DebuggerRunControl::isCurrentProjectCppBased() +{ + Project *startupProject = ProjectExplorerPlugin::instance()->startupProject(); + if (!startupProject) + return false; + const QString id = startupProject->id(); + return id == _("GenericProjectManager.GenericProject") + || id == _("CMakeProjectManager.CMakeProject") + || id == _("Qt4ProjectManager.Qt4Project"); +} + + } // namespace Debugger diff --git a/src/plugins/debugger/debuggerrunner.h b/src/plugins/debugger/debuggerrunner.h index 12a3d4d362b..0bd841aabdc 100644 --- a/src/plugins/debugger/debuggerrunner.h +++ b/src/plugins/debugger/debuggerrunner.h @@ -117,6 +117,10 @@ public: QString *settingsCategory = 0, QString *settingsPage = 0); + static bool isQmlProject(RunConfiguration *config); + static bool isCurrentProjectQmlCppBased(); + static bool isCurrentProjectCppBased(); + private slots: void handleFinished(); @@ -130,6 +134,8 @@ private: Internal::DebuggerEngine *m_engine; const QWeakPointer<RunConfiguration> m_myRunConfiguration; bool m_running; + bool m_started; + bool m_isQmlProject; DebuggerEngineType m_enabledEngines; QString m_errorMessage; QString m_settingsIdHint; diff --git a/src/plugins/debugger/debuggeruiswitcher.cpp b/src/plugins/debugger/debuggeruiswitcher.cpp index 3304f97fe7e..a6f7f7c4a26 100644 --- a/src/plugins/debugger/debuggeruiswitcher.cpp +++ b/src/plugins/debugger/debuggeruiswitcher.cpp @@ -31,6 +31,7 @@ #include "debuggermainwindow.h" #include "debuggeractions.h" #include "debuggerconstants.h" +#include "debuggerrunner.h" #include "savedaction.h" #include <utils/savedaction.h> @@ -52,13 +53,18 @@ #include <coreplugin/rightpane.h> #include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/projectexplorer.h> +#include <projectexplorer/session.h> +#include <projectexplorer/project.h> +#include <projectexplorer/target.h> +#include <projectexplorer/runconfiguration.h> #include <QtGui/QActionGroup> #include <QtGui/QStackedWidget> #include <QtGui/QVBoxLayout> #include <QtGui/QMenu> #include <QtGui/QDockWidget> - +#include <QtGui/QResizeEvent> #include <QtCore/QDebug> #include <QtCore/QList> #include <QtCore/QMap> @@ -68,8 +74,25 @@ namespace Debugger { using namespace Debugger::Internal; + + +DockWidgetEventFilter::DockWidgetEventFilter(QObject *parent) + : QObject(parent) +{ + +} + +bool DockWidgetEventFilter::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::Resize || event->type() == QEvent::ZOrderChange) { + emit widgetResized(); + } + + return QObject::eventFilter(obj, event); +} + // first: language id, second: menu item -typedef QPair<int, QAction *> ViewsMenuItems; +typedef QPair<DebuggerLanguage, QAction *> ViewsMenuItems; struct DebuggerUISwitcherPrivate { @@ -78,38 +101,63 @@ struct DebuggerUISwitcherPrivate QList<ViewsMenuItems> m_viewsMenuItems; QList<Internal::DebugToolWindow *> m_dockWidgets; - QMap<QString, QWidget *> m_toolBars; - QStringList m_languages; + QHash<QString, QVariant> m_dockWidgetActiveStateCpp; + QHash<QString, QVariant> m_dockWidgetActiveStateQmlCpp; + Internal::DockWidgetEventFilter *m_resizeEventFilter; + + QMap<DebuggerLanguage, QWidget *> m_toolBars; + + DebuggerLanguages m_supportedLanguages; + int m_languageCount; QStackedWidget *m_toolbarStack; Internal::DebuggerMainWindow *m_mainWindow; - QHash<int, Core::Context> m_contextsForLanguage; + QHash<DebuggerLanguage, Core::Context> m_contextsForLanguage; QActionGroup *m_languageActionGroup; - - int m_activeLanguage; - bool m_isActiveMode; + bool m_inDebugMode; bool m_changingUI; - Core::ActionContainer *m_languageMenu; + Core::ActionContainer *m_debuggerLanguageMenu; + DebuggerLanguages m_previousDebugLanguages; + DebuggerLanguages m_activeDebugLanguages; + QAction *m_activateCppAction; + QAction *m_activateQmlAction; + bool m_qmlEnabled; + Core::ActionContainer *m_viewsMenu; Core::ActionContainer *m_debugMenu; - QMultiHash<int, Core::Command *> m_menuCommands; + QMultiHash<DebuggerLanguage, Core::Command *> m_menuCommands; + + QWeakPointer<ProjectExplorer::Project> m_previousProject; + QWeakPointer<ProjectExplorer::Target> m_previousTarget; + + bool m_initialized; static DebuggerUISwitcher *m_instance; }; -DebuggerUISwitcherPrivate::DebuggerUISwitcherPrivate(DebuggerUISwitcher *q) : - m_toolbarStack(new QStackedWidget), - m_languageActionGroup(new QActionGroup(q)), - m_activeLanguage(-1), - m_isActiveMode(false), - m_changingUI(false), - m_viewsMenu(0), - m_debugMenu(0) +DebuggerUISwitcherPrivate::DebuggerUISwitcherPrivate(DebuggerUISwitcher *q) + : m_resizeEventFilter(new Internal::DockWidgetEventFilter(q)) + , m_supportedLanguages(Lang_None) + , m_languageCount(0) + , m_toolbarStack(new QStackedWidget) + , m_languageActionGroup(new QActionGroup(q)) + , m_inDebugMode(false) + , m_changingUI(false) + , m_debuggerLanguageMenu(0) + , m_previousDebugLanguages(Lang_None) + , m_activeDebugLanguages(Lang_None) + , m_activateCppAction(0) + , m_activateQmlAction(0) + , m_qmlEnabled(false) + , m_viewsMenu(0) + , m_debugMenu(0) + , m_initialized(false) { + m_languageActionGroup->setExclusive(false); } DebuggerUISwitcher *DebuggerUISwitcherPrivate::m_instance = 0; @@ -122,16 +170,19 @@ DebuggerUISwitcher::DebuggerUISwitcher(Core::BaseMode *mode, QObject* parent) : Core::ICore *core = Core::ICore::instance(); Core::ActionManager *am = core->actionManager(); + ProjectExplorer::ProjectExplorerPlugin *pe = ProjectExplorer::ProjectExplorerPlugin::instance(); + connect(pe->session(), SIGNAL(startupProjectChanged(ProjectExplorer::Project*)), + SLOT(updateUiForProject(ProjectExplorer::Project*))); connect(Core::ModeManager::instance(), SIGNAL(currentModeChanged(Core::IMode*)), SLOT(modeChanged(Core::IMode*))); + connect(d->m_resizeEventFilter, SIGNAL(widgetResized()), SLOT(updateDockWidgetSettings())); d->m_debugMenu = am->actionContainer(ProjectExplorer::Constants::M_DEBUG); d->m_viewsMenu = am->actionContainer(QLatin1String(Core::Constants::M_WINDOW_VIEWS)); QTC_ASSERT(d->m_viewsMenu, return) - d->m_languageMenu = am->createMenu(Debugger::Constants::M_DEBUG_LANGUAGES); - d->m_languageActionGroup->setExclusive(true); + d->m_debuggerLanguageMenu = am->createMenu(Debugger::Constants::M_DEBUG_DEBUGGING_LANGUAGES); DebuggerUISwitcherPrivate::m_instance = this; } @@ -144,63 +195,135 @@ DebuggerUISwitcher::~DebuggerUISwitcher() delete d; } -QStringList DebuggerUISwitcher::supportedLanguages() const +void DebuggerUISwitcher::updateUiOnFileListChange() { - return d->m_languages; + if (d->m_previousProject) { + updateUiForTarget(d->m_previousProject.data()->activeTarget()); + } } -void DebuggerUISwitcher::addMenuAction(Core::Command *command, const QString &langName, - const QString &group) +void DebuggerUISwitcher::updateUiForProject(ProjectExplorer::Project *project) { - d->m_debugMenu->addAction(command, group); - d->m_menuCommands.insert(d->m_languages.indexOf(langName), command); + if (project) { + if (d->m_previousProject) { + disconnect(d->m_previousProject.data(), SIGNAL(activeTargetChanged(ProjectExplorer::Target*)), + this, SLOT(updateUiForTarget(ProjectExplorer::Target*))); + } + d->m_previousProject = project; + connect(project, SIGNAL(fileListChanged()), SLOT(updateUiOnFileListChange())); + connect(project, SIGNAL(activeTargetChanged(ProjectExplorer::Target*)), + this, SLOT(updateUiForTarget(ProjectExplorer::Target*))); + updateUiForTarget(project->activeTarget()); + } } -void DebuggerUISwitcher::setActiveLanguage(const QString &langName) +void DebuggerUISwitcher::updateUiForTarget(ProjectExplorer::Target *target) { - //qDebug() << "SET ACTIVE LANGUAGE: " << langName - // << theDebuggerAction(SwitchLanguageAutomatically)->isChecked() - // << d->m_languages; - if (theDebuggerAction(SwitchLanguageAutomatically)->isChecked() - && d->m_languages.contains(langName)) - { - changeDebuggerUI(langName); + if (target) { + if (d->m_previousTarget) { + disconnect(target, SIGNAL(activeRunConfigurationChanged(ProjectExplorer::RunConfiguration*)), + this, SLOT(updateUiForRunConfiguration(ProjectExplorer::RunConfiguration*))); + } + d->m_previousTarget = target; + connect(target, SIGNAL(activeRunConfigurationChanged(ProjectExplorer::RunConfiguration*)), + this, SLOT(updateUiForRunConfiguration(ProjectExplorer::RunConfiguration*))); + updateUiForRunConfiguration(target->activeRunConfiguration()); } } -int DebuggerUISwitcher::activeLanguageId() const +// updates default debug language settings per run config. +void DebuggerUISwitcher::updateUiForRunConfiguration(ProjectExplorer::RunConfiguration *rc) { - return d->m_activeLanguage; + if (rc) { + d->m_languageActionGroup->setDisabled(false); + if (DebuggerRunControl::isQmlProject(rc) && d->m_qmlEnabled) { + d->m_activateCppAction->setChecked(true); + d->m_activateQmlAction->setChecked(true); + } else { + if (d->m_activateQmlAction) { + d->m_activateQmlAction->setChecked(false); + } + } + } else { + if (d->m_activateCppAction) + d->m_activateCppAction->setChecked(true); + if (d->m_activateQmlAction) + d->m_activateQmlAction->setChecked(false); + d->m_languageActionGroup->setDisabled(true); + } + + updateActiveLanguages(); +} + +void DebuggerUISwitcher::updateActiveLanguages() +{ + d->m_activeDebugLanguages = Lang_None; + + if (d->m_activateCppAction->isChecked()) + d->m_activeDebugLanguages = Lang_Cpp; + + if (d->m_qmlEnabled && d->m_activateQmlAction->isChecked()) + d->m_activeDebugLanguages = d->m_activeDebugLanguages | Lang_Qml; + + if (d->m_activeDebugLanguages == Lang_None) { + d->m_activateCppAction->setChecked(true); + d->m_activeDebugLanguages = Lang_Cpp; + } + + emit activeLanguagesChanged(d->m_activeDebugLanguages); + + updateUi(); +} + +DebuggerLanguages DebuggerUISwitcher::supportedLanguages() const +{ + return d->m_supportedLanguages; +} + +void DebuggerUISwitcher::addMenuAction(Core::Command *command, const DebuggerLanguage &language, + const QString &group) +{ + d->m_debugMenu->addAction(command, group); + d->m_menuCommands.insert(language, command); +} + +DebuggerLanguages DebuggerUISwitcher::activeDebugLanguages() const +{ + return d->m_activeDebugLanguages; } void DebuggerUISwitcher::modeChanged(Core::IMode *mode) { - d->m_isActiveMode = (mode->id() == Debugger::Constants::MODE_DEBUG); - d->m_mainWindow->setDockActionsVisible(d->m_isActiveMode); + d->m_inDebugMode = (mode->id() == Constants::MODE_DEBUG); + d->m_mainWindow->setDockActionsVisible(d->m_inDebugMode); hideInactiveWidgets(); + + if (mode->id() != Constants::MODE_DEBUG) + return; + + Core::EditorManager *editorManager = Core::EditorManager::instance(); + if (editorManager->currentEditor()) { + DebuggerLanguages activeLangs; + if (DebuggerRunControl::isCurrentProjectCppBased()) + activeLangs |= Lang_Cpp; + + if (DebuggerRunControl::isCurrentProjectQmlCppBased()) + activeLangs |= Lang_Qml; + + d->m_activateCppAction->setChecked(activeLangs & Lang_Cpp); + d->m_activateQmlAction->setChecked(activeLangs & Lang_Qml); + updateActiveLanguages(); + } } void DebuggerUISwitcher::hideInactiveWidgets() { - // Hide/Show dock widgets manually in case they are floating. - if (!d->m_isActiveMode) { + // Hide dock widgets manually in case they are floating. + if (!d->m_inDebugMode) { // hide all the debugger windows if mode is different foreach(Internal::DebugToolWindow *window, d->m_dockWidgets) { - if (window->m_languageId == d->m_activeLanguage && - window->m_dockWidget->isVisible()) - { + if (window->m_dockWidget->isFloating()) window->m_dockWidget->hide(); - } - } - } else { - // bring them back - foreach(Internal::DebugToolWindow *window, d->m_dockWidgets) { - if (window->m_languageId == d->m_activeLanguage && - window->m_visible && - !window->m_dockWidget->isVisible()) - { - window->m_dockWidget->show(); - } } } } @@ -211,9 +334,8 @@ void DebuggerUISwitcher::createViewsMenuItems() Core::ActionManager *am = core->actionManager(); Core::Context globalcontext(Core::Constants::C_GLOBAL); - QMenu *mLang = d->m_languageMenu->menu(); - mLang->setTitle(tr("&Languages")); - d->m_debugMenu->addMenu(d->m_languageMenu, Core::Constants::G_DEFAULT_THREE); + d->m_debugMenu->addMenu(d->m_debuggerLanguageMenu, Core::Constants::G_DEFAULT_THREE); + d->m_debuggerLanguageMenu->menu()->setTitle(tr("&Debug Languages")); // Add menu items Core::Command *cmd = am->registerAction(d->m_mainWindow->menuSeparator1(), @@ -235,93 +357,110 @@ DebuggerUISwitcher *DebuggerUISwitcher::instance() return DebuggerUISwitcherPrivate::m_instance; } -void DebuggerUISwitcher::addLanguage(const QString &langName, const Core::Context &context) +void DebuggerUISwitcher::addLanguage(const DebuggerLanguage &languageId, + const QString &languageName, const Core::Context &context) { - //qDebug() << "ADD UI LANGUAGE: " << langName; - d->m_toolBars.insert(langName, 0); - d->m_contextsForLanguage.insert(d->m_languages.count(), context); - d->m_languages.append(langName); + bool activate = (d->m_supportedLanguages == Lang_None); + d->m_supportedLanguages = d->m_supportedLanguages | languageId; + d->m_languageCount++; - Core::ActionManager *am = Core::ICore::instance()->actionManager(); - QAction *langChange = new QAction(langName, this); - langChange->setCheckable(true); - langChange->setChecked(false); - - d->m_languageActionGroup->addAction(langChange); + d->m_toolBars.insert(languageId, 0); + d->m_contextsForLanguage.insert(languageId, context); + Core::ActionManager *am = Core::ICore::instance()->actionManager(); - QString prefix = tr("Alt+L"); - connect(langChange, SIGNAL(triggered()), SLOT(langChangeTriggered())); - Core::Command *cmd = am->registerAction(langChange, - "Debugger.Language." + langName, + QAction *debuggableLang = new QAction(languageName, this); + debuggableLang->setCheckable(true); + debuggableLang->setText(languageName); + d->m_languageActionGroup->addAction(debuggableLang); + Core::Command *activeDebugLanguageCmd = am->registerAction(debuggableLang, + "Debugger.DebugLanguage." + languageName, Core::Context(Core::Constants::C_GLOBAL)); - cmd->setDefaultKeySequence(QKeySequence( - QString("%1,%2").arg(prefix).arg(d->m_languages.count()))); + d->m_debuggerLanguageMenu->addAction(activeDebugLanguageCmd); + + QString shortcutPrefix = tr("Alt+L"); + QString shortcutIndex = QString::number(d->m_languageCount); + activeDebugLanguageCmd->setDefaultKeySequence(QKeySequence( + QString("%1,%2").arg(shortcutPrefix).arg(shortcutIndex))); + + if (languageId == Lang_Qml) { + d->m_qmlEnabled = true; + d->m_activateQmlAction = debuggableLang; + } else if (!d->m_activateCppAction) { + d->m_activateCppAction = debuggableLang; + } + connect(debuggableLang, SIGNAL(triggered()), SLOT(updateActiveLanguages())); - d->m_languageMenu->addAction(cmd); -} + updateUiForRunConfiguration(0); -void DebuggerUISwitcher::langChangeTriggered() -{ - QObject *sdr = sender(); - QAction *act = qobject_cast<QAction*>(sdr); - changeDebuggerUI(act->text()); + if (activate) + updateUi(); } -void DebuggerUISwitcher::changeDebuggerUI(const QString &langName) +void DebuggerUISwitcher::updateUi() { - //qDebug() << "CHANGE DEBUGGER UI: " << langName << d->m_changingUI; - if (d->m_changingUI) + if (d->m_changingUI || !d->m_initialized || !d->m_inDebugMode) return; + d->m_changingUI = true; - int langId = d->m_languages.indexOf(langName); - if (langId != d->m_activeLanguage) { - d->m_languageActionGroup->actions()[langId]->setChecked(true); - if ((d->m_toolBars.value(langName))) - d->m_toolbarStack->setCurrentWidget(d->m_toolBars.value(langName)); - - foreach (DebugToolWindow *window, d->m_dockWidgets) { - //qDebug() << " WINDOW " << window->m_dockWidget->objectName() - // << window->m_visible; - - if (window->m_languageId != langId) { - // visibleTo must be used because during init, debugger is - // not visible, although visibility is explicitly set through - // both default layout and QSettings. - window->m_visible = window->m_dockWidget->isVisibleTo(d->m_mainWindow); - window->m_dockWidget->hide(); - } else { - if (window->m_visible) { - window->m_dockWidget->show(); - } - } - } + if (isQmlActive()) { + activateQmlCppLayout(); + } else { + activateCppLayout(); + } - foreach (ViewsMenuItems menuitem, d->m_viewsMenuItems) { - bool enable = menuitem.first == langId || menuitem.first == -1; - menuitem.second->setEnabled(enable); - } + d->m_previousDebugLanguages = d->m_activeDebugLanguages; - d->m_languageMenu->menu()->setTitle(tr("Language") + " (" + langName + ")"); + d->m_changingUI = false; +} - Core::ICore *core = Core::ICore::instance(); - const Core::Context &oldContexts = d->m_contextsForLanguage.value(d->m_activeLanguage); - const Core::Context &newContexts = d->m_contextsForLanguage.value(langId); - core->updateAdditionalContexts(oldContexts, newContexts); +void DebuggerUISwitcher::activateQmlCppLayout() +{ + Core::ICore *core = Core::ICore::instance(); + Core::Context qmlCppContext = d->m_contextsForLanguage.value(Lang_Qml); + qmlCppContext.add(d->m_contextsForLanguage.value(Lang_Cpp)); + + // always use cpp toolbar + d->m_toolbarStack->setCurrentWidget(d->m_toolBars.value(Lang_Cpp)); + + if (d->m_previousDebugLanguages & Lang_Qml) { + d->m_dockWidgetActiveStateQmlCpp = d->m_mainWindow->saveSettings(); + core->updateAdditionalContexts(qmlCppContext, Core::Context()); + } else if (d->m_previousDebugLanguages & Lang_Cpp) { + d->m_dockWidgetActiveStateCpp = d->m_mainWindow->saveSettings(); + core->updateAdditionalContexts(d->m_contextsForLanguage.value(Lang_Cpp), Core::Context()); + } - d->m_activeLanguage = langId; + d->m_mainWindow->restoreSettings(d->m_dockWidgetActiveStateQmlCpp); + core->updateAdditionalContexts(Core::Context(), qmlCppContext); +} - emit languageChanged(langName); +void DebuggerUISwitcher::activateCppLayout() +{ + Core::ICore *core = Core::ICore::instance(); + Core::Context qmlCppContext = d->m_contextsForLanguage.value(Lang_Qml); + qmlCppContext.add(d->m_contextsForLanguage.value(Lang_Cpp)); + d->m_toolbarStack->setCurrentWidget(d->m_toolBars.value(Lang_Cpp)); + + if (d->m_previousDebugLanguages & Lang_Qml) { + d->m_dockWidgetActiveStateQmlCpp = d->m_mainWindow->saveSettings(); + core->updateAdditionalContexts(qmlCppContext, Core::Context()); + } else if (d->m_previousDebugLanguages & Lang_Cpp) { + d->m_dockWidgetActiveStateCpp = d->m_mainWindow->saveSettings(); + core->updateAdditionalContexts(d->m_contextsForLanguage.value(Lang_Cpp), Core::Context()); } - d->m_changingUI = false; + d->m_mainWindow->restoreSettings(d->m_dockWidgetActiveStateCpp); + + const Core::Context &cppContext = d->m_contextsForLanguage.value(Lang_Cpp); + core->updateAdditionalContexts(Core::Context(), cppContext); } -void DebuggerUISwitcher::setToolbar(const QString &langName, QWidget *widget) +void DebuggerUISwitcher::setToolbar(const DebuggerLanguage &language, QWidget *widget) { - Q_ASSERT(d->m_toolBars.contains(langName)); - d->m_toolBars[langName] = widget; + Q_ASSERT(d->m_toolBars.contains(language)); + d->m_toolBars[language] = widget; d->m_toolbarStack->addWidget(widget); } @@ -337,6 +476,7 @@ QWidget *DebuggerUISwitcher::createMainWindow(Core::BaseMode *mode) d->m_mainWindow->setDockNestingEnabled(true); connect(d->m_mainWindow, SIGNAL(resetLayout()), this, SLOT(resetDebuggerLayout())); + connect(d->m_mainWindow->toggleLockedAction(), SIGNAL(triggered()), SLOT(updateDockWidgetSettings())); QBoxLayout *editorHolderLayout = new QVBoxLayout; editorHolderLayout->setMargin(0); @@ -385,11 +525,54 @@ QWidget *DebuggerUISwitcher::createMainWindow(Core::BaseMode *mode) return d->m_mainWindow; } +QDockWidget *DebuggerUISwitcher::breakWindow() const +{ + return dockWidget(Constants::DW_BREAK); +} + +QDockWidget *DebuggerUISwitcher::stackWindow() const +{ + return dockWidget(Constants::DW_STACK); +} + +QDockWidget *DebuggerUISwitcher::watchWindow() const +{ + return dockWidget(Constants::DW_WATCHERS); +} + +QDockWidget *DebuggerUISwitcher::outputWindow() const +{ + return dockWidget(Constants::DW_OUTPUT); +} + +QDockWidget *DebuggerUISwitcher::snapshotsWindow() const +{ + return dockWidget(Constants::DW_SNAPSHOTS); +} + +QDockWidget *DebuggerUISwitcher::threadsWindow() const +{ + return dockWidget(Constants::DW_THREADS); +} + +QDockWidget *DebuggerUISwitcher::qmlInspectorWindow() const +{ + return dockWidget(Constants::DW_QML_INSPECTOR); +} + +QDockWidget *DebuggerUISwitcher::dockWidget(const QString &objectName) const +{ + foreach(const Debugger::Internal::DebugToolWindow *toolWindow, d->m_dockWidgets) { + if (toolWindow->m_dockWidget->objectName() == objectName) + return toolWindow->m_dockWidget; + } + return 0; +} /*! Keep track of dock widgets so they can be shown/hidden for different languages */ -QDockWidget *DebuggerUISwitcher::createDockWidget(const QString &langName, +QDockWidget *DebuggerUISwitcher::createDockWidget(const DebuggerLanguage &language, QWidget *widget, Qt::DockWidgetArea area, bool visibleByDefault) { //qDebug() << "CREATE DOCK" << widget->objectName() << langName @@ -397,25 +580,31 @@ QDockWidget *DebuggerUISwitcher::createDockWidget(const QString &langName, QDockWidget *dockWidget = d->m_mainWindow->addDockForWidget(widget); d->m_mainWindow->addDockWidget(area, dockWidget); DebugToolWindow *window = new DebugToolWindow; - window->m_languageId = d->m_languages.indexOf(langName); window->m_dockWidget = dockWidget; window->m_visible = visibleByDefault; d->m_dockWidgets.append(window); - if (d->m_languages.indexOf(langName) != d->m_activeLanguage) + if (!(d->m_activeDebugLanguages & language)) { dockWidget->hide(); + } Core::Context globalContext(Core::Constants::C_GLOBAL); Core::ActionManager *am = Core::ICore::instance()->actionManager(); - QAction *action = dockWidget->toggleViewAction(); - Core::Command *cmd = am->registerAction(action, + QAction *toggleViewAction = dockWidget->toggleViewAction(); + Core::Command *cmd = am->registerAction(toggleViewAction, "Debugger." + dockWidget->objectName(), globalContext); cmd->setAttribute(Core::Command::CA_Hide); d->m_viewsMenu->addAction(cmd); - d->m_viewsMenuItems.append(qMakePair(d->m_languages.indexOf(langName), action)); + d->m_viewsMenuItems.append(qMakePair(language, toggleViewAction)); + + dockWidget->installEventFilter(d->m_resizeEventFilter); + + connect(dockWidget->toggleViewAction(), SIGNAL(triggered(bool)), SLOT(updateDockWidgetSettings())); + connect(dockWidget, SIGNAL(topLevelChanged(bool)), SLOT(updateDockWidgetSettings())); + connect(dockWidget, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), SLOT(updateDockWidgetSettings())); return dockWidget; } @@ -446,58 +635,93 @@ void DebuggerUISwitcher::aboutToShutdown() void DebuggerUISwitcher::writeSettings() const { - QSettings *s = Core::ICore::instance()->settings(); - s->beginGroup(QLatin1String("DebugMode")); - - foreach(Internal::DebugToolWindow *toolWindow, d->m_dockWidgets) { - bool visible = toolWindow->m_visible; - if (toolWindow->m_languageId == d->m_activeLanguage) { - visible = toolWindow->m_dockWidget->isVisibleTo(d->m_mainWindow); + QSettings *settings = Core::ICore::instance()->settings(); + { + settings->beginGroup(QLatin1String("DebugMode.CppMode")); + QHashIterator<QString, QVariant> it(d->m_dockWidgetActiveStateCpp); + while (it.hasNext()) { + it.next(); + settings->setValue(it.key(), it.value()); } - toolWindow->m_dockWidget->setMinimumSize(1, 1); - toolWindow->m_dockWidget->setVisible(visible); + settings->endGroup(); + } + { + settings->beginGroup(QLatin1String("DebugMode.CppQmlMode")); + QHashIterator<QString, QVariant> it(d->m_dockWidgetActiveStateQmlCpp); + while (it.hasNext()) { + it.next(); + settings->setValue(it.key(), it.value()); + } + settings->endGroup(); } - - d->m_mainWindow->saveSettings(s); - s->endGroup(); } void DebuggerUISwitcher::readSettings() { - //qDebug() << "\n SWITCHER READ SETTINGS \n"; - QSettings *s = Core::ICore::instance()->settings(); - s->beginGroup(QLatin1String("DebugMode")); - d->m_mainWindow->restoreSettings(s); - s->endGroup(); -/* - foreach(Internal::DebugToolWindow *toolWindow, d->m_dockWidgets) { - toolWindow->m_visible = toolWindow->m_dockWidget->isVisibleTo(d->m_mainWindow); + QSettings *settings = Core::ICore::instance()->settings(); + d->m_dockWidgetActiveStateCpp.clear(); + d->m_dockWidgetActiveStateQmlCpp.clear(); + + settings->beginGroup(QLatin1String("DebugMode.CppMode")); + foreach (const QString &key, settings->childKeys()) { + d->m_dockWidgetActiveStateCpp.insert(key, settings->value(key)); } -*/ + settings->endGroup(); + + settings->beginGroup(QLatin1String("DebugMode.CppQmlMode")); + foreach (const QString &key, settings->childKeys()) { + d->m_dockWidgetActiveStateQmlCpp.insert(key, settings->value(key)); + } + settings->endGroup(); } void DebuggerUISwitcher::initialize() { createViewsMenuItems(); - //qDebug() << "UI SWITCHER INITIALIZE"; - emit dockArranged(QString()); + emit dockResetRequested(Lang_None); readSettings(); - const QString &activeLang = (d->m_activeLanguage != -1 - ? d->m_languages.at(d->m_activeLanguage) - : d->m_languages.first()); - d->m_activeLanguage = -1; // enforce refresh - changeDebuggerUI(activeLang); + updateUi(); hideInactiveWidgets(); d->m_mainWindow->setDockActionsVisible(false); + d->m_initialized = true; } void DebuggerUISwitcher::resetDebuggerLayout() { - //qDebug() << "RESET DEBUGGER LAYOUT" << d->m_languages.at(d->m_activeLanguage); - emit dockArranged(d->m_languages.at(d->m_activeLanguage)); + emit dockResetRequested(d->m_activeDebugLanguages); + + if (isQmlActive()) { + d->m_dockWidgetActiveStateQmlCpp = d->m_mainWindow->saveSettings(); + } else { + d->m_dockWidgetActiveStateCpp = d->m_mainWindow->saveSettings(); + } + + updateActiveLanguages(); +} + +void DebuggerUISwitcher::updateDockWidgetSettings() +{ + if (!d->m_inDebugMode || d->m_changingUI) + return; + + if (isQmlActive()) { + d->m_dockWidgetActiveStateQmlCpp = d->m_mainWindow->saveSettings(); + } else { + d->m_dockWidgetActiveStateCpp = d->m_mainWindow->saveSettings(); + } +} + +bool DebuggerUISwitcher::isQmlCppActive() const +{ + return (d->m_activeDebugLanguages & Lang_Cpp) && (d->m_activeDebugLanguages & Lang_Qml); +} + +bool DebuggerUISwitcher::isQmlActive() const +{ + return (d->m_activeDebugLanguages & Lang_Qml); } QList<Internal::DebugToolWindow* > DebuggerUISwitcher::i_mw_debugToolWindows() const diff --git a/src/plugins/debugger/debuggeruiswitcher.h b/src/plugins/debugger/debuggeruiswitcher.h index f7fcb2bd82a..f4fca6b4997 100644 --- a/src/plugins/debugger/debuggeruiswitcher.h +++ b/src/plugins/debugger/debuggeruiswitcher.h @@ -31,11 +31,13 @@ #define DEBUGGERUISWITCHER_H #include "debugger_global.h" +#include "debuggerconstants.h" #include <QtCore/QObject> #include <QtCore/QMultiHash> -QT_FORWARD_DECLARE_CLASS(QDockWidget); +QT_FORWARD_DECLARE_CLASS(QEvent) +QT_FORWARD_DECLARE_CLASS(QDockWidget) namespace Core { class ActionContainer; @@ -50,6 +52,11 @@ class FancyMainWindow; class SavedAction; } +namespace ProjectExplorer { + class Project; + class Target; + class RunConfiguration; +} namespace Debugger { struct DebuggerUISwitcherPrivate; @@ -69,62 +76,105 @@ public: static DebuggerUISwitcher *instance(); // debuggable languages are registered with this function. - void addLanguage(const QString &langName, const Core::Context &context); + void addLanguage(const DebuggerLanguage &language, const QString &languageName, + const Core::Context &context); // debugger toolbars are registered with this function - void setToolbar(const QString &langName, QWidget *widget); + void setToolbar(const DebuggerLanguage &language, QWidget *widget); // menu actions are registered with this function - void addMenuAction(Core::Command *command, const QString &langName, + void addMenuAction(Core::Command *command, const DebuggerLanguage &language, const QString &group = QString()); - QStringList supportedLanguages() const; + // all supported languagess + DebuggerLanguages supportedLanguages() const; - // Changes the active language UI to the one specified by langName. - // Does nothing if automatic switching is toggled off from settings. - void setActiveLanguage(const QString &langName); - int activeLanguageId() const; + // active languages to be debugged. + DebuggerLanguages activeDebugLanguages() const; // called when all dependent plugins have loaded void initialize(); void aboutToShutdown(); + // most common debugger windows + QDockWidget *breakWindow() const; + QDockWidget *stackWindow() const; + QDockWidget *watchWindow() const; + QDockWidget *snapshotsWindow() const; + QDockWidget *threadsWindow() const; + QDockWidget *outputWindow() const; + QDockWidget *qmlInspectorWindow() const; + + QDockWidget *dockWidget(const QString &objectName) const; + // dockwidgets are registered to the main window - QDockWidget *createDockWidget(const QString &langName, QWidget *widget, + QDockWidget *createDockWidget(const DebuggerLanguage &language, QWidget *widget, Qt::DockWidgetArea area = Qt::TopDockWidgetArea, bool visibleByDefault = true); Utils::FancyMainWindow *mainWindow() const; signals: - void languageChanged(const QString &langName); - // emit when dock needs to be reset - void dockArranged(const QString &activeLanguage); + // emit when user changes active languages from the menu. + // Both UI and debugger startup are affected. + void activeLanguagesChanged(Debugger::DebuggerLanguages activeLanguages); + + void dockResetRequested(const Debugger::DebuggerLanguages &activeLanguages); private slots: void modeChanged(Core::IMode *mode); - void changeDebuggerUI(const QString &langName); + void updateUi(); void resetDebuggerLayout(); - void langChangeTriggered(); + + void updateUiForProject(ProjectExplorer::Project *project); + void updateUiForTarget(ProjectExplorer::Target *target); + void updateUiForRunConfiguration(ProjectExplorer::RunConfiguration *rc); + void updateUiOnFileListChange(); + + void updateActiveLanguages(); + void updateDockWidgetSettings(); + + void onModeChanged(Core::IMode *mode); private: // Used by MainWindow friend class Internal::DebuggerMainWindow; QList<Internal::DebugToolWindow *> i_mw_debugToolWindows() const; + void activateQmlCppLayout(); + void activateCppLayout(); + void hideInactiveWidgets(); void createViewsMenuItems(); void readSettings(); void writeSettings() const; + bool isQmlCppActive() const; + bool isQmlActive() const; QWidget *createContents(Core::BaseMode *mode); QWidget *createMainWindow(Core::BaseMode *mode); DebuggerUISwitcherPrivate *d; - Utils::SavedAction *m_changeLanguageAction; }; +namespace Internal { +class DockWidgetEventFilter : public QObject +{ + Q_OBJECT + +public: + DockWidgetEventFilter(QObject *parent = 0); + +signals: + void widgetResized(); + +protected: + bool eventFilter(QObject *obj, QEvent *event); +}; + +} // namespace Internal + } // namespace Debugger #endif // DEBUGGERUISWITCHER_H diff --git a/src/plugins/debugger/qml/qml.pri b/src/plugins/debugger/qml/qml.pri index 2bc5e8ebd4a..622215080d3 100644 --- a/src/plugins/debugger/qml/qml.pri +++ b/src/plugins/debugger/qml/qml.pri @@ -4,9 +4,10 @@ HEADERS += \ $$PWD/qmlengine.h \ $$PWD/qmladapter.h \ $$PWD/qmldebuggerclient.h \ - $$PWD/qmljsprivateapi.h - + $$PWD/qmljsprivateapi.h \ + $$PWD/qmlcppengine.h SOURCES += \ $$PWD/qmlengine.cpp \ $$PWD/qmladapter.cpp \ - $$PWD/qmldebuggerclient.cpp + $$PWD/qmldebuggerclient.cpp \ + $$PWD/qmlcppengine.cpp diff --git a/src/plugins/debugger/qml/qmladapter.cpp b/src/plugins/debugger/qml/qmladapter.cpp index 2e77cfad262..a322b807f09 100644 --- a/src/plugins/debugger/qml/qmladapter.cpp +++ b/src/plugins/debugger/qml/qmladapter.cpp @@ -56,9 +56,26 @@ QmlAdapter::QmlAdapter(DebuggerEngine *engine, QObject *parent) void QmlAdapter::beginConnection() { + m_connectionAttempts = 0; m_connectionTimer->start(); } +void QmlAdapter::pauseConnection() +{ + m_connectionTimer->stop(); +} + +void QmlAdapter::closeConnection() +{ + if (m_connectionTimer->isActive()) { + m_connectionTimer->stop(); + } else { + if (m_conn) { + m_conn->disconnectFromHost(); + } + } +} + void QmlAdapter::pollInferior() { ++m_connectionAttempts; diff --git a/src/plugins/debugger/qml/qmladapter.h b/src/plugins/debugger/qml/qmladapter.h index 1def1ade698..0bed6588538 100644 --- a/src/plugins/debugger/qml/qmladapter.h +++ b/src/plugins/debugger/qml/qmladapter.h @@ -49,6 +49,8 @@ class DEBUGGER_EXPORT QmlAdapter : public QObject public: explicit QmlAdapter(DebuggerEngine *engine, QObject *parent = 0); void beginConnection(); + void pauseConnection(); + void closeConnection(); bool isConnected() const; bool isUnconnected() const; diff --git a/src/plugins/debugger/qml/qmlcppengine.cpp b/src/plugins/debugger/qml/qmlcppengine.cpp new file mode 100644 index 00000000000..0cd8931349e --- /dev/null +++ b/src/plugins/debugger/qml/qmlcppengine.cpp @@ -0,0 +1,649 @@ +#include "qmlcppengine.h" +#include "qmlengine.h" +#include "debuggeruiswitcher.h" +#include "debuggerplugin.h" + +#include <qmljseditor/qmljseditorconstants.h> +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/editormanager/ieditor.h> + + +namespace Debugger { +namespace Internal { + +const int ConnectionWaitTimeMs = 5000; + +DebuggerEngine *createCdbEngine(const DebuggerStartParameters &); +DebuggerEngine *createGdbEngine(const DebuggerStartParameters &); +DebuggerEngine *createQmlEngine(const DebuggerStartParameters &); + +DebuggerEngine *createQmlCppEngine(const DebuggerStartParameters &sp) +{ + return new QmlCppEngine(sp); +} + +QmlCppEngine::QmlCppEngine(const DebuggerStartParameters &sp) + : DebuggerEngine(sp) + , m_shutdownOk(true) + , m_shutdownDeferred(false) + , m_shutdownDone(false) + , m_isInitialStartup(true) +{ + m_qmlEngine = qobject_cast<QmlEngine*>(createQmlEngine(sp)); + m_qmlEngine->setAttachToRunningExternalApp(true); + + if (startParameters().cppEngineType == GdbEngineType) { + m_cppEngine = createGdbEngine(sp); + } else { + m_cppEngine = createCdbEngine(sp); + } + + m_cppEngine->setRunInWrapperEngine(true); + m_qmlEngine->setRunInWrapperEngine(true); + + m_activeEngine = m_cppEngine; + connect(m_cppEngine, SIGNAL(stateChanged(DebuggerState)), SLOT(masterEngineStateChanged(DebuggerState))); + connect(m_qmlEngine, SIGNAL(stateChanged(DebuggerState)), SLOT(slaveEngineStateChanged(DebuggerState))); + + Core::EditorManager *em = Core::EditorManager::instance(); + connect(em, SIGNAL(currentEditorChanged(Core::IEditor*)), SLOT(editorChanged(Core::IEditor*))); +} + +QmlCppEngine::~QmlCppEngine() +{ + delete m_qmlEngine; + delete m_cppEngine; + m_qmlEngine = 0; + m_cppEngine = 0; +} + +void QmlCppEngine::editorChanged(Core::IEditor *editor) +{ + // change the engine based on editor, but only if we're not currently in stopped state. + if (state() != InferiorRunOk || !editor) + return; + + if (editor->id() == QmlJSEditor::Constants::C_QMLJSEDITOR_ID) { + setActiveEngine(Lang_Qml); + } else { + setActiveEngine(Lang_Cpp); + } +} + +void QmlCppEngine::setActiveEngine(DebuggerLanguage language) +{ + DebuggerEngine *previousEngine = m_activeEngine; + bool updateEngine = false; + QString engineName; + + if (language == Lang_Cpp) { + engineName = QLatin1String("C++"); + m_activeEngine = m_cppEngine; + // don't update cpp engine - at least gdb will stop temporarily, + // which is not nice when you're just switching files. + } else if (language == Lang_Qml) { + engineName = QLatin1String("QML"); + m_activeEngine = m_qmlEngine; + updateEngine = true; + } + if (previousEngine != m_activeEngine) { + showStatusMessage(tr("%1 debugger activated").arg(engineName)); + plugin()->displayDebugger(m_activeEngine, updateEngine); + } +} + +void QmlCppEngine::setToolTipExpression(const QPoint & mousePos, + TextEditor::ITextEditor *editor, int cursorPos) +{ + m_activeEngine->setToolTipExpression(mousePos, editor, cursorPos); +} + +void QmlCppEngine::updateWatchData(const WatchData &data) +{ + m_activeEngine->updateWatchData(data); +} + +void QmlCppEngine::watchPoint(const QPoint &point) +{ + m_cppEngine->watchPoint(point); +} + +void QmlCppEngine::fetchMemory(MemoryViewAgent *mva, QObject *obj, + quint64 addr, quint64 length) +{ + m_cppEngine->fetchMemory(mva, obj, addr, length); +} + +void QmlCppEngine::fetchDisassembler(DisassemblerViewAgent *dva) +{ + m_cppEngine->fetchDisassembler(dva); +} + +void QmlCppEngine::activateFrame(int index) +{ + m_cppEngine->activateFrame(index); +} + +void QmlCppEngine::reloadModules() +{ + m_cppEngine->reloadModules(); +} + +void QmlCppEngine::examineModules() +{ + m_cppEngine->examineModules(); +} + +void QmlCppEngine::loadSymbols(const QString &moduleName) +{ + m_cppEngine->loadSymbols(moduleName); +} + +void QmlCppEngine::loadAllSymbols() +{ + m_cppEngine->loadAllSymbols(); +} + +void QmlCppEngine::requestModuleSymbols(const QString &moduleName) +{ + m_cppEngine->requestModuleSymbols(moduleName); +} + +void QmlCppEngine::reloadRegisters() +{ + m_cppEngine->reloadRegisters(); +} + +void QmlCppEngine::reloadSourceFiles() +{ + m_cppEngine->reloadSourceFiles(); +} + +void QmlCppEngine::reloadFullStack() +{ + m_cppEngine->reloadFullStack(); +} + +void QmlCppEngine::setRegisterValue(int regnr, const QString &value) +{ + m_cppEngine->setRegisterValue(regnr, value); +} + +unsigned QmlCppEngine::debuggerCapabilities() const +{ + // ### this could also be an OR of both engines' capabilities + return m_cppEngine->debuggerCapabilities(); +} + +bool QmlCppEngine::isSynchroneous() const +{ + return m_activeEngine->isSynchroneous(); +} + +QString QmlCppEngine::qtNamespace() const +{ + return m_cppEngine->qtNamespace(); +} + +void QmlCppEngine::createSnapshot() +{ + m_cppEngine->createSnapshot(); +} + +void QmlCppEngine::updateAll() +{ + m_activeEngine->updateAll(); +} + +void QmlCppEngine::attemptBreakpointSynchronization() +{ + m_cppEngine->attemptBreakpointSynchronization(); + static_cast<DebuggerEngine*>(m_qmlEngine)->attemptBreakpointSynchronization(); +} + +void QmlCppEngine::selectThread(int index) +{ + m_cppEngine->selectThread(index); +} + +void QmlCppEngine::assignValueInDebugger(const QString &expr, const QString &value) +{ + m_activeEngine->assignValueInDebugger(expr, value); +} + +QAbstractItemModel *QmlCppEngine::commandModel() const +{ + return m_activeEngine->commandModel(); +} + +QAbstractItemModel *QmlCppEngine::modulesModel() const +{ + return m_cppEngine->modulesModel(); +} + +QAbstractItemModel *QmlCppEngine::breakModel() const +{ + return m_activeEngine->breakModel(); +} + +QAbstractItemModel *QmlCppEngine::registerModel() const +{ + return m_cppEngine->registerModel(); +} + +QAbstractItemModel *QmlCppEngine::stackModel() const +{ + return m_activeEngine->stackModel(); +} + +QAbstractItemModel *QmlCppEngine::threadsModel() const +{ + return m_cppEngine->threadsModel(); +} + +QAbstractItemModel *QmlCppEngine::localsModel() const +{ + return m_activeEngine->localsModel(); +} + +QAbstractItemModel *QmlCppEngine::watchersModel() const +{ + return m_activeEngine->watchersModel(); +} + +QAbstractItemModel *QmlCppEngine::returnModel() const +{ + return m_cppEngine->returnModel(); +} + +QAbstractItemModel *QmlCppEngine::sourceFilesModel() const +{ + return m_cppEngine->sourceFilesModel(); +} + +void QmlCppEngine::detachDebugger() +{ + m_qmlEngine->detachDebugger(); + m_cppEngine->detachDebugger(); +} + +void QmlCppEngine::executeStep() +{ + m_activeEngine->executeStep(); +} + +void QmlCppEngine::executeStepOut() +{ + m_activeEngine->executeStepOut(); +} + +void QmlCppEngine::executeNext() +{ + m_activeEngine->executeNext(); +} + +void QmlCppEngine::executeStepI() +{ + m_activeEngine->executeStepI(); +} + +void QmlCppEngine::executeNextI() +{ + m_activeEngine->executeNextI(); +} + +void QmlCppEngine::executeReturn() +{ + m_activeEngine->executeReturn(); +} + +void QmlCppEngine::continueInferior() +{ + m_activeEngine->continueInferior(); +} + +void QmlCppEngine::interruptInferior() +{ + m_activeEngine->interruptInferior(); +} + +void QmlCppEngine::requestInterruptInferior() +{ + m_activeEngine->requestInterruptInferior(); +} + +void QmlCppEngine::executeRunToLine(const QString &fileName, int lineNumber) +{ + m_activeEngine->executeRunToLine(fileName, lineNumber); +} + +void QmlCppEngine::executeRunToFunction(const QString &functionName) +{ + m_activeEngine->executeRunToFunction(functionName); +} + +void QmlCppEngine::executeJumpToLine(const QString &fileName, int lineNumber) +{ + m_activeEngine->executeJumpToLine(fileName, lineNumber); +} + +void QmlCppEngine::executeDebuggerCommand(const QString &command) +{ + m_activeEngine->executeDebuggerCommand(command); +} + +void QmlCppEngine::frameUp() +{ + m_activeEngine->frameUp(); +} + +void QmlCppEngine::frameDown() +{ + m_activeEngine->frameDown(); +} + +void QmlCppEngine::notifyInferiorRunOk() +{ + DebuggerEngine::notifyInferiorRunOk(); + + Core::EditorManager *em = Core::EditorManager::instance(); + editorChanged(em->currentEditor()); +} + +void QmlCppEngine::setupEngine() +{ + m_cppEngine->startDebugger(runControl()); +} + +void QmlCppEngine::setupInferior() +{ + // called through notifyEngineSetupOk +} + +void QmlCppEngine::runEngine() +{ + // should never happen + showMessage(QString(Q_FUNC_INFO), LogError); +} + +void QmlCppEngine::shutdownInferior() +{ + // user wants to stop inferior: always use cpp engine for this. + if (m_activeEngine == m_qmlEngine) { + m_activeEngine = m_cppEngine; + + // we end up in this state after trying to shut down while debugging qml. + // b/c qml does not shutdown by itself, restore previous state and continue. + if (m_qmlEngine->state() == InferiorShutdownRequested) { + m_qmlEngine->setState(InferiorStopOk, true); + } + + if (m_qmlEngine->state() == InferiorStopOk) { + m_qmlEngine->continueInferior(); + } + } + if (m_cppEngine->state() == InferiorRunOk) { + // first interrupt c++ engine; when done, we can shutdown. + m_shutdownDeferred = true; + m_cppEngine->requestInterruptInferior(); + } + if (!m_shutdownDeferred) + m_cppEngine->shutdownInferior(); +} + +void QmlCppEngine::shutdownEngine() +{ + m_cppEngine->shutdownEngine(); + m_qmlEngine->shutdownEngineAsSlave(); + notifyEngineShutdownOk(); +} + +void QmlCppEngine::finishDebugger() +{ + if (!m_shutdownDone) { + m_shutdownDone = true; + if (m_shutdownOk) { + notifyEngineShutdownOk(); + } else { + notifyEngineShutdownFailed(); + } + } +} + +void QmlCppEngine::setupSlaveEngineOnTimer() +{ + QTimer::singleShot(ConnectionWaitTimeMs, this, SLOT(setupSlaveEngine())); +} + +void QmlCppEngine::setupSlaveEngine() +{ + if (state() == InferiorRunRequested) + m_qmlEngine->startDebugger(runControl()); +} + +void QmlCppEngine::masterEngineStateChanged(const DebuggerState &newState) +{ + //qDebug() << "gdb state set to" << newState; + + switch(newState) { + case EngineSetupRequested: + // go through this state + break; + + case EngineSetupFailed: + notifyEngineSetupFailed(); + break; + + case EngineSetupOk: + notifyEngineSetupOk(); + break; + + case InferiorSetupRequested: + // go through this state + break; + + case InferiorSetupFailed: + notifyInferiorSetupFailed(); + break; + + case EngineRunRequested: + setState(newState); + break; + + case EngineRunFailed: + notifyEngineRunFailed(); + break; + + case InferiorUnrunnable: + setState(newState); + break; + + case InferiorRunRequested: + setState(newState); + break; + + case InferiorRunOk: + if (m_qmlEngine->state() == DebuggerNotReady) { + if (m_isInitialStartup) { + m_isInitialStartup = false; + setupSlaveEngineOnTimer(); + } else { + setupSlaveEngine(); + } + } else { + notifyInferiorRunOk(); + } + break; + + case InferiorRunFailed: + notifyInferiorRunFailed(); + break; + + case InferiorStopRequested: + if (state() == InferiorRunRequested) { + // if stopping on startup, move on to normal state + // and go forward. Also, stop connection and continue later if needed. + m_qmlEngine->pauseConnection(); + setState(EngineRunRequested, true); + notifyEngineRunAndInferiorRunOk(); + setState(newState); + } else { + setState(newState); + } + break; + + case InferiorStopOk: + // debugger can stop while qml connection is not made yet, so we can + // end up in an illegal state transition here. + if (state() == InferiorStopRequested + || state() == InferiorRunFailed) + { + setState(newState); + } else if (state() == InferiorRunOk) { + // if we break on CPP side while running & engine is QML, switch. + if (m_activeEngine == m_qmlEngine) { + setActiveEngine(Lang_Cpp); + } + setState(newState); + } else if (state() == InferiorRunRequested) { + setState(newState, true); + } + if (m_shutdownDeferred) { + m_activeEngine = m_cppEngine; + m_shutdownDeferred = false; + shutdownInferior(); + } + break; + + case InferiorStopFailed: + setState(newState); + if (m_shutdownDeferred) { + m_activeEngine = m_cppEngine; + m_shutdownDeferred = false; + shutdownInferior(); + } + break; + + // here, we shut down the qml engine first. + // but due to everything being asyncronous, we cannot guarantee + // that it is shut down completely before gdb engine is shut down. + case InferiorShutdownRequested: + if (m_activeEngine == m_qmlEngine) { + m_activeEngine = m_cppEngine; + } + + m_qmlEngine->shutdownInferiorAsSlave(); + setState(newState); + break; + + case InferiorShutdownOk: + setState(newState); + m_qmlEngine->shutdownEngineAsSlave(); + break; + + case InferiorShutdownFailed: + setState(newState); + m_qmlEngine->shutdownEngineAsSlave(); + break; + + case EngineShutdownRequested: + setState(newState); + break; + + case EngineShutdownOk: + finishDebugger(); + break; + + case EngineShutdownFailed: + m_shutdownOk = false; + finishDebugger(); + break; + + default: + break; + } +} + +void QmlCppEngine::slaveEngineStateChanged(const DebuggerState &newState) +{ + //qDebug() << " qml engine changed to" << newState; + + if (m_activeEngine == m_qmlEngine) { + handleSlaveEngineStateChangeAsActive(newState); + } else { + handleSlaveEngineStateChange(newState); + } +} + +void QmlCppEngine::handleSlaveEngineStateChange(const DebuggerState &newState) +{ + switch(newState) { + case InferiorRunOk: + if (state() == InferiorRunRequested) { + // force state back so that the notification will succeed on init + setState(EngineRunRequested, true); + notifyEngineRunAndInferiorRunOk(); + } else { + // we have to manually override state with current one, because + // otherwise we'll have debugger controls in inconsistent state. + setState(state(), true); + } + + break; + + case InferiorStopOk: + if (state() == InferiorRunOk) { + // breakpoint was hit while running the app; change the active engine. + setActiveEngine(Lang_Qml); + setState(InferiorStopOk); + } + break; + + case InferiorRunFailed: + notifyInferiorRunFailed(); + break; + + case EngineShutdownFailed: + m_shutdownOk = false; + break; + + default: + // reset wrapper engine state to current state. + setState(state(), true); + break; + } +} + +void QmlCppEngine::handleSlaveEngineStateChangeAsActive(const DebuggerState &newState) +{ + switch(newState) { + case InferiorRunRequested: + setState(newState); + break; + + case InferiorRunOk: + setState(newState); + break; + + case InferiorStopOk: + setState(newState); + break; + + case InferiorRunFailed: + notifyInferiorRunFailed(); + break; + + case InferiorShutdownRequested: + if (m_cppEngine->state() == InferiorRunOk) { + // occurs when user presses stop button from debugger UI. + shutdownInferior(); + } + break; + + default: + break; + } +} + +} // namespace Internal +} // namespace Debugger diff --git a/src/plugins/debugger/qml/qmlcppengine.h b/src/plugins/debugger/qml/qmlcppengine.h new file mode 100644 index 00000000000..1c1a8d0b383 --- /dev/null +++ b/src/plugins/debugger/qml/qmlcppengine.h @@ -0,0 +1,124 @@ +#ifndef QMLGDBENGINE_H +#define QMLGDBENGINE_H + +#include "debuggerengine.h" + +namespace Core { +class IEditor; +} + +namespace Debugger { +namespace Internal { + +class GdbEngine; +class QmlEngine; + +class QmlCppEngine : public DebuggerEngine +{ + Q_OBJECT +public: + QmlCppEngine(const DebuggerStartParameters &sp); + virtual ~QmlCppEngine(); + + void setActiveEngine(DebuggerLanguage language); + + virtual void setToolTipExpression(const QPoint & /* mousePos */, + TextEditor::ITextEditor * /* editor */, int /* cursorPos */); + virtual void updateWatchData(const WatchData & /* data */); + + virtual void watchPoint(const QPoint &); + virtual void fetchMemory(MemoryViewAgent *, QObject *, + quint64 addr, quint64 length); + virtual void fetchDisassembler(DisassemblerViewAgent *); + virtual void activateFrame(int index); + + virtual void reloadModules(); + virtual void examineModules(); + virtual void loadSymbols(const QString &moduleName); + virtual void loadAllSymbols(); + virtual void requestModuleSymbols(const QString &moduleName); + + virtual void reloadRegisters(); + virtual void reloadSourceFiles(); + virtual void reloadFullStack(); + + virtual void setRegisterValue(int regnr, const QString &value); + virtual unsigned debuggerCapabilities() const; + + virtual bool isSynchroneous() const; + virtual QString qtNamespace() const; + + virtual void createSnapshot(); + virtual void updateAll(); + + virtual void attemptBreakpointSynchronization(); + virtual void selectThread(int index); + + virtual void assignValueInDebugger(const QString &expr, const QString &value); + + QAbstractItemModel *commandModel() const; + QAbstractItemModel *modulesModel() const; + QAbstractItemModel *breakModel() const; + QAbstractItemModel *registerModel() const; + QAbstractItemModel *stackModel() const; + QAbstractItemModel *threadsModel() const; + QAbstractItemModel *localsModel() const; + QAbstractItemModel *watchersModel() const; + QAbstractItemModel *returnModel() const; + QAbstractItemModel *sourceFilesModel() const; + +protected: + virtual void detachDebugger(); + virtual void executeStep(); + virtual void executeStepOut(); + virtual void executeNext(); + virtual void executeStepI(); + virtual void executeNextI(); + virtual void executeReturn(); + virtual void continueInferior(); + virtual void interruptInferior(); + virtual void requestInterruptInferior(); + + virtual void executeRunToLine(const QString &fileName, int lineNumber); + virtual void executeRunToFunction(const QString &functionName); + virtual void executeJumpToLine(const QString &fileName, int lineNumber); + virtual void executeDebuggerCommand(const QString &command); + + virtual void frameUp(); + virtual void frameDown(); + + virtual void notifyInferiorRunOk(); + +protected: + virtual void setupEngine(); + virtual void setupInferior(); + virtual void runEngine(); + virtual void shutdownInferior(); + virtual void shutdownEngine(); + +private slots: + void masterEngineStateChanged(const DebuggerState &state); + void slaveEngineStateChanged(const DebuggerState &state); + void setupSlaveEngine(); + void editorChanged(Core::IEditor *editor); + +private: + void setupSlaveEngineOnTimer(); + void finishDebugger(); + void handleSlaveEngineStateChange(const DebuggerState &newState); + void handleSlaveEngineStateChangeAsActive(const DebuggerState &newState); + +private: + QmlEngine *m_qmlEngine; + DebuggerEngine *m_cppEngine; + DebuggerEngine *m_activeEngine; + bool m_shutdownOk; + bool m_shutdownDeferred; + bool m_shutdownDone; + bool m_isInitialStartup; +}; + +} // namespace Internal +} // namespace Debugger + +#endif // QMLGDBENGINE_H diff --git a/src/plugins/debugger/qml/qmlengine.cpp b/src/plugins/debugger/qml/qmlengine.cpp index 7d4177b132c..f0b3577213b 100644 --- a/src/plugins/debugger/qml/qmlengine.cpp +++ b/src/plugins/debugger/qml/qmlengine.cpp @@ -105,6 +105,8 @@ QmlEngine::QmlEngine(const DebuggerStartParameters &startParameters) , m_ping(0) , m_adapter(new QmlAdapter(this)) , m_addedAdapterToObjectPool(false) + , m_attachToRunningExternalApp(false) + , m_hasShutdown(false) { setObjectName(QLatin1String("QmlEngine")); } @@ -113,6 +115,16 @@ QmlEngine::~QmlEngine() { } +void QmlEngine::setAttachToRunningExternalApp(bool value) +{ + m_attachToRunningExternalApp = value; +} + +void QmlEngine::pauseConnection() +{ + m_adapter->pauseConnection(); +} + void QmlEngine::setupInferior() { QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state()); @@ -154,26 +166,71 @@ void QmlEngine::runEngine() { QTC_ASSERT(state() == EngineRunRequested, qDebug() << state()); - // ### TODO for non-qmlproject apps, start in a different way - m_applicationLauncher.start(ProjectExplorer::ApplicationLauncher::Gui, - startParameters().executable, - startParameters().processArgs); + if (!m_attachToRunningExternalApp) { + m_applicationLauncher.start(ProjectExplorer::ApplicationLauncher::Gui, + startParameters().executable, + startParameters().processArgs); + } m_adapter->beginConnection(); plugin()->showMessage(tr("QML Debugger connecting..."), StatusBar); +} + +void QmlEngine::shutdownInferiorAsSlave() +{ + resetLocation(); - // FIXME: refactor the UI - Debugger::DebuggerUISwitcher::instance()->setActiveLanguage("QML"); + // This can be issued in almost any state. We assume, though, + // that at this point of time the inferior is not running anymore, + // even if stop notification were not issued or got lost. + if (state() == InferiorRunOk) { + setState(InferiorStopRequested); + setState(InferiorStopOk); + } + setState(InferiorShutdownRequested); + setState(InferiorShutdownOk); +} + +void QmlEngine::shutdownEngineAsSlave() +{ + if (m_hasShutdown) + return; + + disconnect(m_adapter, SIGNAL(connectionStartupFailed()), this, SLOT(connectionStartupFailed())); + m_adapter->closeConnection(); + + if (m_addedAdapterToObjectPool) { + ExtensionSystem::PluginManager *pluginManager = ExtensionSystem::PluginManager::instance(); + pluginManager->removeObject(m_adapter); + } + + if (m_attachToRunningExternalApp) { + setState(EngineShutdownRequested, true); + setState(EngineShutdownOk, true); + setState(DebuggerFinished, true); + } else { + if (m_applicationLauncher.isRunning()) { + // should only happen if engine is ill + disconnect(&m_applicationLauncher, SIGNAL(processExited(int)), this, SLOT(disconnected())); + m_applicationLauncher.stop(); + } + } + m_hasShutdown = true; } void QmlEngine::shutdownInferior() { + // don't do normal shutdown if running as slave engine + if (m_attachToRunningExternalApp) + return; + QTC_ASSERT(state() == InferiorShutdownRequested, qDebug() << state()); if (!m_applicationLauncher.isRunning()) { showMessage(tr("Trying to stop while process is no longer running."), LogError); } else { disconnect(&m_applicationLauncher, SIGNAL(processExited(int)), this, SLOT(disconnected())); - m_applicationLauncher.stop(); + if (!m_attachToRunningExternalApp) + m_applicationLauncher.stop(); } notifyInferiorShutdownOk(); } @@ -182,16 +239,7 @@ void QmlEngine::shutdownEngine() { QTC_ASSERT(state() == EngineShutdownRequested, qDebug() << state()); - if (m_addedAdapterToObjectPool) { - ExtensionSystem::PluginManager *pluginManager = ExtensionSystem::PluginManager::instance(); - pluginManager->removeObject(m_adapter); - } - - if (m_applicationLauncher.isRunning()) { - // should only happen if engine is ill - disconnect(&m_applicationLauncher, SIGNAL(processExited(int)), this, SLOT(disconnected())); - m_applicationLauncher.stop(); - } + shutdownEngineAsSlave(); notifyEngineShutdownOk(); } @@ -468,7 +516,9 @@ void QmlEngine::messageReceived(const QByteArray &message) showMessage(_("RECEIVED RESPONSE: ") + quoteUnprintableLatin1(message)); if (command == "STOPPED") { - notifyInferiorSpontaneousStop(); + if (state() == InferiorRunOk) { + notifyInferiorSpontaneousStop(); + } QList<QPair<QString, QPair<QString, qint32> > > backtrace; QList<WatchData> watches; @@ -516,14 +566,9 @@ void QmlEngine::messageReceived(const QByteArray &message) else watchHandler()->endCycle(); - // Ensure we got the right ui right now. - Debugger::DebuggerUISwitcher *uiSwitcher - = Debugger::DebuggerUISwitcher::instance(); - uiSwitcher->setActiveLanguage("C++"); - - bool becauseOfexception; - stream >> becauseOfexception; - if (becauseOfexception) { + bool becauseOfException; + stream >> becauseOfException; + if (becauseOfException) { QString error; stream >> error; diff --git a/src/plugins/debugger/qml/qmlengine.h b/src/plugins/debugger/qml/qmlengine.h index 9c77d2e4dff..2a25a0e6ce9 100644 --- a/src/plugins/debugger/qml/qmlengine.h +++ b/src/plugins/debugger/qml/qmlengine.h @@ -63,6 +63,11 @@ public: explicit QmlEngine(const DebuggerStartParameters &startParameters); ~QmlEngine(); + void setAttachToRunningExternalApp(bool value); + void shutdownInferiorAsSlave(); + void shutdownEngineAsSlave(); + void pauseConnection(); + public slots: void messageReceived(const QByteArray &message); void disconnected(); @@ -119,15 +124,18 @@ private slots: void connectionError(); private: - void expandObject(const QByteArray &iname, quint64 objectId); void sendPing(); private: + friend class QmlCppEngine; + int m_ping; QmlAdapter *m_adapter; ProjectExplorer::ApplicationLauncher m_applicationLauncher; bool m_addedAdapterToObjectPool; + bool m_attachToRunningExternalApp; + bool m_hasShutdown; }; } // namespace Internal diff --git a/src/plugins/qmljsinspector/qmlinspectortoolbar.cpp b/src/plugins/qmljsinspector/qmlinspectortoolbar.cpp index f7e5ffb6dbb..55a0908b663 100644 --- a/src/plugins/qmljsinspector/qmlinspectortoolbar.cpp +++ b/src/plugins/qmljsinspector/qmlinspectortoolbar.cpp @@ -33,11 +33,13 @@ #include <extensionsystem/pluginmanager.h> #include <coreplugin/icore.h> -#include <debugger/debuggeruiswitcher.h> #include <projectexplorer/projectexplorerconstants.h> #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/command.h> +#include <utils/styledbar.h> +#include <utils/filterlineedit.h> + #include <QHBoxLayout> #include <QAction> #include <QToolButton> @@ -77,7 +79,8 @@ QmlInspectorToolbar::QmlInspectorToolbar(QObject *parent) : m_isRunning(false), m_animationSpeed(1.0f), m_previousAnimationSpeed(0.0f), - m_activeTool(NoTool) + m_activeTool(NoTool), + m_barWidget(0) { } @@ -182,8 +185,6 @@ void QmlInspectorToolbar::createActions(const Core::Context &context) { Core::ICore *core = Core::ICore::instance(); Core::ActionManager *am = core->actionManager(); - ExtensionSystem::PluginManager *pluginManager = ExtensionSystem::PluginManager::instance(); - Debugger::DebuggerUISwitcher *uiSwitcher = pluginManager->getObject<Debugger::DebuggerUISwitcher>(); m_fromQmlAction = new QAction(QIcon(QLatin1String(":/qml/images/from-qml-small.png")), tr("Apply Changes to Document"), this); m_designmodeAction = new QAction(QIcon(QLatin1String(":/qml/images/designmode.png")), tr("Design Mode"), this); @@ -221,14 +222,15 @@ void QmlInspectorToolbar::createActions(const Core::Context &context) am->registerAction(m_toQmlAction, QmlJSInspector::Constants::TO_QML_ACTION, context); am->registerAction(m_fromQmlAction, QmlJSInspector::Constants::FROM_QML_ACTION, context); - QWidget *configBar = new QWidget; - configBar->setProperty("topBorder", true); + m_barWidget = new Utils::StyledBar; + m_barWidget->setSingleRow(true); + m_barWidget->setProperty("topBorder", true); - QHBoxLayout *configBarLayout = new QHBoxLayout(configBar); + QHBoxLayout *configBarLayout = new QHBoxLayout(m_barWidget); configBarLayout->setMargin(0); configBarLayout->setSpacing(5); - QMenu *playSpeedMenu = new QMenu(configBar); + QMenu *playSpeedMenu = new QMenu(m_barWidget); QActionGroup *playSpeedMenuActions = new QActionGroup(this); playSpeedMenuActions->setExclusive(true); playSpeedMenu->addAction(tr("Animation Speed")); @@ -258,8 +260,8 @@ void QmlInspectorToolbar::createActions(const Core::Context &context) m_menuPauseAction->setCheckable(true); playSpeedMenuActions->addAction(m_menuPauseAction); - configBarLayout->addWidget(createToolButton(am->command(ProjectExplorer::Constants::DEBUG)->action())); - configBarLayout->addWidget(createToolButton(am->command(ProjectExplorer::Constants::STOP)->action())); +// configBarLayout->addWidget(createToolButton(am->command(ProjectExplorer::Constants::DEBUG)->action())); +// configBarLayout->addWidget(createToolButton(am->command(ProjectExplorer::Constants::STOP)->action())); configBarLayout->addWidget(createToolButton(am->command(QmlJSInspector::Constants::FROM_QML_ACTION)->action())); configBarLayout->addWidget(createToolButton(am->command(QmlJSInspector::Constants::DESIGNMODE_ACTION)->action())); configBarLayout->addWidget(createToolButton(am->command(QmlJSInspector::Constants::RELOAD_ACTION)->action())); @@ -275,7 +277,7 @@ void QmlInspectorToolbar::createActions(const Core::Context &context) configBarLayout->addWidget(createToolButton(am->command(QmlJSInspector::Constants::ZOOM_ACTION)->action())); configBarLayout->addWidget(createToolButton(am->command(QmlJSInspector::Constants::COLOR_PICKER_ACTION)->action())); - m_colorBox = new ToolBarColorBox(configBar); + m_colorBox = new ToolBarColorBox(m_barWidget); m_colorBox->setMinimumSize(20, 20); m_colorBox->setMaximumSize(20, 20); m_colorBox->setInnerBorderColor(QColor(192,192,192)); @@ -283,9 +285,11 @@ void QmlInspectorToolbar::createActions(const Core::Context &context) configBarLayout->addWidget(m_colorBox); //configBarLayout->addWidget(createToolButton(am->command(QmlJSInspector::Constants::TO_QML_ACTION)->action())); + m_filterLineEdit = new Utils::FilterLineEdit(m_barWidget); + configBarLayout->addStretch(); + configBarLayout->addWidget(m_filterLineEdit); - uiSwitcher->setToolbar(QmlJSInspector::Constants::LANG_QML, configBar); setEnabled(false); connect(m_designmodeAction, SIGNAL(triggered()), SLOT(activateDesignModeOnClick())); @@ -305,6 +309,11 @@ void QmlInspectorToolbar::createActions(const Core::Context &context) connect(m_fromQmlAction, SIGNAL(triggered()), SLOT(activateFromQml())); } +QWidget *QmlInspectorToolbar::widget() const +{ + return m_barWidget; +} + void QmlInspectorToolbar::changeToDefaultAnimSpeed() { m_animationSpeed = 1.0f; diff --git a/src/plugins/qmljsinspector/qmlinspectortoolbar.h b/src/plugins/qmljsinspector/qmlinspectortoolbar.h index 957044ed645..8b9309a0512 100644 --- a/src/plugins/qmljsinspector/qmlinspectortoolbar.h +++ b/src/plugins/qmljsinspector/qmlinspectortoolbar.h @@ -39,6 +39,11 @@ namespace Core { class Context; } +namespace Utils { + class StyledBar; + class FilterLineEdit; +} + namespace QmlJSInspector { class ToolBarColorBox; @@ -61,6 +66,7 @@ public: explicit QmlInspectorToolbar(QObject *parent = 0); void createActions(const Core::Context &context); + QWidget *widget() const; public slots: void setEnabled(bool value); @@ -136,6 +142,9 @@ private: DesignTool m_activeTool; + Utils::StyledBar *m_barWidget; + Utils::FilterLineEdit *m_filterLineEdit; + }; } // namespace Internal diff --git a/src/plugins/qmljsinspector/qmljsinspector.cpp b/src/plugins/qmljsinspector/qmljsinspector.cpp index eedbd5ec32b..315126abeab 100644 --- a/src/plugins/qmljsinspector/qmljsinspector.cpp +++ b/src/plugins/qmljsinspector/qmljsinspector.cpp @@ -34,6 +34,7 @@ #include "qmljsprivateapi.h" #include "qmljscontextcrumblepath.h" #include "qmljsinspectorsettings.h" +#include "qmljsobjecttree.h" #include <qmljseditor/qmljseditorconstants.h> @@ -73,6 +74,7 @@ #include <texteditor/basetexteditor.h> #include <projectexplorer/runconfiguration.h> +#include <projectexplorer/buildconfiguration.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/project.h> @@ -91,6 +93,7 @@ #include <QtGui/QLabel> #include <QtGui/QDockWidget> +#include <QtGui/QVBoxLayout> #include <QtGui/QAction> #include <QtGui/QLineEdit> #include <QtGui/QLabel> @@ -123,6 +126,10 @@ QmlJS::ModelManagerInterface *modelManager() InspectorUi::InspectorUi(QObject *parent) : QObject(parent) , m_listeningToEditorManager(false) + , m_toolbar(0) + , m_crumblePath(0) + , m_objectTreeWidget(0) + , m_inspectorDockWidget(0) , m_settings(new InspectorSettings(this)) , m_clientProxy(0) , m_debugProject(0) @@ -138,7 +145,6 @@ InspectorUi::~InspectorUi() void InspectorUi::setupUi() { setupDockWidgets(); - m_toolbar->createActions(Core::Context(Constants::C_INSPECTOR)); restoreSettings(); } @@ -165,6 +171,13 @@ void InspectorUi::connected(ClientProxy *clientProxy) m_crumblePath, SLOT(updateContextPath(QStringList))); m_debugProject = ProjectExplorer::ProjectExplorerPlugin::instance()->startupProject(); + if (m_debugProject->activeTarget() + && m_debugProject->activeTarget()->activeBuildConfiguration()) + { + ProjectExplorer::BuildConfiguration *bc = m_debugProject->activeTarget()->activeBuildConfiguration(); + m_debugProjectBuildDir = bc->buildDirectory(); + } + connect(m_debugProject, SIGNAL(destroyed()), SLOT(currentDebugProjectRemoved())); setupToolbar(true); @@ -176,6 +189,7 @@ void InspectorUi::connected(ClientProxy *clientProxy) while(iter.hasNext()) { iter.next(); iter.value()->setClientProxy(m_clientProxy); + iter.value()->updateDebugIds(); } } @@ -200,8 +214,9 @@ void InspectorUi::disconnected() iter.next(); iter.value()->setClientProxy(0); } - m_clientProxy = 0; + m_objectTreeWidget->clear(); + m_pendingPreviewDocumentNames.clear(); } void InspectorUi::updateEngineList() @@ -235,6 +250,9 @@ void InspectorUi::initializeDocuments() m_listeningToEditorManager = true; connect(em, SIGNAL(editorAboutToClose(Core::IEditor*)), SLOT(removePreviewForEditor(Core::IEditor*))); connect(em, SIGNAL(editorOpened(Core::IEditor*)), SLOT(createPreviewForEditor(Core::IEditor*))); + connect(modelManager(), + SIGNAL(documentChangedOnDisk(QmlJS::Document::Ptr)), + SLOT(updatePendingPreviewDocuments(QmlJS::Document::Ptr))); } // initial update @@ -265,8 +283,10 @@ void InspectorUi::removePreviewForEditor(Core::IEditor *oldEditor) } } -void InspectorUi::createPreviewForEditor(Core::IEditor *newEditor) +QmlJSLiveTextPreview *InspectorUi::createPreviewForEditor(Core::IEditor *newEditor) { + QmlJSLiveTextPreview *preview = 0; + if (m_clientProxy && m_clientProxy->isConnected() && newEditor @@ -275,16 +295,26 @@ void InspectorUi::createPreviewForEditor(Core::IEditor *newEditor) { QString filename = newEditor->file()->fileName(); QmlJS::Document::Ptr doc = modelManager()->snapshot().document(filename); - if (!doc || !doc->qmlProgram()) - return; + if (!doc) { + if (filename.endsWith(".qml")) { + // add to list of docs that we have to update when + // snapshot figures out that there's a new document + m_pendingPreviewDocumentNames.append(filename); + } + return 0; + } + if (!doc->qmlProgram()) + return 0; + QmlJS::Document::Ptr initdoc = m_loadedSnapshot.document(filename); if (!initdoc) initdoc = doc; if (m_textPreviews.contains(filename)) { - m_textPreviews.value(filename)->associateEditor(newEditor); + preview = m_textPreviews.value(filename); + preview->associateEditor(newEditor); } else { - QmlJSLiveTextPreview *preview = new QmlJSLiveTextPreview(doc, initdoc, m_clientProxy, this); + preview = new QmlJSLiveTextPreview(doc, initdoc, m_clientProxy, this); connect(preview, SIGNAL(selectedItemsChanged(QList<QDeclarativeDebugObjectReference>)), SLOT(changeSelectedItems(QList<QDeclarativeDebugObjectReference>))); @@ -296,6 +326,8 @@ void InspectorUi::createPreviewForEditor(Core::IEditor *newEditor) preview->updateDebugIds(); } } + + return preview; } void InspectorUi::currentDebugProjectRemoved() @@ -314,16 +346,49 @@ void InspectorUi::reloadQmlViewer() m_clientProxy->reloadQmlViewer(); } -void InspectorUi::setSimpleDockWidgetArrangement() +void InspectorUi::setSimpleDockWidgetArrangement(const Debugger::DebuggerLanguages &activeLanguages) { - Utils::FancyMainWindow *mainWindow = Debugger::DebuggerUISwitcher::instance()->mainWindow(); + Debugger::DebuggerUISwitcher *uiSwitcher = Debugger::DebuggerUISwitcher::instance(); + Utils::FancyMainWindow *mw = uiSwitcher->mainWindow(); + + mw->setTrackingEnabled(false); + + if (activeLanguages.testFlag(Debugger::Lang_Cpp) && activeLanguages.testFlag(Debugger::Lang_Qml)) { + // cpp + qml + QList<QDockWidget *> dockWidgets = mw->dockWidgets(); + foreach (QDockWidget *dockWidget, dockWidgets) { + dockWidget->setFloating(false); + mw->removeDockWidget(dockWidget); + } + foreach (QDockWidget *dockWidget, dockWidgets) { + if (dockWidget == uiSwitcher->outputWindow()) { + mw->addDockWidget(Qt::TopDockWidgetArea, dockWidget); + } else { + mw->addDockWidget(Qt::BottomDockWidgetArea, dockWidget); + } + + if (dockWidget == m_inspectorDockWidget) { + dockWidget->show(); + } else { + dockWidget->hide(); + } + } - mainWindow->setTrackingEnabled(false); - mainWindow->removeDockWidget(m_crumblePathDock); - mainWindow->addDockWidget(Qt::BottomDockWidgetArea, m_crumblePathDock); - mainWindow->splitDockWidget(mainWindow->toolBarDockWidget(), m_crumblePathDock, Qt::Vertical); - m_crumblePathDock->setVisible(true); - mainWindow->setTrackingEnabled(true); + uiSwitcher->stackWindow()->show(); + uiSwitcher->watchWindow()->show(); + uiSwitcher->breakWindow()->show(); + uiSwitcher->threadsWindow()->show(); + uiSwitcher->snapshotsWindow()->show(); + m_inspectorDockWidget->show(); + + mw->splitDockWidget(mw->toolBarDockWidget(), uiSwitcher->stackWindow(), Qt::Vertical); + mw->splitDockWidget(uiSwitcher->stackWindow(), uiSwitcher->watchWindow(), Qt::Horizontal); + mw->tabifyDockWidget(uiSwitcher->watchWindow(), uiSwitcher->breakWindow()); + mw->tabifyDockWidget(uiSwitcher->watchWindow(), m_inspectorDockWidget); + + } + + mw->setTrackingEnabled(true); } void InspectorUi::setSelectedItemsByObjectReference(QList<QDeclarativeDebugObjectReference> objectReferences) @@ -337,11 +402,18 @@ void InspectorUi::gotoObjectReferenceDefinition(const QDeclarativeDebugObjectRef Q_UNUSED(obj); QDeclarativeDebugFileReference source = obj.source(); - const QString fileName = source.url().toLocalFile(); + QString fileName = source.url().toLocalFile(); if (source.lineNumber() < 0 || !QFile::exists(fileName)) return; + + // do some extra checking for filenames with shadow builds - we don't want to + // open the shadow build file, but the real one by default. + if (isShadowBuildProject()) { + fileName = filenameForShadowBuildFile(fileName); + } + Core::EditorManager *editorManager = Core::EditorManager::instance(); Core::IEditor *editor = editorManager->openEditor(fileName, QString(), Core::EditorManager::NoModeSwitch); TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor*>(editor); @@ -353,6 +425,28 @@ void InspectorUi::gotoObjectReferenceDefinition(const QDeclarativeDebugObjectRef } } +QString InspectorUi::filenameForShadowBuildFile(const QString &filename) const +{ + if (!debugProject() || !isShadowBuildProject()) + return filename; + + QDir projectDir(debugProject()->projectDirectory()); + QDir buildDir(debugProjectBuildDirectory()); + QFileInfo fileInfo(filename); + + if (projectDir.exists() && buildDir.exists() && fileInfo.exists()) { + if (fileInfo.absoluteFilePath().startsWith(buildDir.canonicalPath())) { + QString fileRelativePath = fileInfo.canonicalFilePath().mid(debugProjectBuildDirectory().length()); + QFileInfo projectFile(projectDir.canonicalPath() + QLatin1Char('/') + fileRelativePath); + + if (projectFile.exists()) + return projectFile.canonicalFilePath(); + } + + } + return filename; +} + bool InspectorUi::addQuotesForData(const QVariant &value) const { switch (value.type()) { @@ -369,15 +463,35 @@ bool InspectorUi::addQuotesForData(const QVariant &value) const void InspectorUi::setupDockWidgets() { + Debugger::DebuggerUISwitcher *uiSwitcher = Debugger::DebuggerUISwitcher::instance(); + + m_toolbar->createActions(Core::Context(Constants::C_INSPECTOR)); + m_toolbar->setObjectName("QmlInspectorToolbar"); + m_crumblePath = new ContextCrumblePath; m_crumblePath->setObjectName("QmlContextPath"); - m_crumblePath->setWindowTitle("Context Path"); + m_crumblePath->setWindowTitle(tr("Context Path")); connect(m_crumblePath, SIGNAL(elementClicked(int)), SLOT(crumblePathElementClicked(int))); - Debugger::DebuggerUISwitcher *uiSwitcher = Debugger::DebuggerUISwitcher::instance(); - m_crumblePathDock = uiSwitcher->createDockWidget(QmlJSInspector::Constants::LANG_QML, - m_crumblePath, Qt::BottomDockWidgetArea); - m_crumblePathDock->setAllowedAreas(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea); - m_crumblePathDock->setTitleBarWidget(new QWidget(m_crumblePathDock)); + + m_objectTreeWidget = new QmlJSObjectTree; + + QWidget *inspectorWidget = new QWidget; + inspectorWidget->setWindowTitle(tr("Inspector")); + + QVBoxLayout *wlay = new QVBoxLayout(inspectorWidget); + wlay->setMargin(0); + wlay->setSpacing(0); + inspectorWidget->setLayout(wlay); + wlay->addWidget(m_toolbar->widget()); + wlay->addWidget(m_objectTreeWidget); + wlay->addWidget(m_crumblePath); + + + m_inspectorDockWidget = uiSwitcher->createDockWidget(Debugger::Lang_Qml, + inspectorWidget, Qt::BottomDockWidgetArea); + m_inspectorDockWidget->setObjectName(Debugger::Constants::DW_QML_INSPECTOR); + m_inspectorDockWidget->setAllowedAreas(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea); + m_inspectorDockWidget->setTitleBarWidget(new QWidget(m_inspectorDockWidget)); } void InspectorUi::crumblePathElementClicked(int pathIndex) @@ -407,6 +521,19 @@ ProjectExplorer::Project *InspectorUi::debugProject() const return m_debugProject; } +bool InspectorUi::isShadowBuildProject() const +{ + if (!debugProject()) + return false; + + return (debugProject()->projectDirectory() != debugProjectBuildDirectory()); +} + +QString InspectorUi::debugProjectBuildDirectory() const +{ + return m_debugProjectBuildDir; +} + void InspectorUi::setApplyChangesToQmlObserver(bool applyChanges) { emit livePreviewActivated(applyChanges); @@ -422,6 +549,29 @@ void InspectorUi::applyChangesToQmlObserverHelper(bool applyChanges) } } +void InspectorUi::updatePendingPreviewDocuments(QmlJS::Document::Ptr doc) +{ + int idx = -1; + idx = m_pendingPreviewDocumentNames.indexOf(doc->fileName()); + + if (idx == -1) + return; + + QList<Core::IEditor *> editors = Core::EditorManager::instance()->editorsForFileName(doc->fileName()); + + if (editors.isEmpty()) + return; + + m_pendingPreviewDocumentNames.removeAt(idx); + + QmlJSLiveTextPreview *preview = createPreviewForEditor(editors.first()); + editors.removeFirst(); + + foreach(Core::IEditor *editor, editors) { + preview->associateEditor(editor); + } +} + void InspectorUi::disableLivePreview() { setApplyChangesToQmlObserver(false); diff --git a/src/plugins/qmljsinspector/qmljsinspector.h b/src/plugins/qmljsinspector/qmljsinspector.h index b7b5020a2a4..3c53ce2daad 100644 --- a/src/plugins/qmljsinspector/qmljsinspector.h +++ b/src/plugins/qmljsinspector/qmljsinspector.h @@ -33,6 +33,7 @@ #include "qmljsprivateapi.h" #include <coreplugin/basemode.h> +#include <debugger/debuggerconstants.h> #include <qmlprojectmanager/qmlprojectrunconfiguration.h> #include <qmljs/qmljsdocument.h> @@ -60,6 +61,7 @@ namespace QmlJSInspector { namespace Internal { class QmlInspectorToolbar; +class QmlJSObjectTree; class ClientProxy; class InspectorSettings; class ContextCrumblePath; @@ -89,6 +91,8 @@ public: // returns the project being currently debugged, or 0 if not debugging anything ProjectExplorer::Project *debugProject() const; + QString debugProjectBuildDirectory() const; + bool isShadowBuildProject() const; void setupUi(); void connected(ClientProxy *clientProxy); @@ -99,7 +103,7 @@ signals: void livePreviewActivated(bool isActivated); public slots: - void setSimpleDockWidgetArrangement(); + void setSimpleDockWidgetArrangement(const Debugger::DebuggerLanguages &activeLanguages); void reloadQmlViewer(); void serverReloaded(); void setApplyChangesToQmlObserver(bool applyChanges); @@ -114,12 +118,13 @@ private slots: void removePreviewForEditor(Core::IEditor *newEditor); - void createPreviewForEditor(Core::IEditor *newEditor); + QmlJSLiveTextPreview *createPreviewForEditor(Core::IEditor *newEditor); void disableLivePreview(); void crumblePathElementClicked(int); void currentDebugProjectRemoved(); + void updatePendingPreviewDocuments(QmlJS::Document::Ptr doc); private: bool addQuotesForData(const QVariant &value) const; @@ -129,15 +134,15 @@ private: void applyChangesToQmlObserverHelper(bool applyChanges); void setupToolbar(bool doConnect); void setupDockWidgets(); + QString filenameForShadowBuildFile(const QString &filename) const; private: - QWeakPointer<QDeclarativeEngineDebug> m_client; - bool m_listeningToEditorManager; QmlInspectorToolbar *m_toolbar; ContextCrumblePath *m_crumblePath; - QDockWidget *m_crumblePathDock; + QmlJSObjectTree *m_objectTreeWidget; + QDockWidget *m_inspectorDockWidget; InspectorSettings *m_settings; ClientProxy *m_clientProxy; @@ -145,7 +150,12 @@ private: // Qml/JS integration QHash<QString, QmlJSLiveTextPreview *> m_textPreviews; QmlJS::Snapshot m_loadedSnapshot; //the snapshot loaded by the viewer + + // project is needed for matching filenames, esp. with shadow builds. ProjectExplorer::Project *m_debugProject; + QString m_debugProjectBuildDir; + + QStringList m_pendingPreviewDocumentNames; static InspectorUi *m_instance; }; diff --git a/src/plugins/qmljsinspector/qmljsinspector.pro b/src/plugins/qmljsinspector/qmljsinspector.pro index 31b405eb7c3..27eaeaf3b29 100644 --- a/src/plugins/qmljsinspector/qmljsinspector.pro +++ b/src/plugins/qmljsinspector/qmljsinspector.pro @@ -18,7 +18,8 @@ qmljslivetextpreview.h \ qmljstoolbarcolorbox.h \ qmljsdesigndebugclient.h \ qmljscontextcrumblepath.h \ -qmljsinspectorsettings.h +qmljsinspectorsettings.h \ +qmljsobjecttree.h SOURCES += \ qmljsinspectorplugin.cpp \ @@ -29,7 +30,8 @@ qmljslivetextpreview.cpp \ qmljstoolbarcolorbox.cpp \ qmljsdesigndebugclient.cpp \ qmljscontextcrumblepath.cpp \ -qmljsinspectorsettings.cpp +qmljsinspectorsettings.cpp \ +qmljsobjecttree.cpp include(../../libs/qmljsdebugclient/qmljsdebugclient-lib.pri) diff --git a/src/plugins/qmljsinspector/qmljsinspectorconstants.h b/src/plugins/qmljsinspector/qmljsinspectorconstants.h index 1f9b5313b72..d9f08fbd7c2 100644 --- a/src/plugins/qmljsinspector/qmljsinspectorconstants.h +++ b/src/plugins/qmljsinspector/qmljsinspectorconstants.h @@ -43,8 +43,6 @@ const char * const M_DEBUG_SIMULTANEOUSLY = "QmlInspector.Menu.SimultaneousDebug const char * const INFO_EXPERIMENTAL = "QmlInspector.Experimental"; const char * const INFO_OUT_OF_SYNC = "QmlInspector.OutOfSyncWarning"; -const char * const LANG_QML = "QML"; - const char * const DESIGNMODE_ACTION = "QmlInspector.DesignMode"; const char * const RELOAD_ACTION = "QmlInspector.Reload"; const char * const PLAY_ACTION = "QmlInspector.Play"; diff --git a/src/plugins/qmljsinspector/qmljsinspectorplugin.cpp b/src/plugins/qmljsinspector/qmljsinspectorplugin.cpp index 211a55f325d..a344b8d2fac 100644 --- a/src/plugins/qmljsinspector/qmljsinspectorplugin.cpp +++ b/src/plugins/qmljsinspector/qmljsinspectorplugin.cpp @@ -107,7 +107,7 @@ bool InspectorPlugin::initialize(const QStringList &arguments, QString *errorStr ExtensionSystem::PluginManager *pluginManager = ExtensionSystem::PluginManager::instance(); Debugger::DebuggerUISwitcher *uiSwitcher = pluginManager->getObject<Debugger::DebuggerUISwitcher>(); - uiSwitcher->addLanguage(LANG_QML, Core::Context(C_INSPECTOR)); + uiSwitcher->addLanguage(Debugger::Lang_Qml, tr("QML"), Core::Context(C_INSPECTOR)); return true; } @@ -116,21 +116,12 @@ void InspectorPlugin::extensionsInitialized() { ExtensionSystem::PluginManager *pluginManager = ExtensionSystem::PluginManager::instance(); - Debugger::DebuggerUISwitcher *uiSwitcher = pluginManager->getObject<Debugger::DebuggerUISwitcher>(); - connect(uiSwitcher, SIGNAL(dockArranged(QString)), SLOT(setDockWidgetArrangement(QString))); - connect(pluginManager, SIGNAL(objectAdded(QObject*)), SLOT(objectAdded(QObject*))); connect(pluginManager, SIGNAL(aboutToRemoveObject(QObject*)), SLOT(aboutToRemoveObject(QObject*))); m_inspectorUi->setupUi(); } -void InspectorPlugin::setDockWidgetArrangement(const QString &activeLanguage) -{ - if (activeLanguage == QmlJSInspector::Constants::LANG_QML || activeLanguage.isEmpty()) - m_inspectorUi->setSimpleDockWidgetArrangement(); -} - // The adapter object is only added to the pool with a succesful connection, // so we can immediately init our stuff. void InspectorPlugin::objectAdded(QObject *object) diff --git a/src/plugins/qmljsinspector/qmljsinspectorplugin.h b/src/plugins/qmljsinspector/qmljsinspectorplugin.h index 7634643041b..ea4a378c74e 100644 --- a/src/plugins/qmljsinspector/qmljsinspectorplugin.h +++ b/src/plugins/qmljsinspector/qmljsinspectorplugin.h @@ -31,6 +31,7 @@ #include <extensionsystem/iplugin.h> #include <qmljs/qmljsmodelmanagerinterface.h> +#include <debugger/debuggerconstants.h> #include <QtCore/QObject> #include <QtCore/QPointer> @@ -70,9 +71,6 @@ public: virtual void extensionsInitialized(); virtual ExtensionSystem::IPlugin::ShutdownFlag aboutToShutdown(); -public slots: - void setDockWidgetArrangement(const QString &activeLanguage); - private slots: void objectAdded(QObject *object); void aboutToRemoveObject(QObject *obj); diff --git a/src/plugins/qmljsinspector/qmljslivetextpreview.cpp b/src/plugins/qmljsinspector/qmljslivetextpreview.cpp index 3a731d65166..4d88481645e 100644 --- a/src/plugins/qmljsinspector/qmljslivetextpreview.cpp +++ b/src/plugins/qmljsinspector/qmljslivetextpreview.cpp @@ -76,6 +76,8 @@ class MapObjectWithDebugReference : public Visitor QHash<UiObjectMember *, DebugIdList> result; QSet<QmlJS::AST::UiObjectMember *> lookupObjects; Document::Ptr doc; + private: + bool filenamesMatch(const QString &objectFileName, const QString &buildFilename) const; private: int activated; void processRecursive(const QDeclarativeDebugObjectReference &object, UiObjectMember *ast); @@ -117,6 +119,26 @@ void MapObjectWithDebugReference::endVisit(UiObjectBinding* ast) activated--; } +bool MapObjectWithDebugReference::filenamesMatch(const QString &objectFileName, const QString &buildFilename) const +{ + bool isShadowBuild = InspectorUi::instance()->isShadowBuildProject(); + ProjectExplorer::Project *debugProject = InspectorUi::instance()->debugProject(); + + if (!isShadowBuild) { + return (objectFileName == buildFilename); + } else { + QString projectDir = debugProject->projectDirectory(); + QString shadowBuildDir = InspectorUi::instance()->debugProjectBuildDirectory(); + + QFileInfo objectFileInfo(objectFileName); + QFileInfo buildFileInfo(buildFilename); + QString objectRelativePath = objectFileInfo.absoluteFilePath().mid(shadowBuildDir.length()); + QString buildRelativePath = buildFileInfo.absoluteFilePath().mid(projectDir.length()); + + return (objectRelativePath == buildRelativePath); + } +} + void MapObjectWithDebugReference::processRecursive(const QDeclarativeDebugObjectReference& object, UiObjectMember* ast) { // If this is too slow, it can be speed up by indexing @@ -125,7 +147,7 @@ void MapObjectWithDebugReference::processRecursive(const QDeclarativeDebugObject SourceLocation loc = ast->firstSourceLocation(); if (object.source().columnNumber() == int(loc.startColumn)) { QString objectFileName = object.source().url().toLocalFile(); - if (!doc && object.source().lineNumber() == int(loc.startLine) && objectFileName == filename) { + if (!doc && object.source().lineNumber() == int(loc.startLine) && filenamesMatch(objectFileName, filename)) { result[ast] += object.debugId(); } else if (doc && objectFileName.startsWith(filename + QLatin1Char('_') + QString::number(doc->editorRevision()) + QLatin1Char(':'))) { bool ok; @@ -548,7 +570,7 @@ public: void QmlJSLiveTextPreview::documentChanged(QmlJS::Document::Ptr doc) { - if (doc->fileName() != m_previousDoc->fileName() || !m_clientProxy) + if (doc->fileName() != m_previousDoc->fileName() || m_clientProxy.isNull()) return; Core::ICore *core = Core::ICore::instance(); diff --git a/src/plugins/qmljsinspector/qmljsobjecttree.cpp b/src/plugins/qmljsinspector/qmljsobjecttree.cpp new file mode 100644 index 00000000000..8d6befb1e21 --- /dev/null +++ b/src/plugins/qmljsinspector/qmljsobjecttree.cpp @@ -0,0 +1,238 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ +#include "qmljsobjecttree.h" + +#include <QContextMenuEvent> +#include <QEvent> +#include <QtGui/QMenu> +#include <QtGui/QAction> +#include <QApplication> +#include <QInputDialog> + +#include <QDebug> + +namespace QmlJSInspector { +namespace Internal { + +// ************************************************************************* +// ObjectTreeItem +// ************************************************************************* + +class ObjectTreeItem : public QTreeWidgetItem +{ +public: + explicit ObjectTreeItem(QTreeWidget *widget, int type = 0); + ObjectTreeItem(QTreeWidgetItem *parentItem, int type = 0); + QVariant data (int column, int role) const; + void setData (int column, int role, const QVariant & value); + + void setHasValidDebugId(bool value); + + +private: + bool m_hasValidDebugId; +}; + +ObjectTreeItem::ObjectTreeItem(QTreeWidget *widget, int type) : + QTreeWidgetItem(widget, type), m_hasValidDebugId(true) +{ + +} + +ObjectTreeItem::ObjectTreeItem(QTreeWidgetItem *parentItem, int type) : + QTreeWidgetItem(parentItem, type), m_hasValidDebugId(true) +{ + +} + +QVariant ObjectTreeItem::data (int column, int role) const +{ + if (role == Qt::ForegroundRole) + return m_hasValidDebugId ? qApp->palette().color(QPalette::Foreground) : qApp->palette().color(QPalette::Disabled, QPalette::Foreground); + + return QTreeWidgetItem::data(column, role); +} + +void ObjectTreeItem::setData (int column, int role, const QVariant & value) +{ + QTreeWidgetItem::setData(column, role, value); +} + +void ObjectTreeItem::setHasValidDebugId(bool value) +{ + m_hasValidDebugId = value; +} + +// ************************************************************************* +// QmlJSObjectTree +// ************************************************************************* + +QmlJSObjectTree::QmlJSObjectTree(QWidget *parent) + : QTreeWidget(parent) + , m_clickedItem(0) + , m_currentObjectDebugId(0) +{ + setAttribute(Qt::WA_MacShowFocusRect, false); + setFrameStyle(QFrame::NoFrame); + setHeaderHidden(true); + setExpandsOnDoubleClick(false); + + m_goToFileAction = new QAction(tr("Go to file"), this); + connect(m_goToFileAction, SIGNAL(triggered()), SLOT(goToFile())); + + connect(this, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), + SLOT(currentItemChanged(QTreeWidgetItem *))); + connect(this, SIGNAL(itemActivated(QTreeWidgetItem *, int)), + SLOT(activated(QTreeWidgetItem *))); + connect(this, SIGNAL(itemSelectionChanged()), SLOT(selectionChanged())); +} + +void QmlJSObjectTree::selectionChanged() +{ + if (selectedItems().isEmpty()) + return; + + // TODO +} + +void QmlJSObjectTree::setCurrentObject(int debugId) +{ + QTreeWidgetItem *item = findItemByObjectId(debugId); + if (item) { + setCurrentItem(item); + scrollToItem(item); + item->setExpanded(true); + } +} + +void QmlJSObjectTree::currentItemChanged(QTreeWidgetItem *item) +{ + if (!item) + return; + + QDeclarativeDebugObjectReference obj = item->data(0, Qt::UserRole).value<QDeclarativeDebugObjectReference>(); + if (obj.debugId() >= 0) + emit currentObjectChanged(obj); +} + +void QmlJSObjectTree::activated(QTreeWidgetItem *item) +{ + if (!item) + return; + + QDeclarativeDebugObjectReference obj = item->data(0, Qt::UserRole).value<QDeclarativeDebugObjectReference>(); + if (obj.debugId() >= 0) + emit activated(obj); +} + +void QmlJSObjectTree::buildTree(const QDeclarativeDebugObjectReference &obj, QTreeWidgetItem *parent) +{ + if (!parent) + clear(); + + if (obj.contextDebugId() < 0) + return; + + ObjectTreeItem *item = parent ? new ObjectTreeItem(parent) : new ObjectTreeItem(this); + if (obj.idString().isEmpty()) + item->setText(0, QString("<%1>").arg(obj.className())); + else + item->setText(0, obj.idString()); + item->setData(0, Qt::UserRole, qVariantFromValue(obj)); + + if (parent && obj.contextDebugId() >= 0 + && obj.contextDebugId() != parent->data(0, Qt::UserRole + ).value<QDeclarativeDebugObjectReference>().contextDebugId()) + { + + QDeclarativeDebugFileReference source = obj.source(); + if (!source.url().isEmpty()) { + QString toolTipString = tr("Url: ") + source.url().toString(); + item->setToolTip(0, toolTipString); + } + } else { + item->setExpanded(true); + } + + if (obj.contextDebugId() < 0) + item->setHasValidDebugId(false); + + for (int ii = 0; ii < obj.children().count(); ++ii) + buildTree(obj.children().at(ii), item); +} + +QTreeWidgetItem *QmlJSObjectTree::findItemByObjectId(int debugId) const +{ + for (int i=0; i<topLevelItemCount(); ++i) { + QTreeWidgetItem *item = findItem(topLevelItem(i), debugId); + if (item) + return item; + } + + return 0; +} + +QTreeWidgetItem *QmlJSObjectTree::findItem(QTreeWidgetItem *item, int debugId) const +{ + if (item->data(0, Qt::UserRole).value<QDeclarativeDebugObjectReference>().debugId() == debugId) + return item; + + QTreeWidgetItem *child; + for (int i=0; i<item->childCount(); ++i) { + child = findItem(item->child(i), debugId); + if (child) + return child; + } + + return 0; +} + +void QmlJSObjectTree::goToFile() +{ + QDeclarativeDebugObjectReference obj = + currentItem()->data(0, Qt::UserRole).value<QDeclarativeDebugObjectReference>(); + + if (obj.debugId() >= 0) + emit activated(obj); +} + +void QmlJSObjectTree::contextMenuEvent(QContextMenuEvent *event) +{ + m_clickedItem = itemAt(QPoint(event->pos().x(), + event->pos().y() )); + if (!m_clickedItem) + return; + + QMenu menu; + menu.addAction(m_goToFileAction); + menu.exec(event->globalPos()); +} + +} // Internal +} // QmlJSInspector diff --git a/src/plugins/qmljsinspector/qmljsobjecttree.h b/src/plugins/qmljsinspector/qmljsobjecttree.h new file mode 100644 index 00000000000..4f56e0393d7 --- /dev/null +++ b/src/plugins/qmljsinspector/qmljsobjecttree.h @@ -0,0 +1,84 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ +#ifndef OBJECTTREE_H +#define OBJECTTREE_H + +#include <qmljsprivateapi.h> +#include <QtGui/QTreeWidget> + +QT_BEGIN_NAMESPACE + +class QTreeWidgetItem; + +QT_END_NAMESPACE + +namespace QmlJSInspector { +namespace Internal { + +class QmlJSObjectTree : public QTreeWidget +{ + Q_OBJECT +public: + QmlJSObjectTree(QWidget *parent = 0); + +signals: + void currentObjectChanged(const QDeclarativeDebugObjectReference &); + void activated(const QDeclarativeDebugObjectReference &); + void expressionWatchRequested(const QDeclarativeDebugObjectReference &, const QString &); + void contextHelpIdChanged(const QString &contextHelpId); + +public slots: + void reload(int objectDebugId); // set the root object + void setCurrentObject(int debugId); // select an object in the tree + +protected: + virtual void contextMenuEvent(QContextMenuEvent *); + +private slots: + void addWatch(); + void currentItemChanged(QTreeWidgetItem *); + void activated(QTreeWidgetItem *); + void selectionChanged(); + void goToFile(); + +private: + QTreeWidgetItem *findItemByObjectId(int debugId) const; + QTreeWidgetItem *findItem(QTreeWidgetItem *item, int debugId) const; + void buildTree(const QDeclarativeDebugObjectReference &, QTreeWidgetItem *parent); + + QTreeWidgetItem *m_clickedItem; + QAction *m_addWatchAction; + QAction *m_goToFileAction; + int m_currentObjectDebugId; +}; + +} // Internal +} // QmlJSInspector + +#endif diff --git a/src/plugins/qmlprojectmanager/qmlprojectruncontrol.cpp b/src/plugins/qmlprojectmanager/qmlprojectruncontrol.cpp index 8d19e78b5b3..d1a607d25d0 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectruncontrol.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectruncontrol.cpp @@ -85,10 +85,6 @@ void QmlRunControl::start() m_applicationLauncher.start(ProjectExplorer::ApplicationLauncher::Gui, m_executable, m_commandLineArguments); - // FIXME this line should be refactored out in order to remove the dependency between - // debugger and qmlprojectmanager, because debugger also relies on cpptools. - Debugger::DebuggerUISwitcher::instance()->setActiveLanguage(QmlJSInspector::Constants::LANG_QML); - emit started(); emit appendMessage(this, tr("Starting %1 %2").arg(QDir::toNativeSeparators(m_executable), m_commandLineArguments.join(QLatin1String(" "))), false); @@ -148,7 +144,8 @@ bool QmlRunControlFactory::canRun(RunConfiguration *runConfiguration, if (mode == ProjectExplorer::Constants::RUNMODE) { return config != 0; } else if (mode == ProjectExplorer::Constants::DEBUGMODE) { - bool qmlDebugSupportInstalled = Debugger::DebuggerUISwitcher::instance()->supportedLanguages().contains(QmlProjectManager::Constants::LANG_QML); + bool qmlDebugSupportInstalled = Debugger::DebuggerUISwitcher::instance()->supportedLanguages() + & Debugger::Lang_Qml; return (config != 0) && qmlDebugSupportInstalled; } diff --git a/src/tools/qml/qmlobserver/main.cpp b/src/tools/qml/qmlobserver/main.cpp index b57e6093f7d..786c494d5c9 100644 --- a/src/tools/qml/qmlobserver/main.cpp +++ b/src/tools/qml/qmlobserver/main.cpp @@ -409,6 +409,9 @@ int main(int argc, char ** argv) viewer->enableExperimentalGestures(); viewer->setDesignModeBehavior(designModeBehavior); + + // FIXME debug mode is always on for qml observer + debuggerModeBehavior = true; viewer->setDebugMode(debuggerModeBehavior); foreach (QString lib, imports) diff --git a/src/tools/qml/qmlobserver/qmlruntime.cpp b/src/tools/qml/qmlobserver/qmlruntime.cpp index 6fef7bfd801..6e53705f9d4 100644 --- a/src/tools/qml/qmlobserver/qmlruntime.cpp +++ b/src/tools/qml/qmlobserver/qmlruntime.cpp @@ -64,7 +64,6 @@ #include "qdeclarative.h" #include <QAbstractAnimation> #include <private/qabstractanimation_p.h> -#include <private/qdeclarativeengine_p.h> #include <QSettings> #include <QXmlStreamReader> @@ -672,14 +671,9 @@ void QDeclarativeViewer::setDesignModeBehavior(bool value) void QDeclarativeViewer::setDebugMode(bool on) { - Q_UNUSED(on); - //if (on) - { - new JSDebuggerAgent(QDeclarativeEnginePrivate::getScriptEngine(canvas->engine())); - } + canvas->setDebugMode(on); } - void QDeclarativeViewer::enableExperimentalGestures() { canvas->viewport()->grabGesture(Qt::TapGesture,Qt::DontStartGestureOnChildren|Qt::ReceivePartialGestures|Qt::IgnoredGesturesPropagateToParent); -- GitLab