cppclasswizard.cpp 14.4 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
7
**
hjk's avatar
hjk committed
8 9 10 11 12 13 14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17 18 19 20 21 22 23 24 25
** 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.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
26 27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
hjk's avatar
hjk committed
29

con's avatar
con committed
30
#include "cppclasswizard.h"
31

con's avatar
con committed
32 33
#include "cppeditorconstants.h"

34 35
#include <coreplugin/icore.h>
#include <coreplugin/mimedatabase.h>
36 37
#include <cpptools/abstracteditorsupport.h>
#include <cpptools/cpptoolsconstants.h>
38

con's avatar
con committed
39 40
#include <utils/codegeneration.h>
#include <utils/newclasswidget.h>
hjk's avatar
hjk committed
41
#include <utils/qtcassert.h>
con's avatar
con committed
42

43 44 45 46 47
#include <QDebug>
#include <QTextStream>
#include <QVBoxLayout>
#include <QSpacerItem>
#include <QWizard>
con's avatar
con committed
48 49 50 51 52 53

using namespace CppEditor;
using namespace CppEditor::Internal;

// ========= ClassNamePage =========

54
ClassNamePage::ClassNamePage(QWidget *parent) :
con's avatar
con committed
55 56 57
    QWizardPage(parent),
    m_isValid(false)
{
58
    setTitle(tr("Enter Class Name"));
con's avatar
con committed
59 60
    setSubTitle(tr("The header and source file names will be derived from the class name"));

61
    m_newClassWidget = new Utils::NewClassWidget;
con's avatar
con committed
62 63 64 65 66
    // Order, set extensions first before suggested name is derived
    m_newClassWidget->setBaseClassInputVisible(true);
    m_newClassWidget->setBaseClassChoices(QStringList() << QString()
            << QLatin1String("QObject")
            << QLatin1String("QWidget")
67
            << QLatin1String("QMainWindow")
68 69
            << QLatin1String("QDeclarativeItem")
            << QLatin1String("QQuickItem"));
con's avatar
con committed
70 71 72
    m_newClassWidget->setBaseClassEditable(true);
    m_newClassWidget->setFormInputVisible(false);
    m_newClassWidget->setNamespacesEnabled(true);
73
    m_newClassWidget->setAllowDirectories(true);
74
    m_newClassWidget->setBaseClassInputVisible(true);
con's avatar
con committed
75

76
    connect(m_newClassWidget, SIGNAL(validChanged()), this, SLOT(slotValidChanged()));
con's avatar
con committed
77

78
    QVBoxLayout *pageLayout = new QVBoxLayout(this);
con's avatar
con committed
79
    pageLayout->addWidget(m_newClassWidget);
80 81
    QSpacerItem *vSpacer = new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::Expanding);
    pageLayout->addItem(vSpacer);
82

83 84 85 86
    initParameters();
}

// Retrieve settings of CppTools plugin.
hjk's avatar
hjk committed
87
static bool lowerCaseFiles()
88
{
89 90 91 92
    QString lowerCaseSettingsKey = QLatin1String(CppTools::Constants::CPPTOOLS_SETTINGSGROUP);
    lowerCaseSettingsKey += QLatin1Char('/');
    lowerCaseSettingsKey += QLatin1String(CppTools::Constants::LOWERCASE_CPPFILES_KEY);
    const bool lowerCaseDefault = CppTools::Constants::lowerCaseFilesDefault;
hjk's avatar
hjk committed
93
    return Core::ICore::settings()->value(lowerCaseSettingsKey, QVariant(lowerCaseDefault)).toBool();
94 95 96 97 98
}

// Set up new class widget from settings
void ClassNamePage::initParameters()
{
hjk's avatar
hjk committed
99 100
    m_newClassWidget->setHeaderExtension(Core::MimeDatabase::preferredSuffixByType(QLatin1String(Constants::CPP_HEADER_MIMETYPE)));
    m_newClassWidget->setSourceExtension(Core::MimeDatabase::preferredSuffixByType(QLatin1String(Constants::CPP_SOURCE_MIMETYPE)));
hjk's avatar
hjk committed
101
    m_newClassWidget->setLowerCaseFiles(lowerCaseFiles());
102 103
}

