/** ****************************************************************************** * * @file uavobjectgeneratorgcs.cpp * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. * @brief produce gcs code for uavobjects * * @see The GNU Public License (GPL) Version 3 * *****************************************************************************/ /* * 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 3 of the License, or * (at your option) any later version. * * 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, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "uavobjectgeneratorgcs.h" #define VERBOSE false #define DEPRECATED true #define DEFAULT_ENUM_PREFIX "E_" using namespace std; void error(QString msg) { cerr << "error: " << msg.toStdString() << endl; } void warning(ObjectInfo *object, QString msg) { cerr << "warning: " << object->filename.toStdString() << ": " << msg.toStdString() << endl; } void info(ObjectInfo *object, QString msg) { if (VERBOSE) { cout << "info: " << object->filename.toStdString() << ": " << msg.toStdString() << endl; } } struct Context { ObjectInfo *object; // enums QString enums; QString registerImpl; // interface QString fields; QString fieldsInfo; QString properties; QString deprecatedProperties; QString getters; QString setters; QString notifications; // implementation QString fieldsInit; QString fieldsDefault; QString propertiesImpl; QString notificationsImpl; }; struct FieldContext { FieldInfo *field; // field QString fieldName; QString fieldType; // property QString propName; QString ucPropName; QString propType; QString propRefType; // deprecation bool hasDeprecatedProperty; bool hasDeprecatedGetter; bool hasDeprecatedSetter; bool hasDeprecatedNotification; }; QString fieldTypeStrCPP(int type) { QStringList fieldTypeStrCPP; fieldTypeStrCPP << "qint8" << "qint16" << "qint32" << "quint8" << "quint16" << "quint32" << "float" << "quint8"; return fieldTypeStrCPP[type]; } QString fieldTypeStrCPPClass(int type) { QStringList fieldTypeStrCPPClass; fieldTypeStrCPPClass << "INT8" << "INT16" << "INT32" << "UINT8" << "UINT16" << "UINT32" << "FLOAT32" << "ENUM"; return fieldTypeStrCPPClass[type]; } QString toPropertyName(const QString & name) { QString str = name; // make sure 1st letter is upper case str[0] = str[0].toUpper(); // handle underscore int p = str.indexOf('_'); while (p != -1) { str.remove(p, 1); str[p] = str[p].toUpper(); p = str.indexOf('_', p); } return str; } /* * Convert a string to lower camel case. * Handles following cases : * - Property -> property * - MyProperty -> myProperty * - MYProperty -> myProperty * - MY_Property -> my_Property * - MY -> my */ QString toLowerCamelCase(const QString & name) { QString str = name; for (int i = 0; i < str.length(); ++i) { if (str[i].isLower() || !str[i].isLetter()) { break; } if (i > 0 && i < str.length() - 1) { // after first, look ahead one if (str[i + 1].isLower()) { break; } } str[i] = str[i].toLower(); } return str; } QString toEnumName(ObjectInfo *object, FieldInfo *field, int index) { QString option = field->options[index]; if (option.contains(QRegExp(ENUM_SPECIAL_CHARS))) { info(object, "Enumeration value \"" + option + "\" contains special chars, cleaning."); option.replace(QRegExp(ENUM_SPECIAL_CHARS), ""); } if (option[0].isDigit()) { info(object, "Enumeration value \"" + option + "\" starts with a digit, prefixing with \"" + DEFAULT_ENUM_PREFIX "\"."); option = DEFAULT_ENUM_PREFIX + option; } if (option == option.toLower()) { warning(object, "Enumeration value \"" + option + "\" is all lower case, consider capitalizing."); } if (option[0].isLower()) { warning(object, "Enumeration value \"" + option + "\" does not start with an upper case letter."); option[0] = option[0].toUpper(); } if (option == "FALSE") { warning(object, "Invalid enumeration name FALSE, converting to False."); option = "False"; } if (option == "TRUE") { warning(object, "Invalid enumeration name TRUE, converting to True."); option = "True"; } return option; } QString toEnumStringList(ObjectInfo *object, FieldInfo *field) { QString enumListString; for (int m = 0; m < field->options.length(); ++m) { if (m > 0) { enumListString.append(", "); } QString option = toEnumName(object, field, m); enumListString.append(option); } return enumListString; } QString generate(Context &ctxt, const QString &fragment) { QString str = fragment; str.replace(":ClassName", ctxt.object->name); str.replace(":className", ctxt.object->namelc); return str; } QString generate(Context &ctxt, FieldContext &fieldCtxt, const QString &fragment) { QString str = generate(ctxt, fragment); str.replace(":PropName", fieldCtxt.ucPropName); str.replace(":propName", fieldCtxt.propName); str.replace(":propType", fieldCtxt.propType); str.replace(":propRefType", fieldCtxt.propRefType); str.replace(":fieldName", fieldCtxt.fieldName); str.replace(":fieldType", fieldCtxt.fieldType); str.replace(":fieldDesc", fieldCtxt.field->description); str.replace(":fieldUnits", fieldCtxt.field->units); str.replace(":fieldLimitValues", fieldCtxt.field->limitValues); str.replace(":elementCount", QString::number(fieldCtxt.field->numElements)); return str; } void generateFieldInfo(Context &ctxt, FieldContext &fieldCtxt) { ctxt.fieldsInfo += generate(ctxt, fieldCtxt, " // :fieldName\n"); if (fieldCtxt.field->type == FIELDTYPE_ENUM) { QStringList options = fieldCtxt.field->options; ctxt.fieldsInfo += " typedef enum { "; for (int m = 0; m < options.length(); ++m) { if (m > 0) { ctxt.fieldsInfo.append(", "); } ctxt.fieldsInfo += generate(ctxt, fieldCtxt, "%1_%2=%3") .arg(fieldCtxt.field->name.toUpper()) .arg(options[m].toUpper().replace(QRegExp(ENUM_SPECIAL_CHARS), "")) .arg(m); } ctxt.fieldsInfo += generate(ctxt, fieldCtxt, " } :fieldNameOptions;\n"); } // Generate element names (only if field has more than one element) if (fieldCtxt.field->numElements > 1 && !fieldCtxt.field->defaultElementNames) { QStringList elemNames = fieldCtxt.field->elementNames; ctxt.fieldsInfo += " typedef enum { "; for (int m = 0; m < elemNames.length(); ++m) { if (m > 0) { ctxt.fieldsInfo.append(", "); } ctxt.fieldsInfo += QString("%1_%2=%3") .arg(fieldCtxt.field->name.toUpper()) .arg(elemNames[m].toUpper()) .arg(m); } ctxt.fieldsInfo += generate(ctxt, fieldCtxt, " } :fieldNameElem;\n"); } // Generate array information if (fieldCtxt.field->numElements > 1) { ctxt.fieldsInfo += generate(ctxt, fieldCtxt, " static const quint32 %1_NUMELEM = :elementCount;\n") .arg(fieldCtxt.field->name.toUpper()); } } void generateFieldInit(Context &ctxt, FieldContext &fieldCtxt) { ctxt.fieldsInit += generate(ctxt, fieldCtxt, " // :fieldName\n"); // Setup element names ctxt.fieldsInit += generate(ctxt, fieldCtxt, " QStringList :fieldNameElemNames;\n"); QStringList elemNames = fieldCtxt.field->elementNames; ctxt.fieldsInit += generate(ctxt, fieldCtxt, " :fieldNameElemNames"); for (int m = 0; m < elemNames.length(); ++m) { ctxt.fieldsInit += QString(" << \"%1\"").arg(elemNames[m]); } ctxt.fieldsInit += ";\n"; if (fieldCtxt.field->type == FIELDTYPE_ENUM) { ctxt.fieldsInit += generate(ctxt, fieldCtxt, " QStringList :fieldNameEnumOptions;\n"); QStringList options = fieldCtxt.field->options; ctxt.fieldsInit += generate(ctxt, fieldCtxt, " :fieldNameEnumOptions"); for (int m = 0; m < options.length(); ++m) { ctxt.fieldsInit += QString(" << \"%1\"").arg(options[m]); } ctxt.fieldsInit += ";\n"; ctxt.fieldsInit += generate(ctxt, fieldCtxt, " fields.append(new UAVObjectField(\":fieldName\", tr(\":fieldDesc\"), \":fieldUnits\", UAVObjectField::ENUM, :fieldNameElemNames, :fieldNameEnumOptions, \":fieldLimitValues\"));\n"); } else { ctxt.fieldsInit += generate(ctxt, fieldCtxt, " fields.append(new UAVObjectField(\":fieldName\", tr(\":fieldDesc\"), \":fieldUnits\", UAVObjectField::%1, :fieldNameElemNames, QStringList(), \":fieldLimitValues\"));\n") .arg(fieldTypeStrCPPClass(fieldCtxt.field->type)); } } void generateFieldDefault(Context &ctxt, FieldContext &fieldCtxt) { if (!fieldCtxt.field->defaultValues.isEmpty()) { // For non-array fields if (fieldCtxt.field->numElements == 1) { ctxt.fieldsDefault += generate(ctxt, fieldCtxt, " // :fieldName\n"); if (fieldCtxt.field->type == FIELDTYPE_ENUM) { ctxt.fieldsDefault += generate(ctxt, fieldCtxt, " data_.:fieldName = %1;\n") .arg(fieldCtxt.field->options.indexOf(fieldCtxt.field->defaultValues[0])); } else if (fieldCtxt.field->type == FIELDTYPE_FLOAT32) { ctxt.fieldsDefault += generate(ctxt, fieldCtxt, " data_.:fieldName = %1;\n") .arg(fieldCtxt.field->defaultValues[0].toFloat()); } else { ctxt.fieldsDefault += generate(ctxt, fieldCtxt, " data_.:fieldName = %1;\n") .arg(fieldCtxt.field->defaultValues[0].toInt()); } } else { // Initialize all fields in the array ctxt.fieldsDefault += generate(ctxt, fieldCtxt, " // :fieldName\n"); for (int idx = 0; idx < fieldCtxt.field->numElements; ++idx) { if (fieldCtxt.field->type == FIELDTYPE_ENUM) { ctxt.fieldsDefault += generate(ctxt, fieldCtxt, " data_.:fieldName[%1] = %2;\n") .arg(idx) .arg(fieldCtxt.field->options.indexOf(fieldCtxt.field->defaultValues[idx])); } else if (fieldCtxt.field->type == FIELDTYPE_FLOAT32) { ctxt.fieldsDefault += generate(ctxt, fieldCtxt, " data_.:fieldName[%1] = %2;\n") .arg(idx) .arg(fieldCtxt.field->defaultValues[idx].toFloat()); } else { ctxt.fieldsDefault += generate(ctxt, fieldCtxt, " data_.:fieldName[%1] = %2;\n") .arg(idx) .arg(fieldCtxt.field->defaultValues[idx].toInt()); } } } } } void generateField(Context &ctxt, FieldContext &fieldCtxt) { if (fieldCtxt.field->numElements > 1) { ctxt.fields += generate(ctxt, fieldCtxt, " :fieldType :fieldName[:elementCount];\n"); } else { ctxt.fields += generate(ctxt, fieldCtxt, " :fieldType :fieldName;\n"); } generateFieldInfo(ctxt, fieldCtxt); generateFieldInit(ctxt, fieldCtxt); generateFieldDefault(ctxt, fieldCtxt); } void generateEnum(Context &ctxt, FieldContext &fieldCtxt) { Q_ASSERT(fieldCtxt.field->type == FIELDTYPE_ENUM); QString enumStringList = toEnumStringList(ctxt.object, fieldCtxt.field); ctxt.enums += generate(ctxt, fieldCtxt, "class :ClassName_:PropName : public QObject {\n" " Q_OBJECT\n" "public:\n" " enum Enum { %1 };\n" " Q_ENUMS(Enum) // TODO switch to Q_ENUM once on Qt 5.5\n" "};\n\n").arg(enumStringList); ctxt.registerImpl += generate(ctxt, fieldCtxt, " qmlRegisterType<:ClassName_:PropName>(\"%1.:ClassName\", 1, 0, \":PropName\");\n").arg("UAVTalk"); } void generateBaseProperty(Context &ctxt, FieldContext &fieldCtxt) { ctxt.properties += generate(ctxt, fieldCtxt, " Q_PROPERTY(:propType :propName READ :propName WRITE set:PropName NOTIFY :propNameChanged)\n"); ctxt.getters += generate(ctxt, fieldCtxt, " :propType :propName() const;\n"); ctxt.setters += generate(ctxt, fieldCtxt, " void set:PropName(const :propRefType value);\n"); ctxt.notifications += generate(ctxt, fieldCtxt, " void :propNameChanged(const :propRefType value);\n"); ctxt.notificationsImpl += generate(ctxt, fieldCtxt, " emit :propNameChanged(:propName());\n"); if (DEPRECATED) { // generate deprecated property for retro compatibility if (fieldCtxt.hasDeprecatedProperty) { ctxt.deprecatedProperties += generate(ctxt, fieldCtxt, " /*DEPRECATED*/ Q_PROPERTY(:fieldType :fieldName READ get:fieldName WRITE set:fieldName NOTIFY :fieldNameChanged);\n"); } if (fieldCtxt.hasDeprecatedGetter) { ctxt.getters += generate(ctxt, fieldCtxt, " /*DEPRECATED*/ Q_INVOKABLE :fieldType get:fieldName() const { return static_cast<:fieldType>(:propName()); }\n"); } if (fieldCtxt.hasDeprecatedSetter) { ctxt.setters += generate(ctxt, fieldCtxt, " /*DEPRECATED*/ void set:fieldName(:fieldType value) { set:fieldName(static_cast<:propType>(value)); }\n"); } if (fieldCtxt.hasDeprecatedNotification) { ctxt.notifications += generate(ctxt, fieldCtxt, " /*DEPRECATED*/ void :fieldNameChanged(:fieldType value);\n"); ctxt.notificationsImpl += generate(ctxt, fieldCtxt, " /*DEPRECATED*/ emit :fieldNameChanged(get:fieldName());\n"); } } } void generateSimpleProperty(Context &ctxt, FieldContext &fieldCtxt) { if (fieldCtxt.field->type == FIELDTYPE_ENUM) { generateEnum(ctxt, fieldCtxt); } generateBaseProperty(ctxt, fieldCtxt); // getter implementation ctxt.propertiesImpl += generate(ctxt, fieldCtxt, ":propType :ClassName:::propName() const\n" "{\n" " QMutexLocker locker(mutex);\n" " return static_cast<:propType>(data_.:fieldName);\n" "}\n"); // emitters QString emitters = generate(ctxt, fieldCtxt, "emit :propNameChanged(value);"); if (fieldCtxt.hasDeprecatedNotification) { emitters += " "; emitters += generate(ctxt, fieldCtxt, "emit :fieldNameChanged(static_cast<:fieldType>(value));"); } // setter implementation ctxt.propertiesImpl += generate(ctxt, fieldCtxt, "void :ClassName::set:PropName(const :propRefType value)\n" "{\n" " mutex->lock();\n" " bool changed = (data_.:fieldName != static_cast<:fieldType>(value));\n" " data_.:fieldName = static_cast<:fieldType>(value);\n" " mutex->unlock();\n" " if (changed) { %1 }\n" "}\n\n").arg(emitters); } void generateIndexedProperty(Context &ctxt, FieldContext &fieldCtxt) { if (fieldCtxt.field->type == FIELDTYPE_ENUM) { generateEnum(ctxt, fieldCtxt); } // indexed getter/setter ctxt.getters += generate(ctxt, fieldCtxt, " Q_INVOKABLE :propType :propName(quint32 index) const;\n"); ctxt.setters += generate(ctxt, fieldCtxt, " Q_INVOKABLE void set:PropName(quint32 index, const :propRefType value);\n"); // getter implementation ctxt.propertiesImpl += generate(ctxt, fieldCtxt, ":propType :ClassName:::propName(quint32 index) const\n" "{\n" " QMutexLocker locker(mutex);\n" " return static_cast<:propType>(data_.:fieldName[index]);\n" "}\n"); // emitters QString emitters = generate(ctxt, fieldCtxt, "emit :propNameChanged(index, value);"); if (fieldCtxt.hasDeprecatedNotification) { emitters += " "; emitters += generate(ctxt, fieldCtxt, "emit :fieldNameChanged(index, static_cast<:fieldType>(value));"); } // setter implementation ctxt.propertiesImpl += generate(ctxt, fieldCtxt, "void :ClassName::set:PropName(quint32 index, const :propRefType value)\n" "{\n" " mutex->lock();\n" " bool changed = (data_.:fieldName[index] != static_cast<:fieldType>(value));\n" " data_.:fieldName[index] = static_cast<:fieldType>(value);\n" " mutex->unlock();\n" " if (changed) { %1 }\n" "}\n\n").arg(emitters); ctxt.notifications += generate(ctxt, fieldCtxt, " void :propNameChanged(quint32 index, const :propRefType value);\n"); if (DEPRECATED) { // backward compatibility if (fieldCtxt.hasDeprecatedGetter) { ctxt.getters += generate(ctxt, fieldCtxt, " /*DEPRECATED*/ Q_INVOKABLE :fieldType get:fieldName(quint32 index) const { return static_cast<:fieldType>(:propName(index)); }\n"); } if (fieldCtxt.hasDeprecatedSetter) { ctxt.setters += generate(ctxt, fieldCtxt, " /*DEPRECATED*/ void set:fieldName(quint32 index, :fieldType value) { set:fieldName(index, static_cast<:propType>(value)); }\n"); } if (fieldCtxt.hasDeprecatedNotification) { ctxt.notifications += generate(ctxt, fieldCtxt, " /*DEPRECATED*/ void :fieldNameChanged(quint32 index, :fieldType value);\n"); } } for (int elementIndex = 0; elementIndex < fieldCtxt.field->numElements; elementIndex++) { QString elementName = fieldCtxt.field->elementNames[elementIndex]; FieldContext elementCtxt; elementCtxt.field = fieldCtxt.field; elementCtxt.fieldName = fieldCtxt.fieldName + "_" + elementName; elementCtxt.fieldType = fieldCtxt.fieldType; elementCtxt.propName = fieldCtxt.propName + elementName; elementCtxt.ucPropName = fieldCtxt.ucPropName + elementName; elementCtxt.propType = fieldCtxt.propType; elementCtxt.propRefType = fieldCtxt.propRefType; // deprecation elementCtxt.hasDeprecatedProperty = (elementCtxt.fieldName != elementCtxt.propName) && DEPRECATED; elementCtxt.hasDeprecatedGetter = DEPRECATED; elementCtxt.hasDeprecatedSetter = ((elementCtxt.fieldName != elementCtxt.ucPropName) || (elementCtxt.fieldType != elementCtxt.propType)) && DEPRECATED; elementCtxt.hasDeprecatedNotification = ((elementCtxt.fieldName != elementCtxt.propName) || (elementCtxt.fieldType != elementCtxt.propType)) && DEPRECATED; generateBaseProperty(ctxt, elementCtxt); ctxt.propertiesImpl += generate(ctxt, elementCtxt, ":propType :ClassName:::propName() const { return %1(%2); }\n").arg(fieldCtxt.propName).arg(elementIndex); ctxt.propertiesImpl += generate(ctxt, elementCtxt, "void :ClassName::set:PropName(const :propRefType value) { set%1(%2, value); }\n").arg(fieldCtxt.ucPropName).arg(elementIndex); } } void generateProperty(Context &ctxt, FieldContext &fieldCtxt) { // do some checks QString fieldName = fieldCtxt.fieldName; if (fieldName[0].isLower()) { info(ctxt.object, "Field \"" + fieldName + "\" does not start with an upper case letter."); } // generate all properties if (fieldCtxt.field->numElements > 1) { generateIndexedProperty(ctxt, fieldCtxt); } else { generateSimpleProperty(ctxt, fieldCtxt); } } bool UAVObjectGeneratorGCS::generate(UAVObjectParser *parser, QString templatepath, QString outputpath) { gcsCodePath = QDir(templatepath + QString(GCS_CODE_DIR)); gcsOutputPath = QDir(outputpath); gcsOutputPath.mkpath(gcsOutputPath.absolutePath()); gcsCodeTemplate = readFile(gcsCodePath.absoluteFilePath("uavobject.cpp.template")); gcsIncludeTemplate = readFile(gcsCodePath.absoluteFilePath("uavobject.h.template")); QString gcsInitTemplate = readFile(gcsCodePath.absoluteFilePath("uavobjectsinit.cpp.template")); if (gcsCodeTemplate.isEmpty() || gcsIncludeTemplate.isEmpty() || gcsInitTemplate.isEmpty()) { error("Error: Failed to read gcs code templates."); return false; } QString objInc; QString gcsObjInit; for (int objidx = 0; objidx < parser->getNumObjects(); ++objidx) { ObjectInfo *object = parser->getObjectByIndex(objidx); process_object(object); Context ctxt; ctxt.object = object; objInc.append(QString("#include \"%1.h\"\n").arg(object->namelc)); gcsObjInit += ::generate(ctxt, " objMngr->registerObject( new :ClassName() );\n"); gcsObjInit += ::generate(ctxt, " :ClassName::registerQMLTypes();\n"); } // Write the gcs object initialization files gcsInitTemplate.replace("$(OBJINC)", objInc); gcsInitTemplate.replace("$(OBJINIT)", gcsObjInit); bool res = writeFileIfDifferent(gcsOutputPath.absolutePath() + "/uavobjectsinit.cpp", gcsInitTemplate); if (!res) { error("Error: Could not write output files"); return false; } return true; } /** * Generate the GCS object files * * TODO add getter to get enum names * * TODO handle "char" unit * TODO handle "bool" unit * TODO handle "hex" unit * TODO handle Vector */ bool UAVObjectGeneratorGCS::process_object(ObjectInfo *object) { if (info == NULL) { return false; } // Prepare output strings QString outInclude = gcsIncludeTemplate; QString outCode = gcsCodeTemplate; // Replace common tags replaceCommonTags(outInclude, object); replaceCommonTags(outCode, object); // to avoid name conflicts QStringList reservedProperties; reservedProperties << "Description" << "Metadata"; Context ctxt; ctxt.object = object; ctxt.registerImpl += ::generate(ctxt, " qmlRegisterType<:ClassName>(\"%1.:ClassName\", 1, 0, \":ClassName\");\n").arg("UAVTalk"); for (int n = 0; n < object->fields.length(); ++n) { FieldInfo *field = object->fields[n]; // field context FieldContext fieldCtxt; fieldCtxt.field = field; // field properties fieldCtxt.fieldName = field->name; fieldCtxt.fieldType = fieldTypeStrCPP(field->type); fieldCtxt.ucPropName = toPropertyName(field->name); fieldCtxt.propName = toLowerCamelCase(fieldCtxt.ucPropName); fieldCtxt.propType = fieldCtxt.fieldType; fieldCtxt.propRefType = fieldCtxt.fieldType; if (field->type == FIELDTYPE_ENUM) { QString enumClassName = object->name + "_" + fieldCtxt.ucPropName; fieldCtxt.propType = enumClassName + "::Enum"; fieldCtxt.propRefType = fieldCtxt.propType; } // deprecation fieldCtxt.hasDeprecatedProperty = (fieldCtxt.fieldName != fieldCtxt.propName) && DEPRECATED; fieldCtxt.hasDeprecatedGetter = DEPRECATED; fieldCtxt.hasDeprecatedSetter = ((fieldCtxt.fieldName != fieldCtxt.ucPropName) || (fieldCtxt.fieldType != fieldCtxt.propType)) && DEPRECATED; fieldCtxt.hasDeprecatedNotification = ((fieldCtxt.fieldName != fieldCtxt.propName) || (fieldCtxt.fieldType != fieldCtxt.propType)) && DEPRECATED; generateField(ctxt, fieldCtxt); if (reservedProperties.contains(field->name)) { warning(object, "Ignoring reserved property " + field->name + "."); continue; } generateProperty(ctxt, fieldCtxt); } outInclude.replace("$(ENUMS)", ctxt.enums); outInclude.replace("$(DATAFIELDS)", ctxt.fields); outInclude.replace("$(DATAFIELDINFO)", ctxt.fieldsInfo); outInclude.replace("$(PROPERTIES)", ctxt.properties); outInclude.replace("$(DEPRECATED_PROPERTIES)", ctxt.deprecatedProperties); outInclude.replace("$(PROPERTY_GETTERS)", ctxt.getters); outInclude.replace("$(PROPERTY_SETTERS)", ctxt.setters); outInclude.replace("$(PROPERTY_NOTIFICATIONS)", ctxt.notifications); outCode.replace("$(FIELDSINIT)", ctxt.fieldsInit); outCode.replace("$(FIELDSDEFAULT)", ctxt.fieldsDefault); outCode.replace("$(PROPERTIES_IMPL)", ctxt.propertiesImpl); outCode.replace("$(NOTIFY_PROPERTIES_CHANGED)", ctxt.notificationsImpl); outCode.replace("$(REGISTER_QML_TYPES)", ctxt.registerImpl); // Write the GCS code bool res = writeFileIfDifferent(gcsOutputPath.absolutePath() + "/" + object->namelc + ".cpp", outCode); if (!res) { error("Error: Could not write gcs output files"); return false; } res = writeFileIfDifferent(gcsOutputPath.absolutePath() + "/" + object->namelc + ".h", outInclude); if (!res) { error("Error: Could not write gcs output files"); return false; } return true; }