Upgrade to Tcl/Tk 8.5b2
[msysgit.git] / mingw / lib / tk8.5 / tkfbox.tcl
blobfe6eccd968e70fce1140083bdb7442f009950d90
1 # tkfbox.tcl --
3 # Implements the "TK" standard file selection dialog box. This
4 # dialog box is used on the Unix platforms whenever the tk_strictMotif
5 # flag is not set.
7 # The "TK" standard file selection dialog box is similar to the
8 # file selection dialog box on Win95(TM). The user can navigate
9 # the directories by clicking on the folder icons or by
10 # selecting the "Directory" option menu. The user can select
11 # files by clicking on the file icons or by entering a filename
12 # in the "Filename:" entry.
14 # RCS: @(#) $Id: tkfbox.tcl,v 1.60 2007/10/25 21:44:22 hobbs Exp $
16 # Copyright (c) 1994-1998 Sun Microsystems, Inc.
18 # See the file "license.terms" for information on usage and redistribution
19 # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
22 #----------------------------------------------------------------------
24 # I C O N L I S T
26 # This is a pseudo-widget that implements the icon list inside the
27 # ::tk::dialog::file:: dialog box.
29 #----------------------------------------------------------------------
31 # ::tk::IconList --
33 # Creates an IconList widget.
35 proc ::tk::IconList {w args} {
36 IconList_Config $w $args
37 IconList_Create $w
40 proc ::tk::IconList_Index {w i} {
41 upvar #0 ::tk::$w data ::tk::$w:itemList itemList
42 if {![info exists data(list)]} {
43 set data(list) {}
45 switch -regexp -- $i {
46 "^-?[0-9]+$" {
47 if {$i < 0} {
48 set i 0
50 if {$i >= [llength $data(list)]} {
51 set i [expr {[llength $data(list)] - 1}]
53 return $i
55 "^active$" {
56 return $data(index,active)
58 "^anchor$" {
59 return $data(index,anchor)
61 "^end$" {
62 return [llength $data(list)]
64 "@-?[0-9]+,-?[0-9]+" {
65 foreach {x y} [scan $i "@%d,%d"] {
66 break
68 set item [$data(canvas) find closest \
69 [$data(canvas) canvasx $x] [$data(canvas) canvasy $y]]
70 return [lindex [$data(canvas) itemcget $item -tags] 1]
75 proc ::tk::IconList_Selection {w op args} {
76 upvar ::tk::$w data
77 switch -exact -- $op {
78 "anchor" {
79 if {[llength $args] == 1} {
80 set data(index,anchor) [tk::IconList_Index $w [lindex $args 0]]
81 } else {
82 return $data(index,anchor)
85 "clear" {
86 if {[llength $args] == 2} {
87 foreach {first last} $args {
88 break
90 } elseif {[llength $args] == 1} {
91 set first [set last [lindex $args 0]]
92 } else {
93 error "wrong # args: should be [lindex [info level 0] 0] path\
94 clear first ?last?"
96 set first [IconList_Index $w $first]
97 set last [IconList_Index $w $last]
98 if {$first > $last} {
99 set tmp $first
100 set first $last
101 set last $tmp
103 set ind 0
104 foreach item $data(selection) {
105 if { $item >= $first } {
106 set first $ind
107 break
109 incr ind
111 set ind [expr {[llength $data(selection)] - 1}]
112 for {} {$ind >= 0} {incr ind -1} {
113 set item [lindex $data(selection) $ind]
114 if { $item <= $last } {
115 set last $ind
116 break
120 if { $first > $last } {
121 return
123 set data(selection) [lreplace $data(selection) $first $last]
124 event generate $w <<ListboxSelect>>
125 IconList_DrawSelection $w
127 "includes" {
128 set index [lsearch -exact $data(selection) [lindex $args 0]]
129 return [expr {$index != -1}]
131 "set" {
132 if { [llength $args] == 2 } {
133 foreach {first last} $args {
134 break
136 } elseif { [llength $args] == 1 } {
137 set last [set first [lindex $args 0]]
138 } else {
139 error "wrong # args: should be [lindex [info level 0] 0] path\
140 set first ?last?"
143 set first [IconList_Index $w $first]
144 set last [IconList_Index $w $last]
145 if { $first > $last } {
146 set tmp $first
147 set first $last
148 set last $tmp
150 for {set i $first} {$i <= $last} {incr i} {
151 lappend data(selection) $i
153 set data(selection) [lsort -integer -unique $data(selection)]
154 event generate $w <<ListboxSelect>>
155 IconList_DrawSelection $w
160 proc ::tk::IconList_CurSelection {w} {
161 upvar ::tk::$w data
162 return $data(selection)
165 proc ::tk::IconList_DrawSelection {w} {
166 upvar ::tk::$w data
167 upvar ::tk::$w:itemList itemList
169 $data(canvas) delete selection
170 foreach item $data(selection) {
171 set rTag [lindex [lindex $data(list) $item] 2]
172 foreach {iTag tTag text serial} $itemList($rTag) {
173 break
176 set bbox [$data(canvas) bbox $tTag]
177 $data(canvas) create rect $bbox -fill \#a0a0ff -outline \#a0a0ff \
178 -tags selection
180 $data(canvas) lower selection
181 return
184 proc ::tk::IconList_Get {w item} {
185 upvar ::tk::$w data
186 upvar ::tk::$w:itemList itemList
187 set rTag [lindex [lindex $data(list) $item] 2]
188 foreach {iTag tTag text serial} $itemList($rTag) {
189 break
191 return $text
194 # ::tk::IconList_Config --
196 # Configure the widget variables of IconList, according to the command
197 # line arguments.
199 proc ::tk::IconList_Config {w argList} {
201 # 1: the configuration specs
203 set specs {
204 {-command "" "" ""}
205 {-multiple "" "" "0"}
208 # 2: parse the arguments
210 tclParseConfigSpec ::tk::$w $specs "" $argList
213 # ::tk::IconList_Create --
215 # Creates an IconList widget by assembling a canvas widget and a
216 # scrollbar widget. Sets all the bindings necessary for the IconList's
217 # operations.
219 proc ::tk::IconList_Create {w} {
220 upvar ::tk::$w data
222 frame $w
223 set data(sbar) [scrollbar $w.sbar -orient horizontal -takefocus 0]
224 catch {$data(sbar) configure -highlightthickness 0}
225 set data(canvas) [canvas $w.canvas -bd 2 -relief sunken \
226 -width 400 -height 120 -takefocus 1]
227 pack $data(sbar) -side bottom -fill x -padx 2
228 pack $data(canvas) -expand yes -fill both
230 $data(sbar) configure -command [list $data(canvas) xview]
231 $data(canvas) configure -xscrollcommand [list $data(sbar) set]
233 # Initializes the max icon/text width and height and other variables
235 set data(maxIW) 1
236 set data(maxIH) 1
237 set data(maxTW) 1
238 set data(maxTH) 1
239 set data(numItems) 0
240 set data(noScroll) 1
241 set data(selection) {}
242 set data(index,anchor) ""
243 set fg [option get $data(canvas) foreground Foreground]
244 if {$fg eq ""} {
245 set data(fill) black
246 } else {
247 set data(fill) $fg
250 # Creates the event bindings.
252 bind $data(canvas) <Configure> [list tk::IconList_Arrange $w]
254 bind $data(canvas) <1> [list tk::IconList_Btn1 $w %x %y]
255 bind $data(canvas) <B1-Motion> [list tk::IconList_Motion1 $w %x %y]
256 bind $data(canvas) <B1-Leave> [list tk::IconList_Leave1 $w %x %y]
257 bind $data(canvas) <Control-1> [list tk::IconList_CtrlBtn1 $w %x %y]
258 bind $data(canvas) <Shift-1> [list tk::IconList_ShiftBtn1 $w %x %y]
259 bind $data(canvas) <B1-Enter> [list tk::CancelRepeat]
260 bind $data(canvas) <ButtonRelease-1> [list tk::CancelRepeat]
261 bind $data(canvas) <Double-ButtonRelease-1> \
262 [list tk::IconList_Double1 $w %x %y]
264 bind $data(canvas) <Control-B1-Motion> {;}
265 bind $data(canvas) <Shift-B1-Motion> \
266 [list tk::IconList_ShiftMotion1 $w %x %y]
268 bind $data(canvas) <Up> [list tk::IconList_UpDown $w -1]
269 bind $data(canvas) <Down> [list tk::IconList_UpDown $w 1]
270 bind $data(canvas) <Left> [list tk::IconList_LeftRight $w -1]
271 bind $data(canvas) <Right> [list tk::IconList_LeftRight $w 1]
272 bind $data(canvas) <Return> [list tk::IconList_ReturnKey $w]
273 bind $data(canvas) <KeyPress> [list tk::IconList_KeyPress $w %A]
274 bind $data(canvas) <Control-KeyPress> ";"
275 bind $data(canvas) <Alt-KeyPress> ";"
277 bind $data(canvas) <FocusIn> [list tk::IconList_FocusIn $w]
278 bind $data(canvas) <FocusOut> [list tk::IconList_FocusOut $w]
280 return $w
283 # ::tk::IconList_AutoScan --
285 # This procedure is invoked when the mouse leaves an entry window
286 # with button 1 down. It scrolls the window up, down, left, or
287 # right, depending on where the mouse left the window, and reschedules
288 # itself as an "after" command so that the window continues to scroll until
289 # the mouse moves back into the window or the mouse button is released.
291 # Arguments:
292 # w - The IconList window.
294 proc ::tk::IconList_AutoScan {w} {
295 upvar ::tk::$w data
296 variable ::tk::Priv
298 if {![winfo exists $w]} return
299 set x $Priv(x)
300 set y $Priv(y)
302 if {$data(noScroll)} {
303 return
305 if {$x >= [winfo width $data(canvas)]} {
306 $data(canvas) xview scroll 1 units
307 } elseif {$x < 0} {
308 $data(canvas) xview scroll -1 units
309 } elseif {$y >= [winfo height $data(canvas)]} {
310 # do nothing
311 } elseif {$y < 0} {
312 # do nothing
313 } else {
314 return
317 IconList_Motion1 $w $x $y
318 set Priv(afterId) [after 50 [list tk::IconList_AutoScan $w]]
321 # Deletes all the items inside the canvas subwidget and reset the IconList's
322 # state.
324 proc ::tk::IconList_DeleteAll {w} {
325 upvar ::tk::$w data
326 upvar ::tk::$w:itemList itemList
328 $data(canvas) delete all
329 unset -nocomplain data(selected) data(rect) data(list) itemList
330 set data(maxIW) 1
331 set data(maxIH) 1
332 set data(maxTW) 1
333 set data(maxTH) 1
334 set data(numItems) 0
335 set data(noScroll) 1
336 set data(selection) {}
337 set data(index,anchor) ""
338 $data(sbar) set 0.0 1.0
339 $data(canvas) xview moveto 0
342 # Adds an icon into the IconList with the designated image and text
344 proc ::tk::IconList_Add {w image items} {
345 upvar ::tk::$w data
346 upvar ::tk::$w:itemList itemList
347 upvar ::tk::$w:textList textList
349 foreach text $items {
350 set iTag [$data(canvas) create image 0 0 -image $image -anchor nw \
351 -tags [list icon $data(numItems) item$data(numItems)]]
352 set tTag [$data(canvas) create text 0 0 -text $text -anchor nw \
353 -font $data(font) -fill $data(fill) \
354 -tags [list text $data(numItems) item$data(numItems)]]
355 set rTag [$data(canvas) create rect 0 0 0 0 -fill "" -outline "" \
356 -tags [list rect $data(numItems) item$data(numItems)]]
358 foreach {x1 y1 x2 y2} [$data(canvas) bbox $iTag] {
359 break
361 set iW [expr {$x2 - $x1}]
362 set iH [expr {$y2 - $y1}]
363 if {$data(maxIW) < $iW} {
364 set data(maxIW) $iW
366 if {$data(maxIH) < $iH} {
367 set data(maxIH) $iH
370 foreach {x1 y1 x2 y2} [$data(canvas) bbox $tTag] {
371 break
373 set tW [expr {$x2 - $x1}]
374 set tH [expr {$y2 - $y1}]
375 if {$data(maxTW) < $tW} {
376 set data(maxTW) $tW
378 if {$data(maxTH) < $tH} {
379 set data(maxTH) $tH
382 lappend data(list) [list $iTag $tTag $rTag $iW $iH $tW \
383 $tH $data(numItems)]
384 set itemList($rTag) [list $iTag $tTag $text $data(numItems)]
385 set textList($data(numItems)) [string tolower $text]
386 incr data(numItems)
390 # Places the icons in a column-major arrangement.
392 proc ::tk::IconList_Arrange {w} {
393 upvar ::tk::$w data
395 if {![info exists data(list)]} {
396 if {[info exists data(canvas)] && [winfo exists $data(canvas)]} {
397 set data(noScroll) 1
398 $data(sbar) configure -command ""
400 return
403 set W [winfo width $data(canvas)]
404 set H [winfo height $data(canvas)]
405 set pad [expr {[$data(canvas) cget -highlightthickness] + \
406 [$data(canvas) cget -bd]}]
407 if {$pad < 2} {
408 set pad 2
411 incr W -[expr {$pad*2}]
412 incr H -[expr {$pad*2}]
414 set dx [expr {$data(maxIW) + $data(maxTW) + 8}]
415 if {$data(maxTH) > $data(maxIH)} {
416 set dy $data(maxTH)
417 } else {
418 set dy $data(maxIH)
420 incr dy 2
421 set shift [expr {$data(maxIW) + 4}]
423 set x [expr {$pad * 2}]
424 set y [expr {$pad * 1}] ; # Why * 1 ?
425 set usedColumn 0
426 foreach sublist $data(list) {
427 set usedColumn 1
428 foreach {iTag tTag rTag iW iH tW tH} $sublist {
429 break
432 set i_dy [expr {($dy - $iH)/2}]
433 set t_dy [expr {($dy - $tH)/2}]
435 $data(canvas) coords $iTag $x [expr {$y + $i_dy}]
436 $data(canvas) coords $tTag [expr {$x + $shift}] [expr {$y + $t_dy}]
437 $data(canvas) coords $rTag $x $y [expr {$x+$dx}] [expr {$y+$dy}]
439 incr y $dy
440 if {($y + $dy) > $H} {
441 set y [expr {$pad * 1}] ; # *1 ?
442 incr x $dx
443 set usedColumn 0
447 if {$usedColumn} {
448 set sW [expr {$x + $dx}]
449 } else {
450 set sW $x
453 if {$sW < $W} {
454 $data(canvas) configure -scrollregion [list $pad $pad $sW $H]
455 $data(sbar) configure -command ""
456 $data(canvas) xview moveto 0
457 set data(noScroll) 1
458 } else {
459 $data(canvas) configure -scrollregion [list $pad $pad $sW $H]
460 $data(sbar) configure -command [list $data(canvas) xview]
461 set data(noScroll) 0
464 set data(itemsPerColumn) [expr {($H-$pad)/$dy}]
465 if {$data(itemsPerColumn) < 1} {
466 set data(itemsPerColumn) 1
469 IconList_DrawSelection $w
472 # Gets called when the user invokes the IconList (usually by double-clicking
473 # or pressing the Return key).
475 proc ::tk::IconList_Invoke {w} {
476 upvar ::tk::$w data
478 if {$data(-command) ne "" && [llength $data(selection)]} {
479 uplevel #0 $data(-command)
483 # ::tk::IconList_See --
485 # If the item is not (completely) visible, scroll the canvas so that
486 # it becomes visible.
487 proc ::tk::IconList_See {w rTag} {
488 upvar ::tk::$w data
489 upvar ::tk::$w:itemList itemList
491 if {$data(noScroll)} {
492 return
494 set sRegion [$data(canvas) cget -scrollregion]
495 if {$sRegion eq ""} {
496 return
499 if { $rTag < 0 || $rTag >= [llength $data(list)] } {
500 return
503 set bbox [$data(canvas) bbox item$rTag]
504 set pad [expr {[$data(canvas) cget -highlightthickness] + \
505 [$data(canvas) cget -bd]}]
507 set x1 [lindex $bbox 0]
508 set x2 [lindex $bbox 2]
509 incr x1 -[expr {$pad * 2}]
510 incr x2 -[expr {$pad * 1}] ; # *1 ?
512 set cW [expr {[winfo width $data(canvas)] - $pad*2}]
514 set scrollW [expr {[lindex $sRegion 2]-[lindex $sRegion 0]+1}]
515 set dispX [expr {int([lindex [$data(canvas) xview] 0]*$scrollW)}]
516 set oldDispX $dispX
518 # check if out of the right edge
520 if {($x2 - $dispX) >= $cW} {
521 set dispX [expr {$x2 - $cW}]
523 # check if out of the left edge
525 if {($x1 - $dispX) < 0} {
526 set dispX $x1
529 if {$oldDispX ne $dispX} {
530 set fraction [expr {double($dispX)/double($scrollW)}]
531 $data(canvas) xview moveto $fraction
535 proc ::tk::IconList_Btn1 {w x y} {
536 upvar ::tk::$w data
538 focus $data(canvas)
539 set i [IconList_Index $w @$x,$y]
540 if {$i eq ""} {
541 return
543 IconList_Selection $w clear 0 end
544 IconList_Selection $w set $i
545 IconList_Selection $w anchor $i
548 proc ::tk::IconList_CtrlBtn1 {w x y} {
549 upvar ::tk::$w data
551 if { $data(-multiple) } {
552 focus $data(canvas)
553 set i [IconList_Index $w @$x,$y]
554 if {$i eq ""} {
555 return
557 if { [IconList_Selection $w includes $i] } {
558 IconList_Selection $w clear $i
559 } else {
560 IconList_Selection $w set $i
561 IconList_Selection $w anchor $i
566 proc ::tk::IconList_ShiftBtn1 {w x y} {
567 upvar ::tk::$w data
569 if { $data(-multiple) } {
570 focus $data(canvas)
571 set i [IconList_Index $w @$x,$y]
572 if {$i eq ""} {
573 return
575 set a [IconList_Index $w anchor]
576 if {$a eq ""} {
577 set a $i
579 IconList_Selection $w clear 0 end
580 IconList_Selection $w set $a $i
584 # Gets called on button-1 motions
586 proc ::tk::IconList_Motion1 {w x y} {
587 variable ::tk::Priv
588 set Priv(x) $x
589 set Priv(y) $y
590 set i [IconList_Index $w @$x,$y]
591 if {$i eq ""} {
592 return
594 IconList_Selection $w clear 0 end
595 IconList_Selection $w set $i
598 proc ::tk::IconList_ShiftMotion1 {w x y} {
599 upvar ::tk::$w data
600 variable ::tk::Priv
601 set Priv(x) $x
602 set Priv(y) $y
603 set i [IconList_Index $w @$x,$y]
604 if {$i eq ""} {
605 return
607 IconList_Selection $w clear 0 end
608 IconList_Selection $w set anchor $i
611 proc ::tk::IconList_Double1 {w x y} {
612 upvar ::tk::$w data
614 if {[llength $data(selection)]} {
615 IconList_Invoke $w
619 proc ::tk::IconList_ReturnKey {w} {
620 IconList_Invoke $w
623 proc ::tk::IconList_Leave1 {w x y} {
624 variable ::tk::Priv
626 set Priv(x) $x
627 set Priv(y) $y
628 IconList_AutoScan $w
631 proc ::tk::IconList_FocusIn {w} {
632 upvar ::tk::$w data
634 if {![info exists data(list)]} {
635 return
638 if {[llength $data(selection)]} {
639 IconList_DrawSelection $w
643 proc ::tk::IconList_FocusOut {w} {
644 IconList_Selection $w clear 0 end
647 # ::tk::IconList_UpDown --
649 # Moves the active element up or down by one element
651 # Arguments:
652 # w - The IconList widget.
653 # amount - +1 to move down one item, -1 to move back one item.
655 proc ::tk::IconList_UpDown {w amount} {
656 upvar ::tk::$w data
658 if {![info exists data(list)]} {
659 return
662 set curr [tk::IconList_CurSelection $w]
663 if { [llength $curr] == 0 } {
664 set i 0
665 } else {
666 set i [tk::IconList_Index $w anchor]
667 if {$i eq ""} {
668 return
670 incr i $amount
672 IconList_Selection $w clear 0 end
673 IconList_Selection $w set $i
674 IconList_Selection $w anchor $i
675 IconList_See $w $i
678 # ::tk::IconList_LeftRight --
680 # Moves the active element left or right by one column
682 # Arguments:
683 # w - The IconList widget.
684 # amount - +1 to move right one column, -1 to move left one column.
686 proc ::tk::IconList_LeftRight {w amount} {
687 upvar ::tk::$w data
689 if {![info exists data(list)]} {
690 return
693 set curr [IconList_CurSelection $w]
694 if { [llength $curr] == 0 } {
695 set i 0
696 } else {
697 set i [IconList_Index $w anchor]
698 if {$i eq ""} {
699 return
701 incr i [expr {$amount*$data(itemsPerColumn)}]
703 IconList_Selection $w clear 0 end
704 IconList_Selection $w set $i
705 IconList_Selection $w anchor $i
706 IconList_See $w $i
709 #----------------------------------------------------------------------
710 # Accelerator key bindings
711 #----------------------------------------------------------------------
713 # ::tk::IconList_KeyPress --
715 # Gets called when user enters an arbitrary key in the listbox.
717 proc ::tk::IconList_KeyPress {w key} {
718 variable ::tk::Priv
720 append Priv(ILAccel,$w) $key
721 IconList_Goto $w $Priv(ILAccel,$w)
722 catch {
723 after cancel $Priv(ILAccel,$w,afterId)
725 set Priv(ILAccel,$w,afterId) [after 500 [list tk::IconList_Reset $w]]
728 proc ::tk::IconList_Goto {w text} {
729 upvar ::tk::$w data
730 upvar ::tk::$w:textList textList
732 if {![info exists data(list)]} {
733 return
736 if {$text eq "" || $data(numItems) == 0} {
737 return
740 if {[llength [IconList_CurSelection $w]]} {
741 set start [IconList_Index $w anchor]
742 } else {
743 set start 0
746 set theIndex -1
747 set less 0
748 set len [string length $text]
749 set len0 [expr {$len-1}]
750 set i $start
752 # Search forward until we find a filename whose prefix is a
753 # case-insensitive match with $text
754 while {1} {
755 if {[string equal -nocase -length $len0 $textList($i) $text]} {
756 set theIndex $i
757 break
759 incr i
760 if {$i == $data(numItems)} {
761 set i 0
763 if {$i == $start} {
764 break
768 if {$theIndex > -1} {
769 IconList_Selection $w clear 0 end
770 IconList_Selection $w set $theIndex
771 IconList_Selection $w anchor $theIndex
772 IconList_See $w $theIndex
776 proc ::tk::IconList_Reset {w} {
777 variable ::tk::Priv
779 unset -nocomplain Priv(ILAccel,$w)
782 #----------------------------------------------------------------------
784 # F I L E D I A L O G
786 #----------------------------------------------------------------------
788 namespace eval ::tk::dialog {}
789 namespace eval ::tk::dialog::file {
790 namespace import -force ::tk::msgcat::*
791 set ::tk::dialog::file::showHiddenBtn 0
792 set ::tk::dialog::file::showHiddenVar 1
795 # ::tk::dialog::file:: --
797 # Implements the TK file selection dialog. This dialog is used when
798 # the tk_strictMotif flag is set to false. This procedure shouldn't
799 # be called directly. Call tk_getOpenFile or tk_getSaveFile instead.
801 # Arguments:
802 # type "open" or "save"
803 # args Options parsed by the procedure.
806 proc ::tk::dialog::file:: {type args} {
807 variable ::tk::Priv
808 set dataName __tk_filedialog
809 upvar ::tk::dialog::file::$dataName data
811 Config $dataName $type $args
813 if {$data(-parent) eq "."} {
814 set w .$dataName
815 } else {
816 set w $data(-parent).$dataName
819 # (re)create the dialog box if necessary
821 if {![winfo exists $w]} {
822 Create $w TkFDialog
823 } elseif {[winfo class $w] ne "TkFDialog"} {
824 destroy $w
825 Create $w TkFDialog
826 } else {
827 set data(dirMenuBtn) $w.f1.menu
828 set data(dirMenu) $w.f1.menu.menu
829 set data(upBtn) $w.f1.up
830 set data(icons) $w.icons
831 set data(ent) $w.f2.ent
832 set data(typeMenuLab) $w.f2.lab2
833 set data(typeMenuBtn) $w.f2.menu
834 set data(typeMenu) $data(typeMenuBtn).m
835 set data(okBtn) $w.f2.ok
836 set data(cancelBtn) $w.f2.cancel
837 set data(hiddenBtn) $w.f2.hidden
838 SetSelectMode $w $data(-multiple)
840 if {$::tk::dialog::file::showHiddenBtn} {
841 $data(hiddenBtn) configure -state normal
842 grid $data(hiddenBtn)
843 } else {
844 $data(hiddenBtn) configure -state disabled
845 grid remove $data(hiddenBtn)
848 # Make sure subseqent uses of this dialog are independent [Bug 845189]
849 unset -nocomplain data(extUsed)
851 # Dialog boxes should be transient with respect to their parent,
852 # so that they will always stay on top of their parent window. However,
853 # some window managers will create the window as withdrawn if the parent
854 # window is withdrawn or iconified. Combined with the grab we put on the
855 # window, this can hang the entire application. Therefore we only make
856 # the dialog transient if the parent is viewable.
858 if {[winfo viewable [winfo toplevel $data(-parent)]]} {
859 wm transient $w $data(-parent)
862 # Add traces on the selectPath variable
865 trace add variable data(selectPath) write \
866 [list ::tk::dialog::file::SetPath $w]
867 $data(dirMenuBtn) configure \
868 -textvariable ::tk::dialog::file::${dataName}(selectPath)
870 # Cleanup previous menu
872 $data(typeMenu) delete 0 end
873 $data(typeMenuBtn) configure -state normal -text ""
875 # Initialize the file types menu
877 if {[llength $data(-filetypes)]} {
878 # Default type and name to first entry
879 set initialtype [lindex $data(-filetypes) 0]
880 set initialTypeName [lindex $initialtype 0]
881 if {($data(-typevariable) ne "")
882 && [uplevel 2 [list info exists $data(-typevariable)]]} {
883 set initialTypeName [uplevel 2 [list set $data(-typevariable)]]
885 foreach type $data(-filetypes) {
886 set title [lindex $type 0]
887 set filter [lindex $type 1]
888 $data(typeMenu) add command -label $title \
889 -command [list ::tk::dialog::file::SetFilter $w $type]
890 # string first avoids glob-pattern char issues
891 if {[string first ${initialTypeName} $title] == 0} {
892 set initialtype $type
895 SetFilter $w $initialtype
896 $data(typeMenuBtn) configure -state normal
897 $data(typeMenuLab) configure -state normal
898 } else {
899 set data(filter) "*"
900 $data(typeMenuBtn) configure -state disabled -takefocus 0
901 $data(typeMenuLab) configure -state disabled
903 UpdateWhenIdle $w
905 # Withdraw the window, then update all the geometry information
906 # so we know how big it wants to be, then center the window in the
907 # display and de-iconify it.
909 ::tk::PlaceWindow $w widget $data(-parent)
910 wm title $w $data(-title)
912 # Set a grab and claim the focus too.
914 ::tk::SetFocusGrab $w $data(ent)
915 $data(ent) delete 0 end
916 $data(ent) insert 0 $data(selectFile)
917 $data(ent) selection range 0 end
918 $data(ent) icursor end
920 # Wait for the user to respond, then restore the focus and
921 # return the index of the selected button. Restore the focus
922 # before deleting the window, since otherwise the window manager
923 # may take the focus away so we can't redirect it. Finally,
924 # restore any grab that was in effect.
926 vwait ::tk::Priv(selectFilePath)
928 ::tk::RestoreFocusGrab $w $data(ent) withdraw
930 # Cleanup traces on selectPath variable
933 foreach trace [trace info variable data(selectPath)] {
934 trace remove variable data(selectPath) [lindex $trace 0] [lindex $trace 1]
936 $data(dirMenuBtn) configure -textvariable {}
938 return $Priv(selectFilePath)
941 # ::tk::dialog::file::Config --
943 # Configures the TK filedialog according to the argument list
945 proc ::tk::dialog::file::Config {dataName type argList} {
946 upvar ::tk::dialog::file::$dataName data
948 set data(type) $type
950 # 0: Delete all variable that were set on data(selectPath) the
951 # last time the file dialog is used. The traces may cause troubles
952 # if the dialog is now used with a different -parent option.
954 foreach trace [trace info variable data(selectPath)] {
955 trace remove variable data(selectPath) [lindex $trace 0] [lindex $trace 1]
958 # 1: the configuration specs
960 set specs {
961 {-defaultextension "" "" ""}
962 {-filetypes "" "" ""}
963 {-initialdir "" "" ""}
964 {-initialfile "" "" ""}
965 {-parent "" "" "."}
966 {-title "" "" ""}
967 {-typevariable "" "" ""}
970 # The "-multiple" option is only available for the "open" file dialog.
972 if {$type eq "open"} {
973 lappend specs {-multiple "" "" "0"}
976 # 2: default values depending on the type of the dialog
978 if {![info exists data(selectPath)]} {
979 # first time the dialog has been popped up
980 set data(selectPath) [pwd]
981 set data(selectFile) ""
984 # 3: parse the arguments
986 tclParseConfigSpec ::tk::dialog::file::$dataName $specs "" $argList
988 if {$data(-title) eq ""} {
989 if {$type eq "open"} {
990 set data(-title) [mc "Open"]
991 } else {
992 set data(-title) [mc "Save As"]
996 # 4: set the default directory and selection according to the -initial
997 # settings
999 if {$data(-initialdir) ne ""} {
1000 # Ensure that initialdir is an absolute path name.
1001 if {[file isdirectory $data(-initialdir)]} {
1002 set old [pwd]
1003 cd $data(-initialdir)
1004 set data(selectPath) [pwd]
1005 cd $old
1006 } else {
1007 set data(selectPath) [pwd]
1010 set data(selectFile) $data(-initialfile)
1012 # 5. Parse the -filetypes option
1014 set data(-filetypes) [::tk::FDGetFileTypes $data(-filetypes)]
1016 if {![winfo exists $data(-parent)]} {
1017 error "bad window path name \"$data(-parent)\""
1020 # Set -multiple to a one or zero value (not other boolean types
1021 # like "yes") so we can use it in tests more easily.
1022 if {$type eq "save"} {
1023 set data(-multiple) 0
1024 } elseif {$data(-multiple)} {
1025 set data(-multiple) 1
1026 } else {
1027 set data(-multiple) 0
1031 proc ::tk::dialog::file::Create {w class} {
1032 set dataName [lindex [split $w .] end]
1033 upvar ::tk::dialog::file::$dataName data
1034 variable ::tk::Priv
1035 global tk_library
1037 toplevel $w -class $class
1039 # f1: the frame with the directory option menu
1041 set f1 [frame $w.f1]
1042 bind [::tk::AmpWidget label $f1.lab -text [mc "&Directory:"]] \
1043 <<AltUnderlined>> [list focus $f1.menu]
1045 set data(dirMenuBtn) $f1.menu
1046 set data(dirMenu) [tk_optionMenu $f1.menu \
1047 [format %s(selectPath) ::tk::dialog::file::$dataName] ""]
1048 set data(upBtn) [button $f1.up]
1049 if {![info exists Priv(updirImage)]} {
1050 set Priv(updirImage) [image create bitmap -data {
1051 #define updir_width 28
1052 #define updir_height 16
1053 static char updir_bits[] = {
1054 0x00, 0x00, 0x00, 0x00, 0x80, 0x1f, 0x00, 0x00, 0x40, 0x20, 0x00, 0x00,
1055 0x20, 0x40, 0x00, 0x00, 0xf0, 0xff, 0xff, 0x01, 0x10, 0x00, 0x00, 0x01,
1056 0x10, 0x02, 0x00, 0x01, 0x10, 0x07, 0x00, 0x01, 0x90, 0x0f, 0x00, 0x01,
1057 0x10, 0x02, 0x00, 0x01, 0x10, 0x02, 0x00, 0x01, 0x10, 0x02, 0x00, 0x01,
1058 0x10, 0xfe, 0x07, 0x01, 0x10, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x01,
1059 0xf0, 0xff, 0xff, 0x01};}]
1061 $data(upBtn) configure -image $Priv(updirImage)
1063 $f1.menu configure -takefocus 1 -highlightthickness 2
1065 pack $data(upBtn) -side right -padx 4 -fill both
1066 pack $f1.lab -side left -padx 4 -fill both
1067 pack $f1.menu -expand yes -fill both -padx 4
1069 # data(icons): the IconList that list the files and directories.
1071 if {$class eq "TkFDialog"} {
1072 if { $data(-multiple) } {
1073 set fNameCaption [mc "File &names:"]
1074 } else {
1075 set fNameCaption [mc "File &name:"]
1077 set fTypeCaption [mc "Files of &type:"]
1078 set iconListCommand [list ::tk::dialog::file::OkCmd $w]
1079 } else {
1080 set fNameCaption [mc "&Selection:"]
1081 set iconListCommand [list ::tk::dialog::file::chooseDir::DblClick $w]
1083 set data(icons) [::tk::IconList $w.icons \
1084 -command $iconListCommand -multiple $data(-multiple)]
1085 bind $data(icons) <<ListboxSelect>> \
1086 [list ::tk::dialog::file::ListBrowse $w]
1088 # f2: the frame with the OK button, cancel button, "file name" field
1089 # and file types field.
1091 set f2 [frame $w.f2 -bd 0]
1092 bind [::tk::AmpWidget label $f2.lab -text $fNameCaption -anchor e -pady 0]\
1093 <<AltUnderlined>> [list focus $f2.ent]
1094 set data(ent) [entry $f2.ent]
1096 # The font to use for the icons. The default Canvas font on Unix
1097 # is just deviant.
1098 set ::tk::$w.icons(font) [$data(ent) cget -font]
1100 # Make the file types bits only if this is a File Dialog
1101 if {$class eq "TkFDialog"} {
1102 set data(typeMenuLab) [::tk::AmpWidget label $f2.lab2 \
1103 -text $fTypeCaption -anchor e -pady [$f2.lab cget -pady]]
1104 set data(typeMenuBtn) [menubutton $f2.menu -indicatoron 1 \
1105 -menu $f2.menu.m]
1106 set data(typeMenu) [menu $data(typeMenuBtn).m -tearoff 0]
1107 $data(typeMenuBtn) configure -takefocus 1 -highlightthickness 2 \
1108 -relief raised -bd 2 -anchor w
1109 bind $data(typeMenuLab) <<AltUnderlined>> [list \
1110 focus $data(typeMenuBtn)]
1113 # The hidden button is displayed when ::tk::dialog::file::showHiddenBtn
1114 # is true. Create it disabled so the binding doesn't trigger if it
1115 # isn't shown.
1116 if {$class eq "TkFDialog"} {
1117 set text [mc "Show &Hidden Files and Directories"]
1118 } else {
1119 set text [mc "Show &Hidden Directories"]
1121 set data(hiddenBtn) [::tk::AmpWidget checkbutton $f2.hidden \
1122 -text $text -anchor w -padx 3 -state disabled \
1123 -variable ::tk::dialog::file::showHiddenVar \
1124 -command [list ::tk::dialog::file::UpdateWhenIdle $w]]
1126 # the okBtn is created after the typeMenu so that the keyboard traversal
1127 # is in the right order, and add binding so that we find out when the
1128 # dialog is destroyed by the user (added here instead of to the overall
1129 # window so no confusion about how much <Destroy> gets called; exactly
1130 # once will do). [Bug 987169]
1132 set data(okBtn) [::tk::AmpWidget button $f2.ok \
1133 -text [mc "&OK"] -default active -pady 3]
1134 bind $data(okBtn) <Destroy> [list ::tk::dialog::file::Destroyed $w]
1135 set data(cancelBtn) [::tk::AmpWidget button $f2.cancel \
1136 -text [mc "&Cancel"] -default normal -pady 3]
1138 # grid the widgets in f2
1140 grid $f2.lab $f2.ent $data(okBtn) -padx 4 -sticky ew
1141 grid configure $f2.ent -padx 2
1142 if {$class eq "TkFDialog"} {
1143 grid $data(typeMenuLab) $data(typeMenuBtn) $data(cancelBtn) \
1144 -padx 4 -sticky ew
1145 grid configure $data(typeMenuBtn) -padx 0
1146 grid $data(hiddenBtn) -columnspan 2 -padx 4 -sticky ew
1147 } else {
1148 grid $data(hiddenBtn) - $data(cancelBtn) -padx 4 -sticky ew
1150 grid columnconfigure $f2 1 -weight 1
1152 # Pack all the frames together. We are done with widget construction.
1154 pack $f1 -side top -fill x -pady 4
1155 pack $f2 -side bottom -fill x
1156 pack $data(icons) -expand yes -fill both -padx 4 -pady 1
1158 # Set up the event handlers that are common to Directory and File Dialogs
1161 wm protocol $w WM_DELETE_WINDOW [list ::tk::dialog::file::CancelCmd $w]
1162 $data(upBtn) configure -command [list ::tk::dialog::file::UpDirCmd $w]
1163 $data(cancelBtn) configure -command [list ::tk::dialog::file::CancelCmd $w]
1164 bind $w <KeyPress-Escape> [list tk::ButtonInvoke $data(cancelBtn)]
1165 bind $w <Alt-Key> [list tk::AltKeyInDialog $w %A]
1167 # Set up event handlers specific to File or Directory Dialogs
1169 if {$class eq "TkFDialog"} {
1170 bind $data(ent) <Return> [list ::tk::dialog::file::ActivateEnt $w]
1171 $data(okBtn) configure -command [list ::tk::dialog::file::OkCmd $w]
1172 bind $w <Alt-t> [format {
1173 if {[%s cget -state] eq "normal"} {
1174 focus %s
1176 } $data(typeMenuBtn) $data(typeMenuBtn)]
1177 } else {
1178 set okCmd [list ::tk::dialog::file::chooseDir::OkCmd $w]
1179 bind $data(ent) <Return> $okCmd
1180 $data(okBtn) configure -command $okCmd
1181 bind $w <Alt-s> [list focus $data(ent)]
1182 bind $w <Alt-o> [list tk::ButtonInvoke $data(okBtn)]
1184 bind $w <Alt-h> [list $data(hiddenBtn) invoke]
1186 # Build the focus group for all the entries
1188 ::tk::FocusGroup_Create $w
1189 ::tk::FocusGroup_BindIn $w $data(ent) [list \
1190 ::tk::dialog::file::EntFocusIn $w]
1191 ::tk::FocusGroup_BindOut $w $data(ent) [list \
1192 ::tk::dialog::file::EntFocusOut $w]
1195 # ::tk::dialog::file::SetSelectMode --
1197 # Set the select mode of the dialog to single select or multi-select.
1199 # Arguments:
1200 # w The dialog path.
1201 # multi 1 if the dialog is multi-select; 0 otherwise.
1203 # Results:
1204 # None.
1206 proc ::tk::dialog::file::SetSelectMode {w multi} {
1207 set dataName __tk_filedialog
1208 upvar ::tk::dialog::file::$dataName data
1209 if { $multi } {
1210 set fNameCaption [mc "File &names:"]
1211 } else {
1212 set fNameCaption [mc "File &name:"]
1214 set iconListCommand [list ::tk::dialog::file::OkCmd $w]
1215 ::tk::SetAmpText $w.f2.lab $fNameCaption
1216 ::tk::IconList_Config $data(icons) \
1217 [list -multiple $multi -command $iconListCommand]
1218 return
1221 # ::tk::dialog::file::UpdateWhenIdle --
1223 # Creates an idle event handler which updates the dialog in idle
1224 # time. This is important because loading the directory may take a long
1225 # time and we don't want to load the same directory for multiple times
1226 # due to multiple concurrent events.
1228 proc ::tk::dialog::file::UpdateWhenIdle {w} {
1229 upvar ::tk::dialog::file::[winfo name $w] data
1231 if {[info exists data(updateId)]} {
1232 return
1233 } else {
1234 set data(updateId) [after idle [list ::tk::dialog::file::Update $w]]
1238 # ::tk::dialog::file::Update --
1240 # Loads the files and directories into the IconList widget. Also
1241 # sets up the directory option menu for quick access to parent
1242 # directories.
1244 proc ::tk::dialog::file::Update {w} {
1246 # This proc may be called within an idle handler. Make sure that the
1247 # window has not been destroyed before this proc is called
1248 if {![winfo exists $w]} {
1249 return
1251 set class [winfo class $w]
1252 if {($class ne "TkFDialog") && ($class ne "TkChooseDir")} {
1253 return
1256 set dataName [winfo name $w]
1257 upvar ::tk::dialog::file::$dataName data
1258 variable ::tk::Priv
1259 global tk_library
1260 unset -nocomplain data(updateId)
1262 if {![info exists Priv(folderImage)]} {
1263 set Priv(folderImage) [image create photo -data {
1264 R0lGODlhEAAMAKEAAAD//wAAAPD/gAAAACH5BAEAAAAALAAAAAAQAAwAAAIghINhyycvVFsB
1265 QtmS3rjaH1Hg141WaT5ouprt2HHcUgAAOw==}]
1266 set Priv(fileImage) [image create photo -data {
1267 R0lGODlhDAAMAKEAALLA3AAAAP//8wAAACH5BAEAAAAALAAAAAAMAAwAAAIgRI4Ha+IfWHsO
1268 rSASvJTGhnhcV3EJlo3kh53ltF5nAhQAOw==}]
1270 set folder $Priv(folderImage)
1271 set file $Priv(fileImage)
1273 set appPWD [pwd]
1274 if {[catch {
1275 cd $data(selectPath)
1276 }]} {
1277 # We cannot change directory to $data(selectPath). $data(selectPath)
1278 # should have been checked before ::tk::dialog::file::Update is called, so
1279 # we normally won't come to here. Anyways, give an error and abort
1280 # action.
1281 tk_messageBox -type ok -parent $w -icon warning -message \
1282 [mc "Cannot change to the directory \"%1\$s\".\nPermission denied." $data(selectPath)]
1283 cd $appPWD
1284 return
1287 # Turn on the busy cursor. BUG?? We haven't disabled X events, though,
1288 # so the user may still click and cause havoc ...
1290 set entCursor [$data(ent) cget -cursor]
1291 set dlgCursor [$w cget -cursor]
1292 $data(ent) configure -cursor watch
1293 $w configure -cursor watch
1294 update idletasks
1296 ::tk::IconList_DeleteAll $data(icons)
1298 set showHidden $::tk::dialog::file::showHiddenVar
1300 # Make the dir list
1301 # Using -directory [pwd] is better in some VFS cases.
1302 set cmd [list glob -tails -directory [pwd] -type d -nocomplain *]
1303 if {$showHidden} { lappend cmd .* }
1304 set dirs [lsort -dictionary -unique [eval $cmd]]
1305 set dirList {}
1306 foreach d $dirs {
1307 if {$d eq "." || $d eq ".."} {
1308 continue
1310 lappend dirList $d
1312 ::tk::IconList_Add $data(icons) $folder $dirList
1314 if {$class eq "TkFDialog"} {
1315 # Make the file list if this is a File Dialog, selecting all
1316 # but 'd'irectory type files.
1318 set cmd [list glob -tails -directory [pwd] \
1319 -type {f b c l p s} -nocomplain]
1320 if {$data(filter) eq "*"} {
1321 lappend cmd *
1322 if {$showHidden} {
1323 lappend cmd .*
1325 } else {
1326 eval [list lappend cmd] $data(filter)
1328 set fileList [lsort -dictionary -unique [eval $cmd]]
1329 ::tk::IconList_Add $data(icons) $file $fileList
1332 ::tk::IconList_Arrange $data(icons)
1334 # Update the Directory: option menu
1336 set list ""
1337 set dir ""
1338 foreach subdir [file split $data(selectPath)] {
1339 set dir [file join $dir $subdir]
1340 lappend list $dir
1343 $data(dirMenu) delete 0 end
1344 set var [format %s(selectPath) ::tk::dialog::file::$dataName]
1345 foreach path $list {
1346 $data(dirMenu) add command -label $path -command [list set $var $path]
1349 # Restore the PWD to the application's PWD
1351 cd $appPWD
1353 if {$class eq "TkFDialog"} {
1354 # Restore the Open/Save Button if this is a File Dialog
1356 if {$data(type) eq "open"} {
1357 ::tk::SetAmpText $data(okBtn) [mc "&Open"]
1358 } else {
1359 ::tk::SetAmpText $data(okBtn) [mc "&Save"]
1363 # turn off the busy cursor.
1365 $data(ent) configure -cursor $entCursor
1366 $w configure -cursor $dlgCursor
1369 # ::tk::dialog::file::SetPathSilently --
1371 # Sets data(selectPath) without invoking the trace procedure
1373 proc ::tk::dialog::file::SetPathSilently {w path} {
1374 upvar ::tk::dialog::file::[winfo name $w] data
1376 trace remove variable data(selectPath) write [list ::tk::dialog::file::SetPath $w]
1377 set data(selectPath) $path
1378 trace add variable data(selectPath) write [list ::tk::dialog::file::SetPath $w]
1382 # This proc gets called whenever data(selectPath) is set
1384 proc ::tk::dialog::file::SetPath {w name1 name2 op} {
1385 if {[winfo exists $w]} {
1386 upvar ::tk::dialog::file::[winfo name $w] data
1387 UpdateWhenIdle $w
1388 # On directory dialogs, we keep the entry in sync with the currentdir.
1389 if {[winfo class $w] eq "TkChooseDir"} {
1390 $data(ent) delete 0 end
1391 $data(ent) insert end $data(selectPath)
1396 # This proc gets called whenever data(filter) is set
1398 proc ::tk::dialog::file::SetFilter {w type} {
1399 upvar ::tk::dialog::file::[winfo name $w] data
1400 upvar ::tk::$data(icons) icons
1402 set data(filterType) $type
1403 set data(filter) [lindex $type 1]
1404 $data(typeMenuBtn) configure -text [lindex $type 0] -indicatoron 1
1406 # If we aren't using a default extension, use the one suppled
1407 # by the filter.
1408 if {![info exists data(extUsed)]} {
1409 if {[string length $data(-defaultextension)]} {
1410 set data(extUsed) 1
1411 } else {
1412 set data(extUsed) 0
1416 if {!$data(extUsed)} {
1417 # Get the first extension in the list that matches {^\*\.\w+$}
1418 # and remove all * from the filter.
1419 set index [lsearch -regexp $data(filter) {^\*\.\w+$}]
1420 if {$index >= 0} {
1421 set data(-defaultextension) \
1422 [string trimleft [lindex $data(filter) $index] "*"]
1423 } else {
1424 # Couldn't find anything! Reset to a safe default...
1425 set data(-defaultextension) ""
1429 $icons(sbar) set 0.0 0.0
1431 UpdateWhenIdle $w
1434 # tk::dialog::file::ResolveFile --
1436 # Interpret the user's text input in a file selection dialog.
1437 # Performs:
1439 # (1) ~ substitution
1440 # (2) resolve all instances of . and ..
1441 # (3) check for non-existent files/directories
1442 # (4) check for chdir permissions
1443 # (5) conversion of environment variable references to their
1444 # contents (once only)
1446 # Arguments:
1447 # context: the current directory you are in
1448 # text: the text entered by the user
1449 # defaultext: the default extension to add to files with no extension
1450 # expandEnv: whether to expand environment variables (yes by default)
1452 # Return vaue:
1453 # [list $flag $directory $file]
1455 # flag = OK : valid input
1456 # = PATTERN : valid directory/pattern
1457 # = PATH : the directory does not exist
1458 # = FILE : the directory exists by the file doesn't
1459 # exist
1460 # = CHDIR : Cannot change to the directory
1461 # = ERROR : Invalid entry
1463 # directory : valid only if flag = OK or PATTERN or FILE
1464 # file : valid only if flag = OK or PATTERN
1466 # directory may not be the same as context, because text may contain
1467 # a subdirectory name
1469 proc ::tk::dialog::file::ResolveFile {context text defaultext {expandEnv 1}} {
1470 set appPWD [pwd]
1472 set path [JoinFile $context $text]
1474 # If the file has no extension, append the default. Be careful not
1475 # to do this for directories, otherwise typing a dirname in the box
1476 # will give back "dirname.extension" instead of trying to change dir.
1477 if {
1478 ![file isdirectory $path] && ([file ext $path] eq "") &&
1479 ![string match {$*} [file tail $path]]
1480 } then {
1481 set path "$path$defaultext"
1484 if {[catch {file exists $path}]} {
1485 # This "if" block can be safely removed if the following code
1486 # stop generating errors.
1488 # file exists ~nonsuchuser
1490 return [list ERROR $path ""]
1493 if {[file exists $path]} {
1494 if {[file isdirectory $path]} {
1495 if {[catch {cd $path}]} {
1496 return [list CHDIR $path ""]
1498 set directory [pwd]
1499 set file ""
1500 set flag OK
1501 cd $appPWD
1502 } else {
1503 if {[catch {cd [file dirname $path]}]} {
1504 return [list CHDIR [file dirname $path] ""]
1506 set directory [pwd]
1507 set file [file tail $path]
1508 set flag OK
1509 cd $appPWD
1511 } else {
1512 set dirname [file dirname $path]
1513 if {[file exists $dirname]} {
1514 if {[catch {cd $dirname}]} {
1515 return [list CHDIR $dirname ""]
1517 set directory [pwd]
1518 cd $appPWD
1519 set file [file tail $path]
1520 # It's nothing else, so check to see if it is an env-reference
1521 if {$expandEnv && [string match {$*} $file]} {
1522 set var [string range $file 1 end]
1523 if {[info exist ::env($var)]} {
1524 return [ResolveFile $context $::env($var) $defaultext 0]
1527 if {[regexp {[*?]} $file]} {
1528 set flag PATTERN
1529 } else {
1530 set flag FILE
1532 } else {
1533 set directory $dirname
1534 set file [file tail $path]
1535 set flag PATH
1536 # It's nothing else, so check to see if it is an env-reference
1537 if {$expandEnv && [string match {$*} $file]} {
1538 set var [string range $file 1 end]
1539 if {[info exist ::env($var)]} {
1540 return [ResolveFile $context $::env($var) $defaultext 0]
1546 return [list $flag $directory $file]
1550 # Gets called when the entry box gets keyboard focus. We clear the selection
1551 # from the icon list . This way the user can be certain that the input in the
1552 # entry box is the selection.
1554 proc ::tk::dialog::file::EntFocusIn {w} {
1555 upvar ::tk::dialog::file::[winfo name $w] data
1557 if {[$data(ent) get] ne ""} {
1558 $data(ent) selection range 0 end
1559 $data(ent) icursor end
1560 } else {
1561 $data(ent) selection clear
1564 if {[winfo class $w] eq "TkFDialog"} {
1565 # If this is a File Dialog, make sure the buttons are labeled right.
1566 if {$data(type) eq "open"} {
1567 ::tk::SetAmpText $data(okBtn) [mc "&Open"]
1568 } else {
1569 ::tk::SetAmpText $data(okBtn) [mc "&Save"]
1574 proc ::tk::dialog::file::EntFocusOut {w} {
1575 upvar ::tk::dialog::file::[winfo name $w] data
1577 $data(ent) selection clear
1581 # Gets called when user presses Return in the "File name" entry.
1583 proc ::tk::dialog::file::ActivateEnt {w} {
1584 upvar ::tk::dialog::file::[winfo name $w] data
1586 set text [$data(ent) get]
1587 if {$data(-multiple)} {
1588 # For the multiple case we have to be careful to get the file
1589 # names as a true list, watching out for a single file with a
1590 # space in the name. Thus we query the IconList directly.
1592 set selIcos [::tk::IconList_CurSelection $data(icons)]
1593 set data(selectFile) ""
1594 if {[llength $selIcos] == 0 && $text ne ""} {
1595 # This assumes the user typed something in without selecting
1596 # files - so assume they only type in a single filename.
1597 VerifyFileName $w $text
1598 } else {
1599 foreach item $selIcos {
1600 VerifyFileName $w [::tk::IconList_Get $data(icons) $item]
1603 } else {
1604 VerifyFileName $w $text
1608 # Verification procedure
1610 proc ::tk::dialog::file::VerifyFileName {w filename} {
1611 upvar ::tk::dialog::file::[winfo name $w] data
1613 set list [ResolveFile $data(selectPath) $filename $data(-defaultextension)]
1614 foreach {flag path file} $list {
1615 break
1618 switch -- $flag {
1619 OK {
1620 if {$file eq ""} {
1621 # user has entered an existing (sub)directory
1622 set data(selectPath) $path
1623 $data(ent) delete 0 end
1624 } else {
1625 SetPathSilently $w $path
1626 if {$data(-multiple)} {
1627 lappend data(selectFile) $file
1628 } else {
1629 set data(selectFile) $file
1631 Done $w
1634 PATTERN {
1635 set data(selectPath) $path
1636 set data(filter) $file
1638 FILE {
1639 if {$data(type) eq "open"} {
1640 tk_messageBox -icon warning -type ok -parent $w \
1641 -message [mc "File \"%1\$s\" does not exist." \
1642 [file join $path $file]]
1643 $data(ent) selection range 0 end
1644 $data(ent) icursor end
1645 } else {
1646 SetPathSilently $w $path
1647 if {$data(-multiple)} {
1648 lappend data(selectFile) $file
1649 } else {
1650 set data(selectFile) $file
1652 Done $w
1655 PATH {
1656 tk_messageBox -icon warning -type ok -parent $w \
1657 -message [mc "Directory \"%1\$s\" does not exist." $path]
1658 $data(ent) selection range 0 end
1659 $data(ent) icursor end
1661 CHDIR {
1662 tk_messageBox -type ok -parent $w -message -icon warning \
1663 [mc "Cannot change to the directory \"%1\$s\".\nPermission denied." $path]
1664 $data(ent) selection range 0 end
1665 $data(ent) icursor end
1667 ERROR {
1668 tk_messageBox -type ok -parent $w -message -icon warning \
1669 [mc "Invalid file name \"%1\$s\"." $path]
1670 $data(ent) selection range 0 end
1671 $data(ent) icursor end
1676 # Gets called when user presses the Alt-s or Alt-o keys.
1678 proc ::tk::dialog::file::InvokeBtn {w key} {
1679 upvar ::tk::dialog::file::[winfo name $w] data
1681 if {[$data(okBtn) cget -text] eq $key} {
1682 ::tk::ButtonInvoke $data(okBtn)
1686 # Gets called when user presses the "parent directory" button
1688 proc ::tk::dialog::file::UpDirCmd {w} {
1689 upvar ::tk::dialog::file::[winfo name $w] data
1691 if {$data(selectPath) ne "/"} {
1692 set data(selectPath) [file dirname $data(selectPath)]
1696 # Join a file name to a path name. The "file join" command will break
1697 # if the filename begins with ~
1699 proc ::tk::dialog::file::JoinFile {path file} {
1700 if {[string match {~*} $file] && [file exists $path/$file]} {
1701 return [file join $path ./$file]
1702 } else {
1703 return [file join $path $file]
1707 # Gets called when user presses the "OK" button
1709 proc ::tk::dialog::file::OkCmd {w} {
1710 upvar ::tk::dialog::file::[winfo name $w] data
1712 set filenames {}
1713 foreach item [::tk::IconList_CurSelection $data(icons)] {
1714 lappend filenames [::tk::IconList_Get $data(icons) $item]
1717 if {([llength $filenames] && !$data(-multiple)) || \
1718 ($data(-multiple) && ([llength $filenames] == 1))} {
1719 set filename [lindex $filenames 0]
1720 set file [JoinFile $data(selectPath) $filename]
1721 if {[file isdirectory $file]} {
1722 ListInvoke $w [list $filename]
1723 return
1727 ActivateEnt $w
1730 # Gets called when user presses the "Cancel" button
1732 proc ::tk::dialog::file::CancelCmd {w} {
1733 upvar ::tk::dialog::file::[winfo name $w] data
1734 variable ::tk::Priv
1736 bind $data(okBtn) <Destroy> {}
1737 set Priv(selectFilePath) ""
1740 # Gets called when user destroys the dialog directly [Bug 987169]
1742 proc ::tk::dialog::file::Destroyed {w} {
1743 upvar ::tk::dialog::file::[winfo name $w] data
1744 variable ::tk::Priv
1746 set Priv(selectFilePath) ""
1749 # Gets called when user browses the IconList widget (dragging mouse, arrow
1750 # keys, etc)
1752 proc ::tk::dialog::file::ListBrowse {w} {
1753 upvar ::tk::dialog::file::[winfo name $w] data
1755 set text {}
1756 foreach item [::tk::IconList_CurSelection $data(icons)] {
1757 lappend text [::tk::IconList_Get $data(icons) $item]
1759 if {[llength $text] == 0} {
1760 return
1762 if { [llength $text] > 1 } {
1763 set newtext {}
1764 foreach file $text {
1765 set fullfile [JoinFile $data(selectPath) $file]
1766 if { ![file isdirectory $fullfile] } {
1767 lappend newtext $file
1770 set text $newtext
1771 set isDir 0
1772 } else {
1773 set text [lindex $text 0]
1774 set file [JoinFile $data(selectPath) $text]
1775 set isDir [file isdirectory $file]
1777 if {!$isDir} {
1778 $data(ent) delete 0 end
1779 $data(ent) insert 0 $text
1781 if {[winfo class $w] eq "TkFDialog"} {
1782 if {$data(type) eq "open"} {
1783 ::tk::SetAmpText $data(okBtn) [mc "&Open"]
1784 } else {
1785 ::tk::SetAmpText $data(okBtn) [mc "&Save"]
1788 } elseif {[winfo class $w] eq "TkFDialog"} {
1789 ::tk::SetAmpText $data(okBtn) [mc "&Open"]
1793 # Gets called when user invokes the IconList widget (double-click,
1794 # Return key, etc)
1796 proc ::tk::dialog::file::ListInvoke {w filenames} {
1797 upvar ::tk::dialog::file::[winfo name $w] data
1799 if {[llength $filenames] == 0} {
1800 return
1803 set file [JoinFile $data(selectPath) [lindex $filenames 0]]
1805 set class [winfo class $w]
1806 if {$class eq "TkChooseDir" || [file isdirectory $file]} {
1807 set appPWD [pwd]
1808 if {[catch {cd $file}]} {
1809 tk_messageBox -type ok -parent $w -message -icon warning \
1810 [mc "Cannot change to the directory \"%1\$s\".\nPermission denied." $file]
1811 } else {
1812 cd $appPWD
1813 set data(selectPath) $file
1815 } else {
1816 if {$data(-multiple)} {
1817 set data(selectFile) $filenames
1818 } else {
1819 set data(selectFile) $file
1821 Done $w
1825 # ::tk::dialog::file::Done --
1827 # Gets called when user has input a valid filename. Pops up a
1828 # dialog box to confirm selection when necessary. Sets the
1829 # tk::Priv(selectFilePath) variable, which will break the "vwait"
1830 # loop in ::tk::dialog::file:: and return the selected filename to the
1831 # script that calls tk_getOpenFile or tk_getSaveFile
1833 proc ::tk::dialog::file::Done {w {selectFilePath ""}} {
1834 upvar ::tk::dialog::file::[winfo name $w] data
1835 variable ::tk::Priv
1837 if {$selectFilePath eq ""} {
1838 if {$data(-multiple)} {
1839 set selectFilePath {}
1840 foreach f $data(selectFile) {
1841 lappend selectFilePath [JoinFile $data(selectPath) $f]
1843 } else {
1844 set selectFilePath [JoinFile $data(selectPath) $data(selectFile)]
1847 set Priv(selectFile) $data(selectFile)
1848 set Priv(selectPath) $data(selectPath)
1850 if {($data(type) eq "save") && [file exists $selectFilePath]} {
1851 set reply [tk_messageBox -icon warning -type yesno -parent $w \
1852 -message [mc "File \"%1\$s\" already exists.\nDo you want\
1853 to overwrite it?" $selectFilePath]]
1854 if {$reply eq "no"} {
1855 return
1858 if {[info exists data(-typevariable)] && $data(-typevariable) ne ""
1859 && [info exists data(-filetypes)] && [llength $data(-filetypes)]
1860 && [info exists data(filterType)] && $data(filterType) ne ""} {
1861 upvar 4 $data(-typevariable) initialTypeName
1862 set initialTypeName [lindex $data(filterType) 0]
1865 bind $data(okBtn) <Destroy> {}
1866 set Priv(selectFilePath) $selectFilePath