diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 0000000..325a737 --- /dev/null +++ b/Doxyfile @@ -0,0 +1,1749 @@ +# Doxyfile 1.7.4 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = "FLAME Editor" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "Generic editor tool for FLAME projects" + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = docs + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this +# tag. The format is ext=language, where ext is a file extension, and language +# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, +# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions +# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = YES + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penalty. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will roughly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols + +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. The create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.d \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.idl \ + *.odl \ + *.cs \ + *.php \ + *.php3 \ + *.inc \ + *.m \ + *.mm \ + *.dox \ + *.py \ + *.f90 \ + *.f \ + *.for \ + *.vhd \ + *.vhdl + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is adviced to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when +# changing the value of configuration settings such as GENERATE_TREEVIEW! + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the stylesheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 4 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = YES + +# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list. + +USE_INLINE_TREES = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the +# mathjax.org site, so you can quickly see the result without installing +# MathJax, but it is strongly recommended to install a local copy of MathJax +# before deployment. + +MATHJAX_RELPATH = http://www.mathjax.org/mathjax + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a PHP enabled web server instead of at the web client +# using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server +# based approach is that it scales better to large projects and allows +# full text search. The disadvantages are that it is more difficult to setup +# and does not have live searching capabilities. + +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = YES + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = NO + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 8 + +# By default doxygen will write a font called Helvetica to the output +# directory and reference it in all dot files that doxygen generates. +# When you want a differently looking font you can specify the font name +# using DOT_FONTNAME. You need to make sure dot is able to find the font, +# which can be done by putting it in a standard location or by setting the +# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory +# containing the font. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = NO + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = NO + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = NO + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = NO + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = NO + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = NO + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = YES + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/README.md b/README.md new file mode 100644 index 0000000..b650479 --- /dev/null +++ b/README.md @@ -0,0 +1,39 @@ +FLAME Editor + +*** LINUX *** + +To compile you will need Qt4. +The package for Ubuntu is called: libqt4-dev + +To compile type: + + qmake flame_editor.pro + make + +To run: + + ./flame_editor + +*** MAC OSX *** + +To compile you will need Qt4. +Download from the Qt web site. +Normally qmake creates xcode projects on mac. +To stop this use the -spec flag below. + +To compile type: + + qmake -spec macx-g++ flame_editor.pro + make + +To run: + + open flame_editor.app + +If you have any problems try using Qt Creator. + +*** WINDOWS *** + +To compile you will need Qt4. +Download from the Qt web site. +Use Qt Creator to open the project flame_editor.pro and compile. diff --git a/adt.cpp b/adt.cpp new file mode 100644 index 0000000..6f132c8 --- /dev/null +++ b/adt.cpp @@ -0,0 +1,21 @@ +/*! + * \file adt.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of model data type + */ +#include "./adt.h" + +/*! + * \brief Model data type constructor + * \param[in] n The data type name, default "" + * \param[in] d The data type description, default "" + * + * This contructor takes a name and a description string. + * If either are missing they default to an empty string. + */ +ADT::ADT(QString n, QString d) { + name = n; + desc = d; +} diff --git a/adt.h b/adt.h new file mode 100644 index 0000000..c5d47ee --- /dev/null +++ b/adt.h @@ -0,0 +1,31 @@ +/*! + * \file adt.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for model datatypes + */ +#ifndef ADT_H_ +#define ADT_H_ + +#include +#include +#include "./variable.h" + +/*! + * \brief Model data type class + * + * This class holds the information about a user defined abstract data type. + */ +class ADT { + public: + ADT(QString = "", QString = ""); + + private: + QString name; /*!< \brief The name of the data type */ + QString desc; /*!< \brief The description of the data type */ + /*! \brief The list of variables of the data type */ + QList variables; +}; + +#endif // ADT_H_ diff --git a/arrow.cpp b/arrow.cpp new file mode 100644 index 0000000..d4580fa --- /dev/null +++ b/arrow.cpp @@ -0,0 +1,189 @@ +/*! + * \file arrow.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of graphics arrow in stategraphs + */ +#include +#include +#include "./arrow.h" + +const qreal Pi = 3.14; /* definition of Pi */ + +/*! + * \brief Arrow constructor + * \param[in] startItem The tail graphics item + * \param[in] endItem The head graphics item + * \param[in] parent Not required, is automatically initalised + * \param[in] scene Not required, is automatically initalised + * + * This contructor takes starting and ending graphics items for an arrow to be drawn between them. + */ +Arrow::Arrow(GraphicsItem *startItem, GraphicsItem *endItem, + QGraphicsItem *parent, QGraphicsScene *scene) + : QGraphicsLineItem(parent, scene) { + myStartItem = startItem; + myEndItem = endItem; + setFlag(QGraphicsItem::ItemIsSelectable, true); + myColor = Qt::black; + setPen(QPen(myColor, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); + offset = 0; + number = 1; + total = 1; + foreign = false; + showHead = true; + isCommunication = false; + myTransition = 0; +} + +/*! + * \brief Set a new name + * \param[in] n The new name + */ +void Arrow::setName(QString n) { + name = n; +} + +/*! + * \brief Set a new \c Mpre + * \param[in] m The new Mpre + */ +void Arrow::setMpre(Mpre m) { + mpre = m; +} + +/*! + * \brief Set a new \c Mpost + * \param[in] mp The new Mpost + */ +void Arrow::setMpost(Mpost mp) { + mpost = mp; +} + +QRectF Arrow::boundingRect() const { + // qreal extra = ((pen().width() + 20 + offset) / 2.0); + qreal extra = ((pen().width() + 20 + offset)); + + return QRectF(line().p1(), QSizeF(line().p2().x() - line().p1().x(), + line().p2().y() - line().p1().y())) + .normalized() + .adjusted(-extra, -extra, extra, extra); +} + +QPainterPath Arrow::shape() const { + QPainterPath path = QGraphicsLineItem::shape(); + path.addPolygon(arrowHead); + return path; +} + +void Arrow::updatePosition() { + QLineF line(mapFromItem(myStartItem, 0, 0), mapFromItem(myEndItem, 0, 0)); + setLine(line); +} + +void Arrow::paint(QPainter *painter, const QStyleOptionGraphicsItem *, + QWidget *) { + if (myStartItem->collidesWithItem(myEndItem)) + return; + + QPen myPen = pen(); + myPen.setColor(myColor); + qreal arrowSize = 10; + painter->setPen(myPen); + + if (foreign) painter->setPen(Qt::gray); + else + painter->setPen(isSelected() ? Qt::red : Qt::black); + if (isCommunication) + painter->setPen(isSelected() ? Qt::darkGreen : Qt::green); + painter->setRenderHint(QPainter::Antialiasing); + + // new + QPainterPath endPath = myEndItem->shape(); + float dif = 0.1; + QPointF intersectionPoint; + QPointF ip; + QLineF centerLine2(myStartItem->scenePos(), myEndItem->scenePos()); + + + + for (float i = 0; i < 1.0; i+=dif) { + float e = i+dif; + if (e > 0.99) e = 0; + QLineF myLine(endPath.pointAtPercent(i), endPath.pointAtPercent(e)); + QLineF myLine2(myLine.x1()+centerLine2.x2(), + myLine.y1()+centerLine2.y2(), + myLine.x2()+centerLine2.x2(), + myLine.y2()+centerLine2.y2()); + int rc = centerLine2.intersect(myLine2, &ip); + if (rc == QLineF::BoundedIntersection) { + intersectionPoint = ip; + break; + } + } + + setLine(QLineF(intersectionPoint, myStartItem->pos())); + + + + double angle = ::acos(line().dx() / line().length()); + // double angle2 = angle; + if (line().dy() >= 0) + angle = (Pi * 2) - angle; + + QPainterPath path; + QPointF half(line().x1() + (line().dx()/2), line().y1() + (line().dy()/2)); + path.moveTo(line().p1()); + // QPointF c2(line().x1()+(line().dx()/2.0)+(sin(angle2)*offset), + // line().y1()+(line().dy()/2.0)+(cos(angle2)*offset)); + // offset = 20; + + offset = (total-1)*-5+(number-1)*10; + + QPointF c2 = half + QPointF(sin(angle + Pi) * offset, + cos(angle + Pi) * offset); + // QPointF c2 = half - QPointF(sin(angle + Pi) * offset, + // cos(angle + Pi) * offset); + path.quadTo(c2, line().p2()); + // path.lineTo(c2); + // use path to work out angle + QLineF l(line().p1(), c2); + double angle3 = ::acos(l.dx() / l.length()); + angle = angle3; + if (l.dy() >= 0) + angle = (Pi * 2) - angle; + + QPointF arrowP1 = line().p1() + QPointF(sin(angle + Pi / 3) * + arrowSize, cos(angle + Pi / 3) * arrowSize); + QPointF arrowP2 = line().p1() + QPointF(sin(angle + Pi - Pi / 3) * + arrowSize, cos(angle + Pi - Pi / 3) * arrowSize); + + /* The clear() function for QVector has a + * compile warning under Qt 4.5 */ + #if QT_VERSION >= 0x040600 // If Qt version is 4.6 or higher + arrowHead.clear(); + #else + arrowHead = QPolygonF(); + #endif + + arrowHead << line().p1(); + if (showHead) arrowHead << arrowP1 << arrowP2; + + // painter->drawLine(line()); + painter->drawPath(path); + if (foreign) painter->setBrush(Qt::gray); + else + painter->setBrush(isSelected() ? Qt::red : Qt::black); + if (isCommunication) + painter->setBrush(isSelected() ? Qt::darkGreen : Qt::green); + painter->drawPolygon(arrowHead); + + /*if(myTransition != 0) + { + if(myTransition->condition().enabled == true) + { + painter->drawText(half, myTransition->condition().toString()); + } + }*/ +} diff --git a/arrow.h b/arrow.h new file mode 100644 index 0000000..0967566 --- /dev/null +++ b/arrow.h @@ -0,0 +1,87 @@ +/*! + * \file arrow.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for arrow used in stategraphs + */ +#ifndef ARROW_H_ +#define ARROW_H_ + +#include +#include "./graphicsitem.h" +#include "./mpre.h" +#include "./mpost.h" +#include "./memorymodel.h" +#include "./transition.h" + +/*! + * \brief Graphics arrow class + * + * This class represents an arrow in the stategraph graphics scene. + */ +class Arrow : public QGraphicsLineItem { + public: + /*! + * \brief This enum is used to hold a unique int associated with graphicsitems + */ + enum { + Type = UserType + 4 /**< enum Type is the unique graphicsitems int. */ + }; + + Arrow(GraphicsItem *startItem, GraphicsItem *endItem, + QGraphicsItem *parent = 0, QGraphicsScene *scene = 0); + + int type() const + { return Type; } + QRectF boundingRect() const; + QPainterPath shape() const; + void setColor(const QColor &color) + { myColor = color; } + GraphicsItem *startItem() const + { return myStartItem; } + GraphicsItem *endItem() const + { return myEndItem; } + QString getName() const + { return name; } + void setName(QString n); + Mpre getMpre() const + { return mpre; } + void setMpre(Mpre m); + Mpost getMpost() const + { return mpost; } + Mpre * getMprePointer() { return &mpre; } + void setMpost(Mpost mp); + void setTransition(Transition * t) { myTransition = t; } + void drawHead(bool b) { showHead = b; } + + int number; /**< \brief . */ + int total; /**< \brief . */ + double offset; /**< \brief . */ + /*! \brief flag for local to current model or foreign. */ + bool foreign; + /*! \brief flag for state transition or communication arrows. */ + bool isCommunication; + + public slots: + void updatePosition(); + + protected: + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, + QWidget *widget = 0); + + private: + QString name; /**< \brief . */ + GraphicsItem *myStartItem; /**< \brief the tail graphics item. */ + GraphicsItem *myEndItem; /**< \brief the head graphics item. */ + QColor myColor; /**< \brief the arrow colour. */ + /*! \brief the polygon representation of the arrow head. */ + QPolygonF arrowHead; + Mpre mpre; /**< \brief any associated condition. */ + Mpost mpost; /**< \brief . */ + bool showHead; /**< \brief flag to show the arrow head. */ + /*! \brief any associated function transition. */ + Transition * myTransition; +}; + +#endif // ARROW_H_ diff --git a/commdelegate.cpp b/commdelegate.cpp new file mode 100644 index 0000000..6f304fe --- /dev/null +++ b/commdelegate.cpp @@ -0,0 +1,145 @@ +/*! + * \file commdelegate.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of message communication delegate + */ +#include +#include +#include "./commdelegate.h" +#include "./communication.h" +#include "./commdialog.h" + +CommDelegate::CommDelegate(Machine * m, QObject *parent) + : QItemDelegate(parent) { + machine = m; +} + +void CommDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const { + if (qVariantCanConvert(index.data())) { + Communication communication = + qVariantValue(index.data()); + QStyle &style = *QApplication::style(); + painter->save(); + + QStyleOptionViewItemV4 opt = option; + + style.drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, 0); + + QRect textRect = style.subElementRect( + QStyle::SE_ItemViewItemText, &opt, 0); + + // following code borrowed from qcommonstyle.cpp + QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled ? + QPalette::Normal : QPalette::Disabled; + if (cg == QPalette::Normal && !(opt.state & QStyle::State_Active)) { + cg = QPalette::Inactive; + } + if (opt.state & QStyle::State_Selected) { + painter->setPen(QPen(opt.palette.brush(cg, + QPalette::HighlightedText), 0)); + } else { + painter->setPen(QPen(opt.palette.brush(cg, QPalette::Text), 0)); + } + if (opt.state & QStyle::State_Editing) { + painter->setPen(QPen(opt.palette.brush(cg, QPalette::Text), 0)); + painter->drawRect(textRect.adjusted(0, 0, -1, -1)); + } + + QFontMetrics metrics(opt.font); + int height = 0; + + QStringList slist = communication.toString().split("\n"); + for (int i = 0; i < slist.count(); i++) { + textRect.setTop(textRect.top() + height); + style.drawItemText(painter, textRect, Qt::AlignLeft, + opt.palette, true, elidedText(opt.fontMetrics, + textRect.width(), Qt::ElideRight, slist.at(i))); + QRect bRect = metrics.boundingRect(slist.at(i)); + height = bRect.height(); + } + + painter->restore(); + } else { + QItemDelegate::paint(painter, option, index); + } +} + +QSize CommDelegate::sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const { + if (qVariantCanConvert(index.data())) { + Communication communication = + qVariantValue(index.data()); + // return communication.sizeHint(option, index); + int height = 0; + int width = 0; + QStringList slist = communication.toString().split("\n"); + for (int i = 0; i < slist.count(); i++) { + QRect rect = option.fontMetrics.boundingRect(slist.at(i)); + height += rect.height(); + width = std::max(width, rect.width()); + } + + int hmargin = QApplication::style()->pixelMetric( + QStyle::PM_FocusFrameHMargin) + 1; + int vmargin = QApplication::style()->pixelMetric( + QStyle::PM_FocusFrameVMargin) + 1; + + return QSize(width+hmargin, height+vmargin); + } else { + return QItemDelegate::sizeHint(option, index); + } +} + +QWidget *CommDelegate::createEditor(QWidget */*parent*/, + const QStyleOptionViewItem &/*option*/, + const QModelIndex &/*index*/) const { + CommDialog *editor = new CommDialog(machine); + + connect(editor, SIGNAL(accepted()), this, SLOT(commitAndCloseEditor())); + connect(editor, SIGNAL(rejected()), this, SLOT(commitAndCloseEditor())); + + // editor->setParent(windowParent); + editor->setModal(true); + // editor->move(100, 100); + // editor->setWindowFlags(WShowModal); + + return editor; +} + +void CommDelegate::setEditorData(QWidget *editor, + const QModelIndex &index) const { + if (qVariantCanConvert(index.data())) { + Communication communication = + qVariantValue(index.data()); + CommDialog *dialog = static_cast(editor); + + dialog->setCommunication(communication); + } else { + QItemDelegate::setEditorData(editor, index); + } +} + +void CommDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const { + if (qVariantCanConvert(index.data())) { + CommDialog *dialog = static_cast(editor); + model->setData(index, qVariantFromValue(dialog->getCommunication())); + } else { + QItemDelegate::setModelData(editor, model, index); + } +} + +/*void CommDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + editor->setGeometry(option.rect); +}*/ + +void CommDelegate::commitAndCloseEditor() { + CommDialog *editor = qobject_cast(sender()); + emit commitData(editor); + emit closeEditor(editor); +} diff --git a/commdelegate.h b/commdelegate.h new file mode 100644 index 0000000..924f622 --- /dev/null +++ b/commdelegate.h @@ -0,0 +1,39 @@ +/*! + * \file commdelegate.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for message communication delegate + */ +#ifndef COMMDELEGATE_H_ +#define COMMDELEGATE_H_ + +#include +#include +#include +#include "./machine.h" + +class CommDelegate : public QItemDelegate { + Q_OBJECT + + public: + CommDelegate(Machine * machine, QObject *parent = 0); + + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + QSize sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const; + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + + private slots: + void commitAndCloseEditor(); + + private: + Machine * machine; +}; + +#endif // COMMDELEGATE_H_ diff --git a/commdialog.cpp b/commdialog.cpp new file mode 100644 index 0000000..74e6a5c --- /dev/null +++ b/commdialog.cpp @@ -0,0 +1,60 @@ +/*! + * \file commdialog.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of message communication dialog + */ +#include "./commdialog.h" +#include "./messagemodel.h" +#include "./mpredelegate.h" +#include "./messagedelegate.h" +#include "./sortdelegate.h" + +CommDialog::CommDialog(Machine * m, QWidget *parent) + : QDialog(parent) { + setupUi(this); + + machine = m; + + QHeaderView *headerView = tableView_messages->horizontalHeader(); + headerView->setResizeMode(QHeaderView::Stretch); + headerView->setResizeMode(1, QHeaderView::Interactive); + tableView_messages->verticalHeader()->hide(); + tableView_messages->setItemDelegateForColumn(0, + new MessageDelegate(m->getMessageNames())); +} + +void CommDialog::setCommunication(Communication c) { + comm = c; + + tableView_messages->setModel(comm.messageModel); + tableView_messages->setItemDelegateForColumn(1, + new MpreDelegate(machine, &comm)); + tableView_messages->setItemDelegateForColumn(2, + new SortDelegate(machine, &comm)); +} + +Communication CommDialog::getCommunication() { + return comm; +} + +void CommDialog::on_pushButton_plus_clicked() { + QStringList messages = machine->getMessageNames(); + if (messages.count() > 0) { + comm.messageModel->addMessage(messages.first()); + } else { + QMessageBox::warning(this, tr("FLAME Editor"), + tr("There are no message types associated with this model to add.")); + } +} + +void CommDialog::on_pushButton_minus_clicked() { + QModelIndexList indexList = + tableView_messages->selectionModel()->selectedRows(); + + while (indexList.count() > 0) { + comm.messageModel->removeRow(indexList.at(0).row()); + indexList = tableView_messages->selectionModel()->selectedRows(); + } +} diff --git a/commdialog.h b/commdialog.h new file mode 100644 index 0000000..a7ad62e --- /dev/null +++ b/commdialog.h @@ -0,0 +1,33 @@ +/*! + * \file commdialog.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for message communication dialog + */ +#ifndef COMMDIALOG_H_ +#define COMMDIALOG_H_ + +#include +#include "./machine.h" +#include "./communication.h" +#include "./ui_commdialog.h" + +class CommDialog : public QDialog, public Ui::CommDialog { + Q_OBJECT + + public: + CommDialog(Machine * machine, QWidget *parent = 0); + void setCommunication(Communication c); + Communication getCommunication(); + + private slots: + void on_pushButton_plus_clicked(); + void on_pushButton_minus_clicked(); + + private: + Machine * machine; + Communication comm; +}; + +#endif // COMMDIALOG_H_ diff --git a/commdialog.ui b/commdialog.ui new file mode 100644 index 0000000..29908e7 --- /dev/null +++ b/commdialog.ui @@ -0,0 +1,108 @@ + + + CommDialog + + + + 0 + 0 + 699 + 331 + + + + Communication Editor + + + + + + QAbstractItemView::SelectRows + + + + + + + + + Add message type + + + + + + + + 0 + 0 + + + + Remove message type + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + CommDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + CommDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/communication.cpp b/communication.cpp new file mode 100644 index 0000000..bebe560 --- /dev/null +++ b/communication.cpp @@ -0,0 +1,27 @@ +/*! + * \file communication.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of message communication + */ +#include "./communication.h" + +Communication::Communication() { + messageModel = new MessageModel(1); + flag = 1; +} + +Communication::Communication(int f) { + messageModel = new MessageModel(f); + flag = f; +} + +QString Communication::toString() const { + QString text; + for (int i = 0; i < messageModel->rowCount(); i++) { + text.append(messageModel->getMessages()[i].toString()); + text.append("\n"); + } + return text; +} diff --git a/communication.h b/communication.h new file mode 100644 index 0000000..df09cfe --- /dev/null +++ b/communication.h @@ -0,0 +1,31 @@ +/*! + * \file communication.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for message communication + */ +#ifndef COMMUNICATION_H_ +#define COMMUNICATION_H_ + +#include +#include +#include +#include "./messagemodel.h" + +class Communication { + public: + enum EditMode { Editable, ReadOnly }; + + Communication(); + explicit Communication(int flag); + + QString toString() const; + + MessageModel * messageModel; + int flag; // 1 for input, 0 for output +}; + +Q_DECLARE_METATYPE(Communication) + +#endif // COMMUNICATION_H_ diff --git a/condition.cpp b/condition.cpp new file mode 100644 index 0000000..f378821 --- /dev/null +++ b/condition.cpp @@ -0,0 +1,241 @@ +/*! + * \file condition.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of model condition + */ +#include +#include "./condition.h" + +Condition::Condition() { + parentCondition = 0; + isNot = false; + isTime = false; + isValues = true; + isConditions = false; + lhsCondition = 0; + rhsCondition = 0; + lhsIsAgentVariable = false; + rhsIsAgentVariable = false; + lhsIsMessageVariable = false; + rhsIsMessageVariable = false; + lhsIsValue = true; + rhsIsValue = true; + timePeriod = ""; + timePhaseVariable = ""; + timeDuration = 0; + lhs = ""; + op = "=="; + rhs = ""; + enabled = false; + lhsDouble = 0.0; + rhsDouble = 0.0; +} + +Condition::~Condition() { +} + +void Condition::processSymbols() { + if (lhs.startsWith("a.")) { + lhsIsAgentVariable = true; + lhsIsValue = false; + lhs.remove(0, 2); + } else if (lhs.startsWith("m.")) { + lhsIsMessageVariable = true; + lhsIsValue = false; + lhs.remove(0, 2); + } else { + lhsIsValue = true; + lhsDouble = lhs.toDouble(); + } + + if (rhs.startsWith("a.")) { + rhsIsAgentVariable = true; + rhsIsValue = false; + rhs.remove(0, 2); + } else if (rhs.startsWith("m.")) { + rhsIsMessageVariable = true; + rhsIsValue = false; + rhs.remove(0, 2); + } else { + rhsIsValue = true; + rhsDouble = rhs.toDouble(); + } + + if (isTime) { + if (timePhaseVariable.startsWith("a.")) { + timePhaseVariable.remove(0, 2); + timePhaseIsVariable = true; + } else { + timePhaseValue = timePhaseVariable.toInt(); + timePhaseIsVariable = false; + } + } + + if (op == "EQ") op = "=="; + else if (op == "NEQ") op = "!="; + else if (op == "LEQ") op = "<="; + else if (op == "GEQ") op = ">="; + else if (op == "LT") op = "<"; + else if (op == "GT") op = ">"; +} + +void writeCondition(const Condition * c, QString * s) { + Q_ASSERT(c != 0); + + if (c->isNot) { + s->append("not("); + } + if (c->isValues) { + if (c->lhsIsAgentVariable) { + s->append("a."); + s->append(c->lhs); + } else if (c->lhsIsMessageVariable) { + s->append("m."); + s->append(c->lhs); + } else if (c->lhsIsValue) { + s->append(QString().setNum(c->lhsDouble)); + } + s->append(" "); + s->append(c->op); + s->append(" "); + if (c->rhsIsAgentVariable) { + s->append("a."); + s->append(c->rhs); + } else if (c->rhsIsMessageVariable) { + s->append("m."); + s->append(c->rhs); + } else if (c->rhsIsValue) { + s->append(QString().setNum(c->rhsDouble)); + } + } + if (c->isConditions) { + s->append("("); + writeCondition(c->lhsCondition, s); + s->append(") "); + s->append(c->op); + s->append(" ("); + writeCondition(c->rhsCondition, s); + s->append(")"); + } + if (c->isTime) { + s->append("time("); + s->append(c->timePeriod); + s->append(", "); + if (c->timePhaseIsVariable) s->append(c->timePhaseVariable); + else + s->append(QString::number(c->timePhaseValue)); + if (c->timeDuration > 0) { + s->append(", "); + s->append(QString::number(c->timeDuration)); + } + s->append(")"); + } + if (c->isNot) { + s->append(")"); + } +} + +QString Condition::toString() { + QString s; + if (enabled) writeCondition(this, &s); + return s; +} + +void Condition::writeConditionXML(const Condition * c, + QXmlStreamWriter * stream) { + Q_ASSERT(c != 0); + + if (c->isNot) { + stream->writeStartElement("not"); + } + if (c->isValues) { + QString lhsString = ""; + QString rhsString = ""; + QString opString; + + if (c->lhsIsAgentVariable) { + lhsString.append("a."); + lhsString.append(c->lhs); + } else if (c->lhsIsMessageVariable) { + lhsString.append("m."); + lhsString.append(c->lhs); + } else if (c->lhsIsValue) { + lhsString.append(QString().setNum(c->lhsDouble)); + } + + if (c->op == "==") opString = "EQ"; + else if (c->op == "!=") opString = "NEQ"; + else if (c->op == "<=") opString = "LEQ"; + else if (c->op == ">=") opString = "GEQ"; + else if (c->op == "<") opString = "LT"; + else if (c->op == ">") opString = "GT"; + + if (c->rhsIsAgentVariable) { + rhsString.append("a."); + rhsString.append(c->rhs); + } else if (c->rhsIsMessageVariable) { + rhsString.append("m."); + rhsString.append(c->rhs); + } else if (c->rhsIsValue) { + rhsString.append(QString().setNum(c->rhsDouble)); + } + + stream->writeStartElement("lhs"); + stream->writeTextElement("value", lhsString); + stream->writeEndElement(); // lhs + stream->writeTextElement("op", opString); + stream->writeStartElement("rhs"); + stream->writeTextElement("value", rhsString); + stream->writeEndElement(); // rhs + } + if (c->isConditions) { + stream->writeStartElement("lhs"); + writeConditionXML(c->lhsCondition, stream); + stream->writeEndElement(); // lhs + stream->writeTextElement("op", c->op); + stream->writeStartElement("rhs"); + writeConditionXML(c->rhsCondition, stream); + stream->writeEndElement(); // rhs + } + if (c->isTime) { + stream->writeStartElement("time"); + stream->writeTextElement("period", c->timePeriod); + if (c->timePhaseIsVariable) { + QString s = "a."; + s.append(c->timePhaseVariable); + stream->writeTextElement("phase", s); + } else { + stream->writeTextElement("phase", + QString::number(c->timePhaseValue)); + } + if (c->timeDuration > 0) + stream->writeTextElement("duration", + QString::number(c->timeDuration)); + stream->writeEndElement(); // time + } + if (c->isNot) { + stream->writeEndElement(); // not + } +} + +void Condition::toXML(QXmlStreamWriter * stream) { + if (enabled) writeConditionXML(this, stream); +} + +void Condition::paint(QPainter *painter, const QRect &rect, + const QPalette &/*palette*/, EditMode /*mode*/) const { + painter->save(); + + painter->setRenderHint(QPainter::Antialiasing, true); + + if (enabled) { + QString s; + writeCondition(this, &s); + + painter->drawText(rect, s); + } + + painter->restore(); +} diff --git a/condition.h b/condition.h new file mode 100644 index 0000000..e317864 --- /dev/null +++ b/condition.h @@ -0,0 +1,59 @@ +/*! + * \file condition.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for model condition + */ +#ifndef CONDITION_H_ +#define CONDITION_H_ + +#include +#include +#include +#include + +class Condition { + public: + enum EditMode { Editable, ReadOnly }; + + Condition(); + ~Condition(); + + void paint(QPainter *painter, const QRect &rect, + const QPalette &palette, EditMode mode) const; + + void processSymbols(); + QString toString(); + void writeConditionXML(const Condition * c, QXmlStreamWriter * stream); + void toXML(QXmlStreamWriter * stream); + + bool enabled; + bool isNot; + bool isTime; + bool isValues; + bool isConditions; + bool lhsIsAgentVariable; + bool rhsIsAgentVariable; + bool lhsIsValue; + bool rhsIsValue; + bool lhsIsMessageVariable; + bool rhsIsMessageVariable; + double lhsDouble; + double rhsDouble; + QString lhs; + QString op; + QString rhs; + QString timePeriod; + QString timePhaseVariable; + int timePhaseValue; + bool timePhaseIsVariable; + int timeDuration; + Condition * lhsCondition; + Condition * rhsCondition; + Condition * parentCondition; +}; + +Q_DECLARE_METATYPE(Condition) + +#endif // CONDITION_H_ diff --git a/conditiondialog.ui b/conditiondialog.ui new file mode 100644 index 0000000..7e9bd25 --- /dev/null +++ b/conditiondialog.ui @@ -0,0 +1,393 @@ + + + ConditionDialog + + + + 0 + 0 + 793 + 516 + + + + + 0 + 0 + + + + Condition Editor + + + + + + Enabled + + + + + + + + 0 + 0 + + + + + 690 + 0 + + + + true + + + + + + + Value + + + + + + + Value Condition + + + + + + Not + + + + + + + + + + + Agent Variable + + + + + + + + + + + + + + Message Variable + + + + + + + + + + + + + + Value + + + + + + + -999999.989999999990687 + + + 999999.989999999990687 + + + + + + + + + + + + + + + + + + Agent Variable + + + + + + + + + + + + + + Message Variable + + + + + + + + + + + + + + Value + + + + + + + -999999.989999999990687 + + + 999999.989999999990687 + + + + + + + + + + + + + + Time + + + + + + + Time Condition + + + + + + + 0 + 0 + + + + Not + + + + + + + + + Period + + + + + + + Phase + + + + + + + Duration + + + + + + + + + + + + + + Variable + + + + + + + + + + + + + + Value + + + + + + + + + + + + + + + + + + + + + + Nested + + + + + + + Nested Condition + + + + + + Not + + + + + + + + + true + + + + + + + Edit + + + + + + + + + + + + + + true + + + + + + + Edit + + + + + + + + + + + + + + Up a Level + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + ConditionDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ConditionDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/datatypedelegate.cpp b/datatypedelegate.cpp new file mode 100644 index 0000000..ac23054 --- /dev/null +++ b/datatypedelegate.cpp @@ -0,0 +1,49 @@ +/*! + * \file datatypedelegate.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of model data type delegate + */ +#include "./datatypedelegate.h" + +DataTypeDelegate::DataTypeDelegate(Machine * m, QObject * parent) + : QItemDelegate(parent) { + machine = m; +} + + +QWidget *DataTypeDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &/*option*/, + const QModelIndex &/*index*/) const { + QComboBox *editor = new QComboBox(parent); + QStringList dataTypes = machine->getDataTypes(); + for (int i = 0; i < dataTypes.count(); i++) { + editor->insertItem(i, dataTypes.at(i)); + } + return editor; +} + +void DataTypeDelegate::setEditorData(QWidget *editor, + const QModelIndex &index) const { + QString value = index.data().toString(); + + QComboBox *comboBox = static_cast(editor); + for (int i = 0; i < comboBox->count(); i++) { + if (comboBox->itemText(i) == value) + comboBox->setCurrentIndex(i); + } +} + +void DataTypeDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const { + QComboBox *comboBox = static_cast(editor); + QString value = comboBox->currentText(); + + model->setData(index, value, Qt::EditRole); +} + +void DataTypeDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &/*index*/) const { + editor->setGeometry(option.rect); +} diff --git a/datatypedelegate.h b/datatypedelegate.h new file mode 100644 index 0000000..aeaeeda --- /dev/null +++ b/datatypedelegate.h @@ -0,0 +1,34 @@ +/*! + * \file datatypedelegate.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for model datatype delegate + */ +#ifndef DATATYPEDELEGATE_H_ +#define DATATYPEDELEGATE_H_ + +#include +#include "./machine.h" + +class DataTypeDelegate : public QItemDelegate { + Q_OBJECT + + public: + DataTypeDelegate(Machine * m, QObject *parent = 0); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + + void updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const; + + private: + Machine * machine; +}; + +#endif // DATATYPEDELEGATE_H_ diff --git a/flame_editor.pro b/flame_editor.pro new file mode 100644 index 0000000..da0aeaa --- /dev/null +++ b/flame_editor.pro @@ -0,0 +1,161 @@ +# ------------------------------------------------- +# Project created by QtCreator 2009-05-22T10:29:59 +# ------------------------------------------------- +TARGET = flame_editor +TEMPLATE = app +SOURCES += main.cpp \ + mainwindow.cpp \ + machinescene.cpp \ + arrow.cpp \ + machinemodel.cpp \ + memorydelegate.cpp \ + memorymodel.cpp \ + mpre.cpp \ + simulationthread.cpp \ + mpredelegate.cpp \ + mpredialog.cpp \ + mpost.cpp \ + mpostdelegate.cpp \ + mpostdialog.cpp \ + adt.cpp \ + memoryvariable.cpp \ + modelxmlreader.cpp \ + machine.cpp \ + transition.cpp \ + state.cpp \ + variable.cpp \ + statedelegate.cpp \ + machinetree.cpp \ + condition.cpp \ + timeunit.cpp \ + communication.cpp \ + commdelegate.cpp \ + commdialog.cpp \ + messagecomm.cpp \ + messagemodel.cpp \ + messagesort.cpp \ + messagedelegate.cpp \ + sortdelegate.cpp \ + sortdialog.cpp \ + datatypedelegate.cpp \ + lineeditdelegate.cpp \ + machinetreedelegate.cpp \ + timeunitdialog.cpp \ + texteditdelegate.cpp \ + graphicsitem.cpp +HEADERS += mainwindow.h \ + machinescene.h \ + arrow.h \ + machinemodel.h \ + memorydelegate.h \ + memorymodel.h \ + mpre.h \ + simulationthread.h \ + mpredelegate.h \ + mpredialog.h \ + mpost.h \ + mpostdelegate.h \ + mpostdialog.h \ + adt.h \ + memoryvariable.h \ + modelxmlreader.h \ + machine.h \ + transition.h \ + state.h \ + variable.h \ + statedelegate.h \ + machinetree.h \ + condition.h \ + timeunit.h \ + communication.h \ + commdelegate.h \ + commdialog.h \ + messagecomm.h \ + messagemodel.h \ + messagesort.h \ + messagedelegate.h \ + sortdelegate.h \ + sortdialog.h \ + datatypedelegate.h \ + lineeditdelegate.h \ + machinetreedelegate.h \ + timeunitdialog.h \ + texteditdelegate.h \ + graphicsitem.h +FORMS += mainwindow.ui \ + conditiondialog.ui \ + commdialog.ui \ + sortdialog.ui \ + timeunitdialog.ui + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/graphicsitem.cpp b/graphicsitem.cpp new file mode 100644 index 0000000..092e4e8 --- /dev/null +++ b/graphicsitem.cpp @@ -0,0 +1,174 @@ +/*! + * \file graphicsitem.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of graphics item for stategraphs + */ +#include +#include +#include +#include +#include "./graphicsitem.h" + +GraphicsItem::GraphicsItem(QGraphicsItem *parent, QGraphicsScene *scene) + : QGraphicsItem(parent, scene) { + setFlag(QGraphicsItem::ItemIsMovable, true); + setFlag(QGraphicsItem::ItemIsSelectable, true); + + sansFont = new QFont("Helvetica", 12); + foreign = false; +} + +void GraphicsItem::setState(State *s) { + myState = s; + mytype = 0; + setName(s->name()); +} + +void GraphicsItem::setTransition(Transition *t) { + transition = t; + mytype = 1; + setName(t->name()); + setCondition(t->condition()); + preState = t->currentState()->name(); + postState = t->nextState()->name(); +} + +void GraphicsItem::setMessage(MessageComm *m) { + message = m; + mytype = 2; + setName(m->messageType); +} + +QString GraphicsItem::getName() { + return name; +} + +void GraphicsItem::nameChanged() { + if (mytype == 0) setName(myState->name()); + else if (mytype == 1) setName(transition->name()); + else if (mytype == 2) setName(message->messageType); +} + +void GraphicsItem::setName(QString n) { + name = n; + QFontMetrics fm(*sansFont); + nameWidth = fm.width(name) + 10; + nameHeight = fm.height(); + // prepareGeometryChange(); + if (mytype == 2) { // message + myNameRect = QRectF(-(nameWidth/2.0)-5.0, + static_cast(-nameHeight), + static_cast(nameWidth+10.0), nameHeight*2.0); + myPath = QPainterPath(); + myPath.moveTo(myNameRect.bottomLeft()); + myPath.lineTo(myNameRect.bottomRight().x()-10, + myNameRect.bottomRight().y()); + myPath.lineTo(myNameRect.topRight()); + myPath.lineTo(myNameRect.topLeft().x()+10, myNameRect.topLeft().y()); + myPath.lineTo(myNameRect.bottomLeft()); + } else { + myNameRect = QRectF(-nameWidth/2.0, static_cast(-nameHeight), + static_cast(nameWidth), nameHeight*2.0); + } + setBoundingRect(); +} + +void GraphicsItem::setCondition(Condition c) { + condition = c; + QString cond = c.toString(); + QFontMetrics fm(*sansFont); + condWidth = fm.width(cond) + 10; + condHeight = fm.height(); + myConditionRect = QRectF(-condWidth/2.0, + static_cast(-(condHeight*2)-nameHeight), + static_cast(condWidth), condHeight*2.0); + setBoundingRect(); +} + +void GraphicsItem::addTransitionArrow(Arrow *arrow) { + transitionArrows.append(arrow); +} + +void GraphicsItem::addMessageArrow(Arrow *arrow) { + messageArrows.append(arrow); +} + +void GraphicsItem::removeMessageArrow(Arrow *arrow) { + messageArrows.removeOne(arrow); +} + +void GraphicsItem::removeTransitionArrow(Arrow *arrow) { + transitionArrows.removeOne(arrow); +} + +void GraphicsItem::setBoundingRect() { + prepareGeometryChange(); + if (condition.enabled) { + myBoundingRect = QRectF(qMin(myNameRect.x(), + myConditionRect.x()), myConditionRect.y(), + qMax(myNameRect.width(), myConditionRect.width()), +myNameRect.height()+myConditionRect.height()); + } else { + myBoundingRect = myNameRect; + } +} + +/** \fn GraphicsItem::boundingRect() const + * \brief The bounding rect where all painting must be restricted to. Used to determine if the item needs redrawing. + * \return The bounding rect. + */ +QRectF GraphicsItem::boundingRect() const { + qreal adjust = 0.5; + // return QRectF(-20 - adjust, -10 - adjust, 40 + adjust, 20 + adjust); + return myBoundingRect.adjusted(-adjust, -adjust, adjust, adjust); +} + +/** \fn GraphicsItem::shape() const + * \brief The shape of the object used for collision detection, hit tests etc. + * \return The shape. + */ +QPainterPath GraphicsItem::shape() const { + QPainterPath path; + // path.addRect(-20, -10, 40, 20); + // path.addRect(myRect); + if (mytype == 0) path.addEllipse(myNameRect); + if (mytype == 1) path.addRect(myNameRect); + if (mytype == 2) return myPath; + return path; +} + +int GraphicsItem::width() { + return myBoundingRect.width(); +} + +void GraphicsItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, + QWidget *) { + // Body + // painter->setBrush(Qt::black); + painter->setRenderHint(QPainter::Antialiasing); + // if(startState) painter->setBrush(Qt::black); + // else + painter->setBrush(Qt::white); + if (foreign) painter->setPen(Qt::gray); + else + painter->setPen(isSelected() ? Qt::red : Qt::black); + if (mytype == 2) painter->setPen(isSelected() ? Qt::darkGreen : Qt::green); + // painter->drawEllipse(-20, -10, 40, 20); + if (mytype == 0) painter->drawEllipse(myNameRect); + if (mytype == 1) painter->drawRect(myNameRect); + if (mytype == 2) painter->drawPath(myPath); + + // QFont sansFont("Helvetica", fontSize); + + painter->setFont(*sansFont); + painter->drawText(-(nameWidth/2)+5, (nameHeight/2)-2, getName()); + if (condition.enabled) painter->drawText(-(condWidth/2)+5, + (condHeight/2)-2 - nameHeight - condHeight, condition.toString()); + + // Draw bounding box (for development) + /*painter->setPen(Qt::DashLine); + painter->setBrush(Qt::NoBrush); + painter->drawRect(myBoundingRect);*/ +} diff --git a/graphicsitem.h b/graphicsitem.h new file mode 100644 index 0000000..459ad67 --- /dev/null +++ b/graphicsitem.h @@ -0,0 +1,82 @@ +/*! + * \file graphicsitem.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for graphics items in stategraphs + */ +#ifndef GRAPHICSITEM_H_ +#define GRAPHICSITEM_H_ + +#include +#include +#include "./state.h" +#include "./transition.h" +#include "./messagecomm.h" + +class Arrow; + +class GraphicsItem : public QObject, public QGraphicsItem { + Q_OBJECT + #if QT_VERSION >= 0x040600 // If Qt version is 4.6 or higher + Q_INTERFACES(QGraphicsItem) + #endif + + public: + enum { Type = UserType + 15 }; + GraphicsItem(QGraphicsItem *parent = 0, QGraphicsScene *scene = 0); + + QRectF boundingRect() const; + QPainterPath shape() const; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, + QWidget *widget); + QString getName(); + void nameChanged(); + void setCondition(Condition c); + QPolygonF polygon() const + { return myPolygon; } + int type() const + { return Type;} + void addTransitionArrow(Arrow *arrow); + void addMessageArrow(Arrow *arrow); + void removeMessageArrow(Arrow * arrow); + void removeTransitionArrow(Arrow * arrow); + void setState(State * s); + State * state() { return myState; } + void setTransition(Transition * t); + void setMessage(MessageComm * m); + QList getTransitionArrows() const { return transitionArrows; } + QList getMessageArrows() const { return messageArrows; } + int width(); + int layer; + int layered; + bool foreign; + int aveX; + int mytype; // 0-state, 1-transition, 2-message + QString agentName; + QString preState; + QString postState; + Transition * transition; + MessageComm * message; + + private: + void setBoundingRect(); + void setName(QString n); + QPolygonF myPolygon; + QRectF myNameRect; + QRectF myConditionRect; + QRectF myBoundingRect; + QFont * sansFont; + QString name; + QList transitionArrows; + QList messageArrows; + int nameWidth; + int nameHeight; + int condWidth; + int condHeight; + State * myState; + QPainterPath myPath; + Condition condition; +}; + +#endif // GRAPHICSITEM_H_ diff --git a/lineeditdelegate.cpp b/lineeditdelegate.cpp new file mode 100644 index 0000000..cae7c33 --- /dev/null +++ b/lineeditdelegate.cpp @@ -0,0 +1,41 @@ +/*! + * \file lineeditdelegate.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of text line edit delegate + */ +#include +#include "./lineeditdelegate.h" + +LineEditDelegate::LineEditDelegate(QObject * parent) + : QItemDelegate(parent) { +} + +QWidget *LineEditDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &/*option*/, + const QModelIndex &/*index*/) const { + QLineEdit *editor = new QLineEdit(parent); + return editor; +} + +void LineEditDelegate::setEditorData(QWidget *editor, + const QModelIndex &index) const { + QString value = index.data().toString(); + + QLineEdit *lineEdit = static_cast(editor); + lineEdit->setText(value); +} + +void LineEditDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const { + QLineEdit *lineEdit = static_cast(editor); + QString value = lineEdit->text(); + + model->setData(index, value, Qt::EditRole); +} + +void LineEditDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &/*index*/) const { + editor->setGeometry(option.rect); +} diff --git a/lineeditdelegate.h b/lineeditdelegate.h new file mode 100644 index 0000000..ec144d1 --- /dev/null +++ b/lineeditdelegate.h @@ -0,0 +1,30 @@ +/*! + * \file lineeditdelegate.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for text line edit delegate +*/ +#ifndef LINEEDITDELEGATE_H_ +#define LINEEDITDELEGATE_H_ + +#include + +class LineEditDelegate : public QItemDelegate { + Q_OBJECT + + public: + explicit LineEditDelegate(QObject *parent = 0); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + + void updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const; +}; + +#endif // LINEEDITDELEGATE_H_ diff --git a/machine.cpp b/machine.cpp new file mode 100644 index 0000000..a586437 --- /dev/null +++ b/machine.cpp @@ -0,0 +1,628 @@ +/*! + * \file machine.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of model machine + */ +#include +#include "./machine.h" + +Machine::Machine(int t, Machine *parent) { + type = t; + parentItem = parent; + // itemData = data; + location = 0; + enabled = true; + isSubModel = false; + memoryModel = new MemoryModel(); + machineModel = 0; + machineScene = 0; + + if (t == 0) { // model + machineModel = 0; + machineScene = new MachineScene(this); + machineScene->setSceneRect(0, 0, 500, 500); + + memoryModel->setIsModel(true); + memoryModel->addVariable("string", "version", ""); + memoryModel->addVariable("string", "author", ""); + memoryModel->addVariable("string", "description", ""); + } else if (t == 1) { // agent + machineModel = new MachineModel(memoryModel); + machineScene = new MachineScene(this); + machineScene->setSceneRect(0, 0, 500, 500); + machineScene->setMachineModel(machineModel); + + QObject::connect(machineModel, SIGNAL(updateStateName(State*)), + machineScene, SLOT(updateStateName(State*))); + QObject::connect(machineModel, + SIGNAL(updateTransitionName(Transition*)), + machineScene, SLOT(updateTransitionName(Transition*))); + QObject::connect(machineModel, SIGNAL(updateInput(Transition*)), + machineScene, SLOT(updateInput(Transition*))); + QObject::connect(machineModel, SIGNAL(updateOutput(Transition*)), + machineScene, SLOT(updateOutput(Transition*))); + } +} + +Machine::~Machine() { + // If not root model then remove from parent childItems + if (type != -1) { + parent()->removeChild(this); + } + + qDeleteAll(childItems); + delete memoryModel; + if (machineModel != 0) delete machineModel; + if (machineScene != 0) delete machineScene; +} + +void Machine::removeChild(Machine *m) { + childItems.removeAt(childItems.indexOf(m)); +} + +void Machine::insertChild(Machine *m, int index) { + childItems.insert(index, m); +} + +/*void Machine::setName(QString n) +{ + //myName = n; + //machineItem->setText(0, n); +}*/ + +void Machine::addTransitionString(QString fname, QString cs, QString ns, + Condition c, Mpost mpost, Communication input, Communication output, + QString desc) { + Transition * t = + machineModel->addTransitionString(fname, cs, ns, c, + mpost, input, output, desc); + // machineScene->addTransitionString(name, cs, fname, ns, 0); + machineScene->addTransitionTransition(name, t); +} + +bool Machine::writeModelXML(QFile * file) { + QXmlStreamWriter stream(file); + stream.setAutoFormatting(true); + stream.writeStartDocument(); + + stream.writeStartElement("xmodel"); + stream.writeAttribute("version", "2"); + stream.writeAttribute("xmlns:xsi", + "http://www.w3.org/2001/XMLSchema-instance"); + stream.writeAttribute("xsi:noNamespaceSchemaLocation", + "http://flame.ac.uk/schema/xmml_v2.xsd"); + + stream.writeTextElement("name", name); + stream.writeTextElement(memoryModel->variables[0].name, + memoryModel->variables[0].description); // version + stream.writeTextElement(memoryModel->variables[1].name, + memoryModel->variables[1].description); // author + stream.writeTextElement(memoryModel->variables[2].name, + memoryModel->variables[2].description); // description + + bool hasSubModels = false; + for (int i = 0; i < childItems.size(); i++) { + if (childItems[i]->type == 0) hasSubModels = true; + } + // Sub models + if (hasSubModels) { + stream.writeStartElement("models"); + for (int i = 0; i < childItems.size(); i++) { + if (childItems[i]->type == 0) { + stream.writeStartElement("model"); + + QDir dir(fileDirectory); + QString s = dir.canonicalPath(); + QDir dir2(s); + s = dir2.relativeFilePath(childItems[i]->filePath()); + stream.writeTextElement("file", s); + + if (childItems[i]->enabled) stream.writeTextElement( + "enabled", "true"); + else + stream.writeTextElement("enabled", "false"); + stream.writeEndElement(); // model + } + } + stream.writeEndElement(); // models + } + // Environment + Machine * env = 0; + for (int i = 0; i < childItems.size(); i++) + if (childItems[i]->type == 3) env = childItems[i]; + Q_ASSERT(env != 0); + stream.writeStartElement("environment"); + if (env->memoryModel->variables.size() > 0) { + stream.writeStartElement("constants"); + for (int i = 0; i < env->memoryModel->variables.size(); i++) { + stream.writeStartElement("variable"); + stream.writeTextElement("type", + env->memoryModel->variables[i].type); + stream.writeTextElement("name", + env->memoryModel->variables[i].name); + stream.writeTextElement("description", + env->memoryModel->variables[i].description); + stream.writeEndElement(); // variable + } + stream.writeEndElement(); // constants + } + + bool hasFunctionFiles = false; + bool hasDataTypes = false; + bool hasTimeUnits = false; + for (int i = 0; i < env->childItems.size(); i++) { + if (env->childItems[i]->type == 6) hasFunctionFiles = true; + if (env->childItems[i]->type == 5) hasDataTypes = true; + if (env->childItems[i]->type == 4) hasTimeUnits = true; + } + + if (hasFunctionFiles) { + stream.writeStartElement("functionFiles"); + for (int i = 0; i < env->childItems.size(); i++) { + if (env->childItems[i]->type == 6) { + stream.writeTextElement("file", env->childItems[i]->fileName); + } + } + stream.writeEndElement(); // functionFiles + } + if (hasTimeUnits) { + stream.writeStartElement("timeUnits"); + for (int i = 0; i < env->childItems.size(); i++) { + if (env->childItems[i]->type == 4) { + stream.writeStartElement("timeUnit"); + stream.writeTextElement("name", env->childItems[i]-> + myTimeUnit.name); + stream.writeTextElement("unit", env->childItems[i]-> + myTimeUnit.unit); + stream.writeTextElement("period", QString::number( + env->childItems[i]->myTimeUnit.period)); + stream.writeEndElement(); // timeUnit + } + } + stream.writeEndElement(); // timeUnits + } + if (hasDataTypes) { + stream.writeStartElement("dataTypes"); + for (int i = 0; i < env->childItems.size(); i++) { + if (env->childItems[i]->type == 5) { + stream.writeStartElement("dataType"); + stream.writeTextElement("name", env->childItems[i]->name); + stream.writeTextElement("description", env->childItems[i]-> + description); + stream.writeStartElement("variables"); + for (int j = 0; j < env->childItems[i]->memoryModel-> + variables.size(); j++) { + stream.writeStartElement("variable"); + stream.writeTextElement("type", env->childItems[i]-> + memoryModel->variables[j].type); + stream.writeTextElement("name", env->childItems[i]-> + memoryModel->variables[j].name); + stream.writeTextElement("description", env->childItems[i]-> + memoryModel->variables[j].description); + stream.writeEndElement(); // variable + } + stream.writeEndElement(); // variables + stream.writeEndElement(); // dataType + } + } + stream.writeEndElement(); // dataTypes + } + stream.writeEndElement(); // environment + stream.writeStartElement("agents"); + for (int i = 0; i < childItems.size(); i++) { + if (childItems[i]->type == 1) { + stream.writeStartElement("xagent"); + stream.writeTextElement("name", childItems[i]->name); + stream.writeTextElement("description", childItems[i]->description); + stream.writeStartElement("memory"); + for (int j = 0; j < childItems[i]->memoryModel->variables.size(); + j++) { + stream.writeStartElement("variable"); + stream.writeTextElement("type", childItems[i]->memoryModel-> + variables[j].type); + stream.writeTextElement("name", childItems[i]->memoryModel-> + variables[j].name); + if (childItems[i]->memoryModel->variables[j].constant) + stream.writeTextElement("constant", "true"); + stream.writeTextElement("description", childItems[i]-> + memoryModel->variables[j].description); + stream.writeEndElement(); // variable + } + stream.writeEndElement(); // memory + stream.writeStartElement("functions"); + for (int j = 0; j < childItems[i]->machineModel-> + transitions.size(); j++) { + stream.writeStartElement("function"); + stream.writeTextElement("name", childItems[i]->machineModel-> + transitions[j]->name()); + stream.writeTextElement("description", childItems[i]-> + machineModel->transitions[j]->description()); + stream.writeTextElement("currentState", childItems[i]-> + machineModel->transitions[j]->currentState()->name()); + stream.writeTextElement("nextState", childItems[i]-> + machineModel->transitions[j]->nextState()->name()); + // condition + if (childItems[i]->machineModel->transitions[j]-> + condition().enabled) { + stream.writeStartElement("condition"); + childItems[i]->machineModel->transitions[j]-> + condition().toXML(&stream); + stream.writeEndElement(); // condition + } + if (childItems[i]->machineModel->transitions[j]-> + input().messageModel->messages.size() > 0) { + stream.writeStartElement("inputs"); + for (int k = 0; k < childItems[i]->machineModel-> + transitions[j]->input().messageModel->messages.size(); + k++) { + MessageComm message = childItems[i]->machineModel-> + transitions[j]->input().messageModel-> + messages[k]; + stream.writeStartElement("input"); + stream.writeTextElement("messageName", + message.messageType); + // random + if (message.sort.isRandom) { + stream.writeTextElement("random", "true"); + } + // filter + if (message.filter.enabled) { + stream.writeStartElement("filter"); + message.filter.toXML(&stream); + stream.writeEndElement(); // filter + } + // sort + if (message.sort.isSort) { + stream.writeStartElement("sort"); + stream.writeTextElement("key", message.sort.key); + stream.writeTextElement("order", + message.sort.order); + stream.writeEndElement(); // sort + } + stream.writeEndElement(); // input + } + stream.writeEndElement(); // inputs + } + if (childItems[i]->machineModel->transitions[j]-> + output().messageModel->messages.size() > 0) { + stream.writeStartElement("outputs"); + for (int k = 0; + k < childItems[i]->machineModel->transitions[j]-> + output().messageModel->messages.size(); k++) { + MessageComm message = + childItems[i]->machineModel->transitions[j]-> + output().messageModel->messages[k]; + stream.writeStartElement("output"); + stream.writeTextElement("messageName", + message.messageType); + stream.writeEndElement(); // output + } + stream.writeEndElement(); // outputs + } + stream.writeEndElement(); // function + } + stream.writeEndElement(); // functions + stream.writeEndElement(); // xagent + } + } + stream.writeEndElement(); // agents + stream.writeStartElement("messages"); + for (int i = 0; i < childItems.size(); i++) { + if (childItems[i]->type == 2) { + stream.writeStartElement("message"); + stream.writeTextElement("name", childItems[i]->name); + stream.writeTextElement("description", childItems[i]->description); + stream.writeStartElement("variables"); + for (int j = 0; j < childItems[i]->memoryModel->variables.size(); + j++) { + stream.writeStartElement("variable"); + stream.writeTextElement("type", + childItems[i]->memoryModel->variables[j].type); + stream.writeTextElement("name", + childItems[i]->memoryModel->variables[j].name); + stream.writeTextElement("description", + childItems[i]->memoryModel->variables[j].description); + stream.writeEndElement(); // variable + } + stream.writeEndElement(); // variables + stream.writeEndElement(); // message + } + } + stream.writeEndElement(); // messages + +/* stream.writeStartElement("memory"); + for(int i = 0; i < this->memoryModel->rowCount(); i++) + { + stream.writeStartElement("variable"); + stream.writeTextElement("type", memoryModel->variables[i].type);// memoryModel->getTypes().at(i)); + stream.writeTextElement("name", memoryModel->variables[i].name);//getNames().at(i)); + if(memoryModel->variables[i].type == "int") + stream.writeTextElement("value", QString::number(memoryModel->variables[i].ivalue));//getintValues().at(i))); + if(memoryModel->variables[i].type == "double") + stream.writeTextElement("value", QString::number(memoryModel->variables[i].dvalue));//getdoubleValues().at(i))); + stream.writeEndElement(); // variable + } + stream.writeEndElement(); // memory + + stream.writeStartElement("transitions"); + for(int i = 0; i < this->machineModel->getTransitions().size(); i++) + { + Transition * t = machineModel->getTransitions().at(i); + + stream.writeStartElement("transition"); + stream.writeTextElement("currentState", t->currentState()->name()); + stream.writeTextElement("input", ""); + + Mpre mpre = t->mpre(); + stream.writeStartElement("mpre"); + if(mpre.enabled()) + { + stream.writeTextElement("enabled", "true"); + if(mpre.isNot()) stream.writeTextElement("not", "true"); + else stream.writeTextElement("not", "false"); + stream.writeTextElement("name", mpre.name()); + stream.writeTextElement("op", mpre.op()); + stream.writeTextElement("value", QString::number( mpre.value() )); + } + else + { + stream.writeTextElement("enabled", "false"); + stream.writeTextElement("not", "false"); + stream.writeTextElement("name", ""); + stream.writeTextElement("op", ""); + stream.writeTextElement("value", 0); + } + stream.writeEndElement(); // mpre + + stream.writeTextElement("name", t->name()); + + Mpost mpost = t->mpost(); + stream.writeStartElement("mpost"); + if(mpost.enabled()) + { + stream.writeTextElement("enabled", "true"); + stream.writeTextElement("name", mpost.name()); + stream.writeTextElement("op", mpost.op()); + //stream.writeTextElement("value", QString::number( mpost.value() )); + stream.writeTextElement("name", mpost.name2()); + } + else + { + stream.writeTextElement("enabled", "false"); + stream.writeTextElement("name", ""); + stream.writeTextElement("op", ""); + stream.writeTextElement("value", 0); + } + stream.writeEndElement(); // mpost + + stream.writeTextElement("output", ""); + stream.writeTextElement("nextState", t->nextState()->name()); + stream.writeEndElement(); // transition + } + stream.writeEndElement(); // transitions +*/ + stream.writeEndElement(); // xmodel + + stream.writeEndDocument(); + file->close(); + if (file->error()) { + return false; + } + + location = file; + // this->setText(1, location->fileName()); + + return true; +} + +void Machine::appendChild(Machine *item) { + childItems.append(item); +} + +Machine *Machine::child(int row) { + return reinterpret_cast(childItems.value(row)); +} + +int Machine::childCount() const { + return childItems.count(); +} + +int Machine::columnCount() const { + return 2; // itemData.count(); +} + +QVariant Machine::data(int column) const { + // return itemData.value(column); + if (type == 0) { + if (column == 0) return qVariantFromValue(QString("model")); + else if (column == 1) return qVariantFromValue(name); + } else if (type == 1) { + if (column == 0) return qVariantFromValue(QString("agent")); + else if (column == 1) return qVariantFromValue(name); + else if (column == 2) return qVariantFromValue(description); + } else if (type == 2) { + if (column == 0) return qVariantFromValue(QString("message")); + else if (column == 1) return qVariantFromValue(name); + else if (column == 2) return qVariantFromValue(description); + } else if (type == 3) { + if (column == 0) return qVariantFromValue(QString("environment")); + else if (column == 1) return qVariantFromValue(QString("")); + } else if (type == 4) { + if (column == 0) return qVariantFromValue(QString("time unit")); + else if (column == 1) return qVariantFromValue(timeUnit().toString()); + } else if (type == 5) { + if (column == 0) return qVariantFromValue(QString("data type")); + else if (column == 1) return qVariantFromValue(name); + else if (column == 2) return qVariantFromValue(description); + } else if (type == 6) { + if (column == 0) return qVariantFromValue(QString("function file")); + else if (column == 1) return qVariantFromValue(name); + } + return QVariant(); +} + +Machine *Machine::parent() { + return parentItem; +} + +int Machine::row() const { + if (parentItem) + return parentItem->childItems.indexOf(const_cast(this)); + + return 0; +} + +QStringList Machine::getTimeUnits(bool b) { + QStringList list; + + // If first call then find root model + if (b) { + list.append(myRootModel->getTimeUnits(false)); + list.prepend("iteration"); + list = list.toSet().toList(); // Remove duplicates + list.removeOne(timeUnit().name); // Remove this time unit + } else { + if (type == 0 || type == 3) { // model + for (int i = 0; i < childCount(); i++) { + if (child(i)->type == 4) { // time unit + if (!list.contains(child(i)->timeUnit().name)) + list.append(child(i)->timeUnit().name); + } else if (child(i)->type == 0 || child(i)->type == 3) { + // model or environment + list.append(child(i)->getTimeUnits(false)); + } + } + } + } + + return list; +} + +QStringList Machine::getMessageNames(bool b) { + QStringList list; + + // If first call then find root model + if (b) { + list.append(myRootModel->getMessageNames(false)); + } else { + if (type == 0) { // model + for (int i = 0; i < childCount(); i++) { + if (child(i)->type == 2) { // message + list.append(child(i)->name); + } else if (child(i)->type == 0) { // model + list.append(child(i)->getMessageNames(false)); + } + } + } + } + + return list; +} + +QStringList Machine::getAgentMemoryNames(QString n, bool b) { + QStringList list; + + // If first call then find root model + if (b) { + list.append(myRootModel->getAgentMemoryNames(n, false)); + } else { + // Add this agent memory if called n + if (type == 1 && name == n) { + list.append(memoryModel->getNames()); + } + if (type == 0) { // model + for (int i = 0; i < childCount(); i++) { + list.append(child(i)->getAgentMemoryNames(n, false)); + } + } + } + + return list; +} + +QStringList Machine::getMessageMemoryNames(QString n, bool b) { + QStringList list; + + // If first call then find root model + if (b) { + list.append(myRootModel->getMessageMemoryNames(n, false)); + } else { + // Add this message memory if called n + if (type == 2 && name == n) { + list.append(memoryModel->getNames()); + } + if (type == 0) { // model + for (int i = 0; i < childCount(); i++) { + list.append(child(i)->getMessageMemoryNames(n, false)); + } + } + } + + return list; +} + +QStringList Machine::getDataTypes(bool b) { + QStringList list; + + // If first call then find root model + if (b) { + list.append("int"); + list.append("float"); + list.append("double"); + list.append("char"); + list.append(myRootModel->getDataTypes(false)); + if (type != 2 || type != 3) { // not message or environment + // For every data type add dynamic array version + int listSize = list.size(); + for (int i = 0; i < listSize; i++) { + QString n = list.at(i); + n.append("_array"); + list.append(n); + } + } + } else { + if (type == 0 || type == 3) { // model + for (int i = 0; i < childCount(); i++) { + if (child(i)->type == 5) { // datatype + list.append(child(i)->name); // memoryModel->getNames()); + } else if (child(i)->type == 0 || child(i)->type == 3) { + // model or environment + list.append(child(i)->getDataTypes(false)); + } + } + } + } + + return list; +} + +Machine * Machine::getMessageType(QString n, bool b) { + Machine * m = 0; + + if (b) { + myRootModel = getMessageType(n, false); + } else { + // Add this agent memory if called n + if (type == 2 && name == n) { + return this; + } + if (type == 0) { // model + for (int i = 0; i < childCount(); i++) { + Machine * m2 = child(i)->getMessageType(n, false); + if (m2) return m2; + } + } + } + + return m; +} + +QString Machine::filePath() { + QString filepath; + filepath.append(fileDirectory); + filepath.append("/"); + filepath.append(fileName); + return filepath; +} diff --git a/machine.h b/machine.h new file mode 100644 index 0000000..552b7b0 --- /dev/null +++ b/machine.h @@ -0,0 +1,88 @@ +/*! + * \file machine.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for model machine +*/ +#ifndef MACHINE_H_ +#define MACHINE_H_ + +#include +#include +#include +#include "./machinemodel.h" +#include "./memorymodel.h" +#include "./machinescene.h" +#include "./timeunit.h" + +class MachineItem; + +class Machine { + public: + Machine(int type, Machine *parent = 0); + ~Machine(); + + bool writeModelXML(QFile * file); + + void appendChild(Machine *child); + + Machine *child(int row); + int childCount() const; + int columnCount() const; + QVariant data(int column) const; + int row() const; + Machine *parent(); + void removeChild(Machine * m); + void insertChild(Machine * m, int index); + void addTransitionString(QString name, QString cs, QString ns, + Condition c, Mpost mpost, + Communication input, Communication output, QString desc); + + QStringList getTimeUnits(bool b = true); + QStringList getAgentMemoryNames(QString name, bool b = true); + QStringList getMessageMemoryNames(QString name, bool b = true); + QStringList getMessageNames(bool b = true); + QStringList getDataTypes(bool b = true); + Machine * getMessageType(QString name, bool = true); + bool isEnabled() { return enabled; } + void setEnabled(bool b) { enabled = b; } + Machine * rootModel() { return myRootModel; } + void setRootModel(Machine * m) { myRootModel = m; } + void setTimeUnit(TimeUnit t) { myTimeUnit = t; } + TimeUnit timeUnit() const { return myTimeUnit; } + QString filePath(); + void setTimeUnitName(QString s) { myTimeUnit.name = s; } + + MachineModel * machineModel; + MemoryModel * memoryModel; + MachineScene * machineScene; + + QStringList timeUnitNames; + QString name; + QString description; + /* -1-rootItem + * 0-model + * 1-agent + * 2-message + * 3-environment + * 4-timeunit + * 5-datatype + * 6-functionFiles + */ + int type; + bool isSubModel; + QString fileDirectory; + QString fileName; + QList childItems; + + private: + QList itemData; + Machine *parentItem; + QFile * location; + bool enabled; + Machine * myRootModel; + TimeUnit myTimeUnit; +}; + +#endif // MACHINE_H_ diff --git a/machinemodel.cpp b/machinemodel.cpp new file mode 100644 index 0000000..03dfc23 --- /dev/null +++ b/machinemodel.cpp @@ -0,0 +1,312 @@ +/*! + * \file machinemodel.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of machine transition function table + */ +#include +#include +#include "./machinemodel.h" + +State * MachineModel::addState(QString name) { + /*states.append(s); + if(s->getStartState()) startState = s;*/ + // qDebug() << "machine addState " << s->getName(); + + State * s = new State(name); + // if(start) startState = s; + states.append(s); + return s; +} + +Transition * MachineModel::addTransition(QString name, State *current, + State *next) { + // qDebug() << cs << " " << name << " " << ns; + +// State * pcs = 0; +// State * pns = 0; + + this->insertRows(0, 1); + + // Search for and add states +/* for(int i = 0; i < states.size(); i ++) + { + State * s = states.at(i); + if(s->name() == t->preState) pcs = s; + if(s->name() == t->postState) pns = s; + } + + if(pcs == 0) + { + State * s = new State(t->preState); + states.append(s); + pcs = s; + } + if(pns == 0) + { + State * s = new State(t->postState); + states.append(s); + pns = s; + } +*/ + Transition * transition = new Transition(current, name, next); + transitions.append(transition); +// t->transition = transition; + return transition; + + // connect(a, SIGNAL(updateMpre(Arrow*)), this, + // SLOT(handleUpdatedMpre(Arrow*))); + + /*transitionTable->insertRow(0); + transitionTable->setItem(0,0,new StateTableItem(a->startItem()));//new QTableWidgetItem(a->startItem()->getName())); + transitionTable->setItem(0,3,new QTableWidgetItem(a->getName())); + transitionTable->setItem(0,6,new StateTableItem(a->endItem()));//new QTableWidgetItem(a->endItem()->getName()));*/ + + // qDebug() << "machine addTransition " << t->name() << + // " " << pcs->name() << " -> " << pns->name(); +} + +Transition * MachineModel::addTransitionString(QString n, QString cs, + QString ns, Condition c, Mpost mpost, + Communication input, Communication output, QString desc) { + State * pcs = 0; + State * pns = 0; + + this->insertRows(0, 1); + + // Search for and add states + for (int i = 0; i < states.size(); i ++) { + State * s = states.at(i); + if (s->name() == cs) pcs = s; + if (s->name() == ns) pns = s; + } + + if (pcs == 0) { + State * s = new State(cs); + states.append(s); + pcs = s; + } + if (pns == 0) { + State * s = new State(ns); + states.append(s); + pns = s; + } + + Transition * t = new Transition(pcs, n, pns); + // t->setMpre(mpre); + t->setCondition(c); + t->setMpost(mpost); + t->setInput(input); + t->setOutput(output); + t->setDescription(desc); + transitions.append(t); + + return t; +} + +int MachineModel::rowCount(const QModelIndex &/*parent*/) const { + return transitions.count(); +} + +int MachineModel::columnCount(const QModelIndex &/*parent*/) const { + return 8; +} + +QVariant MachineModel::data(const QModelIndex &index, int role) const { + if (!index.isValid()) + return QVariant(); + + if (index.row() >= transitions.size()) + return QVariant(); + + if (role == Qt::DisplayRole) { + if (index.column() == 0) return transitions.at( + index.row())->currentState()->name(); + if (index.column() == 1) return qVariantFromValue( + transitions.at(index.row())->input()); + if (index.column() == 2) return qVariantFromValue( + transitions.at(index.row())->condition()); + if (index.column() == 3) return transitions.at( + index.row())->name(); + if (index.column() == 4) return qVariantFromValue( + transitions.at(index.row())->mpost()); + if (index.column() == 5) return qVariantFromValue( + transitions.at(index.row())->output()); + if (index.column() == 6) return transitions.at( + index.row())->nextState()->name(); + if (index.column() == 7) return transitions.at( + index.row())->description(); + return QVariant(); + } + return QVariant(); +} + +QVariant MachineModel::headerData(int section, Qt::Orientation orientation, + int role) const { + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) { + if (section == 0) return QString("Current State"); + else if (section == 1) return QString("Input"); + else if (section == 2) return QString("Condition"); + else if (section == 3) return QString("Function Name"); + else if (section == 4) return QString("Mpost"); + else if (section == 5) return QString("Output"); + else if (section == 6) return QString("Next State"); + else if (section == 7) return QString("Description"); + else + return QString("Row %1").arg(section); + } else { + return QString("Row %1").arg(section); + } +} + +Qt::ItemFlags MachineModel::flags(const QModelIndex &index) const { + if (!index.isValid()) + return Qt::ItemIsEnabled; + + if (index.column() == 4) return QAbstractItemModel::flags(index); + return QAbstractItemModel::flags(index) | Qt::ItemIsEditable; +} + +bool MachineModel::setData(const QModelIndex &index, + const QVariant &value, int role) { + if (index.isValid() && role == Qt::EditRole) { + if (index.column() == 0) { + transitions.at(index.row())->currentState()-> + setName(value.toString()); + emit(updateStateName(transitions.at(index.row())->currentState())); + } + if (index.column() == 1) { + transitions.at(index.row())-> + setInput(qVariantValue(value)); + emit(updateInput(transitions.at(index.row()))); + } + if (index.column() == 2) transitions.at(index.row())-> + setCondition(qVariantValue(value)); + if (index.column() == 3) { + transitions.at(index.row())->setName(value.toString()); + emit(updateTransitionName(transitions.at(index.row()))); + } + if (index.column() == 4) transitions.at(index.row())-> + setMpost(qVariantValue(value)); + if (index.column() == 5) { + transitions.at(index.row())-> + setOutput(qVariantValue(value)); + emit(updateOutput(transitions.at(index.row()))); + } + if (index.column() == 6) { + transitions.at(index.row())->nextState()-> + setName(value.toString()); + emit(updateStateName(transitions.at(index.row())->nextState())); + } + if (index.column() == 7) transitions.at(index.row())-> + setDescription(value.toString()); + emit dataChanged(index, index); + return true; + } + return false; +} + +void MachineModel::addMessageToTransition(Transition * t, bool isInput, + QString messageType) { + if (isInput) { + t->input().messageModel->addMessage(messageType); + emit dataChanged(index(transitions.indexOf(t), 1, QModelIndex()), + index(transitions.indexOf(t), 1, QModelIndex())); + } else { // output + t->output().messageModel->addMessage(messageType); + emit dataChanged(index(transitions.indexOf(t), 5, QModelIndex()), + index(transitions.indexOf(t), 5, QModelIndex())); + } + emit(communicationChanged()); +} + +bool MachineModel::insertRows(int position, int rows, + const QModelIndex &/*parent*/) { + beginInsertRows(QModelIndex(), position, position+rows-1); + + for (int row = 0; row < rows; ++row) { + /*stringListCurrentState.insert(position, ""); + stringListInput.insert(position, ""); + stringListMpre.insert(position, ""); + stringListFunctionName.insert(position, ""); + stringListMpost.insert(position, ""); + stringListOutput.insert(position, ""); + stringListNextState.insert(position, "");*/ + } + + endInsertRows(); + return true; +} + +bool MachineModel::removeRows(int position, int rows, + const QModelIndex &/*parent*/) { + beginRemoveRows(QModelIndex(), position, position+rows-1); + + for (int row = position; row < position+rows; row++) { + transitions.removeAt(row); + } + + endRemoveRows(); + return true; +} + +/*void MachineModel::setStartStateString(QString n) +{ + // Check to see if state exists + startState = 0; + + // Check if states exist + for(int i = 0; i < states.size(); i++) + { + states.at(i)->setStartState(false); + if(states.at(i)->name() == n) + { + startState = states.at(i); + states.at(i)->setStartState(true); + } + } + if(startState == 0) + { + this->addState(n, true); + } +}*/ + +void MachineModel::transitionUpdated(QModelIndex topLeft, + QModelIndex /*bottomRight*/) { + // ui->label->setText(QString( + // "transitionChanged r: %1 c: %2").arg(topLeft.row(), topLeft.column())); + + // emit( updateScene() ); + + // If mpre was updated + if (topLeft.column() == 2) { + // If another branch from the same state exists + Transition * t = transitions.at(topLeft.row()); + for (int i = 0; i < transitions.size(); i++) { + Transition * t2 = transitions.at(i); + if (t->currentState() == t2->currentState() && + i != topLeft.row()) { + // qDebug() << "Found other branch: " << t2->name(); + t2->getMprePointer()->setNot(false); + t2->getMprePointer()->setEnabled(t->mpre().enabled()); + t2->getMprePointer()->setName(t->mpre().name()); + if (t->mpre().op() == "==") t2->getMprePointer()->setOp("!="); + if (t->mpre().op() == "!=") t2->getMprePointer()->setOp("=="); + if (t->mpre().op() == ">") t2->getMprePointer()->setOp("<="); + if (t->mpre().op() == "<") t2->getMprePointer()->setOp(">="); + if (t->mpre().op() == ">=") t2->getMprePointer()->setOp("<"); + if (t->mpre().op() == "<=") t2->getMprePointer()->setOp(">"); + t2->getMprePointer()->setValue(t->mpre().value()); + } + } + } +} + +void MachineModel::deleteTransition(Transition *t) { + removeRows(transitions.indexOf(t), 1); +} + diff --git a/machinemodel.h b/machinemodel.h new file mode 100644 index 0000000..c220f22 --- /dev/null +++ b/machinemodel.h @@ -0,0 +1,67 @@ +/*! + * \file machinemodel.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for model machine function transition table +*/ +#ifndef MACHINEMODEL_H_ +#define MACHINEMODEL_H_ + +#include +#include "./memorymodel.h" +#include "./mpre.h" +#include "./mpost.h" +#include "./transition.h" +#include "./state.h" + +class MachineModel : public QAbstractTableModel { + Q_OBJECT + + public: + MachineModel(MemoryModel *m, QObject *parent = 0) + : QAbstractTableModel(parent) { memory = m; } + + Transition * addTransitionString(QString name, QString cs, + QString ns, Condition c, Mpost mpost, + Communication input, Communication output, QString desc); + void setStartStateString(QString n); + void deleteTransition(Transition * t); + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + /* Functions to provide editing */ + Qt::ItemFlags flags(const QModelIndex &index) const; + bool setData(const QModelIndex &index, const QVariant &value, + int role = Qt::EditRole); + /* Adding and removing rows */ + bool insertRows(int position, int rows, + const QModelIndex &index = QModelIndex()); + bool removeRows(int position, int rows, + const QModelIndex &index = QModelIndex()); + QList getStates() const { return states; } + QList getTransitions() const { return transitions; } + Transition * addTransition(QString name, State * current, State * next); + State * addState(QString name); + void addMessageToTransition(Transition * t, + bool isInput, QString messageType); + QList transitions; + + signals: + void updateStateName(State * s); + void updateTransitionName(Transition * t); + void communicationChanged(); + void updateInput(Transition * t); + void updateOutput(Transition * t); + + public slots: + void transitionUpdated(QModelIndex topLeft, QModelIndex bottomRight); + + private: + MemoryModel * memory; + QList states; +}; + +#endif // MACHINEMODEL_H_ diff --git a/machinescene.cpp b/machinescene.cpp new file mode 100644 index 0000000..6cee2a8 --- /dev/null +++ b/machinescene.cpp @@ -0,0 +1,1080 @@ +/*! + * \file machinescene.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of the graphics Scene for the stategraph + */ +#include +#include "./machinescene.h" +#include "./machine.h" + +MachineScene::MachineScene(Machine * m, QObject *parent) + : QGraphicsScene(parent) { + myMode = MoveItem; // InsertItem; + line = 0; + num_states = 0; + num_transitions = 0; + num_messages = 0; + rootMachine = m; + mytype = 0; + zoomOn = false; + myGraphicsView = 0; + scaleFactor = 1.15; // How fast we zoom + selectedFunction = 0; +} + +void MachineScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent) { + GraphicsItem * startItem = 0; + GraphicsItem * endItem = 0; + + if (line != 0) { + QList startItems = items(line->line().p1()); + if (startItems.count() && startItems.first() == line) + startItems.removeFirst(); + QList endItems = items(line->line().p2()); + if (endItems.count() && endItems.first() == line) + endItems.removeFirst(); + + removeItem(line); + delete line; + + if (startItems.count() > 0 && + startItems.first()->type() == GraphicsItem::Type) + startItem = qgraphicsitem_cast(startItems.first()); + if (endItems.count() > 0 && + endItems.first()->type() == GraphicsItem::Type) + endItem = qgraphicsitem_cast(endItems.first()); + + // If start = state and no end item + if (startItem->mytype == 0 && endItem == 0) { + QString s; + s.append("state_"); + s.append(QString("%1").arg(num_states)); + num_states++; + + State * state = machineModel->addState(s); + + endItem = new GraphicsItem; + endItem->setState(state); + endItem->agentName = startItem->agentName; + addItem(endItem); + endItem->setPos(mouseEvent->scenePos()); + // emit( this->addedState(s) ); + statesAndTransitions.append(endItem); + } + // If start = transition and no end item + if (startItem->mytype == 1 && endItem == 0) { + /*endItem = new GraphicsItem; + QString s; + s.append("message_"); + s.append(QString("%1").arg(num_messages)); + num_messages++; + endItem->mytype = 2; + endItem->setName(s); + //endItem->agentName = startItem->agentName; + addItem(endItem); + endItem->setPos(mouseEvent->scenePos()); + messages.append(endItem);*/ + } + + if (startItem != 0 && endItem != 0 && startItem != endItem) { + // If both states from same agent + if (startItem->mytype == 0 && endItem->mytype == 0 && + startItem->agentName == endItem->agentName) { + startItem->foreign = false; startItem->update(); + endItem->foreign = false; endItem->update(); + + QString s = "transition_"; + s.append(QString("%1").arg(num_transitions)); + + Transition * t = machineModel->addTransition(s, + startItem->state(), endItem->state()); + + GraphicsItem * fitem = new GraphicsItem; + fitem->setTransition(t); + fitem->agentName = startItem->agentName; + addTransition(fitem); + fitem->setPos((startItem->x() + endItem->x())/2.0, + (startItem->y() + endItem->y())/2.0); + + Arrow * arrow = new Arrow(startItem, fitem); + startItem->addTransitionArrow(arrow); + fitem->addTransitionArrow(arrow); + arrow->drawHead(false); + addArrow(arrow); + arrow->updatePosition(); + + arrow = new Arrow(fitem, endItem); + fitem->addTransitionArrow(arrow); + endItem->addTransitionArrow(arrow); + addArrow(arrow); + arrow->updatePosition(); + + // emit( this->addedTransition(fitem) ); + } + + // If start = transition and end = message + if (startItem->mytype == 1 && endItem->mytype == 2) { + Arrow * arrow = new Arrow(startItem, endItem); + startItem->addMessageArrow(arrow); + endItem->addMessageArrow(arrow); + arrow->isCommunication = true; + addArrow(arrow); + arrow->updatePosition(); + + machineModel->addMessageToTransition(startItem->transition, + false, endItem->getName()); + } + + // If start = message and end = transition + if (startItem->mytype == 2 && endItem->mytype == 1) { + Arrow * arrow = new Arrow(startItem, endItem); + startItem->addMessageArrow(arrow); + endItem->addMessageArrow(arrow); + arrow->isCommunication = true; + addArrow(arrow); + arrow->updatePosition(); + + machineModel->addMessageToTransition( + endItem->transition, true, startItem->getName()); + } + } + } + + line = 0; + QGraphicsScene::mouseReleaseEvent(mouseEvent); +} + +void MachineScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent) { + if (line != 0) { + QLineF newLine(line->line().p1(), mouseEvent->scenePos()); + line->setLine(newLine); + + /*QGraphicsItem * qitem = itemAt(mouseEvent->scenePos()); + if (qitem == 0) + { + GraphicsItem * state = qgraphicsitem_cast(qitem); + state->setSelected(true); + state->update(); + }*/ + } else { + QGraphicsScene::mouseMoveEvent(mouseEvent); + } +} + +void MachineScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent) { + selectedFunction = 0; + emit(functionSelected(false)); + + QGraphicsItem * qitem = itemAt(mouseEvent->scenePos()); + + /* If left mouse click proceed with event, + * which is passed to scene items to select/move them */ + if (mouseEvent->button() == Qt::LeftButton) { + if (qitem != 0) { + clearSelection(); + qitem->setSelected(true); + + if (qgraphicsitem_cast(qitem)) { + GraphicsItem * sitem = + qgraphicsitem_cast(qitem); + // if function and not foreign + if (sitem->mytype == 1 && !sitem->foreign) { + selectedFunction = sitem; + emit(functionSelected(true)); + } + } + } + + QGraphicsScene::mousePressEvent(mouseEvent); + } + + /* If right mouse button add state or start line */ + if (mouseEvent->button() == Qt::RightButton) { + if (rootMachine->type == 1) { // agent + // qitem = itemAt(mouseEvent->scenePos()); + /* If there is no item at mouse position */ + if (qitem == 0) { + /* Create new state, add to scene, and set position */ + /* GraphicsItem *state = new GraphicsItem; + QString s; + + s.append("state_"); + s.append(QString("%1").arg(num_states)); + num_states++; + state->setName(s); + addItem(state); + state->setPos(mouseEvent->scenePos()); + //machine->addState(state); + emit( this->addedState(s, state->getStartState()) ); + */ + } else { + // GraphicsItem * state = qgraphicsitem_cast + // (qitem); + // state->setSelected(true); + // state->update(); + + /* If graphics item is a state */ + if (qgraphicsitem_cast(qitem)) { + GraphicsItem * sitem = + qgraphicsitem_cast(qitem); + + // if item is a state + // if(sitem->mytype==0) + // { + int count = 0; + for (int i = 0; i < sitem-> + getTransitionArrows().size(); i ++) { + Arrow * a = sitem->getTransitionArrows().at(i); + if (a->startItem() == sitem) count++; + } + + // if(count < 2) + // { + line = new QGraphicsLineItem( + QLineF(mouseEvent->scenePos(), + mouseEvent->scenePos())); + line->setPen(QPen(Qt::black, 1)); + addItem(line); + // } + // else + // { + // emit( updateStatus( + // "States cannot have more than 2 outgoing transitions") ); + // } + // } + } + // else qDebug() << "arrow clicked"; + } + } + } +} + +void MachineScene::wheelEvent(QGraphicsSceneWheelEvent *event) { + // Get the position of the mouse before scaling, in scene coords +// QPointF pointBeforeScale(mapToScene(event->pos())); + + // Get the original screen centerpoint +// QPointF screenCenter = GetCenter(); //CurrentCenterPoint; + // (visRect.center()); + + // Scale the view ie. do the zoom + if (zoomOn) { + if (event->delta() > 0) { + zoomIn(); + } else { + zoomOut(); + } + } + + // Get the position after scaling, in scene coords +// QPointF pointAfterScale(mapToScene(event->pos())); + + // Get the offset of how the screen moved +// QPointF offset = pointBeforeScale - pointAfterScale; + + // Adjust to the new center for correct zooming +// QPointF newCenter = screenCenter + offset; +// SetCenter(newCenter); +} + +void MachineScene::zoomIn() { + if (myGraphicsView) myGraphicsView->scale(scaleFactor, scaleFactor); +} + +void MachineScene::zoomOut() { + if (myGraphicsView) myGraphicsView-> + scale(1.0 / scaleFactor, 1.0 / scaleFactor); +} + +void MachineScene::keyPressEvent(QKeyEvent *event) { + switch (event->key()) { + case Qt::Key_Z: + zoomOn = true; + break; + case Qt::Key_Minus: + zoomOut(); + break; + case Qt::Key_Equal: + zoomIn(); + break; + default: + event->ignore(); + break; + } +} + +void MachineScene::keyReleaseEvent(QKeyEvent *event) { + switch (event->key()) { + case Qt::Key_Z: + zoomOn = false; + break; + default: + event->ignore(); + break; + } +} + +void calcState(GraphicsItem * sitem, QList * layerWidth, int layerHeight) { + // Set state position, width after the current layer width + sitem->setPos(layerWidth->at(sitem->layer)+(sitem->width()/2.0), + 20.0 + (sitem->layer*layerHeight)); + // Update the layer width to be after the current state + layerWidth->replace(sitem->layer, + (layerWidth->at(sitem->layer) + sitem->width()+10)); + // Set layered flag to be 1 + sitem->layered = 1; + + /*for(int j = 0; j < sitem->getTransitions().size(); j++) + { + Arrow * a = sitem->getTransitions().at(j); + + if(sitem->layered < 0) calcState(a->endItem(), layerWidth); + }*/ +} + +void MachineScene::calcLayers() { + // WARNING: does not currently handle loops + + selectedFunction = 0; + int done; + int noLayers; + int noStatesInLayer; + int largestWidth; + int globalLargestWidth = 0; + int globalHighestLayer = 0; + int layerHeight = 80; + QList layerWidth; + QList< QList > statesLayer; + QList agentWidth; + QList messageWidth; + + /* Initialise all graphics items + layer = -1 to show the item has no layerWidth + layered = -1 to show the item has not been layered yet */ + for (int i = 0; i < statesAndTransitions.count(); i++) { + statesAndTransitions[i]->layer = -1; + statesAndTransitions[i]->layered = -1; + } + for (int i = 0; i < messages.count(); i++) { + messages[i]->layer = -1; + messages[i]->layered = -1; + } + + /* For each agent type */ + for (int agent = 0; agent < agentNames.count(); agent++) { + done = 0; + noLayers = 0; + largestWidth = 0; + layerWidth.clear(); + statesLayer.clear(); + agentWidth.append(0); + + int tcount; + int highestn; + /* For each state or transition count the number of arrows + * going to the same state */ + for (int i = 0; i < statesAndTransitions.count(); i++) { + GraphicsItem * sitem = statesAndTransitions[i]; + + if (sitem->agentName == agentNames[agent]) { // If the item is + // from the current agent type + for (int j = 0; j < sitem-> + getTransitionArrows().size(); j++) { // for each arrow + tcount = 0; + highestn = 0; + Arrow * a = sitem->getTransitionArrows().at(j); + if (a->startItem() == sitem) { // If the arrow is incoming + for (int k = 0; k < sitem-> + getTransitionArrows().size(); k++) { + Arrow * a2 = sitem->getTransitionArrows().at(k); + if (a != a2) { + if (a->endItem() == a2->endItem()) { + tcount++; + if (highestn < a2->number) highestn = + a2->number; + } + } + } + a->total = tcount+1; + a->number = highestn+1; + } + } + } + } + + while (done == 0) { + done = 1; + noStatesInLayer = 0; + QList list; + statesLayer.append(list); + + for (int i = 0; i < statesAndTransitions.count(); i++) { + GraphicsItem * sitem = statesAndTransitions[i]; + + if (sitem->agentName == agentNames[agent]) { + bool include = false; + + if (sitem->layer == -1) { + int count_in = 0; + + // Find first state + if (noLayers == 0) { + for (int j = 0; j < sitem-> + getTransitionArrows().size(); j++) { + Arrow * a = sitem->getTransitionArrows().at(j); + if (a->endItem() == sitem && + (a->startItem()->layer == -1 || + a->startItem()->layer == noLayers)) + count_in++; + } + if (count_in == 0) { + include = true; + } + } else { // Find next states from current layer + for (int j = 0; j < sitem-> + getTransitionArrows().size(); j++) { + Arrow * a = + sitem->getTransitionArrows().at(j); + if (a->endItem() == sitem && + a->startItem()->layer == noLayers-1) { + include = true; + } + } + } + } else if (sitem->layer == -2) { + include = true; + } + + if (include) { + done = 0; + sitem->layer = noLayers; + noStatesInLayer++; + statesLayer[noLayers].append(sitem); + + /* Messages */ +/* if(sitem->mytype == 0) // if state + { + for(int j = 0; j < sitem->getTransitions().size(); j++) + { + Arrow * a = sitem->getTransitions().at(j); + + if(a->endItem()->mytype == 1) // If state next transition + // has incoming message + { + for(int k = 0; k < messages.count(); k++) + { + if(messages[k]->layer == -1) + { + for(int l = 0; l < messages[k]-> + getTransitions().size(); l++) + { + Arrow * a2 = messages[k]->getTransitions().at(l); + if(a2->endItem() == a->endItem()) + { + messages[k]->layer = noLayers; + noStatesInLayer++; + statesLayer[noLayers].append(messages[k]); + } + } + } + } + } + if(a->startItem()->mytype == 1) // If state before transition + // has outgoing message + { + for(int k = 0; k < messages.count(); k++) + { + for(int l = 0; l < messages[k]->getTransitions().size(); + l++) + { + if(messages[k]->layer == -1) + { + Arrow * a2 = messages[k]->getTransitions().at(l); + if(a2->startItem() == a->startItem()) + { + messages[k]->layer = noLayers; + noStatesInLayer++; + statesLayer[noLayers].append(messages[k]); + } + } + } + } + } + } + }*/ + } + } + } + + if (done == 0) { + // Give an order to the states in the same layer + int posCount; + for (int j = 0; j < statesLayer[noLayers].count(); j++) { + statesLayer[noLayers][j]->aveX = 0; + posCount = 0; + for (int k = 0; k + getTransitionArrows().size(); k++) { + Arrow * a = statesLayer[noLayers][j]-> + getTransitionArrows().at(k); + // If the end state of the transition is the + // current state + if (a->endItem() == statesLayer[noLayers][j]) { + statesLayer[noLayers][j]->aveX += + a->startItem()->x(); + } + } + if (posCount > 0) + statesLayer[noLayers][j]->aveX = + statesLayer[noLayers][j]->aveX/posCount; + } + + // Calculate the x-axis position of the states from left + // side (no centring) + layerWidth.append(0); + + // Position states in order that their previous transition + // states were in + GraphicsItem * lowest = 0; + for (int j = 0; j < statesLayer[noLayers].count(); j++) { + int lowestX = -1; + for (int k = 0; k < statesLayer[noLayers].count(); k++) { + if (statesLayer[noLayers][k]->aveX > -1) { + if (lowestX == -1) { + lowestX = statesLayer[noLayers][k]->aveX; + lowest = statesLayer[noLayers][k]; + } else if (statesLayer[noLayers][k]->aveX <= + lowestX) { + lowestX = statesLayer[noLayers][k]->aveX; + lowest = statesLayer[noLayers][k]; + } + } + } + + // Calculate position + calcState(lowest, &layerWidth, layerHeight); + lowest->aveX = -1; + } + + // Remove the last gap between states as there is not another + // state to add + layerWidth[noLayers] -= 10.0; + // messageWidth.append(layerWidth[noLayers]); + + if (layerWidth[noLayers] > largestWidth) + largestWidth = layerWidth[noLayers]; + + // If a state has incoming transitions that have not been + // layered + // then remove the state from the current layer and add + // to the next layer + for (int j = 0; j < statesLayer[noLayers].count(); j++) { + bool flag = false; + for (int k = 0; k < statesLayer[noLayers][j]-> + getTransitionArrows().size(); k++) { + Arrow * a = statesLayer[noLayers][j]-> + getTransitionArrows().at(k); + // If the end state of the transition is the current + // state and + // the start state does not yet have a layer or is + // in the current layer then... + if (a->endItem() == statesLayer[noLayers][j] && + (a->startItem()->layer == noLayers || + a->startItem()->layer == -1)) + flag = true; + } + if (flag) { + // Add the state to the next layer + statesLayer[noLayers][j]->layer = -2; // noLayers+1; + statesLayer[noLayers][j]->layered = -1; + statesLayer[noLayers].removeAt(j); + j--; + } + } + + noLayers++; + } + } + + /////////// + + // qDebug() << largestWidth; +// for(int i = 0; i < noLayers; i++) +// if(messageWidth[i] > largestWidth) largestWidth = messageWidth[i]; + // qDebug() << largestWidth; + // largestWidth += 20; + + if (largestWidth > globalLargestWidth) globalLargestWidth = + largestWidth; + if (noLayers > globalHighestLayer) globalHighestLayer = noLayers; + + agentWidth[agent] = largestWidth; + // nextWidth = largestWidth + (); + + // Centre the states + for (int i = 0; i < noLayers; i++) { + for (int j = 0; j < statesLayer[i].count(); j++) { + statesLayer[i][j]->moveBy((largestWidth/2.0) - + (layerWidth[i]/2.0), 0.0); + } + } + } + + int rollingWidth = 0; + if (agentNames.count() > 0) rollingWidth = agentWidth[0] + 10; + /* Move agent items so they are separated */ + if (agentWidth.count() > 0) { + for (int agent = 1; agent < agentNames.count(); agent++) { + for (int i = 0; i < statesAndTransitions.count(); i++) { + GraphicsItem * sitem = statesAndTransitions[i]; + + if (sitem->agentName == agentNames[agent]) { + sitem->moveBy(rollingWidth, 0.0); + } + } + + rollingWidth += agentWidth[agent] + 10; + } + } + + // Handle messages + // if(agentWidth.count() == 1) + // { + for (int i = 0; i < globalHighestLayer; i++) + messageWidth.append(rollingWidth); + + /*QList agentMessageCount; + for(int i = 0; i < agentNames.count(); i++) + agentMessageCount.append(0);*/ + + for (int i = 0; i < messages.count(); i++) { + /*for(int j = 0; j < agentMessageCount.size(); j++) + * agentMessageCount[j] = 0;*/ + int lastOutputLayer = -1; + int firstInputLayer = -1; + for (int j = 0; j < messages[i]->getMessageArrows().size(); j++) { + Arrow * a = messages[i]->getMessageArrows().at(j); + if (a->endItem() == messages[i]) { + if (lastOutputLayer == -1 || + lastOutputLayer < a->startItem()->layer) + lastOutputLayer = a->startItem()->layer; + /*for(int agent = 0; agent < agentNames.count(); agent++) + if(a->startItem()->agentName == agentNames[agent]) + agentMessageCount[agent]++;*/ + } + if (a->startItem() == messages[i]) { + if (firstInputLayer == -1 || + firstInputLayer > a->endItem()->layer) + firstInputLayer = a->endItem()->layer; + /*for(int agent = 0; agent < agentNames.count(); agent++) + if(a->endItem()->agentName == agentNames[agent]) agentMessageCount[agent]++;*/ + } + } + + /*qDebug() << messages[i]->getName(); + for(int j = 0; j < agentMessageCount.size(); j++) qDebug() << "\t" << agentNames[j] << agentMessageCount[j];*/ + + int messageLayer; + if (lastOutputLayer != -1) messageLayer = lastOutputLayer+1; + else + if (firstInputLayer != -1) messageLayer = firstInputLayer-1; + else + messageLayer = 0; + messages[i]->setPos(messageWidth[messageLayer]+10.0+ + (messages[i]->width()/2.0), 20.0 + + ((messageLayer)*layerHeight)); + messageWidth[messageLayer] += 10.0+messages[i]->width(); + // statesLayer[messageLayer].append(messages[i]); + if (messageWidth[messageLayer] > rollingWidth) + rollingWidth = messageWidth[messageLayer]; + } + // } + + setSceneRect(0, 0, rollingWidth, 20.0+(globalHighestLayer*layerHeight)); + + /* Update line positions with respect to state positions */ + for (int i = 0; i < items().size(); i++) { + QGraphicsItem * qitem = items().at(i); + + if (qgraphicsitem_cast(qitem)) { + Arrow * sitem = qgraphicsitem_cast(qitem); + sitem->updatePosition(); + } + } + + update(); +} + +void MachineScene::addMessageCommunication(GraphicsItem *t, MessageComm * m, + bool isInput) { + // Find the associated message state item if not make one + GraphicsItem * message = 0; + for (int j = 0; j < messages.size(); j++) { + GraphicsItem * sitem1 = messages.at(j); + if (sitem1->mytype == 2 && sitem1->getName() == m->messageType) + message = sitem1; + } + if (message == 0) { + message = new GraphicsItem; + message->setMessage(m); + addItem(message); + messages.append(message); + num_messages++; + int shift = 40; + if (isInput) shift *= -1; + message->setPos(t->x(), t->y()+shift); + } + + Q_ASSERT(message != 0); + + Arrow * arrow; + if (isInput) arrow = new Arrow(message, t); + else + arrow = new Arrow(t, message); + arrow->isCommunication = true; + message->addMessageArrow(arrow); + t->addMessageArrow(arrow); + addArrow(arrow); +} + +void MachineScene::addTransitionTransition(QString agentName, + Transition * t, int isForeign) { + GraphicsItem * func = addTransitionString(agentName, t, isForeign); + + if (!isForeign) { + Q_ASSERT(func != 0); + + // For all input messages + for (int i = 0; i < t->input().messageModel->messages.count(); i++) { + addMessageCommunication(func, + &(t->input().messageModel->messages[i]), true); + } + + for (int i = 0; i < t->output().messageModel->messages.count(); i++) { + addMessageCommunication(func, + &(t->output().messageModel->messages[i]), false); + } + } +} + +GraphicsItem * MachineScene::addTransitionString(QString agentName, + Transition * t, int flag) { + QString csn = t->currentState()->name(); + QString n = t->name(); + QString nsn = t->nextState()->name(); + GraphicsItem * cs = 0; + GraphicsItem * ns = 0; + GraphicsItem * func = 0; + Arrow * arrow = 0; + + // add agent name to list + if (!agentNames.contains(agentName)) agentNames.append(agentName); + + for (int j = 0; j < statesAndTransitions.size(); j++) { + GraphicsItem * sitem1 = statesAndTransitions.at(j); + + if (sitem1->agentName == agentName) { + if (sitem1->mytype == 0) { + if (sitem1->getName() == csn) cs = sitem1; + if (sitem1->getName() == nsn) ns = sitem1; + } else { + if (sitem1->mytype == 1 && sitem1->getName() == n && + sitem1->preState == csn && sitem1->postState == nsn) + func = sitem1; + } + } + } + + if (cs == 0) { + GraphicsItem *state = new GraphicsItem; + state->setState(t->currentState()); + state->agentName = agentName; + cs = state; + addState(state); + if (flag == 1) state->foreign = true; + } + if (ns == 0) { + GraphicsItem *state = new GraphicsItem; + state->setState(t->nextState()); + state->agentName = agentName; + ns = state; + addState(state); + if (flag == 1) state->foreign = true; + } + + if (func == 0) { + func = new GraphicsItem; + func->setTransition(t); + func->agentName = agentName; + if (flag == 1) func->foreign = true; + addTransition(func); + + arrow = new Arrow(cs, func); + cs->addTransitionArrow(arrow); + func->addTransitionArrow(arrow); + if (flag == 1) arrow->foreign = true; + arrow->drawHead(false); + addArrow(arrow); + + arrow = new Arrow(func, ns); + func->addTransitionArrow(arrow); + ns->addTransitionArrow(arrow); + if (flag == 1) arrow->foreign = true; + addArrow(arrow); + } + + return func; +} + +void MachineScene::addState(GraphicsItem * s) { + addItem(s); + statesAndTransitions.append(s); + num_states++; +} + +void MachineScene::addTransition(GraphicsItem * t) { + addItem(t); + statesAndTransitions.append(t); + num_transitions++; +} + +void MachineScene::addArrow(Arrow * t) { + addItem(t); + t->setZValue(-1.0); +} + +void MachineScene::updateStateName(State *s) { + for (int i = 0; i < statesAndTransitions.size(); i++) { + GraphicsItem * sitem1 = statesAndTransitions.at(i); + + if (sitem1->state() == s) { + sitem1->nameChanged(); + } + } + this->update(); +} + +void MachineScene::updateTransitionName(Transition * t) { + for (int i = 0; i < statesAndTransitions.size(); i++) { + GraphicsItem * sitem1 = statesAndTransitions.at(i); + + if (sitem1->transition == t) { + sitem1->nameChanged(); + } + } + this->update(); +} + +void MachineScene::updateInput(Transition *t) { + // Find the graphics item that corresponds to the transition + for (int i = 0; i < statesAndTransitions.size(); i++) { + GraphicsItem * transitionItem = statesAndTransitions.at(i); + + if (transitionItem->transition == t) { + // Add missing arrows + // For each transition input message + for (int k = 0; k < t->input().messageModel->messages.count(); + k++) { + MessageComm * message = &(t->input().messageModel-> + messages[k]); + // Look to see if it already exists + bool isFound = false; + for (int j = 0; j < transitionItem->getMessageArrows().size(); + j++) { + GraphicsItem * messageItem = + transitionItem->getMessageArrows().at(j)-> + startItem(); + if (messageItem->mytype == 2 && + messageItem->getName() == message->messageType) + isFound = true; + } + if (!isFound) + addMessageCommunication(transitionItem, message, true); + } + + // Remove redundant arrows + QList arrowsToDelete; + for (int j = 0; j < transitionItem->getMessageArrows().size(); + j++) { + Arrow * arrow = transitionItem->getMessageArrows()[j]; + GraphicsItem * messageItem = arrow->startItem(); + // If the arrow is an input arrow + // I.e. the startItem is a message GraphicsItem + if (messageItem->mytype == 2) { + bool isFound = false; + for (int k = 0; k < t->input().messageModel-> + messages.count(); k++) { + MessageComm * message = &(t->input().messageModel-> + messages[k]); + if (messageItem->getName() == message->messageType) + isFound = true; + } + if (!isFound) { + arrowsToDelete.append(arrow); + // remove arrow from message + messageItem->removeMessageArrow(arrow); + // Check to see if the message state item is redundant + if (messageItem->getMessageArrows().size() == 0) { + removeItem(messageItem); + messages.removeOne(messageItem); + delete messageItem; + } + } + } + } + // Delete arrows + for (int i = 0; i < arrowsToDelete.size(); i++) { + Arrow * arrow = arrowsToDelete[i]; + // Remove arrow + transitionItem->removeMessageArrow(arrow); + removeItem(arrow); + delete arrow; + } + } + } + this->update(); +} + +void MachineScene::updateOutput(Transition *t) { + for (int i = 0; i < statesAndTransitions.size(); i++) { + GraphicsItem * transitionItem = statesAndTransitions.at(i); + + if (transitionItem->transition == t) { + // Add missing arrows + // For each transition input message + for (int k = 0; k < t->output().messageModel->messages.count(); + k++) { + MessageComm * message = &(t->output().messageModel-> + messages[k]); + // Look to see if it already exists + bool isFound = false; + for (int j = 0; j < transitionItem->getMessageArrows().size(); + j++) { + GraphicsItem * messageItem = + transitionItem->getMessageArrows().at(j) + ->endItem(); + if (messageItem->mytype == 2 && + messageItem->getName() == message->messageType) + isFound = true; + } + if (!isFound) + addMessageCommunication(transitionItem, message, false); + } + + // Remove redundant arrows + QList arrowsToDelete; + for (int j = 0; j < transitionItem-> + getMessageArrows().size(); j++) { + Arrow * arrow = transitionItem->getMessageArrows()[j]; + GraphicsItem * messageItem = arrow->endItem(); + // If the arrow is an input arrow + // I.e. the startItem is a message GraphicsItem + if (messageItem->mytype == 2) { + bool isFound = false; + for (int k = 0; k < t->output().messageModel-> + messages.count(); k++) { + MessageComm * message = &(t->output().messageModel-> + messages[k]); + if (messageItem->getName() == message->messageType) + isFound = true; + } + if (!isFound) { + arrowsToDelete.append(arrow); + // remove arrow from message + messageItem->removeMessageArrow(arrow); + // Check to see if the message state item is redundant + if (messageItem->getMessageArrows().size() == 0) { + removeItem(messageItem); + messages.removeOne(messageItem); + delete messageItem; + } + } + } + } + // Delete arrows + for (int i = 0; i < arrowsToDelete.size(); i++) { + Arrow * arrow = arrowsToDelete[i]; + // Remove arrow + transitionItem->removeMessageArrow(arrow); + removeItem(arrow); + delete arrow; + } + } + } + this->update(); +} + +void MachineScene::selectState(QString n) { + clearSelection(); + + for (int j = 0; j < items().size(); j++) { + if (qgraphicsitem_cast(items().at(j))) { + GraphicsItem * sitem1 = + qgraphicsitem_cast(items().at(j)); + + if (sitem1->getName() == n) { + sitem1->setSelected(true); + } + } + } + update(); +} + +void MachineScene::selectTransition(QString n) { + clearSelection(); + + for (int j = 0; j < items().size(); j++) { + if (qgraphicsitem_cast(items().at(j))) { + Arrow * a = qgraphicsitem_cast(items().at(j)); + + if (a->getName() == n) { + a->setSelected(true); + } + } + } + update(); +} + +void MachineScene::removeTransitionFunction(GraphicsItem *t) { + // Remove message arrows + t->transition->setInput(Communication(1)); + updateInput(t->transition); + t->transition->setOutput(Communication(0)); + updateOutput(t->transition); + // Remove transition arrows + QList arrowsToDelete; + for (int i = 0; i < t->getTransitionArrows().size(); i++) { + Arrow * arrow = t->getTransitionArrows()[i]; + arrowsToDelete.append(arrow); + GraphicsItem * state; + if (arrow->startItem() == t) state = arrow->endItem(); + else + state = arrow->startItem(); + state->removeTransitionArrow(arrow); + if (state->getTransitionArrows().size() == 0) { + statesAndTransitions.removeOne(state); + removeItem(state); + delete state; + } + } + for (int i = 0; i < arrowsToDelete.size(); i++) { + Arrow * arrow = arrowsToDelete[i]; + // Remove arrow + t->removeTransitionArrow(arrow); + removeItem(arrow); + delete arrow; + } + statesAndTransitions.removeOne(t); + removeItem(t); + delete t; +} + +void MachineScene::deleteSelectedFunction() { + Q_ASSERT(selectedFunction != 0); + + // Remove function from transition table + machineModel->deleteTransition(selectedFunction->transition); + // Remove function from scene + removeTransitionFunction(selectedFunction); +} diff --git a/machinescene.h b/machinescene.h new file mode 100644 index 0000000..95e1225 --- /dev/null +++ b/machinescene.h @@ -0,0 +1,81 @@ +/*! + * \file machinescene.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for the graphics scene for stategraphs +*/ +#ifndef MACHINESCENE_H_ +#define MACHINESCENE_H_ + +#include +#include "./graphicsitem.h" +#include "./arrow.h" +#include "./machinemodel.h" + +class Machine; + +class MachineScene : public QGraphicsScene { + Q_OBJECT + + public: + enum Mode { InsertItem, InsertLine, InsertText, MoveItem }; + + MachineScene(Machine * m, QObject *parent = 0); + + void calcLayers(); + void setMachineModel(MachineModel * m) { machineModel = m; } + void selectTransition(QString n); + void selectState(QString n); + void addTransitionTransition(QString agentName, + Transition * t, int isForeign = 0); + void setGraphicsView(QGraphicsView * g) { myGraphicsView = g; } + + + signals: + void functionSelected(bool b); + + public slots: + void updateStateName(State * s); + void updateTransitionName(Transition * t); + void updateInput(Transition * t); + void updateOutput(Transition * t); + void deleteSelectedFunction(); + + protected: + void mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent); + void mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent); + void wheelEvent(QGraphicsSceneWheelEvent *event); + void keyPressEvent(QKeyEvent *event); + void keyReleaseEvent(QKeyEvent *event); + + private: + GraphicsItem * addTransitionString(QString agentName, + Transition * t, int flag); + void addMessageCommunication(GraphicsItem *t, + MessageComm * m, bool isInput); + void removeTransitionFunction(GraphicsItem * t); + void zoomIn(); + void zoomOut(); + void addState(GraphicsItem * s); + void addTransition(GraphicsItem * s); + void addArrow(Arrow * t); + qreal scaleFactor; + Mode myMode; + QGraphicsLineItem *line; + int num_states; + int num_transitions; + int num_messages; + MachineModel * machineModel; + int mytype; + Machine * rootMachine; + QStringList agentNames; + bool zoomOn; + QGraphicsView * myGraphicsView; + QList statesAndTransitions; + QList messages; + GraphicsItem * selectedFunction; +}; + +#endif // MACHINESCENE_H_ diff --git a/machinetree.cpp b/machinetree.cpp new file mode 100644 index 0000000..b02e718 --- /dev/null +++ b/machinetree.cpp @@ -0,0 +1,255 @@ +/*! + * \file machinetree.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of model tree + */ +#include "./machinetree.h" +#include "./machine.h" + +MachineTree::MachineTree(QObject *parent) + : QAbstractItemModel(parent) { + QList rootData; + rootData << "Title" << "Summary"; + rootItem = new Machine(-1); + /*Machine * m = new Machine(0, rootItem); + Machine * p = new Machine(0, rootItem); + m->appendChild(new Machine(1, m)); + m->appendChild(new Machine(2, m)); + rootItem->appendChild(m); + rootItem->appendChild(p);*/ + // setupModelData(data.split(QString("\n")), rootItem); +} + +MachineTree::~MachineTree() { + delete rootItem; +} + +int MachineTree::columnCount(const QModelIndex &/*parent*/) const { + return 3; + /*if (parent.isValid()) + return static_cast(parent.internalPointer())->columnCount(); + else + return rootItem->columnCount();*/ +} + +QVariant MachineTree::data(const QModelIndex &index, int role) const { + if (!index.isValid()) + return QVariant(); + + Machine *item = static_cast(index.internalPointer()); + + if (role == Qt::DisplayRole) { + return item->data(index.column()); + } else if (role == Qt::CheckStateRole) { + if (item->isSubModel && index.column() == 0) { + if (item->isEnabled()) return Qt::Checked; + else + return Qt::Unchecked; + } + } + + return QVariant(); +} + +bool MachineTree::setData(const QModelIndex &index, const QVariant &value, + int role) { + Machine *item = static_cast(index.internalPointer()); + + if (index.isValid()) { // && role == Qt::EditRole) + if (index.column() == 0) { + // plots.at(index.row())->setEnable(qVariantValue(value)); + if (role == Qt::CheckStateRole) { + item->setEnabled(qVariantValue(value)); + } + } else if (index.column() == 1) { + if (item->type == 0 || item->type == 1 || item->type == 2 || + item->type == 5) { + item->name = value.toString(); + } else if (item->type == 6) { // functionFile + item->name = value.toString(); + } else if (item->type == 4) { // timeUnit + item->setTimeUnit(qVariantValue(value)); + } + } else if (index.column() == 2) { + item->description = value.toString(); + } + emit dataChanged(index, index); + return true; + } + return false; +} + +Qt::ItemFlags MachineTree::flags(const QModelIndex &index) const { + if (!index.isValid()) + return 0; + + Machine *item = static_cast(index.internalPointer()); + + if (item->type == 0 && index.column() == 0) + return QAbstractItemModel::flags(index) | Qt::ItemIsEnabled | + Qt::ItemIsSelectable | Qt::ItemIsUserCheckable; + // | Qt::ItemIsEditable; + // If name or datatype/agent/message description + else if (index.column() == 1 || ( (item->type == 5 || item->type == 1 || + item->type == 2) && index.column() == 2)) + return QAbstractItemModel::flags(index) | Qt::ItemIsEnabled | + Qt::ItemIsSelectable | Qt::ItemIsEditable; + else + return QAbstractItemModel::flags(index) | Qt::ItemIsEnabled | + Qt::ItemIsSelectable; +} + +QVariant MachineTree::headerData(int section, + Qt::Orientation orientation, int role) const { + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + // return rootItem->data(section); + if (section == 0) return QString("Type"); + else if (section == 1) return QString("Value"); + else if (section == 2) return QString("Description"); + } + + return QVariant(); +} + +QModelIndex MachineTree::index(int row, int column, + const QModelIndex &parent) const { + if (!hasIndex(row, column, parent)) + return QModelIndex(); + + Machine *parentItem; + + if (!parent.isValid()) + parentItem = rootItem; + else + parentItem = static_cast(parent.internalPointer()); + + Machine *childItem = parentItem->child(row); + if (childItem) + return createIndex(row, column, childItem); + else + return QModelIndex(); +} + +/** \fn MachineTree::getIndex(Machine *m) + * \brief Return the index for the machine in the tree model. + * + * Because the model is a tree structure the index of an element + * depends on the index of its parent. Therefore this function is + * recursive. See http://developer.qt.nokia.com/doc/qt-4.8/model-view-programming.html#id-0935927b-e27e-4ca5-b394-99175f570a41 + * for more details. + * + * \param *m Pointer to the machine that the index is for + * + */ +const QModelIndex MachineTree::getIndex(Machine *m) { + if (m == rootItem) return QModelIndex(); + + for (int i = 0; i < m->parent()->childCount(); i++) { + if (m == m->parent()->child(i)) { + // qDebug() << "getIndex" << m->name << i; + + return this->index(i, 0, getIndex(m->parent())); + } + } + return QModelIndex(); +} + +QModelIndex MachineTree::parent(const QModelIndex &index) const { + if (!index.isValid()) + return QModelIndex(); + + Machine *childItem = static_cast(index.internalPointer()); + Machine *parentItem = childItem->parent(); + + if (parentItem == rootItem) + return QModelIndex(); + + return createIndex(parentItem->row(), 0, parentItem); +} + +int MachineTree::rowCount(const QModelIndex &parent) const { + Machine *parentItem; + if (parent.column() > 0) + return 0; + + if (!parent.isValid()) + parentItem = rootItem; + else + parentItem = static_cast(parent.internalPointer()); + + return parentItem->childCount(); +} + +Machine* MachineTree::addMachine() { + Machine * m = new Machine(0, rootItem); + rootItem->appendChild(m); + m->setRootModel(m); + + Machine * a = new Machine(3, m); + m->appendChild(a); + a->setRootModel(m->rootModel()); + a->name = "environment"; + + return m; +} + +void MachineTree::removeMachine(Machine *m) { + beginRemoveRows(getIndex(m), m->parent()->childItems.indexOf(m), + m->parent()->childItems.indexOf(m)); + m->parent()->removeChild(m); + endRemoveRows(); +} + +Machine * MachineTree::insertMachine(Machine *m, int type) { + // Find index to place new machine + // sub model + // environment + // functions files + // time units + // data types + // agents + // messages + + Machine * n = m; + int index = -1; + if (type == 4 || type == 5 || type == 6) { + for (int i = 0; i < m->childCount(); i++) + if (m->child(i)->type == 3) n = m->child(i); + } + + // qDebug() << n->name; + + if (type == 3) index = 0; + for (int i = 0; i < n->childCount(); i++) { + // qDebug() << i << n->child(i)->type << n->child(i)->name; + if (type == 0 && n->child(i)->type == 3) { + index = i; + break; } + if (type == 1 && n->child(i)->type == 2) { + index = i; + break; } + if (type == 6 && (n->child(i)->type == 4 || n->child(i)->type == 5)) { + index = i; + break; } + if (type == 4 && n->child(i)->type == 5) { + index = i; + break; } + } + if (index == -1) index = n->childCount(); + + beginInsertRows(getIndex(n), index, index); + Machine * m1 = new Machine(type, n); + m1->setRootModel(n->rootModel()); + if (type == 0) { + m1->isSubModel = true; + Machine * a = new Machine(3, m1); + m1->appendChild(a); + a->setRootModel(m1->rootModel()); + } + n->insertChild(m1, index); + endInsertRows(); + + return m1; +} diff --git a/machinetree.h b/machinetree.h new file mode 100644 index 0000000..6b6a0e2 --- /dev/null +++ b/machinetree.h @@ -0,0 +1,44 @@ +/*! + * \file machinetree.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for the model tree +*/ +#ifndef MACHINETREE_H_ +#define MACHINETREE_H_ + +#include +#include +#include +#include "./machine.h" + +class MachineTree : public QAbstractItemModel { + Q_OBJECT + + public: + explicit MachineTree(QObject *parent = 0); + ~MachineTree(); + + QVariant data(const QModelIndex &index, int role) const; + bool setData(const QModelIndex &index, const QVariant &value, + int role = Qt::EditRole); + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + QModelIndex index(int row, int column, + const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &index) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + + Machine* addMachine(); + Machine* insertMachine(Machine * m, int type); + void removeMachine(Machine * m); + const QModelIndex getIndex(Machine * m); + + private: + Machine *rootItem; +}; + +#endif // MACHINETREE_H_ diff --git a/machinetreedelegate.cpp b/machinetreedelegate.cpp new file mode 100644 index 0000000..5954e9a --- /dev/null +++ b/machinetreedelegate.cpp @@ -0,0 +1,104 @@ +/*! + * \file machinetreedelegate.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of the model tree delegate + */ +#include "./machinetreedelegate.h" +#include "./machine.h" +#include "./timeunitdialog.h" + +MachineTreeDelegate::MachineTreeDelegate(QObject * parent) + : QItemDelegate(parent) { +} + +QWidget *MachineTreeDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &/*option*/, + const QModelIndex &index) const { + Machine * machine = static_cast(index.internalPointer()); + + if (machine->type == 0 || machine->type == 1 || machine->type == 2 + || machine->type == 5) { + QLineEdit *editor = new QLineEdit(parent); + return editor; + } else if (machine->type == 6) { // functionFile + QFileDialog *editor = new QFileDialog(parent, Qt::Dialog); + editor->setModal(true); + return editor; + } else if (machine->type == 4) { // timeUnit + TimeUnitDialog *editor = new TimeUnitDialog(machine); + connect(editor, SIGNAL(accepted()), this, SLOT(commitAndCloseEditor())); + connect(editor, SIGNAL(rejected()), this, SLOT(commitAndCloseEditor())); + editor->setModal(true); + return editor; + } + + return 0; +} + +void MachineTreeDelegate::setEditorData(QWidget *editor, + const QModelIndex &index) const { + Machine * machine = static_cast(index.internalPointer()); + + if (machine->type == 0 || machine->type == 1 || machine->type == 2 + || machine->type == 5) { + QString value = index.data().toString(); + + QLineEdit *lineEdit = static_cast(editor); + lineEdit->setText(value); + } else if (machine->type == 6) { // functionFile + QFileDialog *fileDialog = static_cast(editor); + if (machine->fileDirectory == "" || machine->fileName == "") { + fileDialog->setDirectory(machine->rootModel()->fileDirectory); + } else { + fileDialog->setDirectory(machine->fileDirectory); + fileDialog->selectFile(machine->fileName); + } + } else if (machine->type == 4) { // timeUnit + TimeUnit tu = machine->timeUnit(); + TimeUnitDialog *dialog = static_cast(editor); + dialog->setTimeUnit(tu); + } +} + +void MachineTreeDelegate::setModelData(QWidget *editor, + QAbstractItemModel *model, const QModelIndex &index) const { + Machine * machine = static_cast(index.internalPointer()); + + if (machine->type == 0 || machine->type == 1 || machine->type == 2 || + machine->type == 5) { + QLineEdit *lineEdit = static_cast(editor); + QString value = lineEdit->text(); + + model->setData(index, value, Qt::EditRole); + } else if (machine->type == 6) { // functionFile + QFileDialog *fileDialog = static_cast(editor); + QStringList valueList = fileDialog->selectedFiles(); + + if (valueList.size() > 0) { + QDir dir(machine->rootModel()->fileDirectory); + QString s1 = dir.canonicalPath(); + QDir dir2(s1); + QString s = dir2.relativeFilePath(valueList.at(0)); + QFileInfo finfo(valueList.at(0)); + machine->fileDirectory = finfo.absoluteFilePath(); + machine->fileName = finfo.fileName(); + model->setData(index, s, Qt::EditRole); + } + } else if (machine->type == 4) { // timeUnit + TimeUnitDialog *dialog = static_cast(editor); + model->setData(index, qVariantFromValue(dialog->getTimeUnit())); + } +} + +void MachineTreeDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &/*index*/) const { + editor->setGeometry(option.rect); +} + +void MachineTreeDelegate::commitAndCloseEditor() { + TimeUnitDialog *editor = qobject_cast(sender()); + emit commitData(editor); + emit closeEditor(editor); +} diff --git a/machinetreedelegate.h b/machinetreedelegate.h new file mode 100644 index 0000000..afecacc --- /dev/null +++ b/machinetreedelegate.h @@ -0,0 +1,33 @@ +/*! + * \file machinetreedelegate.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for the model tree delegate +*/ +#ifndef MACHINETREEDELEGATE_H_ +#define MACHINETREEDELEGATE_H_ + +#include + +class MachineTreeDelegate : public QItemDelegate { + Q_OBJECT + + public: + explicit MachineTreeDelegate(QObject *parent = 0); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + + void updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const; + + private slots: + void commitAndCloseEditor(); +}; + +#endif // MACHINETREEDELEGATE_H_ diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..4ff5db5 --- /dev/null +++ b/main.cpp @@ -0,0 +1,114 @@ +/*! + * \file main.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Contains \c main() + * \details Starts the program and shows the \c MainWindow + */ +#include +#include "./mainwindow.h" + +int main(int argc, char *argv[]) { + QApplication a(argc, argv); + MainWindow w; + w.show(); + return a.exec(); +} + +/*! + * \mainpage FLAME Editor + * + * \section OVERVIEW Overview + * The FLAME Editor is a generic visual editor for FLAME models that use the FLAME framework (http://www.flame.ac.uk). + * + * \section DESIGN Design + * The GUI is split into 4 main sections: + * -# The model tree + * -# The memory table + * -# The stategraph graphics scene + * -# The transition functions table + * + * \section DATASTRUCT Data Structures + * -# The model tree class \c MachineTree is an implementation of \c QAbstractItemModel + * - which holds objects of type \c Machine in a tree structure + * -# The memory table class \c MemoryModel is an implementation of \c QAbstractTableModel + * - which holds objects of type \c Variable in a table structure + * -# The stategraph graphics scene class \c MachineScene is an implementation of \c QGraphicsScene + * - which holds objects of type \c GraphicsItem and \c Arrow + * -# The transition functions table class \c MachineModel is an implementation of \c QAbstractTableModel + * - which holds objects of type \c Transition + * + * The \c Machine class is used to represent a FLAME model and all of its main elements: + * - model - either the main model or any sub model + * - agent + * - message + * - environment - an element that contains any environment elements of the model, which include: + * - time unit + * - data type + * - function file + * + * The \c Machine class can contain: + * - \c MachineModel - to hold a table of agent transitions + * - \c MemoryModel - to hold a memory table for agents, messages, environment constants, + * data types and model version/author/description fields + * - \c MachineScene - to hold a graph of agent transitions + * - \c TimeUnit - to hold a time unit + * + * The \c Variable class is used to represent agent/message memory variables, + * environment constants, data type variables and model version/author/description fields and contains: + * - type - the data type of the variable/constant + * - name - the name of the variable/constant or the model attribute label + * - description - the variable/constant description or the model attribute value + * + * The \c GraphicsItem class is used to represent agent states, transition functions or message types. + * + * The \c Arrow class is used to represent an arrow between two objects of type \c GraphicsItem. + * + * The \c Transition class is used to represent agent state transitions and contains: + * - \c State current state + * - \c Communication input + * - \c Mpre pre condition + * - function name + * - \c Mpost post condition - currently unused + * - \c Communication output + * - \c State next state + * + * The \c Communication class is used to represent incoming and outgoing message communication and contains: + * - \c MessageModel which is an implementation of \c QAbstractTableModel and holds a table of: + * - \c MessageComm which contains: + * - message type + * - \c Condition - a filter on incoming messages + * - \c MessageSort - a sort on incoming messages + * + * \section DELEGATES Object Delegates + * A delegate class handles the interaction of the user with editing objects within cells of a tree or table. + * A dialog class can be associated with the editing of certain objects. + * + * -# The model tree is implemented in \c MachineTree. + * It holds objects of type \c Machine. + * It has associated delegates: + * - \c MachineTreeDelegate. This calls dialogs to edit certain objects: + * - \c TimeUnitDialog + * -# The memory model is implemented in \c MemoryModel. + * It holds objects of type \c Variable. + * It has associated delegates: + * - \c TextEditDelegate + * - \c DataTypeDelegate + * - \c LineEditDelegate + * -# The transition functions table is implemented in \c MachineModel. + * It holds objects of type \c Transition. + * It has associated delegates: + * - \c LineEditDelegate + * - \c CommDelegate + * - \c CommDialog + * - \c MessageDelegate + * - \c MpreDelegate + * - \c MpreDialog + * - \c SortDelegate + * - \c SortDialog + * - \c MpreDelegate + * - \c MpreDialog + * - \c MpostDelegate + * - \c MpostDialog + */ diff --git a/mainwindow.cpp b/mainwindow.cpp new file mode 100644 index 0000000..bc70a75 --- /dev/null +++ b/mainwindow.cpp @@ -0,0 +1,664 @@ +/*! + * \file mainwindow.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of the main window + */ +#include "./mainwindow.h" +#include "./ui_mainwindow.h" +#include "./graphicsitem.h" +#include "./memorydelegate.h" +#include "./mpredelegate.h" +#include "./mpostdelegate.h" +#include "./statedelegate.h" +#include "./commdelegate.h" +#include "./datatypedelegate.h" +#include "./lineeditdelegate.h" +#include "./texteditdelegate.h" +#include "./machinetreedelegate.h" +#include "./modelxmlreader.h" + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent), ui(new Ui::MainWindowClass) { + ui->setupUi(this); + + currentMachine = 0; + + QAction * newAction = new QAction(tr("&New"), this); + newAction->setShortcut(QKeySequence::New); + newAction->setStatusTip(tr("Create a new machine")); + + /* Machine tree */ + machineTree = new MachineTree(); + ui->treeView_machines->setModel(machineTree); + ui->treeView_machines->header()->setStretchLastSection(true); + ui->treeView_machines->setItemDelegate(new MachineTreeDelegate()); + ui->treeView_machines->setSelectionBehavior(QAbstractItemView::SelectRows); + + /* Transition table */ + ui->tableViewMachine->setModel(0); + ui->tableViewMachine->horizontalHeader()->setResizeMode( + QHeaderView::Interactive); + ui->tableViewMachine->setHorizontalScrollMode( + QAbstractItemView::ScrollPerPixel); + ui->tableViewMachine->setVerticalScrollMode( + QAbstractItemView::ScrollPerPixel); + ui->tableViewMachine->verticalHeader()->hide(); + + /* Memory table */ + ui->tableViewMemory->setModel(0); + ui->tableViewMemory->horizontalHeader()->setResizeMode( + QHeaderView::Interactive); + ui->tableViewMemory->setHorizontalScrollMode( + QAbstractItemView::ScrollPerPixel); + ui->tableViewMemory->setVerticalScrollMode( + QAbstractItemView::ScrollPerPixel); + ui->tableViewMemory->verticalHeader()->hide(); + ui->tableViewMemory->setSelectionBehavior(QAbstractItemView::SelectRows); + + /* Simulation */ + simulationThread = new SimulationThread(); + + defaultGuiSettings(); + + connect(ui->pushButtonNew, SIGNAL(clicked()), this, SLOT(newModel())); + connect(ui->pushButtonOpen, SIGNAL(clicked()), this, SLOT(openModel())); + connect(ui->pushButtonSave, SIGNAL(clicked()), this, SLOT(saveModel())); + connect(ui->pushButtonAddMemory, SIGNAL(clicked()), + this, SLOT(insertMemory())); + connect(ui->pushButtonDeleteMemory, SIGNAL(clicked()), + this, SLOT(deleteMemory())); + connect(ui->treeView_machines, SIGNAL(clicked(QModelIndex)), + this, SLOT(machineTreeClicked(QModelIndex))); + ui->treeView_machines->setContextMenuPolicy(Qt::CustomContextMenu); + connect(ui->treeView_machines, SIGNAL(customContextMenuRequested(QPoint)), + this, SLOT(machineTreeContextMenu(QPoint))); + connect(ui->pushButton_update, SIGNAL(clicked()), + this, SLOT(reload_scene())); +} + +MainWindow::~MainWindow() { + delete ui; +} + +/*void MainWindow::transitionChanged(QModelIndex topLeft, QModelIndex) +{ + //ui->label->setText(QString("transitionChanged r: %1 c: %2").arg(topLeft.row(), topLeft.column())); + + emit( updateScene() ); + + // If mpre was updated + if(topLeft.column() == 2) + { + //qDebug() << "mpre updated"; + + // If another branch from the same state exists + Arrow * a = machine->getTransitions().at(topLeft.row()); + for(int i = 0; i < machine->getTransitions().size(); i++) + { + Arrow * ab = machine->getTransitions().at(i); + if(a->startItem() == ab->startItem() && i != topLeft.row()) + { + qDebug() << "Found other branch: " << ab; + ab->getMprePointer()->setNot(true); + ab->getMprePointer()->setEnabled(a->getMpre().enabled()); + ab->getMprePointer()->setName(a->getMpre().name()); + ab->getMprePointer()->setOp(a->getMpre().op()); + ab->getMprePointer()->setValue(a->getMpre().value()); + } + } + } +}*/ + +void MainWindow::startModel() { + // Check for a valid start state + int flag = 0; // machine->checkValidStartState(); + if (flag == 0) { + emit(clearSceneSelection()); + // if(machine->getStartState() != 0) + // machine->getStartState()->setSelected(true); + + // ui->pushButtonPause->setEnabled(true); + // ui->pushButtonStop->setEnabled(true); + /*ui->treeWidget->setEnabled(false); + ui->graphicsView->setEnabled(false); + ui->tableViewMachine->setEnabled(false); + ui->tableViewMemory->setEnabled(false);*/ + + simulationThread->startSim(); + // ui->label->setText("Model started"); + } +/* else if(flag == 1) + ui->label->setText("Error: More than one start state possible"); + else if(flag == 2) + ui->label->setText("Error: No start states found");*/ +} + +void MainWindow::pauseModel() { + // ui->label->setText("Model paused"); +} + +void MainWindow::stopModel() { + // ui->label->setText("Model stopped"); + + // emit( clearSceneSelection() ); + + // ui->pushButtonPause->setEnabled(false); + // ui->pushButtonStop->setEnabled(false); + /*ui->treeWidget->setEnabled(true); + ui->graphicsView->setEnabled(true); + ui->tableViewMachine->setEnabled(true); + ui->tableViewMemory->setEnabled(true);*/ +} + +void MainWindow::selectTransition(QString n) { + for (int i = 0; + i < currentMachine->machineModel->getTransitions().size(); i++) { + if (currentMachine->machineModel->getTransitions().at(i)->name() == n) + ui->tableViewMachine->selectRow(i); + } + currentMachine->machineScene->selectTransition(n); +} + +void MainWindow::selectState(QString n) { + currentMachine->machineScene->selectState(n); +} + +void MainWindow::updateStatusLabel(QString /*s*/) { + // ui->label->setText(s); +} + +void MainWindow::insertMemory() { + currentMachine->memoryModel->insertRow( + currentMachine->memoryModel->rowCount()); +} + +void MainWindow::deleteMemory() { + QModelIndexList indexList = + ui->tableViewMemory->selectionModel()->selectedRows(); + + while (indexList.count() > 0) { + currentMachine->memoryModel->removeRow(indexList.at(0).row()); + indexList = ui->tableViewMemory->selectionModel()->selectedRows(); + } +} + +void MainWindow::newModel() { + // Add new machine to the machine tree + Machine * m = machineTree->addMachine(); + // Select file for model + // Provide dialog to select file + QString filepath = + QFileDialog::getSaveFileName(this, + tr("Choose file to save model to..."), ""); + + if (filepath.isEmpty()) { + machineTree->removeMachine(m); + return; + } + + // Get file directory and file name from file info + QFileInfo finfo(filepath); + m->fileDirectory = finfo.absolutePath(); // File directory + m->fileDirectory.append("/"); // Append directory slash + m->fileName = finfo.fileName(); // File name + + // Organise the tree view in the gui + handleNewAndOpenedModel(m); +} + +void MainWindow::openModel() { + QString fileName = + QFileDialog::getOpenFileName(this, tr("Open model"), + "", + tr("XML Files (*.xml)")); + if (fileName.isEmpty()) + return; + + QFile file(fileName); + if (!file.open(QFile::ReadOnly | QFile::Text)) { + QMessageBox::warning(this, tr("x-editor"), + tr("Cannot read file %1:\n%2.") + .arg(fileName) + .arg(file.errorString())); + return; + } + + // Add a new machine to the machine tree + Machine * m = machineTree->addMachine(); + // Read the model into the machine + modelXMLReader reader(m); + // If the read fails + if (!reader.read(&file)) { + QMessageBox::warning(this, "FLAME Editor", + tr("Cannot parse model in file %1 at line %2, column %3:\n%4").arg( + fileName).arg(reader.lineNumber()).arg( + reader.columnNumber()).arg(reader.errorString())); + // Delete model from tree + machineTree->removeMachine(m); + defaultGuiSettings(); + } else { // If the read is successful + // Organise the tree view in the gui + handleNewAndOpenedModel(m); + + statusBar()->showMessage(tr("File loaded"), 2000); + } +} + +void MainWindow::handleNewAndOpenedModel(Machine *m) { + // Organise model tree view + ui->treeView_machines->expandToDepth(0); + ui->treeView_machines->resizeColumnToContents(0); + // Select opened model + ui->treeView_machines->setCurrentIndex(machineTree->getIndex(m)); + // React as if it had been clicked by the user + machineTreeClicked(machineTree->getIndex(m)); +} + +void MainWindow::saveModel() { + QString fileName; + fileName.append(currentMachine->fileDirectory); + // Qt will translate these directory separators to the native OS + fileName.append("/"); + fileName.append(currentMachine->fileName); + + QFile file(fileName); + if (!file.open(QFile::WriteOnly | QFile::Text)) { + QMessageBox::warning(this, tr("FLAME Editor"), + tr("Cannot write file %1:\n%2.") + .arg(fileName) + .arg(file.errorString())); + return; + } + + if (currentMachine->writeModelXML(&file)) + statusBar()->showMessage(tr("File saved"), 2000); +} + +void addRestOfModel(Machine * m, Machine * agent, int foreign) { + if (m->type == 0) { // model + for (int i = 0; i < m->childCount(); i++) { + if (m->child(i)->type == 1) { // if agent + if ((agent->type == 1 && m->child(i)->name == agent->name && + m->child(i) != agent) || agent->type == 0) { + QList transitions = + m->child(i)->machineModel->getTransitions(); + + for (int j = 0; j < transitions.count(); j++) { + Transition * t = transitions.at(j); + agent->machineScene->addTransitionTransition( + m->child(i)->name, t, foreign); + } + } + } + if (foreign == 1) { + if (m->child(i)->type == 0) { // model + if (m->child(i) != agent) + addRestOfModel(m->child(i), agent, foreign); + } + } + } + } +} + +void MainWindow::machineTreeClicked(QModelIndex index) { + // Disconnect buttons from current machineScene + if (currentMachine != 0) + if (currentMachine->machineScene != 0) + disconnect(ui->pushButtonDeleteMemory, + SIGNAL(clicked()), + currentMachine->machineScene, + SLOT(deleteSelectedFunction())); + + // Get the machine that was clicked on + Machine * m = static_cast(index.internalPointer()); + // Make the currentMachine equal to the selected machine + currentMachine = m; + + // Set memory, machine and scene models to be null and update + ui->tableViewMemory->setModel(0); + ui->tableViewMemory->update(); + ui->tableViewMachine->setModel(0); + ui->tableViewMachine->update(); + ui->graphicsView->setScene(0); + ui->graphicsView->update(); + + // Set up buttons for models + if (m->type == 0) { + // If the model is a root model then it can be closed + if (m->rootModel() == m) ui->pushButtonClose->setEnabled(true); + else + ui->pushButtonClose->setEnabled(false); + // Models and sub models can be saved + ui->pushButtonSave->setEnabled(true); + // Put the file location as the window title + setWindowTitle(QString("FLAME Editor - ").append(m->filePath())); + } else { + // Only models can be closed and saved + ui->pushButtonClose->setEnabled(false); + ui->pushButtonSave->setEnabled(false); + // Reset the window title + setWindowTitle("FLAME Editor"); + } + + // Set up memory table for + // models, agents, messages, environment, datatypes + if (m->type == 0 || m->type == 1 || m->type == 2 + || m->type == 3 || m->type == 5) { + // Set up the table view for the current memory + ui->tableViewMemory->setModel(m->memoryModel); + if (m->type == 0) { + ui->tableViewMemory->setItemDelegateForColumn(1, + new TextEditDelegate()); + } else { + ui->tableViewMemory->setItemDelegateForColumn(0, + new DataTypeDelegate(m)); + ui->tableViewMemory->setItemDelegateForColumn(1, + new LineEditDelegate()); + ui->tableViewMemory->setItemDelegateForColumn(2, + new LineEditDelegate()); + } + ui->tableViewMemory->resizeColumnsToContents(); + ui->tableViewMemory->resizeRowsToContents(); + ui->tableViewMemory->update(); + + // Handle memory buttons + if (m->type == 0) { // Model memory cannot be added to or deleted + // Disable memory buttons + ui->pushButtonAddMemory->setEnabled(false); + ui->pushButtonDeleteMemory->setEnabled(false); + } else { + // Enable memory buttons + ui->pushButtonAddMemory->setEnabled(true); + ui->pushButtonDeleteMemory->setEnabled(true); + } + } else { + // Disable memory buttons + ui->pushButtonAddMemory->setEnabled(false); + ui->pushButtonDeleteMemory->setEnabled(false); + } + + // Set up machine table for + // agents + if (m->type == 1) { + // Set up the table view for the current machine model + ui->tableViewMachine->setModel(m->machineModel); + ui->tableViewMachine->setItemDelegateForColumn(0, + new LineEditDelegate()); + ui->tableViewMachine->setItemDelegateForColumn(1, + new CommDelegate(m)); + ui->tableViewMachine->setItemDelegateForColumn(2, + new MpreDelegate(m)); + ui->tableViewMachine->setItemDelegateForColumn(3, + new LineEditDelegate()); + ui->tableViewMachine->setItemDelegateForColumn(4, + new MpostDelegate(m->memoryModel)); + ui->tableViewMachine->setItemDelegateForColumn(5, + new CommDelegate(m)); + ui->tableViewMachine->setItemDelegateForColumn(6, + new LineEditDelegate()); + ui->tableViewMachine->setItemDelegateForColumn(7, + new LineEditDelegate()); + ui->tableViewMachine->resizeColumnsToContents(); + ui->tableViewMachine->resizeRowsToContents(); + ui->tableViewMachine->update(); + + // When input/output messages have been edited then + // resize the rows depending on the new contents + connect(ui->tableViewMachine->itemDelegateForColumn(1), + SIGNAL(closeEditor(QWidget*)), + ui->tableViewMachine, SLOT(resizeRowsToContents())); + connect(ui->tableViewMachine->itemDelegateForColumn(5), + SIGNAL(closeEditor(QWidget*)), + ui->tableViewMachine, SLOT(resizeRowsToContents())); + connect(m->machineModel, SIGNAL(communicationChanged()), + ui->tableViewMachine, SLOT(resizeRowsToContents())); + } + + // Set up graphics scene for + // models, agents + if (m->type == 0 || m->type == 1) { + // Load other states and transitions from the rest of the other model + addRestOfModel(m, m, 0); // Add local agent functions + addRestOfModel(m->rootModel(), m, 1); // Add global agent functions + + // Set up graphics view + ui->graphicsView->setScene(m->machineScene); + m->machineScene->setGraphicsView(ui->graphicsView); + m->machineScene->calcLayers(); + ui->graphicsView->setAlignment(Qt::AlignTop); + ui->graphicsView->verticalScrollBar()->setValue(0); + ui->graphicsView->show(); + + if (m->type == 1) { // if agent + connect(m->machineScene, SIGNAL(functionSelected(bool)), + ui->pushButtonSceneDelete, SLOT(setEnabled(bool))); + connect(ui->pushButtonSceneDelete, SIGNAL(clicked()), + m->machineScene, SLOT(deleteSelectedFunction())); + } + + // Enable/disable graph buttons + ui->pushButtonSceneDelete->setEnabled(false); + ui->pushButton_viewModel->setEnabled(true); + ui->pushButton_update->setEnabled(true); + } else { + // Disable graph buttons + ui->pushButton_viewModel->setEnabled(false); + ui->pushButton_update->setEnabled(false); + } +} + +void MainWindow::machineTreeContextMenu(QPoint p) { + QPoint globalPos = ui->treeView_machines->mapToGlobal(p); + QModelIndex index = ui->treeView_machines->indexAt(p); + Machine * m = static_cast(index.internalPointer()); + + QMenu myMenu; + myMenu.addAction("Add agent"); + myMenu.addAction("Add message"); + myMenu.addAction("Add function file"); + myMenu.addAction("Add sub model"); + myMenu.addAction("Add time unit"); + myMenu.addAction("Add data type"); + if (m->type == 0) { + if (m->rootModel() != m) myMenu.addAction("Remove sub model"); } + else if (m->type == 1) myMenu.addAction("Delete agent"); + else if (m->type == 2) myMenu.addAction("Delete message"); + else if (m->type == 4) myMenu.addAction("Delete time unit"); + else if (m->type == 5) myMenu.addAction("Delete data type"); + else if (m->type == 6) myMenu.addAction("Delete function file"); + + QAction* selectedItem = myMenu.exec(globalPos); + if (selectedItem) { + if (selectedItem->text().startsWith("Add")) { + // Find current model/sub model + Machine * modelm = m; // The model machine to add to + while (modelm->type != 0) modelm = modelm->parent(); + int newType = 0; + + if (selectedItem->text() == "Add agent") newType = 1; + else if (selectedItem->text() == "Add message") newType = 2; + else if (selectedItem->text() == "Add function file") newType = 6; + else if (selectedItem->text() == "Add sub model") newType = 0; + else if (selectedItem->text() == "Add time unit") newType = 4; + else if (selectedItem->text() == "Add data type") newType = 5; + Machine * m1 = machineTree->insertMachine(modelm, newType); + if (newType == 0) { // Sub model + // Select file for sub model + QString s = modelm->fileDirectory; + /* Provide dialog to select folder */ + QString filepath = + QFileDialog::getSaveFileName(this, + tr("Sub model file"), s); + /* If new file is selected, i.e. the user canceled */ + if (filepath.isEmpty()) { + machineTree->removeMachine(m1); + return; + } + /* Return relative path from currentPath to location */ + QDir dir(modelm->fileDirectory); + QString s1 = dir.canonicalPath(); + QDir dir2(s1); + s = dir2.relativeFilePath(filepath); + QFileInfo finfo(filepath); + QString s2 = finfo.path(); // canonicalPath(); + // Cannot use canonical because file needs to exist + m1->fileDirectory = s2; + m1->fileName = s; + } else if (newType == 1) { // Agent + int count = 0; + // Count number of agents + for (int i = 0; i < modelm->childCount(); i++) + if (modelm->child(i)->type == 1) count++; + m1->name = QString("agent_%1").arg(QString::number(count-1)); + // Add a starting transition + m1->addTransitionString("transition_0", "start", "end", + Condition(), Mpost(), + Communication(), Communication(), ""); + } else if (newType == 2) { // Message + int count = 0; + // Count number of agents + for (int i = 0; i < modelm->childCount(); i++) + if (modelm->child(i)->type == 2) count++; + m1->name = QString("message_%1").arg(QString::number(count-1)); + } else if (newType == 4) { // Time unit + // Name the time unit as it can be used + // straight away as a variable type + // Count the number of time units and use this for the name + int tuCount = 0; + // Find environment machine + Machine * env = 0; + for (int i = 0; i < modelm->childCount(); i++) + if (modelm->child(i)->type == 3) env = modelm->child(i); + // Count number of data types + for (int i = 0; i < env->childCount(); i++) + if (env->child(i)->type == 4) tuCount++; + m1->setTimeUnitName(QString("timeunit_%1").arg( + QString::number(tuCount-1))); + } else if (newType == 5) { // Data type + // Name the data type as it can be used + // straight away as a variable type + // Count the number of data types and use this for the name + int dtCount = 0; + // Find environment machine + Machine * env = 0; + for (int i = 0; i < modelm->childCount(); i++) + if (modelm->child(i)->type == 3) env = modelm->child(i); + // Count number of data types + for (int i = 0; i < env->childCount(); i++) + if (env->child(i)->type == 5) dtCount++; + m1->name = + QString("datatype_%1").arg(QString::number(dtCount-1)); + } + // Select the machine in the tree + QModelIndex index = machineTree->getIndex(m1); // Get its index + ui->treeView_machines->scrollTo(index); // Scroll to it + ui->treeView_machines->setCurrentIndex(index); // Select it + emit(machineTreeClicked(index)); // Act like it has been clicked + } else if (selectedItem->text().startsWith("Delete") || + selectedItem->text().startsWith("Remove")) { + QMessageBox msgBox; + QString text; + if (selectedItem->text().startsWith("Delete")) + text.append("Do you want to delete the "); + else if (selectedItem->text().startsWith("Remove")) + text.append("Do you want to remove the "); + if (m->type == 0) text.append("sub model: "); + else if (m->type == 1) text.append("agent: "); + else if (m->type == 2) text.append("message: "); + else if (m->type == 4) text.append("time unit: "); + else if (m->type == 5) text.append("data type: "); + else if (m->type == 6) text.append("function file: "); + if (m->type == 4) text.append(m->timeUnit().name); + else + text.append(m->name); + text.append("?"); + msgBox.setText(text); + msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); + msgBox.setDefaultButton(QMessageBox::Yes); + if (msgBox.exec() == QMessageBox::Yes) { + machineTree->removeMachine(m); + } + } + } else { + // nothing was chosen + // qDebug() << "nothing chosen"; + } +} + +void MainWindow::repositionView(float x, float y) { + ui->graphicsView->centerOn(x, y); +} + +void MainWindow::on_pushButton_viewModel_clicked() { + QGraphicsView * modelView = new QGraphicsView(currentMachine->machineScene); + currentMachine->machineScene->setGraphicsView(modelView); + modelView->show(); + modelView->fitInView(currentMachine->machineScene->sceneRect(), + Qt::KeepAspectRatioByExpanding); +} + +void MainWindow::on_pushButtonClose_clicked() { + machineTree->removeMachine(currentMachine); + defaultGuiSettings(); +} + +void MainWindow::defaultGuiSettings() { + /* Disable gui features */ + ui->tableViewMemory->setModel(0); + ui->tableViewMemory->update(); + ui->tableViewMachine->setModel(0); + ui->tableViewMachine->update(); + ui->graphicsView->setScene(0); + ui->graphicsView->update(); + ui->pushButton_viewModel->setEnabled(false); + ui->pushButtonAddMemory->setEnabled(false); + ui->pushButtonClose->setEnabled(false); + ui->pushButtonDeleteMemory->setEnabled(false); + ui->pushButtonSave->setEnabled(false); + ui->pushButtonSceneDelete->setEnabled(false); + ui->pushButton_update->setEnabled(false); +} + +void MainWindow::on_actionHelp_triggered() { + QTextBrowser *help = new QTextBrowser(this); + help->setWindowFlags(Qt::Dialog); + help->setReadOnly(true); + help->setAutoFormatting(QTextEdit::AutoNone); + help->setOpenExternalLinks(true); + QString helpText; + helpText.append("

