diff --git a/src/plugins/designer/codemodelhelpers.cpp b/src/plugins/designer/codemodelhelpers.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5baaeca65c69ef78c49eb519fbc49cce277dd261 --- /dev/null +++ b/src/plugins/designer/codemodelhelpers.cpp @@ -0,0 +1,150 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** 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 "codemodelhelpers.h" + +#include <cpptools/cppmodelmanagerinterface.h> +#include <cplusplus/Symbols.h> +#include <cplusplus/CoreTypes.h> +#include <cplusplus/Name.h> +#include <cplusplus/Names.h> +#include <cplusplus/Literals.h> +#include <cplusplus/Scope.h> +#include <cplusplus/Control.h> +#include <SymbolVisitor.h> + +#include <coreplugin/icore.h> +#include <projectexplorer/projectexplorer.h> +#include <projectexplorer/project.h> +#include <projectexplorer/session.h> +#include <utils/qtcassert.h> + +#include <QtCore/QCoreApplication> +#include <QtCore/QDebug> + +// Debug helpers for code model. @todo: Move to some CppTools library? + +typedef QMap<QString, QStringList> DependencyMap; +typedef CPlusPlus::Document::Ptr DocumentPtr; +typedef QList<CPlusPlus::Symbol *> SymbolList; +typedef QList<DocumentPtr> DocumentPtrList; + +static const char setupUiC[] = "setupUi"; + +// Find the generated "ui_form.h" header of the form via project. +static QString generatedHeaderOf(const QString &uiFileName) +{ + const ProjectExplorer::SessionManager *sessionMgr = ProjectExplorer::ProjectExplorerPlugin::instance()->session(); + if (const ProjectExplorer::Project *uiProject = sessionMgr->projectForFile(uiFileName)) + return uiProject->generatedUiHeader(uiFileName); + return QString(); +} + +namespace { +// Find function symbols in a document by name. +class SearchFunction : public CPlusPlus::SymbolVisitor { +public: + typedef QList<CPlusPlus::Function *> FunctionList; + + explicit SearchFunction(const char *name); + FunctionList operator()(const DocumentPtr &doc); + + virtual bool visit(CPlusPlus::Function * f); + +private: + const size_t m_length; + const char *m_name; + + FunctionList m_matches; +}; + +SearchFunction::SearchFunction(const char *name) : + m_length(qstrlen(name)), + m_name(name) +{ +} + +SearchFunction::FunctionList SearchFunction::operator()(const DocumentPtr &doc) +{ + m_matches.clear(); + const unsigned globalSymbolCount = doc->globalSymbolCount(); + for (unsigned i = 0; i < globalSymbolCount; i++) + accept(doc->globalSymbolAt(i)); + return m_matches; +} + +bool SearchFunction::visit(CPlusPlus::Function * f) +{ + if (const CPlusPlus::Name *name = f->name()) + if (const CPlusPlus::Identifier *id = name->identifier()) + if (id->size() == m_length) + if (!qstrncmp(m_name, id->chars(), m_length)) + m_matches.push_back(f); + return true; +} + +} // anonymous namespace + +namespace Designer { +namespace Internal { + +// Goto slot invoked by the designer context menu. Either navigates +// to an existing slot function or create a new one. +bool navigateToSlot(const QString &uiFileName, + const QString & /* objectName */, + const QString & /* signalSignature */, + const QStringList & /* parameterNames */, + QString *errorMessage) +{ + + // Find the generated header. + const QString generatedHeaderFile = generatedHeaderOf(uiFileName); + if (generatedHeaderFile.isEmpty()) { + *errorMessage = QCoreApplication::translate("Designer", "The generated header of the form '%1' could be found.\nRebuilding the project might help.").arg(uiFileName); + return false; + } + const CPlusPlus::Snapshot snapshot = CppTools::CppModelManagerInterface::instance()->snapshot(); + const DocumentPtr generatedHeaderDoc = snapshot.value(generatedHeaderFile); + if (!generatedHeaderDoc) { + *errorMessage = QCoreApplication::translate("Designer", "The generated header '%1' could not be found in the code model.\nRebuilding the project might help.").arg(generatedHeaderFile); + return false; + } + + // Look for setupUi + SearchFunction searchFunc(setupUiC); + const SearchFunction::FunctionList funcs = searchFunc(generatedHeaderDoc); + if (funcs.size() != 1) { + *errorMessage = QString::fromLatin1("Internal error: The function '%1' could not be found in in %2").arg(QLatin1String(setupUiC), generatedHeaderFile); + return false; + } + return true; +} + +} // namespace Internal +} // namespace Designer diff --git a/src/plugins/designer/codemodelhelpers.h b/src/plugins/designer/codemodelhelpers.h new file mode 100644 index 0000000000000000000000000000000000000000..8decf878379c68fb7b062c3d534dffcac6a2d698 --- /dev/null +++ b/src/plugins/designer/codemodelhelpers.h @@ -0,0 +1,54 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** 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. +** +**************************************************************************/ + +#ifndef CODEMODELHELPERS_H +#define CODEMODELHELPERS_H + +#include <QtCore/QtGlobal> + +QT_BEGIN_NAMESPACE +class QString; +class QStringList; +QT_END_NAMESPACE + +namespace Designer { +namespace Internal { + +// Goto slot invoked by the designer context menu. Either navigates +// to an existing slot function or create a new one. +bool navigateToSlot(const QString &uiFileName, + const QString &objectName, + const QString &signalSignature, + const QStringList ¶meterNames, + QString *errorMessage); + +} // namespace Internal +} // namespace Designer + +#endif // CODEMODELHELPERS_H diff --git a/src/plugins/designer/designer.pro b/src/plugins/designer/designer.pro index c7e5936b38679f59c71805388844b421b82c41a2..bf4653357485a54507bd79b3b510e2e321e7fe00 100644 --- a/src/plugins/designer/designer.pro +++ b/src/plugins/designer/designer.pro @@ -35,6 +35,7 @@ HEADERS += formeditorplugin.h \ settingsmanager.h \ formtemplatewizardpage.h \ formwizarddialog.h \ + codemodelhelpers.h \ designer_export.h SOURCES += formeditorplugin.cpp \ @@ -49,7 +50,8 @@ SOURCES += formeditorplugin.cpp \ formeditorw.cpp \ settingsmanager.cpp \ formtemplatewizardpage.cpp \ - formwizarddialog.cpp + formwizarddialog.cpp \ + codemodelhelpers.cpp RESOURCES += designer.qrc diff --git a/src/plugins/designer/qtcreatorintegration.cpp b/src/plugins/designer/qtcreatorintegration.cpp index 4f08b925faed4b53ed09705d39a2fc00e218652f..3d40f6dd4209aad94e719109cdd4b7e747f19d41 100644 --- a/src/plugins/designer/qtcreatorintegration.cpp +++ b/src/plugins/designer/qtcreatorintegration.cpp @@ -31,6 +31,7 @@ #include "qtcreatorintegration.h" #include "formeditorw.h" #include "formwindoweditor.h" +#include "codemodelhelpers.h" #include <cpptools/cppmodelmanagerinterface.h> #include <cplusplus/Symbols.h> @@ -570,7 +571,9 @@ bool QtCreatorIntegration::navigateToSlot(const QString &objectName, QString *errorMessage) { const QString currentUiFile = m_few->activeFormWindow()->file()->fileName(); - +#if 0 + return Designer::Internal::navigateToSlot(currentUiFile, objectName, signalSignature, parameterNames, errorMessage); +#endif // TODO: we should pass to findDocumentsIncluding an absolute path to generated .h file from ui. // Currently we are guessing the name of ui_<>.h file and pass the file name only to the findDocumentsIncluding(). // The idea is that the .pro file knows if the .ui files is inside, and the .pro file knows it will diff --git a/src/plugins/projectexplorer/project.cpp b/src/plugins/projectexplorer/project.cpp index 941cd1806fa70a542db28041379920a632192eb8..d5b746be869ada84f40284d3a864ff35591e764f 100644 --- a/src/plugins/projectexplorer/project.cpp +++ b/src/plugins/projectexplorer/project.cpp @@ -467,3 +467,9 @@ QStringList Project::frameworkPaths(const QString &) const { return QStringList(); } + +QString Project::generatedUiHeader(const QString & /* formFile */) const +{ + return QString(); +} + diff --git a/src/plugins/projectexplorer/project.h b/src/plugins/projectexplorer/project.h index 8e1ea86383d34a219ee08aec79f0871b311524b2..21cd21f3d94a45808401dc709906180d42c9c255 100644 --- a/src/plugins/projectexplorer/project.h +++ b/src/plugins/projectexplorer/project.h @@ -113,6 +113,8 @@ public: enum FilesMode { AllFiles, ExcludeGeneratedFiles }; virtual QStringList files(FilesMode fileMode) const = 0; + // TODO: generalize to find source(s) of generated files? + virtual QString generatedUiHeader(const QString &formFile) const; // C++ specific // TODO do a C++ project as a base ? diff --git a/src/plugins/qt4projectmanager/qt4nodes.cpp b/src/plugins/qt4projectmanager/qt4nodes.cpp index 550e7593772f7b7cf2d639f46fb3f3622982770e..e18d6ec5a016507d91489568d1735c583f8c512b 100644 --- a/src/plugins/qt4projectmanager/qt4nodes.cpp +++ b/src/plugins/qt4projectmanager/qt4nodes.cpp @@ -1252,6 +1252,23 @@ void Qt4ProFileNode::updateCodeModelSupportFromEditor(const QString &uiFileName, qt4proFileNode->updateCodeModelSupportFromEditor(uiFileName, fw); } +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); +} + void Qt4ProFileNode::createUiCodeModelSupport() { // qDebug()<<"creatUiCodeModelSupport()"; @@ -1271,17 +1288,10 @@ void Qt4ProFileNode::createUiCodeModelSupport() 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(); - - foreach (FileNode *uiFile, uiFiles) { - QString uiHeaderFilePath - = QString("%1/ui_%2.h").arg(uiDir, QFileInfo(uiFile->path()).completeBaseName()); - uiHeaderFilePath = QDir::cleanPath(uiHeaderFilePath); - -// qDebug()<<"code model support for "<<uiFile->path()<<" "<<uiHeaderFilePath; + const QString uiDir = uiDirectory(); + foreach (const FileNode *uiFile, uiFiles) { + const QString uiHeaderFilePath = uiHeaderFile(uiDir, uiFile->path()); +// qDebug()<<"code model support for "<<uiFile->path()<<" "<<uiHeaderFilePath; QMap<QString, Qt4UiCodeModelSupport *>::iterator it = oldCodeModelSupport.find(uiFile->path()); if (it != oldCodeModelSupport.end()) { // qDebug()<<"updated old codemodelsupport"; diff --git a/src/plugins/qt4projectmanager/qt4nodes.h b/src/plugins/qt4projectmanager/qt4nodes.h index 58ecafe5eb30bc51eb730c5cf5f037c418e37c15..6c63fbc1957cd6fde694f7b67c62ab2d740b33d7 100644 --- a/src/plugins/qt4projectmanager/qt4nodes.h +++ b/src/plugins/qt4projectmanager/qt4nodes.h @@ -196,6 +196,10 @@ public: void updateCodeModelSupportFromBuild(const QStringList &files); void updateCodeModelSupportFromEditor(const QString &uiFileName, Designer::FormWindowEditor *fw); + + QString uiDirectory() const; + static QString uiHeaderFile(const QString &uiDir, const QString &formFile); + public slots: void scheduleUpdate(); void update(); @@ -203,6 +207,8 @@ private slots: void buildStateChanged(ProjectExplorer::Project*); private: + typedef QHash<Qt4Variable, QStringList> Qt4VariablesHash; + void createUiCodeModelSupport(); QStringList updateUiFiles(); Qt4ProFileNode *createSubProFileNode(const QString &path); @@ -215,7 +221,7 @@ private: void invalidate(); Qt4ProjectType m_projectType; - QHash<Qt4Variable, QStringList> m_varValues; + Qt4VariablesHash m_varValues; QTimer m_updateTimer; QMap<QString, QDateTime> m_uitimestamps; diff --git a/src/plugins/qt4projectmanager/qt4project.cpp b/src/plugins/qt4projectmanager/qt4project.cpp index 78f830c9b1c108932ad1073b8e6bc9a3d47fde94..d4057a9b0361ba50efc3aaa02ec27c46e84969da 100644 --- a/src/plugins/qt4projectmanager/qt4project.cpp +++ b/src/plugins/qt4projectmanager/qt4project.cpp @@ -787,6 +787,38 @@ QStringList Qt4Project::files(FilesMode fileMode) const return files; } +// Find the folder that contains a file a certain type (recurse down) +static FolderNode *folderOf(FolderNode *in, FileType fileType, const QString &fileName) +{ + foreach(FileNode *fn, in->fileNodes()) + if (fn->fileType() == fileType && fn->path() == fileName) + return in; + foreach(FolderNode *folder, in->subFolderNodes()) + if (FolderNode *pn = folderOf(folder, fileType, fileName)) + return pn; + return 0; +} + +// Find the Qt4ProFileNode that contains a file of a certain type. +// First recurse down to folder, then find the pro-file. +static Qt4ProFileNode *proFileNodeOf(Qt4ProFileNode *in, FileType fileType, const QString &fileName) +{ + for (FolderNode *folder = folderOf(in, fileType, fileName); folder; folder = folder->parentFolderNode()) + if (Qt4ProFileNode *proFile = qobject_cast<Qt4ProFileNode *>(folder)) + return proFile; + return 0; +} + +QString Qt4Project::generatedUiHeader(const QString &formFile) const +{ + // Look in sub-profiles as SessionManager::projectForFile returns + // the top-level project only. + if (m_rootProjectNode) + if (const Qt4ProFileNode *pro = proFileNodeOf(m_rootProjectNode, FormType, formFile)) + return Qt4ProFileNode::uiHeaderFile(pro->uiDirectory(), formFile); + return QString(); +} + QList<ProjectExplorer::Project*> Qt4Project::dependsOn() { // NBS implement dependsOn @@ -820,8 +852,6 @@ void Qt4Project::updateActiveRunConfiguration() emit targetInformationChanged(); } - - BuildConfigWidget *Qt4Project::createConfigWidget() { return new Qt4ProjectConfigWidget(this); diff --git a/src/plugins/qt4projectmanager/qt4project.h b/src/plugins/qt4projectmanager/qt4project.h index 189c999bc4fb5baff0f529bc7f960e9debe685ab..94a6b4c32633c67eba44de511d90fd078e777a87 100644 --- a/src/plugins/qt4projectmanager/qt4project.h +++ b/src/plugins/qt4projectmanager/qt4project.h @@ -179,6 +179,7 @@ public: Internal::Qt4ProFileNode *rootProjectNode() const; virtual QStringList files(FilesMode fileMode) const; + virtual QString generatedUiHeader(const QString &formFile) const; // returns the CONFIG variable from the .pro file QStringList qmakeConfig() const;