qtcreatorintegration.cpp 29.1 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2 3 4
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
con's avatar
con committed
8
**
9
** Commercial Usage
10
**
11 12 13 14
** 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.
15
**
16
** GNU Lesser General Public License Usage
17
**
18 19 20 21 22 23
** 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.
24
**
25
** If you are unsure which license is appropriate for your use, please
hjk's avatar
hjk committed
26
** contact the sales department at http://qt.nokia.com/contact.
con's avatar
con committed
27
**
28
**************************************************************************/
hjk's avatar
hjk committed
29

con's avatar
con committed
30
#include "formeditorplugin.h"
31
#include "formwindoweditor.h"
32
#include "qtcreatorintegration.h"
con's avatar
con committed
33
#include "formeditorw.h"
34
#include "editordata.h"
35
#include "codemodelhelpers.h"
36
#include <widgethost.h>
con's avatar
con committed
37 38 39 40 41 42 43 44 45 46 47 48 49

#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>
50
#include <extensionsystem/pluginmanager.h>
con's avatar
con committed
51 52
#include <texteditor/basetexteditor.h>
#include <texteditor/itexteditable.h>
53 54
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/session.h>
55
#include <utils/qtcassert.h>
con's avatar
con committed
56 57

#include <QtDesigner/QDesignerFormWindowInterface>
58
#include <QtDesigner/QDesignerFormEditorInterface>
con's avatar
con committed
59

60
#include <QtGui/QMessageBox>
con's avatar
con committed
61

62
#include <QtCore/QFileInfo>
con's avatar
con committed
63 64
#include <QtCore/QDebug>

65
enum { indentation = 4 };
66

con's avatar
con committed
67 68 69 70
using namespace Designer::Internal;
using namespace CPlusPlus;
using namespace TextEditor;

71 72 73 74 75 76 77 78
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();
    }
79
    return QtCreatorIntegration::tr("The class definition of '%1' could not be found in %2.").arg(uiClassName, files);
80 81
}

82 83
static inline CppTools::CppModelManagerInterface *cppModelManagerInstance()
{
84 85
    return ExtensionSystem::PluginManager::instance()
        ->getObject<CppTools::CppModelManagerInterface>();
86 87
}

88
QtCreatorIntegration::QtCreatorIntegration(QDesignerFormEditorInterface *core, FormEditorW *parent) :
con's avatar
con committed
89 90 91 92 93 94
    qdesigner_internal::QDesignerIntegration(core, ::qobject_cast<QObject*>(parent)),
    m_few(parent)
{
    setResourceFileWatcherBehaviour(QDesignerIntegration::ReloadSilently);
    setResourceEditingEnabled(false);
    setSlotNavigationEnabled(true);
95 96
    connect(this, SIGNAL(navigateToSlot(QString, QString, QStringList)),
            this, SLOT(slotNavigateToSlot(QString, QString, QStringList)));
97 98 99 100 101 102 103 104
    connect(this, SIGNAL(helpRequested(QString,QString)),
            this, SLOT(slotDesignerHelpRequested(QString,QString)));
}

void QtCreatorIntegration::slotDesignerHelpRequested(const QString &manual, const QString &document)
{
    // Pass on as URL.
    emit creatorHelpRequested(QString::fromLatin1("qthelp://com.trolltech.%1/qdoc/%2").arg(manual, document));
con's avatar
con committed
105 106
}

107
void QtCreatorIntegration::updateSelection()
con's avatar
con committed
108
{
109
    if (const EditorData ed = m_few->activeEditor())
110
        ed.widgetHost->updateFormWindowSelectionHandles(true);
con's avatar
con committed
111 112 113
    qdesigner_internal::QDesignerIntegration::updateSelection();
}

114
QWidget *QtCreatorIntegration::containerWindow(QWidget * /*widget*/) const
con's avatar
con committed
115
{
116
    if (const EditorData ed = m_few->activeEditor())
117
        return ed.widgetHost->integrationContainer();
118
    return 0;
con's avatar
con committed
119 120
}

121 122
static QList<Document::Ptr> findDocumentsIncluding(const CPlusPlus::Snapshot &docTable,
                                                   const QString &fileName, bool checkFileNameOnly)
