qmlengine.cpp 26.4 KB
Newer Older
1
2
3
4
5
6
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
hjk's avatar
hjk committed
7
** Contact: Nokia Corporation (info@qt.nokia.com)
8
9
10
11
**
**
** GNU Lesser General Public License Usage
**
hjk's avatar
hjk committed
12
13
14
15
16
17
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21
22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23
24
25
26
27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
con's avatar
con committed
28
** If you have questions regarding the use of this file, please contact
Tobias Hunger's avatar
Tobias Hunger committed
29
** Nokia at info@qt.nokia.com.
30
31
32
33
**
**************************************************************************/

#include "qmlengine.h"
Lasse Holmstedt's avatar
Lasse Holmstedt committed
34
#include "qmladapter.h"
35

Friedemann Kleint's avatar
Friedemann Kleint committed
36
#include "debuggerstartparameters.h"
37
#include "debuggeractions.h"
38
#include "debuggerconstants.h"
hjk's avatar
hjk committed
39
#include "debuggercore.h"
40
#include "debuggerdialogs.h"
41
#include "debuggermainwindow.h"
hjk's avatar
hjk committed
42
#include "debuggerrunner.h"
43
#include "debuggerstringutils.h"
44
#include "debuggertooltipmanager.h"
45

46
47
48
49
50
51
52
#include "breakhandler.h"
#include "moduleshandler.h"
#include "registerhandler.h"
#include "stackhandler.h"
#include "watchhandler.h"
#include "watchutils.h"

Lasse Holmstedt's avatar
Lasse Holmstedt committed
53
#include <extensionsystem/pluginmanager.h>
54
#include <projectexplorer/applicationlauncher.h>
55

56
#include <utils/environment.h>
57
#include <utils/qtcassert.h>
58
#include <utils/fileinprojectfinder.h>
59

60
#include <coreplugin/icore.h>
61
62
#include <coreplugin/helpmanager.h>

63
64
65
66
67
68
69
70
71
72
73
#include <QtCore/QDateTime>
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QTimer>

#include <QtGui/QAction>
#include <QtGui/QApplication>
#include <QtGui/QMainWindow>
#include <QtGui/QMessageBox>
#include <QtGui/QToolTip>
74
#include <QtGui/QTextDocument>
75
76

#include <QtNetwork/QTcpSocket>
77
78
#include <QtNetwork/QHostAddress>

79
80
81
82
83
84
85
86
#define DEBUG_QML 1
#if DEBUG_QML
#   define SDEBUG(s) qDebug() << s
#else
#   define SDEBUG(s)
#endif
# define XSDEBUG(s) qDebug() << s

hjk's avatar
hjk committed
87
88
using namespace ProjectExplorer;

