qmlengine.cpp 23.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).
**
7
** Contact: Nokia Corporation (qt-info@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
29
** Nokia at qt-info@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
#include <qmljsdebugclient/qdeclarativeoutputparser.h>
56

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

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

64 65 66 67 68 69 70 71 72 73 74
#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>
75
#include <QtGui/QTextDocument>
76 77

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

80 81 82 83 84 85 86 87
#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
88 89
using namespace ProjectExplorer;

90 91 92
namespace Debugger {
namespace Internal {

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

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

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

111

112 113 114 115 116 117
///////////////////////////////////////////////////////////////////////
//
// QmlEngine
//
///////////////////////////////////////////////////////////////////////

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

    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,
142 143 144 145 146 147
        SIGNAL(appendMessage(QString, Utils::OutputFormat)),
        SLOT(appendMessage(QString, Utils::OutputFormat)));
    connect(&d->m_applicationLauncher,
            SIGNAL(processStarted()),
            &d->m_noDebugOutputTimer,
            SLOT(start()));
148

149 150 151 152 153 154 155 156
    d->m_outputParser.setNoOutputText(ApplicationLauncher::msgWinCannotRetrieveDebuggingOutput());
    connect(&d->m_outputParser, SIGNAL(waitingForConnectionMessage()),
            this, SLOT(beginConnection()));
    connect(&d->m_outputParser, SIGNAL(noOutputMessage()),
            this, SLOT(beginConnection()));
    connect(&d->m_outputParser, SIGNAL(errorMessage(QString)),
            this, SLOT(wrongSetupMessageBox(QString)));

157
    // Only wait 8 seconds for the 'Waiting for connection' on application ouput, then just try to connect
158 159 160 161
    // (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()));
162 163 164
}

QmlEngine::~QmlEngine()
hjk's avatar
hjk committed
165
{
166 167 168 169 170 171
    ExtensionSystem::PluginManager *pluginManager =
        ExtensionSystem::PluginManager::instance();

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

hjk's avatar
hjk committed
173 174
    delete d;
}
175

hjk's avatar
hjk committed
176
void QmlEngine::setupInferior()
177
{
hjk's avatar
hjk committed
178
    QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
Lasse Holmstedt's avatar
Lasse Holmstedt committed
179

180
    if (startParameters().startMode == AttachToRemoteServer) {
181
        emit requestRemoteSetup();
182 183
        if (startParameters().qmlServerPort != quint16(-1))
            notifyInferiorSetupOk();
184 185 186
    } if (startParameters().startMode == AttachToQmlPort) {
            notifyInferiorSetupOk();

187 188 189 190 191 192
    } else {
        d->m_applicationLauncher.setEnvironment(startParameters().environment);
        d->m_applicationLauncher.setWorkingDirectory(startParameters().workingDirectory);

        notifyInferiorSetupOk();
    }
hjk's avatar
hjk committed
193
}
hjk's avatar
hjk committed
194

con's avatar
con committed
195
void QmlEngine::appendMessage(const QString &msg, Utils::OutputFormat /* format */)
hjk's avatar
hjk committed
196
{
197
    showMessage(msg, AppOutput); // FIXME: Redirect to RunControl
hjk's avatar
hjk committed
198 199
}

Lasse Holmstedt's avatar
Lasse Holmstedt committed
200 201 202 203
void QmlEngine::connectionEstablished()
{
    attemptBreakpointSynchronization();

204 205 206 207
    if (!watchHandler()->watcherNames().isEmpty()) {
        synchronizeWatchers();
    }
    connect(watchersModel(),SIGNAL(layoutChanged()),this,SLOT(synchronizeWatchers()));
Christiaan Janssen's avatar
Christiaan Janssen committed
208

209 210
    if (state() == EngineRunRequested)
        notifyEngineRunAndInferiorRunOk();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
211
}
212

213 214 215 216
void QmlEngine::beginConnection()
{
    d->m_noDebugOutputTimer.stop();
    d->m_adapter.beginConnection();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
217 218 219 220
}

void QmlEngine::connectionStartupFailed()
{
221 222 223 224 225 226 227 228
    if (isSlaveEngine()) {
        if (masterEngine()->state() != InferiorRunOk) {
            // we're right now debugging C++, just try longer ...
            beginConnection();
            return;
        }
    }

229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
    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) {
248
    case QMessageBox::Retry: {
249
        beginConnection();
250 251 252 253 254
        break;
    }
    case QMessageBox::Help: {
        Core::HelpManager *helpManager = Core::HelpManager::instance();
        helpManager->handleHelpRequest("qthelp://com.nokia.qtcreator/doc/creator-debugging-qml.html");
255
        // fall through
256 257
    }
    default:
258 259 260 261
        if (state() == InferiorRunOk) {
            notifyInferiorSpontaneousStop();
            notifyInferiorIll();
        } else {
262
        notifyEngineRunFailed();
263
        }
Kai Koehne's avatar
Kai Koehne committed
264
        break;
265
    }
Lasse Holmstedt's avatar
Lasse Holmstedt committed
266 267
}