con's avatar
con committed
123 124
{
    QList<Document::Ptr> docList;
125 126 127
    foreach (const Document::Ptr &doc, docTable) { // we go through all documents
        const QStringList includes = doc->includedFiles();
        foreach (const QString &include, includes) {
128 129 130 131 132 133 134 135
            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);
con's avatar
con committed
136 137 138 139 140 141
            }
        }
    }
    return docList;
}

142 143 144 145 146 147 148 149 150 151
// Does klass inherit baseClass?
static bool inherits(const Overview &o, const Class *klass, const QString &baseClass)
{
    const int baseClassCount = klass->baseClassCount();
    for (int b = 0; b < baseClassCount; b++)
        if (o.prettyName(klass->baseClassAt(b)->name()) == baseClass)
            return true;
    return false;
}

152 153 154 155
// 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".
156

157 158 159 160 161 162 163 164 165 166
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(':');
}
con's avatar
con committed
167

168 169 170
// Find class definition in namespace (that is, the outer class
// containing a member of the desired class type) or inheriting the desired class
// in case of forms using the Multiple Inheritance approach
171 172
static const Class *findClass(const Namespace *parentNameSpace, const QString &className, QString *namespaceName)
{
173
    if (Designer::Constants::Internal::debug)
174 175 176 177 178 179 180 181
        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()) {
182
            // 1) we go through class members
183
            const unsigned classMemberCount = cl->memberCount();
184
            for (unsigned j = 0; j < classMemberCount; j++)
185 186 187 188
                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();
con's avatar
con committed
189 190 191 192
                    // handle pointers to member variables
                    if (PointerType *pt = decl->type()->asPointerType())
                        nt = pt->elementType()->asNamedType();

193
                    if (nt && matchMemberClassName(className, o.prettyName(nt->name())))
con's avatar
con committed
194
                            return cl;
195
                } // decl
196 197 198
            // 2) does it inherit the desired class
            if (inherits(o, cl, className))
                return cl;
199 200 201 202 203 204 205 206 207
        } 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;
con's avatar
con committed
208
                }
209 210 211
            } // member is namespave
        } // member is no class
    } // for members
con's avatar
con committed
212 213 214
    return 0;
}

215
static const Function *findDeclaration(const Class *cl, const QString &functionName)
con's avatar
con committed
216
{
217
    const QString funName = QString::fromUtf8(QMetaObject::normalizedSignature(functionName.toUtf8()));
218 219 220 221 222 223
    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())
224
            if (const Function *fun = decl->type()->asFunctionType()) {
225 226 227 228 229 230
                // 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();
231 232 233 234 235 236 237 238
                    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
con's avatar
con committed
239 240 241 242 243 244 245
                    return fun;
            }
    }
    return 0;
}

// TODO: remove me, see below
246
static bool isCompatible(const Name *name, const Name *otherName)
con's avatar
con committed
247
{
248 249
    if (const NameId *nameId = name->asNameId()) {
        if (const TemplateNameId *otherTemplId = otherName->asTemplateNameId())
con's avatar
con committed
250
            return nameId->identifier()->isEqualTo(otherTemplId->identifier());
251 252
    } else if (const TemplateNameId *templId = name->asTemplateNameId()) {
        if (const NameId *otherNameId = otherName->asNameId())
con's avatar
con committed
253 254 255 256 257 258 259
            return templId->identifier()->isEqualTo(otherNameId->identifier());
    }

    return name->isEqualTo(otherName);
}

