From c4e491387007d89f86b6adb6ea0609842a3a2a4b Mon Sep 17 00:00:00 2001
From: Kai Koehne <kai.koehne@nokia.com>
Date: Fri, 3 Dec 2010 17:04:29 +0100
Subject: [PATCH] QmlProject: Add mainFile property

The mainFile property of QmlProject is the default file to run. People
have still the opportunity to override this in their run settings,
though.

The wizard generated code was updated accordingly. Note that this makes
projects generated by the wizard incompatible with QtCreator 2.1!

Task-number: QTCREATORBUG-3249
---
 .../qmlproject-types.xml                      |  1 +
 .../fileformat/qmlprojectitem.cpp             | 16 +++++
 .../fileformat/qmlprojectitem.h               |  6 ++
 src/plugins/qmlprojectmanager/qmlproject.cpp  |  7 ++
 src/plugins/qmlprojectmanager/qmlproject.h    |  1 +
 .../qmlprojectapplicationwizard.cpp           | 10 ++-
 .../qmlprojectrunconfiguration.cpp            | 69 +++++++++++++-----
 .../qmlprojectrunconfiguration.h              |  9 ++-
 .../qmlprojectrunconfigurationwidget.cpp      | 70 ++++++++++++++-----
 .../qmlprojectrunconfigurationwidget.h        |  8 +--
 .../fileformat/tst_fileformat.cpp             | 26 +++++++
 11 files changed, 183 insertions(+), 40 deletions(-)

diff --git a/share/qtcreator/qml-type-descriptions/qmlproject-types.xml b/share/qtcreator/qml-type-descriptions/qmlproject-types.xml
index 0a7a68e0348..192b45d6860 100644
--- a/share/qtcreator/qml-type-descriptions/qmlproject-types.xml
+++ b/share/qtcreator/qml-type-descriptions/qmlproject-types.xml
@@ -6,6 +6,7 @@
             <export module="QmlProject" version="1.1" type="Project"/>
         </exports>
         <property name="sourceDirectory" type="string"/>
+        <property name="mainFile" type="string"/>
         <property name="importPaths" type="string" isList="true"/>
         <property name="content" type="QmlProjectItem" isList="true"/>
     </type>
diff --git a/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.cpp b/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.cpp
index 8abc6eac7d6..a0a7b275bb9 100644
--- a/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.cpp
+++ b/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.cpp
@@ -11,6 +11,7 @@ public:
     QString sourceDirectory;
     QStringList importPaths;
     QStringList absoluteImportPaths;
+    QString mainFile;
 
     QList<QmlFileFilterItem*> qmlFileFilters() const;
 
@@ -148,6 +149,21 @@ bool QmlProjectItem::matchesFile(const QString &filePath) const
     return false;
 }
 
+QString QmlProjectItem::mainFile() const
+{
+    Q_D(const QmlProjectItem);
+    return d->mainFile;
+}
+
+void QmlProjectItem::setMainFile(const QString &mainFilePath)
+{
+    Q_D(QmlProjectItem);
+    if (mainFilePath == d->mainFile)
+        return;
+    d->mainFile = mainFilePath;
+    emit mainFileChanged();
+}
+
 } // namespace QmlProjectManager
 
 #include "qmlprojectitem.moc"
diff --git a/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h b/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h
index 32a2f1df53b..ff79f9aa781 100644
--- a/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h
+++ b/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h
@@ -25,6 +25,7 @@ class QmlProjectItem : public QObject {
     Q_PROPERTY(QDeclarativeListProperty<QmlProjectManager::QmlProjectContentItem> content READ content DESIGNABLE false)
     Q_PROPERTY(QString sourceDirectory READ sourceDirectory NOTIFY sourceDirectoryChanged)
     Q_PROPERTY(QStringList importPaths READ importPaths WRITE setImportPaths NOTIFY importPathsChanged)
+    Q_PROPERTY(QString mainFile READ mainFile WRITE setMainFile NOTIFY mainFileChanged)
 
     Q_CLASSINFO("DefaultProperty", "content");
 
@@ -43,10 +44,15 @@ public:
     QStringList files() const;
     bool matchesFile(const QString &filePath) const;
 
+    QString mainFile() const;
+    void setMainFile(const QString &mainFilePath);
+
+
 signals:
     void qmlFilesChanged(const QSet<QString> &, const QSet<QString> &);
     void sourceDirectoryChanged();
     void importPathsChanged();
+    void mainFileChanged();
 
 protected:
     QmlProjectItemPrivate *d_ptr;
diff --git a/src/plugins/qmlprojectmanager/qmlproject.cpp b/src/plugins/qmlprojectmanager/qmlproject.cpp
index bb43ea94fd7..a6d89362bac 100644
--- a/src/plugins/qmlprojectmanager/qmlproject.cpp
+++ b/src/plugins/qmlprojectmanager/qmlproject.cpp
@@ -158,6 +158,13 @@ QStringList QmlProject::files() const
     return files;
 }
 
