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