con's avatar
con committed
104 105 106 107 108 109 110 111 112
void ClassNamePage::slotValidChanged()
{
    const bool validNow = m_newClassWidget->isValid();
    if (m_isValid != validNow) {
        m_isValid = validNow;
        emit completeChanged();
    }
}

113
CppClassWizardDialog::CppClassWizardDialog(QWidget *parent) :
114
    Utils::Wizard(parent),
115
    m_classNamePage(new ClassNamePage(this))
con's avatar
con committed
116 117
{
    setWindowTitle(tr("C++ Class Wizard"));
118 119
    const int classNameId = addPage(m_classNamePage);
    wizardProgress()->item(classNameId)->setTitle(tr("Details"));
con's avatar
con committed
120 121 122 123 124 125 126 127 128 129
}

void CppClassWizardDialog::setPath(const QString &path)
{
    m_classNamePage->newClassWidget()->setPath(path);
}

CppClassWizardParameters  CppClassWizardDialog::parameters() const
{
    CppClassWizardParameters rc;
130
    const Utils::NewClassWidget *ncw = m_classNamePage->newClassWidget();
con's avatar
con committed
131 132 133 134 135
    rc.className = ncw->className();
    rc.headerFile = ncw->headerFileName();
    rc.sourceFile = ncw->sourceFileName();
    rc.baseClass = ncw->baseClassName();
    rc.path = ncw->path();
136
    rc.classType = ncw->classType();
con's avatar
con committed
137 138 139 140 141
    return rc;
}

// ========= CppClassWizard =========

hjk's avatar
hjk committed
142
CppClassWizard::CppClassWizard()
con's avatar
con committed
143 144 145 146 147 148 149 150 151 152 153 154 155 156
{
}

QString CppClassWizard::sourceSuffix() const
{
    return preferredSuffix(QLatin1String(Constants::CPP_SOURCE_MIMETYPE));
}

QString CppClassWizard::headerSuffix() const
{
    return preferredSuffix(QLatin1String(Constants::CPP_HEADER_MIMETYPE));
}

QWizard *CppClassWizard::createWizardDialog(QWidget *parent,
157
                                            const Core::WizardDialogParameters &wizardDialogParameters) const
con's avatar
con committed
158
{
159
    CppClassWizardDialog *wizard = new CppClassWizardDialog(parent);
160
    foreach (QWizardPage *p, wizardDialogParameters.extensionPages())
161
        BaseFileWizard::applyExtensionPageShortTitle(wizard, wizard->addPage(p));
162
    wizard->setPath(wizardDialogParameters.defaultPath());
con's avatar
con committed
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
    return wizard;
}

Core::GeneratedFiles CppClassWizard::generateFiles(const QWizard *w, QString *errorMessage) const
{
    const CppClassWizardDialog *wizard = qobject_cast<const CppClassWizardDialog *>(w);
    const CppClassWizardParameters params = wizard->parameters();

    const QString sourceFileName = Core::BaseFileWizard::buildFileName(params.path, params.sourceFile, sourceSuffix());
    const QString headerFileName = Core::BaseFileWizard::buildFileName(params.path, params.headerFile, headerSuffix());

    Core::GeneratedFile sourceFile(sourceFileName);
    Core::GeneratedFile headerFile(headerFileName);

    QString header, source;
    if (!generateHeaderAndSource(params, &header, &source)) {
        *errorMessage = tr("Error while generating file contents.");
        return Core::GeneratedFiles();
    }
    headerFile.setContents(header);
183 184
    headerFile.setAttributes(Core::GeneratedFile::OpenEditorAttribute);

con's avatar
con committed
185
    sourceFile.setContents(source);
186
    sourceFile.setAttributes(Core::GeneratedFile::OpenEditorAttribute);
con's avatar
con committed
187 188 189 190 191 192 193 194
    return Core::GeneratedFiles() << headerFile << sourceFile;
}