268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
void QmlEngine::wrongSetupMessageBox(const QString &errorMessage)
{
    d->m_noDebugOutputTimer.stop();
    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();
}

290
void QmlEngine::connectionError(QAbstractSocket::SocketError socketError)
Lasse Holmstedt's avatar
Lasse Holmstedt committed
291
{
hjk's avatar
hjk committed
292
    if (socketError == QAbstractSocket::RemoteHostClosedError)
293
        showMessage(tr("QML Debugger: Remote host closed connection."), StatusBar);
294

295 296 297 298
    if (!isSlaveEngine()) { // normal flow for slave engine when gdb exits
        notifyInferiorSpontaneousStop();
        notifyInferiorIll();
    }
Lasse Holmstedt's avatar
Lasse Holmstedt committed
299 300
}

301 302
void QmlEngine::serviceConnectionError(const QString &serviceName)
{
303 304
    showMessage(tr("QML Debugger: Could not connect to service '%1'.")
        .arg(serviceName), StatusBar);
305 306
}

307 308 309 310 311
bool QmlEngine::canDisplayTooltip() const
{
    return state() == InferiorRunOk || state() == InferiorStopOk;
}

312
void QmlEngine::filterApplicationMessage(const QString &output, int /*channel*/)
313
{
314
    d->m_outputParser.processOutput(output);
315 316 317 318 319 320 321 322 323 324
}

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

325 326
void QmlEngine::closeConnection()
{
327
    disconnect(watchersModel(),SIGNAL(layoutChanged()),this,SLOT(synchronizeWatchers()));
328
    d->m_adapter.closeConnection();
329 330
}

hjk's avatar
hjk committed
331 332 333
void QmlEngine::runEngine()
{
    QTC_ASSERT(state() == EngineRunRequested, qDebug() << state());
Lasse Holmstedt's avatar
Lasse Holmstedt committed
334

335
    if (!isSlaveEngine() && startParameters().startMode != AttachToRemoteServer
336
            && startParameters().startMode != AttachToQmlPort)
337
        startApplicationLauncher();
338 339 340

    if (startParameters().startMode == AttachToQmlPort)
        beginConnection();
341 342 343 344 345
}

void QmlEngine::startApplicationLauncher()
{
    if (!d->m_applicationLauncher.isRunning()) {
346 347 348 349
        appendMessage(tr("Starting %1 %2").arg(
                          QDir::toNativeSeparators(startParameters().executable),
                          startParameters().processArgs)
                      + QLatin1Char('\n')
con's avatar
con committed
350
                     , Utils::NormalMessageFormat);
hjk's avatar
hjk committed
351
        d->m_applicationLauncher.start(ApplicationLauncher::Gui,
352 353 354
                                    startParameters().executable,
                                    startParameters().processArgs);
    }
355
}
Lasse Holmstedt's avatar
Lasse Holmstedt committed
356

