diff --git a/umbrello/codegenerators/cpp/cppwriter.cpp b/umbrello/codegenerators/cpp/cppwriter.cpp index af5535aa4..6499c602a 100644 --- a/umbrello/codegenerators/cpp/cppwriter.cpp +++ b/umbrello/codegenerators/cpp/cppwriter.cpp @@ -1,1422 +1,1441 @@ /*************************************************************************** * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * copyright (C) 2003 Brian Thomas * * * * copyright (C) 2004-2014 Umbrello UML Modeller Authors * * * ***************************************************************************/ // own header #include "cppwriter.h" // app includes #include "association.h" #include "classifier.h" #include "codegen_utils.h" #include "datatype.h" #include "debug_utils.h" #include "model_utils.h" #include "uml.h" #include "umldoc.h" #include "operation.h" #include "template.h" #include "umltemplatelist.h" #include "umlclassifierlistitemlist.h" #include "classifierlistitem.h" #include "codegenerationpolicy.h" #include "enumliteral.h" // qt includes #include #include #include // 3-14-2003: this code developed from the javawriter with parts of the // original cppwriter by Luis De la Parra Blum /** * Constructor, initialises a couple of variables. */ CppWriter::CppWriter() + : m_stringIncludeRequired(false) { // Probably we could resolve this better through the use of templates, // but it is a quick n dirty fix for the timebeing.. until codegeneration // template system is in place. // You can insert code here. 3 general variables exist: "%VARNAME%" // allows you to specify where the vector variable should be in your code, // and "%ITEMCLASS%", if needed, where the class of the item is declared. VECTOR_METHOD_APPEND = QLatin1String("%VARNAME%.push_back(add_object);"); // for std::vector VECTOR_METHOD_REMOVE = QString(QLatin1String("int i, size = %VARNAME%.size();\nfor (i = 0; i < size; ++i) {\n\t%ITEMCLASS% item = %VARNAME%.at(i);\n\tif(item == remove_object) {\n\t\t%1<%ITEMCLASS%>::iterator it = %VARNAME%.begin() + i;\n\t\t%VARNAME%.erase(it);\n\t\treturn;\n\t}\n }")).arg(policyExt()->getVectorClassName()); // for std::vector VECTOR_METHOD_INIT.clear(); // nothing to be done /* VECTOR_METHOD_APPEND = QLatin1String("%VARNAME%.append(&add_object);"); // Qt lib implementation VECTOR_METHOD_REMOVE = QLatin1String("%VARNAME%.removeRef(&remove_object);"); // Qt lib implementation VECTOR_METHOD_INIT = QLatin1String("%VARNAME%.setAutoDelete(false);"); // Qt library */ OBJECT_METHOD_INIT = QLatin1String("%VARNAME% = new %ITEMCLASS%();"); // Qt library // boolean config params INLINE_ASSOCIATION_METHODS = false; } /** * Destructor, empty. */ CppWriter::~CppWriter() { } /** * Returns "C++". * @return the programming language identifier */ Uml::ProgrammingLanguage::Enum CppWriter::language() const { return Uml::ProgrammingLanguage::Cpp; } /** * Return the policy object. */ CPPCodeGenerationPolicy *CppWriter::policyExt() { return static_cast(UMLApp::app()->policyExt()); } /** * Call this method to generate cpp code for a UMLClassifier. * @param c the class to generate code for */ void CppWriter::writeClass(UMLClassifier *c) { if (!c) { uDebug() << "Cannot write class of NULL concept!"; return; } QFile fileh, filecpp; // find an appropriate name for our file fileName_ = findFileName(c, QLatin1String(".h")); if (fileName_.isEmpty()) { emit codeGenerated(c, false); return; } className_ = cleanName(c->name()); if (c->visibility() != Uml::Visibility::Implementation) { if (!openFile(fileh, fileName_)) { emit codeGenerated(c, false); return; } // write Header file writeHeaderFile(c, fileh); fileh.close(); } // Determine whether the implementation file is required. // (It is not required if the class is an enumeration.) bool need_impl = true; if (c->baseType() == UMLObject::ot_Enum || c->isInterface()) { need_impl = false; } if (need_impl) { fileName_.replace(QRegExp(QLatin1String(".h$")), QLatin1String(".cpp")); if (!openFile(filecpp, fileName_)) { emit codeGenerated(c, false); return; } // write Source file writeSourceFile(c, filecpp); filecpp.close(); } emit codeGenerated(c, true); } /** * Write the header file for this classifier. */ void CppWriter::writeHeaderFile (UMLClassifier *c, QFile &file) { // open stream for writing - QTextStream h (&file); + QTextStream h(&file); // up the indent level to one m_indentLevel = 1; // write header blurb QString str = getHeadingFile(QLatin1String(".h")); if (!str.isEmpty()) { str.replace(QRegExp(QLatin1String("%filename%")), fileName_ + QLatin1String(".h")); str.replace(QRegExp(QLatin1String("%filepath%")), file.fileName()); h << str<< m_endl; } // Write the hash define stuff to prevent multiple parsing/inclusion of header QString hashDefine = className_.toUpper().simplified().replace(QRegExp(QLatin1String(" ")), QLatin1String("_")); writeBlankLine(h); h << "#ifndef "<< hashDefine << "_H" << m_endl; h << "#define "<< hashDefine << "_H" << m_endl; + h << m_endl; - writeClassDecl(c, h); + QString s; + QTextStream t(&s); + writeClassDecl(c, t); + writeIncludes(c, h); + h << s; // last thing..close our hashdefine h << m_endl << "#endif // " << hashDefine << "_H" << m_endl; } void CppWriter::writeHeaderAccessorMethodDecl(UMLClassifier *c, Uml::Visibility::Enum permitScope, QTextStream &stream) { // attributes writeHeaderAttributeAccessorMethods(c, permitScope, true, stream); // write static attributes first writeHeaderAttributeAccessorMethods(c, permitScope, false, stream); // associations writeAssociationMethods(c->getSpecificAssocs(Uml::AssociationType::Association), permitScope, true, INLINE_ASSOCIATION_METHODS, true, c->id(), stream); writeAssociationMethods(c->getUniAssociationToBeImplemented(), permitScope, true, INLINE_ASSOCIATION_METHODS, true, c->id(), stream); writeAssociationMethods(c->getAggregations(), permitScope, true, INLINE_ASSOCIATION_METHODS, true, c->id(), stream); writeAssociationMethods(c->getCompositions(), permitScope, true, INLINE_ASSOCIATION_METHODS, false, c->id(), stream); writeBlankLine(stream); } /** * Write out fields and operations for this class selected on a particular * visibility. */ void CppWriter::writeHeaderFieldDecl(UMLClassifier *c, Uml::Visibility::Enum permitScope, QTextStream &stream) { // attributes writeAttributeDecls(c, permitScope, true, stream); // write static attributes first writeAttributeDecls(c, permitScope, false, stream); // associations writeAssociationDecls(c->getSpecificAssocs(Uml::AssociationType::Association), permitScope, c->id(), stream); writeAssociationDecls(c->getUniAssociationToBeImplemented(), permitScope, c->id(), stream); writeAssociationDecls(c->getAggregations(), permitScope, c->id(), stream); writeAssociationDecls(c->getCompositions(), permitScope, c->id(), stream); } /** * Write the source code body file for this classifier. */ void CppWriter::writeSourceFile(UMLClassifier *c, QFile &file) { // open stream for writing QTextStream cpp (&file); // set the starting indentation at zero m_indentLevel = 0; //try to find a heading file (license, coments, etc) QString str; str = getHeadingFile(QLatin1String(".cpp")); if (!str.isEmpty()) { str.replace(QRegExp(QLatin1String("%filename%")), fileName_ + QLatin1String(".cpp")); str.replace(QRegExp(QLatin1String("%filepath%")), file.fileName()); cpp << str << m_endl; } // IMPORT statements // Q: Why all utils? Isnt just List and Vector the only classes we are using? // Our import *should* also look at operations, and check that objects being // used arent in another package (and thus need to be explicitly imported here). cpp << "#include \"" << className_ << ".h\"" << m_endl; writeBlankLine(cpp); if (c->visibility() == Uml::Visibility::Implementation) { writeClassDecl(c, cpp); } // Start body of class // Constructors: anything we more we need to do here ? // if (!c->isInterface()) writeConstructorMethods(c, cpp); // METHODS // // write comment for section IF needed QString indnt = indent(); if (forceDoc() || c->hasAccessorMethods() || c->hasOperationMethods()) { writeComment(QLatin1String(" "), indnt, cpp); writeComment(QLatin1String("Methods"), indnt, cpp); writeComment(QLatin1String(" "), indnt, cpp); writeBlankLine(cpp); writeBlankLine(cpp); } // write comment for sub-section IF needed if (forceDoc() || c->hasAccessorMethods()) { writeComment(QLatin1String("Accessor methods"), indnt, cpp); writeComment(QLatin1String(" "), indnt, cpp); writeBlankLine(cpp); } // Accessor methods for attributes const bool bInlineAccessors = policyExt()->getAccessorsAreInline(); if (!bInlineAccessors && c->hasAttributes()) { writeAttributeMethods(c->getAttributeListStatic(Uml::Visibility::Public), Uml::Visibility::Public, false, true, !bInlineAccessors, cpp); writeAttributeMethods(c->getAttributeList(Uml::Visibility::Public), Uml::Visibility::Public, false, false, !bInlineAccessors, cpp); writeAttributeMethods(c->getAttributeListStatic(Uml::Visibility::Protected), Uml::Visibility::Protected, false, true, !bInlineAccessors, cpp); writeAttributeMethods(c->getAttributeList(Uml::Visibility::Protected), Uml::Visibility::Protected, false, false, !bInlineAccessors, cpp); writeAttributeMethods(c->getAttributeListStatic(Uml::Visibility::Private), Uml::Visibility::Private, false, true, !bInlineAccessors, cpp); writeAttributeMethods(c->getAttributeList(Uml::Visibility::Private), Uml::Visibility::Private, false, false, !bInlineAccessors, cpp); } // accessor methods for associations // public writeAssociationMethods(c->getSpecificAssocs(Uml::AssociationType::Association), Uml::Visibility::Public, false, !INLINE_ASSOCIATION_METHODS, true, c->id(), cpp); writeAssociationMethods(c->getUniAssociationToBeImplemented(), Uml::Visibility::Public, false, !INLINE_ASSOCIATION_METHODS, true, c->id(), cpp); writeAssociationMethods(c->getAggregations(), Uml::Visibility::Public, false, !INLINE_ASSOCIATION_METHODS, true, c->id(), cpp); writeAssociationMethods(c->getCompositions(), Uml::Visibility::Public, false, !INLINE_ASSOCIATION_METHODS, true, c->id(), cpp); // protected writeAssociationMethods(c->getSpecificAssocs(Uml::AssociationType::Association), Uml::Visibility::Protected, false, !INLINE_ASSOCIATION_METHODS, true, c->id(), cpp); writeAssociationMethods(c->getUniAssociationToBeImplemented(), Uml::Visibility::Protected, false, !INLINE_ASSOCIATION_METHODS, true, c->id(), cpp); writeAssociationMethods(c->getAggregations(), Uml::Visibility::Protected, false, !INLINE_ASSOCIATION_METHODS, true, c->id(), cpp); writeAssociationMethods(c->getCompositions(), Uml::Visibility::Protected, false, !INLINE_ASSOCIATION_METHODS, true, c->id(), cpp); // private writeAssociationMethods(c->getSpecificAssocs(Uml::AssociationType::Association), Uml::Visibility::Private, false, !INLINE_ASSOCIATION_METHODS, true, c->id(), cpp); writeAssociationMethods(c->getUniAssociationToBeImplemented(), Uml::Visibility::Private, false, !INLINE_ASSOCIATION_METHODS, true, c->id(), cpp); writeAssociationMethods(c->getAggregations(), Uml::Visibility::Private, false, !INLINE_ASSOCIATION_METHODS, true, c->id(), cpp); writeAssociationMethods(c->getCompositions(), Uml::Visibility::Private, false, !INLINE_ASSOCIATION_METHODS, true, c->id(), cpp); writeBlankLine(cpp); // Other operation methods -- all other operations are now written // // write comment for sub-section IF needed if (forceDoc() || c->hasOperationMethods()) { writeComment(QLatin1String("Other methods"), indnt, cpp); writeComment(QLatin1String(" "), indnt, cpp); writeBlankLine(cpp); } if (!policyExt()->getOperationsAreInline()) { writeOperations(c, false, Uml::Visibility::Public, cpp); writeOperations(c, false, Uml::Visibility::Protected, cpp); writeOperations(c, false, Uml::Visibility::Private, cpp); } // Yep, bringing up the back of the bus, our initialization method for attributes writeInitAttributeMethod(c, cpp); writeBlankLine(cpp); } /** - * Writes class's documentation to the class header - * "public abstract class Foo extents {". + * write includes + * @param c uml classifier + * @param stream text stream */ -void CppWriter::writeClassDecl(UMLClassifier *c, QTextStream &cpp) +void CppWriter::writeIncludes(UMLClassifier *c, QTextStream &stream) { UMLClassifierList superclasses = c->getSuperClasses(); foreach (UMLClassifier* classifier, superclasses) { QString headerName = findFileName(classifier, QLatin1String(".h")); if (!headerName.isEmpty()) { - cpp << "#include \"" << headerName << "\"" << m_endl; + stream << "#include \"" << headerName << "\"" << m_endl; } } + if (superclasses.size() > 0) + writeBlankLine(stream); + + if (m_stringIncludeRequired) + stream << "#include " << policyExt()->getStringClassNameInclude() << m_endl; - writeBlankLine(cpp); - cpp << "#include " << policyExt()->getStringClassNameInclude() << m_endl; if (c->hasVectorFields()) - { - cpp << "#include " << policyExt()->getVectorClassNameInclude() << m_endl; - writeBlankLine(cpp); - } + stream << "#include " << policyExt()->getVectorClassNameInclude() << m_endl; + if (m_stringIncludeRequired || c->hasVectorFields()) + writeBlankLine(stream); +} + +/** + * Writes class's documentation to the class header + * "public abstract class Foo extents {". + */ +void CppWriter::writeClassDecl(UMLClassifier *c, QTextStream &cpp) +{ if (c->hasAssociations()) { // write all includes we need to include other classes, that arent us. printAssociationIncludeDecl (c->getSpecificAssocs(Uml::AssociationType::Association), c->id(), cpp); printAssociationIncludeDecl (c->getUniAssociationToBeImplemented(), c->id(), cpp); printAssociationIncludeDecl (c->getAggregations(), c->id(), cpp); printAssociationIncludeDecl (c->getCompositions(), c->id(), cpp); writeBlankLine(cpp); } - foreach (UMLClassifier* classifier, superclasses) { + foreach (UMLClassifier* classifier, c->getSuperClasses()) { if (classifier->package()!=c->package() && !classifier->package().isEmpty()) { cpp << "using " << cleanName(classifier->package()) << "::" << cleanName(classifier->name()) << ";" << m_endl; } } if (!c->package().isEmpty() && policyExt()->getPackageIsNamespace()) cpp << m_endl << "namespace " << cleanName(c->package()) << " {" << m_endl << m_endl; //Write class Documentation if there is somthing or if force option if (forceDoc() || !c->doc().isEmpty()) { cpp << m_endl << "/**" << m_endl; cpp << " * class " << className_ << m_endl; cpp << formatDoc(c->doc(), QLatin1String(" * ")); cpp << " */"; writeBlankLine(cpp); writeBlankLine(cpp); } //check if class is abstract and / or has abstract methods if ((c->isAbstract() || c->isInterface()) && !hasAbstractOps(c)) cpp << "/******************************* Abstract Class ****************************" << m_endl << className_ << " does not have any pure virtual methods, but its author" << m_endl << " defined it as an abstract class, so you should not use it directly." << m_endl << " Inherit from it instead and create only objects from the derived classes" << m_endl << "*****************************************************************************/" << m_endl << m_endl; if (c->baseType() == UMLObject::ot_Enum) { UMLClassifierListItemList litList = c->getFilteredList(UMLObject::ot_EnumLiteral); uint i = 0; cpp << "enum " << className_ << " {" << m_endl; foreach (UMLClassifierListItem* lit, litList) { UMLEnumLiteral *el = static_cast(lit); QString enumLiteral = cleanName(lit->name()); cpp << indent() << enumLiteral; if (!el->value().isEmpty()) cpp << " = " << el->value(); if (++i < (uint)litList.count()) cpp << ","; cpp << m_endl; } cpp << m_endl << "};" << m_endl; // end of class header if (!c->package().isEmpty() && policyExt()->getPackageIsNamespace()) cpp << "} // end of package namespace" << m_endl; return; } // Generate template parameters. UMLTemplateList template_params = c->getTemplateList(); if (template_params.count()) { cpp << "template<"; for (UMLTemplateListIt tlit(template_params); tlit.hasNext();) { UMLTemplate* t = tlit.next(); QString formalName = t->name(); QString typeName = t->getTypeName(); cpp << typeName << " " << formalName; if (tlit.hasNext()) { tlit.next(); cpp << ", "; } } cpp << ">" << m_endl; } cpp << "class " << className_; uint numOfSuperClasses = c->getSuperClasses().count(); if (numOfSuperClasses > 0) cpp << " : "; uint i = 0; foreach (UMLClassifier* superClass, c->getSuperClasses()) { i++; if (superClass->isAbstract() || superClass->isInterface()) cpp << "virtual "; cpp << "public " << cleanName(superClass->name()); if (i < numOfSuperClasses) cpp << ", "; } cpp << m_endl << "{" << m_endl; // begin the body of the class // // write out field and operations decl grouped by visibility // QString s; QTextStream tmp(&s); // PUBLIC attribs/methods writeDataTypes(c, Uml::Visibility::Public, tmp); // for public: constructors are first ops we print out if (!c->isInterface()) writeConstructorDecls(tmp); writeHeaderFieldDecl(c, Uml::Visibility::Public, tmp); writeHeaderAccessorMethodDecl(c, Uml::Visibility::Public, tmp); writeOperations(c, true, Uml::Visibility::Public, tmp); s = s.trimmed(); if (!s.isEmpty()) { cpp << "public:" << m_endl << indent() << s << m_endl << m_endl; } s.clear(); // PROTECTED attribs/methods writeDataTypes(c, Uml::Visibility::Protected, tmp); writeHeaderFieldDecl(c, Uml::Visibility::Protected, tmp); writeHeaderAccessorMethodDecl(c, Uml::Visibility::Protected, tmp); writeOperations(c, true, Uml::Visibility::Protected, tmp); s = s.trimmed(); if (!s.isEmpty()) { cpp << "protected:" << m_endl << indent() << s << m_endl << m_endl; } s.clear(); // PRIVATE attribs/methods writeDataTypes(c, Uml::Visibility::Private, tmp); writeHeaderFieldDecl(c, Uml::Visibility::Private, tmp); writeHeaderAccessorMethodDecl(c, Uml::Visibility::Private, tmp); writeOperations(c, true, Uml::Visibility::Private, tmp); writeInitAttributeDecl(c, tmp); // this is always private, used by constructors to initialize class s = s.trimmed(); if (!s.isEmpty()) { cpp << "private:" << m_endl << indent() << s << m_endl; } // end of class header cpp << m_endl << "};" << m_endl; // end of class namespace, if any if (!c->package().isEmpty() && policyExt()->getPackageIsNamespace()) cpp << "} // end of package namespace" << m_endl; } /** * Writes the attribute declarations. * @param c the classifier * @param visibility the visibility of the attribs to print out * @param writeStatic whether to write static or non-static attributes out * @param stream text stream */ void CppWriter::writeAttributeDecls (UMLClassifier *c, Uml::Visibility::Enum visibility, bool writeStatic, QTextStream &stream) { if (c->isInterface()) return; UMLAttributeList list; if (writeStatic) list = c->getAttributeListStatic(visibility); else list = c->getAttributeList(visibility); //write documentation if (forceDoc() || list.count() > 0) { QString strVis = Codegen_Utils::capitalizeFirstLetter(Uml::Visibility::toString(visibility)); QString strStatic = writeStatic ? QLatin1String("Static ") : QString(); writeComment(strStatic + strVis + QLatin1String(" attributes"), indent(), stream); writeComment(QLatin1String(" "), indent(), stream); writeBlankLine(stream); } if (list.count() > 0) { // write attrib declarations now // bool isFirstAttrib = true; QString documentation; foreach (UMLAttribute* at, list) { // bool noPriorDocExists = documentation.isEmpty(); documentation = at->doc(); // add a line for code clarity IF PRIOR attrib has comment on it // OR this line has documentation // if (!isFirstAttrib && (!documentation.isEmpty()||!noPriorDocExists)) // writeBlankLine(stream); // isFirstAttrib = false; QString varName = getAttributeVariableName(at); QString staticValue = at->isStatic() ? QLatin1String("static ") : QString(); QString typeName = fixTypeName(at->getTypeName()); int i = typeName.indexOf(QLatin1Char('[')); if (i > -1) { varName += typeName.mid(i); typeName = typeName.left(i); } if (!documentation.isEmpty()) writeComment(documentation, indent(), stream); stream << indent() << staticValue << typeName << " " << varName << ";" << m_endl; } /* if (list.count() > 0) writeBlankLine(stream); */ } } void CppWriter::writeHeaderAttributeAccessorMethods (UMLClassifier *c, Uml::Visibility::Enum visibility, bool writeStatic, QTextStream &stream) { // check the current policy about generate accessors as public UMLAttributeList list; if (writeStatic) list = c->getAttributeListStatic(visibility); else list = c->getAttributeList(visibility); // write accessor methods for attribs we found writeAttributeMethods(list, visibility, true, false, policyExt()->getAccessorsAreInline(), stream); } /** * This is for writing *source* or *header* file attribute methods. * Calls @ref writeSingleAttributeAccessorMethods() on each of the attributes in attribs list. */ void CppWriter::writeAttributeMethods(UMLAttributeList attribs, Uml::Visibility::Enum visibility, bool isHeaderMethod, bool isStatic, bool writeMethodBody, QTextStream &stream) { if (!policyExt()->getAutoGenerateAccessors()) return; if (forceDoc() || attribs.count() > 0) { QString strVis = Codegen_Utils::capitalizeFirstLetter(Uml::Visibility::toString(visibility)); QString strStatic = (isStatic ? QLatin1String(" static") : QString()); writeBlankLine(stream); writeComment(strVis + strStatic + QLatin1String(" attribute accessor methods"), indent(), stream); writeComment(QLatin1String(" "), indent(), stream); writeBlankLine(stream); } // return now if NO attributes to work on if (attribs.count() == 0) return; foreach (UMLAttribute* at, attribs) { QString varName = getAttributeVariableName(at); QString methodBaseName = getAttributeMethodBaseName(cleanName(at->name())); methodBaseName = methodBaseName.trimmed(); writeSingleAttributeAccessorMethods(at->getTypeName(), varName, methodBaseName, at->doc(), Uml::Changeability::Changeable, isHeaderMethod, at->isStatic(), writeMethodBody, stream); } } /** * Writes a // style comment. */ void CppWriter::writeComment(const QString &comment, const QString &myIndent, QTextStream &cpp) { // in the case we have several line comment.. // NOTE: this part of the method has the problem of adopting UNIX newline, // need to resolve for using with MAC/WinDoze eventually I assume if (comment.contains(QRegExp(QLatin1String("\n")))) { QStringList lines = comment.split(QLatin1Char('\n')); for (int i= 0; i < lines.count(); ++i) { cpp << myIndent << QLatin1String("// ") << lines[i] << m_endl; } } else { // this should be more fancy in the future, breaking it up into 80 char // lines so that it doesn't look too bad cpp << myIndent << QLatin1String("// ")<< comment << m_endl; } } /** * Writes a documentation comment. */ void CppWriter::writeDocumentation(QString header, QString body, QString end, QTextStream &cpp) { writeBlankLine(cpp); QString indnt = indent(); cpp << indnt << QLatin1String("/**") << m_endl; if (!header.isEmpty()) cpp << formatDoc(header, indnt + QLatin1String(" * ")); if (!body.isEmpty()) cpp << formatDoc(body, indnt + QLatin1String(" * ")); if (!end.isEmpty()) { QStringList lines = end.split(QLatin1Char('\n')); for (int i = 0; i < lines.count(); ++i) { cpp << formatDoc(lines[i], indnt + QLatin1String(" * ")); } } cpp << indnt << QLatin1String(" */") << m_endl; } /** * Searches a list of associations for appropriate ones to write out as attributes. */ void CppWriter::writeAssociationDecls(UMLAssociationList associations, Uml::Visibility::Enum permitScope, Uml::ID::Type id, QTextStream &h) { if (forceSections() || !associations.isEmpty()) { bool printRoleA = false, printRoleB = false; foreach (UMLAssociation *a, associations) { // it may seem counter intuitive, but you want to insert the role of the // *other* class into *this* class. if (a->getObjectId(Uml::RoleType::A) == id && !a->getRoleName(Uml::RoleType::B).isEmpty()) printRoleB = true; if (a->getObjectId(Uml::RoleType::B) == id && !a->getRoleName(Uml::RoleType::A).isEmpty()) printRoleA = true; // First: we insert documentaion for association IF it has either role AND some documentation (!) if ((printRoleA || printRoleB) && !(a->doc().isEmpty())) writeComment(a->doc(), indent(), h); // print RoleB decl if (printRoleB && a->visibility(Uml::RoleType::B) == permitScope) { QString fieldClassName = cleanName(umlObjectName(a->getObject(Uml::RoleType::B))); writeAssociationRoleDecl(fieldClassName, a->getRoleName(Uml::RoleType::B), a->getMultiplicity(Uml::RoleType::B), a->getRoleDoc(Uml::RoleType::B), h); } // print RoleA decl if (printRoleA && a->visibility(Uml::RoleType::A) == permitScope) { QString fieldClassName = cleanName(umlObjectName(a->getObject(Uml::RoleType::A))); writeAssociationRoleDecl(fieldClassName, a->getRoleName(Uml::RoleType::A), a->getMultiplicity(Uml::RoleType::A), a->getRoleDoc(Uml::RoleType::A), h); } // reset for next association in our loop printRoleA = false; printRoleB = false; } } } /** * Writes out an association as an attribute using Vector. */ void CppWriter::writeAssociationRoleDecl(QString fieldClassName, QString roleName, QString multi, QString doc, QTextStream &stream) { // ONLY write out IF there is a rolename given // otherwise it is not meant to be declared in the code if (roleName.isEmpty()) return; QString indnt = indent(); // always put space between this and prior decl, if any writeBlankLine(stream); if (!doc.isEmpty()) writeComment(doc, indnt, stream); // declare the association based on whether it is this a single variable // or a List (Vector). One day this will be done correctly with special // multiplicity object that we don't have to figure out what it means via regex. if (multi.isEmpty() || multi.contains(QRegExp(QLatin1String("^[01]$")))) { QString fieldVarName = QLatin1String("m_") + roleName.toLower(); // record this for later consideration of initialization IF the // multi value requires 1 of these objects if (ObjectFieldVariables.indexOf(fieldVarName) == -1 && multi.contains(QRegExp(QLatin1String("^1$")))) { // ugh. UGLY. Storing variable name and its class in pairs. ObjectFieldVariables.append(fieldVarName); ObjectFieldVariables.append(fieldClassName); } stream << indnt << fieldClassName << " * " << fieldVarName << ";" << m_endl; } else { QString fieldVarName = QLatin1String("m_") + roleName.toLower() + QLatin1String("Vector"); // record unique occurrences for later when we want to check // for initialization of this vector if (VectorFieldVariables.indexOf(fieldVarName) == -1) VectorFieldVariables.append(fieldVarName); stream << indnt << policyExt()->getVectorClassName() << "<" << fieldClassName << "*"; stream << "> " << fieldVarName << ";" << m_endl; } } /** * Calls @ref writeAssociationRoleMethod() on each of the associations in the given list * for either source or header files. */ void CppWriter::writeAssociationMethods (UMLAssociationList associations, Uml::Visibility::Enum permitVisib, bool isHeaderMethod, bool writeMethodBody, bool writePointerVar, Uml::ID::Type myID, QTextStream &stream) { if (forceSections() || !associations.isEmpty()) { foreach (UMLAssociation *a, associations) { // insert the methods to access the role of the other // class in the code of this one if (a->getObjectId(Uml::RoleType::A) == myID && a->visibility(Uml::RoleType::B) == permitVisib) { // only write out IF there is a rolename given if (!a->getRoleName(Uml::RoleType::B).isEmpty()) { QString fieldClassName = umlObjectName(a->getObject(Uml::RoleType::B)) + (writePointerVar ? QLatin1String(" *") : QString()); writeAssociationRoleMethod(fieldClassName, isHeaderMethod, writeMethodBody, a->getRoleName(Uml::RoleType::B), a->getMultiplicity(Uml::RoleType::B), a->getRoleDoc(Uml::RoleType::B), a->changeability(Uml::RoleType::B), stream); } } if (a->getObjectId(Uml::RoleType::B) == myID && a->visibility(Uml::RoleType::A) == permitVisib) { // only write out IF there is a rolename given if (!a->getRoleName(Uml::RoleType::A).isEmpty()) { QString fieldClassName = umlObjectName(a->getObject(Uml::RoleType::A)) + (writePointerVar ? QLatin1String(" *") : QString()); writeAssociationRoleMethod(fieldClassName, isHeaderMethod, writeMethodBody, a->getRoleName(Uml::RoleType::A), a->getMultiplicity(Uml::RoleType::A), a->getRoleDoc(Uml::RoleType::A), a->changeability(Uml::RoleType::A), stream); } } } } } /** * Calls @ref writeSingleAttributeAccessorMethods() or @ref * writeVectorAttributeAccessorMethods() on the association * role. */ void CppWriter::writeAssociationRoleMethod (const QString &fieldClassName, bool isHeaderMethod, bool writeMethodBody, const QString &roleName, const QString &multi, const QString &description, Uml::Changeability::Enum change, QTextStream &stream) { if (multi.isEmpty() || multi.contains(QRegExp(QLatin1String("^[01]$")))) { QString fieldVarName = QLatin1String("m_") + roleName.toLower(); writeSingleAttributeAccessorMethods(fieldClassName, fieldVarName, roleName, description, change, isHeaderMethod, false, writeMethodBody, stream); } else { QString fieldVarName = QLatin1String("m_") + roleName.toLower() + QLatin1String("Vector"); writeVectorAttributeAccessorMethods(fieldClassName, fieldVarName, roleName, description, change, isHeaderMethod, writeMethodBody, stream); } } /** * Writes addFoo() and removeFoo() accessor methods for the Vector attribute. */ void CppWriter::writeVectorAttributeAccessorMethods ( const QString &fieldClassName, const QString &fieldVarName, const QString &fieldName, const QString &description, Uml::Changeability::Enum changeType, bool isHeaderMethod, bool writeMethodBody, QTextStream &stream) { QString className = fixTypeName(fieldClassName); QString fldName = Codegen_Utils::capitalizeFirstLetter(fieldName); QString indnt = indent(); // ONLY IF changeability is NOT Frozen if (changeType != Uml::Changeability::Frozen) { writeDocumentation(QLatin1String("Add a ") + fldName + QLatin1String(" object to the ") + fieldVarName + QLatin1String(" List"), description, QString(), stream); stream << indnt << QLatin1String("void "); if (!isHeaderMethod) stream << className_ << "::"; stream << "add" << fldName << " (" << className << " add_object)"; if (writeMethodBody) { QString method = VECTOR_METHOD_APPEND; method.replace(QRegExp(QLatin1String("%VARNAME%")), fieldVarName); method.replace(QRegExp(QLatin1String("%VECTORTYPENAME%")), policyExt()->getVectorClassName()); method.replace(QRegExp(QLatin1String("%ITEMCLASS%")), className); stream << indnt << " {" << m_endl; m_indentLevel++; printTextAsSeparateLinesWithIndent(method, indent(), stream); m_indentLevel--; stream << indnt << "}" << m_endl; } else stream << ";" << m_endl; } // ONLY IF changeability is Changeable if (changeType == Uml::Changeability::Changeable) { writeDocumentation(QLatin1String("Remove a ") + fldName + QLatin1String(" object from ") + fieldVarName + QLatin1String(" List"), description, QString(), stream); stream << indnt << "void "; if (!isHeaderMethod) stream << className_ << "::"; stream << "remove" << fldName << " (" << className << " remove_object)"; if (writeMethodBody) { QString method = VECTOR_METHOD_REMOVE; method.replace(QRegExp(QLatin1String("%VARNAME%")), fieldVarName); method.replace(QRegExp(QLatin1String("%VECTORTYPENAME%")), policyExt()->getVectorClassName()); method.replace(QRegExp(QLatin1String("%ITEMCLASS%")), className); stream << indnt << " {" << m_endl; m_indentLevel++; printTextAsSeparateLinesWithIndent(method, indent(), stream); m_indentLevel--; stream << indnt << "}" << m_endl; } else stream << ";" << m_endl; } // always allow getting the list of stuff QString returnVarName = policyExt()->getVectorClassName() + QLatin1Char('<') + className + QLatin1Char('>'); writeDocumentation(QLatin1String("Get the list of ") + fldName + QLatin1String(" objects held by ") + fieldVarName, description, policyExt()->getDocToolTag() + QLatin1String("return ") + returnVarName + QLatin1String(" list of ") + fldName + QLatin1String(" objects held by ") + fieldVarName, stream); stream << indnt << returnVarName << " "; if (!isHeaderMethod) stream << className_ << "::"; stream << "get" << fldName << "List()"; if (writeMethodBody) { stream << indnt << " {" << m_endl; m_indentLevel++; stream << indent() << "return " << fieldVarName << ";" << m_endl; m_indentLevel--; stream << indnt << "}" << m_endl; } else { stream << ";" << m_endl; } } /** * Writes getFoo() and setFoo() accessor methods for the attribute. */ void CppWriter::writeSingleAttributeAccessorMethods( const QString& fieldClassName, const QString& fieldVarName, const QString& fieldName, const QString &description, Uml::Changeability::Enum change, bool isHeaderMethod, bool isStatic, bool writeMethodBody, QTextStream &stream) { // DON'T write this IF it is a source method AND writeMethodBody is "false" if (!isHeaderMethod && !writeMethodBody) return; QString className = fixTypeName(fieldClassName); QString indnt = indent(); QString varName = QLatin1String("value"); QString fullVarName = varName; int i = className.indexOf(QLatin1Char('[')); bool isArrayType = false; if (i > -1) { fullVarName += className.mid(i); className = className.left(i); isArrayType = true; } QString fldName = fieldName; if (policyExt()->getAccessorMethodsStartWithUpperCase()) fldName = Codegen_Utils::capitalizeFirstLetter(fieldName); // set method if (change == Uml::Changeability::Changeable) { writeDocumentation(QLatin1String("Set the value of ") + fieldVarName, description, policyExt()->getDocToolTag() + QString(QLatin1String("param %1 the new value of ")).arg(varName) + fieldVarName, stream); stream << indnt << "void "; if (!isHeaderMethod) stream << className_ << "::"; stream << "set" << setFldName << "(" << className << " " << fullVarName << ")"; if (writeMethodBody) { stream << m_endl << indnt << "{" << m_endl; m_indentLevel++; stream << indent(); m_indentLevel--; if (isStatic) stream << className_ << "::"; if (isArrayType) stream << "*" << fieldVarName << " = *" << varName << ";" << m_endl; else stream << fieldVarName << " = " << fullVarName << ";" << m_endl; stream << indnt << "}" << m_endl; } else stream << ";" << m_endl; } if (i > -1) className += QLatin1String("*"); // get method writeDocumentation(QLatin1String("Get the value of ") + fieldVarName, description, policyExt()->getDocToolTag() + QLatin1String("return the value of ") + fieldVarName, stream); stream << indnt << className << " "; if (!isHeaderMethod) stream << className_ << "::"; if (policyExt()->getGetterWithGetPrefix()) stream << "get" << fldName << "()"; else stream << fieldName << "()"; if (writeMethodBody) { stream << m_endl << indnt << "{" << m_endl; m_indentLevel++; stream << indent() << "return "; m_indentLevel--; if (isStatic) stream << className_ << "::"; stream << fieldVarName << ";" << m_endl; stream << indnt << "}"; } else stream << ";" << m_endl; writeBlankLine(stream); } /** * Writes the comment and class constructor declaration or methods. * Note: * One day, this should print out non-empty constructor operations too. */ void CppWriter::writeConstructorDecls(QTextStream &stream) { const bool generateEmptyConstructors = UMLApp::app()->commonPolicy()->getAutoGenerateConstructors(); if (forceDoc() || generateEmptyConstructors) { writeComment(QLatin1String("Constructors/Destructors"), indent(), stream); writeComment(QLatin1String(" "), indent(), stream); writeBlankLine(stream); } if (!generateEmptyConstructors) return; writeDocumentation(QString(), QLatin1String("Empty Constructor"), QString(), stream); stream << indent() << className_ << "();" << m_endl; writeDocumentation(QString(), QLatin1String("Empty Destructor"), QString(), stream); stream << indent(); stream << "virtual ~" << className_ << "();" << m_endl; writeBlankLine(stream); } /** * If needed, write out the declaration for the method to initialize attributes of our class. */ void CppWriter::writeInitAttributeDecl(UMLClassifier * c, QTextStream &stream) { if (UMLApp::app()->commonPolicy()->getAutoGenerateConstructors() && c->hasAttributes()) stream << indent() << "void initAttributes();" << m_endl; } /** * If needed, write out the method to initialize attributes of our class. */ void CppWriter::writeInitAttributeMethod(UMLClassifier * c, QTextStream &stream) { // only need to do this under certain conditions if (!UMLApp::app()->commonPolicy()->getAutoGenerateConstructors() || !c->hasAttributes()) return; QString indnt = indent(); stream << indnt << "void " << className_ << "::" << "initAttributes()" << m_endl << "{" << m_endl; m_indentLevel++; // first, initiation of fields derived from attributes UMLAttributeList atl = c->getAttributeList(); foreach (UMLAttribute* at, atl) { if (!at->getInitialValue().isEmpty()) { QString varName = getAttributeVariableName(at); stream << indent() << varName << " = " << at->getInitialValue() << ";" << m_endl; } } // Now initialize the association related fields (e.g. vectors) if (!VECTOR_METHOD_INIT.isEmpty()) { QStringList::Iterator it; for (it = VectorFieldVariables.begin(); it != VectorFieldVariables.end(); ++it) { QString fieldVarName = *it; QString method = VECTOR_METHOD_INIT; method.replace(QRegExp(QLatin1String("%VARNAME%")), fieldVarName); method.replace(QRegExp(QLatin1String("%VECTORTYPENAME%")), policyExt()->getVectorClassName()); stream << indent() << method << m_endl; } } if (!OBJECT_METHOD_INIT.isEmpty()) { QStringList::Iterator it; for (it = ObjectFieldVariables.begin(); it != ObjectFieldVariables.end(); ++it) { QString fieldVarName = *it; it++; QString fieldClassName = *it; QString method = OBJECT_METHOD_INIT; method.replace(QRegExp(QLatin1String("%VARNAME%")), fieldVarName); method.replace(QRegExp(QLatin1String("%ITEMCLASS%")), fieldClassName); stream << indent() << method << m_endl; } } // clean up ObjectFieldVariables.clear(); // shouldn't be needed? VectorFieldVariables.clear(); // shouldn't be needed? m_indentLevel--; stream << indnt << "}" << m_endl; } // one day, this should print out non-empty constructor operations too. void CppWriter::writeConstructorMethods(UMLClassifier * c, QTextStream &stream) { const bool generateEmptyConstructors = UMLApp::app()->commonPolicy()->getAutoGenerateConstructors(); if (forceDoc() || generateEmptyConstructors) { writeComment(QLatin1String("Constructors/Destructors"), indent(), stream); writeComment(QLatin1String(" "), indent(), stream); writeBlankLine(stream); } if (!generateEmptyConstructors) return; // empty constructor QString indnt = indent(); stream << indent() << className_ << "::" << className_ << "()" << m_endl << indent() << "{" << m_endl; m_indentLevel++; if (c->hasAttributes()) stream << indent() << "initAttributes();" << m_endl; m_indentLevel--; stream << indent() << "}" << m_endl; writeBlankLine(stream); // empty destructor stream << indent() << className_ << "::~" << className_ << "()" << m_endl << indent() << "{" << m_endl << indent() << "}" << m_endl; writeBlankLine(stream); } /** * Write all datatypes for a given class. * @param c the class for which we are generating code * @param permitScope what type of method to write (by Scope) * @param cpp the stream associated with the output file */ void CppWriter::writeDataTypes(UMLClassifier *c, Uml::Visibility::Enum permitScope, QTextStream &stream) { foreach (UMLObject* o, c->containedObjects()) { uIgnoreZeroPointer(o); if (o->visibility() != permitScope) continue; if (!o->isUMLDatatype()) continue; UMLDatatype *d = o->asUMLDatatype(); if (d && d->isReference() && d->originType()) { stream << indent() << "typedef " << d->originType()->name() << " " << d->name() << ";" << m_endl; } } } /** * Replaces `string' with STRING_TYPENAME. */ QString CppWriter::fixTypeName(const QString &string) { if (string.isEmpty()) return QLatin1String("void"); - if (string == QLatin1String("string")) + if (string == QLatin1String("string")) { + m_stringIncludeRequired = true; return policyExt()->getStringClassName(); + } return string; } /** * Write all ooperations for a given class. * @param c the class for which we are generating code * @param isHeaderMethod true when writing to a header file, false for body file * @param permitScope what type of method to write (by Scope) * @param cpp the stream associated with the output file */ void CppWriter::writeOperations(UMLClassifier *c, bool isHeaderMethod, Uml::Visibility::Enum permitScope, QTextStream &cpp) { UMLOperationList oplist; //sort operations by scope first and see if there are abstract methods UMLOperationList inputlist = c->getOpList(); foreach (UMLOperation* op, inputlist) { if (op->visibility() == permitScope) { oplist.append(op); } } // do people REALLY want these comments? Hmm. /* if (forceSections() || oppub.count()) { writeComment(QLatin1String("public operations"), indent(), cpp); writeBlankLine(cpp); } */ writeOperations(c, oplist, isHeaderMethod, cpp); } /** * Write a list of operations for a given class. * Writes operation in either header or source file. * @param c the class for which we are generating code * @param oplist the list of operations you want to write * @param isHeaderMethod true when writing to a header file, false for body file * @param cpp the stream associated with the output file */ void CppWriter::writeOperations(UMLClassifier *c, UMLOperationList &oplist, bool isHeaderMethod, QTextStream &cpp) { const bool generateEmptyConstructors = UMLApp::app()->commonPolicy()->getAutoGenerateConstructors(); // generate method decl for each operation given foreach (UMLOperation* op, oplist) { QString doc; // buffer for documentation QString methodReturnType; UMLAttributeList atl = op->getParmList(); // method parameters if (op->isConstructorOperation()) { if (generateEmptyConstructors && atl.count() == 0) continue; // it's already been written, see writeConstructor{Decls, Methods} } else if (op->isDestructorOperation()) { if (generateEmptyConstructors) continue; // it's already been written, see writeConstructor{Decls, Methods} } else { methodReturnType = fixTypeName(op->getTypeName()); if (methodReturnType != QLatin1String("void")) doc += policyExt()->getDocToolTag() + QLatin1String("return ") + methodReturnType + QLatin1Char('\n'); } QString str; if (op->isAbstract() || c->isInterface()) { if (isHeaderMethod) { // declare abstract method as 'virtual' str += QLatin1String("virtual "); } } // static declaration for header file if (isHeaderMethod && op->isStatic()) str += QLatin1String("static "); // returntype of method str += methodReturnType + QLatin1Char(' '); if (!isHeaderMethod) str += className_ + QLatin1String("::"); str += cleanName(op->name()) + QLatin1String("("); // generate parameters uint j = 0; for (UMLAttributeListIt atlIt(atl); atlIt.hasNext() ; ++j) { UMLAttribute* at = atlIt.next(); QString typeName = fixTypeName(at->getTypeName()); QString atName = cleanName(at->name()); QString atNameType = atName; int i = typeName.indexOf(QLatin1Char('[')); if (i > -1) { atNameType += typeName.mid(i); typeName = typeName.left(i); } str += typeName + QLatin1Char(' ') + atName; const QString initVal = at->getInitialValue(); if (! initVal.isEmpty()) str += QLatin1String(" = ") + initVal; if (j < (uint)(atl.count() - 1)) str += QLatin1String(", "); doc += policyExt()->getDocToolTag() + QLatin1String("param ") + atName + QLatin1Char(' ') + at->doc() + m_endl; } doc = doc.remove(doc.size() - 1, 1); // remove last endl of comment str += QLatin1Char(')'); if (op->getConst()) str += QLatin1String(" const"); if (op->getOverride()) str += QLatin1String(" override"); if (isHeaderMethod && op->isAbstract()) { str += QLatin1String(" = 0;"); } // method body : only gets IF it is not in a header else if (isHeaderMethod && !policyExt()->getOperationsAreInline()) { str += QLatin1Char(';'); // terminate now } else { str += m_endl + indent() + QLatin1Char('{') + m_endl; QString sourceCode = op->getSourceCode(); if (sourceCode.isEmpty()) { // empty method body - TODO: throw exception } else { str += formatSourceCode(sourceCode, indent() + indent()); } str += indent() + QLatin1Char('}'); } // write it out writeDocumentation(QString(), op->doc(), doc, cpp); cpp << indent() << str << m_endl; writeBlankLine(cpp); } } /** * Intellegently print out header include/forward decl. for associated classes. * Note: * To prevent circular including when both classifiers on either end * of an association have roles we need to have forward declaration of * the other class...but only IF it is not THIS class (as could happen * in self-association relationship). */ void CppWriter::printAssociationIncludeDecl(UMLAssociationList list, Uml::ID::Type myId, QTextStream &stream) { foreach (UMLAssociation *a, list) { UMLClassifier *current = 0; bool isFirstClass = true; // only use OTHER classes (e.g. we don't need to write includes for ourselves!! // AND only IF the roleName is defined, otherwise, it is not meant to be noticed. if (a->getObjectId(Uml::RoleType::A) == myId && !a->getRoleName(Uml::RoleType::B).isEmpty()) { current = a->getObject(Uml::RoleType::B)->asUMLClassifier(); } else if (a->getObjectId(Uml::RoleType::B) == myId && !a->getRoleName(Uml::RoleType::A).isEmpty()) { current = a->getObject(Uml::RoleType::A)->asUMLClassifier(); isFirstClass = false; } // as header doc for this method indicates, we need to be a bit sophisticated on // how to declare some associations. if (current) { if (!isFirstClass && !a->getRoleName(Uml::RoleType::A).isEmpty() && !a->getRoleName(Uml::RoleType::B).isEmpty()) stream << "class " << current->name() << ";" << m_endl; // special case: use forward declaration else stream << "#include \"" << current->name() << ".h\"" << m_endl; // just the include statement } } } /** * Check that initial values of strings have quotes around them. */ QString CppWriter::fixInitialStringDeclValue(const QString &value, const QString &type) { QString val = value; // check for strings only if (!val.isEmpty() && type == policyExt()->getStringClassName()) { if (!val.startsWith(QLatin1Char('"'))) val.prepend(QLatin1Char('"')); if (!val.endsWith(QLatin1Char('"'))) val.append(QLatin1Char('"')); } return val; } /** * Returns the name of the given object (if it exists). * Note: * Methods like this _shouldn't_ be needed IF we properly did things thruought the code. */ QString CppWriter::umlObjectName(UMLObject *obj) { return (obj ? obj->name() : QLatin1String("NULL")); } /** * Write a blank line. */ void CppWriter::writeBlankLine(QTextStream &stream) { stream << m_endl; } /** * Utility method to break up a block of text, which has embedded newline chars, * and print them to a stream as separate lines of text, indented as directed. */ void CppWriter::printTextAsSeparateLinesWithIndent(const QString &text, const QString &indent, QTextStream &stream) { if (text.isEmpty()) { return; } QStringList lines = text.split(QLatin1Char('\n')); for (int i= 0; i < lines.count(); ++i) { stream << indent << lines[i] << m_endl; } } /** * Determine what the variable name of this attribute should be. */ QString CppWriter::getAttributeVariableName(UMLAttribute *at) { QString fieldName; CodeGenPolicyExt *pe = UMLApp::app()->policyExt(); CPPCodeGenerationPolicy * policy = dynamic_cast(pe); if (policy && !policy->getClassMemberPrefix().isEmpty()) fieldName = policy->getClassMemberPrefix() + cleanName(at->name()); else fieldName = cleanName(at->name()); return fieldName; } QString CppWriter::getAttributeMethodBaseName(const QString &fieldName) { QString fldName = fieldName; if (policyExt()->getRemovePrefixFromAccessorMethods()) fldName.replace(QRegExp(QLatin1String("^[a-zA-Z]_")), QLatin1String("")); return fldName; } /** * Add C++ primitives as datatypes. * @return the list of default datatypes */ QStringList CppWriter::defaultDatatypes() { return Codegen_Utils::cppDatatypes(); } /** * Get list of reserved keywords. * @return the list of reserved keywords */ QStringList CppWriter::reservedKeywords() const { return Codegen_Utils::reservedCppKeywords(); } diff --git a/umbrello/codegenerators/cpp/cppwriter.h b/umbrello/codegenerators/cpp/cppwriter.h index 47104faeb..0b17e5306 100644 --- a/umbrello/codegenerators/cpp/cppwriter.h +++ b/umbrello/codegenerators/cpp/cppwriter.h @@ -1,162 +1,162 @@ /*************************************************************************** * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * copyright (C) 2003 Brian Thomas * * * * copyright (C) 2004-2014 Umbrello UML Modeller Authors * * * ***************************************************************************/ #ifndef CPPWRITER_H #define CPPWRITER_H #include "simplecodegenerator.h" #include "cppcodegenerationpolicy.h" #include "umloperationlist.h" #include "umlattributelist.h" #include "umlassociationlist.h" class QFile; /** * Class CppWriter is a code generator for UMLClassifier objects. * Create an instance of this class, and feed it a UMLClassifier when * calling writeClass and it will generate both a header (.h) and * source (.cpp) file for that classifier. * Note: * This is the "old" code generator that does not support code editing * in the Modeller but uses significantly less file space because the * source code is not replicated in the XMI file. */ class CppWriter : public SimpleCodeGenerator { public: CppWriter(); virtual ~CppWriter(); virtual void writeClass(UMLClassifier *c); virtual Uml::ProgrammingLanguage::Enum language() const; QStringList defaultDatatypes(); virtual QStringList reservedKeywords() const; private: - + void writeIncludes(UMLClassifier *c, QTextStream &cpp); void writeClassDecl(UMLClassifier *c, QTextStream &cpp); void writeConstructorDecls(QTextStream &h); void writeConstructorMethods(UMLClassifier * c, QTextStream &cpp); // /** // * Write all field declarations, for both attributes and associations for the // * given permitted scope. // */ // void writeFieldDecl(UMLClassifier *c, Uml::Visibility::Enum permitScope, QTextStream &stream); // /** // * Write all method declarations, for attributes and associations // * for the given permitted scope. // */ // void writeAccessorMethodDecl(UMLClassifier *c, Uml::Visibility::Enum permitScope, QTextStream &stream); void writeOperations(UMLClassifier *c, bool isHeaderMethod, Uml::Visibility::Enum permitScope, QTextStream &cpp); void writeOperations(UMLClassifier *c, UMLOperationList &oplist, bool isHeaderMethod, QTextStream &cpp); // /** // * Write all attributes for a given class. // * @param c the class for which we are generating code // * @param j the stream associated with the output file // */ // void writeAttributes(UMLClassifier *c, QTextStream &j); void writeAttributeDecls(UMLClassifier *c, Uml::Visibility::Enum visibility, bool writeStatic, QTextStream &stream); void writeHeaderFieldDecl(UMLClassifier *c, Uml::Visibility::Enum permitVisibility, QTextStream &stream); void writeHeaderAttributeAccessorMethods(UMLClassifier *c, Uml::Visibility::Enum visibility, bool writeStatic, QTextStream &stream); void writeHeaderAttributeAccessorMethodDecls(UMLClassifier *c, Uml::Visibility::Enum permitVisibility, QTextStream &stream); void writeHeaderAccessorMethodDecl(UMLClassifier *c, Uml::Visibility::Enum permitScope, QTextStream &stream); void writeAssociationDecls(UMLAssociationList associations, Uml::Visibility::Enum permit, Uml::ID::Type id, QTextStream &stream); void writeAssociationRoleDecl(QString fieldClassName, QString roleName, QString multi, QString doc, QTextStream &stream); void writeAttributeMethods(UMLAttributeList attribs, Uml::Visibility::Enum visib, bool isHeaderMethod, bool isStatic, bool writeMethodBody, QTextStream &stream); void writeAssociationMethods(UMLAssociationList associations, Uml::Visibility::Enum permitVisib, bool isHeaderMethod, bool writeMethodBody, bool writePointerVar, Uml::ID::Type id, QTextStream &stream); void writeAssociationRoleMethod(const QString &fieldClassName, bool isHeaderMethod, bool writeMethodBody, const QString &roleName, const QString &multi, const QString &description, Uml::Changeability::Enum change, QTextStream &stream); void writeSingleAttributeAccessorMethods( const QString &fieldClassName, const QString &Name, const QString &fieldName, const QString &description, Uml::Changeability::Enum change, bool isHeaderMethod, bool isStatic, bool writeMethodBody, QTextStream &cpp); void writeVectorAttributeAccessorMethods( const QString &fieldClassName, const QString &fieldVarName, const QString &fieldName, const QString &description, Uml::Changeability::Enum change, bool isHeaderMethod, bool writeMethodBody, QTextStream &stream); void writeComment(const QString &text, const QString &indent, QTextStream &cpp); void writeDocumentation(QString header, QString body, QString end, QTextStream &cpp); void writeHeaderFile(UMLClassifier *c, QFile &file); void writeSourceFile(UMLClassifier *c, QFile &file); void printTextAsSeparateLinesWithIndent (const QString &text, const QString &indent, QTextStream &stream); void printAssociationIncludeDecl(UMLAssociationList list, Uml::ID::Type this_id, QTextStream &stream); void writeInitAttributeMethod(UMLClassifier * c, QTextStream &stream); void writeInitAttributeDecl(UMLClassifier * c, QTextStream &stream); void writeDataTypes(UMLClassifier *c, Uml::Visibility::Enum permitScope, QTextStream &stream); QString umlObjectName(UMLObject *obj); QString fixTypeName(const QString &string); QString fixInitialStringDeclValue(const QString &value, const QString &type); QString getAttributeVariableName(UMLAttribute *at); QString getAttributeMethodBaseName(const QString &fieldName); void writeBlankLine(QTextStream &stream); CPPCodeGenerationPolicy *policyExt(); QString VECTOR_METHOD_APPEND; QString VECTOR_METHOD_REMOVE; QString VECTOR_METHOD_INIT; QString OBJECT_METHOD_INIT; /** * Create association methods for class attributes/associations/operations as inline decl in header. */ bool INLINE_ASSOCIATION_METHODS; QStringList ObjectFieldVariables; QStringList VectorFieldVariables; - + bool m_stringIncludeRequired; }; #endif // CPPWRITER_H diff --git a/umbrello/uml1model/package.cpp b/umbrello/uml1model/package.cpp index 8a2be68de..fb5a41670 100644 --- a/umbrello/uml1model/package.cpp +++ b/umbrello/uml1model/package.cpp @@ -1,454 +1,445 @@ /*************************************************************************** * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * copyright (C) 2003-2014 * * Umbrello UML Modeller Authors * ***************************************************************************/ // own header file #include "package.h" // local includes #include "debug_utils.h" #include "dialog_utils.h" #include "uml.h" #include "umldoc.h" #include "classifier.h" #include "association.h" #include "entity.h" #include "object_factory.h" #include "model_utils.h" // kde includes #include #include // qt includes using namespace Uml; /** * Sets up a Package. * @param name The name of the Concept. * @param id The unique id of the Concept. */ UMLPackage::UMLPackage(const QString & name, Uml::ID::Type id) : UMLCanvasObject(name, id) { m_BaseType = ot_Package; } /** * Destructor. */ UMLPackage::~UMLPackage() { } /** * Copy the internal presentation of this object into the new object. */ void UMLPackage::copyInto(UMLObject *lhs) const { UMLPackage *target = lhs->asUMLPackage(); UMLCanvasObject::copyInto(target); m_objects.copyInto(&(target->m_objects)); } /** * Make a clone of this object. */ UMLObject* UMLPackage::clone() const { UMLPackage *clone = new UMLPackage(); copyInto(clone); return clone; } /** * Adds an existing association to the matching concept in the list of concepts. * The selection of the matching concept depends on the association type: * For generalizations, the assoc is added to the concept that matches role A. * For aggregations and compositions, the assoc is added to the concept * that matches role B. * @param assoc the association to add */ void UMLPackage::addAssocToConcepts(UMLAssociation* assoc) { if (! AssociationType::hasUMLRepresentation(assoc->getAssocType())) return; Uml::ID::Type AId = assoc->getObjectId(Uml::RoleType::A); Uml::ID::Type BId = assoc->getObjectId(Uml::RoleType::B); - UMLObject *o = 0; - for (UMLObjectListIt oit(m_objects); oit.hasNext();) { - o = oit.next(); + foreach (UMLObject *o, m_objects) { UMLCanvasObject *c = o->asUMLCanvasObject(); if (c == 0) continue; if (AId == c->id() || (BId == c->id())) { if (c->hasAssociation(assoc)) uDebug() << c->name() << " already has association id=" << Uml::ID::toString(assoc->id()); else c->addAssociationEnd(assoc); } UMLPackage *pkg = c->asUMLPackage(); if (pkg) pkg->addAssocToConcepts(assoc); } } /** * Remove the association from the participating concepts. * @param assoc the association to remove */ void UMLPackage::removeAssocFromConcepts(UMLAssociation *assoc) { - UMLObject *o = 0; - for (UMLObjectListIt oit(m_objects); oit.hasNext();) { - o = oit.next(); + foreach (UMLObject *o, m_objects) { UMLCanvasObject *c = o->asUMLCanvasObject(); if (c) { if (c->hasAssociation(assoc)) c->removeAssociationEnd(assoc); UMLPackage *pkg = c->asUMLPackage(); if (pkg) pkg->removeAssocFromConcepts(assoc); } } } /** * Adds an object in this package. * * @param pObject Pointer to the UMLObject to add. * @return True if the object was actually added. */ bool UMLPackage::addObject(UMLObject *pObject) { if (pObject == 0) { uError() << "is called with a NULL object"; return false; } if (pObject == this) { uError() << "adding myself as child is not allowed"; return false; } if (m_objects.indexOf(pObject) != -1) { uDebug() << pObject->name() << " is already there"; return false; } if (pObject->baseType() == UMLObject::ot_Association) { UMLAssociation *assoc = pObject->asUMLAssociation(); // Adding the UMLAssociation at the participating concepts is done // again later (in UMLAssociation::resolveRef()) if they are not yet // known right here. if (assoc->getObject(Uml::RoleType::A) && assoc->getObject(Uml::RoleType::B)) { UMLPackage *pkg = pObject->umlPackage(); if (pkg != this) { uError() << "UMLPackage " << name() << " addObject: " << "assoc's UMLPackage is " << pkg->name(); } addAssocToConcepts(assoc); } } else { QString name = pObject->name(); QString oldName = name; while (findObject(name) != 0 && pObject->baseType() != UMLObject::ot_Instance) { QString prevName = name; name = Model_Utils::uniqObjectName(pObject->baseType(), this); bool ok = Dialog_Utils::askName(i18nc("object name", "Name"), i18n("An object with the name %1\nalready exists in the package %2.\nPlease enter a new name:", prevName, this->name()), name); if (!ok) { name = oldName; continue; } if (name.length() == 0) { KMessageBox::error(0, i18n("That is an invalid name."), i18n("Invalid Name")); continue; } } if (oldName != name) { pObject->setName(name); } } m_objects.append(pObject); return true; } /** * Removes an object from this package. * Does not physically delete the object. * * @param pObject Pointer to the UMLObject to be removed. */ void UMLPackage::removeObject(UMLObject *pObject) { if (pObject->baseType() == UMLObject::ot_Association) { UMLObject *o = const_cast(pObject); UMLAssociation *assoc = o->asUMLAssociation(); if (assoc) removeAssocFromConcepts(assoc); else uError() << "invalid cast found"; } if (m_objects.indexOf(pObject) == -1) uDebug() << name() << " removeObject: object with id=" << Uml::ID::toString(pObject->id()) << "not found."; else m_objects.removeAll(pObject); } /** * Removes all objects from this package. * Inner containers (e.g. nested packages) are removed recursively. */ void UMLPackage::removeAllObjects() { UMLCanvasObject::removeAllChildObjects(); UMLObject *o = 0; while (!m_objects.isEmpty() && (o = m_objects.first()) != 0) { UMLPackage *pkg = o->asUMLPackage(); if (pkg) pkg->removeAllObjects(); removeObject(o); delete o; } } /** * Returns the list of objects contained in this package. */ UMLObjectList &UMLPackage::containedObjects() { return m_objects; } /** * Find the object of the given name in the list of contained objects. * * @param name The name to seek. * @return Pointer to the UMLObject found or NULL if not found. */ UMLObject * UMLPackage::findObject(const QString &name) { const bool caseSensitive = UMLApp::app()->activeLanguageIsCaseSensitive(); - for (UMLObjectListIt oit(m_objects); oit.hasNext();) { - UMLObject *obj = oit.next(); + foreach (UMLObject *obj, m_objects) { if (!obj) continue; if (caseSensitive) { if (obj->name() == name) return obj; } else if (obj->name().toLower() == name.toLower()) { return obj; } } return 0; } /** * Find the object of the given ID in the list of contained objects. * * @param id The ID to seek. * @return Pointer to the UMLObject found or NULL if not found. */ UMLObject * UMLPackage::findObjectById(Uml::ID::Type id) { return Model_Utils::findObjectInList(id, m_objects); } /** * Append all packages from this package (and those from nested packages) * to the given UMLPackageList. * * @param packages The list to append to * @param includeNested Whether to include the packages from nested packages * (default:true) */ void UMLPackage::appendPackages(UMLPackageList& packages, bool includeNested) { - for (UMLObjectListIt oit(m_objects); oit.hasNext();) { - UMLObject *o = oit.next(); + foreach (UMLObject *o, m_objects) { uIgnoreZeroPointer(o); ObjectType ot = o->baseType(); if (ot == ot_Package || ot == ot_Folder) { packages.append(o->asUMLPackage()); if (includeNested) { UMLPackage *inner = o->asUMLPackage(); inner->appendPackages(packages); } } } } /** * Append all classifiers from this package (and those from * nested packages) to the given UMLClassifierList. * * @param classifiers The list to append to. * @param includeNested Whether to include the classifiers from * nested packages (default: true.) */ void UMLPackage::appendClassifiers(UMLClassifierList& classifiers, bool includeNested /* = true */) { - for (UMLObjectListIt oit(m_objects); oit.hasNext();) { - UMLObject *o = oit.next(); + foreach (UMLObject *o, m_objects) { uIgnoreZeroPointer(o); ObjectType ot = o->baseType(); if (ot == ot_Class || ot == ot_Interface || ot == ot_Datatype || ot == ot_Enum || ot == ot_Entity) { classifiers.append((UMLClassifier *)o); } else if (includeNested && (ot == ot_Package || ot == ot_Folder)) { UMLPackage *inner = o->asUMLPackage (); inner->appendClassifiers(classifiers); } } } /** * Append all entities from this package (and those * from nested packages) to the given UMLEntityList. * * @param entities The list to append to. * @param includeNested Whether to include the entities from * nested packages (default: true.) */ void UMLPackage::appendEntities(UMLEntityList& entities, bool includeNested /* = true */) { - for (UMLObjectListIt oit(m_objects); oit.hasNext();) { - UMLObject *o = oit.next(); + foreach (UMLObject *o, m_objects) { uIgnoreZeroPointer(o); ObjectType ot = o->baseType(); if (ot == ot_Entity) { UMLEntity *c = o->asUMLEntity(); entities.append(c); } else if (includeNested && (ot == ot_Package || ot == ot_Folder)) { UMLPackage *inner = o->asUMLPackage (); inner->appendEntities(entities); } } } /** * Append all classes and interfaces from this package (and those * from nested packages) to the given UMLClassifierList. * * @param classifiers The list to append to. * @param includeNested Whether to include the classifiers from * nested packages (default: true.) */ void UMLPackage::appendClassesAndInterfaces(UMLClassifierList& classifiers, bool includeNested /* = true */) { - for (UMLObjectListIt oit(m_objects); oit.hasNext();) { - UMLObject *o = oit.next(); + foreach (UMLObject *o, m_objects) { uIgnoreZeroPointer(o); ObjectType ot = o->baseType(); if (ot == ot_Class || ot == ot_Interface) { UMLClassifier *c = o->asUMLClassifier(); classifiers.append(c); } else if (includeNested && (ot == ot_Package || ot == ot_Folder)) { UMLPackage *inner = o->asUMLPackage (); inner->appendClassesAndInterfaces(classifiers); } } } /** * Resolve types. Required when dealing with foreign XMI files. * Needs to be called after all UML objects are loaded from file. * Overrides the method from UMLObject. * Calls resolveRef() on each contained object. * * @return True for overall success. */ bool UMLPackage::resolveRef() { bool overallSuccess = UMLCanvasObject::resolveRef(); - foreach (UMLObject *obj, subordinates()) { + foreach (UMLObject *obj, m_objects) { uIgnoreZeroPointer(obj); if (! obj->resolveRef()) { UMLObject::ObjectType ot = obj->baseType(); if (ot != UMLObject::ot_Package && ot != UMLObject::ot_Folder) m_objects.removeAll(obj); overallSuccess = false; } } return overallSuccess; } /** * Creates the XMI element. */ void UMLPackage::saveToXMI1(QDomDocument& qDoc, QDomElement& qElement) { QDomElement packageElement = UMLObject::save1(QLatin1String("UML:Package"), qDoc); QDomElement ownedElement = qDoc.createElement(QLatin1String("UML:Namespace.ownedElement")); // save classifiers etc. foreach (UMLObject *obj, m_objects) { uIgnoreZeroPointer(obj); obj->saveToXMI1 (qDoc, ownedElement); } // save associations foreach (UMLObject *obj, subordinates()) { obj->saveToXMI1 (qDoc, ownedElement); } packageElement.appendChild(ownedElement); qElement.appendChild(packageElement); } /** * Loads the XMI element. * Auxiliary to UMLObject::loadFromXMI. */ bool UMLPackage::load1(QDomElement& element) { for (QDomNode node = element.firstChild(); !node.isNull(); node = node.nextSibling()) { if (node.isComment()) continue; QDomElement tempElement = node.toElement(); QString type = tempElement.tagName(); if (Model_Utils::isCommonXMI1Attribute(type)) continue; if (UMLDoc::tagEq(type, QLatin1String("Namespace.ownedElement")) || UMLDoc::tagEq(type, QLatin1String("Element.ownedElement")) || // Embarcadero's Describe UMLDoc::tagEq(type, QLatin1String("Namespace.contents"))) { //CHECK: Umbrello currently assumes that nested elements // are ownedElements anyway. // Therefore these tags are not further interpreted. if (! load1(tempElement)) return false; continue; } else if (UMLDoc::tagEq(type, QLatin1String("packagedElement")) || UMLDoc::tagEq(type, QLatin1String("ownedElement"))) { type = tempElement.attribute(QLatin1String("xmi:type")); } UMLObject *pObject = Object_Factory::makeObjectFromXMI(type); if(!pObject) { uWarning() << "Unknown type of umlobject to create: " << type; continue; } pObject->setUMLPackage(this); if (!pObject->loadFromXMI1(tempElement)) { removeObject(pObject); delete pObject; } } return true; } diff --git a/umbrello/umlwidgets/umlwidget.cpp b/umbrello/umlwidgets/umlwidget.cpp index 7206439db..e7de17386 100644 --- a/umbrello/umlwidgets/umlwidget.cpp +++ b/umbrello/umlwidgets/umlwidget.cpp @@ -1,2198 +1,2143 @@ /*************************************************************************** * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * copyright (C) 2002-2014 * * Umbrello UML Modeller Authors * ***************************************************************************/ #include "umlwidget.h" // local includes #include "artifact.h" #include "artifactwidget.h" #include "activitywidget.h" #include "actor.h" #include "actorwidget.h" #include "associationwidget.h" #include "classifier.h" #include "classpropertiesdialog.h" #include "cmds.h" #include "component.h" #include "componentwidget.h" #include "debug_utils.h" #include "dialog_utils.h" #include "docwindow.h" #include "floatingtextwidget.h" #include "forkjoinwidget.h" #include "interfacewidget.h" #include "notewidget.h" #include "messagewidget.h" #include "object_factory.h" #include "idchangelog.h" #include "menus/listpopupmenu.h" #include "objectnodewidget.h" #include "pinwidget.h" #include "port.h" #include "portwidget.h" #include "regionwidget.h" #include "signalwidget.h" #include "settingsdialog.h" #include "statewidget.h" #include "uml.h" #include "umldoc.h" #include "umllistview.h" #include "umlobject.h" #include "umlscene.h" #include "umlview.h" #include "usecase.h" #include "usecasewidget.h" #include "uniqueid.h" #include "widget_factory.h" // kde includes #include #include // qt includes #include #include #include #include using namespace Uml; DEBUG_REGISTER_DISABLED(UMLWidget) const QSizeF UMLWidget::DefaultMinimumSize(50, 20); const QSizeF UMLWidget::DefaultMaximumSize(1000, 5000); const int UMLWidget::defaultMargin = 5; const int UMLWidget::selectionMarkerSize = 4; const int UMLWidget::resizeMarkerLineCount = 3; /** * Creates a UMLWidget object. * * @param scene The view to be displayed on. * @param type The WidgetType to construct. * This must be set to the appropriate value by the constructors of inheriting classes. * @param o The UMLObject to represent. */ UMLWidget::UMLWidget(UMLScene * scene, WidgetType type, UMLObject * o) : WidgetBase(scene, type) , DiagramProxyWidget(this) { init(); m_umlObject = o; if (m_umlObject) { connect(m_umlObject, SIGNAL(modified()), this, SLOT(updateWidget())); m_nId = m_umlObject->id(); } } /** * Creates a UMLWidget object. * * @param scene The view to be displayed on. * @param type The WidgetType to construct. * This must be set to the appropriate value by the constructors of inheriting classes. * @param id The id of the widget. * The default value (id_None) will prompt generation of a new ID. */ UMLWidget::UMLWidget(UMLScene *scene, WidgetType type, Uml::ID::Type id) : WidgetBase(scene, type) , DiagramProxyWidget(this) { init(); if (id == Uml::ID::None) m_nId = UniqueID::gen(); else m_nId = id; } /** * Destructor. */ UMLWidget::~UMLWidget() { cleanup(); } /** * Assignment operator */ UMLWidget& UMLWidget::operator=(const UMLWidget & other) { if (this == &other) return *this; WidgetBase::operator=(other); DiagramProxyWidget::operator=(other); // assign members loaded/saved m_useFillColor = other.m_useFillColor; m_usesDiagramFillColor = other.m_usesDiagramFillColor; m_usesDiagramUseFillColor = other.m_usesDiagramUseFillColor; m_fillColor = other.m_fillColor; m_Assocs = other.m_Assocs; m_isInstance = other.m_isInstance; m_instanceName = other.m_instanceName; m_instanceName = other.m_instanceName; m_showStereotype = other.m_showStereotype; setX(other.x()); setY(other.y()); setRect(rect().x(), rect().y(), other.width(), other.height()); // assign volatile (non-saved) members m_startMove = other.m_startMove; m_nPosX = other.m_nPosX; m_doc = other.m_doc; //new m_resizable = other.m_resizable; for (unsigned i = 0; i < FT_INVALID; ++i) m_pFontMetrics[i] = other.m_pFontMetrics[i]; m_activated = other.m_activated; m_ignoreSnapToGrid = other.m_ignoreSnapToGrid; m_ignoreSnapComponentSizeToGrid = other.m_ignoreSnapComponentSizeToGrid; return *this; } /** * Overload '==' operator */ bool UMLWidget::operator==(const UMLWidget& other) const { if (this == &other) return true; if (baseType() != other.baseType()) { return false; } if (id() != other.id()) return false; /* Testing the associations is already an exaggeration, no? The type and ID should uniquely identify an UMLWidget. */ if (m_Assocs.count() != other.m_Assocs.count()) { return false; } // if(getBaseType() != wt_Text) // DON'T do this for floatingtext widgets, an infinite loop will result // { AssociationWidgetListIt assoc_it(m_Assocs); AssociationWidgetListIt assoc_it2(other.m_Assocs); AssociationWidget * assoc = 0, *assoc2 = 0; while (assoc_it.hasNext() && assoc_it2.hasNext()) { assoc = assoc_it.next(); assoc2 = assoc_it2.next(); if (!(*assoc == *assoc2)) { return false; } } // } return true; // NOTE: In the comparison tests we are going to do, we don't need these values. // They will actually stop things functioning correctly so if you change these, be aware of that. /* if(m_useFillColor != other.m_useFillColor) return false; if(m_nId != other.m_nId) return false; if(m_nX != other.m_nX) return false; if(m_nY != other.m_nY) return false; */ } -/** - * Sets the local id of the object. - * - * @param id The local id of the object. - */ -void UMLWidget::setLocalID(Uml::ID::Type id) -{ - m_nLocalID = id; -} - -/** - * Returns the local ID for this object. This ID is used so that - * many objects of the same @ref UMLObject instance can be on the - * same diagram. - * - * @return The local ID. - */ -Uml::ID::Type UMLWidget::localID() const -{ - return m_nLocalID; -} - -/** - * Returns the widget with the given ID. - * The default implementation tests the following IDs: - * - m_nLocalID - * - if m_umlObject is non NULL: m_umlObject->id() - * - m_nID - * Composite widgets override this function to test further owned widgets. - * - * @param id The ID to test this widget against. - * @return 'this' if id is either of m_nLocalID, m_umlObject->id(), or m_nId; - * else NULL. - */ -UMLWidget* UMLWidget::widgetWithID(Uml::ID::Type id) -{ - if (id == m_nLocalID || - (m_umlObject != 0 && id == m_umlObject->id()) || - id == m_nId) - return this; - return 0; -} - /** * Compute the minimum possible width and height. * * @return QSizeF(mininum_width, minimum_height) */ QSizeF UMLWidget::minimumSize() const { return m_minimumSize; } /** * This method is used to set the minimum size variable for this * widget. * * @param newSize The size being set as minimum. */ void UMLWidget::setMinimumSize(const QSizeF& newSize) { m_minimumSize = newSize; } /** * Compute the maximum possible width and height. * * @return maximum size */ QSizeF UMLWidget::maximumSize() { return m_maximumSize; } /** * This method is used to set the maximum size variable for this * widget. * * @param newSize The size being set as maximum. */ void UMLWidget::setMaximumSize(const QSizeF& newSize) { m_maximumSize = newSize; } /** * Event handler for context menu events. */ void UMLWidget::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { WidgetBase::contextMenuEvent(event); } /** * Moves the widget to a new position using the difference between the * current position and the new position. * This method doesn't adjust associations. It only moves the widget. * * It can be overridden to constrain movement only in one axis even when * the user isn't constraining the movement with shift or control buttons, for example. * The movement policy set here is applied whenever the widget is moved, being it * moving it explicitly, or as a part of a selection but not receiving directly the * mouse events. * * Default behaviour is move the widget to the new position using the diffs. * @see constrainMovementForAllWidgets * * @param diffX The difference between current X position and new X position. * @param diffY The difference between current Y position and new Y position. */ void UMLWidget::moveWidgetBy(qreal diffX, qreal diffY) { setX(x() + diffX); setY(y() + diffY); } /** * Modifies the value of the diffX and diffY variables used to move the widgets. * * It can be overridden to constrain movement of all the selected widgets only in one * axis even when the user isn't constraining the movement with shift or control * buttons, for example. * The difference with moveWidgetBy is that the diff positions used here are * applied to all the selected widgets instead of only to m_widget, and that * moveWidgetBy, in fact, moves the widget, and here simply the diff positions * are modified. * * Default behaviour is do nothing. * @see moveWidgetBy * * @param diffX The difference between current X position and new X position. * @param diffY The difference between current Y position and new Y position. */ void UMLWidget::constrainMovementForAllWidgets(qreal &diffX, qreal &diffY) { Q_UNUSED(diffX) Q_UNUSED(diffY) } /** * Bring the widget at the pressed position to the foreground. */ void UMLWidget::toForeground() { QRectF rect = QRectF(scenePos(), QSizeF(width(), height())); QList items = scene()->items(rect, Qt::IntersectsItemShape, Qt::DescendingOrder); DEBUG(DBG_SRC) << "items at " << rect << " = " << items.count(); if (items.count() > 1) { foreach(QGraphicsItem* i, items) { UMLWidget* w = dynamic_cast(i); if (w) { DEBUG(DBG_SRC) << "item=" << w->name() << " with zValue=" << w->zValue(); if (w->name() != name()) { if (w->zValue() >= zValue()) { setZValue(w->zValue() + 1.0); DEBUG(DBG_SRC) << "bring to foreground with zValue: " << zValue(); } } } } } else { setZValue(0.0); } DEBUG(DBG_SRC) << "zValue is " << zValue(); } /** * Handles a mouse press event. * It'll select the widget (or mark it to be deselected) and prepare it to * be moved or resized. Go on reading for more info about this. * * Widget values and message bar status are saved. * * If shift or control buttons are pressed, we're in move area no matter * where the button was pressed in the widget. Moreover, if the widget * wasn't already selected, it's added to the selection. If already selected, * it's marked to be deselected when releasing the button (provided it isn't * moved). * Also, if the widget is already selected with other widgets but shift nor * control buttons are pressed, we're in move area. If finally we don't move * the widget, it's selected and the other widgets deselected when releasing * the left button. * * If shift nor control buttons are pressed, we're facing a single selection. * Depending on the position of the cursor, we're in move or in resize area. * If the widget wasn't selected (both when there are no widgets selected, or * when there're other widgets selected but not the one receiving the press * event) it's selected and the others deselected, if any. If already selected, * it's marked to be deselected when releasing the button (provided it wasn't * moved or resized). * * @param event The QGraphicsSceneMouseEvent event. */ void UMLWidget::mousePressEvent(QGraphicsSceneMouseEvent *event) { if (event->button() != Qt::LeftButton) { event->ignore(); return; } event->accept(); DEBUG(DBG_SRC) << "widget = " << name() << " / type = " << baseTypeStr(); toForeground(); m_startMovePostion = pos(); m_startResizeSize = QSizeF(width(), height()); // saving the values of the widget m_pressOffset = event->scenePos() - pos(); DEBUG(DBG_SRC) << "press offset=" << m_pressOffset; m_oldStatusBarMsg = UMLApp::app()->statusBarMsg(); if (event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier) { m_shiftPressed = true; if (event->button() == Qt::LeftButton) { m_inMoveArea = true; } if (!isSelected()) { selectMultiple(event); } return; } m_shiftPressed = false; int count = m_scene->selectedCount(true); if (event->button() == Qt::LeftButton) { if (isSelected() && count > 1) { // single selection is made in release event if the widget wasn't moved m_inMoveArea = true; m_oldPos = pos(); return; } if (isInResizeArea(event)) { m_inResizeArea = true; m_oldW = width(); m_oldH = height(); } else { m_inMoveArea = true; } } // if widget wasn't selected, or it was selected but with other widgets also selected if (!isSelected() || count > 1) { selectSingle(event); } } /** * Handles a mouse move event. * It resizes or moves the widget, depending on where the cursor is pressed * on the widget. Go on reading for more info about this. * * If resizing, the widget is resized using UMLWidget::resizeWidget (where specific * widget resize constraint can be applied), and then the associations are * adjusted. * The resizing can be constrained also to a specific axis using control * and shift buttons. If on or another is pressed, it's constrained to X axis. * If both are pressed, it's constrained to Y axis. * * If not resizing, the widget is being moved. If the move is being started, * the selection bounds are set (which includes updating the list of selected * widgets). * The difference between the previous position of the selection and the new * one is got (taking in account the selection bounds so widgets don't go * beyond the scene limits). Then, it's constrained to X or Y axis depending * on shift and control buttons. * A further constraint is made using constrainMovementForAllWidgets (for example, * if the widget that receives the event can only be moved in Y axis, with this * method the movement of all the widgets in the selection can be constrained to * be moved only in Y axis). * Then, all the selected widgets are moved using moveWidgetBy (where specific * widget movement constraint can be applied) and, if a certain amount of time * passed from the last move event, the associations are also updated (they're * not updated always to be easy on the CPU). Finally, the scene is resized, * and selection bounds updated. * * @param event The QGraphicsSceneMouseEvent event. */ void UMLWidget::mouseMoveEvent(QGraphicsSceneMouseEvent* event) { if (m_inResizeArea) { resize(event); return; } if (!m_moved) { UMLApp::app()->document()->writeToStatusBar(i18n("Hold shift or ctrl to move in X axis. Hold shift and control to move in Y axis. Right button click to cancel move.")); m_moved = true; //Maybe needed by AssociationWidget m_startMove = true; setSelectionBounds(); } QPointF position = event->scenePos() - m_pressOffset; qreal diffX = position.x() - x(); qreal diffY = position.y() - y(); if ((event->modifiers() & Qt::ShiftModifier) && (event->modifiers() & Qt::ControlModifier)) { // move only in Y axis diffX = 0; } else if ((event->modifiers() & Qt::ShiftModifier) || (event->modifiers() & Qt::ControlModifier)) { // move only in X axis diffY = 0; } constrainMovementForAllWidgets(diffX, diffY); // nothing to move if (diffX == 0 && diffY == 0) { return; } QPointF delta = event->scenePos() - event->lastScenePos(); DEBUG(DBG_SRC) << "diffX=" << diffX << " / diffY=" << diffY; foreach(UMLWidget* widget, umlScene()->selectedWidgets()) { if ((widget->parentItem() == 0) || (!widget->parentItem()->isSelected())) { widget->moveWidgetBy(diffX, diffY); widget->adjustUnselectedAssocs(delta.x(), delta.y()); widget->slotSnapToGrid(); } } // Move any selected associations. foreach(AssociationWidget* aw, m_scene->selectedAssocs()) { if (aw->isSelected()) { aw->moveEntireAssoc(diffX, diffY); } } umlScene()->resizeSceneToItems(); } /** * Handles a mouse release event. * It selects or deselects the widget and cancels or confirms the move or * resize. Go on reading for more info about this. * No matter which tool is selected, Z position of widget is updated. * * Middle button release resets the selection. * Left button release, if it wasn't moved nor resized, selects the widget * and deselect the others if it wasn't selected and there were other widgets * selected. If the widget was marked to be deselected, deselects it. * If it was moved or resized, the document is set to modified if position * or size changed. Also, if moved, all the associations are adjusted because * the timer could have prevented the adjustment in the last move event before * the release. * If mouse was pressed in resize area, cursor is set again to normal cursor * Right button release if right button was pressed shows the pop up menu for * the widget. * If left button was pressed, it cancels the move or resize with a mouse move * event at the same position than the cursor was when pressed. Another left * button release is also sent. * * @param event The QGraphicsSceneMouseEvent event. */ void UMLWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { if (!m_moved && !m_resized) { if (!m_shiftPressed && (m_scene->selectedCount(true) > 1)) { selectSingle(event); } else if (!isSelected()) { deselect(event); } } else { // Commands if (m_moved) { int selectionCount = umlScene()->selectedWidgets().count(); if (selectionCount > 1) { UMLApp::app()->beginMacro(i18n("Move widgets")); } foreach(UMLWidget* widget, umlScene()->selectedWidgets()) { UMLApp::app()->executeCommand(new Uml::CmdMoveWidget(widget)); } if (selectionCount > 1) { UMLApp::app()->endMacro(); } m_moved = false; } else { UMLApp::app()->executeCommand(new Uml::CmdResizeWidget(this)); m_autoResize = false; m_resized = false; } if ((m_inMoveArea && wasPositionChanged()) || (m_inResizeArea && wasSizeChanged())) { umlDoc()->setModified(true); umlScene()->invalidate(); } umlScene()->resizeSceneToItems(); UMLApp::app()->document()->writeToStatusBar(m_oldStatusBarMsg); } if (m_inResizeArea) { m_inResizeArea = false; m_scene->activeView()->setCursor(Qt::ArrowCursor); } else { m_inMoveArea = false; } m_startMove = false; } /** * Event handler for mouse double click events. * @param event the QGraphicsSceneMouseEvent event. */ void UMLWidget::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) { if (event->button() == Qt::LeftButton) { DEBUG(DBG_SRC) << "widget = " << name() << " / type = " << baseTypeStr(); showPropertiesDialog(); event->accept(); } } /** * Return the start position of the move action. * @return point where the move began */ QPointF UMLWidget::startMovePosition() const { return m_startMovePostion; } /** * Set the start position of the move action. * @param position point where the move began */ void UMLWidget::setStartMovePosition(const QPointF &position) { m_startMovePostion = position; } /** * Return the start size of the resize action. * @return size where the resize began */ QSizeF UMLWidget::startResizeSize() const { return m_startResizeSize; } /** * Resizes the widget. * It's called from resize, after the values are constrained and before * the associations are adjusted. * * Default behaviour is resize the widget using the new size values. * @see resize * * @param newW The new width for the widget. * @param newH The new height for the widget. */ void UMLWidget::resizeWidget(qreal newW, qreal newH) { setSize(newW, newH); } /** * Notify child widget about parent resizes. * Child widgets can override this function to move when their parent is resized. */ void UMLWidget::notifyParentResize() { } /** * When a widget changes this slot captures that signal. */ void UMLWidget::updateWidget() { updateGeometry(); switch (baseType()) { case WidgetBase::wt_Class: m_scene->createAutoAttributeAssociations(this); break; case WidgetBase::wt_Entity: m_scene->createAutoConstraintAssociations(this); break; default: break; } if (isVisible()) update(); } /** * Apply possible constraints to the given candidate width and height. * The default implementation limits input values to the bounds returned * by minimumSize()/maximumSize(). * * @param width input value, may be modified by the constraint * @param height input value, may be modified by the constraint */ void UMLWidget::constrain(qreal& width, qreal& height) { QSizeF minSize = minimumSize(); if (width < minSize.width()) width = minSize.width(); if (height < minSize.height()) height = minSize.height(); QSizeF maxSize = maximumSize(); if (width > maxSize.width()) width = maxSize.width(); if (height > maxSize.height()) height = maxSize.height(); if (fixedAspectRatio()) { QSizeF size = rect().size(); float aspectRatio = size.width() > 0 ? (float)size.height()/size.width() : 1; height = width * aspectRatio; } } /** * Initializes key attributes of the class. */ void UMLWidget::init() { m_nId = Uml::ID::None; - m_nLocalID = UniqueID::gen(); m_isInstance = false; setMinimumSize(DefaultMinimumSize); setMaximumSize(DefaultMaximumSize); m_font = QApplication::font(); for (int i = (int)FT_INVALID - 1; i >= 0; --i) { FontType fontType = (FontType)i; setupFontType(m_font, fontType); m_pFontMetrics[fontType] = new QFontMetrics(m_font); } if (m_scene) { m_useFillColor = true; m_usesDiagramFillColor = true; m_usesDiagramUseFillColor = true; const Settings::OptionState& optionState = m_scene->optionState(); m_fillColor = optionState.uiState.fillColor; m_showStereotype = optionState.classState.showStereoType; } else { uError() << "SERIOUS PROBLEM - m_scene is NULL"; m_useFillColor = false; m_usesDiagramFillColor = false; m_usesDiagramUseFillColor = false; m_showStereotype = false; } m_resizable = true; m_fixedAspectRatio = false; m_startMove = false; m_activated = false; m_ignoreSnapToGrid = false; m_ignoreSnapComponentSizeToGrid = false; m_doc = UMLApp::app()->document(); m_nPosX = 0; connect(m_scene, SIGNAL(sigFillColorChanged(Uml::ID::Type)), this, SLOT(slotFillColorChanged(Uml::ID::Type))); connect(m_scene, SIGNAL(sigLineColorChanged(Uml::ID::Type)), this, SLOT(slotLineColorChanged(Uml::ID::Type))); connect(m_scene, SIGNAL(sigTextColorChanged(Uml::ID::Type)), this, SLOT(slotTextColorChanged(Uml::ID::Type))); connect(m_scene, SIGNAL(sigLineWidthChanged(Uml::ID::Type)), this, SLOT(slotLineWidthChanged(Uml::ID::Type))); m_umlObject = 0; m_oldPos = QPointF(); m_pressOffset = QPointF(); m_oldW = 0; m_oldH = 0; m_shiftPressed = false; m_inMoveArea = false; m_inResizeArea = false; m_moved = false; m_resized = false; // propagate line color set by base class constructor // which does not call the virtual methods from this class. setLineColor(lineColor()); setZValue(2.0); // default for most widgets } /** * This is usually called synchronously after menu.exec() and \a * trigger's parent is always the ListPopupMenu which can be used to * get the type of action of \a trigger. * * @note Subclasses can reimplement to handle specific actions and * leave the rest to WidgetBase::slotMenuSelection. */ void UMLWidget::slotMenuSelection(QAction *trigger) { if (!trigger) { return; } ListPopupMenu::MenuType sel = ListPopupMenu::typeFromAction(trigger); switch (sel) { case ListPopupMenu::mt_Resize: umlScene()->resizeSelection(); break; case ListPopupMenu::mt_AutoResize: setAutoResize(trigger->isChecked()); updateGeometry(); break; case ListPopupMenu::mt_Rename_Object: { QString name = m_instanceName; bool ok = Dialog_Utils::askName(i18n("Rename Object"), i18n("Enter object name:"), name); if (ok) { m_instanceName = name; updateGeometry(); moveEvent(0); update(); UMLApp::app()->document()->setModified(true); } break; } case ListPopupMenu::mt_FloatText: { FloatingTextWidget* ft = new FloatingTextWidget(umlScene()); ft->showChangeTextDialog(); //if no text entered delete if (!FloatingTextWidget::isTextValid(ft->text())) { delete ft; } else { ft->setID(UniqueID::gen()); addWidget(ft, false); } break; } case ListPopupMenu::mt_Actor: { UMLActor *actor = new UMLActor; UMLWidget *widget = new ActorWidget(umlScene(), actor); addConnectedWidget(widget, Uml::AssociationType::Association); break; } case ListPopupMenu::mt_Artifact: { UMLArtifact *a = new UMLArtifact(); ArtifactWidget *widget = new ArtifactWidget(umlScene(), a); addConnectedWidget(widget, Uml::AssociationType::Association); break; } case ListPopupMenu::mt_Component: { UMLComponent *c = new UMLComponent(); ComponentWidget *widget = new ComponentWidget(umlScene(), c); addConnectedWidget(widget, Uml::AssociationType::Association, SetupSize); break; } case ListPopupMenu::mt_Interface: { UMLPackage* component = umlObject()->asUMLPackage(); QString name = Model_Utils::uniqObjectName(UMLObject::ot_Interface, component); if (Dialog_Utils::askNewName(WidgetBase::wt_Interface, name)) { UMLClassifier *c = new UMLClassifier(); c->setBaseType(UMLObject::ot_Interface); ClassifierWidget *widget = new ClassifierWidget(umlScene(), c); addConnectedWidget(widget, Uml::AssociationType::Association); } break; } case ListPopupMenu::mt_InterfaceComponent: case ListPopupMenu::mt_InterfaceProvided: { UMLObject *o = Object_Factory::createUMLObject(UMLObject::ot_Interface); InterfaceWidget *w = new InterfaceWidget(umlScene(), o->asUMLClassifier()); w->setDrawAsCircle(true); addConnectedWidget(w, Uml::AssociationType::Association, SetupSize); break; } case ListPopupMenu::mt_InterfaceRequired: { UMLObject *o = Object_Factory::createUMLObject(UMLObject::ot_Interface); InterfaceWidget *w = new InterfaceWidget(umlScene(), o->asUMLClassifier()); w->setDrawAsCircle(true); addConnectedWidget(w, Uml::AssociationType::Association, SetupSize | SwitchDirection); break; } case ListPopupMenu::mt_Note: { NoteWidget *widget = new NoteWidget(umlScene()); addConnectedWidget(widget, Uml::AssociationType::Anchor); break; } case ListPopupMenu::mt_Port: { // TODO: merge with ToolbarStateOneWidget::setWidget() UMLPackage* component = umlObject()->asUMLPackage(); QString name = Model_Utils::uniqObjectName(UMLObject::ot_Port, component); if (Dialog_Utils::askNewName(WidgetBase::wt_Port, name)) { UMLPort *port = Object_Factory::createUMLObject(UMLObject::ot_Port, name, component)->asUMLPort(); UMLWidget *umlWidget = Widget_Factory::createWidget(umlScene(), port); umlWidget->setParentItem(this); QPointF p = mapFromScene(umlScene()->pos()); umlWidget->setPos(p); umlScene()->setupNewWidget(umlWidget, false); } break; } case ListPopupMenu::mt_UseCase: { UMLUseCase *useCase = new UMLUseCase; UMLWidget *widget = new UseCaseWidget(umlScene(), useCase); addConnectedWidget(widget, Uml::AssociationType::Association); break; } case ListPopupMenu::mt_MessageSynchronous: // MessageWidget *widget = new MessageWidget(umlScene(), this); // addConnectedWidget(widget, Uml::AssociationType::Coll_Message_Synchronous); case ListPopupMenu::mt_MessageAsynchronous: case ListPopupMenu::mt_MessageFound: case ListPopupMenu::mt_MessageLost: break; // activity diagrams case ListPopupMenu::mt_Accept_Signal: addConnectedWidget(new SignalWidget(umlScene(), SignalWidget::Accept), Uml::AssociationType::Activity, NoOption); break; case ListPopupMenu::mt_Accept_Time_Event: addConnectedWidget(new SignalWidget(umlScene(), SignalWidget::Time), Uml::AssociationType::Activity, NoOption); break; case ListPopupMenu::mt_Activity: addConnectedWidget(new ActivityWidget(umlScene(), ActivityWidget::Normal), Uml::AssociationType::Activity, NoOption); break; case ListPopupMenu::mt_Activity_Transition: addConnectedWidget(new ActivityWidget(umlScene(), ActivityWidget::Final), Uml::AssociationType::Activity, NoOption); break; case ListPopupMenu::mt_Branch: addConnectedWidget(new ActivityWidget(umlScene(), ActivityWidget::Branch), Uml::AssociationType::Activity, NoOption); break; case ListPopupMenu::mt_Exception: umlScene()->triggerToolbarButton(WorkToolBar::tbb_Exception); break; case ListPopupMenu::mt_Final_Activity: addConnectedWidget(new ActivityWidget(umlScene(), ActivityWidget::Final), Uml::AssociationType::Activity, NoOption); break; case ListPopupMenu::mt_Fork: addConnectedWidget(new ForkJoinWidget(umlScene()), Uml::AssociationType::Activity, NoOption); break; case ListPopupMenu::mt_End_Activity: addConnectedWidget(new ActivityWidget(umlScene(), ActivityWidget::End), Uml::AssociationType::Activity, NoOption); break; case ListPopupMenu::mt_Initial_Activity: addConnectedWidget(new ActivityWidget(umlScene(), ActivityWidget::Initial), Uml::AssociationType::Activity, NoOption); break; case ListPopupMenu::mt_Invoke_Activity: addConnectedWidget(new ActivityWidget(umlScene(), ActivityWidget::Invok), Uml::AssociationType::Activity, NoOption); break; case ListPopupMenu::mt_Object_Node: addConnectedWidget(new ObjectNodeWidget(umlScene(), ObjectNodeWidget::Data), Uml::AssociationType::Activity, NoOption); break; case ListPopupMenu::mt_Pin: umlScene()->triggerToolbarButton(WorkToolBar::tbb_Pin); break; case ListPopupMenu::mt_Param_Activity: addConnectedWidget(new ActivityWidget(umlScene(), ActivityWidget::Param), Uml::AssociationType::Activity, NoOption); break; case ListPopupMenu::mt_PrePostCondition: addConnectedWidget(new NoteWidget(umlScene(), NoteWidget::Normal), Uml::AssociationType::Activity, NoOption); break; case ListPopupMenu::mt_Region: addConnectedWidget(new RegionWidget(umlScene()), Uml::AssociationType::Activity, NoOption); break; case ListPopupMenu::mt_Send_Signal: addConnectedWidget(new SignalWidget(umlScene(), SignalWidget::Send), Uml::AssociationType::Activity, NoOption); break; // state diagrams case ListPopupMenu::mt_Choice: addConnectedWidget(new StateWidget(umlScene(), StateWidget::Choice), Uml::AssociationType::State, NoOption); break; case ListPopupMenu::mt_DeepHistory: addConnectedWidget(new StateWidget(umlScene(), StateWidget::DeepHistory), Uml::AssociationType::State, NoOption); break; case ListPopupMenu::mt_End_State: addConnectedWidget(new StateWidget(umlScene(), StateWidget::End), Uml::AssociationType::State, NoOption); break; case ListPopupMenu::mt_Junction: addConnectedWidget(new StateWidget(umlScene(), StateWidget::Junction), Uml::AssociationType::State, NoOption); break; case ListPopupMenu::mt_ShallowHistory: addConnectedWidget(new StateWidget(umlScene(), StateWidget::ShallowHistory), Uml::AssociationType::State, NoOption); break; case ListPopupMenu::mt_State: addConnectedWidget(new StateWidget(umlScene(), StateWidget::Normal), Uml::AssociationType::State, NoOption); break; case ListPopupMenu::mt_StateFork: addConnectedWidget(new StateWidget(umlScene(), StateWidget::Fork), Uml::AssociationType::State, NoOption); break; case ListPopupMenu::mt_StateJoin: addConnectedWidget(new StateWidget(umlScene(), StateWidget::Join), Uml::AssociationType::State, NoOption); break; case ListPopupMenu::mt_StateTransition: umlScene()->triggerToolbarButton(WorkToolBar::tbb_State_Transition); break; default: WidgetBase::slotMenuSelection(trigger); break; } } /** * Captures when another widget moves if this widget is linked to it. * @see sigWidgetMoved * * @param id The id of object behind the widget. */ void UMLWidget::slotWidgetMoved(Uml::ID::Type /*id*/) { } /** * Captures a color change signal. * * @param viewID The id of the UMLScene behind the widget. */ void UMLWidget::slotFillColorChanged(Uml::ID::Type viewID) { //only change if on the diagram concerned if (m_scene->ID() != viewID) { return; } if (m_usesDiagramFillColor) { WidgetBase::setFillColor(m_scene->fillColor()); } if (m_usesDiagramUseFillColor) { WidgetBase::setUseFillColor(m_scene->useFillColor()); } update(); } /** * Captures a text color change signal. * * @param viewID The id of the UMLScene behind the widget. */ void UMLWidget::slotTextColorChanged(Uml::ID::Type viewID) { //only change if on the diagram concerned if (m_scene->ID() != viewID) return; WidgetBase::setTextColor(m_scene->textColor()); update(); } /** * Captures a line color change signal. * * @param viewID The id of the UMLScene behind the widget. */ void UMLWidget::slotLineColorChanged(Uml::ID::Type viewID) { //only change if on the diagram concerned if (m_scene->ID() != viewID) return; if (m_usesDiagramLineColor) { WidgetBase::setLineColor(m_scene->lineColor()); } update(); } /** * Captures a linewidth change signal. * * @param viewID The id of the UMLScene behind the widget. */ void UMLWidget::slotLineWidthChanged(Uml::ID::Type viewID) { //only change if on the diagram concerned if (m_scene->ID() != viewID) { return; } if (m_usesDiagramLineWidth) { WidgetBase::setLineWidth(m_scene->lineWidth()); } update(); } /** * Set the status of using fill color (undo action) * * @param fc the status of using fill color. */ void UMLWidget::setUseFillColor(bool fc) { if (useFillColor() != fc) { UMLApp::app()->executeCommand(new CmdChangeUseFillColor(this, fc)); } } /** * Set the status of using fill color. * * @param fc the status of using fill color. */ void UMLWidget::setUseFillColorCmd(bool fc) { WidgetBase::setUseFillColor(fc); update(); } /** * Overrides the method from WidgetBase. */ void UMLWidget::setTextColorCmd(const QColor &color) { WidgetBase::setTextColor(color); update(); } /** * Overrides the method from WidgetBase. */ void UMLWidget::setTextColor(const QColor &color) { if (textColor() != color) { UMLApp::app()->executeCommand(new CmdChangeTextColor(this, color)); update(); } } /** * Overrides the method from WidgetBase. */ void UMLWidget::setLineColorCmd(const QColor &color) { WidgetBase::setLineColor(color); update(); } /** * Overrides the method from WidgetBase. */ void UMLWidget::setLineColor(const QColor &color) { if (lineColor() != color) { UMLApp::app()->executeCommand(new CmdChangeLineColor(this, color)); } } /** * Overrides the method from WidgetBase, execute CmdChangeLineWidth */ void UMLWidget::setLineWidth(uint width) { if (lineWidth() != width) { UMLApp::app()->executeCommand(new CmdChangeLineWidth(this, width)); } } /** * Overrides the method from WidgetBase. */ void UMLWidget::setLineWidthCmd(uint width) { WidgetBase::setLineWidth(width); update(); } /** * Sets the background fill color * * @param color the new fill color */ void UMLWidget::setFillColor(const QColor &color) { if (fillColor() != color) { UMLApp::app()->executeCommand(new CmdChangeFillColor(this, color)); } } /** * Sets the background fill color * * @param color the new fill color */ void UMLWidget::setFillColorCmd(const QColor &color) { WidgetBase::setFillColor(color); update(); } /** * Activate the object after serializing it from a QDataStream * * @param ChangeLog * @return true for success */ bool UMLWidget::activate(IDChangeLog* changeLog) { DiagramProxyWidget::activate(changeLog); if (widgetHasUMLObject(baseType()) && m_umlObject == 0) { m_umlObject = m_doc->findObjectById(m_nId); if (m_umlObject == 0) { uError() << "cannot find UMLObject with id=" << Uml::ID::toString(m_nId); return false; } } setFontCmd(m_font); setSize(width(), height()); m_activated = true; updateGeometry(); if (m_scene->getPaste()) { FloatingTextWidget * ft = 0; QPointF point = m_scene->getPastePoint(); int x = point.x() + this->x(); int y = point.y() + this->y(); if (m_scene->isSequenceDiagram()) { switch (baseType()) { case WidgetBase::wt_Object: case WidgetBase::wt_Precondition : setY(this->y()); setX(x); break; case WidgetBase::wt_Message: setY(this->y()); setX(x); break; case WidgetBase::wt_Text: ft = static_cast(this); if (ft->textRole() == Uml::TextRole::Seq_Message) { setX(x); setY(this->y()); } else { setX(this->x()); setY(this->y()); } break; default: setY(y); break; }//end switch base type }//end if sequence else { setX(x); setY(y); } }//end if pastepoint else { setX(this->x()); setY(this->y()); } if (m_scene->getPaste()) m_scene->createAutoAssociations(this); updateGeometry(); return true; } /** * Returns true if the Activate method has been called for this instance * * @return The activate status. */ bool UMLWidget::isActivated() const { return m_activated; } /** * Set the m_activated flag of a widget but does not perform the Activate method * * @param active Status of activation is to be set. */ void UMLWidget::setActivated(bool active /*=true*/) { m_activated = active; } /** - * Adds an already created association to the list of - * associations that include this UMLWidget + * Reimplemented from class WidgetBase */ void UMLWidget::addAssoc(AssociationWidget* pAssoc) { if (pAssoc && !associationWidgetList().contains(pAssoc)) { associationWidgetList().append(pAssoc); } } /** * Returns the list of associations connected to this widget. */ AssociationWidgetList &UMLWidget::associationWidgetList() const { m_Assocs.removeAll(0); return m_Assocs; } /** - * Removes an already created association from the list of - * associations that include this UMLWidget + * Reimplemented from class WidgetBase */ void UMLWidget::removeAssoc(AssociationWidget* pAssoc) { if (pAssoc) { associationWidgetList().removeAll(pAssoc); } if (changesShape()) { updateGeometry(); } } /** * Adjusts associations with the given co-ordinates * * @param dx The amount by which the widget moved in X direction. * @param dy The amount by which the widget moved in Y direction. */ void UMLWidget::adjustAssocs(qreal dx, qreal dy) { qDebug() << this; // don't adjust Assocs on file load, as // the original positions, which are stored in XMI // should be reproduced exactly // (don't try to reposition assocs as long // as file is only partly loaded -> reposition // could be misguided) /// @todo avoid trigger of this event during load if (m_doc->loading()) { // don't recalculate the assocs during load of XMI // -> return immediately without action return; } foreach(AssociationWidget* assocwidget, associationWidgetList()) { assocwidget->saveIdealTextPositions(); } foreach(AssociationWidget* assocwidget, associationWidgetList()) { assocwidget->widgetMoved(this, dx, dy); } } /** * Adjusts all unselected associations with the given co-ordinates * * @param dx The amount by which the widget moved in X direction. * @param dy The amount by which the widget moved in Y direction. */ void UMLWidget::adjustUnselectedAssocs(qreal dx, qreal dy) { foreach(AssociationWidget* assocwidget, associationWidgetList()) { if (!assocwidget->isSelected()) assocwidget->saveIdealTextPositions(); } foreach(AssociationWidget* assocwidget, associationWidgetList()) { if (!assocwidget->isSelected()) { assocwidget->widgetMoved(this, dx, dy); } } } /** * Show a properties dialog for a UMLWidget. */ bool UMLWidget::showPropertiesDialog() { bool result = false; // will already be selected so make sure docWindow updates the doc // back it the widget UMLApp::app()->docWindow()->updateDocumentation(false); QPointer dlg = new ClassPropertiesDialog((QWidget*)UMLApp::app(), this); if (dlg->exec()) { UMLApp::app()->docWindow()->showDocumentation(umlObject(), true); m_doc->setModified(true); result = true; } dlg->close(); //wipe from memory delete dlg; return result; } /** * Move the widget by an X and Y offset relative to * the current position. */ void UMLWidget::moveByLocal(qreal dx, qreal dy) { qreal newX = x() + dx; qreal newY = y() + dy; setX(newX); setY(newY); adjustAssocs(dx, dy); } /** * Set the pen. */ void UMLWidget::setPenFromSettings(QPainter & p) { p.setPen(QPen(m_lineColor, m_lineWidth)); } /** * Set the pen. */ void UMLWidget::setPenFromSettings(QPainter *p) { p->setPen(QPen(m_lineColor, m_lineWidth)); } /** * Returns the cursor to be shown when resizing the widget. * Default cursor is KCursor::sizeFDiagCursor(). * * @return The cursor to be shown when resizing the widget. */ QCursor UMLWidget::resizeCursor() const { return Qt::SizeFDiagCursor; } /** * Checks if the mouse is in resize area (right bottom corner), and sets * the cursor depending on that. * The cursor used when resizing is gotten from resizeCursor(). * * @param me The QMouseEVent to check. * @return true if the mouse is in resize area, false otherwise. */ bool UMLWidget::isInResizeArea(QGraphicsSceneMouseEvent *me) { qreal m = 10.0; const qreal w = width(); const qreal h = height(); // If the widget itself is very small then make the resize area small, too. // Reason: Else it becomes impossible to do a move instead of resize. if (w - m < m || h - m < m) { m = 2.0; } if (m_resizable && me->scenePos().x() >= (x() + w - m) && me->scenePos().y() >= (y() + h - m)) { m_scene->activeView()->setCursor(resizeCursor()); return true; } else { m_scene->activeView()->setCursor(Qt::ArrowCursor); return false; } } /** * calculate content related size of widget. * * @return calculated widget size */ QSizeF UMLWidget::calculateSize(bool withExtensions /* = true */) const { Q_UNUSED(withExtensions) const QFontMetrics &fm = getFontMetrics(UMLWidget::FT_NORMAL); const int fontHeight = fm.lineSpacing(); if (m_umlObject) { qreal width = 0, height = defaultMargin; if (!m_umlObject->stereotype().isEmpty()) { height += fontHeight; const QFontMetrics &bfm = UMLWidget::getFontMetrics(UMLWidget::FT_BOLD); const int stereoWidth = bfm.size(0, m_umlObject->stereotype(true)).width(); if (stereoWidth > width) width = stereoWidth; } height += fontHeight; const QFontMetrics &bfm = UMLWidget::getFontMetrics(UMLWidget::FT_BOLD); const int nameWidth = bfm.size(0, m_umlObject->name()).width(); if (nameWidth > width) width = nameWidth; return QSizeF(width + 2*defaultMargin, height); } else return QSizeF(width(), height()); } /** * Resize widget to minimum size. */ void UMLWidget::resize() { qreal oldW = width(); qreal oldH = height(); // @TODO minimumSize() do not work in all cases, we need a dedicated autoResize() method QSizeF size = minimumSize(); setSize(size.width(), size.height()); DEBUG(DBG_SRC) << "size=" << size; adjustAssocs(size.width()-oldW, size.height()-oldH); } /** * Resizes the widget and adjusts the associations. * It's called when a mouse move event happens and the cursor was * in resize area when pressed. * Resizing can be constrained to an specific axis using control and shift buttons. * * @param me The QGraphicsSceneMouseEvent to get the values from. */ void UMLWidget::resize(QGraphicsSceneMouseEvent *me) { QString msgX = i18n("Hold shift or control to move in X axis."); QString msgY = i18n("Hold shift and control to move in Y axis."); QString msg; if (isMessageWidget()) msg = msgY; else if (isObjectWidget()) msg = msgX; else msg = QString(QLatin1String("%1 %2")).arg(msgX, msgY); UMLApp::app()->document()->writeToStatusBar(msg); m_resized = true; qreal newW = m_oldW + me->scenePos().x() - x() - m_pressOffset.x(); qreal newH = m_oldH + me->scenePos().y() - y() - m_pressOffset.y(); if ((me->modifiers() & Qt::ShiftModifier) && (me->modifiers() & Qt::ControlModifier)) { //Move in Y axis newW = m_oldW; } else if ((me->modifiers() & Qt::ShiftModifier) || (me->modifiers() & Qt::ControlModifier)) { //Move in X axis newH = m_oldH; } constrain(newW, newH); resizeWidget(newW, newH); DEBUG(DBG_SRC) << "event=" << me->scenePos() << "/ pos=" << pos() << " / newW=" << newW << " / newH=" << newH; QPointF delta = me->scenePos() - me->lastScenePos(); adjustAssocs(delta.x(), delta.y()); m_scene->resizeSceneToItems(); } /** * Checks if the size of the widget changed respect to the size that * it had when press event was fired. * * @return true if was resized, false otherwise. */ bool UMLWidget::wasSizeChanged() { return m_oldW != width() || m_oldH != height(); } /** * Checks if the position of the widget changed respect to the position that * it had when press event was fired. * * @return true if was moved, false otherwise. */ bool UMLWidget::wasPositionChanged() { return m_oldPos != pos(); } /** * Fills m_selectedWidgetsList and sets the selection bounds ((m_min/m_max)X/Y attributes). */ void UMLWidget::setSelectionBounds() { } void UMLWidget::setSelectedFlag(bool _select) { WidgetBase::setSelected(_select); } /** * Sets the state of whether the widget is selected. * * @param _select The state of whether the widget is selected. */ void UMLWidget::setSelected(bool _select) { const WidgetBase::WidgetType wt = baseType(); if (_select) { if (m_scene->selectedCount() == 0) { if (widgetHasUMLObject(wt)) { UMLApp::app()->docWindow()->showDocumentation(m_umlObject, false); } else { UMLApp::app()->docWindow()->showDocumentation(this, false); } }//end if /* if (wt != wt_Text && wt != wt_Box) { setZ(9);//keep text on top and boxes behind so don't touch Z value } */ } else { /* if (wt != wt_Text && wt != wt_Box) { setZ(m_origZ); } */ if (isSelected()) UMLApp::app()->docWindow()->updateDocumentation(true); } WidgetBase::setSelected(_select); update(); // selection changed, we have to make sure the copy and paste items // are correctly enabled/disabled UMLApp::app()->slotCopyChanged(); // select in tree view as done for diagrams if (_select) { UMLListViewItem * item = UMLApp::app()->listView()->findItem(id()); if (item) UMLApp::app()->listView()->setCurrentItem(item); else UMLApp::app()->listView()->clearSelection(); } } /** * Selects the widget and clears the other selected widgets, if any. * * @param me The QGraphicsSceneMouseEvent which made the selection. */ void UMLWidget::selectSingle(QGraphicsSceneMouseEvent *me) { m_scene->clearSelected(); // Adds the widget to the selected widgets list, but as it has been cleared // only the current widget is selected. selectMultiple(me); } /** * Selects the widget and adds it to the list of selected widgets. * * @param me The QGraphicsSceneMouseEvent which made the selection. */ void UMLWidget::selectMultiple(QGraphicsSceneMouseEvent *me) { Q_UNUSED(me); setSelected(true); } /** * Deselects the widget and removes it from the list of selected widgets. * * @param me The QGraphicsSceneMouseEvent which made the selection. */ void UMLWidget::deselect(QGraphicsSceneMouseEvent *me) { Q_UNUSED(me); setSelected(false); } /** * Clears the selection, resets the toolbar and deselects the widget. */ //void UMLWidget::resetSelection() //{ // m_scene->clearSelected(); // m_scene->resetToolbar(); // setSelected(false); //} /** * Sets the view the widget is on. * * @param scene The UMLScene the widget is on. */ void UMLWidget::setScene(UMLScene *scene) { //remove signals from old view - was probably 0 anyway disconnect(m_scene, SIGNAL(sigFillColorChanged(Uml::ID::Type)), this, SLOT(slotFillColorChanged(Uml::ID::Type))); disconnect(m_scene, SIGNAL(sigTextColorChanged(Uml::ID::Type)), this, SLOT(slotTextColorChanged(Uml::ID::Type))); disconnect(m_scene, SIGNAL(sigLineWidthChanged(Uml::ID::Type)), this, SLOT(slotLineWidthChanged(Uml::ID::Type))); m_scene = scene; connect(m_scene, SIGNAL(sigFillColorChanged(Uml::ID::Type)), this, SLOT(slotFillColorChanged(Uml::ID::Type))); connect(m_scene, SIGNAL(sigTextColorChanged(Uml::ID::Type)), this, SLOT(slotTextColorChanged(Uml::ID::Type))); connect(m_scene, SIGNAL(sigLineWidthChanged(Uml::ID::Type)), this, SLOT(slotLineWidthChanged(Uml::ID::Type))); } /** * Sets the x-coordinate. * Currently, the only class that reimplements this method is * ObjectWidget. * * @param x The x-coordinate to be set. */ void UMLWidget::setX(qreal x) { QGraphicsObject::setX(x); } /** * Sets the y-coordinate. * Currently, the only class that reimplements this method is * ObjectWidget. * * @param y The y-coordinate to be set. */ void UMLWidget::setY(qreal y) { QGraphicsObject::setY(y); } /** * Used to cleanup any other widget it may need to delete. * Used by child classes. This should be called before deleting a widget of a diagram. */ void UMLWidget::cleanup() { } /** * Tells the widget to snap to grid. * Will use the grid settings of the @ref UMLView it belongs to. */ void UMLWidget::slotSnapToGrid() { if (!m_ignoreSnapToGrid) { qreal newX = m_scene->snappedX(x()); setX(newX); qreal newY = m_scene->snappedY(y()); setY(newY); } } /** * Returns whether the widget type has an associated UMLObject */ bool UMLWidget::widgetHasUMLObject(WidgetBase::WidgetType type) { if (type == WidgetBase::wt_Actor || type == WidgetBase::wt_UseCase || type == WidgetBase::wt_Class || type == WidgetBase::wt_Interface || type == WidgetBase::wt_Enum || type == WidgetBase::wt_Datatype || type == WidgetBase::wt_Package || type == WidgetBase::wt_Component || type == WidgetBase::wt_Port || type == WidgetBase::wt_Node || type == WidgetBase::wt_Artifact || type == WidgetBase::wt_Object) { return true; } else { return false; } } /** * Set m_ignoreSnapToGrid. */ void UMLWidget::setIgnoreSnapToGrid(bool to) { m_ignoreSnapToGrid = to; } /** * Return the value of m_ignoreSnapToGrid. */ bool UMLWidget::getIgnoreSnapToGrid() const { return m_ignoreSnapToGrid; } /** * Sets the size. * If m_scene->snapComponentSizeToGrid() is true, then * set the next larger size that snaps to the grid. */ void UMLWidget::setSize(qreal width, qreal height) { // snap to the next larger size that is a multiple of the grid if (!m_ignoreSnapComponentSizeToGrid && m_scene->snapComponentSizeToGrid()) { // integer divisions int numX = width / m_scene->snapX(); int numY = height / m_scene->snapY(); // snap to the next larger valid value if (width > numX * m_scene->snapX()) width = (numX + 1) * m_scene->snapX(); if (height > numY * m_scene->snapY()) height = (numY + 1) * m_scene->snapY(); } const QRectF newRect(rect().x(), rect().y(), width, height); setRect(newRect); foreach(QGraphicsItem* child, childItems()) { UMLWidget* umlChild = static_cast(child); umlChild->notifyParentResize(); } } /** * Sets the size with another size. */ void UMLWidget::setSize(const QSizeF& size) { setSize(size.width(), size.height()); } /** * Update the size of this widget. * * @param withAssocs true - update associations too */ void UMLWidget::updateGeometry(bool withAssocs) { if (m_doc->loading()) { return; } if (!m_autoResize) return; qreal oldW = width(); qreal oldH = height(); QSizeF size = calculateSize(); qreal clipWidth = size.width(); qreal clipHeight = size.height(); constrain(clipWidth, clipHeight); setSize(clipWidth, clipHeight); slotSnapToGrid(); if (withAssocs) adjustAssocs(size.width()-oldW, size.height()-oldH); update(); } /** * clip the size of this widget against the * minimal and maximal limits. */ void UMLWidget::clipSize() { qreal clipWidth = width(); qreal clipHeight = height(); constrain(clipWidth, clipHeight); setSize(clipWidth, clipHeight); } /** * Template Method, override this to set the default font metric. */ void UMLWidget::setDefaultFontMetrics(QFont &font, UMLWidget::FontType fontType) { setupFontType(font, fontType); setFontMetrics(fontType, QFontMetrics(font)); } void UMLWidget::setupFontType(QFont &font, UMLWidget::FontType fontType) { switch (fontType) { case FT_NORMAL: font.setBold(false); font.setItalic(false); font.setUnderline(false); break; case FT_BOLD: font.setBold(true); font.setItalic(false); font.setUnderline(false); break; case FT_ITALIC: font.setBold(false); font.setItalic(true); font.setUnderline(false); break; case FT_UNDERLINE: font.setBold(false); font.setItalic(false); font.setUnderline(true); break; case FT_BOLD_ITALIC: font.setBold(true); font.setItalic(true); font.setUnderline(false); break; case FT_BOLD_UNDERLINE: font.setBold(true); font.setItalic(false); font.setUnderline(true); break; case FT_ITALIC_UNDERLINE: font.setBold(false); font.setItalic(true); font.setUnderline(true); break; case FT_BOLD_ITALIC_UNDERLINE: font.setBold(true); font.setItalic(true); font.setUnderline(true); break; default: return; } } void UMLWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(option); Q_UNUSED(widget); if (option->state & QStyle::State_Selected) { const qreal w = width(); const qreal h = height(); const qreal s = selectionMarkerSize; QBrush brush(Qt::blue); painter->fillRect(0, 0, s, s, brush); painter->fillRect(0, 0 + h - s, s, s, brush); painter->fillRect(0 + w - s, 0, s, s, brush); // Draw the resize anchor in the lower right corner. // Don't draw it if the widget is so small that the // resize anchor would cover up most of the widget. if (m_resizable && w >= s+8 && h >= s+8) { brush.setColor(Qt::red); const int right = 0 + w; const int bottom = 0 + h; painter->drawLine(right - s, 0 + h - 1, 0 + w - 1, 0 + h - s); painter->drawLine(right - (s*2), bottom - 1, right - 1, bottom - (s*2)); painter->drawLine(right - (s*3), bottom - 1, right - 1, bottom - (s*3)); } else { painter->fillRect(0 + w - s, 0 + h - s, s, s, brush); } // debug info if (Tracer::instance()->isEnabled(QLatin1String(metaObject()->className()))) { painter->setPen(Qt::green); painter->setBrush(Qt::NoBrush); painter->drawPath(shape()); painter->setPen(Qt::blue); painter->drawRect(boundingRect()); // origin painter->drawLine(-10, 0, 10, 0); painter->drawLine(0, -10, 0, 10); } } if (umlScene()->isShowDocumentationIndicator() && hasDocumentation()) { const qreal h = height(); const qreal d = 8; QPolygonF p; p << QPointF(0, h - d) << QPointF(d, h) << QPointF(0, h); painter->setPen(Qt::blue); painter->setBrush(Qt::red); painter->drawPolygon(p); } } /** * Template Method, override this to set the default font metric. */ void UMLWidget::setDefaultFontMetrics(QFont &font, UMLWidget::FontType fontType, QPainter &painter) { setupFontType(font, fontType); painter.setFont(font); setFontMetrics(fontType, painter.fontMetrics()); } /** * Returns the font metric used by this object for Text * which uses bold/italic fonts. */ QFontMetrics &UMLWidget::getFontMetrics(UMLWidget::FontType fontType) const { return *m_pFontMetrics[fontType]; } /** * Set the font metric to use. */ void UMLWidget::setFontMetrics(UMLWidget::FontType fontType, QFontMetrics fm) { delete m_pFontMetrics[fontType]; m_pFontMetrics[fontType] = new QFontMetrics(fm); } /** * Sets the font the widget is to use. * * @param font Font to be set. */ void UMLWidget::setFont(const QFont &font) { QFont newFont = font; forceUpdateFontMetrics(newFont, 0); if (m_font != newFont) { UMLApp::app()->executeCommand(new CmdChangeFont(this, font)); } } /** * Sets the font the widget is to use. * * @param font Font to be set. */ void UMLWidget::setFontCmd(const QFont &font) { WidgetBase::setFont(font); forceUpdateFontMetrics(0); if (m_doc->loading()) return; update(); } /** * Updates font metrics for widgets current m_font */ void UMLWidget::forceUpdateFontMetrics(QPainter *painter) { forceUpdateFontMetrics(m_font, painter); } /** * @note For performance Reasons, only FontMetrics for already used * font types are updated. Not yet used font types will not get a font metric * and will get the same font metric as if painter was zero. * This behaviour is acceptable, because diagrams will always be shown on Display * first before a special painter like a printer device is used. */ void UMLWidget::forceUpdateFontMetrics(QFont& font, QPainter *painter) { if (painter == 0) { for (int i = (int)FT_INVALID - 1; i >= 0; --i) { if (m_pFontMetrics[(UMLWidget::FontType)i] != 0) setDefaultFontMetrics(font, (UMLWidget::FontType)i); } } else { for (int i2 = (int)FT_INVALID - 1; i2 >= 0; --i2) { if (m_pFontMetrics[(UMLWidget::FontType)i2] != 0) setDefaultFontMetrics(font, (UMLWidget::FontType)i2, *painter); } } if (m_doc->loading()) return; // calculate the size, based on the new font metric updateGeometry(); } /** * Set the status of whether to show Stereotype. * * @param flag True if stereotype shall be shown. */ void UMLWidget::setShowStereotype(bool flag) { m_showStereotype = flag; updateGeometry(); update(); } /** * Returns the status of whether to show Stereotype. * * @return True if stereotype is shown. */ bool UMLWidget::showStereotype() const { return m_showStereotype; } /** * Overrides the standard operation. * * @param me The move event. */ void UMLWidget::moveEvent(QGraphicsSceneMouseEvent* me) { Q_UNUSED(me) } void UMLWidget::saveToXMI1(QDomDocument & qDoc, QDomElement & qElement) { /* Call after required actions in child class. Type must be set in the child class. */ WidgetBase::saveToXMI1(qDoc, qElement); DiagramProxyWidget::saveToXMI1(qDoc, qElement); qElement.setAttribute(QLatin1String("xmi.id"), Uml::ID::toString(id())); qreal dpiScale = UMLApp::app()->document()->dpiScale(); qElement.setAttribute(QLatin1String("x"), QString::number(x() / dpiScale)); qElement.setAttribute(QLatin1String("y"), QString::number(y() / dpiScale)); qElement.setAttribute(QLatin1String("width"), QString::number(width() / dpiScale)); qElement.setAttribute(QLatin1String("height"), QString::number(height() / dpiScale)); qElement.setAttribute(QLatin1String("isinstance"), m_isInstance); if (!m_instanceName.isEmpty()) qElement.setAttribute(QLatin1String("instancename"), m_instanceName); if (m_showStereotype) qElement.setAttribute(QLatin1String("showstereotype"), m_showStereotype); - - // Unique identifier for widget (todo: id() should be unique, new attribute - // should indicate the UMLObject's ID it belongs to) - qElement.setAttribute(QLatin1String("localid"), Uml::ID::toString(m_nLocalID)); } bool UMLWidget::loadFromXMI1(QDomElement & qElement) { QString id = qElement.attribute(QLatin1String("xmi.id"), QLatin1String("-1")); m_nId = Uml::ID::fromString(id); WidgetBase::loadFromXMI1(qElement); DiagramProxyWidget::loadFromXMI1(qElement); QString x = qElement.attribute(QLatin1String("x"), QLatin1String("0")); QString y = qElement.attribute(QLatin1String("y"), QLatin1String("0")); QString h = qElement.attribute(QLatin1String("height"), QLatin1String("0")); QString w = qElement.attribute(QLatin1String("width"), QLatin1String("0")); qreal dpiScale = UMLApp::app()->document()->dpiScale(); setSize(toDoubleFromAnyLocale(w) * dpiScale, toDoubleFromAnyLocale(h) * dpiScale); setX(toDoubleFromAnyLocale(x) * dpiScale); setY(toDoubleFromAnyLocale(y) * dpiScale); QString isinstance = qElement.attribute(QLatin1String("isinstance"), QLatin1String("0")); m_isInstance = (bool)isinstance.toInt(); m_instanceName = qElement.attribute(QLatin1String("instancename")); QString showstereo = qElement.attribute(QLatin1String("showstereotype"), QLatin1String("0")); m_showStereotype = (bool)showstereo.toInt(); - QString localid = qElement.attribute(QLatin1String("localid"), QLatin1String("0")); - if (localid != QLatin1String("0")) { - m_nLocalID = Uml::ID::fromString(localid); - } - return true; } /** * Adds a widget to the diagram, which is connected to the current widget * @param widget widget instance to add to diagram * @param type association type * @param options widget options */ void UMLWidget::addConnectedWidget(UMLWidget *widget, Uml::AssociationType::Enum type, AddWidgetOptions options) { umlScene()->addItem(widget); widget->setX(x() + rect().width() + 100); widget->setY(y()); if (options & SetupSize) { widget->setSize(100, 40); QSizeF size = widget->minimumSize(); widget->setSize(size); } AssociationWidget* assoc = options & SwitchDirection ? AssociationWidget::create(umlScene(), widget, type, this) : AssociationWidget::create(umlScene(), this, type, widget); umlScene()->addAssociation(assoc); umlScene()->clearSelected(); umlScene()->selectWidget(widget); if (options & ShowProperties) widget->showPropertiesDialog(); } /** * Adds a widget to the diagram, which is connected to the current widget * @param widget widget instance to add to diagram * @param showProperties whether to show properties of the widget */ void UMLWidget::addWidget(UMLWidget *widget, bool showProperties) { umlScene()->addItem(widget); widget->setX(x() + rect().width() + 100); widget->setY(y()); widget->setSize(100, 40); if (showProperties) widget->showPropertiesDialog(); QSizeF size = widget->minimumSize(); widget->setSize(size); } diff --git a/umbrello/umlwidgets/umlwidget.h b/umbrello/umlwidgets/umlwidget.h index b3d65c180..7d769e460 100644 --- a/umbrello/umlwidgets/umlwidget.h +++ b/umbrello/umlwidgets/umlwidget.h @@ -1,365 +1,359 @@ /*************************************************************************** * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * copyright (C) 2002-2014 * * Umbrello UML Modeller Authors * ***************************************************************************/ #ifndef UMLWIDGET_H #define UMLWIDGET_H #include "associationwidgetlist.h" #include "basictypes.h" #include "optionstate.h" #include "umlobject.h" #include "umlwidgetlist.h" #include "widgetbase.h" #include "diagramproxywidget.h" #include #include class IDChangeLog; class UMLDoc; class UMLObject; class UMLScene; class QPainter; class QFontMetrics; /** * This is the base class for nearly all graphical widgets. * * @short The base class for graphical UML objects. * @author Paul Hensgen * Bugs and comments to umbrello-devel@kde.org or http://bugs.kde.org */ class UMLWidget : public WidgetBase, public DiagramProxyWidget { Q_OBJECT public: friend class ToolBarStateArrow; // for calling the mouse*Event handlers static const QSizeF DefaultMinimumSize; static const QSizeF DefaultMaximumSize; static const int defaultMargin; static const int selectionMarkerSize; static const int resizeMarkerLineCount; explicit UMLWidget(UMLScene *scene, WidgetType type = wt_UMLWidget, UMLObject *o = 0); explicit UMLWidget(UMLScene *scene, WidgetType type = wt_UMLWidget, Uml::ID::Type id = Uml::ID::None); virtual ~UMLWidget(); // Copy constructor - not implemented. // UMLWidget(const UMLWidget& other); UMLWidget& operator=(const UMLWidget& other); bool operator==(const UMLWidget& other) const; - void setLocalID(Uml::ID::Type id); - Uml::ID::Type localID() const; - - virtual UMLWidget* widgetWithID(Uml::ID::Type id); - virtual QSizeF minimumSize() const; void setMinimumSize(const QSizeF &size); virtual QSizeF maximumSize(); void setMaximumSize(const QSizeF &size); virtual void setUseFillColor(bool fc); void setUseFillColorCmd(bool fc); virtual void setTextColor(const QColor &color); void setTextColorCmd(const QColor &color); virtual void setLineColor(const QColor &color); virtual void setLineColorCmd(const QColor &color); virtual void setLineWidth(uint width); void setLineWidthCmd(uint width); virtual void setFillColor(const QColor &color); void setFillColorCmd(const QColor &color); void setSelectedFlag(bool _select); virtual void setSelected(bool _select); void setScene(UMLScene *scene); virtual bool activate(IDChangeLog* changeLog = 0); void setPenFromSettings(QPainter &p); void setPenFromSettings(QPainter *p); virtual void setFont(const QFont &font); void setFontCmd(const QFont &font); /** * Returns whether we triggered the update of position movement. * If so, you probably don't want to move it. * * @return The moving state. */ bool getStartMove() const { return m_startMove; } virtual void setX(qreal x); virtual void setY(qreal y); /** * Returns the height of widget. */ qreal height() const { return rect().height(); } /** * Returns the width of the widget. */ qreal width() const { return rect().width(); } void setSize(qreal width, qreal height); void setSize(const QSizeF& size); virtual void resizeWidget(qreal newW, qreal newH); virtual void notifyParentResize(); bool getIgnoreSnapToGrid() const; void setIgnoreSnapToGrid(bool to); void moveByLocal(qreal dx, qreal dy); - void removeAssoc(AssociationWidget* pAssoc); - void addAssoc(AssociationWidget* pAssoc); + virtual void removeAssoc(AssociationWidget* pAssoc); + virtual void addAssoc(AssociationWidget* pAssoc); AssociationWidgetList &associationWidgetList() const; /** * Read property of bool m_isInstance */ bool isInstance() const { return m_isInstance; } /** * Write property of bool m_isInstance */ void setIsInstance(bool isInstance) { m_isInstance = isInstance; } /** * Write property of m_instanceName */ void setInstanceName(const QString &instanceName) { m_instanceName = instanceName; } /** * Read property of m_instanceName */ QString instanceName() const { return m_instanceName; } bool showStereotype() const; virtual void setShowStereotype(bool flag); virtual bool showPropertiesDialog(); virtual void adjustAssocs(qreal dx, qreal dy); virtual void adjustUnselectedAssocs(qreal dx, qreal dy); bool isActivated() const; void setActivated(bool active = true); virtual void cleanup(); static bool widgetHasUMLObject(WidgetBase::WidgetType type); void updateGeometry(bool withAssocs = true); void clipSize(); void forceUpdateFontMetrics(QPainter *painter); void forceUpdateFontMetrics(QFont &font, QPainter *painter); virtual bool loadFromXMI1(QDomElement &qElement); virtual void saveToXMI1(QDomDocument &qDoc, QDomElement &qElement); QPointF startMovePosition() const; void setStartMovePosition(const QPointF &position); QSizeF startResizeSize() const; virtual QSizeF calculateSize(bool withExtensions = true) const; void resize(); bool fixedAspectRatio() const { return m_fixedAspectRatio; } void setFixedAspectRatio(bool state) { m_fixedAspectRatio = state; } bool resizable() const { return m_resizable; } void setResizable(bool state) { m_resizable = state; } typedef enum { FT_NORMAL = 0, FT_BOLD = 1, FT_ITALIC = 2, FT_UNDERLINE = 3, FT_BOLD_ITALIC = 4, FT_BOLD_UNDERLINE = 5, FT_ITALIC_UNDERLINE = 6, FT_BOLD_ITALIC_UNDERLINE = 7, FT_INVALID = 8 } FontType; virtual void setDefaultFontMetrics(QFont &font, UMLWidget::FontType fontType); virtual void setDefaultFontMetrics(QFont &font, UMLWidget::FontType fontType, QPainter &painter); QFontMetrics &getFontMetrics(UMLWidget::FontType fontType) const; void setFontMetrics(UMLWidget::FontType fontType, QFontMetrics fm); void setupFontType(QFont &font, UMLWidget::FontType fontType); virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); public Q_SLOTS: virtual void updateWidget(); virtual void slotMenuSelection(QAction* action); virtual void slotWidgetMoved(Uml::ID::Type id); virtual void slotFillColorChanged(Uml::ID::Type viewID); virtual void slotLineColorChanged(Uml::ID::Type viewID); virtual void slotTextColorChanged(Uml::ID::Type viewID); virtual void slotLineWidthChanged(Uml::ID::Type viewID); void slotSnapToGrid(); signals: /** * Emit when the widget moves its' position. * @param id The id of the object behind the widget. */ void sigWidgetMoved(Uml::ID::Type id); protected: virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent* event); virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event); virtual void mousePressEvent(QGraphicsSceneMouseEvent *event); virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event); virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); virtual void moveEvent(QGraphicsSceneMouseEvent *event); virtual void moveWidgetBy(qreal diffX, qreal diffY); virtual void constrainMovementForAllWidgets(qreal &diffX, qreal &diffY); virtual void constrain(qreal& width, qreal& height); virtual bool isInResizeArea(QGraphicsSceneMouseEvent *me); virtual QCursor resizeCursor() const; void selectSingle(QGraphicsSceneMouseEvent *me); void selectMultiple(QGraphicsSceneMouseEvent *me); void deselect(QGraphicsSceneMouseEvent *me); // void resetSelection(); void setSelectionBounds(); void resize(QGraphicsSceneMouseEvent *me); bool wasSizeChanged(); bool wasPositionChanged(); virtual void toForeground(); public: enum AddWidgetOption { NoOption = 0, SetupSize = 1, SwitchDirection = 2, ShowProperties = 4, Default = SetupSize | ShowProperties }; Q_DECLARE_FLAGS(AddWidgetOptions, AddWidgetOption) protected: void addConnectedWidget(UMLWidget *widget, Uml::AssociationType::Enum type = Uml::AssociationType::Association, AddWidgetOptions options = Default); void addConnectedUMLObject(UMLObject::ObjectType otype, Uml::AssociationType::Enum type); void addWidget(UMLWidget *widget, bool showProperties = true); ///////////////// Data Loaded/Saved ///////////////////////////////// QString m_instanceName; ///< instance name (used if on a deployment diagram) bool m_isInstance; ///< holds whether this widget is a component instance (i.e. on a deployment diagram) bool m_showStereotype; ///< should the stereotype be displayed ///////////////// End of Data Loaded/Saved ////////////////////////// - Uml::ID::Type m_nLocalID; bool m_startMove; QPointF m_startMovePostion; QSizeF m_startResizeSize; int m_nPosX; UMLDoc *m_doc; ///< shortcut for UMLApp::app()->getDocument() bool m_resizable; QFontMetrics *m_pFontMetrics[FT_INVALID]; QSizeF m_minimumSize; QSizeF m_maximumSize; /// true if the activate function has been called for this class instance bool m_activated; /** * Change Widget Behaviour */ bool m_ignoreSnapToGrid; bool m_ignoreSnapComponentSizeToGrid; bool m_fixedAspectRatio; /// The text in the status bar when the cursor was pressed. QString m_oldStatusBarMsg; /// The X/Y offset from the position of the cursor when it was pressed to the /// upper left corner of the widget. QPointF m_pressOffset; /// The X/Y position the widget had when the movement started. QPointF m_oldPos; /// The width/height the widget had when the resize started. qreal m_oldW, m_oldH; /// If shift or control button were pressed in mouse press event. bool m_shiftPressed; /** * If cursor was in move/resize area when left button was pressed (and no * other widgets were selected). */ bool m_inMoveArea, m_inResizeArea; /** * If the widget was selected/moved/resized in the press and release cycle. * Moved/resized is true if the widget was moved/resized even if the final * position/size is the same as the starting one. */ bool m_moved, m_resized; private: void init(); /// A list of AssociationWidgets between the UMLWidget and other UMLWidgets in the diagram mutable AssociationWidgetList m_Assocs; }; Q_DECLARE_OPERATORS_FOR_FLAGS(UMLWidget::AddWidgetOptions) #endif diff --git a/umbrello/umlwidgets/widgetbase.cpp b/umbrello/umlwidgets/widgetbase.cpp index ca9819405..f8ea4051b 100644 --- a/umbrello/umlwidgets/widgetbase.cpp +++ b/umbrello/umlwidgets/widgetbase.cpp @@ -1,1348 +1,1421 @@ /*************************************************************************** * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * copyright (C) 2004-2014 * * Umbrello UML Modeller Authors * ***************************************************************************/ #include "widgetbase.h" #include "classifier.h" #include "debug_utils.h" #include "dialog_utils.h" #include "floatingtextwidget.h" #include "uml.h" #include "umldoc.h" #include "umllistview.h" #include "umlobject.h" #include "umlscene.h" #include "widgetbasepopupmenu.h" +#include "uniqueid.h" #if QT_VERSION < 0x050000 #include #include #endif #include #include #if QT_VERSION >= 0x050000 #include #include #endif #include /** * Creates a WidgetBase object. * * @param scene The view to be displayed on. * @param type The WidgetType to construct. This must be set to the appropriate * value by the constructors of inheriting classes. */ WidgetBase::WidgetBase(UMLScene *scene, WidgetType type) : QGraphicsObject(), m_baseType(type), m_scene(scene), m_umlObject(0), + m_nLocalID(UniqueID::gen()), m_textColor(QColor("black")), m_fillColor(QColor("yellow")), m_brush(m_fillColor), m_lineWidth(0), // initialize with 0 to have valid start condition m_useFillColor(true), m_usesDiagramFillColor(true), m_usesDiagramLineColor(true), m_usesDiagramLineWidth(true), m_usesDiagramTextColor(true), m_usesDiagramUseFillColor(true), m_autoResize(true), m_changesShape(false) { Q_ASSERT(m_baseType > wt_Min && m_baseType < wt_Max); // Note: no virtual methods from derived classes available, // this operation need to be finished in derived class constructor. setLineColor(QColor("black")); setSelected(false); // TODO 310283 setFlags(ItemIsSelectable); //setFlags(ItemIsSelectable | ItemIsMovable |ItemSendsGeometryChanges); if (m_scene) { m_usesDiagramLineColor = true; m_usesDiagramLineWidth = true; m_usesDiagramTextColor = true; const Settings::OptionState& optionState = m_scene->optionState(); m_textColor = optionState.uiState.textColor; setLineColor(optionState.uiState.lineColor); setLineWidth(optionState.uiState.lineWidth); m_font = optionState.uiState.font; } else { uError() << "WidgetBase constructor: SERIOUS PROBLEM - m_scene is NULL"; } } /** * Destructor. */ WidgetBase::~WidgetBase() { } /** * Read property of m_baseType. */ WidgetBase::WidgetType WidgetBase::baseType() const { Q_ASSERT(m_baseType > wt_Min && m_baseType < wt_Max); return m_baseType; } /** * Set property m_baseType. Used for types changing their types during runtime. */ void WidgetBase::setBaseType(const WidgetType& baseType) { Q_ASSERT(baseType > wt_Min && baseType < wt_Max); m_baseType = baseType; } /** * @return The type used for rtti as string. */ QLatin1String WidgetBase::baseTypeStr() const { Q_ASSERT(m_baseType > wt_Min && m_baseType < wt_Max); return QLatin1String(ENUM_NAME(WidgetBase, WidgetType, m_baseType)); } /* * Sets the state of whether the widget is selected. * * @param select The state of whether the widget is selected. */ void WidgetBase::setSelected(bool select) { QGraphicsObject::setSelected(select); } /** * Deliver a pointer to the connected UMLView * (needed esp. by event handling of AssociationLine). */ UMLScene* WidgetBase::umlScene() const { return m_scene; } /** * This is shortcut method for UMLApp::app()->document(). * * @return Pointer to the UMLDoc object. */ UMLDoc* WidgetBase::umlDoc() const { return UMLApp::app()->document(); } /** * Returns the @ref UMLObject set to represent. * * @return the UMLObject to represent. */ UMLObject* WidgetBase::umlObject() const { return m_umlObject; } /** * Sets the @ref UMLObject to represent. * * @param obj The object to represent. */ void WidgetBase::setUMLObject(UMLObject *obj) { m_umlObject = obj; } /** * Write property of m_nId. */ void WidgetBase::setID(Uml::ID::Type id) { if (m_umlObject) { if (m_umlObject->id() != Uml::ID::None) uWarning() << "changing old UMLObject " << Uml::ID::toString(m_umlObject->id()) << " to " << Uml::ID::toString(id); m_umlObject->setID(id); } m_nId = id; } /** * Read property of m_nId. */ Uml::ID::Type WidgetBase::id() const { if (m_umlObject) return m_umlObject->id(); return m_nId; } +/** + * Sets the local id of the object. + * + * @param id The local id of the object. + */ +void WidgetBase::setLocalID(Uml::ID::Type id) +{ + m_nLocalID = id; +} + +/** + * Returns the local ID for this object. This ID is used so that + * many objects of the same @ref UMLObject instance can be on the + * same diagram. + * + * @return The local ID. + */ +Uml::ID::Type WidgetBase::localID() const +{ + return m_nLocalID; +} + +/** + * Returns the widget with the given ID. + * The default implementation tests the following IDs: + * - m_nLocalID + * - if m_umlObject is non NULL: m_umlObject->id() + * - m_nID + * Composite widgets override this function to test further owned widgets. + * + * @param id The ID to test this widget against. + * @return 'this' if id is either of m_nLocalID, m_umlObject->id(), or m_nId; + * else NULL. + */ +UMLWidget* WidgetBase::widgetWithID(Uml::ID::Type id) +{ + if (id == m_nLocalID || + (m_umlObject != nullptr && id == m_umlObject->id()) || + id == m_nId) + return this->asUMLWidget(); + return nullptr; +} + /** * Used by some child classes to get documentation. * * @return The documentation from the UMLObject (if m_umlObject is set.) */ QString WidgetBase::documentation() const { if (m_umlObject) return m_umlObject->doc(); return m_Doc; } /** * Returns state of documentation for the widget. * * @return false if documentation is empty */ bool WidgetBase::hasDocumentation() { if (m_umlObject) return m_umlObject->hasDoc(); return !m_Doc.isEmpty(); } /** * Used by some child classes to set documentation. * * @param doc The documentation to be set in the UMLObject * (if m_umlObject is set.) */ void WidgetBase::setDocumentation(const QString& doc) { if (m_umlObject) m_umlObject->setDoc(doc); else m_Doc = doc; } /** * Gets the name from the corresponding UMLObject if this widget has an * underlying UMLObject; if it does not, then it returns the local * m_Text (notably the case for FloatingTextWidget.) * * @return the currently set name */ QString WidgetBase::name() const { if (m_umlObject) return m_umlObject->name(); return m_Text; } /** * Sets the name in the corresponding UMLObject. * Sets the local m_Text if m_umlObject is NULL. * * @param strName The name to be set. */ void WidgetBase::setName(const QString &strName) { if (m_umlObject) m_umlObject->setName(strName); else m_Text = strName; } /** * Returns text color * * @return currently used text color */ QColor WidgetBase::textColor() const { return m_textColor; } /** * Sets the text color * * @param color the new text color */ void WidgetBase::setTextColor(const QColor &color) { m_textColor = color; m_usesDiagramTextColor = false; } /** * Returns line color * * @return currently used line color */ QColor WidgetBase::lineColor() const { return m_lineColor; } /** * Sets the line color * * @param color The new line color */ void WidgetBase::setLineColor(const QColor &color) { m_lineColor = color; m_usesDiagramLineColor = false; } /** * Returns fill color * * @return currently used fill color */ QColor WidgetBase::fillColor() const { return m_fillColor; } /** * Sets the fill color * * @param color The new fill color */ void WidgetBase::setFillColor(const QColor &color) { m_fillColor = color; m_usesDiagramFillColor = false; } /** * Returns line width * * @return currently used line with */ uint WidgetBase::lineWidth() const { return m_lineWidth; } /** * Sets the line width * * @param width The new line width */ void WidgetBase::setLineWidth(uint width) { m_lineWidth = width; m_usesDiagramLineWidth = false; } /** * Return state of fill color usage * * @return True if fill color is used */ bool WidgetBase::useFillColor() { return m_useFillColor; } /** * Set state if fill color is used * * @param state The state to set */ void WidgetBase::setUseFillColor(bool state) { m_useFillColor = state; m_usesDiagramUseFillColor = false; } /** * Returns state if diagram text color is used * * @return True means diagram text color is used */ bool WidgetBase::usesDiagramTextColor() const { return m_usesDiagramTextColor; } /** * Set state if diagram text color is used * * @param state The state to set */ void WidgetBase::setUsesDiagramTextColor(bool state) { if (m_usesDiagramTextColor == state) { return; } m_usesDiagramTextColor = state; setTextColor(m_textColor); } /** * Returns state of diagram line color is used * * @return True means diagrams line color is used */ bool WidgetBase::usesDiagramLineColor() const { return m_usesDiagramLineColor; } /** * Set state of diagram line color is used * * @param state The state to set */ void WidgetBase::setUsesDiagramLineColor(bool state) { m_usesDiagramLineColor = state; } /** * Returns state of diagram fill color is used * * @return True means diagrams fill color is used */ bool WidgetBase::usesDiagramFillColor() const { return m_usesDiagramFillColor; } /** * Set state if diagram fill color is used * * @param state The state to set */ void WidgetBase::setUsesDiagramFillColor(bool state) { m_usesDiagramFillColor = state; } /** * Returns state of diagram use fill color is used * * @return True means diagrams fill color is used */ bool WidgetBase::usesDiagramUseFillColor() const { return m_usesDiagramUseFillColor; } /** * Set state of diagram use fill color is used * * @param state The state to set */ void WidgetBase::setUsesDiagramUseFillColor(bool state) { m_usesDiagramUseFillColor = state; } /** * Returns state of diagram line width is used * * @return True means diagrams line width is used */ bool WidgetBase::usesDiagramLineWidth() const { return m_usesDiagramLineWidth; } /** * Set state of diagram line width is used * * @param state The state to set */ void WidgetBase::setUsesDiagramLineWidth(bool state) { m_usesDiagramLineWidth = state; } /** * Returns the font used for diaplaying any text. * @return the font */ QFont WidgetBase::font() const { return m_font; } /** * Set the font used to display text inside this widget. */ void WidgetBase::setFont(const QFont& font) { m_font = font; } /** * Return state of auto resize property * @return the auto resize state */ bool WidgetBase::autoResize() { return m_autoResize; } /** * set auto resize state * @param state */ void WidgetBase::setAutoResize(bool state) { m_autoResize = state; } /** * Return changes state property * @return the changes shape state */ bool WidgetBase::changesShape() { return m_changesShape; } /** * set changes shape property * @param state */ void WidgetBase::setChangesShape(bool state) { m_changesShape = state; } /** * A virtual method for the widget to display a property dialog box. * Subclasses should reimplment this appropriately. * In case the user cancels the dialog or there are some requirements * not fulfilled the method returns false; true otherwise. * * @return true - properties has been applyed * @return false - properties has not been applied * */ bool WidgetBase::showPropertiesDialog() { return false; } /** * A virtual method to save the properties of this widget into a * QDomElement i.e xml. * * Subclasses should first create a new dedicated element as the child * of \a qElement parameter passed. Then this base method should be * called to save basic widget properties. * * @param qDoc A QDomDocument object representing the xml document. * @param qElement A QDomElement representing xml element data. */ void WidgetBase::saveToXMI1(QDomDocument& qDoc, QDomElement& qElement) { Q_UNUSED(qDoc) + // Unique identifier for widget (todo: id() should be unique, new attribute + // should indicate the UMLObject's ID it belongs to) + qElement.setAttribute(QLatin1String("localid"), Uml::ID::toString(m_nLocalID)); + qElement.setAttribute(QLatin1String("textcolor"), m_usesDiagramTextColor ? QLatin1String("none") : m_textColor.name()); if (m_usesDiagramLineColor) { qElement.setAttribute(QLatin1String("linecolor"), QLatin1String("none")); } else { qElement.setAttribute(QLatin1String("linecolor"), m_lineColor.name()); } if (m_usesDiagramLineWidth) { qElement.setAttribute(QLatin1String("linewidth"), QLatin1String("none")); } else { qElement.setAttribute(QLatin1String("linewidth"), m_lineWidth); } qElement.setAttribute(QLatin1String("usefillcolor"), m_useFillColor); // for consistency the following attributes now use american spelling for "color" qElement.setAttribute(QLatin1String("usesdiagramfillcolor"), m_usesDiagramFillColor); qElement.setAttribute(QLatin1String("usesdiagramusefillcolor"), m_usesDiagramUseFillColor); if (m_usesDiagramFillColor) { qElement.setAttribute(QLatin1String("fillcolor"), QLatin1String("none")); } else { qElement.setAttribute(QLatin1String("fillcolor"), m_fillColor.name()); } qElement.setAttribute(QLatin1String("font"), m_font.toString()); qElement.setAttribute(QLatin1String("autoresize"), m_autoResize ? 1 : 0); } +/** + * Adds an already created association to the list of + * associations that include this UMLWidget + */ +void WidgetBase::addAssoc(AssociationWidget *pAssoc) +{ + Q_UNUSED(pAssoc); +} + +/** + * Removes an already created association from the list of + * associations that include this UMLWidget + */ +void WidgetBase::removeAssoc(AssociationWidget *pAssoc) +{ + Q_UNUSED(pAssoc); +} + /** * A virtual method to load the properties of this widget from a * QDomElement into this widget. * * Subclasses should reimplement this to load addtional properties * required, calling this base method to load the basic properties of * the widget. * * @param qElement A QDomElement which contains xml info for this widget. * * @todo Add support to load older version. */ bool WidgetBase::loadFromXMI1(QDomElement& qElement) { + QString localid = qElement.attribute(QLatin1String("localid"), QLatin1String("0")); + if (localid != QLatin1String("0")) { + m_nLocalID = Uml::ID::fromString(localid); + } + // first load from "linecolour" and then overwrite with the "linecolor" // attribute if that one is present. The "linecolour" name was a "typo" in // earlier versions of Umbrello QString lineColor = qElement.attribute(QLatin1String("linecolour"), QLatin1String("none")); lineColor = qElement.attribute(QLatin1String("linecolor"), lineColor); if (lineColor != QLatin1String("none")) { setLineColor(QColor(lineColor)); m_usesDiagramLineColor = false; } else if (m_baseType != WidgetBase::wt_Box && m_scene != 0) { setLineColor(m_scene->lineColor()); m_usesDiagramLineColor = true; } QString lineWidth = qElement.attribute(QLatin1String("linewidth"), QLatin1String("none")); if (lineWidth != QLatin1String("none")) { setLineWidth(lineWidth.toInt()); m_usesDiagramLineWidth = false; } else if (m_scene) { setLineWidth(m_scene->lineWidth()); m_usesDiagramLineWidth = true; } QString textColor = qElement.attribute(QLatin1String("textcolor"), QLatin1String("none")); if (textColor != QLatin1String("none")) { m_textColor = QColor(textColor); m_usesDiagramTextColor = false; } else if (m_scene) { m_textColor = m_scene->textColor(); m_usesDiagramTextColor = true; } QString usefillcolor = qElement.attribute(QLatin1String("usefillcolor"), QLatin1String("1")); m_useFillColor = (bool)usefillcolor.toInt(); /* For the next three *color attributes, there was a mixup of american and english spelling for "color". So first we need to keep backward compatibility and try to retrieve the *colour attribute. Next we overwrite this value if we find a *color, otherwise the former *colour is kept. */ QString fillColor = qElement.attribute(QLatin1String("fillcolour"), QLatin1String("none")); fillColor = qElement.attribute(QLatin1String("fillcolor"), fillColor); if (fillColor != QLatin1String("none")) { m_fillColor = QColor(fillColor); } QString usesDiagramFillColor = qElement.attribute(QLatin1String("usesdiagramfillcolour"), QLatin1String("1")); usesDiagramFillColor = qElement.attribute(QLatin1String("usesdiagramfillcolor"), usesDiagramFillColor); m_usesDiagramFillColor = (bool)usesDiagramFillColor.toInt(); QString usesDiagramUseFillColor = qElement.attribute(QLatin1String("usesdiagramusefillcolour"), QLatin1String("1")); usesDiagramUseFillColor = qElement.attribute(QLatin1String("usesdiagramusefillcolor"), usesDiagramUseFillColor); m_usesDiagramUseFillColor = (bool)usesDiagramUseFillColor.toInt(); QString font = qElement.attribute(QLatin1String("font")); if (!font.isEmpty()) { QFont newFont; newFont.fromString(font); m_font = newFont; } else { uWarning() << "Using default font " << m_font.toString() << " for widget with xmi.id " << Uml::ID::toString(m_nId); } QString autoResize = qElement.attribute(QLatin1String("autoresize"), QLatin1String("1")); m_autoResize = (bool)autoResize.toInt(); return true; } /** * Assignment operator */ WidgetBase& WidgetBase::operator=(const WidgetBase& other) { m_baseType = other.m_baseType; m_scene = other.m_scene; m_umlObject = other.m_umlObject; m_Doc = other.m_Doc; m_Text = other.m_Text; m_nId = other.m_nId; + m_nLocalID = other.m_nLocalID; m_textColor = other.m_textColor; setLineColor(other.lineColor()); m_fillColor = other.m_fillColor; m_brush = other.m_brush; m_font = other.m_font; m_lineWidth = other.m_lineWidth; m_useFillColor = other.m_useFillColor; m_usesDiagramTextColor = other.m_usesDiagramTextColor; m_usesDiagramLineColor = other.m_usesDiagramLineColor; m_usesDiagramFillColor = other.m_usesDiagramFillColor; m_usesDiagramLineWidth = other.m_usesDiagramLineWidth; setSelected(other.isSelected()); return *this; } /** * return drawing rectangle of widget in local coordinates */ QRectF WidgetBase::rect() const { return m_rect; } /** * set widget rectangle in item coordinates */ void WidgetBase::setRect(const QRectF& rect) { if (m_rect == rect) return; prepareGeometryChange(); m_rect = rect; update(); } /** * set widget rectangle in item coordinates */ void WidgetBase::setRect(qreal x, qreal y, qreal width, qreal height) { setRect(QRectF(x, y, width, height)); } /** * @return The bounding rectangle for this widget. * @see setRect */ QRectF WidgetBase::boundingRect() const { int halfWidth = lineWidth()/2; return m_rect.adjusted(-halfWidth, -halfWidth, halfWidth, halfWidth); } /** * Test if point is inside the bounding rectangle of the widget. * Inheriting classes may reimplement this to test possible child widgets. * * @param p Point to be checked. * * @return 'this' if the given point is in the boundaries of the widget; * else NULL. */ UMLWidget* WidgetBase::onWidget(const QPointF &p) { UMLWidget *uw = this->asUMLWidget(); if (uw == 0) return 0; const qreal w = m_rect.width(); const qreal h = m_rect.height(); const qreal left = x(); // don't use m_rect.x() for this, it is always 0 const qreal right = left + w; const qreal top = y(); // don't use m_rect.y() for this, it is always 0 const qreal bottom = top + h; // uDebug() << "p=(" << p.x() << "," << p.y() // << "), x=" << left << ", y=" << top << ", w=" << w << ", h=" << h // << "; right=" << right << ", bottom=" << bottom; if (p.x() < left || p.x() > right || p.y() < top || p.y() > bottom) { // Qt coord.sys. origin in top left corner // uDebug() << "returning NULL"; return 0; } // uDebug() << "returning this"; return uw; } /** * Draws the UMLWidget on the given paint device * * @param painter The painter for the drawing device * @param option Painting related options * @param widget Background widget on which to paint (optional) * */ void WidgetBase::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(painter); Q_UNUSED(option); Q_UNUSED(widget); } /** * Reimplemented to show appropriate context menu. */ void WidgetBase::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { event->accept(); uDebug() << "widget = " << name() << " / type = " << baseTypeStr(); UMLScene *scene = umlScene(); // If right-click was done on a widget that was not selected, clear the // current selection and select the new widget. The context menu is shown // with actions for that single widget. // If a keyboard modifier was used, add the widget to the current selection // and show the menu with actions for the whole selection. if (!isSelected()) { Qt::KeyboardModifiers forSelection = (Qt::ControlModifier | Qt::ShiftModifier); if ((event->modifiers() & forSelection) == 0) { scene->clearSelected(); } if (umlObject() != 0) { scene->selectWidget(this->asUMLWidget()); } else { setSelected(true); } } int count = scene->selectedCount(true); // Determine multi state bool multi = (isSelected() && count > 1); WidgetBasePopupMenu popup(0, this, multi, scene->getUniqueSelectionType()); // Disable the "view code" menu for simple code generators if (UMLApp::app()->isSimpleCodeGeneratorActive()) { popup.setActionEnabled(ListPopupMenu::mt_ViewCode, false); } QAction *triggered = popup.exec(event->screenPos()); slotMenuSelection(triggered); } /** * This is usually called synchronously after menu.exec() and \a * trigger's parent is always the ListPopupMenu which can be used to * get the type of action of \a trigger. * * @note Subclasses can reimplement to handle specific actions and * leave the rest to WidgetBase::slotMenuSelection. */ void WidgetBase::slotMenuSelection(QAction *trigger) { if (!trigger) { return; } QColor newColor; const WidgetType wt = m_baseType; // short hand name ListPopupMenu::MenuType sel = ListPopupMenu::typeFromAction(trigger); switch (sel) { case ListPopupMenu::mt_Rename: umlDoc()->renameUMLObject(umlObject()); break; case ListPopupMenu::mt_Properties: if (wt == WidgetBase::wt_Actor || wt == WidgetBase::wt_UseCase || wt == WidgetBase::wt_Package || wt == WidgetBase::wt_Interface || wt == WidgetBase::wt_Datatype || wt == WidgetBase::wt_Node || wt == WidgetBase::wt_Component || wt == WidgetBase::wt_Artifact || wt == WidgetBase::wt_Enum || wt == WidgetBase::wt_Entity || wt == WidgetBase::wt_Port || wt == WidgetBase::wt_Instance || (wt == WidgetBase::wt_Class && umlScene()->isClassDiagram())) { showPropertiesDialog(); } else if (wt == WidgetBase::wt_Object) { m_umlObject->showPropertiesDialog(); } else { uWarning() << "making properties dialog for unknown widget type"; } break; case ListPopupMenu::mt_Line_Color: case ListPopupMenu::mt_Line_Color_Selection: #if QT_VERSION >= 0x050000 newColor = QColorDialog::getColor(lineColor()); if (newColor != lineColor()) { #else newColor = lineColor(); if (KColorDialog::getColor(newColor)) { #endif if (sel == ListPopupMenu::mt_Line_Color_Selection) { umlScene()->selectionSetLineColor(newColor); } else { setLineColor(newColor); } setUsesDiagramLineColor(false); umlDoc()->setModified(true); } break; case ListPopupMenu::mt_Fill_Color: case ListPopupMenu::mt_Fill_Color_Selection: #if QT_VERSION >= 0x050000 newColor = QColorDialog::getColor(fillColor()); if (newColor != fillColor()) { #else newColor = fillColor(); if (KColorDialog::getColor(newColor)) { #endif if (sel == ListPopupMenu::mt_Fill_Color_Selection) { umlScene()->selectionSetFillColor(newColor); } else { setFillColor(newColor); } umlDoc()->setModified(true); } break; case ListPopupMenu::mt_Use_Fill_Color: setUseFillColor(!m_useFillColor); break; case ListPopupMenu::mt_Set_Use_Fill_Color_Selection: umlScene()->selectionUseFillColor(true); break; case ListPopupMenu::mt_Unset_Use_Fill_Color_Selection: umlScene()->selectionUseFillColor(false); break; case ListPopupMenu::mt_Show_Attributes_Selection: case ListPopupMenu::mt_Hide_Attributes_Selection: umlScene()->selectionSetVisualProperty( ClassifierWidget::ShowAttributes, sel != ListPopupMenu::mt_Hide_Attributes_Selection ); break; case ListPopupMenu::mt_Show_Operations_Selection: case ListPopupMenu::mt_Hide_Operations_Selection: umlScene()->selectionSetVisualProperty( ClassifierWidget::ShowOperations, sel != ListPopupMenu::mt_Hide_Operations_Selection ); break; case ListPopupMenu::mt_Show_Visibility_Selection: case ListPopupMenu::mt_Hide_Visibility_Selection: umlScene()->selectionSetVisualProperty( ClassifierWidget::ShowVisibility, sel != ListPopupMenu::mt_Hide_Visibility_Selection ); break; case ListPopupMenu::mt_Show_Operation_Signature_Selection: case ListPopupMenu::mt_Hide_Operation_Signature_Selection: umlScene()->selectionSetVisualProperty( ClassifierWidget::ShowOperationSignature, sel != ListPopupMenu::mt_Hide_Operation_Signature_Selection ); break; case ListPopupMenu::mt_Show_Attribute_Signature_Selection: case ListPopupMenu::mt_Hide_Attribute_Signature_Selection: umlScene()->selectionSetVisualProperty( ClassifierWidget::ShowAttributeSignature, sel != ListPopupMenu::mt_Hide_Attribute_Signature_Selection ); break; case ListPopupMenu::mt_Show_Packages_Selection: case ListPopupMenu::mt_Hide_Packages_Selection: umlScene()->selectionSetVisualProperty( ClassifierWidget::ShowPackage, sel != ListPopupMenu::mt_Hide_Packages_Selection ); break; case ListPopupMenu::mt_Show_Stereotypes_Selection: case ListPopupMenu::mt_Hide_Stereotypes_Selection: umlScene()->selectionSetVisualProperty( ClassifierWidget::ShowStereotype, sel != ListPopupMenu::mt_Hide_Stereotypes_Selection ); break; case ListPopupMenu::mt_Hide_NonPublic_Selection: case ListPopupMenu::mt_Show_NonPublic_Selection: umlScene()->selectionSetVisualProperty( ClassifierWidget::ShowPublicOnly, sel != ListPopupMenu::mt_Show_NonPublic_Selection ); break; case ListPopupMenu::mt_ViewCode: { UMLClassifier *c = umlObject()->asUMLClassifier(); if (c) { UMLApp::app()->viewCodeDocument(c); } break; } case ListPopupMenu::mt_Remove: umlScene()->deleteSelection(); break; case ListPopupMenu::mt_Delete: if (!Dialog_Utils::askDeleteAssociation()) break; umlScene()->deleteSelection(); break; case ListPopupMenu::mt_Change_Font: case ListPopupMenu::mt_Change_Font_Selection: { #if QT_VERSION >= 0x050000 bool ok = false; QFont newFont = QFontDialog::getFont(&ok, font()); if (ok) { #else QFont newFont = font(); if (KFontDialog::getFont(newFont, KFontChooser::NoDisplayFlags, 0) == KFontDialog::Accepted) { #endif if (sel == ListPopupMenu::mt_Change_Font_Selection) { m_scene->selectionSetFont(newFont); } else { setFont(newFont); } } } break; case ListPopupMenu::mt_Cut: umlScene()->setStartedCut(); UMLApp::app()->slotEditCut(); break; case ListPopupMenu::mt_Copy: UMLApp::app()->slotEditCopy(); break; case ListPopupMenu::mt_Paste: UMLApp::app()->slotEditPaste(); break; case ListPopupMenu::mt_Refactoring: //check if we are operating on a classifier, or some other kind of UMLObject if (umlObject()->asUMLClassifier()) { UMLApp::app()->refactor(umlObject()->asUMLClassifier()); } break; case ListPopupMenu::mt_Clone: { foreach (UMLWidget* widget, umlScene()->selectedWidgets()) { if (Model_Utils::isCloneable(widget->baseType())) { UMLObject *clone = widget->umlObject()->clone(); umlScene()->addObject(clone); } } } break; case ListPopupMenu::mt_Rename_MultiA: case ListPopupMenu::mt_Rename_MultiB: case ListPopupMenu::mt_Rename_Name: case ListPopupMenu::mt_Rename_RoleAName: case ListPopupMenu::mt_Rename_RoleBName: { FloatingTextWidget *ft = static_cast(this); ft->handleRename(); break; } case ListPopupMenu::mt_Align_Right: umlScene()->alignRight(); break; case ListPopupMenu::mt_Align_Left: umlScene()->alignLeft(); break; case ListPopupMenu::mt_Align_Top: umlScene()->alignTop(); break; case ListPopupMenu::mt_Align_Bottom: umlScene()->alignBottom(); break; case ListPopupMenu::mt_Align_VerticalMiddle: umlScene()->alignVerticalMiddle(); break; case ListPopupMenu::mt_Align_HorizontalMiddle: umlScene()->alignHorizontalMiddle(); break; case ListPopupMenu::mt_Align_VerticalDistribute: umlScene()->alignVerticalDistribute(); break; case ListPopupMenu::mt_Align_HorizontalDistribute: umlScene()->alignHorizontalDistribute(); break; default: uDebug() << "MenuType " << ListPopupMenu::toString(sel) << " not implemented"; break; } } /** * Helper function for debug output. * Returns the given enum value as string. * @param wt WidgetType of which a string representation is wanted * @return the WidgetType as string */ QString WidgetBase::toString(WidgetType wt) { return QLatin1String(ENUM_NAME(WidgetBase, WidgetType, wt)); } /** * Returns the given enum value as localized string. * @param wt WidgetType of which a string representation is wanted * @return the WidgetType as localized string */ QString WidgetBase::toI18nString(WidgetType wt) { QString name; switch (wt) { case wt_Activity: name = i18n("Activity"); break; case wt_Actor: name = i18n("Actor"); break; case wt_Artifact: name = i18n("Artifact"); break; case wt_Association: name = i18n("Association"); break; case wt_Box: name = i18n("Box"); break; case wt_Category: name = i18n("Category"); break; case wt_CombinedFragment: name = i18n("CombinedFragment"); break; case wt_Component: name = i18n("Component"); break; case wt_Class: name = i18n("Class"); break; case wt_Datatype: name = i18n("Datatype"); break; case wt_Entity: name = i18n("Entity"); break; case wt_Enum: name = i18n("Enum"); break; case wt_FloatingDashLine: name = i18n("FloatingDashLine"); break; case wt_ForkJoin: name = i18n("ForkJoin"); break; case wt_Interface: name = i18n("Interface"); break; case wt_Message: name = i18n("Message"); break; case wt_Node: name = i18n("Node"); break; case wt_Note: name = i18n("Note"); break; case wt_Object: name = i18n("Object"); break; case wt_ObjectNode: name = i18n("ObjectNode"); break; case wt_Package: name = i18n("Package"); break; case wt_Pin: name = i18n("Pin"); break; case wt_Port: name = i18n("Port"); break; case wt_Precondition: name = i18n("Precondition"); break; case wt_Region: name = i18n("Region"); break; case wt_Signal: name = i18n("Signal"); break; case wt_State: name = i18n("State"); break; case wt_Text: name = i18n("Text"); break; case wt_UseCase: name = i18n("UseCase"); break; case wt_Instance: name = i18n("Instance"); break; default: name = QLatin1String(" &name:"); uWarning() << "unknown widget type"; break; } return name; } /** * Returns the given enum value as icon type. * @param wt WidgetType of which an icon type representation is wanted * @return the WidgetType as icon type */ Icon_Utils::IconType WidgetBase::toIcon(WidgetBase::WidgetType wt) { Icon_Utils::IconType icon; switch (wt) { case wt_Activity: icon = Icon_Utils::it_Activity; break; case wt_Actor: icon = Icon_Utils::it_Actor; break; case wt_Artifact: icon = Icon_Utils::it_Artifact; break; case wt_Association: icon = Icon_Utils::it_Association; break; case wt_Box: icon = Icon_Utils::it_Box; break; case wt_Category: icon = Icon_Utils::it_Category; break; case wt_CombinedFragment: icon = Icon_Utils::it_Combined_Fragment; break; case wt_Component: icon = Icon_Utils::it_Component; break; case wt_Class: icon = Icon_Utils::it_Class; break; case wt_Datatype: icon = Icon_Utils::it_Datatype; break; case wt_Entity: icon = Icon_Utils::it_Entity; break; case wt_Enum: icon = Icon_Utils::it_Enum; break; case wt_FloatingDashLine: icon = Icon_Utils::it_Association; break; case wt_ForkJoin: icon = Icon_Utils::it_Fork_Join; break; case wt_Instance: icon = Icon_Utils::it_Instance; break; case wt_Interface: icon = Icon_Utils::it_Interface; break; case wt_Message: icon = Icon_Utils::it_Message_Synchronous; break; case wt_Node: icon = Icon_Utils::it_Node; break; case wt_Note: icon = Icon_Utils::it_Note; break; case wt_Object: icon = Icon_Utils::it_Object; break; case wt_ObjectNode: icon = Icon_Utils::it_Object_Node; break; case wt_Package: icon = Icon_Utils::it_Package; break; case wt_Pin: icon = Icon_Utils::it_Pin; break; case wt_Port: icon = Icon_Utils::it_Port; break; case wt_Precondition: icon = Icon_Utils::it_Precondition; break; case wt_Region: icon = Icon_Utils::it_Region; break; case wt_Signal: icon = Icon_Utils::it_Send_Signal; break; case wt_State: icon = Icon_Utils::it_State; break; case wt_Text: icon = Icon_Utils::it_Text; break; case wt_UseCase: icon = Icon_Utils::it_UseCase; break; default: icon = Icon_Utils::it_Home; uWarning() << "unknown widget type"; break; } return icon; } #include "activitywidget.h" #include "actorwidget.h" #include "artifactwidget.h" #include "associationwidget.h" #include "boxwidget.h" #include "categorywidget.h" //#include "classwidget.h" #include "combinedfragmentwidget.h" #include "componentwidget.h" #include "datatypewidget.h" #include "entitywidget.h" #include "enumwidget.h" #include "floatingdashlinewidget.h" #include "forkjoinwidget.h" #include "interfacewidget.h" #include "messagewidget.h" #include "nodewidget.h" #include "notewidget.h" #include "objectnodewidget.h" #include "objectwidget.h" #include "packagewidget.h" #include "pinwidget.h" #include "portwidget.h" #include "preconditionwidget.h" #include "regionwidget.h" #include "signalwidget.h" #include "statewidget.h" #include "usecasewidget.h" ActivityWidget* WidgetBase::asActivityWidget() { return dynamic_cast(this); } ActorWidget* WidgetBase::asActorWidget() { return dynamic_cast(this); } ArtifactWidget* WidgetBase::asArtifactWidget() { return dynamic_cast(this); } AssociationWidget* WidgetBase::asAssociationWidget() { return dynamic_cast(this); } BoxWidget* WidgetBase::asBoxWidget() { return dynamic_cast(this); } CategoryWidget* WidgetBase::asCategoryWidget() { return dynamic_cast(this); } ClassifierWidget* WidgetBase::asClassifierWidget() { return dynamic_cast(this); } CombinedFragmentWidget* WidgetBase::asCombinedFragmentWidget() { return dynamic_cast(this); } ComponentWidget* WidgetBase::asComponentWidget() { return dynamic_cast(this); } DatatypeWidget* WidgetBase::asDatatypeWidget() { return dynamic_cast(this); } EntityWidget* WidgetBase::asEntityWidget() { return dynamic_cast(this); } EnumWidget* WidgetBase::asEnumWidget() { return dynamic_cast(this); } FloatingDashLineWidget* WidgetBase::asFloatingDashLineWidget() { return dynamic_cast(this); } ForkJoinWidget* WidgetBase::asForkJoinWidget() { return dynamic_cast(this); } InterfaceWidget* WidgetBase::asInterfaceWidget() { return dynamic_cast(this); } MessageWidget* WidgetBase::asMessageWidget() { return dynamic_cast(this); } NodeWidget* WidgetBase::asNodeWidget() { return dynamic_cast(this); } NoteWidget* WidgetBase::asNoteWidget() { return dynamic_cast(this); } ObjectNodeWidget* WidgetBase::asObjectNodeWidget() { return dynamic_cast(this); } ObjectWidget* WidgetBase::asObjectWidget() { return dynamic_cast(this); } PackageWidget* WidgetBase::asPackageWidget() { return dynamic_cast(this); } PinWidget* WidgetBase::asPinWidget() { return dynamic_cast(this); } PinPortBase *WidgetBase::asPinPortBase() { return dynamic_cast(this); } PortWidget* WidgetBase::asPortWidget() { return dynamic_cast(this); } PreconditionWidget* WidgetBase::asPreconditionWidget() { return dynamic_cast(this); } RegionWidget* WidgetBase::asRegionWidget() { return dynamic_cast(this); } SignalWidget* WidgetBase::asSignalWidget() { return dynamic_cast(this); } StateWidget* WidgetBase::asStateWidget() { return dynamic_cast(this); } FloatingTextWidget* WidgetBase::asFloatingTextWidget() { return dynamic_cast(this); } //TextWidget* WidgetBase::asTextWidget() { return dynamic_cast(this); } UseCaseWidget* WidgetBase::asUseCaseWidget() { return dynamic_cast(this); } UMLWidget *WidgetBase::asUMLWidget() { return dynamic_cast(this); } diff --git a/umbrello/umlwidgets/widgetbase.h b/umbrello/umlwidgets/widgetbase.h index ce668ba19..32b685920 100644 --- a/umbrello/umlwidgets/widgetbase.h +++ b/umbrello/umlwidgets/widgetbase.h @@ -1,303 +1,316 @@ /*************************************************************************** * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * copyright (C) 2004-2014 * * Umbrello UML Modeller Authors * ***************************************************************************/ #ifndef WIDGETBASE_H #define WIDGETBASE_H #include "basictypes.h" #include "icon_utils.h" #include #include #include #include #include #include #include // forward declarations class QAction; class ActivityWidget; class ActorWidget; class ArtifactWidget; class AssociationWidget; class BoxWidget; class CategoryWidget; class ClassifierWidget; class CombinedFragmentWidget; class ComponentWidget; class DatatypeWidget; class EntityWidget; class EnumWidget; class FloatingDashLineWidget; class FloatingTextWidget; class ForkJoinWidget; class InterfaceWidget; class MessageWidget; class NodeWidget; class NoteWidget; class ObjectNodeWidget; class ObjectWidget; class PackageWidget; class PinWidget; class PortWidget; class PinPortBase; class PreconditionWidget; class RegionWidget; class SignalWidget; class StateWidget; //class TextWidget; class UseCaseWidget; class UMLDoc; class UMLObject; class UMLScene; class UMLWidget; // required by function onWidget() /** * @short Common base class for UMLWidget and AssociationWidget * @author Oliver Kellogg * Bugs and comments to umbrello-devel@kde.org or http://bugs.kde.org */ class WidgetBase : public QGraphicsObject { Q_OBJECT Q_ENUMS(WidgetType) public: enum WidgetType { wt_Min = 299, // lower bounds check value wt_UMLWidget, // does not have UMLObject representation wt_Actor, // has UMLObject representation wt_UseCase, // has UMLObject representation wt_Class, // has UMLObject representation wt_Interface, // has UMLObject representation wt_Datatype, // has UMLObject representation wt_Enum, // has UMLObject representation wt_Entity, // has UMLObject representation wt_Package, // has UMLObject representation wt_Object, // has UMLObject representation wt_Note, // does not have UMLObject representation wt_Box, // does not have UMLObject representation wt_Message, // does not have UMLObject representation wt_Text, // does not have UMLObject representation wt_State, // does not have UMLObject representation wt_Activity, // does not have UMLObject representation wt_Component, // has UMLObject representation wt_Artifact, // has UMLObject representation wt_Node, // has UMLObject representation wt_Association, // has UMLObject representation wt_ForkJoin, // does not have UMLObject representation wt_Precondition, // does not have UMLObject representation wt_CombinedFragment, // does not have UMLObject representation wt_FloatingDashLine, // does not have UMLObject representation wt_Signal, // does not have UMLObject representation wt_Pin, wt_ObjectNode, wt_Region, wt_Category, // has UMLObject representation wt_Port, // has UMLObject representation wt_Instance, // has UMLObject representation == wt_Object wt_Max // upper bounds check value }; static QString toString(WidgetType wt); static QString toI18nString(WidgetType wt); static Icon_Utils::IconType toIcon(WidgetType wt); explicit WidgetBase(UMLScene * scene, WidgetType type= wt_UMLWidget); virtual ~WidgetBase(); UMLObject* umlObject() const; virtual void setUMLObject(UMLObject *obj); Uml::ID::Type id() const; void setID(Uml::ID::Type id); + void setLocalID(Uml::ID::Type id); + Uml::ID::Type localID() const; + + virtual UMLWidget *widgetWithID(Uml::ID::Type id); + WidgetType baseType() const; void setBaseType(const WidgetType& baseType); QLatin1String baseTypeStr() const; virtual void setSelected(bool select); UMLScene* umlScene() const; UMLDoc* umlDoc() const; QString documentation() const; bool hasDocumentation(); virtual void setDocumentation(const QString& doc); QString name() const; virtual void setName(const QString &strName); QColor lineColor() const; virtual void setLineColor(const QColor& color); uint lineWidth() const; virtual void setLineWidth(uint width); QColor textColor() const; virtual void setTextColor(const QColor& color); QColor fillColor() const; virtual void setFillColor(const QColor& color); bool usesDiagramLineColor() const; void setUsesDiagramLineColor(bool state); bool usesDiagramLineWidth() const; void setUsesDiagramLineWidth(bool state); bool useFillColor(); virtual void setUseFillColor(bool state); bool usesDiagramTextColor() const; void setUsesDiagramTextColor(bool state); bool usesDiagramFillColor() const; void setUsesDiagramFillColor(bool state); bool usesDiagramUseFillColor() const; void setUsesDiagramUseFillColor(bool state); virtual QFont font() const; virtual void setFont(const QFont& font); bool autoResize(); void setAutoResize(bool state); bool changesShape(); void setChangesShape(bool state); virtual bool showPropertiesDialog(); virtual bool loadFromXMI1(QDomElement &qElement); virtual void saveToXMI1(QDomDocument &qDoc, QDomElement &qElement); + virtual void removeAssoc(AssociationWidget* pAssoc); + virtual void addAssoc(AssociationWidget* pAssoc); + WidgetBase& operator=(const WidgetBase& other); QRectF rect() const; void setRect(const QRectF& rect); void setRect(qreal x, qreal y, qreal width, qreal height); virtual QRectF boundingRect() const; virtual UMLWidget* onWidget(const QPointF &p); virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); bool isActivityWidget() { return baseType() == wt_Activity; } bool isActorWidget() { return baseType() == wt_Actor; } bool isArtifactWidget() { return baseType() == wt_Artifact; } bool isAssociationWidget() { return baseType() == wt_Association; } bool isBoxWidget() { return baseType() == wt_Box; } bool isCategoryWidget() { return baseType() == wt_Category; } bool isClassWidget() { return baseType() == wt_Class; } bool isCombinedFragmentWidget() { return baseType() == wt_CombinedFragment; } bool isComponentWidget() { return baseType() == wt_Component; } bool isDatatypeWidget() { return baseType() == wt_Datatype; } bool isEntityWidget() { return baseType() == wt_Entity; } bool isEnumWidget() { return baseType() == wt_Enum; } bool isFloatingDashLineWidget() { return baseType() == wt_FloatingDashLine; } bool isForkJoinWidget() { return baseType() == wt_ForkJoin; } bool isInterfaceWidget() { return baseType() == wt_Interface; } bool isMessageWidget() { return baseType() == wt_Message; } bool isNodeWidget() { return baseType() == wt_Node; } bool isNoteWidget() { return baseType() == wt_Note; } bool isObjectNodeWidget() { return baseType() == wt_ObjectNode; } bool isObjectWidget() { return baseType() == wt_Object; } bool isPackageWidget() { return baseType() == wt_Package; } bool isPinWidget() { return baseType() == wt_Pin; } bool isPortWidget() { return baseType() == wt_Port; } bool isPreconditionWidget() { return baseType() == wt_Precondition; } bool isRegionWidget() { return baseType() == wt_Region; } bool isSignalWidget() { return baseType() == wt_Signal; } bool isStateWidget() { return baseType() == wt_State; } bool isTextWidget() { return baseType() == wt_Text; } bool isUseCaseWidget() { return baseType() == wt_UseCase; } ActivityWidget* asActivityWidget(); ActorWidget* asActorWidget(); ArtifactWidget* asArtifactWidget(); AssociationWidget* asAssociationWidget(); BoxWidget* asBoxWidget(); CategoryWidget* asCategoryWidget(); ClassifierWidget* asClassifierWidget(); CombinedFragmentWidget* asCombinedFragmentWidget(); ComponentWidget* asComponentWidget(); DatatypeWidget* asDatatypeWidget(); EntityWidget* asEntityWidget(); EnumWidget* asEnumWidget(); FloatingDashLineWidget* asFloatingDashLineWidget(); ForkJoinWidget* asForkJoinWidget(); InterfaceWidget* asInterfaceWidget(); MessageWidget* asMessageWidget(); NodeWidget* asNodeWidget(); NoteWidget* asNoteWidget(); ObjectNodeWidget* asObjectNodeWidget(); ObjectWidget* asObjectWidget(); PackageWidget* asPackageWidget(); PinWidget* asPinWidget(); PinPortBase* asPinPortBase(); PortWidget* asPortWidget(); PreconditionWidget* asPreconditionWidget(); RegionWidget* asRegionWidget(); SignalWidget* asSignalWidget(); StateWidget* asStateWidget(); FloatingTextWidget* asFloatingTextWidget(); // TextWidget* asTextWidget(); UseCaseWidget* asUseCaseWidget(); UMLWidget* asUMLWidget(); public Q_SLOTS: virtual void slotMenuSelection(QAction *trigger); protected: virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent *event); private: WidgetType m_baseType; ///< Type of widget. protected: UMLScene *m_scene; QPointer m_umlObject; QString m_Doc; ///< Only used if m_umlObject is not set. QString m_Text; QRectF m_rect; ///< widget size /** * This ID is only used when the widget does not have a * corresponding UMLObject (i.e. the m_umlObject pointer is NULL.) * For UMLObjects, the ID from the UMLObject is used. */ Uml::ID::Type m_nId; + /** + * This ID is only used when a widget could be added more then one time to a diagram + */ + Uml::ID::Type m_nLocalID; + QColor m_textColor; ///< Color of the text of the widget. Is saved to XMI. QColor m_lineColor; ///< Color of the lines of the widget. Is saved to XMI. QColor m_fillColor; ///< color of the background of the widget QBrush m_brush; QFont m_font; uint m_lineWidth; ///< Width of the lines of the widget. Is saved to XMI. bool m_useFillColor; ///< flag indicates if the UMLWidget uses the Diagram FillColour /** * true by default, false if the colors have * been explicitly set for this widget. * These are saved to XMI. */ bool m_usesDiagramFillColor; bool m_usesDiagramLineColor; bool m_usesDiagramLineWidth; bool m_usesDiagramTextColor; bool m_usesDiagramUseFillColor; bool m_autoResize; bool m_changesShape; ///< The widget changes its shape when the number of connections or their positions are changed }; #endif diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index d4e773601..904d2fb5c 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -1,179 +1,186 @@ set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}) set(SRC_PATH ../umbrello) include_directories( ${LIBXML2_INCLUDE_DIR} ${LIBXSLT_INCLUDE_DIR} ${CMAKE_SOURCE_DIR} ${SRC_PATH} ${SRC_PATH}/debug/ ${SRC_PATH}/dialogs/ ${SRC_PATH}/dialogs/pages ${SRC_PATH}/dialogs/widgets ${SRC_PATH}/clipboard ${SRC_PATH}/cmds ${SRC_PATH}/codegenerators ${SRC_PATH}/codegenerators/ada/ ${SRC_PATH}/codegenerators/as/ ${SRC_PATH}/codegenerators/cpp/ ${SRC_PATH}/codegenerators/csharp/ ${SRC_PATH}/codegenerators/d/ ${SRC_PATH}/codegenerators/idl/ ${SRC_PATH}/codegenerators/java/ ${SRC_PATH}/codegenerators/js/ ${SRC_PATH}/codegenerators/pascal/ ${SRC_PATH}/codegenerators/perl/ ${SRC_PATH}/codegenerators/php/ ${SRC_PATH}/codegenerators/python/ ${SRC_PATH}/codegenerators/ruby/ ${SRC_PATH}/codegenerators/sql/ ${SRC_PATH}/codegenerators/tcl/ ${SRC_PATH}/codegenerators/vala/ ${SRC_PATH}/codegenerators/xml/ ${SRC_PATH}/codegenwizard ${SRC_PATH}/codeimport ${SRC_PATH}/debug ${SRC_PATH}/dialogs ${SRC_PATH}/docgenerators ${SRC_PATH}/menus/ ${SRC_PATH}/refactoring ${SRC_PATH}/uml1model/ ${SRC_PATH}/umlwidgets/ ${CMAKE_CURRENT_BINARY_DIR} ) if(NOT BUILD_KF5) set(LIBS Qt4::QtCore Qt4::QtGui Qt4::QtXml Qt4::QtTest Qt4::QtWebKit ${KDE4_KFILE_LIBS} ${LIBXML2_LIBRARIES} ${LIBXSLT_LIBRARIES} libumbrello ) else() set(LIBS Qt5::Xml Qt5::Test Qt5::Widgets Qt5::WebKitWidgets KF5::I18n KF5::Crash ${LIBXML2_LIBRARIES} ${LIBXSLT_LIBRARIES} libumbrello ) endif() ecm_add_test( testbasictypes.cpp LINK_LIBRARIES ${LIBS} TEST_NAME testbasictypes ) ecm_add_test( testumlobject.cpp testbase.cpp LINK_LIBRARIES ${LIBS} TEST_NAME testumlobject ) ecm_add_test( testassociation.cpp testbase.cpp LINK_LIBRARIES ${LIBS} TEST_NAME testassociation ) ecm_add_test( testclassifier.cpp testbase.cpp LINK_LIBRARIES ${LIBS} TEST_NAME testclassifier ) +ecm_add_test( + testpackage.cpp + testbase.cpp + LINK_LIBRARIES ${LIBS} + TEST_NAME testpackage +) + ecm_add_test( testcppwriter.cpp testbase.cpp LINK_LIBRARIES ${LIBS} TEST_NAME testcppwriter ) ecm_add_test( testpythonwriter.cpp testbase.cpp LINK_LIBRARIES ${LIBS} TEST_NAME testpythonwriter ) ecm_add_test( testoptionstate.cpp testbase.cpp LINK_LIBRARIES ${LIBS} TEST_NAME testoptionstate ) ecm_add_test( testumlcanvasobject.cpp testbase.cpp LINK_LIBRARIES ${LIBS} TEST_NAME testumlcanvasobject ) set(testumlroledialog_SRCS testumlroledialog.cpp ) add_executable(testumlroledialog ${testumlroledialog_SRCS}) target_link_libraries(testumlroledialog ${LIBS}) add_executable(testcrashhandler testcrashhandler.cpp) target_link_libraries(testcrashhandler ${LIBS}) add_executable(testlistpopupmenu testlistpopupmenu.cpp testbase.cpp) target_link_libraries(testlistpopupmenu ${LIBS}) find_package(LLVM CONFIG) find_package(Clang CONFIG) if(NOT Clang_FOUND) find_package(CLANG QUIET) endif() if(LLVM_FOUND AND (Clang_FOUND OR CLANG_FOUND) AND LLVM_VERSION VERSION_LESS "8.0.0") message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") message(STATUS "Found CLANG ${CLANG_PACKAGE_VERSION}") include_directories(${LLVM_INCLUDE_DIRS}) add_definitions(${LLVM_DEFINITIONS}) include_directories(${CLANG_INCLUDE_DIRS}) add_definitions(${CLANG_DEFINITIONS}) # Now build our tools add_executable(testllvm testllvm.cpp) # Find the libraries that correspond to the LLVM components # that we wish to use if(LLVM_VERSION_MAJOR STREQUAL "7") set(llvm_libs LLVM) else() llvm_map_components_to_libnames(llvm_libs support core irreader analysis) endif() # Link against LLVM libraries target_link_libraries(testllvm ${llvm_libs} clangFrontend clangTooling clangBasic) add_executable(testllvmparser testllvmparser.cpp) if(NOT LLVM_VERSION_MAJOR STREQUAL "7") llvm_map_components_to_libnames(llvm_libs support) endif() target_link_libraries(testllvmparser ${llvm_libs} clangFrontend clangTooling clangAST clangBasic ${LIBS}) ecm_mark_nongui_executable(testllvm testllvmparser) endif() add_custom_target(check COMMAND ${CMAKE_BUILD_TOOL} test) diff --git a/unittests/testassociation.cpp b/unittests/testassociation.cpp index 8e2c1404e..bb827b43c 100644 --- a/unittests/testassociation.cpp +++ b/unittests/testassociation.cpp @@ -1,272 +1,233 @@ /* Copyright 2019 Ralf Habacker This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "testassociation.h" // app include #include "association.h" #include "classifier.h" #include "folder.h" #include "package.h" #include "stereotype.h" #include "uml.h" #include "umldoc.h" #include "umlobject.h" #include "umlrole.h" //----------------------------------------------------------------------------- void TestAssociation::test_equal() { const SignalBlocker sb(UMLApp::app()->document()); UMLPackage parent("Test Parent"); UMLObject o1(nullptr, "objectA"); UMLObject o2(nullptr, "objectB"); UMLAssociation a(Uml::AssociationType::Association, &o1, &o2); a.setUMLPackage(&parent); UMLAssociation b(Uml::AssociationType::Association, &o1, &o2); b.setUMLPackage(&parent); UMLAssociation c(Uml::AssociationType::Association, &o1, nullptr); c.setUMLPackage(&parent); UMLAssociation d(Uml::AssociationType::Association, nullptr, &o2); d.setUMLPackage(&parent); //QCOMPARE(a, b); QCOMPARE(a == c, false); QCOMPARE(b == c, false); QCOMPARE(c == d, false); } void TestAssociation::test_toString() { UMLObject o1(nullptr, "objectA"); UMLObject o2(nullptr, "objectB"); UMLAssociation a(Uml::AssociationType::Association, &o1, nullptr); QString ar = QString(QLatin1String("objectA: %1 null")).arg(Uml::AssociationType::toStringI18n(Uml::AssociationType::Association)); QCOMPARE(a.toString(), ar); UMLAssociation b(Uml::AssociationType::Association, nullptr, &o2); QString br = QString(QLatin1String("null %1 objectB:")).arg(Uml::AssociationType::toStringI18n(Uml::AssociationType::Association)); QCOMPARE(b.toString(), br); UMLAssociation c(Uml::AssociationType::Association, &o1, &o2); QString cr = QString(QLatin1String("objectA: %1 objectB:")).arg(Uml::AssociationType::toStringI18n(Uml::AssociationType::Association)); QCOMPARE(c.toString(), cr); } void TestAssociation::test_UMLRole() { UMLObject o1(nullptr, "objectA"); UMLObject o2(nullptr, "objectB"); UMLAssociation a(Uml::AssociationType::Association, &o1, &o2); QCOMPARE(a.getUMLRole(Uml::RoleType::A)->object(), &o1); QCOMPARE(a.getUMLRole(Uml::RoleType::B)->object(), &o2); } void TestAssociation::test_associationType() { UMLAssociation a(Uml::AssociationType::Association); QVERIFY(a.getAssocType() == Uml::AssociationType::Association); a.setAssociationType(Uml::AssociationType::Aggregation); QVERIFY(a.getAssocType() == Uml::AssociationType::Aggregation); } void TestAssociation::test_objectID() { UMLObject o1(nullptr, "objectA"); UMLObject o2(nullptr, "objectB"); UMLAssociation a(Uml::AssociationType::Association, &o1, &o2); QCOMPARE(a.getObjectId(Uml::RoleType::A), o1.id()); QCOMPARE(a.getObjectId(Uml::RoleType::B), o2.id()); } void TestAssociation::test_visibility() { UMLObject o1(nullptr, "objectA"); UMLObject o2(nullptr, "objectB"); UMLAssociation a(Uml::AssociationType::Association, &o1, &o2); QVERIFY(a.visibility(Uml::RoleType::A) == Uml::Visibility::Public); QVERIFY(a.visibility(Uml::RoleType::B) == Uml::Visibility::Public); a.setVisibility(Uml::Visibility::Protected, Uml::RoleType::A); a.setVisibility(Uml::Visibility::Protected, Uml::RoleType::B); QVERIFY(a.visibility(Uml::RoleType::A) == Uml::Visibility::Protected); QVERIFY(a.visibility(Uml::RoleType::B) == Uml::Visibility::Protected); a.setVisibility(Uml::Visibility::Private, Uml::RoleType::A); a.setVisibility(Uml::Visibility::Private, Uml::RoleType::B); QVERIFY(a.visibility(Uml::RoleType::A) == Uml::Visibility::Private); QVERIFY(a.visibility(Uml::RoleType::B) == Uml::Visibility::Private); a.setVisibility(Uml::Visibility::Implementation, Uml::RoleType::A); a.setVisibility(Uml::Visibility::Implementation, Uml::RoleType::B); QVERIFY(a.visibility(Uml::RoleType::A) == Uml::Visibility::Implementation); QVERIFY(a.visibility(Uml::RoleType::B) == Uml::Visibility::Implementation); UMLAssociation b(Uml::AssociationType::Association); } void TestAssociation::test_changeability() { UMLObject o1(nullptr, "objectA"); UMLObject o2(nullptr, "objectB"); UMLAssociation a(Uml::AssociationType::Association, &o1, &o2); QVERIFY(a.visibility(Uml::RoleType::A) == Uml::Visibility::Public); QVERIFY(a.visibility(Uml::RoleType::B) == Uml::Visibility::Public); a.setVisibility(Uml::Visibility::Protected, Uml::RoleType::A); a.setVisibility(Uml::Visibility::Protected, Uml::RoleType::B); QVERIFY(a.visibility(Uml::RoleType::A) == Uml::Visibility::Protected); QVERIFY(a.visibility(Uml::RoleType::B) == Uml::Visibility::Protected); a.setVisibility(Uml::Visibility::Private, Uml::RoleType::A); a.setVisibility(Uml::Visibility::Private, Uml::RoleType::B); QVERIFY(a.visibility(Uml::RoleType::A) == Uml::Visibility::Private); QVERIFY(a.visibility(Uml::RoleType::B) == Uml::Visibility::Private); a.setVisibility(Uml::Visibility::Implementation, Uml::RoleType::A); a.setVisibility(Uml::Visibility::Implementation, Uml::RoleType::B); QVERIFY(a.visibility(Uml::RoleType::A) == Uml::Visibility::Implementation); QVERIFY(a.visibility(Uml::RoleType::B) == Uml::Visibility::Implementation); UMLAssociation b(Uml::AssociationType::Association); QVERIFY(b.visibility(Uml::RoleType::A) == Uml::Visibility::Public); QVERIFY(b.visibility(Uml::RoleType::B) == Uml::Visibility::Public); } void TestAssociation::test_multiplicity() { UMLObject o1(nullptr, "objectA"); UMLObject o2(nullptr, "objectB"); UMLAssociation a(Uml::AssociationType::Association, &o1, &o2); QCOMPARE(a.getMultiplicity(Uml::RoleType::A), QString()); QCOMPARE(a.getMultiplicity(Uml::RoleType::B), QString()); a.setMultiplicity("1", Uml::RoleType::A); a.setMultiplicity("2", Uml::RoleType::B); QCOMPARE(a.getMultiplicity(Uml::RoleType::A), QLatin1String("1")); QCOMPARE(a.getMultiplicity(Uml::RoleType::B), QLatin1String("2")); } void TestAssociation::test_roleName() { UMLObject o1(nullptr, "objectA"); UMLObject o2(nullptr, "objectB"); UMLAssociation a(Uml::AssociationType::Association, &o1, &o2); QCOMPARE(a.getRoleName(Uml::RoleType::A), QString()); QCOMPARE(a.getRoleName(Uml::RoleType::B), QString()); a.setRoleName("test1", Uml::RoleType::A); a.setRoleName("test2", Uml::RoleType::B); QCOMPARE(a.getRoleName(Uml::RoleType::A), QLatin1String("test1")); QCOMPARE(a.getRoleName(Uml::RoleType::B), QLatin1String("test2")); } void TestAssociation::test_roleDoc() { UMLObject o1(nullptr, "objectA"); UMLObject o2(nullptr, "objectB"); UMLAssociation a(Uml::AssociationType::Association, &o1, &o2); QCOMPARE(a.getRoleDoc(Uml::RoleType::A), QString()); QCOMPARE(a.getRoleDoc(Uml::RoleType::B), QString()); a.setRoleDoc("test1", Uml::RoleType::A); a.setRoleDoc("test2", Uml::RoleType::B); QCOMPARE(a.getRoleDoc(Uml::RoleType::A), QLatin1String("test1")); QCOMPARE(a.getRoleDoc(Uml::RoleType::B), QLatin1String("test2")); } void TestAssociation::resolveRef() { UMLPackage parent("Test Parent"); UMLStereotype *stereotype1 = UMLApp::app()->document()->createStereotype("test1"); UMLStereotype *stereotype2 = UMLApp::app()->document()->createStereotype("test2"); UMLObject o1(nullptr, "objectA"); UMLObject o2(nullptr, "objectB"); UMLAssociation a(Uml::AssociationType::Association, &o1, &o2); // no resolve a.setUMLPackage(&parent); QCOMPARE(a.resolveRef(), true); // secondary a.getUMLRole(Uml::RoleType::A)->setSecondaryId(Uml::ID::toString(stereotype1->id())); a.getUMLRole(Uml::RoleType::B)->setSecondaryId(Uml::ID::toString(stereotype2->id())); QCOMPARE(a.resolveRef(), true); // secondary fallback a.getUMLRole(Uml::RoleType::A)->setSecondaryId(QLatin1String("")); a.getUMLRole(Uml::RoleType::A)->setSecondaryFallback(Uml::ID::toString(stereotype1->id())); a.getUMLRole(Uml::RoleType::B)->setSecondaryId(QLatin1String("")); a.getUMLRole(Uml::RoleType::B)->setSecondaryFallback(Uml::ID::toString(stereotype2->id())); QCOMPARE(a.resolveRef(), true); } -class TestUMLAssociation : public UMLAssociation -{ -public: - TestUMLAssociation(Uml::AssociationType::Enum type, UMLObject *roleA, UMLObject *roleB) - : UMLAssociation(type, roleA, roleB) - { - } - TestUMLAssociation(Uml::AssociationType::Enum type = Uml::AssociationType::Unknown) - : UMLAssociation(type) - { - } - - QDomDocument testSave() - { - QDomDocument qDoc; - QDomElement root = qDoc.createElement("unittest"); - qDoc.appendChild(root); - saveToXMI1(qDoc, root); - return qDoc; - } - - bool testLoad(QDomDocument &qDoc) - { - QDomElement root = qDoc.childNodes().at(0).toElement(); - QDomElement e = root.childNodes().at(0).toElement(); - bool result = loadFromXMI1(e); - if (result) { - const SignalBlocker sb(UMLApp::app()->document()); - result = resolveRef(); - } - return result; - } - - void testDump(const QString &title = QString()) - { - QDomDocument doc = testSave(); - QString xml = doc.toString(); - qDebug() << title << doc.toString(); - } -}; +typedef TestUML TestUMLAssociation; void TestAssociation::test_saveAndLoad() { UMLPackage parent("Test Parent"); UMLClassifier c1("Test A"); UMLClassifier c2("Test B"); c1.setUMLPackage(&parent); c2.setUMLPackage(&parent); c1.setStereotypeCmd("test"); UMLFolder *root = UMLApp::app()->document()->rootFolder(Uml::ModelType::Logical); root->addObject(&c1); root->addObject(&c2); TestUMLAssociation a1(Uml::AssociationType::Association, &c1, &c2); a1.setNameCmd("Test assoc"); a1.setUMLPackage(&parent); - QDomDocument save = a1.testSave(); + QDomDocument save = a1.testSave1(); //a1.testDump("save"); TestUMLAssociation a2; a2.setUMLPackage(&parent); - QCOMPARE(a2.testLoad(save), true); - QCOMPARE(a2.testSave().toString(), save.toString()); + QCOMPARE(a2.testLoad1(save), true); + QCOMPARE(a2.testSave1().toString(), save.toString()); //a2.testDump("load"); } QTEST_MAIN(TestAssociation) diff --git a/unittests/testbase.h b/unittests/testbase.h index cd4169815..d53317971 100644 --- a/unittests/testbase.h +++ b/unittests/testbase.h @@ -1,126 +1,184 @@ /* Copyright 2015, 2019 Ralf Habacker This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef TESTBASE_H #define TESTBASE_H // qt includes #include #include #ifdef RUN_ALL #undef QCOMPARE #define QCOMPARE(actual, expected) \ QTest::qCompare(actual, expected, #actual, #expected, __FILE__, __LINE__) #undef QVERIFY #define QVERIFY(statement) \ QTest::qVerify((statement), #statement, "", __FILE__, __LINE__) #endif #define IS_NOT_IMPL() QSKIP("not implemented yet", SkipSingle) /** * The TestBase class is intended as base class for umbrello unit tests. * * Currently it provides an instance of class UMLApp, which is required * to run mostly unit tests. * * @author Ralf Habacker */ class TestBase : public QObject { Q_OBJECT public: explicit TestBase(QObject *parent = 0); protected slots: virtual void initTestCase(); virtual void cleanupTestCase(); virtual void cleanupOnExit(QObject *p); protected: QList> m_objectsToDelete; }; /** * The TestCodeGeneratorBase class is intended as base class for code generator unit tests * * Currently it provides a path to a temporary directory, where generated code could be * placed into. The temporary path is set as default output path for any code generating. * * @author Ralf Habacker */ class TestCodeGeneratorBase : public TestBase { Q_OBJECT private slots: virtual void initTestCase(); protected: QString m_tempPath; ///< holds path to temporary directory QString temporaryPath(); }; #if QT_VERSION < QT_VERSION_CHECK(5,0,0) /** * Automatically block signals of QObject base class */ class SignalBlocker { public: - SignalBlocker(QObject *o) + explicit SignalBlocker(QObject *o) : _o(o) { _state = _o->blockSignals(true); } SignalBlocker(QObject &o) : _o(&o) { _state = _o->blockSignals(true); } ~SignalBlocker() { _o->blockSignals(_state); } protected: QObject *_o; bool _state; }; #else #include typedef QSignalBlocker SignalBlocker; #endif /** * Set loading state to avoid signaling to tree view etc. */ class SetLoading { public: SetLoading(); ~SetLoading(); protected: bool _state; }; +#include +#include "uml.h" +#include "umldoc.h" + +/** + * template for adding test save/load support to UML related classe + */ +template +class TestUML : public T +{ +public: + TestUML() : T() {} + TestUML(N name) : T(name) {} + TestUML(N p1, UMLObject *p2, UMLObject *p3) : T(p1, p2, p3) {} + QDomDocument testSave1(); + bool testLoad1(QDomDocument &qDoc); + void testDump(const QString &title = QString()); + UMLObject *secondary() const; +}; + +template +QDomDocument TestUML::testSave1() +{ + QDomDocument qDoc; + QDomElement root = qDoc.createElement("unittest"); + qDoc.appendChild(root); + T::saveToXMI1(qDoc, root); + return qDoc; +} + +template +bool TestUML::testLoad1(QDomDocument &qDoc) +{ + QDomElement root = qDoc.childNodes().at(0).toElement(); + QDomElement e = root.childNodes().at(0).toElement(); + bool result = T::loadFromXMI1(e); + if (result) { + const SignalBlocker sb(UMLApp::app()->document()); + result = T::resolveRef(); + } + return result; +} + +template +void TestUML::testDump(const QString &title) +{ + QDomDocument doc = testSave1(); + QString xml = doc.toString(); + qDebug() << title << doc.toString(); +} + +// used by resolveRef() tests +template +UMLObject *TestUML::secondary() const +{ + return T::m_pSecondary.data(); +} + #endif // TESTBASE_H diff --git a/unittests/testclassifier.cpp b/unittests/testclassifier.cpp index 693a47f02..cc98bbe14 100644 --- a/unittests/testclassifier.cpp +++ b/unittests/testclassifier.cpp @@ -1,325 +1,340 @@ /* Copyright 2011 Andi Fischer This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "testclassifier.h" // app include #include "uml.h" #include "association.h" #include "classifier.h" #include "datatype.h" #include "operation.h" #include "model_utils.h" //----------------------------------------------------------------------------- void TEST_classifier::test_equal() { UMLClassifier* a = new UMLClassifier("Test A", Uml::ID::None); UMLClassifier* b = a; UMLClassifier* c = new UMLClassifier("Test A", Uml::ID::None); UMLClassifier* d = new UMLClassifier("Test B", Uml::ID::None); QCOMPARE(*a == *b, true); QCOMPARE(*a == *c, true); QCOMPARE(*b == *c, true); QCOMPARE(*c == *d, false); } void TEST_classifier::test_copyInto() { UMLClassifier a("Test A", Uml::ID::None); UMLClassifier b("Test B", Uml::ID::None); b.copyInto(&a); QCOMPARE(a == b, true); } void TEST_classifier::test_clone() { UMLClassifier* a = new UMLClassifier("Test A", Uml::ID::None); UMLClassifier* b = a->clone()->asUMLClassifier(); QCOMPARE(*a == *b, true); } void TEST_classifier::test_addAttributeWithType() { UMLClassifier a("Test A", Uml::ID::None); a.addAttribute("attributeA_", Uml::ID::None); UMLAttribute *attrA = a.addAttribute("attributeA_", Uml::ID::None); /* UMLAttribute* attrB = */ a.addAttribute("attributeB_", Uml::ID::None); int num1 = a.getAttributeList().count(); QCOMPARE(num1, 2); int num2 = a.removeAttribute(attrA); QCOMPARE(num2, 1); // one deleted int num3 = a.getAttributeList().count(); QCOMPARE(num3, num1 - 1); } void TEST_classifier::test_addAttributeWithObject() { IS_NOT_IMPL(); } void TEST_classifier::test_addAttributeWithAttribute() { IS_NOT_IMPL(); } void TEST_classifier::test_removeAndCountAttribute() { UMLClassifier* a = new UMLClassifier("Test A", Uml::ID::None); int num0 = a->getAttributeList().count(); QCOMPARE(num0, 0); // no attributes present yet /*UMLAttribute* attrA = */ a->addAttribute("attributeA_", Uml::ID::None); UMLAttribute* attrB = a->addAttribute("attributeB_", Uml::ID::None); UMLAttribute* attrC = a->addAttribute("attributeC_", Uml::ID::None); /* UMLAttribute* attrD = */ a->addAttribute("attributeD_", Uml::ID::None); int num1 = a->getAttributeList().count(); QCOMPARE(num1, 4); int num2 = a->removeAttribute(attrB); QCOMPARE(num2, 3); // one deleted num2 = a->removeAttribute(attrC); QCOMPARE(num2, 2); // one deleted int num3 = a->getAttributeList().count(); QCOMPARE(num3, 2); } void TEST_classifier::test_getAttributeList() { IS_NOT_IMPL(); } void TEST_classifier::test_addOperationWithPosition() { IS_NOT_IMPL(); } void TEST_classifier::test_addOperationWithLog() { IS_NOT_IMPL(); } void TEST_classifier::test_checkOperationSignature() { IS_NOT_IMPL(); } void TEST_classifier::test_removeAndCountOperation() { IS_NOT_IMPL(); } void TEST_classifier::test_getOperationList() { IS_NOT_IMPL(); } void TEST_classifier::test_addTemplateWithType() { IS_NOT_IMPL(); } void TEST_classifier::test_addTemplateWithLog() { IS_NOT_IMPL(); } void TEST_classifier::test_addTemplateWithPosition() { IS_NOT_IMPL(); } void TEST_classifier::test_removeAndCountTemplate() { IS_NOT_IMPL(); } void TEST_classifier::test_findTemplate() { IS_NOT_IMPL(); } void TEST_classifier::test_getTemplateList() { IS_NOT_IMPL(); } void TEST_classifier::test_takeItem() { IS_NOT_IMPL(); } void TEST_classifier::test_getFilteredList() { IS_NOT_IMPL(); } void TEST_classifier::test_resolveRef() { qDebug() << "already tested by testumlobject"; } void TEST_classifier::test_findOperations() { UMLClassifier c("Test A", Uml::ID::None); UMLOperation o1(nullptr, "testop1"); c.addOperation(&o1); int num1 = c.getOpList().count(); QCOMPARE(num1, 1); UMLOperation o2(nullptr, "testop2"); c.addOperation(&o2); int num2 = c.getOpList().count(); QCOMPARE(num2, 2); QCOMPARE(c.findOperations("testop1").count(), 1); QCOMPARE(c.findOperations("testop2").count(), 1); QCOMPARE(c.findOperations("testOp1").count(), 0); QCOMPARE(c.findOperations("testOp2").count(), 0); // case insensitive language Uml::ProgrammingLanguage::Enum lang = UMLApp::app()->activeLanguage(); UMLApp::app()->setActiveLanguage(Uml::ProgrammingLanguage::PostgreSQL); QCOMPARE(c.findOperations("testOp1").count(), 1); QCOMPARE(c.findOperations("testOp2").count(), 1); UMLApp::app()->setActiveLanguage(lang); } void TEST_classifier::test_findChildObjectById() { IS_NOT_IMPL(); } void TEST_classifier::test_findOperation() { UMLClassifier c("Test A", Uml::ID::None); UMLOperation o1(nullptr, "testop1"); UMLAttribute a1(nullptr, "aParam"); a1.setTypeName("int"); o1.addParm(&a1); c.addOperation(&o1); UMLOperation o2(nullptr, "testop1"); UMLAttribute a2(nullptr, "aParam"); a2.setTypeName("double"); o2.addParm(&a2); c.addOperation(&o2); Model_Utils::NameAndType_List searchTypes; // first function searchTypes << Model_Utils::NameAndType("aParam", a1.getType()); UMLOperation *o = c.findOperation("testop1", searchTypes); QVERIFY(o); // second function searchTypes.clear(); searchTypes << Model_Utils::NameAndType("aParam", a2.getType()); o = c.findOperation("testop1", searchTypes); QVERIFY(o); // unknown type UMLDatatype d1("someType"); searchTypes.clear(); searchTypes << Model_Utils::NameAndType("aParam", &d1); o = c.findOperation("testop1", searchTypes); QVERIFY(!o); #if 0 // different param name searchTypes.clear(); searchTypes << Model_Utils::NameAndType("otherParam", a1.getType()); o = c.findOperation("testop1", searchTypes); QVERIFY(!o); // different param name searchTypes.clear(); searchTypes << Model_Utils::NameAndType("otherParam", a2.getType()); o = c.findOperation("testop1", searchTypes); QVERIFY(!o); #else qDebug() <<"finding param names is not supported"; #endif } void TEST_classifier::test_findSuperClassConcepts() { UMLClassifier c1("Test A"); UMLClassifier c2("Test B"); UMLAssociation a1(Uml::AssociationType::Generalization, &c1, &c2); QCOMPARE(c1.findSuperClassConcepts(UMLClassifier::ALL).size(), 0); c1.addAssociationEnd(&a1); QCOMPARE(c1.findSuperClassConcepts(UMLClassifier::ALL).size(), 1); UMLAssociation a2(Uml::AssociationType::Realization, &c1, &c2); c1.addAssociationEnd(&a2); QCOMPARE(c1.findSuperClassConcepts(UMLClassifier::ALL).size(), 2); } void TEST_classifier::test_findSubClassConcepts() { UMLClassifier c1("Test A"); UMLClassifier c2("Test B"); UMLAssociation a1(Uml::AssociationType::Generalization, &c1, &c2); QCOMPARE(c2.findSubClassConcepts(UMLClassifier::ALL).size(), 0); c2.addAssociationEnd(&a1); QCOMPARE(c2.findSubClassConcepts(UMLClassifier::ALL).size(), 1); UMLAssociation a2(Uml::AssociationType::Realization, &c1, &c2); c2.addAssociationEnd(&a2); QCOMPARE(c2.findSubClassConcepts(UMLClassifier::ALL).size(), 2); } void TEST_classifier::test_setGetClassAssoc() { IS_NOT_IMPL(); } -void TEST_classifier::test_setBaseType() -{ - qDebug() << "already tested by testumlobject"; -} - void TEST_classifier::test_isInterface() { - IS_NOT_IMPL(); -} - -void TEST_classifier::test_isDatatype() -{ - IS_NOT_IMPL(); + UMLClassifier c1("Test A"); + QCOMPARE(c1.isInterface(), false); + c1.setBaseType(UMLObject::ObjectType::ot_Interface); + QCOMPARE(c1.isInterface(), true); + c1.setBaseType(UMLObject::ObjectType::ot_Class); + QCOMPARE(c1.isInterface(), false); } void TEST_classifier::test_setGetOriginType() { IS_NOT_IMPL(); } void TEST_classifier::test_setGetIsReference() { IS_NOT_IMPL(); } void TEST_classifier::test_hasAbstractOps() { IS_NOT_IMPL(); } void TEST_classifier::test_makeChildObject() { IS_NOT_IMPL(); } void TEST_classifier::test_getUniAssociationToBeImplemented() { IS_NOT_IMPL(); } +typedef TestUML TestUMLClassifier; + +void TEST_classifier::test_saveAndLoad() +{ + UMLPackage parent("test package"); + TestUMLClassifier c1("Test A"); + c1.setUMLPackage(&parent); + UMLOperation o1(nullptr, "testop1"); + c1.addOperation(&o1); + UMLOperation o2(nullptr, "testop2"); + c1.addOperation(&o2); + QDomDocument save = c1.testSave1(); + //c1.testDump("save"); + TestUMLClassifier c2; + c2.setUMLPackage(&parent); + QCOMPARE(c2.testLoad1(save), true); + //c2.testDump("after load"); + QCOMPARE(c2.testSave1().toString(), save.toString()); +} + QTEST_MAIN(TEST_classifier) diff --git a/unittests/testclassifier.h b/unittests/testclassifier.h index d1fc7773b..4deb386e1 100644 --- a/unittests/testclassifier.h +++ b/unittests/testclassifier.h @@ -1,71 +1,70 @@ /* Copyright 2011 Andi Fischer This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef TEST_CLASSIFIER_H #define TEST_CLASSIFIER_H #include "testbase.h" /** * Unit test for class UMLClassifier (classifier.h). */ class TEST_classifier: public TestBase { Q_OBJECT private slots: void test_equal(); void test_copyInto(); void test_clone(); void test_addAttributeWithType(); void test_addAttributeWithObject(); void test_addAttributeWithAttribute(); void test_removeAndCountAttribute(); void test_getAttributeList(); void test_addOperationWithPosition(); void test_addOperationWithLog(); void test_checkOperationSignature(); void test_removeAndCountOperation(); void test_getOperationList(); void test_addTemplateWithType(); void test_addTemplateWithLog(); void test_addTemplateWithPosition(); void test_removeAndCountTemplate(); void test_findTemplate(); void test_getTemplateList(); void test_takeItem(); void test_getFilteredList(); void test_resolveRef(); void test_findOperations(); void test_findChildObjectById(); void test_findOperation(); void test_findSuperClassConcepts(); void test_findSubClassConcepts(); void test_setGetClassAssoc(); - void test_setBaseType(); void test_isInterface(); - void test_isDatatype(); void test_setGetOriginType(); void test_setGetIsReference(); void test_hasAbstractOps(); void test_makeChildObject(); void test_getUniAssociationToBeImplemented(); + void test_saveAndLoad(); }; #endif // TEST_CLASSIFIER_H diff --git a/unittests/testpackage.cpp b/unittests/testpackage.cpp new file mode 100644 index 000000000..72e1c11d2 --- /dev/null +++ b/unittests/testpackage.cpp @@ -0,0 +1,65 @@ +/* + Copyright 2019 Ralf Habacker + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License or (at your option) version 3 or any later version + accepted by the membership of KDE e.V. (or its successor approved + by the membership of KDE e.V.), which shall act as a proxy + defined in Section 14 of version 3 of the license. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "testpackage.h" + +#include "classifier.h" +#include "package.h" +#include "stereotype.h" + +void TestPackage::test_appendClassesAndInterfaces() +{ + UMLPackage p("package"); + UMLClassifier c1("class A"); + UMLClassifier c2("class B"); + UMLClassifier c3("class C"); + p.addObject(&c1); + p.addObject(&c2); + p.addObject(&c3); + QCOMPARE(p.containedObjects().size(), 3); + UMLClassifierList items; + p.appendClassesAndInterfaces(items); + QCOMPARE(items.size(), 3); +} + +typedef TestUML TestUMLPackage; + +void TestPackage::test_saveAndLoad() +{ + UMLPackage parent("parent"); + TestUMLPackage p1("Package"); + p1.setUMLPackage(&parent); + UMLClassifier c1("Test A"); + c1.setUMLPackage(&p1); + UMLClassifier c2("Test B"); + c2.setUMLPackage(&p1); + p1.addObject(&c1); + p1.addObject(&c2); + p1.setStereotypeCmd("test"); + QDomDocument save = p1.testSave1(); + //p1.testDump("save"); + TestUMLPackage p2; + p2.setUMLPackage(&parent); + QCOMPARE(p2.testLoad1(save), true); + //p2.testDump("load"); + QCOMPARE(p2.testSave1().toString(), save.toString()); +} + +QTEST_MAIN(TestPackage) diff --git a/unittests/testumlobject.h b/unittests/testpackage.h similarity index 62% copy from unittests/testumlobject.h copy to unittests/testpackage.h index 3fed18a9b..000be1d87 100644 --- a/unittests/testumlobject.h +++ b/unittests/testpackage.h @@ -1,47 +1,48 @@ /* - Copyright 2015 Ralf Habacker + Copyright 2019 Ralf Habacker This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#ifndef TESTUMLOBJECT_H -#define TESTUMLOBJECT_H +#ifndef TESTPACKAGE_H +#define TESTPACKAGE_H #include "testbase.h" -class TestUMLObject : public TestBase +class TestPackage : public TestBase { Q_OBJECT +protected: + SetLoading *_sl; +protected slots: + void initTestCase() + { + TestBase::initTestCase(); + _sl = new SetLoading; + } + + void cleanupTestCase() + { + delete _sl; + } + private slots: - void test_copyInto(); - void test_clone(); - void test_doc(); - void test_equal(); - void test_fullyQualifiedName(); - void test_isAbstract(); - void test_isStatic(); - void test_resolveRef(); + void test_appendClassesAndInterfaces(); void test_saveAndLoad(); - void test_setBaseType(); - void test_setSterotype(); - void test_setUMLPackage(); - void test_setVisibility(); - void test_toString(); - void test_dynamic_cast(); }; -#endif // TESTUMLOBJECT_H +#endif // TESTASSOCIATION_H diff --git a/unittests/testumlobject.cpp b/unittests/testumlobject.cpp index 2b96fecaf..1dc639e65 100644 --- a/unittests/testumlobject.cpp +++ b/unittests/testumlobject.cpp @@ -1,242 +1,296 @@ /* Copyright 2015 Ralf Habacker This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "testumlobject.h" // app include #include "attribute.h" #include "classifier.h" #include "folder.h" #include "operation.h" #include "package.h" #include "stereotype.h" #include "uml.h" #include "umldoc.h" #include "umlobject.h" // kde includes #include //----------------------------------------------------------------------------- void TestUMLObject::test_copyInto() { UMLPackage parent("Test Parent"); UMLObject a("Test A"); a.setUMLPackage(&parent); UMLObject b("Test B"); b.setUMLPackage(&parent); b.copyInto(&a); QCOMPARE(a, b); UMLClassifier c("Test Classifier"); UMLOperation op(&c, "Test Parent"); UMLAttribute at(&op, "Attribute"); UMLAttribute at2(&op,"Attribute 2"); at2.copyInto(&at); QCOMPARE(at, at2); QCOMPARE(at2.umlParent(), at.umlParent()); } void TestUMLObject::test_clone() { UMLPackage parent("Test Parent"); UMLObject a("Test A"); a.setUMLPackage(&parent); UMLObject &b = *a.clone(); QCOMPARE(a, b); } void TestUMLObject::test_doc() { UMLPackage parent("Test Parent"); UMLObject a("Test A"); QCOMPARE(a.hasDoc(), false); a.setDoc(QLatin1String("new doc")); QCOMPARE(a.hasDoc(), true); QCOMPARE(a.doc(), QLatin1String("new doc")); } void TestUMLObject::test_equal() { UMLPackage parent("Test Parent"); UMLObject a("Test A", Uml::ID::Reserved); a.setUMLPackage(&parent); UMLObject b(a); UMLObject c("Test A", Uml::ID::Reserved); c.setUMLPackage(&parent); UMLObject d("Test B", Uml::ID::None); QCOMPARE(a, b); QCOMPARE(a, c); QCOMPARE(b, c); QCOMPARE(c == d, false); } void TestUMLObject::test_fullyQualifiedName() { UMLObject* a = new UMLObject("Test A"); cleanupOnExit(a); QCOMPARE(a->fullyQualifiedName(), QLatin1String("Test A")); UMLPackage* topParent = new UMLPackage("Top Parent"); cleanupOnExit(topParent); UMLPackage* parent = new UMLPackage("Test Parent"); cleanupOnExit(parent); parent->setUMLPackage(topParent); a->setUMLPackage(parent); QCOMPARE(a->umlPackage()->fullyQualifiedName(), a->package()); QCOMPARE(a->fullyQualifiedName(), QLatin1String("Top Parent::Test Parent::Test A")); QCOMPARE(a->fullyQualifiedName(QLatin1String("-")), QLatin1String("Top Parent-Test Parent-Test A")); UMLFolder *f = UMLApp::app()->document()->rootFolder(Uml::ModelType::Logical); parent->setUMLPackage(f); QCOMPARE(a->fullyQualifiedName(QLatin1String("::"), true), QLatin1String("Logical View::Test Parent::Test A")); } void TestUMLObject::test_isAbstract() { UMLObject a("Test A"); QCOMPARE(a.isAbstract(), false); a.setAbstract(true); QCOMPARE(a.isAbstract(), true); } void TestUMLObject::test_isStatic() { UMLObject a("Test A"); QCOMPARE(a.isStatic(), false); a.setStatic(true); QCOMPARE(a.isStatic(), true); } -class LocalUMLObject : public UMLObject -{ -public: - LocalUMLObject(const QString& name = QString(), Uml::ID::Type id = Uml::ID::None) - : UMLObject(name, id) - { - } - - UMLObject *secondary() const - { - return m_pSecondary.data(); - } -}; +typedef TestUML TESTUMLObject; void TestUMLObject::test_resolveRef() { UMLPackage parent("Test Parent"); UMLStereotype *stereotype = UMLApp::app()->document()->createStereotype("test"); UMLObject a("Test A"); // no resolve a.setUMLPackage(&parent); QCOMPARE(a.resolveRef(), true); // secondary a.setSecondaryId(Uml::ID::toString(stereotype->id())); QCOMPARE(a.resolveRef(), true); // secondary fallback a.setSecondaryId(QLatin1String("")); a.setSecondaryFallback(Uml::ID::toString(stereotype->id())); QCOMPARE(a.resolveRef(), true); // unknown stereotype - LocalUMLObject b("Test B"); + TESTUMLObject b("Test B"); UMLStereotype stereotype2("test"); b.setUMLPackage(&parent); b.setSecondaryId(Uml::ID::toString(stereotype2.id())); QCOMPARE(b.resolveRef(), true); // resolveRef creates an "undef" datatype and assigns it to m_Secondary QCOMPARE(b.secondary()->name(), QLatin1String("undef")); } void TestUMLObject::test_saveAndLoad() { UMLPackage parent("Test Parent"); UMLObject a("Test A"); a.setUMLPackage(&parent); a.setStereotypeCmd("test"); QDomDocument doc; QDomElement save = a.save1("test", doc); UMLObject b; b.setUMLPackage(&parent); QCOMPARE(b.loadFromXMI1(save), true); QCOMPARE(a, b); } void TestUMLObject::test_setBaseType() { UMLObject a("Test A"); QCOMPARE(a.baseType(), UMLObject::ot_UMLObject); a.setBaseType(UMLObject::ot_Class); QCOMPARE(a.baseType(), UMLObject::ot_Class); } void TestUMLObject::test_setSterotype() { UMLObject a("Test A"); QCOMPARE(a.stereotype(), QLatin1String("")); a.setStereotypeCmd(QLatin1String("test")); QCOMPARE(a.stereotype(), QLatin1String("test")); } void TestUMLObject::test_setUMLPackage() { UMLPackage parent("Test Parent"); UMLObject a("Test A"); QCOMPARE(a.umlPackage(), (UMLPackage*)0); a.setUMLPackage(&parent); QCOMPARE(a.umlPackage(), &parent); } void TestUMLObject::test_setVisibility() { UMLObject a("Test A"); QVERIFY(a.visibility() == Uml::Visibility::Public); a.setVisibilityCmd(Uml::Visibility::Protected); QVERIFY(a.visibility() == Uml::Visibility::Protected); a.setVisibilityCmd(Uml::Visibility::Private); QVERIFY(a.visibility() == Uml::Visibility::Private); a.setVisibilityCmd(Uml::Visibility::Implementation); QVERIFY(a.visibility() == Uml::Visibility::Implementation); a.setVisibilityCmd(Uml::Visibility::FromParent); QVERIFY(a.visibility() == Uml::Visibility::FromParent); } void TestUMLObject::test_toString() { QCOMPARE(UMLObject::toString(UMLObject::ot_Class), QLatin1String("ot_Class")); QCOMPARE(UMLObject::toI18nString(UMLObject::ot_Class), i18n("Class &name:")); } void TestUMLObject::test_dynamic_cast() { QScopedPointer a1(new UMLClassifier); UMLClassifier *b = a1->asUMLClassifier(); QVERIFY(b); UMLObject *a2 = 0; b = a2->asUMLClassifier(); QVERIFY(!b); } +void TestUMLObject::test_isUMLXXX() +{ + UMLObject a("Test A"); + QVERIFY(a.isUMLObject()); + a.setBaseType(UMLObject::ObjectType::ot_Actor); + QVERIFY(a.isUMLActor()); + a.setBaseType(UMLObject::ObjectType::ot_Artifact); + QVERIFY(a.isUMLArtifact()); + a.setBaseType(UMLObject::ObjectType::ot_Association); + QVERIFY(a.isUMLAssociation()); + a.setBaseType(UMLObject::ObjectType::ot_Attribute); + QVERIFY(a.isUMLAttribute()); + a.setBaseType(UMLObject::ObjectType::ot_Category); + QVERIFY(a.isUMLCategory()); + a.setBaseType(UMLObject::ObjectType::ot_CheckConstraint); + QVERIFY(a.isUMLCheckConstraint()); + a.setBaseType(UMLObject::ObjectType::ot_Class); + QVERIFY(a.isUMLClassifier()); + a.setBaseType(UMLObject::ObjectType::ot_Component); + QVERIFY(a.isUMLComponent()); + a.setBaseType(UMLObject::ObjectType::ot_Datatype); + QVERIFY(a.isUMLDatatype()); + a.setBaseType(UMLObject::ObjectType::ot_Entity); + QVERIFY(a.isUMLEntity()); + a.setBaseType(UMLObject::ObjectType::ot_EntityAttribute); + QVERIFY(a.isUMLEntityAttribute()); + a.setBaseType(UMLObject::ObjectType::ot_EntityConstraint); + QVERIFY(a.isUMLEntityConstraint()); + a.setBaseType(UMLObject::ObjectType::ot_Enum); + QVERIFY(a.isUMLEnum()); + a.setBaseType(UMLObject::ObjectType::ot_EnumLiteral); + QVERIFY(a.isUMLEnumLiteral()); + a.setBaseType(UMLObject::ObjectType::ot_Folder); + QVERIFY(a.isUMLFolder()); + a.setBaseType(UMLObject::ObjectType::ot_ForeignKeyConstraint); + QVERIFY(a.isUMLForeignKeyConstraint()); + a.setBaseType(UMLObject::ObjectType::ot_Instance); + QVERIFY(a.isUMLInstance()); + a.setBaseType(UMLObject::ObjectType::ot_InstanceAttribute); + QVERIFY(a.isUMLInstanceAttribute()); +// UMLClassifier has isInterface() +// a.setBaseType(UMLObject::ObjectType::ot_Interface); +// QVERIFY(a.isUMLInterface()); + a.setBaseType(UMLObject::ObjectType::ot_Node); + QVERIFY(a.isUMLNode()); + a.setBaseType(UMLObject::ObjectType::ot_Operation); + QVERIFY(a.isUMLOperation()); + a.setBaseType(UMLObject::ObjectType::ot_Package); + QVERIFY(a.isUMLPackage()); + a.setBaseType(UMLObject::ObjectType::ot_Port); + QVERIFY(a.isUMLPort()); + a.setBaseType(UMLObject::ObjectType::ot_Role); + QVERIFY(a.isUMLRole()); + a.setBaseType(UMLObject::ObjectType::ot_Stereotype); + QVERIFY(a.isUMLStereotype()); +// a.setBaseType(UMLObject::ObjectType::ot_SubSystem); +// QVERIFY(a.isUMLSubSystem()); + a.setBaseType(UMLObject::ObjectType::ot_Template); + QVERIFY(a.isUMLTemplate()); + a.setBaseType(UMLObject::ObjectType::ot_UMLObject); + QVERIFY(a.isUMLObject()); + a.setBaseType(UMLObject::ObjectType::ot_UniqueConstraint); + QVERIFY(a.isUMLUniqueConstraint()); + a.setBaseType(UMLObject::ObjectType::ot_UseCase); + QVERIFY(a.isUMLUseCase()); +} QTEST_MAIN(TestUMLObject) diff --git a/unittests/testumlobject.h b/unittests/testumlobject.h index 3fed18a9b..6e1c22685 100644 --- a/unittests/testumlobject.h +++ b/unittests/testumlobject.h @@ -1,47 +1,48 @@ /* Copyright 2015 Ralf Habacker This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef TESTUMLOBJECT_H #define TESTUMLOBJECT_H #include "testbase.h" class TestUMLObject : public TestBase { Q_OBJECT private slots: void test_copyInto(); void test_clone(); void test_doc(); void test_equal(); void test_fullyQualifiedName(); void test_isAbstract(); void test_isStatic(); void test_resolveRef(); void test_saveAndLoad(); void test_setBaseType(); void test_setSterotype(); void test_setUMLPackage(); void test_setVisibility(); void test_toString(); void test_dynamic_cast(); + void test_isUMLXXX(); }; #endif // TESTUMLOBJECT_H