1
#-----------------------------------------------------------------------
9
# Simple Now In Tcl, a simple object system in Pure Tcl.
11
#-----------------------------------------------------------------------
13
package provide snit 0.97
15
#-----------------------------------------------------------------------
18
namespace eval ::snit:: {
20
compile type widget widgetadaptor typemethod method macro
23
#-----------------------------------------------------------------------
26
namespace eval ::snit:: {
27
variable reservedArgs {type selfns win self}
29
# If true, get a pretty, fixed-up stack trace. Otherwise, get raw
31
# NOTE: Not Yet Implemented
32
variable prettyStackTrace 1
35
#-----------------------------------------------------------------------
36
# Snit Type Implementation template
38
namespace eval ::snit:: {
39
# Template type definition: All internal and user-visible Snit
40
# implementation code.
42
# The following placeholders will automatically be replaced with
43
# the client's code, in two passes:
46
# %COMPILEDDEFS% The compiled type definition.
49
# %TYPE% The fully qualified type name.
50
# %IVARDECS% Instance variable declarations
51
# %TVARDECS% Type variable declarations
52
# %TCONSTBODY% Type constructor body
53
# %INSTANCEVARS% The compiled instance variable initialization code.
54
# %TYPEVARS% The compiled type variable initialization code.
56
# This is the overall type template.
59
# This is the normal type proc
60
variable nominalTypeProc
62
# This is the "-hastypemethods no" type proc
63
variable simpleTypeProc
66
set ::snit::typeTemplate {
68
#-------------------------------------------------------------------
69
# The type's namespace definition and the user's type variables
71
namespace eval %TYPE% {%TYPEVARS%
74
#----------------------------------------------------------------
75
# Commands for use in methods, typemethods, etc.
77
# These are implemented as aliases into the Snit runtime library.
79
interp alias {} %TYPE%::installhull {} ::snit::RT.installhull %TYPE%
80
interp alias {} %TYPE%::install {} ::snit::RT.install %TYPE%
81
interp alias {} %TYPE%::typevariable {} ::variable
82
interp alias {} %TYPE%::variable {} ::snit::RT.variable
83
interp alias {} %TYPE%::mytypevar {} ::snit::RT.mytypevar %TYPE%
84
interp alias {} %TYPE%::typevarname {} ::snit::RT.mytypevar %TYPE%
85
interp alias {} %TYPE%::myvar {} ::snit::RT.myvar
86
interp alias {} %TYPE%::varname {} ::snit::RT.myvar
87
interp alias {} %TYPE%::codename {} ::snit::RT.codename %TYPE%
88
interp alias {} %TYPE%::myproc {} ::snit::RT.myproc %TYPE%
89
interp alias {} %TYPE%::mymethod {} ::snit::RT.mymethod
90
interp alias {} %TYPE%::mytypemethod {} ::snit::RT.mytypemethod %TYPE%
91
interp alias {} %TYPE%::from {} ::snit::RT.from %TYPE%
93
#-------------------------------------------------------------------
94
# Snit's internal variables
96
namespace eval %TYPE% {
97
# Array: General Snit Info
99
# ns: The type's namespace
100
# hasinstances: T or F, from pragma -hasinstances.
101
# simpledispatch: T or F, from pragma -hasinstances.
102
# canreplace: T or F, from pragma -canreplace.
103
# counter: Count of instances created so far.
104
# widgetclass: Set by widgetclass statement.
105
# hulltype: Hull type (frame or toplevel) for widgets only.
106
# exceptmethods: Methods explicitly not delegated to *
107
# excepttypemethods: Methods explicitly not delegated to *
108
# tvardecs: Type variable declarations--for dynamic methods
109
# ivardecs: Instance variable declarations--for dyn. methods
110
typevariable Snit_info
111
set Snit_info(ns) %TYPE%::
112
set Snit_info(hasinstances) 1
113
set Snit_info(simpledispatch) 0
114
set Snit_info(canreplace) 0
115
set Snit_info(counter) 0
116
set Snit_info(widgetclass) {}
117
set Snit_info(hulltype) frame
118
set Snit_info(exceptmethods) {}
119
set Snit_info(excepttypemethods) {}
120
set Snit_info(tvardecs) {%TVARDECS%}
121
set Snit_info(ivardecs) {%IVARDECS%}
123
# Array: Public methods of this type.
124
# The index is the method name, or "*".
125
# The value is [list $pattern $componentName], where
126
# $componentName is "" for normal methods.
127
typevariable Snit_typemethodInfo
128
array unset Snit_typemethodInfo
130
# Array: Public methods of instances of this type.
131
# The index is the method name, or "*".
132
# The value is [list $pattern $componentName], where
133
# $componentName is "" for normal methods.
134
typevariable Snit_methodInfo
135
array unset Snit_methodInfo
137
# Array: option information. See dictionary.txt.
138
typevariable Snit_optionInfo
139
array unset Snit_optionInfo
140
set Snit_optionInfo(local) {}
141
set Snit_optionInfo(delegated) {}
142
set Snit_optionInfo(starcomp) {}
143
set Snit_optionInfo(except) {}
146
#----------------------------------------------------------------
149
# These commands are created or replaced during compilation:
152
# Snit_instanceVars selfns
154
# Initializes the instance variables, if any. Called during
157
proc %TYPE%::Snit_instanceVars {selfns} {
162
proc %TYPE%::Snit_typeconstructor {type} {
167
#----------------------------------------------------------------
170
# These commands might be replaced during compilation:
172
# Snit_destructor type selfns win self
174
# Default destructor for the type. By default, it does
175
# nothing. It's replaced by any user destructor.
176
# For types, it's called by method destroy; for widgettypes,
177
# it's called by a destroy event handler.
179
proc %TYPE%::Snit_destructor {type selfns win self} { }
181
#----------------------------------------------------------
182
# Compiled Definitions
186
#----------------------------------------------------------
187
# Finally, call the Type Constructor
189
%TYPE%::Snit_typeconstructor %TYPE%
192
#-----------------------------------------------------------------------
195
# These procs expect the fully-qualified type name to be
196
# substituted in for %TYPE%.
198
# This is the nominal type proc. It supports typemethods and
199
# delegated typemethods.
200
set ::snit::nominalTypeProc {
201
# Type dispatcher function. Note: This function lives
202
# in the parent of the %TYPE% namespace! All accesses to
203
# %TYPE% variables and methods must be qualified!
204
proc %TYPE% {{method ""} args} {
205
# First, if there's no method, and no args, and there's a create
206
# method, and this isn't a widget, then method is "create" and
208
if {$method eq "" && [llength $args] == 0} {
209
::variable %TYPE%::Snit_info
211
if {$Snit_info(hasinstances) && !$Snit_info(isWidget)} {
215
error "wrong \# args: should be \"%TYPE% method args\""
219
# Next, retrieve the command.
221
if {[catch {set %TYPE%::Snit_typemethodCache($method)} commandRec]} {
222
set commandRec [::snit::RT.CacheTypemethodCommand %TYPE% $method]
224
if {[llength $commandRec] == 0} {
225
return -code error "\"%TYPE% $method\" is not defined"
229
# If we've got a real command, break.
230
if {[lindex $commandRec 0] == 0} {
234
# Otherwise, we need to look up again...if we can.
235
if {[llength $args] == 0} {
237
"wrong number args: should be \"%TYPE% $method method args\""
240
lappend method [lindex $args 0]
241
set args [lrange $args 1 end]
244
set command [lindex $commandRec 1]
246
# Pass along the return code unchanged.
247
set retval [catch {uplevel $command $args} result]
253
return -code error -errorinfo $errorInfo \
254
-errorcode $errorCode $result
256
return -code $retval $result
264
# This is the simplified type proc for when there are no typemethods
265
# except create. In this case, it doesn't take a method argument;
266
# the method is always "create".
267
set ::snit::simpleTypeProc {
268
# Type dispatcher function. Note: This function lives
269
# in the parent of the %TYPE% namespace! All accesses to
270
# %TYPE% variables and methods must be qualified!
272
::variable %TYPE%::Snit_info
274
# FIRST, if the are no args, the single arg is %AUTO%
275
if {[llength $args] == 0} {
276
if {$Snit_info(isWidget)} {
277
error "wrong \# args: should be \"%TYPE% name args\""
283
# NEXT, we're going to call the create method.
284
# Pass along the return code unchanged.
285
if {$Snit_info(isWidget)} {
286
set command [list ::snit::RT.widget.typemethod.create %TYPE%]
288
set command [list ::snit::RT.type.typemethod.create %TYPE%]
291
set retval [catch {uplevel $command $args} result]
297
return -code error -errorinfo $errorInfo \
298
-errorcode $errorCode $result
300
return -code $retval $result
308
#-----------------------------------------------------------------------
311
# The following must be substituted into these proc bodies:
313
# %SELFNS% The instance namespace
314
# %WIN% The original instance name
315
# %TYPE% The fully-qualified type name
318
# Nominal instance proc body: supports method caching and delegation.
320
# proc $instanceName {method args} ....
321
set ::snit::nominalInstanceProc {
322
set self [set %SELFNS%::Snit_instance]
325
if {[catch {set %SELFNS%::Snit_methodCache($method)} commandRec]} {
326
set commandRec [snit::RT.CacheMethodCommand %TYPE% %SELFNS% %WIN% $self $method]
328
if {[llength $commandRec] == 0} {
330
"\"$self $method\" is not defined"
334
# If we've got a real command, break.
335
if {[lindex $commandRec 0] == 0} {
339
# Otherwise, we need to look up again...if we can.
340
if {[llength $args] == 0} {
342
"wrong number args: should be \"$self $method method args\""
345
lappend method [lindex $args 0]
346
set args [lrange $args 1 end]
349
set command [lindex $commandRec 1]
351
# Pass along the return code unchanged.
352
set retval [catch {uplevel 1 $command $args} result]
358
return -code error -errorinfo $errorInfo \
359
-errorcode $errorCode $result
361
return -code $retval $result
368
# Simplified method proc body: No delegation allowed; no support for
369
# upvar or exotic return codes or hierarchical methods. Designed for
370
# max speed for simple types.
372
# proc $instanceName {method args} ....
374
set ::snit::simpleInstanceProc {
375
set self [set %SELFNS%::Snit_instance]
377
if {[lsearch -exact ${%TYPE%::Snit_methods} $method] == -1} {
378
set optlist [join ${%TYPE%::Snit_methods} ", "]
379
set optlist [linsert $optlist "end-1" "or"]
380
error "bad option \"$method\": must be $optlist"
383
eval [linsert $args 0 \
384
%TYPE%::Snit_method$method %TYPE% %SELFNS% %WIN% $self]
388
#=======================================================================
389
# Snit Type Definition
391
# These are the procs used to define Snit types, widgets, and
395
#-----------------------------------------------------------------------
396
# Snit Compilation Variables
398
# The following variables are used while Snit is compiling a type,
399
# and are disposed afterwards.
401
namespace eval ::snit:: {
402
# The compiler variable contains the name of the slave interpreter
403
# used to compile type definitions.
406
# The compile array accumulates information about the type or
407
# widgettype being compiled. It is cleared before and after each
408
# compilation. It has these indices:
410
# type: The name of the type being compiled, for use
411
# in compilation procs.
412
# defs: Compiled definitions, both standard and client.
413
# which: type, widget, widgetadaptor
414
# instancevars: Instance variable definitions and initializations.
415
# ivprocdec: Instance variable proc declarations.
416
# tvprocdec: Type variable proc declarations.
417
# typeconstructor: Type constructor body.
418
# widgetclass: The widgetclass, for snit::widgets, only
419
# hasoptions: False, initially; set to true when first
421
# localoptions: Names of local options.
422
# delegatedoptions: Names of delegated options.
423
# localmethods: Names of locally defined methods.
424
# delegatesmethods: no if no delegated methods, yes otherwise.
425
# hashierarchic : no if no hierarchic methods, yes otherwise.
426
# components: Names of defined components.
427
# typecomponents: Names of defined typecomponents.
428
# typevars: Typevariable definitions and initializations.
429
# varnames: Names of instance variables
430
# typevarnames Names of type variables
431
# hasconstructor False, initially; true when constructor is
433
# resource-$opt The option's resource name
434
# class-$opt The option's class
435
# -default-$opt The option's default value
436
# -validatemethod-$opt The option's validate method
437
# -configuremethod-$opt The option's configure method
438
# -cgetmethod-$opt The option's cget method.
439
# -hastypeinfo The -hastypeinfo pragma
440
# -hastypedestroy The -hastypedestroy pragma
441
# -hastypemethods The -hastypemethods pragma
442
# -hasinfo The -hasinfo pragma
443
# -hasinstances The -hasinstances pragma
444
# -simpledispatch The -simpledispatch pragma
445
# -canreplace The -canreplace pragma
448
# This variable accumulates method dispatch information; it has
449
# the same structure as the %TYPE%::Snit_methodInfo array, and is
450
# used to initialize it.
453
# This variable accumulates typemethod dispatch information; it has
454
# the same structure as the %TYPE%::Snit_typemethodInfo array, and is
455
# used to initialize it.
456
variable typemethodInfo
458
# The following variable lists the reserved type definition statement
459
# names, e.g., the names you can't use as macros. It's built at
460
# compiler definition time using "info commands".
461
variable reservedwords {}
464
#-----------------------------------------------------------------------
465
# type compilation commands
467
# The type and widgettype commands use a slave interpreter to compile
468
# the type definition. These are the procs
469
# that are aliased into it.
471
# Initialize the compiler
472
proc ::snit::Comp.Init {} {
474
variable reservedwords
476
if {$compiler eq ""} {
477
# Create the compiler's interpreter
478
set compiler [interp create]
480
# Initialize the interpreter
482
# Load package information
483
# TBD: see if this can be moved outside.
484
catch {package require ::snit::__does_not_exist__}
486
# Protect some Tcl commands our type definitions
489
rename variable _variable
492
# Define compilation aliases.
493
$compiler alias pragma ::snit::Comp.statement.pragma
494
$compiler alias widgetclass ::snit::Comp.statement.widgetclass
495
$compiler alias hulltype ::snit::Comp.statement.hulltype
496
$compiler alias constructor ::snit::Comp.statement.constructor
497
$compiler alias destructor ::snit::Comp.statement.destructor
498
$compiler alias option ::snit::Comp.statement.option
499
$compiler alias oncget ::snit::Comp.statement.oncget
500
$compiler alias onconfigure ::snit::Comp.statement.onconfigure
501
$compiler alias method ::snit::Comp.statement.method
502
$compiler alias typemethod ::snit::Comp.statement.typemethod
503
$compiler alias typeconstructor ::snit::Comp.statement.typeconstructor
504
$compiler alias proc ::snit::Comp.statement.proc
505
$compiler alias typevariable ::snit::Comp.statement.typevariable
506
$compiler alias variable ::snit::Comp.statement.variable
507
$compiler alias typecomponent ::snit::Comp.statement.typecomponent
508
$compiler alias component ::snit::Comp.statement.component
509
$compiler alias delegate ::snit::Comp.statement.delegate
510
$compiler alias expose ::snit::Comp.statement.expose
512
# Get the list of reserved words
513
set reservedwords [$compiler eval {info commands}]
517
# Compile a type definition, and return the results as a list of two
518
# items: the fully-qualified type name, and a script that will define
519
# the type when executed.
521
# which type, widget, or widgetadaptor
523
# body the type definition
524
proc ::snit::Comp.Compile {which type body} {
525
variable typeTemplate
526
variable nominalTypeProc
527
variable simpleTypeProc
531
variable typemethodInfo
533
# FIRST, qualify the name.
534
if {![string match "::*" $type]} {
535
# Get caller's namespace;
536
# append :: if not global namespace.
537
set ns [uplevel 2 namespace current]
545
# NEXT, create and initialize the compiler, if needed.
548
# NEXT, initialize the class data
549
array unset methodInfo
550
array unset typemethodInfo
553
set compile(type) $type
555
set compile(which) $which
556
set compile(hasoptions) no
557
set compile(localoptions) {}
558
set compile(instancevars) {}
559
set compile(typevars) {}
560
set compile(delegatedoptions) {}
561
set compile(ivprocdec) {}
562
set compile(tvprocdec) {}
563
set compile(typeconstructor) {}
564
set compile(widgetclass) {}
565
set compile(hulltype) {}
566
set compile(localmethods) {}
567
set compile(delegatesmethods) no
568
set compile(hashierarchic) no
569
set compile(components) {}
570
set compile(typecomponents) {}
571
set compile(varnames) {}
572
set compile(typevarnames) {}
573
set compile(hasconstructor) no
574
set compile(-hastypedestroy) yes
575
set compile(-hastypeinfo) yes
576
set compile(-hastypemethods) yes
577
set compile(-hasinfo) yes
578
set compile(-hasinstances) yes
579
set compile(-simpledispatch) no
580
set compile(-canreplace) no
582
set isWidget [string match widget* $which]
583
set isWidgetAdaptor [string match widgetadaptor $which]
585
# NEXT, Evaluate the type's definition in the class interpreter.
588
# NEXT, Add the standard definitions
589
append compile(defs) \
590
"\nset %TYPE%::Snit_info(isWidget) $isWidget\n"
592
append compile(defs) \
593
"\nset %TYPE%::Snit_info(isWidgetAdaptor) $isWidgetAdaptor\n"
595
# Indicate whether the type can create instances that replace
597
append compile(defs) "\nset %TYPE%::Snit_info(canreplace) $compile(-canreplace)\n"
600
# Check pragmas for conflict.
602
if {!$compile(-hastypemethods) && !$compile(-hasinstances)} {
603
error "$which $type has neither typemethods nor instances"
606
if {$compile(-simpledispatch) && $compile(delegatesmethods)} {
607
error "$which $type requests -simpledispatch but delegates methods."
610
if {$compile(-simpledispatch) && $compile(hashierarchic)} {
611
error "$which $type requests -simpledispatch but defines hierarchical methods."
614
# If there are typemethods, define the standard typemethods and
615
# the nominal type proc. Otherwise define the simple type proc.
616
if {$compile(-hastypemethods)} {
617
# Add the info typemethod unless the pragma forbids it.
618
if {$compile(-hastypeinfo)} {
619
Comp.statement.delegate typemethod info \
620
using {::snit::RT.typemethod.info %t}
623
# Add the destroy typemethod unless the pragma forbids it.
624
if {$compile(-hastypedestroy)} {
625
Comp.statement.delegate typemethod destroy \
626
using {::snit::RT.typemethod.destroy %t}
629
# Add the nominal type proc.
630
append compile(defs) $nominalTypeProc
632
# Add the simple type proc.
633
append compile(defs) $simpleTypeProc
636
# Add standard methods/typemethods that only make sense if the
637
# type has instances.
638
if {$compile(-hasinstances)} {
639
# If we're using simple dispatch, remember that.
640
if {$compile(-simpledispatch)} {
641
append compile(defs) "\nset %TYPE%::Snit_info(simpledispatch) 1\n"
644
# Add the info method unless the pragma forbids it.
645
if {$compile(-hasinfo)} {
646
if {!$compile(-simpledispatch)} {
647
Comp.statement.delegate method info \
648
using {::snit::RT.method.info %t %n %w %s}
650
Comp.statement.method info {args} {
651
eval [linsert $args 0 \
652
::snit::RT.method.info $type $selfns $win $self]
657
# Add the option handling stuff if there are any options.
658
if {$compile(hasoptions)} {
659
Comp.statement.variable options
661
if {!$compile(-simpledispatch)} {
662
Comp.statement.delegate method cget \
663
using {::snit::RT.method.cget %t %n %w %s}
664
Comp.statement.delegate method configurelist \
665
using {::snit::RT.method.configurelist %t %n %w %s}
666
Comp.statement.delegate method configure \
667
using {::snit::RT.method.configure %t %n %w %s}
669
Comp.statement.method cget {args} {
670
eval [linsert $args 0 \
671
::snit::RT.method.cget $type $selfns $win $self]
673
Comp.statement.method configurelist {args} {
674
eval [linsert $args 0 \
675
::snit::RT.method.configurelist $type $selfns $win $self]
677
Comp.statement.method configure {args} {
678
eval [linsert $args 0 \
679
::snit::RT.method.configure $type $selfns $win $self]
684
# Add a default constructor, if they haven't already defined one.
685
# If there are options, it will configure args; otherwise it
687
if {!$compile(hasconstructor)} {
688
if {$compile(hasoptions)} {
689
Comp.statement.constructor {args} {
690
$self configurelist $args
693
Comp.statement.constructor {} {}
698
if {!$compile(-simpledispatch)} {
699
Comp.statement.delegate method destroy \
700
using {::snit::RT.method.destroy %t %n %w %s}
702
Comp.statement.method destroy {args} {
703
eval [linsert $args 0 \
704
::snit::RT.method.destroy $type $selfns $win $self]
708
Comp.statement.delegate typemethod create \
709
using {::snit::RT.type.typemethod.create %t}
711
Comp.statement.delegate typemethod create \
712
using {::snit::RT.widget.typemethod.create %t}
715
# Save the list of method names, for -simpledispatch; otherwise,
716
# save the method info.
717
if {$compile(-simpledispatch)} {
718
append compile(defs) \
719
"\nset %TYPE%::Snit_methods [list $compile(localmethods)]\n"
721
append compile(defs) \
722
"\narray set %TYPE%::Snit_methodInfo [list [array get methodInfo]]\n"
726
append compile(defs) "\nset %TYPE%::Snit_info(hasinstances) 0\n"
729
# NEXT, compiling the type definition built up a set of information
730
# about the type's locally defined options; add this information to
731
# the compiled definition.
734
# NEXT, compiling the type definition built up a set of information
735
# about the typemethods; save the typemethod info.
736
append compile(defs) \
737
"\narray set %TYPE%::Snit_typemethodInfo [list [array get typemethodInfo]]\n"
739
# NEXT, if this is a widget define the hull component if it isn't
742
Comp.DefineComponent hull
745
# NEXT, substitute the compiled definition into the type template
746
# to get the type definition script.
747
set defscript [Expand $typeTemplate \
748
%COMPILEDDEFS% $compile(defs)]
750
# NEXT, substitute the defined macros into the type definition script.
751
# This is done as a separate step so that the compile(defs) can
752
# contain the macros defined below.
754
set defscript [Expand $defscript \
756
%IVARDECS% $compile(ivprocdec) \
757
%TVARDECS% $compile(tvprocdec) \
758
%TCONSTBODY% $compile(typeconstructor) \
759
%INSTANCEVARS% $compile(instancevars) \
760
%TYPEVARS% $compile(typevars) \
765
return [list $type $defscript]
768
# Information about locally-defined options is accumulated during
769
# compilation, but not added to the compiled definition--the option
770
# statement can appear multiple times, so it's easier this way.
771
# This proc fills in Snit_optionInfo with the accumulated information.
773
# It also computes the option's resource and class names if needed.
775
# Note that the information for delegated options was put in
776
# Snit_optionInfo during compilation.
778
proc ::snit::Comp.SaveOptionInfo {} {
781
foreach option $compile(localoptions) {
782
if {$compile(resource-$option) eq ""} {
783
set compile(resource-$option) [string range $option 1 end]
786
if {$compile(class-$option) eq ""} {
787
set compile(class-$option) [Capitalize $compile(resource-$option)]
790
# NOTE: Don't verify that the validate, configure, and cget
791
# values name real methods; the methods might be defined outside
792
# the typedefinition using snit::method.
794
Mappend compile(defs) {
796
lappend %TYPE%::Snit_optionInfo(local) %OPTION%
798
set %TYPE%::Snit_optionInfo(islocal-%OPTION%) 1
799
set %TYPE%::Snit_optionInfo(resource-%OPTION%) %RESOURCE%
800
set %TYPE%::Snit_optionInfo(class-%OPTION%) %CLASS%
801
set %TYPE%::Snit_optionInfo(default-%OPTION%) %DEFAULT%
802
set %TYPE%::Snit_optionInfo(validate-%OPTION%) %VALIDATE%
803
set %TYPE%::Snit_optionInfo(configure-%OPTION%) %CONFIGURE%
804
set %TYPE%::Snit_optionInfo(cget-%OPTION%) %CGET%
805
set %TYPE%::Snit_optionInfo(readonly-%OPTION%) %READONLY%
807
%RESOURCE% $compile(resource-$option) \
808
%CLASS% $compile(class-$option) \
809
%DEFAULT% [list $compile(-default-$option)] \
810
%VALIDATE% [list $compile(-validatemethod-$option)] \
811
%CONFIGURE% [list $compile(-configuremethod-$option)] \
812
%CGET% [list $compile(-cgetmethod-$option)] \
813
%READONLY% $compile(-readonly-$option)
818
# Evaluates a compiled type definition, thus making the type available.
819
proc ::snit::Comp.Define {compResult} {
820
# The compilation result is a list containing the fully qualified
821
# type name and a script to evaluate to define the type.
822
set type [lindex $compResult 0]
823
set defscript [lindex $compResult 1]
825
# Execute the type definition script.
826
# Consider using namespace eval %TYPE%. See if it's faster.
827
if {[catch {eval $defscript} result]} {
828
namespace delete $type
829
catch {rename $type ""}
836
# Sets pragma options which control how the type is defined.
837
proc ::snit::Comp.statement.pragma {args} {
840
set errRoot "Error in \"pragma...\""
842
foreach {opt val} $args {
843
switch -exact -- $opt {
851
if {![string is boolean -strict $val]} {
852
error "$errRoot, \"$opt\" requires a boolean value"
854
set compile($opt) $val
857
error "$errRoot, unknown pragma"
863
# Defines a widget's option class name.
864
# This statement is only available for snit::widgets,
865
# not for snit::types or snit::widgetadaptors.
866
proc ::snit::Comp.statement.widgetclass {name} {
869
# First, widgetclass can only be set for true widgets
870
if {"widget" != $compile(which)} {
871
error "widgetclass cannot be set for snit::$compile(which)s"
874
# Next, validate the option name. We'll require that it begin
875
# with an uppercase letter.
876
set initial [string index $name 0]
877
if {![string is upper $initial]} {
878
error "widgetclass \"$name\" does not begin with an uppercase letter"
881
if {"" != $compile(widgetclass)} {
882
error "too many widgetclass statements"
886
Mappend compile(defs) {
887
set %TYPE%::Snit_info(widgetclass) %WIDGETCLASS%
888
} %WIDGETCLASS% [list $name]
890
set compile(widgetclass) $name
893
# Defines a widget's hull type.
894
# This statement is only available for snit::widgets,
895
# not for snit::types or snit::widgetadaptors.
896
proc ::snit::Comp.statement.hulltype {name} {
899
# First, hulltype can only be set for true widgets
900
if {"widget" != $compile(which)} {
901
error "hulltype cannot be set for snit::$compile(which)s"
904
# Next, it must be either "frame" or "toplevel"
905
if {"frame" != $name && "toplevel" != $name} {
906
error "invalid hulltype \"$name\", should be \"frame\" or \"toplevel\""
909
if {"" != $compile(hulltype)} {
910
error "too many hulltype statements"
914
Mappend compile(defs) {
915
set %TYPE%::Snit_info(hulltype) %HULLTYPE%
918
set compile(hulltype) $name
921
# Defines a constructor.
922
proc ::snit::Comp.statement.constructor {arglist body} {
925
CheckArgs "constructor" $arglist
927
# Next, add a magic reference to self.
928
set arglist [concat type selfns win self $arglist]
930
# Next, add variable declarations to body:
931
set body "%TVARDECS%%IVARDECS%\n$body"
933
set compile(hasconstructor) yes
934
append compile(defs) "proc %TYPE%::Snit_constructor [list $arglist] [list $body]\n"
937
# Defines a destructor.
938
proc ::snit::Comp.statement.destructor {body} {
941
# Next, add variable declarations to body:
942
set body "%TVARDECS%%IVARDECS%\n$body"
944
append compile(defs) "proc %TYPE%::Snit_destructor {type selfns win self} [list $body]"
947
# Defines a type option. The option value can be a triple, specifying
948
# the option's -name, resource name, and class name.
949
proc ::snit::Comp.statement.option {optionDef args} {
952
# First, get the three option names.
953
set option [lindex $optionDef 0]
954
set resourceName [lindex $optionDef 1]
955
set className [lindex $optionDef 2]
957
set errRoot "Error in \"option [list $optionDef]...\""
959
# Next, validate the option name.
960
if {![Comp.OptionNameIsValid $option]} {
961
error "$errRoot, badly named option \"$option\""
964
if {[Contains $option $compile(delegatedoptions)]} {
965
error "$errRoot, cannot define \"$option\" locally, it has been delegated"
968
if {![Contains $option $compile(localoptions)]} {
969
# Remember that we've seen this one.
970
set compile(hasoptions) yes
971
lappend compile(localoptions) $option
973
# Initialize compilation info for this option.
974
set compile(resource-$option) ""
975
set compile(class-$option) ""
976
set compile(-default-$option) ""
977
set compile(-validatemethod-$option) ""
978
set compile(-configuremethod-$option) ""
979
set compile(-cgetmethod-$option) ""
980
set compile(-readonly-$option) 0
983
# NEXT, see if we have a resource name. If so, make sure it
984
# isn't being redefined differently.
985
if {$resourceName ne ""} {
986
if {$compile(resource-$option) eq ""} {
987
# If it's undefined, just save the value.
988
set compile(resource-$option) $resourceName
989
} elseif {$resourceName ne $compile(resource-$option)} {
990
# It's been redefined differently.
991
error "$errRoot, resource name redefined from \"$compile(resource-$option)\" to \"$resourceName\""
995
# NEXT, see if we have a class name. If so, make sure it
996
# isn't being redefined differently.
997
if {$className ne ""} {
998
if {$compile(class-$option) eq ""} {
999
# If it's undefined, just save the value.
1000
set compile(class-$option) $className
1001
} elseif {$className ne $compile(class-$option)} {
1002
# It's been redefined differently.
1003
error "$errRoot, class name redefined from \"$compile(class-$option)\" to \"$className\""
1007
# NEXT, handle the args; it's not an error to redefine these.
1008
if {[llength $args] == 1} {
1009
set compile(-default-$option) [lindex $args 0]
1011
foreach {optopt val} $args {
1012
switch -exact -- $optopt {
1017
set compile($optopt-$option) $val
1020
if {![string is boolean -strict $val]} {
1021
error "$errRoot, -readonly requires a boolean, got \"$val\""
1023
set compile($optopt-$option) $val
1026
error "$errRoot, unknown option definition option \"$optopt\""
1033
# 1 if the option name is valid, 0 otherwise.
1034
proc ::snit::Comp.OptionNameIsValid {option} {
1035
if {![string match {-*} $option] || [string match {*[A-Z ]*} $option]} {
1042
# Defines an option's cget handler
1043
proc ::snit::Comp.statement.oncget {option body} {
1046
set errRoot "Error in \"oncget $option...\""
1048
if {[lsearch $compile(delegatedoptions) $option] != -1} {
1049
error "$errRoot, option \"$option\" is delegated"
1052
if {[lsearch $compile(localoptions) $option] == -1} {
1053
error "$errRoot, option \"$option\" unknown"
1056
# Next, add variable declarations to body:
1057
set body "%TVARDECS%%IVARDECS%\n$body"
1059
Comp.statement.method _cget$option {_option} $body
1060
Comp.statement.option $option -cgetmethod _cget$option
1063
# Defines an option's configure handler.
1064
proc ::snit::Comp.statement.onconfigure {option arglist body} {
1067
if {[lsearch $compile(delegatedoptions) $option] != -1} {
1068
error "onconfigure $option: option \"$option\" is delegated"
1071
if {[lsearch $compile(localoptions) $option] == -1} {
1072
error "onconfigure $option: option \"$option\" unknown"
1075
if {[llength $arglist] != 1} {
1077
"onconfigure $option handler should have one argument, got \"$arglist\""
1080
CheckArgs "onconfigure $option" $arglist
1082
# Next, add a magic reference to the option name
1083
set arglist [concat _option $arglist]
1085
Comp.statement.method _configure$option $arglist $body
1086
Comp.statement.option $option -configuremethod _configure$option
1089
# Defines an instance method.
1090
proc ::snit::Comp.statement.method {method arglist body} {
1094
# FIRST, check the method name against previously defined
1096
Comp.CheckMethodName $method 0 ::snit::methodInfo \
1097
"Error in \"method [list $method]...\""
1099
if {[llength $method] > 1} {
1100
set compile(hashierarchic) yes
1103
# Remeber this method
1104
lappend compile(localmethods) $method
1106
CheckArgs "method [list $method]" $arglist
1108
# Next, add magic references to type and self.
1109
set arglist [concat type selfns win self $arglist]
1111
# Next, add variable declarations to body:
1112
set body "%TVARDECS%%IVARDECS%\n$body"
1114
# Next, save the definition script.
1115
if {[llength $method] == 1} {
1116
set methodInfo($method) {0 "%t::Snit_method%m %t %n %w %s" ""}
1117
Mappend compile(defs) {
1118
proc %TYPE%::Snit_method%METHOD% %ARGLIST% %BODY%
1119
} %METHOD% $method %ARGLIST% [list $arglist] %BODY% [list $body]
1121
set methodInfo($method) {0 "%t::Snit_hmethod%j %t %n %w %s" ""}
1123
Mappend compile(defs) {
1124
proc %TYPE%::Snit_hmethod%JMETHOD% %ARGLIST% %BODY%
1125
} %JMETHOD% [join $method _] %ARGLIST% [list $arglist] \
1130
# Check for name collisions; save prefix information.
1132
# method The name of the method or typemethod.
1133
# delFlag 1 if delegated, 0 otherwise.
1134
# infoVar The fully qualified name of the array containing
1135
# information about the defined methods.
1136
# errRoot The root string for any error messages.
1138
proc ::snit::Comp.CheckMethodName {method delFlag infoVar errRoot} {
1139
upvar $infoVar methodInfo
1141
# FIRST, make sure the method name is a valid Tcl list.
1142
if {[catch {lindex $method 0}]} {
1143
error "$errRoot, the name \"$method\" must have list syntax."
1146
# NEXT, check whether we can define it.
1147
if {![catch {set methodInfo($method)} data]} {
1148
# We can't redefine methods with submethods.
1149
if {[lindex $data 0] == 1} {
1150
error "$errRoot, \"$method\" has submethods."
1153
# You can't delegate a method that's defined locally,
1154
# and you can't define a method locally if it's been delegated.
1155
if {$delFlag && [lindex $data 2] eq ""} {
1156
error "$errRoot, \"$method\" has been defined locally."
1157
} elseif {!$delFlag && [lindex $data 2] ne ""} {
1158
error "$errRoot, \"$method\" has been delegated"
1162
# Handle hierarchical case.
1163
if {[llength $method] > 1} {
1166
while {[llength $tokens] > 1} {
1167
lappend prefix [lindex $tokens 0]
1168
set tokens [lrange $tokens 1 end]
1170
if {![catch {set methodInfo($prefix)} result]} {
1171
# Prefix is known. If it's not a prefix, throw an
1173
if {[lindex $result 0] == 0} {
1174
error "$errRoot, \"$prefix\" has no submethods."
1178
set methodInfo($prefix) [list 1]
1183
# Defines a typemethod method.
1184
proc ::snit::Comp.statement.typemethod {method arglist body} {
1186
variable typemethodInfo
1188
# FIRST, check the typemethod name against previously defined
1190
Comp.CheckMethodName $method 0 ::snit::typemethodInfo \
1191
"Error in \"typemethod [list $method]...\""
1193
CheckArgs "typemethod $method" $arglist
1195
# First, add magic reference to type.
1196
set arglist [concat type $arglist]
1198
# Next, add typevariable declarations to body:
1199
set body "%TVARDECS%\n$body"
1201
# Next, save the definition script
1202
if {[llength $method] == 1} {
1203
set typemethodInfo($method) {0 "%t::Snit_typemethod%m %t" ""}
1205
Mappend compile(defs) {
1206
proc %TYPE%::Snit_typemethod%METHOD% %ARGLIST% %BODY%
1207
} %METHOD% $method %ARGLIST% [list $arglist] %BODY% [list $body]
1209
set typemethodInfo($method) {0 "%t::Snit_htypemethod%j %t" ""}
1211
Mappend compile(defs) {
1212
proc %TYPE%::Snit_htypemethod%JMETHOD% %ARGLIST% %BODY%
1213
} %JMETHOD% [join $method _] \
1214
%ARGLIST% [list $arglist] %BODY% [list $body]
1219
# Defines a type constructor.
1220
proc ::snit::Comp.statement.typeconstructor {body} {
1223
if {"" != $compile(typeconstructor)} {
1224
error "too many typeconstructors"
1227
set compile(typeconstructor) $body
1230
# Defines a static proc in the type's namespace.
1231
proc ::snit::Comp.statement.proc {proc arglist body} {
1234
# If "ns" is defined, the proc can see instance variables.
1235
if {[lsearch -exact $arglist selfns] != -1} {
1236
# Next, add instance variable declarations to body:
1237
set body "%IVARDECS%\n$body"
1240
# The proc can always see typevariables.
1241
set body "%TVARDECS%\n$body"
1243
append compile(defs) "
1246
proc [list %TYPE%::$proc $arglist $body]
1250
# Defines a static variable in the type's namespace.
1251
proc ::snit::Comp.statement.typevariable {name args} {
1254
set errRoot "Error in \"typevariable $name...\""
1256
set len [llength $args]
1259
($len == 2 && [lindex $args 0] ne "-array")} {
1260
error "$errRoot, too many initializers"
1263
if {[lsearch -exact $compile(varnames) $name] != -1} {
1264
error "$errRoot, \"$name\" is already an instance variable"
1267
lappend compile(typevarnames) $name
1270
append compile(typevars) \
1271
"\n\t [list ::variable $name [lindex $args 0]]"
1272
} elseif {$len == 2} {
1273
append compile(typevars) \
1274
"\n\t [list ::variable $name]"
1275
append compile(typevars) \
1276
"\n\t [list array set $name [lindex $args 1]]"
1278
append compile(typevars) \
1279
"\n\t [list ::variable $name]"
1282
append compile(tvprocdec) "\n\t typevariable ${name}"
1285
# Defines an instance variable; the definition will go in the
1286
# type's create typemethod.
1287
proc ::snit::Comp.statement.variable {name args} {
1290
set errRoot "Error in \"variable $name...\""
1292
set len [llength $args]
1295
($len == 2 && [lindex $args 0] ne "-array")} {
1296
error "$errRoot, too many initializers"
1299
if {[lsearch -exact $compile(typevarnames) $name] != -1} {
1300
error "$errRoot, \"$name\" is already a typevariable"
1303
lappend compile(varnames) $name
1306
append compile(instancevars) \
1307
"\nset \${selfns}::$name [list [lindex $args 0]]\n"
1308
} elseif {$len == 2} {
1309
append compile(instancevars) \
1310
"\narray set \${selfns}::$name [list [lindex $args 1]]\n"
1313
append compile(ivprocdec) "\n\t "
1314
Mappend compile(ivprocdec) {::variable ${selfns}::%N} %N $name
1317
# Defines a typecomponent, and handles component options.
1319
# component The logical name of the delegate
1322
proc ::snit::Comp.statement.typecomponent {component args} {
1325
set errRoot "Error in \"typecomponent $component...\""
1327
# FIRST, define the component
1328
Comp.DefineTypecomponent $component $errRoot
1330
# NEXT, handle the options.
1334
foreach {opt val} $args {
1335
switch -exact -- $opt {
1337
set publicMethod $val
1340
set inheritFlag $val
1341
if {![string is boolean $inheritFlag]} {
1342
error "typecomponent $component -inherit: expected boolean value, got \"$val\""
1346
error "typecomponent $component: Invalid option \"$opt\""
1351
# NEXT, if -public specified, define the method.
1352
if {$publicMethod ne ""} {
1353
Comp.statement.delegate typemethod [list $publicMethod *] to $component
1356
# NEXT, if "-inherit 1" is specified, delegate typemethod * to
1359
Comp.statement.delegate typemethod "*" to $component
1365
# Defines a name to be a typecomponent
1367
# The name becomes a typevariable; in addition, it gets a
1368
# write trace so that when it is set, all of the component mechanisms
1371
# component The component name
1373
proc ::snit::Comp.DefineTypecomponent {component {errRoot "Error"}} {
1376
if {[lsearch -exact $compile(varnames) $component] != -1} {
1377
error "$errRoot, \"$component\" is already an instance variable"
1380
if {[lsearch $compile(typecomponents) $component] == -1} {
1381
# Remember we've done this.
1382
lappend compile(typecomponents) $component
1384
# Make it a type variable with no initial value
1385
Comp.statement.typevariable $component ""
1387
# Add a write trace to do the component thing.
1388
Mappend compile(typevars) {
1389
trace add variable %COMP% write \
1390
[list ::snit::RT.TypecomponentTrace [list %TYPE%] %COMP%]
1391
} %TYPE% $compile(type) %COMP% $component
1395
# Defines a component, and handles component options.
1397
# component The logical name of the delegate
1400
# TBD: Ideally, it should be possible to call this statement multiple
1401
# times, possibly changing the option values. To do that, I'd need
1402
# to cache the option values and not act on them until *after* I'd
1403
# read the entire type definition.
1405
proc ::snit::Comp.statement.component {component args} {
1408
set errRoot "Error in \"component $component...\""
1410
# FIRST, define the component
1411
Comp.DefineComponent $component $errRoot
1413
# NEXT, handle the options.
1417
foreach {opt val} $args {
1418
switch -exact -- $opt {
1420
set publicMethod $val
1423
set inheritFlag $val
1424
if {![string is boolean $inheritFlag]} {
1425
error "component $component -inherit: expected boolean value, got \"$val\""
1429
error "component $component: Invalid option \"$opt\""
1434
# NEXT, if -public specified, define the method.
1435
if {$publicMethod ne ""} {
1436
Comp.statement.delegate method [list $publicMethod *] to $component
1439
# NEXT, if -inherit is specified, delegate method/option * to
1442
Comp.statement.delegate method "*" to $component
1443
Comp.statement.delegate option "*" to $component
1448
# Defines a name to be a component
1450
# The name becomes an instance variable; in addition, it gets a
1451
# write trace so that when it is set, all of the component mechanisms
1454
# component The component name
1456
proc ::snit::Comp.DefineComponent {component {errRoot "Error"}} {
1459
if {[lsearch -exact $compile(typevarnames) $component] != -1} {
1460
error "$errRoot, \"$component\" is already a typevariable"
1463
if {[lsearch $compile(components) $component] == -1} {
1464
# Remember we've done this.
1465
lappend compile(components) $component
1467
# Make it an instance variable with no initial value
1468
Comp.statement.variable $component ""
1470
# Add a write trace to do the component thing.
1471
Mappend compile(instancevars) {
1472
trace add variable ${selfns}::%COMP% write \
1473
[list ::snit::RT.ComponentTrace [list %TYPE%] $selfns %COMP%]
1474
} %TYPE% $compile(type) %COMP% $component
1478
# Creates a delegated method, typemethod, or option.
1479
proc ::snit::Comp.statement.delegate {what name args} {
1480
# FIRST, dispatch to correct handler.
1482
typemethod { Comp.DelegatedTypemethod $name $args }
1483
method { Comp.DelegatedMethod $name $args }
1484
option { Comp.DelegatedOption $name $args }
1486
error "Error in \"delegate $what $name...\", \"$what\"?"
1490
if {([llength $args] % 2) != 0} {
1491
error "Error in \"delegate $what $name...\", invalid syntax"
1495
# Creates a delegated typemethod delegating it to a particular
1496
# typecomponent or an arbitrary command.
1498
# method The name of the method
1499
# arglist Delegation options
1501
proc ::snit::Comp.DelegatedTypemethod {method arglist} {
1503
variable typemethodInfo
1505
set errRoot "Error in \"delegate typemethod [list $method]...\""
1507
# Next, parse the delegation options.
1512
set methodTail [lindex $method end]
1514
foreach {opt value} $arglist {
1515
switch -exact $opt {
1516
to { set component $value }
1517
as { set target $value }
1518
except { set exceptions $value }
1519
using { set pattern $value }
1521
error "$errRoot, unknown delegation option \"$opt\""
1526
if {$component eq "" && $pattern eq ""} {
1527
error "$errRoot, missing \"to\""
1530
if {$methodTail eq "*" && $target ne ""} {
1531
error "$errRoot, cannot specify \"as\" with \"*\""
1534
if {$methodTail ne "*" && $exceptions ne ""} {
1535
error "$errRoot, can only specify \"except\" with \"*\""
1538
if {$pattern ne "" && $target ne ""} {
1539
error "$errRoot, cannot specify both \"as\" and \"using\""
1542
foreach token [lrange $method 1 end-1] {
1543
if {$token eq "*"} {
1544
error "$errRoot, \"*\" must be the last token."
1548
# NEXT, define the component
1549
if {$component ne ""} {
1550
Comp.DefineTypecomponent $component $errRoot
1553
# NEXT, define the pattern.
1554
if {$pattern eq ""} {
1555
if {$methodTail eq "*"} {
1557
} elseif {$target ne ""} {
1558
set pattern "%c $target"
1564
# Make sure the pattern is a valid list.
1565
if {[catch {lindex $pattern 0} result]} {
1566
error "$errRoot, the using pattern, \"$pattern\", is not a valid list"
1569
# NEXT, check the method name against previously defined
1571
Comp.CheckMethodName $method 1 ::snit::typemethodInfo $errRoot
1573
set typemethodInfo($method) [list 0 $pattern $component]
1575
if {[string equal $methodTail "*"]} {
1576
Mappend compile(defs) {
1577
set %TYPE%::Snit_info(excepttypemethods) %EXCEPT%
1578
} %EXCEPT% [list $exceptions]
1583
# Creates a delegated method delegating it to a particular
1584
# component or command.
1586
# method The name of the method
1587
# arglist Delegation options.
1589
proc ::snit::Comp.DelegatedMethod {method arglist} {
1593
set errRoot "Error in \"delegate method [list $method]...\""
1595
# Next, parse the delegation options.
1600
set methodTail [lindex $method end]
1602
foreach {opt value} $arglist {
1603
switch -exact $opt {
1604
to { set component $value }
1605
as { set target $value }
1606
except { set exceptions $value }
1607
using { set pattern $value }
1609
error "$errRoot, unknown delegation option \"$opt\""
1614
if {$component eq "" && $pattern eq ""} {
1615
error "$errRoot, missing \"to\""
1618
if {$methodTail eq "*" && $target ne ""} {
1619
error "$errRoot, cannot specify \"as\" with \"*\""
1622
if {$methodTail ne "*" && $exceptions ne ""} {
1623
error "$errRoot, can only specify \"except\" with \"*\""
1626
if {$pattern ne "" && $target ne ""} {
1627
error "$errRoot, cannot specify both \"as\" and \"using\""
1630
foreach token [lrange $method 1 end-1] {
1631
if {$token eq "*"} {
1632
error "$errRoot, \"*\" must be the last token."
1636
# NEXT, we delegate some methods
1637
set compile(delegatesmethods) yes
1639
# NEXT, define the component. Allow typecomponents.
1640
if {$component ne ""} {
1641
if {[lsearch -exact $compile(typecomponents) $component] == -1} {
1642
Comp.DefineComponent $component $errRoot
1646
# NEXT, define the pattern.
1647
if {$pattern eq ""} {
1648
if {$methodTail eq "*"} {
1650
} elseif {$target ne ""} {
1651
set pattern "%c $target"
1657
# Make sure the pattern is a valid list.
1658
if {[catch {lindex $pattern 0} result]} {
1659
error "$errRoot, the using pattern, \"$pattern\", is not a valid list"
1662
# NEXT, check the method name against previously defined
1664
Comp.CheckMethodName $method 1 ::snit::methodInfo $errRoot
1666
# NEXT, save the method info.
1667
set methodInfo($method) [list 0 $pattern $component]
1669
if {[string equal $methodTail "*"]} {
1670
Mappend compile(defs) {
1671
set %TYPE%::Snit_info(exceptmethods) %EXCEPT%
1672
} %EXCEPT% [list $exceptions]
1676
# Creates a delegated option, delegating it to a particular
1677
# component and, optionally, to a particular option of that
1680
# optionDef The option definition
1681
# args definition arguments.
1683
proc ::snit::Comp.DelegatedOption {optionDef arglist} {
1686
# First, get the three option names.
1687
set option [lindex $optionDef 0]
1688
set resourceName [lindex $optionDef 1]
1689
set className [lindex $optionDef 2]
1691
set errRoot "Error in \"delegate option [list $optionDef]...\""
1693
# Next, parse the delegation options.
1698
foreach {opt value} $arglist {
1699
switch -exact $opt {
1700
to { set component $value }
1701
as { set target $value }
1702
except { set exceptions $value }
1704
error "$errRoot, unknown delegation option \"$opt\""
1709
if {$component eq ""} {
1710
error "$errRoot, missing \"to\""
1713
if {$option eq "*" && $target ne ""} {
1714
error "$errRoot, cannot specify \"as\" with \"delegate option *\""
1717
if {$option ne "*" && $exceptions ne ""} {
1718
error "$errRoot, can only specify \"except\" with \"delegate option *\""
1721
# Next, validate the option name
1723
if {"*" != $option} {
1724
if {![Comp.OptionNameIsValid $option]} {
1725
error "$errRoot, badly named option \"$option\""
1729
if {[Contains $option $compile(localoptions)]} {
1730
error "$errRoot, \"$option\" has been defined locally"
1733
if {[Contains $option $compile(delegatedoptions)]} {
1734
error "$errRoot, \"$option\" is multiply delegated"
1737
# NEXT, define the component
1738
Comp.DefineComponent $component $errRoot
1740
# Next, define the target option, if not specified.
1741
if {![string equal $option "*"] &&
1742
[string equal $target ""]} {
1746
# NEXT, save the delegation data.
1747
set compile(hasoptions) yes
1749
if {![string equal $option "*"]} {
1750
lappend compile(delegatedoptions) $option
1752
# Next, compute the resource and class names, if they aren't
1755
if {"" == $resourceName} {
1756
set resourceName [string range $option 1 end]
1759
if {"" == $className} {
1760
set className [Capitalize $resourceName]
1763
Mappend compile(defs) {
1764
set %TYPE%::Snit_optionInfo(islocal-%OPTION%) 0
1765
set %TYPE%::Snit_optionInfo(resource-%OPTION%) %RES%
1766
set %TYPE%::Snit_optionInfo(class-%OPTION%) %CLASS%
1767
lappend %TYPE%::Snit_optionInfo(delegated) %OPTION%
1768
set %TYPE%::Snit_optionInfo(target-%OPTION%) [list %COMP% %TARGET%]
1769
lappend %TYPE%::Snit_optionInfo(delegated-%COMP%) %OPTION%
1770
} %OPTION% $option \
1773
%RES% $resourceName \
1776
Mappend compile(defs) {
1777
set %TYPE%::Snit_optionInfo(starcomp) %COMP%
1778
set %TYPE%::Snit_optionInfo(except) %EXCEPT%
1779
} %COMP% $component %EXCEPT% [list $exceptions]
1783
# Exposes a component, effectively making the component's command an
1786
# component The logical name of the delegate
1787
# "as" sugar; if not "", must be "as"
1788
# methodname The desired method name for the component's command, or ""
1790
proc ::snit::Comp.statement.expose {component {"as" ""} {methodname ""}} {
1794
# FIRST, define the component
1795
Comp.DefineComponent $component
1797
# NEXT, define the method just as though it were in the type
1799
if {[string equal $methodname ""]} {
1800
set methodname $component
1803
Comp.statement.method $methodname args [Expand {
1804
if {[llength $args] == 0} {
1808
if {[string equal $%COMPONENT% ""]} {
1809
error "undefined component \"%COMPONENT%\""
1813
set cmd [linsert $args 0 $%COMPONENT%]
1814
return [uplevel 1 $cmd]
1815
} %COMPONENT% $component]
1820
#-----------------------------------------------------------------------
1823
# Compile a type definition, and return the results as a list of two
1824
# items: the fully-qualified type name, and a script that will define
1825
# the type when executed.
1827
# which type, widget, or widgetadaptor
1828
# type the type name
1829
# body the type definition
1830
proc ::snit::compile {which type body} {
1831
return [Comp.Compile $which $type $body]
1834
proc ::snit::type {type body} {
1835
return [Comp.Define [Comp.Compile type $type $body]]
1838
proc ::snit::widget {type body} {
1839
return [Comp.Define [Comp.Compile widget $type $body]]
1842
proc ::snit::widgetadaptor {type body} {
1843
return [Comp.Define [Comp.Compile widgetadaptor $type $body]]
1846
proc ::snit::typemethod {type method arglist body} {
1847
# Make sure the type exists.
1848
if {![info exists ${type}::Snit_info]} {
1849
error "no such type: \"$type\""
1852
upvar ${type}::Snit_info Snit_info
1853
upvar ${type}::Snit_typemethodInfo Snit_typemethodInfo
1855
# FIRST, check the typemethod name against previously defined
1857
Comp.CheckMethodName $method 0 ${type}::Snit_typemethodInfo \
1858
"Cannot define \"$method\""
1860
# NEXT, check the arguments
1861
CheckArgs "snit::typemethod $type $method" $arglist
1863
# Next, add magic reference to type.
1864
set arglist [concat type $arglist]
1866
# Next, add typevariable declarations to body:
1867
set body "$Snit_info(tvardecs)\n$body"
1870
if {[llength $method] == 1} {
1871
set Snit_typemethodInfo($method) {0 "%t::Snit_typemethod%m %t" ""}
1872
uplevel [list proc ${type}::Snit_typemethod$method $arglist $body]
1874
set Snit_typemethodInfo($method) {0 "%t::Snit_htypemethod%j %t" ""}
1875
set suffix [join $method _]
1876
uplevel [list proc ${type}::Snit_htypemethod$suffix $arglist $body]
1880
proc ::snit::method {type method arglist body} {
1881
# Make sure the type exists.
1882
if {![info exists ${type}::Snit_info]} {
1883
error "no such type: \"$type\""
1886
upvar ${type}::Snit_methodInfo Snit_methodInfo
1887
upvar ${type}::Snit_info Snit_info
1889
# FIRST, check the method name against previously defined
1891
Comp.CheckMethodName $method 0 ${type}::Snit_methodInfo \
1892
"Cannot define \"$method\""
1894
# NEXT, check the arguments
1895
CheckArgs "snit::method $type $method" $arglist
1897
# Next, add magic references to type and self.
1898
set arglist [concat type selfns win self $arglist]
1900
# Next, add variable declarations to body:
1901
set body "$Snit_info(tvardecs)$Snit_info(ivardecs)\n$body"
1904
if {[llength $method] == 1} {
1905
set Snit_methodInfo($method) {0 "%t::Snit_method%m %t %n %w %s" ""}
1906
uplevel [list proc ${type}::Snit_method$method $arglist $body]
1908
set Snit_methodInfo($method) {0 "%t::Snit_hmethod%j %t %n %w %s" ""}
1910
set suffix [join $method _]
1911
uplevel [list proc ${type}::Snit_hmethod$suffix $arglist $body]
1915
# Defines a proc within the compiler; this proc can call other
1916
# type definition statements, and thus can be used for meta-programming.
1917
proc ::snit::macro {name arglist body} {
1919
variable reservedwords
1921
# FIRST, make sure the compiler is defined.
1924
# NEXT, check the macro name against the reserved words
1925
if {[lsearch -exact $reservedwords $name] != -1} {
1926
error "invalid macro name \"$name\""
1929
# NEXT, see if the name has a namespace; if it does, define the
1931
set ns [namespace qualifiers $name]
1934
$compiler eval "namespace eval $ns {}"
1937
# NEXT, define the macro
1938
$compiler eval [list _proc $name $arglist $body]
1941
#-----------------------------------------------------------------------
1944
# These are utility functions used while compiling Snit types.
1946
# Builds a template from a tagged list of text blocks, then substitutes
1947
# all symbols in the mapTable, returning the expanded template.
1948
proc ::snit::Expand {template args} {
1949
return [string map $args $template]
1952
# Expands a template and appends it to a variable.
1953
proc ::snit::Mappend {varname template args} {
1954
upvar $varname myvar
1956
append myvar [string map $args $template]
1959
# Checks argument list against reserved args
1960
proc ::snit::CheckArgs {which arglist} {
1961
variable reservedArgs
1963
foreach name $reservedArgs {
1964
if {[Contains $name $arglist]} {
1965
error "$which's arglist may not contain \"$name\" explicitly"
1970
# Returns 1 if a value is in a list, and 0 otherwise.
1971
proc ::snit::Contains {value list} {
1972
if {[lsearch -exact $list $value] != -1} {
1979
# Capitalizes the first letter of a string.
1980
proc ::snit::Capitalize {text} {
1981
set first [string index $text 0]
1982
set rest [string range $text 1 end]
1983
return "[string toupper $first]$rest"
1986
# Converts an arbitrary white-space-delimited string into a list
1987
# by splitting on white-space and deleting empty tokens.
1989
proc ::snit::Listify {str} {
1991
foreach token [split [string trim $str]] {
1992
if {[string length $token] > 0} {
1993
lappend result $token
2001
#=======================================================================
2002
# Snit Runtime Library
2004
# These are procs used by Snit types and widgets at runtime.
2006
#-----------------------------------------------------------------------
2009
# Creates a new instance of the snit::type given its name and the args.
2011
# type The snit::type
2012
# name The instance name
2013
# args Args to pass to the constructor
2015
proc ::snit::RT.type.typemethod.create {type name args} {
2016
variable ${type}::Snit_info
2017
variable ${type}::Snit_optionInfo
2019
# FIRST, qualify the name.
2020
if {![string match "::*" $name]} {
2021
# Get caller's namespace;
2022
# append :: if not global namespace.
2023
set ns [uplevel 1 namespace current]
2031
# NEXT, if %AUTO% appears in the name, generate a unique
2032
# command name. Otherwise, ensure that the name isn't in use.
2033
if {[string match "*%AUTO%*" $name]} {
2034
set name [::snit::RT.UniqueName Snit_info(counter) $type $name]
2035
} elseif {!$Snit_info(canreplace) && [info commands $name] ne ""} {
2036
error "command \"$name\" already exists"
2039
# NEXT, create the instance's namespace.
2041
[::snit::RT.UniqueInstanceNamespace Snit_info(counter) $type]
2042
namespace eval $selfns {}
2044
# NEXT, install the dispatcher
2045
RT.MakeInstanceCommand $type $selfns $name
2047
# Initialize the options to their defaults.
2048
upvar ${selfns}::options options
2049
foreach opt $Snit_optionInfo(local) {
2050
set options($opt) $Snit_optionInfo(default-$opt)
2053
# Initialize the instance vars to their defaults.
2054
# selfns must be defined, as it is used implicitly.
2055
${type}::Snit_instanceVars $selfns
2057
# Execute the type's constructor.
2058
set errcode [catch {
2059
RT.ConstructInstance $type $selfns $name $args
2066
set theInfo $errorInfo
2067
set theCode $errorCode
2068
::snit::RT.DestroyObject $type $selfns $name
2069
error "Error in constructor: $result" $theInfo $theCode
2072
# NEXT, return the object's name.
2076
# Creates a new instance of the snit::widget or snit::widgetadaptor
2077
# given its name and the args.
2079
# type The snit::widget or snit::widgetadaptor
2080
# name The instance name
2081
# args Args to pass to the constructor
2083
proc ::snit::RT.widget.typemethod.create {type name args} {
2084
variable ${type}::Snit_info
2085
variable ${type}::Snit_optionInfo
2087
# FIRST, if %AUTO% appears in the name, generate a unique
2089
if {[string match "*%AUTO%*" $name]} {
2090
set name [::snit::RT.UniqueName Snit_info(counter) $type $name]
2093
# NEXT, create the instance's namespace.
2095
[::snit::RT.UniqueInstanceNamespace Snit_info(counter) $type]
2096
namespace eval $selfns { }
2098
# NEXT, Initialize the widget's own options to their defaults.
2099
upvar ${selfns}::options options
2100
foreach opt $Snit_optionInfo(local) {
2101
set options($opt) $Snit_optionInfo(default-$opt)
2104
# Initialize the instance vars to their defaults.
2105
${type}::Snit_instanceVars $selfns
2107
# NEXT, if this is a normal widget (not a widget adaptor) then
2108
# create a frame as its hull. We set the frame's -class to
2109
# the user's widgetclass, or, if none, to the basename of
2110
# the $type with an initial upper case letter.
2111
if {!$Snit_info(isWidgetAdaptor)} {
2112
# FIRST, determine the class name
2113
if {"" == $Snit_info(widgetclass)} {
2114
set Snit_info(widgetclass) \
2115
[::snit::Capitalize [namespace tail $type]]
2118
# NEXT, create the widget
2121
${type}::installhull using \
2122
$Snit_info(hulltype) -class $Snit_info(widgetclass)
2124
# NEXT, let's query the option database for our
2125
# widget, now that we know that it exists.
2126
foreach opt $Snit_optionInfo(local) {
2127
set dbval [RT.OptionDbGet $type $name $opt]
2130
set options($opt) $dbval
2135
# Execute the type's constructor, and verify that it
2137
set errcode [catch {
2138
RT.ConstructInstance $type $selfns $name $args
2140
::snit::RT.Component $type $selfns hull
2142
# Prepare to call the object's destructor when the
2143
# <Destroy> event is received. Use a Snit-specific bindtag
2144
# so that the widget name's tag is unencumbered.
2146
bind Snit$type$name <Destroy> [::snit::Expand {
2147
::snit::RT.DestroyObject %TYPE% %NS% %W
2148
} %TYPE% $type %NS% $selfns]
2150
# Insert the bindtag into the list of bindtags right
2151
# after the widget name.
2152
set taglist [bindtags $name]
2153
set ndx [lsearch $taglist $name]
2155
bindtags $name [linsert $taglist $ndx Snit$type$name]
2162
set theInfo $errorInfo
2163
set theCode $errorCode
2164
::snit::RT.DestroyObject $type $selfns $name
2165
error "Error in constructor: $result" $theInfo $theCode
2168
# NEXT, return the object's name.
2173
# RT.MakeInstanceCommand type selfns instance
2175
# type The object type
2176
# selfns The instance namespace
2177
# instance The instance name
2179
# Creates the instance proc.
2181
proc ::snit::RT.MakeInstanceCommand {type selfns instance} {
2182
variable ${type}::Snit_info
2184
# FIRST, remember the instance name. The Snit_instance variable
2185
# allows the instance to figure out its current name given the
2186
# instance namespace.
2187
upvar ${selfns}::Snit_instance Snit_instance
2188
set Snit_instance $instance
2190
# NEXT, qualify the proc name if it's a widget.
2191
if {$Snit_info(isWidget)} {
2192
set procname ::$instance
2194
set procname $instance
2197
# NEXT, install the new proc
2198
if {!$Snit_info(simpledispatch)} {
2199
set instanceProc $::snit::nominalInstanceProc
2201
set instanceProc $::snit::simpleInstanceProc
2204
proc $procname {method args} \
2206
[list %SELFNS% $selfns %WIN% $instance %TYPE% $type] \
2209
# NEXT, add the trace.
2210
trace add command $procname {rename delete} \
2211
[list ::snit::RT.InstanceTrace $type $selfns $instance]
2214
# This proc is called when the instance command is renamed.
2215
# If op is delete, then new will always be "", so op is redundant.
2217
# type The fully-qualified type name
2218
# selfns The instance namespace
2219
# win The original instance/tk window name.
2220
# old old instance command name
2221
# new new instance command name
2222
# op rename or delete
2224
# If the op is delete, we need to clean up the object; otherwise,
2225
# we need to track the change.
2227
# NOTE: In Tcl 8.4.2 there's a bug: errors in rename and delete
2228
# traces aren't propagated correctly. Instead, they silently
2229
# vanish. Add a catch to output any error message.
2231
proc ::snit::RT.InstanceTrace {type selfns win old new op} {
2232
variable ${type}::Snit_info
2234
# Note to developers ...
2235
# For Tcl 8.4.0, errors thrown in trace handlers vanish silently.
2236
# Therefore we catch them here and create some output to help in
2237
# debugging such problems.
2240
# FIRST, clean up if necessary
2242
if {$Snit_info(isWidget)} {
2245
::snit::RT.DestroyObject $type $selfns $win
2248
# Otherwise, track the change.
2249
variable ${selfns}::Snit_instance
2250
set Snit_instance [uplevel namespace which -command $new]
2252
# Also, clear the instance caches, as many cached commands
2254
RT.ClearInstanceCaches $selfns
2258
# Pop up the console on Windows wish, to enable stdout.
2259
# This clobbers errorInfo on unix, so save it so we can print it.
2261
catch {console show}
2262
puts "Error in ::snit::RT.InstanceTrace $type $selfns $win $old $new $op:"
2267
# Calls the instance constructor and handles related housekeeping.
2268
proc ::snit::RT.ConstructInstance {type selfns instance arglist} {
2269
variable ${type}::Snit_optionInfo
2270
variable ${selfns}::Snit_iinfo
2272
# Track whether we are constructed or not.
2273
set Snit_iinfo(constructed) 0
2275
# Call the user's constructor
2276
eval [linsert $arglist 0 \
2277
${type}::Snit_constructor $type $selfns $instance $instance]
2279
set Snit_iinfo(constructed) 1
2281
# Unset the configure cache for all -readonly options.
2282
# This ensures that the next time anyone tries to
2283
# configure it, an error is thrown.
2284
foreach opt $Snit_optionInfo(local) {
2285
if {$Snit_optionInfo(readonly-$opt)} {
2286
unset -nocomplain ${selfns}::Snit_configureCache($opt)
2293
# Returns a unique command name.
2295
# REQUIRE: type is a fully qualified name.
2296
# REQUIRE: name contains "%AUTO%"
2297
# PROMISE: the returned command name is unused.
2298
proc ::snit::RT.UniqueName {countervar type name} {
2299
upvar $countervar counter
2301
# FIRST, bump the counter and define the %AUTO% instance name;
2302
# then substitute it into the specified name. Wrap around at
2303
# 2^31 - 2 to prevent overflow problems.
2305
if {$counter > 2147483646} {
2308
set auto "[namespace tail $type]$counter"
2309
set candidate [Expand $name %AUTO% $auto]
2310
if {[info commands $candidate] eq ""} {
2316
# Returns a unique instance namespace, fully qualified.
2318
# countervar The name of a counter variable
2319
# type The instance's type
2321
# REQUIRE: type is fully qualified
2322
# PROMISE: The returned namespace name is unused.
2324
proc ::snit::RT.UniqueInstanceNamespace {countervar type} {
2325
upvar $countervar counter
2327
# FIRST, bump the counter and define the namespace name.
2328
# Then see if it already exists. Wrap around at
2329
# 2^31 - 2 to prevent overflow problems.
2331
if {$counter > 2147483646} {
2334
set ins "${type}::Snit_inst${counter}"
2335
if {![namespace exists $ins]} {
2341
# Retrieves an option's value from the option database.
2342
# Returns "" if no value is found.
2343
proc ::snit::RT.OptionDbGet {type self opt} {
2344
variable ${type}::Snit_optionInfo
2346
return [option get $self \
2347
$Snit_optionInfo(resource-$opt) \
2348
$Snit_optionInfo(class-$opt)]
2351
#-----------------------------------------------------------------------
2352
# Object Destruction
2354
# Implements the standard "destroy" method
2356
# type The snit type
2357
# selfns The instance's instance namespace
2358
# win The instance's original name
2359
# self The instance's current name
2361
proc ::snit::RT.method.destroy {type selfns win self} {
2362
# Calls Snit_cleanup, which (among other things) calls the
2363
# user's destructor.
2364
::snit::RT.DestroyObject $type $selfns $win
2367
# This is the function that really cleans up; it's automatically
2368
# called when any instance is destroyed, e.g., by "$object destroy"
2369
# for types, and by the <Destroy> event for widgets.
2371
# type The fully-qualified type name.
2372
# selfns The instance namespace
2373
# win The original instance command name.
2375
proc ::snit::RT.DestroyObject {type selfns win} {
2376
variable ${type}::Snit_info
2378
# If the variable Snit_instance doesn't exist then there's no
2379
# instance command for this object -- it's most likely a
2380
# widgetadaptor. Consequently, there are some things that
2381
# we don't need to do.
2382
if {[info exists ${selfns}::Snit_instance]} {
2383
upvar ${selfns}::Snit_instance instance
2385
# First, remove the trace on the instance name, so that we
2386
# don't call RT.DestroyObject recursively.
2387
RT.RemoveInstanceTrace $type $selfns $win $instance
2389
# Next, call the user's destructor
2390
${type}::Snit_destructor $type $selfns $win $instance
2392
# Next, if this isn't a widget, delete the instance command.
2393
# If it is a widget, get the hull component's name, and rename
2394
# it back to the widget name
2396
# Next, delete the hull component's instance command,
2398
if {$Snit_info(isWidget)} {
2399
set hullcmd [::snit::RT.Component $type $selfns hull]
2401
catch {rename $instance ""}
2403
# Clear the bind event
2404
bind Snit$type$win <Destroy> ""
2406
if {[info command $hullcmd] != ""} {
2407
rename $hullcmd ::$instance
2410
catch {rename $instance ""}
2414
# Next, delete the instance's namespace. This kills any
2415
# instance variables.
2416
namespace delete $selfns
2419
# Remove instance trace
2421
# type The fully qualified type name
2422
# selfns The instance namespace
2423
# win The original instance name/Tk window name
2424
# instance The current instance name
2426
proc ::snit::RT.RemoveInstanceTrace {type selfns win instance} {
2427
variable ${type}::Snit_info
2429
if {$Snit_info(isWidget)} {
2430
set procname ::$instance
2432
set procname $instance
2435
# NEXT, remove any trace on this name
2437
trace remove command $procname {rename delete} \
2438
[list ::snit::RT.InstanceTrace $type $selfns $win]
2442
#-----------------------------------------------------------------------
2443
# Typecomponent Management and Method Caching
2445
# Typecomponent trace; used for write trace on typecomponent
2446
# variables. Saves the new component object name, provided
2447
# that certain conditions are met. Also clears the typemethod
2450
proc ::snit::RT.TypecomponentTrace {type component n1 n2 op} {
2451
upvar ${type}::Snit_info Snit_info
2452
upvar ${type}::${component} cvar
2453
upvar ${type}::Snit_typecomponents Snit_typecomponents
2455
# Save the new component value.
2456
set Snit_typecomponents($component) $cvar
2458
# Clear the typemethod cache.
2459
# TBD: can we unset just the elements related to
2461
unset -nocomplain -- ${type}::Snit_typemethodCache
2464
# Generates and caches the command for a typemethod.
2467
# method The name of the typemethod to call.
2469
# The return value is one of the following lists:
2471
# {} There's no such method.
2472
# {1} The method has submethods; look again.
2473
# {0 <command>} Here's the command to execute.
2475
proc snit::RT.CacheTypemethodCommand {type method} {
2476
upvar ${type}::Snit_typemethodInfo Snit_typemethodInfo
2477
upvar ${type}::Snit_typecomponents Snit_typecomponents
2478
upvar ${type}::Snit_typemethodCache Snit_typemethodCache
2479
upvar ${type}::Snit_info Snit_info
2481
# FIRST, get the pattern data and the typecomponent name.
2482
set implicitCreate 0
2485
set starredMethod [lreplace $method end end *]
2486
set methodTail [lindex $method end]
2488
if {[info exists Snit_typemethodInfo($method)]} {
2490
} elseif {[info exists Snit_typemethodInfo($starredMethod)]} {
2491
if {[lsearch -exact $Snit_info(excepttypemethods) $methodTail] == -1} {
2492
set key $starredMethod
2496
} elseif {$Snit_info(hasinstances)} {
2497
# Assume the unknown name is an instance name to create, unless
2498
# this is a widget and the style of the name is wrong, or the
2499
# name mimics a standard typemethod.
2501
if {[set ${type}::Snit_info(isWidget)] &&
2502
![string match ".*" $method]} {
2506
# Without this check, the call "$type info" will redefine the
2507
# standard "::info" command, with disastrous results. Since it's
2508
# a likely thing to do if !-typeinfo, put in an explicit check.
2509
if {$method eq "info" || $method eq "destroy"} {
2513
set implicitCreate 1
2514
set instanceName $method
2521
foreach {flag pattern compName} $Snit_typemethodInfo($key) {}
2527
# NEXT, build the substitution list
2532
%m [lindex $method end] \
2533
%j [join $method _]]
2535
if {$compName ne ""} {
2536
if {![info exists Snit_typecomponents($compName)]} {
2537
error "$type delegates typemethod \"$method\" to undefined typecomponent \"$compName\""
2540
lappend subList %c [list $Snit_typecomponents($compName)]
2545
foreach subpattern $pattern {
2546
lappend command [string map $subList $subpattern]
2549
if {$implicitCreate} {
2550
# In this case, $method is the name of the instance to
2551
# create. Don't cache, as we usually won't do this one
2553
lappend command $instanceName
2555
set Snit_typemethodCache($method) [list 0 $command]
2558
return [list 0 $command]
2562
#-----------------------------------------------------------------------
2563
# Component Management and Method Caching
2565
# Retrieves the object name given the component name.
2566
proc ::snit::RT.Component {type selfns name} {
2567
variable ${selfns}::Snit_components
2569
if {[catch {set Snit_components($name)} result]} {
2570
variable ${selfns}::Snit_instance
2572
error "component \"$name\" is undefined in $type $Snit_instance"
2578
# Component trace; used for write trace on component instance
2579
# variables. Saves the new component object name, provided
2580
# that certain conditions are met. Also clears the method
2583
proc ::snit::RT.ComponentTrace {type selfns component n1 n2 op} {
2584
upvar ${type}::Snit_info Snit_info
2585
upvar ${selfns}::${component} cvar
2586
upvar ${selfns}::Snit_components Snit_components
2588
# If they try to redefine the hull component after
2589
# it's been defined, that's an error--but only if
2590
# this is a widget or widget adaptor.
2591
if {"hull" == $component &&
2592
$Snit_info(isWidget) &&
2593
[info exists Snit_components($component)]} {
2594
set cvar $Snit_components($component)
2595
error "The hull component cannot be redefined"
2598
# Save the new component value.
2599
set Snit_components($component) $cvar
2601
# Clear the instance caches.
2602
# TBD: can we unset just the elements related to
2604
RT.ClearInstanceCaches $selfns
2607
# Generates and caches the command for a method.
2609
# type: The instance's type
2610
# selfns: The instance's private namespace
2611
# win: The instance's original name (a Tk widget name, for
2613
# self: The instance's current name.
2614
# method: The name of the method to call.
2616
# The return value is one of the following lists:
2618
# {} There's no such method.
2619
# {1} The method has submethods; look again.
2620
# {0 <command>} Here's the command to execute.
2622
proc ::snit::RT.CacheMethodCommand {type selfns win self method} {
2623
variable ${type}::Snit_info
2624
variable ${type}::Snit_methodInfo
2625
variable ${type}::Snit_typecomponents
2626
variable ${selfns}::Snit_components
2627
variable ${selfns}::Snit_methodCache
2629
# FIRST, get the pattern data and the component name.
2630
set starredMethod [lreplace $method end end *]
2631
set methodTail [lindex $method end]
2633
if {[info exists Snit_methodInfo($method)]} {
2635
} elseif {[info exists Snit_methodInfo($starredMethod)] &&
2636
[lsearch -exact $Snit_info(exceptmethods) $methodTail] == -1} {
2637
set key $starredMethod
2642
foreach {flag pattern compName} $Snit_methodInfo($key) {}
2648
# NEXT, build the substitution list
2653
%m [lindex $method end] \
2654
%j [join $method _] \
2659
if {$compName ne ""} {
2660
if {[info exists Snit_components($compName)]} {
2661
set compCmd $Snit_components($compName)
2662
} elseif {[info exists Snit_typecomponents($compName)]} {
2663
set compCmd $Snit_typecomponents($compName)
2665
error "$type $self delegates method \"$method\" to undefined component \"$compName\""
2668
lappend subList %c [list $compCmd]
2671
# Note: The cached command will executed faster if it's
2675
foreach subpattern $pattern {
2676
lappend command [string map $subList $subpattern]
2679
set commandRec [list 0 $command]
2681
set Snit_methodCache($method) $commandRec
2687
# Looks up a method's command.
2689
# type: The instance's type
2690
# selfns: The instance's private namespace
2691
# win: The instance's original name (a Tk widget name, for
2693
# self: The instance's current name.
2694
# method: The name of the method to call.
2695
# errPrefix: Prefix for any error method
2696
proc ::snit::RT.LookupMethodCommand {type selfns win self method errPrefix} {
2697
set commandRec [snit::RT.CacheMethodCommand \
2698
$type $selfns $win $self \
2702
if {[llength $commandRec] == 0} {
2703
return -code error \
2704
"$errPrefix, \"$self $method\" is not defined"
2705
} elseif {[lindex $commandRec 0] == 1} {
2706
return -code error \
2707
"$errPrefix, wrong number args: should be \"$self\" $method method args"
2710
return [lindex $commandRec 1]
2714
# Clears all instance command caches
2715
proc ::snit::RT.ClearInstanceCaches {selfns} {
2716
unset -nocomplain -- ${selfns}::Snit_methodCache
2717
unset -nocomplain -- ${selfns}::Snit_cgetCache
2718
unset -nocomplain -- ${selfns}::Snit_configureCache
2719
unset -nocomplain -- ${selfns}::Snit_validateCache
2723
#-----------------------------------------------------------------------
2724
# Component Installation
2726
# Implements %TYPE%::installhull. The variables self and selfns
2727
# must be defined in the caller's context.
2729
# Installs the named widget as the hull of a
2730
# widgetadaptor. Once the widget is hijacked, its new name
2731
# is assigned to the hull component.
2733
proc ::snit::RT.installhull {type {using "using"} {widgetType ""} args} {
2734
variable ${type}::Snit_info
2735
variable ${type}::Snit_optionInfo
2738
upvar ${selfns}::hull hull
2739
upvar ${selfns}::options options
2741
# FIRST, make sure we can do it.
2742
if {!$Snit_info(isWidget)} {
2743
error "installhull is valid only for snit::widgetadaptors"
2746
if {[info exists ${selfns}::Snit_instance]} {
2747
error "hull already installed for $type $self"
2750
# NEXT, has it been created yet? If not, create it using
2751
# the specified arguments.
2752
if {"using" == $using} {
2753
# FIRST, create the widget
2754
set cmd [concat [list $widgetType $self] $args]
2755
set obj [uplevel 1 $cmd]
2757
# NEXT, for each option explicitly delegated to the hull
2758
# that doesn't appear in the usedOpts list, get the
2759
# option database value and apply it--provided that the
2760
# real option name and the target option name are different.
2761
# (If they are the same, then the option database was
2762
# already queried as part of the normal widget creation.)
2764
# Also, we don't need to worry about implicitly delegated
2765
# options, as the option and target option names must be
2767
if {[info exists Snit_optionInfo(delegated-hull)]} {
2769
# FIRST, extract all option names from args
2771
set ndx [lsearch -glob $args "-*"]
2772
foreach {opt val} [lrange $args $ndx end] {
2773
lappend usedOpts $opt
2776
foreach opt $Snit_optionInfo(delegated-hull) {
2777
set target [lindex $Snit_optionInfo(target-$opt) 1]
2779
if {"$target" == $opt} {
2783
set result [lsearch -exact $usedOpts $target]
2785
if {$result != -1} {
2789
set dbval [RT.OptionDbGet $type $self $opt]
2790
$obj configure $target $dbval
2796
if {![string equal $obj $self]} {
2798
"hull name mismatch: \"$obj\" != \"$self\""
2802
# NEXT, get the local option defaults.
2803
foreach opt $Snit_optionInfo(local) {
2804
set dbval [RT.OptionDbGet $type $self $opt]
2807
set options($opt) $dbval
2812
# NEXT, do the magic
2816
set newName "::hull${i}$self"
2817
if {"" == [info commands $newName]} {
2822
rename ::$self $newName
2823
RT.MakeInstanceCommand $type $selfns $self
2825
# Note: this relies on RT.ComponentTrace to do the dirty work.
2831
# Implements %TYPE%::install.
2833
# Creates a widget and installs it as the named component.
2834
# It expects self and selfns to be defined in the caller's context.
2836
proc ::snit::RT.install {type compName "using" widgetType winPath args} {
2837
variable ${type}::Snit_optionInfo
2838
variable ${type}::Snit_info
2841
upvar ${selfns}::$compName comp
2842
upvar ${selfns}::hull hull
2844
# We do the magic option database stuff only if $self is
2846
if {$Snit_info(isWidget)} {
2848
error "tried to install \"$compName\" before the hull exists"
2851
# FIRST, query the option database and save the results
2852
# into args. Insert them before the first option in the
2853
# list, in case there are any non-standard parameters.
2855
# Note: there might not be any delegated options; if so,
2858
if {[info exists Snit_optionInfo(delegated-$compName)]} {
2859
set ndx [lsearch -glob $args "-*"]
2861
foreach opt $Snit_optionInfo(delegated-$compName) {
2862
set dbval [RT.OptionDbGet $type $self $opt]
2865
set target [lindex $Snit_optionInfo(target-$opt) 1]
2866
set args [linsert $args $ndx $target $dbval]
2872
# NEXT, create the component and save it.
2873
set cmd [concat [list $widgetType $winPath] $args]
2874
set comp [uplevel 1 $cmd]
2876
# NEXT, handle the option database for "delegate option *",
2878
if {$Snit_info(isWidget) && $Snit_optionInfo(starcomp) eq $compName} {
2879
# FIRST, get the list of option specs from the widget.
2880
# If configure doesn't work, skip it.
2881
if {[catch {$comp configure} specs]} {
2885
# NEXT, get the set of explicitly used options from args
2887
set ndx [lsearch -glob $args "-*"]
2888
foreach {opt val} [lrange $args $ndx end] {
2889
lappend usedOpts $opt
2892
# NEXT, "delegate option *" matches all options defined
2893
# by this widget that aren't defined by the widget as a whole,
2894
# and that aren't excepted. Plus, we skip usedOpts. So build
2895
# a list of the options it can't match.
2896
set skiplist [concat \
2898
$Snit_optionInfo(except) \
2899
$Snit_optionInfo(local) \
2900
$Snit_optionInfo(delegated)]
2902
# NEXT, loop over all of the component's options, and set
2903
# any not in the skip list for which there is an option
2905
foreach spec $specs {
2907
if {[llength $spec] != 5} {
2911
set opt [lindex $spec 0]
2913
if {[lsearch -exact $skiplist $opt] != -1} {
2917
set res [lindex $spec 1]
2918
set cls [lindex $spec 2]
2920
set dbvalue [option get $self $res $cls]
2922
if {"" != $dbvalue} {
2923
$comp configure $opt $dbvalue
2932
#-----------------------------------------------------------------------
2933
# Method/Variable Name Qualification
2935
# Implements %TYPE%::variable. Requires selfns.
2936
proc ::snit::RT.variable {varname} {
2939
if {![string match "::*" $varname]} {
2940
uplevel upvar ${selfns}::$varname $varname
2942
# varname is fully qualified; let the standard
2943
# "variable" command handle it.
2944
uplevel ::variable $varname
2948
# Fully qualifies a typevariable name.
2950
# This is used to implement the mytypevar command.
2952
proc ::snit::RT.mytypevar {type name} {
2953
return ${type}::$name
2956
# Fully qualifies an instance variable name.
2958
# This is used to implement the myvar command.
2959
proc ::snit::RT.myvar {name} {
2961
return ${selfns}::$name
2964
# Use this like "list" to convert a proc call into a command
2965
# string to pass to another object (e.g., as a -command).
2966
# Qualifies the proc name properly.
2968
# This is used to implement the "myproc" command.
2970
proc ::snit::RT.myproc {type procname args} {
2971
set procname "${type}::$procname"
2972
return [linsert $args 0 $procname]
2976
proc ::snit::RT.codename {type name} {
2977
return "${type}::$name"
2980
# Use this like "list" to convert a typemethod call into a command
2981
# string to pass to another object (e.g., as a -command).
2982
# Inserts the type command at the beginning.
2984
# This is used to implement the "mytypemethod" command.
2986
proc ::snit::RT.mytypemethod {type args} {
2987
return [linsert $args 0 $type]
2990
# Use this like "list" to convert a method call into a command
2991
# string to pass to another object (e.g., as a -command).
2992
# Inserts the code at the beginning to call the right object, even if
2993
# the object's name has changed. Requires that selfns be defined
2994
# in the calling context, eg. can only be called in instance
2997
# This is used to implement the "mymethod" command.
2999
proc ::snit::RT.mymethod {args} {
3001
return [linsert $args 0 ::snit::RT.CallInstance ${selfns}]
3004
# Calls an instance method for an object given its
3005
# instance namespace and remaining arguments (the first of which
3006
# will be the method name.
3008
# selfns The instance namespace
3009
# args The arguments
3011
# Uses the selfns to determine $self, and calls the method
3012
# in the normal way.
3014
# This is used to implement the "mymethod" command.
3016
proc ::snit::RT.CallInstance {selfns args} {
3017
upvar ${selfns}::Snit_instance self
3019
set retval [catch {uplevel 1 [linsert $args 0 $self]} result]
3025
return -code error -errorinfo $errorInfo \
3026
-errorcode $errorCode $result
3028
return -code $retval $result
3035
# Looks for the named option in the named variable. If found,
3036
# it and its value are removed from the list, and the value
3037
# is returned. Otherwise, the default value is returned.
3038
# If the option is undelegated, it's own default value will be
3039
# used if none is specified.
3041
# Implements the "from" command.
3043
proc ::snit::RT.from {type argvName option {defvalue ""}} {
3044
variable ${type}::Snit_optionInfo
3045
upvar $argvName argv
3047
set ioption [lsearch -exact $argv $option]
3049
if {$ioption == -1} {
3050
if {"" == $defvalue &&
3051
[info exists Snit_optionInfo(default-$option)]} {
3052
return $Snit_optionInfo(default-$option)
3058
set ivalue [expr {$ioption + 1}]
3059
set value [lindex $argv $ivalue]
3061
set argv [lreplace $argv $ioption $ivalue]
3066
#-----------------------------------------------------------------------
3069
# Implements the standard "destroy" typemethod:
3070
# Destroys a type completely.
3072
# type The snit type
3074
proc ::snit::RT.typemethod.destroy {type} {
3075
variable ${type}::Snit_info
3077
# FIRST, destroy all instances
3078
foreach selfns [namespace children $type] {
3079
if {![namespace exists $selfns]} {
3082
upvar ${selfns}::Snit_instance obj
3084
if {$Snit_info(isWidget)} {
3087
if {"" != [info commands $obj]} {
3093
# NEXT, destroy the type's data.
3094
namespace delete $type
3096
# NEXT, get rid of the type command.
3102
#-----------------------------------------------------------------------
3105
# Implements the standard "cget" method
3107
# type The snit type
3108
# selfns The instance's instance namespace
3109
# win The instance's original name
3110
# self The instance's current name
3111
# option The name of the option
3113
proc ::snit::RT.method.cget {type selfns win self option} {
3114
if {[catch {set ${selfns}::Snit_cgetCache($option)} command]} {
3115
set command [snit::RT.CacheCgetCommand $type $selfns $win $self $option]
3117
if {[llength $command] == 0} {
3118
return -code error "unknown option \"$option\""
3125
# Retrieves and caches the command that implements "cget" for the
3128
# type The snit type
3129
# selfns The instance's instance namespace
3130
# win The instance's original name
3131
# self The instance's current name
3132
# option The name of the option
3134
proc ::snit::RT.CacheCgetCommand {type selfns win self option} {
3135
variable ${type}::Snit_optionInfo
3136
variable ${selfns}::Snit_cgetCache
3138
if {[info exists Snit_optionInfo(islocal-$option)]} {
3139
# We know the item; it's either local, or explicitly delegated.
3140
if {$Snit_optionInfo(islocal-$option)} {
3141
# It's a local option. If it has a cget method defined,
3142
# use it; otherwise just return the value.
3144
if {$Snit_optionInfo(cget-$option) eq ""} {
3145
set command [list set ${selfns}::options($option)]
3147
set command [snit::RT.LookupMethodCommand \
3148
$type $selfns $win $self \
3149
$Snit_optionInfo(cget-$option) \
3150
"can't cget $option"]
3152
lappend command $option
3155
set Snit_cgetCache($option) $command
3159
# Explicitly delegated option; get target
3160
set comp [lindex $Snit_optionInfo(target-$option) 0]
3161
set target [lindex $Snit_optionInfo(target-$option) 1]
3162
} elseif {$Snit_optionInfo(starcomp) ne "" &&
3163
[lsearch -exact $Snit_optionInfo(except) $option] == -1} {
3164
# Unknown option, but unknowns are delegated; get target.
3165
set comp $Snit_optionInfo(starcomp)
3171
# Get the component's object.
3172
set obj [RT.Component $type $selfns $comp]
3174
set command [list $obj cget $target]
3175
set Snit_cgetCache($option) $command
3180
# Implements the standard "configurelist" method
3182
# type The snit type
3183
# selfns The instance's instance namespace
3184
# win The instance's original name
3185
# self The instance's current name
3186
# optionlist A list of options and their values.
3188
proc ::snit::RT.method.configurelist {type selfns win self optionlist} {
3189
variable ${type}::Snit_optionInfo
3191
foreach {option value} $optionlist {
3192
# FIRST, get the configure command, caching it if need be.
3193
if {[catch {set ${selfns}::Snit_configureCache($option)} command]} {
3194
set command [snit::RT.CacheConfigureCommand \
3195
$type $selfns $win $self $option]
3197
if {[llength $command] == 0} {
3198
return -code error "unknown option \"$option\""
3202
# NEXT, the caching the configure command also cached the
3203
# validate command, if any. If we have one, run it.
3204
set valcommand [set ${selfns}::Snit_validateCache($option)]
3206
if {$valcommand ne ""} {
3207
lappend valcommand $value
3208
uplevel 1 $valcommand
3211
# NEXT, configure the option with the value.
3212
lappend command $value
3219
# Retrieves and caches the command that stores the named option.
3220
# Also stores the command that validates the name option if any;
3221
# If none, the validate command is "", so that the cache is always
3224
# type The snit type
3225
# selfns The instance's instance namespace
3226
# win The instance's original name
3227
# self The instance's current name
3228
# option An option name
3230
proc ::snit::RT.CacheConfigureCommand {type selfns win self option} {
3231
variable ${type}::Snit_optionInfo
3232
variable ${selfns}::Snit_configureCache
3233
variable ${selfns}::Snit_validateCache
3235
if {[info exist Snit_optionInfo(islocal-$option)]} {
3236
# We know the item; it's either local, or explicitly delegated.
3238
if {$Snit_optionInfo(islocal-$option)} {
3239
# It's a local option.
3241
# If it's readonly, it throws an error if we're already
3243
if {$Snit_optionInfo(readonly-$option)} {
3244
if {[set ${selfns}::Snit_iinfo(constructed)]} {
3245
error "option $option can only be set at instance creation"
3249
# If it has a validate method, cache that for later.
3250
if {$Snit_optionInfo(validate-$option) ne ""} {
3251
set command [snit::RT.LookupMethodCommand \
3252
$type $selfns $win $self \
3253
$Snit_optionInfo(validate-$option) \
3254
"can't validate $option"]
3256
lappend command $option
3257
set Snit_validateCache($option) $command
3259
set Snit_validateCache($option) ""
3262
# If it has a configure method defined,
3263
# cache it; otherwise, just set the value.
3265
if {$Snit_optionInfo(configure-$option) eq ""} {
3266
set command [list set ${selfns}::options($option)]
3268
set command [snit::RT.LookupMethodCommand \
3269
$type $selfns $win $self \
3270
$Snit_optionInfo(configure-$option) \
3271
"can't configure $option"]
3273
lappend command $option
3276
set Snit_configureCache($option) $command
3280
# Delegated option: get target.
3281
set comp [lindex $Snit_optionInfo(target-$option) 0]
3282
set target [lindex $Snit_optionInfo(target-$option) 1]
3283
} elseif {$Snit_optionInfo(starcomp) != "" &&
3284
[lsearch -exact $Snit_optionInfo(except) $option] == -1} {
3285
# Unknown option, but unknowns are delegated.
3286
set comp $Snit_optionInfo(starcomp)
3292
# There is no validate command in this case; save an empty string.
3293
set Snit_validateCache($option) ""
3295
# Get the component's object
3296
set obj [RT.Component $type $selfns $comp]
3298
set command [list $obj configure $target]
3299
set Snit_configureCache($option) $command
3304
# Implements the standard "configure" method
3306
# type The snit type
3307
# selfns The instance's instance namespace
3308
# win The instance's original name
3309
# self The instance's current name
3310
# args A list of options and their values, possibly empty.
3312
proc ::snit::RT.method.configure {type selfns win self args} {
3313
# If two or more arguments, set values as usual.
3314
if {[llength $args] >= 2} {
3315
::snit::RT.method.configurelist $type $selfns $win $self $args
3319
# If zero arguments, acquire data for each known option
3320
# and return the list
3321
if {[llength $args] == 0} {
3323
foreach opt [RT.method.info.options $type $selfns $win $self] {
3324
# Refactor this, so that we don't need to call via $self.
3325
lappend result [RT.GetOptionDbSpec \
3326
$type $selfns $win $self $opt]
3332
# They want it for just one.
3333
set opt [lindex $args 0]
3335
return [RT.GetOptionDbSpec $type $selfns $win $self $opt]
3339
# Retrieves the option database spec for a single option.
3341
# type The snit type
3342
# selfns The instance's instance namespace
3343
# win The instance's original name
3344
# self The instance's current name
3345
# option The name of an option
3347
# TBD: This is a bad name. What it's returning is the
3348
# result of the configure query.
3350
proc ::snit::RT.GetOptionDbSpec {type selfns win self opt} {
3351
variable ${type}::Snit_optionInfo
3353
upvar ${selfns}::Snit_components Snit_components
3354
upvar ${selfns}::options options
3356
if {[info exists options($opt)]} {
3357
# This is a locally-defined option. Just build the
3358
# list and return it.
3359
set res $Snit_optionInfo(resource-$opt)
3360
set cls $Snit_optionInfo(class-$opt)
3361
set def $Snit_optionInfo(default-$opt)
3363
return [list $opt $res $cls $def \
3364
[RT.method.cget $type $selfns $win $self $opt]]
3365
} elseif {[info exists Snit_optionInfo(target-$opt)]} {
3366
# This is an explicitly delegated option. The only
3367
# thing we don't have is the default.
3368
set res $Snit_optionInfo(resource-$opt)
3369
set cls $Snit_optionInfo(class-$opt)
3372
set logicalName [lindex $Snit_optionInfo(target-$opt) 0]
3373
set comp $Snit_components($logicalName)
3374
set target [lindex $Snit_optionInfo(target-$opt) 1]
3376
if {[catch {$comp configure $target} result]} {
3379
set defValue [lindex $result 3]
3382
return [list $opt $res $cls $defValue [$self cget $opt]]
3383
} elseif {$Snit_optionInfo(starcomp) ne "" &&
3384
[lsearch -exact $Snit_optionInfo(except) $opt] == -1} {
3385
set logicalName $Snit_optionInfo(starcomp)
3387
set comp $Snit_components($logicalName)
3389
if {[catch {set value [$comp cget $target]} result]} {
3390
error "unknown option \"$opt\""
3393
if {![catch {$comp configure $target} result]} {
3394
# Replace the delegated option name with the local name.
3395
return [::snit::Expand $result $target $opt]
3398
# configure didn't work; return simple form.
3399
return [list $opt "" "" "" $value]
3401
error "unknown option \"$opt\""
3405
#-----------------------------------------------------------------------
3406
# Type Introspection
3408
# Implements the standard "info" typemethod.
3410
# type The snit type
3411
# command The info subcommand
3412
# args All other arguments.
3414
proc ::snit::RT.typemethod.info {type command args} {
3418
switch -exact $command {
3422
# TBD: it should be possible to delete this error
3424
set errflag [catch {
3425
uplevel ::snit::RT.typemethod.info.$command \
3430
return -code error -errorinfo $errorInfo \
3431
-errorcode $errorCode $result
3437
error "\"$type info $command\" is not defined"
3443
# Returns a list of the type's typevariables whose names match a
3444
# pattern, excluding Snit internal variables.
3447
# pattern Optional. The glob pattern to match. Defaults
3450
proc ::snit::RT.typemethod.info.typevars {type {pattern *}} {
3452
foreach name [info vars "${type}::$pattern"] {
3453
set tail [namespace tail $name]
3454
if {![string match "Snit_*" $tail]} {
3455
lappend result $name
3462
# Returns a list of the type's methods whose names match a
3463
# pattern. If "delegate typemethod *" is used, the list may
3467
# pattern Optional. The glob pattern to match. Defaults
3470
proc ::snit::RT.typemethod.info.typemethods {type {pattern *}} {
3471
variable ${type}::Snit_typemethodInfo
3472
variable ${type}::Snit_typemethodCache
3474
# FIRST, get the explicit names, skipping prefixes.
3477
foreach name [array names Snit_typemethodInfo -glob $pattern] {
3478
if {[lindex $Snit_typemethodInfo($name) 0] != 1} {
3479
lappend result $name
3483
# NEXT, add any from the cache that aren't explicit.
3484
if {[info exists Snit_typemethodInfo(*)]} {
3485
# First, remove "*" from the list.
3486
set ndx [lsearch -exact $result "*"]
3488
set result [lreplace $result $ndx $ndx]
3491
foreach name [array names Snit_typemethodCache -glob $pattern] {
3492
if {[lsearch -exact $result $name] == -1} {
3493
lappend result $name
3501
# Returns a list of the type's instances whose names match
3505
# pattern Optional. The glob pattern to match
3508
# REQUIRE: type is fully qualified.
3510
proc ::snit::RT.typemethod.info.instances {type {pattern *}} {
3513
foreach selfns [namespace children $type] {
3514
upvar ${selfns}::Snit_instance instance
3516
if {[string match $pattern $instance]} {
3517
lappend result $instance
3524
#-----------------------------------------------------------------------
3525
# Instance Introspection
3527
# Implements the standard "info" method.
3529
# type The snit type
3530
# selfns The instance's instance namespace
3531
# win The instance's original name
3532
# self The instance's current name
3533
# command The info subcommand
3534
# args All other arguments.
3536
proc ::snit::RT.method.info {type selfns win self command args} {
3537
switch -exact $command {
3544
set errflag [catch {
3545
uplevel ::snit::RT.method.info.$command \
3546
$type $selfns $win $self $args
3551
return -code error -errorinfo $errorInfo $result
3557
# error "\"$self info $command\" is not defined"
3558
return -code error "\"$self info $command\" is not defined"
3565
# Returns the instance's type
3566
proc ::snit::RT.method.info.type {type selfns win self} {
3570
# $self info typevars
3572
# Returns the instance's type's typevariables
3573
proc ::snit::RT.method.info.typevars {type selfns win self {pattern *}} {
3574
return [RT.typemethod.info.typevars $type $pattern]
3577
# $self info typemethods
3579
# Returns the instance's type's typemethods
3580
proc ::snit::RT.method.info.typemethods {type selfns win self {pattern *}} {
3581
return [RT.typemethod.info.typemethods $type $pattern]
3584
# Returns a list of the instance's methods whose names match a
3585
# pattern. If "delegate method *" is used, the list may
3589
# selfns The instance namespace
3590
# win The original instance name
3591
# self The current instance name
3592
# pattern Optional. The glob pattern to match. Defaults
3595
proc ::snit::RT.method.info.methods {type selfns win self {pattern *}} {
3596
variable ${type}::Snit_methodInfo
3597
variable ${selfns}::Snit_methodCache
3599
# FIRST, get the explicit names, skipping prefixes.
3602
foreach name [array names Snit_methodInfo -glob $pattern] {
3603
if {[lindex $Snit_methodInfo($name) 0] != 1} {
3604
lappend result $name
3608
# NEXT, add any from the cache that aren't explicit.
3609
if {[info exists Snit_methodInfo(*)]} {
3610
# First, remove "*" from the list.
3611
set ndx [lsearch -exact $result "*"]
3613
set result [lreplace $result $ndx $ndx]
3616
foreach name [array names Snit_methodCache -glob $pattern] {
3617
if {[lsearch -exact $result $name] == -1} {
3618
lappend result $name
3628
# Returns the instance's instance variables
3629
proc ::snit::RT.method.info.vars {type selfns win self {pattern *}} {
3631
foreach name [info vars "${selfns}::$pattern"] {
3632
set tail [namespace tail $name]
3633
if {![string match "Snit_*" $tail]} {
3634
lappend result $name
3641
# $self info options
3643
# Returns a list of the names of the instance's options
3644
proc ::snit::RT.method.info.options {type selfns win self {pattern *}} {
3645
variable ${type}::Snit_optionInfo
3647
# First, get the local and explicitly delegated options
3648
set result [concat $Snit_optionInfo(local) $Snit_optionInfo(delegated)]
3650
# If "configure" works as for Tk widgets, add the resulting
3651
# options to the list. Skip excepted options
3652
if {$Snit_optionInfo(starcomp) ne ""} {
3653
upvar ${selfns}::Snit_components Snit_components
3654
set logicalName $Snit_optionInfo(starcomp)
3655
set comp $Snit_components($logicalName)
3657
if {![catch {$comp configure} records]} {
3658
foreach record $records {
3659
set opt [lindex $record 0]
3660
if {[lsearch -exact $result $opt] == -1 &&
3661
[lsearch -exact $Snit_optionInfo(except) $opt] == -1} {
3668
# Next, apply the pattern
3671
foreach name $result {
3672
if {[string match $pattern $name]} {