89
90
91
namespace Debugger {
namespace Internal {

92
93
class QmlEnginePrivate
{
94
public:
95
96
    explicit QmlEnginePrivate(QmlEngine *q);

97
private:
98
    friend class QmlEngine;
hjk's avatar
hjk committed
99
100
    QmlAdapter m_adapter;
    ApplicationLauncher m_applicationLauncher;
101
    Utils::FileInProjectFinder fileFinder;
102
    QTimer m_noDebugOutputTimer;
103
    QString m_outputBuffer;
104
105
};

106
QmlEnginePrivate::QmlEnginePrivate(QmlEngine *q)
107
    : m_adapter(q)
108
109
{}

110

111
112
113
114
115
116
///////////////////////////////////////////////////////////////////////
//
// QmlEngine
//
///////////////////////////////////////////////////////////////////////

117
118
QmlEngine::QmlEngine(const DebuggerStartParameters &startParameters,
        DebuggerEngine *masterEngine)
119
  : DebuggerEngine(startParameters, QmlLanguage, masterEngine),
120
    d(new QmlEnginePrivate(this))
121
{
Friedemann Kleint's avatar
Friedemann Kleint committed
122
    setObjectName(QLatin1String("QmlEngine"));
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140

    ExtensionSystem::PluginManager *pluginManager =
        ExtensionSystem::PluginManager::instance();
    pluginManager->addObject(this);

    connect(&d->m_adapter, SIGNAL(connectionError(QAbstractSocket::SocketError)),
        SLOT(connectionError(QAbstractSocket::SocketError)));
    connect(&d->m_adapter, SIGNAL(serviceConnectionError(QString)),
        SLOT(serviceConnectionError(QString)));
    connect(&d->m_adapter, SIGNAL(connected()),
        SLOT(connectionEstablished()));
    connect(&d->m_adapter, SIGNAL(connectionStartupFailed()),
        SLOT(connectionStartupFailed()));

    connect(&d->m_applicationLauncher,
        SIGNAL(processExited(int)),
        SLOT(disconnected()));
    connect(&d->m_applicationLauncher,
141
142
143
144
145
146
        SIGNAL(appendMessage(QString, Utils::OutputFormat)),
        SLOT(appendMessage(QString, Utils::OutputFormat)));
    connect(&d->m_applicationLauncher,
            SIGNAL(processStarted()),
            &d->m_noDebugOutputTimer,
            SLOT(start()));
147

148
    // Only wait 8 seconds for the 'Waiting for connection' on application ouput, then just try to connect
149
150
151
152
    // (application output might be redirected / blocked)
    d->m_noDebugOutputTimer.setSingleShot(true);
    d->m_noDebugOutputTimer.setInterval(8000);
    connect(&d->m_noDebugOutputTimer, SIGNAL(timeout()), this, SLOT(beginConnection()));
153
154
155
}

QmlEngine::~QmlEngine()
hjk's avatar
hjk committed
156
{
157
158
159
160
161
162
    ExtensionSystem::PluginManager *pluginManager =
        ExtensionSystem::PluginManager::instance();

    if (pluginManager->allObjects().contains(this)) {
        pluginManager->removeObject(this);
    }
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
163

hjk's avatar
hjk committed
164
165
    delete d;
}
166

hjk's avatar
hjk committed
167
void QmlEngine::setupInferior()
168
{
hjk's avatar
hjk committed
169
    QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
Lasse Holmstedt's avatar
Lasse Holmstedt committed
170

171
    if (startParameters().startMode == AttachToRemoteServer) {
172
        emit requestRemoteSetup();
173
174
        if (startParameters().qmlServerPort != quint16(-1))
            notifyInferiorSetupOk();
175
176
177
    } if (startParameters().startMode == AttachToQmlPort) {
            notifyInferiorSetupOk();

178
179
180
181
182
183
    } else {
        d->m_applicationLauncher.setEnvironment(startParameters().environment);
        d->m_applicationLauncher.setWorkingDirectory(startParameters().workingDirectory);

        notifyInferiorSetupOk();
    }
hjk's avatar
hjk committed
184
}
hjk's avatar
hjk committed
185

con's avatar
con committed
186
void QmlEngine::appendMessage(const QString &msg, Utils::OutputFormat /* format */)
hjk's avatar
hjk committed
187
{
188
    showMessage(msg, AppOutput); // FIXME: Redirect to RunControl
hjk's avatar
hjk committed
189
190
}

Lasse Holmstedt's avatar
Lasse Holmstedt committed
191
192
193
194
void QmlEngine::connectionEstablished()
{
    attemptBreakpointSynchronization();

195
    showMessage(tr("QML Debugger connected."), StatusBar);
Lasse Holmstedt's avatar
Lasse Holmstedt committed
196

197
198
199
200
    if (!watchHandler()->watcherNames().isEmpty()) {
        synchronizeWatchers();
    }
    connect(watchersModel(),SIGNAL(layoutChanged()),this,SLOT(synchronizeWatchers()));
Christiaan Janssen's avatar
Christiaan Janssen committed
201

202
203
    if (state() == EngineRunRequested)
        notifyEngineRunAndInferiorRunOk();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
204
}
205

206
207
208
void QmlEngine::beginConnection()
{
    d->m_noDebugOutputTimer.stop();
209
    showMessage(tr("QML Debugger connecting..."), StatusBar);
210
    d->m_adapter.beginConnection();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
211
212
213
214
}

void QmlEngine::connectionStartupFailed()
{
215
216
217
218
219
220
221
222
    if (isSlaveEngine()) {
        if (masterEngine()->state() != InferiorRunOk) {
            // we're right now debugging C++, just try longer ...
            beginConnection();
            return;
        }
    }

223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
    Core::ICore * const core = Core::ICore::instance();
    QMessageBox *infoBox = new QMessageBox(core->mainWindow());
    infoBox->setIcon(QMessageBox::Critical);
    infoBox->setWindowTitle(tr("Qt Creator"));
    infoBox->setText(tr("Could not connect to the in-process QML debugger.\n"
                        "Do you want to retry?"));
    infoBox->setStandardButtons(QMessageBox::Retry | QMessageBox::Cancel | QMessageBox::Help);
    infoBox->setDefaultButton(QMessageBox::Retry);
    infoBox->setModal(true);

    connect(infoBox, SIGNAL(finished(int)),
            this, SLOT(retryMessageBoxFinished(int)));

    infoBox->show();
}

void QmlEngine::retryMessageBoxFinished(int result)
{
    switch (result) {
242
    case QMessageBox::Retry: {
243
        beginConnection();
244
245
246
247
248
        break;
    }
    case QMessageBox::Help: {
        Core::HelpManager *helpManager = Core::HelpManager::instance();
        helpManager->handleHelpRequest("qthelp://com.nokia.qtcreator/doc/creator-debugging-qml.html");
249
        // fall through
250
251
    }
    default:
252
253
254
255
        if (state() == InferiorRunOk) {
            notifyInferiorSpontaneousStop();
            notifyInferiorIll();
        } else {
256
        notifyEngineRunFailed();
257
        }
Kai Koehne's avatar
Kai Koehne committed
258
        break;
259
    }
Lasse Holmstedt's avatar
Lasse Holmstedt committed
260
261
}

262
void QmlEngine::connectionError(QAbstractSocket::SocketError socketError)
Lasse Holmstedt's avatar
Lasse Holmstedt committed
263
{
hjk's avatar
hjk committed
264
    if (socketError == QAbstractSocket::RemoteHostClosedError)
265
        showMessage(tr("QML Debugger: Remote host closed connection."), StatusBar);
266

267
268
269
270
    if (!isSlaveEngine()) { // normal flow for slave engine when gdb exits
        notifyInferiorSpontaneousStop();
        notifyInferiorIll();
    }
Lasse Holmstedt's avatar
Lasse Holmstedt committed
271
272
}

273
274
void QmlEngine::serviceConnectionError(const QString &serviceName)
{
275
276
    showMessage(tr("QML Debugger: Could not connect to service '%1'.")
        .arg(serviceName), StatusBar);
277
278
}

279
280
281
282
283
bool QmlEngine::canDisplayTooltip() const
{
    return state() == InferiorRunOk || state() == InferiorStopOk;
}

284
void QmlEngine::filterApplicationMessage(const QString &output, int /*channel*/)
285
{
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
    d->m_outputBuffer.append(output);
    while (d->m_outputBuffer.contains(QLatin1Char('\n'))) {
        const int nlIndex = d->m_outputBuffer.indexOf(QLatin1Char('\n'));
        const QString msg = d->m_outputBuffer.left(nlIndex);

        static const QString qddserver = QLatin1String("QDeclarativeDebugServer: ");
        static const QString cannotRetrieveDebuggingOutput = ApplicationLauncher::msgWinCannotRetrieveDebuggingOutput();

        const int index = msg.indexOf(qddserver);
        if (index != -1) {
            // we're actually getting debug output
            d->m_noDebugOutputTimer.stop();

            QString status = msg;
            status.remove(0, index + qddserver.length()); // chop of 'QDeclarativeDebugServer: '

            static QString waitingForConnection = QLatin1String("Waiting for connection ");
            static QString unableToListen = QLatin1String("Unable to listen ");
            static QString debuggingNotEnabled = QLatin1String("Ignoring \"-qmljsdebugger=");
            static QString debuggingNotEnabled2 = QLatin1String("Ignoring\"-qmljsdebugger="); // There is (was?) a bug in one of the error strings - safest to handle both
            static QString connectionEstablished = QLatin1String("Connection established");

            QString errorMessage;
            if (status.startsWith(waitingForConnection)) {
                beginConnection();
            } else if (status.startsWith(unableToListen)) {
                //: Error message shown after 'Could not connect ... debugger:"
                errorMessage = tr("The port seems to be in use.");
            } else if (status.startsWith(debuggingNotEnabled) || status.startsWith(debuggingNotEnabled2)) {
                //: Error message shown after 'Could not connect ... debugger:"
                errorMessage = tr("The application is not set up for QML/JS debugging.");
            } else if (status.startsWith(connectionEstablished)) {
                // nothing to do
            } else {
                qWarning() << "Unknown QDeclarativeDebugServer status message: " << status;
            }

            if (!errorMessage.isEmpty()) {
                notifyEngineRunFailed();

                Core::ICore * const core = Core::ICore::instance();
                QMessageBox *infoBox = new QMessageBox(core->mainWindow());
                infoBox->setIcon(QMessageBox::Critical);
                infoBox->setWindowTitle(tr("Qt Creator"));
                //: %1 is detailed error message
                infoBox->setText(tr("Could not connect to the in-process QML debugger:\n%1")
                                 .arg(errorMessage));
                infoBox->setStandardButtons(QMessageBox::Ok | QMessageBox::Help);
                infoBox->setDefaultButton(QMessageBox::Ok);
                infoBox->setModal(true);

                connect(infoBox, SIGNAL(finished(int)),
                        this, SLOT(wrongSetupMessageBoxFinished(int)));

                infoBox->show();
            }
        } else if (msg.contains(cannotRetrieveDebuggingOutput)) {
            // we won't get debugging output, so just try to connect ...
344
            beginConnection();
345
346
        }

347
        d->m_outputBuffer = d->m_outputBuffer.right(d->m_outputBuffer.size() - nlIndex - 1);
348
349
350
351
352
353
354
355
356
357
358
    }
}

void QmlEngine::showMessage(const QString &msg, int channel, int timeout) const
{
    if (channel == AppOutput || channel == AppError) {
        const_cast<QmlEngine*>(this)->filterApplicationMessage(msg, channel);
    }
    DebuggerEngine::showMessage(msg, channel, timeout);
}

359
360
void QmlEngine::closeConnection()
{
361
    disconnect(watchersModel(),SIGNAL(layoutChanged()),this,SLOT(synchronizeWatchers()));
362
    d->m_adapter.closeConnection();
363
364
}

hjk's avatar
hjk committed
365
366
367
void QmlEngine::runEngine()
{
    QTC_ASSERT(state() == EngineRunRequested, qDebug() << state());
Lasse Holmstedt's avatar
Lasse Holmstedt committed
368

369
    if (!isSlaveEngine() && startParameters().startMode != AttachToRemoteServer
370
            && startParameters().startMode != AttachToQmlPort)
371
        startApplicationLauncher();
372
373
374

    if (startParameters().startMode == AttachToQmlPort)
        beginConnection();
375
376
377
378
379
}

void QmlEngine::startApplicationLauncher()
{
    if (!d->m_applicationLauncher.isRunning()) {
380
381
382
383
        appendMessage(tr("Starting %1 %2").arg(
                          QDir::toNativeSeparators(startParameters().executable),
                          startParameters().processArgs)
                      + QLatin1Char('\n')
con's avatar
con committed
384
                     , Utils::NormalMessageFormat);
hjk's avatar
hjk committed
385
        d->m_applicationLauncher.start(ApplicationLauncher::Gui,
386
387
388
                                    startParameters().executable,
                                    startParameters().processArgs);
    }
389
}
Lasse Holmstedt's avatar
Lasse Holmstedt committed
390

391
392
393
394
395
396
void QmlEngine::stopApplicationLauncher()
{
    if (d->m_applicationLauncher.isRunning()) {
        disconnect(&d->m_applicationLauncher, SIGNAL(processExited(int)), this, SLOT(disconnected()));
        d->m_applicationLauncher.stop();
    }
397
398
}

Christian Kandeler's avatar
Christian Kandeler committed
399
void QmlEngine::handleRemoteSetupDone(int gdbServerPort, int qmlPort)
400
{
Christian Kandeler's avatar
Christian Kandeler committed
401
402
403
    Q_UNUSED(gdbServerPort);
    if (qmlPort != -1)
        startParameters().qmlServerPort = qmlPort;
404
405
406
407
408
409
410
411
412
413
    notifyInferiorSetupOk();
}

void QmlEngine::handleRemoteSetupFailed(const QString &message)
{
    QMessageBox::critical(0,tr("Failed to start application"),
        tr("Application startup failed: %1").arg(message));
    notifyInferiorSetupFailed();
}

hjk's avatar
hjk committed
414
void QmlEngine::shutdownInferior()
415
{
416
417
418
419
    d->m_noDebugOutputTimer.stop();

    if (d->m_adapter.activeDebuggerClient())
        d->m_adapter.activeDebuggerClient()->endSession();
420

421
422
    if (isSlaveEngine()) {
        resetLocation();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
423
    }
424
    stopApplicationLauncher();
425

hjk's avatar
hjk committed
426
427
428
429
430
    notifyInferiorShutdownOk();
}

void QmlEngine::shutdownEngine()
{
431
    closeConnection();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
432

433
434
    // double check (ill engine?):
    stopApplicationLauncher();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
435

436
    notifyEngineShutdownOk();
437
438
    if (!isSlaveEngine())
        showMessage(QString(), StatusBar);
439
440
}

hjk's avatar
hjk committed
441
void QmlEngine::setupEngine()
442
{
443
444
445
    connect(&d->m_applicationLauncher, SIGNAL(bringToForegroundRequested(qint64)),
            runControl(), SLOT(bringApplicationToForeground(qint64)),
            Qt::UniqueConnection);
Lasse Holmstedt's avatar
Lasse Holmstedt committed
446

447
    notifyEngineSetupOk();
448
449
}

450
451
void QmlEngine::continueInferior()
{
hjk's avatar
hjk committed
452
    QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
453
454
455
456
    if (d->m_adapter.activeDebuggerClient()) {
        logMessage(LogSend, "CONTINUE");
        d->m_adapter.activeDebuggerClient()->continueInferior();
    }
457
    resetLocation();
hjk's avatar
hjk committed
458
459
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
460
461
462
463
}

void QmlEngine::interruptInferior()
{
464
465
466
467
    if (d->m_adapter.activeDebuggerClient()) {
        logMessage(LogSend, "INTERRUPT");
        d->m_adapter.activeDebuggerClient()->interruptInferior();
    }
468
    notifyInferiorStopOk();
469
470
471
472
}

void QmlEngine::executeStep()
{
473
474
475
476
    if (d->m_adapter.activeDebuggerClient()) {
        logMessage(LogSend, "STEPINTO");
        d->m_adapter.activeDebuggerClient()->executeStep();
    }
hjk's avatar
hjk committed
477
478
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
479
480
481
482
}

void QmlEngine::executeStepI()
{
483
484
485
486
    if (d->m_adapter.activeDebuggerClient()) {
        logMessage(LogSend, "STEPINTO");
        d->m_adapter.activeDebuggerClient()->executeStepI();
    }
hjk's avatar
hjk committed
487
488
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
489
490
491
492
}

void QmlEngine::executeStepOut()
{
493
494
495
496
    if (d->m_adapter.activeDebuggerClient()) {
        logMessage(LogSend, "STEPOUT");
        d->m_adapter.activeDebuggerClient()->executeStepOut();
    }
hjk's avatar
hjk committed
497
498
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
499
500
501
502
}

void QmlEngine::executeNext()
{
503
504
505
506
    if (d->m_adapter.activeDebuggerClient()) {
        logMessage(LogSend, "STEPOVER");
        d->m_adapter.activeDebuggerClient()->executeNext();
    }
hjk's avatar
hjk committed
507
508
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
509
510
511
512
}

void QmlEngine::executeNextI()
{
513
    executeNext();
514
515
}

516
void QmlEngine::executeRunToLine(const ContextData &data)
517
{
518
    Q_UNUSED(data)
519
520
521
522
523
524
525
526
527
    SDEBUG("FIXME:  QmlEngine::executeRunToLine()");
}

void QmlEngine::executeRunToFunction(const QString &functionName)
{
    Q_UNUSED(functionName)
    XSDEBUG("FIXME:  QmlEngine::executeRunToFunction()");
}

528
void QmlEngine::executeJumpToLine(const ContextData &data)
529
{
530
    Q_UNUSED(data)
531
532
533
534
535
    XSDEBUG("FIXME:  QmlEngine::executeJumpToLine()");
}

void QmlEngine::activateFrame(int index)
{
536
537
538
    if (state() != InferiorStopOk && state() != InferiorUnrunnable)
        return;

539
540
541
542
    if (d->m_adapter.activeDebuggerClient()) {
        logMessage(LogSend, QString("%1 %2").arg(QString("ACTIVATE_FRAME"), QString::number(index)));
        d->m_adapter.activeDebuggerClient()->activateFrame(index);
    }
543
    gotoLocation(stackHandler()->frames().value(index));
544
545
546
547
548
549
550
}

void QmlEngine::selectThread(int index)
{
    Q_UNUSED(index)
}

551
552
553
554
555
556
557
558
void QmlEngine::insertBreakpoint(BreakpointModelId id)
{
    BreakHandler *handler = breakHandler();
    BreakpointState state = handler->state(id);
    QTC_ASSERT(state == BreakpointInsertRequested, qDebug() << id << this << state);
    handler->notifyBreakpointInsertProceeding(id);

    if (d->m_adapter.activeDebuggerClient()) {
559
        d->m_adapter.activeDebuggerClient()->insertBreakpoint(id);
560
561
    } else {
        foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients()) {
562
            client->insertBreakpoint(id);
563
564
565
566
567
568
569
570
571
572
573
574
        }
    }
}

void QmlEngine::removeBreakpoint(BreakpointModelId id)
{
    BreakHandler *handler = breakHandler();
    BreakpointState state = handler->state(id);
    QTC_ASSERT(state == BreakpointRemoveRequested, qDebug() << id << this << state);
    handler->notifyBreakpointRemoveProceeding(id);

    if (d->m_adapter.activeDebuggerClient()) {
575
        d->m_adapter.activeDebuggerClient()->removeBreakpoint(id);
576
577
    } else {
        foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients()) {
578
            client->removeBreakpoint(id);
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
        }
    }