357 358 359 360 361 362
void QmlEngine::stopApplicationLauncher()
{
    if (d->m_applicationLauncher.isRunning()) {
        disconnect(&d->m_applicationLauncher, SIGNAL(processExited(int)), this, SLOT(disconnected()));
        d->m_applicationLauncher.stop();
    }
363 364
}

Christian Kandeler's avatar
Christian Kandeler committed
365
void QmlEngine::handleRemoteSetupDone(int gdbServerPort, int qmlPort)
366
{
Christian Kandeler's avatar
Christian Kandeler committed
367 368 369
    Q_UNUSED(gdbServerPort);
    if (qmlPort != -1)
        startParameters().qmlServerPort = qmlPort;
370 371 372 373 374 375 376 377 378 379
    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
380
void QmlEngine::shutdownInferior()
381
{
382 383 384 385
    d->m_noDebugOutputTimer.stop();

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

387 388
    if (isSlaveEngine()) {
        resetLocation();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
389
    }
390
    stopApplicationLauncher();
391

hjk's avatar
hjk committed
392 393 394 395 396
    notifyInferiorShutdownOk();
}

void QmlEngine::shutdownEngine()
{
397
    closeConnection();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
398

399 400
    // double check (ill engine?):
    stopApplicationLauncher();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
401

402
    notifyEngineShutdownOk();
403 404
    if (!isSlaveEngine())
        showMessage(QString(), StatusBar);
405 406
}

hjk's avatar
hjk committed
407
void QmlEngine::setupEngine()
408
{
409 410 411
    connect(&d->m_applicationLauncher, SIGNAL(bringToForegroundRequested(qint64)),
            runControl(), SLOT(bringApplicationToForeground(qint64)),
            Qt::UniqueConnection);
Lasse Holmstedt's avatar
Lasse Holmstedt committed
412

413
    notifyEngineSetupOk();
414 415
}

416 417
void QmlEngine::continueInferior()
{
hjk's avatar
hjk committed
418
    QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
419 420 421
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->continueInferior();
    }
422
    resetLocation();
hjk's avatar
hjk committed
423 424
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
425 426 427 428
}

void QmlEngine::interruptInferior()
{
429 430 431
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->interruptInferior();
    }
432
    notifyInferiorStopOk();
433 434 435 436
}

void QmlEngine::executeStep()
{
437 438 439
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->executeStep();
    }
hjk's avatar
hjk committed
440 441
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
442 443 444 445
}

void QmlEngine::executeStepI()
{
446 447 448
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->executeStepI();
    }
hjk's avatar
hjk committed
449 450
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
451 452 453 454
}

void QmlEngine::executeStepOut()
{
455 456 457
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->executeStepOut();
    }
hjk's avatar
hjk committed
458 459
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
460 461 462 463
}

void QmlEngine::executeNext()
{
464 465 466
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->executeNext();
    }
hjk's avatar
hjk committed
467 468
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
469 470 471 472
}

void QmlEngine::executeNextI()
{
473
    executeNext();
474 475
}

476
void QmlEngine::executeRunToLine(const ContextData &data)
477
{
478
    Q_UNUSED(data)
479 480 481 482 483 484 485 486 487
    SDEBUG("FIXME:  QmlEngine::executeRunToLine()");
}

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

488
void QmlEngine::executeJumpToLine(const ContextData &data)
489
{
490
    Q_UNUSED(data)
491 492 493 494 495
    XSDEBUG("FIXME:  QmlEngine::executeJumpToLine()");
}

void QmlEngine::activateFrame(int index)
{
496 497 498
    if (state() != InferiorStopOk && state() != InferiorUnrunnable)
        return;

499 500 501
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->activateFrame(index);
    }
502
    gotoLocation(stackHandler()->frames().value(index));
503 504 505 506 507 508 509
}

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

