diff options
| author | Jenkins CI <null@kde.org> | 2013-12-18 00:45:18 +0000 |
|---|---|---|
| committer | Jenkins CI <null@kde.org> | 2013-12-18 00:45:18 +0000 |
| commit | c38b88497a833e482e6892b72c8f52adec6de857 (patch) | |
| tree | 8c2d4b788cf54ab2179ffe53515d276feaeba2d1 /tier1/kconfig/src/kconfig_compiler | |
| download | kconfig-c38b88497a833e482e6892b72c8f52adec6de857.tar.gz kconfig-c38b88497a833e482e6892b72c8f52adec6de857.tar.bz2 | |
Initial import from the monolithic kdelibs.
This is the beginning of revision history for this module. If you
want to look at revision history older than this, please refer to the
techbase wiki for how to use Git history grafting. At the time of
writing, this wiki is located here:
http://community.kde.org/Frameworks/GitOldHistory
If you have already performed the grafting and you don't see any
history beyond this commit, try running "git log" with the "--follow"
argument.
Branched from the monolithic repo, kdelibs frameworks branch, at commit
162066dbbecde401a7347a1cff4fe72a9c919f58
Diffstat (limited to 'tier1/kconfig/src/kconfig_compiler')
| -rw-r--r-- | tier1/kconfig/src/kconfig_compiler/CMakeLists.txt | 15 | ||||
| -rw-r--r-- | tier1/kconfig/src/kconfig_compiler/README.dox | 446 | ||||
| -rw-r--r-- | tier1/kconfig/src/kconfig_compiler/TODO | 0 | ||||
| -rwxr-xr-x | tier1/kconfig/src/kconfig_compiler/checkkcfg.pl | 83 | ||||
| -rw-r--r-- | tier1/kconfig/src/kconfig_compiler/kcfg.xsd | 234 | ||||
| -rw-r--r-- | tier1/kconfig/src/kconfig_compiler/kconfig_compiler.cpp | 2338 |
6 files changed, 3116 insertions, 0 deletions
diff --git a/tier1/kconfig/src/kconfig_compiler/CMakeLists.txt b/tier1/kconfig/src/kconfig_compiler/CMakeLists.txt new file mode 100644 index 00000000..31726df1 --- /dev/null +++ b/tier1/kconfig/src/kconfig_compiler/CMakeLists.txt @@ -0,0 +1,15 @@ + + +set(kconfig_compiler_SRCS kconfig_compiler.cpp) + + +add_executable(kconfig_compiler ${kconfig_compiler_SRCS}) +add_executable(KF5::kconfig_compiler ALIAS kconfig_compiler) + +find_package(Qt5Xml 5.2.0 REQUIRED NO_MODULE) + +target_link_libraries(kconfig_compiler Qt5::Xml) + +# "export" this target too so we can use the LOCATION property of the imported target in +# FindKDE4Internal.cmake to get the full path to the installed executable instead of using FIND_PROGRAM(), Alex +install(TARGETS kconfig_compiler EXPORT KF5ConfigTargets ${INSTALL_TARGETS_DEFAULT_ARGS} ) diff --git a/tier1/kconfig/src/kconfig_compiler/README.dox b/tier1/kconfig/src/kconfig_compiler/README.dox new file mode 100644 index 00000000..b9606f1d --- /dev/null +++ b/tier1/kconfig/src/kconfig_compiler/README.dox @@ -0,0 +1,446 @@ +/** +\page kconfig_compiler The KDE Configuration Compiler + +kconfig_compiler generates C++ source code from an XML file containing +information about configuration options (.kcfg) and a file that provides +the code generation options (.kcfgc) The generated class is based on +KConfigSkeleton and provides an API for the application to access its +configuration data. + +The generated C++ source code is output to a .h and a .cpp file, whose base +name is the same as that of the .kcfgc file. + +<h2>XML description of the configuration options</h2> + +The structure of the .kcfg file is described by its DTD kcfg.xsd. + +The \<kcfgfile\> tag may contain either the "name" attribute, which should be the name +of the configuration file described, or the "arg" attribute, which, if set to +"true", will allow you to pass the KSharedConfig::Ptr object to use. + +If neither "name" nor "arg" is set, the default configuration file +("\<appname\>rc") will be used. + +The \<include\> tags are optional and may contain C++ header files that +are needed to compile the code needed to compute default values. To generate +a \#include "..." statement instead of \#include \<...\>, enclose the header +file name in double quotes (e.g. \<include\>"header.h"\</include\>). + +The remaining entries in the XML file are grouped by the tag \<group\> +which describes the corresponding group in the configuration file. + +The individual entries must have at least a name or a key. The key is used +as the key in the config file, while the name is used to create accessor and +modifier functions. If \<key\> is given, but not \<name\>, the name is +constructed by removing all spaces from \<key\>. If \<name\> is given, but +not \<key\>, the key is the same as \<name\>. + +An entry must also have a type. The list of allowable types is +specified in the DTD and loosely follows the list of types supported +by the QVariant with exception of the clearly binary types +(e.g. Pixmap, Image...) which are not supported. Besides those basic +types the following special types are supported: + +- Path This is a string that is specially treated as a file-path. + In particular paths in the home directory are prefixed with $HOME in + when being stored in the configuration file. + +- Enum This indicates an enumeration. The possible enum values and optional + enum name should be provided via the \<choices\> tag. Enum values are + accessed as integers by the application but stored as strings in the + configuration file. This makes it possible to add more values at a later + date without breaking compatibility. + +- IntList This indicates a list of integers. This information is provided + to the application as QValueList<int>. Useful for storing QSplitter + geometries. + +An entry can optionally have a default value which is used as default when +the value isn't specified in any config file. Default values are interpreted +as literal constant values. If a default value needs to be computed +or if it needs to be obtained from a function call, the \<default\> tag +should contain the code="true" attribute. The contents of the \<default\> +tag is then considered to be a C++ expression. Note that in this case you +might have to add an \<include\> tag as described above, or a +SourceIncludeFiles entry in the .kcfgc file as described below, so that the +code which computes the default value can be compiled. + +Additional code for computing default values can be provided outside any +entry definition via the \<code\> tag. The contents of the \<code\> tag is +inserted as-is. A typical use for this is to compute a common default value +which can then be referenced by multiple entries that follow. + +<h2>Code generation options</h2> + +The options for generating the C++ sources are read from the file with the +extension .kcfgc. To generate a class add the corresponding kcfgc file to the +SOURCES line in the Makefile.am. + +The following options are read from the kcfgc file: + +<table> +<tr> + <td><b><i>Name</i></b></td> + <td><b><i>Type</i></b></td> + <td><b><i>Default</i></b></td> + <td><b><i>Description</i></b></td> +</tr> +<tr> + <td><b>File</b></td> + <td>string</td> + <td>programname.kcfg</td> + <td>Name of kcfg file containing the options the class is generated for</td> +</tr> +<tr> + <td><b>NameSpace</b></td> + <td>string</td> + <td>-</td> + <td>Optional namespace for generated class</td> +</tr> +<tr> + <td><b>ClassName</b></td> + <td>string</td> + <td>-</td> + <td>Name of generated class (required)</td> +</tr> +<tr> + <td><b>Inherits</b></td> + <td>string</td> + <td>KConfigSkeleton</td> + <td>Class the generated class inherits from. This class must inherit + KConfigSkeleton.</td> +</tr> +<tr> + <td><b>Visibility</b></td> + <td>string</td> + <td>-</td> + <td>Inserts visibility directive (for example KDE_EXPORT) between "class" keyword and class + name in header file</td> +</tr> +<tr> + <td><b>Singleton</b></td> + <td>bool</td> + <td>false</td> + <td>Generated class is a singleton.</td> +</tr> +<tr> + <td><b>CustomAdditions</b></td> + <td>bool</td> + <td>-</td> + <td></td> +</tr> +<tr> + <td><b>MemberVariables</b></td> + <td>string: public|protected|private|dpointer</td> + <td>private</td> + <td>C++ access modifier used for member variables holding the configuration + values</td> +</tr> +<tr> + <td><b>IncludeFiles</b></td> + <td>comma separated list of strings</td> + <td>-</td> + <td>Names of files to be included in the header of the generated class. Enclose a + file name in (escaped) double quotes to generate \#include "..." instead of + \#include \<...\>.</td> +</tr> +<tr> + <td><b>SourceIncludeFiles</b></td> + <td>comma separated list of strings</td> + <td>-</td> + <td>Names of files to be included in the source file of the generated class. Enclose + a file name in (escaped) double quotes to generate \#include "..." instead of + \#include \<...\>.</td> +</tr> +<tr> + <td><b>Mutators</b></td> + <td>true, false or a comma separated list of options</td> + <td>false</td> + <td>If true, mutator functions for all configuration options are generated. + If false, no mutator functions are generated. If a list is provided, + mutator functions are generated for the options that are listed.</td> +</tr> +<tr> + <td><b>DefaultValueGetters</b></td> + <td>true, false or a comma separated list of options</td> + <td>false</td> + <td>If true, functions to return the default value of all configuration options + are generated. If false, no default value functions are generated. If a list + is provided, default value functions are generated for the options that are listed.</td> +</tr> +<tr> + <td><b>ItemAccessors</b></td> + <td>bool</td> + <td>false</td> + <td>Generate accessor functions for the KConfigSkeletonItem objects + corresponding to the configuration options. If <b>SetUserTexts</b> is set, + <b>ItemAccessors</b> also has to be set.</td> +</tr> +<tr> + <td><b>SetUserTexts</b></td> + <td>bool</td> + <td>false</td> + <td>Set the label and whatthis texts of the items from the kcfg file.If + <b>SetUserTexts</b> is set, <b>ItemAccessors</b> also has to be set.</td> +</tr> +<tr> + <td><b>GlobalEnums</b></td> + <td>bool</td> + <td>false</td> + <td>If set to true all choices of Enum items will be created in the global + scope of the generated class. If set to false, each Enum item whose enum is not + explicitly named will get its own namespace for its choices.</td> +</tr> +<tr> + <td><b>UseEnumTypes</b></td> + <td>bool</td> + <td>false</td> + <td>If set to true, all Enum items whose enums are named will use enum types for + the return value of accessor functions and for the parameter of mutator + functions. This eliminates the need to cast accessor return values to the enum + type if you want to use the enum type in your own code. If set to false, + accessor return values and mutator parameters will be of type int.</td> +</tr> +<tr> + <td><b>ForceStringFilename</b></td> + <td>bool</td> + <td>false</td> + <td>If set to true, forces the first parameter of the generated class to be a QString when using an argument for the filename. This is useful to specify at runtime the filename of the configuration class.</td> +</table> + + +<h2>Advanced options</h2> + +There are several possibilities to parameterize entries. + +- Parameterized entries + +An entry can be parameterized using a fixed range parameter specified with +the \<parameter\> tag. Such parameter can either be an Enum or an int. An Enum +parameter should specify the possible enumeration values with the \<choices\> +tag. An int parameter should specify its maximum value. Its minimum value +is always 0. + +A parameterized entry is expanded to a number of entries, one for each +value in the parameter range. The name and key should contain a reference +to the parameter in the form of $(parameter-name). When expanding the entries +the $(parameter-name) part is replaced with the value of the parameter. +In the case of an Enum parameter it is replaced with the name of the +enumuration value. In the case of an int parameter it is replaced with +the numeric value of the parameter. + +Parameterized entries all share the same default value unless different +default values have been specified for specific parameter values. +This can be done with the param= attribute of the \<default\>. When a +param attribute is specified the default value only applies to that +particular parameter value. + +Example 1: +\verbatim + <entry name="Color$(ColorIndex)" type="Color" key="color_$(ColorIndex)"> + <parameter name="ColorIndex" type="Int" max="3"/> + <default param="0">#ff0000</default> + <default param="1">#00ff00</default> + <default param="2">#0000ff</default> + <default param="3">#ffff00</default> + </entry> +\endverbatim + +The above describes 4 color configuration entries with the following defaults: + +\verbatim +color_0=#ff0000 +color_1=#00ff00 +color_2=#0000ff +color_3=#ffff00 +\endverbatim + +The configuration options will be accessible to the application via +a QColor color(int ColorIndex) and a +void setColor(int ColorIndex, const QColor &v) function. + +Example 2: +\verbatim + <entry name="Sound$(SoundEvent)" type="String" key="sound_$(SoundEvent)"> + <parameter name="SoundEvent" type="Enum"> + <values> + <value>Explosion</value> + <value>Crash</value> + <value>Missile</value> + </values> + </parameter> + <default param="Explosion">boom.wav</default> + <default param="Crash">crash.wav</default> + <default param="Missile">missile.wav</default> + </entry> +\endverbatim + +The above describes 3 string configuration entries with the following defaults: + +sound_Explosion=boom.wav +sound_Crash=crash.wav +sound_Missile=missile.wav + +The configuration options will be accessible to the application via +a QString sound(int SoundEvent) and a +void setSound(int SoundEvent, const QString &v) function. + +- Parameterized groups + +A group name can be parametrized using a parameter given to the KConfigSkeleton +instance (which means this feature cannot be used with singleton classes). + +Example 1: +\verbatim + <kcfgfile name="testrc"> + <parameter name="groupname"/> + </kcfgfile> + <group name="$(groupname)"> + <entry key="Text" type="string"> + </entry> + </group> +\endverbatim + +In this case passing "Group2" as the 'groupname' parameter to the generated class +will make it use group "Group2" for the entry "Text". + +- Enums + +By default, if <b>GlobalEnums</b> is set to false, a separate named enum will be generated +for each Enum entry. Since each enum is defined in a little enclosing class of its own, +this allows the same Enum value names to be used in different enums. For example, the +.kcfg entry + +\verbatim +<entry name="KeepData" type="Enum"> + <choices> + <choice name="Do"> + <choice name="Dont"> + </choices> +</entry> +\endverbatim + +will generate this public class containing the enum definition, inside the generated class: + +\verbatim + class EnumKeepData + { + public: + enum type { Do, Dont, COUNT }; + }; +\endverbatim + +Alternatively, if <b>GlobalEnums</b> is set to true, all Enum items are defined as +unnamed enums in the global scope of the generated class. In this case, all Enum values +must have different names to avoid clashes. However, you can use a 'prefix' argument +in \<choices\> to prevent duplicate enum member names clashing. Using this, the Enum value +names are prefixed in code with the string you specify. For example, if <b>GlobalEnums</b> +is set to true, the .kcfg entry + +\verbatim +<entry name="KeepData" type="Enum"> + <choices prefix="Keep_"> + <choice name="Do"> + <choice name="Dont"> + </choices> +</entry> +\endverbatim + +will generate config file entries of "KeepData=Do" and "KeepData=Dont", but the enum +will be declared + +\verbatim + enum { Keep_Do, Keep_Dont }; +\endverbatim + +It is possible to specify your own name for a generated enum, by including a +'name' parameter in \<choices\>. Just like unnamed enums, this enum will be defined in +the global scope of the generated class (without any enclosing class of its own). +Therefore the names of Enum values must be unique across both unnamed enums (if +<b>GlobalEnums</b> is set to true) and all specifically named enums. + +An example of a specifically named enum: + +\verbatim +<entry name="KeepData" type="Enum"> + <choices name="Types"> + <choice name="Do"> + <choice name="Dont"> + </choices> +</entry> +\endverbatim + +which results in the following enum declaration, inside the generated class: + +\verbatim + enum Types { Do, Dont }; +\endverbatim + +It is also possible to specify the use of enums external to the generated class, by +including the string "::" in the enum name - just ensure that it is sufficiently +qualified to be unambiguous in use. To specify use of an unnamed enum, append a +trailing "::". For example, to use the enum 'myEnum' defined in class ClassA, use +either of + +\verbatim +<choices name="ClassA::myEnum"> +<choices name="::ClassA::myEnum"> +\endverbatim + +To specify an unnamed enum in namespace ProgSpace, use + +\verbatim +<choices name="ProgSpace::"> +\endverbatim + +To specify a top-level unnamed enum, use + +\verbatim +<choices name="::"> +\endverbatim + +To specify the top-level enum 'anotherEnum', use + +\verbatim +<choices name="::anotherEnum"> +\endverbatim + + +- Signal support. + +An entry can emit a signal when it gets changed. First of all, you must +define a list of signals for the configuration class. The signal's name may be +any legal identifier you wish. The \<argument\> tag allows you to specify arguments +for the emitted signal. It supports all types as defined in +the KConfigXT DTD. The argument value must specify the name, without spaces, of one +of the entries defined in the .kcfg file. +A signal definition can also contain a \<label\> tag which will be +the documentation line in the generated file. + +\verbatim +<signal name="emoticonSettingsChanged" /> + +<signal name="styleChanged"> + <label>Tell when a complete style change.</label> + <argument type="String">stylePath</argument> + <argument type="String">StyleCSSVariant</argument> +</signal> +\endverbatim + +After defining the signals, you must tell which signal to emit for the entry. +A signal can be emitted by multiple entries. Also, you don't need to specify the arguments +for a signal, the signal name will suffice. + +\verbatim +<entry key="stylePath" type="String"> + <label>Absolute path to a directory containing a Adium/Kopete chat window style.</label> + <emit signal="styleChanged" /> +</entry> +\endverbatim + +You can also use the generic configChanged() signal from KConfigSkeleton to notify your application +about configuration changes. + +If you have questions or comments please contact Cornelius Schumacher +<schumacher@kde.org> or Waldo Bastian <bastian@kde.org> +*/ diff --git a/tier1/kconfig/src/kconfig_compiler/TODO b/tier1/kconfig/src/kconfig_compiler/TODO new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tier1/kconfig/src/kconfig_compiler/TODO diff --git a/tier1/kconfig/src/kconfig_compiler/checkkcfg.pl b/tier1/kconfig/src/kconfig_compiler/checkkcfg.pl new file mode 100755 index 00000000..2eddbeee --- /dev/null +++ b/tier1/kconfig/src/kconfig_compiler/checkkcfg.pl @@ -0,0 +1,83 @@ +#!/usr/bin/perl + +if ( @ARGV != 1 ) { + print STDERR "Missing arg: filename\n"; + exit 1; +} + +$file = $ARGV[0]; + +$file =~ /^(.*)\.[^\.]*$/; +$filebase = $1; + +$file_h = "$filebase.h"; +$file_cpp = "$filebase.cpp"; + +$kcfgc = $file . "c"; + +$cmd = "./kconfig_compiler $file $kcfgc"; + +#print "CMD $cmd\n"; + +if ( system( $cmd ) != 0 ) { + print STDERR "Unable to run kconfig_compiler\n"; + exit 1; +} + +checkfile( $file_h ); +checkfile( $file_cpp ); + +exit 0; + +sub checkfile() +{ + my $file = shift; + + $file =~ /\/([^\/]*)$/; + my $filename = $1; + + print "Checking '$filename':\n"; + + my @ref; + if ( !open( REF, "$file.ref" ) ) { + print STDERR "Unable to open $file.ref\n"; + exit 1; + } + while( <REF> ) { + push @ref, $_; + } + close REF; + + if ( !open( READ, $filename ) ) { + print STDERR "Unable to open $filename\n"; + exit 1; + } + + $error = 0; + $i = 0; + $line = 1; + while( <READ> ) { + $out = $_; + $ref = @ref[$i++]; + + if ( $out ne $ref ) { + $error++; + print " Line $line: Expected : $ref"; + print " Line $line: Compiler output : $out"; + } + + $line++; + } + + close READ; + + if ( $error > 0 ) { + print "\n FAILED: $error errors found.\n"; + if ( $error > 5 ) { + system( "diff -u $file.ref $filename" ); + } + exit 1; + } else { + print " OK\n"; + } +} diff --git a/tier1/kconfig/src/kconfig_compiler/kcfg.xsd b/tier1/kconfig/src/kconfig_compiler/kcfg.xsd new file mode 100644 index 00000000..4926fb19 --- /dev/null +++ b/tier1/kconfig/src/kconfig_compiler/kcfg.xsd @@ -0,0 +1,234 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- kcfg XSD v1.0 --> +<xsd:schema + xmlns:xsd="http://www.w3.org/2001/XMLSchema" + xmlns="http://www.kde.org/standards/kcfg/1.0" + xmlns:kcfg="http://www.kde.org/standards/kcfg/1.0" + targetNamespace="http://www.kde.org/standards/kcfg/1.0" + version="1.0" + elementFormDefault="qualified" > + + <xsd:annotation> + <xsd:documentation> + + Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> + Copyright (c) 2003 Waldo Bastian <bastian@kde.org> + Copyright (c) 2003 Zack Rusin <zack@kde.org> + Copyright (c) 2004 Frans Englich <frans.englich@telia.com> + Copyright (c) 2006 Michaël Larouche <michael.larouche@kdemail.net> + + Permission to use, copy, modify and distribute this DTD + and its accompanying documentation for any purpose and without fee + is hereby granted in perpetuity, provided that the above copyright + notice and this paragraph appear in all copies. The copyright + holders make no representation about the suitability of the DTD for + any purpose. It is provided "as is" without expressed or implied + warranty. + + </xsd:documentation> + </xsd:annotation> + <xsd:annotation> + <xsd:documentation> + + A Schema for KDE's KConfigXT XML format. It is similar to the DTD + found at: + + http://www.kde.org/standards/kcfg/1.0/kcfg.dtd + + Documents valid against the Schema version are backwards compatible + to the DTD. Validating against the Schema instead of the DTD is + recommended, since the former provides better validation. + + A document instance of this Schema should have a declaration + looking like this: + + <![CDATA[ + + <?xml version="1.0" encoding="UTF-8" ?> + <kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 + http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" > + <!-- the content --> + </kcfg> + + ]]> + + </xsd:documentation> + </xsd:annotation> + + <xsd:element name="kcfg"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="include" minOccurs="0" maxOccurs="unbounded" type="xsd:string"/> + <xsd:element name="kcfgfile" > + <xsd:complexType> + <xsd:sequence> + <xsd:element name="parameter" type="kcfg:parameter" minOccurs="0" maxOccurs="unbounded" /> + <!-- FIXME: Are really unbounded occurances of parameter allowed? --> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="optional"/> + <xsd:attribute name="arg" type="xsd:boolean" use="optional"/> + </xsd:complexType> + </xsd:element> + <xsd:element name="signal" type="kcfg:signal" minOccurs="0" maxOccurs="unbounded" /> + <xsd:element name="group" maxOccurs="unbounded" > + <xsd:complexType> + <xsd:sequence> + <xsd:element name="entry" maxOccurs="unbounded"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="parameter" minOccurs="0" type="kcfg:parameter"/> + <xsd:element name="label" minOccurs="0" type="kcfg:translatableString"/> + <xsd:element name="whatsthis" minOccurs="0" type="kcfg:translatableString"/> + <xsd:element name="tooltip" minOccurs="0" type="kcfg:translatableString"/> + <xsd:element name="choices" minOccurs="0"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="choice" maxOccurs="unbounded"> + <xsd:complexType> + <xsd:all> + <xsd:element minOccurs="0" name="label" type="kcfg:translatableString"/> + <xsd:element minOccurs="0" name="whatsthis" type="kcfg:translatableString"/> + <xsd:element minOccurs="0" name="tooltip" type="kcfg:translatableString"/> + </xsd:all> + <xsd:attribute name="name" use="required" type="xsd:string"/> + </xsd:complexType> + </xsd:element> + </xsd:sequence> + <xsd:attribute name="name" use="optional" type="xsd:string"/> + <xsd:attribute name="prefix" use="optional" type="xsd:string"/> + + </xsd:complexType> + </xsd:element> + + <xsd:element name="code" minOccurs="0" type="kcfg:code"/> + + <xsd:element name="default" maxOccurs="unbounded" minOccurs="0" > + <xsd:complexType> + <xsd:simpleContent> + <xsd:extension base="xsd:string"> + <xsd:attribute use="optional" name="code" type="xsd:boolean"/> + <xsd:attribute use="optional" name="param" type="xsd:string"/> + </xsd:extension> + </xsd:simpleContent> + </xsd:complexType> + </xsd:element> + + <xsd:element name="min" minOccurs="0" > + <xsd:complexType> + <xsd:simpleContent> + <xsd:extension base="xsd:string"> + <xsd:attribute name="code" type="xsd:boolean"/> + </xsd:extension> + </xsd:simpleContent> + </xsd:complexType> + </xsd:element> + + <xsd:element name="max" minOccurs="0"> + <xsd:complexType> + <xsd:simpleContent> + <xsd:extension base="xsd:string"> + <xsd:attribute name="code" type="xsd:boolean"/> + </xsd:extension> + </xsd:simpleContent> + </xsd:complexType> + </xsd:element> + + <xsd:element name="emit" minOccurs="0"> + <xsd:complexType> + <xsd:simpleContent> + <xsd:extension base="xsd:string"> + <xsd:attribute name="signal" use="required" type="xsd:string"/> + </xsd:extension> + </xsd:simpleContent> + </xsd:complexType> + </xsd:element> + + </xsd:choice> + <xsd:attribute name="name" use="optional" type="xsd:string"/> + <xsd:attribute name="key" use="optional" type="xsd:string"/> + <xsd:attribute name="hidden" use="optional" type="xsd:boolean"/> + <xsd:attribute name="type" type="kcfg:datatype"/> + </xsd:complexType> + </xsd:element> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string"/> + </xsd:complexType> + </xsd:element> + </xsd:sequence> + </xsd:complexType> + </xsd:element> + + <xsd:simpleType name="datatype"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="String"/> + <xsd:enumeration value="StringList"/> + <xsd:enumeration value="Font"/> + <xsd:enumeration value="Rect"/> + <xsd:enumeration value="Size"/> + <xsd:enumeration value="Color"/> + <xsd:enumeration value="Point"/> + <xsd:enumeration value="Int"/> + <xsd:enumeration value="UInt"/> + <xsd:enumeration value="Bool"/> + <xsd:enumeration value="Double"/> + <xsd:enumeration value="DateTime"/> + <xsd:enumeration value="LongLong"/> + <xsd:enumeration value="ULongLong"/> + <xsd:enumeration value="IntList"/> + <xsd:enumeration value="Enum"/> + <xsd:enumeration value="Path"/> + <xsd:enumeration value="PathList"/> + <xsd:enumeration value="Password"/> + <xsd:enumeration value="Url"/> + <xsd:enumeration value="UrlList"/> + </xsd:restriction> + </xsd:simpleType> + + <xsd:complexType name="parameter"> + <xsd:sequence> + <xsd:element minOccurs="0" name="values"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" maxOccurs="unbounded" type="xsd:string"/> + </xsd:sequence> + </xsd:complexType> + </xsd:element> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string"/> + <xsd:attribute name="type" use="optional" type="kcfg:datatype" /> + <xsd:attribute name="max" use="optional" type="xsd:positiveInteger"/> + </xsd:complexType> + + <xsd:complexType name="code"> + <xsd:simpleContent> + <xsd:extension base="xsd:string"/> + </xsd:simpleContent> + </xsd:complexType> + + <xsd:complexType name="signal"> + <xsd:sequence> + <xsd:element name="label" minOccurs="0" type="xsd:string"/> + <xsd:element name="argument" maxOccurs="unbounded" minOccurs="0" > + <xsd:complexType> + <xsd:simpleContent> + <xsd:extension base="xsd:string"> + <xsd:attribute use="required" name="type" type="kcfg:datatype"/> + </xsd:extension> + </xsd:simpleContent> + </xsd:complexType> + </xsd:element> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + </xsd:complexType> + + <xsd:complexType name="translatableString"> + <xsd:simpleContent> + <xsd:extension base="xsd:string"> + <xsd:attribute use="optional" name="context" type="xsd:string"/> + </xsd:extension> + </xsd:simpleContent> + </xsd:complexType> +</xsd:schema> + diff --git a/tier1/kconfig/src/kconfig_compiler/kconfig_compiler.cpp b/tier1/kconfig/src/kconfig_compiler/kconfig_compiler.cpp new file mode 100644 index 00000000..ae192eec --- /dev/null +++ b/tier1/kconfig/src/kconfig_compiler/kconfig_compiler.cpp @@ -0,0 +1,2338 @@ +// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +/* + This file is part of KDE. + + Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> + Copyright (c) 2003 Waldo Bastian <bastian@kde.org> + Copyright (c) 2003 Zack Rusin <zack@kde.org> + Copyright (c) 2006 Michaël Larouche <michael.larouche@kdemail.net> + Copyright (c) 2008 Allen Winter <winter@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +// Compiling this file with this flag is just crazy +#undef QT_NO_CAST_FROM_ASCII + +#include <QtCore/QCoreApplication> +#include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtCore/QSettings> +#include <QtCore/QTextStream> +#include <QtXml/QDomAttr> +#include <QtCore/QRegExp> +#include <QtCore/QStringList> + +#include <ostream> +#include <iostream> +#include <stdlib.h> + +namespace +{ + QTextStream cout(stdout); + QTextStream cerr(stderr); +} + +static void parseArgs(const QStringList &args, QString &directory, QString &file1, QString &file2) +{ + int fileCount = 0; + directory = QChar::fromLatin1('.'); + + for (int i = 1; i < args.count(); ++i) { + if (args.at(i) == QLatin1String("-d") || args.at(i) == QLatin1String("--directory")) { + if (i + 1 > args.count()) { + cerr << args.at(i) << " needs an argument" << endl; + exit(1); + } + directory = args.at(++i); + } else if (args.at(i).startsWith(QLatin1String("-d"))) { + directory = args.at(i).mid(2); + } else if (args.at(i) == QLatin1String("--help") || args.at(i) == QLatin1String("-h")) { + cout << "Options:" << endl; + cout << " -L --license Display software license" << endl; + cout << " -d, --directory <dir> Directory to generate files in [.]" << endl; + cout << " -h, --help Display this help" << endl; + cout << endl; + cout << "Arguments:" << endl; + cout << " file.kcfg Input kcfg XML file" << endl; + cout << " file.kcfgc Code generation options file" << endl; + exit(0); + } else if (args.at(i) == QLatin1String("--license") || args.at(i) == QLatin1String("-L")) { + cout << "Copyright 2003 Cornelius Schumacher, Waldo Bastian, Zack Rusin," << endl; + cout << " Reinhold Kainhofer, Duncan Mac-Vicar P., Harald Fernengel" << endl; + cout << "This program comes with ABSOLUTELY NO WARRANTY." << endl; + cout << "You may redistribute copies of this program" << endl; + cout << "under the terms of the GNU Library Public License." << endl; + cout << "For more information about these matters, see the file named COPYING." << endl; + exit(0); + } else if (args.at(i).startsWith(QLatin1Char('-'))) { + cerr << "Unknown option: " << args.at(i) << endl; + exit(1); + } else if (fileCount == 0) { + file1 = args.at(i); + ++fileCount; + } else if (fileCount == 1) { + file2 = args.at(i); + ++fileCount; + } else { + cerr << "Too many arguments" << endl; + exit(1); + } + } + if (fileCount < 2) { + cerr << "Too few arguments" << endl; + exit(1); + } +} + +QStringList allNames; +QRegExp *validNameRegexp; +QString This; +QString Const; + +/** + Configuration Compiler Configuration +*/ +class CfgConfig +{ +public: + CfgConfig( const QString &codegenFilename ) + { + // Configure the compiler with some settings + QSettings codegenConfig(codegenFilename, QSettings::IniFormat); + + nameSpace = codegenConfig.value("NameSpace").toString(); + className = codegenConfig.value("ClassName").toString(); + if ( className.isEmpty() ) { + cerr << "Class name missing" << endl; + exit(1); + } + inherits = codegenConfig.value("Inherits").toString(); + if ( inherits.isEmpty() ) inherits = "KConfigSkeleton"; + visibility = codegenConfig.value("Visibility").toString(); + if ( !visibility.isEmpty() ) visibility += ' '; + forceStringFilename = codegenConfig.value("ForceStringFilename", false).toBool(); + singleton = codegenConfig.value("Singleton", false).toBool(); + staticAccessors = singleton; + customAddons = codegenConfig.value("CustomAdditions", false).toBool(); + memberVariables = codegenConfig.value("MemberVariables").toString(); + dpointer = (memberVariables == "dpointer"); + headerIncludes = codegenConfig.value("IncludeFiles", QStringList()).toStringList(); + sourceIncludes = codegenConfig.value("SourceIncludeFiles", QStringList()).toStringList(); + mutators = codegenConfig.value("Mutators", QStringList()).toStringList(); + allMutators = ((mutators.count() == 1) && (mutators.at(0).toLower() == "true")); + itemAccessors = codegenConfig.value("ItemAccessors", false).toBool(); + setUserTexts = codegenConfig.value("SetUserTexts", false).toBool(); + defaultGetters = codegenConfig.value("DefaultValueGetters", QStringList()).toStringList(); + allDefaultGetters = (defaultGetters.count() == 1) && (defaultGetters.at(0).toLower() == "true"); + globalEnums = codegenConfig.value("GlobalEnums", false).toBool(); + useEnumTypes = codegenConfig.value("UseEnumTypes", false).toBool(); + + const QString trString = codegenConfig.value("TranslationSystem").toString().toLower(); + if ( trString == "kde" ) { + translationSystem = KdeTranslation; + } else { + if ( !trString.isEmpty() && trString != "qt" ) { + cerr << "Unknown translation system, falling back to Qt tr()" << endl; + } + translationSystem = QtTranslation; + } + } + +public: + enum TranslationSystem { + QtTranslation, + KdeTranslation + }; + + // These are read from the .kcfgc configuration file + QString nameSpace; // The namespace for the class to be generated + QString className; // The class name to be generated + QString inherits; // The class the generated class inherits (if empty, from KConfigSkeleton) + QString visibility; + bool forceStringFilename; + bool singleton; // The class will be a singleton + bool staticAccessors; // provide or not static accessors + bool customAddons; + QString memberVariables; + QStringList headerIncludes; + QStringList sourceIncludes; + QStringList mutators; + QStringList defaultGetters; + bool allMutators; + bool setUserTexts; + bool allDefaultGetters; + bool dpointer; + bool globalEnums; + bool useEnumTypes; + bool itemAccessors; + TranslationSystem translationSystem; +}; + + +struct SignalArguments +{ + QString type; + QString variableName; +}; + +class Signal { +public: + QString name; + QString label; + QList<SignalArguments> arguments; +}; + + + + +class CfgEntry +{ + public: + struct Choice + { + QString name; + QString context; + QString label; + QString toolTip; + QString whatsThis; + }; + class Choices + { + public: + Choices() {} + Choices( const QList<Choice> &d, const QString &n, const QString &p ) + : prefix(p), choices(d), mName(n) + { + int i = n.indexOf(QLatin1String("::")); + if (i >= 0) + mExternalQual = n.left(i + 2); + } + QString prefix; + QList<Choice> choices; + const QString& name() const { return mName; } + const QString& externalQualifier() const { return mExternalQual; } + bool external() const { return !mExternalQual.isEmpty(); } + private: + QString mName; + QString mExternalQual; + }; + + CfgEntry( const QString &group, const QString &type, const QString &key, + const QString &name, const QString &labelContext, const QString &label, + const QString &toolTipContext, const QString &toolTip, const QString &whatsThisContext, const QString &whatsThis, const QString &code, + const QString &defaultValue, const Choices &choices, const QList<Signal> signalList, + bool hidden ) + : mGroup( group ), mType( type ), mKey( key ), mName( name ), + mLabelContext( labelContext ), mLabel( label ), mToolTipContext( toolTipContext ), mToolTip( toolTip ), + mWhatsThisContext( whatsThisContext ), mWhatsThis( whatsThis ), + mCode( code ), mDefaultValue( defaultValue ), mChoices( choices ), + mSignalList(signalList), mHidden( hidden ) + { + } + + void setGroup( const QString &group ) { mGroup = group; } + QString group() const { return mGroup; } + + void setType( const QString &type ) { mType = type; } + QString type() const { return mType; } + + void setKey( const QString &key ) { mKey = key; } + QString key() const { return mKey; } + + void setName( const QString &name ) { mName = name; } + QString name() const { return mName; } + + void setLabelContext( const QString &labelContext ) { mLabelContext = labelContext; } + QString labelContext() const { return mLabelContext; } + + void setLabel( const QString &label ) { mLabel = label; } + QString label() const { return mLabel; } + + void setToolTipContext( const QString &toolTipContext ) { mToolTipContext = toolTipContext; } + QString toolTipContext() const { return mToolTipContext; } + + void setToolTip( const QString &toolTip ) { mToolTip = toolTip; } + QString toolTip() const { return mToolTip; } + + void setWhatsThisContext( const QString &whatsThisContext ) { mWhatsThisContext = whatsThisContext; } + QString whatsThisContext() const { return mWhatsThisContext; } + + void setWhatsThis( const QString &whatsThis ) { mWhatsThis = whatsThis; } + QString whatsThis() const { return mWhatsThis; } + + void setDefaultValue( const QString &d ) { mDefaultValue = d; } + QString defaultValue() const { return mDefaultValue; } + + void setCode( const QString &d ) { mCode = d; } + QString code() const { return mCode; } + + void setMinValue( const QString &d ) { mMin = d; } + QString minValue() const { return mMin; } + + void setMaxValue( const QString &d ) { mMax = d; } + QString maxValue() const { return mMax; } + + void setParam( const QString &d ) { mParam = d; } + QString param() const { return mParam; } + + void setParamName( const QString &d ) { mParamName = d; } + QString paramName() const { return mParamName; } + + void setParamType( const QString &d ) { mParamType = d; } + QString paramType() const { return mParamType; } + + void setChoices( const QList<Choice> &d, const QString &n, const QString &p ) { mChoices = Choices( d, n, p ); } + Choices choices() const { return mChoices; } + + void setParamValues( const QStringList &d ) { mParamValues = d; } + QStringList paramValues() const { return mParamValues; } + + void setParamDefaultValues( const QStringList &d ) { mParamDefaultValues = d; } + QString paramDefaultValue(int i) const { return mParamDefaultValues[i]; } + + void setParamMax( int d ) { mParamMax = d; } + int paramMax() const { return mParamMax; } + + void setSignalList( const QList<Signal> &value ) { mSignalList = value; } + QList<Signal> signalList() const { return mSignalList; } + + bool hidden() const { return mHidden; } + + void dump() const + { + cerr << "<entry>" << endl; + cerr << " group: " << mGroup << endl; + cerr << " type: " << mType << endl; + cerr << " key: " << mKey << endl; + cerr << " name: " << mName << endl; + cerr << " label context: " << mLabelContext << endl; + cerr << " label: " << mLabel << endl; +// whatsthis + cerr << " code: " << mCode << endl; +// cerr << " values: " << mValues.join(":") << endl; + + if (!param().isEmpty()) + { + cerr << " param name: "<< mParamName << endl; + cerr << " param type: "<< mParamType << endl; + cerr << " paramvalues: " << mParamValues.join(QChar::fromLatin1(':')) << endl; + } + cerr << " default: " << mDefaultValue << endl; + cerr << " hidden: " << mHidden << endl; + cerr << " min: " << mMin << endl; + cerr << " max: " << mMax << endl; + cerr << "</entry>" << endl; + } + + private: + QString mGroup; + QString mType; + QString mKey; + QString mName; + QString mLabelContext; + QString mLabel; + QString mToolTipContext; + QString mToolTip; + QString mWhatsThisContext; + QString mWhatsThis; + QString mCode; + QString mDefaultValue; + QString mParam; + QString mParamName; + QString mParamType; + Choices mChoices; + QList<Signal> mSignalList; + QStringList mParamValues; + QStringList mParamDefaultValues; + int mParamMax; + bool mHidden; + QString mMin; + QString mMax; +}; + +class Param { +public: + QString name; + QString type; +}; + +// returns the name of an member variable +// use itemPath to know the full path +// like using d-> in case of dpointer +static QString varName(const QString &n, const CfgConfig &cfg) +{ + QString result; + if ( !cfg.dpointer ) { + result = QChar::fromLatin1('m') + n; + result[1] = result[1].toUpper(); + } + else { + result = n; + result[0] = result[0].toLower(); + } + return result; +} + +static QString varPath(const QString &n, const CfgConfig &cfg) +{ + QString result; + if ( cfg.dpointer ) { + result = "d->"+varName(n, cfg); + } + else { + result = varName(n, cfg); + } + return result; +} + +static QString enumName(const QString &n) +{ + QString result = QString::fromLatin1("Enum") + n; + result[4] = result[4].toUpper(); + return result; +} + +static QString enumName(const QString &n, const CfgEntry::Choices &c) +{ + QString result = c.name(); + if ( result.isEmpty() ) + { + result = QString::fromLatin1("Enum") + n; + result[4] = result[4].toUpper(); + } + return result; +} + +static QString enumType(const CfgEntry *e, bool globalEnums) +{ + QString result = e->choices().name(); + if ( result.isEmpty() ) + { + result = QString::fromLatin1("Enum") + e->name(); + if( !globalEnums ) + result += QString::fromLatin1("::type"); + result[4] = result[4].toUpper(); + } + return result; +} + +static QString enumTypeQualifier(const QString &n, const CfgEntry::Choices &c) +{ + QString result = c.name(); + if ( result.isEmpty() ) + { + result = QString::fromLatin1("Enum") + n + QString::fromLatin1("::"); + result[4] = result[4].toUpper(); + } + else if ( c.external() ) + result = c.externalQualifier(); + else + result.clear(); + return result; +} + +static QString setFunction(const QString &n, const QString &className = QString()) +{ + QString result = QString::fromLatin1("set") + n; + result[3] = result[3].toUpper(); + + if ( !className.isEmpty() ) + result = className + QString::fromLatin1("::") + result; + return result; +} + +static QString getDefaultFunction(const QString &n, const QString &className = QString()) +{ + QString result = QString::fromLatin1("default") + n + QString::fromLatin1("Value"); + result[7] = result[7].toUpper(); + + if ( !className.isEmpty() ) + result = className + QString::fromLatin1("::") + result; + return result; +} + +static QString getFunction(const QString &n, const QString &className = QString()) +{ + QString result = n; + result[0] = result[0].toLower(); + + if ( !className.isEmpty() ) + result = className + QString::fromLatin1("::") + result; + return result; +} + + +static void addQuotes( QString &s ) +{ + if ( !s.startsWith( QLatin1Char('"') ) ) + s.prepend( QLatin1Char('"') ); + if ( !s.endsWith( QLatin1Char('"') ) ) + s.append( QLatin1Char('"') ); +} + +static QString quoteString( const QString &s ) +{ + QString r = s; + r.replace( QLatin1Char('\\'), QLatin1String("\\\\") ); + r.replace( QLatin1Char('\"'), QLatin1String("\\\"") ); + r.remove( QLatin1Char('\r') ); + r.replace( QLatin1Char('\n'), QLatin1String("\\n\"\n\"") ); + return QLatin1Char('\"') + r + QLatin1Char('\"'); +} + +static QString literalString( const QString &s ) +{ + bool isAscii = true; + for(int i = s.length(); i--;) + if (s[i].unicode() > 127) isAscii = false; + + if (isAscii) + return QString::fromLatin1("QLatin1String( ") + quoteString(s) + QString::fromLatin1(" )"); + else + return QString::fromLatin1("QString::fromUtf8( ") + quoteString(s) + QString::fromLatin1(" )"); +} + +static QString dumpNode(const QDomNode &node) +{ + QString msg; + QTextStream s(&msg, QIODevice::WriteOnly ); + node.save(s, 0); + + msg = msg.simplified(); + if (msg.length() > 40) + return msg.left(37) + QString::fromLatin1("..."); + return msg; +} + +static QString filenameOnly(const QString& path) +{ + int i = path.lastIndexOf(QRegExp(QLatin1String("[/\\]"))); + if (i >= 0) + return path.mid(i+1); + return path; +} + +static QString signalEnumName(const QString &signalName) +{ + QString result; + result = QString::fromLatin1("signal") + signalName; + result[6] = result[6].toUpper(); + + return result; +} + +static void preProcessDefault( QString &defaultValue, const QString &name, + const QString &type, + const CfgEntry::Choices &choices, + QString &code, const CfgConfig &cfg ) +{ + if ( type == QLatin1String("String") && !defaultValue.isEmpty() ) { + defaultValue = literalString(defaultValue); + + } else if ( type == QLatin1String("Path") && !defaultValue.isEmpty() ) { + defaultValue = literalString( defaultValue ); + } else if ( type == QLatin1String("Url") && !defaultValue.isEmpty() ) { + // Use fromUserInput in order to support absolute paths and absolute urls, like KDE4's KUrl(QString) did. + defaultValue = QString::fromLatin1("QUrl::fromUserInput( ") + literalString(defaultValue) + QLatin1Char(')'); + } else if ( ( type == QLatin1String("UrlList") || type == QLatin1String("StringList") || type == QLatin1String("PathList")) && !defaultValue.isEmpty() ) { + QTextStream cpp( &code, QIODevice::WriteOnly | QIODevice::Append ); + if (!code.isEmpty()) + cpp << endl; + + if( type == "UrlList" ) { + cpp << " QList<QUrl> default" << name << ";" << endl; + } else { + cpp << " QStringList default" << name << ";" << endl; + } + const QStringList defaults = defaultValue.split(QLatin1Char(',')); + QStringList::ConstIterator it; + for( it = defaults.constBegin(); it != defaults.constEnd(); ++it ) { + cpp << " default" << name << ".append( "; + if( type == QLatin1String("UrlList") ) { + cpp << "QUrl::fromUserInput("; + } + cpp << "QString::fromUtf8( \"" << *it << "\" ) "; + if( type == QLatin1String("UrlList") ) { + cpp << ") "; + } + cpp << ");" << endl; + } + defaultValue = QString::fromLatin1("default") + name; + + } else if ( type == QLatin1String("Color") && !defaultValue.isEmpty() ) { + QRegExp colorRe(QLatin1String("\\d+,\\s*\\d+,\\s*\\d+(,\\s*\\d+)?")); + if (colorRe.exactMatch(defaultValue)) + { + defaultValue = QLatin1String("QColor( ") + defaultValue + QLatin1String(" )"); + } + else + { + defaultValue = QLatin1String("QColor( \"") + defaultValue + QLatin1String("\" )"); + } + + } else if ( type == QLatin1String("Enum") ) { + QList<CfgEntry::Choice>::ConstIterator it; + for( it = choices.choices.constBegin(); it != choices.choices.constEnd(); ++it ) { + if ( (*it).name == defaultValue ) { + if ( cfg.globalEnums && choices.name().isEmpty() ) + defaultValue.prepend( choices.prefix ); + else + defaultValue.prepend( enumTypeQualifier(name, choices) + choices.prefix ); + break; + } + } + + } else if ( type == QLatin1String("IntList") ) { + QTextStream cpp( &code, QIODevice::WriteOnly | QIODevice::Append ); + if (!code.isEmpty()) + cpp << endl; + + cpp << " QList<int> default" << name << ";" << endl; + if (!defaultValue.isEmpty()) + { + const QStringList defaults = defaultValue.split( QLatin1Char(',') ); + QStringList::ConstIterator it; + for( it = defaults.constBegin(); it != defaults.constEnd(); ++it ) { + cpp << " default" << name << ".append( " << *it << " );" + << endl; + } + } + defaultValue = QString::fromLatin1("default") + name; + } +} + + +CfgEntry *parseEntry( const QString &group, const QDomElement &element, const CfgConfig &cfg ) +{ + bool defaultCode = false; + QString type = element.attribute( "type" ); + QString name = element.attribute( "name" ); + QString key = element.attribute( "key" ); + QString hidden = element.attribute( "hidden" ); + QString labelContext; + QString label; + QString toolTipContext; + QString toolTip; + QString whatsThisContext; + QString whatsThis; + QString defaultValue; + QString code; + QString param; + QString paramName; + QString paramType; + CfgEntry::Choices choices; + QList<Signal> signalList; + QStringList paramValues; + QStringList paramDefaultValues; + QString minValue; + QString maxValue; + int paramMax = 0; + + for ( QDomElement e = element.firstChildElement(); !e.isNull(); e = e.nextSiblingElement() ) { + QString tag = e.tagName(); + if ( tag == "label" ) { + label = e.text(); + labelContext = e.attribute( "context" ); + } + else if ( tag == "tooltip" ) { + toolTip = e.text(); + toolTipContext = e.attribute( "context" ); + } + else if ( tag == "whatsthis" ) { + whatsThis = e.text(); + whatsThisContext = e.attribute( "context" ); + } + else if ( tag == "min" ) minValue = e.text(); + else if ( tag == "max" ) maxValue = e.text(); + else if ( tag == "code" ) code = e.text(); + else if ( tag == "parameter" ) + { + param = e.attribute( "name" ); + paramType = e.attribute( "type" ); + if ( param.isEmpty() ) { + cerr << "Parameter must have a name: " << dumpNode(e) << endl; + return 0; + } + if ( paramType.isEmpty() ) { + cerr << "Parameter must have a type: " << dumpNode(e) << endl; + return 0; + } + if ((paramType == "Int") || (paramType == "UInt")) + { + bool ok; + paramMax = e.attribute("max").toInt(&ok); + if (!ok) + { + cerr << "Integer parameter must have a maximum (e.g. max=\"0\"): " + << dumpNode(e) << endl; + return 0; + } + } + else if (paramType == "Enum") + { + for ( QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement() ) { + if (e2.tagName() == "values") + { + for ( QDomElement e3 = e2.firstChildElement(); !e3.isNull(); e3 = e3.nextSiblingElement() ) { + if (e3.tagName() == "value") + { + paramValues.append( e3.text() ); + } + } + break; + } + } + if (paramValues.isEmpty()) + { + cerr << "No values specified for parameter '" << param + << "'." << endl; + return 0; + } + paramMax = paramValues.count()-1; + } + else + { + cerr << "Parameter '" << param << "' has type " << paramType + << " but must be of type int, uint or Enum." << endl; + return 0; + } + } + else if ( tag == "default" ) + { + if (e.attribute("param").isEmpty()) + { + defaultValue = e.text(); + if (e.attribute( "code" ) == "true") + defaultCode = true; + } + } + else if ( tag == "choices" ) { + QString name = e.attribute( "name" ); + QString prefix = e.attribute( "prefix" ); + QList<CfgEntry::Choice> chlist; + for( QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement() ) { + if ( e2.tagName() == "choice" ) { + CfgEntry::Choice choice; + choice.name = e2.attribute( "name" ); + if ( choice.name.isEmpty() ) { + cerr << "Tag <choice> requires attribute 'name'." << endl; + } + for( QDomElement e3 = e2.firstChildElement(); !e3.isNull(); e3 = e3.nextSiblingElement() ) { + if ( e3.tagName() == "label" ) { + choice.label = e3.text(); + choice.context = e3.attribute( "context" ); + } + if ( e3.tagName() == "tooltip" ) { + choice.toolTip = e3.text(); + choice.context = e3.attribute( "context" ); + } + if ( e3.tagName() == "whatsthis" ) { + choice.whatsThis = e3.text(); + choice.context = e3.attribute( "context" ); + } + } + chlist.append( choice ); + } + } + choices = CfgEntry::Choices( chlist, name, prefix ); + } + else if ( tag == "emit" ) { + QDomNode signalNode; + Signal signal; + signal.name = e.attribute( "signal" ); + signalList.append( signal); + } + } + + + bool nameIsEmpty = name.isEmpty(); + if ( nameIsEmpty && key.isEmpty() ) { + cerr << "Entry must have a name or a key: " << dumpNode(element) << endl; + return 0; + } + + if ( key.isEmpty() ) { + key = name; + } + + if ( nameIsEmpty ) { + name = key; + name.remove( ' ' ); + } else if ( name.contains( ' ' ) ) { + cout<<"Entry '"<<name<<"' contains spaces! <name> elements can not contain spaces!"<<endl; + name.remove( ' ' ); + } + + if (name.contains("$(")) + { + if (param.isEmpty()) + { + cerr << "Name may not be parameterized: " << name << endl; + return 0; + } + } + else + { + if (!param.isEmpty()) + { + cerr << "Name must contain '$(" << param << ")': " << name << endl; + return 0; + } + } + + if ( label.isEmpty() ) { + label = key; + } + + if ( type.isEmpty() ) type = "String"; // XXX : implicit type might be bad + + if (!param.isEmpty()) + { + // Adjust name + paramName = name; + name.remove("$("+param+')'); + // Lookup defaults for indexed entries + for(int i = 0; i <= paramMax; i++) + { + paramDefaultValues.append(QString()); + } + + for ( QDomElement e = element.firstChildElement(); !e.isNull(); e = e.nextSiblingElement() ) { + QString tag = e.tagName(); + if ( tag == "default" ) + { + QString index = e.attribute("param"); + if (index.isEmpty()) + continue; + + bool ok; + int i = index.toInt(&ok); + if (!ok) + { + i = paramValues.indexOf(index); + if (i == -1) + { + cerr << "Index '" << index << "' for default value is unknown." << endl; + return 0; + } + } + + if ((i < 0) || (i > paramMax)) + { + cerr << "Index '" << i << "' for default value is out of range [0, "<< paramMax<<"]." << endl; + return 0; + } + + QString tmpDefaultValue = e.text(); + + if (e.attribute( "code" ) != "true") + preProcessDefault(tmpDefaultValue, name, type, choices, code, cfg); + + paramDefaultValues[i] = tmpDefaultValue; + } + } + } + + if (!validNameRegexp->exactMatch(name)) + { + if (nameIsEmpty) + cerr << "The key '" << key << "' can not be used as name for the entry because " + "it is not a valid name. You need to specify a valid name for this entry." << endl; + else + cerr << "The name '" << name << "' is not a valid name for an entry." << endl; + return 0; + } + + if (allNames.contains(name)) + { + if (nameIsEmpty) + cerr << "The key '" << key << "' can not be used as name for the entry because " + "it does not result in a unique name. You need to specify a unique name for this entry." << endl; + else + cerr << "The name '" << name << "' is not unique." << endl; + return 0; + } + allNames.append(name); + + if (!defaultCode) + { + preProcessDefault(defaultValue, name, type, choices, code, cfg); + } + + CfgEntry *result = new CfgEntry( group, type, key, name, labelContext, label, toolTipContext, toolTip, whatsThisContext, whatsThis, + code, defaultValue, choices, signalList, + hidden == "true" ); + if (!param.isEmpty()) + { + result->setParam(param); + result->setParamName(paramName); + result->setParamType(paramType); + result->setParamValues(paramValues); + result->setParamDefaultValues(paramDefaultValues); + result->setParamMax(paramMax); + } + result->setMinValue(minValue); + result->setMaxValue(maxValue); + + return result; +} + +static bool isUnsigned(const QString& type) +{ + if ( type == "UInt" ) return true; + if ( type == "ULongLong" ) return true; + return false; +} + +/** + Return parameter declaration for given type. +*/ +QString param( const QString &t ) +{ + const QString type = t.toLower(); + if ( type == "string" ) return "const QString &"; + else if ( type == "stringlist" ) return "const QStringList &"; + else if ( type == "font" ) return "const QFont &"; + else if ( type == "rect" ) return "const QRect &"; + else if ( type == "size" ) return "const QSize &"; + else if ( type == "color" ) return "const QColor &"; + else if ( type == "point" ) return "const QPoint &"; + else if ( type == "int" ) return "int"; + else if ( type == "uint" ) return "uint"; + else if ( type == "bool" ) return "bool"; + else if ( type == "double" ) return "double"; + else if ( type == "datetime" ) return "const QDateTime &"; + else if ( type == "longlong" ) return "qint64"; + else if ( type == "ulonglong" ) return "quint64"; + else if ( type == "intlist" ) return "const QList<int> &"; + else if ( type == "enum" ) return "int"; + else if ( type == "path" ) return "const QString &"; + else if ( type == "pathlist" ) return "const QStringList &"; + else if ( type == "password" ) return "const QString &"; + else if ( type == "url" ) return "const QUrl &"; + else if ( type == "urllist" ) return "const QList<QUrl> &"; + else { + cerr <<"kconfig_compiler does not support type \""<< type <<"\""<<endl; + return "QString"; //For now, but an assert would be better + } +} + +/** + Actual C++ storage type for given type. +*/ +QString cppType( const QString &t ) +{ + const QString type = t.toLower(); + if ( type == "string" ) return "QString"; + else if ( type == "stringlist" ) return "QStringList"; + else if ( type == "font" ) return "QFont"; + else if ( type == "rect" ) return "QRect"; + else if ( type == "size" ) return "QSize"; + else if ( type == "color" ) return "QColor"; + else if ( type == "point" ) return "QPoint"; + else if ( type == "int" ) return "int"; + else if ( type == "uint" ) return "uint"; + else if ( type == "bool" ) return "bool"; + else if ( type == "double" ) return "double"; + else if ( type == "datetime" ) return "QDateTime"; + else if ( type == "longlong" ) return "qint64"; + else if ( type == "ulonglong" ) return "quint64"; + else if ( type == "intlist" ) return "QList<int>"; + else if ( type == "enum" ) return "int"; + else if ( type == "path" ) return "QString"; + else if ( type == "pathlist" ) return "QStringList"; + else if ( type == "password" ) return "QString"; + else if ( type == "url" ) return "QUrl"; + else if ( type == "urllist" ) return "QList<QUrl>"; + else { + cerr<<"kconfig_compiler does not support type \""<< type <<"\""<<endl; + return "QString"; //For now, but an assert would be better + } +} + +QString defaultValue( const QString &t ) +{ + const QString type = t.toLower(); + if ( type == "string" ) return "\"\""; // Use empty string, not null string! + else if ( type == "stringlist" ) return "QStringList()"; + else if ( type == "font" ) return "QFont()"; + else if ( type == "rect" ) return "QRect()"; + else if ( type == "size" ) return "QSize()"; + else if ( type == "color" ) return "QColor(128, 128, 128)"; + else if ( type == "point" ) return "QPoint()"; + else if ( type == "int" ) return "0"; + else if ( type == "uint" ) return "0"; + else if ( type == "bool" ) return "false"; + else if ( type == "double" ) return "0.0"; + else if ( type == "datedime" ) return "QDateTime()"; + else if ( type == "longlong" ) return "0"; + else if ( type == "ulonglong" ) return "0"; + else if ( type == "intlist" ) return "QList<int>()"; + else if ( type == "enum" ) return "0"; + else if ( type == "path" ) return "\"\""; // Use empty string, not null string! + else if ( type == "pathlist" ) return "QStringList()"; + else if ( type == "password" ) return "\"\""; // Use empty string, not null string! + else if ( type == "url" ) return "QUrl()"; + else if ( type == "urllist" ) return "QList<QUrl>()"; + else { + cerr<<"Error, kconfig_compiler does not support the \""<< type <<"\" type!"<<endl; + return "QString"; //For now, but an assert would be better + } +} + +QString itemType( const QString &type ) +{ + QString t; + + t = type; + t.replace( 0, 1, t.left( 1 ).toUpper() ); + + return t; +} + +static QString itemDeclaration(const CfgEntry *e, const CfgConfig &cfg) +{ + if (cfg.itemAccessors) + return QString(); + + QString fCap = e->name(); + fCap[0] = fCap[0].toUpper(); + return " "+cfg.inherits+"::Item"+itemType( e->type() ) + + " *item" + fCap + + ( (!e->param().isEmpty())?(QString("[%1]").arg(e->paramMax()+1)) : QString()) + + ";\n"; +} + +// returns the name of an item variable +// use itemPath to know the full path +// like using d-> in case of dpointer +static QString itemVar(const CfgEntry *e, const CfgConfig &cfg) +{ + QString result; + if (cfg.itemAccessors) + { + if ( !cfg.dpointer ) + { + result = 'm' + e->name() + "Item"; + result[1] = result[1].toUpper(); + } + else + { + result = e->name() + "Item"; + result[0] = result[0].toLower(); + } + } + else + { + result = "item" + e->name(); + result[4] = result[4].toUpper(); + } + return result; +} + +static QString itemPath(const CfgEntry *e, const CfgConfig &cfg) +{ + QString result; + if ( cfg.dpointer ) { + result = "d->"+itemVar(e, cfg); + } + else { + result = itemVar(e, cfg); + } + return result; +} + +QString newItem( const QString &type, const QString &name, const QString &key, + const QString &defaultValue, const CfgConfig &cfg, const QString ¶m = QString()) +{ + QString t = "new "+cfg.inherits+"::Item" + itemType( type ) + + "( currentGroup(), " + key + ", " + varPath( name, cfg ) + param; + if ( type == "Enum" ) t += ", values" + name; + if ( !defaultValue.isEmpty() ) { + t += ", "; + if ( type == "String" ) t += defaultValue; + else t+= defaultValue; + } + t += " );"; + + return t; +} + +QString paramString(const QString &s, const CfgEntry *e, int i) +{ + QString result = s; + QString needle = "$("+e->param()+')'; + if (result.contains(needle)) + { + QString tmp; + if (e->paramType() == "Enum") + { + tmp = e->paramValues()[i]; + } + else + { + tmp = QString::number(i); + } + + result.replace(needle, tmp); + } + return result; +} + +QString paramString(const QString &group, const QList<Param> ¶meters) +{ + QString paramString = group; + QString arguments; + int i = 1; + for (QList<Param>::ConstIterator it = parameters.constBegin(); + it != parameters.constEnd(); ++it) + { + if (paramString.contains("$("+(*it).name+')')) + { + QString tmp; + tmp.sprintf("%%%d", i++); + paramString.replace("$("+(*it).name+')', tmp); + arguments += ".arg( mParam"+(*it).name+" )"; + } + } + if (arguments.isEmpty()) + return "QLatin1String( \""+group+"\" )"; + + return "QString( QLatin1String( \""+paramString+"\" ) )"+arguments; +} + +QString translatedString(const CfgConfig &cfg, const QString &string, const QString &context = QString(), const QString ¶m = QString(), const QString ¶mValue = QString()) +{ + QString result; + + switch (cfg.translationSystem) { + case CfgConfig::QtTranslation: + if (!context.isEmpty()) { + result+= "/*: " + context + " */ QCoreApplication::translate(\""; + } else { + result+= "QCoreApplication::translate(\""; + } + result+= cfg.className + "\", "; + break; + + case CfgConfig::KdeTranslation: + if (!context.isEmpty()) { + result+= "i18nc(" + quoteString(context) + ", "; + } else { + result+= "i18n("; + } + break; + } + + if (!param.isEmpty()) { + QString resolvedString = string; + resolvedString.replace("$("+param+')', paramValue); + result+= quoteString(resolvedString); + } else { + result+= quoteString(string); + } + + result+= ')'; + + return result; +} + +/* int i is the value of the parameter */ +QString userTextsFunctions( CfgEntry *e, const CfgConfig &cfg, QString itemVarStr=QString(), QString i=QString() ) +{ + QString txt; + if (itemVarStr.isNull()) itemVarStr=itemPath(e, cfg); + if ( !e->label().isEmpty() ) { + txt += " " + itemVarStr + "->setLabel( "; + txt += translatedString(cfg, e->label(), e->labelContext(), e->param(), i); + txt += " );\n"; + } + if ( !e->toolTip().isEmpty() ) { + txt += " " + itemVarStr + "->setToolTip( "; + txt += translatedString(cfg, e->toolTip(), e->toolTipContext(), e->param(), i); + txt += " );\n"; + } + if ( !e->whatsThis().isEmpty() ) { + txt += " " + itemVarStr + "->setWhatsThis( "; + txt += translatedString(cfg, e->whatsThis(), e->whatsThisContext(), e->param(), i); + txt += " );\n"; + } + return txt; +} + +// returns the member accesor implementation +// which should go in the h file if inline +// or the cpp file if not inline +QString memberAccessorBody( CfgEntry *e, bool globalEnums, const CfgConfig &cfg ) +{ + QString result; + QTextStream out(&result, QIODevice::WriteOnly); + QString n = e->name(); + QString t = e->type(); + bool useEnumType = cfg.useEnumTypes && t == "Enum"; + + out << "return "; + if (useEnumType) + out << "static_cast<" << enumType(e, globalEnums) << ">("; + out << This << varPath(n, cfg); + if (!e->param().isEmpty()) + out << "[i]"; + if (useEnumType) + out << ")"; + out << ";" << endl; + + return result; +} + +// returns the member mutator implementation +// which should go in the h file if inline +// or the cpp file if not inline +QString memberMutatorBody( CfgEntry *e, const CfgConfig &cfg ) +{ + QString result; + QTextStream out(&result, QIODevice::WriteOnly); + QString n = e->name(); + QString t = e->type(); + + if (!e->minValue().isEmpty()) + { + if (e->minValue() != "0" || !isUnsigned(t)) { // skip writing "if uint<0" (#187579) + out << "if (v < " << e->minValue() << ")" << endl; + out << "{" << endl; + out << " qDebug() << \"" << setFunction(n); + out << ": value \" << v << \" is less than the minimum value of "; + out << e->minValue()<< "\";" << endl; + out << " v = " << e->minValue() << ";" << endl; + out << "}" << endl; + } + } + + if (!e->maxValue().isEmpty()) + { + out << endl << "if (v > " << e->maxValue() << ")" << endl; + out << "{" << endl; + out << " qDebug() << \"" << setFunction(n); + out << ": value \" << v << \" is greater than the maximum value of "; + out << e->maxValue()<< "\";" << endl; + out << " v = " << e->maxValue() << ";" << endl; + out << "}" << endl << endl; + } + + out << "if (!" << This << "isImmutable( QString::fromLatin1( \""; + if (!e->param().isEmpty()) + { + out << e->paramName().replace("$("+e->param()+")", "%1") << "\" ).arg( "; + if ( e->paramType() == "Enum" ) { + out << "QLatin1String( "; + + if (cfg.globalEnums) + out << enumName(e->param()) << "ToString[i]"; + else + out << enumName(e->param()) << "::enumToString[i]"; + + out << " )"; + } + else + { + out << "i"; + } + out << " )"; + } + else + { + out << n << "\" )"; + } + out << " ))" << (!e->signalList().empty() ? " {" : "") << endl; + out << " " << This << varPath(n, cfg); + if (!e->param().isEmpty()) + out << "[i]"; + out << " = v;" << endl; + + if ( !e->signalList().empty() ) { + Q_FOREACH(const Signal &signal, e->signalList()) { + out << " " << This << varPath("settingsChanged", cfg) << " |= " << signalEnumName(signal.name) << ";" << endl; + } + out << "}" << endl; + } + + return result; +} + +// returns the member get default implementation +// which should go in the h file if inline +// or the cpp file if not inline +QString memberGetDefaultBody( CfgEntry *e ) +{ + QString result = e->code(); + QTextStream out(&result, QIODevice::WriteOnly); + out << endl; + + if (!e->param().isEmpty()) { + out << " switch (i) {" << endl; + for (int i = 0; i <= e->paramMax(); ++i) { + if (!e->paramDefaultValue(i).isEmpty()) { + out << " case " << i << ": return " << e->paramDefaultValue(i) << ';' << endl; + } + } + out << " default:" << endl; + out << " return " << e->defaultValue().replace("$("+e->param()+')', "i") << ';' << endl; + out << " }" << endl; + } else { + out << " return " << e->defaultValue() << ';'; + } + + return result; +} + +// returns the item accesor implementation +// which should go in the h file if inline +// or the cpp file if not inline +QString itemAccessorBody( CfgEntry *e, const CfgConfig &cfg ) +{ + QString result; + QTextStream out(&result, QIODevice::WriteOnly); + + out << "return " << itemPath(e, cfg); + if (!e->param().isEmpty()) out << "[i]"; + out << ";" << endl; + + return result; +} + +//indents text adding X spaces per line +QString indent(QString text, int spaces) +{ + QString result; + QTextStream out(&result, QIODevice::WriteOnly); + QTextStream in(&text, QIODevice::ReadOnly); + QString currLine; + while ( !in.atEnd() ) + { + currLine = in.readLine(); + if (!currLine.isEmpty()) + for (int i=0; i < spaces; i++) + out << " "; + out << currLine << endl; + } + return result; +} + +// adds as many 'namespace foo {' lines to p_out as +// there are namespaces in p_ns +void beginNamespaces(const QString &p_ns, QTextStream &p_out) +{ + if ( !p_ns.isEmpty() ) { + const QStringList nameSpaces = p_ns.split( "::" ); + foreach (const QString &ns, nameSpaces ) + p_out << "namespace " << ns << " {" << endl; + p_out << endl; + } +} + +// adds as many '}' lines to p_out as +// there are namespaces in p_ns +void endNamespaces(const QString &p_ns, QTextStream &p_out) +{ + if ( !p_ns.isEmpty() ) { + const int namespaceCount = p_ns.count( "::" ) + 1; + for ( int i = 0; i < namespaceCount; ++i ) + p_out << "}" << endl; + p_out << endl; + } +} + + +int main( int argc, char **argv ) +{ + QCoreApplication app(argc, argv); + + validNameRegexp = new QRegExp("[a-zA-Z_][a-zA-Z0-9_]*"); + + QString directoryName, inputFilename, codegenFilename; + parseArgs(app.arguments(), directoryName, inputFilename, codegenFilename); + + QString baseDir = directoryName; +#ifdef Q_OS_WIN + if (!baseDir.endsWith('/') && !baseDir.endsWith('\\')) +#else + if (!baseDir.endsWith('/')) +#endif + baseDir.append("/"); + + if (!codegenFilename.endsWith(QLatin1String(".kcfgc"))) + { + cerr << "Codegen options file must have extension .kcfgc" << endl; + return 1; + } + QString baseName = QFileInfo(codegenFilename).fileName(); + baseName = baseName.left(baseName.length() - 6); + + CfgConfig cfg = CfgConfig( codegenFilename ); + + QFile input( inputFilename ); + + QDomDocument doc; + QString errorMsg; + int errorRow; + int errorCol; + if ( !doc.setContent( &input, &errorMsg, &errorRow, &errorCol ) ) { + cerr << "Unable to load document." << endl; + cerr << "Parse error in " << inputFilename << ", line " << errorRow << ", col " << errorCol << ": " << errorMsg << endl; + return 1; + } + + QDomElement cfgElement = doc.documentElement(); + + if ( cfgElement.isNull() ) { + cerr << "No document in kcfg file" << endl; + return 1; + } + + QString cfgFileName; + bool cfgFileNameArg = false; + QList<Param> parameters; + QList<Signal> signalList; + QStringList includes; + bool hasSignals = false; + + QList<CfgEntry*> entries; + + for ( QDomElement e = cfgElement.firstChildElement(); !e.isNull(); e = e.nextSiblingElement() ) { + QString tag = e.tagName(); + + if ( tag == "include" ) { + QString includeFile = e.text(); + if (!includeFile.isEmpty()) + includes.append(includeFile); + + } else if ( tag == "kcfgfile" ) { + cfgFileName = e.attribute( "name" ); + cfgFileNameArg = e.attribute( "arg" ).toLower() == "true"; + for( QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement() ) { + if ( e2.tagName() == "parameter" ) { + Param p; + p.name = e2.attribute( "name" ); + p.type = e2.attribute( "type" ); + if (p.type.isEmpty()) + p.type = "String"; + parameters.append( p ); + } + } + + } else if ( tag == "group" ) { + QString group = e.attribute( "name" ); + if ( group.isEmpty() ) { + cerr << "Group without name" << endl; + return 1; + } + for( QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement() ) { + if ( e2.tagName() != "entry" ) continue; + CfgEntry *entry = parseEntry( group, e2, cfg ); + if ( entry ) entries.append( entry ); + else { + cerr << "Can not parse entry." << endl; + return 1; + } + } + } + else if ( tag == "signal" ) { + QString signalName = e.attribute( "name" ); + if ( signalName.isEmpty() ) { + cerr << "Signal without name." << endl; + return 1; + } + Signal theSignal; + theSignal.name = signalName; + + for( QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement() ) { + if ( e2.tagName() == "argument") { + SignalArguments argument; + argument.type = e2.attribute("type"); + if ( argument.type.isEmpty() ) { + cerr << "Signal argument without type." << endl; + return 1; + } + argument.variableName = e2.text(); + theSignal.arguments.append(argument); + } + else if( e2.tagName() == "label") { + theSignal.label = e2.text(); + } + } + signalList.append(theSignal); + } + } + + if ( cfg.className.isEmpty() ) { + cerr << "Class name missing" << endl; + return 1; + } + + if ( cfg.singleton && !parameters.isEmpty() ) { + cerr << "Singleton class can not have parameters" << endl; + return 1; + } + + if ( !cfgFileName.isEmpty() && cfgFileNameArg) + { + cerr << "Having both a fixed filename and a filename as argument is not possible." << endl; + return 1; + } + + if ( entries.isEmpty() ) { + cerr << "No entries." << endl; + } + +#if 0 + CfgEntry *cfg; + for( cfg = entries.first(); cfg; cfg = entries.next() ) { + cfg->dump(); + } +#endif + + hasSignals = !signalList.empty(); + QString headerFileName = baseName + ".h"; + QString implementationFileName = baseName + ".cpp"; + QString mocFileName = baseName + ".moc"; + QString cppPreamble; // code to be inserted at the beginnin of the cpp file, e.g. initialization of static values + + QFile header( baseDir + headerFileName ); + if ( !header.open( QIODevice::WriteOnly ) ) { + cerr << "Can not open '" << baseDir << headerFileName << "for writing." << endl; + return 1; + } + + QTextStream h( &header ); + + h << "// This file is generated by kconfig_compiler from " << QFileInfo(inputFilename).fileName() << "." << endl; + h << "// All changes you do to this file will be lost." << endl; + + h << "#ifndef " << ( !cfg.nameSpace.isEmpty() ? QString (QString(cfg.nameSpace).replace( "::", "_" ).toUpper() + '_') : "" ) + << cfg.className.toUpper() << "_H" << endl; + h << "#define " << ( !cfg.nameSpace.isEmpty() ? QString (QString(cfg.nameSpace).replace( "::", "_" ).toUpper() + '_') : "" ) + << cfg.className.toUpper() << "_H" << endl << endl; + + // Includes + QStringList::ConstIterator it; + for( it = cfg.headerIncludes.constBegin(); it != cfg.headerIncludes.constEnd(); ++it ) { + if ( (*it).startsWith('"') ) + h << "#include " << *it << endl; + else + h << "#include <" << *it << ">" << endl; + } + + if ( cfg.headerIncludes.count() > 0 ) h << endl; + + if ( !cfg.singleton && parameters.isEmpty() ) + h << "#include <qglobal.h>" << endl; + + if ( cfg.inherits=="KCoreConfigSkeleton" ) { + h << "#include <kcoreconfigskeleton.h>" << endl; + } else { + h << "#include <kconfigskeleton.h>" << endl; + } + + h << "#include <QCoreApplication>" << endl; + h << "#include <QDebug>" << endl << endl; + + // Includes + for( it = includes.constBegin(); it != includes.constEnd(); ++it ) { + if ( (*it).startsWith('"') ) + h << "#include " << *it << endl; + else + h << "#include <" << *it << ">" << endl; + } + + beginNamespaces(cfg.nameSpace, h); + + // Private class declaration + if ( cfg.dpointer ) + h << "class " << cfg.className << "Private;" << endl << endl; + + // Class declaration header + h << "class " << cfg.visibility << cfg.className << " : public " << cfg.inherits << endl; + + h << "{" << endl; + // Add Q_OBJECT macro if the config need signals. + if( hasSignals ) + h << " Q_OBJECT" << endl; + h << " public:" << endl; + + // enums + QList<CfgEntry*>::ConstIterator itEntry; + for( itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry ) { + const CfgEntry::Choices &choices = (*itEntry)->choices(); + const QList<CfgEntry::Choice> chlist = choices.choices; + if ( !chlist.isEmpty() ) { + QStringList values; + QList<CfgEntry::Choice>::ConstIterator itChoice; + for( itChoice = chlist.constBegin(); itChoice != chlist.constEnd(); ++itChoice ) { + values.append( choices.prefix + (*itChoice).name ); + } + if ( choices.name().isEmpty() ) { + if ( cfg.globalEnums ) { + h << " enum " << enumName( (*itEntry)->name(), (*itEntry)->choices() ) << " { " << values.join( ", " ) << " };" << endl; + } else { + // Create an automatically named enum + h << " class " << enumName( (*itEntry)->name(), (*itEntry)->choices() ) << endl; + h << " {" << endl; + h << " public:" << endl; + h << " enum type { " << values.join( ", " ) << ", COUNT };" << endl; + h << " };" << endl; + } + } else if ( !choices.external() ) { + // Create a named enum + h << " enum " << enumName( (*itEntry)->name(), (*itEntry)->choices() ) << " { " << values.join( ", " ) << " };" << endl; + } + } + const QStringList values = (*itEntry)->paramValues(); + if ( !values.isEmpty() ) { + if ( cfg.globalEnums ) { + // ### FIXME!! + // make the following string table an index-based string search! + // ### + h << " enum " << enumName( (*itEntry)->param() ) << " { " << values.join( ", " ) << " };" << endl; + h << " static const char* const " << enumName( (*itEntry)->param() ) << "ToString[];" << endl; + cppPreamble += "const char* const " + cfg.className + "::" + enumName( (*itEntry)->param() ) + + "ToString[] = { \"" + values.join( "\", \"" ) + "\" };\n"; + } else { + h << " class " << enumName( (*itEntry)->param() ) << endl; + h << " {" << endl; + h << " public:" << endl; + h << " enum type { " << values.join( ", " ) << ", COUNT };" << endl; + h << " static const char* const enumToString[];" << endl; + h << " };" << endl; + cppPreamble += "const char* const " + cfg.className + "::" + enumName( (*itEntry)->param() ) + + "::enumToString[] = { \"" + values.join( "\", \"" ) + "\" };\n"; + } + } + } + if ( hasSignals ) { + h << "\n enum {" << endl; + unsigned val = 1; + QList<Signal>::ConstIterator it, itEnd = signalList.constEnd(); + for ( it = signalList.constBegin(); it != itEnd; val <<= 1) { + if ( !val ) { + cerr << "Too many signals to create unique bit masks" << endl; + exit(1); + } + Signal signal = *it; + h << " " << signalEnumName(signal.name) << " = 0x" << hex << val; + if ( ++it != itEnd ) + h << ","; + h << endl; + } + h << " };" << dec << endl; + } + h << endl; + // Constructor or singleton accessor + if ( !cfg.singleton ) { + h << " " << cfg.className << "("; + if (cfgFileNameArg) + { + if(cfg.forceStringFilename) + h << " const QString &cfgfilename" + << (parameters.isEmpty() ? " = QString()" : ", "); + else + h << " KSharedConfig::Ptr config" + << (parameters.isEmpty() ? " = KSharedConfig::openConfig()" : ", "); + } + for (QList<Param>::ConstIterator it = parameters.constBegin(); + it != parameters.constEnd(); ++it) + { + if (it != parameters.constBegin()) + h << ","; + h << " " << param((*it).type) << " " << (*it).name; + } + h << " );" << endl; + } else { + h << " static " << cfg.className << " *self();" << endl; + if (cfgFileNameArg) + { + h << " static void instance(const QString& cfgfilename);" << endl; + } + } + + // Destructor + h << " ~" << cfg.className << "();" << endl << endl; + + // global variables + if (cfg.staticAccessors) + This = "self()->"; + else + Const = " const"; + + for( itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry ) { + QString n = (*itEntry)->name(); + QString t = (*itEntry)->type(); + + // Manipulator + if (cfg.allMutators || cfg.mutators.contains(n)) + { + h << " /**" << endl; + h << " Set " << (*itEntry)->label() << endl; + h << " */" << endl; + if (cfg.staticAccessors) + h << " static" << endl; + h << " void " << setFunction(n) << "( "; + if (!(*itEntry)->param().isEmpty()) + h << cppType((*itEntry)->paramType()) << " i, "; + if (cfg.useEnumTypes && t == "Enum") + h << enumType(*itEntry, cfg.globalEnums); + else + h << param( t ); + h << " v )"; + // function body inline only if not using dpointer + // for BC mode + if ( !cfg.dpointer ) + { + h << endl << " {" << endl; + h << indent(memberMutatorBody(*itEntry, cfg), 6 ); + h << " }" << endl; + } + else + { + h << ";" << endl; + } + } + h << endl; + // Accessor + h << " /**" << endl; + h << " Get " << (*itEntry)->label() << endl; + h << " */" << endl; + if (cfg.staticAccessors) + h << " static" << endl; + h << " "; + if (cfg.useEnumTypes && t == "Enum") + h << enumType(*itEntry, cfg.globalEnums); + else + h << cppType(t); + h << " " << getFunction(n) << "("; + if (!(*itEntry)->param().isEmpty()) + h << " " << cppType((*itEntry)->paramType()) <<" i "; + h << ")" << Const; + // function body inline only if not using dpointer + // for BC mode + if ( !cfg.dpointer ) + { + h << endl << " {" << endl; + h << indent(memberAccessorBody(*itEntry, cfg.globalEnums, cfg), 6 ); + h << " }" << endl; + } + else + { + h << ";" << endl; + } + + // Default value Accessor + if ((cfg.allDefaultGetters || cfg.defaultGetters.contains(n)) && !(*itEntry)->defaultValue().isEmpty()) { + h << endl; + h << " /**" << endl; + h << " Get " << (*itEntry)->label() << " default value" << endl; + h << " */" << endl; + if (cfg.staticAccessors) + h << " static" << endl; + h << " "; + if (cfg.useEnumTypes && t == "Enum") + h << enumType(*itEntry, cfg.globalEnums); + else + h << cppType(t); + h << " " << getDefaultFunction(n) << "("; + if ( !(*itEntry)->param().isEmpty() ) + h << " " << cppType( (*itEntry)->paramType() ) <<" i "; + h << ")" << Const << endl; + h << " {" << endl; + h << " return "; + if (cfg.useEnumTypes && t == "Enum") + h << "static_cast<" << enumType(*itEntry, cfg.globalEnums) << ">("; + h << getDefaultFunction(n) << "_helper("; + if ( !(*itEntry)->param().isEmpty() ) + h << " i "; + h << ")"; + if (cfg.useEnumTypes && t == "Enum") + h << ")"; + h << ";" << endl; + h << " }" << endl; + } + + // Item accessor + if ( cfg.itemAccessors ) { + h << endl; + h << " /**" << endl; + h << " Get Item object corresponding to " << n << "()" + << endl; + h << " */" << endl; + h << " Item" << itemType( (*itEntry)->type() ) << " *" + << getFunction( n ) << "Item("; + if (!(*itEntry)->param().isEmpty()) { + h << " " << cppType((*itEntry)->paramType()) << " i "; + } + h << ")"; + if ( !cfg.dpointer ) + { + h << endl << " {" << endl; + h << indent( itemAccessorBody((*itEntry), cfg), 6); + h << " }" << endl; + } + else + { + h << ";" << endl; + } + } + + h << endl; + } + + + // Signal definition. + if( hasSignals ) { + h << endl; + h << " Q_SIGNALS:"; + Q_FOREACH(const Signal &signal, signalList) { + h << endl; + if ( !signal.label.isEmpty() ) { + h << " /**" << endl; + h << " " << signal.label << endl; + h << " */" << endl; + } + h << " void " << signal.name << "("; + QList<SignalArguments>::ConstIterator it, itEnd = signal.arguments.constEnd(); + for ( it = signal.arguments.constBegin(); it != itEnd; ) { + SignalArguments argument = *it; + QString type = param(argument.type); + if ( cfg.useEnumTypes && argument.type == "Enum" ) { + for ( int i = 0, end = entries.count(); i < end; ++i ) { + if ( entries[i]->name() == argument.variableName ) { + type = enumType(entries[i], cfg.globalEnums); + break; + } + } + } + h << type << " " << argument.variableName; + if ( ++it != itEnd ) { + h << ", "; + } + } + h << ");" << endl; + } + h << endl; + } + + h << " protected:" << endl; + + // Private constructor for singleton + if ( cfg.singleton ) { + h << " " << cfg.className << "("; + if ( cfgFileNameArg ) + h << "const QString& arg"; + h << ");" << endl; + h << " friend class " << cfg.className << "Helper;" << endl << endl; + } + + if ( hasSignals ) { + h << " virtual bool usrWriteConfig();" << endl; + } + + // Member variables + if ( !cfg.memberVariables.isEmpty() && cfg.memberVariables != "private" && cfg.memberVariables != "dpointer") { + h << " " << cfg.memberVariables << ":" << endl; + } + + // Class Parameters + for (QList<Param>::ConstIterator it = parameters.constBegin(); + it != parameters.constEnd(); ++it) + { + h << " " << cppType((*it).type) << " mParam" << (*it).name << ";" << endl; + } + + if ( cfg.memberVariables != "dpointer" ) + { + QString group; + for( itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry ) { + if ( (*itEntry)->group() != group ) { + group = (*itEntry)->group(); + h << endl; + h << " // " << group << endl; + } + h << " " << cppType( (*itEntry)->type() ) << " " << varName( (*itEntry)->name(), cfg ); + if ( !(*itEntry)->param().isEmpty() ) + { + h << QString("[%1]").arg( (*itEntry)->paramMax()+1 ); + } + h << ";" << endl; + + if ( cfg.allDefaultGetters || cfg.defaultGetters.contains((*itEntry)->name()) ) + { + h << " "; + if (cfg.staticAccessors) + h << "static "; + h << cppType((*itEntry)->type()) << " " << getDefaultFunction((*itEntry)->name()) << "_helper("; + if ( !(*itEntry)->param().isEmpty() ) + h << " " << cppType( (*itEntry)->paramType() ) <<" i "; + h << ")" << Const << ";" << endl; + } + } + + h << endl << " private:" << endl; + if ( cfg.itemAccessors ) { + for( itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry ) { + h << " Item" << itemType( (*itEntry)->type() ) << " *" << itemVar( *itEntry, cfg ); + if ( !(*itEntry)->param().isEmpty() ) h << QString("[%1]").arg( (*itEntry)->paramMax()+1 ); + h << ";" << endl; + } + } + if ( hasSignals ) + h << " uint " << varName("settingsChanged", cfg) << ";" << endl; + + } + else + { + // use a private class for both member variables and items + h << " private:" << endl; + for( itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry ) { + if ( cfg.allDefaultGetters || cfg.defaultGetters.contains((*itEntry)->name()) ) { + h << " "; + if (cfg.staticAccessors) + h << "static "; + h << cppType((*itEntry)->type()) << " " << getDefaultFunction((*itEntry)->name()) << "_helper("; + if ( !(*itEntry)->param().isEmpty() ) + h << " " << cppType( (*itEntry)->paramType() ) <<" i "; + h << ")" << Const << ";" << endl; + } + } + h << " " + cfg.className + "Private *d;" << endl; + } + + if (cfg.customAddons) + { + h << " // Include custom additions" << endl; + h << " #include \"" << filenameOnly(baseName) << "_addons.h\"" <<endl; + } + + h << "};" << endl << endl; + + endNamespaces(cfg.nameSpace, h); + + h << "#endif" << endl << endl; + + + header.close(); + + QFile implementation( baseDir + implementationFileName ); + if ( !implementation.open( QIODevice::WriteOnly ) ) { + cerr << "Can not open '" << implementationFileName << "for writing." + << endl; + return 1; + } + + QTextStream cpp( &implementation ); + + + cpp << "// This file is generated by kconfig_compiler from " << QFileInfo(inputFilename).fileName() << "." << endl; + cpp << "// All changes you do to this file will be lost." << endl << endl; + + cpp << "#include \"" << headerFileName << "\"" << endl << endl; + + for( it = cfg.sourceIncludes.constBegin(); it != cfg.sourceIncludes.constEnd(); ++it ) { + if ( (*it).startsWith('"') ) + cpp << "#include " << *it << endl; + else + cpp << "#include <" << *it << ">" << endl; + } + + if ( cfg.sourceIncludes.count() > 0 ) cpp << endl; + + if ( cfg.setUserTexts && cfg.translationSystem==CfgConfig::KdeTranslation) + cpp << "#include <klocalizedstring.h>" << endl << endl; + + // Header required by singleton implementation + if ( cfg.singleton ) + cpp << "#include <qglobal.h>" << endl << "#include <QtCore/QFile>" << endl << endl; + if ( cfg.singleton && cfgFileNameArg ) + cpp << "#include <QDebug>" << endl << endl; + + if ( !cfg.nameSpace.isEmpty() ) + cpp << "using namespace " << cfg.nameSpace << ";" << endl << endl; + + QString group; + + // private class implementation + if ( cfg.dpointer ) + { + beginNamespaces(cfg.nameSpace, cpp); + cpp << "class " << cfg.className << "Private" << endl; + cpp << "{" << endl; + cpp << " public:" << endl; + for( itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry ) { + if ( (*itEntry)->group() != group ) { + group = (*itEntry)->group(); + cpp << endl; + cpp << " // " << group << endl; + } + cpp << " " << cppType( (*itEntry)->type() ) << " " << varName( (*itEntry)->name(), cfg ); + if ( !(*itEntry)->param().isEmpty() ) + { + cpp << QString("[%1]").arg( (*itEntry)->paramMax()+1 ); + } + cpp << ";" << endl; + } + cpp << endl << " // items" << endl; + for( itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry ) { + cpp << " "+cfg.inherits+"::Item" << itemType( (*itEntry)->type() ) << " *" << itemVar( *itEntry, cfg ); + if ( !(*itEntry)->param().isEmpty() ) cpp << QString("[%1]").arg( (*itEntry)->paramMax()+1 ); + cpp << ";" << endl; + } + if ( hasSignals ) { + cpp << " uint " << varName("settingsChanged", cfg) << ";" << endl; + } + + cpp << "};" << endl << endl; + endNamespaces(cfg.nameSpace, cpp); + } + + // Singleton implementation + if ( cfg.singleton ) { + beginNamespaces(cfg.nameSpace, cpp); + cpp << "class " << cfg.className << "Helper" << endl; + cpp << '{' << endl; + cpp << " public:" << endl; + cpp << " " << cfg.className << "Helper() : q(0) {}" << endl; + cpp << " ~" << cfg.className << "Helper() { delete q; }" << endl; + cpp << " " << cfg.className << " *q;" << endl; + cpp << "};" << endl; + endNamespaces(cfg.nameSpace, cpp); + cpp << "Q_GLOBAL_STATIC(" << cfg.className << "Helper, s_global" << cfg.className << ")" << endl; + + cpp << cfg.className << " *" << cfg.className << "::self()" << endl; + cpp << "{" << endl; + if ( cfgFileNameArg ) { + cpp << " if (!s_global" << cfg.className << "()->q)" << endl; + cpp << " qFatal(\"you need to call " << cfg.className << "::instance before using\");" << endl; + } else { + cpp << " if (!s_global" << cfg.className << "()->q) {" << endl; + cpp << " new " << cfg.className << ';' << endl; + cpp << " s_global" << cfg.className << "()->q->readConfig();" << endl; + cpp << " }" << endl << endl; + } + cpp << " return s_global" << cfg.className << "()->q;" << endl; + cpp << "}" << endl << endl; + + if ( cfgFileNameArg ) { + cpp << "void " << cfg.className << "::instance(const QString& cfgfilename)" << endl; + cpp << "{" << endl; + cpp << " if (s_global" << cfg.className << "()->q) {" << endl; + cpp << " qDebug() << \"" << cfg.className << "::instance called after the first use - ignoring\";" << endl; + cpp << " return;" << endl; + cpp << " }" << endl; + cpp << " new " << cfg.className << "(cfgfilename);" << endl; + cpp << " s_global" << cfg.className << "()->q->readConfig();" << endl; + cpp << "}" << endl << endl; + } + } + + if ( !cppPreamble.isEmpty() ) + cpp << cppPreamble << endl; + + // Constructor + cpp << cfg.className << "::" << cfg.className << "( "; + if ( cfgFileNameArg ) { + if ( !cfg.singleton && ! cfg.forceStringFilename) + cpp << " KSharedConfig::Ptr config"; + else + cpp << " const QString& config"; + cpp << (parameters.isEmpty() ? " " : ", "); + } + + for (QList<Param>::ConstIterator it = parameters.constBegin(); + it != parameters.constEnd(); ++it) + { + if (it != parameters.constBegin()) + cpp << ","; + cpp << " " << param((*it).type) << " " << (*it).name; + } + cpp << " )" << endl; + + cpp << " : " << cfg.inherits << "("; + if ( !cfgFileName.isEmpty() ) cpp << " QLatin1String( \"" << cfgFileName << "\" "; + if ( cfgFileNameArg ) cpp << " config "; + if ( !cfgFileName.isEmpty() ) cpp << ") "; + cpp << ")" << endl; + + // Store parameters + for (QList<Param>::ConstIterator it = parameters.constBegin(); + it != parameters.constEnd(); ++it) + { + cpp << " , mParam" << (*it).name << "(" << (*it).name << ")" << endl; + } + + if ( hasSignals && !cfg.dpointer ) + cpp << " , " << varName("settingsChanged", cfg) << "(0)" << endl; + + cpp << "{" << endl; + + if (cfg.dpointer) + { + cpp << " d = new " + cfg.className + "Private;" << endl; + if (hasSignals) + cpp << " " << varPath("settingsChanged", cfg) << " = 0;" << endl; + } + // Needed in case the singleton class is used as baseclass for + // another singleton. + if (cfg.singleton) { + cpp << " Q_ASSERT(!s_global" << cfg.className << "()->q);" << endl; + cpp << " s_global" << cfg.className << "()->q = this;" << endl; + } + + group.clear(); + + for( itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry ) { + if ( (*itEntry)->group() != group ) { + if ( !group.isEmpty() ) cpp << endl; + group = (*itEntry)->group(); + cpp << " setCurrentGroup( " << paramString(group, parameters) << " );" << endl << endl; + } + + QString key = paramString( (*itEntry)->key(), parameters ); + if ( !(*itEntry)->code().isEmpty() ) { + cpp << (*itEntry)->code() << endl; + } + if ( (*itEntry)->type() == "Enum" ) { + cpp << " QList<"+cfg.inherits+"::ItemEnum::Choice> values" + << (*itEntry)->name() << ";" << endl; + const QList<CfgEntry::Choice> choices = (*itEntry)->choices().choices; + QList<CfgEntry::Choice>::ConstIterator it; + for( it = choices.constBegin(); it != choices.constEnd(); ++it ) { + cpp << " {" << endl; + cpp << " "+cfg.inherits+"::ItemEnum::Choice choice;" << endl; + cpp << " choice.name = QLatin1String(\"" << (*it).name << "\");" << endl; + if ( cfg.setUserTexts ) { + if ( !(*it).label.isEmpty() ) { + cpp << " choice.label = " + << translatedString(cfg, (*it).label, (*it).context) + << ";" << endl; + } + if ( !(*it).toolTip.isEmpty() ) { + cpp << " choice.toolTip = " + << translatedString(cfg, (*it).toolTip, (*it).context) + << ";" << endl; + } + if ( !(*it).whatsThis.isEmpty() ) { + cpp << " choice.whatsThis = " + << translatedString(cfg, (*it).whatsThis, (*it).context) + << ";" << endl; + } + } + cpp << " values" << (*itEntry)->name() << ".append( choice );" << endl; + cpp << " }" << endl; + } + } + + if (!cfg.dpointer) + cpp << itemDeclaration( *itEntry, cfg ); + + if ( (*itEntry)->param().isEmpty() ) + { + // Normal case + cpp << " " << itemPath( *itEntry, cfg ) << " = " + << newItem( (*itEntry)->type(), (*itEntry)->name(), key, (*itEntry)->defaultValue(), cfg ) << endl; + + if ( !(*itEntry)->minValue().isEmpty() ) + cpp << " " << itemPath( *itEntry, cfg ) << "->setMinValue(" << (*itEntry)->minValue() << ");" << endl; + if ( !(*itEntry)->maxValue().isEmpty() ) + cpp << " " << itemPath( *itEntry, cfg ) << "->setMaxValue(" << (*itEntry)->maxValue() << ");" << endl; + + if ( cfg.setUserTexts ) + cpp << userTextsFunctions( (*itEntry), cfg ); + + cpp << " addItem( " << itemPath( *itEntry, cfg ); + QString quotedName = (*itEntry)->name(); + addQuotes( quotedName ); + if ( quotedName != key ) cpp << ", QLatin1String( \"" << (*itEntry)->name() << "\" )"; + cpp << " );" << endl; + } + else + { + // Indexed + for(int i = 0; i <= (*itEntry)->paramMax(); i++) + { + QString defaultStr; + QString itemVarStr(itemPath( *itEntry, cfg )+QString("[%1]").arg(i)); + + if ( !(*itEntry)->paramDefaultValue(i).isEmpty() ) + defaultStr = (*itEntry)->paramDefaultValue(i); + else if ( !(*itEntry)->defaultValue().isEmpty() ) + defaultStr = paramString( (*itEntry)->defaultValue(), (*itEntry), i ); + else + defaultStr = defaultValue( (*itEntry)->type() ); + + cpp << " " << itemVarStr << " = " + << newItem( (*itEntry)->type(), (*itEntry)->name(), paramString(key, *itEntry, i), defaultStr,cfg, QString("[%1]").arg(i) ) + << endl; + + if ( cfg.setUserTexts ) + cpp << userTextsFunctions( *itEntry, cfg, itemVarStr, (*itEntry)->paramName() ); + + // Make mutators for enum parameters work by adding them with $(..) replaced by the + // param name. The check for isImmutable in the set* functions doesn't have the param + // name available, just the corresponding enum value (int), so we need to store the + // param names in a separate static list!. + cpp << " addItem( " << itemVarStr << ", QLatin1String( \""; + if ( (*itEntry)->paramType()=="Enum" ) + cpp << (*itEntry)->paramName().replace( "$("+(*itEntry)->param()+')', "%1").arg((*itEntry)->paramValues()[i] ); + else + cpp << (*itEntry)->paramName().replace( "$("+(*itEntry)->param()+')', "%1").arg(i); + cpp << "\" ) );" << endl; + } + } + } + + cpp << "}" << endl << endl; + + if (cfg.dpointer) + { + // setters and getters go in Cpp if in dpointer mode + for( itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry ) { + QString n = (*itEntry)->name(); + QString t = (*itEntry)->type(); + + // Manipulator + if (cfg.allMutators || cfg.mutators.contains(n)) + { + cpp << "void " << setFunction(n, cfg.className) << "( "; + if ( !(*itEntry)->param().isEmpty() ) + cpp << cppType( (*itEntry)->paramType() ) << " i, "; + if (cfg.useEnumTypes && t == "Enum") + cpp << enumType(*itEntry, cfg.globalEnums); + else + cpp << param( t ); + cpp << " v )" << endl; + // function body inline only if not using dpointer + // for BC mode + cpp << "{" << endl; + cpp << indent(memberMutatorBody( *itEntry, cfg ), 6); + cpp << "}" << endl << endl; + } + + // Accessor + if (cfg.useEnumTypes && t == "Enum") + cpp << enumType(*itEntry, cfg.globalEnums); + else + cpp << cppType(t); + cpp << " " << getFunction(n, cfg.className) << "("; + if ( !(*itEntry)->param().isEmpty() ) + cpp << " " << cppType( (*itEntry)->paramType() ) <<" i "; + cpp << ")" << Const << endl; + // function body inline only if not using dpointer + // for BC mode + cpp << "{" << endl; + cpp << indent(memberAccessorBody( *itEntry, cfg.globalEnums, cfg ), 2); + cpp << "}" << endl << endl; + + // Default value Accessor -- written by the loop below + + // Item accessor + if ( cfg.itemAccessors ) + { + cpp << endl; + cpp << cfg.inherits+"::Item" << itemType( (*itEntry)->type() ) << " *" + << getFunction( n, cfg.className ) << "Item("; + if ( !(*itEntry)->param().isEmpty() ) { + cpp << " " << cppType( (*itEntry)->paramType() ) << " i "; + } + cpp << ")" << endl; + cpp << "{" << endl; + cpp << indent(itemAccessorBody( *itEntry, cfg ), 2); + cpp << "}" << endl; + } + + cpp << endl; + } + } + + // default value getters always go in Cpp + for( itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry ) { + QString n = (*itEntry)->name(); + QString t = (*itEntry)->type(); + + // Default value Accessor, as "helper" function + if (( cfg.allDefaultGetters || cfg.defaultGetters.contains(n) ) && !(*itEntry)->defaultValue().isEmpty() ) { + cpp << cppType(t) << " " << getDefaultFunction(n, cfg.className) << "_helper("; + if ( !(*itEntry)->param().isEmpty() ) + cpp << " " << cppType( (*itEntry)->paramType() ) <<" i "; + cpp << ")" << Const << endl; + cpp << "{" << endl; + cpp << memberGetDefaultBody(*itEntry) << endl; + cpp << "}" << endl << endl; + } + } + + // Destructor + cpp << cfg.className << "::~" << cfg.className << "()" << endl; + cpp << "{" << endl; + if ( cfg.singleton ) { + if ( cfg.dpointer ) + cpp << " delete d;" << endl; + cpp << " s_global" << cfg.className << "()->q = 0;" << endl; + } + cpp << "}" << endl << endl; + + if ( hasSignals ) { + cpp << "bool " << cfg.className << "::" << "usrWriteConfig()" << endl; + cpp << "{" << endl; + cpp << " const bool res = " << cfg.inherits << "::usrWriteConfig();" << endl; + cpp << " if (!res) return false;" << endl << endl; + Q_FOREACH(const Signal &signal, signalList) { + cpp << " if ( " << varPath("settingsChanged", cfg) << " & " << signalEnumName(signal.name) << " ) " << endl; + cpp << " emit " << signal.name << "("; + QList<SignalArguments>::ConstIterator it, itEnd = signal.arguments.constEnd(); + for ( it = signal.arguments.constBegin(); it != itEnd; ) { + SignalArguments argument = *it; + bool cast = false; + if ( cfg.useEnumTypes && argument.type == "Enum" ) { + for ( int i = 0, end = entries.count(); i < end; ++i ) { + if ( entries[i]->name() == argument.variableName ) { + cpp << "static_cast<" << enumType(entries[i], cfg.globalEnums) << ">("; + cast = true; + break; + } + } + } + cpp << varPath(argument.variableName, cfg); + if ( cast ) + cpp << ")"; + if ( ++it != itEnd ) + cpp << ", "; + } + cpp << ");" << endl << endl; + } + cpp << " " << varPath("settingsChanged", cfg) << " = 0;" << endl; + cpp << " return true;" << endl; + cpp << "}" << endl; + } + + // Add includemoc if they are signals defined. + if( hasSignals ) { + cpp << endl; + cpp << "#include \"" << mocFileName << "\"" << endl; + cpp << endl; + } + + // clear entries list + qDeleteAll( entries ); + + implementation.close(); +} |
