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