qmlprojectrunconfiguration.cpp 15.1 KB
Newer Older
Kai Koehne's avatar
Kai Koehne committed
1 2 3 4
/**************************************************************************
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
Kai Koehne's avatar
Kai Koehne committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, 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.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/

dt's avatar
dt committed
30
#include "qmlprojectrunconfiguration.h"
Kai Koehne's avatar
Kai Koehne committed
31 32 33
#include "qmlproject.h"
#include "qmlprojectmanagerconstants.h"
#include "qmlprojecttarget.h"
34
#include "projectexplorer/projectexplorer.h"
Kai Koehne's avatar
Kai Koehne committed
35

36 37
#include <coreplugin/mimedatabase.h>
#include <projectexplorer/buildconfiguration.h>
Kai Koehne's avatar
Kai Koehne committed
38 39 40 41 42 43
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/icore.h>
#include <coreplugin/ifile.h>
#include <utils/synchronousprocess.h>
#include <utils/pathchooser.h>
44
#include <utils/debuggerlanguagechooser.h>
45 46
#include <qt4projectmanager/qtversionmanager.h>
#include <qt4projectmanager/qt4projectmanagerconstants.h>
Kai Koehne's avatar
Kai Koehne committed
47 48 49 50 51 52

#include <QFormLayout>
#include <QComboBox>
#include <QCoreApplication>
#include <QLineEdit>
#include <QSpinBox>
53
#include <QStringListModel>
54
#include <QDebug>
Kai Koehne's avatar
Kai Koehne committed
55 56 57

namespace QmlProjectManager {

58 59 60 61 62 63

QmlProjectRunConfigurationDebugData::QmlProjectRunConfigurationDebugData() :
        serverAddress("127.0.0.1"), serverPort(Constants::QML_DEFAULT_DEBUG_SERVER_PORT)
{
}

Kai Koehne's avatar
Kai Koehne committed
64 65
QmlProjectRunConfiguration::QmlProjectRunConfiguration(Internal::QmlProjectTarget *parent) :
    ProjectExplorer::RunConfiguration(parent, QLatin1String(Constants::QML_RC_ID)),
66
    m_fileListModel(new QStringListModel(this)),
67
    m_projectTarget(parent),
68
    m_usingCurrentFile(true),
69
    m_isEnabled(false)
Kai Koehne's avatar
Kai Koehne committed
70 71 72 73 74 75 76
{
    ctor();
}

QmlProjectRunConfiguration::QmlProjectRunConfiguration(Internal::QmlProjectTarget *parent, QmlProjectRunConfiguration *source) :
    ProjectExplorer::RunConfiguration(parent, source),
    m_qmlViewerCustomPath(source->m_qmlViewerCustomPath),
77
    m_qmlViewerArgs(source->m_qmlViewerArgs),
78
    m_fileListModel(new QStringListModel(this)),
79
    m_projectTarget(parent)
Kai Koehne's avatar
Kai Koehne committed
80
{
81
    ctor();
82 83
    m_debugData.serverAddress = source->m_debugData.serverAddress;
    m_debugData.serverPort = source->m_debugData.serverPort;
84
    setMainScript(source->m_scriptFile);
Kai Koehne's avatar
Kai Koehne committed
85 86
}

87 88 89 90
bool QmlProjectRunConfiguration::isEnabled(ProjectExplorer::BuildConfiguration *bc) const
{
    Q_UNUSED(bc);

91
    return m_isEnabled;
92 93
}

Kai Koehne's avatar
Kai Koehne committed
94 95
void QmlProjectRunConfiguration::ctor()
{
96 97 98 99
    // reset default settings in constructor
    setUseCppDebugger(false);
    setUseQmlDebugger(true);

100 101 102 103
    Core::EditorManager *em = Core::EditorManager::instance();
    connect(em, SIGNAL(currentEditorChanged(Core::IEditor*)),
            this, SLOT(changeCurrentFile(Core::IEditor*)));

104 105
    Qt4ProjectManager::QtVersionManager *qtVersions = Qt4ProjectManager::QtVersionManager::instance();
    connect(qtVersions, SIGNAL(qtVersionsChanged(QList<int>)), this, SLOT(updateEnabled()));
106
    connect(qtVersions, SIGNAL(qtVersionsChanged(QList<int>)), this, SLOT(onViewerChanged()));
107

108
    setDisplayName(tr("QML Viewer", "QMLRunConfiguration display name."));
Kai Koehne's avatar
Kai Koehne committed
109 110 111 112 113 114
}

QmlProjectRunConfiguration::~QmlProjectRunConfiguration()
{
}

115 116
QString QmlProjectRunConfiguration::debugServerAddress() const
{
117
    return m_debugData.serverAddress;
118 119
}

Kai Koehne's avatar
Kai Koehne committed
120 121 122 123 124 125 126 127 128
Internal::QmlProjectTarget *QmlProjectRunConfiguration::qmlTarget() const
{
    return static_cast<Internal::QmlProjectTarget *>(target());
}

QString QmlProjectRunConfiguration::viewerPath() const
{
    if (!m_qmlViewerCustomPath.isEmpty())
        return m_qmlViewerCustomPath;
129 130

    return viewerDefaultPath();
Kai Koehne's avatar
Kai Koehne committed
131 132 133 134 135 136 137 138
}

QStringList QmlProjectRunConfiguration::viewerArguments() const
{
    QStringList args;

    // arguments in .user file
    if (!m_qmlViewerArgs.isEmpty())
139
        args.append(m_qmlViewerArgs.split(QLatin1Char(' ')));
Kai Koehne's avatar
Kai Koehne committed
140 141

    // arguments from .qmlproject file
142
    foreach (const QString &importPath, qmlTarget()->qmlProject()->importPaths()) {
143
        args.append(QLatin1String("-I"));
144
        args.append(importPath);
Kai Koehne's avatar
Kai Koehne committed
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
    }

    const QString s = mainScript();
    if (! s.isEmpty())
        args.append(s);
    return args;
}

QString QmlProjectRunConfiguration::workingDirectory() const
{
    QFileInfo projectFile(qmlTarget()->qmlProject()->file()->fileName());
    return projectFile.absolutePath();
}

uint QmlProjectRunConfiguration::debugServerPort() const
{
161
    return m_debugData.serverPort;
Kai Koehne's avatar
Kai Koehne committed
162 163
}

164 165 166 167 168
static bool caseInsensitiveLessThan(const QString &s1, const QString &s2)
{
    return s1.toLower() < s2.toLower();
}

169
QWidget *QmlProjectRunConfiguration::createConfigurationWidget()
Kai Koehne's avatar
Kai Koehne committed
170 171 172 173
{
    QWidget *config = new QWidget;
    QFormLayout *form = new QFormLayout(config);

174 175 176
    m_fileListCombo = new QComboBox;
    m_fileListCombo.data()->setModel(m_fileListModel);
    updateFileComboBox();
Kai Koehne's avatar
Kai Koehne committed
177

178 179
    connect(m_fileListCombo.data(), SIGNAL(activated(QString)), this, SLOT(setMainScript(QString)));
    connect(ProjectExplorer::ProjectExplorerPlugin::instance(), SIGNAL(fileListChanged()), SLOT(updateFileComboBox()));
Kai Koehne's avatar
Kai Koehne committed
180 181 182

    Utils::PathChooser *qmlViewer = new Utils::PathChooser;
    qmlViewer->setExpectedKind(Utils::PathChooser::Command);
183
    qmlViewer->setPath(m_qmlViewerCustomPath);
184

185
    connect(qmlViewer, SIGNAL(changed(QString)), this, SLOT(onViewerChanged()));
Kai Koehne's avatar
Kai Koehne committed
186

187 188 189
    m_qmlViewerExecutable = new QLabel;
    m_qmlViewerExecutable.data()->setText(viewerPath() + " " + m_qmlViewerArgs);

Kai Koehne's avatar
Kai Koehne committed
190 191
    QLineEdit *qmlViewerArgs = new QLineEdit;
    qmlViewerArgs->setText(m_qmlViewerArgs);
192
    connect(qmlViewerArgs, SIGNAL(textChanged(QString)), this, SLOT(onViewerArgsChanged()));
Kai Koehne's avatar
Kai Koehne committed
193

194
    QLineEdit *debugServer = new QLineEdit;
195
    debugServer->setText(m_debugData.serverAddress);
196 197
    connect(debugServer, SIGNAL(textChanged(QString)), this, SLOT(onDebugServerAddressChanged()));

Kai Koehne's avatar
Kai Koehne committed
198 199 200
    QSpinBox *debugPort = new QSpinBox;
    debugPort->setMinimum(1024); // valid registered/dynamic/free ports according to http://www.iana.org/assignments/port-numbers
    debugPort->setMaximum(65535);
201
    debugPort->setValue(m_debugData.serverPort);
Kai Koehne's avatar
Kai Koehne committed
202 203
    connect(debugPort, SIGNAL(valueChanged(int)), this, SLOT(onDebugServerPortChanged()));

204
    form->addRow(tr("Custom QML Viewer:"), qmlViewer);
205
    form->addRow(tr("QML Viewer arguments:"), qmlViewerArgs);
206
    form->addRow(QString(), m_qmlViewerExecutable.data());
207
    form->addRow(tr("Debugging Address:"), debugServer);
Kai Koehne's avatar
Kai Koehne committed
208 209
    form->addRow(tr("Debugging Port:"), debugPort);

210 211 212
    QLabel *debuggerLabel = new QLabel(tr("Debugger:"), config);
    Utils::DebuggerLanguageChooser *debuggerLanguageChooser = new Utils::DebuggerLanguageChooser(config);

213
    form->addRow(tr("Main QML File:"), m_fileListCombo.data());
214 215 216 217 218 219 220 221 222
    form->addRow(debuggerLabel, debuggerLanguageChooser);

    debuggerLanguageChooser->setCppChecked(useCppDebugger());
    debuggerLanguageChooser->setQmlChecked(useQmlDebugger());

    connect(debuggerLanguageChooser, SIGNAL(cppLanguageToggled(bool)),
            this, SLOT(useCppDebuggerToggled(bool)));
    connect(debuggerLanguageChooser, SIGNAL(qmlLanguageToggled(bool)),
            this, SLOT(useQmlDebuggerToggled(bool)));
Kai Koehne's avatar
Kai Koehne committed
223 224 225 226

    return config;
}

227

Kai Koehne's avatar
Kai Koehne committed
228 229
QString QmlProjectRunConfiguration::mainScript() const
{
230 231
    if (m_usingCurrentFile)
        return m_currentFileFilename;
Kai Koehne's avatar
Kai Koehne committed
232

233
    return m_mainScriptFilename;
Kai Koehne's avatar
Kai Koehne committed
234 235
}

236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
void QmlProjectRunConfiguration::updateFileComboBox()
{
    if (m_fileListCombo.isNull())
        return;

    QDir projectDir = qmlTarget()->qmlProject()->projectDir();
    QStringList files;

    files.append(CURRENT_FILE);
    int currentIndex = -1;
    QStringList sortedFiles = qmlTarget()->qmlProject()->files();
    qStableSort(sortedFiles.begin(), sortedFiles.end(), caseInsensitiveLessThan);

    foreach (const QString &fn, sortedFiles) {
        QFileInfo fileInfo(fn);
        if (fileInfo.suffix() != QLatin1String("qml"))
            continue;

        QString fileName = projectDir.relativeFilePath(fn);
        if (fileName == m_scriptFile)
            currentIndex = files.size();

        files.append(fileName);
    }
    m_fileListModel->setStringList(files);

    if (currentIndex != -1)
        m_fileListCombo.data()->setCurrentIndex(currentIndex);
    else
        m_fileListCombo.data()->setCurrentIndex(0);
}

268 269 270
void QmlProjectRunConfiguration::onDebugServerAddressChanged()
{
    if (QLineEdit *lineEdit = qobject_cast<QLineEdit*>(sender()))
271
        m_debugData.serverAddress = lineEdit->text();
272 273
}

Kai Koehne's avatar
Kai Koehne committed
274 275 276
void QmlProjectRunConfiguration::setMainScript(const QString &scriptFile)
{
    m_scriptFile = scriptFile;
277 278 279
    // replace with locale-agnostic string
    if (m_scriptFile == CURRENT_FILE)
        m_scriptFile = M_CURRENT_FILE;
280

281
    if (m_scriptFile.isEmpty() || m_scriptFile == M_CURRENT_FILE) {
282 283 284 285 286
        m_usingCurrentFile = true;
        changeCurrentFile(Core::EditorManager::instance()->currentEditor());
    } else {
        m_usingCurrentFile = false;
        m_mainScriptFilename = qmlTarget()->qmlProject()->projectDir().absoluteFilePath(scriptFile);
287
        updateEnabled();
288
    }
Kai Koehne's avatar
Kai Koehne committed
289 290
}

291
void QmlProjectRunConfiguration::onViewerChanged()
Kai Koehne's avatar
Kai Koehne committed
292 293 294 295
{
    if (Utils::PathChooser *chooser = qobject_cast<Utils::PathChooser *>(sender())) {
        m_qmlViewerCustomPath = chooser->path();
    }
296 297 298
    if (!m_qmlViewerExecutable.isNull()) {
        m_qmlViewerExecutable.data()->setText(viewerPath() + " " + m_qmlViewerArgs);
    }
Kai Koehne's avatar
Kai Koehne committed
299 300
}

301
void QmlProjectRunConfiguration::onViewerArgsChanged()
Kai Koehne's avatar
Kai Koehne committed
302 303 304
{
    if (QLineEdit *lineEdit = qobject_cast<QLineEdit*>(sender()))
        m_qmlViewerArgs = lineEdit->text();
305 306 307 308

    if (!m_qmlViewerExecutable.isNull()) {
        m_qmlViewerExecutable.data()->setText(viewerPath() + " " + m_qmlViewerArgs);
    }
Kai Koehne's avatar
Kai Koehne committed
309 310 311 312 313
}

void QmlProjectRunConfiguration::onDebugServerPortChanged()
{
    if (QSpinBox *spinBox = qobject_cast<QSpinBox*>(sender())) {
314
        m_debugData.serverPort = spinBox->value();
Kai Koehne's avatar
Kai Koehne committed
315 316 317
    }
}

318 319 320 321 322 323 324 325 326 327 328
void QmlProjectRunConfiguration::useCppDebuggerToggled(bool toggled)
{
    setUseCppDebugger(toggled);
}

void QmlProjectRunConfiguration::useQmlDebuggerToggled(bool toggled)
{
    setUseQmlDebugger(toggled);
}


Kai Koehne's avatar
Kai Koehne committed
329 330 331 332 333 334 335
QVariantMap QmlProjectRunConfiguration::toMap() const
{
    QVariantMap map(ProjectExplorer::RunConfiguration::toMap());

    map.insert(QLatin1String(Constants::QML_VIEWER_KEY), m_qmlViewerCustomPath);
    map.insert(QLatin1String(Constants::QML_VIEWER_ARGUMENTS_KEY), m_qmlViewerArgs);
    map.insert(QLatin1String(Constants::QML_MAINSCRIPT_KEY),  m_scriptFile);
336 337
    map.insert(QLatin1String(Constants::QML_DEBUG_SERVER_PORT_KEY), m_debugData.serverPort);
    map.insert(QLatin1String(Constants::QML_DEBUG_SERVER_ADDRESS_KEY), m_debugData.serverAddress);
Kai Koehne's avatar
Kai Koehne committed
338 339 340 341 342 343 344
    return map;
}

bool QmlProjectRunConfiguration::fromMap(const QVariantMap &map)
{
    m_qmlViewerCustomPath = map.value(QLatin1String(Constants::QML_VIEWER_KEY)).toString();
    m_qmlViewerArgs = map.value(QLatin1String(Constants::QML_VIEWER_ARGUMENTS_KEY)).toString();
345
    m_scriptFile = map.value(QLatin1String(Constants::QML_MAINSCRIPT_KEY), M_CURRENT_FILE).toString();
346 347
    m_debugData.serverPort = map.value(QLatin1String(Constants::QML_DEBUG_SERVER_PORT_KEY), Constants::QML_DEFAULT_DEBUG_SERVER_PORT).toUInt();
    m_debugData.serverAddress = map.value(QLatin1String(Constants::QML_DEBUG_SERVER_ADDRESS_KEY), QLatin1String("127.0.0.1")).toString();
348
    setMainScript(m_scriptFile);
Kai Koehne's avatar
Kai Koehne committed
349 350 351 352

    return RunConfiguration::fromMap(map);
}

353 354 355 356 357 358
void QmlProjectRunConfiguration::changeCurrentFile(Core::IEditor * /*editor*/)
{
    updateEnabled();
}

