OSDN Git Service

QML JS Debugger: clicking on the stackstrace shows the locals of this context
[qt-creator-jp/qt-creator-jp.git] / src / plugins / debugger / qml / qmlengine.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** Commercial Usage
10 **
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.
15 **
16 ** GNU Lesser General Public License Usage
17 **
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.
24 **
25 ** If you are unsure which license is appropriate for your use, please
26 ** contact the sales department at http://qt.nokia.com/contact.
27 **
28 **************************************************************************/
29
30 #include "qmlengine.h"
31
32 #include "debuggerconstants.h"
33 #include "debuggerplugin.h"
34 #include "debuggerdialogs.h"
35 #include "debuggerstringutils.h"
36
37 #include "breakhandler.h"
38 #include "moduleshandler.h"
39 #include "registerhandler.h"
40 #include "stackhandler.h"
41 #include "watchhandler.h"
42 #include "watchutils.h"
43
44 #include "canvasframerate.h"
45
46 #include <projectexplorer/environment.h>
47
48 #include <utils/qtcassert.h>
49
50 #include <QtCore/QDateTime>
51 #include <QtCore/QDebug>
52 #include <QtCore/QDir>
53 #include <QtCore/QFileInfo>
54 #include <QtCore/QTimer>
55
56 #include <QtGui/QAction>
57 #include <QtGui/QApplication>
58 #include <QtGui/QMainWindow>
59 #include <QtGui/QMessageBox>
60 #include <QtGui/QToolTip>
61
62 #include <QtNetwork/QTcpSocket>
63 #include <QtNetwork/QHostAddress>
64
65 #include <private/qdeclarativedebug_p.h>
66 #include <private/qdeclarativedebugclient_p.h>
67
68 #define DEBUG_QML 1
69 #if DEBUG_QML
70 #   define SDEBUG(s) qDebug() << s
71 #else
72 #   define SDEBUG(s)
73 #endif
74 # define XSDEBUG(s) qDebug() << s
75
76 #define CB(callback) &QmlEngine::callback, STRINGIFY(callback)
77
78 //#define USE_CONGESTION_CONTROL
79
80
81 namespace Debugger {
82 namespace Internal {
83
84 QDataStream& operator>>(QDataStream& s, WatchData &data)
85 {
86     data = WatchData();
87     QString value;
88     QString type;
89     bool hasChildren;
90     s >> data.exp >> data.name >> value >> type >> hasChildren >> data.objectId;
91     data.setType(type, false);
92     data.setValue(value);
93     data.setHasChildren(hasChildren);
94     data.setAllUnneeded();
95     return s;
96 }
97
98 class QmlResponse
99 {
100 public:
101     QmlResponse() {}
102     QmlResponse(const QByteArray &data_) : data(data_) {}
103
104     QString toString() const { return data; }
105
106     QByteArray data;
107 };
108
109 ///////////////////////////////////////////////////////////////////////
110 //
111 // QmlDebuggerClient
112 //
113 ///////////////////////////////////////////////////////////////////////
114
115 #if 0 //QmlJSInspector does that for us now.
116 class QmlDebuggerClient : public QDeclarativeDebugClient
117 {
118     Q_OBJECT
119
120 public:
121     QmlDebuggerClient(QDeclarativeDebugConnection *connection, QmlEngine *engine)
122         : QDeclarativeDebugClient(QLatin1String("QDeclarativeEngine"), connection)
123         , m_connection(connection), m_engine(engine)
124     {
125         setEnabled(true);
126     }
127
128     void sendMessage(const QByteArray &msg)
129     {
130         QTC_kASSERT(isConnected(), /**/);
131         qDebug() << "SENDING: " << quoteUnprintableLatin1(msg);
132         QDeclarativeDebugClient::sendMessage(msg);
133     }
134
135     void messageReceived(const QByteArray &data)
136     {
137         m_engine->messageReceived(data);
138     }
139
140
141     QDeclarativeDebugConnection *m_connection;
142     QmlEngine *m_engine;
143 };
144
145
146 class QmlFrameRateClient : public QDeclarativeDebugClient
147 {
148     Q_OBJECT
149
150 public:
151     QmlFrameRateClient(QDeclarativeDebugConnection *connection, QmlEngine *engine)
152         : QDeclarativeDebugClient(QLatin1String("CanvasFrameRate"), connection)
153         , m_connection(connection), m_engine(engine)
154     {
155         setEnabled(true);
156     }
157
158     void messageReceived(const QByteArray &data)
159     {
160         Q_UNUSED(data);
161         // FIXME
162         //qDebug() << "CANVAS FRAME RATE: " << data.size();
163         //m_engine->messageReceived(data);
164     }
165
166
167     QDeclarativeDebugConnection *m_connection;
168     QmlEngine *m_engine;
169 };
170 #endif
171
172 ///////////////////////////////////////////////////////////////////////
173 //
174 // QmlEngine
175 //
176 ///////////////////////////////////////////////////////////////////////
177
178 QmlEngine::QmlEngine(const DebuggerStartParameters &startParameters)
179     : DebuggerEngine(startParameters)
180 {
181 /*
182     m_conn = 0;
183     m_client = 0;
184     m_engineDebugInterface = 0;
185     m_frameRate = 0;
186
187     m_watchTableModel = new Internal::WatchTableModel(0, this);
188
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"));
195
196     connect(Debugger::DebuggerPlugin::instance(),
197         SIGNAL(stateChanged(int)), this, SLOT(debuggerStateChanged(int)));
198
199     m_editablePropertyTypes = QStringList() << "qreal" << "bool" << "QString"
200                                             << "int" << "QVariant" << "QUrl" << "QColor";
201
202     connect(m_connectionTimer, SIGNAL(timeout()), SLOT(pollInspector()));
203     */
204 }
205
206 QmlEngine::~QmlEngine()
207 {
208 }
209
210 void QmlEngine::executeDebuggerCommand(const QString &command)
211 {
212     QByteArray cmd = command.toUtf8();
213     cmd = cmd.mid(cmd.indexOf(' ') + 1);
214     QByteArray null;
215     null.append('\0');
216     // FIXME: works for single-digit escapes only
217     cmd.replace("\\0", null);
218     cmd.replace("\\1", "\1");
219     cmd.replace("\\3", "\3");
220     //QmlCommand tcf;
221     //tcf.command = cmd;
222     //enqueueCommand(tcf);
223 }
224
225 void QmlEngine::setupInferior()
226 {
227     QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
228     attemptBreakpointSynchronization();
229     notifyInferiorSetupOk();
230 }
231
232 void QmlEngine::runEngine()
233 {
234     QTC_ASSERT(state() == EngineRunRequested, qDebug() << state());
235     notifyEngineRunAndInferiorRunOk();
236 }
237
238 void QmlEngine::shutdownInferior()
239 {
240     QTC_ASSERT(state() == InferiorShutdownRequested, qDebug() << state());
241     notifyInferiorShutdownOk();
242 }
243
244 void QmlEngine::shutdownEngine()
245 {
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());
250 }
251
252 const int serverPort = 3768;
253
254 void QmlEngine::setupEngine()
255 {
256  #if 0
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;
264   
265     ProjectExplorer::Environment env = ProjectExplorer::Environment::systemEnvironment(); // empty env by default
266     env.set("QML_DEBUG_SERVER_PORT", QString::number(serverPort));
267
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()));
276
277     m_proc.setEnvironment(env.toStringList());
278     m_proc.setWorkingDirectory(sp.workingDirectory);
279     m_proc.start(sp.executable, sp.processArgs);
280
281     if (!m_proc.waitForStarted()) {
282         notifyEngineSetupFailed();
283         return;
284     }
285 #endif
286     notifyEngineSetupOk();
287
288     //m_frameRate = new CanvasFrameRate(0);
289     //m_frameRate->show();
290 }
291
292 void QmlEngine::setupConnection()
293 {
294     #if 0
295     //the qmlviewer right now connected using QmlJSInspector::InternalInspectorPlugin::ClientProxy
296     QTC_ASSERT(m_conn == 0, /**/);
297     m_conn = new QDeclarativeDebugConnection(this);
298
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()));
305
306     QTC_ASSERT(m_client == 0, /**/);
307     m_client = new QmlDebuggerClient(m_conn, this);
308     (void) new QmlFrameRateClient(m_conn, this);
309
310
311     QTC_ASSERT(m_engineDebugInterface == 0, /**/);
312     m_engineDebugInterface = new QDeclarativeEngineDebug(m_conn, this);
313
314     //m_objectTreeWidget->setEngineDebug(m_engineDebugInterface);
315     //m_propertiesWidget->setEngineDebug(m_engineDebugInterface);
316     //m_watchTableModel->setEngineDebug(m_engineDebugInterface);
317     //m_expressionWidget->setEngineDebug(m_engineDebugInterface);
318     //resetViews();
319     //m_frameRateWidget->reset(m_conn);
320
321     QHostAddress ha(QHostAddress::LocalHost);
322
323     qDebug() << "CONNECTING TO " << ha.toString() << serverPort;
324     m_conn->connectToHost(ha, serverPort);
325
326     if (!m_conn->waitForConnected()) {
327         qDebug() << "CONNECTION FAILED";
328         notifyEngineSetupFailed();
329         return;
330     }
331  #endif
332
333     notifyEngineRunAndInferiorStopOk();
334     qDebug() << "CONNECTION SUCCESSFUL";
335
336 //    reloadEngines();
337 //    continueInferior();
338 }
339
340 void QmlEngine::continueInferior()
341 {
342     QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
343     SDEBUG("QmlEngine::continueInferior()");
344     QByteArray reply;
345     QDataStream rs(&reply, QIODevice::WriteOnly);
346     rs << QByteArray("CONTINUE");
347     sendMessage(reply);
348     notifyInferiorRunRequested();
349     notifyInferiorRunOk();
350 }
351
352 void QmlEngine::interruptInferior()
353 {
354     qDebug() << "INTERRUPT";
355     QByteArray reply;
356     QDataStream rs(&reply, QIODevice::WriteOnly);
357     rs << QByteArray("INTERRUPT");
358     sendMessage(reply);
359 }
360
361 void QmlEngine::executeStep()
362 {
363     SDEBUG("QmlEngine::executeStep()");
364     QByteArray reply;
365     QDataStream rs(&reply, QIODevice::WriteOnly);
366     rs << QByteArray("STEPINTO");
367     sendMessage(reply);
368     notifyInferiorRunRequested();
369     notifyInferiorRunOk();
370 }
371
372 void QmlEngine::executeStepI()
373 {
374     SDEBUG("QmlEngine::executeStepI()");
375     QByteArray reply;
376     QDataStream rs(&reply, QIODevice::WriteOnly);
377     rs << QByteArray("STEPINTO");
378     sendMessage(reply);
379     notifyInferiorRunRequested();
380     notifyInferiorRunOk();
381 }
382
383 void QmlEngine::executeStepOut()
384 {
385     SDEBUG("QmlEngine::executeStepOut()");
386     QByteArray reply;
387     QDataStream rs(&reply, QIODevice::WriteOnly);
388     rs << QByteArray("STEPOUT");
389     sendMessage(reply);
390     notifyInferiorRunRequested();
391     notifyInferiorRunOk();
392 }
393
394 void QmlEngine::executeNext()
395 {
396     QByteArray reply;
397     QDataStream rs(&reply, QIODevice::WriteOnly);
398     rs << QByteArray("STEPOVER");
399     sendMessage(reply);
400     notifyInferiorRunRequested();
401     notifyInferiorRunOk();
402     SDEBUG("QmlEngine::nextExec()");
403 }
404
405 void QmlEngine::executeNextI()
406 {
407     SDEBUG("QmlEngine::executeNextI()");
408 }
409
410 void QmlEngine::executeRunToLine(const QString &fileName, int lineNumber)
411 {
412     Q_UNUSED(fileName)
413     Q_UNUSED(lineNumber)
414     SDEBUG("FIXME:  QmlEngine::executeRunToLine()");
415 }
416
417 void QmlEngine::executeRunToFunction(const QString &functionName)
418 {
419     Q_UNUSED(functionName)
420     XSDEBUG("FIXME:  QmlEngine::executeRunToFunction()");
421 }
422
423 void QmlEngine::executeJumpToLine(const QString &fileName, int lineNumber)
424 {
425     Q_UNUSED(fileName)
426     Q_UNUSED(lineNumber)
427     XSDEBUG("FIXME:  QmlEngine::executeJumpToLine()");
428 }
429
430 void QmlEngine::activateFrame(int index)
431 {
432     Q_UNUSED(index)
433
434     QByteArray reply;
435     QDataStream rs(&reply, QIODevice::WriteOnly);
436     rs << QByteArray("ACTIVATE_FRAME");
437     rs << index;
438     sendMessage(reply);
439
440     gotoLocation(stackHandler()->frames().value(index), true);
441 }
442
443 void QmlEngine::selectThread(int index)
444 {
445     Q_UNUSED(index)
446 }
447
448 void QmlEngine::attemptBreakpointSynchronization()
449 {
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());
456     }
457
458     {
459     QByteArray reply;
460     QDataStream rs(&reply, QIODevice::WriteOnly);
461     rs << QByteArray("BREAKPOINTS");
462     rs << breakList;
463     //qDebug() << Q_FUNC_INFO << breakList;
464     sendMessage(reply);
465     }
466 }
467
468 void QmlEngine::loadSymbols(const QString &moduleName)
469 {
470     Q_UNUSED(moduleName)
471 }
472
473 void QmlEngine::loadAllSymbols()
474 {
475 }
476
477 void QmlEngine::reloadModules()
478 {
479 }
480
481 void QmlEngine::requestModuleSymbols(const QString &moduleName)
482 {
483     Q_UNUSED(moduleName)
484 }
485
486 #if 0 //this is currently a signal connected to the QmlJSInspector::Internal::DebuggerClient
487 void QmlEngine::sendMessage(const QByteArray &msg)
488 {
489     QTC_ASSERT(m_client, return);
490     m_client->sendMessage(msg);
491 }
492 #endif
493
494
495 //////////////////////////////////////////////////////////////////////
496 //
497 // Tooltip specific stuff
498 //
499 //////////////////////////////////////////////////////////////////////
500
501 static WatchData m_toolTip;
502 static QPoint m_toolTipPos;
503 static QHash<QString, WatchData> m_toolTipCache;
504
505 void QmlEngine::setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos)
506 {
507     Q_UNUSED(mousePos)
508     Q_UNUSED(editor)
509     Q_UNUSED(cursorPos)
510 }
511
512 //////////////////////////////////////////////////////////////////////
513 //
514 // Watch specific stuff
515 //
516 //////////////////////////////////////////////////////////////////////
517
518 void QmlEngine::assignValueInDebugger(const QString &expression,
519     const QString &value)
520 {
521     XSDEBUG("ASSIGNING: " << expression + '=' + value);
522     updateLocals();
523 }
524
525 void QmlEngine::updateLocals()
526 {
527     qDebug() << "UPDATE LOCALS";
528 }
529
530 void QmlEngine::updateWatchData(const WatchData &data)
531 {
532     qDebug() << "UPDATE WATCH DATA" << data.toString();
533     //watchHandler()->rebuildModel();
534     showStatusMessage(tr("Stopped."), 5000);
535
536     if (!data.name.isEmpty() && data.isValueNeeded()) {
537         QByteArray reply;
538         QDataStream rs(&reply, QIODevice::WriteOnly);
539         rs << QByteArray("EXEC");
540         rs << data.iname << data.name;
541         sendMessage(reply);
542     }
543
544     if (!data.name.isEmpty() && data.isChildrenNeeded() && watchHandler()->isExpandedIName(data.iname)) {
545         expandObject(data.iname, data.objectId);
546     }
547
548     {
549         QByteArray reply;
550         QDataStream rs(&reply, QIODevice::WriteOnly);
551         rs << QByteArray("WATCH_EXPRESSIONS");
552         rs << watchHandler()->watchedExpressions();
553         sendMessage(reply);
554     }
555 }
556
557 void QmlEngine::expandObject(const QByteArray& iname, quint64 objectId)
558 {
559     QByteArray reply;
560     QDataStream rs(&reply, QIODevice::WriteOnly);
561     rs << QByteArray("EXPAND");
562     rs << iname << objectId;
563     sendMessage(reply);
564 }
565
566
567 DebuggerEngine *createQmlEngine(const DebuggerStartParameters &sp)
568 {
569     return new QmlEngine(sp);
570 }
571
572 unsigned QmlEngine::debuggerCapabilities() const
573 {
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;*/
584 }
585
586 void QmlEngine::messageReceived(const QByteArray &message)
587 {
588     QByteArray rwData = message;
589     QDataStream stream(&rwData, QIODevice::ReadOnly);
590
591     QByteArray command;
592     stream >> command;
593
594     showMessage(_("RECEIVED RESPONSE: ") + quoteUnprintableLatin1(message));
595     if (command == "STOPPED") {
596         notifyInferiorSpontaneousStop();
597
598         QList<QPair<QString, QPair<QString, qint32> > > backtrace;
599         QList<WatchData> watches;
600         QList<WatchData> locals;
601         stream >> backtrace >> watches >> locals;
602
603         StackFrames stackFrames;
604         typedef QPair<QString, QPair<QString, qint32> > Iterator;
605         foreach (const Iterator &it, backtrace) {
606             StackFrame frame;
607             frame.file = it.second.first;
608             frame.line = it.second.second;
609             frame.function = it.first;
610             stackFrames.append(frame);
611         }
612
613         gotoLocation(stackFrames.value(0), true);
614         stackHandler()->setFrames(stackFrames);
615
616         watchHandler()->beginCycle();
617
618         foreach (WatchData data, watches) {
619             data.iname = watchHandler()->watcherName(data.exp);
620             watchHandler()->insertData(data);
621
622             if (watchHandler()->expandedINames().contains(data.iname))
623                 expandObject(data.iname, data.objectId);
624         }
625
626         foreach (WatchData data, locals) {
627             data.iname = "local." + data.exp;
628             watchHandler()->insertData(data);
629
630             if (watchHandler()->expandedINames().contains(data.iname))
631                 expandObject(data.iname, data.objectId);
632         }
633
634         watchHandler()->endCycle();
635
636     } else if (command == "RESULT") {
637         WatchData data;
638         QByteArray iname;
639         stream >> iname >> data;
640         data.iname = iname;
641         watchHandler()->insertData(data);
642
643     } else if (command == "EXPANDED") {
644         QList<WatchData> result;
645         QByteArray iname;
646         stream >> iname >> result;
647         foreach (WatchData data, result) {
648             data.iname = iname + '.' + data.exp;
649             watchHandler()->insertData(data);
650
651             if (watchHandler()->expandedINames().contains(data.iname)) 
652                 expandObject(data.iname, data.objectId);
653         }
654     } else if (command == "LOCALS") {
655         QList<WatchData> locals;
656         int frameId;
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);
665         }
666         watchHandler()->endCycle();
667     } else {
668         qDebug() << Q_FUNC_INFO << "Unknown command: " << command;
669     }
670
671 }
672
673
674 #if 0
675 class EngineComboBox : public QComboBox
676 {
677     Q_OBJECT
678 public:
679     struct EngineInfo
680     {
681         QString name;
682         int id;
683     };
684
685     EngineComboBox(QWidget *parent = 0);
686
687     void addEngine(int engine, const QString &name);
688     void clearEngines();
689
690 protected:
691
692 private:
693     QList<EngineInfo> m_engines;
694 };
695
696 EngineComboBox::EngineComboBox(QWidget *parent)
697     : QComboBox(parent)
698 {
699     setEnabled(false);
700     setEditable(false);
701 }
702
703 void EngineComboBox::addEngine(int engine, const QString &name)
704 {
705     EngineInfo info;
706     info.id = engine;
707     if (name.isEmpty())
708         info.name = tr("Engine %1", "engine number").arg(engine);
709     else
710         info.name = name;
711     m_engines << info;
712
713     addItem(info.name);
714 }
715
716 void EngineComboBox::clearEngines()
717 {
718     m_engines.clear();
719     clear();
720 }
721
722
723 bool QmlEngine::setDebugConfigurationDataFromProject(ProjectExplorer::Project *projectToDebug)
724 {
725     if (!projectToDebug) {
726         emit statusMessage(tr("Invalid project, debugging canceled."));
727         return false;
728     }
729
730     QmlProjectManager::QmlProjectRunConfiguration* config =
731             qobject_cast<QmlProjectManager::QmlProjectRunConfiguration*>(projectToDebug->activeTarget()->activeRunConfiguration());
732     if (!config) {
733         emit statusMessage(tr("Cannot find project run configuration, debugging canceled."));
734         return false;
735     }
736     m_runConfigurationDebugData.serverAddress = config->debugServerAddress();
737     m_runConfigurationDebugData.serverPort = config->debugServerPort();
738     m_connectionTimer->setInterval(ConnectionAttemptDefaultInterval);
739
740     return true;
741 }
742
743 void QmlEngine::startQmlProjectDebugger()
744 {
745     m_simultaneousCppAndQmlDebugMode = false;
746     m_connectionTimer->start();
747 }
748
749 bool QmlEngine::connectToViewer()
750 {
751     if (m_conn && m_conn->state() != QAbstractSocket::UnconnectedState)
752         return false;
753
754     delete m_engineDebugInterface; m_engineDebugInterface = 0;
755
756     if (m_conn) {
757         m_conn->disconnectFromHost();
758         delete m_conn;
759         m_conn = 0;
760     }
761
762     QString host = m_runConfigurationDebugData.serverAddress;
763     quint16 port = quint16(m_runConfigurationDebugData.serverPort);
764
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()));
770
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
774
775     if (!m_conn->waitForConnected())
776         return false;
777
778     QTC_ASSERT(m_debuggerRunControl, return false);
779     QmlEngine *engine = qobject_cast<QmlEngine *>(m_debuggerRunControl->engine());
780     QTC_ASSERT(engine, return false);
781
782     (void) new DebuggerClient(m_conn, engine);
783
784     return true;
785 }
786
787 void QmlEngine::disconnectFromViewer()
788 {
789     m_conn->disconnectFromHost();
790     updateMenuActions();
791 }
792
793 void QmlEngine::connectionStateChanged()
794 {
795     switch (m_conn->state()) {
796         case QAbstractSocket::UnconnectedState:
797         {
798             emit statusMessage(tr("[Inspector] disconnected.\n\n"));
799             resetViews();
800             updateMenuActions();
801             break;
802         }
803         case QAbstractSocket::HostLookupState:
804             emit statusMessage(tr("[Inspector] resolving host..."));
805             break;
806         case QAbstractSocket::ConnectingState:
807             emit statusMessage(tr("[Inspector] connecting to debug server..."));
808             break;
809         case QAbstractSocket::ConnectedState:
810         {
811             emit statusMessage(tr("[Inspector] connected.\n"));
812
813             resetViews();
814 //            m_frameRateWidget->reset(m_conn);
815
816             break;
817         }
818         case QAbstractSocket::ClosingState:
819             emit statusMessage(tr("[Inspector] closing..."));
820             break;
821         case QAbstractSocket::BoundState:
822         case QAbstractSocket::ListeningState:
823             break;
824     }
825 }
826
827 void QmlEngine::resetViews()
828 {
829     m_objectTreeWidget->cleanup();
830     m_propertiesWidget->clear();
831     m_expressionWidget->clear();
832     m_watchTableModel->removeAllWatches();
833 }
834
835 void QmlEngine::createDockWidgets()
836 {
837
838     m_engineComboBox = new Internal::EngineComboBox;
839     m_engineComboBox->setEnabled(false);
840     connect(m_engineComboBox, SIGNAL(currentIndexChanged(int)),
841             SLOT(queryEngineContext(int)));
842
843     // FancyMainWindow uses widgets' window titles for tab labels
844 //    m_frameRateWidget->setWindowTitle(tr("Frame rate"));
845
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);
852
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);
862
863
864     m_watchTableView->setModel(m_watchTableModel);
865     Internal::WatchTableHeaderView *header = new Internal::WatchTableHeaderView(m_watchTableModel);
866     m_watchTableView->setHorizontalHeader(header);
867
868     connect(m_objectTreeWidget, SIGNAL(activated(QDeclarativeDebugObjectReference)),
869             this, SLOT(treeObjectActivated(QDeclarativeDebugObjectReference)));
870
871     connect(m_objectTreeWidget, SIGNAL(currentObjectChanged(QDeclarativeDebugObjectReference)),
872             m_propertiesWidget, SLOT(reload(QDeclarativeDebugObjectReference)));
873
874     connect(m_objectTreeWidget, SIGNAL(expressionWatchRequested(QDeclarativeDebugObjectReference,QString)),
875             m_watchTableModel, SLOT(expressionWatchRequested(QDeclarativeDebugObjectReference,QString)));
876
877     connect(m_propertiesWidget, SIGNAL(watchToggleRequested(QDeclarativeDebugObjectReference,QDeclarativeDebugPropertyReference)),
878             m_watchTableModel, SLOT(togglePropertyWatch(QDeclarativeDebugObjectReference,QDeclarativeDebugPropertyReference)));
879
880     connect(m_watchTableModel, SIGNAL(watchCreated(QDeclarativeDebugWatch*)),
881             m_propertiesWidget, SLOT(watchCreated(QDeclarativeDebugWatch*)));
882
883     connect(m_watchTableModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
884             m_watchTableView, SLOT(scrollToBottom()));
885
886     connect(m_watchTableView, SIGNAL(objectActivated(int)),
887             m_objectTreeWidget, SLOT(setCurrentObject(int)));
888
889     connect(m_objectTreeWidget, SIGNAL(currentObjectChanged(QDeclarativeDebugObjectReference)),
890             m_expressionWidget, SLOT(setCurrentObject(QDeclarativeDebugObjectReference)));
891
892
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);
900
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);
907
908     InspectorOutputWidget *inspectorOutput = new InspectorOutputWidget();
909     inspectorOutput->setObjectName(QLatin1String("QmlDebugInspectorOutput"));
910     connect(this, SIGNAL(statusMessage(QString)),
911             inspectorOutput, SLOT(addInspectorStatus(QString)));
912
913     Debugger::DebuggerUISwitcher *uiSwitcher = Debugger::DebuggerUISwitcher::instance();
914
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);
924
925     m_expressionWidget->setWindowTitle(tr("Script Console"));
926     m_expressionQueryDock = uiSwitcher->createDockWidget(Qml::Constants::LANG_QML,
927                                                          m_expressionWidget, Qt::BottomDockWidgetArea);
928
929     m_inspectorOutputDock->setToolTip(tr("Output of the QML inspector, such as information on connecting to the server."));
930
931     m_dockWidgets << /*m_objectTreeDock << *//*m_frameRateDock << */ m_propertyWatcherDock
932                   << m_inspectorOutputDock << m_expressionQueryDock;
933
934     m_context = new Internal::InspectorContext(m_objectTreeWidget);
935     m_propWatcherContext = new Internal::InspectorContext(m_propertyWatcherDock);
936
937     Core::ICore *core = Core::ICore::instance();
938     core->addContextObject(m_propWatcherContext);
939     core->addContextObject(m_context);
940
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()));
945
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);
952
953     m_settings.readSettings(core->settings());
954     m_objectTreeWidget->readSettings(m_settings);
955     m_propertiesWidget->readSettings(m_settings);
956
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)));
965 }
966
967 void QmlEngine::simultaneouslyDebugQmlCppApplication()
968 {
969     QString errorMessage;
970     ProjectExplorer::ProjectExplorerPlugin *pex = ProjectExplorer::ProjectExplorerPlugin::instance();
971     ProjectExplorer::Project *project = pex->startupProject();
972
973     if (!project)
974          errorMessage = QString(tr("No project was found."));
975     else {
976         if (project->id() == "QmlProjectManager.QmlProject")
977             errorMessage = attachToQmlViewerAsExternalApp(project);
978         else {
979             errorMessage = attachToExternalCppAppWithQml(project);
980         }
981     }
982
983     if (!errorMessage.isEmpty())
984         QMessageBox::warning(Core::ICore::instance()->mainWindow(), "Failed to debug C++ and QML", errorMessage);
985 }
986
987 QString QmlEngine::attachToQmlViewerAsExternalApp(ProjectExplorer::Project *project)
988 {
989     m_debugMode = QmlProjectWithCppPlugins;
990
991     QmlProjectManager::QmlProjectRunConfiguration* runConfig =
992                 qobject_cast<QmlProjectManager::QmlProjectRunConfiguration*>(project->activeTarget()->activeRunConfiguration());
993
994     if (!runConfig)
995         return QString(tr("No run configurations were found for the project '%1'.").arg(project->displayName()));
996
997     Internal::StartExternalQmlDialog dlg(Debugger::DebuggerUISwitcher::instance()->mainWindow());
998
999     QString importPathArgument = "-I";
1000     QString execArgs;
1001     if (runConfig->viewerArguments().contains(importPathArgument))
1002         execArgs = runConfig->viewerArguments().join(" ");
1003     else {
1004         QFileInfo qmlFileInfo(runConfig->viewerArguments().last());
1005         importPathArgument.append(" " + qmlFileInfo.absolutePath() + " ");
1006         execArgs = importPathArgument + runConfig->viewerArguments().join(" ");
1007     }
1008
1009
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());
1016
1017     if (dlg.exec() != QDialog::Accepted)
1018         return QString();
1019
1020     m_runConfigurationDebugData.serverAddress = dlg.debuggerUrl();
1021     m_runConfigurationDebugData.serverPort = dlg.port();
1022     m_settings.setExternalPort(dlg.port());
1023     m_settings.setExternalUrl(dlg.debuggerUrl());
1024
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()));
1027
1028     Debugger::DebuggerRunControl *debuggableRunControl =
1029      createDebuggerRunControl(runConfig, dlg.qmlViewerPath(), dlg.qmlViewerArguments());
1030
1031     return executeDebuggerRunControl(debuggableRunControl, &customEnv);
1032 }
1033
1034 QString QmlEngine::attachToExternalCppAppWithQml(ProjectExplorer::Project *project)
1035 {
1036     m_debugMode = CppProjectWithQmlEngines;
1037
1038     ProjectExplorer::LocalApplicationRunConfiguration* runConfig =
1039                 qobject_cast<ProjectExplorer::LocalApplicationRunConfiguration*>(project->activeTarget()->activeRunConfiguration());
1040
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()));
1047
1048     Internal::StartExternalQmlDialog dlg(Debugger::DebuggerUISwitcher::instance()->mainWindow());
1049
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)
1055         return QString();
1056
1057     m_runConfigurationDebugData.serverAddress = dlg.debuggerUrl();
1058     m_runConfigurationDebugData.serverPort = dlg.port();
1059     m_settings.setExternalPort(dlg.port());
1060     m_settings.setExternalUrl(dlg.debuggerUrl());
1061
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);
1066 }
1067
1068 QString QmlEngine::executeDebuggerRunControl(Debugger::DebuggerRunControl *debuggableRunControl, ProjectExplorer::Environment *environment)
1069 {
1070     ProjectExplorer::ProjectExplorerPlugin *pex = ProjectExplorer::ProjectExplorerPlugin::instance();
1071
1072     // to make sure we have a valid, debuggable run control, find the correct factory for it
1073     if (debuggableRunControl) {
1074
1075         // modify the env
1076         debuggableRunControl->setCustomEnvironment(*environment);
1077
1078         pex->startRunControl(debuggableRunControl, ProjectExplorer::Constants::DEBUGMODE);
1079         m_simultaneousCppAndQmlDebugMode = true;
1080
1081         return QString();
1082     }
1083     return QString(tr("A valid run control was not registered in Qt Creator for this project run configuration."));;
1084 }
1085
1086 Debugger::DebuggerRunControl *QmlEngine::createDebuggerRunControl(ProjectExplorer::RunConfiguration *runConfig,
1087                                                                      const QString &executableFile, const QString &executableArguments)
1088 {
1089     ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
1090     const QList<Debugger::DebuggerRunControlFactory *> factories = pm->getObjects<Debugger::DebuggerRunControlFactory>();
1091     ProjectExplorer::RunControl *runControl = 0;
1092
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);
1100     }
1101
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);
1106         }
1107     }
1108
1109     return 0;
1110 }
1111
1112 void QmlEngine::updateMenuActions()
1113 {
1114
1115     bool enabled = true;
1116     if (m_simultaneousCppAndQmlDebugMode)
1117         enabled = (m_cppDebuggerState == Debugger::DebuggerNotReady && (!m_conn || m_conn->state() == QAbstractSocket::UnconnectedState));
1118     else
1119         enabled = (!m_conn || m_conn->state() == QAbstractSocket::UnconnectedState);
1120
1121     m_simultaneousDebugAction->setEnabled(enabled);
1122 }
1123
1124
1125 void QmlEngine::debuggerStateChanged(int newState)
1126 {
1127     if (m_simultaneousCppAndQmlDebugMode) {
1128
1129         switch(newState) {
1130         case Debugger::EngineSetupRequested:
1131             {
1132                 m_connectionInitialized = false;
1133                 break;
1134             }
1135         case Debugger::AdapterStartFailed:
1136         case Debugger::InferiorSetupFailed:
1137             emit statusMessage(QString(tr("Debugging failed: could not start C++ debugger.")));
1138             break;
1139         case Debugger::InferiorRunRequested:
1140             {
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);
1146                 }
1147                 break;
1148             }
1149         case Debugger::InferiorRunOk:
1150             {
1151                 if (!m_connectionInitialized) {
1152                     m_connectionInitialized = true;
1153                     m_connectionTimer->setInterval(ConnectionAttemptSimultaneousInterval);
1154                     m_connectionTimer->start();
1155                 }
1156                 break;
1157             }
1158         case Debugger::InferiorStopOk:
1159             {
1160                 m_objectTreeWidget->setEnabled(false);
1161                 m_propertiesWidget->setEnabled(false);
1162                 m_expressionWidget->setEnabled(false);
1163                 break;
1164             }
1165         case Debugger::EngineShutdownRequested:
1166             {
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;
1174                 break;
1175             }
1176         default:
1177             break;
1178         }
1179     }
1180
1181     m_cppDebuggerState = newState;
1182     updateMenuActions();
1183 }
1184
1185
1186 void QmlEngine::setSimpleDockWidgetArrangement()
1187 {
1188     Utils::FancyMainWindow *mainWindow = Debugger::DebuggerUISwitcher::instance()->mainWindow();
1189
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);
1196         }
1197     }
1198
1199     foreach (QDockWidget *dockWidget, dockWidgets) {
1200         if (m_dockWidgets.contains(dockWidget)) {
1201             mainWindow->addDockWidget(Qt::BottomDockWidgetArea, dockWidget);
1202             dockWidget->show();
1203         }
1204     }
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();
1210
1211     m_inspectorOutputDock->setVisible(false);
1212
1213     mainWindow->setTrackingEnabled(true);
1214 }
1215 #endif
1216 #if 0
1217 void QmlEngine::reloadEngines()
1218 {
1219     //m_engineComboBox->setEnabled(false);
1220
1221     QDeclarativeDebugEnginesQuery *query =
1222            m_engineDebugInterface->queryAvailableEngines(this);
1223     if (!query->isWaiting())
1224         enginesChanged(query);
1225     else
1226         QObject::connect(query, SIGNAL(stateChanged(QDeclarativeDebugQuery::State)),
1227                          this, SLOT(enginesChanged()));
1228 }
1229
1230 void QmlEngine::enginesChanged()
1231 {
1232     enginesChanged(qobject_cast<QDeclarativeDebugEnginesQuery *>(sender()));
1233 }
1234
1235 void QmlEngine::enginesChanged(QDeclarativeDebugEnginesQuery *query)
1236 {
1237     //m_engineComboBox->clearEngines();
1238     QList<QDeclarativeDebugEngineReference> engines = query->engines();
1239     if (engines.isEmpty())
1240         qWarning("QMLDEBUGGER: NO ENGINES FOUND!");
1241
1242     //m_engineComboBox->setEnabled(true);
1243
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());
1247
1248     if (engines.count() > 0) {
1249     //    m_engineComboBox->setCurrentIndex(engines.at(0).debugId());
1250         queryEngineContext(engines.at(0));
1251     }
1252 }
1253
1254 void QmlEngine::queryEngineContext(const QDeclarativeDebugEngineReference &engine)
1255 {
1256     QDeclarativeDebugRootContextQuery *query =
1257         m_engineDebugInterface->queryRootContexts(engine, this);
1258
1259     if (!query->isWaiting())
1260         contextChanged();
1261     else
1262         QObject::connect(query, SIGNAL(stateChanged(QDeclarativeDebugQuery::State)),
1263                          this, SLOT(contextChanged()));
1264 }
1265
1266 void QmlEngine::contextChanged()
1267 {
1268     contextChanged(qobject_cast<QDeclarativeDebugRootContextQuery *>(sender()));
1269 }
1270
1271 void QmlEngine::contextChanged(QDeclarativeDebugRootContextQuery *query)
1272 {
1273     QTC_ASSERT(query, return);
1274     //dump(query->rootContext(), 0);
1275     foreach (const QDeclarativeDebugObjectReference &object, query->rootContext().objects())
1276         reloadObject(object);
1277 }
1278
1279 void QmlEngine::reloadObject(const QDeclarativeDebugObjectReference &object)
1280 {
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);
1287     else
1288         QObject::connect(query, SIGNAL(stateChanged(QDeclarativeDebugQuery::State)),
1289                          this, SLOT(objectFetched(QDeclarativeDebugQuery::State)));
1290 }
1291
1292 void QmlEngine::objectFetched(QDeclarativeDebugQuery::State state)
1293 {
1294     objectFetched(qobject_cast<QDeclarativeDebugObjectQuery *>(sender()), state);
1295 }
1296
1297 void QmlEngine::objectFetched(QDeclarativeDebugObjectQuery *query,
1298     QDeclarativeDebugQuery::State state)
1299 {
1300     QTC_ASSERT(query, return);
1301     QTC_ASSERT(state == QDeclarativeDebugQuery::Completed, return);
1302     //dump(m_query->object(), 0);
1303
1304     m_watches.clear();
1305     buildTree(query->object(), "local");
1306
1307     qDebug() << "WATCHES CREATED: " << m_watches.size();
1308     //watchHandler()->beginCycle();
1309     //watchHandler()->insertBulkData(list);
1310     //watchHandler()->endCycle();
1311     //setCurrentItem(topLevelItem(0));
1312
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);
1320     //}
1321 }
1322
1323 void QmlEngine::buildTree(const QDeclarativeDebugObjectReference &obj,
1324     const QByteArray &iname)
1325 {
1326     //QTC_ASSERT(obj.contextDebugId() >= 0, return);
1327     WatchData data;
1328     data.iname = iname;
1329
1330     if (obj.idString().isEmpty())
1331         data.name = QString("<%1>").arg(obj.className());
1332     else
1333         data.name = obj.idString();
1334
1335     data.value = "?";
1336     data.type = "?";
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)
1342
1343     //data.userRole = qVariantFromValue(obj);
1344     /*
1345     if (parent && obj.contextDebugId() >= 0
1346             && obj.contextDebugId() != parent->data(0, Qt::UserRole
1347                     ).value<QDeclarativeDebugObjectReference>().contextDebugId())
1348     {
1349
1350         QDeclarativeDebugFileReference source = obj.source();
1351         if (!source.url().isEmpty()) {
1352             QString toolTipString = QLatin1String("URL: ") + source.url().toString();
1353             item->setToolTip(0, toolTipString);
1354         }
1355
1356     } else {
1357         item->setExpanded(true);
1358     }
1359
1360     if (obj.contextDebugId() < 0)
1361         item->setHasValidDebugId(false);
1362 */
1363
1364     for (int i = 0; i < obj.children().size(); ++i)
1365         buildTree(obj.children().at(i), iname + '.' + QByteArray::number(i));
1366 }
1367 #endif
1368 #if 0
1369 void QmlEngine::treeObjectActivated(const QDeclarativeDebugObjectReference &obj)
1370 {
1371     QDeclarativeDebugFileReference source = obj.source();
1372     QString fileName = source.url().toLocalFile();
1373
1374     if (source.lineNumber() < 0 || !QFile::exists(fileName))
1375         return;
1376
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);
1380
1381     if (textEditor) {
1382         editorManager->addCurrentPositionToNavigationHistory();
1383         textEditor->gotoLine(source.lineNumber());
1384         textEditor->widget()->setFocus();
1385     }
1386 }
1387
1388 bool QmlEngine::canEditProperty(const QString &propertyType)
1389 {
1390     return m_editablePropertyTypes.contains(propertyType);
1391 }
1392
1393 QDeclarativeDebugExpressionQuery *QmlEngine::executeExpression(int objectDebugId, const QString &objectId,
1394                                                                   const QString &propertyName, const QVariant &value)
1395 {
1396     //qDebug() << entity.property << entity.title << entity.objectId;
1397     if (objectId.length()) {
1398
1399         QString quoteWrappedValue = value.toString();
1400         if (addQuotesForData(value))
1401             quoteWrappedValue = QString("'%1'").arg(quoteWrappedValue);
1402
1403         QString constructedExpression = objectId + "." + propertyName + "=" + quoteWrappedValue;
1404         //qDebug() << "EXPRESSION:" << constructedExpression;
1405         return m_client->queryExpressionResult(objectDebugId, constructedExpression, this);
1406     }
1407
1408     return 0;
1409 }
1410
1411 bool QmlEngine::addQuotesForData(const QVariant &value) const
1412 {
1413     switch (value.type()) {
1414     case QVariant::String:
1415     case QVariant::Color:
1416     case QVariant::Date:
1417         return true;
1418     default:
1419         break;
1420     }
1421
1422     return false;
1423 }
1424
1425 ObjectTree::ObjectTree(QDeclarativeEngineDebug *client, QWidget *parent)
1426     : QTreeWidget(parent),
1427       m_client(client),
1428       m_query(0), m_clickedItem(0), m_showUninspectableItems(false),
1429       m_currentObjectDebugId(0), m_showUninspectableOnInitDone(false)
1430 {
1431     setAttribute(Qt::WA_MacShowFocusRect, false);
1432     setFrameStyle(QFrame::NoFrame);
1433     setHeaderHidden(true);
1434     setExpandsOnDoubleClick(false);
1435
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()));
1443
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()));
1449 }
1450
1451 void ObjectTree::readSettings(const InspectorSettings &settings)
1452 {
1453     if (settings.showUninspectableItems() != m_showUninspectableItems)
1454         toggleUninspectableItems();
1455 }
1456 void ObjectTree::saveSettings(InspectorSettings &settings)
1457 {
1458     settings.setShowUninspectableItems(m_showUninspectableItems);
1459 }
1460
1461 void ObjectTree::setEngineDebug(QDeclarativeEngineDebug *client)
1462 {
1463     m_client = client;
1464 }
1465
1466 void ObjectTree::toggleUninspectableItems()
1467 {
1468     m_showUninspectableItems = !m_showUninspectableItems;
1469     m_toggleUninspectableItemsAction->setChecked(m_showUninspectableItems);
1470     reload(m_currentObjectDebugId);
1471 }
1472
1473 void ObjectTree::selectionChanged()
1474 {
1475     if (selectedItems().isEmpty())
1476         return;
1477
1478     QTreeWidgetItem *item = selectedItems().first();
1479     if (item)
1480         emit contextHelpIdChanged(InspectorContext::contextHelpIdForItem(item->text(0)));
1481 }
1482
1483
1484 void ObjectTree::setCurrentObject(int debugId)
1485 {
1486     QTreeWidgetItem *item = findItemByObjectId(debugId);
1487     if (item) {
1488         setCurrentItem(item);
1489         scrollToItem(item);
1490         item->setExpanded(true);
1491     }
1492
1493
1494 }
1495
1496 {
1497     if (!item)
1498         return;
1499
1500     QDeclarativeDebugObjectReference obj = item->data(0, Qt::UserRole).value<QDeclarativeDebugObjectReference>();
1501     if (obj.debugId() >= 0)
1502         emit currentObjectChanged(obj);
1503 }
1504
1505 void ObjectTree::activated(QTreeWidgetItem *item)
1506 {
1507     if (!item)
1508         return;
1509
1510     QDeclarativeDebugObjectReference obj = item->data(0, Qt::UserRole).value<QDeclarativeDebugObjectReference>();
1511     if (obj.debugId() >= 0)
1512         emit activated(obj);
1513 }
1514
1515 void ObjectTree::cleanup()
1516 {
1517     m_showUninspectableOnInitDone = false;
1518     clear();
1519 }
1520
1521 void ObjectTree::dump(const QDeclarativeDebugContextReference &ctxt, int ind)
1522 {
1523     QByteArray indent(ind * 4, ' ');
1524     qWarning().nospace() << indent.constData() << ctxt.debugId() << " "
1525                          << qPrintable(ctxt.name());
1526
1527     for (int ii = 0; ii < ctxt.contexts().count(); ++ii)
1528         dump(ctxt.contexts().at(ii), ind + 1);
1529
1530     for (int ii = 0; ii < ctxt.objects().count(); ++ii)
1531         dump(ctxt.objects().at(ii), ind);
1532 }
1533
1534 void ObjectTree::dump(const QDeclarativeDebugObjectReference &obj, int ind)
1535 {
1536     QByteArray indent(ind * 4, ' ');
1537     qWarning().nospace() << indent.constData() << qPrintable(obj.className())
1538                          << " " << qPrintable(obj.idString()) << " "
1539                          << obj.debugId();
1540
1541     for (int ii = 0; ii < obj.children().count(); ++ii)
1542         dump(obj.children().at(ii), ind + 1);
1543 }
1544
1545 QTreeWidgetItem *ObjectTree::findItemByObjectId(int debugId) const
1546 {
1547     for (int i=0; i<topLevelItemCount(); ++i) {
1548         QTreeWidgetItem *item = findItem(topLevelItem(i), debugId);
1549         if (item)
1550             return item;
1551     }
1552
1553     return 0;
1554 }
1555
1556 QTreeWidgetItem *ObjectTree::findItem(QTreeWidgetItem *item, int debugId) const
1557 {
1558     if (item->data(0, Qt::UserRole).value<QDeclarativeDebugObjectReference>().debugId() == debugId)
1559         return item;
1560
1561     QTreeWidgetItem *child;
1562     for (int i=0; i<item->childCount(); ++i) {
1563         child = findItem(item->child(i), debugId);
1564         if (child)
1565             return child;
1566     }
1567
1568     return 0;
1569 }
1570
1571 void ObjectTree::addWatch()
1572 {
1573     QDeclarativeDebugObjectReference obj =
1574             currentItem()->data(0, Qt::UserRole).value<QDeclarativeDebugObjectReference>();
1575
1576     bool ok = false;
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);
1581
1582 }
1583
1584 void ObjectTree::goToFile()
1585 {
1586     QDeclarativeDebugObjectReference obj =
1587             currentItem()->data(0, Qt::UserRole).value<QDeclarativeDebugObjectReference>();
1588
1589     if (obj.debugId() >= 0)
1590         emit activated(obj);
1591 }
1592
1593 void ObjectTree::contextMenuEvent(QContextMenuEvent *event)
1594 {
1595
1596     m_clickedItem = itemAt(QPoint(event->pos().x(),
1597                                   event->pos().y() ));
1598     if (!m_clickedItem)
1599         return;
1600
1601     QMenu menu;
1602     menu.addAction(m_addWatchAction);
1603     menu.addAction(m_goToFileAction);
1604     if (m_currentObjectDebugId) {
1605         menu.addSeparator();
1606         menu.addAction(m_toggleUninspectableItemsAction);
1607     }
1608
1609     menu.exec(event->globalPos());
1610 }
1611
1612 } // Internal
1613 } // Qml
1614 #endif
1615
1616 } // namespace Internal
1617 } // namespace Debugger
1618
1619 #include "qmlengine.moc"