main2.tcl

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

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1