qmlinspectoradapter.cpp 13.4 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
Eike Ziller's avatar
Eike Ziller committed
3 4
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
7
**
hjk's avatar
hjk committed
8 9 10 11
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
Eike Ziller's avatar
Eike Ziller committed
12 13
** a written agreement between you and The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
Eike Ziller's avatar
Eike Ziller committed
14
** use the contact form at http://www.qt.io/contact-us.
15 16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18 19 20 21 22 23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
hjk's avatar
hjk committed
24
**
Eike Ziller's avatar
Eike Ziller committed
25 26
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company LGPL Exception
27 28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
30 31 32 33 34

#include "qmlinspectoradapter.h"

#include "qmladapter.h"
#include "qmlinspectoragent.h"
35 36 37 38
#include <debugger/debuggeractions.h>
#include <debugger/debuggercore.h>
#include <debugger/debuggerstringutils.h>
#include <debugger/debuggerengine.h>
39 40 41

#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/icore.h>
42 43 44
#include <coreplugin/idocument.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/editormanager/documentmodel.h>
45
#include <qmldebug/declarativeenginedebugclient.h>
46
#include <qmldebug/declarativeenginedebugclientv2.h>
47 48 49 50 51 52
#include <qmldebug/declarativetoolsclient.h>
#include <qmldebug/qmlenginedebugclient.h>
#include <qmldebug/qmltoolsclient.h>
#include <utils/qtcassert.h>
#include <utils/savedaction.h>

Friedemann Kleint's avatar
Friedemann Kleint committed
53 54
#include <QFileInfo>

55 56 57 58 59 60 61 62 63
using namespace QmlDebug;