510 511 512 513 514 515 516 517
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()) {
518
        d->m_adapter.activeDebuggerClient()->insertBreakpoint(id);
519 520
    } else {
        foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients()) {
521
            client->insertBreakpoint(id);
522 523 524 525 526 527 528 529 530 531 532 533
        }
    }
}

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()) {
534
        d->m_adapter.activeDebuggerClient()->removeBreakpoint(id);
535 536
    } else {
        foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients()) {
537
            client->removeBreakpoint(id);
538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553
        }
    }

    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()) {
554
        d->m_adapter.activeDebuggerClient()->changeBreakpoint(id);
555 556
    } else {
        foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients()) {
557
            client->changeBreakpoint(id);
558 559 560 561 562 563 564 565
        }
    }

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

566 567
void QmlEngine::attemptBreakpointSynchronization()
{
568 569 570 571 572
    if (!stateAcceptsBreakpointChanges()) {
        showMessage(_("BREAKPOINT SYNCHRONIZATION NOT POSSIBLE IN CURRENT STATE"));
        return;
    }

hjk's avatar
hjk committed
573
    BreakHandler *handler = breakHandler();
574

575
    foreach (BreakpointModelId id, handler->unclaimedBreakpointIds()) {
576 577 578 579 580
        // Take ownership of the breakpoint. Requests insertion.
        if (acceptsBreakpoint(id))
            handler->setEngine(id, this);
    }

581
    foreach (BreakpointModelId id, handler->engineBreakpointIds(this)) {
582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601
        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;
602
        }
603
        QTC_ASSERT(false, qDebug() << "UNKNOWN STATE"  << id << state());
604 605
    }

606
    DebuggerEngine::attemptBreakpointSynchronization();
607

608
    if (d->m_adapter.activeDebuggerClient()) {
609
        d->m_adapter.activeDebuggerClient()->synchronizeBreakpoints();
610 611
    } else {
        foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients()) {
612
            client->synchronizeBreakpoints();
613
        }
614
    }
615 616
}

617
bool QmlEngine::acceptsBreakpoint(BreakpointModelId id) const
618
{
619 620 621 622 623 624
    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
625
    //This is because the older client does not support BreakpointOnQmlSignalHandler
626 627 628 629 630
    bool acceptBreakpoint = false;
    if (d->m_adapter.activeDebuggerClient()) {
        acceptBreakpoint = d->m_adapter.activeDebuggerClient()->acceptsBreakpoint(id);
    }
    return acceptBreakpoint;
631 632
}

633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656
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
//
//////////////////////////////////////////////////////////////////////

657
bool QmlEngine::setToolTipExpression(const QPoint &mousePos,
658
    TextEditor::ITextEditor *editor, const DebuggerToolTipContext &ctx)
659
{
660
    // This is processed by QML inspector, which has dependencies to
hjk's avatar
hjk committed
661
    // the qml js editor. Makes life easier.
662
    emit tooltipRequested(mousePos, editor, ctx.position);
663
    return true;
664 665 666 667 668 669 670 671
}

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

672
void QmlEngine::assignValueInDebugger(const WatchData *data,
hjk's avatar
hjk committed
673
    const QString &expression, const QVariant &valueV)
674
{
675
    quint64 objectId =  data->id;
676
    if (objectId > 0 && !expression.isEmpty() && d->m_adapter.activeDebuggerClient()) {
677
        d->m_adapter.activeDebuggerClient()->assignValueInDebugger(expression.toUtf8(), objectId, expression, valueV.toString());
678
    }
679 680
}

hjk's avatar
hjk committed
681 682
void QmlEngine::updateWatchData(const WatchData &data,
    const WatchUpdateFlags &)
