FileBox.tcl 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. # -*- mode: TCL; fill-column: 75; tab-width: 8; coding: iso-latin-1-unix -*-
  2. #
  3. # $Id: FileBox.tcl,v 1.5 2004/03/28 02:44:57 hobbs Exp $
  4. #
  5. # FileBox.tcl --
  6. #
  7. # Implements the File Selection Box widget.
  8. #
  9. # Copyright (c) 1993-1999 Ioi Kim Lam.
  10. # Copyright (c) 2000-2001 Tix Project Group.
  11. # Copyright (c) 2004 ActiveState
  12. #
  13. # See the file "license.terms" for information on usage and redistribution
  14. # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  15. #
  16. # ToDo
  17. # (1) If user has entered an invalid directory, give an error dialog
  18. #
  19. tixWidgetClass tixFileSelectBox {
  20. -superclass tixPrimitive
  21. -classname TixFileSelectBox
  22. -method {
  23. filter invoke
  24. }
  25. -flag {
  26. -browsecmd -command -dir -directory -disablecallback
  27. -grab -pattern -selection -value
  28. }
  29. -configspec {
  30. {-browsecmd browseCmd BrowseCmd ""}
  31. {-command command Command ""}
  32. {-directory directory Directory ""}
  33. {-disablecallback disableCallback DisableCallback 0 tixVerifyBoolean}
  34. {-grab grab Grab global}
  35. {-pattern pattern Pattern *}
  36. {-value value Value ""}
  37. }
  38. -alias {
  39. {-selection -value}
  40. {-dir -directory}
  41. }
  42. -forcecall {
  43. -value
  44. }
  45. -default {
  46. {.relief raised}
  47. {*filelist*Listbox.takeFocus true}
  48. {.borderWidth 1}
  49. {*Label.anchor w}
  50. {*Label.borderWidth 0}
  51. {*TixComboBox*scrollbar auto}
  52. {*TixComboBox*Label.anchor w}
  53. {*TixScrolledListBox.scrollbar auto}
  54. {*Listbox.exportSelection false}
  55. {*directory*Label.text "Directories:"}
  56. {*directory*Label.underline 0}
  57. {*file*Label.text "Files:"}
  58. {*file*Label.underline 2}
  59. {*filter.label "Filter:"}
  60. {*filter*label.underline 3}
  61. {*filter.labelSide top}
  62. {*selection.label "Selection:"}
  63. {*selection*label.underline 0}
  64. {*selection.labelSide top}
  65. }
  66. }
  67. proc tixFileSelectBox:InitWidgetRec {w} {
  68. upvar #0 $w data
  69. global env
  70. tixChainMethod $w InitWidgetRec
  71. if {$data(-directory) eq ""} {
  72. set data(-directory) [pwd]
  73. }
  74. if {$data(-pattern) eq ""} {
  75. set data(-pattern) "*"
  76. }
  77. tixFileSelectBox:SetPat $w $data(-pattern)
  78. tixFileSelectBox:SetDir $w [tixFSNormalize $data(-directory)]
  79. set data(flag) 0
  80. set data(fakeDir) 0
  81. }
  82. #----------------------------------------------------------------------
  83. # Construct widget
  84. #----------------------------------------------------------------------
  85. proc tixFileSelectBox:ConstructWidget {w} {
  86. upvar #0 $w data
  87. tixChainMethod $w ConstructWidget
  88. set frame1 [tixFileSelectBox:CreateFrame1 $w]
  89. set frame2 [tixFileSelectBox:CreateFrame2 $w]
  90. set frame3 [tixFileSelectBox:CreateFrame3 $w]
  91. pack $frame1 -in $w -side top -fill x
  92. pack $frame3 -in $w -side bottom -fill x
  93. pack $frame2 -in $w -side top -fill both -expand yes
  94. }
  95. proc tixFileSelectBox:CreateFrame1 {w} {
  96. upvar #0 $w data
  97. frame $w.f1 -border 10
  98. tixComboBox $w.f1.filter -editable 1\
  99. -command [list $w filter] -anchor e \
  100. -options {
  101. slistbox.scrollbar auto
  102. listbox.height 5
  103. label.anchor w
  104. }
  105. set data(w:filter) $w.f1.filter
  106. pack $data(w:filter) -side top -expand yes -fill both
  107. return $w.f1
  108. }
  109. proc tixFileSelectBox:CreateFrame2 {w} {
  110. upvar #0 $w data
  111. tixPanedWindow $w.f2 -orientation horizontal
  112. # THE LEFT FRAME
  113. #-----------------------
  114. set dir [$w.f2 add directory -size 120]
  115. $dir config -relief flat
  116. label $dir.lab
  117. set data(w:dirlist) [tixScrolledListBox $dir.dirlist\
  118. -scrollbar auto\
  119. -options {listbox.width 4 listbox.height 6}]
  120. pack $dir.lab -side top -fill x -padx 10
  121. pack $data(w:dirlist) -side bottom -expand yes -fill both -padx 10
  122. # THE RIGHT FRAME
  123. #-----------------------
  124. set file [$w.f2 add file -size 160]
  125. $file config -relief flat
  126. label $file.lab
  127. set data(w:filelist) [tixScrolledListBox $file.filelist \
  128. -scrollbar auto\
  129. -options {listbox.width 4 listbox.height 6}]
  130. pack $file.lab -side top -fill x -padx 10
  131. pack $data(w:filelist) -side bottom -expand yes -fill both -padx 10
  132. return $w.f2
  133. }
  134. proc tixFileSelectBox:CreateFrame3 {w} {
  135. upvar #0 $w data
  136. frame $w.f3 -border 10
  137. tixComboBox $w.f3.selection -editable 1\
  138. -command [list tixFileSelectBox:SelInvoke $w] \
  139. -anchor e \
  140. -options {
  141. slistbox.scrollbar auto
  142. listbox.height 5
  143. label.anchor w
  144. }
  145. set data(w:selection) $w.f3.selection
  146. pack $data(w:selection) -side top -fill both
  147. return $w.f3
  148. }
  149. proc tixFileSelectBox:SelInvoke {w args} {
  150. upvar #0 $w data
  151. set event [tixEvent type]
  152. if {$event ne "<FocusOut>" && $event ne "<Tab>"} {
  153. $w invoke
  154. }
  155. }
  156. proc tixFileSelectBox:SetValue {w value} {
  157. upvar #0 $w data
  158. set data(i-value) $value
  159. set data(-value) [tixFSNative $value]
  160. }
  161. proc tixFileSelectBox:SetDir {w value} {
  162. upvar #0 $w data
  163. set data(i-directory) $value
  164. set data(-directory) [tixFSNative $value]
  165. }
  166. proc tixFileSelectBox:SetPat {w value} {
  167. upvar #0 $w data
  168. set data(i-pattern) $value
  169. set data(-pattern) [tixFSNative $value]
  170. }
  171. #----------------------------------------------------------------------
  172. # BINDINGS
  173. #----------------------------------------------------------------------
  174. proc tixFileSelectBox:SetBindings {w} {
  175. upvar #0 $w data
  176. tixChainMethod $w SetBindings
  177. tixDoWhenMapped $w [list tixFileSelectBox:FirstMapped $w]
  178. $data(w:dirlist) config \
  179. -browsecmd [list tixFileSelectBox:SelectDir $w] \
  180. -command [list tixFileSelectBox:InvokeDir $w]
  181. $data(w:filelist) config \
  182. -browsecmd [list tixFileSelectBox:SelectFile $w] \
  183. -command [list tixFileSelectBox:InvokeFile $w]
  184. }
  185. #----------------------------------------------------------------------
  186. # CONFIG OPTIONS
  187. #----------------------------------------------------------------------
  188. proc tixFileSelectBox:config-directory {w value} {
  189. upvar #0 $w data
  190. if {$value eq ""} {
  191. set value [pwd]
  192. }
  193. tixFileSelectBox:SetDir $w [tixFSNormalize $value]
  194. tixFileSelectBox:SetFilter $w $data(i-directory) $data(i-pattern)
  195. $w filter
  196. return $data(-directory)
  197. }
  198. proc tixFileSelectBox:config-pattern {w value} {
  199. upvar #0 $w data
  200. if {$value eq ""} {
  201. set value "*"
  202. }
  203. tixFileSelectBox:SetPat $w $value
  204. tixFileSelectBox:SetFilter $w $data(i-directory) $data(i-pattern)
  205. # Returning a value means we have overridden the value and updated
  206. # the widget record ourselves.
  207. #
  208. return $data(-pattern)
  209. }
  210. proc tixFileSelectBox:config-value {w value} {
  211. upvar #0 $w data
  212. tixFileSelectBox:SetValue $w [tixFSNormalize $value]
  213. tixSetSilent $data(w:selection) $value
  214. return $data(-value)
  215. }
  216. #----------------------------------------------------------------------
  217. # PUBLIC METHODS
  218. #----------------------------------------------------------------------
  219. proc tixFileSelectBox:filter {w args} {
  220. upvar #0 $w data
  221. $data(w:filter) popdown
  222. tixFileSelectBox:InterpFilter $w
  223. tixFileSelectBox:LoadDir $w
  224. }
  225. proc tixFileSelectBox:invoke {w args} {
  226. upvar #0 $w data
  227. if {[$data(w:selection) cget -value] ne
  228. [$data(w:selection) cget -selection]} {
  229. # this will in turn call "invoke" again ...
  230. #
  231. $data(w:selection) invoke
  232. return
  233. }
  234. # record the filter
  235. #
  236. set filter [tixFileSelectBox:InterpFilter $w]
  237. $data(w:filter) addhistory $filter
  238. # record the selection
  239. #
  240. set userInput [string trim [$data(w:selection) cget -value]]
  241. tixFileSelectBox:SetValue $w \
  242. [tixFSNormalize [file join $data(i-directory) $userInput]]
  243. $data(w:selection) addhistory $data(-value)
  244. $data(w:filter) align
  245. $data(w:selection) align
  246. if {[llength $data(-command)] && !$data(-disablecallback)} {
  247. set bind(specs) "%V"
  248. set bind(%V) $data(-value)
  249. tixEvalCmdBinding $w $data(-command) bind $data(-value)
  250. }
  251. }
  252. #----------------------------------------------------------------------
  253. # INTERNAL METHODS
  254. #----------------------------------------------------------------------
  255. # InterpFilter:
  256. # Interprets the value of the w:filter widget.
  257. #
  258. # Side effects:
  259. # Changes the fields data(-directory) and data(-pattenn)
  260. #
  261. proc tixFileSelectBox:InterpFilter {w {filter ""}} {
  262. upvar #0 $w data
  263. if {$filter == ""} {
  264. set filter [$data(w:filter) cget -selection]
  265. if {$filter == ""} {
  266. set filter [$data(w:filter) cget -value]
  267. }
  268. }
  269. set i_filter [tixFSNormalize $filter]
  270. if {[file isdirectory $filter]} {
  271. tixFileSelectBox:SetDir $w $i_filter
  272. tixFileSelectBox:SetPat $w "*"
  273. } else {
  274. set nDir [file dirname $filter]
  275. if {$nDir eq "" || $nDir eq "."} {
  276. tixFileSelectBox:SetDir $w [tixFSNormalize $data(i-directory)]
  277. } else {
  278. tixFileSelectBox:SetDir $w [tixFSNormalize $nDir]
  279. }
  280. tixFileSelectBox:SetPat $w [file tail $filter]
  281. }
  282. tixFileSelectBox:SetFilter $w $data(i-directory) $data(i-pattern)
  283. return $data(filter)
  284. }
  285. proc tixFileSelectBox:SetFilter {w dir pattern} {
  286. upvar #0 $w data
  287. set data(filter) [file join $dir $pattern]
  288. tixSetSilent $data(w:filter) $data(filter)
  289. }
  290. proc tixFileSelectBox:LoadDirIntoLists {w} {
  291. upvar #0 $w data
  292. $data(w:dirlist) subwidget listbox delete 0 end
  293. $data(w:filelist) subwidget listbox delete 0 end
  294. set dir $data(i-directory)
  295. # (1) List the directories
  296. #
  297. set isDrive [expr {[llength [file split $dir]] == 1}]
  298. foreach name [tixFSListDir $dir 1 0 1 1] {
  299. if {".." eq $name && $isDrive} { continue }
  300. $data(w:dirlist) subwidget listbox insert end $name
  301. }
  302. # (2) List the files
  303. #
  304. # %% UNIX'ISM:
  305. # If the pattern is "*" force glob to list the .* files.
  306. # However, since the user might not
  307. # be interested in them, shift the listbox so that the "normal" files
  308. # are seen first
  309. #
  310. # NOTE: if we pass $pat == "" but with $showHidden set to true,
  311. # tixFSListDir will list "* .*" in Unix. See the comment on top of
  312. # the tixFSListDir code.
  313. #
  314. if {$data(i-pattern) eq "*"} {
  315. set pat ""
  316. } else {
  317. set pat $data(i-pattern)
  318. }
  319. set top 0
  320. foreach name [tixFSListDir $dir 0 1 0 0 $pat] {
  321. $data(w:filelist) subwidget listbox insert end $name
  322. if {[string match .* $name]} {
  323. incr top
  324. }
  325. }
  326. $data(w:filelist) subwidget listbox yview $top
  327. }
  328. proc tixFileSelectBox:LoadDir {w} {
  329. upvar #0 $w data
  330. tixBusy $w on [$data(w:dirlist) subwidget listbox]
  331. tixFileSelectBox:LoadDirIntoLists $w
  332. if {[$data(w:dirlist) subwidget listbox size] == 0} {
  333. # fail safe, just in case the user has inputed an errnoeuos
  334. # directory
  335. $data(w:dirlist) subwidget listbox insert 0 ".."
  336. }
  337. tixWidgetDoWhenIdle tixBusy $w off [$data(w:dirlist) subwidget listbox]
  338. }
  339. # User single clicks on the directory listbox
  340. #
  341. proc tixFileSelectBox:SelectDir {w} {
  342. upvar #0 $w data
  343. if {$data(fakeDir) > 0} {
  344. incr data(fakeDir) -1
  345. $data(w:dirlist) subwidget listbox select clear 0 end
  346. $data(w:dirlist) subwidget listbox activate -1
  347. return
  348. }
  349. if {$data(flag)} {
  350. return
  351. }
  352. set data(flag) 1
  353. set subdir [tixListboxGetCurrent [$data(w:dirlist) subwidget listbox]]
  354. if {$subdir == ""} {
  355. set subdir "."
  356. }
  357. tixFileSelectBox:SetFilter $w \
  358. [tixFSNormalize [file join $data(i-directory) $subdir]] \
  359. $data(i-pattern)
  360. set data(flag) 0
  361. }
  362. proc tixFileSelectBox:InvokeDir {w} {
  363. upvar #0 $w data
  364. set theDir [$data(w:dirlist) subwidget listbox get active]
  365. tixFileSelectBox:SetDir $w \
  366. [tixFSNormalize [file join $data(i-directory) $theDir]]
  367. $data(w:dirlist) subwidget listbox select clear 0 end
  368. tixFileSelectBox:SetFilter $w $data(i-directory) $data(i-pattern)
  369. tixFileSelectBox:InterpFilter $w [tixFSNativeNorm $data(filter)]
  370. tixFileSelectBox:LoadDir $w
  371. if {![tixEvent match <Return>]} {
  372. incr data(fakeDir) 1
  373. }
  374. }
  375. proc tixFileSelectBox:SelectFile {w} {
  376. upvar #0 $w data
  377. if {$data(flag)} {
  378. return
  379. }
  380. set data(flag) 1
  381. # Reset the "Filter:" box to the current directory:
  382. #
  383. $data(w:dirlist) subwidget listbox select clear 0 end
  384. tixFileSelectBox:SetFilter $w $data(i-directory) $data(i-pattern)
  385. # Now select the file
  386. #
  387. set selected [tixListboxGetCurrent [$data(w:filelist) subwidget listbox]]
  388. if {$selected != ""} {
  389. # Make sure that the selection is not empty!
  390. #
  391. tixFileSelectBox:SetValue $w \
  392. [tixFSNormalize [file join $data(i-directory) $selected]]
  393. tixSetSilent $data(w:selection) $data(-value)
  394. if {[llength $data(-browsecmd)]} {
  395. tixEvalCmdBinding $w $data(-browsecmd) "" $data(-value)
  396. }
  397. }
  398. set data(flag) 0
  399. }
  400. proc tixFileSelectBox:InvokeFile {w} {
  401. upvar #0 $w data
  402. set selected [tixListboxGetCurrent [$data(w:filelist) subwidget listbox]]
  403. if {$selected != ""} {
  404. $w invoke
  405. }
  406. }
  407. # This is only called the first this fileBox is mapped -- load the directory
  408. #
  409. proc tixFileSelectBox:FirstMapped {w} {
  410. if {![winfo exists $w]} {
  411. return
  412. }
  413. upvar #0 $w data
  414. tixFileSelectBox:SetFilter $w $data(i-directory) $data(i-pattern)
  415. tixFileSelectBox:LoadDir $w
  416. $data(w:filter) align
  417. }
  418. #----------------------------------------------------------------------
  419. #
  420. #
  421. # C O N V E N I E N C E R O U T I N E S
  422. #
  423. #
  424. #----------------------------------------------------------------------
  425. # This is obsolete. Use the widget tixFileSelectDialog instead
  426. #
  427. #
  428. proc tixMkFileDialog {w args} {
  429. set option(-okcmd) ""
  430. set option(-helpcmd) ""
  431. tixHandleOptions option {-okcmd -helpcmd} $args
  432. toplevel $w
  433. wm minsize $w 10 10
  434. tixStdDlgBtns $w.btns
  435. if {$option(-okcmd) != ""} {
  436. tixFileSelectBox $w.fsb \
  437. -command "[list wm withdraw $w]; $option(-okcmd)"
  438. } else {
  439. tixFileSelectBox $w.fsb -command [list wm withdraw $w]
  440. }
  441. $w.btns button ok config -command [list $w.fsb invoke]
  442. $w.btns button apply config -command [list $w.fsb filter] -text Filter
  443. $w.btns button cancel config -command [list wm withdraw $w]
  444. if {$option(-helpcmd) == ""} {
  445. $w.btns button help config -state disabled
  446. } else {
  447. $w.btns button help config -command $option(-helpcmd)
  448. }
  449. wm protocol $w WM_DELETE_WINDOW [list wm withdraw $w]
  450. pack $w.btns -side bottom -fill both
  451. pack $w.fsb -fill both -expand yes
  452. return $w.fsb
  453. }