subcomponentmanager.cpp 12.2 KB
Newer Older
1 2 3 4
/**************************************************************************
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
6
**
Eike Ziller's avatar
Eike Ziller committed
7
** Contact: http://www.qt-project.org/
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.
**
28 29 30 31
**
**************************************************************************/

#include "subcomponentmanager.h"
32
#include "model.h"
33 34
#include "metainfo.h"

35 36
#include <utils/hostosinfo.h>

37 38 39 40 41 42
#include <QDir>
#include <QMetaType>
#include <QUrl>

enum { debug = false };

43
QT_BEGIN_NAMESPACE
44

45
// Allow usage of QFileInfo in qSort
46 47 48 49 50 51 52

static bool operator<(const QFileInfo &file1, const QFileInfo &file2)
{
    return file1.filePath() < file2.filePath();
}


53
QT_END_NAMESPACE
54

55 56 57 58 59 60
static inline QStringList importPaths() {
    QStringList paths;

    // env import paths
    QByteArray envImportPath = qgetenv("QML_IMPORT_PATH");
    if (!envImportPath.isEmpty()) {
61 62
        paths = QString::fromLatin1(envImportPath)
                .split(Utils::HostOsInfo::pathListSeparator(), QString::SkipEmptyParts);
63 64 65 66 67
    }

    return paths;
}