FLAME Editor Help

"); + helpText.append(""); + helpText.append("Follow this link to the online help page."); + + help->setGeometry(50, 50, 600, 400); + help->insertHtml(helpText); + help->moveCursor(QTextCursor::Start); + help->show(); +} + +void MainWindow::on_actionAbout_triggered() { + QTextEdit *about = new QTextEdit(this); + about->setWindowFlags(Qt::Dialog); + about->setReadOnly(true); + about->setAutoFormatting(QTextEdit::AutoNone); + + QString aboutText; + aboutText.append("

FLAME Editor

"); + aboutText.append("

Simon Coakley

"); + aboutText.append("

Version 1

"); + aboutText.append("

Changelog

"); + /* Add new release notes here */ + aboutText.append("

Version 1 (released 2012-01-13)

"); + aboutText.append("
  • Beta first release
"); + + about->setGeometry(50, 50, 600, 400); + about->insertHtml(aboutText); + about->moveCursor(QTextCursor::Start); + about->show(); +} + +void MainWindow::reload_scene() { + currentMachine->machineScene->calcLayers(); +} diff --git a/mainwindow.h b/mainwindow.h new file mode 100644 index 0000000..2b5cb93 --- /dev/null +++ b/mainwindow.h @@ -0,0 +1,74 @@ +/*! + * \file mainwindow.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for main window +*/ +#ifndef MAINWINDOW_H_ +#define MAINWINDOW_H_ + +#include +#include +#include +#include +#include "./machinemodel.h" +#include "./memorymodel.h" +#include "./simulationthread.h" +#include "./arrow.h" +#include "./machine.h" +#include "./machinescene.h" +#include "./machinetree.h" + +namespace Ui { + class MainWindowClass; +} + +class MainWindow : public QMainWindow { + Q_OBJECT + + public: + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); + + signals: + void clearSceneSelection(); + void updateScene(); + void startSimulation(); + void pauseSimulation(); + + public slots: + void startModel(); + void pauseModel(); + void stopModel(); + void selectTransition(QString n); + void selectState(QString n); + void updateStatusLabel(QString s); + void insertMemory(); + void deleteMemory(); + void newModel(); + void openModel(); + void saveModel(); + void machineTreeClicked(QModelIndex); + void machineTreeContextMenu(QPoint); + void repositionView(float x, float y); + + private slots: + void on_pushButton_viewModel_clicked(); + void on_pushButtonClose_clicked(); + void on_actionHelp_triggered(); + void on_actionAbout_triggered(); + void reload_scene(); + + private: + void handleNewAndOpenedModel(Machine * m); + void defaultGuiSettings(); + Ui::MainWindowClass *ui; + SimulationThread * simulationThread; + QToolBar * fileToolBar; + MachineTree * machineTree; + QList machines; + Machine * currentMachine; +}; + +#endif // MAINWINDOW_H_ diff --git a/mainwindow.ui b/mainwindow.ui new file mode 100644 index 0000000..1d2ab9b --- /dev/null +++ b/mainwindow.ui @@ -0,0 +1,216 @@ + + + MainWindowClass + + + + 0 + 0 + 924 + 567 + + + + FLAME Editor + + + + + + + Qt::Vertical + + + + Qt::Horizontal + + + + + + + New + + + + + + + Open + + + + + + + Save + + + + + + + + + + Models + + + + + + + Close + + + + + + + + Qt::Horizontal + + + + + + + + + + Add Variable + + + + + + + Delete Variable + + + + + + + Memory + + + + + + + + + + + Qt::NoFocus + + + + + + + Delete + + + + + + + View In Window + + + + + + + Stategraph + + + + + + + Replot + + + + + + + + + + + + + Transition Functions + + + + + + + + + + + + + + + + 0 + 0 + 924 + 22 + + + + + File + + + + + + + + + TopToolBarArea + + + false + + + + + + &New + + + + + &Open + + + + + &Save + + + + + About + + + + + Help + + + + + + + diff --git a/memorydelegate.cpp b/memorydelegate.cpp new file mode 100644 index 0000000..3e81915 --- /dev/null +++ b/memorydelegate.cpp @@ -0,0 +1,145 @@ +/*! + * \file memorydelegate.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of the model memory delegate + */ +#include +#include "./memorydelegate.h" + +MemoryDelegate::MemoryDelegate(QObject *parent) + : QItemDelegate(parent) { +} + +QWidget *MemoryDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, + const QModelIndex &index) const { + /*if(index.column() == 0) + { + QComboBox *editor = new QComboBox(parent); + editor->insertItem(0, "int"); + editor->insertItem(1, "double"); + + return editor; + } + else if(index.column() == 2) + { + QModelIndex typeIndex = index.model()->index(index.row(), 0, QModelIndex()); + + if (typeIndex.data().toString() == "int") + { + QSpinBox *editor = new QSpinBox(parent); + editor->setMinimum(-1000); + editor->setMaximum(1000); + return editor; + } + //if (typeIndex.data().toString() == "double") + else + { + QDoubleSpinBox *editor = new QDoubleSpinBox(parent); + editor->setMinimum(-1000); + editor->setMaximum(1000); + return editor; + } + } + else */if(index.column() == 0 || index.column() == 1 || + index.column() == 2) { + QLineEdit *editor = new QLineEdit(parent); + return editor; + } else { + return QItemDelegate::createEditor(parent, option, index); + } +} + +void MemoryDelegate::setEditorData(QWidget *editor, + const QModelIndex &index) const { + /*if(index.column() == 0) + { + QString value = index.data().toString(); + + QComboBox *comboBox = static_cast(editor); + for(int i = 0; i < comboBox->count(); i++) + { + if(comboBox->itemText(i) == value) + comboBox->setCurrentIndex(i); + } + } + else if(index.column() == 2) + { + QModelIndex typeIndex = index.model()->index(index.row(), 0, QModelIndex()); + + if (typeIndex.data().toString() == "int") + { + int value = index.data().toInt(); + + QSpinBox *spinBox = static_cast(editor); + spinBox->setValue(value); + } + //if (typeIndex.data().toString() == "double") + else + { + double value = index.data().toDouble(); + + QDoubleSpinBox *spinBox = static_cast(editor); + spinBox->setValue(value); + } + } + else */if(index.column() == 0 || index.column() == 1 || + index.column() == 2) { + QString value = index.data().toString(); + + QLineEdit * lineEdit = static_cast(editor); + lineEdit->setText(value); + } else { + QItemDelegate::setEditorData(editor, index); + } +} + +void MemoryDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const { + /*if(index.column() == 0) + { + QComboBox *comboBox = static_cast(editor); + QString value = comboBox->currentText(); + + model->setData(index, value, Qt::EditRole); + } + else if(index.column() == 2) + { + QModelIndex typeIndex = index.model()->index(index.row(), 0, QModelIndex()); + + if (typeIndex.data().toString() == "int") + { + QSpinBox *spinBox = static_cast(editor); + spinBox->interpretText(); + int value = spinBox->value(); + + model->setData(index, value, Qt::EditRole); + } + //if (typeIndex.data().toString() == "double") + else + { + QDoubleSpinBox *spinBox = static_cast(editor); + //spinBox->interpretText(); + double value = spinBox->value(); + + model->setData(index, value, Qt::EditRole); + } + } + else */ + if (index.column() == 0 || index.column() == 1 || + index.column() == 2) { + QLineEdit *lineEdit = static_cast(editor); + QString value = lineEdit->text(); + + model->setData(index, value, Qt::EditRole); + } else { + QItemDelegate::setModelData(editor, model, index); + } +} + +void MemoryDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &/*index*/) const { + editor->setGeometry(option.rect); +} diff --git a/memorydelegate.h b/memorydelegate.h new file mode 100644 index 0000000..2cd6dc9 --- /dev/null +++ b/memorydelegate.h @@ -0,0 +1,37 @@ +/*! + * \file memorydelegate.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for the memory table delegate +*/ +#ifndef MEMORYDELEGATE_H_ +#define MEMORYDELEGATE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +class MemoryDelegate : public QItemDelegate { + Q_OBJECT + + public: + explicit MemoryDelegate(QObject *parent = 0); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + + void updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const; +}; + +#endif // MEMORYDELEGATE_H_ diff --git a/memorymodel.cpp b/memorymodel.cpp new file mode 100644 index 0000000..68f6833 --- /dev/null +++ b/memorymodel.cpp @@ -0,0 +1,210 @@ +/*! + * \file memorymodel.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of model memory table + */ +#include +#include "./memorymodel.h" + +/*MemoryModel::MemoryModel() +{ +}*/ + +int MemoryModel::rowCount(const QModelIndex &/*parent*/) const { + // return stringListType.count(); + return variables.count(); +} + +int MemoryModel::columnCount(const QModelIndex &/*parent*/) const { + if (myIsModel) + return 2; + else + return 3; +} + +QVariant MemoryModel::data(const QModelIndex &index, int role) const { + if (!index.isValid()) + return QVariant(); + + if (index.row() >= variables.size()) + return QVariant(); + + if (role == Qt::DisplayRole) { + if (myIsModel) { + if (index.column() == 0) + return variables.at(index.row()).name; + if (index.column() == 1) + return variables.at(index.row()).description; + } else { + if (index.column() == 0) + return variables.at(index.row()).type; + // stringListType.at(index.row()); + if (index.column() == 1) + return variables.at(index.row()).name; + // stringListName.at(index.row()); + if (index.column() == 2) + return variables.at(index.row()).description; + /*if(index.column() == 2) + { + //return variables[index.row()].getValue(); + if(variables.at(index.row()).type == "int") + return variables.at(index.row()).ivalue; + else + return variables.at(index.row()).dvalue; + }*/ + } + return QVariant(); + } else { + return QVariant(); + } +} + +QVariant MemoryModel::headerData(int section, Qt::Orientation orientation, + int role) const { + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) { + if (myIsModel) { + if (section == 0) return QString("Type"); + else if (section == 1) return QString("Value"); + } else { + if (section == 0) return QString("Type"); + else if (section == 1) return QString("Name"); + else if (section == 2) return QString("Description"); + } + return QString(""); + } else { + return QString("Row %1").arg(section); + } +} + +Qt::ItemFlags MemoryModel::flags(const QModelIndex &index) const { + if (!index.isValid()) + return Qt::ItemIsEnabled; + + if (myIsModel && index.column() == 0) + return QAbstractItemModel::flags(index); + else + return QAbstractItemModel::flags(index) | Qt::ItemIsEditable; +} + +bool MemoryModel::setData(const QModelIndex &index, + const QVariant &value, int role) { + if (index.isValid() && role == Qt::EditRole) { + if (isModel()) { + if (index.column() == 0) + variables[index.row()].name = value.toString(); + if (index.column() == 1) + variables[index.row()].description = value.toString(); + } else { + if (index.column() == 0) + variables[index.row()].type = value.toString(); + // stringListType.replace(index.row(), value.toString()); + if (index.column() == 1) + variables[index.row()].name = value.toString(); + // stringListName.replace(index.row(), value.toString()); + if (index.column() == 2) + variables[index.row()].description = value.toString(); + /*if(index.column() == 2) + //variables[index.row()].setValue(value.toDouble()); + { + variables[index.row()].ivalue = value.toInt(); + variables[index.row()].dvalue = value.toDouble(); + }*/ + } + + emit dataChanged(index, index); + return true; + } + return false; +} + +bool MemoryModel::insertRows(int position, int rows, + const QModelIndex &/*parent*/) { + beginInsertRows(QModelIndex(), position, position+rows-1); + + for (int row = 0; row < rows; ++row) { + QString s = "name_"; + s.append(QString("%1").arg(this->rowCount())); + + variables.insert(position, Variable(s, "int")); + + /*stringListType.insert(position, "int"); + stringListName.insert(position, s); + intListValue.insert(position, 0); + doubleListValue.insert(position, 0);*/ + } + + endInsertRows(); + return true; +} + +bool MemoryModel::removeRows(int position, int rows, + const QModelIndex &/*parent*/) { + beginRemoveRows(QModelIndex(), position, position+rows-1); + + for (int row = 0; row < rows; ++row) { + variables.removeAt(position); + + /*stringListType.removeAt(position); + stringListName.removeAt(position); + intListValue.removeAt(position); + doubleListValue.removeAt(position);*/ + } + + endRemoveRows(); + return true; +} + +/*void MemoryModel::replaceIntValue(int i, int value) +{ + intListValue.replace(i, value); + + QModelIndex myIndex = this->index(i, 2, QModelIndex()); + + emit( this->dataChanged(myIndex, myIndex) ); +}*/ + +void MemoryModel::replaceValue(int i, double value) { + variables[i].ivalue = static_cast(value); + variables[i].dvalue = value; + + QModelIndex myIndex = this->index(i, 2, QModelIndex()); + emit(this->dataChanged(myIndex, myIndex)); +} + +void MemoryModel::replaceValue(QString type, QString value) { + for (int i = 0; i < variables.size(); i++) { + if (variables[i].name == type) variables[i].description = value; + QModelIndex myIndex = this->index(i, 1, QModelIndex()); + emit(this->dataChanged(myIndex, myIndex)); + } +} + +void MemoryModel::addVariable(QString t, QString n, QString desc, + bool constant, double i) { + // if(myIsModel) qDebug() << n << desc; + + insertRow(rowCount()); + variables[(rowCount()-1)].type = t; + variables[(rowCount()-1)].name = n; + variables[(rowCount()-1)].description = desc; + variables[(rowCount()-1)].constant = constant; + variables[(rowCount()-1)].ivalue = static_cast(i); + variables[(rowCount()-1)].dvalue = i; + /*stringListType.replace(rowCount()-1, t); + stringListName.replace(rowCount()-1, n); + intListValue.replace(rowCount()-1, (int)i); + doubleListValue.replace(rowCount()-1, i);*/ +} + +QStringList MemoryModel::getNames() { + QStringList names; + for (int i = 0; i < variables.count(); i++) { + names.append(variables[i].name); + } + return names; +} diff --git a/memorymodel.h b/memorymodel.h new file mode 100644 index 0000000..857411f --- /dev/null +++ b/memorymodel.h @@ -0,0 +1,52 @@ +/*! + * \file memorymodel.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for the memory table +*/ +#ifndef MEMORYMODEL_H_ +#define MEMORYMODEL_H_ + +#include +#include +#include +#include "./variable.h" + +class MemoryModel : public QAbstractTableModel { + Q_OBJECT + + public: + explicit MemoryModel(QObject *parent = 0) + : QAbstractTableModel(parent) { myIsModel = false; } + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + /* Functions to provide editing */ + Qt::ItemFlags flags(const QModelIndex &index) const; + bool setData(const QModelIndex &index, const QVariant &value, + int role = Qt::EditRole); + /* Adding and removing rows */ + bool insertRows(int position, int rows, + const QModelIndex &index = QModelIndex()); + bool removeRows(int position, int rows, + const QModelIndex &index = QModelIndex()); + QList getVariables() { return variables; } + QStringList getNames(); + void replaceValue(int index, double value); + void replaceValue(QString type, QString value); + void addVariable(QString type, QString name, + QString desc, bool constant = false, double i = 0.0); + void setIsModel(bool b) { myIsModel = b; } + bool isModel() { return myIsModel; } + + QList variables; + + private: + bool myIsModel; /*!< Flag to show if the memory is for model descriptors */ +}; + +#endif // MEMORYMODEL_H_ diff --git a/memoryvariable.cpp b/memoryvariable.cpp new file mode 100644 index 0000000..8e183c6 --- /dev/null +++ b/memoryvariable.cpp @@ -0,0 +1,16 @@ +/*! + * \file memoryvariable.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of memory variable + */ +#include "./memoryvariable.h" + +MemoryVariable::MemoryVariable(QString n, QString d, ADT t, int s) { + myName = n; + myDesc = d; + myType = t; + mySize = s; + myValue = 0; +} diff --git a/memoryvariable.h b/memoryvariable.h new file mode 100644 index 0000000..cbf35d1 --- /dev/null +++ b/memoryvariable.h @@ -0,0 +1,37 @@ +/*! + * \file memoryvariable.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for memory variable +*/ +#ifndef MEMORYVARIABLE_H_ +#define MEMORYVARIABLE_H_ + +#include +#include "./adt.h" + +class MemoryVariable { + public: + MemoryVariable(QString n = "", QString d = "", ADT t = ADT(), int s = 1); + + void setName(QString n) { myName = n; } + QString name() const { return myName; } + void setType(ADT t) { myType = t; } + ADT type() const { return myType; } + void setValue(void * v) { myValue = v; } + void * value() const { return myValue; } + void setSize(int s) { mySize = s; } + int size() const { return mySize; } + void setDesc(QString d) { myDesc = d; } + QString desc() const { return myDesc; } + + private: + QString myName; + QString myDesc; + ADT myType; + void * myValue; + int mySize; /* array size */ +}; + +#endif // MEMORYVARIABLE_H_ diff --git a/messagecomm.cpp b/messagecomm.cpp new file mode 100644 index 0000000..70ed1bb --- /dev/null +++ b/messagecomm.cpp @@ -0,0 +1,35 @@ +/*! + * \file messagecomm.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of message communication + */ +#include "./messagecomm.h" + +MessageComm::MessageComm() { + messageType = ""; +} + +MessageComm::MessageComm(QString m) { + messageType = m; +} + +QString MessageComm::toString() { + QString s; + s.append(messageType); + if (this->filter.enabled) { + s.append(" filter("); + s.append(filter.toString()); + s.append(")"); + } + if (sort.isRandom) s.append(" random"); + if (sort.isSort) { + s.append(" sort("); + s.append(sort.key); + s.append(", "); + s.append(sort.order); + s.append(")"); + } + return s; +} diff --git a/messagecomm.h b/messagecomm.h new file mode 100644 index 0000000..aeed84a --- /dev/null +++ b/messagecomm.h @@ -0,0 +1,27 @@ +/*! + * \file messagecomm.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for a message communication +*/ +#ifndef MESSAGECOMM_H_ +#define MESSAGECOMM_H_ + +#include +#include "./condition.h" +#include "./messagesort.h" + +class MessageComm { + public: + MessageComm(); + explicit MessageComm(QString m); + + QString toString(); + + QString messageType; + Condition filter; + MessageSort sort; +}; + +#endif // MESSAGECOMM_H_ diff --git a/messagedelegate.cpp b/messagedelegate.cpp new file mode 100644 index 0000000..08ee705 --- /dev/null +++ b/messagedelegate.cpp @@ -0,0 +1,60 @@ +/*! + * \file messagedelegate.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of message delegate + */ +#include +#include "./messagedelegate.h" + +MessageDelegate::MessageDelegate(QStringList mts, QObject *parent) + : QItemDelegate(parent) { + messageTypes = mts; +} + + +QWidget *MessageDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, + const QModelIndex &index) const { + if (index.column() == 0) { + QComboBox *editor = new QComboBox(parent); + editor->addItems(messageTypes); + + return editor; + } else { + return QItemDelegate::createEditor(parent, option, index); + } +} + +void MessageDelegate::setEditorData(QWidget *editor, + const QModelIndex &index) const { + if (index.column() == 0) { + QString value = index.data().toString(); + + QComboBox *comboBox = static_cast(editor); + for (int i = 0; i < comboBox->count(); i++) { + if (comboBox->itemText(i) == value) + comboBox->setCurrentIndex(i); + } + } else { + QItemDelegate::setEditorData(editor, index); + } +} + +void MessageDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const { + if (index.column() == 0) { + QComboBox *comboBox = static_cast(editor); + QString value = comboBox->currentText(); + + model->setData(index, value, Qt::EditRole); + } else { + QItemDelegate::setModelData(editor, model, index); + } +} + +void MessageDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &/*index*/) const { + editor->setGeometry(option.rect); +} diff --git a/messagedelegate.h b/messagedelegate.h new file mode 100644 index 0000000..2e4585f --- /dev/null +++ b/messagedelegate.h @@ -0,0 +1,38 @@ +/*! + * \file messagedelegate.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for the message delegate +*/ +#ifndef MESSAGEDELEGATE_H_ +#define MESSAGEDELEGATE_H_ + +#include +#include +#include +#include +#include +#include + +class MessageDelegate : public QItemDelegate { + Q_OBJECT + + public: + MessageDelegate(QStringList messageTypes, QObject *parent = 0); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + + void updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const; + + private: + QStringList messageTypes; +}; + +#endif // MESSAGEDELEGATE_H_ diff --git a/messagemodel.cpp b/messagemodel.cpp new file mode 100644 index 0000000..bdfb758 --- /dev/null +++ b/messagemodel.cpp @@ -0,0 +1,115 @@ +/*! + * \file messagemodel.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of the message table + */ +#include "./messagemodel.h" + +int MessageModel::rowCount(const QModelIndex &/*parent*/) const { + return messages.count(); +} + +int MessageModel::columnCount(const QModelIndex &/*parent*/) const { + if (flag == 1) + return 3; + else + if (flag == 0) return 1; + return 3; +} + +QVariant MessageModel::data(const QModelIndex &index, int role) const { + if (!index.isValid()) + return QVariant(); + + if (index.row() >= messages.size()) + return QVariant(); + + if (role == Qt::DisplayRole) { + if (index.column() == 0) + return messages.at(index.row()).messageType; + // stringListType.at(index.row()); + if (index.column() == 1) + return qVariantFromValue(messages.at(index.row()).filter); + if (index.column() == 2) + return qVariantFromValue(messages.at(index.row()).sort); + return QVariant(); + } else { + return QVariant(); + } +} + +QVariant MessageModel::headerData(int section, Qt::Orientation orientation, + int role) const { + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) { + if (section == 0) return QString("Message Type"); + else if (section == 1) return QString("Filter"); + else if (section == 2) return QString("Sort"); + return QString(""); + } else { + return QString("Row %1").arg(section); + } +} + +Qt::ItemFlags MessageModel::flags(const QModelIndex &index) const { + if (!index.isValid()) + return Qt::ItemIsEnabled; + + return QAbstractItemModel::flags(index) | Qt::ItemIsEditable; +} + +bool MessageModel::setData(const QModelIndex &index, + const QVariant &value, int role) { + if (index.isValid() && role == Qt::EditRole) { + if (index.column() == 0) + messages[index.row()].messageType = value.toString(); + if (index.column() == 1) + messages[index.row()].filter = qVariantValue(value); + if (index.column() == 2) + messages[index.row()].sort = qVariantValue(value); + + emit dataChanged(index, index); + return true; + } + return false; +} + +bool MessageModel::insertRows(int position, int rows, + const QModelIndex &/*parent*/) { + beginInsertRows(QModelIndex(), position, position+rows-1); + + for (int row = 0; row < rows; ++row) { + messages.insert(position, MessageComm()); + } + + endInsertRows(); + return true; +} + +bool MessageModel::removeRows(int position, int rows, + const QModelIndex &/*parent*/) { + beginRemoveRows(QModelIndex(), position, position+rows-1); + + for (int row = 0; row < rows; ++row) { + messages.removeAt(position); + } + + endRemoveRows(); + return true; +} + +void MessageModel::addMessage(MessageComm mc) { + beginInsertRows(QModelIndex(), messages.count(), messages.count()); + messages.insert(messages.count(), mc); + endInsertRows(); +} + +void MessageModel::addMessage(QString n) { + MessageComm mc; + mc.messageType = n; + addMessage(mc); +} diff --git a/messagemodel.h b/messagemodel.h new file mode 100644 index 0000000..1f80684 --- /dev/null +++ b/messagemodel.h @@ -0,0 +1,45 @@ +/*! + * \file messagemodel.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for message table model +*/ +#ifndef MESSAGEMODEL_H_ +#define MESSAGEMODEL_H_ + +#include +#include +#include +#include "./messagecomm.h" + +class MessageModel : public QAbstractTableModel { + Q_OBJECT + + public: + MessageModel(int f, QObject *parent = 0) + : QAbstractTableModel(parent) { flag = f; } + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + /* Functions to provide editing */ + Qt::ItemFlags flags(const QModelIndex &index) const; + bool setData(const QModelIndex &index, const QVariant &value, + int role = Qt::EditRole); + /* Adding and removing rows */ + bool insertRows(int position, int rows, + const QModelIndex &index = QModelIndex()); + bool removeRows(int position, int rows, + const QModelIndex &index = QModelIndex()); + QList getMessages() { return messages; } + void addMessage(MessageComm mc); + void addMessage(QString n); + + QList messages; + int flag; +}; + +#endif // MESSAGEMODEL_H_ diff --git a/messagesort.cpp b/messagesort.cpp new file mode 100644 index 0000000..e4011a8 --- /dev/null +++ b/messagesort.cpp @@ -0,0 +1,37 @@ +/*! + * \file messagesort.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of message sort + */ +#include "./messagesort.h" + +MessageSort::MessageSort() { + isRandom = false; + isSort = false; + key = ""; + order = ""; +} + +void MessageSort::paint(QPainter *painter, const QRect &rect, + const QPalette &/*palette*/, EditMode /*mode*/) const { + painter->save(); + + painter->setRenderHint(QPainter::Antialiasing, true); + + QString s; + if (isRandom) + s.append("random"); + else + if (isSort) { + s.append("sort("); + s.append(key); + s.append(", "); + s.append(order); + s.append(")"); + } + painter->drawText(rect, s); + + painter->restore(); +} diff --git a/messagesort.h b/messagesort.h new file mode 100644 index 0000000..3fe6c42 --- /dev/null +++ b/messagesort.h @@ -0,0 +1,32 @@ +/*! + * \file messagesort.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for the message sort +*/ +#ifndef MESSAGESORT_H_ +#define MESSAGESORT_H_ + +#include +#include +#include + +class MessageSort { + public: + enum EditMode { Editable, ReadOnly }; + + MessageSort(); + + void paint(QPainter *painter, const QRect &rect, + const QPalette &palette, EditMode mode) const; + + bool isRandom; + bool isSort; + QString key; + QString order; +}; + +Q_DECLARE_METATYPE(MessageSort) + +#endif // MESSAGESORT_H_ diff --git a/modelxmlreader.cpp b/modelxmlreader.cpp new file mode 100644 index 0000000..0667c41 --- /dev/null +++ b/modelxmlreader.cpp @@ -0,0 +1,713 @@ +/*! + * \file modelxmlreader.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of the XML model reader + */ +#include +#include "./modelxmlreader.h" +#include "./timeunit.h" + +modelXMLReader::modelXMLReader(Machine * m) { + machine = m; +} + +bool modelXMLReader::read(QFile *f) { // (QIODevice * device) + file = f; + setDevice(file); + + QFileInfo fileInfo(file->fileName()); + machine->fileDirectory = fileInfo.absolutePath(); + machine->fileName = fileInfo.fileName(); + + while (!atEnd()) { + readNext(); + + if (isStartElement()) { + if (name() == "xmodel" && attributes().value("version") == "2") + readModel(); + else + raiseError(QObject::tr( + "The file is not an xmodel version 2 file.")); + } + } + + return !error(); +} + +void modelXMLReader::readUnknownElement() { + Q_ASSERT(isStartElement()); + + while (!atEnd()) { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + readUnknownElement(); + } +} + +void modelXMLReader::readModel() { + Q_ASSERT(isStartElement() && name() == "xmodel"); + + while (!atEnd()) { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) { + if (name() == "name") + machine->name = readElementText(); + else if (name() == "version") + machine->memoryModel->replaceValue("version", + readElementText()); + else if (name() == "author") + machine->memoryModel->replaceValue("author", readElementText()); + else if (name() == "description") + machine->memoryModel->replaceValue("description", + readElementText()); + else if (name() == "models") + readIncludedModels(); + else if (name() == "environment") + readEnvironment(); + else if (name() == "agents") + readAgents(); + else if (name() == "messages") + readMessages(); + else + readUnknownElement(); + } + } +} + +void modelXMLReader::readIncludedModels() { + while (!atEnd()) { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) { + if (name() == "model") + readIncludedModel(); + else + readUnknownElement(); + } + } +} + +void modelXMLReader::readIncludedModel() { + bool enabled = false; + QString fileName = ""; + + while (!atEnd()) { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) { + if (name() == "file") + fileName = readElementText(); + else + if (name() == "enabled") { + QString v = readElementText(); + if (v == "true") enabled = true; + if (v == "false") enabled = false; + } else { + readUnknownElement(); + } + } + } + + // Create the new sub model + Machine * a = new Machine(0, machine); + a->setRootModel(machine->rootModel()); + machine->appendChild(a); + a->isSubModel = true; + a->setEnabled(enabled); + // Add the environment to the new sub model + Machine * b = new Machine(3, a); + a->appendChild(b); + b->setRootModel(a->rootModel()); + b->name = "environment"; + + // Get file info of the current file being read + QFileInfo fileInfo(file->fileName()); + // Get the absolute path to the current file + QString path = fileInfo.absolutePath(); + path.append("/"); // Append a directory slash + path.append(fileName); // Append the path to the sub model file + + // Create a qfile for the sub model + QFile filet(path); + // Try and open the qfile + if (!filet.open(QFile::ReadOnly | QFile::Text)) { + raiseError( + QString( + "Because cannot open sub model file: %1\n%2"). + arg(path). + arg(filet.errorString())); + return; + } + // Try and read the qfile + modelXMLReader reader(a); + if (!reader.read(&filet)) { + raiseError( + QString( + "Because cannot parse sub model in file %1 at line %2, column %3:\n%4"). + arg(path). + arg(reader.lineNumber()). + arg(reader.columnNumber()). + arg(reader.errorString())); + } +} + +void modelXMLReader::readAgents() { + while (!atEnd()) { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) { + if (name() == "xagent") + readAgent(); + else + readUnknownElement(); + } + } +} + +void modelXMLReader::readAgent() { + Machine * a = new Machine(1, machine); + machine->appendChild(a); + a->setRootModel(machine->rootModel()); + + while (!atEnd()) { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) { + if (name() == "name") + a->name = readElementText(); + else if (name() == "description") + a->description = readElementText(); + else if (name() == "memory") + readVariables(a); + else if (name() == "functions") + readTransitions(a); + else + readUnknownElement(); + } + } +} + +void modelXMLReader::readMessages() { + while (!atEnd()) { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) { + if (name() == "message") + readMessage(); + else + readUnknownElement(); + } + } +} + +void modelXMLReader::readMessage() { + Machine * a = new Machine(2, machine); + machine->appendChild(a); + a->setRootModel(machine->rootModel()); + + while (!atEnd()) { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) { + if (name() == "name") + a->name = readElementText(); + else if (name() == "description") + a->description = readElementText(); + else if (name() == "variables") + readVariables(a); + else + readUnknownElement(); + } + } +} + +void modelXMLReader::readVariables(Machine * a) { + while (!atEnd()) { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) { + if (name() == "variable") + readVariable(a); + else + readUnknownElement(); + } + } +} + +void modelXMLReader::readVariable(Machine * a) { + QString myname = ""; + QString mytype = ""; + QString description = ""; + double myvalue = 0.0; + bool constant = false; + + while (!atEnd()) { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) { + if (name() == "type") + mytype = readElementText(); + else if (name() == "name") + myname = readElementText(); + else if (name() == "description") + description = readElementText(); + else + if (name() == "constant") { + QString constantString = readElementText(); + if (constantString == "true") constant = true; + } else if (name() == "value") { + if (mytype == "int") myvalue = readElementText().toInt(); + if (mytype == "double") myvalue = readElementText().toDouble(); + } else { + readUnknownElement(); + } + } + } + + a->memoryModel->addVariable(mytype, myname, + description, constant, myvalue); + + // qDebug() << machine->name << a->type << a->name << myname; +} + +void modelXMLReader::readTransitions(Machine * a) { + while (!atEnd()) { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) { + if (name() == "function") + readTransition(a); + else + readUnknownElement(); + } + } +} + +void modelXMLReader::readTransition(Machine * a) { + QString cState = ""; + QString nState = ""; + QString myname = ""; + QString myinput = ""; + QString myoutput = ""; + QString description = ""; + Condition c = Condition(); + Communication input = Communication(1); + Communication output = Communication(0); + + while (!atEnd()) { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) { + if (name() == "currentState") + cState = readElementText(); + else if (name() == "inputs") + readInputs(&input); + else + if (name() == "condition") { + readCondition(&c, 0); + c.enabled = true; + } else if (name() == "name") { + myname = readElementText(); + } else if (name() == "description") { + description = readElementText(); + } else if (name() == "outputs") { + readOutputs(&output); + } else if (name() == "nextState") { + nState = readElementText(); + } else { + readUnknownElement(); + } + } + } + + a->addTransitionString(myname, cState, nState, c, mpost, + input, output, description); + // a->machineModel->addTransitionString(myname, cState, + // nState, c, mpost, input, output); + // a->machineScene->addTransitionString(a->name, cState, + // myname, nState, 0); +} + +void modelXMLReader::readCondition(Condition * c, QString * s) { + Q_ASSERT(c != 0); + + while (!atEnd()) { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) { + if (name() == "not") { + c->isNot = true; + readCondition(c, 0); + } else if (name() == "time") { + c->isTime = true; + c->isValues = false; + c->isConditions = false; + + while (!atEnd()) { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) { + if (name() == "period") + c->timePeriod = readElementText(); + else if (name() == "phase") + c->timePhaseVariable = readElementText(); + else if (name() == "duration") + c->timeDuration = readElementText().toInt(); + else + readUnknownElement(); + } + } + + c->processSymbols(); + } else if (name() == "lhs") { + c->lhsCondition = new Condition(); + c->lhsCondition->enabled = true; + readCondition(c->lhsCondition, &c->lhs); + if (c->lhs != "") { + c->isValues = true; + c->isConditions = false; + } else { + c->isValues = false; + c->isConditions = true; + } + } else if (name() == "op") { + c->op = readElementText(); + } else if (name() == "rhs") { + c->rhsCondition = new Condition(); + c->rhsCondition->enabled = true; + readCondition(c->rhsCondition, &c->rhs); + if (c->rhs != "") { + c->isValues = true; + c->isConditions = false; + } else { + c->isValues = false; + c->isConditions = true; + } + + c->processSymbols(); + } else if (name() == "value") { + Q_ASSERT(s != 0); + + *s = readElementText(); + } else { + readUnknownElement(); + } + } + } +} + +void modelXMLReader::readMpost() { + QString eText; + + while (!atEnd()) { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) { + if (name() == "enabled") { + eText = readElementText(); + if (eText == "true") mpost.setEnabled(true); + if (eText == "false") mpost.setEnabled(false); + } else if (name() == "name") { + mpost.setName(readElementText()); + } else if (name() == "op") { + mpost.setOp(readElementText()); + } else if (name() == "name") { + mpost.setName2(readElementText()); + // mpost.setValue(readElementText().toInt()); + } else { + readUnknownElement(); + } + } + } +} + +void modelXMLReader::readEnvironment() { + Machine * a = 0; + for (int i = 0; i < machine->childCount(); i++) { + if (machine->child(i)->type == 3) a = machine->child(i); + } + Q_ASSERT(a != 0); + + while (!atEnd()) { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) { + if (name() == "timeUnits") + readTimeUnits(a); + else if (name() == "dataTypes") + readDataTypes(a); + else if (name() == "constants") + readVariables(a); + else if (name() == "functionFiles") + readFunctionFiles(a); + else + readUnknownElement(); + } + } +} + +void modelXMLReader::readFunctionFiles(Machine *m) { + while (!atEnd()) { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) { + if (name() == "file") { + Machine * a = new Machine(6, m); + m->appendChild(a); + a->setRootModel(machine->rootModel()); + + a->name = readElementText(); + + // For function files keep fileDirectory the same as the + // model file directory + // and have the fileName as the path from the model + // directory to the file + a->fileDirectory = machine->fileDirectory; + a->fileName = a->name; + } else { + readUnknownElement(); + } + } + } +} + +void modelXMLReader::readDataTypes(Machine * a) { + while (!atEnd()) { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) { + if (name() == "dataType") + readDataType(a); + else + readUnknownElement(); + } + } +} + +void modelXMLReader::readDataType(Machine * m) { + Machine * a = new Machine(5, m); + m->appendChild(a); + a->setRootModel(machine->rootModel()); + + while (!atEnd()) { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) { + if (name() == "name") + a->name = readElementText(); + else if (name() == "description") + a->description = readElementText(); + else if (name() == "variables") + readVariables(a); + else + readUnknownElement(); + } + } +} + +void modelXMLReader::readTimeUnits(Machine * a) { + while (!atEnd()) { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) { + if (name() == "timeUnit") + readTimeUnit(a); + else + readUnknownElement(); + } + } +} + +void modelXMLReader::readTimeUnit(Machine * m) { + Machine * a = new Machine(4, m); + m->appendChild(a); + a->setRootModel(machine->rootModel()); + + // a->timeUnit = new TimeUnit(); + TimeUnit t; + + while (!atEnd()) { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) { + if (name() == "name") { + t.name = readElementText(); + a->name = t.name; + machine->timeUnitNames.append(a->name); + } else if (name() == "unit") { + t.unit = readElementText(); + } else if (name() == "period") { + t.period = readElementText().toInt(); + } else { + readUnknownElement(); + } + } + } + + a->setTimeUnit(t); +} + +void modelXMLReader::readInputs(Communication * input) { + while (!atEnd()) { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) { + if (name() == "input") + readInput(input); + else + readUnknownElement(); + } + } +} + +void modelXMLReader::readSort(MessageComm * mc) { + mc->sort.isSort = true; + + while (!atEnd()) { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) { + if (name() == "key") + mc->sort.key = readElementText(); + else + if (name() == "order") + mc->sort.order = readElementText(); + else + readUnknownElement(); + } + } +} + +void modelXMLReader::readInput(Communication * input) { + MessageComm mc; + + while (!atEnd()) { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) { + if (name() == "messageName") { + mc.messageType = readElementText(); + } else if (name() == "random") { + if (readElementText() == "true") mc.sort.isRandom = true; + } else if (name() == "filter") { + mc.filter.enabled = true; + readCondition(&mc.filter, 0); + } else if (name() == "sort") { + readSort(&mc); + } else { + readUnknownElement(); + } + } + } + + input->messageModel->addMessage(mc); +} + +void modelXMLReader::readOutputs(Communication * output) { + while (!atEnd()) { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) { + if (name() == "output") + readOutput(output); + else + readUnknownElement(); + } + } +} + +void modelXMLReader::readOutput(Communication * output) { + while (!atEnd()) { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) { + if (name() == "messageName") { + output->messageModel->addMessage(readElementText()); + } else { + readUnknownElement(); + } + } + } +} diff --git a/modelxmlreader.h b/modelxmlreader.h new file mode 100644 index 0000000..f04be1d --- /dev/null +++ b/modelxmlreader.h @@ -0,0 +1,60 @@ +/*! + * \file modelxmlreader.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for the XML model reader +*/ +#ifndef MODELXMLREADER_H_ +#define MODELXMLREADER_H_ + +#include +#include "./machinemodel.h" +#include "./memorymodel.h" +#include "./machinescene.h" +#include "./mpre.h" +#include "./mpost.h" +#include "./machine.h" +#include "./condition.h" +#include "./messagecomm.h" + +class modelXMLReader : public QXmlStreamReader { + public: + explicit modelXMLReader(Machine * m); + + bool read(QFile * file); + + private: + void readUnknownElement(); + void readModel(); + void readAgents(); + void readAgent(); + void readMessages(); + void readMessage(); + void readVariables(Machine * a); + void readVariable(Machine * a); + void readTransitions(Machine * a); + void readTransition(Machine * a); + void readCondition(Condition * c, QString * s); + void readMpost(); + void readIncludedModels(); + void readIncludedModel(); + void readEnvironment(); + void readDataTypes(Machine * a); + void readDataType(Machine * a); + void readTimeUnits(Machine * a); + void readTimeUnit(Machine * a); + void readInputs(Communication * input); + void readInput(Communication * input); + void readOutputs(Communication * output); + void readOutput(Communication * output); + void readSort(MessageComm * input); + void readFunctionFiles(Machine * a); + + Machine * machine; + Mpre mpre; + Mpost mpost; + QFile * file; +}; + +#endif // MODELXMLREADER_H_ diff --git a/mpost.cpp b/mpost.cpp new file mode 100644 index 0000000..d7a80b6 --- /dev/null +++ b/mpost.cpp @@ -0,0 +1,139 @@ +/*! + * \file mpost.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of mpost + */ +#include +#include "./mpost.h" + +Mpost::Mpost(QString n, QString op, int v, bool enabled) { + myName = n; + myOp = op; + myValue = v; + myEnabled = enabled; + text = ""; +} + +void Mpost::setText(QString t) { + this->text = t; + statements.clear(); + + // QString t = " var1 = var2\nvar3 += 1\n\tvar3 *= 22 "; + // qDebug() << "t: " << t; + QStringList list1 = t.split("\n", QString::SkipEmptyParts); + for (int i = 0; i < list1.size(); ++i) { + statement s1; + s1.lhs = ""; + s1.op = ""; + s1.rhs = ""; + s1.isVariable = true; + + QString t1 = list1.at(i); + t1 = t1.simplified(); // replaces all white space with spaces + // qDebug() << "t1: " << t1; + // split on the spaces removing any empty elements + QStringList list2 = t1.split(" ", QString::SkipEmptyParts); + qDebug() << "list2: " << list2; + /* Make sure lhs is a memory var */ + + // If list is not 3 member long then skip + if (list2.size() < 3) { + if (list2.size() > 0) s1.lhs = list2.at(0); + if (list2.size() > 1) s1.op = list2.at(1); + qDebug() << "Error: Statement is less than 3 elements long"; + } else { + // For all memory variables + for (int j = 0; j < memory->getNames().size(); j++) { + // If the first word == memory variable + if (list2.at(0) == memory->getNames().at(j)) { + // Then make statement lhs = memory variable + s1.lhs = list2.at(0); + } + // If third word == memory variable + if (list2.at(2) == memory->getNames().at(j)) { + // Then make statement rhs = memory variable + s1.rhs = list2.at(2); + } + } + // Validate + s1.isValid = true; // Start with statement being valid + // If the lhs is not set then error + if (s1.lhs == "") { + s1.lhs = list2.at(0); + qDebug() << "Error: lhs doesn't equal a memory variable - " + << s1.lhs; + s1.isValid = false; + } + s1.op = list2.at(1); // Make statement op = second word + // If the op is not an operator then error + if (s1.op != "=" && s1.op != "+=" && s1.op != "-=" && + s1.op != "/=" && s1.op != "*=") { + qDebug() << "Error: op doesn't equal one of =,+=,-=,/=,*= - " + << s1.op; + s1.isValid = false; + } + // If the lhs is not set + if (s1.rhs == "") { + // Make statement rhs = text of value + s1.rhs = list2.at(2); + // Check if word is a number + bool ok; + // Make statement drhs = double value + s1.drhs = list2.at(2).toDouble(&ok); + // If string can convert to double + if (ok) { + // Make statement isVariable = false + s1.isVariable = false; + } else { + qDebug() << + "Error: rhs is not a memory value or a valid number - " + << list2.at(2); + s1.isValid = false; + } + } + } + // Print out statement + if (s1.isVariable) qDebug() << s1.lhs << " " << s1.op << " " << s1.rhs; + else + qDebug() << s1.lhs << " " << s1.op << " " << s1.drhs; + if (s1.isValid) qDebug() << "Statement is valid"; + else + qDebug() << "Statement is not valid"; + + // Append statement to the list of statements + statements.append(s1); + } + + getText(); + this->myEnabled = true; +} + +QString Mpost::getText() { + QString s; + for (int i = 0; i < statements.size(); i++) { + s.append(statements.at(i).lhs); + s.append(" "); + s.append(statements.at(i).op); + s.append(" "); + s.append(statements.at(i).rhs); + s.append("\n"); + } + this->text = s; + return s; +} + +void Mpost::paint(QPainter *painter, const QRect &rect, + const QPalette &/*palette*/, EditMode /*mode*/) const { + painter->save(); + + painter->setRenderHint(QPainter::Antialiasing, true); + + if (myEnabled) { + painter->drawText(rect, text); + } + + painter->restore(); +} + diff --git a/mpost.h b/mpost.h new file mode 100644 index 0000000..55c656a --- /dev/null +++ b/mpost.h @@ -0,0 +1,67 @@ +/*! + * \file mpost.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for mpost +*/ +#ifndef MPOST_H_ +#define MPOST_H_ + +#include +#include +#include "./variable.h" +#include "./memorymodel.h" + +struct statement { + QString lhs; + QString op; + QString rhs; + double drhs; + bool isVariable; + bool isValid; +}; + +typedef statement statement; + +class Mpost { + public: + enum EditMode { Editable, ReadOnly }; + + Mpost(QString n = "", QString op = "", int v = 0, bool enabled = false); + + void paint(QPainter *painter, const QRect &rect, + const QPalette &palette, EditMode mode) const; + + QString name() const { return myName; } + QString name2() const { return myName2; } + int value() const { return myValue; } + void setName(QString n) { myName = n; } + void setName2(QString n) { myName2 = n; } + void setValue(int v) { myValue = v; } + bool enabled() const { return myEnabled; } + void setEnabled(bool enabled) { myEnabled = enabled; } + QString op() const { return myOp; } + void setOp(QString o) { myOp = o; } + int value2() const { return myValue2; } + void setValue2(int v) { myValue2 = v; } + QString getText(); + void setText(QString t); + void setMemory(MemoryModel * m) { memory = m; } + QList getStatements() { return statements; } + + private: + bool myEnabled; + QString myName; + QString myName2; + int myValue; + QString myOp; + int myValue2; + QString text; + QList statements; + MemoryModel * memory; +}; + +Q_DECLARE_METATYPE(Mpost) + +#endif // MPOST_H_ diff --git a/mpostdelegate.cpp b/mpostdelegate.cpp new file mode 100644 index 0000000..d0c6efc --- /dev/null +++ b/mpostdelegate.cpp @@ -0,0 +1,71 @@ +/*! + * \file mpostdelegate.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of mpost delegate + */ +#include +#include "./mpostdelegate.h" +#include "./mpostdialog.h" +#include "./mpost.h" + +MpostDelegate::MpostDelegate(MemoryModel * m, QObject *parent) + : QItemDelegate(parent) { + memory = m; +} + +QWidget *MpostDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &/*option*/, + const QModelIndex &/*index*/) const { + QTextEdit *editor = new QTextEdit(parent); + return editor; +} + +void MpostDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const { + if (qVariantCanConvert(index.data())) { + Mpost mpost = qVariantValue(index.data()); + // painter->drawText(option.rect, mpre.toString()); + + if (option.state & QStyle::State_Selected) + painter->fillRect(option.rect, option.palette.highlight()); + + /*starRating.paint(painter, option.rect, option.palette, + StarRating::ReadOnly);*/ + mpost.paint(painter, option.rect, option.palette, + Mpost::ReadOnly); + } else { + QItemDelegate::paint(painter, option, index); + } +} + +void MpostDelegate::setEditorData(QWidget *editor, + const QModelIndex &index) const { + if (qVariantCanConvert(index.data())) { + Mpost mpost = qVariantValue(index.data()); + QTextEdit * textEdit = static_cast(editor); + // lineEdit->setText(mpost.getText()); + textEdit->setText(mpost.getText()); + } else { + QItemDelegate::setEditorData(editor, index); + } +} + +void MpostDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const { + if (qVariantCanConvert(index.data())) { + QTextEdit *textEdit = static_cast(editor); + Mpost mpost; + mpost.setMemory(this->memory); + mpost.setText(textEdit->toPlainText()); // lineEdit->text()); + model->setData(index, qVariantFromValue(mpost)); + } else { + QItemDelegate::setModelData(editor, model, index); + } +} + +void MpostDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &/*index*/) const { + editor->setGeometry(option.rect); +} diff --git a/mpostdelegate.h b/mpostdelegate.h new file mode 100644 index 0000000..e6fa16b --- /dev/null +++ b/mpostdelegate.h @@ -0,0 +1,42 @@ +/*! + * \file mpostdelegate.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for mpost delegate +*/ +#ifndef MPOSTDELEGATE_H_ +#define MPOSTDELEGATE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include "./memorymodel.h" + +class MpostDelegate : public QItemDelegate { + Q_OBJECT + + public: + MpostDelegate(MemoryModel * m = 0, QObject *parent = 0); + + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + + void updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const; + + private: + MemoryModel * memory; +}; + +#endif // MPOSTDELEGATE_H_ diff --git a/mpostdialog.cpp b/mpostdialog.cpp new file mode 100644 index 0000000..0a40710 --- /dev/null +++ b/mpostdialog.cpp @@ -0,0 +1,103 @@ +/*! + * \file mpostdialog.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of mpost dialog + */ +#include +#include +#include +#include "./mpostdialog.h" + +MpostDialog::MpostDialog(MemoryModel * m, QWidget *parent) + : QDialog(parent) { + // qDebug() << "MpreDialog"; + memory = m; + + lhsGroup = new QGroupBox(tr("LHS")); + rhsGroup = new QGroupBox(tr("RHS")); + opGroup = new QGroupBox(tr("OP")); + + myEnabled = new QCheckBox(tr("Enabled")); + + connect(myEnabled, SIGNAL(clicked(bool)), lhsGroup, SLOT(setEnabled(bool))); + connect(myEnabled, SIGNAL(clicked(bool)), rhsGroup, SLOT(setEnabled(bool))); + connect(myEnabled, SIGNAL(clicked(bool)), opGroup, SLOT(setEnabled(bool))); + + variable = new QComboBox(); + variable2 = new QComboBox(); + value = new QSpinBox(); + op = new QComboBox(); + value2 = new QSpinBox(); + + operators << "=" << "+=" << "-=" << "*=" << "/="; + op->addItems(operators); + variable->addItems(memory->getNames()); + variable2->addItems(memory->getNames()); + + buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok); + + connect(this, SIGNAL(setVariableComboBox(int)), + variable, SLOT(setCurrentIndex(int))); + connect(this, SIGNAL(setVariable2ComboBox(int)), + variable2, SLOT(setCurrentIndex(int))); + connect(this, SIGNAL(setOpComboBox(int)), op, SLOT(setCurrentIndex(int))); + + connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + + QVBoxLayout *lhsLayout = new QVBoxLayout; + lhsLayout->addWidget(variable); + lhsGroup->setLayout(lhsLayout); + + QVBoxLayout *rhsLayout = new QVBoxLayout; + // rhsLayout->addWidget(value); + rhsLayout->addWidget(variable2); + rhsGroup->setLayout(rhsLayout); + + QHBoxLayout *opLayout = new QHBoxLayout; + opLayout->addWidget(op); + opGroup->setLayout(opLayout); + + QHBoxLayout *mainLayout = new QHBoxLayout; + mainLayout->addWidget(myEnabled); + mainLayout->addWidget(lhsGroup); + mainLayout->addWidget(opGroup); + mainLayout->addWidget(rhsGroup); + mainLayout->addWidget(buttonBox); + setLayout(mainLayout); + + setWindowTitle(tr("Mpost Editor")); +} + +void MpostDialog::setMpost(Mpost m) { + mpost = m; + myEnabled->setChecked(mpost.enabled()); + lhsGroup->setEnabled(mpost.enabled()); + opGroup->setEnabled(mpost.enabled()); + rhsGroup->setEnabled(mpost.enabled()); + value->setValue(mpost.value()); + // value2->setValue(mpost.value2()); + int index = memory->getNames().indexOf(mpost.name()); + if (index == -1) index = 0; + emit(setVariableComboBox(index)); + + index = operators.indexOf(mpost.op()); + if (index == -1) index = 0; + emit(setOpComboBox(index)); + + index = memory->getNames().indexOf(mpost.name2()); + if (index == -1) index = 0; + emit(setVariable2ComboBox(index)); +} + +Mpost MpostDialog::getMpost() { + mpost.setEnabled(myEnabled->isChecked()); + mpost.setValue(value->value()); + mpost.setName(variable->currentText()); + mpost.setOp(op->currentText()); + // mpost.setValue2(value2->value()); + mpost.setName2(variable2->currentText()); + return mpost; +} diff --git a/mpostdialog.h b/mpostdialog.h new file mode 100644 index 0000000..a7b3ab5 --- /dev/null +++ b/mpostdialog.h @@ -0,0 +1,52 @@ +/*! + * \file mpostdialog.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for mpost dialog +*/ +#ifndef MPOSTDIALOG_H_ +#define MPOSTDIALOG_H_ + +#include +#include +#include +#include +#include +#include +#include +#include "./mpost.h" +#include "./memorymodel.h" + +class QDialogButtonBox; + +class MpostDialog : public QDialog { + Q_OBJECT + + public: + MpostDialog(MemoryModel * m, QWidget *parent = 0); + void setMpost(Mpost m); + Mpost getMpost(); + + signals: + void setVariableComboBox(int i); + void setVariable2ComboBox(int i); + void setOpComboBox(int i); + + private: + QDialogButtonBox *buttonBox; + QComboBox *variable; + QComboBox *variable2; + QComboBox * op; + QSpinBox * value; + QSpinBox * value2; + QCheckBox * myEnabled; + QGroupBox *lhsGroup; + QGroupBox *opGroup; + QGroupBox *rhsGroup; + Mpost mpost; + MemoryModel * memory; + QStringList operators; +}; + +#endif // MPOSTDIALOG_H_ diff --git a/mpre.cpp b/mpre.cpp new file mode 100644 index 0000000..3f1a680 --- /dev/null +++ b/mpre.cpp @@ -0,0 +1,59 @@ +/*! + * \file mpre.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of mpre + */ +#include +#include "./mpre.h" + +Mpre::Mpre(QString n, QString o, int v, bool enabled, bool neg) { + myName = n; + myOp = o; + myValue = v; + myEnabled = enabled; + myNot = neg; +} + +void Mpre::paint(QPainter *painter, const QRect &rect, + const QPalette &/*palette*/, EditMode /*mode*/) const { + painter->save(); + + painter->setRenderHint(QPainter::Antialiasing, true); + + if (myEnabled) { + QString s; + if (myNot) s.append("not "); + s.append(myName); + s.append(" "); + s.append(myOp); + s.append(" "); + s.append(QString("%1").arg(myValue)); + + painter->drawText(rect, s); + } + + /*painter->setPen(Qt::NoPen); + + if (mode == Editable) { + painter->setBrush(palette.highlight()); + } else { + painter->setBrush(palette.foreground()); + } + + int yOffset = (rect.height() - PaintingScaleFactor) / 2; + painter->translate(rect.x(), rect.y() + yOffset); + painter->scale(PaintingScaleFactor, PaintingScaleFactor); + + for (int i = 0; i < myMaxStarCount; ++i) { + if (i < myStarCount) { + painter->drawPolygon(starPolygon, Qt::WindingFill); + } else if (mode == Editable) { + painter->drawPolygon(diamondPolygon, Qt::WindingFill); + } + painter->translate(1.0, 0.0); + }*/ + + painter->restore(); +} diff --git a/mpre.h b/mpre.h new file mode 100644 index 0000000..da68328 --- /dev/null +++ b/mpre.h @@ -0,0 +1,45 @@ +/*! + * \file mpre.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for mpre +*/ +#ifndef MPRE_H_ +#define MPRE_H_ + +#include +#include + +class Mpre { + public: + enum EditMode { Editable, ReadOnly }; + + Mpre(QString n = "", QString o = "==", int v = 0, + bool enabled = false, bool neg = false); + + void paint(QPainter *painter, const QRect &rect, + const QPalette &palette, EditMode mode) const; + + QString name() const { return myName; } + QString op() const { return myOp; } + int value() const { return myValue; } + void setName(QString n) { myName = n; } + void setOp(QString o) { myOp = o; } + void setValue(int v) { myValue = v; } + bool enabled() const { return myEnabled; } + void setEnabled(bool enabled) { myEnabled = enabled; } + void setNot(bool n) { myNot = n; } + bool isNot() const { return myNot; } + + private: + bool myEnabled; + bool myNot; + QString myName; + QString myOp; + int myValue; +}; + +Q_DECLARE_METATYPE(Mpre) + +#endif // MPRE_H_ diff --git a/mpredelegate.cpp b/mpredelegate.cpp new file mode 100644 index 0000000..e411308 --- /dev/null +++ b/mpredelegate.cpp @@ -0,0 +1,137 @@ +/*! + * \file mpredelegate.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of mpre delegate + */ +#include +#include "./mpredelegate.h" +#include "./mpredialog.h" +#include "./condition.h" +#include "./messagemodel.h" + +MpreDelegate::MpreDelegate(Machine * m, Communication * comm , QObject *parent) + : QItemDelegate(parent) { + machine = m; + memory = m->memoryModel; + communication = comm; +} + +void MpreDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const { + if (qVariantCanConvert(index.data())) { + Condition condition = qVariantValue(index.data()); + + /*if (option.state & QStyle::State_Selected) + painter->fillRect(option.rect, option.palette.highlight()); + condition.paint(painter, option.rect, option.palette, + Condition::ReadOnly);*/ + + QStyle &style = *QApplication::style(); + painter->save(); + + QStyleOptionViewItemV4 opt = option; + // initStyleOption(&opt, index); + + // opt.textElideMode = Qt::ElideMiddle; //Qt::ElideRight; + // this->elidedText() + + style.drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, 0); + + QRect textRect = style.subElementRect(QStyle::SE_ItemViewItemText, + &opt, 0); + + // following code borrowed from qcommonstyle.cpp + QPalette::ColorGroup cg = + opt.state & QStyle::State_Enabled ? + QPalette::Normal : QPalette::Disabled; + if (cg == QPalette::Normal && !(opt.state & QStyle::State_Active)) { + cg = QPalette::Inactive; + } + if (opt.state & QStyle::State_Selected) { + painter->setPen(QPen(opt.palette.brush(cg, + QPalette::HighlightedText), 0)); + } else { + painter->setPen(QPen(opt.palette.brush(cg, QPalette::Text), 0)); + } + if (opt.state & QStyle::State_Editing) { + painter->setPen(QPen(opt.palette.brush(cg, QPalette::Text), 0)); + painter->drawRect(textRect.adjusted(0, 0, -1, -1)); + } + + QFontMetrics metrics(opt.font); + int height = 0; + + QStringList slist = condition.toString().split("\n"); + for (int i = 0; i < slist.count(); i++) { + textRect.setTop(textRect.top() + height); + style.drawItemText(painter, textRect, Qt::AlignLeft, + opt.palette, true, elidedText(opt.fontMetrics, + textRect.width(), Qt::ElideRight, slist.at(i))); + QRect bRect = metrics.boundingRect(slist.at(i)); + height = bRect.height(); + } + + painter->restore(); + } else { + QItemDelegate::paint(painter, option, index); + } +} + + +QWidget *MpreDelegate::createEditor(QWidget */*parent*/, + const QStyleOptionViewItem &/*option*/, + const QModelIndex &index) const { + MpreDialog *editor; + if (communication) editor = + new MpreDialog(machine, + &communication->messageModel-> + getMessages()[index.row()].messageType); + else + editor = new MpreDialog(machine); + + connect(editor, SIGNAL(accepted()), this, SLOT(commitAndCloseEditor())); + connect(editor, SIGNAL(rejected()), this, SLOT(commitAndCloseEditor())); + + // editor->setParent(windowParent); + editor->setModal(true); + // editor->move(100, 100); + // editor->setWindowFlags(WShowModal); + + return editor; +} + +void MpreDelegate::setEditorData(QWidget *editor, + const QModelIndex &index) const { + if (qVariantCanConvert(index.data())) { + Condition condition = qVariantValue(index.data()); + MpreDialog *dialog = static_cast(editor); + + dialog->setCondition(condition); + } else { + QItemDelegate::setEditorData(editor, index); + } +} + +void MpreDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const { + if (qVariantCanConvert(index.data())) { + MpreDialog *dialog = static_cast(editor); + model->setData(index, qVariantFromValue(dialog->getCondition())); + } else { + QItemDelegate::setModelData(editor, model, index); + } +} + +/*void MpreDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + editor->setGeometry(option.rect); +}*/ + +void MpreDelegate::commitAndCloseEditor() { + MpreDialog *editor = qobject_cast(sender()); + emit commitData(editor); + emit closeEditor(editor); +} diff --git a/mpredelegate.h b/mpredelegate.h new file mode 100644 index 0000000..50b5e97 --- /dev/null +++ b/mpredelegate.h @@ -0,0 +1,50 @@ +/*! + * \file mpredelegate.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for mpre delegate +*/ +#ifndef MPREDELEGATE_H_ +#define MPREDELEGATE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include "./memorymodel.h" +#include "./machine.h" +#include "./communication.h" + +class MpreDelegate : public QItemDelegate { + Q_OBJECT + + public: + MpreDelegate(Machine * machine, Communication * comm = 0, + QObject *parent = 0); + + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + + /*void updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const;*/ + + private slots: + void commitAndCloseEditor(); + + private: + MemoryModel * memory; + Machine * machine; + Communication * communication; +}; + +#endif // MPREDELEGATE_H_ diff --git a/mpredialog.cpp b/mpredialog.cpp new file mode 100644 index 0000000..e18182e --- /dev/null +++ b/mpredialog.cpp @@ -0,0 +1,434 @@ +/*! + * \file mpredialog.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of mpre dialog + */ +#include +#include +#include +#include "./mpredialog.h" + +MpreDialog::MpreDialog(Machine * a, QString * mt, QWidget *parent) + : QDialog(parent) { + setupUi(this); + + agent = a; + + QStringList operators; + operators << "==" << "!=" << "<" << ">" << "<=" << ">=" << "IN"; + comboBox_valueOp->addItems(operators); + operators.clear(); + operators << "AND" << "OR"; + comboBox_nestedOp->addItems(operators); + QStringList agentMemoryNames = agent->getAgentMemoryNames(agent->name); + comboBox_lhsAgentVariable->addItems(agentMemoryNames); + comboBox_rhsAgentVariable->addItems(agentMemoryNames); + comboBox_period->addItems(agent->getTimeUnits()); + comboBox_phaseVariable->addItems(agentMemoryNames); + + if (mt == 0) { // if agent then disable message variables + checkBox_lhsMessageVariable->setEnabled(false); + comboBox_lhsMessageVariable->setEnabled(false); + checkBox_rhsMessageVariable->setEnabled(false); + comboBox_rhsMessageVariable->setEnabled(false); + } else { // if message then populate message variable combo boxes + QStringList messageMemoryNames = agent->getMessageMemoryNames(*mt); + comboBox_lhsMessageVariable->addItems(messageMemoryNames); + comboBox_rhsMessageVariable->addItems(messageMemoryNames); + } + + connect(checkBox_enabled, SIGNAL(clicked(bool)), + this, SLOT(enableCondition(bool))); + connect(checkBox_lhsAgentVariable, SIGNAL(clicked(bool)), + this, SLOT(checkedLhsAgentVariable(bool))); + connect(checkBox_lhsMessageVariable, SIGNAL(clicked(bool)), + this, SLOT(checkedLhsMessageVariable(bool))); + connect(checkBox_lhsValue, SIGNAL(clicked(bool)), + this, SLOT(checkedLhsValue(bool))); + connect(checkBox_rhsAgentVariable, SIGNAL(clicked(bool)), + this, SLOT(checkedRhsAgentVariable(bool))); + connect(checkBox_rhsMessageVariable, SIGNAL(clicked(bool)), + this, SLOT(checkedRhsMessageVariable(bool))); + connect(checkBox_rhsValue, SIGNAL(clicked(bool)), + this, SLOT(checkedRhsValue(bool))); + connect(radioButton_value, SIGNAL(clicked()), this, SLOT(valueClicked())); + connect(radioButton_time, SIGNAL(clicked()), this, SLOT(timeClicked())); + connect(radioButton_nested, SIGNAL(clicked()), this, SLOT(nestedClicked())); + connect(pushButton_editLhs, SIGNAL(clicked()), + this, SLOT(editLhsClicked())); + connect(pushButton_editRhs, SIGNAL(clicked()), + this, SLOT(editRhsClicked())); + connect(checkBox_phaseVariable, SIGNAL(clicked(bool)), + this, SLOT(disableTimePhaseValue(bool))); + connect(checkBox_phaseValue, SIGNAL(clicked(bool)), + this, SLOT(disableTimePhaseVariable(bool))); + connect(pushButton_levelUp, SIGNAL(clicked()), + this, SLOT(levelUpClicked())); +} + +void MpreDialog::setCurrentCondition(Condition *cond) { + c = cond; + + if (c->enabled) lineEdit_condition->setText(c->toString()); + else + lineEdit_condition->setText(""); + + /* If not root condition */ + if (c->parentCondition) { + checkBox_enabled->setChecked(true); + checkBox_enabled->setEnabled(false); + pushButton_levelUp->setEnabled(true); + } else { + checkBox_enabled->setEnabled(true); + pushButton_levelUp->setEnabled(false); + } + + checkBox_enabled->setChecked(c->enabled); + enableCondition(c->enabled); + + if (c->isValues) { + if (c->enabled) { + groupBox_value->setEnabled(true); + groupBox_time->setEnabled(false); + groupBox_nested->setEnabled(false); + } + + radioButton_value->setChecked(true); + checkBox_notValue->setChecked(c->isNot); + checkBox_lhsAgentVariable->setChecked(c->lhsIsAgentVariable); + checkBox_lhsMessageVariable->setChecked(c->lhsIsMessageVariable); + checkBox_lhsValue->setChecked(c->lhsIsValue); + comboBox_lhsAgentVariable->setEnabled(c->lhsIsAgentVariable); + comboBox_lhsMessageVariable->setEnabled(c->lhsIsMessageVariable); + doubleSpinBox_lhsValue->setEnabled(c->lhsIsValue); + doubleSpinBox_lhsValue->setValue(c->lhsDouble); + comboBox_lhsAgentVariable->setCurrentIndex(0); + if (c->lhsIsAgentVariable) { + for (int i = 0; i < comboBox_lhsAgentVariable->count(); i++) { + if (QString::compare(comboBox_lhsAgentVariable->itemText(i), + c->lhs) == 0) + comboBox_lhsAgentVariable->setCurrentIndex(i); + } + } + comboBox_lhsMessageVariable->setCurrentIndex(0); + if (c->lhsIsMessageVariable) { + for (int i = 0; i < comboBox_lhsMessageVariable->count(); i++) { + if (QString::compare(comboBox_lhsMessageVariable->itemText(i), + c->lhs) == 0) + comboBox_lhsMessageVariable->setCurrentIndex(i); + } + } + + checkBox_rhsAgentVariable->setChecked(c->rhsIsAgentVariable); + checkBox_rhsMessageVariable->setChecked(c->rhsIsMessageVariable); + checkBox_rhsValue->setChecked(c->rhsIsValue); + comboBox_rhsAgentVariable->setEnabled(c->rhsIsAgentVariable); + comboBox_rhsMessageVariable->setEnabled(c->rhsIsMessageVariable); + doubleSpinBox_rhsValue->setEnabled(c->rhsIsValue); + doubleSpinBox_rhsValue->setValue(c->rhsDouble); + comboBox_rhsAgentVariable->setCurrentIndex(0); + if (c->rhsIsAgentVariable) { + for (int i = 0; i < comboBox_rhsAgentVariable->count(); i++) { + if (QString::compare(comboBox_rhsAgentVariable->itemText(i), + c->rhs) == 0) + comboBox_rhsAgentVariable->setCurrentIndex(i); + } + } + comboBox_rhsMessageVariable->setCurrentIndex(0); + if (c->rhsIsMessageVariable) { + for (int i = 0; i < comboBox_rhsMessageVariable->count(); i++) { + if (QString::compare(comboBox_rhsMessageVariable->itemText(i), + c->rhs) == 0) + comboBox_rhsMessageVariable->setCurrentIndex(i); + } + } + + for (int i = 0; i < comboBox_valueOp->count(); i++) { + if (QString::compare(comboBox_valueOp->itemText(i), c->op) == 0) + comboBox_valueOp->setCurrentIndex(i); + } + } else { + checkBox_notValue->setChecked(false); + checkBox_lhsAgentVariable->setChecked(false); + checkBox_lhsValue->setChecked(true); + comboBox_lhsAgentVariable->setEnabled(false); + doubleSpinBox_lhsValue->setEnabled(true); + doubleSpinBox_lhsValue->setValue(0.0); + comboBox_lhsAgentVariable->setCurrentIndex(0); + + checkBox_rhsAgentVariable->setChecked(false); + checkBox_rhsValue->setChecked(true); + comboBox_rhsAgentVariable->setEnabled(false); + doubleSpinBox_rhsValue->setEnabled(true); + doubleSpinBox_rhsValue->setValue(0.0); + comboBox_rhsAgentVariable->setCurrentIndex(0); + + comboBox_valueOp->setCurrentIndex(0); + } + + if (c->isTime) { + if (c->enabled) { + groupBox_value->setEnabled(false); + groupBox_time->setEnabled(true); + groupBox_nested->setEnabled(false); + } + + radioButton_time->setChecked(true); + checkBox_notTime->setChecked(c->isNot); + if (c->timePeriod == "") { + comboBox_period->setCurrentIndex(0); + } else { + for (int i = 0; i < comboBox_period->count(); i++) { + if (QString::compare(comboBox_period->itemText(i), + c->timePeriod) == 0) + comboBox_period->setCurrentIndex(i); + } + } + checkBox_phaseVariable->setChecked(c->timePhaseIsVariable); + checkBox_phaseValue->setChecked(!c->timePhaseIsVariable); + comboBox_phaseVariable->setEnabled(c->timePhaseIsVariable); + spinBox_phaseValue->setEnabled(!c->timePhaseIsVariable); + if (c->timePhaseIsVariable) { + if (c->timePhaseVariable == "") { + comboBox_phaseVariable->setCurrentIndex(0); + } else { + for (int i = 0; i < comboBox_phaseVariable->count(); i++) { + if (QString::compare(comboBox_phaseVariable->itemText(i), + c->timePhaseVariable) == 0) + comboBox_phaseVariable->setCurrentIndex(i); + } + } + } else { + spinBox_phaseValue->setValue(c->timePhaseValue); + } + spinBox_duration->setValue(c->timeDuration); + } else { + checkBox_notTime->setChecked(false); + comboBox_period->setCurrentIndex(0); + checkBox_phaseVariable->setChecked(false); + checkBox_phaseValue->setChecked(true); + comboBox_phaseVariable->setEnabled(false); + spinBox_phaseValue->setEnabled(0); + comboBox_phaseVariable->setCurrentIndex(0); + spinBox_duration->setValue(0); + } + + if (c->isConditions) { + if (c->enabled) { + groupBox_value->setEnabled(false); + groupBox_time->setEnabled(false); + groupBox_nested->setEnabled(true); + } + + radioButton_nested->setChecked(true); + checkBox_notNested->setChecked(c->isNot); + + lineEdit_lhsCondition->setText(c->lhsCondition->toString()); + for (int i = 0; i < comboBox_nestedOp->count(); i++) { + if (QString::compare(comboBox_nestedOp->itemText(i), c->op) == 0) + comboBox_nestedOp->setCurrentIndex(i); + } + lineEdit_rhsCondition->setText(c->rhsCondition->toString()); + } else { + checkBox_notNested->setChecked(false); + lineEdit_lhsCondition->setText(""); + comboBox_nestedOp->setCurrentIndex(0); + lineEdit_rhsCondition->setText(""); + } +} + +void MpreDialog::setCondition(Condition c) { + condition = c; + + setCurrentCondition(&condition); +} + +void MpreDialog::getCurrentCondition() { + c->enabled = checkBox_enabled->isChecked(); + + c->isValues = radioButton_value->isChecked(); + c->isTime = radioButton_time->isChecked(); + c->isConditions = radioButton_nested->isChecked(); + + if (c->isValues) { + c->isNot = checkBox_notValue->isChecked(); + c->op = comboBox_valueOp->currentText(); + } + if (c->isTime) c->isNot = checkBox_notTime->isChecked(); + if (c->isConditions) { + c->isNot = checkBox_notNested->isChecked(); + c->op = comboBox_nestedOp->currentText(); + } + + c->lhsIsAgentVariable = checkBox_lhsAgentVariable->isChecked(); + c->lhsIsMessageVariable = + checkBox_lhsMessageVariable->isChecked(); + c->lhsIsValue = checkBox_lhsValue->isChecked(); + if (c->lhsIsAgentVariable) c->lhs = + comboBox_lhsAgentVariable->currentText(); + if (c->lhsIsMessageVariable) c->lhs = + comboBox_lhsMessageVariable->currentText(); + c->lhsDouble = doubleSpinBox_lhsValue->value(); + + c->rhsIsAgentVariable = checkBox_rhsAgentVariable->isChecked(); + c->rhsIsMessageVariable = checkBox_rhsMessageVariable->isChecked(); + c->rhsIsValue = checkBox_rhsValue->isChecked(); + if (c->rhsIsAgentVariable) c->rhs = + comboBox_rhsAgentVariable->currentText(); + if (c->rhsIsMessageVariable) c->rhs = + comboBox_rhsMessageVariable->currentText(); + c->rhsDouble = doubleSpinBox_rhsValue->value(); + + c->timePeriod = comboBox_period->currentText(); + c->timePhaseIsVariable = checkBox_phaseVariable->isChecked(); + c->timePhaseVariable = comboBox_phaseVariable->currentText(); + c->timePhaseValue = spinBox_phaseValue->value(); + c->timeDuration = spinBox_duration->value(); +} + +Condition MpreDialog::getCondition() { + getCurrentCondition(); + + return condition; +} + +void MpreDialog::enableCondition(bool e) { + radioButton_value->setEnabled(e); + radioButton_time->setEnabled(e); + radioButton_nested->setEnabled(e); + + groupBox_value->setEnabled(e); + groupBox_time->setEnabled(e); + groupBox_nested->setEnabled(e); + + if (e) { + if (c->isValues) { + groupBox_time->setEnabled(false); + groupBox_nested->setEnabled(false); + } + if (c->isTime) { + groupBox_value->setEnabled(false); + groupBox_nested->setEnabled(false); + } + if (c->isConditions) { + groupBox_value->setEnabled(false); + groupBox_time->setEnabled(false); + } + } +} + +void MpreDialog::valueClicked() { + groupBox_value->setEnabled(true); + groupBox_time->setEnabled(false); + groupBox_nested->setEnabled(false); +} + +void MpreDialog::timeClicked() { + groupBox_value->setEnabled(false); + groupBox_time->setEnabled(true); + groupBox_nested->setEnabled(false); +} + +void MpreDialog::nestedClicked() { + groupBox_value->setEnabled(false); + groupBox_time->setEnabled(false); + groupBox_nested->setEnabled(true); +} + +void MpreDialog::checkedLhsAgentVariable(bool b) { + if (checkBox_lhsMessageVariable->isEnabled()) + checkBox_lhsMessageVariable->setChecked(!b); + checkBox_lhsValue->setChecked(!b); + + comboBox_lhsAgentVariable->setEnabled(b); + comboBox_lhsMessageVariable->setEnabled(!b); + doubleSpinBox_lhsValue->setEnabled(!b); +} +void MpreDialog::checkedLhsMessageVariable(bool b) { + checkBox_lhsAgentVariable->setChecked(!b); + checkBox_lhsValue->setChecked(!b); + + comboBox_lhsAgentVariable->setEnabled(!b); + comboBox_lhsMessageVariable->setEnabled(b); + doubleSpinBox_lhsValue->setEnabled(!b); +} + +void MpreDialog::checkedLhsValue(bool b) { + checkBox_lhsAgentVariable->setChecked(!b); + if (checkBox_lhsMessageVariable->isEnabled()) + checkBox_lhsMessageVariable->setChecked(!b); + + comboBox_lhsAgentVariable->setEnabled(!b); + comboBox_lhsMessageVariable->setEnabled(!b); + doubleSpinBox_lhsValue->setEnabled(b); +} + +void MpreDialog::checkedRhsAgentVariable(bool b) { + if (checkBox_rhsMessageVariable->isEnabled()) + checkBox_rhsMessageVariable->setChecked(!b); + checkBox_rhsValue->setChecked(!b); + + comboBox_rhsAgentVariable->setEnabled(b); + comboBox_rhsMessageVariable->setEnabled(!b); + doubleSpinBox_rhsValue->setEnabled(!b); +} + +void MpreDialog::checkedRhsMessageVariable(bool b) { + checkBox_rhsAgentVariable->setChecked(!b); + checkBox_rhsValue->setChecked(!b); + + comboBox_rhsAgentVariable->setEnabled(!b); + comboBox_rhsMessageVariable->setEnabled(b); + doubleSpinBox_rhsValue->setEnabled(!b); +} + +void MpreDialog::checkedRhsValue(bool b) { + checkBox_rhsAgentVariable->setChecked(!b); + if (checkBox_rhsMessageVariable->isEnabled()) + checkBox_rhsMessageVariable->setChecked(!b); + + comboBox_rhsAgentVariable->setEnabled(!b); + comboBox_rhsMessageVariable->setEnabled(!b); + doubleSpinBox_rhsValue->setEnabled(b); +} + +void MpreDialog::disableTimePhaseVariable(bool b) { + checkBox_phaseVariable->setChecked(!b); + comboBox_phaseVariable->setEnabled(!b); + spinBox_phaseValue->setEnabled(b); +} + +void MpreDialog::disableTimePhaseValue(bool b) { + checkBox_phaseValue->setEnabled(!b); + comboBox_phaseVariable->setEnabled(b); + spinBox_phaseValue->setEnabled(!b); +} + +void MpreDialog::editLhsClicked() { + getCurrentCondition(); + if (c->lhsCondition == 0) { + c->lhsCondition = new Condition(); + c->lhsCondition->enabled = true; + c->lhsCondition->isValues = true; + } + c->lhsCondition->parentCondition = c; + setCurrentCondition(c->lhsCondition); +} + +void MpreDialog::editRhsClicked() { + getCurrentCondition(); + if (c->rhsCondition == 0) { + c->rhsCondition = new Condition(); + c->lhsCondition->enabled = true; + c->lhsCondition->isValues = true; + } + c->rhsCondition->parentCondition = c; + setCurrentCondition(c->rhsCondition); +} + +void MpreDialog::levelUpClicked() { + getCurrentCondition(); + setCurrentCondition(c->parentCondition); +} diff --git a/mpredialog.h b/mpredialog.h new file mode 100644 index 0000000..f094f9f --- /dev/null +++ b/mpredialog.h @@ -0,0 +1,72 @@ +/*! + * \file mpredialog.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for mpre dialog +*/ +#ifndef MPREDIALOG_H_ +#define MPREDIALOG_H_ + +#include +#include +#include +#include +#include +#include +#include +#include "./mpre.h" +#include "./memorymodel.h" +#include "./condition.h" +#include "./ui_conditiondialog.h" +#include "./machine.h" + +class QDialogButtonBox; + +class MpreDialog : public QDialog, public Ui::ConditionDialog { + Q_OBJECT + + public: + MpreDialog(Machine * agent, QString * messageType = 0, QWidget *parent = 0); + void setCondition(Condition c); + Condition getCondition(); + + signals: + void setVariableComboBox(int i); + void setOpComboBox(int i); + + private slots: + void enableCondition(bool); + void checkedLhsAgentVariable(bool); + void checkedLhsMessageVariable(bool); + void checkedLhsValue(bool); + void checkedRhsAgentVariable(bool); + void checkedRhsMessageVariable(bool); + void checkedRhsValue(bool); + void valueClicked(); + void timeClicked(); + void nestedClicked(); + void editLhsClicked(); + void editRhsClicked(); + void disableTimePhaseVariable(bool); + void disableTimePhaseValue(bool); + void levelUpClicked(); + + private: + void setCurrentCondition(Condition * c); + void getCurrentCondition(); + QDialogButtonBox *buttonBox; + QComboBox *variable; + QComboBox * op; + QSpinBox * value; + QCheckBox * myEnabled; + QGroupBox *lhsGroup; + QGroupBox *opGroup; + QGroupBox *rhsGroup; + Condition condition; + Condition * c; + Machine * agent; + QStringList operators; +}; + +#endif // MPREDIALOG_H_ diff --git a/simulationthread.cpp b/simulationthread.cpp new file mode 100644 index 0000000..285ae83 --- /dev/null +++ b/simulationthread.cpp @@ -0,0 +1,133 @@ +/*! + * \file simulationthread.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of simulation thread + */ +#include +#include "./simulationthread.h" + +SimulationThread::SimulationThread(QObject *parent) + : QThread(parent) { + machine = 0; +} + +SimulationThread::~SimulationThread() { + mutex.lock(); + m_abort = -1; + mutex.unlock(); + + wait(); +} + +void SimulationThread::startSim() { + m_abort = 1; + /*if(machine->machineModel->getStartState() != 0) + { + item = machine->getStartState(); + item->setSelected(true); + + } + else item = 0;*/ + + currentState = 0; // machine->machineModel->getStartState(); + currentTransition = 0; + + start(); +} + +void SimulationThread::stopSim() { + mutex.lock(); + m_abort = -1; + mutex.unlock(); +} + +void SimulationThread::pauseSim() { + mutex.lock(); + if (m_abort == 1) m_abort = 0; + else if (m_abort == 0) m_abort = 1; + mutex.unlock(); +} + +void SimulationThread::run() { + // qDebug() << "SimulationThread machine: " << machine->text(0); + // int rc; + + while (currentState != 0 || currentTransition != 0) { + if (m_abort == -1) return; + while (m_abort == 0) msleep(10); + msleep(100); + + if (currentState != 0) { + // qDebug() << "current State: " << currentState->name(); + emit(selectState(currentState->name())); + + for (int i = 0; i < machine->machineModel->getTransitions().size(); + i++) { + if (machine->machineModel-> + getTransitions().at(i)-> + currentState() == currentState && + machine->machineModel-> + getTransitions().at(i)-> + passesCondition(machine->memoryModel)) + currentTransition = + machine->machineModel->getTransitions().at(i); + } + + currentState = 0; + } else { + // qDebug() << "current Transition: " << currentTransition->name(); + emit(this->selectTransition(currentTransition->name())); + + currentTransition->updateMemory(machine->memoryModel); + currentState = currentTransition->nextState(); + + currentTransition = 0; + } + } + + /*QList transitions = machine->getTransitions(); + QGraphicsItem * newItem; + + while(item != 0) + { + newItem = 0; + + for(int i = 0; i < transitions.size() && newItem == 0; i++) + { + if (m_abort == -1) return; + while(m_abort == 0) msleep(10); + msleep(100); + + Arrow * t = transitions.at(i); + if(t->startItem() == item && t->passesCondition(memory)) + { + //qDebug() << "transition: " << t->getName(); + newItem = t; + emit( selectTransition(t) ); + t->updateMemory(memory); + } + else if(t == item) + { + //qDebug() << "state: " << t->endItem()->getName(); + newItem = t->endItem(); + } + } + + if(newItem == 0) return; + + item->setSelected(false); + item = newItem; + item->setSelected(true); + emit( updateScene() ); + }*/ + + /*for(int i = 0; i < 1000; i ++) + { + qDebug() << "run() " << i; + if (m_abort == -1) return; + while(m_abort == 0) msleep(10); + msleep(10); + }*/ +} diff --git a/simulationthread.h b/simulationthread.h new file mode 100644 index 0000000..b626813 --- /dev/null +++ b/simulationthread.h @@ -0,0 +1,50 @@ +/*! + * \file simulationthread.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for the simulation thread +*/ +#ifndef SIMULATIONTHREAD_H_ +#define SIMULATIONTHREAD_H_ + +#include +#include +#include "./machinemodel.h" +#include "./memorymodel.h" +#include "./arrow.h" +#include "./machine.h" +#include "./state.h" +#include "./transition.h" + +class SimulationThread : public QThread { + Q_OBJECT + + public: + explicit SimulationThread(QObject *parent = 0); + ~SimulationThread(); + void setMachine(Machine * m) { machine = m; } + + signals: + void updateScene(); + void selectTransition(QString n); + void selectState(QString n); + + public slots: + void stopSim(); + void pauseSim(); + void startSim(); + + protected: + void run(); + + private: + int m_abort; + QMutex mutex; + Machine * machine; + QGraphicsItem * item; + State * currentState; + Transition * currentTransition; +}; + +#endif // SIMULATIONTHREAD_H_ diff --git a/sortdelegate.cpp b/sortdelegate.cpp new file mode 100644 index 0000000..81979f3 --- /dev/null +++ b/sortdelegate.cpp @@ -0,0 +1,88 @@ +/*! + * \file sortdelegate.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of message sort delegate + */ +#include +#include "./sortdelegate.h" +#include "./sortdialog.h" +#include "./messagesort.h" + +SortDelegate::SortDelegate(Machine * m, Communication * comm , QObject *parent) + : QItemDelegate(parent) { + machine = m; + communication = comm; +} + +void SortDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const { + if (qVariantCanConvert(index.data())) { + MessageSort sort = qVariantValue(index.data()); + // painter->drawText(option.rect, Comm.toString()); + + if (option.state & QStyle::State_Selected) + painter->fillRect(option.rect, option.palette.highlight()); + + /*starRating.paint(painter, option.rect, option.palette, + StarRating::ReadOnly);*/ + sort.paint(painter, option.rect, option.palette, + MessageSort::ReadOnly); + } else { + QItemDelegate::paint(painter, option, index); + } +} + + +QWidget *SortDelegate::createEditor(QWidget */*parent*/, + const QStyleOptionViewItem &/*option*/, + const QModelIndex &index) const { + SortDialog *editor = new SortDialog(machine, + &communication->messageModel-> + getMessages()[index.row()].messageType); + + connect(editor, SIGNAL(accepted()), this, SLOT(commitAndCloseEditor())); + connect(editor, SIGNAL(rejected()), this, SLOT(commitAndCloseEditor())); + + // editor->setParent(windowParent); + editor->setModal(true); + // editor->move(100, 100); + // editor->setWindowFlags(WShowModal); + + return editor; +} + +void SortDelegate::setEditorData(QWidget *editor, + const QModelIndex &index) const { + if (qVariantCanConvert(index.data())) { + MessageSort sort = qVariantValue(index.data()); + SortDialog *dialog = static_cast(editor); + + dialog->setSort(sort); + } else { + QItemDelegate::setEditorData(editor, index); + } +} + +void SortDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const { + if (qVariantCanConvert(index.data())) { + SortDialog *dialog = static_cast(editor); + model->setData(index, qVariantFromValue(dialog->getSort())); + } else { + QItemDelegate::setModelData(editor, model, index); + } +} + +/*void CommDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + editor->setGeometry(option.rect); +}*/ + +void SortDelegate::commitAndCloseEditor() { + SortDialog *editor = qobject_cast(sender()); + emit commitData(editor); + emit closeEditor(editor); +} diff --git a/sortdelegate.h b/sortdelegate.h new file mode 100644 index 0000000..cb2ea17 --- /dev/null +++ b/sortdelegate.h @@ -0,0 +1,40 @@ +/*! + * \file sortdelegate.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for message sort delegate +*/ +#ifndef SORTDELEGATE_H_ +#define SORTDELEGATE_H_ + +#include +#include +#include +#include "./machine.h" +#include "./communication.h" + +class SortDelegate : public QItemDelegate { + Q_OBJECT + + public: + SortDelegate(Machine * machine, Communication * comm = 0, + QObject *parent = 0); + + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + + private slots: + void commitAndCloseEditor(); + + private: + Machine * machine; + Communication * communication; +}; + +#endif // SORTDELEGATE_H_ diff --git a/sortdialog.cpp b/sortdialog.cpp new file mode 100644 index 0000000..c7417c4 --- /dev/null +++ b/sortdialog.cpp @@ -0,0 +1,89 @@ +/*! + * \file sortdialog.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of message sort dialog + */ +#include "./sortdialog.h" +#include "./messagesort.h" + +SortDialog::SortDialog(Machine * m, QString * mt, QWidget *parent) + : QDialog(parent) { + setupUi(this); + + machine = m; + + QStringList messageMemoryNames = machine->getMessageMemoryNames(*mt); + comboBox_key->addItems(messageMemoryNames); + QStringList sorts; + sorts << "ascend" << "descend"; + comboBox_order->addItems(sorts); + + connect(this->checkBox_notRandomOrSort, SIGNAL(clicked()), + this, SLOT(clickedNotRandomOrSort())); + connect(this->checkBox_random, SIGNAL(clicked()), + this, SLOT(clickedRandom())); + connect(this->checkBox_sort, SIGNAL(clicked()), this, SLOT(clickedSort())); +} + +void SortDialog::setSort(MessageSort s) { + sort = s; + + if (s.isRandom) { + checkBox_notRandomOrSort->setChecked(false); + checkBox_random->setChecked(true); + checkBox_sort->setChecked(false); + groupBox_sort->setEnabled(false); + } else if (s.isSort) { + checkBox_notRandomOrSort->setChecked(false); + checkBox_random->setChecked(false); + checkBox_sort->setChecked(true); + groupBox_sort->setEnabled(true); + comboBox_key->setCurrentIndex(0); + for (int i = 0; i < comboBox_key->count(); i++) { + if (QString::compare(comboBox_key->itemText(i), s.key) == 0) + comboBox_key->setCurrentIndex(i); + } + comboBox_order->setCurrentIndex(0); + for (int i = 0; i < comboBox_order->count(); i++) { + if (QString::compare(comboBox_order->itemText(i), s.order) == 0) + comboBox_order->setCurrentIndex(i); + } + } else { + checkBox_notRandomOrSort->setChecked(true); + checkBox_random->setChecked(false); + checkBox_sort->setChecked(false); + groupBox_sort->setEnabled(false); + } +} + +MessageSort SortDialog::getSort() { + sort.isRandom = checkBox_random->isChecked(); + sort.isSort = checkBox_sort->isChecked(); + sort.key = comboBox_key->currentText(); + sort.order = comboBox_order->currentText(); + + return sort; +} + +void SortDialog::clickedNotRandomOrSort() { + checkBox_notRandomOrSort->setChecked(true); + checkBox_random->setChecked(false); + checkBox_sort->setChecked(false); + groupBox_sort->setEnabled(false); +} + +void SortDialog::clickedRandom() { + checkBox_notRandomOrSort->setChecked(false); + checkBox_random->setChecked(true); + checkBox_sort->setChecked(false); + groupBox_sort->setEnabled(false); +} + +void SortDialog::clickedSort() { + checkBox_notRandomOrSort->setChecked(false); + checkBox_random->setChecked(false); + checkBox_sort->setChecked(true); + groupBox_sort->setEnabled(true); +} diff --git a/sortdialog.h b/sortdialog.h new file mode 100644 index 0000000..e20ab1a --- /dev/null +++ b/sortdialog.h @@ -0,0 +1,34 @@ +/*! + * \file sortdialog.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for the message sort dialog +*/ +#ifndef SORTDIALOG_H_ +#define SORTDIALOG_H_ + +#include +#include "./messagesort.h" +#include "./ui_sortdialog.h" +#include "./machine.h" + +class SortDialog : public QDialog, public Ui::SortDialog { + Q_OBJECT + + public: + SortDialog(Machine * m, QString * messageType = 0, QWidget *parent = 0); + void setSort(MessageSort s); + MessageSort getSort(); + + private slots: + void clickedRandom(); + void clickedSort(); + void clickedNotRandomOrSort(); + + private: + MessageSort sort; + Machine * machine; +}; + +#endif // SORTDIALOG_H_ diff --git a/sortdialog.ui b/sortdialog.ui new file mode 100644 index 0000000..895b238 --- /dev/null +++ b/sortdialog.ui @@ -0,0 +1,126 @@ + + + SortDialog + + + + 0 + 0 + 400 + 209 + + + + Sort Editor + + + + + + not randomised or sorted + + + + + + + random + + + + + + + + + sort + + + + + + + Sort + + + + + + + + key + + + + + + + + + + + + + + order + + + + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + SortDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + SortDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/state.cpp b/state.cpp new file mode 100644 index 0000000..24d88b7 --- /dev/null +++ b/state.cpp @@ -0,0 +1,12 @@ +/*! + * \file state.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of model state + * + * \warning This file is empty as implementation is in the header file \c state.h +*/ +#include "./state.h" + + diff --git a/state.h b/state.h new file mode 100644 index 0000000..7fe53b4 --- /dev/null +++ b/state.h @@ -0,0 +1,25 @@ +/*! + * \file state.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for machine state +*/ +#ifndef STATE_H_ +#define STATE_H_ + +#include + +class State { + public: + State() { myName = ""; } + explicit State(QString n) { myName = n; } + + QString name() const { return myName; } + void setName(QString n) { myName = n; } + + private: + QString myName; +}; + +#endif // STATE_H_ diff --git a/statedelegate.cpp b/statedelegate.cpp new file mode 100644 index 0000000..38f0974 --- /dev/null +++ b/statedelegate.cpp @@ -0,0 +1,41 @@ +/*! + * \file statedelegate.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of model state delegate + */ +#include +#include "./statedelegate.h" + +StateDelegate::StateDelegate(QObject *parent) + : QItemDelegate(parent) { +} + +QWidget *StateDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &/*option*/, + const QModelIndex &/*index*/) const { + QLineEdit *editor = new QLineEdit(parent); + return editor; +} + +void StateDelegate::setEditorData(QWidget *editor, + const QModelIndex &index) const { + QString value = index.data().toString(); + + QLineEdit * lineEdit = static_cast(editor); + lineEdit->setText(value); +} + +void StateDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const { + QLineEdit *lineEdit = static_cast(editor); + QString value = lineEdit->text(); + + model->setData(index, value, Qt::EditRole); +} + +void StateDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &/*index*/) const { + editor->setGeometry(option.rect); +} diff --git a/statedelegate.h b/statedelegate.h new file mode 100644 index 0000000..3de7df9 --- /dev/null +++ b/statedelegate.h @@ -0,0 +1,33 @@ +/*! + * \file statedelegate.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for the machine state delegate +*/ +#ifndef STATEDELEGATE_H_ +#define STATEDELEGATE_H_ + +#include +#include +#include +#include + +class StateDelegate : public QItemDelegate { +Q_OBJECT + + public: + explicit StateDelegate(QObject *parent = 0); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + + void updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const; +}; + +#endif // STATEDELEGATE_H_ diff --git a/tests/models/conditions.xml b/tests/models/conditions.xml new file mode 100644 index 0000000..8b766dd --- /dev/null +++ b/tests/models/conditions.xml @@ -0,0 +1,200 @@ + + conditions + + + + + + + monthly + iteration + 20 + + + + + + + AgentA + + + intagentA_a + intagentA_b + intagentA_c + intagentA_d + + + + + f1 + + 1 + 2 + + + + + + + OR + + + a.agentA_a + + EQ + + 0 + + + + + + + messageA + + + + + + f2 + + 2 + 3 + + + messageA + + m.messageA_aEQa.agentA_a + + + message_a + ascend + + + + + + + f3 + + 2 + 3 + + + messageA + + m.messageA_aEQ1 + + true + + + + + + f4 + + 3 + 4 + + + f5 + + 3 + 4 + + + f6 + + 3 + 4 + + + + f7 + + 4 + 5 + + + f8 + + 4 + 5 + + + f9 + + 4 + 5 + + + f10 + + 4 + 5 + + + + f11 + + 5 + 6 + + + f12 + + 5 + 6 + + + f13 + + 5 + 6 + + + f14 + + 5 + 6 + + + f15 + + 5 + 6 + + + + + + + + + messageA + + + intmessageA_a + intmessageA_b + intmessageA_c + intmessageA_d + + + + + messageB + + + intmessageB_a + intmessageB_b + intmessageB_c + intmessageB_d + + + + + diff --git a/tests/models/malformed_submodel.xml b/tests/models/malformed_submodel.xml new file mode 100644 index 0000000..54b57fe --- /dev/null +++ b/tests/models/malformed_submodel.xml @@ -0,0 +1,205 @@ + + conditions + + + + + malformed_xml.xml + true + + + + + + monthly + iteration + 20 + + + + + + + AgentA + + + intagentA_a + intagentA_b + intagentA_c + intagentA_d + + + + + f1 + + 1 + 2 + + + + + + + OR + + + a.agentA_a + + EQ + + 0 + + + + + + + messageA + + + + + + f2 + + 2 + 3 + + + messageA + + m.messageA_aEQa.agentA_a + + + message_a + ascend + + + + + + + f3 + + 2 + 3 + + + messageA + + m.messageA_aEQ1 + + true + + + + + + f4 + + 3 + 4 + + + f5 + + 3 + 4 + + + f6 + + 3 + 4 + + + + f7 + + 4 + 5 + + + f8 + + 4 + 5 + + + f9 + + 4 + 5 + + + f10 + + 4 + 5 + + + + f11 + + 5 + 6 + + + f12 + + 5 + 6 + + + f13 + + 5 + 6 + + + f14 + + 5 + 6 + + + f15 + + 5 + 6 + + + + + + + + + messageA + + + intmessageA_a + intmessageA_b + intmessageA_c + intmessageA_d + + + + + messageB + + + intmessageB_a + intmessageB_b + intmessageB_c + intmessageB_d + + + + + diff --git a/tests/models/malformed_xml.xml b/tests/models/malformed_xml.xml new file mode 100644 index 0000000..862fec0 --- /dev/null +++ b/tests/models/malformed_xml.xml @@ -0,0 +1,200 @@ + + conditions + + + + + + + monthly + iteration + 20 + + + + + + + AgentA + + + intagentA_a + intagentA_b + intagentA_c + intagentA_d + + + + + f1 + + 1 + 2 + + + + + + + OR + + + a.agentA_a + + EQ + + 0 + + + + + + + messageA + + + + + + f2 + + 2 + 3 + + + messageA + + m.messageA_aEQa.agentA_a + + + message_a + ascend + + + + + + + f3 + + 2 + 3 + + + messageA + + m.messageA_aEQ1 + + true + + + + + + f4 + + 3 + 4 + + + f5 + + 3 + 4 + + + f6 + + 3 + 4 + + + + f7 + + 4 + 5 + + + f8 + + 4 + 5 + + + f9 + + 4 + 5 + + + f10 + + 4 + 5 + + + + f11 + + 5 + 6 + + + f12 + + 5 + 6 + + + f13 + + 5 + 6 + + + f14 + + 5 + 6 + + + f15 + + 5 + 6 + + + + + + + + + messageA + + + intmessageA_a + intmessageA_b + intmessageA_c + intmessageA_d + + + + + messageB + + + intmessageB_a + intmessageB_b + intmessageB_c + intmessageB_d + + + + + diff --git a/tests/models/missing_submodel.xml b/tests/models/missing_submodel.xml new file mode 100644 index 0000000..85e3653 --- /dev/null +++ b/tests/models/missing_submodel.xml @@ -0,0 +1,254 @@ + + + conditions + + + + + + nent.xml + true + + + + + + monthly + iteration + 20 + + + + + + AgentA + + + + int + agentA_a + + + + int + agentA_b + + + + int + agentA_c + + + + int + agentA_d + + + + + + f1 + + 1 + 2 + + + + + + + OR + + + a.agentA_a + + EQ + + 0 + + + + + + messageA + + + + + f2 + + 2 + 3 + + + messageA + + + m.messageA_a + + EQ + + a.agentA_a + + + + message_a + ascend + + + + + + f3 + + 2 + 3 + + + messageA + true + + + m.messageA_a + + EQ + + 1 + + + + + + + f4 + + 3 + 4 + + + f5 + + 3 + 4 + + + f6 + + 3 + 4 + + + f7 + + 4 + 5 + + + f8 + + 4 + 5 + + + f9 + + 4 + 5 + + + f10 + + 4 + 5 + + + f11 + + 5 + 6 + + + f12 + + 5 + 6 + + + f13 + + 5 + 6 + + + f14 + + 5 + 6 + + + f15 + + 5 + 6 + + + + + + + messageA + + + + int + messageA_a + + + + int + messageA_b + + + + int + messageA_c + + + + int + messageA_d + + + + + + messageB + + + + int + messageB_a + + + + int + messageB_b + + + + int + messageB_c + + + + int + messageB_d + + + + + + diff --git a/tests/scripts/HTMLTestRunner/HTMLTestRunner.py b/tests/scripts/HTMLTestRunner/HTMLTestRunner.py new file mode 100644 index 0000000..0439bf4 --- /dev/null +++ b/tests/scripts/HTMLTestRunner/HTMLTestRunner.py @@ -0,0 +1,824 @@ +""" +A TestRunner for use with the Python unit testing framework. It +generates a HTML report to show the result at a glance. + +The simplest way to use this is to invoke its main method. E.g. + + import unittest + import HTMLTestRunner + + ... define your tests ... + + if __name__ == '__main__': + HTMLTestRunner.main() + + +For more customization options, instantiates a HTMLTestRunner object. +HTMLTestRunner is a counterpart to unittest's TextTestRunner. E.g. + + # output to a file + fp = file('my_report.html', 'wb') + runner = HTMLTestRunner.HTMLTestRunner( + stream=fp, + title='My unit test', + description='This demonstrates the report output by HTMLTestRunner.' + ) + + # Use an external stylesheet. + # See the Template_mixin class for more customizable options + runner.STYLESHEET_TMPL = '' + + # run the test + runner.run(my_test_suite) + + +------------------------------------------------------------------------ +Copyright (c) 2004-2007, Wai Yip Tung +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name Wai Yip Tung nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +""" + +# URL: http://tungwaiyip.info/software/HTMLTestRunner.html + +__author__ = "Wai Yip Tung" +__version__ = "0.8.2" + + +""" +Change History + +Version 0.8.2 +* Show output inline instead of popup window (Viorel Lupu). + +Version in 0.8.1 +* Validated XHTML (Wolfgang Borgert). +* Added description of test classes and test cases. + +Version in 0.8.0 +* Define Template_mixin class for customization. +* Workaround a IE 6 bug that it does not treat + +%(heading)s +%(report)s +%(ending)s + + + +""" + # variables: (title, generator, stylesheet, heading, report, ending) + + + # ------------------------------------------------------------------------ + # Stylesheet + # + # alternatively use a for external style sheet, e.g. + # + + STYLESHEET_TMPL = """ + +""" + + + + # ------------------------------------------------------------------------ + # Heading + # + + HEADING_TMPL = """
+

