From 47155f8518f154b7cea32936675e09322d293a8e Mon Sep 17 00:00:00 2001
From: Kai Koehne <kai.koehne@nokia.com>
Date: Tue, 19 Jan 2010 09:33:06 +0100
Subject: [PATCH] New qml based .qmproject file format

---
 .../fileformat/filefilteritems.cpp            | 121 +++++++++++++++
 .../fileformat/filefilteritems.h              |  72 +++++++++
 .../fileformat/fileformat.pri                 |   4 +
 .../fileformat/qmlprojectitem.cpp             | 101 +++++++++++++
 .../fileformat/qmlprojectitem.h               |  55 +++++++
 tests/auto/qml/qml.pro                        |   2 +-
 .../fileformat/data/file1.qml                 |   0
 .../fileformat/data/file1.qrc                 |   0
 .../fileformat/data/file2.qml                 |   0
 .../fileformat/data/subdir/file3.qml          |   0
 .../fileformat/fileformat.pro                 |  21 +++
 .../fileformat/tst_fileformat.cpp             | 141 ++++++++++++++++++
 .../qmlprojectmanager/qmlprojectmanager.pro   |   3 +
 13 files changed, 519 insertions(+), 1 deletion(-)
 create mode 100644 src/plugins/qmlprojectmanager/fileformat/filefilteritems.cpp
 create mode 100644 src/plugins/qmlprojectmanager/fileformat/filefilteritems.h
 create mode 100644 src/plugins/qmlprojectmanager/fileformat/fileformat.pri
 create mode 100644 src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.cpp
 create mode 100644 src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h
 create mode 100644 tests/auto/qml/qmlprojectmanager/fileformat/data/file1.qml
 create mode 100644 tests/auto/qml/qmlprojectmanager/fileformat/data/file1.qrc
 create mode 100644 tests/auto/qml/qmlprojectmanager/fileformat/data/file2.qml
 create mode 100644 tests/auto/qml/qmlprojectmanager/fileformat/data/subdir/file3.qml
 create mode 100644 tests/auto/qml/qmlprojectmanager/fileformat/fileformat.pro
 create mode 100644 tests/auto/qml/qmlprojectmanager/fileformat/tst_fileformat.cpp
 create mode 100644 tests/auto/qml/qmlprojectmanager/qmlprojectmanager.pro