    if (handler->state(id) == BreakpointRemoveProceeding) {
        handler->notifyBreakpointRemoveOk(id);
    }
}

void QmlEngine::changeBreakpoint(BreakpointModelId id)
{
    BreakHandler *handler = breakHandler();
    BreakpointState state = handler->state(id);
    QTC_ASSERT(state == BreakpointChangeRequested, qDebug() << id << this << state);
    handler->notifyBreakpointChangeProceeding(id);

    if (d->m_adapter.activeDebuggerClient()) {
595
        d->m_adapter.activeDebuggerClient()->changeBreakpoint(id);
596
597
    } else {
        foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients()) {
598
            client->changeBreakpoint(id);
599
600
601
602
603
604
605
606
        }
    }

    if (handler->state(id) == BreakpointChangeProceeding) {
        handler->notifyBreakpointChangeOk(id);
    }
}

607
608
void QmlEngine::attemptBreakpointSynchronization()
{
609
610
611
612
613
    if (!stateAcceptsBreakpointChanges()) {
        showMessage(_("BREAKPOINT SYNCHRONIZATION NOT POSSIBLE IN CURRENT STATE"));
        return;
    }

hjk's avatar
hjk committed
614
    BreakHandler *handler = breakHandler();
615

616
    foreach (BreakpointModelId id, handler->unclaimedBreakpointIds()) {
617
618
619
620
621
        // Take ownership of the breakpoint. Requests insertion.
        if (acceptsBreakpoint(id))
            handler->setEngine(id, this);
    }

622
    foreach (BreakpointModelId id, handler->engineBreakpointIds(this)) {
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
        switch (handler->state(id)) {
        case BreakpointNew:
            // Should not happen once claimed.
            QTC_CHECK(false);
            continue;
        case BreakpointInsertRequested:
            insertBreakpoint(id);
            continue;
        case BreakpointChangeRequested:
            changeBreakpoint(id);
            continue;
        case BreakpointRemoveRequested:
            removeBreakpoint(id);
            continue;
        case BreakpointChangeProceeding:
        case BreakpointInsertProceeding:
        case BreakpointRemoveProceeding:
        case BreakpointInserted:
        case BreakpointDead:
            continue;
643
        }
644
        QTC_ASSERT(false, qDebug() << "UNKNOWN STATE"  << id << state());
645
646
    }

647
    DebuggerEngine::attemptBreakpointSynchronization();
648

649
    if (d->m_adapter.activeDebuggerClient()) {
650
        d->m_adapter.activeDebuggerClient()->synchronizeBreakpoints();
651
652
    } else {
        foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients()) {
653
            client->synchronizeBreakpoints();
654
        }
655
    }