683
{
Olivier Goffart's avatar
Olivier Goffart committed
684
//    qDebug() << "UPDATE WATCH DATA" << data.toString();
685
    //watchHandler()->rebuildModel();
686
    showStatusMessage(tr("Stopped."), 5000);
687

688 689
    if (!data.name.isEmpty() && d->m_adapter.activeDebuggerClient()) {
        if (data.isValueNeeded()) {
690
            d->m_adapter.activeDebuggerClient()->updateWatchData(data);
691 692 693 694 695
        }
        if (data.isChildrenNeeded()
                && watchHandler()->isExpandedIName(data.iname)) {
            d->m_adapter.activeDebuggerClient()->expandObject(data.iname, data.id);
        }
Christiaan Janssen's avatar
Christiaan Janssen committed
696 697 698
    }

    synchronizeWatchers();
699

Christiaan Janssen's avatar
Christiaan Janssen committed
700 701 702 703 704 705
    if (!data.isSomethingNeeded())
        watchHandler()->insertData(data);
}

void QmlEngine::synchronizeWatchers()
{
706
    QStringList watchedExpressions = watchHandler()->watchedExpressions();
707
    // send watchers list
708 709
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->synchronizeWatchers(watchedExpressions);
710
    } else {
711 712 713
        foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients())
            client->synchronizeWatchers(watchedExpressions);
    }
714 715
}

716 717
unsigned QmlEngine::debuggerCapabilities() const
{
718
    return AddWatcherCapability|AddWatcherWhileRunningCapability;
719 720 721 722 723 724 725 726 727
    /*ReverseSteppingCapability | SnapshotCapability
        | AutoDerefPointersCapability | DisassemblerCapability
        | RegisterCapability | ShowMemoryCapability
        | JumpToLineCapability | ReloadModuleCapability
        | ReloadModuleSymbolsCapability | BreakOnThrowAndCatchCapability
        | ReturnFromFunctionCapability
        | CreateFullBacktraceCapability
        | WatchpointCapability
        | AddWatcherCapability;*/
728 729
}

730
QString QmlEngine::toFileInProject(const QUrl &fileUrl)
731
{
732 733 734 735
    // make sure file finder is properly initialized
    d->fileFinder.setProjectDirectory(startParameters().projectSourceDirectory);
    d->fileFinder.setProjectFiles(startParameters().projectSourceFiles);
    d->fileFinder.setSysroot(startParameters().sysroot);
736

737
    return d->fileFinder.findFile(fileUrl);
738 739
}

740
void QmlEngine::inferiorSpontaneousStop()
741
{
742 743
    if (state() == InferiorRunOk)
        notifyInferiorSpontaneousStop();
744 745
}

746 747
void QmlEngine::disconnected()
{
748
    showMessage(tr("QML Debugger disconnected."), StatusBar);
749 750 751
    notifyInferiorExited();
}

752
void QmlEngine::wrongSetupMessageBoxFinished(int result)
753 754 755 756 757 758 759 760
{
    if (result == QMessageBox::Help) {
        Core::HelpManager *helpManager = Core::HelpManager::instance();
        helpManager->handleHelpRequest(
                    QLatin1String("qthelp://com.nokia.qtcreator/doc/creator-debugging-qml.html"));
    }
}

761 762
void QmlEngine::executeDebuggerCommand(const QString& command)
{
763 764 765
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->executeDebuggerCommand(command);
    }
766 767
}

768 769 770

QString QmlEngine::qmlImportPath() const
{
771
    return startParameters().environment.value("QML_IMPORT_PATH");
772 773
}

774
void QmlEngine::logMessage(const QString &service, LogDirection direction, const QString &message)
775
{
776
    QString msg = service;
777
    if (direction == LogSend) {
778
        msg += ": sending ";
779
    } else {
780
        msg += ": receiving ";
781 782 783 784 785
    }
    msg += message;
    showMessage(msg, LogDebug);
}

786 787 788 789 790
QmlAdapter *QmlEngine::adapter() const
{
    return &d->m_adapter;
}

791
QmlEngine *createQmlEngine(const DebuggerStartParameters &sp,
792 793 794 795 796
    DebuggerEngine *masterEngine)
{
    return new QmlEngine(sp, masterEngine);
}

797
} // namespace Internal
798
} // namespace Debugger
799