diff --git a/src/plugins/qmlprojectmanager/fileformat/filefilteritems.cpp b/src/plugins/qmlprojectmanager/fileformat/filefilteritems.cpp
new file mode 100644
index 00000000000..d80f3de97ec
--- /dev/null
+++ b/src/plugins/qmlprojectmanager/fileformat/filefilteritems.cpp
@@ -0,0 +1,121 @@
+#include "filefilteritems.h"
+#include <qdebug.h>
+
+namespace QmlProjectManager {
+
+FileFilterBaseItem::FileFilterBaseItem(QObject *parent) :
+        QmlProjectContentItem(parent),
+        m_recursive(false)
+{
+}
+
+QString FileFilterBaseItem::directory() const
+{
+    return m_rootDir;
+}
+
+void FileFilterBaseItem::setDirectory(const QString &dirPath)
+{
+    if (m_rootDir == dirPath)
+        return;
+    m_rootDir = dirPath;
+    emit directoryChanged();
+
+    updateFileList();
+}
+
+void FileFilterBaseItem::setDefaultDirectory(const QString &dirPath)
+{
+    if (m_defaultDir == dirPath)
+        return;
+    m_defaultDir = dirPath;
+
+    updateFileList();
+}
+
+QString FileFilterBaseItem::filter() const
+{
+    return m_filter;
+}
+
+void FileFilterBaseItem::setFilter(const QString &filter)
+{
+    if (filter == m_filter)
+        return;
+    m_filter = filter;
+    m_regex.setPattern(m_filter);
+    m_regex.setPatternSyntax(QRegExp::Wildcard);
+
+    emit filterChanged();
+    updateFileList();
+}
+
+bool FileFilterBaseItem::recursive() const
+{
+    return m_recursive;
+}
+
+void FileFilterBaseItem::setRecursive(bool recursive)
+{
+    if (recursive == m_recursive)
+        return;
+    m_recursive = recursive;
+    updateFileList();
+}
+
+QStringList FileFilterBaseItem::files() const
+{
+    return m_files.toList();
+}
+
+QString FileFilterBaseItem::absoluteDir() const
+{
+    QString absoluteDir;
+    if (QFileInfo(m_rootDir).isAbsolute()) {
+        absoluteDir = m_rootDir;
+    } else if (!m_defaultDir.isEmpty()) {
+        absoluteDir = m_defaultDir + QLatin1Char('/') + m_rootDir;
+    }
+
+    return absoluteDir;
+}
+
+void FileFilterBaseItem::updateFileList()
+{
+    const QString dir = absoluteDir();
+    if (dir.isEmpty())
+        return;
+
+    const QSet<QString> newFiles = filesInSubTree(QDir(m_defaultDir), QDir(dir));
+    if (newFiles != m_files) {
+        m_files = newFiles;
+        emit filesChanged();
+    }
+}
+
+QSet<QString> FileFilterBaseItem::filesInSubTree(const QDir &rootDir, const QDir &dir)
+{
+    QSet<QString> fileSet;
+
+    foreach (const QFileInfo &file, dir.entryInfoList(QDir::Files)) {
+        if (m_regex.exactMatch(file.fileName()))
+            fileSet.insert(rootDir.relativeFilePath(file.absoluteFilePath()));
+    }
+
+    if (m_recursive) {
+        foreach (const QFileInfo &subDir, dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot)) {
+            fileSet += filesInSubTree(rootDir, QDir(subDir.absoluteFilePath()));
+        }
+    }
+    return fileSet;
+}
+
+QmlFileFilterItem::QmlFileFilterItem(QObject *parent)
+    : FileFilterBaseItem(parent)
+{
+    setFilter(QLatin1String("*.qml"));
+}
+
+} // namespace QmlProjectManager
+
+QML_DEFINE_TYPE(QmlProject,1,0,QmlFiles,QmlProjectManager::QmlFileFilterItem)
diff --git a/src/plugins/qmlprojectmanager/fileformat/filefilteritems.h b/src/plugins/qmlprojectmanager/fileformat/filefilteritems.h
new file mode 100644
index 00000000000..5857495be14
--- /dev/null
+++ b/src/plugins/qmlprojectmanager/fileformat/filefilteritems.h
@@ -0,0 +1,72 @@
+#ifndef FILEFILTERITEMS_H
+#define FILEFILTERITEMS_H
+
+#include <QDir>
+#include <QObject>
+#include <QSet>
+#include <qml.h>
+
+#include "qmlprojectitem.h"
+
+namespace QmlProjectManager {
+
+class FileFilterBaseItem : public QmlProjectContentItem {
+    Q_OBJECT
+
+    Q_PROPERTY(QString directory READ directory WRITE setDirectory NOTIFY directoryChanged)
+    Q_PROPERTY(bool recursive READ recursive WRITE setRecursive NOTIFY recursiveChanged)
+
+    Q_PROPERTY(QList<QString> files READ files NOTIFY filesChanged)
+
+public:
+    FileFilterBaseItem(QObject *parent = 0);
+
+    QString directory() const;
+    void setDirectory(const QString &directoryPath);
+
+    void setDefaultDirectory(const QString &directoryPath);
+
+    QString filter() const;
+    void setFilter(const QString &filter);
+
+    bool recursive() const;
+    void setRecursive(bool recursive);
+
+    virtual QStringList files() const;
+
+signals:
+    void directoryChanged();
+    void recursiveChanged();
+    void filterChanged();
+    void filesChanged();
+
+private:
+    QString absoluteDir() const;
+
+    void updateFileList();
+    QSet<QString> filesInSubTree(const QDir &rootDir, const QDir &dir);
+
+    QString m_rootDir;
+    QString m_defaultDir;
+
+    QString m_filter;
+    QRegExp m_regex;
+    bool m_recursive;
+
+    QSet<QString> m_files;
+
+    friend class ProjectItem;
+};
+
+class QmlFileFilterItem : public FileFilterBaseItem {
+    Q_OBJECT
+
+public:
+    QmlFileFilterItem(QObject *parent = 0);
+};
+
+} // namespace QmlProjectManager
+
+QML_DECLARE_TYPE(QmlProjectManager::QmlFileFilterItem)
+
+#endif // FILEFILTERITEMS_HPROJECTITEM_H
diff --git a/src/plugins/qmlprojectmanager/fileformat/fileformat.pri b/src/plugins/qmlprojectmanager/fileformat/fileformat.pri
new file mode 100644
index 00000000000..278e85e6c57
--- /dev/null
+++ b/src/plugins/qmlprojectmanager/fileformat/fileformat.pri
@@ -0,0 +1,4 @@
+HEADERS += $$PWD/qmlprojectitem.h \
+           $$PWD/filefilteritems.h
+SOURCES += $$PWD/qmlprojectitem.cpp \
+           $$PWD/filefilteritems.cpp
diff --git a/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.cpp b/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.cpp
new file mode 100644
index 00000000000..4bc66ecbd49
--- /dev/null
+++ b/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.cpp
@@ -0,0 +1,101 @@
+#include "qmlprojectitem.h"
+#include "filefilteritems.h"
+#include <qdebug.h>
+
+namespace QmlProjectManager {
+
+class QmlProjectItemPrivate : public QObject {
+    Q_OBJECT
+
+public:
+    QString sourceDirectory;
+
+    QList<QmlFileFilterItem*> qmlFileFilters() const;
+
+    // content property
+    QmlConcreteList<QmlProjectContentItem*> content;
+};
+
+QList<QmlFileFilterItem*> QmlProjectItemPrivate::qmlFileFilters() const
+{
+    QList<QmlFileFilterItem*> qmlFilters;
+    for (int i = 0; i < content.size(); ++i) {
+        QmlProjectContentItem *contentElement = content.at(i);
+        QmlFileFilterItem *qmlFileFilter = qobject_cast<QmlFileFilterItem*>(contentElement);
+        if (qmlFileFilter) {
+            qmlFilters << qmlFileFilter;
+        }
+    }
+    return qmlFilters;
+}
+
+QmlProjectItem::QmlProjectItem(QObject *parent) :
+        QObject(parent),
+        d_ptr(new QmlProjectItemPrivate)
+{
+//    Q_D(QmlProjectItem);
+//
+//    QmlFileFilter *defaultQmlFilter = new QmlFileFilter(this);
+//    d->content.append(defaultQmlFilter);
+}
+
+QmlProjectItem::~QmlProjectItem()
+{
+    delete d_ptr;
+}
+
+QmlList<QmlProjectContentItem*> *QmlProjectItem::content()
+{
+    Q_D(QmlProjectItem);
+    return &d->content;
+}
+
+QString QmlProjectItem::sourceDirectory() const
+{
+    const Q_D(QmlProjectItem);
+    return d->sourceDirectory;
+}
+
+void QmlProjectItem::setSourceDirectory(const QString &directoryPath)
+{
+    Q_D(QmlProjectItem);
+
+    if (d->sourceDirectory == directoryPath)
+        return;
+
+    d->sourceDirectory = directoryPath;
+
+    for (int i = 0; i < d->content.size(); ++i) {
+        QmlProjectContentItem *contentElement = d->content.at(i);
+        FileFilterBaseItem *fileFilter = qobject_cast<FileFilterBaseItem*>(contentElement);
+        if (fileFilter)
+            fileFilter->setDefaultDirectory(directoryPath);
+    }
+
+    emit sourceDirectoryChanged();
+}
+
+QStringList QmlProjectItem::qmlFiles() const
+{
+    const Q_D(QmlProjectItem);
+    QStringList qmlFiles;
+
+    for (int i = 0; i < d->content.size(); ++i) {
+        QmlProjectContentItem *contentElement = d->content.at(i);
+        QmlFileFilterItem *qmlFileFilter = qobject_cast<QmlFileFilterItem*>(contentElement);
+        if (qmlFileFilter) {
+            foreach (const QString &file, qmlFileFilter->files()) {
+                if (!qmlFiles.contains(file))
+                    qmlFiles << file;
+            }
+        }
+    }
+    return qmlFiles;
+}
+
+} // namespace QmlProjectManager
+
+QML_DEFINE_NOCREATE_TYPE(QmlProjectManager::QmlProjectContentItem)
+QML_DEFINE_TYPE(QmlProject,1,0,Project,QmlProjectManager::QmlProjectItem)
+
+#include "qmlprojectitem.moc"
diff --git a/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h b/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h
new file mode 100644
index 00000000000..ce7a6b54670
--- /dev/null
+++ b/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h
@@ -0,0 +1,55 @@
+#ifndef PROJECTITEM_H
+#define PROJECTITEM_H
+
+#include <QObject>
+#include <qml.h>
+
+namespace QmlProjectManager {
+
+class QmlProjectContentItem : public QObject {
+    // base class for all elements that should be direct children of Project element
+    Q_OBJECT
+
+public:
+    QmlProjectContentItem(QObject *parent = 0) : QObject(parent) {}
+};
+
+class QmlProjectItemPrivate;
+
+class QmlProjectItem : public QObject {
+    Q_OBJECT
+    Q_DECLARE_PRIVATE(QmlProjectItem)
+    Q_DISABLE_COPY(QmlProjectItem)
+
+    Q_PROPERTY(QmlList<QmlProjectManager::QmlProjectContentItem*> *content READ content DESIGNABLE false)
+    Q_PROPERTY(QString sourceDirectory READ sourceDirectory NOTIFY sourceDirectoryChanged)
+
+    Q_CLASSINFO("DefaultProperty", "content");
+
+public:
+    QmlProjectItem(QObject *parent = 0);
+    ~QmlProjectItem();
+
+    QmlList<QmlProjectContentItem*> *content();
+
+    QString sourceDirectory() const;
+    void setSourceDirectory(const QString &directoryPath);
+
+    QStringList qmlFiles() const;
+
+signals:
+    void sourceDirectoryChanged();
+
+protected:
+    QmlProjectItemPrivate *d_ptr;
+
+};
+
+} // namespace QmlProjectManager
+
+QML_DECLARE_TYPE(QmlProjectManager::QmlProjectItem);
+QML_DECLARE_TYPE(QmlProjectManager::QmlProjectContentItem);
+Q_DECLARE_METATYPE(QList<QmlProjectManager::QmlProjectContentItem *>);
+
+
+#endif // PROJECTITEM_H
diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro
index a902fa669cf..5a8a473eaf8 100644
--- a/tests/auto/qml/qml.pro
+++ b/tests/auto/qml/qml.pro
@@ -1,3 +1,3 @@
 TEMPLATE = subdirs
 
