1
#-----------------------------------------------------------------------
9
# Snit's Not Incr Tcl, a simple object system in Pure Tcl.
11
# Copyright (C) 2003-2005 by William H. Duquette
12
# This code is licensed as described in license.txt.
14
#-----------------------------------------------------------------------
16
package provide snit 1.0
18
#-----------------------------------------------------------------------
21
namespace eval ::snit:: {
23
compile type widget widgetadaptor typemethod method macro
26
#-----------------------------------------------------------------------
29
namespace eval ::snit:: {
30
variable reservedArgs {type selfns win self}
32
# If true, get a pretty, fixed-up stack trace. Otherwise, get raw
34
# NOTE: Not Yet Implemented
35
variable prettyStackTrace 1
38
#-----------------------------------------------------------------------
39
# Snit Type Implementation template
41
namespace eval ::snit:: {
42
# Template type definition: All internal and user-visible Snit
43
# implementation code.
45
# The following placeholders will automatically be replaced with
46
# the client's code, in two passes:
49
# %COMPILEDDEFS% The compiled type definition.
52
# %TYPE% The fully qualified type name.
53
# %IVARDECS% Instance variable declarations
54
# %TVARDECS% Type variable declarations
55
# %TCONSTBODY% Type constructor body
56
# %INSTANCEVARS% The compiled instance variable initialization code.
57
# %TYPEVARS% The compiled type variable initialization code.
59
# This is the overall type template.
62
# This is the normal type proc
63
variable nominalTypeProc
65
# This is the "-hastypemethods no" type proc
66
variable simpleTypeProc
69
set ::snit::typeTemplate {
71
#-------------------------------------------------------------------
72
# The type's namespace definition and the user's type variables
74
namespace eval %TYPE% {%TYPEVARS%
77
#----------------------------------------------------------------
78
# Commands for use in methods, typemethods, etc.
80
# These are implemented as aliases into the Snit runtime library.
82
interp alias {} %TYPE%::installhull {} ::snit::RT.installhull %TYPE%
83
interp alias {} %TYPE%::install {} ::snit::RT.install %TYPE%
84
interp alias {} %TYPE%::typevariable {} ::variable
85
interp alias {} %TYPE%::variable {} ::snit::RT.variable
86
interp alias {} %TYPE%::mytypevar {} ::snit::RT.mytypevar %TYPE%
87
interp alias {} %TYPE%::typevarname {} ::snit::RT.mytypevar %TYPE%
88
interp alias {} %TYPE%::myvar {} ::snit::RT.myvar
89
interp alias {} %TYPE%::varname {} ::snit::RT.myvar
90
interp alias {} %TYPE%::codename {} ::snit::RT.codename %TYPE%
91
interp alias {} %TYPE%::myproc {} ::snit::RT.myproc %TYPE%
92
interp alias {} %TYPE%::mymethod {} ::snit::RT.mymethod
93
interp alias {} %TYPE%::mytypemethod {} ::snit::RT.mytypemethod %TYPE%
94
interp alias {} %TYPE%::from {} ::snit::RT.from %TYPE%
96
#-------------------------------------------------------------------
97
# Snit's internal variables
99
namespace eval %TYPE% {
100
# Array: General Snit Info
102
# ns: The type's namespace
103
# hasinstances: T or F, from pragma -hasinstances.
104
# simpledispatch: T or F, from pragma -hasinstances.
105
# canreplace: T or F, from pragma -canreplace.
106
# counter: Count of instances created so far.
107
# widgetclass: Set by widgetclass statement.
108
# hulltype: Hull type (frame or toplevel) for widgets only.
109
# exceptmethods: Methods explicitly not delegated to *
110
# excepttypemethods: Methods explicitly not delegated to *
111
# tvardecs: Type variable declarations--for dynamic methods
112
# ivardecs: Instance variable declarations--for dyn. methods
113
typevariable Snit_info
114
set Snit_info(ns) %TYPE%::
115
set Snit_info(hasinstances) 1
116
set Snit_info(simpledispatch) 0
117
set Snit_info(canreplace) 0
118
set Snit_info(counter) 0
119
set Snit_info(widgetclass) {}
120
set Snit_info(hulltype) frame
121
set Snit_info(exceptmethods) {}
122
set Snit_info(excepttypemethods) {}
123
set Snit_info(tvardecs) {%TVARDECS%}
124
set Snit_info(ivardecs) {%IVARDECS%}
126
# Array: Public methods of this type.
127
# The index is the method name, or "*".
128
# The value is [list $pattern $componentName], where
129
# $componentName is "" for normal methods.
130
typevariable Snit_typemethodInfo
131
array unset Snit_typemethodInfo
133
# Array: Public methods of instances of this type.
134
# The index is the method name, or "*".
135
# The value is [list $pattern $componentName], where
136
# $componentName is "" for normal methods.
137
typevariable Snit_methodInfo
138
array unset Snit_methodInfo
140
# Array: option information. See dictionary.txt.
141
typevariable Snit_optionInfo
142
array unset Snit_optionInfo
143
set Snit_optionInfo(local) {}
144
set Snit_optionInfo(delegated) {}
145
set Snit_optionInfo(starcomp) {}
146
set Snit_optionInfo(except) {}
149
#----------------------------------------------------------------
152
# These commands are created or replaced during compilation:
155
# Snit_instanceVars selfns
157
# Initializes the instance variables, if any. Called during
160
proc %TYPE%::Snit_instanceVars {selfns} {
165
proc %TYPE%::Snit_typeconstructor {type} {
170
#----------------------------------------------------------------
173
# These commands might be replaced during compilation:
175
# Snit_destructor type selfns win self
177
# Default destructor for the type. By default, it does
178
# nothing. It's replaced by any user destructor.
179
# For types, it's called by method destroy; for widgettypes,
180
# it's called by a destroy event handler.
182
proc %TYPE%::Snit_destructor {type selfns win self} { }
184
#----------------------------------------------------------
185
# Compiled Definitions
189
#----------------------------------------------------------
190
# Finally, call the Type Constructor
192
%TYPE%::Snit_typeconstructor %TYPE%
195
#-----------------------------------------------------------------------
198
# These procs expect the fully-qualified type name to be
199
# substituted in for %TYPE%.
201
# This is the nominal type proc. It supports typemethods and
202
# delegated typemethods.
203
set ::snit::nominalTypeProc {
204
# Type dispatcher function. Note: This function lives
205
# in the parent of the %TYPE% namespace! All accesses to
206
# %TYPE% variables and methods must be qualified!
207
proc %TYPE% {{method ""} args} {
208
# First, if there's no method, and no args, and there's a create
209
# method, and this isn't a widget, then method is "create" and
211
if {$method eq "" && [llength $args] == 0} {
212
::variable %TYPE%::Snit_info
214
if {$Snit_info(hasinstances) && !$Snit_info(isWidget)} {
218
error "wrong \# args: should be \"%TYPE% method args\""
222
# Next, retrieve the command.
223
variable %TYPE%::Snit_typemethodCache
225
if {[catch {set Snit_typemethodCache($method)} commandRec]} {
226
set commandRec [::snit::RT.CacheTypemethodCommand %TYPE% $method]
228
if {[llength $commandRec] == 0} {
229
return -code error "\"%TYPE% $method\" is not defined"
233
# If we've got a real command, break.
234
if {[lindex $commandRec 0] == 0} {
238
# Otherwise, we need to look up again...if we can.
239
if {[llength $args] == 0} {
241
"wrong number args: should be \"%TYPE% $method method args\""
244
lappend method [lindex $args 0]
245
set args [lrange $args 1 end]
248
set command [lindex $commandRec 1]
250
# Pass along the return code unchanged.
251
set retval [catch {uplevel 1 $command $args} result]
257
return -code error -errorinfo $errorInfo \
258
-errorcode $errorCode $result
260
return -code $retval $result
268
# This is the simplified type proc for when there are no typemethods
269
# except create. In this case, it doesn't take a method argument;
270
# the method is always "create".
271
set ::snit::simpleTypeProc {
272
# Type dispatcher function. Note: This function lives
273
# in the parent of the %TYPE% namespace! All accesses to
274
# %TYPE% variables and methods must be qualified!
276
::variable %TYPE%::Snit_info
278
# FIRST, if the are no args, the single arg is %AUTO%
279
if {[llength $args] == 0} {
280
if {$Snit_info(isWidget)} {
281
error "wrong \# args: should be \"%TYPE% name args\""
287
# NEXT, we're going to call the create method.
288
# Pass along the return code unchanged.
289
if {$Snit_info(isWidget)} {
290
set command [list ::snit::RT.widget.typemethod.create %TYPE%]
292
set command [list ::snit::RT.type.typemethod.create %TYPE%]
295
set retval [catch {uplevel 1 $command $args} result]
301
return -code error -errorinfo $errorInfo \
302
-errorcode $errorCode $result
304
return -code $retval $result
312
#-----------------------------------------------------------------------
315
# The following must be substituted into these proc bodies:
317
# %SELFNS% The instance namespace
318
# %WIN% The original instance name
319
# %TYPE% The fully-qualified type name
322
# Nominal instance proc body: supports method caching and delegation.
324
# proc $instanceName {method args} ....
325
set ::snit::nominalInstanceProc {
326
set self [set %SELFNS%::Snit_instance]
329
if {[catch {set %SELFNS%::Snit_methodCache($method)} commandRec]} {
330
set commandRec [snit::RT.CacheMethodCommand %TYPE% %SELFNS% %WIN% $self $method]
332
if {[llength $commandRec] == 0} {
334
"\"$self $method\" is not defined"
338
# If we've got a real command, break.
339
if {[lindex $commandRec 0] == 0} {
343
# Otherwise, we need to look up again...if we can.
344
if {[llength $args] == 0} {
346
"wrong number args: should be \"$self $method method args\""
349
lappend method [lindex $args 0]
350
set args [lrange $args 1 end]
353
set command [lindex $commandRec 1]
355
# Pass along the return code unchanged.
356
set retval [catch {uplevel 1 $command $args} result]
362
return -code error -errorinfo $errorInfo \
363
-errorcode $errorCode $result
365
return -code $retval $result
372
# Simplified method proc body: No delegation allowed; no support for
373
# upvar or exotic return codes or hierarchical methods. Designed for
374
# max speed for simple types.
376
# proc $instanceName {method args} ....
378
set ::snit::simpleInstanceProc {
379
set self [set %SELFNS%::Snit_instance]
381
if {[lsearch -exact ${%TYPE%::Snit_methods} $method] == -1} {
382
set optlist [join ${%TYPE%::Snit_methods} ", "]
383
set optlist [linsert $optlist "end-1" "or"]
384
error "bad option \"$method\": must be $optlist"
387
eval [linsert $args 0 \
388
%TYPE%::Snit_method$method %TYPE% %SELFNS% %WIN% $self]
392
#=======================================================================
393
# Snit Type Definition
395
# These are the procs used to define Snit types, widgets, and
399
#-----------------------------------------------------------------------
400
# Snit Compilation Variables
402
# The following variables are used while Snit is compiling a type,
403
# and are disposed afterwards.
405
namespace eval ::snit:: {
406
# The compiler variable contains the name of the slave interpreter
407
# used to compile type definitions.
410
# The compile array accumulates information about the type or
411
# widgettype being compiled. It is cleared before and after each
412
# compilation. It has these indices:
414
# type: The name of the type being compiled, for use
415
# in compilation procs.
416
# defs: Compiled definitions, both standard and client.
417
# which: type, widget, widgetadaptor
418
# instancevars: Instance variable definitions and initializations.
419
# ivprocdec: Instance variable proc declarations.
420
# tvprocdec: Type variable proc declarations.
421
# typeconstructor: Type constructor body.
422
# widgetclass: The widgetclass, for snit::widgets, only
423
# hasoptions: False, initially; set to true when first
425
# localoptions: Names of local options.
426
# delegatedoptions: Names of delegated options.
427
# localmethods: Names of locally defined methods.
428
# delegatesmethods: no if no delegated methods, yes otherwise.
429
# hashierarchic : no if no hierarchic methods, yes otherwise.
430
# components: Names of defined components.
431
# typecomponents: Names of defined typecomponents.
432
# typevars: Typevariable definitions and initializations.
433
# varnames: Names of instance variables
434
# typevarnames Names of type variables
435
# hasconstructor False, initially; true when constructor is
437
# resource-$opt The option's resource name
438
# class-$opt The option's class
439
# -default-$opt The option's default value
440
# -validatemethod-$opt The option's validate method
441
# -configuremethod-$opt The option's configure method
442
# -cgetmethod-$opt The option's cget method.
443
# -hastypeinfo The -hastypeinfo pragma
444
# -hastypedestroy The -hastypedestroy pragma
445
# -hastypemethods The -hastypemethods pragma
446
# -hasinfo The -hasinfo pragma
447
# -hasinstances The -hasinstances pragma
448
# -simpledispatch The -simpledispatch pragma
449
# -canreplace The -canreplace pragma
452
# This variable accumulates method dispatch information; it has
453
# the same structure as the %TYPE%::Snit_methodInfo array, and is
454
# used to initialize it.
457
# This variable accumulates typemethod dispatch information; it has
458
# the same structure as the %TYPE%::Snit_typemethodInfo array, and is
459
# used to initialize it.
460
variable typemethodInfo
462
# The following variable lists the reserved type definition statement
463
# names, e.g., the names you can't use as macros. It's built at
464
# compiler definition time using "info commands".
465
variable reservedwords {}
468
#-----------------------------------------------------------------------
469
# type compilation commands
471
# The type and widgettype commands use a slave interpreter to compile
472
# the type definition. These are the procs
473
# that are aliased into it.
475
# Initialize the compiler
476
proc ::snit::Comp.Init {} {
478
variable reservedwords
480
if {$compiler eq ""} {
481
# Create the compiler's interpreter
482
set compiler [interp create]
484
# Initialize the interpreter
486
# Load package information
487
# TBD: see if this can be moved outside.
488
catch {package require ::snit::__does_not_exist__}
490
# Protect some Tcl commands our type definitions
493
rename variable _variable
496
# Define compilation aliases.
497
$compiler alias pragma ::snit::Comp.statement.pragma
498
$compiler alias widgetclass ::snit::Comp.statement.widgetclass
499
$compiler alias hulltype ::snit::Comp.statement.hulltype
500
$compiler alias constructor ::snit::Comp.statement.constructor
501
$compiler alias destructor ::snit::Comp.statement.destructor
502
$compiler alias option ::snit::Comp.statement.option
503
$compiler alias oncget ::snit::Comp.statement.oncget
504
$compiler alias onconfigure ::snit::Comp.statement.onconfigure
505
$compiler alias method ::snit::Comp.statement.method
506
$compiler alias typemethod ::snit::Comp.statement.typemethod
507
$compiler alias typeconstructor ::snit::Comp.statement.typeconstructor
508
$compiler alias proc ::snit::Comp.statement.proc
509
$compiler alias typevariable ::snit::Comp.statement.typevariable
510
$compiler alias variable ::snit::Comp.statement.variable
511
$compiler alias typecomponent ::snit::Comp.statement.typecomponent
512
$compiler alias component ::snit::Comp.statement.component
513
$compiler alias delegate ::snit::Comp.statement.delegate
514
$compiler alias expose ::snit::Comp.statement.expose
516
# Get the list of reserved words
517
set reservedwords [$compiler eval {info commands}]
521
# Compile a type definition, and return the results as a list of two
522
# items: the fully-qualified type name, and a script that will define
523
# the type when executed.
525
# which type, widget, or widgetadaptor
527
# body the type definition
528
proc ::snit::Comp.Compile {which type body} {
529
variable typeTemplate
530
variable nominalTypeProc
531
variable simpleTypeProc
535
variable typemethodInfo
537
# FIRST, qualify the name.
538
if {![string match "::*" $type]} {
539
# Get caller's namespace;
540
# append :: if not global namespace.
541
set ns [uplevel 2 [list namespace current]]
549
# NEXT, create and initialize the compiler, if needed.
552
# NEXT, initialize the class data
553
array unset methodInfo
554
array unset typemethodInfo
557
set compile(type) $type
559
set compile(which) $which
560
set compile(hasoptions) no
561
set compile(localoptions) {}
562
set compile(instancevars) {}
563
set compile(typevars) {}
564
set compile(delegatedoptions) {}
565
set compile(ivprocdec) {}
566
set compile(tvprocdec) {}
567
set compile(typeconstructor) {}
568
set compile(widgetclass) {}
569
set compile(hulltype) {}
570
set compile(localmethods) {}
571
set compile(delegatesmethods) no
572
set compile(hashierarchic) no
573
set compile(components) {}
574
set compile(typecomponents) {}
575
set compile(varnames) {}
576
set compile(typevarnames) {}
577
set compile(hasconstructor) no
578
set compile(-hastypedestroy) yes
579
set compile(-hastypeinfo) yes
580
set compile(-hastypemethods) yes
581
set compile(-hasinfo) yes
582
set compile(-hasinstances) yes
583
set compile(-simpledispatch) no
584
set compile(-canreplace) no
586
set isWidget [string match widget* $which]
587
set isWidgetAdaptor [string match widgetadaptor $which]
589
# NEXT, Evaluate the type's definition in the class interpreter.
592
# NEXT, Add the standard definitions
593
append compile(defs) \
594
"\nset %TYPE%::Snit_info(isWidget) $isWidget\n"
596
append compile(defs) \
597
"\nset %TYPE%::Snit_info(isWidgetAdaptor) $isWidgetAdaptor\n"
599
# Indicate whether the type can create instances that replace
601
append compile(defs) "\nset %TYPE%::Snit_info(canreplace) $compile(-canreplace)\n"
604
# Check pragmas for conflict.
606
if {!$compile(-hastypemethods) && !$compile(-hasinstances)} {
607
error "$which $type has neither typemethods nor instances"
610
if {$compile(-simpledispatch) && $compile(delegatesmethods)} {
611
error "$which $type requests -simpledispatch but delegates methods."
614
if {$compile(-simpledispatch) && $compile(hashierarchic)} {
615
error "$which $type requests -simpledispatch but defines hierarchical methods."
618
# If there are typemethods, define the standard typemethods and
619
# the nominal type proc. Otherwise define the simple type proc.
620
if {$compile(-hastypemethods)} {
621
# Add the info typemethod unless the pragma forbids it.
622
if {$compile(-hastypeinfo)} {
623
Comp.statement.delegate typemethod info \
624
using {::snit::RT.typemethod.info %t}
627
# Add the destroy typemethod unless the pragma forbids it.
628
if {$compile(-hastypedestroy)} {
629
Comp.statement.delegate typemethod destroy \
630
using {::snit::RT.typemethod.destroy %t}
633
# Add the nominal type proc.
634
append compile(defs) $nominalTypeProc
636
# Add the simple type proc.
637
append compile(defs) $simpleTypeProc
640
# Add standard methods/typemethods that only make sense if the
641
# type has instances.
642
if {$compile(-hasinstances)} {
643
# If we're using simple dispatch, remember that.
644
if {$compile(-simpledispatch)} {
645
append compile(defs) "\nset %TYPE%::Snit_info(simpledispatch) 1\n"
648
# Add the info method unless the pragma forbids it.
649
if {$compile(-hasinfo)} {
650
if {!$compile(-simpledispatch)} {
651
Comp.statement.delegate method info \
652
using {::snit::RT.method.info %t %n %w %s}
654
Comp.statement.method info {args} {
655
eval [linsert $args 0 \
656
::snit::RT.method.info $type $selfns $win $self]
661
# Add the option handling stuff if there are any options.
662
if {$compile(hasoptions)} {
663
Comp.statement.variable options
665
if {!$compile(-simpledispatch)} {
666
Comp.statement.delegate method cget \
667
using {::snit::RT.method.cget %t %n %w %s}
668
Comp.statement.delegate method configurelist \
669
using {::snit::RT.method.configurelist %t %n %w %s}
670
Comp.statement.delegate method configure \
671
using {::snit::RT.method.configure %t %n %w %s}
673
Comp.statement.method cget {args} {
674
eval [linsert $args 0 \
675
::snit::RT.method.cget $type $selfns $win $self]
677
Comp.statement.method configurelist {args} {
678
eval [linsert $args 0 \
679
::snit::RT.method.configurelist $type $selfns $win $self]
681
Comp.statement.method configure {args} {
682
eval [linsert $args 0 \
683
::snit::RT.method.configure $type $selfns $win $self]
688
# Add a default constructor, if they haven't already defined one.
689
# If there are options, it will configure args; otherwise it
691
if {!$compile(hasconstructor)} {
692
if {$compile(hasoptions)} {
693
Comp.statement.constructor {args} {
694
$self configurelist $args
697
Comp.statement.constructor {} {}
702
if {!$compile(-simpledispatch)} {
703
Comp.statement.delegate method destroy \
704
using {::snit::RT.method.destroy %t %n %w %s}
706
Comp.statement.method destroy {args} {
707
eval [linsert $args 0 \
708
::snit::RT.method.destroy $type $selfns $win $self]
712
Comp.statement.delegate typemethod create \
713
using {::snit::RT.type.typemethod.create %t}
715
Comp.statement.delegate typemethod create \
716
using {::snit::RT.widget.typemethod.create %t}
719
# Save the list of method names, for -simpledispatch; otherwise,
720
# save the method info.
721
if {$compile(-simpledispatch)} {
722
append compile(defs) \
723
"\nset %TYPE%::Snit_methods [list $compile(localmethods)]\n"
725
append compile(defs) \
726
"\narray set %TYPE%::Snit_methodInfo [list [array get methodInfo]]\n"
730
append compile(defs) "\nset %TYPE%::Snit_info(hasinstances) 0\n"
733
# NEXT, compiling the type definition built up a set of information
734
# about the type's locally defined options; add this information to
735
# the compiled definition.
738
# NEXT, compiling the type definition built up a set of information
739
# about the typemethods; save the typemethod info.
740
append compile(defs) \
741
"\narray set %TYPE%::Snit_typemethodInfo [list [array get typemethodInfo]]\n"
743
# NEXT, if this is a widget define the hull component if it isn't
746
Comp.DefineComponent hull
749
# NEXT, substitute the compiled definition into the type template
750
# to get the type definition script.
751
set defscript [Expand $typeTemplate \
752
%COMPILEDDEFS% $compile(defs)]
754
# NEXT, substitute the defined macros into the type definition script.
755
# This is done as a separate step so that the compile(defs) can
756
# contain the macros defined below.
758
set defscript [Expand $defscript \
760
%IVARDECS% $compile(ivprocdec) \
761
%TVARDECS% $compile(tvprocdec) \
762
%TCONSTBODY% $compile(typeconstructor) \
763
%INSTANCEVARS% $compile(instancevars) \
764
%TYPEVARS% $compile(typevars) \
769
return [list $type $defscript]
772
# Information about locally-defined options is accumulated during
773
# compilation, but not added to the compiled definition--the option
774
# statement can appear multiple times, so it's easier this way.
775
# This proc fills in Snit_optionInfo with the accumulated information.
777
# It also computes the option's resource and class names if needed.
779
# Note that the information for delegated options was put in
780
# Snit_optionInfo during compilation.
782
proc ::snit::Comp.SaveOptionInfo {} {
785
foreach option $compile(localoptions) {
786
if {$compile(resource-$option) eq ""} {
787
set compile(resource-$option) [string range $option 1 end]
790
if {$compile(class-$option) eq ""} {
791
set compile(class-$option) [Capitalize $compile(resource-$option)]
794
# NOTE: Don't verify that the validate, configure, and cget
795
# values name real methods; the methods might be defined outside
796
# the typedefinition using snit::method.
798
Mappend compile(defs) {
800
lappend %TYPE%::Snit_optionInfo(local) %OPTION%
802
set %TYPE%::Snit_optionInfo(islocal-%OPTION%) 1
803
set %TYPE%::Snit_optionInfo(resource-%OPTION%) %RESOURCE%
804
set %TYPE%::Snit_optionInfo(class-%OPTION%) %CLASS%
805
set %TYPE%::Snit_optionInfo(default-%OPTION%) %DEFAULT%
806
set %TYPE%::Snit_optionInfo(validate-%OPTION%) %VALIDATE%
807
set %TYPE%::Snit_optionInfo(configure-%OPTION%) %CONFIGURE%
808
set %TYPE%::Snit_optionInfo(cget-%OPTION%) %CGET%
809
set %TYPE%::Snit_optionInfo(readonly-%OPTION%) %READONLY%
811
%RESOURCE% $compile(resource-$option) \
812
%CLASS% $compile(class-$option) \
813
%DEFAULT% [list $compile(-default-$option)] \
814
%VALIDATE% [list $compile(-validatemethod-$option)] \
815
%CONFIGURE% [list $compile(-configuremethod-$option)] \
816
%CGET% [list $compile(-cgetmethod-$option)] \
817
%READONLY% $compile(-readonly-$option)
822
# Evaluates a compiled type definition, thus making the type available.
823
proc ::snit::Comp.Define {compResult} {
824
# The compilation result is a list containing the fully qualified
825
# type name and a script to evaluate to define the type.
826
set type [lindex $compResult 0]
827
set defscript [lindex $compResult 1]
829
# Execute the type definition script.
830
# Consider using namespace eval %TYPE%. See if it's faster.
831
if {[catch {eval $defscript} result]} {
832
namespace delete $type
833
catch {rename $type ""}
840
# Sets pragma options which control how the type is defined.
841
proc ::snit::Comp.statement.pragma {args} {
844
set errRoot "Error in \"pragma...\""
846
foreach {opt val} $args {
847
switch -exact -- $opt {
855
if {![string is boolean -strict $val]} {
856
error "$errRoot, \"$opt\" requires a boolean value"
858
set compile($opt) $val
861
error "$errRoot, unknown pragma"
867
# Defines a widget's option class name.
868
# This statement is only available for snit::widgets,
869
# not for snit::types or snit::widgetadaptors.
870
proc ::snit::Comp.statement.widgetclass {name} {
873
# First, widgetclass can only be set for true widgets
874
if {"widget" != $compile(which)} {
875
error "widgetclass cannot be set for snit::$compile(which)s"
878
# Next, validate the option name. We'll require that it begin
879
# with an uppercase letter.
880
set initial [string index $name 0]
881
if {![string is upper $initial]} {
882
error "widgetclass \"$name\" does not begin with an uppercase letter"
885
if {"" != $compile(widgetclass)} {
886
error "too many widgetclass statements"
890
Mappend compile(defs) {
891
set %TYPE%::Snit_info(widgetclass) %WIDGETCLASS%
892
} %WIDGETCLASS% [list $name]
894
set compile(widgetclass) $name
897
# Defines a widget's hull type.
898
# This statement is only available for snit::widgets,
899
# not for snit::types or snit::widgetadaptors.
900
proc ::snit::Comp.statement.hulltype {name} {
903
# First, hulltype can only be set for true widgets
904
if {"widget" != $compile(which)} {
905
error "hulltype cannot be set for snit::$compile(which)s"
908
# Next, it must be either "frame" or "toplevel"
909
if {"frame" != $name && "toplevel" != $name} {
910
error "invalid hulltype \"$name\", should be \"frame\" or \"toplevel\""
913
if {"" != $compile(hulltype)} {
914
error "too many hulltype statements"
918
Mappend compile(defs) {
919
set %TYPE%::Snit_info(hulltype) %HULLTYPE%
922
set compile(hulltype) $name
925
# Defines a constructor.
926
proc ::snit::Comp.statement.constructor {arglist body} {
929
CheckArgs "constructor" $arglist
931
# Next, add a magic reference to self.
932
set arglist [concat type selfns win self $arglist]
934
# Next, add variable declarations to body:
935
set body "%TVARDECS%%IVARDECS%\n$body"
937
set compile(hasconstructor) yes
938
append compile(defs) "proc %TYPE%::Snit_constructor [list $arglist] [list $body]\n"
941
# Defines a destructor.
942
proc ::snit::Comp.statement.destructor {body} {
945
# Next, add variable declarations to body:
946
set body "%TVARDECS%%IVARDECS%\n$body"
948
append compile(defs) "proc %TYPE%::Snit_destructor {type selfns win self} [list $body]\n\n"
951
# Defines a type option. The option value can be a triple, specifying
952
# the option's -name, resource name, and class name.
953
proc ::snit::Comp.statement.option {optionDef args} {
956
# First, get the three option names.
957
set option [lindex $optionDef 0]
958
set resourceName [lindex $optionDef 1]
959
set className [lindex $optionDef 2]
961
set errRoot "Error in \"option [list $optionDef]...\""
963
# Next, validate the option name.
964
if {![Comp.OptionNameIsValid $option]} {
965
error "$errRoot, badly named option \"$option\""
968
if {[Contains $option $compile(delegatedoptions)]} {
969
error "$errRoot, cannot define \"$option\" locally, it has been delegated"
972
if {![Contains $option $compile(localoptions)]} {
973
# Remember that we've seen this one.
974
set compile(hasoptions) yes
975
lappend compile(localoptions) $option
977
# Initialize compilation info for this option.
978
set compile(resource-$option) ""
979
set compile(class-$option) ""
980
set compile(-default-$option) ""
981
set compile(-validatemethod-$option) ""
982
set compile(-configuremethod-$option) ""
983
set compile(-cgetmethod-$option) ""
984
set compile(-readonly-$option) 0
987
# NEXT, see if we have a resource name. If so, make sure it
988
# isn't being redefined differently.
989
if {$resourceName ne ""} {
990
if {$compile(resource-$option) eq ""} {
991
# If it's undefined, just save the value.
992
set compile(resource-$option) $resourceName
993
} elseif {$resourceName ne $compile(resource-$option)} {
994
# It's been redefined differently.
995
error "$errRoot, resource name redefined from \"$compile(resource-$option)\" to \"$resourceName\""
999
# NEXT, see if we have a class name. If so, make sure it
1000
# isn't being redefined differently.
1001
if {$className ne ""} {
1002
if {$compile(class-$option) eq ""} {
1003
# If it's undefined, just save the value.
1004
set compile(class-$option) $className
1005
} elseif {$className ne $compile(class-$option)} {
1006
# It's been redefined differently.
1007
error "$errRoot, class name redefined from \"$compile(class-$option)\" to \"$className\""
1011
# NEXT, handle the args; it's not an error to redefine these.
1012
if {[llength $args] == 1} {
1013
set compile(-default-$option) [lindex $args 0]
1015
foreach {optopt val} $args {
1016
switch -exact -- $optopt {
1021
set compile($optopt-$option) $val
1024
if {![string is boolean -strict $val]} {
1025
error "$errRoot, -readonly requires a boolean, got \"$val\""
1027
set compile($optopt-$option) $val
1030
error "$errRoot, unknown option definition option \"$optopt\""
1037
# 1 if the option name is valid, 0 otherwise.
1038
proc ::snit::Comp.OptionNameIsValid {option} {
1039
if {![string match {-*} $option] || [string match {*[A-Z ]*} $option]} {
1046
# Defines an option's cget handler
1047
proc ::snit::Comp.statement.oncget {option body} {
1050
set errRoot "Error in \"oncget $option...\""
1052
if {[lsearch -exact $compile(delegatedoptions) $option] != -1} {
1053
return -code error "$errRoot, option \"$option\" is delegated"
1056
if {[lsearch -exact $compile(localoptions) $option] == -1} {
1057
return -code error "$errRoot, option \"$option\" unknown"
1060
# Next, add variable declarations to body:
1061
set body "%TVARDECS%%IVARDECS%\n$body"
1063
Comp.statement.method _cget$option {_option} $body
1064
Comp.statement.option $option -cgetmethod _cget$option
1067
# Defines an option's configure handler.
1068
proc ::snit::Comp.statement.onconfigure {option arglist body} {
1071
if {[lsearch -exact $compile(delegatedoptions) $option] != -1} {
1072
return -code error "onconfigure $option: option \"$option\" is delegated"
1075
if {[lsearch -exact $compile(localoptions) $option] == -1} {
1076
return -code error "onconfigure $option: option \"$option\" unknown"
1079
if {[llength $arglist] != 1} {
1081
"onconfigure $option handler should have one argument, got \"$arglist\""
1084
CheckArgs "onconfigure $option" $arglist
1086
# Next, add a magic reference to the option name
1087
set arglist [concat _option $arglist]
1089
Comp.statement.method _configure$option $arglist $body
1090
Comp.statement.option $option -configuremethod _configure$option
1093
# Defines an instance method.
1094
proc ::snit::Comp.statement.method {method arglist body} {
1098
# FIRST, check the method name against previously defined
1100
Comp.CheckMethodName $method 0 ::snit::methodInfo \
1101
"Error in \"method [list $method]...\""
1103
if {[llength $method] > 1} {
1104
set compile(hashierarchic) yes
1107
# Remeber this method
1108
lappend compile(localmethods) $method
1110
CheckArgs "method [list $method]" $arglist
1112
# Next, add magic references to type and self.
1113
set arglist [concat type selfns win self $arglist]
1115
# Next, add variable declarations to body:
1116
set body "%TVARDECS%%IVARDECS%\n$body"
1118
# Next, save the definition script.
1119
if {[llength $method] == 1} {
1120
set methodInfo($method) {0 "%t::Snit_method%m %t %n %w %s" ""}
1121
Mappend compile(defs) {
1122
proc %TYPE%::Snit_method%METHOD% %ARGLIST% %BODY%
1123
} %METHOD% $method %ARGLIST% [list $arglist] %BODY% [list $body]
1125
set methodInfo($method) {0 "%t::Snit_hmethod%j %t %n %w %s" ""}
1127
Mappend compile(defs) {
1128
proc %TYPE%::Snit_hmethod%JMETHOD% %ARGLIST% %BODY%
1129
} %JMETHOD% [join $method _] %ARGLIST% [list $arglist] \
1134
# Check for name collisions; save prefix information.
1136
# method The name of the method or typemethod.
1137
# delFlag 1 if delegated, 0 otherwise.
1138
# infoVar The fully qualified name of the array containing
1139
# information about the defined methods.
1140
# errRoot The root string for any error messages.
1142
proc ::snit::Comp.CheckMethodName {method delFlag infoVar errRoot} {
1143
upvar $infoVar methodInfo
1145
# FIRST, make sure the method name is a valid Tcl list.
1146
if {[catch {lindex $method 0}]} {
1147
error "$errRoot, the name \"$method\" must have list syntax."
1150
# NEXT, check whether we can define it.
1151
if {![catch {set methodInfo($method)} data]} {
1152
# We can't redefine methods with submethods.
1153
if {[lindex $data 0] == 1} {
1154
error "$errRoot, \"$method\" has submethods."
1157
# You can't delegate a method that's defined locally,
1158
# and you can't define a method locally if it's been delegated.
1159
if {$delFlag && [lindex $data 2] eq ""} {
1160
error "$errRoot, \"$method\" has been defined locally."
1161
} elseif {!$delFlag && [lindex $data 2] ne ""} {
1162
error "$errRoot, \"$method\" has been delegated"
1166
# Handle hierarchical case.
1167
if {[llength $method] > 1} {
1170
while {[llength $tokens] > 1} {
1171
lappend prefix [lindex $tokens 0]
1172
set tokens [lrange $tokens 1 end]
1174
if {![catch {set methodInfo($prefix)} result]} {
1175
# Prefix is known. If it's not a prefix, throw an
1177
if {[lindex $result 0] == 0} {
1178
error "$errRoot, \"$prefix\" has no submethods."
1182
set methodInfo($prefix) [list 1]
1187
# Defines a typemethod method.
1188
proc ::snit::Comp.statement.typemethod {method arglist body} {
1190
variable typemethodInfo
1192
# FIRST, check the typemethod name against previously defined
1194
Comp.CheckMethodName $method 0 ::snit::typemethodInfo \
1195
"Error in \"typemethod [list $method]...\""
1197
CheckArgs "typemethod $method" $arglist
1199
# First, add magic reference to type.
1200
set arglist [concat type $arglist]
1202
# Next, add typevariable declarations to body:
1203
set body "%TVARDECS%\n$body"
1205
# Next, save the definition script
1206
if {[llength $method] == 1} {
1207
set typemethodInfo($method) {0 "%t::Snit_typemethod%m %t" ""}
1209
Mappend compile(defs) {
1210
proc %TYPE%::Snit_typemethod%METHOD% %ARGLIST% %BODY%
1211
} %METHOD% $method %ARGLIST% [list $arglist] %BODY% [list $body]
1213
set typemethodInfo($method) {0 "%t::Snit_htypemethod%j %t" ""}
1215
Mappend compile(defs) {
1216
proc %TYPE%::Snit_htypemethod%JMETHOD% %ARGLIST% %BODY%
1217
} %JMETHOD% [join $method _] \
1218
%ARGLIST% [list $arglist] %BODY% [list $body]
1223
# Defines a type constructor.
1224
proc ::snit::Comp.statement.typeconstructor {body} {
1227
if {"" != $compile(typeconstructor)} {
1228
error "too many typeconstructors"
1231
set compile(typeconstructor) $body
1234
# Defines a static proc in the type's namespace.
1235
proc ::snit::Comp.statement.proc {proc arglist body} {
1238
# If "ns" is defined, the proc can see instance variables.
1239
if {[lsearch -exact $arglist selfns] != -1} {
1240
# Next, add instance variable declarations to body:
1241
set body "%IVARDECS%\n$body"
1244
# The proc can always see typevariables.
1245
set body "%TVARDECS%\n$body"
1247
append compile(defs) "
1250
proc [list %TYPE%::$proc $arglist $body]
1254
# Defines a static variable in the type's namespace.
1255
proc ::snit::Comp.statement.typevariable {name args} {
1258
set errRoot "Error in \"typevariable $name...\""
1260
set len [llength $args]
1263
($len == 2 && [lindex $args 0] ne "-array")} {
1264
error "$errRoot, too many initializers"
1267
if {[lsearch -exact $compile(varnames) $name] != -1} {
1268
error "$errRoot, \"$name\" is already an instance variable"
1271
lappend compile(typevarnames) $name
1274
append compile(typevars) \
1275
"\n\t [list ::variable $name [lindex $args 0]]"
1276
} elseif {$len == 2} {
1277
append compile(typevars) \
1278
"\n\t [list ::variable $name]"
1279
append compile(typevars) \
1280
"\n\t [list array set $name [lindex $args 1]]"
1282
append compile(typevars) \
1283
"\n\t [list ::variable $name]"
1286
append compile(tvprocdec) "\n\t typevariable ${name}"
1289
# Defines an instance variable; the definition will go in the
1290
# type's create typemethod.
1291
proc ::snit::Comp.statement.variable {name args} {
1294
set errRoot "Error in \"variable $name...\""
1296
set len [llength $args]
1299
($len == 2 && [lindex $args 0] ne "-array")} {
1300
error "$errRoot, too many initializers"
1303
if {[lsearch -exact $compile(typevarnames) $name] != -1} {
1304
error "$errRoot, \"$name\" is already a typevariable"
1307
lappend compile(varnames) $name
1310
append compile(instancevars) \
1311
"\nset \${selfns}::$name [list [lindex $args 0]]\n"
1312
} elseif {$len == 2} {
1313
append compile(instancevars) \
1314
"\narray set \${selfns}::$name [list [lindex $args 1]]\n"
1317
append compile(ivprocdec) "\n\t "
1318
Mappend compile(ivprocdec) {::variable ${selfns}::%N} %N $name
1321
# Defines a typecomponent, and handles component options.
1323
# component The logical name of the delegate
1326
proc ::snit::Comp.statement.typecomponent {component args} {
1329
set errRoot "Error in \"typecomponent $component...\""
1331
# FIRST, define the component
1332
Comp.DefineTypecomponent $component $errRoot
1334
# NEXT, handle the options.
1338
foreach {opt val} $args {
1339
switch -exact -- $opt {
1341
set publicMethod $val
1344
set inheritFlag $val
1345
if {![string is boolean $inheritFlag]} {
1346
error "typecomponent $component -inherit: expected boolean value, got \"$val\""
1350
error "typecomponent $component: Invalid option \"$opt\""
1355
# NEXT, if -public specified, define the method.
1356
if {$publicMethod ne ""} {
1357
Comp.statement.delegate typemethod [list $publicMethod *] to $component
1360
# NEXT, if "-inherit 1" is specified, delegate typemethod * to
1363
Comp.statement.delegate typemethod "*" to $component
1369
# Defines a name to be a typecomponent
1371
# The name becomes a typevariable; in addition, it gets a
1372
# write trace so that when it is set, all of the component mechanisms
1375
# component The component name
1377
proc ::snit::Comp.DefineTypecomponent {component {errRoot "Error"}} {
1380
if {[lsearch -exact $compile(varnames) $component] != -1} {
1381
error "$errRoot, \"$component\" is already an instance variable"
1384
if {[lsearch -exact $compile(typecomponents) $component] == -1} {
1385
# Remember we've done this.
1386
lappend compile(typecomponents) $component
1388
# Make it a type variable with no initial value
1389
Comp.statement.typevariable $component ""
1391
# Add a write trace to do the component thing.
1392
Mappend compile(typevars) {
1393
trace add variable %COMP% write \
1394
[list ::snit::RT.TypecomponentTrace [list %TYPE%] %COMP%]
1395
} %TYPE% $compile(type) %COMP% $component
1399
# Defines a component, and handles component options.
1401
# component The logical name of the delegate
1404
# TBD: Ideally, it should be possible to call this statement multiple
1405
# times, possibly changing the option values. To do that, I'd need
1406
# to cache the option values and not act on them until *after* I'd
1407
# read the entire type definition.
1409
proc ::snit::Comp.statement.component {component args} {
1412
set errRoot "Error in \"component $component...\""
1414
# FIRST, define the component
1415
Comp.DefineComponent $component $errRoot
1417
# NEXT, handle the options.
1421
foreach {opt val} $args {
1422
switch -exact -- $opt {
1424
set publicMethod $val
1427
set inheritFlag $val
1428
if {![string is boolean $inheritFlag]} {
1429
error "component $component -inherit: expected boolean value, got \"$val\""
1433
error "component $component: Invalid option \"$opt\""
1438
# NEXT, if -public specified, define the method.
1439
if {$publicMethod ne ""} {
1440
Comp.statement.delegate method [list $publicMethod *] to $component
1443
# NEXT, if -inherit is specified, delegate method/option * to
1446
Comp.statement.delegate method "*" to $component
1447
Comp.statement.delegate option "*" to $component
1452
# Defines a name to be a component
1454
# The name becomes an instance variable; in addition, it gets a
1455
# write trace so that when it is set, all of the component mechanisms
1458
# component The component name
1460
proc ::snit::Comp.DefineComponent {component {errRoot "Error"}} {
1463
if {[lsearch -exact $compile(typevarnames) $component] != -1} {
1464
error "$errRoot, \"$component\" is already a typevariable"
1467
if {[lsearch -exact $compile(components) $component] == -1} {
1468
# Remember we've done this.
1469
lappend compile(components) $component
1471
# Make it an instance variable with no initial value
1472
Comp.statement.variable $component ""
1474
# Add a write trace to do the component thing.
1475
Mappend compile(instancevars) {
1476
trace add variable ${selfns}::%COMP% write \
1477
[list ::snit::RT.ComponentTrace [list %TYPE%] $selfns %COMP%]
1478
} %TYPE% $compile(type) %COMP% $component
1482
# Creates a delegated method, typemethod, or option.
1483
proc ::snit::Comp.statement.delegate {what name args} {
1484
# FIRST, dispatch to correct handler.
1486
typemethod { Comp.DelegatedTypemethod $name $args }
1487
method { Comp.DelegatedMethod $name $args }
1488
option { Comp.DelegatedOption $name $args }
1490
error "Error in \"delegate $what $name...\", \"$what\"?"
1494
if {([llength $args] % 2) != 0} {
1495
error "Error in \"delegate $what $name...\", invalid syntax"
1499
# Creates a delegated typemethod delegating it to a particular
1500
# typecomponent or an arbitrary command.
1502
# method The name of the method
1503
# arglist Delegation options
1505
proc ::snit::Comp.DelegatedTypemethod {method arglist} {
1507
variable typemethodInfo
1509
set errRoot "Error in \"delegate typemethod [list $method]...\""
1511
# Next, parse the delegation options.
1516
set methodTail [lindex $method end]
1518
foreach {opt value} $arglist {
1519
switch -exact $opt {
1520
to { set component $value }
1521
as { set target $value }
1522
except { set exceptions $value }
1523
using { set pattern $value }
1525
error "$errRoot, unknown delegation option \"$opt\""
1530
if {$component eq "" && $pattern eq ""} {
1531
error "$errRoot, missing \"to\""
1534
if {$methodTail eq "*" && $target ne ""} {
1535
error "$errRoot, cannot specify \"as\" with \"*\""
1538
if {$methodTail ne "*" && $exceptions ne ""} {
1539
error "$errRoot, can only specify \"except\" with \"*\""
1542
if {$pattern ne "" && $target ne ""} {
1543
error "$errRoot, cannot specify both \"as\" and \"using\""
1546
foreach token [lrange $method 1 end-1] {
1547
if {$token eq "*"} {
1548
error "$errRoot, \"*\" must be the last token."
1552
# NEXT, define the component
1553
if {$component ne ""} {
1554
Comp.DefineTypecomponent $component $errRoot
1557
# NEXT, define the pattern.
1558
if {$pattern eq ""} {
1559
if {$methodTail eq "*"} {
1561
} elseif {$target ne ""} {
1562
set pattern "%c $target"
1568
# Make sure the pattern is a valid list.
1569
if {[catch {lindex $pattern 0} result]} {
1570
error "$errRoot, the using pattern, \"$pattern\", is not a valid list"
1573
# NEXT, check the method name against previously defined
1575
Comp.CheckMethodName $method 1 ::snit::typemethodInfo $errRoot
1577
set typemethodInfo($method) [list 0 $pattern $component]
1579
if {[string equal $methodTail "*"]} {
1580
Mappend compile(defs) {
1581
set %TYPE%::Snit_info(excepttypemethods) %EXCEPT%
1582
} %EXCEPT% [list $exceptions]
1587
# Creates a delegated method delegating it to a particular
1588
# component or command.
1590
# method The name of the method
1591
# arglist Delegation options.
1593
proc ::snit::Comp.DelegatedMethod {method arglist} {
1597
set errRoot "Error in \"delegate method [list $method]...\""
1599
# Next, parse the delegation options.
1604
set methodTail [lindex $method end]
1606
foreach {opt value} $arglist {
1607
switch -exact $opt {
1608
to { set component $value }
1609
as { set target $value }
1610
except { set exceptions $value }
1611
using { set pattern $value }
1613
error "$errRoot, unknown delegation option \"$opt\""
1618
if {$component eq "" && $pattern eq ""} {
1619
error "$errRoot, missing \"to\""
1622
if {$methodTail eq "*" && $target ne ""} {
1623
error "$errRoot, cannot specify \"as\" with \"*\""
1626
if {$methodTail ne "*" && $exceptions ne ""} {
1627
error "$errRoot, can only specify \"except\" with \"*\""
1630
if {$pattern ne "" && $target ne ""} {
1631
error "$errRoot, cannot specify both \"as\" and \"using\""
1634
foreach token [lrange $method 1 end-1] {
1635
if {$token eq "*"} {
1636
error "$errRoot, \"*\" must be the last token."
1640
# NEXT, we delegate some methods
1641
set compile(delegatesmethods) yes
1643
# NEXT, define the component. Allow typecomponents.
1644
if {$component ne ""} {
1645
if {[lsearch -exact $compile(typecomponents) $component] == -1} {
1646
Comp.DefineComponent $component $errRoot
1650
# NEXT, define the pattern.
1651
if {$pattern eq ""} {
1652
if {$methodTail eq "*"} {
1654
} elseif {$target ne ""} {
1655
set pattern "%c $target"
1661
# Make sure the pattern is a valid list.
1662
if {[catch {lindex $pattern 0} result]} {
1663
error "$errRoot, the using pattern, \"$pattern\", is not a valid list"
1666
# NEXT, check the method name against previously defined
1668
Comp.CheckMethodName $method 1 ::snit::methodInfo $errRoot
1670
# NEXT, save the method info.
1671
set methodInfo($method) [list 0 $pattern $component]
1673
if {[string equal $methodTail "*"]} {
1674
Mappend compile(defs) {
1675
set %TYPE%::Snit_info(exceptmethods) %EXCEPT%
1676
} %EXCEPT% [list $exceptions]
1680
# Creates a delegated option, delegating it to a particular
1681
# component and, optionally, to a particular option of that
1684
# optionDef The option definition
1685
# args definition arguments.
1687
proc ::snit::Comp.DelegatedOption {optionDef arglist} {
1690
# First, get the three option names.
1691
set option [lindex $optionDef 0]
1692
set resourceName [lindex $optionDef 1]
1693
set className [lindex $optionDef 2]
1695
set errRoot "Error in \"delegate option [list $optionDef]...\""
1697
# Next, parse the delegation options.
1702
foreach {opt value} $arglist {
1703
switch -exact $opt {
1704
to { set component $value }
1705
as { set target $value }
1706
except { set exceptions $value }
1708
error "$errRoot, unknown delegation option \"$opt\""
1713
if {$component eq ""} {
1714
error "$errRoot, missing \"to\""
1717
if {$option eq "*" && $target ne ""} {
1718
error "$errRoot, cannot specify \"as\" with \"delegate option *\""
1721
if {$option ne "*" && $exceptions ne ""} {
1722
error "$errRoot, can only specify \"except\" with \"delegate option *\""
1725
# Next, validate the option name
1727
if {"*" != $option} {
1728
if {![Comp.OptionNameIsValid $option]} {
1729
error "$errRoot, badly named option \"$option\""
1733
if {[Contains $option $compile(localoptions)]} {
1734
error "$errRoot, \"$option\" has been defined locally"
1737
if {[Contains $option $compile(delegatedoptions)]} {
1738
error "$errRoot, \"$option\" is multiply delegated"
1741
# NEXT, define the component
1742
Comp.DefineComponent $component $errRoot
1744
# Next, define the target option, if not specified.
1745
if {![string equal $option "*"] &&
1746
[string equal $target ""]} {
1750
# NEXT, save the delegation data.
1751
set compile(hasoptions) yes
1753
if {![string equal $option "*"]} {
1754
lappend compile(delegatedoptions) $option
1756
# Next, compute the resource and class names, if they aren't
1759
if {"" == $resourceName} {
1760
set resourceName [string range $option 1 end]
1763
if {"" == $className} {
1764
set className [Capitalize $resourceName]
1767
Mappend compile(defs) {
1768
set %TYPE%::Snit_optionInfo(islocal-%OPTION%) 0
1769
set %TYPE%::Snit_optionInfo(resource-%OPTION%) %RES%
1770
set %TYPE%::Snit_optionInfo(class-%OPTION%) %CLASS%
1771
lappend %TYPE%::Snit_optionInfo(delegated) %OPTION%
1772
set %TYPE%::Snit_optionInfo(target-%OPTION%) [list %COMP% %TARGET%]
1773
lappend %TYPE%::Snit_optionInfo(delegated-%COMP%) %OPTION%
1774
} %OPTION% $option \
1777
%RES% $resourceName \
1780
Mappend compile(defs) {
1781
set %TYPE%::Snit_optionInfo(starcomp) %COMP%
1782
set %TYPE%::Snit_optionInfo(except) %EXCEPT%
1783
} %COMP% $component %EXCEPT% [list $exceptions]
1787
# Exposes a component, effectively making the component's command an
1790
# component The logical name of the delegate
1791
# "as" sugar; if not "", must be "as"
1792
# methodname The desired method name for the component's command, or ""
1794
proc ::snit::Comp.statement.expose {component {"as" ""} {methodname ""}} {
1798
# FIRST, define the component
1799
Comp.DefineComponent $component
1801
# NEXT, define the method just as though it were in the type
1803
if {[string equal $methodname ""]} {
1804
set methodname $component
1807
Comp.statement.method $methodname args [Expand {
1808
if {[llength $args] == 0} {
1812
if {[string equal $%COMPONENT% ""]} {
1813
error "undefined component \"%COMPONENT%\""
1817
set cmd [linsert $args 0 $%COMPONENT%]
1818
return [uplevel 1 $cmd]
1819
} %COMPONENT% $component]
1824
#-----------------------------------------------------------------------
1827
# Compile a type definition, and return the results as a list of two
1828
# items: the fully-qualified type name, and a script that will define
1829
# the type when executed.
1831
# which type, widget, or widgetadaptor
1832
# type the type name
1833
# body the type definition
1834
proc ::snit::compile {which type body} {
1835
return [Comp.Compile $which $type $body]
1838
proc ::snit::type {type body} {
1839
return [Comp.Define [Comp.Compile type $type $body]]
1842
proc ::snit::widget {type body} {
1843
return [Comp.Define [Comp.Compile widget $type $body]]
1846
proc ::snit::widgetadaptor {type body} {
1847
return [Comp.Define [Comp.Compile widgetadaptor $type $body]]
1850
proc ::snit::typemethod {type method arglist body} {
1851
# Make sure the type exists.
1852
if {![info exists ${type}::Snit_info]} {
1853
error "no such type: \"$type\""
1856
upvar ${type}::Snit_info Snit_info
1857
upvar ${type}::Snit_typemethodInfo Snit_typemethodInfo
1859
# FIRST, check the typemethod name against previously defined
1861
Comp.CheckMethodName $method 0 ${type}::Snit_typemethodInfo \
1862
"Cannot define \"$method\""
1864
# NEXT, check the arguments
1865
CheckArgs "snit::typemethod $type $method" $arglist
1867
# Next, add magic reference to type.
1868
set arglist [concat type $arglist]
1870
# Next, add typevariable declarations to body:
1871
set body "$Snit_info(tvardecs)\n$body"
1874
if {[llength $method] == 1} {
1875
set Snit_typemethodInfo($method) {0 "%t::Snit_typemethod%m %t" ""}
1876
uplevel 1 [list proc ${type}::Snit_typemethod$method $arglist $body]
1878
set Snit_typemethodInfo($method) {0 "%t::Snit_htypemethod%j %t" ""}
1879
set suffix [join $method _]
1880
uplevel 1 [list proc ${type}::Snit_htypemethod$suffix $arglist $body]
1884
proc ::snit::method {type method arglist body} {
1885
# Make sure the type exists.
1886
if {![info exists ${type}::Snit_info]} {
1887
error "no such type: \"$type\""
1890
upvar ${type}::Snit_methodInfo Snit_methodInfo
1891
upvar ${type}::Snit_info Snit_info
1893
# FIRST, check the method name against previously defined
1895
Comp.CheckMethodName $method 0 ${type}::Snit_methodInfo \
1896
"Cannot define \"$method\""
1898
# NEXT, check the arguments
1899
CheckArgs "snit::method $type $method" $arglist
1901
# Next, add magic references to type and self.
1902
set arglist [concat type selfns win self $arglist]
1904
# Next, add variable declarations to body:
1905
set body "$Snit_info(tvardecs)$Snit_info(ivardecs)\n$body"
1908
if {[llength $method] == 1} {
1909
set Snit_methodInfo($method) {0 "%t::Snit_method%m %t %n %w %s" ""}
1910
uplevel 1 [list proc ${type}::Snit_method$method $arglist $body]
1912
set Snit_methodInfo($method) {0 "%t::Snit_hmethod%j %t %n %w %s" ""}
1914
set suffix [join $method _]
1915
uplevel 1 [list proc ${type}::Snit_hmethod$suffix $arglist $body]
1919
# Defines a proc within the compiler; this proc can call other
1920
# type definition statements, and thus can be used for meta-programming.
1921
proc ::snit::macro {name arglist body} {
1923
variable reservedwords
1925
# FIRST, make sure the compiler is defined.
1928
# NEXT, check the macro name against the reserved words
1929
if {[lsearch -exact $reservedwords $name] != -1} {
1930
error "invalid macro name \"$name\""
1933
# NEXT, see if the name has a namespace; if it does, define the
1935
set ns [namespace qualifiers $name]
1938
$compiler eval "namespace eval $ns {}"
1941
# NEXT, define the macro
1942
$compiler eval [list _proc $name $arglist $body]
1945
#-----------------------------------------------------------------------
1948
# These are utility functions used while compiling Snit types.
1950
# Builds a template from a tagged list of text blocks, then substitutes
1951
# all symbols in the mapTable, returning the expanded template.
1952
proc ::snit::Expand {template args} {
1953
return [string map $args $template]
1956
# Expands a template and appends it to a variable.
1957
proc ::snit::Mappend {varname template args} {
1958
upvar $varname myvar
1960
append myvar [string map $args $template]
1963
# Checks argument list against reserved args
1964
proc ::snit::CheckArgs {which arglist} {
1965
variable reservedArgs
1967
foreach name $reservedArgs {
1968
if {[Contains $name $arglist]} {
1969
error "$which's arglist may not contain \"$name\" explicitly"
1974
# Returns 1 if a value is in a list, and 0 otherwise.
1975
proc ::snit::Contains {value list} {
1976
if {[lsearch -exact $list $value] != -1} {
1983
# Capitalizes the first letter of a string.
1984
proc ::snit::Capitalize {text} {
1985
set first [string index $text 0]
1986
set rest [string range $text 1 end]
1987
return "[string toupper $first]$rest"
1990
# Converts an arbitrary white-space-delimited string into a list
1991
# by splitting on white-space and deleting empty tokens.
1993
proc ::snit::Listify {str} {
1995
foreach token [split [string trim $str]] {
1996
if {[string length $token] > 0} {
1997
lappend result $token
2005
#=======================================================================
2006
# Snit Runtime Library
2008
# These are procs used by Snit types and widgets at runtime.
2010
#-----------------------------------------------------------------------
2013
# Creates a new instance of the snit::type given its name and the args.
2015
# type The snit::type
2016
# name The instance name
2017
# args Args to pass to the constructor
2019
proc ::snit::RT.type.typemethod.create {type name args} {
2020
variable ${type}::Snit_info
2021
variable ${type}::Snit_optionInfo
2023
# FIRST, qualify the name.
2024
if {![string match "::*" $name]} {
2025
# Get caller's namespace;
2026
# append :: if not global namespace.
2027
set ns [uplevel 1 [list namespace current]]
2035
# NEXT, if %AUTO% appears in the name, generate a unique
2036
# command name. Otherwise, ensure that the name isn't in use.
2037
if {[string match "*%AUTO%*" $name]} {
2038
set name [::snit::RT.UniqueName Snit_info(counter) $type $name]
2039
} elseif {!$Snit_info(canreplace) && [info commands $name] ne ""} {
2040
error "command \"$name\" already exists"
2043
# NEXT, create the instance's namespace.
2045
[::snit::RT.UniqueInstanceNamespace Snit_info(counter) $type]
2046
namespace eval $selfns {}
2048
# NEXT, install the dispatcher
2049
RT.MakeInstanceCommand $type $selfns $name
2051
# Initialize the options to their defaults.
2052
upvar ${selfns}::options options
2053
foreach opt $Snit_optionInfo(local) {
2054
set options($opt) $Snit_optionInfo(default-$opt)
2057
# Initialize the instance vars to their defaults.
2058
# selfns must be defined, as it is used implicitly.
2059
${type}::Snit_instanceVars $selfns
2061
# Execute the type's constructor.
2062
set errcode [catch {
2063
RT.ConstructInstance $type $selfns $name $args
2070
set theInfo $errorInfo
2071
set theCode $errorCode
2072
::snit::RT.DestroyObject $type $selfns $name
2073
error "Error in constructor: $result" $theInfo $theCode
2076
# NEXT, return the object's name.
2080
# Creates a new instance of the snit::widget or snit::widgetadaptor
2081
# given its name and the args.
2083
# type The snit::widget or snit::widgetadaptor
2084
# name The instance name
2085
# args Args to pass to the constructor
2087
proc ::snit::RT.widget.typemethod.create {type name args} {
2088
variable ${type}::Snit_info
2089
variable ${type}::Snit_optionInfo
2091
# FIRST, if %AUTO% appears in the name, generate a unique
2093
if {[string match "*%AUTO%*" $name]} {
2094
set name [::snit::RT.UniqueName Snit_info(counter) $type $name]
2097
# NEXT, create the instance's namespace.
2099
[::snit::RT.UniqueInstanceNamespace Snit_info(counter) $type]
2100
namespace eval $selfns { }
2102
# NEXT, Initialize the widget's own options to their defaults.
2103
upvar ${selfns}::options options
2104
foreach opt $Snit_optionInfo(local) {
2105
set options($opt) $Snit_optionInfo(default-$opt)
2108
# Initialize the instance vars to their defaults.
2109
${type}::Snit_instanceVars $selfns
2111
# NEXT, if this is a normal widget (not a widget adaptor) then
2112
# create a frame as its hull. We set the frame's -class to
2113
# the user's widgetclass, or, if none, to the basename of
2114
# the $type with an initial upper case letter.
2115
if {!$Snit_info(isWidgetAdaptor)} {
2116
# FIRST, determine the class name
2117
if {"" == $Snit_info(widgetclass)} {
2118
set Snit_info(widgetclass) \
2119
[::snit::Capitalize [namespace tail $type]]
2122
# NEXT, create the widget
2125
${type}::installhull using \
2126
$Snit_info(hulltype) -class $Snit_info(widgetclass)
2128
# NEXT, let's query the option database for our
2129
# widget, now that we know that it exists.
2130
foreach opt $Snit_optionInfo(local) {
2131
set dbval [RT.OptionDbGet $type $name $opt]
2134
set options($opt) $dbval
2139
# Execute the type's constructor, and verify that it
2141
set errcode [catch {
2142
RT.ConstructInstance $type $selfns $name $args
2144
::snit::RT.Component $type $selfns hull
2146
# Prepare to call the object's destructor when the
2147
# <Destroy> event is received. Use a Snit-specific bindtag
2148
# so that the widget name's tag is unencumbered.
2150
bind Snit$type$name <Destroy> [::snit::Expand {
2151
::snit::RT.DestroyObject %TYPE% %NS% %W
2152
} %TYPE% $type %NS% $selfns]
2154
# Insert the bindtag into the list of bindtags right
2155
# after the widget name.
2156
set taglist [bindtags $name]
2157
set ndx [lsearch -exact $taglist $name]
2159
bindtags $name [linsert $taglist $ndx Snit$type$name]
2166
set theInfo $errorInfo
2167
set theCode $errorCode
2168
::snit::RT.DestroyObject $type $selfns $name
2169
error "Error in constructor: $result" $theInfo $theCode
2172
# NEXT, return the object's name.
2177
# RT.MakeInstanceCommand type selfns instance
2179
# type The object type
2180
# selfns The instance namespace
2181
# instance The instance name
2183
# Creates the instance proc.
2185
proc ::snit::RT.MakeInstanceCommand {type selfns instance} {
2186
variable ${type}::Snit_info
2188
# FIRST, remember the instance name. The Snit_instance variable
2189
# allows the instance to figure out its current name given the
2190
# instance namespace.
2191
upvar ${selfns}::Snit_instance Snit_instance
2192
set Snit_instance $instance
2194
# NEXT, qualify the proc name if it's a widget.
2195
if {$Snit_info(isWidget)} {
2196
set procname ::$instance
2198
set procname $instance
2201
# NEXT, install the new proc
2202
if {!$Snit_info(simpledispatch)} {
2203
set instanceProc $::snit::nominalInstanceProc
2205
set instanceProc $::snit::simpleInstanceProc
2208
proc $procname {method args} \
2210
[list %SELFNS% $selfns %WIN% $instance %TYPE% $type] \
2213
# NEXT, add the trace.
2214
trace add command $procname {rename delete} \
2215
[list ::snit::RT.InstanceTrace $type $selfns $instance]
2218
# This proc is called when the instance command is renamed.
2219
# If op is delete, then new will always be "", so op is redundant.
2221
# type The fully-qualified type name
2222
# selfns The instance namespace
2223
# win The original instance/tk window name.
2224
# old old instance command name
2225
# new new instance command name
2226
# op rename or delete
2228
# If the op is delete, we need to clean up the object; otherwise,
2229
# we need to track the change.
2231
# NOTE: In Tcl 8.4.2 there's a bug: errors in rename and delete
2232
# traces aren't propagated correctly. Instead, they silently
2233
# vanish. Add a catch to output any error message.
2235
proc ::snit::RT.InstanceTrace {type selfns win old new op} {
2236
variable ${type}::Snit_info
2238
# Note to developers ...
2239
# For Tcl 8.4.0, errors thrown in trace handlers vanish silently.
2240
# Therefore we catch them here and create some output to help in
2241
# debugging such problems.
2244
# FIRST, clean up if necessary
2246
if {$Snit_info(isWidget)} {
2249
::snit::RT.DestroyObject $type $selfns $win
2252
# Otherwise, track the change.
2253
variable ${selfns}::Snit_instance
2254
set Snit_instance [uplevel 1 [list namespace which -command $new]]
2256
# Also, clear the instance caches, as many cached commands
2258
RT.ClearInstanceCaches $selfns
2262
# Pop up the console on Windows wish, to enable stdout.
2263
# This clobbers errorInfo on unix, so save it so we can print it.
2265
catch {console show}
2266
puts "Error in ::snit::RT.InstanceTrace $type $selfns $win $old $new $op:"
2271
# Calls the instance constructor and handles related housekeeping.
2272
proc ::snit::RT.ConstructInstance {type selfns instance arglist} {
2273
variable ${type}::Snit_optionInfo
2274
variable ${selfns}::Snit_iinfo
2276
# Track whether we are constructed or not.
2277
set Snit_iinfo(constructed) 0
2279
# Call the user's constructor
2280
eval [linsert $arglist 0 \
2281
${type}::Snit_constructor $type $selfns $instance $instance]
2283
set Snit_iinfo(constructed) 1
2285
# Unset the configure cache for all -readonly options.
2286
# This ensures that the next time anyone tries to
2287
# configure it, an error is thrown.
2288
foreach opt $Snit_optionInfo(local) {
2289
if {$Snit_optionInfo(readonly-$opt)} {
2290
unset -nocomplain ${selfns}::Snit_configureCache($opt)
2297
# Returns a unique command name.
2299
# REQUIRE: type is a fully qualified name.
2300
# REQUIRE: name contains "%AUTO%"
2301
# PROMISE: the returned command name is unused.
2302
proc ::snit::RT.UniqueName {countervar type name} {
2303
upvar $countervar counter
2305
# FIRST, bump the counter and define the %AUTO% instance name;
2306
# then substitute it into the specified name. Wrap around at
2307
# 2^31 - 2 to prevent overflow problems.
2309
if {$counter > 2147483646} {
2312
set auto "[namespace tail $type]$counter"
2313
set candidate [Expand $name %AUTO% $auto]
2314
if {[info commands $candidate] eq ""} {
2320
# Returns a unique instance namespace, fully qualified.
2322
# countervar The name of a counter variable
2323
# type The instance's type
2325
# REQUIRE: type is fully qualified
2326
# PROMISE: The returned namespace name is unused.
2328
proc ::snit::RT.UniqueInstanceNamespace {countervar type} {
2329
upvar $countervar counter
2331
# FIRST, bump the counter and define the namespace name.
2332
# Then see if it already exists. Wrap around at
2333
# 2^31 - 2 to prevent overflow problems.
2335
if {$counter > 2147483646} {
2338
set ins "${type}::Snit_inst${counter}"
2339
if {![namespace exists $ins]} {
2345
# Retrieves an option's value from the option database.
2346
# Returns "" if no value is found.
2347
proc ::snit::RT.OptionDbGet {type self opt} {
2348
variable ${type}::Snit_optionInfo
2350
return [option get $self \
2351
$Snit_optionInfo(resource-$opt) \
2352
$Snit_optionInfo(class-$opt)]
2355
#-----------------------------------------------------------------------
2356
# Object Destruction
2358
# Implements the standard "destroy" method
2360
# type The snit type
2361
# selfns The instance's instance namespace
2362
# win The instance's original name
2363
# self The instance's current name
2365
proc ::snit::RT.method.destroy {type selfns win self} {
2366
# Calls Snit_cleanup, which (among other things) calls the
2367
# user's destructor.
2368
::snit::RT.DestroyObject $type $selfns $win
2371
# This is the function that really cleans up; it's automatically
2372
# called when any instance is destroyed, e.g., by "$object destroy"
2373
# for types, and by the <Destroy> event for widgets.
2375
# type The fully-qualified type name.
2376
# selfns The instance namespace
2377
# win The original instance command name.
2379
proc ::snit::RT.DestroyObject {type selfns win} {
2380
variable ${type}::Snit_info
2382
# If the variable Snit_instance doesn't exist then there's no
2383
# instance command for this object -- it's most likely a
2384
# widgetadaptor. Consequently, there are some things that
2385
# we don't need to do.
2386
if {[info exists ${selfns}::Snit_instance]} {
2387
upvar ${selfns}::Snit_instance instance
2389
# First, remove the trace on the instance name, so that we
2390
# don't call RT.DestroyObject recursively.
2391
RT.RemoveInstanceTrace $type $selfns $win $instance
2393
# Next, call the user's destructor
2394
${type}::Snit_destructor $type $selfns $win $instance
2396
# Next, if this isn't a widget, delete the instance command.
2397
# If it is a widget, get the hull component's name, and rename
2398
# it back to the widget name
2400
# Next, delete the hull component's instance command,
2402
if {$Snit_info(isWidget)} {
2403
set hullcmd [::snit::RT.Component $type $selfns hull]
2405
catch {rename $instance ""}
2407
# Clear the bind event
2408
bind Snit$type$win <Destroy> ""
2410
if {[info command $hullcmd] != ""} {
2411
# FIRST, rename the hull back to its original name.
2412
# If the hull is itself a megawidget, it will have its
2413
# own cleanup to do, and it might not do it properly
2414
# if it doesn't have the right name.
2415
rename $hullcmd ::$instance
2421
catch {rename $instance ""}
2425
# Next, delete the instance's namespace. This kills any
2426
# instance variables.
2427
namespace delete $selfns
2430
# Remove instance trace
2432
# type The fully qualified type name
2433
# selfns The instance namespace
2434
# win The original instance name/Tk window name
2435
# instance The current instance name
2437
proc ::snit::RT.RemoveInstanceTrace {type selfns win instance} {
2438
variable ${type}::Snit_info
2440
if {$Snit_info(isWidget)} {
2441
set procname ::$instance
2443
set procname $instance
2446
# NEXT, remove any trace on this name
2448
trace remove command $procname {rename delete} \
2449
[list ::snit::RT.InstanceTrace $type $selfns $win]
2453
#-----------------------------------------------------------------------
2454
# Typecomponent Management and Method Caching
2456
# Typecomponent trace; used for write trace on typecomponent
2457
# variables. Saves the new component object name, provided
2458
# that certain conditions are met. Also clears the typemethod
2461
proc ::snit::RT.TypecomponentTrace {type component n1 n2 op} {
2462
upvar ${type}::Snit_info Snit_info
2463
upvar ${type}::${component} cvar
2464
upvar ${type}::Snit_typecomponents Snit_typecomponents
2466
# Save the new component value.
2467
set Snit_typecomponents($component) $cvar
2469
# Clear the typemethod cache.
2470
# TBD: can we unset just the elements related to
2472
unset -nocomplain -- ${type}::Snit_typemethodCache
2475
# Generates and caches the command for a typemethod.
2478
# method The name of the typemethod to call.
2480
# The return value is one of the following lists:
2482
# {} There's no such method.
2483
# {1} The method has submethods; look again.
2484
# {0 <command>} Here's the command to execute.
2486
proc snit::RT.CacheTypemethodCommand {type method} {
2487
upvar ${type}::Snit_typemethodInfo Snit_typemethodInfo
2488
upvar ${type}::Snit_typecomponents Snit_typecomponents
2489
upvar ${type}::Snit_typemethodCache Snit_typemethodCache
2490
upvar ${type}::Snit_info Snit_info
2492
# FIRST, get the pattern data and the typecomponent name.
2493
set implicitCreate 0
2496
set starredMethod [lreplace $method end end *]
2497
set methodTail [lindex $method end]
2499
if {[info exists Snit_typemethodInfo($method)]} {
2501
} elseif {[info exists Snit_typemethodInfo($starredMethod)]} {
2502
if {[lsearch -exact $Snit_info(excepttypemethods) $methodTail] == -1} {
2503
set key $starredMethod
2507
} elseif {$Snit_info(hasinstances)} {
2508
# Assume the unknown name is an instance name to create, unless
2509
# this is a widget and the style of the name is wrong, or the
2510
# name mimics a standard typemethod.
2512
if {[set ${type}::Snit_info(isWidget)] &&
2513
![string match ".*" $method]} {
2517
# Without this check, the call "$type info" will redefine the
2518
# standard "::info" command, with disastrous results. Since it's
2519
# a likely thing to do if !-typeinfo, put in an explicit check.
2520
if {$method eq "info" || $method eq "destroy"} {
2524
set implicitCreate 1
2525
set instanceName $method
2532
foreach {flag pattern compName} $Snit_typemethodInfo($key) {}
2538
# NEXT, build the substitution list
2543
%m [lindex $method end] \
2544
%j [join $method _]]
2546
if {$compName ne ""} {
2547
if {![info exists Snit_typecomponents($compName)]} {
2548
error "$type delegates typemethod \"$method\" to undefined typecomponent \"$compName\""
2551
lappend subList %c [list $Snit_typecomponents($compName)]
2556
foreach subpattern $pattern {
2557
lappend command [string map $subList $subpattern]
2560
if {$implicitCreate} {
2561
# In this case, $method is the name of the instance to
2562
# create. Don't cache, as we usually won't do this one
2564
lappend command $instanceName
2566
set Snit_typemethodCache($method) [list 0 $command]
2569
return [list 0 $command]
2573
#-----------------------------------------------------------------------
2574
# Component Management and Method Caching
2576
# Retrieves the object name given the component name.
2577
proc ::snit::RT.Component {type selfns name} {
2578
variable ${selfns}::Snit_components
2580
if {[catch {set Snit_components($name)} result]} {
2581
variable ${selfns}::Snit_instance
2583
error "component \"$name\" is undefined in $type $Snit_instance"
2589
# Component trace; used for write trace on component instance
2590
# variables. Saves the new component object name, provided
2591
# that certain conditions are met. Also clears the method
2594
proc ::snit::RT.ComponentTrace {type selfns component n1 n2 op} {
2595
upvar ${type}::Snit_info Snit_info
2596
upvar ${selfns}::${component} cvar
2597
upvar ${selfns}::Snit_components Snit_components
2599
# If they try to redefine the hull component after
2600
# it's been defined, that's an error--but only if
2601
# this is a widget or widget adaptor.
2602
if {"hull" == $component &&
2603
$Snit_info(isWidget) &&
2604
[info exists Snit_components($component)]} {
2605
set cvar $Snit_components($component)
2606
error "The hull component cannot be redefined"
2609
# Save the new component value.
2610
set Snit_components($component) $cvar
2612
# Clear the instance caches.
2613
# TBD: can we unset just the elements related to
2615
RT.ClearInstanceCaches $selfns
2618
# Generates and caches the command for a method.
2620
# type: The instance's type
2621
# selfns: The instance's private namespace
2622
# win: The instance's original name (a Tk widget name, for
2624
# self: The instance's current name.
2625
# method: The name of the method to call.
2627
# The return value is one of the following lists:
2629
# {} There's no such method.
2630
# {1} The method has submethods; look again.
2631
# {0 <command>} Here's the command to execute.
2633
proc ::snit::RT.CacheMethodCommand {type selfns win self method} {
2634
variable ${type}::Snit_info
2635
variable ${type}::Snit_methodInfo
2636
variable ${type}::Snit_typecomponents
2637
variable ${selfns}::Snit_components
2638
variable ${selfns}::Snit_methodCache
2640
# FIRST, get the pattern data and the component name.
2641
set starredMethod [lreplace $method end end *]
2642
set methodTail [lindex $method end]
2644
if {[info exists Snit_methodInfo($method)]} {
2646
} elseif {[info exists Snit_methodInfo($starredMethod)] &&
2647
[lsearch -exact $Snit_info(exceptmethods) $methodTail] == -1} {
2648
set key $starredMethod
2653
foreach {flag pattern compName} $Snit_methodInfo($key) {}
2659
# NEXT, build the substitution list
2664
%m [lindex $method end] \
2665
%j [join $method _] \
2670
if {$compName ne ""} {
2671
if {[info exists Snit_components($compName)]} {
2672
set compCmd $Snit_components($compName)
2673
} elseif {[info exists Snit_typecomponents($compName)]} {
2674
set compCmd $Snit_typecomponents($compName)
2676
error "$type $self delegates method \"$method\" to undefined component \"$compName\""
2679
lappend subList %c [list $compCmd]
2682
# Note: The cached command will executed faster if it's
2686
foreach subpattern $pattern {
2687
lappend command [string map $subList $subpattern]
2690
set commandRec [list 0 $command]
2692
set Snit_methodCache($method) $commandRec
2698
# Looks up a method's command.
2700
# type: The instance's type
2701
# selfns: The instance's private namespace
2702
# win: The instance's original name (a Tk widget name, for
2704
# self: The instance's current name.
2705
# method: The name of the method to call.
2706
# errPrefix: Prefix for any error method
2707
proc ::snit::RT.LookupMethodCommand {type selfns win self method errPrefix} {
2708
set commandRec [snit::RT.CacheMethodCommand \
2709
$type $selfns $win $self \
2713
if {[llength $commandRec] == 0} {
2714
return -code error \
2715
"$errPrefix, \"$self $method\" is not defined"
2716
} elseif {[lindex $commandRec 0] == 1} {
2717
return -code error \
2718
"$errPrefix, wrong number args: should be \"$self\" $method method args"
2721
return [lindex $commandRec 1]
2725
# Clears all instance command caches
2726
proc ::snit::RT.ClearInstanceCaches {selfns} {
2727
unset -nocomplain -- ${selfns}::Snit_methodCache
2728
unset -nocomplain -- ${selfns}::Snit_cgetCache
2729
unset -nocomplain -- ${selfns}::Snit_configureCache
2730
unset -nocomplain -- ${selfns}::Snit_validateCache
2734
#-----------------------------------------------------------------------
2735
# Component Installation
2737
# Implements %TYPE%::installhull. The variables self and selfns
2738
# must be defined in the caller's context.
2740
# Installs the named widget as the hull of a
2741
# widgetadaptor. Once the widget is hijacked, its new name
2742
# is assigned to the hull component.
2744
proc ::snit::RT.installhull {type {using "using"} {widgetType ""} args} {
2745
variable ${type}::Snit_info
2746
variable ${type}::Snit_optionInfo
2749
upvar ${selfns}::hull hull
2750
upvar ${selfns}::options options
2752
# FIRST, make sure we can do it.
2753
if {!$Snit_info(isWidget)} {
2754
error "installhull is valid only for snit::widgetadaptors"
2757
if {[info exists ${selfns}::Snit_instance]} {
2758
error "hull already installed for $type $self"
2761
# NEXT, has it been created yet? If not, create it using
2762
# the specified arguments.
2763
if {"using" == $using} {
2764
# FIRST, create the widget
2765
set cmd [linsert $args 0 $widgetType $self]
2766
set obj [uplevel 1 $cmd]
2768
# NEXT, for each option explicitly delegated to the hull
2769
# that doesn't appear in the usedOpts list, get the
2770
# option database value and apply it--provided that the
2771
# real option name and the target option name are different.
2772
# (If they are the same, then the option database was
2773
# already queried as part of the normal widget creation.)
2775
# Also, we don't need to worry about implicitly delegated
2776
# options, as the option and target option names must be
2778
if {[info exists Snit_optionInfo(delegated-hull)]} {
2780
# FIRST, extract all option names from args
2782
set ndx [lsearch -glob $args "-*"]
2783
foreach {opt val} [lrange $args $ndx end] {
2784
lappend usedOpts $opt
2787
foreach opt $Snit_optionInfo(delegated-hull) {
2788
set target [lindex $Snit_optionInfo(target-$opt) 1]
2790
if {"$target" == $opt} {
2794
set result [lsearch -exact $usedOpts $target]
2796
if {$result != -1} {
2800
set dbval [RT.OptionDbGet $type $self $opt]
2801
$obj configure $target $dbval
2807
if {$obj ne $self} {
2809
"hull name mismatch: \"$obj\" != \"$self\""
2813
# NEXT, get the local option defaults.
2814
foreach opt $Snit_optionInfo(local) {
2815
set dbval [RT.OptionDbGet $type $self $opt]
2818
set options($opt) $dbval
2823
# NEXT, do the magic
2827
set newName "::hull${i}$self"
2828
if {"" == [info commands $newName]} {
2833
rename ::$self $newName
2834
RT.MakeInstanceCommand $type $selfns $self
2836
# Note: this relies on RT.ComponentTrace to do the dirty work.
2842
# Implements %TYPE%::install.
2844
# Creates a widget and installs it as the named component.
2845
# It expects self and selfns to be defined in the caller's context.
2847
proc ::snit::RT.install {type compName "using" widgetType winPath args} {
2848
variable ${type}::Snit_optionInfo
2849
variable ${type}::Snit_info
2852
upvar ${selfns}::$compName comp
2853
upvar ${selfns}::hull hull
2855
# We do the magic option database stuff only if $self is
2857
if {$Snit_info(isWidget)} {
2859
error "tried to install \"$compName\" before the hull exists"
2862
# FIRST, query the option database and save the results
2863
# into args. Insert them before the first option in the
2864
# list, in case there are any non-standard parameters.
2866
# Note: there might not be any delegated options; if so,
2869
if {[info exists Snit_optionInfo(delegated-$compName)]} {
2870
set ndx [lsearch -glob $args "-*"]
2872
foreach opt $Snit_optionInfo(delegated-$compName) {
2873
set dbval [RT.OptionDbGet $type $self $opt]
2876
set target [lindex $Snit_optionInfo(target-$opt) 1]
2877
set args [linsert $args $ndx $target $dbval]
2883
# NEXT, create the component and save it.
2884
set cmd [concat [list $widgetType $winPath] $args]
2885
set comp [uplevel 1 $cmd]
2887
# NEXT, handle the option database for "delegate option *",
2889
if {$Snit_info(isWidget) && $Snit_optionInfo(starcomp) eq $compName} {
2890
# FIRST, get the list of option specs from the widget.
2891
# If configure doesn't work, skip it.
2892
if {[catch {$comp configure} specs]} {
2896
# NEXT, get the set of explicitly used options from args
2898
set ndx [lsearch -glob $args "-*"]
2899
foreach {opt val} [lrange $args $ndx end] {
2900
lappend usedOpts $opt
2903
# NEXT, "delegate option *" matches all options defined
2904
# by this widget that aren't defined by the widget as a whole,
2905
# and that aren't excepted. Plus, we skip usedOpts. So build
2906
# a list of the options it can't match.
2907
set skiplist [concat \
2909
$Snit_optionInfo(except) \
2910
$Snit_optionInfo(local) \
2911
$Snit_optionInfo(delegated)]
2913
# NEXT, loop over all of the component's options, and set
2914
# any not in the skip list for which there is an option
2916
foreach spec $specs {
2918
if {[llength $spec] != 5} {
2922
set opt [lindex $spec 0]
2924
if {[lsearch -exact $skiplist $opt] != -1} {
2928
set res [lindex $spec 1]
2929
set cls [lindex $spec 2]
2931
set dbvalue [option get $self $res $cls]
2933
if {"" != $dbvalue} {
2934
$comp configure $opt $dbvalue
2943
#-----------------------------------------------------------------------
2944
# Method/Variable Name Qualification
2946
# Implements %TYPE%::variable. Requires selfns.
2947
proc ::snit::RT.variable {varname} {
2950
if {![string match "::*" $varname]} {
2951
uplevel 1 [list upvar 1 ${selfns}::$varname $varname]
2953
# varname is fully qualified; let the standard
2954
# "variable" command handle it.
2955
uplevel 1 [list ::variable $varname]
2959
# Fully qualifies a typevariable name.
2961
# This is used to implement the mytypevar command.
2963
proc ::snit::RT.mytypevar {type name} {
2964
return ${type}::$name
2967
# Fully qualifies an instance variable name.
2969
# This is used to implement the myvar command.
2970
proc ::snit::RT.myvar {name} {
2972
return ${selfns}::$name
2975
# Use this like "list" to convert a proc call into a command
2976
# string to pass to another object (e.g., as a -command).
2977
# Qualifies the proc name properly.
2979
# This is used to implement the "myproc" command.
2981
proc ::snit::RT.myproc {type procname args} {
2982
set procname "${type}::$procname"
2983
return [linsert $args 0 $procname]
2987
proc ::snit::RT.codename {type name} {
2988
return "${type}::$name"
2991
# Use this like "list" to convert a typemethod call into a command
2992
# string to pass to another object (e.g., as a -command).
2993
# Inserts the type command at the beginning.
2995
# This is used to implement the "mytypemethod" command.
2997
proc ::snit::RT.mytypemethod {type args} {
2998
return [linsert $args 0 $type]
3001
# Use this like "list" to convert a method call into a command
3002
# string to pass to another object (e.g., as a -command).
3003
# Inserts the code at the beginning to call the right object, even if
3004
# the object's name has changed. Requires that selfns be defined
3005
# in the calling context, eg. can only be called in instance
3008
# This is used to implement the "mymethod" command.
3010
proc ::snit::RT.mymethod {args} {
3012
return [linsert $args 0 ::snit::RT.CallInstance ${selfns}]
3015
# Calls an instance method for an object given its
3016
# instance namespace and remaining arguments (the first of which
3017
# will be the method name.
3019
# selfns The instance namespace
3020
# args The arguments
3022
# Uses the selfns to determine $self, and calls the method
3023
# in the normal way.
3025
# This is used to implement the "mymethod" command.
3027
proc ::snit::RT.CallInstance {selfns args} {
3028
upvar ${selfns}::Snit_instance self
3030
set retval [catch {uplevel 1 [linsert $args 0 $self]} result]
3036
return -code error -errorinfo $errorInfo \
3037
-errorcode $errorCode $result
3039
return -code $retval $result
3046
# Looks for the named option in the named variable. If found,
3047
# it and its value are removed from the list, and the value
3048
# is returned. Otherwise, the default value is returned.
3049
# If the option is undelegated, it's own default value will be
3050
# used if none is specified.
3052
# Implements the "from" command.
3054
proc ::snit::RT.from {type argvName option {defvalue ""}} {
3055
variable ${type}::Snit_optionInfo
3056
upvar $argvName argv
3058
set ioption [lsearch -exact $argv $option]
3060
if {$ioption == -1} {
3061
if {"" == $defvalue &&
3062
[info exists Snit_optionInfo(default-$option)]} {
3063
return $Snit_optionInfo(default-$option)
3069
set ivalue [expr {$ioption + 1}]
3070
set value [lindex $argv $ivalue]
3072
set argv [lreplace $argv $ioption $ivalue]
3077
#-----------------------------------------------------------------------
3080
# Implements the standard "destroy" typemethod:
3081
# Destroys a type completely.
3083
# type The snit type
3085
proc ::snit::RT.typemethod.destroy {type} {
3086
variable ${type}::Snit_info
3088
# FIRST, destroy all instances
3089
foreach selfns [namespace children $type] {
3090
if {![namespace exists $selfns]} {
3093
upvar ${selfns}::Snit_instance obj
3095
if {$Snit_info(isWidget)} {
3098
if {"" != [info commands $obj]} {
3104
# NEXT, destroy the type's data.
3105
namespace delete $type
3107
# NEXT, get rid of the type command.
3113
#-----------------------------------------------------------------------
3116
# Implements the standard "cget" method
3118
# type The snit type
3119
# selfns The instance's instance namespace
3120
# win The instance's original name
3121
# self The instance's current name
3122
# option The name of the option
3124
proc ::snit::RT.method.cget {type selfns win self option} {
3125
if {[catch {set ${selfns}::Snit_cgetCache($option)} command]} {
3126
set command [snit::RT.CacheCgetCommand $type $selfns $win $self $option]
3128
if {[llength $command] == 0} {
3129
return -code error "unknown option \"$option\""
3136
# Retrieves and caches the command that implements "cget" for the
3139
# type The snit type
3140
# selfns The instance's instance namespace
3141
# win The instance's original name
3142
# self The instance's current name
3143
# option The name of the option
3145
proc ::snit::RT.CacheCgetCommand {type selfns win self option} {
3146
variable ${type}::Snit_optionInfo
3147
variable ${selfns}::Snit_cgetCache
3149
if {[info exists Snit_optionInfo(islocal-$option)]} {
3150
# We know the item; it's either local, or explicitly delegated.
3151
if {$Snit_optionInfo(islocal-$option)} {
3152
# It's a local option. If it has a cget method defined,
3153
# use it; otherwise just return the value.
3155
if {$Snit_optionInfo(cget-$option) eq ""} {
3156
set command [list set ${selfns}::options($option)]
3158
set command [snit::RT.LookupMethodCommand \
3159
$type $selfns $win $self \
3160
$Snit_optionInfo(cget-$option) \
3161
"can't cget $option"]
3163
lappend command $option
3166
set Snit_cgetCache($option) $command
3170
# Explicitly delegated option; get target
3171
set comp [lindex $Snit_optionInfo(target-$option) 0]
3172
set target [lindex $Snit_optionInfo(target-$option) 1]
3173
} elseif {$Snit_optionInfo(starcomp) ne "" &&
3174
[lsearch -exact $Snit_optionInfo(except) $option] == -1} {
3175
# Unknown option, but unknowns are delegated; get target.
3176
set comp $Snit_optionInfo(starcomp)
3182
# Get the component's object.
3183
set obj [RT.Component $type $selfns $comp]
3185
set command [list $obj cget $target]
3186
set Snit_cgetCache($option) $command
3191
# Implements the standard "configurelist" method
3193
# type The snit type
3194
# selfns The instance's instance namespace
3195
# win The instance's original name
3196
# self The instance's current name
3197
# optionlist A list of options and their values.
3199
proc ::snit::RT.method.configurelist {type selfns win self optionlist} {
3200
variable ${type}::Snit_optionInfo
3202
foreach {option value} $optionlist {
3203
# FIRST, get the configure command, caching it if need be.
3204
if {[catch {set ${selfns}::Snit_configureCache($option)} command]} {
3205
set command [snit::RT.CacheConfigureCommand \
3206
$type $selfns $win $self $option]
3208
if {[llength $command] == 0} {
3209
return -code error "unknown option \"$option\""
3213
# NEXT, the caching the configure command also cached the
3214
# validate command, if any. If we have one, run it.
3215
set valcommand [set ${selfns}::Snit_validateCache($option)]
3217
if {[llength $valcommand]} {
3218
lappend valcommand $value
3219
uplevel 1 $valcommand
3222
# NEXT, configure the option with the value.
3223
lappend command $value
3230
# Retrieves and caches the command that stores the named option.
3231
# Also stores the command that validates the name option if any;
3232
# If none, the validate command is "", so that the cache is always
3235
# type The snit type
3236
# selfns The instance's instance namespace
3237
# win The instance's original name
3238
# self The instance's current name
3239
# option An option name
3241
proc ::snit::RT.CacheConfigureCommand {type selfns win self option} {
3242
variable ${type}::Snit_optionInfo
3243
variable ${selfns}::Snit_configureCache
3244
variable ${selfns}::Snit_validateCache
3246
if {[info exist Snit_optionInfo(islocal-$option)]} {
3247
# We know the item; it's either local, or explicitly delegated.
3249
if {$Snit_optionInfo(islocal-$option)} {
3250
# It's a local option.
3252
# If it's readonly, it throws an error if we're already
3254
if {$Snit_optionInfo(readonly-$option)} {
3255
if {[set ${selfns}::Snit_iinfo(constructed)]} {
3256
error "option $option can only be set at instance creation"
3260
# If it has a validate method, cache that for later.
3261
if {$Snit_optionInfo(validate-$option) ne ""} {
3262
set command [snit::RT.LookupMethodCommand \
3263
$type $selfns $win $self \
3264
$Snit_optionInfo(validate-$option) \
3265
"can't validate $option"]
3267
lappend command $option
3268
set Snit_validateCache($option) $command
3270
set Snit_validateCache($option) ""
3273
# If it has a configure method defined,
3274
# cache it; otherwise, just set the value.
3276
if {$Snit_optionInfo(configure-$option) eq ""} {
3277
set command [list set ${selfns}::options($option)]
3279
set command [snit::RT.LookupMethodCommand \
3280
$type $selfns $win $self \
3281
$Snit_optionInfo(configure-$option) \
3282
"can't configure $option"]
3284
lappend command $option
3287
set Snit_configureCache($option) $command
3291
# Delegated option: get target.
3292
set comp [lindex $Snit_optionInfo(target-$option) 0]
3293
set target [lindex $Snit_optionInfo(target-$option) 1]
3294
} elseif {$Snit_optionInfo(starcomp) != "" &&
3295
[lsearch -exact $Snit_optionInfo(except) $option] == -1} {
3296
# Unknown option, but unknowns are delegated.
3297
set comp $Snit_optionInfo(starcomp)
3303
# There is no validate command in this case; save an empty string.
3304
set Snit_validateCache($option) ""
3306
# Get the component's object
3307
set obj [RT.Component $type $selfns $comp]
3309
set command [list $obj configure $target]
3310
set Snit_configureCache($option) $command
3315
# Implements the standard "configure" method
3317
# type The snit type
3318
# selfns The instance's instance namespace
3319
# win The instance's original name
3320
# self The instance's current name
3321
# args A list of options and their values, possibly empty.
3323
proc ::snit::RT.method.configure {type selfns win self args} {
3324
# If two or more arguments, set values as usual.
3325
if {[llength $args] >= 2} {
3326
::snit::RT.method.configurelist $type $selfns $win $self $args
3330
# If zero arguments, acquire data for each known option
3331
# and return the list
3332
if {[llength $args] == 0} {
3334
foreach opt [RT.method.info.options $type $selfns $win $self] {
3335
# Refactor this, so that we don't need to call via $self.
3336
lappend result [RT.GetOptionDbSpec \
3337
$type $selfns $win $self $opt]
3343
# They want it for just one.
3344
set opt [lindex $args 0]
3346
return [RT.GetOptionDbSpec $type $selfns $win $self $opt]
3350
# Retrieves the option database spec for a single option.
3352
# type The snit type
3353
# selfns The instance's instance namespace
3354
# win The instance's original name
3355
# self The instance's current name
3356
# option The name of an option
3358
# TBD: This is a bad name. What it's returning is the
3359
# result of the configure query.
3361
proc ::snit::RT.GetOptionDbSpec {type selfns win self opt} {
3362
variable ${type}::Snit_optionInfo
3364
upvar ${selfns}::Snit_components Snit_components
3365
upvar ${selfns}::options options
3367
if {[info exists options($opt)]} {
3368
# This is a locally-defined option. Just build the
3369
# list and return it.
3370
set res $Snit_optionInfo(resource-$opt)
3371
set cls $Snit_optionInfo(class-$opt)
3372
set def $Snit_optionInfo(default-$opt)
3374
return [list $opt $res $cls $def \
3375
[RT.method.cget $type $selfns $win $self $opt]]
3376
} elseif {[info exists Snit_optionInfo(target-$opt)]} {
3377
# This is an explicitly delegated option. The only
3378
# thing we don't have is the default.
3379
set res $Snit_optionInfo(resource-$opt)
3380
set cls $Snit_optionInfo(class-$opt)
3383
set logicalName [lindex $Snit_optionInfo(target-$opt) 0]
3384
set comp $Snit_components($logicalName)
3385
set target [lindex $Snit_optionInfo(target-$opt) 1]
3387
if {[catch {$comp configure $target} result]} {
3390
set defValue [lindex $result 3]
3393
return [list $opt $res $cls $defValue [$self cget $opt]]
3394
} elseif {$Snit_optionInfo(starcomp) ne "" &&
3395
[lsearch -exact $Snit_optionInfo(except) $opt] == -1} {
3396
set logicalName $Snit_optionInfo(starcomp)
3398
set comp $Snit_components($logicalName)
3400
if {[catch {set value [$comp cget $target]} result]} {
3401
error "unknown option \"$opt\""
3404
if {![catch {$comp configure $target} result]} {
3405
# Replace the delegated option name with the local name.
3406
return [::snit::Expand $result $target $opt]
3409
# configure didn't work; return simple form.
3410
return [list $opt "" "" "" $value]
3412
error "unknown option \"$opt\""
3416
#-----------------------------------------------------------------------
3417
# Type Introspection
3419
# Implements the standard "info" typemethod.
3421
# type The snit type
3422
# command The info subcommand
3423
# args All other arguments.
3425
proc ::snit::RT.typemethod.info {type command args} {
3429
switch -exact $command {
3433
# TBD: it should be possible to delete this error
3435
set errflag [catch {
3436
uplevel 1 [linsert $args 0 \
3437
::snit::RT.typemethod.info.$command $type]
3441
return -code error -errorinfo $errorInfo \
3442
-errorcode $errorCode $result
3448
error "\"$type info $command\" is not defined"
3454
# Returns a list of the type's typevariables whose names match a
3455
# pattern, excluding Snit internal variables.
3458
# pattern Optional. The glob pattern to match. Defaults
3461
proc ::snit::RT.typemethod.info.typevars {type {pattern *}} {
3463
foreach name [info vars "${type}::$pattern"] {
3464
set tail [namespace tail $name]
3465
if {![string match "Snit_*" $tail]} {
3466
lappend result $name
3473
# Returns a list of the type's methods whose names match a
3474
# pattern. If "delegate typemethod *" is used, the list may
3478
# pattern Optional. The glob pattern to match. Defaults
3481
proc ::snit::RT.typemethod.info.typemethods {type {pattern *}} {
3482
variable ${type}::Snit_typemethodInfo
3483
variable ${type}::Snit_typemethodCache
3485
# FIRST, get the explicit names, skipping prefixes.
3488
foreach name [array names Snit_typemethodInfo -glob $pattern] {
3489
if {[lindex $Snit_typemethodInfo($name) 0] != 1} {
3490
lappend result $name
3494
# NEXT, add any from the cache that aren't explicit.
3495
if {[info exists Snit_typemethodInfo(*)]} {
3496
# First, remove "*" from the list.
3497
set ndx [lsearch -exact $result "*"]
3499
set result [lreplace $result $ndx $ndx]
3502
foreach name [array names Snit_typemethodCache -glob $pattern] {
3503
if {[lsearch -exact $result $name] == -1} {
3504
lappend result $name
3512
# Returns a list of the type's instances whose names match
3516
# pattern Optional. The glob pattern to match
3519
# REQUIRE: type is fully qualified.
3521
proc ::snit::RT.typemethod.info.instances {type {pattern *}} {
3524
foreach selfns [namespace children $type] {
3525
upvar ${selfns}::Snit_instance instance
3527
if {[string match $pattern $instance]} {
3528
lappend result $instance
3535
#-----------------------------------------------------------------------
3536
# Instance Introspection
3538
# Implements the standard "info" method.
3540
# type The snit type
3541
# selfns The instance's instance namespace
3542
# win The instance's original name
3543
# self The instance's current name
3544
# command The info subcommand
3545
# args All other arguments.
3547
proc ::snit::RT.method.info {type selfns win self command args} {
3548
switch -exact $command {
3555
set errflag [catch {
3556
uplevel 1 [linsert $args 0 ::snit::RT.method.info.$command \
3557
$type $selfns $win $self]
3562
return -code error -errorinfo $errorInfo $result
3568
# error "\"$self info $command\" is not defined"
3569
return -code error "\"$self info $command\" is not defined"
3576
# Returns the instance's type
3577
proc ::snit::RT.method.info.type {type selfns win self} {
3581
# $self info typevars
3583
# Returns the instance's type's typevariables
3584
proc ::snit::RT.method.info.typevars {type selfns win self {pattern *}} {
3585
return [RT.typemethod.info.typevars $type $pattern]
3588
# $self info typemethods
3590
# Returns the instance's type's typemethods
3591
proc ::snit::RT.method.info.typemethods {type selfns win self {pattern *}} {
3592
return [RT.typemethod.info.typemethods $type $pattern]
3595
# Returns a list of the instance's methods whose names match a
3596
# pattern. If "delegate method *" is used, the list may
3600
# selfns The instance namespace
3601
# win The original instance name
3602
# self The current instance name
3603
# pattern Optional. The glob pattern to match. Defaults
3606
proc ::snit::RT.method.info.methods {type selfns win self {pattern *}} {
3607
variable ${type}::Snit_methodInfo
3608
variable ${selfns}::Snit_methodCache
3610
# FIRST, get the explicit names, skipping prefixes.
3613
foreach name [array names Snit_methodInfo -glob $pattern] {
3614
if {[lindex $Snit_methodInfo($name) 0] != 1} {
3615
lappend result $name
3619
# NEXT, add any from the cache that aren't explicit.
3620
if {[info exists Snit_methodInfo(*)]} {
3621
# First, remove "*" from the list.
3622
set ndx [lsearch -exact $result "*"]
3624
set result [lreplace $result $ndx $ndx]
3627
foreach name [array names Snit_methodCache -glob $pattern] {
3628
if {[lsearch -exact $result $name] == -1} {
3629
lappend result $name
3639
# Returns the instance's instance variables
3640
proc ::snit::RT.method.info.vars {type selfns win self {pattern *}} {
3642
foreach name [info vars "${selfns}::$pattern"] {
3643
set tail [namespace tail $name]
3644
if {![string match "Snit_*" $tail]} {
3645
lappend result $name
3652
# $self info options
3654
# Returns a list of the names of the instance's options
3655
proc ::snit::RT.method.info.options {type selfns win self {pattern *}} {
3656
variable ${type}::Snit_optionInfo
3658
# First, get the local and explicitly delegated options
3659
set result [concat $Snit_optionInfo(local) $Snit_optionInfo(delegated)]
3661
# If "configure" works as for Tk widgets, add the resulting
3662
# options to the list. Skip excepted options
3663
if {$Snit_optionInfo(starcomp) ne ""} {
3664
upvar ${selfns}::Snit_components Snit_components
3665
set logicalName $Snit_optionInfo(starcomp)
3666
set comp $Snit_components($logicalName)
3668
if {![catch {$comp configure} records]} {
3669
foreach record $records {
3670
set opt [lindex $record 0]
3671
if {[lsearch -exact $result $opt] == -1 &&
3672
[lsearch -exact $Snit_optionInfo(except) $opt] == -1} {
3679
# Next, apply the pattern
3682
foreach name $result {
3683
if {[string match $pattern $name]} {