1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** Licensees holding valid Qt Commercial licenses may use this file in
12 ** accordance with the Qt Commercial License Agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and Nokia.
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** If you are unsure which license is appropriate for your use, please
26 ** contact the sales department at http://qt.nokia.com/contact.
28 **************************************************************************/
30 #include "qmlengine.h"
32 #include "debuggerconstants.h"
33 #include "debuggerplugin.h"
34 #include "debuggerdialogs.h"
35 #include "debuggerstringutils.h"
37 #include "breakhandler.h"
38 #include "moduleshandler.h"
39 #include "registerhandler.h"
40 #include "stackhandler.h"
41 #include "watchhandler.h"
42 #include "watchutils.h"
44 #include "canvasframerate.h"
46 #include <projectexplorer/environment.h>
48 #include <utils/qtcassert.h>
50 #include <QtCore/QDateTime>
51 #include <QtCore/QDebug>
52 #include <QtCore/QDir>
53 #include <QtCore/QFileInfo>
54 #include <QtCore/QTimer>
56 #include <QtGui/QAction>
57 #include <QtGui/QApplication>
58 #include <QtGui/QMainWindow>
59 #include <QtGui/QMessageBox>
60 #include <QtGui/QToolTip>
62 #include <QtNetwork/QTcpSocket>
63 #include <QtNetwork/QHostAddress>
65 #include <private/qdeclarativedebug_p.h>
66 #include <private/qdeclarativedebugclient_p.h>
70 # define SDEBUG(s) qDebug() << s
74 # define XSDEBUG(s) qDebug() << s
76 #define CB(callback) &QmlEngine::callback, STRINGIFY(callback)
78 //#define USE_CONGESTION_CONTROL
84 QDataStream& operator>>(QDataStream& s, WatchData &data)
90 s >> data.exp >> data.name >> value >> type >> hasChildren >> data.objectId;
91 data.setType(type, false);
93 data.setHasChildren(hasChildren);
94 data.setAllUnneeded();
102 QmlResponse(const QByteArray &data_) : data(data_) {}
104 QString toString() const { return data; }
109 ///////////////////////////////////////////////////////////////////////
113 ///////////////////////////////////////////////////////////////////////
115 #if 0 //QmlJSInspector does that for us now.
116 class QmlDebuggerClient : public QDeclarativeDebugClient
121 QmlDebuggerClient(QDeclarativeDebugConnection *connection, QmlEngine *engine)
122 : QDeclarativeDebugClient(QLatin1String("QDeclarativeEngine"), connection)
123 , m_connection(connection), m_engine(engine)
128 void sendMessage(const QByteArray &msg)
130 QTC_kASSERT(isConnected(), /**/);
131 qDebug() << "SENDING: " << quoteUnprintableLatin1(msg);
132 QDeclarativeDebugClient::sendMessage(msg);
135 void messageReceived(const QByteArray &data)
137 m_engine->messageReceived(data);
141 QDeclarativeDebugConnection *m_connection;
146 class QmlFrameRateClient : public QDeclarativeDebugClient
151 QmlFrameRateClient(QDeclarativeDebugConnection *connection, QmlEngine *engine)
152 : QDeclarativeDebugClient(QLatin1String("CanvasFrameRate"), connection)
153 , m_connection(connection), m_engine(engine)
158 void messageReceived(const QByteArray &data)
162 //qDebug() << "CANVAS FRAME RATE: " << data.size();
163 //m_engine->messageReceived(data);
167 QDeclarativeDebugConnection *m_connection;
172 ///////////////////////////////////////////////////////////////////////
176 ///////////////////////////////////////////////////////////////////////
178 QmlEngine::QmlEngine(const DebuggerStartParameters &startParameters)
179 : DebuggerEngine(startParameters)
184 m_engineDebugInterface = 0;
187 m_watchTableModel = new Internal::WatchTableModel(0, this);
189 m_objectTreeWidget = new Internal::ObjectTree;
190 m_propertiesWidget = new Internal::ObjectPropertiesView(m_watchTableModel);
191 m_watchTableView = new Internal::WatchTableView(m_watchTableModel);
192 m_expressionWidget = new Internal::ExpressionQueryWidget(Internal::ExpressionQueryWidget::SeparateEntryMode);
193 // m_frameRateWidget = new Internal::CanvasFrameRate;
194 // m_frameRateWidget->setObjectName(QLatin1String("QmlDebugFrameRate"));
196 connect(Debugger::DebuggerPlugin::instance(),
197 SIGNAL(stateChanged(int)), this, SLOT(debuggerStateChanged(int)));
199 m_editablePropertyTypes = QStringList() << "qreal" << "bool" << "QString"
200 << "int" << "QVariant" << "QUrl" << "QColor";
202 connect(m_connectionTimer, SIGNAL(timeout()), SLOT(pollInspector()));
206 QmlEngine::~QmlEngine()
210 void QmlEngine::executeDebuggerCommand(const QString &command)
212 QByteArray cmd = command.toUtf8();
213 cmd = cmd.mid(cmd.indexOf(' ') + 1);
216 // FIXME: works for single-digit escapes only
217 cmd.replace("\\0", null);
218 cmd.replace("\\1", "\1");
219 cmd.replace("\\3", "\3");
222 //enqueueCommand(tcf);
225 void QmlEngine::setupInferior()
227 QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
228 attemptBreakpointSynchronization();
229 notifyInferiorSetupOk();
232 void QmlEngine::runEngine()
234 QTC_ASSERT(state() == EngineRunRequested, qDebug() << state());
235 notifyEngineRunAndInferiorRunOk();
238 void QmlEngine::shutdownInferior()
240 QTC_ASSERT(state() == InferiorShutdownRequested, qDebug() << state());
241 notifyInferiorShutdownOk();
244 void QmlEngine::shutdownEngine()
246 QTC_ASSERT(state() == EngineShutdownRequested, qDebug() << state());
247 //m_objectTreeWidget->saveSettings(m_settings);
248 //m_propertiesWidget->saveSettings(m_settings);
249 //m_settings.saveSettings(Core::ICore::instance()->settings());
252 const int serverPort = 3768;
254 void QmlEngine::setupEngine()
257 QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state());
258 const DebuggerStartParameters &sp = startParameters();
259 const int pos = sp.remoteChannel.indexOf(QLatin1Char(':'));
260 const QString host = sp.remoteChannel.left(pos);
261 const quint16 port = sp.remoteChannel.mid(pos + 1).toInt();
262 qDebug() << "STARTING QML ENGINE" << host << port << sp.remoteChannel
263 << sp.executable << sp.processArgs << sp.workingDirectory;
265 ProjectExplorer::Environment env = ProjectExplorer::Environment::systemEnvironment(); // empty env by default
266 env.set("QML_DEBUG_SERVER_PORT", QString::number(serverPort));
268 connect(&m_proc, SIGNAL(error(QProcess::ProcessError)),
269 SLOT(handleProcError(QProcess::ProcessError)));
270 connect(&m_proc, SIGNAL(finished(int, QProcess::ExitStatus)),
271 SLOT(handleProcFinished(int, QProcess::ExitStatus)));
272 connect(&m_proc, SIGNAL(readyReadStandardOutput()),
273 SLOT(readProcStandardOutput()));
274 connect(&m_proc, SIGNAL(readyReadStandardError()),
275 SLOT(readProcStandardError()));
277 m_proc.setEnvironment(env.toStringList());
278 m_proc.setWorkingDirectory(sp.workingDirectory);
279 m_proc.start(sp.executable, sp.processArgs);
281 if (!m_proc.waitForStarted()) {
282 notifyEngineSetupFailed();
286 notifyEngineSetupOk();
288 //m_frameRate = new CanvasFrameRate(0);
289 //m_frameRate->show();
292 void QmlEngine::setupConnection()
295 //the qmlviewer right now connected using QmlJSInspector::InternalInspectorPlugin::ClientProxy
296 QTC_ASSERT(m_conn == 0, /**/);
297 m_conn = new QDeclarativeDebugConnection(this);
299 connect(m_conn, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
300 SLOT(connectionStateChanged()));
301 connect(m_conn, SIGNAL(error(QAbstractSocket::SocketError)),
302 SLOT(connectionError()));
303 connect(m_conn, SIGNAL(connected()),
304 SLOT(connectionConnected()));
306 QTC_ASSERT(m_client == 0, /**/);
307 m_client = new QmlDebuggerClient(m_conn, this);
308 (void) new QmlFrameRateClient(m_conn, this);
311 QTC_ASSERT(m_engineDebugInterface == 0, /**/);
312 m_engineDebugInterface = new QDeclarativeEngineDebug(m_conn, this);
314 //m_objectTreeWidget->setEngineDebug(m_engineDebugInterface);
315 //m_propertiesWidget->setEngineDebug(m_engineDebugInterface);
316 //m_watchTableModel->setEngineDebug(m_engineDebugInterface);
317 //m_expressionWidget->setEngineDebug(m_engineDebugInterface);
319 //m_frameRateWidget->reset(m_conn);
321 QHostAddress ha(QHostAddress::LocalHost);
323 qDebug() << "CONNECTING TO " << ha.toString() << serverPort;
324 m_conn->connectToHost(ha, serverPort);
326 if (!m_conn->waitForConnected()) {
327 qDebug() << "CONNECTION FAILED";
328 notifyEngineSetupFailed();
333 notifyEngineRunAndInferiorStopOk();
334 qDebug() << "CONNECTION SUCCESSFUL";
337 // continueInferior();
340 void QmlEngine::continueInferior()
342 QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
343 SDEBUG("QmlEngine::continueInferior()");
345 QDataStream rs(&reply, QIODevice::WriteOnly);
346 rs << QByteArray("CONTINUE");
348 notifyInferiorRunRequested();
349 notifyInferiorRunOk();
352 void QmlEngine::interruptInferior()
354 qDebug() << "INTERRUPT";
356 QDataStream rs(&reply, QIODevice::WriteOnly);
357 rs << QByteArray("INTERRUPT");
361 void QmlEngine::executeStep()
363 SDEBUG("QmlEngine::executeStep()");
365 QDataStream rs(&reply, QIODevice::WriteOnly);
366 rs << QByteArray("STEPINTO");
368 notifyInferiorRunRequested();
369 notifyInferiorRunOk();
372 void QmlEngine::executeStepI()
374 SDEBUG("QmlEngine::executeStepI()");
376 QDataStream rs(&reply, QIODevice::WriteOnly);
377 rs << QByteArray("STEPINTO");
379 notifyInferiorRunRequested();
380 notifyInferiorRunOk();
383 void QmlEngine::executeStepOut()
385 SDEBUG("QmlEngine::executeStepOut()");
387 QDataStream rs(&reply, QIODevice::WriteOnly);
388 rs << QByteArray("STEPOUT");
390 notifyInferiorRunRequested();
391 notifyInferiorRunOk();
394 void QmlEngine::executeNext()
397 QDataStream rs(&reply, QIODevice::WriteOnly);
398 rs << QByteArray("STEPOVER");
400 notifyInferiorRunRequested();
401 notifyInferiorRunOk();
402 SDEBUG("QmlEngine::nextExec()");
405 void QmlEngine::executeNextI()
407 SDEBUG("QmlEngine::executeNextI()");
410 void QmlEngine::executeRunToLine(const QString &fileName, int lineNumber)
414 SDEBUG("FIXME: QmlEngine::executeRunToLine()");
417 void QmlEngine::executeRunToFunction(const QString &functionName)
419 Q_UNUSED(functionName)
420 XSDEBUG("FIXME: QmlEngine::executeRunToFunction()");
423 void QmlEngine::executeJumpToLine(const QString &fileName, int lineNumber)
427 XSDEBUG("FIXME: QmlEngine::executeJumpToLine()");
430 void QmlEngine::activateFrame(int index)
435 QDataStream rs(&reply, QIODevice::WriteOnly);
436 rs << QByteArray("ACTIVATE_FRAME");
440 gotoLocation(stackHandler()->frames().value(index), true);
443 void QmlEngine::selectThread(int index)
448 void QmlEngine::attemptBreakpointSynchronization()
450 BreakHandler *handler = breakHandler();
451 //bool updateNeeded = false;
452 QSet< QPair<QString, qint32> > breakList;
453 for (int index = 0; index != handler->size(); ++index) {
454 BreakpointData *data = handler->at(index);
455 breakList << qMakePair(data->fileName, data->lineNumber.toInt());
460 QDataStream rs(&reply, QIODevice::WriteOnly);
461 rs << QByteArray("BREAKPOINTS");
463 //qDebug() << Q_FUNC_INFO << breakList;
468 void QmlEngine::loadSymbols(const QString &moduleName)
473 void QmlEngine::loadAllSymbols()
477 void QmlEngine::reloadModules()
481 void QmlEngine::requestModuleSymbols(const QString &moduleName)
486 #if 0 //this is currently a signal connected to the QmlJSInspector::Internal::DebuggerClient
487 void QmlEngine::sendMessage(const QByteArray &msg)
489 QTC_ASSERT(m_client, return);
490 m_client->sendMessage(msg);
495 //////////////////////////////////////////////////////////////////////
497 // Tooltip specific stuff
499 //////////////////////////////////////////////////////////////////////
501 static WatchData m_toolTip;
502 static QPoint m_toolTipPos;
503 static QHash<QString, WatchData> m_toolTipCache;
505 void QmlEngine::setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos)
512 //////////////////////////////////////////////////////////////////////
514 // Watch specific stuff
516 //////////////////////////////////////////////////////////////////////
518 void QmlEngine::assignValueInDebugger(const QString &expression,
519 const QString &value)
521 XSDEBUG("ASSIGNING: " << expression + '=' + value);
525 void QmlEngine::updateLocals()
527 qDebug() << "UPDATE LOCALS";
530 void QmlEngine::updateWatchData(const WatchData &data)
532 qDebug() << "UPDATE WATCH DATA" << data.toString();
533 //watchHandler()->rebuildModel();
534 showStatusMessage(tr("Stopped."), 5000);
536 if (!data.name.isEmpty() && data.isValueNeeded()) {
538 QDataStream rs(&reply, QIODevice::WriteOnly);
539 rs << QByteArray("EXEC");
540 rs << data.iname << data.name;
544 if (!data.name.isEmpty() && data.isChildrenNeeded() && watchHandler()->isExpandedIName(data.iname)) {
545 expandObject(data.iname, data.objectId);
550 QDataStream rs(&reply, QIODevice::WriteOnly);
551 rs << QByteArray("WATCH_EXPRESSIONS");
552 rs << watchHandler()->watchedExpressions();
557 void QmlEngine::expandObject(const QByteArray& iname, quint64 objectId)
560 QDataStream rs(&reply, QIODevice::WriteOnly);
561 rs << QByteArray("EXPAND");
562 rs << iname << objectId;
567 DebuggerEngine *createQmlEngine(const DebuggerStartParameters &sp)
569 return new QmlEngine(sp);
572 unsigned QmlEngine::debuggerCapabilities() const
574 return AddWatcherCapability;
575 /*ReverseSteppingCapability | SnapshotCapability
576 | AutoDerefPointersCapability | DisassemblerCapability
577 | RegisterCapability | ShowMemoryCapability
578 | JumpToLineCapability | ReloadModuleCapability
579 | ReloadModuleSymbolsCapability | BreakOnThrowAndCatchCapability
580 | ReturnFromFunctionCapability
581 | CreateFullBacktraceCapability
582 | WatchpointCapability
583 | AddWatcherCapability;*/
586 void QmlEngine::messageReceived(const QByteArray &message)
588 QByteArray rwData = message;
589 QDataStream stream(&rwData, QIODevice::ReadOnly);
594 showMessage(_("RECEIVED RESPONSE: ") + quoteUnprintableLatin1(message));
595 if (command == "STOPPED") {
596 notifyInferiorSpontaneousStop();
598 QList<QPair<QString, QPair<QString, qint32> > > backtrace;
599 QList<WatchData> watches;
600 QList<WatchData> locals;
601 stream >> backtrace >> watches >> locals;
603 StackFrames stackFrames;
604 typedef QPair<QString, QPair<QString, qint32> > Iterator;
605 foreach (const Iterator &it, backtrace) {
607 frame.file = it.second.first;
608 frame.line = it.second.second;
609 frame.function = it.first;
610 stackFrames.append(frame);
613 gotoLocation(stackFrames.value(0), true);
614 stackHandler()->setFrames(stackFrames);
616 watchHandler()->beginCycle();
618 foreach (WatchData data, watches) {
619 data.iname = watchHandler()->watcherName(data.exp);
620 watchHandler()->insertData(data);
622 if (watchHandler()->expandedINames().contains(data.iname))
623 expandObject(data.iname, data.objectId);
626 foreach (WatchData data, locals) {
627 data.iname = "local." + data.exp;
628 watchHandler()->insertData(data);
630 if (watchHandler()->expandedINames().contains(data.iname))
631 expandObject(data.iname, data.objectId);
634 watchHandler()->endCycle();
636 } else if (command == "RESULT") {
639 stream >> iname >> data;
641 watchHandler()->insertData(data);
643 } else if (command == "EXPANDED") {
644 QList<WatchData> result;
646 stream >> iname >> result;
647 foreach (WatchData data, result) {
648 data.iname = iname + '.' + data.exp;
649 watchHandler()->insertData(data);
651 if (watchHandler()->expandedINames().contains(data.iname))
652 expandObject(data.iname, data.objectId);
654 } else if (command == "LOCALS") {
655 QList<WatchData> locals;
657 stream >> frameId >> locals;
658 watchHandler()->beginCycle();
659 foreach (WatchData data, locals) {
660 data.iname = "local." + data.exp;
661 qDebug() << data.iname << data.value;
662 watchHandler()->insertData(data);
663 if (watchHandler()->expandedINames().contains(data.iname))
664 expandObject(data.iname, data.objectId);
666 watchHandler()->endCycle();
668 qDebug() << Q_FUNC_INFO << "Unknown command: " << command;
675 class EngineComboBox : public QComboBox
685 EngineComboBox(QWidget *parent = 0);
687 void addEngine(int engine, const QString &name);
693 QList<EngineInfo> m_engines;
696 EngineComboBox::EngineComboBox(QWidget *parent)
703 void EngineComboBox::addEngine(int engine, const QString &name)
708 info.name = tr("Engine %1", "engine number").arg(engine);
716 void EngineComboBox::clearEngines()
723 bool QmlEngine::setDebugConfigurationDataFromProject(ProjectExplorer::Project *projectToDebug)
725 if (!projectToDebug) {
726 emit statusMessage(tr("Invalid project, debugging canceled."));
730 QmlProjectManager::QmlProjectRunConfiguration* config =
731 qobject_cast<QmlProjectManager::QmlProjectRunConfiguration*>(projectToDebug->activeTarget()->activeRunConfiguration());
733 emit statusMessage(tr("Cannot find project run configuration, debugging canceled."));
736 m_runConfigurationDebugData.serverAddress = config->debugServerAddress();
737 m_runConfigurationDebugData.serverPort = config->debugServerPort();
738 m_connectionTimer->setInterval(ConnectionAttemptDefaultInterval);
743 void QmlEngine::startQmlProjectDebugger()
745 m_simultaneousCppAndQmlDebugMode = false;
746 m_connectionTimer->start();
749 bool QmlEngine::connectToViewer()
751 if (m_conn && m_conn->state() != QAbstractSocket::UnconnectedState)
754 delete m_engineDebugInterface; m_engineDebugInterface = 0;
757 m_conn->disconnectFromHost();
762 QString host = m_runConfigurationDebugData.serverAddress;
763 quint16 port = quint16(m_runConfigurationDebugData.serverPort);
765 m_conn = new QDeclarativeDebugConnection(this);
766 connect(m_conn, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
767 SLOT(connectionStateChanged()));
768 connect(m_conn, SIGNAL(error(QAbstractSocket::SocketError)),
769 SLOT(connectionError()));
771 emit statusMessage(tr("[Inspector] set to connect to debug server %1:%2").arg(host).arg(port));
772 m_conn->connectToHost(host, port);
773 // blocks until connected; if no connection is available, will fail immediately
775 if (!m_conn->waitForConnected())
778 QTC_ASSERT(m_debuggerRunControl, return false);
779 QmlEngine *engine = qobject_cast<QmlEngine *>(m_debuggerRunControl->engine());
780 QTC_ASSERT(engine, return false);
782 (void) new DebuggerClient(m_conn, engine);
787 void QmlEngine::disconnectFromViewer()
789 m_conn->disconnectFromHost();
793 void QmlEngine::connectionStateChanged()
795 switch (m_conn->state()) {
796 case QAbstractSocket::UnconnectedState:
798 emit statusMessage(tr("[Inspector] disconnected.\n\n"));
803 case QAbstractSocket::HostLookupState:
804 emit statusMessage(tr("[Inspector] resolving host..."));
806 case QAbstractSocket::ConnectingState:
807 emit statusMessage(tr("[Inspector] connecting to debug server..."));
809 case QAbstractSocket::ConnectedState:
811 emit statusMessage(tr("[Inspector] connected.\n"));
814 // m_frameRateWidget->reset(m_conn);
818 case QAbstractSocket::ClosingState:
819 emit statusMessage(tr("[Inspector] closing..."));
821 case QAbstractSocket::BoundState:
822 case QAbstractSocket::ListeningState:
827 void QmlEngine::resetViews()
829 m_objectTreeWidget->cleanup();
830 m_propertiesWidget->clear();
831 m_expressionWidget->clear();
832 m_watchTableModel->removeAllWatches();
835 void QmlEngine::createDockWidgets()
838 m_engineComboBox = new Internal::EngineComboBox;
839 m_engineComboBox->setEnabled(false);
840 connect(m_engineComboBox, SIGNAL(currentIndexChanged(int)),
841 SLOT(queryEngineContext(int)));
843 // FancyMainWindow uses widgets' window titles for tab labels
844 // m_frameRateWidget->setWindowTitle(tr("Frame rate"));
846 Utils::StyledBar *treeOptionBar = new Utils::StyledBar;
847 QHBoxLayout *treeOptionBarLayout = new QHBoxLayout(treeOptionBar);
848 treeOptionBarLayout->setContentsMargins(5, 0, 5, 0);
849 treeOptionBarLayout->setSpacing(5);
850 treeOptionBarLayout->addWidget(new QLabel(tr("QML engine:")));
851 treeOptionBarLayout->addWidget(m_engineComboBox);
853 QWidget *treeWindow = new QWidget;
854 treeWindow->setObjectName(QLatin1String("QmlDebugTree"));
855 treeWindow->setWindowTitle(tr("Object Tree"));
856 QVBoxLayout *treeWindowLayout = new QVBoxLayout(treeWindow);
857 treeWindowLayout->setMargin(0);
858 treeWindowLayout->setSpacing(0);
859 treeWindowLayout->setContentsMargins(0,0,0,0);
860 treeWindowLayout->addWidget(treeOptionBar);
861 treeWindowLayout->addWidget(m_objectTreeWidget);
864 m_watchTableView->setModel(m_watchTableModel);
865 Internal::WatchTableHeaderView *header = new Internal::WatchTableHeaderView(m_watchTableModel);
866 m_watchTableView->setHorizontalHeader(header);
868 connect(m_objectTreeWidget, SIGNAL(activated(QDeclarativeDebugObjectReference)),
869 this, SLOT(treeObjectActivated(QDeclarativeDebugObjectReference)));
871 connect(m_objectTreeWidget, SIGNAL(currentObjectChanged(QDeclarativeDebugObjectReference)),
872 m_propertiesWidget, SLOT(reload(QDeclarativeDebugObjectReference)));
874 connect(m_objectTreeWidget, SIGNAL(expressionWatchRequested(QDeclarativeDebugObjectReference,QString)),
875 m_watchTableModel, SLOT(expressionWatchRequested(QDeclarativeDebugObjectReference,QString)));
877 connect(m_propertiesWidget, SIGNAL(watchToggleRequested(QDeclarativeDebugObjectReference,QDeclarativeDebugPropertyReference)),
878 m_watchTableModel, SLOT(togglePropertyWatch(QDeclarativeDebugObjectReference,QDeclarativeDebugPropertyReference)));
880 connect(m_watchTableModel, SIGNAL(watchCreated(QDeclarativeDebugWatch*)),
881 m_propertiesWidget, SLOT(watchCreated(QDeclarativeDebugWatch*)));
883 connect(m_watchTableModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
884 m_watchTableView, SLOT(scrollToBottom()));
886 connect(m_watchTableView, SIGNAL(objectActivated(int)),
887 m_objectTreeWidget, SLOT(setCurrentObject(int)));
889 connect(m_objectTreeWidget, SIGNAL(currentObjectChanged(QDeclarativeDebugObjectReference)),
890 m_expressionWidget, SLOT(setCurrentObject(QDeclarativeDebugObjectReference)));
893 Core::MiniSplitter *propSplitter = new Core::MiniSplitter(Qt::Horizontal);
894 Core::MiniSplitter *propWatcherSplitter = new Core::MiniSplitter(Qt::Vertical);
895 propWatcherSplitter->addWidget(m_propertiesWidget);
896 propWatcherSplitter->addWidget(m_watchTableView);
897 propWatcherSplitter->setStretchFactor(0, 2);
898 propWatcherSplitter->setStretchFactor(1, 1);
899 propWatcherSplitter->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding);
901 propSplitter->setWindowTitle(tr("Properties and Watchers"));
902 propSplitter->setObjectName(QLatin1String("QmlDebugProperties"));
903 propSplitter->addWidget(m_objectTreeWidget);
904 propSplitter->addWidget(propWatcherSplitter);
905 propSplitter->setStretchFactor(0, 1);
906 propSplitter->setStretchFactor(1, 3);
908 InspectorOutputWidget *inspectorOutput = new InspectorOutputWidget();
909 inspectorOutput->setObjectName(QLatin1String("QmlDebugInspectorOutput"));
910 connect(this, SIGNAL(statusMessage(QString)),
911 inspectorOutput, SLOT(addInspectorStatus(QString)));
913 Debugger::DebuggerUISwitcher *uiSwitcher = Debugger::DebuggerUISwitcher::instance();
915 m_watchTableView->hide();
916 // m_objectTreeDock = uiSwitcher->createDockWidget(Qml::Constants::LANG_QML,
917 // treeWindow, Qt::BottomDockWidgetArea);
918 // m_frameRateDock = uiSwitcher->createDockWidget(Qml::Constants::LANG_QML,
919 // m_frameRateWidget, Qt::BottomDockWidgetArea);
920 m_propertyWatcherDock = uiSwitcher->createDockWidget(Qml::Constants::LANG_QML,
921 propSplitter, Qt::BottomDockWidgetArea);
922 m_inspectorOutputDock = uiSwitcher->createDockWidget(Qml::Constants::LANG_QML,
923 inspectorOutput, Qt::BottomDockWidgetArea);
925 m_expressionWidget->setWindowTitle(tr("Script Console"));
926 m_expressionQueryDock = uiSwitcher->createDockWidget(Qml::Constants::LANG_QML,
927 m_expressionWidget, Qt::BottomDockWidgetArea);
929 m_inspectorOutputDock->setToolTip(tr("Output of the QML inspector, such as information on connecting to the server."));
931 m_dockWidgets << /*m_objectTreeDock << *//*m_frameRateDock << */ m_propertyWatcherDock
932 << m_inspectorOutputDock << m_expressionQueryDock;
934 m_context = new Internal::InspectorContext(m_objectTreeWidget);
935 m_propWatcherContext = new Internal::InspectorContext(m_propertyWatcherDock);
937 Core::ICore *core = Core::ICore::instance();
938 core->addContextObject(m_propWatcherContext);
939 core->addContextObject(m_context);
941 m_simultaneousDebugAction = new QAction(this);
942 m_simultaneousDebugAction->setText(tr("Start Debugging C++ and QML Simultaneously..."));
943 connect(m_simultaneousDebugAction, SIGNAL(triggered()),
944 this, SLOT(simultaneouslyDebugQmlCppApplication()));
946 Core::ActionManager *am = core->actionManager();
947 Core::ActionContainer *mstart = am->actionContainer(ProjectExplorer::Constants::M_DEBUG_STARTDEBUGGING);
948 Core::Command *cmd = am->registerAction(m_simultaneousDebugAction, Constants::M_DEBUG_SIMULTANEOUSLY,
949 m_context->context());
950 cmd->setAttribute(Core::Command::CA_Hide);
951 mstart->addAction(cmd, Core::Constants::G_DEFAULT_ONE);
953 m_settings.readSettings(core->settings());
954 m_objectTreeWidget->readSettings(m_settings);
955 m_propertiesWidget->readSettings(m_settings);
957 connect(m_objectTreeWidget, SIGNAL(contextHelpIdChanged(QString)), m_context,
958 SLOT(setContextHelpId(QString)));
959 connect(m_watchTableView, SIGNAL(contextHelpIdChanged(QString)), m_propWatcherContext,
960 SLOT(setContextHelpId(QString)));
961 connect(m_propertiesWidget, SIGNAL(contextHelpIdChanged(QString)), m_propWatcherContext,
962 SLOT(setContextHelpId(QString)));
963 connect(m_expressionWidget, SIGNAL(contextHelpIdChanged(QString)), m_propWatcherContext,
964 SLOT(setContextHelpId(QString)));
967 void QmlEngine::simultaneouslyDebugQmlCppApplication()
969 QString errorMessage;
970 ProjectExplorer::ProjectExplorerPlugin *pex = ProjectExplorer::ProjectExplorerPlugin::instance();
971 ProjectExplorer::Project *project = pex->startupProject();
974 errorMessage = QString(tr("No project was found."));
976 if (project->id() == "QmlProjectManager.QmlProject")
977 errorMessage = attachToQmlViewerAsExternalApp(project);
979 errorMessage = attachToExternalCppAppWithQml(project);
983 if (!errorMessage.isEmpty())
984 QMessageBox::warning(Core::ICore::instance()->mainWindow(), "Failed to debug C++ and QML", errorMessage);
987 QString QmlEngine::attachToQmlViewerAsExternalApp(ProjectExplorer::Project *project)
989 m_debugMode = QmlProjectWithCppPlugins;
991 QmlProjectManager::QmlProjectRunConfiguration* runConfig =
992 qobject_cast<QmlProjectManager::QmlProjectRunConfiguration*>(project->activeTarget()->activeRunConfiguration());
995 return QString(tr("No run configurations were found for the project '%1'.").arg(project->displayName()));
997 Internal::StartExternalQmlDialog dlg(Debugger::DebuggerUISwitcher::instance()->mainWindow());
999 QString importPathArgument = "-I";
1001 if (runConfig->viewerArguments().contains(importPathArgument))
1002 execArgs = runConfig->viewerArguments().join(" ");
1004 QFileInfo qmlFileInfo(runConfig->viewerArguments().last());
1005 importPathArgument.append(" " + qmlFileInfo.absolutePath() + " ");
1006 execArgs = importPathArgument + runConfig->viewerArguments().join(" ");
1010 dlg.setPort(runConfig->debugServerPort());
1011 dlg.setDebuggerUrl(runConfig->debugServerAddress());
1012 dlg.setProjectDisplayName(project->displayName());
1013 dlg.setDebugMode(Internal::StartExternalQmlDialog::QmlProjectWithCppPlugins);
1014 dlg.setQmlViewerArguments(execArgs);
1015 dlg.setQmlViewerPath(runConfig->viewerPath());
1017 if (dlg.exec() != QDialog::Accepted)
1020 m_runConfigurationDebugData.serverAddress = dlg.debuggerUrl();
1021 m_runConfigurationDebugData.serverPort = dlg.port();
1022 m_settings.setExternalPort(dlg.port());
1023 m_settings.setExternalUrl(dlg.debuggerUrl());
1025 ProjectExplorer::Environment customEnv = ProjectExplorer::Environment::systemEnvironment(); // empty env by default
1026 customEnv.set(QmlProjectManager::Constants::E_QML_DEBUG_SERVER_PORT, QString::number(m_settings.externalPort()));
1028 Debugger::DebuggerRunControl *debuggableRunControl =
1029 createDebuggerRunControl(runConfig, dlg.qmlViewerPath(), dlg.qmlViewerArguments());
1031 return executeDebuggerRunControl(debuggableRunControl, &customEnv);
1034 QString QmlEngine::attachToExternalCppAppWithQml(ProjectExplorer::Project *project)
1036 m_debugMode = CppProjectWithQmlEngines;
1038 ProjectExplorer::LocalApplicationRunConfiguration* runConfig =
1039 qobject_cast<ProjectExplorer::LocalApplicationRunConfiguration*>(project->activeTarget()->activeRunConfiguration());
1041 if (!project->activeTarget() || !project->activeTarget()->activeRunConfiguration())
1042 return QString(tr("No run configurations were found for the project '%1'.").arg(project->displayName()));
1043 else if (!runConfig)
1044 return QString(tr("No valid run configuration was found for the project %1. "
1045 "Only locally runnable configurations are supported.\n"
1046 "Please check your project settings.").arg(project->displayName()));
1048 Internal::StartExternalQmlDialog dlg(Debugger::DebuggerUISwitcher::instance()->mainWindow());
1050 dlg.setPort(m_settings.externalPort());
1051 dlg.setDebuggerUrl(m_settings.externalUrl());
1052 dlg.setProjectDisplayName(project->displayName());
1053 dlg.setDebugMode(Internal::StartExternalQmlDialog::CppProjectWithQmlEngine);
1054 if (dlg.exec() != QDialog::Accepted)
1057 m_runConfigurationDebugData.serverAddress = dlg.debuggerUrl();
1058 m_runConfigurationDebugData.serverPort = dlg.port();
1059 m_settings.setExternalPort(dlg.port());
1060 m_settings.setExternalUrl(dlg.debuggerUrl());
1062 ProjectExplorer::Environment customEnv = runConfig->environment();
1063 customEnv.set(QmlProjectManager::Constants::E_QML_DEBUG_SERVER_PORT, QString::number(m_settings.externalPort()));
1064 Debugger::DebuggerRunControl *debuggableRunControl = createDebuggerRunControl(runConfig);
1065 return executeDebuggerRunControl(debuggableRunControl, &customEnv);
1068 QString QmlEngine::executeDebuggerRunControl(Debugger::DebuggerRunControl *debuggableRunControl, ProjectExplorer::Environment *environment)
1070 ProjectExplorer::ProjectExplorerPlugin *pex = ProjectExplorer::ProjectExplorerPlugin::instance();
1072 // to make sure we have a valid, debuggable run control, find the correct factory for it
1073 if (debuggableRunControl) {
1076 debuggableRunControl->setCustomEnvironment(*environment);
1078 pex->startRunControl(debuggableRunControl, ProjectExplorer::Constants::DEBUGMODE);
1079 m_simultaneousCppAndQmlDebugMode = true;
1083 return QString(tr("A valid run control was not registered in Qt Creator for this project run configuration."));;
1086 Debugger::DebuggerRunControl *QmlEngine::createDebuggerRunControl(ProjectExplorer::RunConfiguration *runConfig,
1087 const QString &executableFile, const QString &executableArguments)
1089 ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
1090 const QList<Debugger::DebuggerRunControlFactory *> factories = pm->getObjects<Debugger::DebuggerRunControlFactory>();
1091 ProjectExplorer::RunControl *runControl = 0;
1093 if (m_debugMode == QmlProjectWithCppPlugins) {
1094 Debugger::DebuggerStartParameters sp;
1095 sp.startMode = Debugger::StartExternal;
1096 sp.executable = executableFile;
1097 sp.processArgs = executableArguments.split(QLatin1Char(' '));
1098 runControl = factories.first()->create(sp);
1099 return qobject_cast<Debugger::DebuggerRunControl *>(runControl);
1102 if (m_debugMode == CppProjectWithQmlEngines) {
1103 if (factories.length() && factories.first()->canRun(runConfig, ProjectExplorer::Constants::DEBUGMODE)) {
1104 runControl = factories.first()->create(runConfig, ProjectExplorer::Constants::DEBUGMODE);
1105 return qobject_cast<Debugger::DebuggerRunControl *>(runControl);
1112 void QmlEngine::updateMenuActions()
1115 bool enabled = true;
1116 if (m_simultaneousCppAndQmlDebugMode)
1117 enabled = (m_cppDebuggerState == Debugger::DebuggerNotReady && (!m_conn || m_conn->state() == QAbstractSocket::UnconnectedState));
1119 enabled = (!m_conn || m_conn->state() == QAbstractSocket::UnconnectedState);
1121 m_simultaneousDebugAction->setEnabled(enabled);
1125 void QmlEngine::debuggerStateChanged(int newState)
1127 if (m_simultaneousCppAndQmlDebugMode) {
1130 case Debugger::EngineSetupRequested:
1132 m_connectionInitialized = false;
1135 case Debugger::AdapterStartFailed:
1136 case Debugger::InferiorSetupFailed:
1137 emit statusMessage(QString(tr("Debugging failed: could not start C++ debugger.")));
1139 case Debugger::InferiorRunRequested:
1141 if (m_cppDebuggerState == Debugger::InferiorStopOk) {
1142 // re-enable UI again
1143 m_objectTreeWidget->setEnabled(true);
1144 m_propertiesWidget->setEnabled(true);
1145 m_expressionWidget->setEnabled(true);
1149 case Debugger::InferiorRunOk:
1151 if (!m_connectionInitialized) {
1152 m_connectionInitialized = true;
1153 m_connectionTimer->setInterval(ConnectionAttemptSimultaneousInterval);
1154 m_connectionTimer->start();
1158 case Debugger::InferiorStopOk:
1160 m_objectTreeWidget->setEnabled(false);
1161 m_propertiesWidget->setEnabled(false);
1162 m_expressionWidget->setEnabled(false);
1165 case Debugger::EngineShutdownRequested:
1167 m_connectionInitialized = false;
1168 // here it's safe to enable the debugger windows again -
1169 // disabled ones look ugly.
1170 m_objectTreeWidget->setEnabled(true);
1171 m_propertiesWidget->setEnabled(true);
1172 m_expressionWidget->setEnabled(true);
1173 m_simultaneousCppAndQmlDebugMode = false;
1181 m_cppDebuggerState = newState;
1182 updateMenuActions();
1186 void QmlEngine::setSimpleDockWidgetArrangement()
1188 Utils::FancyMainWindow *mainWindow = Debugger::DebuggerUISwitcher::instance()->mainWindow();
1190 mainWindow->setTrackingEnabled(false);
1191 QList<QDockWidget *> dockWidgets = mainWindow->dockWidgets();
1192 foreach (QDockWidget *dockWidget, dockWidgets) {
1193 if (m_dockWidgets.contains(dockWidget)) {
1194 dockWidget->setFloating(false);
1195 mainWindow->removeDockWidget(dockWidget);
1199 foreach (QDockWidget *dockWidget, dockWidgets) {
1200 if (m_dockWidgets.contains(dockWidget)) {
1201 mainWindow->addDockWidget(Qt::BottomDockWidgetArea, dockWidget);
1205 mainWindow->splitDockWidget(mainWindow->toolBarDockWidget(), m_propertyWatcherDock, Qt::Vertical);
1206 //mainWindow->tabifyDockWidget(m_frameRateDock, m_propertyWatcherDock);
1207 mainWindow->tabifyDockWidget(m_propertyWatcherDock, m_expressionQueryDock);
1208 mainWindow->tabifyDockWidget(m_propertyWatcherDock, m_inspectorOutputDock);
1209 m_propertyWatcherDock->raise();
1211 m_inspectorOutputDock->setVisible(false);
1213 mainWindow->setTrackingEnabled(true);
1217 void QmlEngine::reloadEngines()
1219 //m_engineComboBox->setEnabled(false);
1221 QDeclarativeDebugEnginesQuery *query =
1222 m_engineDebugInterface->queryAvailableEngines(this);
1223 if (!query->isWaiting())
1224 enginesChanged(query);
1226 QObject::connect(query, SIGNAL(stateChanged(QDeclarativeDebugQuery::State)),
1227 this, SLOT(enginesChanged()));
1230 void QmlEngine::enginesChanged()
1232 enginesChanged(qobject_cast<QDeclarativeDebugEnginesQuery *>(sender()));
1235 void QmlEngine::enginesChanged(QDeclarativeDebugEnginesQuery *query)
1237 //m_engineComboBox->clearEngines();
1238 QList<QDeclarativeDebugEngineReference> engines = query->engines();
1239 if (engines.isEmpty())
1240 qWarning("QMLDEBUGGER: NO ENGINES FOUND!");
1242 //m_engineComboBox->setEnabled(true);
1244 for (int i = 0; i < engines.count(); ++i)
1245 qDebug() << "ENGINE: " << engines.at(i).debugId() << engines.at(i).name();
1246 // m_engineComboBox->addEngine(engines.at(i).debugId(), engines.at(i).name());
1248 if (engines.count() > 0) {
1249 // m_engineComboBox->setCurrentIndex(engines.at(0).debugId());
1250 queryEngineContext(engines.at(0));
1254 void QmlEngine::queryEngineContext(const QDeclarativeDebugEngineReference &engine)
1256 QDeclarativeDebugRootContextQuery *query =
1257 m_engineDebugInterface->queryRootContexts(engine, this);
1259 if (!query->isWaiting())
1262 QObject::connect(query, SIGNAL(stateChanged(QDeclarativeDebugQuery::State)),
1263 this, SLOT(contextChanged()));
1266 void QmlEngine::contextChanged()
1268 contextChanged(qobject_cast<QDeclarativeDebugRootContextQuery *>(sender()));
1271 void QmlEngine::contextChanged(QDeclarativeDebugRootContextQuery *query)
1273 QTC_ASSERT(query, return);
1274 //dump(query->rootContext(), 0);
1275 foreach (const QDeclarativeDebugObjectReference &object, query->rootContext().objects())
1276 reloadObject(object);
1279 void QmlEngine::reloadObject(const QDeclarativeDebugObjectReference &object)
1281 qDebug() << "RELOAD OBJECT: " << object.debugId() << object.idString()
1282 << object.className();
1283 QDeclarativeDebugObjectQuery *query =
1284 m_engineDebugInterface->queryObjectRecursive(object, this);
1285 if (!query->isWaiting())
1286 objectFetched(query, QDeclarativeDebugQuery::Completed);
1288 QObject::connect(query, SIGNAL(stateChanged(QDeclarativeDebugQuery::State)),
1289 this, SLOT(objectFetched(QDeclarativeDebugQuery::State)));
1292 void QmlEngine::objectFetched(QDeclarativeDebugQuery::State state)
1294 objectFetched(qobject_cast<QDeclarativeDebugObjectQuery *>(sender()), state);
1297 void QmlEngine::objectFetched(QDeclarativeDebugObjectQuery *query,
1298 QDeclarativeDebugQuery::State state)
1300 QTC_ASSERT(query, return);
1301 QTC_ASSERT(state == QDeclarativeDebugQuery::Completed, return);
1302 //dump(m_query->object(), 0);
1305 buildTree(query->object(), "local");
1307 qDebug() << "WATCHES CREATED: " << m_watches.size();
1308 //watchHandler()->beginCycle();
1309 //watchHandler()->insertBulkData(list);
1310 //watchHandler()->endCycle();
1311 //setCurrentItem(topLevelItem(0));
1313 // this ugly hack is needed if user wants to see internal structs
1314 // on startup - debugger does not load them until towards the end,
1315 // so essentially loading twice gives us the full list as everything
1316 // is already loaded.
1317 //if (m_showUninspectableItems && !m_showUninspectableOnInitDone) {
1318 // m_showUninspectableOnInitDone = true;
1319 // reloadObject(m_currentObjectDebugId);
1323 void QmlEngine::buildTree(const QDeclarativeDebugObjectReference &obj,
1324 const QByteArray &iname)
1326 //QTC_ASSERT(obj.contextDebugId() >= 0, return);
1330 if (obj.idString().isEmpty())
1331 data.name = QString("<%1>").arg(obj.className());
1333 data.name = obj.idString();
1337 data.setHasChildren(!obj.children().isEmpty());
1338 data.setAllUnneeded();
1339 qDebug() << "CREATED ITEM " << data.iname << data.name;
1340 m_watches.append(m_engineDebugInterface->addWatch(obj, data.name, 0));
1341 //QDeclarativeDebugPropertyWatch *QDeclarativeEngineDebug::addWatch(const QDeclarativeDebugPropertyReference &property, QObject *parent)
1343 //data.userRole = qVariantFromValue(obj);
1345 if (parent && obj.contextDebugId() >= 0
1346 && obj.contextDebugId() != parent->data(0, Qt::UserRole
1347 ).value<QDeclarativeDebugObjectReference>().contextDebugId())
1350 QDeclarativeDebugFileReference source = obj.source();
1351 if (!source.url().isEmpty()) {
1352 QString toolTipString = QLatin1String("URL: ") + source.url().toString();
1353 item->setToolTip(0, toolTipString);
1357 item->setExpanded(true);
1360 if (obj.contextDebugId() < 0)
1361 item->setHasValidDebugId(false);
1364 for (int i = 0; i < obj.children().size(); ++i)
1365 buildTree(obj.children().at(i), iname + '.' + QByteArray::number(i));
1369 void QmlEngine::treeObjectActivated(const QDeclarativeDebugObjectReference &obj)
1371 QDeclarativeDebugFileReference source = obj.source();
1372 QString fileName = source.url().toLocalFile();
1374 if (source.lineNumber() < 0 || !QFile::exists(fileName))
1377 Core::EditorManager *editorManager = Core::EditorManager::instance();
1378 Core::IEditor *editor = editorManager->openEditor(fileName, QString(), Core::EditorManager::NoModeSwitch);
1379 TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor*>(editor);
1382 editorManager->addCurrentPositionToNavigationHistory();
1383 textEditor->gotoLine(source.lineNumber());
1384 textEditor->widget()->setFocus();
1388 bool QmlEngine::canEditProperty(const QString &propertyType)
1390 return m_editablePropertyTypes.contains(propertyType);
1393 QDeclarativeDebugExpressionQuery *QmlEngine::executeExpression(int objectDebugId, const QString &objectId,
1394 const QString &propertyName, const QVariant &value)
1396 //qDebug() << entity.property << entity.title << entity.objectId;
1397 if (objectId.length()) {
1399 QString quoteWrappedValue = value.toString();
1400 if (addQuotesForData(value))
1401 quoteWrappedValue = QString("'%1'").arg(quoteWrappedValue);
1403 QString constructedExpression = objectId + "." + propertyName + "=" + quoteWrappedValue;
1404 //qDebug() << "EXPRESSION:" << constructedExpression;
1405 return m_client->queryExpressionResult(objectDebugId, constructedExpression, this);
1411 bool QmlEngine::addQuotesForData(const QVariant &value) const
1413 switch (value.type()) {
1414 case QVariant::String:
1415 case QVariant::Color:
1416 case QVariant::Date:
1425 ObjectTree::ObjectTree(QDeclarativeEngineDebug *client, QWidget *parent)
1426 : QTreeWidget(parent),
1428 m_query(0), m_clickedItem(0), m_showUninspectableItems(false),
1429 m_currentObjectDebugId(0), m_showUninspectableOnInitDone(false)
1431 setAttribute(Qt::WA_MacShowFocusRect, false);
1432 setFrameStyle(QFrame::NoFrame);
1433 setHeaderHidden(true);
1434 setExpandsOnDoubleClick(false);
1436 m_addWatchAction = new QAction(tr("Add watch expression..."), this);
1437 m_toggleUninspectableItemsAction = new QAction(tr("Show uninspectable items"), this);
1438 m_toggleUninspectableItemsAction->setCheckable(true);
1439 m_goToFileAction = new QAction(tr("Go to file"), this);
1440 connect(m_toggleUninspectableItemsAction, SIGNAL(triggered()), SLOT(toggleUninspectableItems()));
1441 connect(m_addWatchAction, SIGNAL(triggered()), SLOT(addWatch()));
1442 connect(m_goToFileAction, SIGNAL(triggered()), SLOT(goToFile()));
1444 connect(this, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)),
1445 SLOT(currentItemChanged(QTreeWidgetItem *)));
1446 connect(this, SIGNAL(itemActivated(QTreeWidgetItem *, int)),
1447 SLOT(activated(QTreeWidgetItem *)));
1448 connect(this, SIGNAL(itemSelectionChanged()), SLOT(selectionChanged()));
1451 void ObjectTree::readSettings(const InspectorSettings &settings)
1453 if (settings.showUninspectableItems() != m_showUninspectableItems)
1454 toggleUninspectableItems();
1456 void ObjectTree::saveSettings(InspectorSettings &settings)
1458 settings.setShowUninspectableItems(m_showUninspectableItems);
1461 void ObjectTree::setEngineDebug(QDeclarativeEngineDebug *client)
1466 void ObjectTree::toggleUninspectableItems()
1468 m_showUninspectableItems = !m_showUninspectableItems;
1469 m_toggleUninspectableItemsAction->setChecked(m_showUninspectableItems);
1470 reload(m_currentObjectDebugId);
1473 void ObjectTree::selectionChanged()
1475 if (selectedItems().isEmpty())
1478 QTreeWidgetItem *item = selectedItems().first();
1480 emit contextHelpIdChanged(InspectorContext::contextHelpIdForItem(item->text(0)));
1484 void ObjectTree::setCurrentObject(int debugId)
1486 QTreeWidgetItem *item = findItemByObjectId(debugId);
1488 setCurrentItem(item);
1490 item->setExpanded(true);
1500 QDeclarativeDebugObjectReference obj = item->data(0, Qt::UserRole).value<QDeclarativeDebugObjectReference>();
1501 if (obj.debugId() >= 0)
1502 emit currentObjectChanged(obj);
1505 void ObjectTree::activated(QTreeWidgetItem *item)
1510 QDeclarativeDebugObjectReference obj = item->data(0, Qt::UserRole).value<QDeclarativeDebugObjectReference>();
1511 if (obj.debugId() >= 0)
1512 emit activated(obj);
1515 void ObjectTree::cleanup()
1517 m_showUninspectableOnInitDone = false;
1521 void ObjectTree::dump(const QDeclarativeDebugContextReference &ctxt, int ind)
1523 QByteArray indent(ind * 4, ' ');
1524 qWarning().nospace() << indent.constData() << ctxt.debugId() << " "
1525 << qPrintable(ctxt.name());
1527 for (int ii = 0; ii < ctxt.contexts().count(); ++ii)
1528 dump(ctxt.contexts().at(ii), ind + 1);
1530 for (int ii = 0; ii < ctxt.objects().count(); ++ii)
1531 dump(ctxt.objects().at(ii), ind);
1534 void ObjectTree::dump(const QDeclarativeDebugObjectReference &obj, int ind)
1536 QByteArray indent(ind * 4, ' ');
1537 qWarning().nospace() << indent.constData() << qPrintable(obj.className())
1538 << " " << qPrintable(obj.idString()) << " "
1541 for (int ii = 0; ii < obj.children().count(); ++ii)
1542 dump(obj.children().at(ii), ind + 1);
1545 QTreeWidgetItem *ObjectTree::findItemByObjectId(int debugId) const
1547 for (int i=0; i<topLevelItemCount(); ++i) {
1548 QTreeWidgetItem *item = findItem(topLevelItem(i), debugId);
1556 QTreeWidgetItem *ObjectTree::findItem(QTreeWidgetItem *item, int debugId) const
1558 if (item->data(0, Qt::UserRole).value<QDeclarativeDebugObjectReference>().debugId() == debugId)
1561 QTreeWidgetItem *child;
1562 for (int i=0; i<item->childCount(); ++i) {
1563 child = findItem(item->child(i), debugId);
1571 void ObjectTree::addWatch()
1573 QDeclarativeDebugObjectReference obj =
1574 currentItem()->data(0, Qt::UserRole).value<QDeclarativeDebugObjectReference>();
1577 QString watch = QInputDialog::getText(this, tr("Watch expression"),
1578 tr("Expression:"), QLineEdit::Normal, QString(), &ok);
1579 if (ok && !watch.isEmpty())
1580 emit expressionWatchRequested(obj, watch);
1584 void ObjectTree::goToFile()
1586 QDeclarativeDebugObjectReference obj =
1587 currentItem()->data(0, Qt::UserRole).value<QDeclarativeDebugObjectReference>();
1589 if (obj.debugId() >= 0)
1590 emit activated(obj);
1593 void ObjectTree::contextMenuEvent(QContextMenuEvent *event)
1596 m_clickedItem = itemAt(QPoint(event->pos().x(),
1597 event->pos().y() ));
1602 menu.addAction(m_addWatchAction);
1603 menu.addAction(m_goToFileAction);
1604 if (m_currentObjectDebugId) {
1605 menu.addSeparator();
1606 menu.addAction(m_toggleUninspectableItemsAction);
1609 menu.exec(event->globalPos());
1616 } // namespace Internal
1617 } // namespace Debugger
1619 #include "qmlengine.moc"