forked from qt-creator/qt-creator
CppEditor: Quick fix "Insert (Pure) Virtual Methods"
This quick fix inserts (pure) virtual functions of base classes to the current class. For selecting the functions which should be inserted and for choosing the insertion mode (only declarations or with definitions inside, outside or in the implementation file) a dialog is shown. Task-number: QTCREATORBUG-2210 Task-number: QTCREATORBUG-2692 Task-number: QTCREATORBUG-3908 Task-number: QTCREATORBUG-5868 Task-number: QTCREATORBUG-7982 Change-Id: I8e94905afcae4778986f4c3925a494e0c6b3b8ee Reviewed-by: Nikolai Kosjar <nikolai.kosjar@digia.com>
This commit is contained in:
committed by
Nikolai Kosjar
parent
7ef611047f
commit
d288e3999b
@@ -33,6 +33,8 @@
|
||||
#include "cppfunctiondecldeflink.h"
|
||||
#include "cppquickfixassistant.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
#include <cpptools/cppclassesfilter.h>
|
||||
#include <cpptools/cppcodestylesettings.h>
|
||||
#include <cpptools/cpppointerdeclarationformatter.h>
|
||||
@@ -49,16 +51,33 @@
|
||||
|
||||
#include <extensionsystem/pluginmanager.h>
|
||||
|
||||
#include <texteditor/fontsettings.h>
|
||||
#include <texteditor/texteditorsettings.h>
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QGroupBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QInputDialog>
|
||||
#include <QItemDelegate>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
#include <QPointer>
|
||||
#include <QPushButton>
|
||||
#include <QQueue>
|
||||
#include <QSharedPointer>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QStandardItemModel>
|
||||
#include <QTextBlock>
|
||||
#include <QTextCursor>
|
||||
#include <QTreeView>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include <cctype>
|
||||
|
||||
@@ -109,6 +128,8 @@ void CppEditor::Internal::registerQuickFixes(ExtensionSystem::IPlugin *plugIn)
|
||||
plugIn->addAutoReleasedObject(new MoveFuncDefToDecl);
|
||||
|
||||
plugIn->addAutoReleasedObject(new AssignToLocalVariable);
|
||||
|
||||
plugIn->addAutoReleasedObject(new InsertVirtualMethods);
|
||||
}
|
||||
|
||||
// In the following anonymous namespace all functions are collected, which could be of interest for
|
||||
@@ -2356,7 +2377,7 @@ public:
|
||||
targetFile->apply();
|
||||
}
|
||||
|
||||
static QString generateDeclaration(Function *function);
|
||||
static QString generateDeclaration(const Function *function);
|
||||
|
||||
private:
|
||||
QString m_targetFileName;
|
||||
@@ -2450,7 +2471,7 @@ void InsertDeclFromDef::match(const CppQuickFixInterface &interface, QuickFixOpe
|
||||
}
|
||||
}
|
||||
|
||||
QString InsertDeclOperation::generateDeclaration(Function *function)
|
||||
QString InsertDeclOperation::generateDeclaration(const Function *function)
|
||||
{
|
||||
Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
|
||||
oo.showFunctionSignatures = true;
|
||||
@@ -4284,3 +4305,735 @@ void AssignToLocalVariable::match(const CppQuickFixInterface &interface, QuickFi
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class InsertVirtualMethodsOp : public CppQuickFixOperation
|
||||
{
|
||||
public:
|
||||
InsertVirtualMethodsOp(const QSharedPointer<const CppQuickFixAssistInterface> &interface,
|
||||
InsertVirtualMethodsDialog *factory)
|
||||
: CppQuickFixOperation(interface, 0)
|
||||
, m_factory(factory)
|
||||
, m_classAST(0)
|
||||
, m_valid(false)
|
||||
, m_cppFileName(QString::null)
|
||||
, m_insertPosDecl(0)
|
||||
, m_insertPosOutside(0)
|
||||
, m_functionCount(0)
|
||||
, m_implementedFunctionCount(0)
|
||||
{
|
||||
setDescription(QCoreApplication::translate(
|
||||
"CppEditor::QuickFix", "Insert Virtual Functions of Base Classes"));
|
||||
|
||||
const QList<AST *> &path = interface->path();
|
||||
const int pathSize = path.size();
|
||||
if (pathSize < 2)
|
||||
return;
|
||||
|
||||
// Determine if cursor is on a class or a base class
|
||||
if (SimpleNameAST *nameAST = path.at(pathSize - 1)->asSimpleName()) {
|
||||
if (!interface->isCursorOn(nameAST))
|
||||
return;
|
||||
|
||||
if (!(m_classAST = path.at(pathSize - 2)->asClassSpecifier())) { // normal class
|
||||
int index = pathSize - 2;
|
||||
const BaseSpecifierAST *baseAST = path.at(index)->asBaseSpecifier();// simple bclass
|
||||
if (!baseAST) {
|
||||
if (index > 0 && path.at(index)->asQualifiedName()) // namespaced base class
|
||||
baseAST = path.at(--index)->asBaseSpecifier();
|
||||
}
|
||||
--index;
|
||||
if (baseAST && index >= 0)
|
||||
m_classAST = path.at(index)->asClassSpecifier();
|
||||
}
|
||||
}
|
||||
if (!m_classAST || !m_classAST->base_clause_list)
|
||||
return;
|
||||
|
||||
// Determine insert positions
|
||||
const int endOfClassAST = interface->currentFile()->endOf(m_classAST);
|
||||
m_insertPosDecl = endOfClassAST - 1; // Skip last "}"
|
||||
m_insertPosOutside = endOfClassAST + 1; // Step over ";"
|
||||
|
||||
// Determine base classes
|
||||
QList<const Class *> baseClasses;
|
||||
QQueue<ClassOrNamespace *> baseClassQueue;
|
||||
QSet<ClassOrNamespace *> visitedBaseClasses;
|
||||
if (ClassOrNamespace *clazz = interface->context().lookupType(m_classAST->symbol))
|
||||
baseClassQueue.enqueue(clazz);
|
||||
while (!baseClassQueue.isEmpty()) {
|
||||
ClassOrNamespace *clazz = baseClassQueue.dequeue();
|
||||
visitedBaseClasses.insert(clazz);
|
||||
const QList<ClassOrNamespace *> bases = clazz->usings();
|
||||
foreach (ClassOrNamespace *baseClass, bases) {
|
||||
foreach (Symbol *symbol, baseClass->symbols()) {
|
||||
Class *base = symbol->asClass();
|
||||
if (base
|
||||
&& (clazz = interface->context().lookupType(symbol))
|
||||
&& !visitedBaseClasses.contains(clazz)
|
||||
&& !baseClasses.contains(base)) {
|
||||
baseClasses << base;
|
||||
baseClassQueue.enqueue(clazz);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine virtual functions
|
||||
m_factory->classFunctionModel->clear();
|
||||
Overview printer = CppCodeStyleSettings::currentProjectCodeStyleOverview();
|
||||
printer.showFunctionSignatures = true;
|
||||
const TextEditor::FontSettings &fs =
|
||||
TextEditor::TextEditorSettings::instance()->fontSettings();
|
||||
const Format formatReimpFunc = fs.formatFor(C_DISABLED_CODE);
|
||||
foreach (const Class *clazz, baseClasses) {
|
||||
QStandardItem *itemBase = new QStandardItem(printer.prettyName(clazz->name()));
|
||||
itemBase->setData(false, InsertVirtualMethodsDialog::Implemented);
|
||||
itemBase->setData(qVariantFromValue((void *) clazz),
|
||||
InsertVirtualMethodsDialog::ClassOrFunction);
|
||||
const QString baseClassName = printer.prettyName(clazz->name());
|
||||
for (Scope::iterator it = clazz->firstMember(); it != clazz->lastMember(); ++it) {
|
||||
if (const Function *func = (*it)->type()->asFunctionType()) {
|
||||
if (!func->isVirtual())
|
||||
continue;
|
||||
|
||||
// Filter virtual destructors
|
||||
if (printer.prettyName(func->name()).startsWith(QLatin1Char('~')))
|
||||
continue;
|
||||
|
||||
// Filter OQbject's
|
||||
// - virtual const QMetaObject *metaObject() const;
|
||||
// - virtual void *qt_metacast(const char *);
|
||||
// - virtual int qt_metacall(QMetaObject::Call, int, void **);
|
||||
if (baseClassName == QLatin1String("QObject")) {
|
||||
if (printer.prettyName(func->name()) == QLatin1String("metaObject"))
|
||||
continue;
|
||||
if (printer.prettyName(func->name()) == QLatin1String("qt_metacast"))
|
||||
continue;
|
||||
if (printer.prettyName(func->name()) == QLatin1String("qt_metacall"))
|
||||
continue;
|
||||
}
|
||||
|
||||
// Do not implement existing functions inside target class
|
||||
bool funcExistsInClass = false;
|
||||
const Name *funcName = func->name();
|
||||
for (Symbol *symbol = m_classAST->symbol->find(funcName->identifier());
|
||||
symbol; symbol = symbol->next()) {
|
||||
if (!symbol->name()
|
||||
|| !funcName->identifier()->isEqualTo(symbol->identifier())) {
|
||||
continue;
|
||||
}
|
||||
if (symbol->type().isEqualTo(func->type())) {
|
||||
funcExistsInClass = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Do not show when reimplemented from an other class
|
||||
bool funcReimplemented = false;
|
||||
for (int i = baseClasses.count() - 1; i >= 0; --i) {
|
||||
const Class *prevClass = baseClasses.at(i);
|
||||
if (clazz == prevClass)
|
||||
break; // reached current class
|
||||
|
||||
for (const Symbol *symbol = prevClass->find(funcName->identifier());
|
||||
symbol; symbol = symbol->next()) {
|
||||
if (!symbol->name()
|
||||
|| !funcName->identifier()->isEqualTo(symbol->identifier())) {
|
||||
continue;
|
||||
}
|
||||
if (symbol->type().isEqualTo(func->type())) {
|
||||
funcReimplemented = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (funcReimplemented)
|
||||
continue;
|
||||
|
||||
// Construct function item
|
||||
const bool isPureVirtual = func->isPureVirtual();
|
||||
QString itemName = printer.prettyType(func->type(), func->name());
|
||||
if (isPureVirtual)
|
||||
itemName += QLatin1String(" = 0");
|
||||
const QString itemReturnTypeString = printer.prettyType(func->returnType());
|
||||
QStandardItem *funcItem = new QStandardItem(
|
||||
itemName + QLatin1String(" : ") + itemReturnTypeString);
|
||||
if (!funcExistsInClass) {
|
||||
funcItem->setCheckable(true);
|
||||
funcItem->setCheckState(Qt::Checked);
|
||||
} else {
|
||||
funcItem->setEnabled(false);
|
||||
funcItem->setData(formatReimpFunc.foreground(), Qt::ForegroundRole);
|
||||
if (formatReimpFunc.background().isValid())
|
||||
funcItem->setData(formatReimpFunc.background(), Qt::BackgroundRole);
|
||||
}
|
||||
|
||||
funcItem->setData(qVariantFromValue((void *) func),
|
||||
InsertVirtualMethodsDialog::ClassOrFunction);
|
||||
funcItem->setData(isPureVirtual, InsertVirtualMethodsDialog::PureVirtual);
|
||||
funcItem->setData(acessSpec(*it), InsertVirtualMethodsDialog::AccessSpec);
|
||||
funcItem->setData(funcExistsInClass, InsertVirtualMethodsDialog::Implemented);
|
||||
|
||||
itemBase->appendRow(funcItem);
|
||||
|
||||
// update internal counters
|
||||
if (funcExistsInClass)
|
||||
++m_implementedFunctionCount;
|
||||
else
|
||||
++m_functionCount;
|
||||
}
|
||||
}
|
||||
if (itemBase->hasChildren()) {
|
||||
for (int i = 0; i < itemBase->rowCount(); ++i) {
|
||||
if (itemBase->child(i, 0)->isCheckable()) {
|
||||
itemBase->setCheckable(true);
|
||||
itemBase->setTristate(true);
|
||||
itemBase->setCheckState(Qt::Checked);
|
||||
itemBase->setData(false, InsertVirtualMethodsDialog::Implemented);
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_factory->classFunctionModel->invisibleRootItem()->appendRow(itemBase);
|
||||
}
|
||||
}
|
||||
if (!m_factory->classFunctionModel->invisibleRootItem()->hasChildren()
|
||||
|| m_functionCount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool isHeaderFile = false;
|
||||
m_cppFileName = correspondingHeaderOrSource(interface->fileName(), &isHeaderFile);
|
||||
m_factory->setHasImplementationFile(isHeaderFile && !m_cppFileName.isEmpty());
|
||||
m_factory->setHasReimplementedFunctions(m_implementedFunctionCount != 0);
|
||||
|
||||
m_valid = true;
|
||||
}
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return m_valid;
|
||||
}
|
||||
|
||||
InsertionPointLocator::AccessSpec acessSpec(const Symbol *symbol)
|
||||
{
|
||||
const Function *func = symbol->type()->asFunctionType();
|
||||
if (!func)
|
||||
return InsertionPointLocator::Invalid;
|
||||
if (func->isSignal())
|
||||
return InsertionPointLocator::Signals;
|
||||
|
||||
InsertionPointLocator::AccessSpec spec = InsertionPointLocator::Invalid;
|
||||
if (symbol->isPrivate())
|
||||
spec = InsertionPointLocator::Private;
|
||||
else if (symbol->isProtected())
|
||||
spec = InsertionPointLocator::Protected;
|
||||
else if (symbol->isPublic())
|
||||
spec = InsertionPointLocator::Public;
|
||||
else
|
||||
return InsertionPointLocator::Invalid;
|
||||
|
||||
if (func->isSlot()) {
|
||||
switch (spec) {
|
||||
case InsertionPointLocator::Private:
|
||||
return InsertionPointLocator::PrivateSlot;
|
||||
break;
|
||||
case InsertionPointLocator::Protected:
|
||||
return InsertionPointLocator::ProtectedSlot;
|
||||
break;
|
||||
case InsertionPointLocator::Public:
|
||||
return InsertionPointLocator::PublicSlot;
|
||||
break;
|
||||
default:
|
||||
return spec;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return spec;
|
||||
}
|
||||
|
||||
void perform()
|
||||
{
|
||||
if (!m_factory->gather())
|
||||
return;
|
||||
|
||||
Core::ICore::settings()->setValue(
|
||||
QLatin1String("QuickFix/InsertVirtualMethods/insertKeywordVirtual"),
|
||||
m_factory->insertKeywordVirtual());
|
||||
Core::ICore::settings()->setValue(
|
||||
QLatin1String("QuickFix/InsertVirtualMethods/implementationMode"),
|
||||
m_factory->implementationMode());
|
||||
Core::ICore::settings()->setValue(
|
||||
QLatin1String("QuickFix/InsertVirtualMethods/hideReimplementedFunctions"),
|
||||
m_factory->hideReimplementedFunctions());
|
||||
|
||||
// Insert declarations (and definition if InsideClass)
|
||||
Overview printer = CppCodeStyleSettings::currentProjectCodeStyleOverview();
|
||||
printer.showFunctionSignatures = true;
|
||||
printer.showReturnTypes = true;
|
||||
printer.showArgumentNames = true;
|
||||
ChangeSet headerChangeSet;
|
||||
for (int i = 0; i < m_factory->classFunctionModel->rowCount(); ++i) {
|
||||
const QStandardItem *parent =
|
||||
m_factory->classFunctionModel->invisibleRootItem()->child(i, 0);
|
||||
if (!parent->isCheckable() || parent->checkState() == Qt::Unchecked)
|
||||
continue;
|
||||
const Class *clazz = (const Class *)
|
||||
parent->data(InsertVirtualMethodsDialog::ClassOrFunction).value<void *>();
|
||||
|
||||
// Add comment
|
||||
const QString comment = QLatin1String("\n// ") + printer.prettyName(clazz->name()) +
|
||||
QLatin1String(" interface\n");
|
||||
headerChangeSet.insert(m_insertPosDecl, comment);
|
||||
|
||||
// Insert Declarations (+ definitions)
|
||||
QString lastAccessSpecString;
|
||||
for (int j = 0; j < parent->rowCount(); ++j) {
|
||||
const QStandardItem *item = parent->child(j, 0);
|
||||
if (!item->isCheckable() || item->checkState() == Qt::Unchecked)
|
||||
continue;
|
||||
const Function *func = (const Function *)
|
||||
item->data(InsertVirtualMethodsDialog::ClassOrFunction).value<void *>();
|
||||
|
||||
// Construct declaration
|
||||
QString declaration = InsertDeclOperation::generateDeclaration(func);
|
||||
if (m_factory->insertKeywordVirtual())
|
||||
declaration = QLatin1String("virtual ") + declaration;
|
||||
if (m_factory->implementationMode() & InsertVirtualMethodsDialog::ModeInsideClass)
|
||||
declaration.replace(declaration.size() - 2, 2, QLatin1String("\n{\n}\n"));
|
||||
const InsertionPointLocator::AccessSpec spec =
|
||||
static_cast<InsertionPointLocator::AccessSpec>(
|
||||
item->data(InsertVirtualMethodsDialog::AccessSpec).toInt());
|
||||
const QString accessSpecString = InsertionPointLocator::accessSpecToString(spec);
|
||||
if (accessSpecString != lastAccessSpecString) {
|
||||
declaration = accessSpecString + declaration;
|
||||
if (!lastAccessSpecString.isEmpty()) // separate if not direct after the comment
|
||||
declaration = QLatin1String("\n") + declaration;
|
||||
lastAccessSpecString = accessSpecString;
|
||||
}
|
||||
headerChangeSet.insert(m_insertPosDecl, declaration);
|
||||
}
|
||||
}
|
||||
|
||||
// Insert outside class
|
||||
const QString filename = assistInterface()->currentFile()->fileName();
|
||||
const CppRefactoringChanges refactoring(assistInterface()->snapshot());
|
||||
const CppRefactoringFilePtr headerFile = refactoring.file(filename);
|
||||
const Document::Ptr headerDoc = headerFile->cppDocument();
|
||||
Class *targetClass = m_classAST->symbol;
|
||||
if (m_factory->implementationMode() & InsertVirtualMethodsDialog::ModeOutsideClass) {
|
||||
// make target lookup context
|
||||
unsigned line, column;
|
||||
headerDoc->translationUnit()->getPosition(m_insertPosOutside, &line, &column);
|
||||
Scope *targetScope = headerDoc->scopeAt(line, column);
|
||||
const LookupContext targetContext(headerDoc, assistInterface()->snapshot());
|
||||
ClassOrNamespace *targetCoN = targetContext.lookupType(targetScope);
|
||||
if (!targetCoN)
|
||||
targetCoN = targetContext.globalNamespace();
|
||||
|
||||
// setup rewriting to get minimally qualified names
|
||||
SubstitutionEnvironment env;
|
||||
env.setContext(assistInterface()->context());
|
||||
env.switchScope(targetClass);
|
||||
UseMinimalNames q(targetCoN);
|
||||
env.enter(&q);
|
||||
Control *control = assistInterface()->context().control().data();
|
||||
const QString fullClassName = printer.prettyName(LookupContext::minimalName(
|
||||
targetClass, targetCoN, control));
|
||||
|
||||
for (int i = 0; i < m_factory->classFunctionModel->rowCount(); ++i) {
|
||||
const QStandardItem *parent =
|
||||
m_factory->classFunctionModel->invisibleRootItem()->child(i, 0);
|
||||
if (!parent->isCheckable() || parent->checkState() == Qt::Unchecked)
|
||||
continue;
|
||||
|
||||
for (int j = 0; j < parent->rowCount(); ++j) {
|
||||
const QStandardItem *item = parent->child(j, 0);
|
||||
if (!item->isCheckable() || item->checkState() == Qt::Unchecked)
|
||||
continue;
|
||||
const Function *func = (const Function *)
|
||||
item->data(InsertVirtualMethodsDialog::ClassOrFunction).value<void *>();
|
||||
|
||||
// rewrite the function type and name
|
||||
const FullySpecifiedType tn = rewriteType(func->type(), &env, control);
|
||||
const QString name = fullClassName + QLatin1String("::") +
|
||||
printer.prettyName(func->name());
|
||||
const QString defText = printer.prettyType(tn, name) + QLatin1String("\n{\n}");
|
||||
|
||||
headerChangeSet.insert(m_insertPosOutside, QLatin1String("\n\n") + defText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write header file
|
||||
headerFile->setChangeSet(headerChangeSet);
|
||||
headerFile->appendIndentRange(ChangeSet::Range(m_insertPosDecl, m_insertPosDecl + 1));
|
||||
headerFile->setOpenEditor(true, m_insertPosDecl);
|
||||
headerFile->apply();
|
||||
|
||||
// Insert in implementation file
|
||||
if (m_factory->implementationMode() & InsertVirtualMethodsDialog::ModeImplementationFile) {
|
||||
const Symbol *symbol = headerFile->cppDocument()->lastVisibleSymbolAt(
|
||||
targetClass->line(), targetClass->column());
|
||||
if (!symbol)
|
||||
return;
|
||||
const Class *clazz = symbol->asClass();
|
||||
if (!clazz)
|
||||
return;
|
||||
|
||||
CppRefactoringFilePtr implementationFile = refactoring.file(m_cppFileName);
|
||||
ChangeSet implementationChangeSet;
|
||||
const int insertPos = qMax(0, implementationFile->document()->characterCount() - 1);
|
||||
|
||||
// make target lookup context
|
||||
Document::Ptr implementationDoc = implementationFile->cppDocument();
|
||||
unsigned line, column;
|
||||
implementationDoc->translationUnit()->getPosition(insertPos, &line, &column);
|
||||
Scope *targetScope = implementationDoc->scopeAt(line, column);
|
||||
const LookupContext targetContext(implementationDoc, assistInterface()->snapshot());
|
||||
ClassOrNamespace *targetCoN = targetContext.lookupType(targetScope);
|
||||
if (!targetCoN)
|
||||
targetCoN = targetContext.globalNamespace();
|
||||
|
||||
// Loop through inserted declarations
|
||||
for (unsigned i = targetClass->memberCount(); i < clazz->memberCount(); ++i) {
|
||||
Declaration *decl = clazz->memberAt(i)->asDeclaration();
|
||||
if (!decl)
|
||||
continue;
|
||||
|
||||
// setup rewriting to get minimally qualified names
|
||||
SubstitutionEnvironment env;
|
||||
env.setContext(assistInterface()->context());
|
||||
env.switchScope(decl->enclosingScope());
|
||||
UseMinimalNames q(targetCoN);
|
||||
env.enter(&q);
|
||||
Control *control = assistInterface()->context().control().data();
|
||||
|
||||
// rewrite the function type and name + create definition
|
||||
const FullySpecifiedType type = rewriteType(decl->type(), &env, control);
|
||||
const QString name = printer.prettyName(
|
||||
LookupContext::minimalName(decl, targetCoN, control));
|
||||
const QString defText = printer.prettyType(type, name) + QLatin1String("\n{\n}");
|
||||
|
||||
implementationChangeSet.insert(insertPos, QLatin1String("\n\n") + defText);
|
||||
}
|
||||
|
||||
implementationFile->setChangeSet(implementationChangeSet);
|
||||
implementationFile->appendIndentRange(ChangeSet::Range(insertPos, insertPos + 1));
|
||||
implementationFile->apply();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
InsertVirtualMethodsDialog *m_factory;
|
||||
const ClassSpecifierAST *m_classAST;
|
||||
bool m_valid;
|
||||
QString m_cppFileName;
|
||||
int m_insertPosDecl;
|
||||
int m_insertPosOutside;
|
||||
unsigned m_functionCount;
|
||||
unsigned m_implementedFunctionCount;
|
||||
};
|
||||
|
||||
class InsertVirtualMethodsFilterModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
InsertVirtualMethodsFilterModel(QObject *parent = 0)
|
||||
: QSortFilterProxyModel(parent)
|
||||
, m_hideReimplemented(false)
|
||||
{}
|
||||
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
||||
{
|
||||
QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
|
||||
|
||||
// Handle base class
|
||||
if (!sourceParent.isValid()) {
|
||||
// check if any child is valid
|
||||
if (!sourceModel()->hasChildren(index))
|
||||
return false;
|
||||
if (!m_hideReimplemented)
|
||||
return true;
|
||||
|
||||
for (int i = 0; i < sourceModel()->rowCount(index); ++i) {
|
||||
const QModelIndex child = sourceModel()->index(i, 0, index);
|
||||
if (!child.data(InsertVirtualMethodsDialog::Implemented).toBool())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_hideReimplemented)
|
||||
return !index.data(InsertVirtualMethodsDialog::Implemented).toBool();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hideReimplemented() const
|
||||
{
|
||||
return m_hideReimplemented;
|
||||
}
|
||||
|
||||
void setHideReimplementedFunctions(bool show)
|
||||
{
|
||||
m_hideReimplemented = show;
|
||||
invalidateFilter();
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_hideReimplemented;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
InsertVirtualMethodsDialog::InsertVirtualMethodsDialog(QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, m_view(0)
|
||||
, m_hideReimplementedFunctions(0)
|
||||
, m_insertMode(0)
|
||||
, m_virtualKeyword(0)
|
||||
, m_buttons(0)
|
||||
, m_hasImplementationFile(false)
|
||||
, m_hasReimplementedFunctions(false)
|
||||
, m_implementationMode(ModeOnlyDeclarations)
|
||||
, m_insertKeywordVirtual(false)
|
||||
, classFunctionModel(new QStandardItemModel(this))
|
||||
, classFunctionFilterModel(new InsertVirtualMethodsFilterModel(this))
|
||||
{
|
||||
classFunctionFilterModel->setSourceModel(classFunctionModel);
|
||||
}
|
||||
|
||||
void InsertVirtualMethodsDialog::initGui()
|
||||
{
|
||||
if (m_view)
|
||||
return;
|
||||
|
||||
setWindowTitle(tr("Insert Virtual Functions"));
|
||||
QVBoxLayout *globalVerticalLayout = new QVBoxLayout;
|
||||
|
||||
// View
|
||||
QGroupBox *groupBoxView = new QGroupBox(tr("&Functions to insert:"), this);
|
||||
QVBoxLayout *groupBoxViewLayout = new QVBoxLayout(groupBoxView);
|
||||
m_view = new QTreeView(this);
|
||||
m_view->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
m_view->setHeaderHidden(true);
|
||||
groupBoxViewLayout->addWidget(m_view);
|
||||
m_hideReimplementedFunctions =
|
||||
new QCheckBox(tr("&Hide already implemented functions of current class"), this);
|
||||
groupBoxViewLayout->addWidget(m_hideReimplementedFunctions);
|
||||
|
||||
// Insertion options
|
||||
QGroupBox *groupBoxImplementation = new QGroupBox(tr("&Insertion options:"), this);
|
||||
QVBoxLayout *groupBoxImplementationLayout = new QVBoxLayout(groupBoxImplementation);
|
||||
m_insertMode = new QComboBox(this);
|
||||
m_insertMode->addItem(tr("Insert only declarations"), ModeOnlyDeclarations);
|
||||
m_insertMode->addItem(tr("Insert definitions inside class"), ModeInsideClass);
|
||||
m_insertMode->addItem(tr("Insert definitions outside class"), ModeOutsideClass);
|
||||
m_insertMode->addItem(tr("Insert definitions in implementation file"), ModeImplementationFile);
|
||||
m_virtualKeyword = new QCheckBox(tr("&Add keyword 'virtual' to function declaration"), this);
|
||||
groupBoxImplementationLayout->addWidget(m_insertMode);
|
||||
groupBoxImplementationLayout->addWidget(m_virtualKeyword);
|
||||
groupBoxImplementationLayout->addStretch(99);
|
||||
|
||||
// Bottom button box
|
||||
m_buttons = new QDialogButtonBox(this);
|
||||
m_buttons->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(m_buttons, SIGNAL(accepted()), this, SLOT(accept()));
|
||||
connect(m_buttons, SIGNAL(rejected()), this, SLOT(reject()));
|
||||
|
||||
globalVerticalLayout->addWidget(groupBoxView, 9);
|
||||
globalVerticalLayout->addWidget(groupBoxImplementation, 0);
|
||||
globalVerticalLayout->addWidget(m_buttons, 0);
|
||||
setLayout(globalVerticalLayout);
|
||||
|
||||
connect(classFunctionModel, SIGNAL(itemChanged(QStandardItem *)),
|
||||
this, SLOT(updateCheckBoxes(QStandardItem *)));
|
||||
connect(m_hideReimplementedFunctions, SIGNAL(toggled(bool)),
|
||||
this, SLOT(setHideReimplementedFunctions(bool)));
|
||||
}
|
||||
|
||||
void InsertVirtualMethodsDialog::initData()
|
||||
{
|
||||
m_insertKeywordVirtual = Core::ICore::settings()->value(
|
||||
QLatin1String("QuickFix/InsertVirtualMethods/insertKeywordVirtual"),
|
||||
false).toBool();
|
||||
m_implementationMode = static_cast<InsertVirtualMethodsDialog::ImplementationMode>(
|
||||
Core::ICore::settings()->value(
|
||||
QLatin1String("QuickFix/InsertVirtualMethods/implementationMode"), 1).toInt());
|
||||
m_hideReimplementedFunctions->setChecked(
|
||||
Core::ICore::settings()->value(
|
||||
QLatin1String("QuickFix/InsertVirtualMethods/hideReimplementedFunctions"),
|
||||
false).toBool());
|
||||
|
||||
m_view->setModel(classFunctionFilterModel);
|
||||
m_expansionStateNormal.clear();
|
||||
m_expansionStateReimp.clear();
|
||||
m_hideReimplementedFunctions->setVisible(m_hasReimplementedFunctions);
|
||||
m_virtualKeyword->setChecked(m_insertKeywordVirtual);
|
||||
m_insertMode->setCurrentIndex(m_insertMode->findData(m_implementationMode));
|
||||
|
||||
setHideReimplementedFunctions(m_hideReimplementedFunctions->isChecked());
|
||||
|
||||
if (m_hasImplementationFile) {
|
||||
if (m_insertMode->count() == 3) {
|
||||
m_insertMode->addItem(tr("Insert definitions in implementation file"),
|
||||
ModeImplementationFile);
|
||||
}
|
||||
} else {
|
||||
if (m_insertMode->count() == 4)
|
||||
m_insertMode->removeItem(3);
|
||||
}
|
||||
}
|
||||
|
||||
bool InsertVirtualMethodsDialog::gather()
|
||||
{
|
||||
initGui();
|
||||
initData();
|
||||
|
||||
// Expand the dialog a little bit
|
||||
adjustSize();
|
||||
resize(size() * 1.5);
|
||||
|
||||
QPointer<InsertVirtualMethodsDialog> that(this);
|
||||
const int ret = exec();
|
||||
if (!that)
|
||||
return false;
|
||||
|
||||
m_implementationMode = implementationMode();
|
||||
m_insertKeywordVirtual = insertKeywordVirtual();
|
||||
return (ret == QDialog::Accepted);
|
||||
}
|
||||
|
||||
InsertVirtualMethodsDialog::ImplementationMode
|
||||
InsertVirtualMethodsDialog::implementationMode() const
|
||||
{
|
||||
return static_cast<InsertVirtualMethodsDialog::ImplementationMode>(
|
||||
m_insertMode->itemData(m_insertMode->currentIndex()).toInt());
|
||||
}
|
||||
|
||||
void InsertVirtualMethodsDialog::setImplementationsMode(InsertVirtualMethodsDialog::ImplementationMode mode)
|
||||
{
|
||||
m_implementationMode = mode;
|
||||
}
|
||||
|
||||
bool InsertVirtualMethodsDialog::insertKeywordVirtual() const
|
||||
{
|
||||
return m_virtualKeyword->isChecked();
|
||||
}
|
||||
|
||||
void InsertVirtualMethodsDialog::setInsertKeywordVirtual(bool insert)
|
||||
{
|
||||
m_insertKeywordVirtual = insert;
|
||||
}
|
||||
|
||||
void InsertVirtualMethodsDialog::setHasImplementationFile(bool file)
|
||||
{
|
||||
m_hasImplementationFile = file;
|
||||
}
|
||||
|
||||
void InsertVirtualMethodsDialog::setHasReimplementedFunctions(bool functions)
|
||||
{
|
||||
m_hasReimplementedFunctions = functions;
|
||||
}
|
||||
|
||||
bool InsertVirtualMethodsDialog::hideReimplementedFunctions() const
|
||||
{
|
||||
// Safty check necessary because of testing class
|
||||
return (m_hideReimplementedFunctions && m_hideReimplementedFunctions->isChecked());
|
||||
}
|
||||
|
||||
void InsertVirtualMethodsDialog::updateCheckBoxes(QStandardItem *item)
|
||||
{
|
||||
if (item->hasChildren()) {
|
||||
const Qt::CheckState state = item->checkState();
|
||||
if (!item->isCheckable() || state == Qt::PartiallyChecked)
|
||||
return;
|
||||
for (int i = 0; i < item->rowCount(); ++i) {
|
||||
if (item->child(i, 0)->isCheckable())
|
||||
item->child(i, 0)->setCheckState(state);
|
||||
}
|
||||
} else {
|
||||
QStandardItem *parent = item->parent();
|
||||
if (!parent->isCheckable())
|
||||
return;
|
||||
const Qt::CheckState state = item->checkState();
|
||||
for (int i = 0; i < parent->rowCount(); ++i) {
|
||||
if (state != parent->child(i, 0)->checkState()) {
|
||||
parent->setCheckState(Qt::PartiallyChecked);
|
||||
return;
|
||||
}
|
||||
}
|
||||
parent->setCheckState(state);
|
||||
}
|
||||
}
|
||||
|
||||
void InsertVirtualMethodsDialog::setHideReimplementedFunctions(bool hide)
|
||||
{
|
||||
InsertVirtualMethodsFilterModel *model =
|
||||
qobject_cast<InsertVirtualMethodsFilterModel *>(classFunctionFilterModel);
|
||||
|
||||
if (m_expansionStateNormal.isEmpty() && m_expansionStateReimp.isEmpty()) {
|
||||
model->setHideReimplementedFunctions(hide);
|
||||
m_view->expandAll();
|
||||
saveExpansionState();
|
||||
return;
|
||||
}
|
||||
|
||||
if (model->hideReimplemented() == hide)
|
||||
return;
|
||||
|
||||
saveExpansionState();
|
||||
model->setHideReimplementedFunctions(hide);
|
||||
restoreExpansionState();
|
||||
}
|
||||
|
||||
void InsertVirtualMethodsDialog::saveExpansionState()
|
||||
{
|
||||
InsertVirtualMethodsFilterModel *model =
|
||||
qobject_cast<InsertVirtualMethodsFilterModel *>(classFunctionFilterModel);
|
||||
|
||||
QList<bool> &state = model->hideReimplemented() ? m_expansionStateReimp
|
||||
: m_expansionStateNormal;
|
||||
state.clear();
|
||||
for (int i = 0; i < model->rowCount(); ++i)
|
||||
state << m_view->isExpanded(model->index(i, 0));
|
||||
}
|
||||
|
||||
void InsertVirtualMethodsDialog::restoreExpansionState()
|
||||
{
|
||||
InsertVirtualMethodsFilterModel *model =
|
||||
qobject_cast<InsertVirtualMethodsFilterModel *>(classFunctionFilterModel);
|
||||
|
||||
const QList<bool> &state = model->hideReimplemented() ? m_expansionStateReimp
|
||||
: m_expansionStateNormal;
|
||||
const int stateCount = state.count();
|
||||
for (int i = 0; i < model->rowCount(); ++i) {
|
||||
if (i < stateCount && !state.at(i)) {
|
||||
m_view->collapse(model->index(i, 0));
|
||||
continue;
|
||||
}
|
||||
m_view->expand(model->index(i, 0));
|
||||
}
|
||||
}
|
||||
|
||||
InsertVirtualMethods::InsertVirtualMethods(InsertVirtualMethodsDialog *dialog)
|
||||
: m_dialog(dialog)
|
||||
{}
|
||||
|
||||
InsertVirtualMethods::~InsertVirtualMethods()
|
||||
{
|
||||
if (m_dialog)
|
||||
m_dialog->deleteLater();
|
||||
}
|
||||
|
||||
void InsertVirtualMethods::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
|
||||
{
|
||||
InsertVirtualMethodsOp *op = new InsertVirtualMethodsOp(interface, m_dialog);
|
||||
if (op->isValid())
|
||||
result.append(QuickFixOperation::Ptr(op));
|
||||
else
|
||||
delete op;
|
||||
}
|
||||
|
||||
#include "cppquickfixes.moc"
|
||||
|
||||
Reference in New Issue
Block a user