68
namespace QmlDesigner {
69
static const QString s_qmlFilePattern = QString(QLatin1String("*.qml"));
70

71 72 73
SubComponentManager::SubComponentManager(Model *model, QObject *parent)
    : QObject(parent),
      m_model(model)
74 75 76 77
{
    connect(&m_watcher, SIGNAL(directoryChanged(QString)), this, SLOT(parseDirectory(QString)));
}

78
void SubComponentManager::addImport(int pos, const Import &import)
79 80
{
    if (debug)
81
        qDebug() << Q_FUNC_INFO << pos << import.file().toAscii();
82

83 84
    if (import.isFileImport()) {
        QFileInfo dirInfo = QFileInfo(m_filePath.resolved(import.file()).toLocalFile());
85
        if (dirInfo.exists() && dirInfo.isDir()) {
86 87
            const QString canonicalDirPath = dirInfo.canonicalFilePath();
            m_watcher.addPath(canonicalDirPath);
88
            //m_dirToQualifier.insertMulti(canonicalDirPath, import.qualifier()); ### todo: proper support for import as
89 90
        }
    } else {
91
        QString url = import.url();
92 93 94
        
        url.replace(QLatin1Char('.'), QLatin1Char('/'));

Friedemann Kleint's avatar
Friedemann Kleint committed
95 96
        foreach (const QString &path, importPaths()) {
            url  = path + QLatin1Char('/') + url;
97 98 99 100
            QFileInfo dirInfo = QFileInfo(url);
            if (dirInfo.exists() && dirInfo.isDir()) {
                const QString canonicalDirPath = dirInfo.canonicalFilePath();
                m_watcher.addPath(canonicalDirPath);
101
                //m_dirToQualifier.insertMulti(canonicalDirPath, import.qualifier()); ### todo: proper support for import as
102 103
            }
        }
104
        // TODO: QDeclarativeDomImport::Library
105 106 107 108 109
    }

    m_imports.insert(pos, import);
}

110
void SubComponentManager::removeImport(int pos)
111
{
112
    const Import import = m_imports.takeAt(pos);
113

114 115
    if (import.isFileImport()) {
        const QFileInfo dirInfo = QFileInfo(m_filePath.resolved(import.file()).toLocalFile());
116
        const QString canonicalDirPath = dirInfo.canonicalFilePath();
117

118
        //m_dirToQualifier.remove(canonicalDirPath, import.qualifier()); ### todo: proper support for import as
119

120 121
        if (!m_dirToQualifier.contains(canonicalDirPath))
            m_watcher.removePath(canonicalDirPath);
122

123 124 125 126
//        foreach (const QFileInfo &monitoredFile, watchedFiles(canonicalDirPath)) { ### todo: proper support for import as
//            if (!m_dirToQualifier.contains(canonicalDirPath))
//                unregisterQmlFile(monitoredFile, import.qualifier());
//        }
127
    } else {
128
            // TODO: QDeclarativeDomImport::Library
129 130 131
    }
}

132
void SubComponentManager::parseDirectories()
133 134 135 136 137
{
    if (!m_filePath.isEmpty()) {
        const QString file = m_filePath.toLocalFile();
        QFileInfo dirInfo = QFileInfo(QFileInfo(file).path());
        if (dirInfo.exists() && dirInfo.isDir())
138
            parseDirectory(dirInfo.canonicalFilePath());
139 140
    }

141 142 143
    foreach (const Import &import, m_imports) {
        if (import.isFileImport()) {
            QFileInfo dirInfo = QFileInfo(m_filePath.resolved(import.file()).toLocalFile());
144
            if (dirInfo.exists() && dirInfo.isDir()) {
145
                parseDirectory(dirInfo.canonicalFilePath());
146
            }
147
        } else {
148
            QString url = import.url();
Friedemann Kleint's avatar
Friedemann Kleint committed
149
            foreach (const QString &path, importPaths()) {
150
                url.replace(QLatin1Char('.'), QLatin1Char('/'));
Friedemann Kleint's avatar
Friedemann Kleint committed
151
                url  = path + QLatin1Char('/') + url;
152 153 154 155 156 157
                QFileInfo dirInfo = QFileInfo(url);
                if (dirInfo.exists() && dirInfo.isDir()) {
                    //### todo full qualified names QString nameSpace = import.uri();
                    parseDirectory(dirInfo.canonicalFilePath(), false);
                }
            }
158 159 160 161
        }
    }
}

162
void SubComponentManager::parseDirectory(const QString &canonicalDirPath, bool addToLibrary, const QString& qualification)
163 164
{
    if (debug)
165
        qDebug() << Q_FUNC_INFO << canonicalDirPath;
166

167
    QDir dir(canonicalDirPath);
168

169
    dir.setNameFilters(QStringList(s_qmlFilePattern));
170 171
    dir.setFilter(QDir::Files | QDir::Readable | QDir::CaseSensitive);

172
    QList<QFileInfo> monitoredList = watchedFiles(canonicalDirPath);
173 174 175 176 177 178 179
    QList<QFileInfo> newList;
    foreach (const QFileInfo &qmlFile, dir.entryInfoList()) {
        if (QFileInfo(m_filePath.toLocalFile()) == qmlFile) {
            // do not parse main file
            continue;
        }
        if (!qmlFile.fileName().at(0).isUpper()) {
180
            // QML sub components must be upper case
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
            continue;
        }
        newList << qmlFile;
    }

    qSort(monitoredList);
    qSort(newList);

    if (debug)
        qDebug() << "monitored list " << monitoredList.size() << "new list " << newList.size();
    QList<QFileInfo>::const_iterator oldIter = monitoredList.constBegin();
    QList<QFileInfo>::const_iterator newIter = newList.constBegin();

    while (oldIter != monitoredList.constEnd() && newIter != newList.constEnd()) {
        const QFileInfo oldFileInfo = *oldIter;
        const QFileInfo newFileInfo = *newIter;
        if (oldFileInfo == newFileInfo) {
            ++oldIter;
            ++newIter;
            continue;
        }
        if (oldFileInfo < newFileInfo) {
203
            foreach (const QString &qualifier, m_dirToQualifier.value(canonicalDirPath))
204 205 206 207 208 209
                unregisterQmlFile(oldFileInfo, qualifier);
            m_watcher.removePath(oldFileInfo.filePath());
            ++oldIter;
            continue;
        }
        // oldFileInfo > newFileInfo
210
        parseFile(newFileInfo.filePath(), addToLibrary, qualification);
211 212 213 214
        ++newIter;
    }

    while (oldIter != monitoredList.constEnd()) {
215
        foreach (const QString &qualifier, m_dirToQualifier.value(canonicalDirPath))
216 217 218 219 220
            unregisterQmlFile(*oldIter, qualifier);
        ++oldIter;
    }

    while (newIter != newList.constEnd()) {
221
        parseFile(newIter->filePath(), addToLibrary, qualification);
222
        if (debug)
223
            qDebug() << "m_watcher.addPath(" << newIter->filePath() << ')';
224 225 226 227
        ++newIter;
    }
}

228
void SubComponentManager::parseFile(const QString &canonicalFilePath, bool addToLibrary, const QString& /* qualification */)
229 230
{
    if (debug)
231
        qDebug() << Q_FUNC_INFO << canonicalFilePath;
232

233
    QFile file(canonicalFilePath);
234 235 236 237
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        return;
    }

238
    QString dir = QFileInfo(canonicalFilePath).path();
239
    foreach (const QString &qualifier, m_dirToQualifier.values(dir)) {
240
        registerQmlFile(canonicalFilePath, qualifier, addToLibrary);
241
    }
242
    registerQmlFile(canonicalFilePath, QString(), addToLibrary);
243 244
}

245
void SubComponentManager::parseFile(const QString &canonicalFilePath)
246 247 248 249
{
    parseFile(canonicalFilePath, true, QString());
}

