#============================================================================== # Contains utility procedures for mega-widgets. # # Structure of the module: # - Namespace initialization # - Public utility procedures # # Copyright (c) 2000-2004 Csaba Nemethi (E-mail: csaba.nemethi@t-online.de) #============================================================================== package require Tcl 8 package require Tk 8 # # Namespace initialization # ======================== # namespace eval mwutil { # # Public variables: # variable version 1.7 variable library [file dirname [info script]] # # Public procedures: # namespace export wrongNumArgs defineKeyNav generateEvent \ configure fullConfigOpt fullOpt enumOpts \ setConfigVals configSubCmd attribSubCmd } # # Public utility procedures # ========================= # #------------------------------------------------------------------------------ # mwutil::wrongNumArgs # # Generates a "wrong # args" error message. #------------------------------------------------------------------------------ proc mwutil::wrongNumArgs args { set optList {} foreach arg $args { lappend optList \"$arg\" } return -code error "wrong # args: should be [enumOpts $optList]" } #------------------------------------------------------------------------------ # mwutil::defineKeyNav # # For a given mega-widget class, the procedure defines the binding tag # ${class}KeyNav as a partial replacement for "all", by substituting the # scripts bound to the events , , and <> # with new ones which propagate these events to the mega-widget of the given # class containing the widget to which the event was reported. (The event # was replaced with <> in Tk 8.3.0.) This tag is # designed to be inserted before "all" in the list of binding tags of a # descendant of a mega-widget of the specified class. #------------------------------------------------------------------------------ proc mwutil::defineKeyNav class { foreach event { <>} { bind ${class}KeyNav $event \ [list mwutil::generateEvent %W $class $event] } } #------------------------------------------------------------------------------ # mwutil::generateEvent # # This procedure generates the given event for the mega-widget of the specified # class containing the widget w if that mega-widget is not the only widget # receiving the focus during keyboard traversal within its top-level widget. #------------------------------------------------------------------------------ proc mwutil::generateEvent {w class event} { while {[string compare [winfo class $w] $class] != 0} { set w [winfo parent $w] } if {[string compare [tk_focusNext $w] $w] != 0} { focus $w ;# necessary on Windows event generate $w $event } return -code break "" } #------------------------------------------------------------------------------ # mwutil::configure # # Configures the widget win by processing the command-line arguments specified # in optValPairs and, if the value of initialize is true, also those database # options that don't match any command-line arguments. #------------------------------------------------------------------------------ proc mwutil::configure {win configSpecsName configValsName \ configCmd optValPairs initialize} { upvar $configSpecsName configSpecs upvar $configValsName configVals # # Process the command-line arguments # set cmdLineOpts {} set savedVals {} set failed 0 set count [llength $optValPairs] foreach {opt val} $optValPairs { if {[catch {fullConfigOpt $opt configSpecs} result] != 0} { set failed 1 break } if {$count == 1} { set result "value for \"$opt\" missing" set failed 1 break } set opt $result lappend cmdLineOpts $opt lappend savedVals $configVals($opt) if {[catch {eval $configCmd [list $win $opt $val]} result] != 0} { set failed 1 break } incr count -2 } if {$failed} { # # Restore the saved values # foreach opt $cmdLineOpts val $savedVals { eval $configCmd [list $win $opt $val] } return -code error $result } if {$initialize} { # # Process those configuration options that were not # given as command-line arguments; use the corresponding # values from the option database if available # foreach opt [lsort [array names configSpecs]] { if {[llength $configSpecs($opt)] == 1 || [lsearch -exact $cmdLineOpts $opt] >= 0} { continue } set dbName [lindex $configSpecs($opt) 0] set dbClass [lindex $configSpecs($opt) 1] set dbValue [option get $win $dbName $dbClass] if {[string compare $dbValue ""] != 0} { if {[catch { eval $configCmd [list $win $opt $dbValue] } result] != 0} { return -code error $result } } else { set default [lindex $configSpecs($opt) 3] eval $configCmd [list $win $opt $default] } } } return "" } #------------------------------------------------------------------------------ # mwutil::fullConfigOpt # # Returns the full configuration option corresponding to the possibly # abbreviated option opt. #------------------------------------------------------------------------------ proc mwutil::fullConfigOpt {opt configSpecsName} { upvar $configSpecsName configSpecs if {[info exists configSpecs($opt)]} { if {[llength $configSpecs($opt)] == 1} { return $configSpecs($opt) } else { return $opt } } set optList [lsort [array names configSpecs]] set count 0 foreach elem $optList { if {[string first $opt $elem] == 0} { incr count if {$count == 1} { set option $elem } else { break } } } switch $count { 0 { ### return -code error "unknown option \"$opt\"" return -code error \ "bad option \"$opt\": must be [enumOpts $optList]" } 1 { if {[llength $configSpecs($option)] == 1} { return $configSpecs($option) } else { return $option } } default { ### return -code error "unknown option \"$opt\"" return -code error \ "ambiguous option \"$opt\": must be [enumOpts $optList]" } } } #------------------------------------------------------------------------------ # mwutil::fullOpt # # Returns the full option corresponding to the possibly abbreviated option opt. #------------------------------------------------------------------------------ proc mwutil::fullOpt {kind opt optList} { if {[lsearch -exact $optList $opt] >= 0} { return $opt } set count 0 foreach elem $optList { if {[string first $opt $elem] == 0} { incr count if {$count == 1} { set option $elem } else { break } } } switch $count { 0 { return -code error \ "bad $kind \"$opt\": must be [enumOpts $optList]" } 1 { return $option } default { return -code error \ "ambiguous $kind \"$opt\": must be [enumOpts $optList]" } } } #------------------------------------------------------------------------------ # mwutil::enumOpts # # Returns a string consisting of the elements of the given list, separated by # commas and spaces. #------------------------------------------------------------------------------ proc mwutil::enumOpts optList { set optCount [llength $optList] set n 1 foreach opt $optList { if {$n == 1} { set str $opt } elseif {$n < $optCount} { append str ", $opt" } else { if {$optCount > 2} { append str "," } append str " or $opt" } incr n } return $str } #------------------------------------------------------------------------------ # mwutil::setConfigVals # # Sets the elements of the array specified by configValsName to the values # returned by passing the widget name win and the relevant options to the # command given by cgetCmd. #------------------------------------------------------------------------------ proc mwutil::setConfigVals {win configSpecsName configValsName cgetCmd argList} { upvar $configSpecsName configSpecs upvar $configValsName configVals set optList {} if {[llength $argList] == 0} { foreach opt [array names configSpecs] { if {[llength $configSpecs($opt)] > 1} { lappend optList $opt } } } else { foreach {opt val} $argList { lappend optList [fullConfigOpt $opt configSpecs] } } foreach opt $optList { set configVals($opt) [eval $cgetCmd [list $win $opt]] } } #------------------------------------------------------------------------------ # mwutil::configSubCmd # # This procedure is invoked to process configuration subcommands. #------------------------------------------------------------------------------ proc mwutil::configSubCmd {win configSpecsName configValsName configCmd argList} { upvar $configSpecsName configSpecs upvar $configValsName configVals switch [llength $argList] { 0 { # # Return a list describing all available configuration options # foreach opt [lsort [array names configSpecs]] { if {[llength $configSpecs($opt)] == 1} { set alias $configSpecs($opt) if {$::tk_version < 8.1} { set dbName [lindex $configSpecs($alias) 0] lappend result [list $opt $dbName] } else { lappend result [list $opt $alias] } } else { set dbName [lindex $configSpecs($opt) 0] set dbClass [lindex $configSpecs($opt) 1] set default [lindex $configSpecs($opt) 3] lappend result [list $opt $dbName $dbClass $default \ $configVals($opt)] } } return $result } 1 { # # Return the description of the specified configuration option # set opt [fullConfigOpt [lindex $argList 0] configSpecs] set dbName [lindex $configSpecs($opt) 0] set dbClass [lindex $configSpecs($opt) 1] set default [lindex $configSpecs($opt) 3] return [list $opt $dbName $dbClass $default $configVals($opt)] } default { # # Set the specified configuration options to the given values # return [configure $win configSpecs configVals $configCmd $argList 0] } } } #------------------------------------------------------------------------------ # mwutil::attribSubCmd # # This procedure is invoked to process the attrib subcommand. #------------------------------------------------------------------------------ proc mwutil::attribSubCmd {win argList} { set classNs [string tolower [winfo class $win]] upvar ::${classNs}::ns${win}::attribVals attribVals set argCount [llength $argList] switch $argCount { 0 { # # Return the current list of attribute names and values # set result {} foreach attr [lsort [array names attribVals]] { lappend result [list $attr $attribVals($attr)] } return $result } 1 { # # Return the value of the specified attribute # set attr [lindex $argList 0] if {[info exists attribVals($attr)]} { return $attribVals($attr) } else { return "" } } default { # # Set the specified attributes to the given values # if {$argCount % 2 != 0} { return -code error "value for \"[lindex $argList end]\" missing" } array set attribVals $argList return "" } } }