656
657
}

658
bool QmlEngine::acceptsBreakpoint(BreakpointModelId id) const
659
{
660
661
662
663
664
665
    if (!DebuggerEngine::isCppBreakpoint(breakHandler()->breakpointData(id)))
            return true;

    //If it is a Cpp Breakpoint query if the type can be also handled by the debugger client
    //TODO: enable setting of breakpoints before start of debug session
    //For now, the event breakpoint can be set after the activeDebuggerClient is known
666
    //This is because the older client does not support BreakpointOnQmlSignalHandler
667
668
669
670
671
    bool acceptBreakpoint = false;
    if (d->m_adapter.activeDebuggerClient()) {
        acceptBreakpoint = d->m_adapter.activeDebuggerClient()->acceptsBreakpoint(id);
    }
    return acceptBreakpoint;
672
673
}

674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
void QmlEngine::loadSymbols(const QString &moduleName)
{
    Q_UNUSED(moduleName)
}

void QmlEngine::loadAllSymbols()
{
}

void QmlEngine::reloadModules()
{
}

void QmlEngine::requestModuleSymbols(const QString &moduleName)
{
    Q_UNUSED(moduleName)
}

//////////////////////////////////////////////////////////////////////
//
// Tooltip specific stuff
//
//////////////////////////////////////////////////////////////////////

