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
This commit is contained in:
Lasse Holmstedt
2010-08-18 13:54:12 +02:00
parent ef11c4e7ac
commit b557c58eac
35 changed files with 2227 additions and 432 deletions

View File

@@ -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();
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);
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();
}
}
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);