qmlprojectrunconfiguration.cpp 12 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
30
31
32
33
**
** 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.
**
**************************************************************************/

#include "qmlproject.h"
#include "qmlprojectmanagerconstants.h"
#include "qmlprojectrunconfiguration.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
44
45
46
47
48
49
#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>

#include <QFormLayout>
#include <QComboBox>
#include <QCoreApplication>
#include <QLineEdit>
#include <QSpinBox>
50
#include <QStringListModel>
51
#include <QDebug>
Kai Koehne's avatar
Kai Koehne committed
52
53
54

namespace QmlProjectManager {

55
56
57
58
59
60

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

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

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

84
85
86
87
88
89
90
91
92
93
94
95
bool QmlProjectRunConfiguration::isEnabled(ProjectExplorer::BuildConfiguration *bc) const
{
    Q_UNUSED(bc);

    if (!QFile::exists(mainScript())
        || !Core::ICore::instance()->mimeDatabase()->findByFile(mainScript()).matchesType(QLatin1String("application/x-qml")))
    {
        return false;
    }
    return true;
}

Kai Koehne's avatar
Kai Koehne committed
96
97
void QmlProjectRunConfiguration::ctor()
{
98
99
100
101
    Core::EditorManager *em = Core::EditorManager::instance();
    connect(em, SIGNAL(currentEditorChanged(Core::IEditor*)),
            this, SLOT(changeCurrentFile(Core::IEditor*)));

102
    setDisplayName(tr("QML Viewer", "QMLRunConfiguration display name."));
Kai Koehne's avatar
Kai Koehne committed
103
104
105
106
107

    // prepend creator/bin dir to search path (only useful for special creator-qml package)
    const QString searchPath = QCoreApplication::applicationDirPath()
                               + Utils::SynchronousProcess::pathSeparator()
                               + QString(qgetenv("PATH"));
108
109
110
111
112
113
114
115

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

    m_qmlViewerDefaultPath = Utils::SynchronousProcess::locateBinary(searchPath, qmlViewerName);
Kai Koehne's avatar
Kai Koehne committed
116
117
118
119
120
121
}

QmlProjectRunConfiguration::~QmlProjectRunConfiguration()
{
}

122
123
QString QmlProjectRunConfiguration::debugServerAddress() const
{
124
    return m_debugData.serverAddress;
125
126
}

Kai Koehne's avatar
Kai Koehne committed
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
Internal::QmlProjectTarget *QmlProjectRunConfiguration::qmlTarget() const
{
    return static_cast<Internal::QmlProjectTarget *>(target());
}

QString QmlProjectRunConfiguration::viewerPath() const
{
    if (!m_qmlViewerCustomPath.isEmpty())
        return m_qmlViewerCustomPath;
    return m_qmlViewerDefaultPath;
}

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

    // arguments in .user file
    if (!m_qmlViewerArgs.isEmpty())
145
        args.append(m_qmlViewerArgs.split(QLatin1Char(' ')));
Kai Koehne's avatar
Kai Koehne committed
146
147

    // arguments from .qmlproject file
148
    foreach (const QString &importPath, qmlTarget()->qmlProject()->importPaths()) {
149
        args.append(QLatin1String("-I"));
150
        args.append(importPath);
Kai Koehne's avatar
Kai Koehne committed
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
    }

    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
{
167
    return m_debugData.serverPort;
Kai Koehne's avatar
Kai Koehne committed
168
169
}

170
171
172
173
174
static bool caseInsensitiveLessThan(const QString &s1, const QString &s2)
{
    return s1.toLower() < s2.toLower();
}

175
QWidget *QmlProjectRunConfiguration::createConfigurationWidget()
Kai Koehne's avatar
Kai Koehne committed
176
177
178
179
{
    QWidget *config = new QWidget;
    QFormLayout *form = new QFormLayout(config);

180
181
182
    m_fileListCombo = new QComboBox;
    m_fileListCombo.data()->setModel(m_fileListModel);
    updateFileComboBox();
Kai Koehne's avatar
Kai Koehne committed
183

184
185
    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
186
187
188
189

    Utils::PathChooser *qmlViewer = new Utils::PathChooser;
    qmlViewer->setExpectedKind(Utils::PathChooser::Command);
    qmlViewer->setPath(viewerPath());
190
    connect(qmlViewer, SIGNAL(changed(QString)), this, SLOT(onViewerChanged()));
Kai Koehne's avatar
Kai Koehne committed
191
192
193

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

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

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

206
207
    form->addRow(tr("QML Viewer"), qmlViewer);
    form->addRow(tr("QML Viewer arguments:"), qmlViewerArgs);
208
    form->addRow(tr("Main QML File:"), m_fileListCombo.data());
209
    form->addRow(tr("Debugging Address:"), debugServer);
Kai Koehne's avatar
Kai Koehne committed
210
211
212
213
214
    form->addRow(tr("Debugging Port:"), debugPort);

    return config;
}

215

Kai Koehne's avatar
Kai Koehne committed
216
217
QString QmlProjectRunConfiguration::mainScript() const
{
218
219
    if (m_usingCurrentFile)
        return m_currentFileFilename;
Kai Koehne's avatar
Kai Koehne committed
220

221
    return m_mainScriptFilename;
Kai Koehne's avatar
Kai Koehne committed
222
223
}

224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
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);
}