698
bool QmlEngine::setToolTipExpression(const QPoint &mousePos,
699
    TextEditor::ITextEditor *editor, const DebuggerToolTipContext &ctx)
700
{
701
    // This is processed by QML inspector, which has dependencies to
hjk's avatar
hjk committed
702
    // the qml js editor. Makes life easier.
703
    emit tooltipRequested(mousePos, editor, ctx.position);
704
    return true;
705
706
707
708
709
710
711
712
}

//////////////////////////////////////////////////////////////////////
//
// Watch specific stuff
//
//////////////////////////////////////////////////////////////////////

713
void QmlEngine::assignValueInDebugger(const WatchData *data,
hjk's avatar
hjk committed
714
    const QString &expression, const QVariant &valueV)
715
{
716
    quint64 objectId =  data->id;
717
    if (objectId > 0 && !expression.isEmpty() && d->m_adapter.activeDebuggerClient()) {
718
        logMessage(LogSend, QString("%1 %2 %3 %4 %5").arg(
719
720
                       QString("SET_PROPERTY"), QString::number(objectId), QString(expression),
                       valueV.toString()));
721
        d->m_adapter.activeDebuggerClient()->assignValueInDebugger(expression.toUtf8(), objectId, expression, valueV.toString());
722
    }
723
724
}

