main1_83.tcl

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

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1