-SUBDIRS += qmleditor qmldesigner
+SUBDIRS += qmleditor qmldesigner qmlprojectmanager
diff --git a/tests/auto/qml/qmlprojectmanager/fileformat/data/file1.qml b/tests/auto/qml/qmlprojectmanager/fileformat/data/file1.qml
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tests/auto/qml/qmlprojectmanager/fileformat/data/file1.qrc b/tests/auto/qml/qmlprojectmanager/fileformat/data/file1.qrc
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tests/auto/qml/qmlprojectmanager/fileformat/data/file2.qml b/tests/auto/qml/qmlprojectmanager/fileformat/data/file2.qml
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tests/auto/qml/qmlprojectmanager/fileformat/data/subdir/file3.qml b/tests/auto/qml/qmlprojectmanager/fileformat/data/subdir/file3.qml
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tests/auto/qml/qmlprojectmanager/fileformat/fileformat.pro b/tests/auto/qml/qmlprojectmanager/fileformat/fileformat.pro
new file mode 100644
index 00000000000..b4b4577d86b
--- /dev/null
+++ b/tests/auto/qml/qmlprojectmanager/fileformat/fileformat.pro
@@ -0,0 +1,21 @@
+TEMPLATE = app
+
+CONFIG += qt warn_on console depend_includepath
+CONFIG -= app_bundle
+
+QT += testlib \
+    script \
+    declarative
+
+PLUGIN_DIR=../../../../../src/plugins/qmlprojectmanager
+
+include($$PLUGIN_DIR/fileformat/fileformat.pri)
+
+INCLUDEPATH += $$PLUGIN_DIR/fileformat
+
+TARGET=tst_$$TARGET
+
+DEFINES += SRCDIR=\\\"$$PWD\\\"
+
+TEMPLATE = app
+SOURCES += tst_fileformat.cpp
diff --git a/tests/auto/qml/qmlprojectmanager/fileformat/tst_fileformat.cpp b/tests/auto/qml/qmlprojectmanager/fileformat/tst_fileformat.cpp
new file mode 100644
index 00000000000..18a90bf4741
--- /dev/null
+++ b/tests/auto/qml/qmlprojectmanager/fileformat/tst_fileformat.cpp
@@ -0,0 +1,141 @@
+#include "qmlprojectitem.h"
+#include "filefilteritems.h"
+#include <QmlComponent>
+#include <QmlContext>
+#include <QmlEngine>
+#include <QtTest>
+
+using namespace QmlProjectManager;
+
+class TestProject : public QObject
+{
+    Q_OBJECT
+public:
+    TestProject();
+
+private slots:
+    void testQmlFileFilter();
+};
+
+TestProject::TestProject()
+{
+
+}
+
+QString testDataDir = QLatin1String(SRCDIR "/data");
+
+void TestProject::testQmlFileFilter()
+{
+    //
+    // search for qml files in local directory
+    //
+    QString projectFile = QLatin1String(
+            "import QmlProject 1.0\n"
+            "Project {\n"
+            "  QmlFiles {"
+            "  }"
+            "}\n");
+
+    {
+        QmlEngine engine;
+        QmlComponent component(&engine);
+        component.setData(projectFile.toUtf8(), QUrl());
+        if (!component.isReady())
+            qDebug() << component.errorsString();
+        QVERIFY(component.isReady());
+
+        QmlProjectItem *project = qobject_cast<QmlProjectItem*>(component.create());
+        QVERIFY(project);
+
+        project->setSourceDirectory(testDataDir);
+
+        QStringList expectedFiles(QStringList() << "file1.qml" << "file2.qml");
+        QCOMPARE(project->qmlFiles().toSet(), expectedFiles.toSet());
+    }
+
+    //
+    // search for all qml files in all subdirectories
+    //
+    projectFile = QLatin1String(
+            "import QmlProject 1.0\n"
+            "Project {\n"
+            "  QmlFiles {\n"
+            "    recursive: true\n"
+            "  }\n"
+            "}\n");
+
+    {
+        QmlEngine engine;
+        QmlComponent component(&engine);
+        component.setData(projectFile.toUtf8(), QUrl());
+        QVERIFY(component.isReady());
+
+        QmlProjectItem *project = qobject_cast<QmlProjectItem*>(component.create());
+        QVERIFY(project);
+
+        project->setSourceDirectory(testDataDir);
+
+        QStringList expectedFiles(QStringList() << "file1.qml" << "file2.qml" << "subdir/file3.qml");
+        QCOMPARE(project->qmlFiles().toSet(), expectedFiles.toSet());
+    }
+
+    //
+    // search for all qml files in subdirectory
+    //
+    projectFile = QLatin1String(
+            "import QmlProject 1.0\n"
+            "Project {\n"
+            "  QmlFiles {\n"
+            "    directory: \"subdir\"\n"
+            "  }\n"
+            "}\n");
+
+    {
+        QmlEngine engine;
+        QmlComponent component(&engine);
+        component.setData(projectFile.toUtf8(), QUrl());
+        QVERIFY(component.isReady());
+
+        QmlProjectItem *project = qobject_cast<QmlProjectItem*>(component.create());
+        QVERIFY(project);
+
+        project->setSourceDirectory(testDataDir);
+
+        QStringList expectedFiles(QStringList() << "subdir/file3.qml");
+        QCOMPARE(project->qmlFiles().toSet(), expectedFiles.toSet());
+    }
+
+    //
+    // combination
+    //
+    projectFile = QLatin1String(
+            "import QmlProject 1.0\n"
+            "Project {\n"
+            "  QmlFiles {\n"
+            "  }"
+            "  QmlFiles {\n"
+            "    directory: \"subdir\"\n"
+            "  }\n"
+            "}\n");
+
+    {
+        QmlEngine engine;
+        QmlComponent component(&engine);
+        component.setData(projectFile.toUtf8(), QUrl());
+        qDebug() << component.errorsString();
+        QVERIFY(component.isReady());
+
+        QmlProjectItem *project = qobject_cast<QmlProjectItem*>(component.create());
+        QVERIFY(project);
+
+        project->setSourceDirectory(testDataDir);
+
+        QStringList expectedFiles(QStringList() << "file1.qml" << "file2.qml" << "subdir/file3.qml");
+        QCOMPARE(project->qmlFiles().size(), 3);
+        QCOMPARE(project->qmlFiles().toSet(), expectedFiles.toSet());
+    }
+}
+
+
+QTEST_MAIN(TestProject);
+#include "tst_fileformat.moc"
diff --git a/tests/auto/qml/qmlprojectmanager/qmlprojectmanager.pro b/tests/auto/qml/qmlprojectmanager/qmlprojectmanager.pro
new file mode 100644
index 00000000000..e36121cd0de
--- /dev/null
+++ b/tests/auto/qml/qmlprojectmanager/qmlprojectmanager.pro
@@ -0,0 +1,3 @@
+TEMPLATE = subdirs
+
+SUBDIRS += fileformat
-- 
GitLab