hjk's avatar
hjk committed
725
726
void QmlEngine::updateWatchData(const WatchData &data,
    const WatchUpdateFlags &)
727
{
Olivier Goffart's avatar
Olivier Goffart committed
728
//    qDebug() << "UPDATE WATCH DATA" << data.toString();
729
    //watchHandler()->rebuildModel();
730
    showStatusMessage(tr("Stopped."), 5000);
731

732
733
734
735
    if (!data.name.isEmpty() && d->m_adapter.activeDebuggerClient()) {
        if (data.isValueNeeded()) {
            logMessage(LogSend, QString("%1 %2 %3").arg(QString("EXEC"), QString(data.iname),
                                                        QString(data.name)));
736
            d->m_adapter.activeDebuggerClient()->updateWatchData(data);
737
738
739
740
741
        }
        if (data.isChildrenNeeded()
                && watchHandler()->isExpandedIName(data.iname)) {
            d->m_adapter.activeDebuggerClient()->expandObject(data.iname, data.id);
        }
Christiaan Janssen's avatar
Christiaan Janssen committed
742
743
744
    }

    synchronizeWatchers();
745

Christiaan Janssen's avatar
Christiaan Janssen committed
746
747
748
749
750
751
    if (!data.isSomethingNeeded())
        watchHandler()->insertData(data);
}