bool CppClassWizard::generateHeaderAndSource(const CppClassWizardParameters &params,
                                             QString *header, QString *source)
{
    // TODO:
195 196
    //  Quite a bit of this code has been copied from FormClassWizardParameters::generateCpp
    //  and is duplicated in the library wizard.
197
    //  Maybe more of it could be merged into Utils.
con's avatar
con committed
198 199 200 201 202 203 204 205

    const QString indent = QString(4, QLatin1Char(' '));

    // Do we have namespaces?
    QStringList namespaceList = params.className.split(QLatin1String("::"));
    if (namespaceList.empty()) // Paranoia!
        return false;

206 207 208 209 210 211
    const QString headerLicense =
            CppTools::AbstractEditorSupport::licenseTemplate(params.headerFile,
                                                             params.className);
    const QString sourceLicense =
            CppTools::AbstractEditorSupport::licenseTemplate(params.sourceFile,
                                                             params.className);
212

con's avatar
con committed
213
    const QString unqualifiedClassName = namespaceList.takeLast();
214
    const QString guard = Utils::headerGuard(params.headerFile, namespaceList);
con's avatar
con committed
215 216 217

    // == Header file ==
    QTextStream headerStr(header);
218
    headerStr << headerLicense << "#ifndef " << guard
219
              << "\n#define " <<  guard << '\n';
con's avatar
con committed
220

221
    QRegExp qtClassExpr(QLatin1String("^Q[A-Z3].+"));
222
    QTC_CHECK(qtClassExpr.isValid());
223 224 225 226
    // Determine parent QObject type for Qt types. Provide base
    // class in case the user did not specify one.
    QString parentQObjectClass;
    bool defineQObjectMacro = false;
227
    switch (params.classType) {
228 229 230 231 232 233 234 235
    case Utils::NewClassWidget::ClassInheritsQObject:
        parentQObjectClass = QLatin1String("QObject");
        defineQObjectMacro = true;
        break;
    case Utils::NewClassWidget::ClassInheritsQWidget:
        parentQObjectClass = QLatin1String("QWidget");
        defineQObjectMacro = true;
        break;
236 237 238 239
    case Utils::NewClassWidget::ClassInheritsQDeclarativeItem:
        parentQObjectClass = QLatin1String("QDeclarativeItem");
        defineQObjectMacro = true;
        break;
240 241 242 243
    case Utils::NewClassWidget::ClassInheritsQQuickItem:
        parentQObjectClass = QLatin1String("QQuickItem");
        defineQObjectMacro = true;
        break;
244 245 246
    case Utils::NewClassWidget::NoClassType:
    case Utils::NewClassWidget::SharedDataClass:
        break;
247 248 249 250 251
    }
    const QString baseClass = params.baseClass.isEmpty()
                              && params.classType != Utils::NewClassWidget::NoClassType ?
                              parentQObjectClass : params.baseClass;
    const bool superIsQtClass = qtClassExpr.exactMatch(baseClass);
con's avatar
con committed
252 253
    if (superIsQtClass) {
        headerStr << '\n';
254
        Utils::writeIncludeFileDirective(baseClass, true, headerStr);
con's avatar
con committed
255
    }
256 257 258 259
    if (params.classType == Utils::NewClassWidget::SharedDataClass) {
        headerStr << '\n';
        Utils::writeIncludeFileDirective(QLatin1String("QSharedDataPointer"), true, headerStr);
    }
con's avatar
con committed
260

261
    const QString namespaceIndent = Utils::writeOpeningNameSpaces(namespaceList, QString(), headerStr);
con's avatar
con committed
262

263 264 265 266 267
    const QString sharedDataClass = unqualifiedClassName + QLatin1String("Data");

    if (params.classType == Utils::NewClassWidget::SharedDataClass)
        headerStr << '\n' << "class " << sharedDataClass << ";\n";

con's avatar
con committed
268
    // Class declaration
269
    headerStr << '\n' << namespaceIndent << "class " << unqualifiedClassName;
270 271
    if (!baseClass.isEmpty())
        headerStr << " : public " << baseClass << "\n";
con's avatar
con committed
272 273 274
    else
        headerStr << "\n";
    headerStr << namespaceIndent << "{\n";
275
    if (defineQObjectMacro)
276
        headerStr << namespaceIndent << indent << "Q_OBJECT\n";
con's avatar
con committed
277
    headerStr << namespaceIndent << "public:\n"
278 279 280 281 282 283 284 285
              << namespaceIndent << indent;
    // Constructor
    if (parentQObjectClass.isEmpty()) {
        headerStr << unqualifiedClassName << "();\n";
    } else {
        headerStr << "explicit " << unqualifiedClassName << '(' << parentQObjectClass
                << " *parent = 0);\n";
    }
286 287 288
    // Copy/Assignment for shared data classes.
    if (params.classType == Utils::NewClassWidget::SharedDataClass) {
        headerStr << namespaceIndent << indent
Friedemann Kleint's avatar
Friedemann Kleint committed
289
                  << unqualifiedClassName << "(const " << unqualifiedClassName << " &);\n"
290
                  << namespaceIndent << indent
Friedemann Kleint's avatar
Friedemann Kleint committed
291
                  << unqualifiedClassName << " &operator=(const " << unqualifiedClassName << " &);\n"
292 293 294
                  << namespaceIndent << indent
                  << '~' << unqualifiedClassName << "();\n";
    }
295 296
    if (defineQObjectMacro)
        headerStr << '\n' << namespaceIndent << "signals:\n\n" << namespaceIndent << "public slots:\n\n";
297 298 299 300
    if (params.classType == Utils::NewClassWidget::SharedDataClass) {
        headerStr << '\n' << namespaceIndent << "private:\n"
                  << namespaceIndent << indent << "QSharedDataPointer<" << sharedDataClass << "> data;\n";
    }
301 302
    headerStr << namespaceIndent << "};\n";

303
    Utils::writeClosingNameSpaces(namespaceList, QString(), headerStr);
con's avatar
con committed
304

305
    headerStr << '\n';
con's avatar
con committed
306 307 308 309
    headerStr << "#endif // "<<  guard << '\n';

    // == Source file ==
    QTextStream sourceStr(source);
310
    sourceStr << sourceLicense;
311
    Utils::writeIncludeFileDirective(params.headerFile, false, sourceStr);
312 313 314
    if (params.classType == Utils::NewClassWidget::SharedDataClass)
        Utils::writeIncludeFileDirective(QLatin1String("QSharedData"), true, sourceStr);

315
    Utils::writeOpeningNameSpaces(namespaceList, QString(), sourceStr);
316 317 318 319 320 321 322
    // Private class:
    if (params.classType == Utils::NewClassWidget::SharedDataClass) {
        sourceStr << '\n' << namespaceIndent << "class " << sharedDataClass
                  << " : public QSharedData {\n"
                  << namespaceIndent << "public:\n"
                  << namespaceIndent << "};\n";
    }
con's avatar
con committed
323 324

    // Constructor
325
    sourceStr << '\n' << namespaceIndent;
326
    if (parentQObjectClass.isEmpty()) {
327 328 329 330
        sourceStr << unqualifiedClassName << "::" << unqualifiedClassName << "()";
        if (params.classType == Utils::NewClassWidget::SharedDataClass)
            sourceStr << " : data(new " << sharedDataClass << ')';
        sourceStr << '\n';
331 332 333 334 335 336
    } else {
        sourceStr << unqualifiedClassName << "::" << unqualifiedClassName
                << '(' << parentQObjectClass << " *parent) :\n"
                << namespaceIndent << indent << baseClass << "(parent)\n";
    }

con's avatar
con committed
337
    sourceStr << namespaceIndent << "{\n" << namespaceIndent << "}\n";
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
    if (params.classType == Utils::NewClassWidget::SharedDataClass) {
        // Copy
        sourceStr << '\n' << namespaceIndent << unqualifiedClassName << "::" << unqualifiedClassName << "(const "
                << unqualifiedClassName << " &rhs) : data(rhs.data)\n"
                << namespaceIndent << "{\n" << namespaceIndent << "}\n\n";
        // Assignment
        sourceStr << namespaceIndent << unqualifiedClassName << " &"
                  << unqualifiedClassName << "::operator=(const " << unqualifiedClassName << " &rhs)\n"
                  << namespaceIndent << "{\n"
                  << namespaceIndent << indent << "if (this != &rhs)\n"
                  << namespaceIndent << indent << indent << "data.operator=(rhs.data);\n"
                  << namespaceIndent << indent << "return *this;\n"
                  << namespaceIndent << "}\n\n";
         // Destructor
        sourceStr << namespaceIndent << unqualifiedClassName << "::~"
                  << unqualifiedClassName << "()\n"
                  << namespaceIndent << "{\n"
                  << namespaceIndent << "}\n";
    }
357
    Utils::writeClosingNameSpaces(namespaceList, QString(), sourceStr);
con's avatar
con committed
358 359
    return true;
}