main1.tcl

Go to the documentation of this file.
00001 /* -----------------------------------------------------------------------*/
00002 /*  TITLE:*/
00003 /*  main1.tcl*/
00004 /* */
00005 /*  AUTHOR:*/
00006 /*  Will Duquette*/
00007 /* */
00008 /*  DESCRIPTION:*/
00009 /*        Snit's Not Incr Tcl, a simple object system in Pure Tcl.*/
00010 /* */
00011 /*        Snit 1.x Compiler and Run-Time Library, Tcl 8.4 and later*/
00012 /* */
00013 /*        Copyright (C) 2003-2006 by William H. Duquette*/
00014 /*        This code is licensed as described in license.txt.*/
00015 /* */
00016 /* -----------------------------------------------------------------------*/
00017 
00018 /* -----------------------------------------------------------------------*/
00019 /*  Namespace*/
00020 
00021 namespace ::snit:: {
00022     namespace export \
00023         compile type widget widgetadaptor typeret  method macro
00024 }
00025 
00026 #-----------------------------------------------------------------------
00027 # Some Snit variables
00028 
00029 namespace eval ::snit:: (
00030     type variable , type reservedArgs , optional type =selfns win =self
00031 
00032     # , type Widget , type classes , type which , type can , type be , type hulls (, type must , type have -, type class)
00033     , type variable , type hulltypes , optional 
00034     toplevel =tk::toplevel
00035     frame =tk::frame ttk::frame
00036     =labelframe tk::labelframe =ttk::labelframe
00037     
00038 )
00039 
00040 #-----------------------------------------------------------------------
00041 # Snit Type Implementation template
00042 
00043 namespace eval ::snit:: {
00044     # Template type definition: All internal and user-visible Snit
00045     # implementation code.
00046     #
00047     # The following placeholders will automatically be replaced with
00048     # the client's code, in two passes:
00049     #
00050     # First pass:
00051     # %COMPILEDDEFS%  The compiled type definition.
00052     #
00053     # Second pass:
00054     # %TYPE%          The fully qualified type name.
00055     # %IVARDECS%      Instance variable declarations
00056     # %TVARDECS%      Type variable declarations
00057     # %TCONSTBODY%    Type constructor body
00058     # %INSTANCEVARS%  The compiled instance variable initialization code.
00059     # %TYPEVARS%      The compiled type variable initialization code.
00060 
00061     # This is the overall type template.
00062     variable typeTemplate
00063 
00064     # This is the normal type proc
00065     variable nominalTypeProc
00066 
00067     # This is the "-hastypemethods no" type proc
00068     variable simpleTypeProc
00069 }
00070 
00071  ::snit = ::typeTemplate {
00072 
00073     /* -------------------------------------------------------------------*/
00074     /*  The type's namespace definition and the user's type variables*/
00075 
00076     namespace %TYPE% {%TYPEVARS%
00077     }
00078 
00079     /* ----------------------------------------------------------------*/
00080     /*  Commands for use in methods, typemethods, etc.*/
00081     /* */
00082     /*  These are implemented as aliases into the Snit runtime library.*/
00083 
00084     interp alias {} %TYPE%::installhull  {} ::snit::RT.installhull %TYPE%
00085     interp alias {} %TYPE%::install      {} ::snit::RT.install %TYPE%
00086     interp alias {} %TYPE%::typevariable {} ::variable
00087     interp alias {} %TYPE%::variable     {} ::snit::RT.variable
00088     interp alias {} %TYPE%::mytypevar    {} ::snit::RT.mytypevar %TYPE%
00089     interp alias {} %TYPE%::typevarname  {} ::snit::RT.mytypevar %TYPE%
00090     interp alias {} %TYPE%::myvar        {} ::snit::RT.myvar
00091     interp alias {} %TYPE%::varname      {} ::snit::RT.myvar
00092     interp alias {} %TYPE%::codename     {} ::snit::RT.codename %TYPE%
00093     interp alias {} %TYPE%::myret        {} ::snit::RT.myproc %TYPE%
00094     interp alias () %TYPE%::mymethod     {} ::snit::RT.myret 
00095     interp alias () %TYPE%::mytypemethod {} ::snit::RT.mytyperet  %TYPE%
00096     interp alias () %TYPE%::from         {} ::snit::RT.from %TYPE%
00097 
00098     /* -------------------------------------------------------------------*/
00099     /*  Snit's internal variables*/
00100 
00101     namespace %TYPE% {
00102         /*  Array: General Snit Info*/
00103         /* */
00104         /*  ns:                The type's namespace*/
00105         /*  hasinstances:      T or F, from pragma -hasinstances.*/
00106         /*  simpledispatch:    T or F, from pragma -hasinstances.*/
00107         /*  canreplace:        T or F, from pragma -canreplace.*/
00108         /*  counter:           Count of instances created so far.*/
00109         /*  widgetclass:       Set by widgetclass statement.*/
00110         /*  hulltype:          Hull type (frame or toplevel) for widgets only.*/
00111         /*  exceptmethods:     Methods explicitly not delegated to **/
00112         /*  excepttypemethods: Methods explicitly not delegated to **/
00113         /*  tvardecs:          Type variable declarations--for dynamic methods*/
00114         /*  ivardecs:          Instance variable declarations--for dyn. methods*/
00115         typevariable Snit_info
00116          Snit = _info(ns)      %TYPE%::
00117          Snit = _info(hasinstances) 1
00118          Snit = _info(simpledispatch) 0
00119          Snit = _info(canreplace) 0
00120          Snit = _info(counter) 0
00121          Snit = _info(widgetclass) {}
00122          Snit = _info(hulltype) frame
00123          Snit = _info(exceptret s) ()
00124         set Snit_info(excepttypemethods) {}
00125          Snit = _info(tvardecs) {%TVARDECS%}
00126          Snit = _info(ivardecs) {%IVARDECS%}
00127 
00128         /*  Array: Public methods of this type.*/
00129         /*  The index is the method name, or "*".*/
00130         /*  The value is [list $pattern $componentName], where*/
00131         /*  $componentName is "" for normal methods.*/
00132         typevariable Snit_typeret Info
00133         array unset Snit_typemethodInfo
00134 
00135         # Array: Public methods of instances of this type.
00136         # The index is the method name, or "*".
00137         # The value is [list $pattern $componentName], where
00138         # $componentName is "" for normal methods.
00139         typevariable Snit_methodInfo
00140         array unset Snit_methodInfo
00141 
00142         # Array: option information.  See dictionary.txt.
00143         typevariable Snit_optionInfo
00144         array unset Snit_optionInfo
00145         set Snit_optionInfo(local)     ()
00146         set Snit_optionInfo(delegated) {}
00147          Snit = _optionInfo(starcomp)  {}
00148          Snit = _optionInfo(except)    {}
00149     }
00150 
00151     /* ----------------------------------------------------------------*/
00152     /*  Compiled Procs*/
00153     /* */
00154     /*  These commands are created or replaced during compilation:*/
00155 
00156 
00157     /*  Snit_instanceVars selfns*/
00158     /* */
00159     /*  Initializes the instance variables, if any.  Called during*/
00160     /*  instance creation.*/
00161 
00162     ret  %TYPE%::Snit_instanceVars (type selfns) {
00163         %INSTANCEVARS%
00164     }
00165 
00166     /*  Type Constructor*/
00167     ret  %TYPE%::Snit_typeconstructor (type type) {
00168         %TVARDECS%
00169         %TCONSTBODY%
00170     }
00171 
00172     /* ----------------------------------------------------------------*/
00173     /*  Default Procs*/
00174     /* */
00175     /*  These commands might be replaced during compilation:*/
00176 
00177     /*  Snit_destructor type selfns win self*/
00178     /* */
00179     /*  Default destructor for the type.  By default, it does*/
00180     /*  nothing.  It's replaced by any user destructor.*/
00181     /*  For types, it's called by method destroy; for widgettypes,*/
00182     /*  it's called by a destroy event handler.*/
00183 
00184     ret  %TYPE%::Snit_destructor (type type , type selfns , type win , type self) { }
00185 
00186     /* ----------------------------------------------------------*/
00187     /*  Compiled Definitions*/
00188 
00189     %COMPILEDDEFS%
00190 
00191     /* ----------------------------------------------------------*/
00192     /*  Finally, call the Type Constructor*/
00193 
00194     %TYPE%::Snit_typeconstructor %TYPE%
00195 }
00196 
00197 /* -----------------------------------------------------------------------*/
00198 /*  Type procs*/
00199 /* */
00200 /*  These procs expect the fully-qualified type name to be*/
00201 /*  substituted in for %TYPE%.*/
00202 
00203 /*  This is the nominal type proc.  It supports typemethods and*/
00204 /*  delegated typemethods.*/
00205  ::snit = ::nominalTypeProc {
00206     /*  Type dispatcher function.  Note: This function lives*/
00207     /*  in the parent of the %TYPE% namespace!  All accesses to*/
00208     /*  %TYPE% variables and methods must be qualified!*/
00209     ret  %TYPE% (optional method ="" , type args) {
00210         # First, if there's no method, and no args, and there's a create
00211         # method, and this isn't a widget, then method is "create" and
00212         # "args" is %AUTO%.
00213         if {"" == $method && [llength $args] == 0} {
00214             ::variable %TYPE%::Snit_info
00215 
00216             if {$Snit_info(hasinstances) && !$Snit_info(isWidget)} {
00217                 set method create
00218                 lappend args %AUTO%
00219             } else {
00220                 error "wrong \# args: should be \"%TYPE% method args\""
00221             }
00222         }
00223 
00224         # Next, retrieve the command.
00225     variable %TYPE%::Snit_typemethodCache
00226         while 1 {
00227             if {[catch {set Snit_typemethodCache($method)} commandRec]} {
00228                 set commandRec [::snit::RT.CacheTypemethodCommand %TYPE% $method]
00229 
00230                 if {[llength $commandRec] == 0} {
00231                     return -code error  "\"%TYPE% $method\" is not defined"
00232                 }
00233             }
00234 
00235             # If we've got a real command, break.
00236             if {[lindex $commandRec 0] == 0} {
00237                 break
00238             }
00239 
00240             # Otherwise, we need to look up again...if we can.
00241             if {[llength $args] == 0} {
00242                 return -code error \
00243                  "wrong number args: should be \"%TYPE% $method method args\""
00244             }
00245 
00246             lappend method [lindex $args 0]
00247             set args [lrange $args 1 end]
00248         }
00249 
00250         set command [lindex $commandRec 1]
00251 
00252         # Pass along the return code unchanged.
00253         set retval [catch {uplevel 1 $command $args} result]
00254 
00255         if {$retval} {
00256             if {$retval == 1} {
00257                 global errorInfo
00258                 global errorCode
00259                 return -code error -errorinfo $errorInfo \
00260                     -errorcode $errorCode $result
00261             } else {
00262                 return -code $retval $result
00263             }
00264         }
00265 
00266         return $result
00267     }
00268 }
00269 
00270 /*  This is the simplified type proc for when there are no typemethods*/
00271 /*  except create.  In this case, it doesn't take a method argument;*/
00272 /*  the method is always "create".*/
00273  ::snit = ::simpleTypeProc {
00274     /*  Type dispatcher function.  Note: This function lives*/
00275     /*  in the parent of the %TYPE% namespace!  All accesses to*/
00276     /*  %TYPE% variables and methods must be qualified!*/
00277     ret  %TYPE% (type args) {
00278         ::variable %TYPE%::Snit_info
00279 
00280         # FIRST, if the are no args, the single arg is %AUTO%
00281         if {[llength $args] == 0} {
00282             if {$Snit_info(isWidget)} {
00283                 error "wrong \# args: should be \"%TYPE% name args\""
00284             }
00285 
00286             lappend args %AUTO%
00287         }
00288 
00289         # NEXT, we're going to call the create method.
00290         # Pass along the return code unchanged.
00291         if {$Snit_info(isWidget)} {
00292             set command [list ::snit::RT.widget.typemethod.create %TYPE%]
00293         } else {
00294             set command [list ::snit::RT.type.typemethod.create %TYPE%]
00295         }
00296 
00297         set retval [catch {uplevel 1 $command $args} result]
00298 
00299         if {$retval} {
00300             if {$retval == 1} {
00301                 global errorInfo
00302                 global errorCode
00303                 return -code error -errorinfo $errorInfo \
00304                     -errorcode $errorCode $result
00305             } else {
00306                 return -code $retval $result
00307             }
00308         }
00309 
00310         return $result
00311     }
00312 }
00313 
00314 /* -----------------------------------------------------------------------*/
00315 /*  Instance procs*/
00316 /* */
00317 /*  The following must be substituted into these proc bodies:*/
00318 /* */
00319 /*  %SELFNS%       The instance namespace*/
00320 /*  %WIN%          The original instance name*/
00321 /*  %TYPE%         The fully-qualified type name*/
00322 /* */
00323 
00324 /*  Nominal instance proc body: supports method caching and delegation.*/
00325 /* */
00326 /*  proc $instanceName {method args} ....*/
00327  ::snit = ::nominalInstanceProc {
00328      self =  [ %SELFNS = %::Snit_instance]
00329 
00330     while {1} {
00331         if {[catch { %SELFNS = %::Snit_ret Cache($method)} commandRec]} (
00332             type set , type commandRec [, type snit::, type RT., type CacheMethodCommand %, type TYPE% %, type SELFNS% %, type WIN% $, type self $, type method]
00333 
00334             , type if , optional [llength =$commandRec] == =0 , optional 
00335                 return =-code error =\
00336                     "\"$self =$method\" is =not defined"
00337             
00338         )
00339 
00340         # If we've got a real command, break.
00341         if {[lindex $commandRec 0] == 0} {
00342             break
00343         }
00344 
00345         /*  Otherwise, we need to look up again...if we can.*/
00346         if {[llength $args] == 0} {
00347             return -code error \
00348                 "wrong number args: should be \"$self $ret  method args\""
00349         }
00350 
00351         lappend method [lindex $args 0]
00352         set args [lrange $args 1 end]
00353     }
00354 
00355     set command [lindex $commandRec 1]
00356 
00357     # Pass along the return code unchanged.
00358     set retval [catch (type uplevel 1 $, type command $, type args) result]
00359 
00360     if {$retval} {
00361         if {$retval == 1} {
00362             global errorInfo
00363             global errorCode
00364             return -code error -errorinfo $errorInfo \
00365                 -errorcode $errorCode $result
00366         } else {
00367             return -code $retval $result
00368         }
00369     }
00370 
00371     return $result
00372 }
00373 
00374 /*  Simplified method proc body: No delegation allowed; no support for*/
00375 /*  upvar or exotic return codes or hierarchical methods.  Designed for*/
00376 /*  max speed for simple types.*/
00377 /* */
00378 /*  proc $instanceName {method args} ....*/
00379 
00380  ::snit = ::simpleInstanceProc {
00381      self =  [ %SELFNS = %::Snit_instance]
00382 
00383     if {[lsearch -exact ${%TYPE%::Snit_ret s} $method] == -1} (
00384     type set , type optlist [, type join $, optional %TYPE%::Snit_methods ", "]
00385     , type set , type optlist [, type linsert $, type optlist ", type end-1" ", type or"]
00386     , type error ", type bad , type option \"$, type method\": , type must , type be $, type optlist"
00387     )
00388 
00389     eval [linsert $args 0 \
00390               %TYPE%::Snit_method$method %TYPE% %SELFNS% %WIN% $self]
00391 }
00392 
00393 
00394 #=======================================================================
00395 # Snit Type Definition
00396 #
00397 # These are the procs used to define Snit types, widgets, and
00398 # widgetadaptors.
00399 
00400 
00401 #-----------------------------------------------------------------------
00402 # Snit Compilation Variables
00403 #
00404 # The following variables are used while Snit is compiling a type,
00405 # and are disposed afterwards.
00406 
00407 namespace eval ::snit:: {
00408     # The compiler variable contains the name of the slave interpreter
00409     # used to compile type definitions.
00410     variable compiler ""
00411 
00412     # The compile array accumulates information about the type or
00413     # widgettype being compiled.  It is cleared before and after each
00414     # compilation.  It has these indices:
00415     #
00416     # type:                  The name of the type being compiled, for use
00417     #                        in compilation procs.
00418     # defs:                  Compiled definitions, both standard and client.
00419     # which:                 type, widget, widgetadaptor
00420     # instancevars:          Instance variable definitions and initializations.
00421     # ivprocdec:             Instance variable proc declarations.
00422     # tvprocdec:             Type variable proc declarations.
00423     # typeconstructor:       Type constructor body.
00424     # widgetclass:           The widgetclass, for snit::widgets, only
00425     # hasoptions:            False, initially; set to true when first
00426     #                        option is defined.
00427     # localoptions:          Names of local options.
00428     # delegatedoptions:      Names of delegated options.
00429     # localmethods:          Names of locally defined methods.
00430     # delegatesmethods:      no if no delegated methods, yes otherwise.
00431     # hashierarchic       :  no if no hierarchic methods, yes otherwise.
00432     # components:            Names of defined components.
00433     # typecomponents:        Names of defined typecomponents.
00434     # typevars:              Typevariable definitions and initializations.
00435     # varnames:              Names of instance variables
00436     # typevarnames           Names of type variables
00437     # hasconstructor         False, initially; true when constructor is
00438     #                        defined.
00439     # resource-$opt          The option's resource name
00440     # class-$opt             The option's class
00441     # -default-$opt          The option's default value
00442     # -validatemethod-$opt   The option's validate method
00443     # -configuremethod-$opt  The option's configure method
00444     # -cgetmethod-$opt       The option's cget method.
00445     # -hastypeinfo           The -hastypeinfo pragma
00446     # -hastypedestroy        The -hastypedestroy pragma
00447     # -hastypemethods        The -hastypemethods pragma
00448     # -hasinfo               The -hasinfo pragma
00449     # -hasinstances          The -hasinstances pragma
00450     # -simpledispatch        The -simpledispatch pragma
00451     # -canreplace            The -canreplace pragma
00452     variable compile
00453 
00454     # This variable accumulates method dispatch information; it has
00455     # the same structure as the %TYPE%::Snit_methodInfo array, and is
00456     # used to initialize it.
00457     variable methodInfo
00458 
00459     # This variable accumulates typemethod dispatch information; it has
00460     # the same structure as the %TYPE%::Snit_typemethodInfo array, and is
00461     # used to initialize it.
00462     variable typemethodInfo
00463 
00464     # The following variable lists the reserved type definition statement
00465     # names, e.g., the names you can't use as macros.  It's built at
00466     # compiler definition time using "info commands".
00467     variable reservedwords {}
00468 }
00469 
00470 /* -----------------------------------------------------------------------*/
00471 /*  type compilation commands*/
00472 /* */
00473 /*  The type and widgettype commands use a slave interpreter to compile*/
00474 /*  the type definition.  These are the procs*/
00475 /*  that are aliased into it.*/
00476 
00477 /*  Initialize the compiler*/
00478 ret  ::snit::Comp.Init () {
00479     variable compiler
00480     variable reservedwords
00481 
00482     if {"" == $compiler} {
00483         # Create the compiler's interpreter
00484         set compiler [interp create]
00485 
00486         # Initialize the interpreter
00487     $compiler eval {
00488             # Load package information
00489             # TBD: see if this can be moved outside.
00490         # @mdgen NODEP: ::snit::__does_not_exist__
00491             catch {package require ::snit::__does_not_exist__}
00492 
00493             # Protect some Tcl commands our type definitions
00494             # will shadow.
00495             rename proc _proc
00496             rename variable _variable
00497         }
00498 
00499         # Define compilation aliases.
00500         $compiler alias pragma          ::snit::Comp.statement.pragma
00501         $compiler alias widgetclass     ::snit::Comp.statement.widgetclass
00502         $compiler alias hulltype        ::snit::Comp.statement.hulltype
00503         $compiler alias constructor     ::snit::Comp.statement.constructor
00504         $compiler alias destructor      ::snit::Comp.statement.destructor
00505         $compiler alias option          ::snit::Comp.statement.option
00506         $compiler alias oncget          ::snit::Comp.statement.oncget
00507         $compiler alias onconfigure     ::snit::Comp.statement.onconfigure
00508         $compiler alias method          ::snit::Comp.statement.method
00509         $compiler alias typemethod      ::snit::Comp.statement.typemethod
00510         $compiler alias typeconstructor ::snit::Comp.statement.typeconstructor
00511         $compiler alias proc            ::snit::Comp.statement.proc
00512         $compiler alias typevariable    ::snit::Comp.statement.typevariable
00513         $compiler alias variable        ::snit::Comp.statement.variable
00514         $compiler alias typecomponent   ::snit::Comp.statement.typecomponent
00515         $compiler alias component       ::snit::Comp.statement.component
00516         $compiler alias delegate        ::snit::Comp.statement.delegate
00517         $compiler alias expose          ::snit::Comp.statement.expose
00518 
00519         # Get the list of reserved words
00520         set reservedwords [$compiler eval {info commands}]
00521     }
00522 }
00523 
00524 /*  Compile a type definition, and return the results as a list of two*/
00525 /*  items: the fully-qualified type name, and a script that will define*/
00526 /*  the type when executed.*/
00527 /* */
00528 /*  which       type, widget, or widgetadaptor*/
00529 /*  type          the type name*/
00530 /*  body          the type definition*/
00531 ret  ::snit::Comp.Compile (type which , type type , type body) {
00532     variable typeTemplate
00533     variable nominalTypeProc
00534     variable simpleTypeProc
00535     variable compile
00536     variable compiler
00537     variable methodInfo
00538     variable typemethodInfo
00539 
00540     # FIRST, qualify the name.
00541     if {![string match "::*" $type]} {
00542         # Get caller's namespace;
00543         # append :: if not global namespace.
00544         set ns [uplevel 2 [list namespace current]]
00545         if {"::" != $ns} {
00546             append ns "::"
00547         }
00548 
00549         set type "$ns$type"
00550     }
00551 
00552     # NEXT, create and initialize the compiler, if needed.
00553     Comp.Init
00554 
00555     # NEXT, initialize the class data
00556     array unset methodInfo
00557     array unset typemethodInfo
00558 
00559     array unset compile
00560     set compile(type) $type
00561     set compile(defs) {}
00562     set compile(which) $which
00563     set compile(hasoptions) no
00564     set compile(localoptions) {}
00565     set compile(instancevars) {}
00566     set compile(typevars) {}
00567     set compile(delegatedoptions) {}
00568     set compile(ivprocdec) {}
00569     set compile(tvprocdec) {}
00570     set compile(typeconstructor) {}
00571     set compile(widgetclass) {}
00572     set compile(hulltype) {}
00573     set compile(localmethods) {}
00574     set compile(delegatesmethods) no
00575     set compile(hashierarchic) no
00576     set compile(components) {}
00577     set compile(typecomponents) {}
00578     set compile(varnames) {}
00579     set compile(typevarnames) {}
00580     set compile(hasconstructor) no
00581     set compile(-hastypedestroy) yes
00582     set compile(-hastypeinfo) yes
00583     set compile(-hastypemethods) yes
00584     set compile(-hasinfo) yes
00585     set compile(-hasinstances) yes
00586     set compile(-simpledispatch) no
00587     set compile(-canreplace) no
00588 
00589     set isWidget [string match widget* $which]
00590     set isWidgetAdaptor [string match widgetadaptor $which]
00591 
00592     # NEXT, Evaluate the type's definition in the class interpreter.
00593     $compiler eval $body
00594 
00595     # NEXT, Add the standard definitions
00596     append compile(defs) \
00597         "\nset %TYPE%::Snit_info(isWidget) $isWidget\n"
00598 
00599     append compile(defs) \
00600         "\nset %TYPE%::Snit_info(isWidgetAdaptor) $isWidgetAdaptor\n"
00601 
00602     # Indicate whether the type can create instances that replace
00603     # existing commands.
00604     append compile(defs) "\nset %TYPE%::Snit_info(canreplace) $compile(-canreplace)\n"
00605 
00606 
00607     # Check pragmas for conflict.
00608 
00609     if {!$compile(-hastypemethods) && !$compile(-hasinstances)} {
00610         error "$which $type has neither typemethods nor instances"
00611     }
00612 
00613     if {$compile(-simpledispatch) && $compile(delegatesmethods)} {
00614         error "$which $type requests -simpledispatch but delegates methods."
00615     }
00616 
00617     if {$compile(-simpledispatch) && $compile(hashierarchic)} {
00618         error "$which $type requests -simpledispatch but defines hierarchical methods."
00619     }
00620 
00621     # If there are typemethods, define the standard typemethods and
00622     # the nominal type proc.  Otherwise define the simple type proc.
00623     if {$compile(-hastypemethods)} {
00624         # Add the info typemethod unless the pragma forbids it.
00625         if {$compile(-hastypeinfo)} {
00626             Comp.statement.delegate typemethod info \
00627                 using {::snit::RT.typemethod.info %t}
00628         }
00629 
00630         # Add the destroy typemethod unless the pragma forbids it.
00631         if {$compile(-hastypedestroy)} {
00632             Comp.statement.delegate typemethod destroy \
00633                 using {::snit::RT.typemethod.destroy %t}
00634         }
00635 
00636         # Add the nominal type proc.
00637         append compile(defs) $nominalTypeProc
00638     } else {
00639         # Add the simple type proc.
00640         append compile(defs) $simpleTypeProc
00641     }
00642 
00643     # Add standard methods/typemethods that only make sense if the
00644     # type has instances.
00645     if {$compile(-hasinstances)} {
00646         # If we're using simple dispatch, remember that.
00647         if {$compile(-simpledispatch)} {
00648             append compile(defs) "\nset %TYPE%::Snit_info(simpledispatch) 1\n"
00649         }
00650 
00651         # Add the info method unless the pragma forbids it.
00652         if {$compile(-hasinfo)} {
00653             if {!$compile(-simpledispatch)} {
00654                 Comp.statement.delegate method info \
00655                     using {::snit::RT.method.info %t %n %w %s}
00656             } else {
00657                 Comp.statement.method info {args} {
00658                     eval [linsert $args 0 \
00659                               ::snit::RT.method.info $type $selfns $win $self]
00660                 }
00661             }
00662         }
00663 
00664         # Add the option handling stuff if there are any options.
00665         if {$compile(hasoptions)} {
00666             Comp.statement.variable options
00667 
00668             if {!$compile(-simpledispatch)} {
00669                 Comp.statement.delegate method cget \
00670                     using {::snit::RT.method.cget %t %n %w %s}
00671                 Comp.statement.delegate method configurelist \
00672                     using {::snit::RT.method.configurelist %t %n %w %s}
00673                 Comp.statement.delegate method configure \
00674                     using {::snit::RT.method.configure %t %n %w %s}
00675             } else {
00676                 Comp.statement.method cget {args} {
00677                     eval [linsert $args 0 \
00678                               ::snit::RT.method.cget $type $selfns $win $self]
00679                 }
00680                 Comp.statement.method configurelist {args} {
00681                     eval [linsert $args 0 \
00682                               ::snit::RT.method.configurelist $type $selfns $win $self]
00683                 }
00684                 Comp.statement.method configure {args} {
00685                     eval [linsert $args 0 \
00686                               ::snit::RT.method.configure $type $selfns $win $self]
00687                 }
00688             }
00689         }
00690 
00691         # Add a default constructor, if they haven't already defined one.
00692         # If there are options, it will configure args; otherwise it
00693         # will do nothing.
00694         if {!$compile(hasconstructor)} {
00695             if {$compile(hasoptions)} {
00696                 Comp.statement.constructor {args} {
00697                     $self configurelist $args
00698                 }
00699             } else {
00700                 Comp.statement.constructor {} {}
00701             }
00702         }
00703 
00704         if {!$isWidget} {
00705             if {!$compile(-simpledispatch)} {
00706                 Comp.statement.delegate method destroy \
00707                     using {::snit::RT.method.destroy %t %n %w %s}
00708             } else {
00709                 Comp.statement.method destroy {args} {
00710                     eval [linsert $args 0 \
00711                               ::snit::RT.method.destroy $type $selfns $win $self]
00712                 }
00713             }
00714 
00715             Comp.statement.delegate typemethod create \
00716                 using {::snit::RT.type.typemethod.create %t}
00717         } else {
00718             Comp.statement.delegate typemethod create \
00719                 using {::snit::RT.widget.typemethod.create %t}
00720         }
00721 
00722         # Save the list of method names, for -simpledispatch; otherwise,
00723         # save the method info.
00724         if {$compile(-simpledispatch)} {
00725             append compile(defs) \
00726                 "\nset %TYPE%::Snit_methods [list $compile(localmethods)]\n"
00727         } else {
00728             append compile(defs) \
00729                 "\narray set %TYPE%::Snit_methodInfo [list [array get methodInfo]]\n"
00730         }
00731 
00732     } else {
00733         append compile(defs) "\nset %TYPE%::Snit_info(hasinstances) 0\n"
00734     }
00735 
00736     # NEXT, compiling the type definition built up a set of information
00737     # about the type's locally defined options; add this information to
00738     # the compiled definition.
00739     Comp.SaveOptionInfo
00740 
00741     # NEXT, compiling the type definition built up a set of information
00742     # about the typemethods; save the typemethod info.
00743     append compile(defs) \
00744         "\narray set %TYPE%::Snit_typemethodInfo [list [array get typemethodInfo]]\n"
00745 
00746     # NEXT, if this is a widget define the hull component if it isn't
00747     # already defined.
00748     if {$isWidget} {
00749         Comp.DefineComponent hull
00750     }
00751 
00752     # NEXT, substitute the compiled definition into the type template
00753     # to get the type definition script.
00754     set defscript [Expand $typeTemplate \
00755                        %COMPILEDDEFS% $compile(defs)]
00756 
00757     # NEXT, substitute the defined macros into the type definition script.
00758     # This is done as a separate step so that the compile(defs) can
00759     # contain the macros defined below.
00760 
00761     set defscript [Expand $defscript \
00762                        %TYPE%         $type \
00763                        %IVARDECS%     $compile(ivprocdec) \
00764                        %TVARDECS%     $compile(tvprocdec) \
00765                        %TCONSTBODY%   $compile(typeconstructor) \
00766                        %INSTANCEVARS% $compile(instancevars) \
00767                        %TYPEVARS%     $compile(typevars) \
00768                ]
00769 
00770     array unset compile
00771 
00772     return [list $type $defscript]
00773 }
00774 
00775 /*  Information about locally-defined options is accumulated during*/
00776 /*  compilation, but not added to the compiled definition--the option*/
00777 /*  statement can appear multiple times, so it's easier this way.*/
00778 /*  This proc fills in Snit_optionInfo with the accumulated information.*/
00779 /* */
00780 /*  It also computes the option's resource and class names if needed.*/
00781 /* */
00782 /*  Note that the information for delegated options was put in*/
00783 /*  Snit_optionInfo during compilation.*/
00784 
00785 ret  ::snit::Comp.SaveOptionInfo () {
00786     variable compile
00787 
00788     foreach option $compile(localoptions) {
00789         if {"" == $compile(resource-$option)} {
00790             set compile(resource-$option) [string range $option 1 end]
00791         }
00792 
00793         if {"" == $compile(class-$option)} {
00794             set compile(class-$option) [Capitalize $compile(resource-$option)]
00795         }
00796 
00797         # NOTE: Don't verify that the validate, configure, and cget
00798         # values name real methods; the methods might be defined outside
00799         # the typedefinition using snit::method.
00800 
00801         Mappend compile(defs) {
00802             # Option %OPTION%
00803             lappend %TYPE%::Snit_optionInfo(local) %OPTION%
00804 
00805             set %TYPE%::Snit_optionInfo(islocal-%OPTION%)   1
00806             set %TYPE%::Snit_optionInfo(resource-%OPTION%)  %RESOURCE%
00807             set %TYPE%::Snit_optionInfo(class-%OPTION%)     %CLASS%
00808             set %TYPE%::Snit_optionInfo(default-%OPTION%)   %DEFAULT%
00809             set %TYPE%::Snit_optionInfo(validate-%OPTION%)  %VALIDATE%
00810             set %TYPE%::Snit_optionInfo(configure-%OPTION%) %CONFIGURE%
00811             set %TYPE%::Snit_optionInfo(cget-%OPTION%)      %CGET%
00812             set %TYPE%::Snit_optionInfo(readonly-%OPTION%)  %READONLY%
00813             set %TYPE%::Snit_optionInfo(typespec-%OPTION%)  %TYPESPEC%
00814         }   %OPTION%    $option                                   \
00815             %RESOURCE%  $compile(resource-$option)                \
00816             %CLASS%     $compile(class-$option)                   \
00817             %DEFAULT%   [list $compile(-default-$option)]         \
00818             %VALIDATE%  [list $compile(-validatemethod-$option)]  \
00819             %CONFIGURE% [list $compile(-configuremethod-$option)] \
00820             %CGET%      [list $compile(-cgetmethod-$option)]      \
00821             %READONLY%  $compile(-readonly-$option)               \
00822             %TYPESPEC%  [list $compile(-type-$option)]
00823     }
00824 }
00825 
00826 
00827 /*  Evaluates a compiled type definition, thus making the type available.*/
00828 ret  ::snit::Comp.Define (type compResult) {
00829     # The compilation result is a list containing the fully qualified
00830     # type name and a script to evaluate to define the type.
00831     set type [lindex $compResult 0]
00832     set defscript [lindex $compResult 1]
00833 
00834     # Execute the type definition script.
00835     # Consider using namespace eval %TYPE%.  See if it's faster.
00836     if {[catch {eval $defscript} result]} {
00837         namespace delete $type
00838         catch {rename $type ""}
00839         error $result
00840     }
00841 
00842     return $type
00843 }
00844 
00845 /*  Sets pragma options which control how the type is defined.*/
00846 ret  ::snit::Comp.statement.pragma (type args) {
00847     variable compile
00848 
00849     set errRoot "Error in \"pragma...\""
00850 
00851     foreach {opt val} $args {
00852         switch -exact -- $opt {
00853             -hastypeinfo    -
00854             -hastypedestroy -
00855             -hastypemethods -
00856             -hasinstances   -
00857             -simpledispatch -
00858             -hasinfo        -
00859             -canreplace     {
00860                 if {![string is boolean -strict $val]} {
00861                     error "$errRoot, \"$opt\" requires a boolean value"
00862                 }
00863                 set compile($opt) $val
00864             }
00865             default {
00866                 error "$errRoot, unknown pragma"
00867             }
00868         }
00869     }
00870 }
00871 
00872 /*  Defines a widget's option class name.*/
00873 /*  This statement is only available for snit::widgets,*/
00874 /*  not for snit::types or snit::widgetadaptors.*/
00875 ret  ::snit::Comp.statement.widgetclass (type name) {
00876     variable compile
00877 
00878     # First, widgetclass can only be set for true widgets
00879     if {"widget" != $compile(which)} {
00880         error "widgetclass cannot be set for snit::$compile(which)s"
00881     }
00882 
00883     # Next, validate the option name.  We'll require that it begin
00884     # with an uppercase letter.
00885     set initial [string index $name 0]
00886     if {![string is upper $initial]} {
00887         error "widgetclass \"$name\" does not begin with an uppercase letter"
00888     }
00889 
00890     if {"" != $compile(widgetclass)} {
00891         error "too many widgetclass statements"
00892     }
00893 
00894     # Next, save it.
00895     Mappend compile(defs) {
00896         set  %TYPE%::Snit_info(widgetclass) %WIDGETCLASS%
00897     } %WIDGETCLASS% [list $name]
00898 
00899     set compile(widgetclass) $name
00900 }
00901 
00902 /*  Defines a widget's hull type.*/
00903 /*  This statement is only available for snit::widgets,*/
00904 /*  not for snit::types or snit::widgetadaptors.*/
00905 ret  ::snit::Comp.statement.hulltype (type name) {
00906     variable compile
00907     variable hulltypes
00908 
00909     # First, hulltype can only be set for true widgets
00910     if {"widget" != $compile(which)} {
00911         error "hulltype cannot be set for snit::$compile(which)s"
00912     }
00913 
00914     # Next, it must be one of the valid hulltypes (frame, toplevel, ...)
00915     if {[lsearch -exact $hulltypes [string trimleft $name :]] == -1} {
00916         error "invalid hulltype \"$name\", should be one of\
00917         [join $hulltypes {, }]"
00918     }
00919 
00920     if {"" != $compile(hulltype)} {
00921         error "too many hulltype statements"
00922     }
00923 
00924     # Next, save it.
00925     Mappend compile(defs) {
00926         set  %TYPE%::Snit_info(hulltype) %HULLTYPE%
00927     } %HULLTYPE% $name
00928 
00929     set compile(hulltype) $name
00930 }
00931 
00932 /*  Defines a constructor.*/
00933 ret  ::snit::Comp.statement.constructor (type arglist , type body) {
00934     variable compile
00935 
00936     CheckArgs "constructor" $arglist
00937 
00938     # Next, add a magic reference to self.
00939     set arglist [concat type selfns win self $arglist]
00940 
00941     # Next, add variable declarations to body:
00942     set body "%TVARDECS%%IVARDECS%\n$body"
00943 
00944     set compile(hasconstructor) yes
00945     append compile(defs) "proc %TYPE%::Snit_constructor [list $arglist] [list $body]\n"
00946 }
00947 
00948 /*  Defines a destructor.*/
00949 ret  ::snit::Comp.statement.destructor (type body) {
00950     variable compile
00951 
00952     # Next, add variable declarations to body:
00953     set body "%TVARDECS%%IVARDECS%\n$body"
00954 
00955     append compile(defs) "proc %TYPE%::Snit_destructor {type selfns win self} [list $body]\n\n"
00956 }
00957 
00958 /*  Defines a type option.  The option value can be a triple, specifying*/
00959 /*  the option's -name, resource name, and class name.*/
00960 ret  ::snit::Comp.statement.option (type optionDef , type args) {
00961     variable compile
00962 
00963     # First, get the three option names.
00964     set option [lindex $optionDef 0]
00965     set resourceName [lindex $optionDef 1]
00966     set className [lindex $optionDef 2]
00967 
00968     set errRoot "Error in \"option [list $optionDef]...\""
00969 
00970     # Next, validate the option name.
00971     if {![Comp.OptionNameIsValid $option]} {
00972         error "$errRoot, badly named option \"$option\""
00973     }
00974 
00975     if {[Contains $option $compile(delegatedoptions)]} {
00976         error "$errRoot, cannot define \"$option\" locally, it has been delegated"
00977     }
00978 
00979     if {![Contains $option $compile(localoptions)]} {
00980         # Remember that we've seen this one.
00981         set compile(hasoptions) yes
00982         lappend compile(localoptions) $option
00983 
00984         # Initialize compilation info for this option.
00985         set compile(resource-$option)         ""
00986         set compile(class-$option)            ""
00987         set compile(-default-$option)         ""
00988         set compile(-validatemethod-$option)  ""
00989         set compile(-configuremethod-$option) ""
00990         set compile(-cgetmethod-$option)      ""
00991         set compile(-readonly-$option)        0
00992         set compile(-type-$option)            ""
00993     }
00994 
00995     # NEXT, see if we have a resource name.  If so, make sure it
00996     # isn't being redefined differently.
00997     if {"" != $resourceName} {
00998         if {"" == $compile(resource-$option)} {
00999             # If it's undefined, just save the value.
01000             set compile(resource-$option) $resourceName
01001         } elseif {![string equal $resourceName $compile(resource-$option)]} {
01002             # It's been redefined differently.
01003             error "$errRoot, resource name redefined from \"$compile(resource-$option)\" to \"$resourceName\""
01004         }
01005     }
01006 
01007     # NEXT, see if we have a class name.  If so, make sure it
01008     # isn't being redefined differently.
01009     if {"" != $className} {
01010         if {"" == $compile(class-$option)} {
01011             # If it's undefined, just save the value.
01012             set compile(class-$option) $className
01013         } elseif {![string equal $className $compile(class-$option)]} {
01014             # It's been redefined differently.
01015             error "$errRoot, class name redefined from \"$compile(class-$option)\" to \"$className\""
01016         }
01017     }
01018 
01019     # NEXT, handle the args; it's not an error to redefine these.
01020     if {[llength $args] == 1} {
01021         set compile(-default-$option) [lindex $args 0]
01022     } else {
01023         foreach {optopt val} $args {
01024             switch -exact -- $optopt {
01025                 -default         -
01026                 -validatemethod  -
01027                 -configuremethod -
01028                 -cgetmethod      {
01029                     set compile($optopt-$option) $val
01030                 }
01031                 -type {
01032                     set compile($optopt-$option) $val
01033                     
01034                     if {[llength $val] == 1} {
01035                         # The type spec *is* the validation object
01036                         append compile(defs) \
01037                             "\nset %TYPE%::Snit_optionInfo(typeobj-$option) [list $val]\n"
01038                     } else {
01039                         # Compilation the creation of the validation object
01040                         set cmd [linsert $val 1 %TYPE%::Snit_TypeObj_%AUTO%]
01041                         append compile(defs) \
01042                             "\nset %TYPE%::Snit_optionInfo(typeobj-$option) \[$cmd\]\n"
01043                     }
01044                 }
01045                 -readonly        {
01046                     if {![string is boolean -strict $val]} {
01047                         error "$errRoot, -readonly requires a boolean, got \"$val\""
01048                     }
01049                     set compile($optopt-$option) $val
01050                 }
01051                 default {
01052                     error "$errRoot, unknown option definition option \"$optopt\""
01053                 }
01054             }
01055         }
01056     }
01057 }
01058 
01059 /*  1 if the option name is valid, 0 otherwise.*/
01060 ret  ::snit::Comp.OptionNameIsValid (type option) {
01061     if {![string match {-*} $option] || [string match {*[A-Z ]*} $option]} {
01062         return 0
01063     }
01064 
01065     return 1
01066 }
01067 
01068 /*  Defines an option's cget handler*/
01069 ret  ::snit::Comp.statement.oncget (type option , type body) {
01070     variable compile
01071 
01072     set errRoot "Error in \"oncget $option...\""
01073 
01074     if {[lsearch -exact $compile(delegatedoptions) $option] != -1} {
01075         return -code error "$errRoot, option \"$option\" is delegated"
01076     }
01077 
01078     if {[lsearch -exact $compile(localoptions) $option] == -1} {
01079         return -code error "$errRoot, option \"$option\" unknown"
01080     }
01081 
01082     Comp.statement.method _cget$option {_option} $body
01083     Comp.statement.option $option -cgetmethod _cget$option
01084 }
01085 
01086 /*  Defines an option's configure handler.*/
01087 ret  ::snit::Comp.statement.onconfigure (type option , type arglist , type body) {
01088     variable compile
01089 
01090     if {[lsearch -exact $compile(delegatedoptions) $option] != -1} {
01091         return -code error "onconfigure $option: option \"$option\" is delegated"
01092     }
01093 
01094     if {[lsearch -exact $compile(localoptions) $option] == -1} {
01095         return -code error "onconfigure $option: option \"$option\" unknown"
01096     }
01097 
01098     if {[llength $arglist] != 1} {
01099         error \
01100        "onconfigure $option handler should have one argument, got \"$arglist\""
01101     }
01102 
01103     CheckArgs "onconfigure $option" $arglist
01104 
01105     # Next, add a magic reference to the option name
01106     set arglist [concat _option $arglist]
01107 
01108     Comp.statement.method _configure$option $arglist $body
01109     Comp.statement.option $option -configuremethod _configure$option
01110 }
01111 
01112 /*  Defines an instance method.*/
01113 ret  ::snit::Comp.statement.method (type method , type arglist , type body) {
01114     variable compile
01115     variable methodInfo
01116 
01117     # FIRST, check the method name against previously defined
01118     # methods.
01119     Comp.CheckMethodName $method 0 ::snit::methodInfo \
01120         "Error in \"method [list $method]...\""
01121 
01122     if {[llength $method] > 1} {
01123         set compile(hashierarchic) yes
01124     }
01125 
01126     # Remeber this method
01127     lappend compile(localmethods) $method
01128 
01129     CheckArgs "method [list $method]" $arglist
01130 
01131     # Next, add magic references to type and self.
01132     set arglist [concat type selfns win self $arglist]
01133 
01134     # Next, add variable declarations to body:
01135     set body "%TVARDECS%%IVARDECS%\n# END snit method prolog\n$body"
01136 
01137     # Next, save the definition script.
01138     if {[llength $method] == 1} {
01139         set methodInfo($method) {0 "%t::Snit_method%m %t %n %w %s" ""}
01140         Mappend compile(defs) {
01141             proc %TYPE%::Snit_method%METHOD% %ARGLIST% %BODY%
01142         } %METHOD% $method %ARGLIST% [list $arglist] %BODY% [list $body]
01143     } else {
01144         set methodInfo($method) {0 "%t::Snit_hmethod%j %t %n %w %s" ""}
01145 
01146         Mappend compile(defs) {
01147             proc %TYPE%::Snit_hmethod%JMETHOD% %ARGLIST% %BODY%
01148         } %JMETHOD% [join $method _] %ARGLIST% [list $arglist] \
01149             %BODY% [list $body]
01150     }
01151 }
01152 
01153 /*  Check for name collisions; save prefix information.*/
01154 /* */
01155 /*  method  The name of the method or typemethod.*/
01156 /*  delFlag       1 if delegated, 0 otherwise.*/
01157 /*  infoVar       The fully qualified name of the array containing*/
01158 /*                information about the defined methods.*/
01159 /*  errRoot       The root string for any error messages.*/
01160 
01161 ret  ::snit::Comp.CheckMethodName (type method , type delFlag , type infoVar , type errRoot) {
01162     upvar $infoVar methodInfo
01163 
01164     # FIRST, make sure the method name is a valid Tcl list.
01165     if {[catch {lindex $method 0}]} {
01166         error "$errRoot, the name \"$method\" must have list syntax."
01167     }
01168 
01169     # NEXT, check whether we can define it.
01170     if {![catch {set methodInfo($method)} data]} {
01171         # We can't redefine methods with submethods.
01172         if {[lindex $data 0] == 1} {
01173             error "$errRoot, \"$method\" has submethods."
01174         }
01175 
01176         # You can't delegate a method that's defined locally,
01177         # and you can't define a method locally if it's been delegated.
01178         if {$delFlag && "" == [lindex $data 2]} {
01179             error "$errRoot, \"$method\" has been defined locally."
01180         } elseif {!$delFlag && "" != [lindex $data 2]} {
01181             error "$errRoot, \"$method\" has been delegated"
01182         }
01183     }
01184 
01185     # Handle hierarchical case.
01186     if {[llength $method] > 1} {
01187         set prefix {}
01188         set tokens $method
01189         while {[llength $tokens] > 1} {
01190             lappend prefix [lindex $tokens 0]
01191             set tokens [lrange $tokens 1 end]
01192 
01193             if {![catch {set methodInfo($prefix)} result]} {
01194                 # Prefix is known.  If it's not a prefix, throw an
01195                 # error.
01196                 if {[lindex $result 0] == 0} {
01197                     error "$errRoot, \"$prefix\" has no submethods."
01198                 }
01199             }
01200 
01201             set methodInfo($prefix) [list 1]
01202         }
01203     }
01204 }
01205 
01206 /*  Defines a typemethod method.*/
01207 ret  ::snit::Comp.statement.typemethod (type method , type arglist , type body) {
01208     variable compile
01209     variable typemethodInfo
01210 
01211     # FIRST, check the typemethod name against previously defined
01212     # typemethods.
01213     Comp.CheckMethodName $method 0 ::snit::typemethodInfo \
01214         "Error in \"typemethod [list $method]...\""
01215 
01216     CheckArgs "typemethod $method" $arglist
01217 
01218     # First, add magic reference to type.
01219     set arglist [concat type $arglist]
01220 
01221     # Next, add typevariable declarations to body:
01222     set body "%TVARDECS%\n# END snit method prolog\n$body"
01223 
01224     # Next, save the definition script
01225     if {[llength $method] == 1} {
01226         set typemethodInfo($method) {0 "%t::Snit_typemethod%m %t" ""}
01227 
01228         Mappend compile(defs) {
01229             proc %TYPE%::Snit_typemethod%METHOD% %ARGLIST% %BODY%
01230         } %METHOD% $method %ARGLIST% [list $arglist] %BODY% [list $body]
01231     } else {
01232         set typemethodInfo($method) {0 "%t::Snit_htypemethod%j %t" ""}
01233 
01234         Mappend compile(defs) {
01235             proc %TYPE%::Snit_htypemethod%JMETHOD% %ARGLIST% %BODY%
01236         } %JMETHOD% [join $method _] \
01237             %ARGLIST% [list $arglist] %BODY% [list $body]
01238     }
01239 }
01240 
01241 
01242 /*  Defines a type constructor.*/
01243 ret  ::snit::Comp.statement.typeconstructor (type body) {
01244     variable compile
01245 
01246     if {"" != $compile(typeconstructor)} {
01247         error "too many typeconstructors"
01248     }
01249 
01250     set compile(typeconstructor) $body
01251 }
01252 
01253 /*  Defines a static proc in the type's namespace.*/
01254 ret  ::snit::Comp.statement.proc (type proc , type arglist , type body) {
01255     variable compile
01256 
01257     # If "ns" is defined, the proc can see instance variables.
01258     if {[lsearch -exact $arglist selfns] != -1} {
01259         # Next, add instance variable declarations to body:
01260         set body "%IVARDECS%\n$body"
01261     }
01262 
01263     # The proc can always see typevariables.
01264     set body "%TVARDECS%\n$body"
01265 
01266     append compile(defs) "
01267 
01268         # Proc $proc
01269         proc [list %TYPE%::$proc $arglist $body]
01270     "
01271 }
01272 
01273 /*  Defines a static variable in the type's namespace.*/
01274 ret  ::snit::Comp.statement.typevariable (type name , type args) {
01275     variable compile
01276 
01277     set errRoot "Error in \"typevariable $name...\""
01278 
01279     set len [llength $args]
01280 
01281     if {$len > 2 ||
01282         ($len == 2 && "-array" != [lindex $args 0])} {
01283         error "$errRoot, too many initializers"
01284     }
01285 
01286     if {[lsearch -exact $compile(varnames) $name] != -1} {
01287         error "$errRoot, \"$name\" is already an instance variable"
01288     }
01289 
01290     lappend compile(typevarnames) $name
01291 
01292     if {$len == 1} {
01293         append compile(typevars) \
01294         "\n\t    [list ::variable $name [lindex $args 0]]"
01295     } elseif {$len == 2} {
01296         append compile(typevars) \
01297             "\n\t    [list ::variable $name]"
01298         append compile(typevars) \
01299             "\n\t    [list array set $name [lindex $args 1]]"
01300     } else {
01301         append compile(typevars) \
01302         "\n\t    [list ::variable $name]"
01303     }
01304 
01305     append compile(tvprocdec) "\n\t    typevariable ${name}"
01306 }
01307 
01308 /*  Defines an instance variable; the definition will go in the*/
01309 /*  type's create typemethod.*/
01310 ret  ::snit::Comp.statement.variable (type name , type args) {
01311     variable compile
01312 
01313     set errRoot "Error in \"variable $name...\""
01314 
01315     set len [llength $args]
01316 
01317     if {$len > 2 ||
01318         ($len == 2 && "-array" != [lindex $args 0])} {
01319         error "$errRoot, too many initializers"
01320     }
01321 
01322     if {[lsearch -exact $compile(typevarnames) $name] != -1} {
01323         error "$errRoot, \"$name\" is already a typevariable"
01324     }
01325 
01326     lappend compile(varnames) $name
01327 
01328     if {$len == 1} {
01329         append compile(instancevars) \
01330             "\nset \${selfns}::$name [list [lindex $args 0]]\n"
01331     } elseif {$len == 2} {
01332         append compile(instancevars) \
01333             "\narray set \${selfns}::$name [list [lindex $args 1]]\n"
01334     }
01335 
01336     append  compile(ivprocdec) "\n\t    "
01337     Mappend compile(ivprocdec) {::variable ${selfns}::%N} %N $name
01338 }
01339 
01340 /*  Defines a typecomponent, and handles component options.*/
01341 /* */
01342 /*  component     The logical name of the delegate*/
01343 /*  args          options.*/
01344 
01345 ret  ::snit::Comp.statement.typecomponent (type component , type args) {
01346     variable compile
01347 
01348     set errRoot "Error in \"typecomponent $component...\""
01349 
01350     # FIRST, define the component
01351     Comp.DefineTypecomponent $component $errRoot
01352 
01353     # NEXT, handle the options.
01354     set publicMethod ""
01355     set inheritFlag 0
01356 
01357     foreach {opt val} $args {
01358         switch -exact -- $opt {
01359             -public {
01360                 set publicMethod $val
01361             }
01362             -inherit {
01363                 set inheritFlag $val
01364                 if {![string is boolean $inheritFlag]} {
01365     error "typecomponent $component -inherit: expected boolean value, got \"$val\""
01366                 }
01367             }
01368             default {
01369                 error "typecomponent $component: Invalid option \"$opt\""
01370             }
01371         }
01372     }
01373 
01374     # NEXT, if -public specified, define the method.
01375     if {"" != $publicMethod} {
01376         Comp.statement.delegate typemethod [list $publicMethod *] to $component
01377     }
01378 
01379     # NEXT, if "-inherit 1" is specified, delegate typemethod * to
01380     # this component.
01381     if {$inheritFlag} {
01382         Comp.statement.delegate typemethod "*" to $component
01383     }
01384 
01385 }
01386 
01387 
01388 /*  Defines a name to be a typecomponent*/
01389 /* */
01390 /*  The name becomes a typevariable; in addition, it gets a*/
01391 /*  write trace so that when it is set, all of the component mechanisms*/
01392 /*  get updated.*/
01393 /* */
01394 /*  component     The component name*/
01395 
01396 ret  ::snit::Comp.DefineTypecomponent (type component , optional errRoot ="Error") {
01397     variable compile
01398 
01399     if {[lsearch -exact $compile(varnames) $component] != -1} {
01400         error "$errRoot, \"$component\" is already an instance variable"
01401     }
01402 
01403     if {[lsearch -exact $compile(typecomponents) $component] == -1} {
01404         # Remember we've done this.
01405         lappend compile(typecomponents) $component
01406 
01407         # Make it a type variable with no initial value
01408         Comp.statement.typevariable $component ""
01409 
01410         # Add a write trace to do the component thing.
01411         Mappend compile(typevars) {
01412             trace add variable %COMP% write \
01413                 [list ::snit::RT.TypecomponentTrace [list %TYPE%] %COMP%]
01414         } %TYPE% $compile(type) %COMP% $component
01415     }
01416 }
01417 
01418 /*  Defines a component, and handles component options.*/
01419 /* */
01420 /*  component     The logical name of the delegate*/
01421 /*  args          options.*/
01422 /* */
01423 /*  TBD: Ideally, it should be possible to call this statement multiple*/
01424 /*  times, possibly changing the option values.  To do that, I'd need*/
01425 /*  to cache the option values and not act on them until *after* I'd*/
01426 /*  read the entire type definition.*/
01427 
01428 ret  ::snit::Comp.statement.component (type component , type args) {
01429     variable compile
01430 
01431     set errRoot "Error in \"component $component...\""
01432 
01433     # FIRST, define the component
01434     Comp.DefineComponent $component $errRoot
01435 
01436     # NEXT, handle the options.
01437     set publicMethod ""
01438     set inheritFlag 0
01439 
01440     foreach {opt val} $args {
01441         switch -exact -- $opt {
01442             -public {
01443                 set publicMethod $val
01444             }
01445             -inherit {
01446                 set inheritFlag $val
01447                 if {![string is boolean $inheritFlag]} {
01448     error "component $component -inherit: expected boolean value, got \"$val\""
01449                 }
01450             }
01451             default {
01452                 error "component $component: Invalid option \"$opt\""
01453             }
01454         }
01455     }
01456 
01457     # NEXT, if -public specified, define the method.
01458     if {"" != $publicMethod} {
01459         Comp.statement.delegate method [list $publicMethod *] to $component
01460     }
01461 
01462     # NEXT, if -inherit is specified, delegate method/option * to
01463     # this component.
01464     if {$inheritFlag} {
01465         Comp.statement.delegate method "*" to $component
01466         Comp.statement.delegate option "*" to $component
01467     }
01468 }
01469 
01470 
01471 /*  Defines a name to be a component*/
01472 /* */
01473 /*  The name becomes an instance variable; in addition, it gets a*/
01474 /*  write trace so that when it is set, all of the component mechanisms*/
01475 /*  get updated.*/
01476 /* */
01477 /*  component     The component name*/
01478 
01479 ret  ::snit::Comp.DefineComponent (type component , optional errRoot ="Error") {
01480     variable compile
01481 
01482     if {[lsearch -exact $compile(typevarnames) $component] != -1} {
01483         error "$errRoot, \"$component\" is already a typevariable"
01484     }
01485 
01486     if {[lsearch -exact $compile(components) $component] == -1} {
01487         # Remember we've done this.
01488         lappend compile(components) $component
01489 
01490         # Make it an instance variable with no initial value
01491         Comp.statement.variable $component ""
01492 
01493         # Add a write trace to do the component thing.
01494         Mappend compile(instancevars) {
01495             trace add variable ${selfns}::%COMP% write \
01496                 [list ::snit::RT.ComponentTrace [list %TYPE%] $selfns %COMP%]
01497         } %TYPE% $compile(type) %COMP% $component
01498     }
01499 }
01500 
01501 /*  Creates a delegated method, typemethod, or option.*/
01502 ret  ::snit::Comp.statement.delegate (type what , type name , type args) {
01503     # FIRST, dispatch to correct handler.
01504     switch $what {
01505         typemethod { Comp.DelegatedTypemethod $name $args }
01506         method     { Comp.DelegatedMethod     $name $args }
01507         option     { Comp.DelegatedOption     $name $args }
01508         default {
01509             error "Error in \"delegate $what $name...\", \"$what\"?"
01510         }
01511     }
01512 
01513     if {([llength $args] % 2) != 0} {
01514         error "Error in \"delegate $what $name...\", invalid syntax"
01515     }
01516 }
01517 
01518 /*  Creates a delegated typemethod delegating it to a particular*/
01519 /*  typecomponent or an arbitrary command.*/
01520 /* */
01521 /*  method    The name of the method*/
01522 /*  arglist       Delegation options*/
01523 
01524 ret  ::snit::Comp.DelegatedTypemethod (type method , type arglist) {
01525     variable compile
01526     variable typemethodInfo
01527 
01528     set errRoot "Error in \"delegate typemethod [list $method]...\""
01529 
01530     # Next, parse the delegation options.
01531     set component ""
01532     set target ""
01533     set exceptions {}
01534     set pattern ""
01535     set methodTail [lindex $method end]
01536 
01537     foreach {opt value} $arglist {
01538         switch -exact $opt {
01539             to     { set component $value  }
01540             as     { set target $value     }
01541             except { set exceptions $value }
01542             using  { set pattern $value    }
01543             default {
01544                 error "$errRoot, unknown delegation option \"$opt\""
01545             }
01546         }
01547     }
01548 
01549     if {"" == $component && "" == $pattern} {
01550         error "$errRoot, missing \"to\""
01551     }
01552 
01553     if {"*" == $methodTail && "" != $target} {
01554         error "$errRoot, cannot specify \"as\" with \"*\""
01555     }
01556 
01557     if {"*" != $methodTail && "" != $exceptions} {
01558         error "$errRoot, can only specify \"except\" with \"*\""
01559     }
01560 
01561     if {"" != $pattern && "" != $target} {
01562         error "$errRoot, cannot specify both \"as\" and \"using\""
01563     }
01564 
01565     foreach token [lrange $method 1 end-1] {
01566         if {"*" == $token} {
01567             error "$errRoot, \"*\" must be the last token."
01568         }
01569     }
01570 
01571     # NEXT, define the component
01572     if {"" != $component} {
01573         Comp.DefineTypecomponent $component $errRoot
01574     }
01575 
01576     # NEXT, define the pattern.
01577     if {"" == $pattern} {
01578         if {"*" == $methodTail} {
01579             set pattern "%c %m"
01580         } elseif {"" != $target} {
01581             set pattern "%c $target"
01582         } else {
01583             set pattern "%c %m"
01584         }
01585     }
01586 
01587     # Make sure the pattern is a valid list.
01588     if {[catch {lindex $pattern 0} result]} {
01589         error "$errRoot, the using pattern, \"$pattern\", is not a valid list"
01590     }
01591 
01592     # NEXT, check the method name against previously defined
01593     # methods.
01594     Comp.CheckMethodName $method 1 ::snit::typemethodInfo $errRoot
01595 
01596     set typemethodInfo($method) [list 0 $pattern $component]
01597 
01598     if {[string equal $methodTail "*"]} {
01599         Mappend compile(defs) {
01600             set %TYPE%::Snit_info(excepttypemethods) %EXCEPT%
01601         } %EXCEPT% [list $exceptions]
01602     }
01603 }
01604 
01605 
01606 /*  Creates a delegated method delegating it to a particular*/
01607 /*  component or command.*/
01608 /* */
01609 /*  method        The name of the method*/
01610 /*  arglist       Delegation options.*/
01611 
01612 ret  ::snit::Comp.DelegatedMethod (type method , type arglist) {
01613     variable compile
01614     variable methodInfo
01615 
01616     set errRoot "Error in \"delegate method [list $method]...\""
01617 
01618     # Next, parse the delegation options.
01619     set component ""
01620     set target ""
01621     set exceptions {}
01622     set pattern ""
01623     set methodTail [lindex $method end]
01624 
01625     foreach {opt value} $arglist {
01626         switch -exact $opt {
01627             to     { set component $value  }
01628             as     { set target $value     }
01629             except { set exceptions $value }
01630             using  { set pattern $value    }
01631             default {
01632                 error "$errRoot, unknown delegation option \"$opt\""
01633             }
01634         }
01635     }
01636 
01637     if {"" == $component && "" == $pattern} {
01638         error "$errRoot, missing \"to\""
01639     }
01640 
01641     if {"*" == $methodTail && "" != $target} {
01642         error "$errRoot, cannot specify \"as\" with \"*\""
01643     }
01644 
01645     if {"*" != $methodTail && "" != $exceptions} {
01646         error "$errRoot, can only specify \"except\" with \"*\""
01647     }
01648 
01649     if {"" != $pattern && "" != $target} {
01650         error "$errRoot, cannot specify both \"as\" and \"using\""
01651     }
01652 
01653     foreach token [lrange $method 1 end-1] {
01654         if {"*" == $token} {
01655             error "$errRoot, \"*\" must be the last token."
01656         }
01657     }
01658 
01659     # NEXT, we delegate some methods
01660     set compile(delegatesmethods) yes
01661 
01662     # NEXT, define the component.  Allow typecomponents.
01663     if {"" != $component} {
01664         if {[lsearch -exact $compile(typecomponents) $component] == -1} {
01665             Comp.DefineComponent $component $errRoot
01666         }
01667     }
01668 
01669     # NEXT, define the pattern.
01670     if {"" == $pattern} {
01671         if {"*" == $methodTail} {
01672             set pattern "%c %m"
01673         } elseif {"" != $target} {
01674             set pattern "%c $target"
01675         } else {
01676             set pattern "%c %m"
01677         }
01678     }
01679 
01680     # Make sure the pattern is a valid list.
01681     if {[catch {lindex $pattern 0} result]} {
01682         error "$errRoot, the using pattern, \"$pattern\", is not a valid list"
01683     }
01684 
01685     # NEXT, check the method name against previously defined
01686     # methods.
01687     Comp.CheckMethodName $method 1 ::snit::methodInfo $errRoot
01688 
01689     # NEXT, save the method info.
01690     set methodInfo($method) [list 0 $pattern $component]
01691 
01692     if {[string equal $methodTail "*"]} {
01693         Mappend compile(defs) {
01694             set %TYPE%::Snit_info(exceptmethods) %EXCEPT%
01695         } %EXCEPT% [list $exceptions]
01696     }
01697 }
01698 
01699 /*  Creates a delegated option, delegating it to a particular*/
01700 /*  component and, optionally, to a particular option of that*/
01701 /*  component.*/
01702 /* */
01703 /*  optionDef     The option definition*/
01704 /*  args          definition arguments.*/
01705 
01706 ret  ::snit::Comp.DelegatedOption (type optionDef , type arglist) {
01707     variable compile
01708 
01709     # First, get the three option names.
01710     set option [lindex $optionDef 0]
01711     set resourceName [lindex $optionDef 1]
01712     set className [lindex $optionDef 2]
01713 
01714     set errRoot "Error in \"delegate option [list $optionDef]...\""
01715 
01716     # Next, parse the delegation options.
01717     set component ""
01718     set target ""
01719     set exceptions {}
01720 
01721     foreach {opt value} $arglist {
01722         switch -exact $opt {
01723             to     { set component $value  }
01724             as     { set target $value     }
01725             except { set exceptions $value }
01726             default {
01727                 error "$errRoot, unknown delegation option \"$opt\""
01728             }
01729         }
01730     }
01731 
01732     if {"" == $component} {
01733         error "$errRoot, missing \"to\""
01734     }
01735 
01736     if {"*" == $option && "" != $target} {
01737         error "$errRoot, cannot specify \"as\" with \"delegate option *\""
01738     }
01739 
01740     if {"*" != $option && "" != $exceptions} {
01741         error "$errRoot, can only specify \"except\" with \"delegate option *\""
01742     }
01743 
01744     # Next, validate the option name
01745 
01746     if {"*" != $option} {
01747         if {![Comp.OptionNameIsValid $option]} {
01748             error "$errRoot, badly named option \"$option\""
01749         }
01750     }
01751 
01752     if {[Contains $option $compile(localoptions)]} {
01753         error "$errRoot, \"$option\" has been defined locally"
01754     }
01755 
01756     if {[Contains $option $compile(delegatedoptions)]} {
01757         error "$errRoot, \"$option\" is multiply delegated"
01758     }
01759 
01760     # NEXT, define the component
01761     Comp.DefineComponent $component $errRoot
01762 
01763     # Next, define the target option, if not specified.
01764     if {![string equal $option "*"] &&
01765         [string equal $target ""]} {
01766         set target $option
01767     }
01768 
01769     # NEXT, save the delegation data.
01770     set compile(hasoptions) yes
01771 
01772     if {![string equal $option "*"]} {
01773         lappend compile(delegatedoptions) $option
01774 
01775         # Next, compute the resource and class names, if they aren't
01776         # already defined.
01777 
01778         if {"" == $resourceName} {
01779             set resourceName [string range $option 1 end]
01780         }
01781 
01782         if {"" == $className} {
01783             set className [Capitalize $resourceName]
01784         }
01785 
01786         Mappend  compile(defs) {
01787             set %TYPE%::Snit_optionInfo(islocal-%OPTION%) 0
01788             set %TYPE%::Snit_optionInfo(resource-%OPTION%) %RES%
01789             set %TYPE%::Snit_optionInfo(class-%OPTION%) %CLASS%
01790             lappend %TYPE%::Snit_optionInfo(delegated) %OPTION%
01791             set %TYPE%::Snit_optionInfo(target-%OPTION%) [list %COMP% %TARGET%]
01792             lappend %TYPE%::Snit_optionInfo(delegated-%COMP%) %OPTION%
01793         }   %OPTION% $option \
01794             %COMP% $component \
01795             %TARGET% $target \
01796             %RES% $resourceName \
01797             %CLASS% $className
01798     } else {
01799         Mappend  compile(defs) {
01800             set %TYPE%::Snit_optionInfo(starcomp) %COMP%
01801             set %TYPE%::Snit_optionInfo(except) %EXCEPT%
01802         } %COMP% $component %EXCEPT% [list $exceptions]
01803     }
01804 }
01805 
01806 /*  Exposes a component, effectively making the component's command an*/
01807 /*  instance method.*/
01808 /* */
01809 /*  component     The logical name of the delegate*/
01810 /*  "as"          sugar; if not "", must be "as"*/
01811 /*  methodname    The desired method name for the component's command, or ""*/
01812 
01813 ret  ::snit::Comp.statement.expose (type component , optional "as" ="" , optional methodname ="") {
01814     variable compile
01815 
01816 
01817     # FIRST, define the component
01818     Comp.DefineComponent $component
01819 
01820     # NEXT, define the method just as though it were in the type
01821     # definition.
01822     if {[string equal $methodname ""]} {
01823         set methodname $component
01824     }
01825 
01826     Comp.statement.method $methodname args [Expand {
01827         if {[llength $args] == 0} {
01828             return $%COMPONENT%
01829         }
01830 
01831         if {[string equal $%COMPONENT% ""]} {
01832             error "undefined component \"%COMPONENT%\""
01833         }
01834 
01835 
01836         set cmd [linsert $args 0 $%COMPONENT%]
01837         return [uplevel 1 $cmd]
01838     } %COMPONENT% $component]
01839 }
01840 
01841 
01842 
01843 /* -----------------------------------------------------------------------*/
01844 /*  Public commands*/
01845 
01846 /*  Compile a type definition, and return the results as a list of two*/
01847 /*  items: the fully-qualified type name, and a script that will define*/
01848 /*  the type when executed.*/
01849 /* */
01850 /*  which       type, widget, or widgetadaptor*/
01851 /*  type          the type name*/
01852 /*  body          the type definition*/
01853 ret  ::snit::compile (type which , type type , type body) {
01854     return [Comp.Compile $which $type $body]
01855 }
01856 
01857 ret  ::snit::type (type type , type body) {
01858     return [Comp.Define [Comp.Compile type $type $body]]
01859 }
01860 
01861 ret  ::snit::widget (type type , type body) {
01862     return [Comp.Define [Comp.Compile widget $type $body]]
01863 }
01864 
01865 ret  ::snit::widgetadaptor (type type , type body) {
01866     return [Comp.Define [Comp.Compile widgetadaptor $type $body]]
01867 }
01868 
01869 ret  ::snit::typemethod (type type , type method , type arglist , type body) {
01870     # Make sure the type exists.
01871     if {![info exists ${type}::Snit_info]} {
01872         error "no such type: \"$type\""
01873     }
01874 
01875     upvar ${type}::Snit_info           Snit_info
01876     upvar ${type}::Snit_typemethodInfo Snit_typemethodInfo
01877 
01878     # FIRST, check the typemethod name against previously defined
01879     # typemethods.
01880     Comp.CheckMethodName $method 0 ${type}::Snit_typemethodInfo \
01881         "Cannot define \"$method\""
01882 
01883     # NEXT, check the arguments
01884     CheckArgs "snit::typemethod $type $method" $arglist
01885 
01886     # Next, add magic reference to type.
01887     set arglist [concat type $arglist]
01888 
01889     # Next, add typevariable declarations to body:
01890     set body "$Snit_info(tvardecs)\n$body"
01891 
01892     # Next, define it.
01893     if {[llength $method] == 1} {
01894         set Snit_typemethodInfo($method) {0 "%t::Snit_typemethod%m %t" ""}
01895         uplevel 1 [list proc ${type}::Snit_typemethod$method $arglist $body]
01896     } else {
01897         set Snit_typemethodInfo($method) {0 "%t::Snit_htypemethod%j %t" ""}
01898         set suffix [join $method _]
01899         uplevel 1 [list proc ${type}::Snit_htypemethod$suffix $arglist $body]
01900     }
01901 }
01902 
01903 ret  ::snit::method (type type , type method , type arglist , type body) {
01904     # Make sure the type exists.
01905     if {![info exists ${type}::Snit_info]} {
01906         error "no such type: \"$type\""
01907     }
01908 
01909     upvar ${type}::Snit_methodInfo  Snit_methodInfo
01910     upvar ${type}::Snit_info        Snit_info
01911 
01912     # FIRST, check the method name against previously defined
01913     # methods.
01914     Comp.CheckMethodName $method 0 ${type}::Snit_methodInfo \
01915         "Cannot define \"$method\""
01916 
01917     # NEXT, check the arguments
01918     CheckArgs "snit::method $type $method" $arglist
01919 
01920     # Next, add magic references to type and self.
01921     set arglist [concat type selfns win self $arglist]
01922 
01923     # Next, add variable declarations to body:
01924     set body "$Snit_info(tvardecs)$Snit_info(ivardecs)\n$body"
01925 
01926     # Next, define it.
01927     if {[llength $method] == 1} {
01928         set Snit_methodInfo($method) {0 "%t::Snit_method%m %t %n %w %s" ""}
01929         uplevel 1 [list proc ${type}::Snit_method$method $arglist $body]
01930     } else {
01931         set Snit_methodInfo($method) {0 "%t::Snit_hmethod%j %t %n %w %s" ""}
01932 
01933         set suffix [join $method _]
01934         uplevel 1 [list proc ${type}::Snit_hmethod$suffix $arglist $body]
01935     }
01936 }
01937 
01938 /*  Defines a proc within the compiler; this proc can call other*/
01939 /*  type definition statements, and thus can be used for meta-programming.*/
01940 ret  ::snit::macro (type name , type arglist , type body) {
01941     variable compiler
01942     variable reservedwords
01943 
01944     # FIRST, make sure the compiler is defined.
01945     Comp.Init
01946 
01947     # NEXT, check the macro name against the reserved words
01948     if {[lsearch -exact $reservedwords $name] != -1} {
01949         error "invalid macro name \"$name\""
01950     }
01951 
01952     # NEXT, see if the name has a namespace; if it does, define the
01953     # namespace.
01954     set ns [namespace qualifiers $name]
01955 
01956     if {"" != $ns} {
01957         $compiler eval "namespace eval $ns {}"
01958     }
01959 
01960     # NEXT, define the macro
01961     $compiler eval [list _proc $name $arglist $body]
01962 }
01963 
01964 /* -----------------------------------------------------------------------*/
01965 /*  Utility Functions*/
01966 /* */
01967 /*  These are utility functions used while compiling Snit types.*/
01968 
01969 /*  Builds a template from a tagged list of text blocks, then substitutes*/
01970 /*  all symbols in the mapTable, returning the expanded template.*/
01971 ret  ::snit::Expand (type template , type args) {
01972     return [string map $args $template]
01973 }
01974 
01975 /*  Expands a template and appends it to a variable.*/
01976 ret  ::snit::Mappend (type varname , type template , type args) {
01977     upvar $varname myvar
01978 
01979     append myvar [string map $args $template]
01980 }
01981 
01982 /*  Checks argument list against reserved args*/
01983 ret  ::snit::CheckArgs (type which , type arglist) {
01984     variable reservedArgs
01985 
01986     foreach name $reservedArgs {
01987         if {[Contains $name $arglist]} {
01988             error "$which's arglist may not contain \"$name\" explicitly"
01989         }
01990     }
01991 }
01992 
01993 /*  Returns 1 if a value is in a list, and 0 otherwise.*/
01994 ret  ::snit::Contains (type value , type list) {
01995     if {[lsearch -exact $list $value] != -1} {
01996         return 1
01997     } else {
01998         return 0
01999     }
02000 }
02001 
02002 /*  Capitalizes the first letter of a string.*/
02003 ret  ::snit::Capitalize (type text) {
02004     return [string toupper $text 0]
02005 }
02006 
02007 /*  Converts an arbitrary white-space-delimited string into a list*/
02008 /*  by splitting on white-space and deleting empty tokens.*/
02009 
02010 ret  ::snit::Listify (type str) {
02011     set result {}
02012     foreach token [split [string trim $str]] {
02013         if {[string length $token] > 0} {
02014             lappend result $token
02015         }
02016     }
02017 
02018     return $result
02019 }
02020 
02021 
02022 /* =======================================================================*/
02023 /*  Snit Runtime Library*/
02024 /* */
02025 /*  These are procs used by Snit types and widgets at runtime.*/
02026 
02027 /* -----------------------------------------------------------------------*/
02028 /*  Object Creation*/
02029 
02030 /*  Creates a new instance of the snit::type given its name and the args.*/
02031 /* */
02032 /*  type        The snit::type*/
02033 /*  name        The instance name*/
02034 /*  args        Args to pass to the constructor*/
02035 
02036 ret  ::snit::RT.type.typemethod.create (type type , type name , type args) {
02037     variable ${type}::Snit_info
02038     variable ${type}::Snit_optionInfo
02039 
02040     # FIRST, qualify the name.
02041     if {![string match "::*" $name]} {
02042         # Get caller's namespace;
02043         # append :: if not global namespace.
02044         set ns [uplevel 1 [list namespace current]]
02045         if {"::" != $ns} {
02046             append ns "::"
02047         }
02048 
02049         set name "$ns$name"
02050     }
02051 
02052     # NEXT, if %AUTO% appears in the name, generate a unique
02053     # command name.  Otherwise, ensure that the name isn't in use.
02054     if {[string match "*%AUTO%*" $name]} {
02055         set name [::snit::RT.UniqueName Snit_info(counter) $type $name]
02056     } elseif {!$Snit_info(canreplace) && [llength [info commands $name]]} {
02057         error "command \"$name\" already exists"
02058     }
02059 
02060     # NEXT, create the instance's namespace.
02061     set selfns \
02062         [::snit::RT.UniqueInstanceNamespace Snit_info(counter) $type]
02063     namespace eval $selfns {}
02064 
02065     # NEXT, install the dispatcher
02066     RT.MakeInstanceCommand $type $selfns $name
02067 
02068     # Initialize the options to their defaults.
02069     upvar ${selfns}::options options
02070     foreach opt $Snit_optionInfo(local) {
02071         set options($opt) $Snit_optionInfo(default-$opt)
02072     }
02073 
02074     # Initialize the instance vars to their defaults.
02075     # selfns must be defined, as it is used implicitly.
02076     ${type}::Snit_instanceVars $selfns
02077 
02078     # Execute the type's constructor.
02079     set errcode [catch {
02080         RT.ConstructInstance $type $selfns $name $args
02081     } result]
02082 
02083     if {$errcode} {
02084         global errorInfo
02085         global errorCode
02086 
02087         set theInfo $errorInfo
02088         set theCode $errorCode
02089         ::snit::RT.DestroyObject $type $selfns $name
02090         error "Error in constructor: $result" $theInfo $theCode
02091     }
02092 
02093     # NEXT, return the object's name.
02094     return $name
02095 }
02096 
02097 /*  Creates a new instance of the snit::widget or snit::widgetadaptor*/
02098 /*  given its name and the args.*/
02099 /* */
02100 /*  type        The snit::widget or snit::widgetadaptor*/
02101 /*  name        The instance name*/
02102 /*  args        Args to pass to the constructor*/
02103 
02104 ret  ::snit::RT.widget.typemethod.create (type type , type name , type args) {
02105     variable ${type}::Snit_info
02106     variable ${type}::Snit_optionInfo
02107 
02108     # FIRST, if %AUTO% appears in the name, generate a unique
02109     # command name.
02110     if {[string match "*%AUTO%*" $name]} {
02111         set name [::snit::RT.UniqueName Snit_info(counter) $type $name]
02112     }
02113 
02114     # NEXT, create the instance's namespace.
02115     set selfns \
02116         [::snit::RT.UniqueInstanceNamespace Snit_info(counter) $type]
02117     namespace eval $selfns { }
02118 
02119     # NEXT, Initialize the widget's own options to their defaults.
02120     upvar ${selfns}::options options
02121     foreach opt $Snit_optionInfo(local) {
02122         set options($opt) $Snit_optionInfo(default-$opt)
02123     }
02124 
02125     # Initialize the instance vars to their defaults.
02126     ${type}::Snit_instanceVars $selfns
02127 
02128     # NEXT, if this is a normal widget (not a widget adaptor) then create a
02129     # frame as its hull.  We set the frame's -class to the user's widgetclass,
02130     # or, if none, search for -class in the args list, otherwise default to
02131     # the basename of the $type with an initial upper case letter.
02132     if {!$Snit_info(isWidgetAdaptor)} {
02133         # FIRST, determine the class name
02134     set wclass $Snit_info(widgetclass)
02135         if {$Snit_info(widgetclass) eq ""} {
02136         set idx [lsearch -exact $args -class]
02137         if {$idx >= 0 && ($idx%2 == 0)} {
02138         # -class exists and is in the -option position
02139         set wclass [lindex $args [expr {$idx+1}]]
02140         set args [lreplace $args $idx [expr {$idx+1}]]
02141         } else {
02142         set wclass [::snit::Capitalize [namespace tail $type]]
02143         }
02144     }
02145 
02146         # NEXT, create the widget
02147         set self $name
02148         package require Tk
02149         ${type}::installhull using $Snit_info(hulltype) -class $wclass
02150 
02151         # NEXT, let's query the option database for our
02152         # widget, now that we know that it exists.
02153         foreach opt $Snit_optionInfo(local) {
02154             set dbval [RT.OptionDbGet $type $name $opt]
02155 
02156             if {"" != $dbval} {
02157                 set options($opt) $dbval
02158             }
02159         }
02160     }
02161 
02162     # Execute the type's constructor, and verify that it
02163     # has a hull.
02164     set errcode [catch {
02165         RT.ConstructInstance $type $selfns $name $args
02166 
02167         ::snit::RT.Component $type $selfns hull
02168 
02169         # Prepare to call the object's destructor when the
02170         # <Destroy> event is received.  Use a Snit-specific bindtag
02171         # so that the widget name's tag is unencumbered.
02172 
02173         bind Snit$type$name <Destroy> [::snit::Expand {
02174             ::snit::RT.DestroyObject %TYPE% %NS% %W
02175         } %TYPE% $type %NS% $selfns]
02176 
02177         # Insert the bindtag into the list of bindtags right
02178         # after the widget name.
02179         set taglist [bindtags $name]
02180         set ndx [lsearch -exact $taglist $name]
02181         incr ndx
02182         bindtags $name [linsert $taglist $ndx Snit$type$name]
02183     } result]
02184 
02185     if {$errcode} {
02186         global errorInfo
02187         global errorCode
02188 
02189         set theInfo $errorInfo
02190         set theCode $errorCode
02191         ::snit::RT.DestroyObject $type $selfns $name
02192         error "Error in constructor: $result" $theInfo $theCode
02193     }
02194 
02195     # NEXT, return the object's name.
02196     return $name
02197 }
02198 
02199 
02200 /*  RT.MakeInstanceCommand type selfns instance*/
02201 /* */
02202 /*  type        The object type*/
02203 /*  selfns      The instance namespace*/
02204 /*  instance    The instance name*/
02205 /* */
02206 /*  Creates the instance proc.*/
02207 
02208 ret  ::snit::RT.MakeInstanceCommand (type type , type selfns , type instance) {
02209     variable ${type}::Snit_info
02210 
02211     # FIRST, remember the instance name.  The Snit_instance variable
02212     # allows the instance to figure out its current name given the
02213     # instance namespace.
02214     upvar ${selfns}::Snit_instance Snit_instance
02215     set Snit_instance $instance
02216 
02217     # NEXT, qualify the proc name if it's a widget.
02218     if {$Snit_info(isWidget)} {
02219         set procname ::$instance
02220     } else {
02221         set procname $instance
02222     }
02223 
02224     # NEXT, install the new proc
02225     if {!$Snit_info(simpledispatch)} {
02226         set instanceProc $::snit::nominalInstanceProc
02227     } else {
02228         set instanceProc $::snit::simpleInstanceProc
02229     }
02230 
02231     proc $procname {method args} \
02232         [string map \
02233              [list %SELFNS% $selfns %WIN% $instance %TYPE% $type] \
02234              $instanceProc]
02235 
02236     # NEXT, add the trace.
02237     trace add command $procname {rename delete} \
02238         [list ::snit::RT.InstanceTrace $type $selfns $instance]
02239 }
02240 
02241 /*  This proc is called when the instance command is renamed.*/
02242 /*  If op is delete, then new will always be "", so op is redundant.*/
02243 /* */
02244 /*  type        The fully-qualified type name*/
02245 /*  selfns  The instance namespace*/
02246 /*  win     The original instance/tk window name.*/
02247 /*  old     old instance command name*/
02248 /*  new     new instance command name*/
02249 /*  op      rename or delete*/
02250 /* */
02251 /*  If the op is delete, we need to clean up the object; otherwise,*/
02252 /*  we need to track the change.*/
02253 /* */
02254 /*  NOTE: In Tcl 8.4.2 there's a bug: errors in rename and delete*/
02255 /*  traces aren't propagated correctly.  Instead, they silently*/
02256 /*  vanish.  Add a catch to output any error message.*/
02257 
02258 ret  ::snit::RT.InstanceTrace (type type , type selfns , type win , type old , type new , type op) {
02259     variable ${type}::Snit_info
02260 
02261     # Note to developers ...
02262     # For Tcl 8.4.0, errors thrown in trace handlers vanish silently.
02263     # Therefore we catch them here and create some output to help in
02264     # debugging such problems.
02265 
02266     if {[catch {
02267         # FIRST, clean up if necessary
02268         if {"" == $new} {
02269             if {$Snit_info(isWidget)} {
02270                 destroy $win
02271             } else {
02272                 ::snit::RT.DestroyObject $type $selfns $win
02273             }
02274         } else {
02275             # Otherwise, track the change.
02276             variable ${selfns}::Snit_instance
02277             set Snit_instance [uplevel 1 [list namespace which -command $new]]
02278 
02279             # Also, clear the instance caches, as many cached commands
02280             # might be invalid.
02281             RT.ClearInstanceCaches $selfns
02282         }
02283     } result]} {
02284         global errorInfo
02285         # Pop up the console on Windows wish, to enable stdout.
02286         # This clobbers errorInfo on unix, so save it so we can print it.
02287         set ei $errorInfo
02288         catch {console show}
02289         puts "Error in ::snit::RT.InstanceTrace $type $selfns $win $old $new $op:"
02290         puts $ei
02291     }
02292 }
02293 
02294 /*  Calls the instance constructor and handles related housekeeping.*/
02295 ret  ::snit::RT.ConstructInstance (type type , type selfns , type instance , type arglist) {
02296     variable ${type}::Snit_optionInfo
02297     variable ${selfns}::Snit_iinfo
02298 
02299     # Track whether we are constructed or not.
02300     set Snit_iinfo(constructed) 0
02301 
02302     # Call the user's constructor
02303     eval [linsert $arglist 0 \
02304               ${type}::Snit_constructor $type $selfns $instance $instance]
02305 
02306     set Snit_iinfo(constructed) 1
02307 
02308     # Validate the initial set of options (including defaults)
02309     foreach option $Snit_optionInfo(local) {
02310         set value [set ${selfns}::options($option)]
02311 
02312         if {"" != $Snit_optionInfo(typespec-$option)} {
02313             if {[catch {
02314                 $Snit_optionInfo(typeobj-$option) validate $value
02315             } result]} {
02316                 return -code error "invalid $option default: $result"
02317             }
02318         }
02319     }
02320 
02321     # Unset the configure cache for all -readonly options.
02322     # This ensures that the next time anyone tries to
02323     # configure it, an error is thrown.
02324     foreach opt $Snit_optionInfo(local) {
02325         if {$Snit_optionInfo(readonly-$opt)} {
02326             unset -nocomplain ${selfns}::Snit_configureCache($opt)
02327         }
02328     }
02329 
02330     return
02331 }
02332 
02333 /*  Returns a unique command name.*/
02334 /* */
02335 /*  REQUIRE: type is a fully qualified name.*/
02336 /*  REQUIRE: name contains "%AUTO%"*/
02337 /*  PROMISE: the returned command name is unused.*/
02338 ret  ::snit::RT.UniqueName (type countervar , type type , type name) {
02339     upvar $countervar counter
02340     while 1 {
02341         # FIRST, bump the counter and define the %AUTO% instance name;
02342         # then substitute it into the specified name.  Wrap around at
02343         # 2^31 - 2 to prevent overflow problems.
02344         incr counter
02345         if {$counter > 2147483646} {
02346             set counter 0
02347         }
02348         set auto "[namespace tail $type]$counter"
02349         set candidate [Expand $name %AUTO% $auto]
02350         if {![llength [info commands $candidate]]} {
02351             return $candidate
02352         }
02353     }
02354 }
02355 
02356 /*  Returns a unique instance namespace, fully qualified.*/
02357 /* */
02358 /*  countervar     The name of a counter variable*/
02359 /*  type           The instance's type*/
02360 /* */
02361 /*  REQUIRE: type is fully qualified*/
02362 /*  PROMISE: The returned namespace name is unused.*/
02363 
02364 ret  ::snit::RT.UniqueInstanceNamespace (type countervar , type type) {
02365     upvar $countervar counter
02366     while 1 {
02367         # FIRST, bump the counter and define the namespace name.
02368         # Then see if it already exists.  Wrap around at
02369         # 2^31 - 2 to prevent overflow problems.
02370         incr counter
02371         if {$counter > 2147483646} {
02372             set counter 0
02373         }
02374         set ins "${type}::Snit_inst${counter}"
02375         if {![namespace exists $ins]} {
02376             return $ins
02377         }
02378     }
02379 }
02380 
02381 /*  Retrieves an option's value from the option database.*/
02382 /*  Returns "" if no value is found.*/
02383 ret  ::snit::RT.OptionDbGet (type type , type self , type opt) {
02384     variable ${type}::Snit_optionInfo
02385 
02386     return [option get $self \
02387                 $Snit_optionInfo(resource-$opt) \
02388                 $Snit_optionInfo(class-$opt)]
02389 }
02390 
02391 /* -----------------------------------------------------------------------*/
02392 /*  Object Destruction*/
02393 
02394 /*  Implements the standard "destroy" method*/
02395 /* */
02396 /*  type        The snit type*/
02397 /*  selfns        The instance's instance namespace*/
02398 /*  win           The instance's original name*/
02399 /*  self          The instance's current name*/
02400 
02401 ret  ::snit::RT.method.destroy (type type , type selfns , type win , type self) {
02402     variable ${selfns}::Snit_iinfo
02403 
02404     # Can't destroy the object if it isn't complete constructed.
02405     if {!$Snit_iinfo(constructed)} {
02406         return -code error "Called 'destroy' method in constructor"
02407     }
02408 
02409     # Calls Snit_cleanup, which (among other things) calls the
02410     # user's destructor.
02411     ::snit::RT.DestroyObject $type $selfns $win
02412 }
02413 
02414 /*  This is the function that really cleans up; it's automatically*/
02415 /*  called when any instance is destroyed, e.g., by "$object destroy"*/
02416 /*  for types, and by the <Destroy> event for widgets.*/
02417 /* */
02418 /*  type        The fully-qualified type name.*/
02419 /*  selfns  The instance namespace*/
02420 /*  win     The original instance command name.*/
02421 
02422 ret  ::snit::RT.DestroyObject (type type , type selfns , type win) {
02423     variable ${type}::Snit_info
02424 
02425     # If the variable Snit_instance doesn't exist then there's no
02426     # instance command for this object -- it's most likely a
02427     # widgetadaptor. Consequently, there are some things that
02428     # we don't need to do.
02429     if {[info exists ${selfns}::Snit_instance]} {
02430         upvar ${selfns}::Snit_instance instance
02431 
02432         # First, remove the trace on the instance name, so that we
02433         # don't call RT.DestroyObject recursively.
02434         RT.RemoveInstanceTrace $type $selfns $win $instance
02435 
02436         # Next, call the user's destructor
02437         ${type}::Snit_destructor $type $selfns $win $instance
02438 
02439         # Next, if this isn't a widget, delete the instance command.
02440         # If it is a widget, get the hull component's name, and rename
02441         # it back to the widget name
02442 
02443         # Next, delete the hull component's instance command,
02444         # if there is one.
02445         if {$Snit_info(isWidget)} {
02446             set hullcmd [::snit::RT.Component $type $selfns hull]
02447 
02448             catch {rename $instance ""}
02449 
02450             # Clear the bind event
02451             bind Snit$type$win <Destroy> ""
02452 
02453             if {[llength [info commands $hullcmd]]} {
02454                 # FIRST, rename the hull back to its original name.
02455                 # If the hull is itself a megawidget, it will have its
02456                 # own cleanup to do, and it might not do it properly
02457                 # if it doesn't have the right name.
02458                 rename $hullcmd ::$instance
02459 
02460                 # NEXT, destroy it.
02461                 destroy $instance
02462             }
02463         } else {
02464             catch {rename $instance ""}
02465         }
02466     }
02467 
02468     # Next, delete the instance's namespace.  This kills any
02469     # instance variables.
02470     namespace delete $selfns
02471 
02472     return
02473 }
02474 
02475 /*  Remove instance trace*/
02476 /* */
02477 /*  type           The fully qualified type name*/
02478 /*  selfns         The instance namespace*/
02479 /*  win            The original instance name/Tk window name*/
02480 /*  instance       The current instance name*/
02481 
02482 ret  ::snit::RT.RemoveInstanceTrace (type type , type selfns , type win , type instance) {
02483     variable ${type}::Snit_info
02484 
02485     if {$Snit_info(isWidget)} {
02486         set procname ::$instance
02487     } else {
02488         set procname $instance
02489     }
02490 
02491     # NEXT, remove any trace on this name
02492     catch {
02493         trace remove command $procname {rename delete} \
02494             [list ::snit::RT.InstanceTrace $type $selfns $win]
02495     }
02496 }
02497 
02498 /* -----------------------------------------------------------------------*/
02499 /*  Typecomponent Management and Method Caching*/
02500 
02501 /*  Typecomponent trace; used for write trace on typecomponent*/
02502 /*  variables.  Saves the new component object name, provided*/
02503 /*  that certain conditions are met.  Also clears the typemethod*/
02504 /*  cache.*/
02505 
02506 ret  ::snit::RT.TypecomponentTrace (type type , type component , type n1 , type n2 , type op) {
02507     upvar ${type}::Snit_info Snit_info
02508     upvar ${type}::${component} cvar
02509     upvar ${type}::Snit_typecomponents Snit_typecomponents
02510 
02511     # Save the new component value.
02512     set Snit_typecomponents($component) $cvar
02513 
02514     # Clear the typemethod cache.
02515     # TBD: can we unset just the elements related to
02516     # this component?
02517     unset -nocomplain -- ${type}::Snit_typemethodCache
02518 }
02519 
02520 /*  Generates and caches the command for a typemethod.*/
02521 /* */
02522 /*  type        The type*/
02523 /*  method  The name of the typemethod to call.*/
02524 /* */
02525 /*  The return value is one of the following lists:*/
02526 /* */
02527 /*     {}              There's no such method.*/
02528 /*     {1}             The method has submethods; look again.*/
02529 /*     {0 <command>}   Here's the command to execute.*/
02530 
02531 ret  snit::RT.CacheTypemethodCommand (type type , type method) {
02532     upvar ${type}::Snit_typemethodInfo  Snit_typemethodInfo
02533     upvar ${type}::Snit_typecomponents  Snit_typecomponents
02534     upvar ${type}::Snit_typemethodCache Snit_typemethodCache
02535     upvar ${type}::Snit_info            Snit_info
02536 
02537     # FIRST, get the pattern data and the typecomponent name.
02538     set implicitCreate 0
02539     set instanceName ""
02540 
02541     set starredMethod [lreplace $method end end *]
02542     set methodTail [lindex $method end]
02543 
02544     if {[info exists Snit_typemethodInfo($method)]} {
02545         set key $method
02546     } elseif {[info exists Snit_typemethodInfo($starredMethod)]} {
02547         if {[lsearch -exact $Snit_info(excepttypemethods) $methodTail] == -1} {
02548             set key $starredMethod
02549         } else {
02550             return [list ]
02551         }
02552     } elseif {$Snit_info(hasinstances)} {
02553         # Assume the unknown name is an instance name to create, unless
02554         # this is a widget and the style of the name is wrong, or the
02555         # name mimics a standard typemethod.
02556 
02557         if {[set ${type}::Snit_info(isWidget)] &&
02558             ![string match ".*" $method]} {
02559             return [list ]
02560         }
02561 
02562         # Without this check, the call "$type info" will redefine the
02563         # standard "::info" command, with disastrous results.  Since it's
02564         # a likely thing to do if !-typeinfo, put in an explicit check.
02565         if {"info" == $method || "destroy" == $method} {
02566             return [list ]
02567         }
02568 
02569         set implicitCreate 1
02570         set instanceName $method
02571         set key create
02572         set method create
02573     } else {
02574         return [list ]
02575     }
02576 
02577     foreach {flag pattern compName} $Snit_typemethodInfo($key) {}
02578 
02579     if {$flag == 1} {
02580         return [list 1]
02581     }
02582 
02583     # NEXT, build the substitution list
02584     set subList [list \
02585                      %% % \
02586                      %t $type \
02587                      %M $method \
02588                      %m [lindex $method end] \
02589                      %j [join $method _]]
02590 
02591     if {"" != $compName} {
02592         if {![info exists Snit_typecomponents($compName)]} {
02593             error "$type delegates typemethod \"$method\" to undefined typecomponent \"$compName\""
02594         }
02595 
02596         lappend subList %c [list $Snit_typecomponents($compName)]
02597     }
02598 
02599     set command {}
02600 
02601     foreach subpattern $pattern {
02602         lappend command [string map $subList $subpattern]
02603     }
02604 
02605     if {$implicitCreate} {
02606         # In this case, $method is the name of the instance to
02607         # create.  Don't cache, as we usually won't do this one
02608         # again.
02609         lappend command $instanceName
02610     } else {
02611         set Snit_typemethodCache($method) [list 0 $command]
02612     }
02613 
02614     return [list 0 $command]
02615 }
02616 
02617 
02618 /* -----------------------------------------------------------------------*/
02619 /*  Component Management and Method Caching*/
02620 
02621 /*  Retrieves the object name given the component name.*/
02622 ret  ::snit::RT.Component (type type , type selfns , type name) {
02623     variable ${selfns}::Snit_components
02624 
02625     if {[catch {set Snit_components($name)} result]} {
02626         variable ${selfns}::Snit_instance
02627 
02628         error "component \"$name\" is undefined in $type $Snit_instance"
02629     }
02630 
02631     return $result
02632 }
02633 
02634 /*  Component trace; used for write trace on component instance*/
02635 /*  variables.  Saves the new component object name, provided*/
02636 /*  that certain conditions are met.  Also clears the method*/
02637 /*  cache.*/
02638 
02639 ret  ::snit::RT.ComponentTrace (type type , type selfns , type component , type n1 , type n2 , type op) {
02640     upvar ${type}::Snit_info Snit_info
02641     upvar ${selfns}::${component} cvar
02642     upvar ${selfns}::Snit_components Snit_components
02643 
02644     # If they try to redefine the hull component after
02645     # it's been defined, that's an error--but only if
02646     # this is a widget or widget adaptor.
02647     if {"hull" == $component &&
02648         $Snit_info(isWidget) &&
02649         [info exists Snit_components($component)]} {
02650         set cvar $Snit_components($component)
02651         error "The hull component cannot be redefined"
02652     }
02653 
02654     # Save the new component value.
02655     set Snit_components($component) $cvar
02656 
02657     # Clear the instance caches.
02658     # TBD: can we unset just the elements related to
02659     # this component?
02660     RT.ClearInstanceCaches $selfns
02661 }
02662 
02663 /*  Generates and caches the command for a method.*/
02664 /* */
02665 /*  type:       The instance's type*/
02666 /*  selfns: The instance's private namespace*/
02667 /*  win:          The instance's original name (a Tk widget name, for*/
02668 /*                snit::widgets.*/
02669 /*  self:         The instance's current name.*/
02670 /*  method: The name of the method to call.*/
02671 /* */
02672 /*  The return value is one of the following lists:*/
02673 /* */
02674 /*     {}              There's no such method.*/
02675 /*     {1}             The method has submethods; look again.*/
02676 /*     {0 <command>}   Here's the command to execute.*/
02677 
02678 ret  ::snit::RT.CacheMethodCommand (type type , type selfns , type win , type self , type method) {
02679     variable ${type}::Snit_info
02680     variable ${type}::Snit_methodInfo
02681     variable ${type}::Snit_typecomponents
02682     variable ${selfns}::Snit_components
02683     variable ${selfns}::Snit_methodCache
02684 
02685     # FIRST, get the pattern data and the component name.
02686     set starredMethod [lreplace $method end end *]
02687     set methodTail [lindex $method end]
02688 
02689     if {[info exists Snit_methodInfo($method)]} {
02690         set key $method
02691     } elseif {[info exists Snit_methodInfo($starredMethod)] &&
02692               [lsearch -exact $Snit_info(exceptmethods) $methodTail] == -1} {
02693         set key $starredMethod
02694     } else {
02695         return [list ]
02696     }
02697 
02698     foreach {flag pattern compName} $Snit_methodInfo($key) {}
02699 
02700     if {$flag == 1} {
02701         return [list 1]
02702     }
02703 
02704     # NEXT, build the substitution list
02705     set subList [list \
02706                      %% % \
02707                      %t $type \
02708                      %M $method \
02709                      %m [lindex $method end] \
02710                      %j [join $method _] \
02711                      %n [list $selfns] \
02712                      %w [list $win] \
02713                      %s [list $self]]
02714 
02715     if {"" != $compName} {
02716         if {[info exists Snit_components($compName)]} {
02717             set compCmd $Snit_components($compName)
02718         } elseif {[info exists Snit_typecomponents($compName)]} {
02719             set compCmd $Snit_typecomponents($compName)
02720         } else {
02721             error "$type $self delegates method \"$method\" to undefined component \"$compName\""
02722         }
02723 
02724         lappend subList %c [list $compCmd]
02725     }
02726 
02727     # Note: The cached command will executed faster if it's
02728     # already a list.
02729     set command {}
02730 
02731     foreach subpattern $pattern {
02732         lappend command [string map $subList $subpattern]
02733     }
02734 
02735     set commandRec [list 0 $command]
02736 
02737     set Snit_methodCache($method) $commandRec
02738 
02739     return $commandRec
02740 }
02741 
02742 
02743 /*  Looks up a method's command.*/
02744 /* */
02745 /*  type:       The instance's type*/
02746 /*  selfns: The instance's private namespace*/
02747 /*  win:          The instance's original name (a Tk widget name, for*/
02748 /*                snit::widgets.*/
02749 /*  self:         The instance's current name.*/
02750 /*  method: The name of the method to call.*/
02751 /*  errPrefix:    Prefix for any error method*/
02752 ret  ::snit::RT.LookupMethodCommand (type type , type selfns , type win , type self , type method , type errPrefix) {
02753     set commandRec [snit::RT.CacheMethodCommand \
02754                         $type $selfns $win $self \
02755                         $method]
02756 
02757 
02758     if {[llength $commandRec] == 0} {
02759         return -code error \
02760             "$errPrefix, \"$self $method\" is not defined"
02761     } elseif {[lindex $commandRec 0] == 1} {
02762         return -code error \
02763             "$errPrefix, wrong number args: should be \"$self\" $method method args"
02764     }
02765 
02766     return  [lindex $commandRec 1]
02767 }
02768 
02769 
02770 /*  Clears all instance command caches*/
02771 ret  ::snit::RT.ClearInstanceCaches (type selfns) {
02772     unset -nocomplain -- ${selfns}::Snit_methodCache
02773     unset -nocomplain -- ${selfns}::Snit_cgetCache
02774     unset -nocomplain -- ${selfns}::Snit_configureCache
02775     unset -nocomplain -- ${selfns}::Snit_validateCache
02776 }
02777 
02778 
02779 /* -----------------------------------------------------------------------*/
02780 /*  Component Installation*/
02781 
02782 /*  Implements %TYPE%::installhull.  The variables self and selfns*/
02783 /*  must be defined in the caller's context.*/
02784 /* */
02785 /*  Installs the named widget as the hull of a*/
02786 /*  widgetadaptor.  Once the widget is hijacked, its new name*/
02787 /*  is assigned to the hull component.*/
02788 
02789 ret  ::snit::RT.installhull (type type , optional using ="using" , optional widgetType ="" , type args) {
02790     variable ${type}::Snit_info
02791     variable ${type}::Snit_optionInfo
02792     upvar self self
02793     upvar selfns selfns
02794     upvar ${selfns}::hull hull
02795     upvar ${selfns}::options options
02796 
02797     # FIRST, make sure we can do it.
02798     if {!$Snit_info(isWidget)} {
02799         error "installhull is valid only for snit::widgetadaptors"
02800     }
02801 
02802     if {[info exists ${selfns}::Snit_instance]} {
02803         error "hull already installed for $type $self"
02804     }
02805 
02806     # NEXT, has it been created yet?  If not, create it using
02807     # the specified arguments.
02808     if {"using" == $using} {
02809         # FIRST, create the widget
02810         set cmd [linsert $args 0 $widgetType $self]
02811         set obj [uplevel 1 $cmd]
02812 
02813         # NEXT, for each option explicitly delegated to the hull
02814         # that doesn't appear in the usedOpts list, get the
02815         # option database value and apply it--provided that the
02816         # real option name and the target option name are different.
02817         # (If they are the same, then the option database was
02818         # already queried as part of the normal widget creation.)
02819         #
02820         # Also, we don't need to worry about implicitly delegated
02821         # options, as the option and target option names must be
02822         # the same.
02823         if {[info exists Snit_optionInfo(delegated-hull)]} {
02824 
02825             # FIRST, extract all option names from args
02826             set usedOpts {}
02827             set ndx [lsearch -glob $args "-*"]
02828             foreach {opt val} [lrange $args $ndx end] {
02829                 lappend usedOpts $opt
02830             }
02831 
02832             foreach opt $Snit_optionInfo(delegated-hull) {
02833                 set target [lindex $Snit_optionInfo(target-$opt) 1]
02834 
02835                 if {"$target" == $opt} {
02836                     continue
02837                 }
02838 
02839                 set result [lsearch -exact $usedOpts $target]
02840 
02841                 if {$result != -1} {
02842                     continue
02843                 }
02844 
02845                 set dbval [RT.OptionDbGet $type $self $opt]
02846                 $obj configure $target $dbval
02847             }
02848         }
02849     } else {
02850         set obj $using
02851 
02852         if {![string equal $obj $self]} {
02853             error \
02854                 "hull name mismatch: \"$obj\" != \"$self\""
02855         }
02856     }
02857 
02858     # NEXT, get the local option defaults.
02859     foreach opt $Snit_optionInfo(local) {
02860         set dbval [RT.OptionDbGet $type $self $opt]
02861 
02862         if {"" != $dbval} {
02863             set options($opt) $dbval
02864         }
02865     }
02866 
02867 
02868     # NEXT, do the magic
02869     set i 0
02870     while 1 {
02871         incr i
02872         set newName "::hull${i}$self"
02873         if {![llength [info commands $newName]]} {
02874             break
02875         }
02876     }
02877 
02878     rename ::$self $newName
02879     RT.MakeInstanceCommand $type $selfns $self
02880 
02881     # Note: this relies on RT.ComponentTrace to do the dirty work.
02882     set hull $newName
02883 
02884     return
02885 }
02886 
02887 /*  Implements %TYPE%::install.*/
02888 /* */
02889 /*  Creates a widget and installs it as the named component.*/
02890 /*  It expects self and selfns to be defined in the caller's context.*/
02891 
02892 ret  ::snit::RT.install (type type , type compName ", type using" , type widgetType , type winPath , type args) {
02893     variable ${type}::Snit_optionInfo
02894     variable ${type}::Snit_info
02895     upvar self self
02896     upvar selfns selfns
02897     upvar ${selfns}::$compName comp
02898     upvar ${selfns}::hull hull
02899 
02900     # We do the magic option database stuff only if $self is
02901     # a widget.
02902     if {$Snit_info(isWidget)} {
02903         if {"" == $hull} {
02904             error "tried to install \"$compName\" before the hull exists"
02905         }
02906 
02907         # FIRST, query the option database and save the results
02908         # into args.  Insert them before the first option in the
02909         # list, in case there are any non-standard parameters.
02910         #
02911         # Note: there might not be any delegated options; if so,
02912         # don't bother.
02913 
02914         if {[info exists Snit_optionInfo(delegated-$compName)]} {
02915             set ndx [lsearch -glob $args "-*"]
02916 
02917             foreach opt $Snit_optionInfo(delegated-$compName) {
02918                 set dbval [RT.OptionDbGet $type $self $opt]
02919 
02920                 if {"" != $dbval} {
02921                     set target [lindex $Snit_optionInfo(target-$opt) 1]
02922                     set args [linsert $args $ndx $target $dbval]
02923                 }
02924             }
02925         }
02926     }
02927 
02928     # NEXT, create the component and save it.
02929     set cmd [concat [list $widgetType $winPath] $args]
02930     set comp [uplevel 1 $cmd]
02931 
02932     # NEXT, handle the option database for "delegate option *",
02933     # in widgets only.
02934     if {$Snit_info(isWidget) && [string equal $Snit_optionInfo(starcomp) $compName]} {
02935         # FIRST, get the list of option specs from the widget.
02936         # If configure doesn't work, skip it.
02937         if {[catch {$comp configure} specs]} {
02938             return
02939         }
02940 
02941         # NEXT, get the set of explicitly used options from args
02942         set usedOpts {}
02943         set ndx [lsearch -glob $args "-*"]
02944         foreach {opt val} [lrange $args $ndx end] {
02945             lappend usedOpts $opt
02946         }
02947 
02948         # NEXT, "delegate option *" matches all options defined
02949         # by this widget that aren't defined by the widget as a whole,
02950         # and that aren't excepted.  Plus, we skip usedOpts.  So build
02951         # a list of the options it can't match.
02952         set skiplist [concat \
02953                           $usedOpts \
02954                           $Snit_optionInfo(except) \
02955                           $Snit_optionInfo(local) \
02956                           $Snit_optionInfo(delegated)]
02957 
02958         # NEXT, loop over all of the component's options, and set
02959         # any not in the skip list for which there is an option
02960         # database value.
02961         foreach spec $specs {
02962             # Skip aliases
02963             if {[llength $spec] != 5} {
02964                 continue
02965             }
02966 
02967             set opt [lindex $spec 0]
02968 
02969             if {[lsearch -exact $skiplist $opt] != -1} {
02970                 continue
02971             }
02972 
02973             set res [lindex $spec 1]
02974             set cls [lindex $spec 2]
02975 
02976             set dbvalue [option get $self $res $cls]
02977 
02978             if {"" != $dbvalue} {
02979                 $comp configure $opt $dbvalue
02980             }
02981         }
02982     }
02983 
02984     return
02985 }
02986 
02987 
02988 /* -----------------------------------------------------------------------*/
02989 /*  Method/Variable Name Qualification*/
02990 
02991 /*  Implements %TYPE%::variable.  Requires selfns.*/
02992 ret  ::snit::RT.variable (type varname) {
02993     upvar selfns selfns
02994 
02995     if {![string match "::*" $varname]} {
02996         uplevel 1 [list upvar 1 ${selfns}::$varname $varname]
02997     } else {
02998         # varname is fully qualified; let the standard
02999         # "variable" command handle it.
03000         uplevel 1 [list ::variable $varname]
03001     }
03002 }
03003 
03004 /*  Fully qualifies a typevariable name.*/
03005 /* */
03006 /*  This is used to implement the mytypevar command.*/
03007 
03008 ret  ::snit::RT.mytypevar (type type , type name) {
03009     return ${type}::$name
03010 }
03011 
03012 /*  Fully qualifies an instance variable name.*/
03013 /* */
03014 /*  This is used to implement the myvar command.*/
03015 ret  ::snit::RT.myvar (type name) {
03016     upvar selfns selfns
03017     return ${selfns}::$name
03018 }
03019 
03020 /*  Use this like "list" to convert a proc call into a command*/
03021 /*  string to pass to another object (e.g., as a -command).*/
03022 /*  Qualifies the proc name properly.*/
03023 /* */
03024 /*  This is used to implement the "myproc" command.*/
03025 
03026 ret  ::snit::RT.myproc (type type , type procname , type args) {
03027     set procname "${type}::$procname"
03028     return [linsert $args 0 $procname]
03029 }
03030 
03031 /*  DEPRECATED*/
03032 ret  ::snit::RT.codename (type type , type name) {
03033     return "${type}::$name"
03034 }
03035 
03036 /*  Use this like "list" to convert a typemethod call into a command*/
03037 /*  string to pass to another object (e.g., as a -command).*/
03038 /*  Inserts the type command at the beginning.*/
03039 /* */
03040 /*  This is used to implement the "mytypemethod" command.*/
03041 
03042 ret  ::snit::RT.mytypemethod (type type , type args) {
03043     return [linsert $args 0 $type]
03044 }
03045 
03046 /*  Use this like "list" to convert a method call into a command*/
03047 /*  string to pass to another object (e.g., as a -command).*/
03048 /*  Inserts the code at the beginning to call the right object, even if*/
03049 /*  the object's name has changed.  Requires that selfns be defined*/
03050 /*  in the calling context, eg. can only be called in instance*/
03051 /*  code.*/
03052 /* */
03053 /*  This is used to implement the "mymethod" command.*/
03054 
03055 ret  ::snit::RT.mymethod (type args) {
03056     upvar selfns selfns
03057     return [linsert $args 0 ::snit::RT.CallInstance ${selfns}]
03058 }
03059 
03060 /*  Calls an instance method for an object given its*/
03061 /*  instance namespace and remaining arguments (the first of which*/
03062 /*  will be the method name.*/
03063 /* */
03064 /*  selfns      The instance namespace*/
03065 /*  args            The arguments*/
03066 /* */
03067 /*  Uses the selfns to determine $self, and calls the method*/
03068 /*  in the normal way.*/
03069 /* */
03070 /*  This is used to implement the "mymethod" command.*/
03071 
03072 ret  ::snit::RT.CallInstance (type selfns , type args) {
03073     upvar ${selfns}::Snit_instance self
03074 
03075     set retval [catch {uplevel 1 [linsert $args 0 $self]} result]
03076 
03077     if {$retval} {
03078         if {$retval == 1} {
03079             global errorInfo
03080             global errorCode
03081             return -code error -errorinfo $errorInfo \
03082                 -errorcode $errorCode $result
03083         } else {
03084             return -code $retval $result
03085         }
03086     }
03087 
03088     return $result
03089 }
03090 
03091 /*  Looks for the named option in the named variable.  If found,*/
03092 /*  it and its value are removed from the list, and the value*/
03093 /*  is returned.  Otherwise, the default value is returned.*/
03094 /*  If the option is undelegated, it's own default value will be*/
03095 /*  used if none is specified.*/
03096 /* */
03097 /*  Implements the "from" command.*/
03098 
03099 ret  ::snit::RT.from (type type , type argvName , type option , optional defvalue ="") {
03100     variable ${type}::Snit_optionInfo
03101     upvar $argvName argv
03102 
03103     set ioption [lsearch -exact $argv $option]
03104 
03105     if {$ioption == -1} {
03106         if {"" == $defvalue &&
03107             [info exists Snit_optionInfo(default-$option)]} {
03108             return $Snit_optionInfo(default-$option)
03109         } else {
03110             return $defvalue
03111         }
03112     }
03113 
03114     set ivalue [expr {$ioption + 1}]
03115     set value [lindex $argv $ivalue]
03116 
03117     set argv [lreplace $argv $ioption $ivalue]
03118 
03119     return $value
03120 }
03121 
03122 /* -----------------------------------------------------------------------*/
03123 /*  Type Destruction*/
03124 
03125 /*  Implements the standard "destroy" typemethod:*/
03126 /*  Destroys a type completely.*/
03127 /* */
03128 /*  type        The snit type*/
03129 
03130 ret  ::snit::RT.typemethod.destroy (type type) {
03131     variable ${type}::Snit_info
03132 
03133     # FIRST, destroy all instances
03134     foreach selfns [namespace children $type] {
03135         if {![namespace exists $selfns]} {
03136             continue
03137         }
03138         upvar ${selfns}::Snit_instance obj
03139 
03140         if {$Snit_info(isWidget)} {
03141             destroy $obj
03142         } else {
03143             if {[llength [info commands $obj]]} {
03144                 $obj destroy
03145             }
03146         }
03147     }
03148 
03149     # NEXT, destroy the type's data.
03150     namespace delete $type
03151 
03152     # NEXT, get rid of the type command.
03153     rename $type ""
03154 }
03155 
03156 
03157 
03158 /* -----------------------------------------------------------------------*/
03159 /*  Option Handling*/
03160 
03161 /*  Implements the standard "cget" method*/
03162 /* */
03163 /*  type        The snit type*/
03164 /*  selfns        The instance's instance namespace*/
03165 /*  win           The instance's original name*/
03166 /*  self          The instance's current name*/
03167 /*  option        The name of the option*/
03168 
03169 ret  ::snit::RT.method.cget (type type , type selfns , type win , type self , type option) {
03170     if {[catch {set ${selfns}::Snit_cgetCache($option)} command]} {
03171         set command [snit::RT.CacheCgetCommand $type $selfns $win $self $option]
03172 
03173         if {[llength $command] == 0} {
03174             return -code error "unknown option \"$option\""
03175         }
03176     }
03177 
03178     uplevel 1 $command
03179 }
03180 
03181 /*  Retrieves and caches the command that implements "cget" for the*/
03182 /*  specified option.*/
03183 /* */
03184 /*  type        The snit type*/
03185 /*  selfns        The instance's instance namespace*/
03186 /*  win           The instance's original name*/
03187 /*  self          The instance's current name*/
03188 /*  option        The name of the option*/
03189 
03190 ret  ::snit::RT.CacheCgetCommand (type type , type selfns , type win , type self , type option) {
03191     variable ${type}::Snit_optionInfo
03192     variable ${selfns}::Snit_cgetCache
03193 
03194     if {[info exists Snit_optionInfo(islocal-$option)]} {
03195         # We know the item; it's either local, or explicitly delegated.
03196         if {$Snit_optionInfo(islocal-$option)} {
03197             # It's a local option.  If it has a cget method defined,
03198             # use it; otherwise just return the value.
03199 
03200             if {"" == $Snit_optionInfo(cget-$option)} {
03201                 set command [list set ${selfns}::options($option)]
03202             } else {
03203                 set command [snit::RT.LookupMethodCommand \
03204                                  $type $selfns $win $self \
03205                                  $Snit_optionInfo(cget-$option) \
03206                                  "can't cget $option"]
03207 
03208                 lappend command $option
03209             }
03210 
03211             set Snit_cgetCache($option) $command
03212             return $command
03213         }
03214 
03215         # Explicitly delegated option; get target
03216         set comp [lindex $Snit_optionInfo(target-$option) 0]
03217         set target [lindex $Snit_optionInfo(target-$option) 1]
03218     } elseif {"" != $Snit_optionInfo(starcomp) &&
03219               [lsearch -exact $Snit_optionInfo(except) $option] == -1} {
03220         # Unknown option, but unknowns are delegated; get target.
03221         set comp $Snit_optionInfo(starcomp)
03222         set target $option
03223     } else {
03224         return ""
03225     }
03226 
03227     # Get the component's object.
03228     set obj [RT.Component $type $selfns $comp]
03229 
03230     set command [list $obj cget $target]
03231     set Snit_cgetCache($option) $command
03232 
03233     return $command
03234 }
03235 
03236 /*  Implements the standard "configurelist" method*/
03237 /* */
03238 /*  type        The snit type*/
03239 /*  selfns        The instance's instance namespace*/
03240 /*  win           The instance's original name*/
03241 /*  self          The instance's current name*/
03242 /*  optionlist    A list of options and their values.*/
03243 
03244 ret  ::snit::RT.method.configurelist (type type , type selfns , type win , type self , type optionlist) {
03245     variable ${type}::Snit_optionInfo
03246 
03247     foreach {option value} $optionlist {
03248         # FIRST, get the configure command, caching it if need be.
03249         if {[catch {set ${selfns}::Snit_configureCache($option)} command]} {
03250             set command [snit::RT.CacheConfigureCommand \
03251                              $type $selfns $win $self $option]
03252 
03253             if {[llength $command] == 0} {
03254                 return -code error "unknown option \"$option\""
03255             }
03256         }
03257 
03258         # NEXT, if we have a type-validation object, use it.
03259         # TBD: Should test (islocal-$option) here, but islocal
03260         # isn't defined for implicitly delegated options.
03261         if {[info exists Snit_optionInfo(typeobj-$option)]
03262             && "" != $Snit_optionInfo(typeobj-$option)} {
03263             if {[catch {
03264                 $Snit_optionInfo(typeobj-$option) validate $value
03265             } result]} {
03266                 return -code error "invalid $option value: $result"
03267             }
03268         }
03269 
03270         # NEXT, the caching the configure command also cached the
03271         # validate command, if any.  If we have one, run it.
03272         set valcommand [set ${selfns}::Snit_validateCache($option)]
03273 
03274         if {[llength $valcommand]} {
03275             lappend valcommand $value
03276             uplevel 1 $valcommand
03277         }
03278 
03279         # NEXT, configure the option with the value.
03280         lappend command $value
03281         uplevel 1 $command
03282     }
03283 
03284     return
03285 }
03286 
03287 /*  Retrieves and caches the command that stores the named option.*/
03288 /*  Also stores the command that validates the name option if any;*/
03289 /*  If none, the validate command is "", so that the cache is always*/
03290 /*  populated.*/
03291 /* */
03292 /*  type        The snit type*/
03293 /*  selfns        The instance's instance namespace*/
03294 /*  win           The instance's original name*/
03295 /*  self          The instance's current name*/
03296 /*  option        An option name*/
03297 
03298 ret  ::snit::RT.CacheConfigureCommand (type type , type selfns , type win , type self , type option) {
03299     variable ${type}::Snit_optionInfo
03300     variable ${selfns}::Snit_configureCache
03301     variable ${selfns}::Snit_validateCache
03302 
03303     if {[info exist Snit_optionInfo(islocal-$option)]} {
03304         # We know the item; it's either local, or explicitly delegated.
03305 
03306         if {$Snit_optionInfo(islocal-$option)} {
03307             # It's a local option.
03308 
03309             # If it's readonly, it throws an error if we're already
03310             # constructed.
03311             if {$Snit_optionInfo(readonly-$option)} {
03312                 if {[set ${selfns}::Snit_iinfo(constructed)]} {
03313                     error "option $option can only be set at instance creation"
03314                 }
03315             }
03316 
03317             # If it has a validate method, cache that for later.
03318             if {"" != $Snit_optionInfo(validate-$option)} {
03319                 set command [snit::RT.LookupMethodCommand \
03320                                  $type $selfns $win $self \
03321                                  $Snit_optionInfo(validate-$option) \
03322                                  "can't validate $option"]
03323 
03324                 lappend command $option
03325                 set Snit_validateCache($option) $command
03326             } else {
03327                 set Snit_validateCache($option) ""
03328             }
03329 
03330             # If it has a configure method defined,
03331             # cache it; otherwise, just set the value.
03332 
03333             if {"" == $Snit_optionInfo(configure-$option)} {
03334                 set command [list set ${selfns}::options($option)]
03335             } else {
03336                 set command [snit::RT.LookupMethodCommand \
03337                                  $type $selfns $win $self \
03338                                  $Snit_optionInfo(configure-$option) \
03339                                  "can't configure $option"]
03340 
03341                 lappend command $option
03342             }
03343 
03344             set Snit_configureCache($option) $command
03345             return $command
03346         }
03347 
03348         # Delegated option: get target.
03349         set comp [lindex $Snit_optionInfo(target-$option) 0]
03350         set target [lindex $Snit_optionInfo(target-$option) 1]
03351     } elseif {$Snit_optionInfo(starcomp) != "" &&
03352               [lsearch -exact $Snit_optionInfo(except) $option] == -1} {
03353         # Unknown option, but unknowns are delegated.
03354         set comp $Snit_optionInfo(starcomp)
03355         set target $option
03356     } else {
03357         return ""
03358     }
03359 
03360     # There is no validate command in this case; save an empty string.
03361     set Snit_validateCache($option) ""
03362 
03363     # Get the component's object
03364     set obj [RT.Component $type $selfns $comp]
03365 
03366     set command [list $obj configure $target]
03367     set Snit_configureCache($option) $command
03368 
03369     return $command
03370 }
03371 
03372 /*  Implements the standard "configure" method*/
03373 /* */
03374 /*  type        The snit type*/
03375 /*  selfns        The instance's instance namespace*/
03376 /*  win           The instance's original name*/
03377 /*  self          The instance's current name*/
03378 /*  args          A list of options and their values, possibly empty.*/
03379 
03380 ret  ::snit::RT.method.configure (type type , type selfns , type win , type self , type args) {
03381     # If two or more arguments, set values as usual.
03382     if {[llength $args] >= 2} {
03383         ::snit::RT.method.configurelist $type $selfns $win $self $args
03384         return
03385     }
03386 
03387     # If zero arguments, acquire data for each known option
03388     # and return the list
03389     if {[llength $args] == 0} {
03390         set result {}
03391         foreach opt [RT.method.info.options $type $selfns $win $self] {
03392             # Refactor this, so that we don't need to call via $self.
03393             lappend result [RT.GetOptionDbSpec \
03394                                 $type $selfns $win $self $opt]
03395         }
03396 
03397         return $result
03398     }
03399 
03400     # They want it for just one.
03401     set opt [lindex $args 0]
03402 
03403     return [RT.GetOptionDbSpec $type $selfns $win $self $opt]
03404 }
03405 
03406 
03407 /*  Retrieves the option database spec for a single option.*/
03408 /* */
03409 /*  type        The snit type*/
03410 /*  selfns        The instance's instance namespace*/
03411 /*  win           The instance's original name*/
03412 /*  self          The instance's current name*/
03413 /*  option        The name of an option*/
03414 /* */
03415 /*  TBD: This is a bad name.  What it's returning is the*/
03416 /*  result of the configure query.*/
03417 
03418 ret  ::snit::RT.GetOptionDbSpec (type type , type selfns , type win , type self , type opt) {
03419     variable ${type}::Snit_optionInfo
03420 
03421     upvar ${selfns}::Snit_components Snit_components
03422     upvar ${selfns}::options         options
03423 
03424     if {[info exists options($opt)]} {
03425         # This is a locally-defined option.  Just build the
03426         # list and return it.
03427         set res $Snit_optionInfo(resource-$opt)
03428         set cls $Snit_optionInfo(class-$opt)
03429         set def $Snit_optionInfo(default-$opt)
03430 
03431         return [list $opt $res $cls $def \
03432                     [RT.method.cget $type $selfns $win $self $opt]]
03433     } elseif {[info exists Snit_optionInfo(target-$opt)]} {
03434         # This is an explicitly delegated option.  The only
03435         # thing we don't have is the default.
03436         set res $Snit_optionInfo(resource-$opt)
03437         set cls $Snit_optionInfo(class-$opt)
03438 
03439         # Get the default
03440         set logicalName [lindex $Snit_optionInfo(target-$opt) 0]
03441         set comp $Snit_components($logicalName)
03442         set target [lindex $Snit_optionInfo(target-$opt) 1]
03443 
03444         if {[catch {$comp configure $target} result]} {
03445             set defValue {}
03446         } else {
03447             set defValue [lindex $result 3]
03448         }
03449 
03450         return [list $opt $res $cls $defValue [$self cget $opt]]
03451     } elseif {"" != $Snit_optionInfo(starcomp) &&
03452               [lsearch -exact $Snit_optionInfo(except) $opt] == -1} {
03453         set logicalName $Snit_optionInfo(starcomp)
03454         set target $opt
03455         set comp $Snit_components($logicalName)
03456 
03457         if {[catch {set value [$comp cget $target]} result]} {
03458             error "unknown option \"$opt\""
03459         }
03460 
03461         if {![catch {$comp configure $target} result]} {
03462             # Replace the delegated option name with the local name.
03463             return [::snit::Expand $result $target $opt]
03464         }
03465 
03466         # configure didn't work; return simple form.
03467         return [list $opt "" "" "" $value]
03468     } else {
03469         error "unknown option \"$opt\""
03470     }
03471 }
03472 
03473 /* -----------------------------------------------------------------------*/
03474 /*  Type Introspection*/
03475 
03476 /*  Implements the standard "info" typemethod.*/
03477 /* */
03478 /*  type        The snit type*/
03479 /*  command       The info subcommand*/
03480 /*  args          All other arguments.*/
03481 
03482 ret  ::snit::RT.typemethod.info (type type , type command , type args) {
03483     global errorInfo
03484     global errorCode
03485 
03486     switch -exact $command {
03487     args        -
03488     body        -
03489     default     -
03490         typevars    -
03491         typemethods -
03492         instances {
03493             # TBD: it should be possible to delete this error
03494             # handling.
03495             set errflag [catch {
03496                 uplevel 1 [linsert $args 0 \
03497                    ::snit::RT.typemethod.info.$command $type]
03498             } result]
03499 
03500             if {$errflag} {
03501                 return -code error -errorinfo $errorInfo \
03502                     -errorcode $errorCode $result
03503             } else {
03504                 return $result
03505             }
03506         }
03507         default {
03508             error "\"$type info $command\" is not defined"
03509         }
03510     }
03511 }
03512 
03513 
03514 /*  Returns a list of the type's typevariables whose names match a*/
03515 /*  pattern, excluding Snit internal variables.*/
03516 /* */
03517 /*  type        A Snit type*/
03518 /*  pattern       Optional.  The glob pattern to match.  Defaults*/
03519 /*                to *.*/
03520 
03521 ret  ::snit::RT.typemethod.info.typevars (type type , optional pattern =*) {
03522     set result {}
03523     foreach name [info vars "${type}::$pattern"] {
03524         set tail [namespace tail $name]
03525         if {![string match "Snit_*" $tail]} {
03526             lappend result $name
03527         }
03528     }
03529 
03530     return $result
03531 }
03532 
03533 /*  Returns a list of the type's methods whose names match a*/
03534 /*  pattern.  If "delegate typemethod *" is used, the list may*/
03535 /*  not be complete.*/
03536 /* */
03537 /*  type        A Snit type*/
03538 /*  pattern       Optional.  The glob pattern to match.  Defaults*/
03539 /*                to *.*/
03540 
03541 ret  ::snit::RT.typemethod.info.typemethods (type type , optional pattern =*) {
03542     variable ${type}::Snit_typemethodInfo
03543     variable ${type}::Snit_typemethodCache
03544 
03545     # FIRST, get the explicit names, skipping prefixes.
03546     set result {}
03547 
03548     foreach name [array names Snit_typemethodInfo $pattern] {
03549         if {[lindex $Snit_typemethodInfo($name) 0] != 1} {
03550             lappend result $name
03551         }
03552     }
03553 
03554     # NEXT, add any from the cache that aren't explicit.
03555     if {[info exists Snit_typemethodInfo(*)]} {
03556         # First, remove "*" from the list.
03557         set ndx [lsearch -exact $result "*"]
03558         if {$ndx != -1} {
03559             set result [lreplace $result $ndx $ndx]
03560         }
03561 
03562         foreach name [array names Snit_typemethodCache $pattern] {
03563             if {[lsearch -exact $result $name] == -1} {
03564                 lappend result $name
03565             }
03566         }
03567     }
03568 
03569     return $result
03570 }
03571 
03572 /*  $type info args*/
03573 /* */
03574 /*  Returns a method's list of arguments. does not work for delegated*/
03575 /*  methods, nor for the internal dispatch methods of multi-word*/
03576 /*  methods.*/
03577 
03578 ret  ::snit::RT.typemethod.info.args (type type , type method) {
03579     upvar ${type}::Snit_typemethodInfo  Snit_typemethodInfo
03580 
03581     # Snit_methodInfo: method -> list (flag cmd component)
03582 
03583     # flag      : 1 -> internal dispatcher for multi-word method.
03584     #             0 -> regular method
03585     #
03586     # cmd       : template mapping from method to command prefix, may
03587     #             contain placeholders for various pieces of information.
03588     #
03589     # component : is empty for normal methods.
03590 
03591     #parray Snit_typemethodInfo
03592 
03593     if {![info exists Snit_typemethodInfo($method)]} {
03594     return -code error "Unknown typemethod \"$method\""
03595     }
03596     foreach {flag cmd component} $Snit_typemethodInfo($method) break
03597     if {$flag} {
03598     return -code error "Unknown typemethod \"$method\""
03599     }
03600     if {$component != ""} {
03601     return -code error "Delegated typemethod \"$method\""
03602     }
03603 
03604     set map     [list %m $method %j [join $method _] %t $type]
03605     set theproc [lindex [string map $map $cmd] 0]
03606     return [lrange [::info args $theproc] 1 end]
03607 }
03608 
03609 /*  $type info body*/
03610 /* */
03611 /*  Returns a method's body. does not work for delegated*/
03612 /*  methods, nor for the internal dispatch methods of multi-word*/
03613 /*  methods.*/
03614 
03615 ret  ::snit::RT.typemethod.info.body (type type , type method) {
03616     upvar ${type}::Snit_typemethodInfo  Snit_typemethodInfo
03617 
03618     # Snit_methodInfo: method -> list (flag cmd component)
03619 
03620     # flag      : 1 -> internal dispatcher for multi-word method.
03621     #             0 -> regular method
03622     #
03623     # cmd       : template mapping from method to command prefix, may
03624     #             contain placeholders for various pieces of information.
03625     #
03626     # component : is empty for normal methods.
03627 
03628     #parray Snit_typemethodInfo
03629 
03630     if {![info exists Snit_typemethodInfo($method)]} {
03631     return -code error "Unknown typemethod \"$method\""
03632     }
03633     foreach {flag cmd component} $Snit_typemethodInfo($method) break
03634     if {$flag} {
03635     return -code error "Unknown typemethod \"$method\""
03636     }
03637     if {$component != ""} {
03638     return -code error "Delegated typemethod \"$method\""
03639     }
03640 
03641     set map     [list %m $method %j [join $method _] %t $type]
03642     set theproc [lindex [string map $map $cmd] 0]
03643     return [RT.body [::info body $theproc]]
03644 }
03645 
03646 /*  $type info default*/
03647 /* */
03648 /*  Returns a method's list of arguments. does not work for delegated*/
03649 /*  methods, nor for the internal dispatch methods of multi-word*/
03650 /*  methods.*/
03651 
03652 ret  ::snit::RT.typemethod.info.default (type type , type method , type aname , type dvar) {
03653     upvar 1 $dvar def
03654     upvar ${type}::Snit_typemethodInfo  Snit_typemethodInfo
03655 
03656     # Snit_methodInfo: method -> list (flag cmd component)
03657 
03658     # flag      : 1 -> internal dispatcher for multi-word method.
03659     #             0 -> regular method
03660     #
03661     # cmd       : template mapping from method to command prefix, may
03662     #             contain placeholders for various pieces of information.
03663     #
03664     # component : is empty for normal methods.
03665 
03666     #parray Snit_methodInfo
03667 
03668     if {![info exists Snit_typemethodInfo($method)]} {
03669     return -code error "Unknown typemethod \"$method\""
03670     }
03671     foreach {flag cmd component} $Snit_typemethodInfo($method) break
03672     if {$flag} {
03673     return -code error "Unknown typemethod \"$method\""
03674     }
03675     if {$component != ""} {
03676     return -code error "Delegated typemethod \"$method\""
03677     }
03678 
03679     set map     [list %m $method %j [join $method _] %t $type]
03680     set theproc [lindex [string map $map $cmd] 0]
03681     return [::info default $theproc $aname def]
03682 }
03683 
03684 /*  Returns a list of the type's instances whose names match*/
03685 /*  a pattern.*/
03686 /* */
03687 /*  type        A Snit type*/
03688 /*  pattern       Optional.  The glob pattern to match*/
03689 /*                Defaults to **/
03690 /* */
03691 /*  REQUIRE: type is fully qualified.*/
03692 
03693 ret  ::snit::RT.typemethod.info.instances (type type , optional pattern =*) {
03694     set result {}
03695 
03696     foreach selfns [namespace children $type] {
03697         upvar ${selfns}::Snit_instance instance
03698 
03699         if {[string match $pattern $instance]} {
03700             lappend result $instance
03701         }
03702     }
03703 
03704     return $result
03705 }
03706 
03707 /* -----------------------------------------------------------------------*/
03708 /*  Instance Introspection*/
03709 
03710 /*  Implements the standard "info" method.*/
03711 /* */
03712 /*  type        The snit type*/
03713 /*  selfns        The instance's instance namespace*/
03714 /*  win           The instance's original name*/
03715 /*  self          The instance's current name*/
03716 /*  command       The info subcommand*/
03717 /*  args          All other arguments.*/
03718 
03719 ret  ::snit::RT.method.info (type type , type selfns , type win , type self , type command , type args) {
03720     switch -exact $command {
03721     args        -
03722     body        -
03723     default     -
03724         type        -
03725         vars        -
03726         options     -
03727         methods     -
03728         typevars    -
03729         typemethods {
03730             set errflag [catch {
03731                 uplevel 1 [linsert $args 0 ::snit::RT.method.info.$command \
03732                    $type $selfns $win $self]
03733             } result]
03734 
03735             if {$errflag} {
03736                 global errorInfo
03737                 return -code error -errorinfo $errorInfo $result
03738             } else {
03739                 return $result
03740             }
03741         }
03742         default {
03743             # error "\"$self info $command\" is not defined"
03744             return -code error "\"$self info $command\" is not defined"
03745         }
03746     }
03747 }
03748 
03749 /*  $self info type*/
03750 /* */
03751 /*  Returns the instance's type*/
03752 ret  ::snit::RT.method.info.type (type type , type selfns , type win , type self) {
03753     return $type
03754 }
03755 
03756 /*  $self info typevars*/
03757 /* */
03758 /*  Returns the instance's type's typevariables*/
03759 ret  ::snit::RT.method.info.typevars (type type , type selfns , type win , type self , optional pattern =*) {
03760     return [RT.typemethod.info.typevars $type $pattern]
03761 }
03762 
03763 /*  $self info typemethods*/
03764 /* */
03765 /*  Returns the instance's type's typemethods*/
03766 ret  ::snit::RT.method.info.typemethods (type type , type selfns , type win , type self , optional pattern =*) {
03767     return [RT.typemethod.info.typemethods $type $pattern]
03768 }
03769 
03770 /*  Returns a list of the instance's methods whose names match a*/
03771 /*  pattern.  If "delegate method *" is used, the list may*/
03772 /*  not be complete.*/
03773 /* */
03774 /*  type        A Snit type*/
03775 /*  selfns        The instance namespace*/
03776 /*  win     The original instance name*/
03777 /*  self          The current instance name*/
03778 /*  pattern       Optional.  The glob pattern to match.  Defaults*/
03779 /*                to *.*/
03780 
03781 ret  ::snit::RT.method.info.methods (type type , type selfns , type win , type self , optional pattern =*) {
03782     variable ${type}::Snit_methodInfo
03783     variable ${selfns}::Snit_methodCache
03784 
03785     # FIRST, get the explicit names, skipping prefixes.
03786     set result {}
03787 
03788     foreach name [array names Snit_methodInfo $pattern] {
03789         if {[lindex $Snit_methodInfo($name) 0] != 1} {
03790             lappend result $name
03791         }
03792     }
03793 
03794     # NEXT, add any from the cache that aren't explicit.
03795     if {[info exists Snit_methodInfo(*)]} {
03796         # First, remove "*" from the list.
03797         set ndx [lsearch -exact $result "*"]
03798         if {$ndx != -1} {
03799             set result [lreplace $result $ndx $ndx]
03800         }
03801 
03802         foreach name [array names Snit_methodCache $pattern] {
03803             if {[lsearch -exact $result $name] == -1} {
03804                 lappend result $name
03805             }
03806         }
03807     }
03808 
03809     return $result
03810 }
03811 
03812 /*  $self info args*/
03813 /* */
03814 /*  Returns a method's list of arguments. does not work for delegated*/
03815 /*  methods, nor for the internal dispatch methods of multi-word*/
03816 /*  methods.*/
03817 
03818 ret  ::snit::RT.method.info.args (type type , type selfns , type win , type self , type method) {
03819 
03820     upvar ${type}::Snit_methodInfo  Snit_methodInfo
03821 
03822     # Snit_methodInfo: method -> list (flag cmd component)
03823 
03824     # flag      : 1 -> internal dispatcher for multi-word method.
03825     #             0 -> regular method
03826     #
03827     # cmd       : template mapping from method to command prefix, may
03828     #             contain placeholders for various pieces of information.
03829     #
03830     # component : is empty for normal methods.
03831 
03832     #parray Snit_methodInfo
03833 
03834     if {![info exists Snit_methodInfo($method)]} {
03835     return -code error "Unknown method \"$method\""
03836     }
03837     foreach {flag cmd component} $Snit_methodInfo($method) break
03838     if {$flag} {
03839     return -code error "Unknown method \"$method\""
03840     }
03841     if {$component != ""} {
03842     return -code error "Delegated method \"$method\""
03843     }
03844 
03845     set map     [list %m $method %j [join $method _] %t $type %n $selfns %w $win %s $self]
03846     set theproc [lindex [string map $map $cmd] 0]
03847     return [lrange [::info args $theproc] 4 end]
03848 }
03849 
03850 /*  $self info body*/
03851 /* */
03852 /*  Returns a method's body. does not work for delegated*/
03853 /*  methods, nor for the internal dispatch methods of multi-word*/
03854 /*  methods.*/
03855 
03856 ret  ::snit::RT.method.info.body (type type , type selfns , type win , type self , type method) {
03857 
03858     upvar ${type}::Snit_methodInfo  Snit_methodInfo
03859 
03860     # Snit_methodInfo: method -> list (flag cmd component)
03861 
03862     # flag      : 1 -> internal dispatcher for multi-word method.
03863     #             0 -> regular method
03864     #
03865     # cmd       : template mapping from method to command prefix, may
03866     #             contain placeholders for various pieces of information.
03867     #
03868     # component : is empty for normal methods.
03869 
03870     #parray Snit_methodInfo
03871 
03872     if {![info exists Snit_methodInfo($method)]} {
03873     return -code error "Unknown method \"$method\""
03874     }
03875     foreach {flag cmd component} $Snit_methodInfo($method) break
03876     if {$flag} {
03877     return -code error "Unknown method \"$method\""
03878     }
03879     if {$component != ""} {
03880     return -code error "Delegated method \"$method\""
03881     }
03882 
03883     set map     [list %m $method %j [join $method _] %t $type %n $selfns %w $win %s $self]
03884     set theproc [lindex [string map $map $cmd] 0]
03885     return [RT.body [::info body $theproc]]
03886 }
03887 
03888 /*  $self info default*/
03889 /* */
03890 /*  Returns a method's list of arguments. does not work for delegated*/
03891 /*  methods, nor for the internal dispatch methods of multi-word*/
03892 /*  methods.*/
03893 
03894 ret  ::snit::RT.method.info.default (type type , type selfns , type win , type self , type method , type aname , type dvar) {
03895     upvar 1 $dvar def
03896     upvar ${type}::Snit_methodInfo  Snit_methodInfo
03897 
03898     # Snit_methodInfo: method -> list (flag cmd component)
03899 
03900     # flag      : 1 -> internal dispatcher for multi-word method.
03901     #             0 -> regular method
03902     #
03903     # cmd       : template mapping from method to command prefix, may
03904     #             contain placeholders for various pieces of information.
03905     #
03906     # component : is empty for normal methods.
03907 
03908     if {![info exists Snit_methodInfo($method)]} {
03909     return -code error "Unknown method \"$method\""
03910     }
03911     foreach {flag cmd component} $Snit_methodInfo($method) break
03912     if {$flag} {
03913     return -code error "Unknown method \"$method\""
03914     }
03915     if {$component != ""} {
03916     return -code error "Delegated method \"$method\""
03917     }
03918 
03919     set map     [list %m $method %j [join $method _] %t $type %n $selfns %w $win %s $self]
03920     set theproc [lindex [string map $map $cmd] 0]
03921     return [::info default $theproc $aname def]
03922 }
03923 
03924 /*  $self info vars*/
03925 /* */
03926 /*  Returns the instance's instance variables*/
03927 ret  ::snit::RT.method.info.vars (type type , type selfns , type win , type self , optional pattern =*) {
03928     set result {}
03929     foreach name [info vars "${selfns}::$pattern"] {
03930         set tail [namespace tail $name]
03931         if {![string match "Snit_*" $tail]} {
03932             lappend result $name
03933         }
03934     }
03935 
03936     return $result
03937 }
03938 
03939 /*  $self info options*/
03940 /* */
03941 /*  Returns a list of the names of the instance's options*/
03942 ret  ::snit::RT.method.info.options (type type , type selfns , type win , type self , optional pattern =*) {
03943     variable ${type}::Snit_optionInfo
03944 
03945     # First, get the local and explicitly delegated options
03946     set result [concat $Snit_optionInfo(local) $Snit_optionInfo(delegated)]
03947 
03948     # If "configure" works as for Tk widgets, add the resulting
03949     # options to the list.  Skip excepted options
03950     if {"" != $Snit_optionInfo(starcomp)} {
03951         upvar ${selfns}::Snit_components Snit_components
03952         set logicalName $Snit_optionInfo(starcomp)
03953         set comp $Snit_components($logicalName)
03954 
03955         if {![catch {$comp configure} records]} {
03956             foreach record $records {
03957                 set opt [lindex $record 0]
03958                 if {[lsearch -exact $result $opt] == -1 &&
03959                     [lsearch -exact $Snit_optionInfo(except) $opt] == -1} {
03960                     lappend result $opt
03961                 }
03962             }
03963         }
03964     }
03965 
03966     # Next, apply the pattern
03967     set names {}
03968 
03969     foreach name $result {
03970         if {[string match $pattern $name]} {
03971             lappend names $name
03972         }
03973     }
03974 
03975     return $names
03976 }
03977 
03978 ret  ::snit::RT.body (type body) {
03979     regsub -all ".*# END snit method prolog\n" $body {} body
03980     return $body
03981 }
03982 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1