00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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
00075
00076 namespace %TYPE% {%TYPEVARS%
00077 }
00078
00079
00080
00081
00082
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
00100
00101 namespace %TYPE% {
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
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
00129
00130
00131
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
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162 ret %TYPE%::Snit_instanceVars (type selfns) {
00163 %INSTANCEVARS%
00164 }
00165
00166
00167 ret %TYPE%::Snit_typeconstructor (type type) {
00168 %TVARDECS%
00169 namespace path [namespace parent $type]
00170 %TCONSTBODY%
00171 }
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185 ret %TYPE%::Snit_destructor (type type , type selfns , type win , type self) { }
00186
00187
00188
00189
00190 %COMPILEDDEFS%
00191
00192
00193
00194
00195 %TYPE%::Snit_typeconstructor %TYPE%
00196 }
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206 ::snit = ::nominalTypeProc {
00207
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
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272 namespace ::snit:: {
00273
00274
00275 variable compiler ""
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317 variable compile
00318
00319
00320
00321
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
00349 compiler = [interp create]
00350
00351
00352 $compiler eval {
00353
00354
00355
00356 catch {package require ::snit::__does_not_exist__}
00357
00358
00359
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
00418 Comp.Init
00419
00420
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
00478
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
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
00688
00689
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
00718
00719
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
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
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
00774
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
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
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
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
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
00969
00970
00971
00972
00973
00974
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
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
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
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
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
01128
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
01169
01170
01171
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
01217
01218
01219
01220
01221
01222
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
01247
01248
01249
01250
01251
01252
01253
01254
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
01300
01301
01302
01303
01304
01305
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
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
01347
01348
01349
01350
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
01435
01436
01437
01438
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
01528
01529
01530
01531
01532
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
01635
01636
01637
01638
01639
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
01673
01674
01675
01676
01677
01678
01679
01680
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
01767
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
01794
01795
01796
01797
01798
01799 ret ::snit::Expand (type template , type args) {
01800 return [string map $args $template]
01801 }
01802
01803
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
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
01822 ret ::snit::Capitalize (type text) {
01823 return [string toupper $text 0]
01824 }
01825
01826
01827
01828
01829
01830
01831
01832
01833
01834
01835
01836
01837
01838
01839
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
01905
01906
01907
01908
01909
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
02009
02010
02011
02012
02013
02014
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
02051
02052
02053
02054
02055
02056
02057
02058
02059
02060
02061
02062
02063
02064
02065
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
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
02143
02144
02145
02146
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
02166
02167
02168
02169
02170
02171
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
02191
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
02202
02203
02204
02205
02206
02207
02208
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
02224
02225
02226
02227
02228
02229
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
02285
02286
02287
02288
02289
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
02309
02310
02311
02312
02313
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
02334
02335
02336
02337
02338
02339
02340
02341
02342
02343
02344
02345
02346
02347
02348
02349
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
02480
02481
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
02495
02496
02497
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
02525
02526
02527
02528
02529
02530
02531
02532
02533
02534
02535
02536
02537
02538
02539
02540
02541
02542
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
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
02667
02668
02669
02670
02671
02672
02673
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
02775
02776
02777
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
02879
02880
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
02894
02895
02896
02897 ret ::snit::RT.mytypevar (type type , type name) {
02898 return ${type}::$name
02899 }
02900
02901
02902
02903
02904 ret ::snit::RT.myvar (type name) {
02905 upvar 1 selfns selfns
02906 return ${selfns}::$name
02907 }
02908
02909
02910
02911
02912
02913
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
02921 ret ::snit::RT.codename (type type , type name) {
02922 return "${type}::$name"
02923 }
02924
02925
02926
02927
02928
02929
02930
02931 ret ::snit::RT.mytypemethod (type type , type args) {
02932 return [linsert $args 0 $type]
02933 }
02934
02935
02936
02937
02938
02939
02940
02941
02942
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
02950
02951
02952
02953
02954
02955
02956
02957
02958
02959
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
02981
02982
02983
02984
02985
02986
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
03013
03014
03015
03016
03017
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
03050
03051
03052
03053
03054
03055
03056
03057
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
03072
03073
03074
03075
03076
03077
03078
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
03126
03127
03128
03129
03130
03131
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
03177
03178
03179
03180
03181
03182
03183
03184
03185
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
03260
03261
03262
03263
03264
03265
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
03295
03296
03297
03298
03299
03300
03301
03302
03303
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
03363
03364
03365
03366
03367
03368
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
03403
03404
03405
03406
03407
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
03422
03423
03424
03425
03426
03427
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
03466
03467
03468
03469
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
03503
03504
03505
03506
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
03540
03541
03542
03543
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
03578
03579
03580
03581
03582
03583
03584
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
03602
03603
03604
03605
03606
03607
03608
03609
03610
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
03643
03644
03645 ret ::snit::RT.method.info.type (type type , type selfns , type win , type self) {
03646 return $type
03647 }
03648
03649
03650
03651
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
03657
03658
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
03664
03665
03666
03667
03668
03669
03670
03671
03672
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
03713
03714
03715
03716
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
03751
03752
03753
03754
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
03789
03790
03791
03792
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
03825
03826
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
03840
03841
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