// TODO: remove me, see below
260
static bool isCompatible(const Function *definition, const Symbol *declaration, const QualifiedNameId *declarationName)
con's avatar
con committed
261
{
262
    Function *declTy = declaration->type()->asFunctionType();
con's avatar
con committed
263 264 265
    if (! declTy)
        return false;

Roberto Raggi's avatar
Roberto Raggi committed
266 267
    const Name *definitionName = definition->name();
    if (const QualifiedNameId *q = definitionName->asQualifiedNameId()) {
con's avatar
con committed
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
        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) {
Roberto Raggi's avatar
Roberto Raggi committed
287 288
            const Name *n = q->nameAt(q->nameCount() - i - 1);
            const Name *m = declarationName->nameAt(declarationName->nameCount() - i - 1);
con's avatar
con committed
289 290 291 292 293 294 295 296 297 298 299
            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
300
static Document::Ptr findDefinition(const Function *functionDeclaration, int *line)
con's avatar
con committed
301
{
302
    CppTools::CppModelManagerInterface *cppModelManager = cppModelManagerInstance();
con's avatar
con committed
303 304 305
    if (!cppModelManager)
        return Document::Ptr();

Roberto Raggi's avatar
Roberto Raggi committed
306
    QVector<const Name *> qualifiedName;
con's avatar
con committed
307 308 309 310
    Scope *scope = functionDeclaration->scope();
    for (; scope; scope = scope->enclosingScope()) {
        if (scope->isClassScope() || scope->isNamespaceScope()) {
            if (scope->owner() && scope->owner()->name()) {
Roberto Raggi's avatar
Roberto Raggi committed
311 312
                const Name *scopeOwnerName = scope->owner()->name();
                if (const QualifiedNameId *q = scopeOwnerName->asQualifiedNameId()) {
con's avatar
con committed
313 314
                    for (unsigned i = 0; i < q->nameCount(); ++i) {
                        qualifiedName.prepend(q->nameAt(i));
315 316

}
con's avatar
con committed
317 318 319 320 321 322 323 324 325 326
                } else {
                    qualifiedName.prepend(scopeOwnerName);
                }
            }
        }
    }

    qualifiedName.append(functionDeclaration->name());

    Control control;
Roberto Raggi's avatar
Roberto Raggi committed
327
    const QualifiedNameId *q = control.qualifiedNameId(&qualifiedName[0], qualifiedName.size());
con's avatar
con committed
328
    LookupContext context(&control);
329
    const Snapshot documents = cppModelManager->snapshot();
con's avatar
con committed
330 331 332 333 334 335
    foreach (Document::Ptr doc, documents) {
        QList<Scope *> visibleScopes;
        visibleScopes.append(doc->globalSymbols());
        visibleScopes = context.expand(visibleScopes);
        foreach (Scope *visibleScope, visibleScopes) {
            Symbol *symbol = 0;
Roberto Raggi's avatar
Roberto Raggi committed
336
            if (const NameId *nameId = q->unqualifiedNameId()->asNameId())
con's avatar
con committed
337
                symbol = visibleScope->lookat(nameId->identifier());
Roberto Raggi's avatar
Roberto Raggi committed
338
            else if (const DestructorNameId *dtorId = q->unqualifiedNameId()->asDestructorNameId())
con's avatar
con committed
339
                symbol = visibleScope->lookat(dtorId->identifier());
Roberto Raggi's avatar
Roberto Raggi committed
340
            else if (const TemplateNameId *templNameId = q->unqualifiedNameId()->asTemplateNameId())
con's avatar
con committed
341
                symbol = visibleScope->lookat(templNameId->identifier());
Roberto Raggi's avatar
Roberto Raggi committed
342
            else if (const OperatorNameId *opId = q->unqualifiedNameId()->asOperatorNameId())
con's avatar
con committed
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
                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();

}

359 360 361 362 363 364 365 366 367 368 369 370 371
static bool isEndingQuote(const QString &contents, int quoteIndex)
{
    bool endingQuote = true;
    if (quoteIndex > 0) {
        int previous = 1;
        while (contents.at(quoteIndex - previous) == QLatin1Char('\\')) {
            previous++;
            endingQuote = !endingQuote;
        }
    }
    return endingQuote;
}

372 373 374 375 376 377 378 379 380 381 382 383 384
// 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("/*")) {
385
            idx = contents.indexOf(QLatin1String("*/"), idx + 2) + 2; // drop everything up to the nearest */
386 387 388 389 390
        } 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 "
391
            } while (idx > 0 && !isEndingQuote(contents, idx)); // if the nearest " is preceded by \ (or by \\\ or by \\\\\, but not by \\ nor \\\\) we find next one
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409
            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;
}

410 411 412 413 414 415
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)
con's avatar
con committed
416
{
417 418 419 420 421 422 423 424 425 426
    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())
