2008-12-02 12:01:29 +01:00
/***************************************************************************
* *
* * This file is part of Qt Creator
* *
2009-01-13 19:21:51 +01:00
* * Copyright ( c ) 2008 - 2009 Nokia Corporation and / or its subsidiary ( - ies ) .
2008-12-02 12:01:29 +01:00
* *
* * Contact : Qt Software Information ( qt - info @ nokia . com )
* *
2008-12-02 14:17:16 +01:00
* *
* * Non - Open Source Usage
* *
2008-12-02 12:01:29 +01:00
* * Licensees may use this file in accordance with the Qt Beta Version
* * License Agreement , Agreement version 2.2 provided with the Software or ,
* * alternatively , in accordance with the terms contained in a written
2008-12-02 14:17:16 +01:00
* * agreement between you and Nokia .
* *
* * GNU General Public License Usage
* *
2008-12-02 12:01:29 +01:00
* * Alternatively , this file may be used under the terms of the GNU General
* * Public License versions 2.0 or 3.0 as published by the Free Software
* * Foundation and appearing in the file LICENSE . GPL included in the packaging
* * of this file . Please review the following information to ensure GNU
* * General Public Licensing requirements will be met :
* *
* * http : //www.fsf.org/licensing/licenses/info/GPLv2.html and
* * http : //www.gnu.org/copyleft/gpl.html.
* *
* * In addition , as a special exception , Nokia gives you certain additional
2008-12-02 14:17:16 +01:00
* * rights . These rights are described in the Nokia Qt GPL Exception
2008-12-16 17:20:00 +01:00
* * version 1.3 , included in the file GPL_EXCEPTION . txt in this package .
2008-12-02 14:17:16 +01:00
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2008-12-02 15:08:31 +01:00
2008-12-02 12:01:29 +01:00
# include "formeditorplugin.h"
# include "workbenchintegration.h"
# include "formeditorw.h"
# include "formwindoweditor.h"
# include <cpptools/cppmodelmanagerinterface.h>
# include <cplusplus/Symbols.h>
# include <cplusplus/Overview.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 <cplusplus/LookupContext.h>
# include <coreplugin/icore.h>
# include <coreplugin/editormanager/editormanager.h>
2009-01-19 12:39:20 +01:00
# include <extensionsystem/pluginmanager.h>
2008-12-02 12:01:29 +01:00
# include <texteditor/basetexteditor.h>
# include <texteditor/itexteditable.h>
# include <QtDesigner/QDesignerFormWindowInterface>
2008-12-16 16:39:19 +01:00
# include <QtGui/QMessageBox>
2008-12-02 12:01:29 +01:00
2008-12-16 16:39:19 +01:00
# include <QtCore/QFileInfo>
2008-12-02 12:01:29 +01:00
# include <QtCore/QDebug>
2008-12-16 16:39:19 +01:00
enum { debugSlotNavigation = 0 } ;
2008-12-18 17:19:54 +01:00
enum { indentation = 4 } ;
2008-12-16 16:39:19 +01:00
2008-12-02 12:01:29 +01:00
using namespace Designer : : Internal ;
using namespace CPlusPlus ;
using namespace TextEditor ;
2008-12-16 16:39:19 +01:00
static QString msgClassNotFound ( const QString & uiClassName , const QList < Document : : Ptr > & docList )
{
QString files ;
foreach ( const Document : : Ptr & doc , docList ) {
if ( ! files . isEmpty ( ) )
files + = QLatin1String ( " , " ) ;
files + = doc - > fileName ( ) ;
}
return WorkbenchIntegration : : tr ( " The class definition of '%1' could not be found in %2. " ) . arg ( uiClassName , files ) ;
}
2008-12-18 17:19:54 +01:00
static inline CppTools : : CppModelManagerInterface * cppModelManagerInstance ( )
{
2009-01-20 11:52:04 +01:00
return ExtensionSystem : : PluginManager : : instance ( )
- > getObject < CppTools : : CppModelManagerInterface > ( ) ;
2008-12-18 17:19:54 +01:00
}
2008-12-02 12:01:29 +01:00
WorkbenchIntegration : : WorkbenchIntegration ( QDesignerFormEditorInterface * core , FormEditorW * parent ) :
qdesigner_internal : : QDesignerIntegration ( core , : : qobject_cast < QObject * > ( parent ) ) ,
m_few ( parent )
{
setResourceFileWatcherBehaviour ( QDesignerIntegration : : ReloadSilently ) ;
setResourceEditingEnabled ( false ) ;
setSlotNavigationEnabled ( true ) ;
2008-12-11 13:43:11 +01:00
connect ( this , SIGNAL ( navigateToSlot ( QString , QString , QStringList ) ) ,
this , SLOT ( slotNavigateToSlot ( QString , QString , QStringList ) ) ) ;
2008-12-02 12:01:29 +01:00
}
void WorkbenchIntegration : : updateSelection ( )
{
if ( FormWindowEditor * afww = m_few - > activeFormWindow ( ) )
afww - > updateFormWindowSelectionHandles ( true ) ;
qdesigner_internal : : QDesignerIntegration : : updateSelection ( ) ;
}
QWidget * WorkbenchIntegration : : containerWindow ( QWidget * /*widget*/ ) const
{
FormWindowEditor * fw = m_few - > activeFormWindow ( ) ;
if ( ! fw )
return 0 ;
return fw - > integrationContainer ( ) ;
}
2008-12-18 17:19:54 +01:00
static QList < Document : : Ptr > findDocumentsIncluding ( const CPlusPlus : : Snapshot & docTable ,
const QString & fileName , bool checkFileNameOnly )
2008-12-02 12:01:29 +01:00
{
QList < Document : : Ptr > docList ;
2008-12-18 17:19:54 +01:00
foreach ( const Document : : Ptr & doc , docTable ) { // we go through all documents
const QStringList includes = doc - > includedFiles ( ) ;
foreach ( const QString & include , includes ) {
2008-12-11 13:43:11 +01:00
if ( checkFileNameOnly ) {
const QFileInfo fi ( include ) ;
if ( fi . fileName ( ) = = fileName ) { // we are only interested in docs which includes fileName only
docList . append ( doc ) ;
}
} else {
if ( include = = fileName )
docList . append ( doc ) ;
2008-12-02 12:01:29 +01:00
}
}
}
return docList ;
}
2008-12-18 17:19:54 +01:00
// Check for a class name where haystack is a member class of an object.
// So, haystack can be shorter (can have some namespaces omitted because of a
// "using namespace" declaration, for example, comparing
// "foo::Ui::form", against "using namespace foo; Ui::form".
2008-12-11 13:43:11 +01:00
2008-12-18 17:19:54 +01:00
static bool matchMemberClassName ( const QString & needle , const QString & hayStack )
{
if ( needle = = hayStack )
return true ;
if ( ! needle . endsWith ( hayStack ) )
return false ;
// Check if there really is a separator "::"
const int separatorPos = needle . size ( ) - hayStack . size ( ) - 1 ;
return separatorPos > 1 & & needle . at ( separatorPos ) = = QLatin1Char ( ' : ' ) ;
}
2008-12-02 12:01:29 +01:00
2008-12-18 17:19:54 +01:00
// Find class definition in namespace
static const Class * findClass ( const Namespace * parentNameSpace , const QString & className , QString * namespaceName )
{
if ( debugSlotNavigation )
qDebug ( ) < < Q_FUNC_INFO < < className ;
const Overview o ;
const unsigned namespaceMemberCount = parentNameSpace - > memberCount ( ) ;
for ( unsigned i = 0 ; i < namespaceMemberCount ; i + + ) { // we go through all namespace members
const Symbol * sym = parentNameSpace - > memberAt ( i ) ;
// we have found a class - we are interested in classes only
if ( const Class * cl = sym - > asClass ( ) ) {
const unsigned classMemberCount = cl - > memberCount ( ) ;
for ( unsigned j = 0 ; j < classMemberCount ; j + + ) // we go through class members
if ( const Declaration * decl = cl - > memberAt ( j ) - > asDeclaration ( ) ) {
// we want to know if the class contains a member (so we look into
// a declaration) of uiClassName type
const NamedType * nt = decl - > type ( ) - > asNamedType ( ) ;
2008-12-02 12:01:29 +01:00
// handle pointers to member variables
if ( PointerType * pt = decl - > type ( ) - > asPointerType ( ) )
nt = pt - > elementType ( ) - > asNamedType ( ) ;
2008-12-18 17:19:54 +01:00
if ( nt & & matchMemberClassName ( className , o . prettyName ( nt - > name ( ) ) ) )
2008-12-02 12:01:29 +01:00
return cl ;
2008-12-18 17:19:54 +01:00
} // decl
} else {
// Check namespaces
if ( const Namespace * ns = sym - > asNamespace ( ) ) {
QString tempNS = * namespaceName ;
tempNS + = o . prettyName ( ns - > name ( ) ) ;
tempNS + = QLatin1String ( " :: " ) ;
if ( const Class * cl = findClass ( ns , className , & tempNS ) ) {
* namespaceName = tempNS ;
return cl ;
2008-12-02 12:01:29 +01:00
}
2008-12-18 17:19:54 +01:00
} // member is namespave
} // member is no class
} // for members
2008-12-02 12:01:29 +01:00
return 0 ;
}
2008-12-18 17:19:54 +01:00
static const Function * findDeclaration ( const Class * cl , const QString & functionName )
2008-12-02 12:01:29 +01:00
{
2008-12-11 13:43:11 +01:00
const QString funName = QString : : fromUtf8 ( QMetaObject : : normalizedSignature ( functionName . toUtf8 ( ) ) ) ;
2008-12-18 17:19:54 +01:00
const unsigned mCount = cl - > memberCount ( ) ;
// we are interested only in declarations (can be decl of method or of a field)
// we are only interested in declarations of methods
const Overview overview ;
for ( unsigned j = 0 ; j < mCount ; j + + ) { // go through all members
if ( const Declaration * decl = cl - > memberAt ( j ) - > asDeclaration ( ) )
if ( const Function * fun = decl - > type ( ) - > asFunction ( ) ) {
// Format signature
QString memberFunction = overview . prettyName ( fun - > name ( ) ) ;
memberFunction + = QLatin1Char ( ' ( ' ) ;
const uint aCount = fun - > argumentCount ( ) ;
for ( uint i = 0 ; i < aCount ; i + + ) { // we build argument types string
const Argument * arg = fun - > argumentAt ( i ) - > asArgument ( ) ;
2008-12-11 13:43:11 +01:00
if ( i > 0 )
memberFunction + = QLatin1Char ( ' , ' ) ;
memberFunction + = overview . prettyType ( arg - > type ( ) ) ;
}
memberFunction + = QLatin1Char ( ' ) ' ) ;
// we compare normalized signatures
memberFunction = QString : : fromUtf8 ( QMetaObject : : normalizedSignature ( memberFunction . toUtf8 ( ) ) ) ;
if ( memberFunction = = funName ) // we match function names and argument lists
2008-12-02 12:01:29 +01:00
return fun ;
}
}
return 0 ;
}
// TODO: remove me, see below
2008-12-18 17:19:54 +01:00
static bool isCompatible ( const Name * name , const Name * otherName )
2008-12-02 12:01:29 +01:00
{
2008-12-18 17:19:54 +01:00
if ( const NameId * nameId = name - > asNameId ( ) ) {
if ( const TemplateNameId * otherTemplId = otherName - > asTemplateNameId ( ) )
2008-12-02 12:01:29 +01:00
return nameId - > identifier ( ) - > isEqualTo ( otherTemplId - > identifier ( ) ) ;
2008-12-18 17:19:54 +01:00
} else if ( const TemplateNameId * templId = name - > asTemplateNameId ( ) ) {
if ( const NameId * otherNameId = otherName - > asNameId ( ) )
2008-12-02 12:01:29 +01:00
return templId - > identifier ( ) - > isEqualTo ( otherNameId - > identifier ( ) ) ;
}
return name - > isEqualTo ( otherName ) ;
}
// TODO: remove me, see below
2008-12-18 17:19:54 +01:00
static bool isCompatible ( const Function * definition , const Symbol * declaration , const QualifiedNameId * declarationName )
2008-12-02 12:01:29 +01:00
{
Function * declTy = declaration - > type ( ) - > asFunction ( ) ;
if ( ! declTy )
return false ;
Name * definitionName = definition - > name ( ) ;
if ( QualifiedNameId * q = definitionName - > asQualifiedNameId ( ) ) {
if ( ! isCompatible ( q - > unqualifiedNameId ( ) , declaration - > name ( ) ) )
return false ;
else if ( q - > nameCount ( ) > declarationName - > nameCount ( ) )
return false ;
else if ( declTy - > argumentCount ( ) ! = definition - > argumentCount ( ) )
return false ;
else if ( declTy - > isConst ( ) ! = definition - > isConst ( ) )
return false ;
else if ( declTy - > isVolatile ( ) ! = definition - > isVolatile ( ) )
return false ;
for ( unsigned i = 0 ; i < definition - > argumentCount ( ) ; + + i ) {
Symbol * arg = definition - > argumentAt ( i ) ;
Symbol * otherArg = declTy - > argumentAt ( i ) ;
if ( ! arg - > type ( ) . isEqualTo ( otherArg - > type ( ) ) )
return false ;
}
for ( unsigned i = 0 ; i ! = q - > nameCount ( ) ; + + i ) {
Name * n = q - > nameAt ( q - > nameCount ( ) - i - 1 ) ;
Name * m = declarationName - > nameAt ( declarationName - > nameCount ( ) - i - 1 ) ;
if ( ! isCompatible ( n , m ) )
return false ;
}
return true ;
} else {
// ### TODO: implement isCompatible for unqualified name ids.
}
return false ;
}
// TODO: remove me, this is taken from cppeditor.cpp. Find some common place for this method
2008-12-18 17:19:54 +01:00
static Document : : Ptr findDefinition ( const Function * functionDeclaration , int * line )
2008-12-02 12:01:29 +01:00
{
2008-12-18 17:19:54 +01:00
CppTools : : CppModelManagerInterface * cppModelManager = cppModelManagerInstance ( ) ;
2008-12-02 12:01:29 +01:00
if ( ! cppModelManager )
return Document : : Ptr ( ) ;
QVector < Name * > qualifiedName ;
Scope * scope = functionDeclaration - > scope ( ) ;
for ( ; scope ; scope = scope - > enclosingScope ( ) ) {
if ( scope - > isClassScope ( ) | | scope - > isNamespaceScope ( ) ) {
if ( scope - > owner ( ) & & scope - > owner ( ) - > name ( ) ) {
Name * scopeOwnerName = scope - > owner ( ) - > name ( ) ;
if ( QualifiedNameId * q = scopeOwnerName - > asQualifiedNameId ( ) ) {
for ( unsigned i = 0 ; i < q - > nameCount ( ) ; + + i ) {
qualifiedName . prepend ( q - > nameAt ( i ) ) ;
2008-12-18 17:19:54 +01:00
}
2008-12-02 12:01:29 +01:00
} else {
qualifiedName . prepend ( scopeOwnerName ) ;
}
}
}
}
qualifiedName . append ( functionDeclaration - > name ( ) ) ;
Control control ;
QualifiedNameId * q = control . qualifiedNameId ( & qualifiedName [ 0 ] , qualifiedName . size ( ) ) ;
LookupContext context ( & control ) ;
2008-12-12 10:07:58 +01:00
const Snapshot documents = cppModelManager - > snapshot ( ) ;
2008-12-02 12:01:29 +01:00
foreach ( Document : : Ptr doc , documents ) {
QList < Scope * > visibleScopes ;
visibleScopes . append ( doc - > globalSymbols ( ) ) ;
visibleScopes = context . expand ( visibleScopes ) ;
foreach ( Scope * visibleScope , visibleScopes ) {
Symbol * symbol = 0 ;
if ( NameId * nameId = q - > unqualifiedNameId ( ) - > asNameId ( ) )
symbol = visibleScope - > lookat ( nameId - > identifier ( ) ) ;
else if ( DestructorNameId * dtorId = q - > unqualifiedNameId ( ) - > asDestructorNameId ( ) )
symbol = visibleScope - > lookat ( dtorId - > identifier ( ) ) ;
else if ( TemplateNameId * templNameId = q - > unqualifiedNameId ( ) - > asTemplateNameId ( ) )
symbol = visibleScope - > lookat ( templNameId - > identifier ( ) ) ;
else if ( OperatorNameId * opId = q - > unqualifiedNameId ( ) - > asOperatorNameId ( ) )
symbol = visibleScope - > lookat ( opId - > kind ( ) ) ;
// ### cast operators
for ( ; symbol ; symbol = symbol - > next ( ) ) {
if ( ! symbol - > isFunction ( ) )
continue ;
else if ( ! isCompatible ( symbol - > asFunction ( ) , functionDeclaration , q ) )
continue ;
* line = symbol - > line ( ) ; // TODO: shift the line so that we are inside a function. Maybe just find the nearest '{'?
return doc ;
}
}
}
return Document : : Ptr ( ) ;
}
2008-12-11 13:43:11 +01:00
// TODO: Wait for robust Roberto's code using AST or whatever for that. Current implementation is hackish.
static int findClassEndPosition ( const QString & headerContents , int classStartPosition )
{
const QString contents = headerContents . mid ( classStartPosition ) ; // we start serching from the beginning of class declaration
// We need to find the position of class closing "}"
int openedBlocksCount = 0 ; // counter of nested {} blocks
int idx = 0 ; // index of current position in the contents
while ( true ) {
if ( idx < 0 | | idx > = contents . length ( ) ) // indexOf returned -1, that means we don't have closing comment mark
break ;
if ( contents . mid ( idx , 2 ) = = QLatin1String ( " // " ) ) {
idx = contents . indexOf ( QLatin1Char ( ' \n ' ) , idx + 2 ) + 1 ; // drop everything up to the end of line
} else if ( contents . mid ( idx , 2 ) = = QLatin1String ( " /* " ) ) {
idx = contents . indexOf ( QLatin1String ( " */ " ) , idx + 2 ) + 1 ; // drop everything up to the nearest */
} else if ( contents . mid ( idx , 4 ) = = QLatin1String ( " ' \\ \" ' " ) ) {
idx + = 4 ; // drop it
} else if ( contents . at ( idx ) = = QLatin1Char ( ' \" ' ) ) {
do {
idx = contents . indexOf ( QLatin1Char ( ' \" ' ) , idx + 1 ) ; // drop everything up to the nearest "
} while ( idx > 0 & & contents . at ( idx - 1 ) = = QLatin1Char ( ' \\ ' ) ) ; // if the nearest " is preceeded by \ we find next one
if ( idx < 0 )
break ;
idx + + ;
} else {
if ( contents . at ( idx ) = = QLatin1Char ( ' { ' ) ) {
openedBlocksCount + + ;
} else if ( contents . at ( idx ) = = QLatin1Char ( ' } ' ) ) {
openedBlocksCount - - ;
if ( openedBlocksCount = = 0 ) {
return classStartPosition + idx ;
}
}
idx + + ;
}
}
return - 1 ;
}
2008-12-18 17:19:54 +01:00
static inline ITextEditable * editableAt ( const QString & fileName , int line , int column )
{
return qobject_cast < ITextEditable * > ( TextEditor : : BaseTextEditor : : openEditorAt ( fileName , line , column ) ) ;
}
static void addDeclaration ( const QString & docFileName , const Class * cl , const QString & functionName )
2008-12-02 12:01:29 +01:00
{
2008-12-18 17:19:54 +01:00
QString declaration = QLatin1String ( " void " ) ;
declaration + = functionName ;
declaration + = QLatin1String ( " ; \n " ) ;
// functionName comes already with argument names (if designer managed to
// do that). First, let's try to find any method which is a private slot
// (then we don't need to add "private slots:" statement)
const unsigned mCount = cl - > memberCount ( ) ;
for ( unsigned j = 0 ; j < mCount ; j + + ) { // go through all members
if ( const Declaration * decl = cl - > memberAt ( j ) - > asDeclaration ( ) )
if ( const Function * fun = decl - > type ( ) - > asFunction ( ) ) {
// we are only interested in declarations of methods.
// fun->column() returns always 0, what can cause trouble in case in one
// line if there is: "private slots: void foo();"
2008-12-02 12:01:29 +01:00
if ( fun - > isSlot ( ) & & fun - > isPrivate ( ) ) {
2008-12-18 17:19:54 +01:00
if ( ITextEditable * editable = editableAt ( docFileName , fun - > line ( ) , fun - > column ( ) ) )
editable - > insert ( declaration + QLatin1String ( " " ) ) ;
2008-12-02 12:01:29 +01:00
return ;
}
}
}
2008-12-11 13:43:11 +01:00
// We didn't find any method under "private slots:", let's add "private slots:". Below code
// adds "private slots:" by the end of the class definition.
2008-12-18 17:19:54 +01:00
if ( ITextEditable * editable = editableAt ( docFileName , cl - > line ( ) , cl - > column ( ) ) ) {
2008-12-11 13:43:11 +01:00
int classEndPosition = findClassEndPosition ( editable - > contents ( ) , editable - > position ( ) ) ;
if ( classEndPosition > = 0 ) {
int line , column ;
editable - > convertPosition ( classEndPosition , & line , & column ) ; // converts back position into a line and column
editable - > gotoLine ( line , column ) ; // go to position (we should be just before closing } of the class)
2008-12-18 17:19:54 +01:00
editable - > insert ( QLatin1String ( " \n private slots: \n " ) + declaration ) ;
2008-12-11 13:43:11 +01:00
}
}
}
2008-12-18 17:19:54 +01:00
static Document : : Ptr addDefinition ( const CPlusPlus : : Snapshot & docTable ,
const QString & headerFileName , const QString & className ,
const QString & functionName , int * line )
2008-12-11 13:43:11 +01:00
{
2008-12-18 17:19:54 +01:00
QString definition = QLatin1String ( " \n void " ) ;
definition + = className ;
definition + = QLatin1String ( " :: " ) ;
definition + = functionName ;
definition + = QLatin1String ( " \n { \n " ) ;
definition + = QString ( indentation , QLatin1Char ( ' ' ) ) ;
definition + = QLatin1String ( " \n } \n " ) ;
2008-12-11 13:43:11 +01:00
// we find all documents which include headerFileName
2008-12-18 17:19:54 +01:00
const QList < Document : : Ptr > docList = findDocumentsIncluding ( docTable , headerFileName , false ) ;
2008-12-11 13:43:11 +01:00
if ( docList . isEmpty ( ) )
return Document : : Ptr ( ) ;
QFileInfo headerFI ( headerFileName ) ;
const QString headerBaseName = headerFI . baseName ( ) ;
const QString headerAbsolutePath = headerFI . absolutePath ( ) ;
2008-12-18 17:19:54 +01:00
foreach ( const Document : : Ptr & doc , docList ) {
const QFileInfo sourceFI ( doc - > fileName ( ) ) ;
2008-12-11 13:43:11 +01:00
// we take only those documents which has the same filename and path (maybe we don't need to compare the path???)
if ( headerBaseName = = sourceFI . baseName ( ) & & headerAbsolutePath = = sourceFI . absolutePath ( ) ) {
2008-12-18 17:19:54 +01:00
if ( ITextEditable * editable = editableAt ( doc - > fileName ( ) , 0 , 0 ) ) {
2008-12-11 13:43:11 +01:00
const QString contents = editable - > contents ( ) ;
int column ;
editable - > convertPosition ( contents . length ( ) , line , & column ) ;
editable - > gotoLine ( * line , column ) ;
2008-12-18 17:19:54 +01:00
editable - > insert ( definition ) ;
2008-12-11 13:43:11 +01:00
* line + = 1 ;
}
return doc ;
}
}
return Document : : Ptr ( ) ;
2008-12-02 12:01:29 +01:00
}
2008-12-18 17:19:54 +01:00
// Insert the parameter names into a signature, "void foo(bool)" ->
// "void foo(bool checked)"
2008-12-11 13:43:11 +01:00
static QString addParameterNames ( const QString & functionSignature , const QStringList & parameterNames )
{
2008-12-18 17:19:54 +01:00
const int firstParen = functionSignature . indexOf ( QLatin1Char ( ' ( ' ) ) ;
QString functionName = functionSignature . left ( firstParen + 1 ) ;
QString argumentsString = functionSignature . mid ( firstParen + 1 ) ;
const int lastParen = argumentsString . lastIndexOf ( QLatin1Char ( ' ) ' ) ) ;
if ( lastParen ! = - 1 )
argumentsString . truncate ( lastParen ) ;
2008-12-11 13:43:11 +01:00
const QStringList arguments = argumentsString . split ( QLatin1Char ( ' , ' ) , QString : : SkipEmptyParts ) ;
2008-12-18 17:19:54 +01:00
const int pCount = parameterNames . count ( ) ;
const int aCount = arguments . count ( ) ;
for ( int i = 0 ; i < aCount ; + + i ) {
2008-12-11 13:43:11 +01:00
if ( i > 0 )
functionName + = QLatin1String ( " , " ) ;
functionName + = arguments . at ( i ) ;
2008-12-18 17:19:54 +01:00
if ( i < pCount ) {
functionName + = QLatin1Char ( ' ' ) ;
functionName + = parameterNames . at ( i ) ;
}
2008-12-11 13:43:11 +01:00
}
functionName + = QLatin1Char ( ' ) ' ) ;
return functionName ;
}
2008-12-18 17:19:54 +01:00
// Recursively find a class definition in the document passed on or in its
// included files (going down [maxIncludeDepth] includes) and return a pair
// of <Class*, Document>.
typedef QPair < const Class * , Document : : Ptr > ClassDocumentPtrPair ;
static ClassDocumentPtrPair
findClassRecursively ( const CPlusPlus : : Snapshot & docTable ,
const Document : : Ptr & doc , const QString & className ,
unsigned maxIncludeDepth , QString * namespaceName )
{
if ( debugSlotNavigation )
qDebug ( ) < < Q_FUNC_INFO < < doc - > fileName ( ) < < maxIncludeDepth ;
// Check document
if ( const Class * cl = findClass ( doc - > globalNamespace ( ) , className , namespaceName ) )
return ClassDocumentPtrPair ( cl , doc ) ;
if ( maxIncludeDepth ) {
// Check the includes
const unsigned recursionMaxIncludeDepth = maxIncludeDepth - 1u ;
foreach ( const QString & include , doc - > includedFiles ( ) ) {
const CPlusPlus : : Snapshot : : const_iterator it = docTable . constFind ( include ) ;
if ( it ! = docTable . constEnd ( ) ) {
const Document : : Ptr includeDoc = it . value ( ) ;
const ClassDocumentPtrPair irc = findClassRecursively ( docTable , it . value ( ) , className , recursionMaxIncludeDepth , namespaceName ) ;
if ( irc . first )
return irc ;
}
}
}
return ClassDocumentPtrPair ( 0 , Document : : Ptr ( ) ) ;
}
2008-12-16 16:39:19 +01:00
2008-12-11 13:43:11 +01:00
void WorkbenchIntegration : : slotNavigateToSlot ( const QString & objectName , const QString & signalSignature ,
const QStringList & parameterNames )
2008-12-16 16:39:19 +01:00
{
QString errorMessage ;
if ( ! navigateToSlot ( objectName , signalSignature , parameterNames , & errorMessage ) & & ! errorMessage . isEmpty ( ) ) {
2008-12-18 17:19:54 +01:00
QMessageBox : : warning ( m_few - > designerEditor ( ) - > topLevel ( ) , tr ( " Error finding/adding a slot. " ) , errorMessage ) ;
2008-12-16 16:39:19 +01:00
}
}
2008-12-18 17:19:54 +01:00
// Build name of the class as generated by uic, insert Ui namespace
// "foo::bar::form" -> "foo::bar::Ui::form"
static inline QString uiClassName ( QString formObjectName )
{
const int indexOfScope = formObjectName . lastIndexOf ( QLatin1String ( " :: " ) ) ;
const int uiNameSpaceInsertionPos = indexOfScope > = 0 ? indexOfScope : 0 ;
formObjectName . insert ( uiNameSpaceInsertionPos , QLatin1String ( " Ui:: " ) ) ;
return formObjectName ;
}
2008-12-16 16:39:19 +01:00
bool WorkbenchIntegration : : navigateToSlot ( const QString & objectName ,
const QString & signalSignature ,
const QStringList & parameterNames ,
QString * errorMessage )
2008-12-02 12:01:29 +01:00
{
const QString currentUiFile = m_few - > activeFormWindow ( ) - > file ( ) - > fileName ( ) ;
2008-12-11 13:43:11 +01:00
// 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().
2008-12-02 12:01:29 +01:00
// The idea is that the .pro file knows if the .ui files is inside, and the .pro file knows it will
// be generating the ui_<>.h file for it, and the .pro file knows what the generated file's name and its absolute path will be.
// So we should somehow get that info from project manager (?)
const QFileInfo fi ( currentUiFile ) ;
const QString uicedName = QLatin1String ( " ui_ " ) + fi . baseName ( ) + QLatin1String ( " .h " ) ;
2008-12-18 17:19:54 +01:00
// take all docs
const CPlusPlus : : Snapshot docTable = cppModelManagerInstance ( ) - > snapshot ( ) ;
QList < Document : : Ptr > docList = findDocumentsIncluding ( docTable , uicedName , true ) ; // change to false when we know the absolute path to generated ui_<>.h file
2008-12-16 16:39:19 +01:00
if ( debugSlotNavigation )
qDebug ( ) < < objectName < < signalSignature < < " Looking for " < < uicedName < < " returned " < < docList . size ( ) ;
if ( docList . isEmpty ( ) ) {
* errorMessage = tr ( " No documents matching %1 could be found. " ) . arg ( uicedName ) ;
return false ;
}
2008-12-02 12:01:29 +01:00
QDesignerFormWindowInterface * fwi = m_few - > activeFormWindow ( ) - > formWindow ( ) ;
2008-12-18 17:19:54 +01:00
const QString uiClass = uiClassName ( fwi - > mainContainer ( ) - > objectName ( ) ) ;
2008-12-02 12:01:29 +01:00
2008-12-16 16:39:19 +01:00
if ( debugSlotNavigation )
2008-12-18 17:19:54 +01:00
qDebug ( ) < < " Checking docs for " < < uiClass ;
// Find the class definition in the file itself or in the directly
// included files (order 1).
QString namespaceName ;
const Class * cl ;
Document : : Ptr doc ;
foreach ( const Document : : Ptr & d , docList ) {
const ClassDocumentPtrPair cd = findClassRecursively ( docTable , d , uiClass , 1u , & namespaceName ) ;
if ( cd . first ) {
cl = cd . first ;
doc = cd . second ;
break ;
}
}
if ( ! cl ) {
* errorMessage = msgClassNotFound ( uiClass , docList ) ;
return false ;
}
2008-12-16 16:39:19 +01:00
2008-12-18 17:19:54 +01:00
Overview o ;
const QString className = namespaceName + o . prettyName ( cl - > name ( ) ) ;
const QString functionName = QLatin1String ( " on_ " ) + objectName + QLatin1Char ( ' _ ' ) + signalSignature ;
const QString functionNameWithParameterNames = addParameterNames ( functionName , parameterNames ) ;
if ( debugSlotNavigation )
qDebug ( ) < < " Found " < < uiClass < < doc - > fileName ( ) < < " checking " < < functionName < < functionNameWithParameterNames ;
int line = 0 ;
Document : : Ptr sourceDoc ;
if ( const Function * fun = findDeclaration ( cl , functionName ) ) {
sourceDoc = findDefinition ( fun , & line ) ;
if ( ! sourceDoc ) {
// add function definition to cpp file
sourceDoc = addDefinition ( docTable , doc - > fileName ( ) , className , functionNameWithParameterNames , & line ) ;
2008-12-02 12:01:29 +01:00
}
2008-12-18 17:19:54 +01:00
} else {
// add function declaration to cl
addDeclaration ( doc - > fileName ( ) , cl , functionNameWithParameterNames ) ;
// add function definition to cpp file
sourceDoc = addDefinition ( docTable , doc - > fileName ( ) , className , functionNameWithParameterNames , & line ) ;
2008-12-02 12:01:29 +01:00
}
2008-12-18 17:19:54 +01:00
if ( ! sourceDoc ) {
* errorMessage = tr ( " Unable to add the method definition. " ) ;
return false ;
}
// jump to function definition, position within code
TextEditor : : BaseTextEditor : : openEditorAt ( sourceDoc - > fileName ( ) , line + 2 , indentation ) ;
return true ;
}