+QString QmlProject::mainFile() const
+{
+    if (m_projectItem)
+        return m_projectItem.data()->mainFile();
+    return QString();
+}
+
 bool QmlProject::validProjectFile() const
 {
     return !m_projectItem.isNull();
diff --git a/src/plugins/qmlprojectmanager/qmlproject.h b/src/plugins/qmlprojectmanager/qmlproject.h
index 0f663eef18b..bbdd2ac5b08 100644
--- a/src/plugins/qmlprojectmanager/qmlproject.h
+++ b/src/plugins/qmlprojectmanager/qmlproject.h
@@ -95,6 +95,7 @@ public:
 
     QDir projectDir() const;
     QStringList files() const;
+    QString mainFile() const;
     QStringList importPaths() const;
 
     bool addFiles(const QStringList &filePaths);
diff --git a/src/plugins/qmlprojectmanager/qmlprojectapplicationwizard.cpp b/src/plugins/qmlprojectmanager/qmlprojectapplicationwizard.cpp
index 6c5a072798b..85cde496b84 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectapplicationwizard.cpp
+++ b/src/plugins/qmlprojectmanager/qmlprojectapplicationwizard.cpp
@@ -31,6 +31,7 @@
 
 #include "qmlprojectconstants.h"
 
+#include <coreplugin/coreconstants.h>
 #include <projectexplorer/customwizard/customwizard.h>
 #include <qt4projectmanager/qt4projectmanagerconstants.h>
 
@@ -39,6 +40,7 @@
 #include <QtGui/QPainter>
 #include <QtGui/QPixmap>
 
+#include <QtCore/QDir>
 #include <QtCore/QTextStream>
 #include <QtCore/QCoreApplication>
 
@@ -141,12 +143,16 @@ Core::GeneratedFiles QmlProjectApplicationWizard::generateFiles(const QWizard *w
 
         out
             //: Comment added to generated .qmlproject file
-            << "/* " << tr("File generated by QtCreator", "qmlproject Template") << " */" << endl
+            << "/* "
+            << tr("File generated by QtCreator, version %1",
+                  "qmlproject Template").arg(Core::Constants::IDE_VERSION_LONG) << " */" << endl
             << endl
-            << "import QmlProject 1.0" << endl
+            << "import QmlProject 1.1" << endl
             << endl
             << "Project {" << endl
             //: Comment added to generated .qmlproject file
+            << "    mainFile: \"" << QDir(projectPath).relativeFilePath(mainFileName) << "\"" << endl
+            << endl
             << "    /* " << tr("Include .qml, .js, and image files from current directory and subdirectories", "qmlproject Template") << " */" << endl
             << "    QmlFiles {" << endl
             << "        directory: \".\"" << endl
diff --git a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp
index 512014cb941..e953208d3b7 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp
+++ b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp
@@ -50,7 +50,7 @@ using namespace QmlProjectManager::Internal;
 
 namespace QmlProjectManager {
 
-const char * const M_CURRENT_FILE  = "CurrentFile";
+const char * const M_CURRENT_FILE = "CurrentFile";
 
 QmlProjectRunConfiguration::QmlProjectRunConfiguration(QmlProjectTarget *parent) :
     ProjectExplorer::RunConfiguration(parent, QLatin1String(Constants::QML_RC_ID)),
@@ -67,12 +67,13 @@ QmlProjectRunConfiguration::QmlProjectRunConfiguration(QmlProjectTarget *parent,
                                                        QmlProjectRunConfiguration *source) :
     ProjectExplorer::RunConfiguration(parent, source),
     m_qtVersionId(source->m_qtVersionId),
+    m_scriptFile(source->m_scriptFile),
     m_qmlViewerArgs(source->m_qmlViewerArgs),
     m_projectTarget(parent),
+    m_usingCurrentFile(source->m_usingCurrentFile),
     m_userEnvironmentChanges(source->m_userEnvironmentChanges)
 {
     ctor();
-    setMainScript(source->m_scriptFile);
     updateQtVersions();
 }
 
@@ -191,29 +192,57 @@ ProjectExplorer::OutputFormatter *QmlProjectRunConfiguration::createOutputFormat
     return new Qt4ProjectManager::QtOutputFormatter(qmlTarget()->qmlProject());
 }
 
+QmlProjectRunConfiguration::MainScriptSource QmlProjectRunConfiguration::mainScriptSource() const
+{
+    if (m_usingCurrentFile) {
+        return FileInEditor;
+    }
+    if (!m_mainScriptFilename.isEmpty()) {
+        return FileInSettings;
+    }
+    return FileInProjectFile;
+}
+
+/**
+  Returns absolute path to main script file.
+  */
 QString QmlProjectRunConfiguration::mainScript() const
 {
-    if (m_usingCurrentFile)
+    if (m_usingCurrentFile) {
         return m_currentFileFilename;
+    }
+
+    if (!m_mainScriptFilename.isEmpty()) {
+        return m_mainScriptFilename;
+    }
 
-    return m_mainScriptFilename;
+    QString path = qmlTarget()->qmlProject()->mainFile();
+    if (QFileInfo(path).isAbsolute()) {
+        return path;
+    } else {
+        return qmlTarget()->qmlProject()->projectDir().absoluteFilePath(path);
+    }
 }
 
-void QmlProjectRunConfiguration::setMainScript(const QString &scriptFile)
+void QmlProjectRunConfiguration::setScriptSource(MainScriptSource source,
+                                                 const QString &settingsPath)
 {
-    m_scriptFile = scriptFile;
-    // replace with locale-agnostic string
-    if (m_scriptFile == CURRENT_FILE)
-        m_scriptFile = M_CURRENT_FILE;
-
-    if (m_scriptFile.isEmpty() || m_scriptFile == M_CURRENT_FILE) {
+    if (source == FileInEditor) { m_scriptFile.clear();
+        m_mainScriptFilename.clear();
         m_usingCurrentFile = true;
-        changeCurrentFile(Core::EditorManager::instance()->currentEditor());
-    } else {
+    } else if (source == FileInProjectFile) {
+        m_scriptFile.clear();
+        m_mainScriptFilename.clear();
+        m_usingCurrentFile = false;
+    } else { // FileInSettings
+        m_scriptFile = settingsPath;
+        m_mainScriptFilename
+                = qmlTarget()->qmlProject()->projectDir().absoluteFilePath(m_scriptFile);
         m_usingCurrentFile = false;
-        m_mainScriptFilename = qmlTarget()->qmlProject()->projectDir().absoluteFilePath(scriptFile);
-        updateEnabled();
     }
+    updateEnabled();
+    if (m_configurationWidget)
+        m_configurationWidget.data()->updateFileComboBox();
 }
 
 Utils::Environment QmlProjectRunConfiguration::environment() const
@@ -245,7 +274,13 @@ bool QmlProjectRunConfiguration::fromMap(const QVariantMap &map)
 
 
     updateQtVersions();
-    setMainScript(m_scriptFile);
+    if (m_scriptFile == M_CURRENT_FILE) {
+        setScriptSource(FileInEditor);
+    } else if (m_scriptFile.isEmpty()) {
+        setScriptSource(FileInProjectFile);
+    } else {
+        setScriptSource(FileInSettings, m_scriptFile);
+    }
 
     return RunConfiguration::fromMap(map);
 }
@@ -284,7 +319,7 @@ void QmlProjectRunConfiguration::updateEnabled()
             }
         }
     } else { // use default one
-        qmlFileFound = !m_mainScriptFilename.isEmpty();
+        qmlFileFound = !mainScript().isEmpty();
     }
 
     bool newValue = (QFileInfo(viewerPath()).exists()
diff --git a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.h b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.h
index 75d60e677fb..b0a24930740 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.h
+++ b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.h
@@ -81,8 +81,15 @@ public:
     int qtVersionId() const;
     Qt4ProjectManager::QtVersion *qtVersion() const;
 
+    enum MainScriptSource {
+        FileInEditor,
+        FileInProjectFile,
+        FileInSettings
+    };
+    MainScriptSource mainScriptSource() const;
+    void setScriptSource(MainScriptSource source, const QString &settingsPath = QString());
+
     QString mainScript() const;
-    void setMainScript(const QString &scriptFile);
 
     Utils::Environment environment() const;
 
diff --git a/src/plugins/qmlprojectmanager/qmlprojectrunconfigurationwidget.cpp b/src/plugins/qmlprojectmanager/qmlprojectrunconfigurationwidget.cpp
index 8a35caac253..cb1eb2ad109 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectrunconfigurationwidget.cpp
+++ b/src/plugins/qmlprojectmanager/qmlprojectrunconfigurationwidget.cpp
@@ -44,7 +44,7 @@
 #include <QLineEdit>
 #include <QFormLayout>
 #include <QPushButton>
-#include <QStringListModel>
+#include <QStandardItemModel>
 
 using Core::ICore;
 using Utils::DebuggerLanguageChooser;
@@ -57,7 +57,7 @@ QmlProjectRunConfigurationWidget::QmlProjectRunConfigurationWidget(QmlProjectRun
     m_runConfiguration(rc),
     m_qtVersionComboBox(0),
     m_fileListCombo(0),
-    m_fileListModel(new QStringListModel(this))
+    m_fileListModel(new QStandardItemModel(this))
 {
     QVBoxLayout *layout = new QVBoxLayout(this);
 
@@ -76,7 +76,7 @@ QmlProjectRunConfigurationWidget::QmlProjectRunConfigurationWidget(QmlProjectRun
     m_fileListCombo = new QComboBox;
     m_fileListCombo->setModel(m_fileListModel);
 
-    connect(m_fileListCombo, SIGNAL(activated(QString)), this, SLOT(setMainScript(QString)));
+    connect(m_fileListCombo, SIGNAL(activated(int)), this, SLOT(setMainScript(int)));
     connect(ProjectExplorer::ProjectExplorerPlugin::instance(), SIGNAL(fileListChanged()),
             SLOT(updateFileComboBox()));
 
@@ -174,35 +174,73 @@ void QmlProjectRunConfigurationWidget::updateFileComboBox()
 {
     QmlProject *project = m_runConfiguration->qmlTarget()->qmlProject();
     QDir projectDir = project->projectDir();
-    QStringList files;
 
-    files.append(CURRENT_FILE);
-    int currentIndex = -1;
+    m_fileListModel->clear();
+    m_fileListModel->appendRow(new QStandardItem(CURRENT_FILE));
+    QModelIndex currentIndex;
+    QModelIndex fileInQmlProjectIndex;
+
+    const QString mainScriptInFilePath = projectDir.absoluteFilePath(project->mainFile());
+
     QStringList sortedFiles = project->files();
+    if (!sortedFiles.contains(mainScriptInFilePath))
+        sortedFiles += mainScriptInFilePath;
+
+    // make paths relative to project directory
+    QStringList relativeFiles;
+    foreach (const QString &fn, sortedFiles) {
+        relativeFiles += projectDir.relativeFilePath(fn);
+    }
+    sortedFiles = relativeFiles;
+
     qStableSort(sortedFiles.begin(), sortedFiles.end(), caseInsensitiveLessThan);
 
+    QString mainScriptPath;
+    if (m_runConfiguration->mainScriptSource() != QmlProjectRunConfiguration::FileInEditor)
+        mainScriptPath = projectDir.relativeFilePath(m_runConfiguration->mainScript());
+
     foreach (const QString &fn, sortedFiles) {
         QFileInfo fileInfo(fn);
         if (fileInfo.suffix() != QLatin1String("qml"))
             continue;
 
-        QString fileName = projectDir.relativeFilePath(fn);
-        if (fileName == m_runConfiguration->m_scriptFile)
-            currentIndex = files.size();
+        QStandardItem *item = new QStandardItem(fn);
+        m_fileListModel->appendRow(item);
 
-        files.append(fileName);
+        if (mainScriptPath == fn)
+            currentIndex = item->index();
+
+        if (mainScriptInFilePath == fn)
+            fileInQmlProjectIndex = item->index();
     }
-    m_fileListModel->setStringList(files);
 
-    if (currentIndex != -1)
-        m_fileListCombo->setCurrentIndex(currentIndex);
-    else
+    if (currentIndex.isValid()) {
+        m_fileListCombo->setCurrentIndex(currentIndex.row());
+    } else {
         m_fileListCombo->setCurrentIndex(0);
+    }
+
+    if (fileInQmlProjectIndex.isValid()) {
+        QFont font;
+        font.setBold(true);
+        m_fileListModel->setData(fileInQmlProjectIndex, font, Qt::FontRole);
+    }
 }
 
-void QmlProjectRunConfigurationWidget::setMainScript(const QString &file)
+void QmlProjectRunConfigurationWidget::setMainScript(int index)
 {
-    m_runConfiguration->setMainScript(file);
+    QmlProject *project = m_runConfiguration->qmlTarget()->qmlProject();
+    QDir projectDir = project->projectDir();
+    if (index == 0) {
+        m_runConfiguration->setScriptSource(QmlProjectRunConfiguration::FileInEditor);
+    } else {
+        const QString path = m_fileListModel->data(m_fileListModel->index(index, 0)).toString();
+        if (projectDir.relativeFilePath(project->mainFile()) == path) {
+            m_runConfiguration->setScriptSource(QmlProjectRunConfiguration::FileInProjectFile);
+        } else {
+            m_runConfiguration->setScriptSource(QmlProjectRunConfiguration::FileInSettings, path);
+        }
+    }
 }
 
 void QmlProjectRunConfigurationWidget::onQtVersionSelectionChanged()
diff --git a/src/plugins/qmlprojectmanager/qmlprojectrunconfigurationwidget.h b/src/plugins/qmlprojectmanager/qmlprojectrunconfigurationwidget.h
index e043eedb685..7f981b45452 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectrunconfigurationwidget.h
+++ b/src/plugins/qmlprojectmanager/qmlprojectrunconfigurationwidget.h
@@ -33,7 +33,7 @@
 #include <QWidget>
 
 QT_FORWARD_DECLARE_CLASS(QComboBox);
-QT_FORWARD_DECLARE_CLASS(QStringListModel);
+QT_FORWARD_DECLARE_CLASS(QStandardItemModel);
 
 namespace ProjectExplorer {
 
@@ -58,11 +58,11 @@ public:
 public slots:
     void updateQtVersionComboBox();
     void userEnvironmentChangesChanged();
+    void updateFileComboBox();
 
 private slots:
-    void updateFileComboBox();
 
-    void setMainScript(const QString &file);
+    void setMainScript(int index);
     void onQtVersionSelectionChanged();
     void onViewerArgsChanged();
     void useCppDebuggerToggled(bool toggled);
@@ -78,7 +78,7 @@ private:
 
     QComboBox *m_qtVersionComboBox;
     QComboBox *m_fileListCombo;
-    QStringListModel *m_fileListModel;
+    QStandardItemModel *m_fileListModel;
 
     ProjectExplorer::EnvironmentWidget *m_environmentWidget;
 };
diff --git a/tests/auto/qml/qmlprojectmanager/fileformat/tst_fileformat.cpp b/tests/auto/qml/qmlprojectmanager/fileformat/tst_fileformat.cpp
index 376395ea289..26df3cf7b54 100644
--- a/tests/auto/qml/qmlprojectmanager/fileformat/tst_fileformat.cpp
+++ b/tests/auto/qml/qmlprojectmanager/fileformat/tst_fileformat.cpp
@@ -20,6 +20,7 @@ private slots:
     void testFileFilter();
     void testMatchesFile();
     void testLibraryPaths();
+    void testMainFile();
 };
 
 tst_FileFormat::tst_FileFormat()
@@ -334,6 +335,31 @@ void tst_FileFormat::testLibraryPaths()
     }
 }
 
+void tst_FileFormat::testMainFile()
+{
+    //
+    // search for qml files in local directory
+    //
+    QString projectFile = QLatin1String(
+            "import QmlProject 1.1\n"
+            "Project {\n"
+            "  mainFile: \"file1.qml\"\n"
+            "}\n");
+
+    {
+        QDeclarativeEngine engine;
+        QDeclarativeComponent component(&engine);
+        component.setData(projectFile.toUtf8(), QUrl());
+        if (!component.isReady())
+            qDebug() << component.errorString();
+        QVERIFY(component.isReady());
+
+        QmlProjectItem *project = qobject_cast<QmlProjectItem*>(component.create());
+        QVERIFY(project);
+
+        QCOMPARE(project->mainFile(), QString("file1.qml"));
+    }
+}
 
 QTEST_MAIN(tst_FileFormat);
 #include "tst_fileformat.moc"
-- 
GitLab