namespace Debugger {
namespace Internal {
/*!
 * QmlInspectorAdapter manages the clients for the inspector, and the
 * integration with the text editor.
 */
QmlInspectorAdapter::QmlInspectorAdapter(QmlAdapter *debugAdapter,
64
                                         DebuggerEngine *engine,
65 66 67 68 69 70 71 72 73 74 75 76 77 78
                                         QObject *parent)
    : QObject(parent)
    , m_debugAdapter(debugAdapter)
    , m_engine(engine)
    , m_engineClient(0)
    , m_toolsClient(0)
    , m_agent(new QmlInspectorAgent(engine, this))
    , m_targetToSync(NoTarget)
    , m_debugIdToSelect(-1)
    , m_currentSelectedDebugId(-1)
    , m_toolsClientConnected(false)
    , m_inspectorToolsContext("Debugger.QmlInspector")
    , m_selectAction(new QAction(this))
    , m_zoomAction(new QAction(this))
hjk's avatar
hjk committed
79
    , m_showAppOnTopAction(action(ShowAppOnTop))
80
    , m_engineClientConnected(false)
81
{
82 83
    if (!m_engine->isMasterEngine())
        m_engine = m_engine->masterEngine();
84 85 86 87 88 89
    connect(m_engine, &DebuggerEngine::stateChanged,
            this, &QmlInspectorAdapter::onEngineStateChanged);
    connect(m_agent, &QmlInspectorAgent::objectFetched,
            this, &QmlInspectorAdapter::onObjectFetched);
    connect(m_agent, &QmlInspectorAgent::jumpToObjectDefinition,
            this, &QmlInspectorAdapter::jumpToObjectDefinitionInEditor);
90 91

    QmlDebugConnection *connection = m_debugAdapter->connection();
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
    auto engineClient1 = new DeclarativeEngineDebugClient(connection);
    connect(engineClient1, &BaseEngineDebugClient::newState,
            this, &QmlInspectorAdapter::clientStateChanged);
    connect(engineClient1, &BaseEngineDebugClient::newState,
            this, &QmlInspectorAdapter::engineClientStateChanged);

    auto engineClient2 = new QmlEngineDebugClient(connection);
    connect(engineClient2, &BaseEngineDebugClient::newState,
            this, &QmlInspectorAdapter::clientStateChanged);
    connect(engineClient2, &BaseEngineDebugClient::newState,
            this, &QmlInspectorAdapter::engineClientStateChanged);

    auto engineClient3 = new DeclarativeEngineDebugClientV2(connection);
    connect(engineClient3, &BaseEngineDebugClient::newState,
            this, &QmlInspectorAdapter::clientStateChanged);
    connect(engineClient3, &BaseEngineDebugClient::newState,
            this, &QmlInspectorAdapter::engineClientStateChanged);
109

110 111
    m_engineClients.insert(engineClient1->name(), engineClient1);
    m_engineClients.insert(engineClient2->name(), engineClient2);
112
    m_engineClients.insert(engineClient3->name(), engineClient3);
113

114
    if (engineClient1->state() == QmlDebugClient::Enabled)
115
        setActiveEngineClient(engineClient1);
116
    if (engineClient2->state() == QmlDebugClient::Enabled)
117
        setActiveEngineClient(engineClient2);
118
    if (engineClient3->state() == QmlDebugClient::Enabled)
119
        setActiveEngineClient(engineClient3);
120

121 122 123 124 125
    auto toolsClient1 = new DeclarativeToolsClient(connection);
    connect(toolsClient1, &BaseToolsClient::newState,
            this, &QmlInspectorAdapter::clientStateChanged);
    connect(toolsClient1, &BaseToolsClient::newState,
            this, &QmlInspectorAdapter::toolsClientStateChanged);
126

127
    auto toolsClient2 = new QmlToolsClient(connection);
hjk's avatar
hjk committed
128 129
    connect(toolsClient2, &BaseToolsClient::newState,
            this, &QmlInspectorAdapter::clientStateChanged);
130 131
    connect(toolsClient2, &BaseToolsClient::newState,
            this, &QmlInspectorAdapter::toolsClientStateChanged);
132 133

    // toolbar
134 135
    m_selectAction->setObjectName(QLatin1String("QML Select Action"));
    m_zoomAction->setObjectName(QLatin1String("QML Zoom Action"));
136 137
    m_selectAction->setCheckable(true);
    m_zoomAction->setCheckable(true);
138 139 140 141
    m_showAppOnTopAction->setCheckable(true);
    m_selectAction->setEnabled(false);
    m_zoomAction->setEnabled(false);
    m_showAppOnTopAction->setEnabled(false);
142

143 144 145 146 147 148
    connect(m_selectAction, &QAction::triggered,
            this, &QmlInspectorAdapter::onSelectActionTriggered);
    connect(m_zoomAction, &QAction::triggered,
            this, &QmlInspectorAdapter::onZoomActionTriggered);
    connect(m_showAppOnTopAction, &QAction::triggered,
            this, &QmlInspectorAdapter::onShowAppOnTopChanged);
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
}

QmlInspectorAdapter::~QmlInspectorAdapter()
{
}

BaseEngineDebugClient *QmlInspectorAdapter::engineClient() const
{
    return m_engineClient;
}

BaseToolsClient *QmlInspectorAdapter::toolsClient() const
{
    return m_toolsClient;
}

QmlInspectorAgent *QmlInspectorAdapter::agent() const
{
    return m_agent;
}

int QmlInspectorAdapter::currentSelectedDebugId() const
{
    return m_currentSelectedDebugId;
}

QString QmlInspectorAdapter::currentSelectedDisplayName() const
{
    return m_currentSelectedDebugName;
}

180
void QmlInspectorAdapter::clientStateChanged(QmlDebugClient::State state)
181 182 183 184 185
{
    QString serviceName;
    float version = 0;
    if (QmlDebugClient *client = qobject_cast<QmlDebugClient*>(sender())) {
        serviceName = client->name();
186
        version = client->remoteVersion();
187 188
    }

189
    m_debugAdapter->logServiceStateChange(serviceName, version, state);
190 191
}

192
void QmlInspectorAdapter::toolsClientStateChanged(QmlDebugClient::State state)
193 194
{
    BaseToolsClient *client = qobject_cast<BaseToolsClient*>(sender());
195
    QTC_ASSERT(client, return);
196
    if (state == QmlDebugClient::Enabled) {
197 198
        m_toolsClient = client;

199 200 201 202 203
        connect(client, &BaseToolsClient::currentObjectsChanged,
                this, &QmlInspectorAdapter::selectObjectsFromToolsClient);
        connect(client, &BaseToolsClient::logActivity,
                m_debugAdapter, &QmlAdapter::logServiceActivity);
        connect(client, &BaseToolsClient::reloaded, this, &QmlInspectorAdapter::onReloaded);
204 205 206 207

        // register actions here
        // because there can be multiple QmlEngines
        // at the same time (but hopefully one one is connected)
Eike Ziller's avatar
Eike Ziller committed
208
        Core::ActionManager::registerAction(m_selectAction,
209 210
                                            Core::Id(Constants::QML_SELECTTOOL),
                                            m_inspectorToolsContext);
Eike Ziller's avatar
Eike Ziller committed
211
        Core::ActionManager::registerAction(m_zoomAction, Core::Id(Constants::QML_ZOOMTOOL),
212 213 214 215
                                            m_inspectorToolsContext);
        Core::ActionManager::registerAction(m_showAppOnTopAction,
                                            Core::Id(Constants::QML_SHOW_APP_ON_TOP),
                                            m_inspectorToolsContext);
216

217
        Core::ICore::addAdditionalContext(m_inspectorToolsContext);
218

219 220 221
        m_toolsClientConnected = true;
        onEngineStateChanged(m_engine->state());
        if (m_showAppOnTopAction->isChecked())
222 223
            m_toolsClient->showAppOnTop(true);

224
    } else if (m_toolsClientConnected && client == m_toolsClient) {
225 226 227 228 229
        disconnect(client, SIGNAL(currentObjectsChanged(QList<int>)),
                   this, SLOT(selectObjectsFromToolsClient(QList<int>)));
        disconnect(client, SIGNAL(logActivity(QString,QString)),
                   m_debugAdapter, SLOT(logServiceActivity(QString,QString)));

230 231 232 233
        Core::ActionManager::unregisterAction(m_selectAction, Core::Id(Constants::QML_SELECTTOOL));
        Core::ActionManager::unregisterAction(m_zoomAction, Core::Id(Constants::QML_ZOOMTOOL));
        Core::ActionManager::unregisterAction(m_showAppOnTopAction,
                                              Core::Id(Constants::QML_SHOW_APP_ON_TOP));
234

235
        Core::ICore::removeAdditionalContext(m_inspectorToolsContext);
236

237
        enableTools(false);
238
        m_toolsClientConnected = false;
239 240 241
        m_selectAction->setCheckable(false);
        m_zoomAction->setCheckable(false);
        m_showAppOnTopAction->setCheckable(false);
242 243 244
    }
}

245
void QmlInspectorAdapter::engineClientStateChanged(QmlDebugClient::State state)
246 247 248
{
    BaseEngineDebugClient *client
            = qobject_cast<BaseEngineDebugClient*>(sender());
249

250
    if (state == QmlDebugClient::Enabled && !m_engineClientConnected) {
251
        // We accept the first client that is enabled and reject the others.
252 253
        QTC_ASSERT(client, return);
        setActiveEngineClient(client);
254
    } else if (m_engineClientConnected && client == m_engineClient) {
255 256
        m_engineClientConnected = false;
    }
257 258 259 260 261 262 263
}

void QmlInspectorAdapter::selectObjectsFromToolsClient(const QList<int> &debugIds)
{
    if (debugIds.isEmpty())
        return;

264 265
    m_targetToSync = EditorTarget;
    m_debugIdToSelect = debugIds.first();
266
    selectObject(agent()->objectForId(m_debugIdToSelect), EditorTarget);
267 268
}

Kai Koehne's avatar
Kai Koehne committed
269
void QmlInspectorAdapter::onObjectFetched(const ObjectReference &ref)
270 271 272 273 274 275 276 277 278
{
    if (ref.debugId() == m_debugIdToSelect) {
        m_debugIdToSelect = -1;
        selectObject(ref, m_targetToSync);
    }
}

void QmlInspectorAdapter::onSelectActionTriggered(bool checked)
{
279
    QTC_ASSERT(toolsClient(), return);
280 281 282 283 284 285 286 287 288 289 290
    if (checked) {
        toolsClient()->setDesignModeBehavior(true);
        toolsClient()->changeToSelectTool();
        m_zoomAction->setChecked(false);
    } else {
        toolsClient()->setDesignModeBehavior(false);
    }
}

void QmlInspectorAdapter::onZoomActionTriggered(bool checked)
{
291
    QTC_ASSERT(toolsClient(), return);
292 293 294 295 296 297 298 299 300
    if (checked) {
        toolsClient()->setDesignModeBehavior(true);
        toolsClient()->changeToZoomTool();
        m_selectAction->setChecked(false);
    } else {
        toolsClient()->setDesignModeBehavior(false);
    }
}

301
void QmlInspectorAdapter::onShowAppOnTopChanged(bool checked)
302
{
303 304
    QTC_ASSERT(toolsClient(), return);
    toolsClient()->showAppOnTop(checked);
305 306 307 308 309 310 311 312 313
}

void QmlInspectorAdapter::setActiveEngineClient(BaseEngineDebugClient *client)
{
    if (m_engineClient == client)
        return;

    m_engineClient = client;
    m_agent->setEngineClient(m_engineClient);
314
    m_engineClientConnected = true;
315 316
}

317
void QmlInspectorAdapter::showConnectionStateMessage(const QString &message)
318 319 320 321
{
    m_engine->showMessage(_("QML Inspector: ") + message, LogStatus);
}

322
void QmlInspectorAdapter::jumpToObjectDefinitionInEditor(
Aurindam Jana's avatar
Aurindam Jana committed
323
        const FileReference &objSource, int debugId)
324
{
325
    const QString fileName = m_engine->toFileInProject(objSource.url());
326

Eike Ziller's avatar
Eike Ziller committed
327
    Core::EditorManager::openEditorAt(fileName, objSource.lineNumber());
Aurindam Jana's avatar
Aurindam Jana committed
328 329 330 331
    if (debugId != -1 && debugId != m_currentSelectedDebugId) {
        m_currentSelectedDebugId = debugId;
        m_currentSelectedDebugName = agent()->displayName(debugId);
    }
332 333
}

Kai Koehne's avatar
Kai Koehne committed
334
void QmlInspectorAdapter::selectObject(const ObjectReference &obj,
335 336
                                       SelectionTarget target)
{
337
    if (m_toolsClient && target == ToolTarget)
338
        m_toolsClient->setObjectIdList(
Kai Koehne's avatar
Kai Koehne committed
339
                    QList<ObjectReference>() << obj);
340 341

    if (target == EditorTarget)
342
        jumpToObjectDefinitionInEditor(obj.source());
343

344
    agent()->selectObjectInTree(obj.debugId());
345 346
}

347 348 349 350 351 352 353 354 355 356 357 358
void QmlInspectorAdapter::enableTools(const bool enable)
{
    if (!m_toolsClientConnected)
        return;
    m_selectAction->setEnabled(enable);
    m_showAppOnTopAction->setEnabled(enable);
    // only enable zoom action for Qt 4.x/old client
    // (zooming is integrated into selection tool in Qt 5).
    if (!qobject_cast<QmlToolsClient*>(m_toolsClient))
        m_zoomAction->setEnabled(enable);
}

359 360
void QmlInspectorAdapter::onReloaded()
{
361
    m_agent->reloadEngines();
362 363
}

364 365 366 367 368
void QmlInspectorAdapter::onEngineStateChanged(const DebuggerState state)
{
    enableTools(state == InferiorRunOk);
}

369 370
} // namespace Internal
} // namespace Debugger