void QmlProjectRunConfiguration::updateEnabled()
359
{
360
    bool qmlFileFound = false;
361
    if (m_usingCurrentFile) {
362
        Core::IEditor *editor = Core::EditorManager::instance()->currentEditor();
363 364
        if (editor) {
            m_currentFileFilename = editor->file()->fileName();
365
            if (Core::ICore::instance()->mimeDatabase()->findByFile(mainScript()).type() == QLatin1String("application/x-qml"))
366
                qmlFileFound = true;
367 368 369
        }
        if (!editor
            || Core::ICore::instance()->mimeDatabase()->findByFile(mainScript()).type() == QLatin1String("application/x-qmlproject")) {
370
            // find a qml file with lowercase filename. This is slow but only done in initialization/other border cases.
371
            foreach(const QString &filename, m_projectTarget->qmlProject()->files()) {
372 373 374
                const QFileInfo fi(filename);

                if (!filename.isEmpty() && fi.baseName()[0].isLower()
375
                    && Core::ICore::instance()->mimeDatabase()->findByFile(fi).type() == QLatin1String("application/x-qml"))
376 377
                {
                    m_currentFileFilename = filename;
378
                    qmlFileFound = true;
379 380 381 382
                    break;
                }

            }
383
        }
384 385 386 387 388
    } else { // use default one
        qmlFileFound = !m_mainScriptFilename.isEmpty();
    }

    bool newValue = QFileInfo(viewerPath()).exists() && qmlFileFound;
389

390 391 392
    if (m_isEnabled != newValue) {
        m_isEnabled = newValue;
        emit isEnabledChanged(m_isEnabled);
393 394 395
    }
}

