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