%(title)s

+%(parameters)s +

%(description)s

+
+ +""" # variables: (title, parameters, description) + + HEADING_ATTRIBUTE_TMPL = """

%(name)s: %(value)s

+""" # variables: (name, value) + + + + # ------------------------------------------------------------------------ + # Report + # + + REPORT_TMPL = """ +

Show +Summary +Failed +All +

+ ++++++++ + + + + + + + + +%(test_list)s + + + + + + + + +
Test Group/Test caseCountPassFailErrorView
Total%(count)s%(Pass)s%(fail)s%(error)s 
+""" # variables: (test_list, count, Pass, fail, error) + + REPORT_CLASS_TMPL = r""" + + %(desc)s + %(count)s + %(Pass)s + %(fail)s + %(error)s + Detail + +""" # variables: (style, desc, count, Pass, fail, error, cid) + + + REPORT_TEST_WITH_OUTPUT_TMPL = r""" + +
%(desc)s
+ + + + + %(status)s + + + + + + +""" # variables: (tid, Class, style, desc, status) + + + REPORT_TEST_NO_OUTPUT_TMPL = r""" + +
%(desc)s
+ %(status)s + +""" # variables: (tid, Class, style, desc, status) + + + REPORT_TEST_OUTPUT_TMPL = r""" +%(id)s: %(output)s +""" # variables: (id, output) + + + + # ------------------------------------------------------------------------ + # ENDING + # + + ENDING_TMPL = """
 
