diff --git a/src/plugins/debugger/qml/qmlengine.cpp b/src/plugins/debugger/qml/qmlengine.cpp index 053abf0da40963746f5ea1ad230a3b54f252ca9d..ed417742a9ac974917f341c366706b53e63e3220 100644 --- a/src/plugins/debugger/qml/qmlengine.cpp +++ b/src/plugins/debugger/qml/qmlengine.cpp @@ -161,10 +161,27 @@ QmlEngine::QmlEngine(const DebuggerStartParameters &startParameters) { m_conn = 0; m_client = 0; - m_engineQuery = 0; - m_contextQuery = 0; - + m_engineDebugInterface = 0; m_frameRate = 0; + + /* + m_watchTableModel = new Internal::WatchTableModel(0, this); + + m_objectTreeWidget = new Internal::ObjectTree; + m_propertiesWidget = new Internal::ObjectPropertiesView(m_watchTableModel); + m_watchTableView = new Internal::WatchTableView(m_watchTableModel); + m_expressionWidget = new Internal::ExpressionQueryWidget(Internal::ExpressionQueryWidget::SeparateEntryMode); +// m_frameRateWidget = new Internal::CanvasFrameRate; +// m_frameRateWidget->setObjectName(QLatin1String("QmlDebugFrameRate")); + + connect(Debugger::DebuggerPlugin::instance(), + SIGNAL(stateChanged(int)), this, SLOT(debuggerStateChanged(int))); + + m_editablePropertyTypes = QStringList() << "qreal" << "bool" << "QString" + << "int" << "QVariant" << "QUrl" << "QColor"; + + connect(m_connectionTimer, SIGNAL(timeout()), SLOT(pollInspector())); + */ } QmlEngine::~QmlEngine() @@ -189,6 +206,10 @@ void QmlEngine::executeDebuggerCommand(const QString &command) void QmlEngine::shutdown() { exitDebugger(); + + //m_objectTreeWidget->saveSettings(m_settings); + //m_propertiesWidget->saveSettings(m_settings); + //m_settings.saveSettings(Core::ICore::instance()->settings()); } void QmlEngine::exitDebugger() @@ -254,14 +275,16 @@ void QmlEngine::setupConnection() m_client = new QmlDebuggerClient(m_conn, this); (void) new QmlFrameRateClient(m_conn, this); - //m_objectTreeWidget->setEngineDebug(m_client); - //m_propertiesWidget->setEngineDebug(m_client); - //m_watchTableModel->setEngineDebug(m_client); - //m_expressionWidget->setEngineDebug(m_client); - // resetViews(); - // m_frameRateWidget->reset(m_conn); - // reloadEngines(); + QTC_ASSERT(m_engineDebugInterface == 0, /**/); + m_engineDebugInterface = new QDeclarativeEngineDebug(m_conn, this); + + //m_objectTreeWidget->setEngineDebug(m_engineDebugInterface); + //m_propertiesWidget->setEngineDebug(m_engineDebugInterface); + //m_watchTableModel->setEngineDebug(m_engineDebugInterface); + //m_expressionWidget->setEngineDebug(m_engineDebugInterface); + //resetViews(); + //m_frameRateWidget->reset(m_conn); QHostAddress ha(QHostAddress::LocalHost); @@ -278,6 +301,8 @@ void QmlEngine::setupConnection() qDebug() << "CONNECTION SUCCESSFUL"; setState(InferiorRunning); startSuccessful(); + + reloadEngines(); } void QmlEngine::continueInferior() @@ -462,10 +487,12 @@ void QmlEngine::assignValueInDebugger(const QString &expression, void QmlEngine::updateLocals() { + qDebug() << "UPDATE LOCALS"; } void QmlEngine::updateWatchData(const WatchData &data) { + qDebug() << "UPDATE WATCH DATA" << data.toString(); //watchHandler()->rebuildModel(); showStatusMessage(tr("Stopped."), 5000); @@ -624,6 +651,14 @@ void QmlEngine::messageReceived(const QByteArray &message) watchHandler()->endCycle(); + foreach (QDeclarativeDebugWatch *watch, m_watches) { + qDebug() << "WATCH" + << watch->objectName() + << watch->queryId() + << watch->state() + << watch->objectDebugId(); + } + } else if (command == "RESULT") { WatchData data; QVariant variant; @@ -710,12 +745,6 @@ void QmlEngine::connectionStateChanged() case QAbstractSocket::UnconnectedState: { showStatusMessage(tr("[QmlEngine] disconnected.\n\n")); - - delete m_engineQuery; - m_engineQuery = 0; - delete m_contextQuery; - m_contextQuery = 0; - // resetViews(); // updateMenuActions(); @@ -762,6 +791,950 @@ void QmlEngine::connectionConnected() } +#if 0 +class EngineComboBox : public QComboBox +{ + Q_OBJECT +public: + struct EngineInfo + { + QString name; + int id; + }; + + EngineComboBox(QWidget *parent = 0); + + void addEngine(int engine, const QString &name); + void clearEngines(); + +protected: + +private: + QList<EngineInfo> m_engines; +}; + +EngineComboBox::EngineComboBox(QWidget *parent) + : QComboBox(parent) +{ + setEnabled(false); + setEditable(false); +} + +void EngineComboBox::addEngine(int engine, const QString &name) +{ + EngineInfo info; + info.id = engine; + if (name.isEmpty()) + info.name = tr("Engine %1", "engine number").arg(engine); + else + info.name = name; + m_engines << info; + + addItem(info.name); +} + +void EngineComboBox::clearEngines() +{ + m_engines.clear(); + clear(); +} + +} // Internal + + +bool QmlEngine::setDebugConfigurationDataFromProject(ProjectExplorer::Project *projectToDebug) +{ + if (!projectToDebug) { + emit statusMessage(tr("Invalid project, debugging canceled.")); + return false; + } + + QmlProjectManager::QmlProjectRunConfiguration* config = + qobject_cast<QmlProjectManager::QmlProjectRunConfiguration*>(projectToDebug->activeTarget()->activeRunConfiguration()); + if (!config) { + emit statusMessage(tr("Cannot find project run configuration, debugging canceled.")); + return false; + } + m_runConfigurationDebugData.serverAddress = config->debugServerAddress(); + m_runConfigurationDebugData.serverPort = config->debugServerPort(); + m_connectionTimer->setInterval(ConnectionAttemptDefaultInterval); + + return true; +} + +void QmlEngine::startQmlProjectDebugger() +{ + m_simultaneousCppAndQmlDebugMode = false; + m_connectionTimer->start(); +} + +bool QmlEngine::connectToViewer() +{ + if (m_conn && m_conn->state() != QAbstractSocket::UnconnectedState) + return false; + + delete m_engineDebugInterface; m_engineDebugInterface = 0; + + if (m_conn) { + m_conn->disconnectFromHost(); + delete m_conn; + m_conn = 0; + } + + QString host = m_runConfigurationDebugData.serverAddress; + quint16 port = quint16(m_runConfigurationDebugData.serverPort); + + m_conn = new QDeclarativeDebugConnection(this); + connect(m_conn, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + SLOT(connectionStateChanged())); + connect(m_conn, SIGNAL(error(QAbstractSocket::SocketError)), + SLOT(connectionError())); + + emit statusMessage(tr("[Inspector] set to connect to debug server %1:%2").arg(host).arg(port)); + m_conn->connectToHost(host, port); + // blocks until connected; if no connection is available, will fail immediately + + if (!m_conn->waitForConnected()) + return false; + + QTC_ASSERT(m_debuggerRunControl, return false); + QmlEngine *engine = qobject_cast<QmlEngine *>(m_debuggerRunControl->engine()); + QTC_ASSERT(engine, return false); + + (void) new DebuggerClient(m_conn, engine); + + return true; +} + +void QmlEngine::disconnectFromViewer() +{ + m_conn->disconnectFromHost(); + updateMenuActions(); +} + +void QmlEngine::connectionStateChanged() +{ + switch (m_conn->state()) { + case QAbstractSocket::UnconnectedState: + { + emit statusMessage(tr("[Inspector] disconnected.\n\n")); + resetViews(); + updateMenuActions(); + break; + } + case QAbstractSocket::HostLookupState: + emit statusMessage(tr("[Inspector] resolving host...")); + break; + case QAbstractSocket::ConnectingState: + emit statusMessage(tr("[Inspector] connecting to debug server...")); + break; + case QAbstractSocket::ConnectedState: + { + emit statusMessage(tr("[Inspector] connected.\n")); + + resetViews(); +// m_frameRateWidget->reset(m_conn); + + break; + } + case QAbstractSocket::ClosingState: + emit statusMessage(tr("[Inspector] closing...")); + break; + case QAbstractSocket::BoundState: + case QAbstractSocket::ListeningState: + break; + } +} + +void QmlEngine::resetViews() +{ + m_objectTreeWidget->cleanup(); + m_propertiesWidget->clear(); + m_expressionWidget->clear(); + m_watchTableModel->removeAllWatches(); +} + +void QmlEngine::createDockWidgets() +{ + + m_engineComboBox = new Internal::EngineComboBox; + m_engineComboBox->setEnabled(false); + connect(m_engineComboBox, SIGNAL(currentIndexChanged(int)), + SLOT(queryEngineContext(int))); + + // FancyMainWindow uses widgets' window titles for tab labels +// m_frameRateWidget->setWindowTitle(tr("Frame rate")); + + Utils::StyledBar *treeOptionBar = new Utils::StyledBar; + QHBoxLayout *treeOptionBarLayout = new QHBoxLayout(treeOptionBar); + treeOptionBarLayout->setContentsMargins(5, 0, 5, 0); + treeOptionBarLayout->setSpacing(5); + treeOptionBarLayout->addWidget(new QLabel(tr("QML engine:"))); + treeOptionBarLayout->addWidget(m_engineComboBox); + + QWidget *treeWindow = new QWidget; + treeWindow->setObjectName(QLatin1String("QmlDebugTree")); + treeWindow->setWindowTitle(tr("Object Tree")); + QVBoxLayout *treeWindowLayout = new QVBoxLayout(treeWindow); + treeWindowLayout->setMargin(0); + treeWindowLayout->setSpacing(0); + treeWindowLayout->setContentsMargins(0,0,0,0); + treeWindowLayout->addWidget(treeOptionBar); + treeWindowLayout->addWidget(m_objectTreeWidget); + + + m_watchTableView->setModel(m_watchTableModel); + Internal::WatchTableHeaderView *header = new Internal::WatchTableHeaderView(m_watchTableModel); + m_watchTableView->setHorizontalHeader(header); + + connect(m_objectTreeWidget, SIGNAL(activated(QDeclarativeDebugObjectReference)), + this, SLOT(treeObjectActivated(QDeclarativeDebugObjectReference))); + + connect(m_objectTreeWidget, SIGNAL(currentObjectChanged(QDeclarativeDebugObjectReference)), + m_propertiesWidget, SLOT(reload(QDeclarativeDebugObjectReference))); + + connect(m_objectTreeWidget, SIGNAL(expressionWatchRequested(QDeclarativeDebugObjectReference,QString)), + m_watchTableModel, SLOT(expressionWatchRequested(QDeclarativeDebugObjectReference,QString))); + + connect(m_propertiesWidget, SIGNAL(watchToggleRequested(QDeclarativeDebugObjectReference,QDeclarativeDebugPropertyReference)), + m_watchTableModel, SLOT(togglePropertyWatch(QDeclarativeDebugObjectReference,QDeclarativeDebugPropertyReference))); + + connect(m_watchTableModel, SIGNAL(watchCreated(QDeclarativeDebugWatch*)), + m_propertiesWidget, SLOT(watchCreated(QDeclarativeDebugWatch*))); + + connect(m_watchTableModel, SIGNAL(rowsInserted(QModelIndex,int,int)), + m_watchTableView, SLOT(scrollToBottom())); + + connect(m_watchTableView, SIGNAL(objectActivated(int)), + m_objectTreeWidget, SLOT(setCurrentObject(int))); + + connect(m_objectTreeWidget, SIGNAL(currentObjectChanged(QDeclarativeDebugObjectReference)), + m_expressionWidget, SLOT(setCurrentObject(QDeclarativeDebugObjectReference))); + + + Core::MiniSplitter *propSplitter = new Core::MiniSplitter(Qt::Horizontal); + Core::MiniSplitter *propWatcherSplitter = new Core::MiniSplitter(Qt::Vertical); + propWatcherSplitter->addWidget(m_propertiesWidget); + propWatcherSplitter->addWidget(m_watchTableView); + propWatcherSplitter->setStretchFactor(0, 2); + propWatcherSplitter->setStretchFactor(1, 1); + propWatcherSplitter->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding); + + propSplitter->setWindowTitle(tr("Properties and Watchers")); + propSplitter->setObjectName(QLatin1String("QmlDebugProperties")); + propSplitter->addWidget(m_objectTreeWidget); + propSplitter->addWidget(propWatcherSplitter); + propSplitter->setStretchFactor(0, 1); + propSplitter->setStretchFactor(1, 3); + + InspectorOutputWidget *inspectorOutput = new InspectorOutputWidget(); + inspectorOutput->setObjectName(QLatin1String("QmlDebugInspectorOutput")); + connect(this, SIGNAL(statusMessage(QString)), + inspectorOutput, SLOT(addInspectorStatus(QString))); + + Debugger::DebuggerUISwitcher *uiSwitcher = Debugger::DebuggerUISwitcher::instance(); + + m_watchTableView->hide(); +// m_objectTreeDock = uiSwitcher->createDockWidget(Qml::Constants::LANG_QML, +// treeWindow, Qt::BottomDockWidgetArea); +// m_frameRateDock = uiSwitcher->createDockWidget(Qml::Constants::LANG_QML, +// m_frameRateWidget, Qt::BottomDockWidgetArea); + m_propertyWatcherDock = uiSwitcher->createDockWidget(Qml::Constants::LANG_QML, + propSplitter, Qt::BottomDockWidgetArea); + m_inspectorOutputDock = uiSwitcher->createDockWidget(Qml::Constants::LANG_QML, + inspectorOutput, Qt::BottomDockWidgetArea); + + m_expressionWidget->setWindowTitle(tr("Script Console")); + m_expressionQueryDock = uiSwitcher->createDockWidget(Qml::Constants::LANG_QML, + m_expressionWidget, Qt::BottomDockWidgetArea); + + m_inspectorOutputDock->setToolTip(tr("Output of the QML inspector, such as information on connecting to the server.")); + + m_dockWidgets << /*m_objectTreeDock << *//*m_frameRateDock << */ m_propertyWatcherDock + << m_inspectorOutputDock << m_expressionQueryDock; + + m_context = new Internal::InspectorContext(m_objectTreeWidget); + m_propWatcherContext = new Internal::InspectorContext(m_propertyWatcherDock); + + Core::ICore *core = Core::ICore::instance(); + core->addContextObject(m_propWatcherContext); + core->addContextObject(m_context); + + m_simultaneousDebugAction = new QAction(this); + m_simultaneousDebugAction->setText(tr("Start Debugging C++ and QML Simultaneously...")); + connect(m_simultaneousDebugAction, SIGNAL(triggered()), + this, SLOT(simultaneouslyDebugQmlCppApplication())); + + Core::ActionManager *am = core->actionManager(); + Core::ActionContainer *mstart = am->actionContainer(ProjectExplorer::Constants::M_DEBUG_STARTDEBUGGING); + Core::Command *cmd = am->registerAction(m_simultaneousDebugAction, Constants::M_DEBUG_SIMULTANEOUSLY, + m_context->context()); + cmd->setAttribute(Core::Command::CA_Hide); + mstart->addAction(cmd, Core::Constants::G_DEFAULT_ONE); + + m_settings.readSettings(core->settings()); + m_objectTreeWidget->readSettings(m_settings); + m_propertiesWidget->readSettings(m_settings); + + connect(m_objectTreeWidget, SIGNAL(contextHelpIdChanged(QString)), m_context, + SLOT(setContextHelpId(QString))); + connect(m_watchTableView, SIGNAL(contextHelpIdChanged(QString)), m_propWatcherContext, + SLOT(setContextHelpId(QString))); + connect(m_propertiesWidget, SIGNAL(contextHelpIdChanged(QString)), m_propWatcherContext, + SLOT(setContextHelpId(QString))); + connect(m_expressionWidget, SIGNAL(contextHelpIdChanged(QString)), m_propWatcherContext, + SLOT(setContextHelpId(QString))); +} + +void QmlEngine::simultaneouslyDebugQmlCppApplication() +{ + QString errorMessage; + ProjectExplorer::ProjectExplorerPlugin *pex = ProjectExplorer::ProjectExplorerPlugin::instance(); + ProjectExplorer::Project *project = pex->startupProject(); + + if (!project) + errorMessage = QString(tr("No project was found.")); + else { + if (project->id() == "QmlProjectManager.QmlProject") + errorMessage = attachToQmlViewerAsExternalApp(project); + else { + errorMessage = attachToExternalCppAppWithQml(project); + } + } + + if (!errorMessage.isEmpty()) + QMessageBox::warning(Core::ICore::instance()->mainWindow(), "Failed to debug C++ and QML", errorMessage); +} + +QString QmlEngine::attachToQmlViewerAsExternalApp(ProjectExplorer::Project *project) +{ + m_debugMode = QmlProjectWithCppPlugins; + + QmlProjectManager::QmlProjectRunConfiguration* runConfig = + qobject_cast<QmlProjectManager::QmlProjectRunConfiguration*>(project->activeTarget()->activeRunConfiguration()); + + if (!runConfig) + return QString(tr("No run configurations were found for the project '%1'.").arg(project->displayName())); + + Internal::StartExternalQmlDialog dlg(Debugger::DebuggerUISwitcher::instance()->mainWindow()); + + QString importPathArgument = "-I"; + QString execArgs; + if (runConfig->viewerArguments().contains(importPathArgument)) + execArgs = runConfig->viewerArguments().join(" "); + else { + QFileInfo qmlFileInfo(runConfig->viewerArguments().last()); + importPathArgument.append(" " + qmlFileInfo.absolutePath() + " "); + execArgs = importPathArgument + runConfig->viewerArguments().join(" "); + } + + + dlg.setPort(runConfig->debugServerPort()); + dlg.setDebuggerUrl(runConfig->debugServerAddress()); + dlg.setProjectDisplayName(project->displayName()); + dlg.setDebugMode(Internal::StartExternalQmlDialog::QmlProjectWithCppPlugins); + dlg.setQmlViewerArguments(execArgs); + dlg.setQmlViewerPath(runConfig->viewerPath()); + + if (dlg.exec() != QDialog::Accepted) + return QString(); + + m_runConfigurationDebugData.serverAddress = dlg.debuggerUrl(); + m_runConfigurationDebugData.serverPort = dlg.port(); + m_settings.setExternalPort(dlg.port()); + m_settings.setExternalUrl(dlg.debuggerUrl()); + + ProjectExplorer::Environment customEnv = ProjectExplorer::Environment::systemEnvironment(); // empty env by default + customEnv.set(QmlProjectManager::Constants::E_QML_DEBUG_SERVER_PORT, QString::number(m_settings.externalPort())); + + Debugger::DebuggerRunControl *debuggableRunControl = + createDebuggerRunControl(runConfig, dlg.qmlViewerPath(), dlg.qmlViewerArguments()); + + return executeDebuggerRunControl(debuggableRunControl, &customEnv); +} + +QString QmlEngine::attachToExternalCppAppWithQml(ProjectExplorer::Project *project) +{ + m_debugMode = CppProjectWithQmlEngines; + + ProjectExplorer::LocalApplicationRunConfiguration* runConfig = + qobject_cast<ProjectExplorer::LocalApplicationRunConfiguration*>(project->activeTarget()->activeRunConfiguration()); + + if (!project->activeTarget() || !project->activeTarget()->activeRunConfiguration()) + return QString(tr("No run configurations were found for the project '%1'.").arg(project->displayName())); + else if (!runConfig) + return QString(tr("No valid run configuration was found for the project %1. " + "Only locally runnable configurations are supported.\n" + "Please check your project settings.").arg(project->displayName())); + + Internal::StartExternalQmlDialog dlg(Debugger::DebuggerUISwitcher::instance()->mainWindow()); + + dlg.setPort(m_settings.externalPort()); + dlg.setDebuggerUrl(m_settings.externalUrl()); + dlg.setProjectDisplayName(project->displayName()); + dlg.setDebugMode(Internal::StartExternalQmlDialog::CppProjectWithQmlEngine); + if (dlg.exec() != QDialog::Accepted) + return QString(); + + m_runConfigurationDebugData.serverAddress = dlg.debuggerUrl(); + m_runConfigurationDebugData.serverPort = dlg.port(); + m_settings.setExternalPort(dlg.port()); + m_settings.setExternalUrl(dlg.debuggerUrl()); + + ProjectExplorer::Environment customEnv = runConfig->environment(); + customEnv.set(QmlProjectManager::Constants::E_QML_DEBUG_SERVER_PORT, QString::number(m_settings.externalPort())); + Debugger::DebuggerRunControl *debuggableRunControl = createDebuggerRunControl(runConfig); + return executeDebuggerRunControl(debuggableRunControl, &customEnv); +} + +QString QmlEngine::executeDebuggerRunControl(Debugger::DebuggerRunControl *debuggableRunControl, ProjectExplorer::Environment *environment) +{ + ProjectExplorer::ProjectExplorerPlugin *pex = ProjectExplorer::ProjectExplorerPlugin::instance(); + + // to make sure we have a valid, debuggable run control, find the correct factory for it + if (debuggableRunControl) { + + // modify the env + debuggableRunControl->setCustomEnvironment(*environment); + + pex->startRunControl(debuggableRunControl, ProjectExplorer::Constants::DEBUGMODE); + m_simultaneousCppAndQmlDebugMode = true; + + return QString(); + } + return QString(tr("A valid run control was not registered in Qt Creator for this project run configuration."));; +} + +Debugger::DebuggerRunControl *QmlEngine::createDebuggerRunControl(ProjectExplorer::RunConfiguration *runConfig, + const QString &executableFile, const QString &executableArguments) +{ + ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance(); + const QList<Debugger::DebuggerRunControlFactory *> factories = pm->getObjects<Debugger::DebuggerRunControlFactory>(); + ProjectExplorer::RunControl *runControl = 0; + + if (m_debugMode == QmlProjectWithCppPlugins) { + Debugger::DebuggerStartParameters sp; + sp.startMode = Debugger::StartExternal; + sp.executable = executableFile; + sp.processArgs = executableArguments.split(QLatin1Char(' ')); + runControl = factories.first()->create(sp); + return qobject_cast<Debugger::DebuggerRunControl *>(runControl); + } + + if (m_debugMode == CppProjectWithQmlEngines) { + if (factories.length() && factories.first()->canRun(runConfig, ProjectExplorer::Constants::DEBUGMODE)) { + runControl = factories.first()->create(runConfig, ProjectExplorer::Constants::DEBUGMODE); + return qobject_cast<Debugger::DebuggerRunControl *>(runControl); + } + } + + return 0; +} + +void QmlEngine::updateMenuActions() +{ + + bool enabled = true; + if (m_simultaneousCppAndQmlDebugMode) + enabled = (m_cppDebuggerState == Debugger::DebuggerNotReady && (!m_conn || m_conn->state() == QAbstractSocket::UnconnectedState)); + else + enabled = (!m_conn || m_conn->state() == QAbstractSocket::UnconnectedState); + + m_simultaneousDebugAction->setEnabled(enabled); +} + + +void QmlEngine::debuggerStateChanged(int newState) +{ + if (m_simultaneousCppAndQmlDebugMode) { + + switch(newState) { + case Debugger::EngineStarting: + { + m_connectionInitialized = false; + break; + } + case Debugger::AdapterStartFailed: + case Debugger::InferiorStartFailed: + emit statusMessage(QString(tr("Debugging failed: could not start C++ debugger."))); + break; + case Debugger::InferiorRunningRequested: + { + if (m_cppDebuggerState == Debugger::InferiorStopped) { + // re-enable UI again + m_objectTreeWidget->setEnabled(true); + m_propertiesWidget->setEnabled(true); + m_expressionWidget->setEnabled(true); + } + break; + } + case Debugger::InferiorRunning: + { + if (!m_connectionInitialized) { + m_connectionInitialized = true; + m_connectionTimer->setInterval(ConnectionAttemptSimultaneousInterval); + m_connectionTimer->start(); + } + break; + } + case Debugger::InferiorStopped: + { + m_objectTreeWidget->setEnabled(false); + m_propertiesWidget->setEnabled(false); + m_expressionWidget->setEnabled(false); + break; + } + case Debugger::EngineShuttingDown: + { + m_connectionInitialized = false; + // here it's safe to enable the debugger windows again - + // disabled ones look ugly. + m_objectTreeWidget->setEnabled(true); + m_propertiesWidget->setEnabled(true); + m_expressionWidget->setEnabled(true); + m_simultaneousCppAndQmlDebugMode = false; + break; + } + default: + break; + } + } + + m_cppDebuggerState = newState; + updateMenuActions(); +} + + +void QmlEngine::setSimpleDockWidgetArrangement() +{ + Utils::FancyMainWindow *mainWindow = Debugger::DebuggerUISwitcher::instance()->mainWindow(); + + mainWindow->setTrackingEnabled(false); + QList<QDockWidget *> dockWidgets = mainWindow->dockWidgets(); + foreach (QDockWidget *dockWidget, dockWidgets) { + if (m_dockWidgets.contains(dockWidget)) { + dockWidget->setFloating(false); + mainWindow->removeDockWidget(dockWidget); + } + } + + foreach (QDockWidget *dockWidget, dockWidgets) { + if (m_dockWidgets.contains(dockWidget)) { + mainWindow->addDockWidget(Qt::BottomDockWidgetArea, dockWidget); + dockWidget->show(); + } + } + mainWindow->splitDockWidget(mainWindow->toolBarDockWidget(), m_propertyWatcherDock, Qt::Vertical); + //mainWindow->tabifyDockWidget(m_frameRateDock, m_propertyWatcherDock); + mainWindow->tabifyDockWidget(m_propertyWatcherDock, m_expressionQueryDock); + mainWindow->tabifyDockWidget(m_propertyWatcherDock, m_inspectorOutputDock); + m_propertyWatcherDock->raise(); + + m_inspectorOutputDock->setVisible(false); + + mainWindow->setTrackingEnabled(true); +} +#endif + +void QmlEngine::reloadEngines() +{ + //m_engineComboBox->setEnabled(false); + + QDeclarativeDebugEnginesQuery *query = + m_engineDebugInterface->queryAvailableEngines(this); + if (!query->isWaiting()) + enginesChanged(query); + else + QObject::connect(query, SIGNAL(stateChanged(QDeclarativeDebugQuery::State)), + this, SLOT(enginesChanged())); +} + +void QmlEngine::enginesChanged() +{ + enginesChanged(qobject_cast<QDeclarativeDebugEnginesQuery *>(sender())); +} + +void QmlEngine::enginesChanged(QDeclarativeDebugEnginesQuery *query) +{ + //m_engineComboBox->clearEngines(); + QList<QDeclarativeDebugEngineReference> engines = query->engines(); + if (engines.isEmpty()) + qWarning("qmldebugger: no engines found!"); + + //m_engineComboBox->setEnabled(true); + + for (int i = 0; i < engines.count(); ++i) + qDebug() << "ENGINE: " << engines.at(i).debugId() << engines.at(i).name(); + // m_engineComboBox->addEngine(engines.at(i).debugId(), engines.at(i).name()); + + if (engines.count() > 0) { + // m_engineComboBox->setCurrentIndex(engines.at(0).debugId()); + queryEngineContext(engines.at(0)); + } +} + +void QmlEngine::queryEngineContext(const QDeclarativeDebugEngineReference &engine) +{ + QDeclarativeDebugRootContextQuery *query = + m_engineDebugInterface->queryRootContexts(engine, this); + + if (!query->isWaiting()) + contextChanged(); + else + QObject::connect(query, SIGNAL(stateChanged(QDeclarativeDebugQuery::State)), + this, SLOT(contextChanged())); +} + +void QmlEngine::contextChanged() +{ + contextChanged(qobject_cast<QDeclarativeDebugRootContextQuery *>(sender())); +} + +void QmlEngine::contextChanged(QDeclarativeDebugRootContextQuery *query) +{ + QTC_ASSERT(query, return); + //dump(query->rootContext(), 0); + foreach (const QDeclarativeDebugObjectReference &object, query->rootContext().objects()) + reloadObject(object); +} + +void QmlEngine::reloadObject(const QDeclarativeDebugObjectReference &object) +{ + qDebug() << "RELOAD OBJECT: " << object.debugId() << object.idString() + << object.className(); + QDeclarativeDebugObjectQuery *query = + m_engineDebugInterface->queryObjectRecursive(object, this); + if (!query->isWaiting()) + objectFetched(query, QDeclarativeDebugQuery::Completed); + else + QObject::connect(query, SIGNAL(stateChanged(QDeclarativeDebugQuery::State)), + this, SLOT(objectFetched(QDeclarativeDebugQuery::State))); +} + +void QmlEngine::objectFetched(QDeclarativeDebugQuery::State state) +{ + objectFetched(qobject_cast<QDeclarativeDebugObjectQuery *>(sender()), state); +} + +void QmlEngine::objectFetched(QDeclarativeDebugObjectQuery *query, + QDeclarativeDebugQuery::State state) +{ + QTC_ASSERT(query, return); + QTC_ASSERT(state == QDeclarativeDebugQuery::Completed, return); + //dump(m_query->object(), 0); + + m_watches.clear(); + buildTree(query->object(), "local"); + + qDebug() << "WATCHES CREATED: " << m_watches.size(); + //watchHandler()->beginCycle(); + //watchHandler()->insertBulkData(list); + //watchHandler()->endCycle(); + //setCurrentItem(topLevelItem(0)); + + // this ugly hack is needed if user wants to see internal structs + // on startup - debugger does not load them until towards the end, + // so essentially loading twice gives us the full list as everything + // is already loaded. + //if (m_showUninspectableItems && !m_showUninspectableOnInitDone) { + // m_showUninspectableOnInitDone = true; + // reloadObject(m_currentObjectDebugId); + //} +} + +void QmlEngine::buildTree(const QDeclarativeDebugObjectReference &obj, + const QByteArray &iname) +{ + //QTC_ASSERT(obj.contextDebugId() >= 0, return); + WatchData data; + data.iname = iname; + + if (obj.idString().isEmpty()) + data.name = QString("<%1>").arg(obj.className()); + else + data.name = obj.idString(); + + data.value = "?"; + data.type = "?"; + data.setHasChildren(!obj.children().isEmpty()); + data.setAllUnneeded(); + qDebug() << "CREATED ITEM " << data.iname << data.name; + m_watches.append(m_engineDebugInterface->addWatch(obj, data.name, 0)); + //QDeclarativeDebugPropertyWatch *QDeclarativeEngineDebug::addWatch(const QDeclarativeDebugPropertyReference &property, QObject *parent) + + //data.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 = QLatin1String("URL: ") + source.url().toString(); + item->setToolTip(0, toolTipString); + } + + } else { + item->setExpanded(true); + } + + if (obj.contextDebugId() < 0) + item->setHasValidDebugId(false); +*/ + + for (int i = 0; i < obj.children().size(); ++i) + buildTree(obj.children().at(i), iname + '.' + QByteArray::number(i)); +} + +#if 0 +void QmlEngine::treeObjectActivated(const QDeclarativeDebugObjectReference &obj) +{ + QDeclarativeDebugFileReference source = obj.source(); + QString fileName = source.url().toLocalFile(); + + if (source.lineNumber() < 0 || !QFile::exists(fileName)) + return; + + Core::EditorManager *editorManager = Core::EditorManager::instance(); + Core::IEditor *editor = editorManager->openEditor(fileName, QString(), Core::EditorManager::NoModeSwitch); + TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor*>(editor); + + if (textEditor) { + editorManager->addCurrentPositionToNavigationHistory(); + textEditor->gotoLine(source.lineNumber()); + textEditor->widget()->setFocus(); + } +} + +bool QmlEngine::canEditProperty(const QString &propertyType) +{ + return m_editablePropertyTypes.contains(propertyType); +} + +QDeclarativeDebugExpressionQuery *QmlEngine::executeExpression(int objectDebugId, const QString &objectId, + const QString &propertyName, const QVariant &value) +{ + //qDebug() << entity.property << entity.title << entity.objectId; + if (objectId.length()) { + + QString quoteWrappedValue = value.toString(); + if (addQuotesForData(value)) + quoteWrappedValue = QString("'%1'").arg(quoteWrappedValue); + + QString constructedExpression = objectId + "." + propertyName + "=" + quoteWrappedValue; + //qDebug() << "EXPRESSION:" << constructedExpression; + return m_client->queryExpressionResult(objectDebugId, constructedExpression, this); + } + + return 0; +} + +bool QmlEngine::addQuotesForData(const QVariant &value) const +{ + switch (value.type()) { + case QVariant::String: + case QVariant::Color: + case QVariant::Date: + return true; + default: + break; + } + + return false; +} + +ObjectTree::ObjectTree(QDeclarativeEngineDebug *client, QWidget *parent) + : QTreeWidget(parent), + m_client(client), + m_query(0), m_clickedItem(0), m_showUninspectableItems(false), + m_currentObjectDebugId(0), m_showUninspectableOnInitDone(false) +{ + setAttribute(Qt::WA_MacShowFocusRect, false); + setFrameStyle(QFrame::NoFrame); + setHeaderHidden(true); + setExpandsOnDoubleClick(false); + + m_addWatchAction = new QAction(tr("Add watch expression..."), this); + m_toggleUninspectableItemsAction = new QAction(tr("Show uninspectable items"), this); + m_toggleUninspectableItemsAction->setCheckable(true); + m_goToFileAction = new QAction(tr("Go to file"), this); + connect(m_toggleUninspectableItemsAction, SIGNAL(triggered()), SLOT(toggleUninspectableItems())); + connect(m_addWatchAction, SIGNAL(triggered()), SLOT(addWatch())); + 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 ObjectTree::readSettings(const InspectorSettings &settings) +{ + if (settings.showUninspectableItems() != m_showUninspectableItems) + toggleUninspectableItems(); +} +void ObjectTree::saveSettings(InspectorSettings &settings) +{ + settings.setShowUninspectableItems(m_showUninspectableItems); +} + +void ObjectTree::setEngineDebug(QDeclarativeEngineDebug *client) +{ + m_client = client; +} + +void ObjectTree::toggleUninspectableItems() +{ + m_showUninspectableItems = !m_showUninspectableItems; + m_toggleUninspectableItemsAction->setChecked(m_showUninspectableItems); + reload(m_currentObjectDebugId); +} + +void ObjectTree::selectionChanged() +{ + if (selectedItems().isEmpty()) + return; + + QTreeWidgetItem *item = selectedItems().first(); + if (item) + emit contextHelpIdChanged(InspectorContext::contextHelpIdForItem(item->text(0))); +} + + +void ObjectTree::setCurrentObject(int debugId) +{ + QTreeWidgetItem *item = findItemByObjectId(debugId); + if (item) { + setCurrentItem(item); + scrollToItem(item); + item->setExpanded(true); + } + + +} + +{ + if (!item) + return; + + QDeclarativeDebugObjectReference obj = item->data(0, Qt::UserRole).value<QDeclarativeDebugObjectReference>(); + if (obj.debugId() >= 0) + emit currentObjectChanged(obj); +} + +void ObjectTree::activated(QTreeWidgetItem *item) +{ + if (!item) + return; + + QDeclarativeDebugObjectReference obj = item->data(0, Qt::UserRole).value<QDeclarativeDebugObjectReference>(); + if (obj.debugId() >= 0) + emit activated(obj); +} + +void ObjectTree::cleanup() +{ + m_showUninspectableOnInitDone = false; + clear(); +} + +void ObjectTree::dump(const QDeclarativeDebugContextReference &ctxt, int ind) +{ + QByteArray indent(ind * 4, ' '); + qWarning().nospace() << indent.constData() << ctxt.debugId() << " " + << qPrintable(ctxt.name()); + + for (int ii = 0; ii < ctxt.contexts().count(); ++ii) + dump(ctxt.contexts().at(ii), ind + 1); + + for (int ii = 0; ii < ctxt.objects().count(); ++ii) + dump(ctxt.objects().at(ii), ind); +} + +void ObjectTree::dump(const QDeclarativeDebugObjectReference &obj, int ind) +{ + QByteArray indent(ind * 4, ' '); + qWarning().nospace() << indent.constData() << qPrintable(obj.className()) + << " " << qPrintable(obj.idString()) << " " + << obj.debugId(); + + for (int ii = 0; ii < obj.children().count(); ++ii) + dump(obj.children().at(ii), ind + 1); +} + +QTreeWidgetItem *ObjectTree::findItemByObjectId(int debugId) const +{ + for (int i=0; i<topLevelItemCount(); ++i) { + QTreeWidgetItem *item = findItem(topLevelItem(i), debugId); + if (item) + return item; + } + + return 0; +} + +QTreeWidgetItem *ObjectTree::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 ObjectTree::addWatch() +{ + QDeclarativeDebugObjectReference obj = + currentItem()->data(0, Qt::UserRole).value<QDeclarativeDebugObjectReference>(); + + bool ok = false; + QString watch = QInputDialog::getText(this, tr("Watch expression"), + tr("Expression:"), QLineEdit::Normal, QString(), &ok); + if (ok && !watch.isEmpty()) + emit expressionWatchRequested(obj, watch); + +} + +void ObjectTree::goToFile() +{ + QDeclarativeDebugObjectReference obj = + currentItem()->data(0, Qt::UserRole).value<QDeclarativeDebugObjectReference>(); + + if (obj.debugId() >= 0) + emit activated(obj); +} + +void ObjectTree::contextMenuEvent(QContextMenuEvent *event) +{ + + m_clickedItem = itemAt(QPoint(event->pos().x(), + event->pos().y() )); + if (!m_clickedItem) + return; + + QMenu menu; + menu.addAction(m_addWatchAction); + menu.addAction(m_goToFileAction); + if (m_currentObjectDebugId) { + menu.addSeparator(); + menu.addAction(m_toggleUninspectableItemsAction); + } + + menu.exec(event->globalPos()); +} + +} // Internal +} // Qml +#endif + } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/qml/qmlengine.h b/src/plugins/debugger/qml/qmlengine.h index 1b6976bbdd49e90cb5715f21bfe02ac792f88838..8e53808852bedcf2019e8772b8aec7002af355cd 100644 --- a/src/plugins/debugger/qml/qmlengine.h +++ b/src/plugins/debugger/qml/qmlengine.h @@ -32,6 +32,10 @@ #include "debuggerengine.h" +#include "private/qdeclarativedebug_p.h" +#include "private/qdeclarativedebugclient_p.h" +#include "private/qdeclarativeenginedebug_p.h" + #include <QtCore/QByteArray> #include <QtCore/QHash> #include <QtCore/QObject> @@ -44,6 +48,7 @@ #include <QtNetwork/QAbstractSocket> #include <QtNetwork/QTcpSocket> + QT_BEGIN_NAMESPACE class QTcpSocket; class QDeclarativeDebugConnection; @@ -74,6 +79,7 @@ public: private: // DebuggerEngine implementation + bool isSynchroneous() const { return true; } void executeStep(); void executeStepOut(); void executeNext(); @@ -130,15 +136,93 @@ private slots: void connectionConnected(); void connectionStateChanged(); + void reloadEngines(); + void enginesChanged(); + void queryEngineContext(const QDeclarativeDebugEngineReference& engine); + void contextChanged(); + + void reloadObject(const QDeclarativeDebugObjectReference &object); + void objectFetched(QDeclarativeDebugQuery::State state); + private: + void objectFetched(QDeclarativeDebugObjectQuery *query, QDeclarativeDebugQuery::State state); + void contextChanged(QDeclarativeDebugRootContextQuery *query); + void enginesChanged(QDeclarativeDebugEnginesQuery *query); + + void buildTree(const QDeclarativeDebugObjectReference &obj, const QByteArray &iname); + QString errorMessage(QProcess::ProcessError error); QProcess m_proc; QDeclarativeDebugConnection *m_conn; + QDeclarativeEngineDebug *m_engineDebugInterface; QmlDebuggerClient *m_client; + CanvasFrameRate *m_frameRate; + + enum DebugMode { + StandaloneMode, + CppProjectWithQmlEngines, + QmlProjectWithCppPlugins + }; + + QList<QDeclarativeDebugWatch *> m_watches; + +#if 0 + void createDockWidgets(); + bool connectToViewer(); // using host, port from widgets + + // returns false if project is not debuggable. + bool setDebugConfigurationDataFromProject(ProjectExplorer::Project *projectToDebug); + void startQmlProjectDebugger(); + + bool canEditProperty(const QString &propertyType); + QDeclarativeDebugExpressionQuery *executeExpression(int objectDebugId, + const QString &objectId, const QString &propertyName, const QVariant &value); + +public slots: + void disconnectFromViewer(); + void setSimpleDockWidgetArrangement(); + +private slots: + void treeObjectActivated(const QDeclarativeDebugObjectReference &obj); + void simultaneouslyDebugQmlCppApplication(); + +private: + void updateMenuActions(); + QString attachToQmlViewerAsExternalApp(ProjectExplorer::Project *project); + QString attachToExternalCppAppWithQml(ProjectExplorer::Project *project); + + bool addQuotesForData(const QVariant &value) const; + void resetViews(); + QDeclarativeDebugEnginesQuery *m_engineQuery; QDeclarativeDebugRootContextQuery *m_contextQuery; - CanvasFrameRate *m_frameRate; + + Internal::ObjectTree *m_objectTreeWidget; + Internal::ObjectPropertiesView *m_propertiesWidget; + Internal::WatchTableModel *m_watchTableModel; + Internal::WatchTableView *m_watchTableView; + Internal::CanvasFrameRate *m_frameRateWidget; + Internal::ExpressionQueryWidget *m_expressionWidget; + + Internal::EngineComboBox *m_engineComboBox; + + QDockWidget *m_objectTreeDock; + QDockWidget *m_frameRateDock; + QDockWidget *m_expressionQueryDock; + QDockWidget *m_propertyWatcherDock; + QDockWidget *m_inspectorOutputDock; + + Internal::InspectorSettings m_settings; + QmlProjectManager::QmlProjectRunConfigurationDebugData m_runConfigurationDebugData; + + QStringList m_editablePropertyTypes; + + // simultaneous debug mode stuff + int m_cppDebuggerState; + bool m_connectionInitialized; + bool m_simultaneousCppAndQmlDebugMode; +#endif }; } // namespace Internal