void QmlEngine::synchronizeWatchers()
{
752
    QStringList watchedExpressions = watchHandler()->watchedExpressions();
753
754
    // send watchers list
    logMessage(LogSend, QString("%1 %2").arg(
755
756
757
                   QString("WATCH_EXPRESSIONS"), watchedExpressions.join(", ")));
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->synchronizeWatchers(watchedExpressions);
758
    } else {
759
760
761
        foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients())
            client->synchronizeWatchers(watchedExpressions);
    }
762
763
}

764
765
unsigned QmlEngine::debuggerCapabilities() const
{
766
    return AddWatcherCapability|AddWatcherWhileRunningCapability;
767
768
769
770
771
772
773
774
775
    /*ReverseSteppingCapability | SnapshotCapability
        | AutoDerefPointersCapability | DisassemblerCapability
        | RegisterCapability | ShowMemoryCapability
        | JumpToLineCapability | ReloadModuleCapability
        | ReloadModuleSymbolsCapability | BreakOnThrowAndCatchCapability
        | ReturnFromFunctionCapability
        | CreateFullBacktraceCapability
        | WatchpointCapability
        | AddWatcherCapability;*/
776
777
}

778
QString QmlEngine::toFileInProject(const QUrl &fileUrl)
779
{
780
781
782
783
    // make sure file finder is properly initialized
    d->fileFinder.setProjectDirectory(startParameters().projectSourceDirectory);
    d->fileFinder.setProjectFiles(startParameters().projectSourceFiles);
    d->fileFinder.setSysroot(startParameters().sysroot);
784

785
    return d->fileFinder.findFile(fileUrl);
786
787
}