427
            if (const Function *fun = decl->type()->asFunctionType())  {
428
                // we are only interested in declarations of methods.
con's avatar
con committed
429
                if (fun->isSlot() && fun->isPrivate()) {
Jarek Kobus's avatar
Jarek Kobus committed
430
                    const int line = fun->line(); // this is the beginning of function name: "void ^foo(...)"
431 432
                    const int column = fun->column();
                    if (ITextEditable *editable = editableAt(docFileName, line, column)) {
Jarek Kobus's avatar
Jarek Kobus committed
433 434
                        unsigned dl, dc; // this position is the beginning of return value: "^void foo(...)"
                        decl->getStartPosition(&dl, &dc);
Tobias Hunger's avatar
Tobias Hunger committed
435
                        dc--; // if the first character in line is 'v' coming from "void" getStartPosition returns 1, not 0, so we always decrement it.
Jarek Kobus's avatar
Jarek Kobus committed
436
                        editable->gotoLine(dl, dc);
437
                        editable->position(ITextEditor::StartOfLine);
Jarek Kobus's avatar
Jarek Kobus committed
438 439
                        const QString indentation = QString(dc, QLatin1Char(' '));
                        editable->insert(declaration + indentation);
440
                    }
con's avatar
con committed
441 442 443 444 445
                    return;
                }
            }
    }

446 447 448
    // 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.

449
    if (ITextEditable *editable = editableAt(docFileName, cl->line(), cl->column())) {
450 451 452 453 454
        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)
455
            editable->insert(QLatin1String("\nprivate slots:\n    ") + declaration);
456 457 458 459
        }
    }
}

460 461 462
static Document::Ptr addDefinition(const CPlusPlus::Snapshot &docTable,
                                   const QString &headerFileName, const QString &className,
                                   const QString &functionName, int *line)
463
{
464 465 466 467 468 469 470 471
    QString definition = QLatin1String("\nvoid ");
    definition += className;
    definition += QLatin1String("::");
    definition += functionName;
    definition += QLatin1String("\n{\n");
    definition += QString(indentation, QLatin1Char(' '));
    definition += QLatin1String("\n}\n");

472
    // we find all documents which include headerFileName
473
    const QList<Document::Ptr> docList = findDocumentsIncluding(docTable, headerFileName, false);
474 475 476 477
    if (docList.isEmpty())
        return Document::Ptr();

    QFileInfo headerFI(headerFileName);
478
    const QString headerBaseName = headerFI.completeBaseName();
479 480
    foreach (const Document::Ptr &doc, docList) {
        const QFileInfo sourceFI(doc->fileName());
481 482
        // we take only those documents which have the same filename
        if (headerBaseName == sourceFI.baseName()) {
483
            if (ITextEditable *editable = editableAt(doc->fileName(), 0, 0)) {
484 485 486 487
                const QString contents = editable->contents();
                int column;
                editable->convertPosition(contents.length(), line, &column);
                editable->gotoLine(*line, column);
488
                editable->insert(definition);
489 490 491 492 493 494
                *line += 1;
            }
            return doc;
        }
    }
    return Document::Ptr();
con's avatar
con committed
495 496
}

497 498
// Insert the parameter names into a signature, "void foo(bool)" ->
// "void foo(bool checked)"
499 500
static QString addParameterNames(const QString &functionSignature, const QStringList &parameterNames)
{
501 502 503 504 505 506
    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);
507
    const QStringList arguments = argumentsString.split(QLatin1Char(','), QString::SkipEmptyParts);
508 509 510
    const int pCount = parameterNames.count();
    const int aCount = arguments.count();
    for (int i = 0; i < aCount; ++i) {
511 512 513
        if (i > 0)
            functionName += QLatin1String(", ");
        functionName += arguments.at(i);
514 515 516 517
        if (i < pCount) {
            functionName += QLatin1Char(' ');
            functionName += parameterNames.at(i);
        }
518 519 520 521 522
    }
    functionName += QLatin1Char(')');
    return functionName;
}

523 524 525 526 527 528 529 530 531 532 533
// 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)
{
534
    if (Designer::Constants::Internal::debug)
535
        qDebug() << Q_FUNC_INFO << doc->fileName() << className << maxIncludeDepth;
536 537 538 539 540 541 542
    // 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()) {
543 544
            const CPlusPlus::Snapshot::const_iterator it = docTable.find(include);
            if (it != docTable.end()) {
545 546 547 548 549 550 551 552 553
                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());
}
554

555
void QtCreatorIntegration::slotNavigateToSlot(const QString &objectName, const QString &signalSignature,
556
        const QStringList &parameterNames)
557 558 559
{
    QString errorMessage;
    if (!navigateToSlot(objectName, signalSignature, parameterNames, &errorMessage) && !errorMessage.isEmpty()) {
560
        QMessageBox::warning(m_few->designerEditor()->topLevel(), tr("Error finding/adding a slot."), errorMessage);
561 562 563
    }
}

