snit2.tcl

来自「Linux下的MSN聊天程序源码」· TCL 代码 · 共 2,009 行 · 第 1/5 页

TCL
2,009
字号
# "as"          sugar; if not "", must be "as"# methodname    The desired method name for the component's command, or ""proc ::snit::Comp.statement.expose {component {"as" ""} {methodname ""}} {    variable compile    # FIRST, define the component    Comp.DefineComponent $component    # NEXT, define the method just as though it were in the type    # definition.    if {[string equal $methodname ""]} {        set methodname $component    }    Comp.statement.method $methodname args [Expand {        if {[llength $args] == 0} {            return $%COMPONENT%        }        if {[string equal $%COMPONENT% ""]} {            error "undefined component \"%COMPONENT%\""        }        set cmd [linsert $args 0 $%COMPONENT%]        return [uplevel 1 $cmd]    } %COMPONENT% $component]}#-----------------------------------------------------------------------# Public commands# Compile a type definition, and return the results as a list of two# items: the fully-qualified type name, and a script that will define# the type when executed.## which		type, widget, or widgetadaptor# type          the type name# body          the type definitionproc ::snit::compile {which type body} {    return [Comp.Compile $which $type $body]}proc ::snit::type {type body} {    return [Comp.Define [Comp.Compile type $type $body]]}proc ::snit::widget {type body} {    return [Comp.Define [Comp.Compile widget $type $body]]}proc ::snit::widgetadaptor {type body} {    return [Comp.Define [Comp.Compile widgetadaptor $type $body]]}proc ::snit::typemethod {type method arglist body} {    # Make sure the type exists.    if {![info exists ${type}::Snit_info]} {        error "no such type: \"$type\""    }    upvar ${type}::Snit_info           Snit_info    upvar ${type}::Snit_typemethodInfo Snit_typemethodInfo    # FIRST, check the typemethod name against previously defined     # typemethods.    Comp.CheckMethodName $method 0 ${type}::Snit_typemethodInfo \        "Cannot define \"$method\""    # NEXT, check the arguments    CheckArgs "snit::typemethod $type $method" $arglist    # Next, add magic reference to type.    set arglist [concat type $arglist]    # Next, add typevariable declarations to body:    set body "$Snit_info(tvardecs)\n$body"    # Next, define it.    if {[llength $method] == 1} {        set Snit_typemethodInfo($method) {0 "%t::Snit_typemethod%m %t" ""}        uplevel 1 [list proc ${type}::Snit_typemethod$method $arglist $body]    } else {        set Snit_typemethodInfo($method) {0 "%t::Snit_htypemethod%j %t" ""}        set suffix [join $method _]        uplevel 1 [list proc ${type}::Snit_htypemethod$suffix $arglist $body]    }}proc ::snit::method {type method arglist body} {    # Make sure the type exists.    if {![info exists ${type}::Snit_info]} {        error "no such type: \"$type\""    }    upvar ${type}::Snit_methodInfo  Snit_methodInfo    upvar ${type}::Snit_info        Snit_info    # FIRST, check the method name against previously defined     # methods.    Comp.CheckMethodName $method 0 ${type}::Snit_methodInfo \        "Cannot define \"$method\""    # NEXT, check the arguments    CheckArgs "snit::method $type $method" $arglist    # Next, add magic references to type and self.    set arglist [concat type selfns win self $arglist]    # Next, add variable declarations to body:    set body "$Snit_info(tvardecs)$Snit_info(ivardecs)\n$body"    # Next, define it.    if {[llength $method] == 1} {        set Snit_methodInfo($method) {0 "%t::Snit_method%m %t %n %w %s" ""}        uplevel 1 [list proc ${type}::Snit_method$method $arglist $body]    } else {        set Snit_methodInfo($method) {0 "%t::Snit_hmethod%j %t %n %w %s" ""}        set suffix [join $method _]        uplevel 1 [list proc ${type}::Snit_hmethod$suffix $arglist $body]    }}# Defines a proc within the compiler; this proc can call other# type definition statements, and thus can be used for meta-programming.proc ::snit::macro {name arglist body} {    variable compiler    variable reservedwords    # FIRST, make sure the compiler is defined.    Comp.Init    # NEXT, check the macro name against the reserved words    if {[lsearch -exact $reservedwords $name] != -1} {        error "invalid macro name \"$name\""    }    # NEXT, see if the name has a namespace; if it does, define the    # namespace.    set ns [namespace qualifiers $name]    if {$ns ne ""} {        $compiler eval "namespace eval $ns {}"    }    # NEXT, define the macro    $compiler eval [list _proc $name $arglist $body]}#-----------------------------------------------------------------------# Utility Functions## These are utility functions used while compiling Snit types.# Builds a template from a tagged list of text blocks, then substitutes# all symbols in the mapTable, returning the expanded template.proc ::snit::Expand {template args} {    return [string map $args $template]}# Expands a template and appends it to a variable.proc ::snit::Mappend {varname template args} {    upvar $varname myvar    append myvar [string map $args $template]}# Checks argument list against reserved args proc ::snit::CheckArgs {which arglist} {    variable reservedArgs        foreach name $reservedArgs {        if {$name in $arglist} {            error "$which's arglist may not contain \"$name\" explicitly"        }    }}# Capitalizes the first letter of a string.proc ::snit::Capitalize {text} {    set first [string index $text 0]    set rest [string range $text 1 end]    return "[string toupper $first]$rest"}#=======================================================================# Snit Runtime Library## These are procs used by Snit types and widgets at runtime.#-----------------------------------------------------------------------# Object Creation# Creates a new instance of the snit::type given its name and the args.## type		The snit::type# name		The instance name# args		Args to pass to the constructorproc ::snit::RT.type.typemethod.create {type name args} {    variable ${type}::Snit_info    variable ${type}::Snit_optionInfo    # FIRST, qualify the name.    if {![string match "::*" $name]} {        # Get caller's namespace;         # append :: if not global namespace.        set ns [uplevel 1 [list namespace current]]        if {"::" != $ns} {            append ns "::"        }                set name "$ns$name"    }    # NEXT, if %AUTO% appears in the name, generate a unique     # command name.  Otherwise, ensure that the name isn't in use.    if {[string match "*%AUTO%*" $name]} {        set name [::snit::RT.UniqueName Snit_info(counter) $type $name]    } elseif {!$Snit_info(canreplace) && [llength [info commands $name]]} {        error "command \"$name\" already exists"    }    # NEXT, create the instance's namespace.    set selfns \        [::snit::RT.UniqueInstanceNamespace Snit_info(counter) $type]    namespace eval $selfns {}    # NEXT, install the dispatcher    RT.MakeInstanceCommand $type $selfns $name    # Initialize the options to their defaults.     upvar ${selfns}::options options    foreach opt $Snit_optionInfo(local) {        set options($opt) $Snit_optionInfo(default-$opt)    }            # Initialize the instance vars to their defaults.    # selfns must be defined, as it is used implicitly.    ${type}::Snit_instanceVars $selfns    # Execute the type's constructor.    set errcode [catch {        RT.ConstructInstance $type $selfns $name $args    } result]    if {$errcode} {        global errorInfo        global errorCode                set theInfo $errorInfo        set theCode $errorCode        ::snit::RT.DestroyObject $type $selfns $name        error "Error in constructor: $result" $theInfo $theCode    }    # NEXT, return the object's name.    return $name}# Creates a new instance of the snit::widget or snit::widgetadaptor# given its name and the args.## type		The snit::widget or snit::widgetadaptor# name		The instance name# args		Args to pass to the constructorproc ::snit::RT.widget.typemethod.create {type name args} {    variable ${type}::Snit_info    variable ${type}::Snit_optionInfo    # FIRST, if %AUTO% appears in the name, generate a unique     # command name.    if {[string match "*%AUTO%*" $name]} {        set name [::snit::RT.UniqueName Snit_info(counter) $type $name]    }                # NEXT, create the instance's namespace.    set selfns \        [::snit::RT.UniqueInstanceNamespace Snit_info(counter) $type]    namespace eval $selfns { }                # NEXT, Initialize the widget's own options to their defaults.    upvar ${selfns}::options options    foreach opt $Snit_optionInfo(local) {        set options($opt) $Snit_optionInfo(default-$opt)    }    # Initialize the instance vars to their defaults.    ${type}::Snit_instanceVars $selfns    # NEXT, if this is a normal widget (not a widget adaptor) then     # create a frame as its hull.  We set the frame's -class to    # the user's widgetclass, or, if none, to the basename of    # the $type with an initial upper case letter.    if {!$Snit_info(isWidgetAdaptor)} {        # FIRST, determine the class name        if {"" == $Snit_info(widgetclass)} {            set Snit_info(widgetclass) \                [::snit::Capitalize [namespace tail $type]]        }        # NEXT, create the widget        set self $name        package require Tk        ${type}::installhull using \            $Snit_info(hulltype) -class $Snit_info(widgetclass)        # NEXT, let's query the option database for our        # widget, now that we know that it exists.        foreach opt $Snit_optionInfo(local) {            set dbval [RT.OptionDbGet $type $name $opt]            if {"" != $dbval} {                set options($opt) $dbval            }        }    }    # Execute the type's constructor, and verify that it    # has a hull.    set errcode [catch {        RT.ConstructInstance $type $selfns $name $args        ::snit::RT.Component $type $selfns hull        # Prepare to call the object's destructor when the        # <Destroy> event is received.  Use a Snit-specific bindtag        # so that the widget name's tag is unencumbered.        bind Snit$type$name <Destroy> [::snit::Expand {            ::snit::RT.DestroyObject %TYPE% %NS% %W        } %TYPE% $type %NS% $selfns]        # Insert the bindtag into the list of bindtags right        # after the widget name.        set taglist [bindtags $name]        set ndx [lsearch -exact $taglist $name]        incr ndx        bindtags $name [linsert $taglist $ndx Snit$type$name]    } result]    if {$errcode} {        global errorInfo        global errorCode        set theInfo $errorInfo        set theCode $errorCode        ::snit::RT.DestroyObject $type $selfns $name        error "Error in constructor: $result" $theInfo $theCode    }    # NEXT, return the object's name.    return $name}# RT.MakeInstanceCommand type selfns instance## type        The object type# selfns      The instance namespace# instance    The instance name## Creates the instance proc.proc ::snit::RT.MakeInstanceCommand {type selfns instance} {    variable ${type}::Snit_info            # FIRST, remember the instance name.  The Snit_instance variable    # allows the instance to figure out its current name given the    # instance namespace.    upvar ${selfns}::Snit_instance Snit_instance    set Snit_instance $instance    # NEXT, qualify the proc name if it's a widget.    if {$Snit_info(isWidget)} {        set procname ::$instance    } else {        set procname $instance    }    # NEXT, install the new proc    # WHD: Snit 2.0 code    set unknownCmd [list ::snit::RT.UnknownMethod $type $selfns $instance ""]    set createCmd [list namespace ensemble create \                       -command $procname \                       -unknown $unknownCmd \                       -prefixes 0]    namespace eval $selfns $createCmd    # NEXT, add the trace.    trace add command $procname {ren

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?