Updated 2005-03-02 12:40:12

MSW: The goal is to offer an entry widget which does auto-path-completion, offers completion on TAB (with cycling through the possibilities), and a popup menu to allow the user to specify the next directory(/ies).

pathentry works as follows: It takes all the arguments an entry takes, (because it's basically an entry with some bindings and extra commands), and offers two own commands, complete and popup.

  • path complete tries to complete the current text in the entry if there is an unambigous completion, and if there is none, the text is not changed, but a list of all possible completions is returned.
  • path popup takes two arguments (X & Y coordinates) and pops up a menu to allow the user to choose from the possible completions. If there is only one unambigous completion, the completion is filled in, and no menu pops up.
  • path partdel (partial delete) deletes either the last path component if the current value exists, cuts back to the next possible start of possible globs if it doesn't and isn't a start of globs, or simply deletes a char if none of that is true.

This implementation is not yet done! I just thought I'd share my thoughts with you, because it happens to be quite usable already :)

Things lacking:

  • The options -popup and -completion only accept 0 and 1 for now, should also accept yes, no, false, true
  • The options -popup and -completion are ignore atm :)
  • an option -menusize, telling the popup menu to launch submenus for more than x entries (on unix, enter /usr/bin into the entry and click on it with the right mousebutton to see what I mean)
  • configure and cget must be caught, too, to offer runtime altering of the pathentries options
  • TAB bindings:
         TAB: when there is 1 completion, complete. (done now through keyrelease)
              when there is > 1 completions, cycle through them

  • a -completiontype option, which accepts all the option glob's -types accepts, and which gets passed through to the calls to glob

Martin Lemburg - 27.09.2002:

There are a some other things lacking:

  • if on windows the user enters the letter of a drive/volume it is expanded to a filename in the current directory, if this file exists. The user can't key in "C:", because it is immedietly replaced by (e.g.) CONFIG.SYS
  • the path shown in the pathentry should be platform specific and not tcl specific. The use of "file nativename" would be the right way to allow the user not only to key in platform specific, but to see the completed path platform specific.


  • I would suggest to bind (e.g.) Control-Space or something similar to the completion not to complete automatically after every KeyRelease!
  • what about extending every popup menu entry representing a directory to be a cascading menu, with itself as the first entry, a seperator below itself and all files/subdirectories of the directory below the seperator

MSW: I don't have windows or mac to test :) For the auto-complete, -completion <bool> is supposed to handle that. If it's false, only complete on <TAB>.

For the cascading menus, thought about that, but initially I wanted to offer the whole directory tree - which would be too compute intensive imho. For what you suggested, sounds reasonable.

I'll read some more into platform independant file name handling...

How to play
 pathentry .e
 pack .e
 Focus it, enter the following three characters: '/', 'u', 'b'.
 In the entry now there should be '/usr/bin/' (on a typical unix)
 Toy around with backspace and the right mousebutton :)

Current implementation (no need to add the points I talked about above, will do that soon :) goes here:
 proc pathentry {path args} {
    set arg {}
    if {[string index $path 0] != {.}} {
        return -code error "$path is no valid widget path!"
    foreach {sw ar} $args {
        switch -- $sw {
            -completion {
                if {![string is boolean -strict $ar]} {
                    return -code error "-completion takes a boolean argument, $ar is none."
            -popup {
                if {![string is boolean -strict $ar]} {
                    return -code error "-popup takes a boolen argument, $ar is none."
            default { lappend arg $sw $ar }
    if {[lsearch $arg {-te*}]==-1} {
        # we NEED a textvar!
        lappend arg {-textvariable} "${path}_var"
    if {[catch { eval [concat entry $path $arg] } err]} {
        return -code error $err
    rename $path ${path}_entry
    proc $path {cmd args} {
        upvar #0 [set vname [[set pat [lindex [info level [info level]] 0]]_entry cget -textvariable]] acc
        switch -- $cmd {
            complete    {
                if {[llength [set glo [glob -nocomplain "$acc*"]]]==1} {
                    if {[file isdirectory [set acc $glo]]} { append acc / }
                    $pat icursor end
                    $pat xview end
                } else {
                    return $glo
            } partdel   {
                for {set ac $acc} {![file exists $ac] && ![llength [glob -nocomplain $ac*]]} {set ac [string range $ac 0 end-1]} {}
                if {$ac != $acc} {
                    set acc $ac
                } elseif {[file exists $acc]} {
                    set acc [file dirname $acc]
                    if {$acc != {/}} {append acc /}
                } else {
                    set acc [string range $acc 0 end-1]
                $pat icursor end
                $pat xview end
            } popup     {
                foreach {xcoord ycoord} $args {break}
                if {[winfo exists $pat.popmen]} {destroy $pat.popmen}
                menu $pat.popmen -tearoff 0 -title {Path Completion} ; set ctr 0
                set possible [lsort [$pat complete]]; if {[llength $possible]==1} {return}
                foreach poss $possible {
                    $pat.popmen add command -label [file tail $poss] \
                         -command "if {\[file isdirectory \[set ::$vname $poss\]\]} { append $vname / } ; $pat icursor end; $pat xview end"
                    incr ctr
                if {$ctr} { tk_popup $pat.popmen $xcoord $ycoord } else { destroy $pat.popmen }
            } default   {
                eval [concat ${pat}_entry $cmd $args]
    bind $path <KeyRelease> "$path complete"
    bind $path <3> "$path popup %X %Y"
    bind $path <KeyRelease-BackSpace> "$path partdel; break"
    bind $path <KeyPress-BackSpace> { break }

Comments ? Welcome ...

Category Widget