396
QString QmlProjectRunConfiguration::viewerDefaultPath() const
397
{
398 399
    QString path;

400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
    // Search for QmlObserver
#ifdef Q_OS_MAC
    const QString qmlObserverName = QLatin1String("QMLObserver.app");
#else
    const QString qmlObserverName = QLatin1String("qmlobserver");
#endif

    QDir appDir(QCoreApplication::applicationDirPath());
    QString qmlObserverPath;
#ifdef Q_OS_WIN
    qmlObserverPath = appDir.absoluteFilePath(qmlObserverName + QLatin1String(".exe"));
#else
    qmlObserverPath = appDir.absoluteFilePath(qmlObserverName);
#endif
    if (QFileInfo(qmlObserverPath).exists()) {
        return qmlObserverPath;
    }

    // Search for QmlViewer

420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
    // prepend creator/bin dir to search path (only useful for special creator-qml package)
    const QString searchPath = QCoreApplication::applicationDirPath()
            + Utils::SynchronousProcess::pathSeparator()
            + QString::fromLocal8Bit(qgetenv("PATH"));

#ifdef Q_OS_MAC
    const QString qmlViewerName = QLatin1String("QMLViewer");
#else
    const QString qmlViewerName = QLatin1String("qmlviewer");
#endif

    path = Utils::SynchronousProcess::locateBinary(searchPath, qmlViewerName);
    if (!path.isEmpty())
        return path;

    // Try to locate default path in Qt Versions
    Qt4ProjectManager::QtVersionManager *qtVersions = Qt4ProjectManager::QtVersionManager::instance();
    foreach (Qt4ProjectManager::QtVersion *version, qtVersions->validVersions()) {
        if (!version->qmlviewerCommand().isEmpty()
                && version->supportsTargetId(Qt4ProjectManager::Constants::DESKTOP_TARGET_ID)) {
            return version->qmlviewerCommand();
        }
    }

    return path;
445 446
}

Kai Koehne's avatar
Kai Koehne committed
447
} // namespace QmlProjectManager