Designer: Prepare re-implementation of "Goto Slot".

...making use of the new code model features. Move code to
separate file. Make it possible to obtain path to generated
header file from project manager.
This commit is contained in:
Friedemann Kleint
2009-12-03 16:23:15 +01:00
parent de4fedbe50
commit 087e72499e
10 changed files with 280 additions and 16 deletions

View File

@@ -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

View File

@@ -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 &parameterNames,
QString *errorMessage);
} // namespace Internal
} // namespace Designer
#endif // CODEMODELHELPERS_H

View File

@@ -35,6 +35,7 @@ HEADERS += formeditorplugin.h \
settingsmanager.h \ settingsmanager.h \
formtemplatewizardpage.h \ formtemplatewizardpage.h \
formwizarddialog.h \ formwizarddialog.h \
codemodelhelpers.h \
designer_export.h designer_export.h
SOURCES += formeditorplugin.cpp \ SOURCES += formeditorplugin.cpp \
@@ -49,7 +50,8 @@ SOURCES += formeditorplugin.cpp \
formeditorw.cpp \ formeditorw.cpp \
settingsmanager.cpp \ settingsmanager.cpp \
formtemplatewizardpage.cpp \ formtemplatewizardpage.cpp \
formwizarddialog.cpp formwizarddialog.cpp \
codemodelhelpers.cpp
RESOURCES += designer.qrc RESOURCES += designer.qrc

View File

@@ -31,6 +31,7 @@
#include "qtcreatorintegration.h" #include "qtcreatorintegration.h"
#include "formeditorw.h" #include "formeditorw.h"
#include "formwindoweditor.h" #include "formwindoweditor.h"
#include "codemodelhelpers.h"
#include <cpptools/cppmodelmanagerinterface.h> #include <cpptools/cppmodelmanagerinterface.h>
#include <cplusplus/Symbols.h> #include <cplusplus/Symbols.h>
@@ -570,7 +571,9 @@ bool QtCreatorIntegration::navigateToSlot(const QString &objectName,
QString *errorMessage) QString *errorMessage)
{ {
const QString currentUiFile = m_few->activeFormWindow()->file()->fileName(); 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. // 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(). // 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 // The idea is that the .pro file knows if the .ui files is inside, and the .pro file knows it will

View File

@@ -467,3 +467,9 @@ QStringList Project::frameworkPaths(const QString &) const
{ {
return QStringList(); return QStringList();
} }
QString Project::generatedUiHeader(const QString & /* formFile */) const
{
return QString();
}

View File

@@ -113,6 +113,8 @@ public:
enum FilesMode { AllFiles, ExcludeGeneratedFiles }; enum FilesMode { AllFiles, ExcludeGeneratedFiles };
virtual QStringList files(FilesMode fileMode) const = 0; 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 // C++ specific
// TODO do a C++ project as a base ? // TODO do a C++ project as a base ?

View File

@@ -1252,6 +1252,23 @@ void Qt4ProFileNode::updateCodeModelSupportFromEditor(const QString &uiFileName,
qt4proFileNode->updateCodeModelSupportFromEditor(uiFileName, fw); 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() void Qt4ProFileNode::createUiCodeModelSupport()
{ {
// qDebug()<<"creatUiCodeModelSupport()"; // qDebug()<<"creatUiCodeModelSupport()";
@@ -1271,16 +1288,9 @@ void Qt4ProFileNode::createUiCodeModelSupport()
const QList<FileNode*> uiFiles = uiFilesVisitor.uiFileNodes; const QList<FileNode*> uiFiles = uiFilesVisitor.uiFileNodes;
// Find the UiDir, there can only ever be one // Find the UiDir, there can only ever be one
QString uiDir = buildDir(); const QString uiDir = uiDirectory();
QStringList tmp = m_varValues[UiDirVar]; foreach (const FileNode *uiFile, uiFiles) {
if (tmp.size() != 0) const QString uiHeaderFilePath = uiHeaderFile(uiDir, uiFile->path());
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; // qDebug()<<"code model support for "<<uiFile->path()<<" "<<uiHeaderFilePath;
QMap<QString, Qt4UiCodeModelSupport *>::iterator it = oldCodeModelSupport.find(uiFile->path()); QMap<QString, Qt4UiCodeModelSupport *>::iterator it = oldCodeModelSupport.find(uiFile->path());
if (it != oldCodeModelSupport.end()) { if (it != oldCodeModelSupport.end()) {

View File

@@ -196,6 +196,10 @@ public:
void updateCodeModelSupportFromBuild(const QStringList &files); void updateCodeModelSupportFromBuild(const QStringList &files);
void updateCodeModelSupportFromEditor(const QString &uiFileName, Designer::FormWindowEditor *fw); void updateCodeModelSupportFromEditor(const QString &uiFileName, Designer::FormWindowEditor *fw);
QString uiDirectory() const;
static QString uiHeaderFile(const QString &uiDir, const QString &formFile);
public slots: public slots:
void scheduleUpdate(); void scheduleUpdate();
void update(); void update();
@@ -203,6 +207,8 @@ private slots:
void buildStateChanged(ProjectExplorer::Project*); void buildStateChanged(ProjectExplorer::Project*);
private: private:
typedef QHash<Qt4Variable, QStringList> Qt4VariablesHash;
void createUiCodeModelSupport(); void createUiCodeModelSupport();
QStringList updateUiFiles(); QStringList updateUiFiles();
Qt4ProFileNode *createSubProFileNode(const QString &path); Qt4ProFileNode *createSubProFileNode(const QString &path);
@@ -215,7 +221,7 @@ private:
void invalidate(); void invalidate();
Qt4ProjectType m_projectType; Qt4ProjectType m_projectType;
QHash<Qt4Variable, QStringList> m_varValues; Qt4VariablesHash m_varValues;
QTimer m_updateTimer; QTimer m_updateTimer;
QMap<QString, QDateTime> m_uitimestamps; QMap<QString, QDateTime> m_uitimestamps;

View File

@@ -787,6 +787,38 @@ QStringList Qt4Project::files(FilesMode fileMode) const
return files; 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() QList<ProjectExplorer::Project*> Qt4Project::dependsOn()
{ {
// NBS implement dependsOn // NBS implement dependsOn
@@ -820,8 +852,6 @@ void Qt4Project::updateActiveRunConfiguration()
emit targetInformationChanged(); emit targetInformationChanged();
} }
BuildConfigWidget *Qt4Project::createConfigWidget() BuildConfigWidget *Qt4Project::createConfigWidget()
{ {
return new Qt4ProjectConfigWidget(this); return new Qt4ProjectConfigWidget(this);

View File

@@ -179,6 +179,7 @@ public:
Internal::Qt4ProFileNode *rootProjectNode() const; Internal::Qt4ProFileNode *rootProjectNode() const;
virtual QStringList files(FilesMode fileMode) const; virtual QStringList files(FilesMode fileMode) const;
virtual QString generatedUiHeader(const QString &formFile) const;
// returns the CONFIG variable from the .pro file // returns the CONFIG variable from the .pro file
QStringList qmakeConfig() const; QStringList qmakeConfig() const;