788
void QmlEngine::inferiorSpontaneousStop()
789
{
790
791
    if (state() == InferiorRunOk)
        notifyInferiorSpontaneousStop();
792
793
}

794
795
void QmlEngine::disconnected()
{
796
    showMessage(tr("QML Debugger disconnected."), StatusBar);
797
798
799
    notifyInferiorExited();
}

800
void QmlEngine::wrongSetupMessageBoxFinished(int result)
801
802
803
804
805
806
807
808
{
    if (result == QMessageBox::Help) {
        Core::HelpManager *helpManager = Core::HelpManager::instance();
        helpManager->handleHelpRequest(
                    QLatin1String("qthelp://com.nokia.qtcreator/doc/creator-debugging-qml.html"));
    }
}

809
810
void QmlEngine::executeDebuggerCommand(const QString& command)
{
811
812
813
814
815
    if (d->m_adapter.activeDebuggerClient()) {
        logMessage(LogSend, QString("%1 %2 %3").arg(QString("EXEC"), QString("console"),
                                                          QString(command)));
        d->m_adapter.activeDebuggerClient()->executeDebuggerCommand(command);
    }
816
817
}

818
819
820

QString QmlEngine::qmlImportPath() const
{
821
    return startParameters().environment.value("QML_IMPORT_PATH");
822
823
}

824
825
void QmlEngine::logMessage(LogDirection direction, const QString &message)
{
826
    QString msg = "QmlDebugger";
827
828
829
830
831
832
833
834
835
    if (direction == LogSend) {
        msg += " sending ";
    } else {
        msg += " receiving ";
    }
    msg += message;
    showMessage(msg, LogDebug);
}

836
837
838
839
840
QmlAdapter *QmlEngine::adapter() const
{
    return &d->m_adapter;
}

841
QmlEngine *createQmlEngine(const DebuggerStartParameters &sp,
842
843
844
845
846
    DebuggerEngine *masterEngine)
{
    return new QmlEngine(sp, masterEngine);
}

847
} // namespace Internal
848
} // namespace Debugger
849