""" + +# -------------------- The end of the Template class ------------------- + + +TestResult = unittest.TestResult + +class _TestResult(TestResult): + # note: _TestResult is a pure representation of results. + # It lacks the output and reporting ability compares to unittest._TextTestResult. + + def __init__(self, verbosity=1): + TestResult.__init__(self) + self.stdout0 = None + self.stderr0 = None + self.success_count = 0 + self.failure_count = 0 + self.error_count = 0 + self.verbosity = verbosity + + # result is a list of result in 4 tuple + # ( + # result code (0: success; 1: fail; 2: error), + # TestCase object, + # Test output (byte string), + # stack trace, + # ) + self.result = [] + + + def startTest(self, test): + TestResult.startTest(self, test) + # just one buffer for both stdout and stderr + self.outputBuffer = StringIO.StringIO() + stdout_redirector.fp = self.outputBuffer + stderr_redirector.fp = self.outputBuffer + self.stdout0 = sys.stdout + self.stderr0 = sys.stderr + sys.stdout = stdout_redirector + sys.stderr = stderr_redirector + + + def complete_output(self): + """ + Disconnect output redirection and return buffer. + Safe to call multiple times. + """ + if self.stdout0: + sys.stdout = self.stdout0 + sys.stderr = self.stderr0 + self.stdout0 = None + self.stderr0 = None + return self.outputBuffer.getvalue() + + + def stopTest(self, test): + # Usually one of addSuccess, addError or addFailure would have been called. + # But there are some path in unittest that would bypass this. + # We must disconnect stdout in stopTest(), which is guaranteed to be called. + self.complete_output() + + + def addSuccess(self, test): + self.success_count += 1 + TestResult.addSuccess(self, test) + output = self.complete_output() + self.result.append((0, test, output, '')) + if self.verbosity > 1: + sys.stderr.write('ok ') + sys.stderr.write(str(test)) + sys.stderr.write('\n') + else: + sys.stderr.write('.') + + def addError(self, test, err): + self.error_count += 1 + TestResult.addError(self, test, err) + _, _exc_str = self.errors[-1] + output = self.complete_output() + self.result.append((2, test, output, _exc_str)) + if self.verbosity > 1: + sys.stderr.write('E ') + sys.stderr.write(str(test)) + sys.stderr.write('\n') + else: + sys.stderr.write('E') + + def addFailure(self, test, err): + self.failure_count += 1 + TestResult.addFailure(self, test, err) + _, _exc_str = self.failures[-1] + output = self.complete_output() + self.result.append((1, test, output, _exc_str)) + if self.verbosity > 1: + sys.stderr.write('F ') + sys.stderr.write(str(test)) + sys.stderr.write('\n') + else: + sys.stderr.write('F') + + +class HTMLTestRunner(Template_mixin): + """ + """ + def __init__(self, stream=sys.stdout, verbosity=1, title=None, description=None): + self.stream = stream + self.verbosity = verbosity + if title is None: + self.title = self.DEFAULT_TITLE + else: + self.title = title + if description is None: + self.description = self.DEFAULT_DESCRIPTION + else: + self.description = description + + self.startTime = datetime.datetime.now() + + + def run(self, test): + "Run the given test case or test suite." + result = _TestResult(self.verbosity) + test(result) + self.stopTime = datetime.datetime.now() + self.generateReport(test, result) + print >>sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime) + return result + + + def sortResult(self, result_list): + # unittest does not seems to run in any particular order. + # Here at least we want to group them together by class. + rmap = {} + classes = [] + for n,t,o,e in result_list: + cls = t.__class__ + if not rmap.has_key(cls): + rmap[cls] = [] + classes.append(cls) + rmap[cls].append((n,t,o,e)) + r = [(cls, rmap[cls]) for cls in classes] + return r + + + def getReportAttributes(self, result): + """ + Return report attributes as a list of (name, value). + Override this to add custom attributes. + """ + startTime = str(self.startTime)[:19] + duration = str(self.stopTime - self.startTime) + status = [] + if result.success_count: status.append('Pass %s' % result.success_count) + if result.failure_count: status.append('Failure %s' % result.failure_count) + if result.error_count: status.append('Error %s' % result.error_count ) + if status: + status = ' '.join(status) + else: + status = 'none' + return [ + ('Start Time', startTime), + ('Duration', duration), + ('Status', status), + ] + + + def generateReport(self, test, result): + report_attrs = self.getReportAttributes(result) + generator = 'HTMLTestRunner %s' % __version__ + stylesheet = self._generate_stylesheet() + heading = self._generate_heading(report_attrs) + report = self._generate_report(result) + ending = self._generate_ending() + output = self.HTML_TMPL % dict( + title = saxutils.escape(self.title), + generator = generator, + stylesheet = stylesheet, + heading = heading, + report = report, + ending = ending, + ) + self.stream.write(output.encode('utf8')) + + + def _generate_stylesheet(self): + return self.STYLESHEET_TMPL + + + def _generate_heading(self, report_attrs): + a_lines = [] + for name, value in report_attrs: + line = self.HEADING_ATTRIBUTE_TMPL % dict( + name = saxutils.escape(name), + value = saxutils.escape(value), + ) + a_lines.append(line) + heading = self.HEADING_TMPL % dict( + title = saxutils.escape(self.title), + parameters = ''.join(a_lines), + description = saxutils.escape(self.description), + ) + return heading + + + def _generate_report(self, result): + rows = [] + sortedResult = self.sortResult(result.result) + for cid, (cls, cls_results) in enumerate(sortedResult): + # subtotal for a class + np = nf = ne = 0 + for n,t,o,e in cls_results: + if n == 0: np += 1 + elif n == 1: nf += 1 + else: ne += 1 + + # format class description + if cls.__module__ == "__main__": + name = cls.__name__ + else: + name = "%s.%s" % (cls.__module__, cls.__name__) + doc = cls.__doc__ and cls.__doc__.split("\n")[0] or "" + desc = doc and '%s: %s' % (name, doc) or name + + row = self.REPORT_CLASS_TMPL % dict( + style = ne > 0 and 'errorClass' or nf > 0 and 'failClass' or 'passClass', + desc = desc, + count = np+nf+ne, + Pass = np, + fail = nf, + error = ne, + cid = 'c%s' % (cid+1), + ) + rows.append(row) + + for tid, (n,t,o,e) in enumerate(cls_results): + self._generate_report_test(rows, cid, tid, n, t, o, e) + + report = self.REPORT_TMPL % dict( + test_list = ''.join(rows), + count = str(result.success_count+result.failure_count+result.error_count), + Pass = str(result.success_count), + fail = str(result.failure_count), + error = str(result.error_count), + ) + return report + + + def _generate_report_test(self, rows, cid, tid, n, t, o, e): + # e.g. 'pt1.1', 'ft1.1', etc + has_output = bool(o or e) + tid = (n == 0 and 'p' or 'f') + 't%s.%s' % (cid+1,tid+1) + name = t.id().split('.')[-1] + doc = t.shortDescription() or "" + desc = doc and ('%s: %s' % (name, doc)) or name + tmpl = has_output and self.REPORT_TEST_WITH_OUTPUT_TMPL or self.REPORT_TEST_NO_OUTPUT_TMPL + + # o and e should be byte string because they are collected from stdout and stderr? + if isinstance(o,str): + # TODO: some problem with 'string_escape': it escape \n and mess up formating + # uo = unicode(o.encode('string_escape')) + uo = o.decode('latin-1') + else: + uo = o + if isinstance(e,str): + # TODO: some problem with 'string_escape': it escape \n and mess up formating + # ue = unicode(e.encode('string_escape')) + ue = e.decode('latin-1') + else: + ue = e + + script = self.REPORT_TEST_OUTPUT_TMPL % dict( + id = tid, + output = saxutils.escape(uo+ue), + ) + + row = tmpl % dict( + tid = tid, + Class = (n == 0 and 'hiddenRow' or 'none'), + style = n == 2 and 'errorCase' or (n == 1 and 'failCase' or 'none'), + desc = desc, + script = script, + status = self.STATUS[n], + ) + rows.append(row) + if not has_output: + return + + def _generate_ending(self): + return self.ENDING_TMPL + + +############################################################################## +# Facilities for running tests from the command line +############################################################################## + +# Note: Reuse unittest.TestProgram to launch test. In the future we may +# build our own launcher to support more specific command line +# parameters like test title, CSS, etc. +class TestProgram(unittest.TestProgram): + """ + A variation of the unittest.TestProgram. Please refer to the base + class for command line parameters. + """ + def runTests(self): + # Pick HTMLTestRunner as the default test runner. + # base class's testRunner parameter is not useful because it means + # we have to instantiate HTMLTestRunner before we know self.verbosity. + if self.testRunner is None: + self.testRunner = HTMLTestRunner(verbosity=self.verbosity) + unittest.TestProgram.runTests(self) + +main = TestProgram + +############################################################################## +# Executing this module from the command line +############################################################################## + +if __name__ == "__main__": + main(module=None) diff --git a/tests/scripts/mac_test_editor.sikuli/mac_lion/Cancel_button.png b/tests/scripts/mac_test_editor.sikuli/mac_lion/Cancel_button.png new file mode 100644 index 0000000..8c4dbb6 Binary files /dev/null and b/tests/scripts/mac_test_editor.sikuli/mac_lion/Cancel_button.png differ diff --git a/tests/scripts/mac_test_editor.sikuli/mac_lion/Documents.png b/tests/scripts/mac_test_editor.sikuli/mac_lion/Documents.png new file mode 100644 index 0000000..6328b99 Binary files /dev/null and b/tests/scripts/mac_test_editor.sikuli/mac_lion/Documents.png differ diff --git a/tests/scripts/mac_test_editor.sikuli/mac_lion/FLAME_Editor_window_title.png b/tests/scripts/mac_test_editor.sikuli/mac_lion/FLAME_Editor_window_title.png new file mode 100644 index 0000000..12e76c5 Binary files /dev/null and b/tests/scripts/mac_test_editor.sikuli/mac_lion/FLAME_Editor_window_title.png differ diff --git a/tests/scripts/mac_test_editor.sikuli/mac_lion/New_button.png b/tests/scripts/mac_test_editor.sikuli/mac_lion/New_button.png new file mode 100644 index 0000000..9077898 Binary files /dev/null and b/tests/scripts/mac_test_editor.sikuli/mac_lion/New_button.png differ diff --git a/tests/scripts/mac_test_editor.sikuli/mac_lion/Open_button.png b/tests/scripts/mac_test_editor.sikuli/mac_lion/Open_button.png new file mode 100644 index 0000000..4a16bcd Binary files /dev/null and b/tests/scripts/mac_test_editor.sikuli/mac_lion/Open_button.png differ diff --git a/tests/scripts/mac_test_editor.sikuli/mac_lion/ValueDescriT.png b/tests/scripts/mac_test_editor.sikuli/mac_lion/ValueDescriT.png new file mode 100644 index 0000000..5c8a4f1 Binary files /dev/null and b/tests/scripts/mac_test_editor.sikuli/mac_lion/ValueDescriT.png differ diff --git a/tests/scripts/mac_test_editor.sikuli/mac_lion/Valuemodelna.png b/tests/scripts/mac_test_editor.sikuli/mac_lion/Valuemodelna.png new file mode 100644 index 0000000..14a3fd1 Binary files /dev/null and b/tests/scripts/mac_test_editor.sikuli/mac_lion/Valuemodelna.png differ diff --git a/tests/scripts/mac_test_editor.sikuli/mac_lion/a_test_models_folder.png b/tests/scripts/mac_test_editor.sikuli/mac_lion/a_test_models_folder.png new file mode 100644 index 0000000..57c07b9 Binary files /dev/null and b/tests/scripts/mac_test_editor.sikuli/mac_lion/a_test_models_folder.png differ diff --git a/tests/scripts/mac_test_editor.sikuli/mac_lion/conditions.xml_file.png b/tests/scripts/mac_test_editor.sikuli/mac_lion/conditions.xml_file.png new file mode 100644 index 0000000..5f8f008 Binary files /dev/null and b/tests/scripts/mac_test_editor.sikuli/mac_lion/conditions.xml_file.png differ diff --git a/tests/scripts/mac_test_editor.sikuli/mac_lion/file_dialog_Open_button.png b/tests/scripts/mac_test_editor.sikuli/mac_lion/file_dialog_Open_button.png new file mode 100644 index 0000000..557bbfa Binary files /dev/null and b/tests/scripts/mac_test_editor.sikuli/mac_lion/file_dialog_Open_button.png differ diff --git a/tests/scripts/mac_test_editor.sikuli/mac_lion/file_dialog_Save_button.png b/tests/scripts/mac_test_editor.sikuli/mac_lion/file_dialog_Save_button.png new file mode 100644 index 0000000..7bf6f5d Binary files /dev/null and b/tests/scripts/mac_test_editor.sikuli/mac_lion/file_dialog_Save_button.png differ diff --git a/tests/scripts/mac_test_editor.sikuli/mac_lion/one_model_in_models_tree.png b/tests/scripts/mac_test_editor.sikuli/mac_lion/one_model_in_models_tree.png new file mode 100644 index 0000000..7b9b9c2 Binary files /dev/null and b/tests/scripts/mac_test_editor.sikuli/mac_lion/one_model_in_models_tree.png differ diff --git a/tests/scripts/mac_test_editor.sikuli/mac_lion/text_line_highlighted.png b/tests/scripts/mac_test_editor.sikuli/mac_lion/text_line_highlighted.png new file mode 100644 index 0000000..0528366 Binary files /dev/null and b/tests/scripts/mac_test_editor.sikuli/mac_lion/text_line_highlighted.png differ diff --git a/tests/scripts/mac_test_editor.sikuli/mac_lion/workspace_folder.png b/tests/scripts/mac_test_editor.sikuli/mac_lion/workspace_folder.png new file mode 100644 index 0000000..a599708 Binary files /dev/null and b/tests/scripts/mac_test_editor.sikuli/mac_lion/workspace_folder.png differ diff --git a/tests/scripts/mac_test_editor.sikuli/mac_test_editor.py b/tests/scripts/mac_test_editor.sikuli/mac_test_editor.py new file mode 100644 index 0000000..ef40cab --- /dev/null +++ b/tests/scripts/mac_test_editor.sikuli/mac_test_editor.py @@ -0,0 +1,133 @@ +from sikuli.Sikuli import * +import unittest +import sys +import os +import platform +# Add HTMLTestRunner to PATH and import +sys.path.append("/Users/stc/workspace/flame/flame_editor/branches/stc/tests/scripts/HTMLTestRunner") +sys.path.append(str(getBundlePath()) + "/../" + "HTMLTestRunner") +import HTMLTestRunner + +myOS = str(Env.getOS()) +myVer = str(Env.getOSVersion()) + +if myOS == "MAC" and myVer.startswith("10.7"): + os = "mac_lion" +elif myOS == "LINUX": + p = os.popen("uname -a") + uname = p.readline() + p.close() + if 'Ubuntu' in uname: + p = os.popen("lsb_release -r") + ver = p.readline() + p.close() + if '11.04' in ver: + os = "ubuntu_11.04" + else: + print "Error: no script for Ubuntu ver " + ver + exit(1) + else: + print "Error: no script for Linux dist " + uname + exit(1) +else: + print "Error: no script for OS " + myOS + " " + myVer + exit(1) + +print "Using script for OS " + os +# Add image path for current OS +addImagePath(str(getBundlePath()) + "/" + os) + +# If run from the test script set parameters from arguments +if len(sys.argv) > 1: + app = sys.argv[1] + outfile = open(sys.argv[2], "w") +# If run from Sikuli GUI set parameters internally +else: + #app = "/home/stc/workspace/flame_editor/stc/flame_editor" + app = "/Users/stc/workspace/flame/flame_editor/branches/flame_editor-build-desktop/flame_editor.app" + #app = "/Users/stc/workspace/flame/flame_editor/tags/flame_editor-build-desktop/flame_editor.app" + #app = "flame_editor" + outfile = open(getBundlePath() + "/../" + "flame_editor_test_report.html", "w") + +# set default wait time out to 10s instead of default 3s +setAutoWaitTimeout(10) + +#setShowActions(True) + +# Test suite classes +class Create_new_model(unittest.TestCase): + def setUp(self): + openApp(app) + wait("FLAME_Editor_window_title.png") + + def tearDown(self): + closeApp("flame_editor") + waitVanish("FLAME_Editor_window_title.png") + + def _create_new_model(self): + click("New_button.png") + wait("text_line_highlighted.png") + type("testmodel.xml") + wait("file_dialog_Save_button.png") + click("file_dialog_Save_button.png") + + def _cancel_new_model(self): + click("New_button.png") + wait("Cancel_button.png") + click("Cancel_button.png") + + def test_cancel_new_model(self): + self._cancel_new_model() + self._create_new_model() + assert exists("one_model_in_models_tree.png") + + def test_create_new_model(self): + self._create_new_model() + assert exists("one_model_in_models_tree.png") + +class Open_a_model(unittest.TestCase): + editor = App(app); + + def setUp(self): + self.editor.open() + openApp(app) + wait("FLAME_Editor_window_title.png") + + def tearDown(self): + #print "editor.close start" + #self.editor.close() + #print "editor.close finish" + closeApp("flame_editor") + waitVanish("FLAME_Editor_window_title.png") + + def _open_test_model_folder(self): + click("Open_button.png") + wait("Documents.png") + click("Documents.png") + click("workspace_folder.png") + click("a_test_models_folder.png") + click("conditions.xml_file.png") + click("file_dialog_Open_button.png") + wait("FLAME_Editor_window_title.png") + assert exists("one_model_in_models_tree.png") + + def test_open_a_valid_model(self): + self._open_test_model_folder() + #click("conditions.xml_file.png") + #wait("file_dialog_Open_button.png") + #click("file_dialog_Open_button.png") + #assert exists("one_model_in_models_tree.png") + +# End test suite classes + +# Define the root test suite +suite = unittest.TestSuite() +# Add test classes +#suite.addTest(unittest.makeSuite(TestNewModel)) +#suite.addTest(unittest.makeSuite(EditorTests)) +# Add single test (for development) +suite.addTest(Open_a_model("test_open_a_valid_model")) +# Initialise HTMLTestRunner and execute +runner = HTMLTestRunner.HTMLTestRunner(stream=outfile, title='FLAME Editor Test Report', description='' ) +runner.run(suite) +print "Output written to " + outfile.name \ No newline at end of file diff --git a/tests/scripts/mac_test_editor.sikuli/ubuntu_11.04/Cancel.png b/tests/scripts/mac_test_editor.sikuli/ubuntu_11.04/Cancel.png new file mode 100644 index 0000000..490694f Binary files /dev/null and b/tests/scripts/mac_test_editor.sikuli/ubuntu_11.04/Cancel.png differ diff --git a/tests/scripts/mac_test_editor.sikuli/ubuntu_11.04/FLAMEEditor.png b/tests/scripts/mac_test_editor.sikuli/ubuntu_11.04/FLAMEEditor.png new file mode 100644 index 0000000..8c77cac Binary files /dev/null and b/tests/scripts/mac_test_editor.sikuli/ubuntu_11.04/FLAMEEditor.png differ diff --git a/tests/scripts/mac_test_editor.sikuli/ubuntu_11.04/New-1.png b/tests/scripts/mac_test_editor.sikuli/ubuntu_11.04/New-1.png new file mode 100644 index 0000000..24b0118 Binary files /dev/null and b/tests/scripts/mac_test_editor.sikuli/ubuntu_11.04/New-1.png differ diff --git a/tests/scripts/mac_test_editor.sikuli/ubuntu_11.04/Save.png b/tests/scripts/mac_test_editor.sikuli/ubuntu_11.04/Save.png new file mode 100644 index 0000000..d46c63b Binary files /dev/null and b/tests/scripts/mac_test_editor.sikuli/ubuntu_11.04/Save.png differ diff --git a/tests/scripts/mac_test_editor.sikuli/ubuntu_11.04/SaveAs.png b/tests/scripts/mac_test_editor.sikuli/ubuntu_11.04/SaveAs.png new file mode 100644 index 0000000..4ab4310 Binary files /dev/null and b/tests/scripts/mac_test_editor.sikuli/ubuntu_11.04/SaveAs.png differ diff --git a/tests/scripts/mac_test_editor.sikuli/ubuntu_11.04/TypeEHVII.png b/tests/scripts/mac_test_editor.sikuli/ubuntu_11.04/TypeEHVII.png new file mode 100644 index 0000000..435ac2a Binary files /dev/null and b/tests/scripts/mac_test_editor.sikuli/ubuntu_11.04/TypeEHVII.png differ diff --git a/tests/scripts/run_sikuli_tests.sh b/tests/scripts/run_sikuli_tests.sh new file mode 100644 index 0000000..65f1915 --- /dev/null +++ b/tests/scripts/run_sikuli_tests.sh @@ -0,0 +1,23 @@ +#!/bin/sh +# Get the full directory name of the current script regardless of where it is called from +#DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +#TEST1=$DIR"/mac_test_editor.sikuli" +#/Applications/Sikuli-IDE.app/sikuli-ide.sh -r $TEST1 + +#JYTHONDIR=/usr/local/Cellar/jython/2.5.2/libexec/jython.jar +# Location of sikuli jar file +SIKULI=/Applications/Sikuli-IDE.app/Contents/Resources/Java/sikuli-script.jar +# Name of sikuli script +SCRIPT=mac_test_editor + +SCRIPTDIRECTORY=$SCRIPT.sikuli +SCRIPTFILE=$SCRIPT.py +#APP=/Users/stc/workspace/flame/flame_editor/branches/flame_editor-build-desktop/flame_editor.app +APP=/Users/stc/workspace/flame/flame_editor/tags/flame_editor-build-desktop/flame_editor.app +OUTPUT=../flame_editor_test_report.html +# Change directory to run script in same directory as its associated images +cd $SCRIPTDIRECTORY +RUN="java -cp "$SIKULI" org.python.util.jython "$SCRIPTFILE" "$APP" "$OUTPUT"" +#echo $RUN +$RUN +cd .. diff --git a/texteditdelegate.cpp b/texteditdelegate.cpp new file mode 100644 index 0000000..161fcd5 --- /dev/null +++ b/texteditdelegate.cpp @@ -0,0 +1,41 @@ +/*! + * \file texteditdelegate.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of text box edit delegate + */ +#include +#include "./texteditdelegate.h" + +TextEditDelegate::TextEditDelegate(QObject * parent) + : QItemDelegate(parent) { +} + +QWidget *TextEditDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &/*option*/, + const QModelIndex &/*index*/) const { + QTextEdit * editor = new QTextEdit(parent); + return editor; +} + +void TextEditDelegate::setEditorData(QWidget *editor, + const QModelIndex &index) const { + QString value = index.data().toString(); + + QTextEdit *textEdit = static_cast(editor); + textEdit->setText(value); +} + +void TextEditDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const { + QTextEdit *textEdit = static_cast(editor); + QString value = textEdit->toPlainText(); + + model->setData(index, value, Qt::EditRole); +} + +void TextEditDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &/*index*/) const { + editor->setGeometry(option.rect); +} diff --git a/texteditdelegate.h b/texteditdelegate.h new file mode 100644 index 0000000..f185c32 --- /dev/null +++ b/texteditdelegate.h @@ -0,0 +1,30 @@ +/*! + * \file texteditdelegate.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for the text box edit delegate +*/ +#ifndef TEXTEDITDELEGATE_H_ +#define TEXTEDITDELEGATE_H_ + +#include + +class TextEditDelegate : public QItemDelegate { + Q_OBJECT + + public: + explicit TextEditDelegate(QObject *parent = 0); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + + void updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const; +}; + +#endif // TEXTEDITDELEGATE_H_ diff --git a/timeunit.cpp b/timeunit.cpp new file mode 100644 index 0000000..0870f99 --- /dev/null +++ b/timeunit.cpp @@ -0,0 +1,23 @@ +/*! + * \file timeunit.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of time unit + */ +#include "./timeunit.h" + +TimeUnit::TimeUnit() { + unit = "iteration"; + period = 1; +} + +QString TimeUnit::toString() { + QString s; + s.append(name); + s.append(", "); + s.append(unit); + s.append(", "); + s.append(QString::number(period)); + return s; +} diff --git a/timeunit.h b/timeunit.h new file mode 100644 index 0000000..21fbd66 --- /dev/null +++ b/timeunit.h @@ -0,0 +1,29 @@ +/*! + * \file timeunit.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for time unit +*/ +#ifndef TIMEUNIT_H_ +#define TIMEUNIT_H_ + +#include +#include + +class TimeUnit { + public: + enum EditMode { Editable, ReadOnly }; + + TimeUnit(); + + QString toString(); + + QString name; + QString unit; + int period; +}; + +Q_DECLARE_METATYPE(TimeUnit) + +#endif // TIMEUNIT_H_ diff --git a/timeunitdialog.cpp b/timeunitdialog.cpp new file mode 100644 index 0000000..f352ac7 --- /dev/null +++ b/timeunitdialog.cpp @@ -0,0 +1,44 @@ +/*! + * \file timeunitdialog.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of time unit dialog + */ +#include "./timeunitdialog.h" +#include "./ui_timeunitdialog.h" +#include "./machine.h" + +TimeUnitDialog::TimeUnitDialog(Machine * m, QWidget *parent) + : QDialog(parent), + ui(new Ui::TimeUnitDialog) { + ui->setupUi(this); + + machine = m; + + ui->comboBox_unit->addItems(machine->getTimeUnits()); +} + +TimeUnitDialog::~TimeUnitDialog() { + delete ui; +} + +void TimeUnitDialog::setTimeUnit(TimeUnit t) { + timeUnit = t; + + ui->lineEdit_name->setText(t.name); + ui->comboBox_unit->setCurrentIndex(0); + for (int i = 0; i < ui->comboBox_unit->count(); i++) { + if (ui->comboBox_unit->itemText(i) == t.unit) + ui->comboBox_unit->setCurrentIndex(i); + } + ui->spinBox_period->setValue(t.period); +} + +TimeUnit TimeUnitDialog::getTimeUnit() { + timeUnit.name = ui->lineEdit_name->text(); + timeUnit.unit = ui->comboBox_unit->currentText(); + timeUnit.period = ui->spinBox_period->value(); + + return timeUnit; +} diff --git a/timeunitdialog.h b/timeunitdialog.h new file mode 100644 index 0000000..ac99354 --- /dev/null +++ b/timeunitdialog.h @@ -0,0 +1,33 @@ +/*! + * \file timeunitdialog.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for the time unit dialog +*/ +#ifndef TIMEUNITDIALOG_H_ +#define TIMEUNITDIALOG_H_ + +#include +#include "./machine.h" + +namespace Ui { + class TimeUnitDialog; +} + +class TimeUnitDialog : public QDialog { + Q_OBJECT + + public: + explicit TimeUnitDialog(Machine * m, QWidget *parent = 0); + ~TimeUnitDialog(); + void setTimeUnit(TimeUnit t); + TimeUnit getTimeUnit(); + + private: + Ui::TimeUnitDialog *ui; + Machine * machine; + TimeUnit timeUnit; +}; + +#endif // TIMEUNITDIALOG_H_ diff --git a/timeunitdialog.ui b/timeunitdialog.ui new file mode 100644 index 0000000..bf848ab --- /dev/null +++ b/timeunitdialog.ui @@ -0,0 +1,98 @@ + + + TimeUnitDialog + + + + 0 + 0 + 400 + 153 + + + + Time Unit Editor + + + + + + Name + + + + + + + + + + Unit + + + + + + + + + + Period + + + + + + + 999999999 + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + TimeUnitDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + TimeUnitDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/transition.cpp b/transition.cpp new file mode 100644 index 0000000..20e4054 --- /dev/null +++ b/transition.cpp @@ -0,0 +1,98 @@ +/*! + * \file transition.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of model function transition + */ +#include "./transition.h" + +Transition::Transition() { + myName = ""; + myCurrentState = 0; + myNextState = 0; +} + +Transition::Transition(State * cs, QString n, State * ns) { + myCurrentState = cs; + myMpre = Mpre(); + myName = n; + myMpost = Mpost(); + myNextState = ns; +} + +Transition::Transition(State * cs, Communication i, Mpre pre, QString n, + Mpost post, Communication o, State * ns) { + myCurrentState = cs; + myInput = i; + myMpre = pre; + myName = n; + myMpost = post; + myOutput = o; + myNextState = ns; +} + +bool Transition::passesCondition(MemoryModel* memory) { + bool rc = false; + double d; + + if (myMpre.enabled() == false) return true; + + /* Find variable in memory */ + for (int i = 0; i < memory->getNames().size(); i++) { + if (memory->getNames().at(i) == myMpre.name()) { + // if(memory->variables[i].type == "int") + // d = memory->getintValues().at(i); + // else if(memory->variables[i].type == "double") + // d = memory->getdoubleValues().at(i); + d = memory->variables[i].getValue(); + if (myMpre.op() == "==") rc = (d == myMpre.value()); + if (myMpre.op() == "!=") rc = (d != myMpre.value()); + if (myMpre.op() == ">") rc = (d > myMpre.value()); + if (myMpre.op() == "<") rc = (d < myMpre.value()); + if (myMpre.op() == ">=") rc = (d >= myMpre.value()); + if (myMpre.op() == "<=") rc = (d <= myMpre.value()); + + if (myMpre.isNot()) return !rc; + else + return rc; + } + } + + return false; +} + +void Transition::updateMemory(MemoryModel* memory) { + if (myMpost.enabled() == false) return; + + QList statements = myMpost.getStatements(); + // Loop though Mpost statements + for (int i = 0; i < statements.size(); i ++) { + // If the statement is valid + if (statements.at(i).isValid) { + double d1 = 0.0, d2 = 0.0, d3 = 0.0; + int n = 0; + // Find variable in memory and set values + for (int j = 0; j < memory->getNames().size(); j++) { + if (statements.at(i).lhs == memory->variables[j].name) { + d1 = memory->variables[j].getValue(); + n = j; + } + if (statements.at(i).isVariable) + if (statements.at(i).rhs == memory->variables[j].name) + d2 = memory->variables[j].getValue(); + } + // if third name is number then set number from value + if (statements.at(i).isVariable == false) + d2 = statements.at(i).drhs; + if (statements.at(i).op == "=") d3 = d2; + else if (statements.at(i).op == "+=") d3 = d1+d2; + else if (statements.at(i).op == "-=") d3 = d1-d2; + else if (statements.at(i).op == "*=") d3 = d1*d2; + else if (statements.at(i).op == "/=") d3 = d1/d2; + // else qDebug() << "Operator not found in mpost"; + // Update memory value + memory->replaceValue(n, d3); + } + } +} diff --git a/transition.h b/transition.h new file mode 100644 index 0000000..0a5ceb5 --- /dev/null +++ b/transition.h @@ -0,0 +1,60 @@ +/*! + * \file transition.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for transition function +*/ +#ifndef TRANSITION_H_ +#define TRANSITION_H_ + +#include "./mpre.h" +#include "./mpost.h" +#include "./state.h" +#include "./memorymodel.h" +#include "./condition.h" +#include "./communication.h" + +class Transition { + public: + Transition(); + Transition(State * cs, QString n, State * ns); + Transition(State * cs, Communication i, Mpre pre, + QString n, Mpost post, Communication o, State * ns); + + void setCurrentState(State * cs) { myCurrentState = cs; } + State * currentState() const { return myCurrentState; } + void setInput(Communication i) { myInput = i; } + Communication input() const { return myInput; } + void setMpre(Mpre pre) { myMpre = pre; } + Mpre mpre() const { return myMpre; } + void setName(QString n) { myName = n; } + QString name() const { return myName; } + void setMpost(Mpost post) { myMpost = post; } + Mpost mpost() const { return myMpost; } + void setOutput(Communication o) { myOutput = o; } + Communication output() const { return myOutput; } + void setNextState(State * ns) { myNextState = ns; } + State * nextState() const { return myNextState; } + Mpre * getMprePointer() { return &myMpre; } + void setCondition(Condition c) { myCondition = c; } + Condition condition() const { return myCondition; } + void setDescription(QString s) { myDescription = s; } + QString description() const { return myDescription; } + + bool passesCondition(MemoryModel * memory); + void updateMemory(MemoryModel * memory); + + private: + State * myCurrentState; + Communication myInput; + Mpre myMpre; + QString myName; + Mpost myMpost; + Communication myOutput; + State * myNextState; + Condition myCondition; + QString myDescription; +}; + +#endif // TRANSITION_H_ diff --git a/variable.cpp b/variable.cpp new file mode 100644 index 0000000..9cefa6d --- /dev/null +++ b/variable.cpp @@ -0,0 +1,33 @@ +/*! + * \file variable.cpp + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Implementation of model memory variable + */ +#include "./variable.h" + +Variable::Variable() { + description = ""; + constant = false; +} + +Variable::Variable(QString n, QString t) { + this->name = n; + this->type = t; + ivalue = 0; + dvalue = 0.0; + description = ""; +} + +double Variable::getValue() { + if (type == "int") return static_cast(ivalue); + if (type == "double") return dvalue; + else + return 0.0; +} + +void Variable::setValue(double v) { + ivalue = static_cast(v); + dvalue = v; +} diff --git a/variable.h b/variable.h new file mode 100644 index 0000000..fa7318b --- /dev/null +++ b/variable.h @@ -0,0 +1,30 @@ +/*! + * \file variable.h + * \author Simon Coakley + * \date 2012 + * \copyright Copyright (c) 2012 University of Sheffield + * \brief Header file for memory variable +*/ +#ifndef VARIABLE_H_ +#define VARIABLE_H_ + +#include + +class ADT; + +class Variable { + public: + Variable(); + Variable(QString n, QString t); + double getValue(); + void setValue(double v); + + QString name; + QString type; + QString description; + bool constant; + int ivalue; + double dvalue; +}; + +#endif // VARIABLE_H_