564 565 566 567 568 569
// 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("::"));
570
    const int uiNameSpaceInsertionPos = indexOfScope >= 0 ? indexOfScope + 2 : 0;
571 572 573 574
    formObjectName.insert(uiNameSpaceInsertionPos, QLatin1String("Ui::"));
    return formObjectName;
}

575 576 577
// Goto slot invoked by the designer context menu. Either navigates
// to an existing slot function or create a new one.

578
bool QtCreatorIntegration::navigateToSlot(const QString &objectName,
579 580 581
                                          const QString &signalSignature,
                                          const QStringList &parameterNames,
                                          QString *errorMessage)
con's avatar
con committed
582
{
583 584
    const EditorData ed = m_few->activeEditor();
    QTC_ASSERT(ed, return false)
585
    const QString currentUiFile = ed.formWindowEditor->file()->fileName();
586 587 588
#if 0
    return Designer::Internal::navigateToSlot(currentUiFile, objectName, signalSignature, parameterNames, errorMessage);
#endif
589 590
    // 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().
con's avatar
con committed
591 592 593 594
    // 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);
595
    const QString uicedName = QLatin1String("ui_") + fi.completeBaseName() + QLatin1String(".h");
con's avatar
con committed
596

597 598 599 600 601 602 603
    // Retrieve code model snapshot restricted to project of ui file.
    const ProjectExplorer::Project *uiProject = ProjectExplorer::ProjectExplorerPlugin::instance()->session()->projectForFile(currentUiFile);
    if (!uiProject) {
        *errorMessage = tr("Internal error: No project could be found for %1.").arg(currentUiFile);
        return false;
    }
    CPlusPlus::Snapshot docTable = cppModelManagerInstance()->snapshot();
604 605 606
    CPlusPlus::Snapshot newDocTable;

    for  (CPlusPlus::Snapshot::iterator it = docTable.begin(); it != docTable.end(); ++it) {
607
        const ProjectExplorer::Project *project = ProjectExplorer::ProjectExplorerPlugin::instance()->session()->projectForFile(it.key());
608 609
        if (project == uiProject)
            newDocTable.insert(it.value());
610
    }
611 612 613

    docTable = newDocTable;

614
    // take all docs, find the ones that include the ui_xx.h.
615
    QList<Document::Ptr> docList = findDocumentsIncluding(docTable, uicedName, true); // change to false when we know the absolute path to generated ui_<>.h file
616

617 618
    if (Designer::Constants::Internal::debug)
        qDebug() << Q_FUNC_INFO << objectName << signalSignature << "Looking for " << uicedName << " returned " << docList.size();
619
    if (docList.isEmpty()) {
620
        *errorMessage = tr("No documents matching '%1' could be found.\nRebuilding the project might help.").arg(uicedName);
621 622
        return false;
    }
con's avatar
con committed
623

624
    QDesignerFormWindowInterface *fwi = ed.widgetHost->formWindow();
con's avatar
con committed
625

626
    const QString uiClass = uiClassName(fwi->mainContainer()->objectName());
con's avatar
con committed
627

628
    if (Designer::Constants::Internal::debug)
629 630
        qDebug() << "Checking docs for " << uiClass;

631 632
    // Find the class definition (ui class defined as member or base class)
    // in the file itself or in the directly included files (order 1).
633
    QString namespaceName;
634
    const Class *cl = 0;
635 636 637 638 639 640 641 642 643 644 645 646 647 648
    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;
    }
649

650 651
    Overview o;
    const QString className = namespaceName + o.prettyName(cl->name());
652 653
    if (Designer::Constants::Internal::debug)
        qDebug() << "Found class  " << className << doc->fileName();
654 655 656 657

    const QString functionName = QLatin1String("on_") + objectName + QLatin1Char('_') + signalSignature;
    const QString functionNameWithParameterNames = addParameterNames(functionName, parameterNames);

658 659
    if (Designer::Constants::Internal::debug)
        qDebug() << Q_FUNC_INFO << "Found " << uiClass << doc->fileName() << " checking " << functionName  << functionNameWithParameterNames;
660 661 662 663 664 665 666 667 668

    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);
con's avatar
con committed
669
        }
670 671 672 673 674 675
    } 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);
con's avatar
con committed
676 677
    }

678 679 680 681 682 683 684 685 686 687
    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;
}