# ACTIVESTATE TEAPOT-PKG BEGIN TM -*- tcl -*- # -- Tcl Module # @@ Meta Begin # Package tepam 0.5 # Meta as::build::date 2015-05-25 # Meta as::origin http://sourceforge.net/projects/tcllib # Meta category Tcl's Enhanced Procedure and Argument Manager Tcl's # Meta category Enhanced Procedure and Argument Manager # Meta description TEPAM argument_dialogbox, reference manual TEPAM # Meta description procedure, reference manual # Meta license BSD # Meta platform tcl # Meta require {Tcl 8.3} # Meta subject {data entry form} {parameter entry form} subcommand # Meta subject procedure arguments {argument integrity} # Meta subject {argument validation} # Meta summary tepam::argument_dialogbox tepam::procedure # @@ Meta End # ACTIVESTATE TEAPOT-PKG BEGIN REQUIREMENTS package require Tcl 8.3 # ACTIVESTATE TEAPOT-PKG END REQUIREMENTS # ACTIVESTATE TEAPOT-PKG BEGIN DECLARE package provide tepam 0.5 # ACTIVESTATE TEAPOT-PKG END DECLARE # ACTIVESTATE TEAPOT-PKG END TM ########################################################################## # TEPAM - Tcl's Enhanced Procedure and Argument Manager ########################################################################## # tepam.tcl - TEPAM's main Tcl package # # TEPAM offers an alternative way to declare Tcl procedures. It provides # enhanced argument handling features like automatically generatehd, # graphical entry forms and checkers for the procedure arguments. # # Copyright (C) 2009/2010/2011 Andreas Drollinger # # Id: tepam.tcl ########################################################################## # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. ########################################################################## package require Tcl 8.3 namespace eval tepam { # Exports the major commands from this package: namespace export procedure argument_dialogbox ########################################################################## # procedure # ########################################################################## ######## Procedure configuration ######## # Set the following variable to 0 (false) prior to the procedure definition, to # use first the unnamed arguments and then the named arguments. set named_arguments_first 1 # Setting the following variable to 0 will disable the automatic argument name # extension feature. set auto_argument_name_completion 1 # Set the following variable prior to the procedure definition to: # - 0 (false): to disable command logging # - 1 (true): to log all commands anytime # - "interactive": to log only interactively called commands set command_log "interactive" # Set the following variable to "short" to generate small interactive dialog boxes. set interactive_display_format "extended" # The following variable defines the maximum line length a created help text can have: set help_line_length 80 ######## Internal variables ######## if {![info exists ProcedureList]} { set ProcedureList {} } ######## PureProcName ######## # PureProcName purifies the procedure name given by the ProcName variable of the calling # function and returns it. PureProcName is basically: # * Eliminating the main namespace indicators # * Encapsulating the name into '' if it is a composed name proc PureProcName {args} { upvar ProcName ProcName set Name $ProcName regsub {^::} $Name {} Name; # Eliminate the main namespace indicators if {[lsearch $args -appo]>=0} { # Encapsulate the name into '' if it is a composed name set Name "'$Name'" } return $Name } ######## Procedure help text ######## set ProcedureHelp { procedure = { [-category ] [-short_description ] [-description ] [-return ] [-example ] [-named_arguments_first 0|1] [-command_log 0|1|"interactive"] [-auto_argument_name_completion 0|1] [-interactive_display_format] [-validatecommand ] [-validatecommand_error_text ] [-args ] } = { [ArgumentDeclaration ...]} = { [-description ] [-type ] [-validatecommand ] [-validatecommand_error_text ] [-default ] [-optional | -mandatory] [-choices ] [-choicelabels ] [-range { } [-multiple] [-auxargs ] [-auxargs_commands ] } = { none double integer alnum alpha ascii control digit graph lower print punct space upper wordchar xdigit color font boolean "" } } # Eliminate leading tabs in the help text and replace eventual tabs through spaces regsub -all -line {^\t\t} $ProcedureHelp "" ProcedureHelp regsub -all -line {\t} $ProcedureHelp " " ProcedureHelp ######## Procedure ######## # Procedure allows declaring a new procedure in the TEPAM syntax: # # procedure my_proc { # -args {message} # } { # puts $message; # Procedure body # } # # Procedure creates in fact a TCL procedure with a patched procedure body. This body calls at # the beginning an argument parser (ProcedureArgumentEvaluation) that is reading and validating # the arguments that have been provided to the procedure. The previous lines are for example # creating the following TCL procedure: # # proc my_proc {args} { # ::tepam::ProcedureArgumentEvaluation; # if {$ProcedureArgumentEvaluationResult!=""} { # if {$ProcedureArgumentEvaluationResult=="cancel"} return; # return -code error $ProcedureArgumentEvaluationResult; # } # if {$SubProcedure!=""} {return [$SubProcedure]}; # # puts $message; # Procedure body # } # # ProcedureArgumentEvaluation uses the TCL procedure's args argument to read all the provided # arguments. It evaluates first if a sub procedure has to be called. This information and the # argument validation result are provided to the calling procedure respectively via the # variables SubProcedure and ProcedureArgumentEvaluationResult. In case the result evaluation # was not successful, the calling procedure body will simply return. In case the procedure # call refers to a sub-procedure, this one will be called. Otherwise, if a valid argument set # has been provided to the procedure, and if no sub-procedure has to be called, the original # procedure body is executed. # Procedure behaves slightly differently in case one or multiple sub-procedures have been # declared without declaring the main procedure itself: # # procedure {my_func sub_func} { # -args {message} # } { # puts $message; # Procedure body # } # # Procedure creates in this case for the main procedure a Tcl procedure as well as for the sub # procedure. The main procedure creates an error when it directly called. The sub-procedure # is executed within the main procedure's context using the uplevel command. # # proc my_proc {args} { # ::tepam::ProcedureArgumentEvaluation; # if {$ProcedureArgumentEvaluationResult!=""} { # if {$ProcedureArgumentEvaluationResult=="cancel"} return; # return -code error $ProcedureArgumentEvaluationResult; # } # if {$SubProcedure!=""} {return [$SubProcedure]}; # error "'my_func' requires a subcommand" # } # # proc {my_proc sub_func} {args} { # uplevel 1 { # puts $message; # Procedure body # } # } # # Procedure parses itself the procedure name and attributes and creates the new TCL procedure # with the modified body. For each declared argument it calls ProcedureArgDef which handles the # argument definition. proc procedure {args} { variable ProcDef variable ProcedureHelp variable named_arguments_first variable command_log variable auto_argument_name_completion variable interactive_display_format variable ProcedureList #### Check if help is requested and extract the (sub) procedure name #### # Check if help is requested: if {[lsearch -exact $args "-help"]>=0} { puts $ProcedureHelp return } # Check that the procedure name, argument list and body has been provided: if {[llength $args]!=3} { return -code error "Missing procedure arguments, correct usage: procedure \ " } # Evaluate the full qualified procedure name including a leading name space identifier. # Evaluate the current namespace in case the procedure is not defined explicitly with # a name space: regsub -all {\s+} [lindex $args 0] " " ProcName if {[string range $ProcName 0 1]!="::"} { set NameSpace [uplevel 1 {namespace current}] if {$NameSpace!="::"} {append NameSpace "::"} set ProcName ${NameSpace}${ProcName} } # Extract the procedure attributes and the procedure body: set ProcedureAttributes [lindex $args 1] set ProcedureBody [lindex $args 2] # Store the procedure name in the procedure list, if it is not already existing: if {[lsearch -exact $ProcedureList $ProcName]<0} { lappend ProcedureList $ProcName } #### Initialize the procedure attributes #### # Clean the information of an eventual previous procedure definition, and store # the actual configured procedure modes: catch {array unset ProcDef $ProcName,*} set ProcDef($ProcName,-named_arguments_first) $named_arguments_first set ProcDef($ProcName,-command_log) $command_log set ProcDef($ProcName,-auto_argument_name_completion) $auto_argument_name_completion set ProcDef($ProcName,-interactive_display_format) $interactive_display_format # The procedure information will be stored in the array variable ProcDef. # The following array members are always defined for each declared procedure: set ProcDef($ProcName,-validatecommand) {} set ProcDef($ProcName,-validatecommand_error_text) {} set ProcDef($ProcName,VarList) {} set ProcDef($ProcName,NamedVarList) {} set ProcDef($ProcName,UnnamedVarList) {} # ProcDef($ProcName,NbrVars); # # ProcDef($ProcName,NbrNamedVars) # ProcDef($ProcName,NbrUnnamedVars) # The following array members are defined optionally in the argument parsing section: # ProcDef($ProcName,$AttributeName) # | AttributeName = {-category -return -short_description # | -description -example} # # ProcDef($ProcName,Arg,$Var,IsNamed) # ProcDef($ProcName,Arg,$Var,-type) # ProcDef($ProcName,Arg,$Var,-optional) # ProcDef($ProcName,Arg,$Var,-validatecommand) # ProcDef($ProcName,Arg,$Var,-validatecommand_error_text) # ProcDef($ProcName,Arg,$Var,-default) # ProcDef($ProcName,Arg,$Var,HasDefault) # ProcDef($ProcName,Arg,$Var,-multiple) # ProcDef($ProcName,Arg,$Var,-description) # ProcDef($ProcName,Arg,$Var,-choices) # | Contains the choice list: { ... } # ProcDef($ProcName,Arg,$Var,-choicelabels) # | Contains the choice label list: { ... } # ProcDef($ProcName,Arg,$Var,-range) # ProcDef($ProcName,Arg,$Var,SectionComment) # ProcDef($ProcName,Arg,$Var,Comment) #### Parse all procedure attributes #### set UnnamedHasToBeOptional 0; # Variable that will be set to '1' if an unnamed argument is optional. set UnnamedWasMultiple 0; # Variable that will be set to '1' if an unnamed argument has the -multiple option # Loop through the argument definition list: foreach {AttributeName AttributeValue} $ProcedureAttributes { # Evaluate the provided argument attribute switch -exact -- $AttributeName { -help { # Help has been required in the procedure attribute definition list: puts $ProcedureHelp return } -category - -return - -short_description - -description - -named_arguments_first - -command_log - -auto_argument_name_completion - -example - -interactive_display_format { # Save all these information simply in the ProcDef array variable: set ProcDef($ProcName,$AttributeName) $AttributeValue } -validatecommand - -validatecommand_error_text { lappend ProcDef($ProcName,$AttributeName) $AttributeValue } -args { # Read the procedure arguments with ProcedureArgDef set Comment "" set SectionComment "" foreach arg $AttributeValue { set ErrorMsg [ProcedureArgDef $arg] if {$ErrorMsg!=""} { return -code error "Procedure declaration [PureProcName -appo]: $ErrorMsg" } } } default { return -code error "Procedure declaration [PureProcName -appo]: Procedure attribute '$AttributeName' not known" } } } # Complete the procedure attributes - # Number of arguments: set ProcDef($ProcName,NbrVars) [llength $ProcDef($ProcName,VarList)] # Number of named arguments set ProcDef($ProcName,NbrNamedVars) [llength $ProcDef($ProcName,NamedVarList)] # Number of unnamed arguments set ProcDef($ProcName,NbrUnnamedVars) [llength $ProcDef($ProcName,UnnamedVarList)] #### Create the TCL procedure(s) #### # Create now the TCL procedures. In case a sub procedure is declared, the created TCL # procedure has not to call the argument evaluator, since this one has already been called # in the main procedure. An additional main procedure is created if a sub procedure is # declared and if no main procedure is existing. set Body "::tepam::ProcedureArgumentEvaluation;\n" append Body "if {\$ProcedureArgumentEvaluationResult!=\"\"} \{\n" append Body " if {\$ProcedureArgumentEvaluationResult==\"cancel\"} return;\n" append Body " return -code error \$ProcedureArgumentEvaluationResult;\n" append Body "\}\n" append Body "if {\$SubProcedure!=\"\"} {return \[\$SubProcedure\]};\n\n" if {[llength $ProcName]==1} { append Body "$ProcedureBody" proc $ProcName {args} $Body } else { proc $ProcName {args} "uplevel 1 \{\n$ProcedureBody\n\}" if {[info commands [lindex $ProcName 0]]==""} { append Body "return -code error \"'[lindex $ProcName 0]' requires a subcommand\"" proc [lindex $ProcName 0] {args} $Body } } } # ProcedureArgDef reads the definition of a single argument that is provided in form of a list: # # -mtype -default Warning -choices {Info Warning Error} -description "M. type" # # ProcedureArgDef is executed by 'procedure'. The argument definition is provided via the # argument 'ArgDef' variable. ProcedureArgDef is recognizing argument comments and section # comments that can be placed into the argument definitions. ProcedureArgDef is also checking # the validity of the argument specifications. proc ProcedureArgDef {ArgDef} { variable ProcDef variable ProcedureHelp variable named_arguments_first variable command_log variable auto_argument_name_completion variable interactive_display_format variable ProcedureList upvar ProcName ProcName upvar Comment Comment upvar SectionComment SectionComment upvar UnnamedHasToBeOptional UnnamedHasToBeOptional upvar UnnamedWasMultiple UnnamedWasMultiple # Read the argument name: set Opt [lindex $ArgDef 0] #### Handle section and argument comments, parse the option name #### # Check if the argument definition is a simply argument comment (either -, "" or {}) if {$Opt=="" || $Opt=="-"} { # Eliminate the entire first word as well as any leading and tailing white spaces regexp {^\s*[^\s]+\s+(.*)\s*$} $ArgDef {} Comment regsub -all "\"" $Comment "\\\"" Comment return "" # Check if the argument definition is an argument section begin } elseif {[string index $Opt 0]=="\#"} { # Eliminate leading and tailing white spaces set SectionComment [string trim [string range $ArgDef 1 end]] # Eliminate the leading and ending #s and white spaces regexp {^\s*\#+\s*(.*?)\s*\#*\s*$} $ArgDef {} SectionComment # regsub -all "\"" $SectionComment "\\\" SectionComment # For an eventual interactive call that requires a GUI, swap to the short # representation mode, since the frames are used to display the sections: set ProcDef($ProcName,-interactive_display_format) "short" return "" # Check if the argument is an option or a flag (named argument), that has with a # leading '-': } elseif {[string index $Opt 0]=="-"} { set Var [string range $Opt 1 end] lappend ProcDef($ProcName,NamedVarList) $Var set ProcDef($ProcName,Arg,$Var,IsNamed) 1 # The argument is an unnamed argument: } else { set Var $Opt lappend ProcDef($ProcName,UnnamedVarList) $Var set ProcDef($ProcName,Arg,$Var,IsNamed) 0 } # Assign to the argument an eventually previously defined section or argument comment: if {$SectionComment!=""} { set ProcDef($ProcName,Arg,$Var,SectionComment) $SectionComment set SectionComment "" } if {$Comment!=""} { set ProcDef($ProcName,Arg,$Var,Comment) $Comment set Comment "" } # Check that an argument is not declared multiple times: if {[lsearch -exact $ProcDef($ProcName,VarList) $Var]>=0} { return "Argument '$Var' defined multiple times" } #### Define the argument attributes #### # Append the argument to the argument list and define the argument attributes with the # default values: lappend ProcDef($ProcName,VarList) $Var set ProcDef($ProcName,Arg,$Var,-type) ""; # Undefined set ProcDef($ProcName,Arg,$Var,-optional) 0 set ProcDef($ProcName,Arg,$Var,HasDefault) 0 set ProcDef($ProcName,Arg,$Var,-multiple) 0 # Parse all argument attribute definitions: for {set a 1} {$a<[llength $ArgDef]} {incr a} { set ArgOption [lindex $ArgDef $a] set ArgOptionValue [lindex $ArgDef [expr {$a+1}]] switch -- $ArgOption { -type { # Argument type definition: Check if the validation command is defined for # the used argument type: if {[catch {Validate($ArgOptionValue) ""}]} { return "Argument type '$ArgOptionValue' not known" } # Store the attribute type: set ProcDef($ProcName,Arg,$Var,-type) $ArgOptionValue # Flags (argument that have the type 'none') are always optional: if {$ProcDef($ProcName,Arg,$Var,-type)=="none"} { set ProcDef($ProcName,Arg,$Var,-optional) 1 } incr a } -default { # Arguments that have default values are always optional: set ProcDef($ProcName,Arg,$Var,-default) $ArgOptionValue set ProcDef($ProcName,Arg,$Var,HasDefault) 1 set ProcDef($ProcName,Arg,$Var,-optional) 1 incr a } -mandatory {# The -mandatory attribute is already the default behavior} -optional - -multiple { # These attributes (flags) have just to be stored for future usage: set ProcDef($ProcName,Arg,$Var,$ArgOption) 1 } -range { # Check that the range is defined by two values and that the min value is # smaller than the max value: if {[llength $ArgOptionValue]!=2 || \ ![Validate(double) [lindex $ArgOptionValue 0]] || \ ![Validate(double) [lindex $ArgOptionValue 1]]} { return "Invalid range definition - $ArgOptionValue" } set ProcDef($ProcName,Arg,$Var,$ArgOption) $ArgOptionValue incr a } -validatecommand - -validatecommand_error_text - -auxargs_commands - -auxargs - -description - -choices - -choicelabels - -widget { # Also these attributes have just to be stored for future usage: set ProcDef($ProcName,Arg,$Var,$ArgOption) $ArgOptionValue incr a } default { # Generate an error if the provided attribute name doesn't match with a known # attribute. return "Argument attribute '$ArgOption' not known" } } } #### Perform various argument attribute validation checks #### # Unnamed argument attribute checks: if {!$ProcDef($ProcName,Arg,$Var,IsNamed)} { # Check that behind an optional unnamed argumeent there are only other optional # unnamed arguments: if {$UnnamedHasToBeOptional && !$ProcDef($ProcName,Arg,$Var,-optional)} { return "Argument '$Var' has to be optional" } # Check that only the last unnamed argument can take multiple values: if {$UnnamedWasMultiple} { return "Attribute '-multiple' is only for the last unnamed argument allowed" } # Check the length of an optional -choicelabels list if {[info exists ProcDef($ProcName,Arg,$Var,-choices)] && \ [info exists ProcDef($ProcName,Arg,$Var,-choicelabels)]} { if {[llength $ProcDef($ProcName,Arg,$Var,-choices)]!= [llength $ProcDef($ProcName,Arg,$Var,-choicelabels)]} { return "Argument '$Var' - Choice label list and choice list have different sizes" } } # Store the information about the argument attributes the check the consistency of # the following arguments: if {$ProcDef($ProcName,Arg,$Var,-optional)} { set UnnamedHasToBeOptional 1 } if {$ProcDef($ProcName,Arg,$Var,-multiple)} { set UnnamedWasMultiple 1 } } # Range checks are only allowed for integers and doubles: if {[info exists ProcDef($ProcName,Arg,$Var,-range)]} { if {[lsearch {integer double} $ProcDef($ProcName,Arg,$Var,-type)]<0} { return "Argument '$Var' - range specification requires type integer or double" } } return "" } ######## ProcedureArgumentEvaluation ######## # ProcedureArgumentEvaluation is the argument evaluator that is embedded by the procedure # declaration command 'procedure' into the procedure's body in the following way: # # proc my_proc {args} { # ::tepam::ProcedureArgumentEvaluation; # if {$ProcedureArgumentEvaluationResult!=""} { # if {$ProcedureArgumentEvaluationResult=="cancel"} return; # return -code error $ProcedureArgumentEvaluationResult; # } # if {$SubProcedure!=""} {return [$SubProcedure]}; # # puts $message; # Procedure body # } # # ProcedureArgumentEvaluation has to define in the calling procedure two variables: # The first one is ProcedureArgumentEvaluationResult that has to contain the result of the # evaluation and validation of the argument set. Zero as results means that the provided # arguments are OK and that the procedure body can be executed. A non-zero value indicates # that the procedure body has not to be evaluated, typically because help was requested via # the -help option. In case of incorrect arguments an error is generated by # ProcedureArgumentEvaluation. # The second variable that is created by ProcedureArgumentEvaluation is 'SubProcedure'. This # variable is set to the sub procedure name in case a sub procedure is called. If the main # procedure is called this variable is set to an empty string. # Delcare first a tiny helper function: ProcedureArgumentEvaluationReturn will assign the # provided result string to the ProcedureArgumentEvaluationResult variable in the context # of the calling procedure and will then emulate a return function. proc ProcedureArgumentEvaluationReturn {Result} { upvar 2 ProcedureArgumentEvaluationResult ProcedureArgumentEvaluationResult set ProcedureArgumentEvaluationResult $Result return -code return } proc ProcedureArgumentEvaluation {} { variable ProcDef upvar args args upvar SubProcedure SubProcedure #### Extract the procedure and sub procedure names, call the procedure help if requested #### # Evaluate the complete main procedure name that contains the namespace identification: # The procedure name is given by the first element of 'info level': set ProcedureCallLine [info level -1] set ProcName [lindex $ProcedureCallLine 0] # Create the full qualified procedure name (procedure name including namespace) regexp {([^:]*)$} $ProcName {} ProcName set NameSpace [uplevel 1 {namespace current}] if {$NameSpace!="::"} {append NameSpace "::"} set ProcName ${NameSpace}${ProcName} # Evaluate the sub command names by checking if the first arguments are matching with # a specified sub command name: set SubProcedure "" while {1} { set ProcNameTmp "$ProcName [lindex $args 0]" if {![info exists ProcDef($ProcNameTmp,VarList)] && [array names ProcDef "$ProcNameTmp *"]==""} { # The argument is not matching with a specified sub command name (so it will be a # real argument): break } # Use the argument as sub procedure name: set ProcName $ProcNameTmp set SubProcedure $ProcName set args [lrange $args 1 end] } # Check if help has been requested in the procedure call: if {[lindex $args end]=="-help"} { ProcedureHelp $ProcName ProcedureArgumentEvaluationReturn "cancel" } # Check if the procedure call is an interactive call set InteractiveCall [string match "-interactive" [lindex $args end]] # Return an empty string if the main procedure has been called and if only sub-commands # have been defined, but not the main procedure itself. if {![info exists ProcDef($ProcName,VarList)]} { ProcedureArgumentEvaluationReturn "" } #### Call an argument_dialogbox if the procedure has been called with'-interactive' #### set NewArgs {} if {$InteractiveCall} { # Start creating the argument_dialogbox's argument list with the title attribute: set DialogBoxArguments [list -title $ProcName -context $ProcName] # Add eventual global validation commands foreach ValidateCommand $ProcDef($ProcName,-validatecommand) { lappend DialogBoxArguments -validatecommand2 $ValidateCommand } foreach ValidateCommandErrorText $ProcDef($ProcName,-validatecommand_error_text) { lappend DialogBoxArguments -validatecommand2_error_text $ValidateCommandErrorText } # Create for each of the procedure arguments an entry for the argument_dialogbox: foreach Var $ProcDef($ProcName,VarList) { # Declare the result variables. These variables refer to the variables in the parent # procedure (upvar). Attribute to these variables directly the default values that can be # overwritten later with the new defined values. upvar $Var Variable__$Var # Create sections, write section and argument comments: if {$ProcDef($ProcName,-interactive_display_format)=="extended"} { if {[info exists ProcDef($ProcName,Arg,$Var,SectionComment)]} { # If a section comment is defined, close an eventual open frame, add the # section comment and add an eventually defined arguement comment: lappend DialogBoxArguments -frame ""; # Close an eventual open frame lappend DialogBoxArguments \ -comment [list -text $ProcDef($ProcName,Arg,$Var,SectionComment)] if {[info exists ProcDef($ProcName,Arg,$Var,Comment)]} { lappend DialogBoxArguments \ -comment [list -text $ProcDef($ProcName,Arg,$Var,Comment)] } } # Create a frame around each argument entry in the extended format: lappend DialogBoxArguments -frame [list -label $Var] } elseif {[info exists ProcDef($ProcName,Arg,$Var,SectionComment)]} { # If a section is defined, create a section frame in the sort format: lappend DialogBoxArguments \ -frame [list -label $ProcDef($ProcName,Arg,$Var,SectionComment)] } # If an argument comment is defined but not yet applied, apply it: if {[info exists ProcDef($ProcName,Arg,$Var,Comment)] && !( $ProcDef($ProcName,-interactive_display_format)=="extended" && [info exists ProcDef($ProcName,Arg,$Var,SectionComment)] )} { lappend DialogBoxArguments \ -comment [list -text $ProcDef($ProcName,Arg,$Var,Comment)] } # Provide to the argument dialogbox all the argument attributes: set ArgAttributes {} if {$ProcDef($ProcName,Arg,$Var,-type)!=""} { lappend ArgAttributes -type $ProcDef($ProcName,Arg,$Var,-type) } if {$ProcDef($ProcName,Arg,$Var,-optional)} { lappend ArgAttributes -optional 1 } if {[info exists ProcDef($ProcName,Arg,$Var,-range)] && \ $ProcDef($ProcName,Arg,$Var,-range)!=""} { lappend ArgAttributes -range $ProcDef($ProcName,Arg,$Var,-range) } if {[info exists ProcDef($ProcName,Arg,$Var,-validatecommand)]} { lappend ArgAttributes -validatecommand $ProcDef($ProcName,Arg,$Var,-validatecommand) } if {[info exists ProcDef($ProcName,Arg,$Var,-validatecommand_error_text)]} { lappend ArgAttributes -validatecommand_error_text $ProcDef($ProcName,Arg,$Var,-validatecommand_error_text) } if {[info exists ProcDef($ProcName,Arg,$Var,-auxargs)] && $ProcDef($ProcName,Arg,$Var,-auxargs)!=""} { set ArgAttributes [concat $ArgAttributes $ProcDef($ProcName,Arg,$Var,-auxargs)] } if {[info exists ProcDef($ProcName,Arg,$Var,-auxargs_commands)]} { foreach {AuxArg_Name AuxArgCommand} $ProcDef($ProcName,Arg,$Var,-auxargs_commands) { lappend ArgAttributes $AuxArg_Name [uplevel #1 $AuxArgCommand] } } if {[info exists ProcDef($ProcName,Arg,$Var,-choicelabels)]} { lappend ArgAttributes -choicelabels $ProcDef($ProcName,Arg,$Var,-choicelabels) } # Set the default values if {[info exists ProcDef($ProcName,Arg,$Var,-default)]} { lappend ArgAttributes -default $ProcDef($ProcName,Arg,$Var,-default) } # Add the variable name, type, description and range as labels and comments: set Label $Var; # Default label if {$ProcDef($ProcName,-interactive_display_format)=="extended"} { # Add the argument description as comment if {[info exists ProcDef($ProcName,Arg,$Var,-description)]} { lappend DialogBoxArguments \ -comment [list -text $ProcDef($ProcName,Arg,$Var,-description)] } # Add the type and ranges as comment if {[lsearch {"" "string" "none"} $ProcDef($ProcName,Arg,$Var,-type)]<0} { set Comment "Type: $ProcDef($ProcName,Arg,$Var,-type), " if {[info exists ProcDef($ProcName,Arg,$Var,-range)]} { append Comment "range: [lindex $ProcDef($ProcName,Arg,$Var,-range) 0] .. \ [lindex $ProcDef($ProcName,Arg,$Var,-range) 1], " } lappend DialogBoxArguments -comment [list -text [string range $Comment 0 end-2]] } } else { if {[info exists ProcDef($ProcName,Arg,$Var,-description)]} { set Label $ProcDef($ProcName,Arg,$Var,-description) } } # Select the adequate widget for the argument: lappend ArgAttributes -label "$Label:" -variable Variable__$Var # A specific entry widget is explicitly specified: if {[info exists ProcDef($ProcName,Arg,$Var,-widget)]} { lappend DialogBoxArguments -$ProcDef($ProcName,Arg,$Var,-widget) $ArgAttributes # A type specific widget exists, so use this one: } elseif {[info procs ad_form($ProcDef($ProcName,Arg,$Var,-type))]!=""} { lappend DialogBoxArguments -$ProcDef($ProcName,Arg,$Var,-type) $ArgAttributes # Use a simple checkbutton for flags: } elseif {$ProcDef($ProcName,Arg,$Var,-type)=="none"} { lappend DialogBoxArguments -checkbutton $ArgAttributes # A choice list is provided with less or equal than 4 options, use radioboxes or checkboxes: } elseif {[info exists ProcDef($ProcName,Arg,$Var,-choices)] && \ [llength $ProcDef($ProcName,Arg,$Var,-choices)]<=4} { if {$ProcDef($ProcName,Arg,$Var,-multiple)} { lappend DialogBoxArguments -checkbox [concat [list \ -choices $ProcDef($ProcName,Arg,$Var,-choices)] $ArgAttributes] } else { lappend DialogBoxArguments -radiobox [concat [list \ -choices $ProcDef($ProcName,Arg,$Var,-choices)] $ArgAttributes] } # A choice list is provided with less than 30 options, use a listbox or a disjointlistbox: } elseif {[info exists ProcDef($ProcName,Arg,$Var,-choices)] && \ [llength $ProcDef($ProcName,Arg,$Var,-choices)]<30} { if {$ProcDef($ProcName,Arg,$Var,-multiple)} { lappend DialogBoxArguments -disjointlistbox [concat [list \ -choicevariable ProcDef($ProcName,Arg,$Var,-choices) -height 3] $ArgAttributes] } else { lappend DialogBoxArguments -listbox [concat [list \ -choicevariable ProcDef($ProcName,Arg,$Var,-choices) -height 3] $ArgAttributes] } # For all other cases, use a simple entry widget: } else { lappend DialogBoxArguments -entry $ArgAttributes } } # Call the argument dialogbox # puts "argument_dialogbox \{$DialogBoxArguments\}" if {[argument_dialogbox $DialogBoxArguments]=="cancel"} { # The argument dialogbox has been canceled, leave the calling procedure without # executing the procedure body: ProcedureArgumentEvaluationReturn cancel } # Set the variables of the optional arguments to the default values, if the variables # haven't been defined by the argument dialogbox: foreach Var $ProcDef($ProcName,VarList) { if {![info exists Variable__$Var] && \ [info exists ProcDef($ProcName,Arg,$Var,-default)]} { set Variable__$Var $ProcDef($ProcName,Arg,$Var,-default) } } #### Non interactive call: Parse all arguments and define the argument variables #### } else { # Result variable declaration and default value definition foreach Var $ProcDef($ProcName,VarList) { # Declare the result variables. These variables refer to the variables in the parent # procedure (upvar). Attribute to these variables directly the default values that can be # overwritten later with the new defined values. upvar $Var Variable__$Var # Set the flags to the default values only when the procedure is called interactively: if {$ProcDef($ProcName,Arg,$Var,-type)=="none"} { set Variable__$Var 0 } elseif {[info exists ProcDef($ProcName,Arg,$Var,-default)]} { # Apply an eventually defined default value, in case the argument is not a flag: set Variable__$Var $ProcDef($ProcName,Arg,$Var,-default) } } # Prepare parsing all arguments set NbrArgs [llength $args]; # Number of provided arguments set NumberUnnamedArgs 0 set ArgPos 0 # Parse the unnamed arguments if they are defined first and if some of them have been # declared: if {!$ProcDef($ProcName,-named_arguments_first)} { # Parse all unnamed arguments. Stop parsing them when: # 1) all unnamed arguments that have been declared have been parsed && # the last unnamed argument has not the -multiple option && # 2) the parsed argument is optional and starts with '-' # 3) the parsed argument has can take multiple values && # one value has already been read && # the parsed argument starts with '-' # An argument value is optional when it has been declared with the -optional option # or when it is declared with the -multiple option and already one value has been # attributed to the argument: set IsOptional 0 # Loop through all arguments (only if unnamed arguments have been declared: for {} {$ArgPos<[llength $args] && $ProcDef($ProcName,NbrUnnamedVars)>0} {incr ArgPos} { # Get the next provided parameter value: set arg [lindex $args $ArgPos] # The ordered unnamed argument list provides the relevant argument: set Var [lindex $ProcDef($ProcName,UnnamedVarList) $NumberUnnamedArgs] # Stop parsing the unnamed arguments, if the procedure has also named arguments, # if the argument to parse is optional, and if it starts with '-': if {$ProcDef($ProcName,Arg,$Var,-optional)} { set IsOptional 1 } if {$ProcDef($ProcName,NbrNamedVars)>0 && $IsOptional && \ [string index $arg 0]=="-"} { break } # If the argument can have multiple values: Don't update the unnamed argument # counter to attribute the next values to the same argument. Declare the next # values also as optional if {$ProcDef($ProcName,Arg,$Var,-multiple)} { lappend Variable__$Var $arg set IsOptional 1 # Otherwise (the argument cannot have multiple values), assign the value to the # variable. Exit the unnamed argument loop when the last declared argument has # been read: } else { set Variable__$Var $arg incr NumberUnnamedArgs if {$NumberUnnamedArgs==$ProcDef($ProcName,NbrUnnamedVars)} { incr ArgPos break } } } # Create an error if there are other argument values that are provided, but when no # named arguments are declared: if {$ProcDef($ProcName,NbrNamedVars)==0 && $ArgPos<[llength $args]} { ProcedureArgumentEvaluationReturn "$ProcName: Too many arguments: [lrange $args $ArgPos end]" } } # Parse the named arguments for {} {$ArgPos<[llength $args]} {incr ArgPos} { # Get the argument name: set arg [lindex $args $ArgPos] # Ignore the '--' flag. Exit the named argument parsing loop if 'named arguments # first' is configured if {$arg=="--"} { if {$ProcDef($ProcName,-named_arguments_first)} { incr ArgPos break } else { continue } } # In case the named arguments are used first: Check if the next argument is not # anymore a named argument and stop parsing the named arguments if this is the case. if {$ProcDef($ProcName,-named_arguments_first) && [string index $arg 0]!="-"} { break } # Otherwise (especially if the unnamed arguments are used first), check that the # option name starts with '-': if {[string index $arg 0]!="-"} { ProcedureArgumentEvaluationReturn "[PureProcName]: Argument '$arg' is not an option" } # Extract the variable name (eliminate the '-'): set Var [string range $arg 1 end] # Check if the variable (name) is known. When it is not known, complete it when the # name matches with the begin of a known variable name, or generate otherwise an # error: if {![info exists ProcDef($ProcName,Arg,$Var,-type)]} { # Argument completion is disabled - generate an error: if {!$ProcDef($ProcName,-auto_argument_name_completion)} { ProcedureArgumentEvaluationReturn "[PureProcName -appr]: Argument '-$Var' not known" # Argument completion is enabled - check if the variable name corresponds to the # begin of a known argument name: } else { # set MatchingVarList [lsearch -all -inline -glob $ProcDef($ProcName,VarList) ${Var}*] -> Tcl 8.3 doesn't support the -all and -inline switches! set MatchingVarList {} set VarList $ProcDef($ProcName,VarList) while {[set Pos [lsearch -glob $VarList ${Var}*]]>=0} { lappend MatchingVarList [lindex $VarList $Pos] set VarList [lrange $VarList [expr $Pos+1] end] } # Complete the argument name if the argument doesn't exist, but if it is the begin of a declared argument. switch [llength $MatchingVarList] { 1 {set Var $MatchingVarList} 0 {ProcedureArgumentEvaluationReturn "[PureProcName]: Argument '-$Var' not known"} default {ProcedureArgumentEvaluationReturn "[PureProcName]: Argument '-$Var' may match multiple options: $MatchingVarList"} } } } # Set the variable value to '1' if the argument is a flag (type=='none'). Read # otherwise the variable value: if {$ProcDef($ProcName,Arg,$Var,-type)=="none"} { # The argument is a flag set Value 1 # No argument value is provided - generate an error: } elseif {$ArgPos==[llength $args]-1} { ProcedureArgumentEvaluationReturn "[PureProcName]: No value is provided for argument '-$Var'" # Read the argument value } else { set Value [lindex $args [incr ArgPos]] } # Define the argument variable. Append the new value to the existing value of the # variable, if the '-multiple' attribute is set for the argument: if {$ProcDef($ProcName,Arg,$Var,-multiple)} { lappend Variable__$Var $Value } else { set Variable__$Var $Value } } # In case the unnamed arguments are defined last, parse them now: if {$ProcDef($ProcName,-named_arguments_first)} { # Loop through the remaining arguments: for {} {$ArgPos<[llength $args]} {incr ArgPos} { # Get the next provided parameter value: set arg [lindex $args $ArgPos] # Assure that the number of provided arguments is not exceeding the total number # of declared unnamed arguments: if {$NumberUnnamedArgs>=$ProcDef($ProcName,NbrUnnamedVars)} { # Too many unnamed arguments are used, generate an adequate error message: if {[string index $arg 0]=="-"} { ProcedureArgumentEvaluationReturn "[PureProcName]: Too many unnamed arguments, or incorrectly used named argument: $arg" } else { ProcedureArgumentEvaluationReturn "[PureProcName]: Too many unnamed arguments: $arg" } } # The ordered unnamed argument list provides the relevant argument: set Var [lindex $ProcDef($ProcName,UnnamedVarList) $NumberUnnamedArgs] # Assign all remaining parameter values to the last argument if this one can # take multiple values: if {$ProcDef($ProcName,Arg,$Var,-multiple) && \ $NumberUnnamedArgs==$ProcDef($ProcName,NbrUnnamedVars)-1} { set Variable__$Var [lrange $args $ArgPos end] # incr NumberUnnamedArgs set ArgPos [llength $args] # Assign otherwise the parameter value to the actual argument } else { set Variable__$Var $arg incr NumberUnnamedArgs } } } } #### Argument validation #### # Check that all mandatory arguments have been defined and that all arguments satisfy the # defined type: # Loop through all named and unnamed arguments: foreach Var $ProcDef($ProcName,VarList) { # An error is created when a variable is not optional and when it is not defined: if {!$ProcDef($ProcName,Arg,$Var,-optional) && ![info exists Variable__$Var]} { ProcedureArgumentEvaluationReturn "[PureProcName]: Required argument is missing: $Var" } # Check the variable value corresponds to the specified type: if {[info exists Variable__$Var]} { # Transform the variable value in a list in case the argument is not multiple # definable: set ValueList [set Variable__$Var] if {!$ProcDef($ProcName,Arg,$Var,-multiple)} { set ValueList [list $ValueList] } # Loop through all elements of this list and check if each element is valid: foreach Value $ValueList { # Check the argument type: if {![Validate($ProcDef($ProcName,Arg,$Var,-type)) $Value]} { ProcedureArgumentEvaluationReturn "[PureProcName]: Argument '$Var' requires type '$ProcDef($ProcName,Arg,$Var,-type)'. Provided value: '$Value'" } # Check the argument with an eventually defined validation command: if {[info exists ProcDef($ProcName,Arg,$Var,-validatecommand)]} { regsub {%P} $ProcDef($ProcName,Arg,$Var,-validatecommand) $Value ValidateCommand if {![uplevel $ValidateCommand]} { if {[info exists ProcDef($ProcName,Arg,$Var,-validatecommand_error_text)]} { ProcedureArgumentEvaluationReturn "[PureProcName]: $ProcDef($ProcName,Arg,$Var,-validatecommand_error_text)" } else { ProcedureArgumentEvaluationReturn "[PureProcName]: Argument '$Var' is invalid. Provided value: '$Value'. Constraint: '$ProcDef($ProcName,Arg,$Var,-validatecommand)'" } } } # Check if the variable value satisfies an eventually defined range: if {[info exists ProcDef($ProcName,Arg,$Var,-range)]} { if {$Value<[lindex $ProcDef($ProcName,Arg,$Var,-range) 0] || \ $Value>[lindex $ProcDef($ProcName,Arg,$Var,-range) 1]} { ProcedureArgumentEvaluationReturn "[PureProcName]: Argument '$Var' has to be between [lindex $ProcDef($ProcName,Arg,$Var,-range) 0] and [lindex $ProcDef($ProcName,Arg,$Var,-range) 1]" } } # Check the variable value is a member of a provided choice list: if {[info exists ProcDef($ProcName,Arg,$Var,-choices)]} { if {[lsearch -exact $ProcDef($ProcName,Arg,$Var,-choices) $Value]<0} { ProcedureArgumentEvaluationReturn "[PureProcName]: Argument '$Var' has to be one of the following elements: [GetChoiceHelpText $ProcName $Var]" } } } } } #### Procedure level validation #### foreach ValidateCommand $ProcDef($ProcName,-validatecommand) ValidateCommandErrorText $ProcDef($ProcName,-validatecommand_error_text) { # regsub {%P} $ProcDef($ProcName,Arg,$Var,-validatecommand) $Value ValidateCommand if {![uplevel $ValidateCommand]} { if {$ValidateCommandErrorText!=""} { ProcedureArgumentEvaluationReturn "[PureProcName]: $ValidateCommandErrorText" } else { ProcedureArgumentEvaluationReturn "[PureProcName]: Invalid argument(s) provided. Constraint: '$ValidateCommand'" } } } #### Log the procedure call #### variable ProcedureCallLogList if {$InteractiveCall && $ProcDef($ProcName,-command_log)=="interactive"} { append ProcedureCallLogList $ProcName if {$ProcDef($ProcName,-named_arguments_first)} { set ParClasses {Named Unnamed} } else { set ParClasses {Unnamed Named} } foreach ParClass $ParClasses { foreach Var $ProcDef($ProcName,${ParClass}VarList) { if {![info exists Variable__$Var]} continue; # Skip optional arguments that haven't been defined if {$ProcDef($ProcName,Arg,$Var,-type)!="none"} { # Non flag arguments if {$ProcDef($ProcName,Arg,$Var,IsNamed)} { append ProcedureCallLogList " -$Var" } append ProcedureCallLogList " \{[set Variable__$Var]\}" } elseif {[set Variable__$Var]} { # Flags that are set append ProcedureCallLogList " -$Var" } } } append ProcedureCallLogList "; \# interactive call\n" } elseif {$ProcDef($ProcName,-command_log)=="1"} { append ProcedureCallLogList "$ProcedureCallLine\n" } ProcedureArgumentEvaluationReturn "" } ######## Validation commands ######## # For each of the standard argument types supported by TEPAM, the validation command # 'Validate() specified in the following section. These commands have to return '1' in # case the provided value correspond to the relevant type and '0' if not. Additional user or # application specific types can easily be supported simply by adding a validation command # for the new type into the 'tepam' namespace. proc Validate() {v} {return 1} proc Validate(none) {v} {return 1} proc Validate(string) {v} {return 1} proc Validate(text) {v} {return 1} proc Validate(boolean) {v} {string is boolean -strict $v} proc Validate(double) {v} {string is double -strict $v} proc Validate(integer) {v} {string is integer -strict $v} proc Validate(alnum) {v} {string is alnum $v} proc Validate(alpha) {v} {string is alpha $v} proc Validate(ascii) {v} {string is ascii $v} proc Validate(control) {v} {string is control $v} proc Validate(digit) {v} {string is digit $v} proc Validate(graph) {v} {string is graph $v} proc Validate(lower) {v} {string is lower $v} proc Validate(print) {v} {string is print $v} proc Validate(punct) {v} {string is punct $v} proc Validate(space) {v} {string is space $v} proc Validate(upper) {v} {string is upper $v} proc Validate(wordchar) {v} {string is wordchar $v} proc Validate(xdigit) {v} {string is xdigit $v} proc Validate(char) {v} {expr [string length $v]==1} proc Validate(color) {v} {expr ![catch {winfo rgb . $v}]} proc Validate(font) {v} {expr ![catch {font measure $v ""}]} proc Validate(file) {v} {expr [string length $v]>0 && ![regexp {[\"*?<>]} $v]} proc Validate(existingfile) {v} {file exists $v} proc Validate(directory) {v} {return 1} proc Validate(existingdirectory) {v} {file isdirectory $v} ######## Help text generation ######## # 'ProcedureHelp_Append' appends a piece of text to the existing HelpText variable of the # calling context (procedure). Tabulator characters are replaced through 3 spaces. Lines are # reformatted to respect the maximum allowed line length. In case a line is wrapped, the leading # spaces of the first line are added to the begin of the following lines. Multiple lines can be # provided as text piece and these multiple lines are handled independently each to another. proc ProcedureHelp_Append {Text} { upvar HelpText HelpText variable help_line_length # Replace tabs through 3 spaces: regsub -all {\t} $Text " " Text # Extract the initial spaces of the first line: regexp {^(\s*)} $Text {} SpaceStart # Loop through each of the provided help text line: foreach line [split $Text "\n"] { # Eliminate leading spaces of the line: regexp {^\s+'*(.*)$} $line {} line # Cut the line into segments that doesn't exceed the maximum allowed help line length. # Add in front of each new line the initial spaces of the first line: while {$line!=""} { # Align the leading line spaces to the first line: set line ${SpaceStart}${line} #### Next line cutoff position evaluation #### # Select the next line cut position. The default position is set to the line end: set LastPos [string length $line] # Search for the last space inside the line section that is inside the specified # maximum line length: if {$LastPos>$help_line_length} { set LastPos [string last " " $line $help_line_length] } # If the evaluated line break position is inside the range of the initial line spaces, # something goes wrong and the line should be broken at another adequate character: if {$LastPos<=[string length $SpaceStart]-1} { # Search for other good line break characters (: set LastPos [lindex [lindex \ [regexp -inline -indices {[^,:\.?\)]+$} \ {ProcDef(::ImportTestPointAssignmentsGeneric,Arg_SectionComment,ColumnSeparation}] 0] 0] # No line break position could be found: if {$LastPos=={}} {set LinePos 0} } # Break the line simply at the maximum allowed length in case no break position could # be found: if {$LastPos<=[string length $SpaceStart]-1} {set LastPos $help_line_length} # Add the line segment to the help text: append HelpText [string range $line 0 [expr $LastPos-1]]\n # Eliminate the segment from the actual line: set line [string range $line [expr $LastPos+1] end] } } } # GetChoiceHelpText returns a help text for the choice options. The returned string corresponds # to the comma separated choice list in case no choice labels are defined. Otherwise, the # choice labels are added behind the choice options in paranthesis. proc GetChoiceHelpText {ProcName Var} { variable ProcDef set ChoiceHelpText "" set LabelList {} catch {set LabelList $ProcDef($ProcName,Arg,$Var,-choicelabels)} foreach Choice $ProcDef($ProcName,Arg,$Var,-choices) Label $LabelList { append ChoiceHelpText ", $Choice" if {$Label!=""} { append ChoiceHelpText "($Label)" } } return [string range $ChoiceHelpText 2 end] } # 'ProcedureHelp' behaves in different ways, depending the provided argument. Called without any # argument, it summarizes all the declared procedures without explaining details about the # procedure arguments. Called with a particular procedure name as parameter, it produces for # this procedure a comprehensive help text. And finally, if it is called with the name of a main # procedure that has multiple sub procedures, it generates for all the sub procedures the # complete help text. proc ProcedureHelp {{ProcName ""} {ReturnHelp 0}} { variable ProcDef variable ProcedureList ProcedureHelp_Append "NAME" # Print a list of available commands when no procedure name has been provided as argument: if {$ProcName==""} { foreach ProcName [lsort -dictionary $ProcedureList] { if {[info exists ProcDef($ProcName,-short_description)]} { ProcedureHelp_Append " [PureProcName] - $ProcDef($ProcName,-short_description)" } else { ProcedureHelp_Append " [PureProcName]" } } # A procedure name has been provided, generate a detailed help text for this procedure, or # for all sub procedures if only the main procedure names has been provided: } else { # Create the full qualified procedure name (procedure name including namespace). # Check if the procedure name contains already the name space identification: if {[string range $ProcName 0 1]!="::"} { # The namespace is not part of the used procedure name call. Evaluate it explicitly: set NameSpace [uplevel 1 {namespace current}] if {$NameSpace!="::"} {append NameSpace "::"} set ProcName ${NameSpace}${ProcName} } set PureProcName [PureProcName] # Add the short description if it exists to the NAME help text section. Please note that # only the short description of a main procedure is used in case the procedure has also # sub procedures. if {[info exists ProcDef($ProcName,-short_description)]} { ProcedureHelp_Append " $PureProcName - $ProcDef($ProcName,-short_description)" } else { ProcedureHelp_Append " $PureProcName" } # Create the SYNOPSIS section which contains also the synopsis of eventual sub procedures: ProcedureHelp_Append "SYNOPSIS" set NbrDescriptions 0 set NbrExamples 0 # Loop through all procedures and sub procedures: set ProcNames [lsort -dictionary [concat [list $ProcName] [info procs "$ProcName *"]]] foreach ProcName $ProcNames { # Skip the (sub) procedure if it has not been explicitly declared. This may be the # case for procedures that are not implemented themselves but which have sub procedures: if {![info exists ProcDef($ProcName,VarList)]} continue set PureProcName [PureProcName] # Add to the help text first the procedure name, and then in the following lines its # arguments: ProcedureHelp_Append " $PureProcName" if {$ProcDef($ProcName,-named_arguments_first)} { set ParClasses {Named Unnamed} } else { set ParClasses {Unnamed Named} } foreach ParClass $ParClasses { foreach Var $ProcDef($ProcName,${ParClass}VarList) { # Section comment: Create a clean separation of the arguments: if {[info exists ProcDef($ProcName,Arg,$Var,SectionComment)]} { ProcedureHelp_Append " --- $ProcDef($ProcName,Arg,$Var,SectionComment) ---" } # Argument declaration - put optional arguments into brackets, show the name # of named arguments, add existing descriptions as well as range, type, choice # definitions: set HelpLine " " if {$ProcDef($ProcName,Arg,$Var,-optional)} { append HelpLine "\[" } if {$ProcDef($ProcName,Arg,$Var,IsNamed)} { append HelpLine "-$Var " } if {$ProcDef($ProcName,Arg,$Var,-type)!="none"} { append HelpLine "<$Var>" } if {$ProcDef($ProcName,Arg,$Var,-optional)} { append HelpLine "\]" } ProcedureHelp_Append $HelpLine set HelpLine " " if {[info exists ProcDef($ProcName,Arg,$Var,-description)]} { append HelpLine "$ProcDef($ProcName,Arg,$Var,-description), " } if {[lsearch -exact {"" "none"} $ProcDef($ProcName,Arg,$Var,-type)]<0} { append HelpLine "type: $ProcDef($ProcName,Arg,$Var,-type), " } if {[info exists ProcDef($ProcName,Arg,$Var,-default)]} { if {[lsearch -exact {"" "string"} $ProcDef($ProcName,Arg,$Var,-type)]>=0 || $ProcDef($ProcName,Arg,$Var,-default)==""} { append HelpLine "default: \"$ProcDef($ProcName,Arg,$Var,-default)\", " } else { append HelpLine "default: $ProcDef($ProcName,Arg,$Var,-default), " } } if {$ProcDef($ProcName,Arg,$Var,-multiple)} { append HelpLine "multiple: yes, " } if {[info exists ProcDef($ProcName,Arg,$Var,-range)]} { append HelpLine "range: [lindex $ProcDef($ProcName,Arg,$Var,-range) 0]..[lindex $ProcDef($ProcName,Arg,$Var,-range) 1], " } if {[info exists ProcDef($ProcName,Arg,$Var,-choices)]} { append HelpLine "choices: \{[GetChoiceHelpText $ProcName $Var]\}, " } # Eliminate the last ", ": ProcedureHelp_Append [string range $HelpLine 0 end-2] } } # Remember if descriptions and/or examples are provided for the procedure: if {[info exists ProcDef($ProcName,-description)]} { incr NbrDescriptions } if {[info exists ProcDef($ProcName,-example)]} { incr NbrExamples } } # Add for the procedure and sub procedures the descriptions: if {$NbrDescriptions>0} { ProcedureHelp_Append "DESCRIPTION" foreach ProcName $ProcNames { if {[info exists ProcDef($ProcName,-description)]} { if {[llength $ProcNames]>1} { ProcedureHelp_Append " $PureProcName" ProcedureHelp_Append " $ProcDef($ProcName,-description)" } else { ProcedureHelp_Append " $ProcDef($ProcName,-description)" } } } } # Add for the procedure and sub procedures the examples: if {$NbrExamples>0} { ProcedureHelp_Append "EXAMPLE" foreach ProcName $ProcNames { if {[info exists ProcDef($ProcName,-example)]} { if {[llength $ProcNames]>1} { ProcedureHelp_Append " $PureProcName" ProcedureHelp_Append " $ProcDef($ProcName,-example)" } else { ProcedureHelp_Append " $ProcDef($ProcName,-example)" } } } } } # The created help text is by default printed to stdout. The text will be returned # as result when 'ReturnHelp' is set to 1: if {$ReturnHelp} { return $HelpText } else { puts $HelpText } } ########################################################################## # argument_dialogbox # ########################################################################## ######## Argument_dialogbox configuration ######## # Application specific entry widget procedures can use this array variable to store their own # data, using as index the widget path provided to the procedure, e.g. # argument_dialogbox($W,): array set argument_dialogbox {} # Special elements of this array variable can be specified for testing purposes: # # Set to following variable to "ok" to simulate an acknowledge of the dialog box and to # "cancel" to simulate an activation of the Cancel button: set argument_dialogbox(test,status) "" # The following variable can contain a script that is executed for test purposes, before # the argument dialog box waits on user interactions. The script is executed in the context # of the argument dialog box. Entire user interaction actions can be emulated together # with the previous variable. set argument_dialogbox(test,script) {} # The array variable 'last_parameters' is only used by an argument dialog box when its context # has been specified via the -context attribute. The argument dialog box' position and size as # well as its entered data are stored inside this variable when the data are acknowledged and # the form is closed. This allows the form to restore its previous state once it is called # another time. array set last_parameters {} ######## Argument_dialogbox help text ######## set ArgumentDialogboxHelp { argument_dialogbox \ [-title ] [-window ] [-context ] [-validatecommand