[MC], 14 May 2003: Along the lines of [Visual menus], [Menus made easy], [Menus Even Easier] and [Menus Even Easier Redux], ''pdmenus'' is a small package to describe pulldown menus in XML (using [tDOM]). I've attempted to keep this page ''reapable'' with [wish-reaper]. The package is also available via CVS[http://sourceforge.net/cvs/?group_id=63662]. First, here is what the example menu from [Menus made easy] looks like: set xml { openFile saveAs known saveAs exit doCut puts foo puts bar puts grill doPaste } Here is the DTD describing our markup language for pull down menus: set DTD { ]> } And the actual code that makes everything happen: #------------------------------------------------------------------------------- # # PULL DOWN MENUS via XML # # PURPOSE: # # Implement pull down menus, based on an XML definition. # # METHODS: # # $menu VerifyStates # Private method called when a menu is posted to run through the menu's # tests to see which items should be in a clickable state. # # $menu CalcUnderline # Private method that returns the position of an asterisk in a label # and then the label without the asterisk. # # $menu loadFromXML xml # Create a menu based on an XML description. # #------------------------------------------------------------------------------- namespace eval ::pdmenu { package require Tcl 8.3 package require Tk 8.3 package require tdom 0.7.5 } proc ::pdmenu::CalcUnderline label { return [list [string first * $label] [string map {* {}} $label]] } proc ::pdmenu::VerifyStates handle { variable $handle upvar 0 $handle menu set counter 0 foreach test $menu(tests) { if {[string length $test]} { if {[expr $test] == 1} { $menu(hull) entryconfigure $counter -state normal } else { $menu(hull) entryconfigure $counter -state disabled } } incr counter } } proc ::pdmenu::loadFromXML xml { set doc [dom parse $xml] set root [$doc documentElement] set handle [$root getAttribute name] variable $handle upvar 0 $handle menu upvar 0 menu(tests) tests set menu(hull) $handle if {[llength [info commands $handle]] == 0} { menu $menu(hull) -postcommand [list ::pdmenu::VerifyStates $handle] \ -tearoff [$root getAttribute tearoff 0] } set tests [list] set varray [$root getAttribute varray ""] if {[$root hasAttribute parent]} { [$root getAttribute parent] configure -menu $menu(hull) } foreach item [$root selectNodes *] { set type [$item nodeName] set node [$item selectNodes code/text()] if {$node != ""} { set code [$node data] foreach node [$item selectNodes binding/text()] { bind . <[$node data]> $code } } else { set code {} } set node [$item selectNodes test/text()] if {$node != ""} { lappend tests [$node data] } else { lappend tests {} } set accel [$item getAttribute accelerator ""] set label [$item getAttribute label ""] foreach {pos label} [CalcUnderline $label] break switch -exact -- $type { menu { set name [$item getAttribute name] $menu(hull) add cascade -label $label \ -menu $name \ -underline $pos ::pdmenu::loadFromXML [$item asXML] } command { $menu(hull) add command -label $label \ -command $code \ -accel $accel \ -underline $pos } radio { set var [string trim ${varray}([$item getAttribute var]) ()] set value [$item getAttribute value] if {[$item hasAttribute default] && [string is true -strict [$item getAttribute default]]} { set ::$var $value } $menu(hull) add radiobutton -label $label \ -command $code \ -value $value \ -accelerator $accel \ -variable $var } separator { $menu(hull) add separator } default { error "Unknown menu entry type \"$type\"" } } } $doc delete } #------------------------------------------------------------------------------- # Initilization package provide pdmenus 1.0 And now, to make our example menu: pdmenu::loadFromXML $xml TODO: add checkbuttons.