set key [string index [$w.$name cget -text] $underIdx] bind $w <Alt-[string tolower $key]> [list $w.$name invoke] bind $w <Alt-[string toupper $key]> [list $w.$name invoke]Oops. When button label is translated (with help of msgcat) and $key is non-ascii character, both string tolower and string toupper are not valid keysyms anymore.When I faced this problem, I tried first to make a function that will return appropriate keysym for unicode character. Alas, there's no (straightforward) way to turn something like \\u430 into Cyrillic_a. Worse than that, I have found that if I press Alt-Cyrillic_a, it behaves differently on X11 and Windows (and incorrectly in both cases):
- On X11, application gets <Alt-f> (cyrillic a is on <f> key on most russian keyboards)
- On Windows, application gets <Alt-agrave>...
bind $w <Alt-c> [list tk::ButtonInvoke $data(cancelBtn)] bind $w <Alt-d> [list focus $data(dirMenuBtn)]When labels "Directory" and "Cancel" are translated, the result is very bad. It's not so bad if the user just cannot use shortcut keys corresponding to letters he sees underlined in labels; but it's much worse if he will press a key that leads to wrong action.To address both of this problems, I wrote some simple procedures (which you can see in the bottom of this page) to manage sets of accelerator keys.Now, if I need to bind some shortcut keys to some widget in dialog window, I can use something like following:
proc about_dialog {} { destroy .about set w [toplevel .about] set ok [button $w.ok -text [mc {Ok}] ] set can [button $w.cancel -text [mc {Cancel}]] tk::accel_set_create $w tk::accel_to_widget $w $ok tk::accel_to_widget $w $can bind $w <Alt-Key> [list tk::accel_dispatch $w %A] bind $w <Destroy> [list tk::accel_set_end $w] .... }In this example, "accel_set_create .about" starts the definition of new set of accelerator keys (further I'll call them accel-sets, to be short). "accel_to_widget $ok" selects appropriate shortcut for button $ok, scanning it's real (translated) label.More examples follow:
set lab [label $w.lab_phone -text [mc "Phone:"]] set ent [entry $w.phone -textvariable phone] tk::accel_to_widget $w $lab [list focus $ent](Some character in $lab text is underlined, and we can use corresponding key to get to entry $ent)
set filemenu [menu .m.file] tk::accel_set_create $filemenu foreach lab cmd { Open cm_open Save cm_save Exit cm_exit } { set mclab [mc $lab] $filemenu add command -label $mclab -command $cmd \ -underline [tk::accel_set_add $filemenu $mclab junk] } tk::accel_set_endIn this last example, we use the notion of accel-sets only to get some letters unredlined in translated menu labels. This letters will be unique (in this menu, of course). Capital letters are selected, if it's possible.
This approach to binding shortcut keys is slightly more convenient than the traditional approach of other toolkits: when &Cancel is translated to &Annuler, and then translator has to find ambigious shortcut combinations to get rid of them (if he's kind enough, and not lazy).Now I'm working on the patch for tk library scripts, to get correct behavior of translated file dialogs and message boxes. That's why my procedures are in tk namespace :-).Vince - this looks very useful! I assume the patch you're working on would also be callable and useful from outside Tk's core (i.e. so any applications written in Tcl/Tk could make use of it...)?Anton Kovalenko - to make it "officially" callable from outside tk's core, we have just to export this procedures into root namespace.Now, this is the patch #563140 at Sourceforge (tktoolkit).A/AK - Now I think that "ampersand magic" is better. All underline handling in Tk standard dialogs is now done with "magic ampersands".
# accel.tcl -- # # This file provides unified way to bind appropriate shortcut keys # for buttons/menus with underlined labels. # NOTE: underlined character in a label may be non-ascii! # As of now, there is only support of russian cyrillic non-ascii # key bindings, # but it will be easy to add other languages # support as well (where it makes sense). # If application needs shortcut keys for some buttons, labels, # menu positions or whatever, it can # Returns [list of one] appropriate keysym for unicode character, or {} # if nothing is appropriate. # Starts new shortcut set definition proc tk::accel_set_create {setname} { variable accel_sets set accel_sets($setname) [list] } # Given shortcut set and label, # returns the underline_position # Character is selected as following: # (must not be already in this accel set) # - First, try uppercase ascii characters # - Next, try uppercase non-ascii characters # - Then try all other alphanumeric characters proc tk::accel_set_add {setname label command} { variable accel_sets if {[info exists accel_sets($setname,for:$label)]} { return $accel_sets($setname,for:$label) } else { if {$command == ""} { return -1 } } set ulinepos -1 set foundchar "-" set founduline -1 foreach char [split $label {}] { incr ulinepos if {-1!=[string first [string toupper $char] $accel_sets($setname)]} { continue } if {![string is alnum $char]} { continue } if {[string is ascii $char] && [string is upper $char ]} { set foundchar $char set founduline $ulinepos break } if {[string is upper $char]} { if {![string is upper $foundchar]} { set foundchar $char set founduline $ulinepos } } else { if {$founduline==-1} { set foundchar $char set founduline $ulinepos } } } set accel_sets($setname,for:$label) $founduline if {$founduline != -1} { set accel_sets($setname,[string toupper $foundchar]) $command append accel_sets($setname) [string toupper $foundchar] } return $founduline } # This procedure supposed to be an event handler for <Alt-Key>. # The second argument muts be %A. proc tk::accel_dispatch {setname char} { set char [string toupper $char] variable accel_sets if {[info exists accel_sets($setname,$char)]} { uplevel \#0 $accel_sets($setname,$char) } } # Convenient procedure for adding shortcut key to widgets. # Automatically selects underline position and adds appropriate # handler to accel-set. proc tk::accel_to_widget {setname widget {command {}}} { if {$command == ""} { set command [list $widget invoke] } $widget configure -underline \ [ ::tk::accel_set_add $setname [$widget cget -text] $command] } # When accel-set is no longer needed, we can free it. proc tk::accel_set_end {setname} { variable accel_sets array unset accel_sets $setname,* array unset accel_sets $setname }