256
257
258
void QmlProjectRunConfiguration::onDebugServerAddressChanged()
{
    if (QLineEdit *lineEdit = qobject_cast<QLineEdit*>(sender()))
259
        m_debugData.serverAddress = lineEdit->text();
260
261
}

Kai Koehne's avatar
Kai Koehne committed
262
263
264
void QmlProjectRunConfiguration::setMainScript(const QString &scriptFile)
{
    m_scriptFile = scriptFile;
265
266
267
    // replace with locale-agnostic string
    if (m_scriptFile == CURRENT_FILE)
        m_scriptFile = M_CURRENT_FILE;
268

269
    if (m_scriptFile.isEmpty() || m_scriptFile == M_CURRENT_FILE) {
270
271
272
273
274
275
276
        m_usingCurrentFile = true;
        changeCurrentFile(Core::EditorManager::instance()->currentEditor());
    } else {
        m_usingCurrentFile = false;
        m_mainScriptFilename = qmlTarget()->qmlProject()->projectDir().absoluteFilePath(scriptFile);
        setEnabled(true);
    }
Kai Koehne's avatar
Kai Koehne committed
277
278
}

279
void QmlProjectRunConfiguration::onViewerChanged()
Kai Koehne's avatar
Kai Koehne committed
280
281
282
283
284
285
{
    if (Utils::PathChooser *chooser = qobject_cast<Utils::PathChooser *>(sender())) {
        m_qmlViewerCustomPath = chooser->path();
    }
}

286
void QmlProjectRunConfiguration::onViewerArgsChanged()
Kai Koehne's avatar
Kai Koehne committed
287
288
289
290
291
292
293
294
{
    if (QLineEdit *lineEdit = qobject_cast<QLineEdit*>(sender()))
        m_qmlViewerArgs = lineEdit->text();
}

void QmlProjectRunConfiguration::onDebugServerPortChanged()
{
    if (QSpinBox *spinBox = qobject_cast<QSpinBox*>(sender())) {
295
        m_debugData.serverPort = spinBox->value();
Kai Koehne's avatar
Kai Koehne committed
296
297
298
299
300
301
302
303
304
305
    }
}

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);
306
307
    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
308
309
310
311
312
313
314
    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();
315
    m_scriptFile = map.value(QLatin1String(Constants::QML_MAINSCRIPT_KEY), M_CURRENT_FILE).toString();
316
317
    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();
318
    setMainScript(m_scriptFile);
Kai Koehne's avatar
Kai Koehne committed
319
320
321
322

    return RunConfiguration::fromMap(map);
}

323
324
325
326
327
328
329
330
void QmlProjectRunConfiguration::changeCurrentFile(Core::IEditor *editor)
{
    if (m_usingCurrentFile) {
        bool enable = false;
        if (editor) {
            m_currentFileFilename = editor->file()->fileName();
            if (Core::ICore::instance()->mimeDatabase()->findByFile(mainScript()).matchesType(QLatin1String("application/x-qml")))
                enable = true;
331
332
333
334
335
336
337
338
339
340
341
342
343
344
        } else {
            // find a qml file with lowercase filename. This is slow but only done in initialization/other border cases.
            foreach(const QString& filename, m_projectTarget->qmlProject()->files()) {
                const QFileInfo fi(filename);

                if (!filename.isEmpty() && fi.baseName()[0].isLower()
                    && Core::ICore::instance()->mimeDatabase()->findByFile(fi).matchesType(QLatin1String("application/x-qml")))
                {
                    m_currentFileFilename = filename;
                    enable = true;
                    break;
                }

            }
345
346
347
348
349
350
351
352
353
354
355
356
        }

        setEnabled(enable);
    }
}

void QmlProjectRunConfiguration::setEnabled(bool value)
{
    m_isEnabled = value;
    emit isEnabledChanged(m_isEnabled);
}

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