Skip to content
Snippets Groups Projects
qt4nodes.cpp 71.1 KiB
Newer Older
con's avatar
con committed
void Qt4PriFileNode::changeFiles(const FileType fileType,
                                 const QStringList &filePaths,
                                 QStringList *notChanged,
                                 ChangeType change)
{
    if (filePaths.isEmpty())
        return;

    *notChanged = filePaths;

    // Check for modified editors
    if (!saveModifiedEditors())
dt's avatar
dt committed
    // Ensure that the file is not read only
    QFileInfo fi(m_projectFilePath);
    if (!fi.isWritable()) {
        // Try via vcs manager
        Core::VCSManager *vcsManager = Core::ICore::instance()->vcsManager();
        Core::IVersionControl *versionControl = vcsManager->findVersionControlForDirectory(fi.absolutePath());
        if (!versionControl || versionControl->vcsOpen(m_projectFilePath)) {
            bool makeWritable = QFile::setPermissions(m_projectFilePath, fi.permissions() | QFile::WriteUser);
            if (!makeWritable) {
                QMessageBox::warning(Core::ICore::instance()->mainWindow(),
                                     tr("Failed!"),
                                     tr("Could not write project file %1.").arg(m_projectFilePath));
                return;
            }
        }
    }

    QStringList lines;
    ProFile *includeFile;
    {
        QString contents;
        {
            QFile qfile(m_projectFilePath);
            if (qfile.open(QIODevice::ReadOnly | QIODevice::Text)) {
                contents = QString::fromLocal8Bit(qfile.readAll());
                qfile.close();
                lines = contents.split(QLatin1Char('\n'));
                while (!lines.isEmpty() && lines.last().isEmpty())
                m_project->proFileParseError(tr("Error while reading .pro file %1: %2")
                                             .arg(m_projectFilePath, qfile.errorString()));
                return;
            }
        }

        ProMessageHandler handler;
        ProFileParser parser(0, &handler);
        includeFile = parser.parsedProBlock(m_projectFilePath, contents);
con's avatar
con committed
    const QStringList vars = varNames(fileType);
    QDir priFileDir = QDir(m_qt4ProFileNode->m_projectDir);
con's avatar
con committed

    if (change == AddToProFile) {
        // Use the first variable for adding.
        // Yes, that's broken for adding objective c sources or other stuff.
        ProWriter::addFiles(includeFile, &lines, priFileDir, filePaths, vars.first());
con's avatar
con committed
    } else { // RemoveFromProFile
        *notChanged = ProWriter::removeFiles(includeFile, &lines, priFileDir, filePaths, vars);
con's avatar
con committed
    }

    // save file
dt's avatar
dt committed
    // This is a hack.
    // We are saving twice in a very short timeframe, once the editor and once the ProFile.
    // So the modification time might not change between those two saves.
    // We manually tell each editor to reload it's file.
    // (The .pro files are notified by the file system watcher.)
    foreach (Core::IEditor *editor, Core::ICore::instance()->editorManager()->editorsForFileName(m_projectFilePath)) {
        if (Core::IFile *editorFile = editor->file()) {
            editorFile->reload(Core::IFile::FlagReload, Core::IFile::TypeContents);
con's avatar
con committed
}

void Qt4PriFileNode::save(const QStringList &lines)
con's avatar
con committed
{
    QFile qfile(m_projectFilePath);
    if (qfile.open(QIODevice::WriteOnly | QIODevice::Text)) {
        foreach (const QString &str, lines) {
            qfile.write(str.toLocal8Bit());
con's avatar
con committed

    m_project->qt4ProjectManager()->notifyChanged(m_projectFilePath);
con's avatar
con committed
}

/*
  Deletes all subprojects/files/virtual folders
  */
void Qt4PriFileNode::clear()
{
    // delete files && folders && projects
    removeFileNodes(fileNodes(), this);
    removeProjectNodes(subProjectNodes());
    removeFolderNodes(subFolderNodes(), this);
con's avatar
con committed
}

QStringList Qt4PriFileNode::varNames(ProjectExplorer::FileType type)
con's avatar
con committed
{
    QStringList vars;
    switch (type) {
    case ProjectExplorer::HeaderType:
        vars << QLatin1String("HEADERS");
        vars << QLatin1String("OBJECTIVE_HEADERS");
con's avatar
con committed
        break;
    case ProjectExplorer::SourceType:
        vars << QLatin1String("SOURCES");
        vars << QLatin1String("OBJECTIVE_SOURCES");
        vars << QLatin1String("LEXSOURCES");
        vars << QLatin1String("YACCSOURCES");
con's avatar
con committed
        break;
    case ProjectExplorer::ResourceType:
        vars << QLatin1String("RESOURCES");
        break;
    case ProjectExplorer::FormType:
        vars << QLatin1String("FORMS");
        break;
    case ProjectExplorer::ProjectFileType:
        vars << QLatin1String("SUBDIRS");
        break;
    case ProjectExplorer::QMLType:
        break;
con's avatar
con committed
    default:
        vars << QLatin1String("OTHER_FILES");
        break;
    }
    return vars;
}


QStringList Qt4PriFileNode::dynamicVarNames(ProFileReader *readerExact, ProFileReader *readerCumulative)
{
    QStringList result;
    // Figure out DEPLOYMENT and INSTALLS
    QStringList listOfVars = readerExact->values("DEPLOYMENT");
    foreach (const QString &var, listOfVars) {
        result << (var + ".sources");
    }
    if (readerCumulative) {
        QStringList listOfVars = readerCumulative->values("DEPLOYMENT");
        foreach (const QString &var, listOfVars) {
            result << (var + ".sources");
        }
    }

    listOfVars = readerExact->values("INSTALLS");
    foreach (const QString &var, listOfVars) {
        result << (var + ".files");
    }
    if (readerCumulative) {
        QStringList listOfVars = readerCumulative->values("INSTALLS");
        foreach (const QString &var, listOfVars) {
            result << (var + ".files");
        }
    }

    return result;
}

QSet<QString> Qt4PriFileNode::filterFiles(ProjectExplorer::FileType fileType, const QSet<QString> &files)
{
    QSet<QString> result;
    if (fileType != ProjectExplorer::QMLType && fileType != ProjectExplorer::UnknownFileType)
        return result;
    if(fileType == ProjectExplorer::QMLType) {
        foreach (const QString &file, files)
            if (file.endsWith(".qml"))
                result << file;
    } else {
        foreach (const QString &file, files)
            if (!file.endsWith(".qml"))
                result << file;
    }
    return result;
}


const Qt4ProFileNode *Qt4ProFileNode::findProFileFor(const QString &fileName) const
{
    if (fileName == path())
        return this;
    foreach (ProjectNode *pn, subProjectNodes())
        if (Qt4ProFileNode *qt4ProFileNode = qobject_cast<Qt4ProFileNode *>(pn))
            if (const Qt4ProFileNode *result = qt4ProFileNode->findProFileFor(fileName))
TargetInformation Qt4ProFileNode::targetInformation(const QString &fileName) const
{
    TargetInformation result;
    const Qt4ProFileNode *qt4ProFileNode = findProFileFor(fileName);
    if (!qt4ProFileNode)
        return result;

    return qt4ProFileNode->targetInformation();
}

con's avatar
con committed
/*!
  \class Qt4ProFileNode
  Implements abstract ProjectNode class
  */
Qt4ProFileNode::Qt4ProFileNode(Qt4Project *project,
                               const QString &filePath,
                               QObject *parent)
        : Qt4PriFileNode(project, this, filePath),
          m_projectType(InvalidProject),
          m_readerExact(0),
          m_readerCumulative(0)
con's avatar
con committed
{
con's avatar
con committed
    if (parent)
        setParent(parent);

    connect(ProjectExplorer::ProjectExplorerPlugin::instance()->buildManager(), SIGNAL(buildStateChanged(ProjectExplorer::Project*)),
            this, SLOT(buildStateChanged(ProjectExplorer::Project*)));

    connect(&m_parseFutureWatcher, SIGNAL(finished()),
            this, SLOT(applyAsyncEvaluate()));
con's avatar
con committed
}

Qt4ProFileNode::~Qt4ProFileNode()
{
dt's avatar
dt committed
    CppTools::CppModelManagerInterface *modelManager
            = ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>();
    QMap<QString, Qt4UiCodeModelSupport *>::const_iterator it, end;
    end = m_uiCodeModelSupport.constEnd();
    for (it = m_uiCodeModelSupport.constBegin(); it != end; ++it) {
        modelManager->removeEditorSupport(it.value());
        delete it.value();
    }
    m_parseFutureWatcher.waitForFinished();
    if (m_readerExact) {
        // Oh we need to clean up
        applyEvaluate(true, true);
        m_project->decrementPendingEvaluateFutures();
    }
}

bool Qt4ProFileNode::isParent(Qt4ProFileNode *node)
{
dt's avatar
dt committed
    while ((node = qobject_cast<Qt4ProFileNode *>(node->parentFolderNode()))) {
        if (node == this)
            return true;
    }
    return false;
void Qt4ProFileNode::buildStateChanged(ProjectExplorer::Project *project)
{
dt's avatar
dt committed
    if (project == m_project && !ProjectExplorer::ProjectExplorerPlugin::instance()->buildManager()->isBuilding(m_project)) {
        QStringList filesToUpdate = updateUiFiles();
        updateCodeModelSupportFromBuild(filesToUpdate);
    }
bool Qt4ProFileNode::hasBuildTargets() const
con's avatar
con committed
{
    return (projectType() == ApplicationTemplate) || (projectType() == LibraryTemplate);
}

Qt4ProjectType Qt4ProFileNode::projectType() const
{
    return m_projectType;
}

QStringList Qt4ProFileNode::variableValue(const Qt4Variable var) const
{
    return m_varValues.value(var);
}

void Qt4ProFileNode::scheduleUpdate()
{
    m_project->scheduleAsyncUpdate(this);
}

void Qt4ProFileNode::asyncUpdate()
{
    m_project->incrementPendingEvaluateFutures();
    setupReader();
    QFuture<bool> future = QtConcurrent::run(&Qt4ProFileNode::asyncEvaluate, this);
    m_parseFutureWatcher.setFuture(future);
con's avatar
con committed
void Qt4ProFileNode::update()
{
    setupReader();
    bool parserError = evaluate();
    applyEvaluate(!parserError, false);
}

void Qt4ProFileNode::setupReader()
{
    Q_ASSERT(!m_readerExact);
    Q_ASSERT(!m_readerCumulative);

    m_readerExact = m_project->createProFileReader(this);
    m_readerExact->setCumulative(false);

    m_readerCumulative = m_project->createProFileReader(this);

    // Find out what flags we pass on to qmake
    QStringList args;
    if (QMakeStep *qs = m_project->activeTarget()->activeBuildConfiguration()->qmakeStep())
        args = qs->parserArguments();
    else
        args = m_project->activeTarget()->activeBuildConfiguration()->configCommandLineArguments();
    m_readerExact->setCommandLineArguments(args);
    m_readerCumulative->setCommandLineArguments(args);
}

bool Qt4ProFileNode::evaluate()
{
    bool parserError = false;
    if (ProFile *pro = m_readerExact->parsedProFile(m_projectFilePath)) {
        if (!m_readerExact->accept(pro, ProFileEvaluator::LoadAll))
        if (!m_readerCumulative->accept(pro, ProFileEvaluator::LoadPreFiles))
        parserError = true;
    return parserError;
}

void Qt4ProFileNode::asyncEvaluate(QFutureInterface<bool> &fi)
{
    bool parserError = evaluate();
    fi.reportResult(!parserError);
}

void Qt4ProFileNode::applyAsyncEvaluate()
{
    applyEvaluate(m_parseFutureWatcher.result(), true);
    m_project->decrementPendingEvaluateFutures();
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
static Qt4ProjectType proFileTemplateTypeToProjectType(ProFileEvaluator::TemplateType type)
{
    switch (type) {
    case ProFileEvaluator::TT_Unknown:
    case ProFileEvaluator::TT_Application:
        return ApplicationTemplate;
    case ProFileEvaluator::TT_Library:
        return LibraryTemplate;
    case ProFileEvaluator::TT_Script:
        return ScriptTemplate;
    case ProFileEvaluator::TT_Subdirs:
        return SubDirsTemplate;
    default:
        return InvalidProject;
    }
}

void Qt4ProFileNode::applyEvaluate(bool parseResult, bool async)
{
    if (!parseResult || m_project->wasEvaluateCanceled()) {
        m_project->destroyProFileReader(m_readerExact);
        if (m_readerCumulative)
            m_project->destroyProFileReader(m_readerCumulative);
        m_readerExact = m_readerCumulative = 0;
        if (!parseResult) {
            m_project->proFileParseError(tr("Error while parsing file %1. Giving up.").arg(m_projectFilePath));
            invalidate();
con's avatar
con committed
        return;
    }

    if (debug)
        qDebug() << "Qt4ProFileNode - updating files for file " << m_projectFilePath;

    Qt4ProjectType projectType = InvalidProject;
    // Check that both are the same if we have both
    if (m_readerExact->templateType() != m_readerCumulative->templateType()) {
        // Now what. The only thing which could be reasonable is that someone
        // changes between template app and library.
        // Well, we are conservative here for now.
        // Let's wait until someone complains and look at what they are doing.
        m_project->destroyProFileReader(m_readerCumulative);
        m_readerCumulative = 0;
con's avatar
con committed
    }

    projectType = proFileTemplateTypeToProjectType(m_readerExact->templateType());

con's avatar
con committed
    if (projectType != m_projectType) {
        Qt4ProjectType oldType = m_projectType;
        // probably all subfiles/projects have changed anyway ...
        clear();
        m_projectType = projectType;
        // really emit here? or at the end? Noone is connected to this signal at the moment
        // so we kind of can ignore that question for now
con's avatar
con committed
        foreach (NodesWatcher *watcher, watchers())
            if (Qt4NodesWatcher *qt4Watcher = qobject_cast<Qt4NodesWatcher*>(watcher))
                emit qt4Watcher->projectTypeChanged(this, oldType, projectType);
    }

    //
    // Add/Remove pri files, sub projects
    //

    QList<ProjectNode*> existingProjectNodes = subProjectNodes();

    QStringList newProjectFilesExact;
    QHash<QString, ProFile*> includeFilesExact;
    ProFile *fileForCurrentProjectExact = 0;
    if (m_projectType == SubDirsTemplate)
        newProjectFilesExact = subDirsPaths(m_readerExact);
    foreach (ProFile *includeFile, m_readerExact->includeFiles()) {
        if (includeFile->fileName() == m_projectFilePath) { // this file
            fileForCurrentProjectExact = includeFile;
        } else {
            newProjectFilesExact << includeFile->fileName();
            includeFilesExact.insert(includeFile->fileName(), includeFile);
con's avatar
con committed
        }
con's avatar
con committed


    QStringList newProjectFilesCumlative;
    QHash<QString, ProFile*> includeFilesCumlative;
    ProFile *fileForCurrentProjectCumlative = 0;
    if (m_readerCumulative) {
        if (m_projectType == SubDirsTemplate)
            newProjectFilesCumlative = subDirsPaths(m_readerCumulative);
        foreach (ProFile *includeFile, m_readerCumulative->includeFiles()) {
            if (includeFile->fileName() == m_projectFilePath) {
                fileForCurrentProjectCumlative = includeFile;
con's avatar
con committed
            } else {
                newProjectFilesCumlative << includeFile->fileName();
                includeFilesCumlative.insert(includeFile->fileName(), includeFile);
con's avatar
con committed
            }
        }
    }

    qSort(existingProjectNodes.begin(), existingProjectNodes.end(),
          sortNodesByPath);
    qSort(newProjectFilesExact);
    qSort(newProjectFilesCumlative);
con's avatar
con committed

    QList<ProjectNode*> toAdd;
    QList<ProjectNode*> toRemove;

    QList<ProjectNode*>::const_iterator existingIt = existingProjectNodes.constBegin();
    QStringList::const_iterator newExactIt = newProjectFilesExact.constBegin();
    QStringList::const_iterator newCumlativeIt = newProjectFilesCumlative.constBegin();

    forever {
        bool existingAtEnd = (existingIt == existingProjectNodes.constEnd());
        bool newExactAtEnd = (newExactIt == newProjectFilesExact.constEnd());
        bool newCumlativeAtEnd = (newCumlativeIt == newProjectFilesCumlative.constEnd());

        if (existingAtEnd && newExactAtEnd && newCumlativeAtEnd)
            break; // we are done, hurray!

        // So this is one giant loop comparing 3 lists at once and sorting the comparision
        // into mainly 2 buckets: toAdd and toRemove
        // We need to distinguish between nodes that came from exact and cumalative
        // parsing, since the update call is diffrent for them
        // I believe this code to be correct, be careful in changing it

        QString nodeToAdd;
        if (! existingAtEnd
            && (newExactAtEnd || (*existingIt)->path() < *newExactIt)
            && (newCumlativeAtEnd || (*existingIt)->path() < *newCumlativeIt)) {
            // Remove case
            toRemove << *existingIt;
            ++existingIt;
        } else if(! newExactAtEnd
                  && (existingAtEnd || *newExactIt < (*existingIt)->path())
                  && (newCumlativeAtEnd || *newExactIt < *newCumlativeIt)) {
            // Mark node from exact for adding
            nodeToAdd = *newExactIt;
            ++newExactIt;
        } else if (! newCumlativeAtEnd
                   && (existingAtEnd ||  *newCumlativeIt < (*existingIt)->path())
                   && (newExactAtEnd || *newCumlativeIt < *newExactIt)) {
            // Mark node from cumalative for adding
            nodeToAdd = *newCumlativeIt;
            ++newCumlativeIt;
        } else if (!newExactAtEnd
                   && !newCumlativeAtEnd
                   && (existingAtEnd || *newExactIt < (*existingIt)->path())
                   && (existingAtEnd || *newCumlativeIt < (*existingIt)->path())) {
            // Mark node from both for adding
            nodeToAdd = *newExactIt;
            ++newExactIt;
            ++newCumlativeIt;
        } else {
            Q_ASSERT(!newExactAtEnd || !newCumlativeAtEnd);
            // update case, figure out which case exactly
            if (newExactAtEnd) {
                ++newCumlativeIt;
            } else if (newCumlativeAtEnd) {
                ++newExactIt;
            } else if(*newExactIt < *newCumlativeIt) {
                ++newExactIt;
            } else if (*newCumlativeIt < *newExactIt) {
                ++newCumlativeIt;
con's avatar
con committed
            } else {
                ++newExactIt;
                ++newCumlativeIt;
con's avatar
con committed
            }
            // Update existingNodeIte
            ProFile *fileExact = includeFilesCumlative.value((*existingIt)->path());
            ProFile *fileCumlative = includeFilesCumlative.value((*existingIt)->path());
            if (fileExact || fileCumlative) {
                static_cast<Qt4PriFileNode *>(*existingIt)->update(fileExact, m_readerExact, fileCumlative, m_readerCumulative);
            } else {
                // We always parse exactly, because we later when async parsing don't know whether
                // the .pro file is included in this .pro file
                // So to compare that later parse with the sync one
                if (async)
                    static_cast<Qt4ProFileNode *>(*existingIt)->asyncUpdate();
                else
                    static_cast<Qt4ProFileNode *>(*existingIt)->update();
con's avatar
con committed
            }
            ++existingIt;
            // newCumalativeIt and newExactIt are already incremented
con's avatar
con committed

        }
dt's avatar
dt committed
        // If we found something to add, do it
        if (!nodeToAdd.isEmpty()) {
            ProFile *fileExact = includeFilesCumlative.value(nodeToAdd);
            ProFile *fileCumlative = includeFilesCumlative.value(nodeToAdd);
dt's avatar
dt committed

            // Loop preventation, make sure that exact same node is not in our parent chain
            bool loop = false;
            ProjectExplorer::Node *n = this;
            while ((n = n->parentFolderNode())) {
                if (qobject_cast<Qt4PriFileNode *>(n) && n->path() == nodeToAdd) {
                    loop = true;
                    break;
                }
            }

            if (loop) {
                // Do nothing
            } else if (fileExact || fileCumlative) {
                Qt4PriFileNode *qt4PriFileNode = new Qt4PriFileNode(m_project, this, nodeToAdd);
dt's avatar
dt committed
                qt4PriFileNode->setParentFolderNode(this); // Needed for loop detection
                qt4PriFileNode->update(fileExact, m_readerExact, fileCumlative, m_readerCumulative);
                toAdd << qt4PriFileNode;
            } else {
                Qt4ProFileNode *qt4ProFileNode = new Qt4ProFileNode(m_project, nodeToAdd);
dt's avatar
dt committed
                qt4ProFileNode->setParentFolderNode(this); // Needed for loop detection
                if (async)
                    qt4ProFileNode->asyncUpdate();
                else
                    qt4ProFileNode->update();
                toAdd << qt4ProFileNode;
            }
con's avatar
con committed
        }
    } // for
con's avatar
con committed

    if (!toRemove.isEmpty())
        removeProjectNodes(toRemove);
    if (!toAdd.isEmpty())
        addProjectNodes(toAdd);

    Qt4PriFileNode::update(fileForCurrentProjectExact, m_readerExact, fileForCurrentProjectCumlative, m_readerCumulative);

    // update TargetInformation
    m_qt4targetInformation = targetInformation(m_readerExact);
con's avatar
con committed

    // update other variables
    QHash<Qt4Variable, QStringList> newVarValues;
    newVarValues[DefinesVar] = m_readerExact->values(QLatin1String("DEFINES"));
    newVarValues[IncludePathVar] = includePaths(m_readerExact);
dt's avatar
dt committed
    newVarValues[UiDirVar] = QStringList() << uiDirPath(m_readerExact);
    newVarValues[MocDirVar] = QStringList() << mocDirPath(m_readerExact);
    newVarValues[PkgConfigVar] = m_readerExact->values(QLatin1String("PKGCONFIG"));
    newVarValues[PrecompiledHeaderVar] =
            m_readerExact->absoluteFileValues(QLatin1String("PRECOMPILED_HEADER"),
                                              m_projectDir,
                                              QStringList() << m_projectDir,
                                              0);
    newVarValues[LibDirectoriesVar] = libDirectories(m_readerExact);
    newVarValues[ConfigVar] = m_readerExact->values(QLatin1String("CONFIG"));
    newVarValues[QmlImportPathVar] = m_readerExact->absolutePathValues(
                QLatin1String("QML_IMPORT_PATH"), m_projectDir);
con's avatar
con committed

    if (m_varValues != newVarValues) {
        m_varValues = newVarValues;
        foreach (NodesWatcher *watcher, watchers())
            if (Qt4NodesWatcher *qt4Watcher = qobject_cast<Qt4NodesWatcher*>(watcher))
                emit qt4Watcher->variablesChanged(this, m_varValues, newVarValues);
    }

dt's avatar
dt committed
    createUiCodeModelSupport();
dt's avatar
dt committed
    updateUiFiles();
con's avatar
con committed

    foreach (NodesWatcher *watcher, watchers())
        if (Qt4NodesWatcher *qt4Watcher = qobject_cast<Qt4NodesWatcher*>(watcher))
            emit qt4Watcher->proFileUpdated(this);
    m_project->destroyProFileReader(m_readerExact);
    if (m_readerCumulative)
        m_project->destroyProFileReader(m_readerCumulative);

    m_readerExact = 0;
    m_readerCumulative = 0;
con's avatar
con committed
}

namespace {
    // find all ui files in project
    class FindUiFileNodesVisitor : public ProjectExplorer::NodesVisitor {
    public:
        void visitProjectNode(ProjectNode *projectNode)
        {
            visitFolderNode(projectNode);
        }
        void visitFolderNode(FolderNode *folderNode)
        {
            foreach (FileNode *fileNode, folderNode->fileNodes()) {
                if (fileNode->fileType() == ProjectExplorer::FormType)
                    uiFileNodes << fileNode;
            }
        }
        QList<FileNode*> uiFileNodes;
    };
}

// This function is triggered after a build, and updates the state ui files
// It does so by storing a modification time for each ui file we know about.

dt's avatar
dt committed
// TODO this function should also be called if the build directory is changed
dt's avatar
dt committed
QStringList Qt4ProFileNode::updateUiFiles()
con's avatar
con committed
{
dt's avatar
dt committed
//    qDebug()<<"Qt4ProFileNode::updateUiFiles()";
    // Only those two project types can have ui files for us
con's avatar
con committed
    if (m_projectType != ApplicationTemplate
        && m_projectType != LibraryTemplate)
dt's avatar
dt committed
        return QStringList();
con's avatar
con committed

    // Find all ui files
con's avatar
con committed
    FindUiFileNodesVisitor uiFilesVisitor;
    this->accept(&uiFilesVisitor);
    const QList<FileNode*> uiFiles = uiFilesVisitor.uiFileNodes;

    // Find the UiDir, there can only ever be one
    QString uiDir = buildDir();
    QStringList tmp = m_varValues[UiDirVar];
    if (tmp.size() != 0)
        uiDir = tmp.first();
con's avatar
con committed

    // Collect all existing generated files
con's avatar
con committed
    QList<FileNode*> existingFileNodes;
    foreach (FileNode *file, fileNodes()) {
        if (file->isGenerated())
            existingFileNodes << file;
    }

    // Convert uiFile to uiHeaderFilePath, find all headers that correspond
    // and try to find them in uiDir
con's avatar
con committed
    QStringList newFilePaths;
    foreach (FileNode *uiFile, uiFiles) {
        const QString uiHeaderFilePath
                = QString("%1/ui_%2.h").arg(uiDir, QFileInfo(uiFile->path()).completeBaseName());
        if (QFileInfo(uiHeaderFilePath).exists())
            newFilePaths << uiHeaderFilePath;
con's avatar
con committed
    }

    // Create a diff between those lists
con's avatar
con committed
    QList<FileNode*> toRemove;
    QList<FileNode*> toAdd;
    // The list of files for which we call updateSourceFile
    QStringList toUpdate;
con's avatar
con committed

    qSort(newFilePaths);
    qSort(existingFileNodes.begin(), existingFileNodes.end(), ProjectNode::sortNodesByPath);

    QList<FileNode*>::const_iterator existingNodeIter = existingFileNodes.constBegin();
    QList<QString>::const_iterator newPathIter = newFilePaths.constBegin();
    while (existingNodeIter != existingFileNodes.constEnd()
           && newPathIter != newFilePaths.constEnd()) {
        if ((*existingNodeIter)->path() < *newPathIter) {
            toRemove << *existingNodeIter;
            ++existingNodeIter;
        } else if ((*existingNodeIter)->path() > *newPathIter) {
            toAdd << new FileNode(*newPathIter, ProjectExplorer::HeaderType, true);
            ++newPathIter;
        } else { // *existingNodeIter->path() == *newPathIter
            QString fileName = (*existingNodeIter)->path();
            QMap<QString, QDateTime>::const_iterator it = m_uitimestamps.find(fileName);
            QDateTime lastModified = QFileInfo(fileName).lastModified();
            if (it == m_uitimestamps.constEnd() || it.value() < lastModified) {
                toUpdate << fileName;
                m_uitimestamps[fileName] = lastModified;
            }
con's avatar
con committed
            ++existingNodeIter;
            ++newPathIter;
        }
    }
    while (existingNodeIter != existingFileNodes.constEnd()) {
        toRemove << *existingNodeIter;
        ++existingNodeIter;
    }
    while (newPathIter != newFilePaths.constEnd()) {
        toAdd << new FileNode(*newPathIter, ProjectExplorer::HeaderType, true);
        ++newPathIter;
    }

    // Update project tree
con's avatar
con committed
    if (!toRemove.isEmpty()) {
        foreach (FileNode *file, toRemove)
            m_uitimestamps.remove(file->path());
con's avatar
con committed
        removeFileNodes(toRemove, this);
    }

    CppTools::CppModelManagerInterface *modelManager =
        ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>();

con's avatar
con committed
    if (!toAdd.isEmpty()) {
        foreach (FileNode *file, toAdd) {
            m_uitimestamps.insert(file->path(), QFileInfo(file->path()).lastModified());
            toUpdate << file->path();

dt's avatar
dt committed
            // Also adding files depending on that
            // We only need to do that for files that were newly created
            QString fileName = QFileInfo(file->path()).fileName();
            foreach (CPlusPlus::Document::Ptr doc, modelManager->snapshot()) {
                if (doc->includedFiles().contains(fileName)) {
                    if (!toUpdate.contains(doc->fileName()))
                        toUpdate << doc->fileName();
                }
            }
        }
con's avatar
con committed
        addFileNodes(toAdd, this);
    }
dt's avatar
dt committed
    return toUpdate;
con's avatar
con committed
}

dt's avatar
dt committed
QString Qt4ProFileNode::uiDirPath(ProFileReader *reader) const
con's avatar
con committed
{
dt's avatar
dt committed
    QString path = reader->value("UI_DIR");
    if (QFileInfo(path).isRelative())
        path = QDir::cleanPath(buildDir() + "/" + path);
    return path;
con's avatar
con committed
}

dt's avatar
dt committed
QString Qt4ProFileNode::mocDirPath(ProFileReader *reader) const
con's avatar
con committed
{
dt's avatar
dt committed
    QString path = reader->value("MOC_DIR");
    if (QFileInfo(path).isRelative())
        path = QDir::cleanPath(buildDir() + "/" + path);
    return path;
con's avatar
con committed
}

QStringList Qt4ProFileNode::includePaths(ProFileReader *reader) const
{
    QStringList paths;
    foreach (const QString &cxxflags, m_readerExact->values("QMAKE_CXXFLAGS")) {
        if (cxxflags.startsWith("-I"))
            paths.append(cxxflags.mid(2));
    }

    paths.append(reader->absolutePathValues(QLatin1String("INCLUDEPATH"), m_projectDir));
dt's avatar
dt committed
    // paths already contains moc dir and ui dir, due to corrrectly parsing uic.prf and moc.prf
    // except if those directories don't exist at the time of parsing
    // thus we add those directories manually (without checking for existance)
    paths << mocDirPath(reader) << uiDirPath(reader);
con's avatar
con committed
    paths.removeDuplicates();
    return paths;
}

QStringList Qt4ProFileNode::libDirectories(ProFileReader *reader) const
{
    QStringList result;
    foreach (const QString &str, reader->values(QLatin1String("LIBS"))) {
        if (str.startsWith("-L")) {
            result.append(str.mid(2));
        }
    }
    return result;
}

con's avatar
con committed
QStringList Qt4ProFileNode::subDirsPaths(ProFileReader *reader) const
{
    QStringList subProjectPaths;

    const QStringList subDirVars = reader->values(QLatin1String("SUBDIRS"));

    foreach (const QString &subDirVar, subDirVars) {
        // Special case were subdir is just an identifier:
        //   "SUBDIR = subid
        //    subid.subdir = realdir"
con's avatar
con committed
        // or
        //   "SUBDIR = subid
        //    subid.file = realdir/realfile.pro"
con's avatar
con committed

        QString realDir;
        const QString subDirKey = subDirVar + QLatin1String(".subdir");
con's avatar
con committed
        const QString subDirFileKey = subDirVar + QLatin1String(".file");
con's avatar
con committed
        if (reader->contains(subDirKey))
            realDir = reader->value(subDirKey);
con's avatar
con committed
        else if (reader->contains(subDirFileKey))
            realDir = reader->value(subDirFileKey);
con's avatar
con committed
        else
con's avatar
con committed
            realDir = subDirVar;
        QFileInfo info(realDir);
        if (!info.isAbsolute())
            info.setFile(m_projectDir + QLatin1Char('/') + realDir);
        realDir = info.filePath();
con's avatar
con committed

        QString realFile;
        if (info.isDir()) {
            realFile = QString::fromLatin1("%1/%2.pro").arg(realDir, info.fileName());
con's avatar
con committed
        } else {
            realFile = realDir;
        }

        if (QFile::exists(realFile)) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
            subProjectPaths << realFile;
        } else {
            m_project->proFileParseError(tr("Could not find .pro file for sub dir '%1' in '%2'")
                                         .arg(subDirVar).arg(realDir));
        }
con's avatar
con committed
    }

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
    subProjectPaths.removeDuplicates();
con's avatar
con committed
    return subProjectPaths;
}

TargetInformation Qt4ProFileNode::targetInformation(ProFileReader *reader) const
{
    TargetInformation result;
    if (!reader)
        return result;

    result.buildDir = buildDir();
    const QString baseDir = result.buildDir;
    // qDebug() << "base build dir is:"<<baseDir;

    // Working Directory
    if (reader->contains("DESTDIR")) {
        //qDebug() << "reader contains destdir:" << reader->value("DESTDIR");
        result.workingDir = reader->value("DESTDIR");
        if (QDir::isRelativePath(result.workingDir)) {
            result.workingDir = baseDir + QLatin1Char('/') + result.workingDir;
            //qDebug() << "was relative and expanded to" << result.workingDir;
        }
    } else {
        //qDebug() << "reader didn't contain DESTDIR, setting to " << baseDir;
        result.workingDir = baseDir;
    }

    result.target = reader->value("TARGET");
    if (result.target.isEmpty())
        result.target = QFileInfo(m_projectFilePath).baseName();

#if defined (Q_OS_MAC)
    if (reader->values("CONFIG").contains("app_bundle")) {
        result.workingDir += QLatin1Char('/')
                           + result.target
                           + QLatin1String(".app/Contents/MacOS");
    }
#endif

    result.workingDir = QDir::cleanPath(result.workingDir);

    QString wd = result.workingDir;
dt's avatar
dt committed
    if ( (!reader->contains("DESTDIR") || reader->value("DESTDIR") == ".")
        && reader->values("CONFIG").contains("debug_and_release")
        && reader->values("CONFIG").contains("debug_and_release_target")) {
        // If we don't have a destdir and debug and release is set
        // then the executable is in a debug/release folder
        //qDebug() << "reader has debug_and_release_target";

        // Hmm can we find out whether it's debug or release in a saner way?
        // Theoretically it's in CONFIG
        QString qmakeBuildConfig = "release";
        if (m_project->activeTarget()->activeBuildConfiguration()->qmakeBuildConfiguration() & QtVersion::DebugBuild)
            qmakeBuildConfig = "debug";
        wd += QLatin1Char('/') + qmakeBuildConfig;
    }

    result.executable = QDir::cleanPath(wd + QLatin1Char('/') + result.target);
    //qDebug() << "##### updateTarget sets:" << result.workingDir << result.executable;

#if defined (Q_OS_WIN)
    result.executable += QLatin1String(".exe");
#endif
    result.valid = true;
    return result;
}

TargetInformation Qt4ProFileNode::targetInformation() const
{
    return m_qt4targetInformation;
}

QString Qt4ProFileNode::buildDir() const
con's avatar
con committed
{
    const QDir srcDirRoot = QFileInfo(m_project->rootProjectNode()->path()).absoluteDir();
    const QString relativeDir = srcDirRoot.relativeFilePath(m_projectDir);
Tobias Hunger's avatar
Tobias Hunger committed
    return QDir(m_project->activeTarget()->activeBuildConfiguration()->buildDirectory()).absoluteFilePath(relativeDir);
con's avatar
con committed
}

/*
  Sets project type to InvalidProject & deletes all subprojects/files/virtual folders
  */
void Qt4ProFileNode::invalidate()
{
    if (m_projectType == InvalidProject)
        return;

    clear();

    // change project type
    Qt4ProjectType oldType = m_projectType;
    m_projectType = InvalidProject;


    foreach (NodesWatcher *watcher, watchers())
        if (Qt4NodesWatcher *qt4Watcher = qobject_cast<Qt4NodesWatcher*>(watcher))
            emit qt4Watcher->projectTypeChanged(this, oldType, InvalidProject);
}

dt's avatar
dt committed
void Qt4ProFileNode::updateCodeModelSupportFromBuild(const QStringList &files)
{
    foreach (const QString &file, files) {
        QMap<QString, Qt4UiCodeModelSupport *>::const_iterator it, end;
        end = m_uiCodeModelSupport.constEnd();
        for (it = m_uiCodeModelSupport.constBegin(); it != end; ++it) {
            if (it.value()->fileName() == file)
                it.value()->updateFromBuild();
        }
    }
}

void Qt4ProFileNode::updateCodeModelSupportFromEditor(const QString &uiFileName,
                                                      const QString &contents)
    const QMap<QString, Qt4UiCodeModelSupport *>::const_iterator it =
            m_uiCodeModelSupport.constFind(uiFileName);
    if (it != m_uiCodeModelSupport.constEnd())
        it.value()->updateFromEditor(contents);
dt's avatar
dt committed
    foreach (ProjectExplorer::ProjectNode *pro, subProjectNodes())
        if (Qt4ProFileNode *qt4proFileNode = qobject_cast<Qt4ProFileNode *>(pro))
            qt4proFileNode->updateCodeModelSupportFromEditor(uiFileName, contents);
QString Qt4ProFileNode::uiDirectory() const
{
    const Qt4VariablesHash::const_iterator it = m_varValues.constFind(UiDirVar);
    if (it != m_varValues.constEnd() && !it.value().isEmpty())
        return it.value().front();
    return buildDir();
}

QString Qt4ProFileNode::uiHeaderFile(const QString &uiDir, const QString &formFile)
{
    QString uiHeaderFilePath = uiDir;
    uiHeaderFilePath += QLatin1String("/ui_");
    uiHeaderFilePath += QFileInfo(formFile).completeBaseName();
    uiHeaderFilePath += QLatin1String(".h");
    return QDir::cleanPath(uiHeaderFilePath);
}

dt's avatar
dt committed
void Qt4ProFileNode::createUiCodeModelSupport()
{
dt's avatar
dt committed
//    qDebug()<<"creatUiCodeModelSupport()";
dt's avatar
dt committed
    CppTools::CppModelManagerInterface *modelManager
            = ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>();

    // First move all to
    QMap<QString, Qt4UiCodeModelSupport *> oldCodeModelSupport;
    oldCodeModelSupport = m_uiCodeModelSupport;
    m_uiCodeModelSupport.clear();

    // Only those two project types can have ui files for us
    if (m_projectType == ApplicationTemplate || m_projectType == LibraryTemplate) {
        // Find all ui files
        FindUiFileNodesVisitor uiFilesVisitor;
        this->accept(&uiFilesVisitor);
        const QList<FileNode*> uiFiles = uiFilesVisitor.uiFileNodes;

        // Find the UiDir, there can only ever be one
        const  QString uiDir = uiDirectory();
        foreach (const FileNode *uiFile, uiFiles) {
            const QString uiHeaderFilePath = uiHeaderFile(uiDir, uiFile->path());
dt's avatar
dt committed
//            qDebug()<<"code model support for "<<uiFile->path()<<" "<<uiHeaderFilePath;
dt's avatar
dt committed
            QMap<QString, Qt4UiCodeModelSupport *>::iterator it = oldCodeModelSupport.find(uiFile->path());
            if (it != oldCodeModelSupport.end()) {
dt's avatar
dt committed
//                qDebug()<<"updated old codemodelsupport";