250
// dirInfo must already contain a canonical path
251
QList<QFileInfo> SubComponentManager::watchedFiles(const QString &canonicalDirPath)
252 253 254 255 256
{
    QList<QFileInfo> files;

    foreach (const QString &monitoredFile, m_watcher.files()) {
        QFileInfo fileInfo(monitoredFile);
257
        if (fileInfo.dir().absolutePath() == canonicalDirPath) {
258 259 260 261 262 263
            files.append(fileInfo);
        }
    }
    return files;
}

264
void SubComponentManager::unregisterQmlFile(const QFileInfo &fileInfo, const QString &qualifier)
265 266 267
{
    QString componentName = fileInfo.baseName();
    if (!qualifier.isEmpty())
268
        componentName = qualifier + '.' + componentName;
269 270
}

271 272
static inline bool isDepricatedQtType(const QString &typeName)
{
273
    if (typeName.length() < 8)
274 275
        return false;

276
    return typeName.contains("Qt.");
277 278 279
}


280
void SubComponentManager::registerQmlFile(const QFileInfo &fileInfo, const QString &qualifier,
281
                                                 bool addToLibrary)
282
{
283 284 285
    if (!model())
        return;

286
    QString componentName = fileInfo.baseName();
287 288 289 290 291

    if (!qualifier.isEmpty()) {
        QString fixedQualifier = qualifier;
        if (qualifier.right(1) == QLatin1String("."))
            fixedQualifier.chop(1); //remove last char if it is a dot
292
        componentName = fixedQualifier + '.' + componentName;
293
    }
294 295 296 297

    if (debug)
        qDebug() << "SubComponentManager" << __FUNCTION__ << componentName;

298 299 300
    if (addToLibrary) {
        // Add file components to the library
        ItemLibraryEntry itemLibraryEntry;
301
        itemLibraryEntry.setType(componentName, -1, -1);
302
        itemLibraryEntry.setName(componentName);
303
        itemLibraryEntry.setCategory("QML Components");
304

305 306 307 308 309 310

        if (model()->metaInfo(componentName).isValid() && model()->metaInfo(componentName).isSubclassOf("QtQuick.Item", -1, -1) &&
            !model()->metaInfo().itemLibraryInfo()->containsEntry(itemLibraryEntry)) {

            model()->metaInfo().itemLibraryInfo()->addEntry(itemLibraryEntry);
        }
311
    }
312 313
}

314
Model *SubComponentManager::model() const
315 316 317 318
{
    return m_model.data();
}

319 320 321 322 323 324 325 326 327 328

/*!
  \class SubComponentManager

  Detects & monitors (potential) component files in a list of directories, and registers
  these in the metatype system.
*/

QStringList SubComponentManager::directories() const
{
329
    return m_watcher.directories();
330 331 332 333
}

QStringList SubComponentManager::qmlFiles() const
{
334
    return m_watcher.files();
335 336
}

337
void SubComponentManager::update(const QUrl &filePath, const QList<Import> &imports)
338 339 340 341 342 343
{
    if (debug)
        qDebug() << Q_FUNC_INFO << filePath << imports.size();

    QFileInfo oldDir, newDir;

344 345
    if (!m_filePath.isEmpty()) {
        const QString file = m_filePath.toLocalFile();
346 347 348 349 350 351 352
        oldDir = QFileInfo(QFileInfo(file).path());
    }
    if (!filePath.isEmpty()) {
        const QString file = filePath.toLocalFile();
        newDir = QFileInfo(QFileInfo(file).path());
    }

353
    m_filePath = filePath;
354 355 356 357 358 359

    //
    // (implicit) import of local directory
    //
    if (oldDir != newDir) {
        if (!oldDir.filePath().isEmpty()) {
360 361 362
            m_dirToQualifier.remove(oldDir.canonicalFilePath(), QString());
            if (!m_dirToQualifier.contains(oldDir.canonicalFilePath()))
                m_watcher.removePath(oldDir.filePath());
363 364 365
        }

        if (!newDir.filePath().isEmpty()) {
366
            m_dirToQualifier.insertMulti(newDir.canonicalFilePath(), QString());
367 368 369 370 371 372 373 374 375
        }
    }

    //
    // Imports
    //

    // skip first list items until the lists differ
    int i = 0;
376 377
    while (i < qMin(imports.size(), m_imports.size())) {
        if (!(imports.at(i) == m_imports.at(i)))
378 379 380 381
            break;
        ++i;
    }

382 383
    for (int ii = m_imports.size() - 1; ii >= i; --ii)
        removeImport(ii);
384 385

    for (int ii = i; ii < imports.size(); ++ii) {
386
        addImport(ii, imports.at(ii));
387 388
    }

389
    parseDirectories();
390 391 392 393
}

} // namespace QmlDesigner