############################################################################### # # BtkFile.tk - File Selector Widget for Tk # # Use the procedure BtkFileSelector to open a file selector widget. The # procedure accepts the following switches: # # -in creates file selector as a child of the window # -parent creates file selector as a transient window # -filter <*.setup> Sets the filter to the given string # -filetypes Ignored (doesn't cause an error) # -title Message displayed at the top of the file selector # # Copyright (c) 1994 # Bradford W. Mott # September 1,1994 ############################################################################### # $Id: BtkFile.tk,v 1.1 1996/08/02 14:55:46 bwmott Exp $ ############################################################################### ############################################################################### # Called to initialize the BtkFileSelector (when sourced) ############################################################################### proc BtkFileSelector:Initialize {} { global BtkFileSelector ## Initialize all of the globals set BtkFileSelector(path) [pwd] set BtkFileSelector(filter) "*" set BtkFileSelector(allFiles) "yes" set BtkFileSelector(filespec) "" ## Add the file selector's resources to the option database option add "*btkFileSelector*Font" \ "-*-helvetica-medium-r-normal--*-120-*-*-*-*-iso8859-*" 40 option add "*btkFileSelector*label*Font" \ "-*-helvetica-bold-r-normal--*-120-*-*-*-*-iso8859-*" 40 option add "*btkFileSelector*Foreground" "black" 40 option add "*btkFileSelector*Background" "gray80" 40 option add "*btkFileSelector*highlightBackground" "gray80" 40 option add "*btkFileSelector*activeForeground" "black" 40 option add "*btkFileSelector*activeBackground" "gray65" 40 option add "*btkFileSelector*scrollbar*foreground" "gray80" 40 option add "*btkFileSelector*scrollbar*activeForeground" "gray65" 40 option add "*btkFileSelector*selectForeground" "black" 40 option add "*btkFileSelector*selectBackground" "gray65" 40 } ############################################################################### # Update the contents of the file selector's listbox with the files # in the current directory ############################################################################### proc BtkFileSelector:Update {window} { global BtkFileSelector ## Change mouse pointer to the watch $window configure -cursor watch ## Tell tk to update the screen update idletasks ## Get the filter and path set filter $BtkFileSelector(filter) set path $BtkFileSelector(path) ## Update the filter entry field $window.filter.entry delete 0 end $window.filter.entry insert end $filter $window.filter.entry xview moveto 1 ## Update the path entry field $window.path.entry delete 0 end $window.path.entry insert end $path $window.path.entry xview moveto 1 ## Get a list of all the files in the current directory if {$BtkFileSelector(allFiles) == "yes"} { set files [glob -nocomplain $path/.* $path/*] set files [lrange $files 2 end] } else { set files [glob -nocomplain $path/*] } ## Start with an empty file list set filelist "" ## Build the rest of the file list foreach i $files { ## Remove the path from the file set file [file tail $i] ## Make sure file really exists (This is for wierd file systems like AFS) if {[file exists "$i"] == 1} { if {[file isdirectory "$i"] == 1} { lappend filelist "$file/" } else { if {[string match $filter $file] == 1} { lappend filelist "$file" } } } } ## Sort the file list set filelist [lsort $filelist] ## Add the parent directory to the top of the list if we're not at the root if {$BtkFileSelector(path) != ""} { set filelist [linsert $filelist 0 "../"] } ## Clear the listbox $window.selection.listbox delete 0 end ## Put the file list in the listbox foreach i $filelist { $window.selection.listbox insert end "$i" } ## Change the mouse pointer back to the default $window configure -cursor {} ## Tell tk to update the screen update idletasks } ############################################################################### ## Process a click event on a file ############################################################################### proc BtkFileSelector:FileClicked {window y} { global BtkFileSelector ## Get the listbox item nearest the y coordinate set nearest [$window nearest $y] ## See if a file was really selected if {$nearest >= 0} { ## Highlight the selected file in the listbox $window selection clear 0 end $window selection set $nearest $nearest ## Ask listbox for the actual filename set file [$window get $nearest] ## See if the file is a regular file if {[regexp {^.*/$} $file] == 0} { ## Update the filename entry field $BtkFileSelector(baseWindow).filename.entry delete 0 end $BtkFileSelector(baseWindow).filename.entry insert end $file } } } ############################################################################### ## Process a double click event on a file ############################################################################### proc BtkFileSelector:FileDoubleClicked {window y} { global BtkFileSelector ## Get the path set path $BtkFileSelector(path) ## Get the listbox item nearest the y coordinate set nearest [$window nearest $y] ## See if a file was really selected if {$nearest >= 0} { ## Get the filename from the listbox set file [$window get $nearest] ## Create the complete filespec for the file set filespec $path/$file ## If the file is a directory then try to change directories if {[regexp {^.*/$} $file] == 1} { ## See if we should move to the parent directory if {$file == "../"} { set filespec [file dirname [string trimright $path '/']]/ } ## Make sure we can 'cd' into the directory if {([file executable $path/$file]==1) \ && ([file readable $path/$file]==1)} { ## Update the path set BtkFileSelector(path) [string trimright $filespec '/'] BtkFileSelector:Update $BtkFileSelector(baseWindow) } else { ## Sound the bell bell } } else { ## Regular file so handle the selection BtkFileSelector:FileSelected } } } ############################################################################### ## Handle when a regular file is selected (i.e. either Ok or a Double-Click) ############################################################################### proc BtkFileSelector:FileSelected {} { global BtkFileSelector ## Get the selected file from the listbox set file [$BtkFileSelector(baseWindow).filename.entry get] if {$file == ""} { return } ## Set the filespec and destroy the file selector widget set BtkFileSelector(filespec) $BtkFileSelector(path)/$file destroy $BtkFileSelector(baseWindow) } ############################################################################### ## Handle the Cancel button ############################################################################### proc BtkFileSelector:Cancel {} { global BtkFileSelector ## Return an empty string since cancel was pressed set BtkFileSelector(filespec) "" destroy $BtkFileSelector(baseWindow) } ############################################################################### ## Try to change the path to whatever is in the path entry widget ############################################################################### proc BtkFileSelector:SetPath {} { global BtkFileSelector ## Get the path from the entry widget set path [string trimright [$BtkFileSelector(baseWindow).path.entry get] '/'] ## Make sure it's a valid path if {([file executable $path/]==1) && ([file readable $path/]==1)} { set BtkFileSelector(path) $path BtkFileSelector:Update $BtkFileSelector(baseWindow) } else { ## Update the path entry field $BtkFileSelector(baseWindow).path.entry delete 0 end $BtkFileSelector(baseWindow).path.entry insert end $BtkFileSelector(path) ## Right align the view $BtkFileSelector(baseWindow).path.entry xview moveto 1 } } ############################################################################### ## Try to change the filter to whatever is in the filter entry widget ############################################################################### proc BtkFileSelector:SetFilter {} { global BtkFileSelector ## Get the path from the entry widget set filter [$BtkFileSelector(baseWindow).filter.entry get] ## Make sure there is a filter if {$filter == ""} { set filter "*" } set BtkFileSelector(filter) $filter BtkFileSelector:Update $BtkFileSelector(baseWindow) } ############################################################################### # Popup a file selector and wait for the user to select something ############################################################################### proc BtkFileSelector args { global BtkFileSelector ## Set defaults set BtkFileSelector(filespec) "" set BtkFileSelector(message) "Files:" set BtkFileSelector(filter) "*" set BtkFileSelector(parentWindow) "root" set BtkFileSelector(masterWindow) "" ## Parse the argument list for {set t 0} {$t < [llength $args]} {incr t} { set arg [lindex $args $t] if {$arg == "-title"} { incr t set BtkFileSelector(message) [lindex $args $t] } elseif {$arg == "-in"} { incr t if {[lindex $args $t] == "."} { set BtkFileSelector(parentWindow) "" } else { set BtkFileSelector(parentWindow) [lindex $args $t] } } elseif {$arg == "-parent"} { incr t set BtkFileSelector(masterWindow) [lindex $args $t] } elseif {$arg == "-filter"} { incr t set BtkFileSelector(filter) [lindex $args $t] } elseif {$arg == "-filetypes"} { incr t } else { bgerror "Incorrect Arguments to BtkFileSelector!" return } } ## Create a toplevel window or a frame to hold the selector in if {$BtkFileSelector(parentWindow) == "root"} { set BtkFileSelector(baseWindow) ".btkFileSelector" set BtkFileSelector(parentWindow) ".btkFileSelector" toplevel $BtkFileSelector(baseWindow) -borderwidth 4 wm title $BtkFileSelector(baseWindow) "File Selector" ## Should it be a transient window? if {$BtkFileSelector(masterWindow) != ""} { wm transient $BtkFileSelector(baseWindow) $BtkFileSelector(masterWindow) scan [winfo geometry $BtkFileSelector(masterWindow)] \ "%dx%d+%d+%d" mw mh mx my set x [expr (($mw / 2) + $mx) - (375 / 2)] set y [expr (($mh / 2) + $my) - (342 / 2)] if {$x < 0} {set x 0} if {$y < 0} {set y 0} wm geometry $BtkFileSelector(baseWindow) 375x342+$x+$y } else { wm geometry $BtkFileSelector(baseWindow) 375x342 } } else { ## Get my parent's width and height if {$BtkFileSelector(parentWindow) == ""} { scan [winfo geometry .] "%dx%d" parentWidth parentHeight } else { scan [winfo geometry $BtkFileSelector(parentWindow)] \ "%dx%d" parentWidth parentHeight } ## Calculate size of my frame based on my parent's size if {$parentWidth > 375} { set myWidth 375 } else { set myWidth [expr $parentWidth - 20] } if {$parentHeight > 350} { set myHeight 350 } else { set myHeight [expr $parentHeight - 20] } ## Create my frame set BtkFileSelector(baseWindow) \ "$BtkFileSelector(parentWindow).btkFileSelector" frame $BtkFileSelector(baseWindow) -borderwidth 5 -relief ridge \ -height $myHeight -width $myWidth place $BtkFileSelector(baseWindow) -relx 0.5 -rely 0.5 -anchor center ## Turn off geometry propagation for the packer pack propagate $BtkFileSelector(baseWindow) 0 } frame $BtkFileSelector(baseWindow).path label $BtkFileSelector(baseWindow).path.label -text "Path:" -anchor w entry $BtkFileSelector(baseWindow).path.entry -relief sunken bind $BtkFileSelector(baseWindow).path.entry \ "BtkFileSelector:SetPath" pack $BtkFileSelector(baseWindow).path.label -side left pack $BtkFileSelector(baseWindow).path.entry -side left -fill x -expand 1 frame $BtkFileSelector(baseWindow).filename label $BtkFileSelector(baseWindow).filename.label \ -text "File name:" -anchor w entry $BtkFileSelector(baseWindow).filename.entry -relief sunken bind $BtkFileSelector(baseWindow).filename.entry \ "BtkFileSelector:FileSelected" pack $BtkFileSelector(baseWindow).filename.label -side left pack $BtkFileSelector(baseWindow).filename.entry -side left \ -fill x -expand 1 frame $BtkFileSelector(baseWindow).filter label $BtkFileSelector(baseWindow).filter.label -text "Filter:" -anchor w entry $BtkFileSelector(baseWindow).filter.entry -relief sunken -width 10 bind $BtkFileSelector(baseWindow).filter.entry \ "BtkFileSelector:SetFilter" checkbutton $BtkFileSelector(baseWindow).filter.all \ -text "Show all files" -offvalue "no" -onvalue "yes" \ -variable BtkFileSelector(allFiles) \ -command {BtkFileSelector:Update $BtkFileSelector(baseWindow)} pack $BtkFileSelector(baseWindow).filter.label -side left pack $BtkFileSelector(baseWindow).filter.entry -side left -fill x \ -expand 1 -padx 4 pack $BtkFileSelector(baseWindow).filter.all -side left frame $BtkFileSelector(baseWindow).selection label $BtkFileSelector(baseWindow).selection.label \ -text $BtkFileSelector(message) scrollbar $BtkFileSelector(baseWindow).selection.scrollbar \ -relief sunken -orient vertical \ -command "$BtkFileSelector(baseWindow).selection.listbox yview" \ -takefocus 0 listbox $BtkFileSelector(baseWindow).selection.listbox \ -relief sunken -width 20 -height 4 \ -yscroll "$BtkFileSelector(baseWindow).selection.scrollbar set" \ -takefocus 0 bind $BtkFileSelector(baseWindow).selection.listbox \ \ "BtkFileSelector:FileDoubleClicked %W %y; break" bind $BtkFileSelector(baseWindow).selection.listbox \ "BtkFileSelector:FileClicked %W %y; break" bind $BtkFileSelector(baseWindow).selection.listbox \ "BtkFileSelector:FileClicked %W %y; break" pack $BtkFileSelector(baseWindow).selection.label \ -side top -anchor w -fill x pack $BtkFileSelector(baseWindow).selection.scrollbar \ -side left -fill y pack $BtkFileSelector(baseWindow).selection.listbox \ -side left -fill both -expand 1 frame $BtkFileSelector(baseWindow).button button $BtkFileSelector(baseWindow).button.okay \ -text "Ok" -command "BtkFileSelector:FileSelected" button $BtkFileSelector(baseWindow).button.filter \ -text "Filter" -command "BtkFileSelector:SetFilter" button $BtkFileSelector(baseWindow).button.cancel \ -text "Cancel" -command "BtkFileSelector:Cancel" pack $BtkFileSelector(baseWindow).button.okay \ -side left -fill x -expand 1 pack $BtkFileSelector(baseWindow).button.filter \ -side left -fill x -expand 1 -padx 4 pack $BtkFileSelector(baseWindow).button.cancel \ -side left -fill x -expand 1 pack $BtkFileSelector(baseWindow).selection \ -side top -padx 4 -pady 2 -fill both -expand 1 pack $BtkFileSelector(baseWindow).path \ -side top -padx 4 -fill x pack $BtkFileSelector(baseWindow).filename \ -side top -padx 4 -fill x pack $BtkFileSelector(baseWindow).filter \ -side top -padx 4 -fill x pack $BtkFileSelector(baseWindow).button \ -side top -padx 4 -pady 7 -fill x ## Update the file selector's file list BtkFileSelector:Update $BtkFileSelector(baseWindow) ## Make this a modal dialog tkwait visibility $BtkFileSelector(baseWindow) while {[catch {grab set $BtkFileSelector(baseWindow)}] == 1} {} tkwait window $BtkFileSelector(baseWindow) ## Tell tk to update the screen (i.e. remove the file selector window) update idletasks return $BtkFileSelector(filespec) } ## Go ahead an initialize file selector upon loading BtkFileSelector:Initialize