home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1993 #3 / NN_1993_3.iso / spool / comp / lang / tcl / 2470 < prev    next >
Encoding:
Text File  |  1993-01-25  |  12.3 KB  |  437 lines

  1. Newsgroups: comp.lang.tcl
  2. Path: sparky!uunet!eco.twg.com!twg.com!news
  3. From: "David Herron" <david@twg.com>
  4. Subject: File browser `object' and new version of interp module
  5. Message-ID: <1993Jan25.180211.26610@twg.com>
  6. Sensitivity: Personal
  7. Encoding:  417 TEXT , 4 TEXT 
  8. Sender: news@twg.com (USENET News System)
  9. Conversion: Prohibited
  10. Organization: The Wollongong Group, Inc., Palo Alto, CA
  11. Conversion-With-Loss: Prohibited
  12. Date: Mon, 25 Jan 1993 18:02:40 GMT
  13. Lines: 422
  14.  
  15. Greetings!  I'd been planning on posting this anyway, regardless of
  16. my message last week.  Included in this posting is the file browser
  17. module I've written over the last week or so.  The current source for the
  18. interp module is large enough I want to hold off until a couple people
  19. request its posting (it's >30K of source right now -- compiles to ~8K
  20. of object on a SPARC with gcc v2.3.1; one job to do soon is seeing how
  21. much of that is *necessary*).
  22.  
  23. My scheme for doing objects is as follows:
  24.  
  25. - The class definition is stored in its own interpretor.  It contains
  26.   the procedures defining the class, and any variables/constants necessary
  27.   to the class.
  28.  
  29. - When creating a new instance of a class you call the `new' procedure
  30.   in the class.  This is/should-be the only time that interpretor is
  31.   used for any execution.
  32.  
  33. - `new' creates a new interpretor.  Then to copy the `methods' over
  34.   it creates a command in the new interpretor which `chains' to the
  35.   command in the `class' interpretor (except that the execution happens
  36.   within the context of the current (new) interpretor).
  37.  
  38. - A few new commands are created, and `exit' is overriden to just
  39.   kill the interpretor rather than exit the process.
  40.   The `unknown' command passes anything unknown either to the "parent"
  41.   interpretor (allows for creating aggregate objects) or the "main"
  42.   interpretor.  Since I expect that `unknown' will happen frequently
  43.   it is coded in C.
  44.  
  45. - PROBLEM: Since callbacks from widgets happen in the "main" interpretor
  46.   there must be easy access to there so that the widgets can be created
  47.   over there.  A command, MainInterp, exists to pass commands there.
  48.  
  49. The above should be enough for y'all to appreciate the following source.
  50. I have created a mailing list for discussing the interp module.  To
  51. subscribe, send mail to services@davids.mmdf.com and set your Subject:
  52. line to "listserv subscribe interp".  I will soon make these file available
  53. from an e-mailable archive server at the same address.  Thanks to the
  54. guys at EITech for creating such a nifty tool (servicemail)!
  55.  
  56. First is my test program which also shows how to use the file
  57. browser object:
  58.  
  59. -----------------------------> Begin `tfb'
  60. source fileBrowserC.tcl
  61.  
  62. option add *Listbox.relief      sunken
  63. option add *Entry.relief        sunken
  64. option add *Text.relief         sunken
  65. option add *Scrollbar.relief    sunken
  66.  
  67. option add *Listbox.exportSelection     false
  68. option add *Entry.exportSelection       false
  69. option add *Radiobutton.anchor          w
  70.  
  71. wm minsize . 1 1
  72.  
  73. interp MainInterp
  74.  
  75. set fb1 [FileBrowserClass new]
  76. $fb1 MakeWidgets .fb
  77. pack append . .fb { top fill expand }
  78. $fb1 {
  79.         setDirectory "/tmp"
  80.         setPattern   "*"
  81.         changeDirectory
  82. #       MainInterp destroy $hlpBtn
  83.  
  84. proc okCommand {} {
  85.         global pathEntry filEntry
  86.         set f "[MainInterp $pathEntry get]/[MainInterp $filEntry get]"
  87.         if {$f != "/"} { puts stdout "$f" }
  88.         MainInterp destroy .
  89. }
  90.  
  91. proc cancelCommand {} { MainInterp destroy . }
  92. }
  93. ------------------------------> End `tfb'
  94.  
  95. And second is the object itself (a convention which might be useful
  96. is to end files which define a `class' with C.tcl rather than
  97. just .tcl):
  98.  
  99. ------------------------------> Begin fileBrowserC.tcl
  100.  
  101. # $Id: fileBrowserC.tcl,v 1.1 1993/01/25 06:32:14 david Exp $
  102. # fileBrowserC.tcl - File Browser class definition.
  103. #
  104. # AUTHOR: David Herron <david@davids.mmdf.com (home)>, <david@twg.com (work)>
  105. #
  106. # $Log: fileBrowserC.tcl,v $
  107. # Revision 1.1  1993/01/25  06:32:14  david
  108. # Initial revisions of the interp module, documentation, and file browser.
  109. #
  110. #
  111. #
  112. # The file browser continually presents the contents of a particular
  113. # directory, with the goal of selecting a file.  The user is able to
  114. # change the current directory at will.  The current list of files can
  115. # be limited with a pattern, and the pattern can be modified at any
  116. # time by the user.  Once a file is selected the browser goes away,
  117. # and calls the okCommand.  The cancel button calls cancelCommand, and
  118. # the help button calls helpCommand.
  119. #
  120. # Each place where a path name is shown there are two entry
  121. # boxes.  One for the path component, and the other for
  122. # the file component.  Two such places are shown, one for
  123. # the current directory and file pattern.  The other for
  124. # the last selected file.
  125. #
  126. # METHODS:
  127. #
  128. # new
  129. #
  130. #    Create a new fileBrowser instance.
  131. #
  132. # delete
  133. #
  134. #    Delete a fileBrowser.
  135. #
  136. # MakeWidgets
  137. #
  138. #    Create the visual components.
  139. #
  140. # setDirectory dirString
  141. #
  142. #    Change directory to the named one.  If dirString ends in ".."
  143. #    then go to the parent.
  144. #
  145. # changeDirectory
  146. #
  147. #    Changes directory to the one stored in $dirEntry.  Finds
  148. #    the files matching the pattern in $patEntry.  Displays
  149. #    all directories there in the directory list, and all matching
  150. #    files in the file list.
  151. #
  152. # setPattern newpat
  153. #
  154. #    Sets the text in $patEntry.
  155. #
  156. # setFile file
  157. #
  158. #    Sets the selected file to be the path from the current
  159. #    directory, and the file name passed in.
  160. #
  161. #
  162.  
  163. if ![interp exists FileBrowserClass] {
  164.  
  165. interp new FileBrowserClass
  166.  
  167. FileBrowserClass {
  168.  
  169. proc new {} {
  170.     global fileb_count
  171.     if ![info exists fileb_count] {set fileb_count 0}
  172.     incr fileb_count
  173.     set name "fileb$fileb_count"
  174.     interp new $name
  175.  
  176.     foreach cmd {    new delete MakeWidgets isModal setDirectory
  177.             changeDirectory
  178.             setPattern setFile rescan getDirectory
  179.             getPattern getFile doubleCommand okCommand
  180.             cancelCommand helpCommand 
  181.         } { $name -chainCommand FileBrowserClass $cmd }
  182.  
  183.     return $name
  184. }
  185.  
  186. proc delete {} { exit }
  187.  
  188. proc MakeWidgets top {
  189.     global topFrame patFrame lstFrame filFrame cmdFrame \
  190.         patLabel dirEntry slashLabel patEntry \
  191.         dirList dirScroll filList filScroll \
  192.         filLabel pathEntry filslashLabel filEntry \
  193.         okBtn canBtn travBtn hlpBtn
  194.  
  195.     global thisInterpretor
  196.  
  197.     set topFrame ${top}
  198.     set patFrame ${top}.pat
  199.     set lstFrame ${top}.lst
  200.     set filFrame ${top}.fil
  201.     set cmdFrame ${top}.cmd
  202.  
  203.     MainInterp frame $topFrame
  204.     MainInterp frame $patFrame
  205.     MainInterp frame $lstFrame
  206.     MainInterp frame $filFrame
  207.     MainInterp frame $cmdFrame
  208.     MainInterp pack append  $topFrame \
  209.                 $patFrame {top fillx} \
  210.                 $lstFrame {top fill expand} \
  211.                 $filFrame {top fillx} \
  212.                 $cmdFrame {top fillx}
  213.  
  214.     set patLabel   ${patFrame}.l
  215.     set dirEntry   ${patFrame}.dir
  216.     set slashLabel ${patFrame}.slash
  217.     set patEntry   ${patFrame}.pat
  218.  
  219.     MainInterp label $patLabel -text "Pattern"
  220.     MainInterp entry $dirEntry
  221.     MainInterp label $slashLabel -text "/"
  222.     MainInterp entry $patEntry
  223.     MainInterp pack append  $patFrame \
  224.                 $patLabel   {left fillx} \
  225.                 $dirEntry   {left fillx expand} \
  226.                 $slashLabel {left fillx} \
  227.                 $patEntry   {left fillx expand}
  228.  
  229.     set dirList    ${lstFrame}.dl
  230.     set dirScroll    ${lstFrame}.ds
  231.     set filList    ${lstFrame}.fl
  232.     set filScroll    ${lstFrame}.fs
  233.  
  234.     MainInterp scrollbar $dirScroll -command        "$dirList   yview"
  235.     MainInterp listbox   $dirList     -yscrollcommand "$dirScroll set" 
  236.     MainInterp scrollbar $filScroll -command        "$filList   yview"
  237.     MainInterp listbox   $filList     -yscrollcommand "$filScroll set" 
  238.  
  239.     MainInterp pack append  $lstFrame \
  240.                 $dirList   {left fill expand} \
  241.                 $dirScroll {left filly} \
  242.                 $filList   {left fill expand} \
  243.                 $filScroll {left filly}
  244.  
  245.     set filLabel ${filFrame}.l
  246.     set pathEntry ${filFrame}.p
  247.     set filslashLabel ${filFrame}.sl
  248.     set filEntry ${filFrame}.e
  249.  
  250.     MainInterp label $filLabel -text "File"
  251.     MainInterp entry $pathEntry
  252.     MainInterp label $filslashLabel -text "/"
  253.     MainInterp entry $filEntry
  254.     MainInterp pack append  $filFrame \
  255.                 $filLabel {left fillx} \
  256.                 $pathEntry {left fillx expand} \
  257.                 $filslashLabel {left fillx} \
  258.                 $filEntry {left fillx expand}
  259.  
  260.     set okBtn   ${cmdFrame}.ok
  261.     set canBtn  ${cmdFrame}.can
  262.     set travBtn ${cmdFrame}.trav
  263.     set hlpBtn  ${cmdFrame}.hlp
  264.  
  265.     MainInterp button $okBtn   -text "OK" \
  266.                    -command "$thisInterpretor okCommand"
  267.     MainInterp button $canBtn  -text "Cancel" \
  268.                    -command "$thisInterpretor cancelCommand"
  269.     MainInterp button $travBtn -text "Change Directory" \
  270.                    -command "$thisInterpretor changeDirectory"
  271.     MainInterp button $hlpBtn  -text "Help" \
  272.                    -command "$thisInterpretor helpCommand"
  273.  
  274.     MainInterp pack append  $cmdFrame \
  275.                 $okBtn   {left fillx expand} \
  276.                 $canBtn  {left fillx expand} \
  277.                 $travBtn {left fillx expand} \
  278.                 $hlpBtn  {left fillx expand}
  279.  
  280.  
  281.     bind $dirEntry <Return> "$thisInterpretor {
  282.             setDirectory \[$dirEntry get\]
  283.             changeDirectory
  284.         }
  285.         $travBtn flash
  286.         "
  287.     bind $patEntry <Return> \
  288.         "$thisInterpretor changeDirectory; $travBtn flash"
  289.  
  290.     bind $filEntry <Return> \
  291.         "$okBtn flash; update; $thisInterpretor okCommand"
  292.  
  293.  
  294.     # Override the unaddorned <1> bindings so that we get
  295.     # notified of any clicks.  This unfortunately means that
  296.     # if the default binding were to change we'd have to be
  297.     # aware of that and change it here.
  298.  
  299.     MainInterp bind $dirList <1> "
  300.         %W select from \[%W nearest %y\]
  301.         $thisInterpretor setDirectory \
  302.             \[%W get \[lindex \[%W curselection\] 0\]\]
  303.     "
  304.     MainInterp bind $dirList <Double-Button-1> "
  305.         %W select from \[%W nearest %y\]
  306.         $thisInterpretor setDirectory \
  307.             \[%W get \[lindex \[%W curselection\] 0\]\]
  308.         $thisInterpretor changeDirectory
  309.         $travBtn flash
  310.     "
  311.     MainInterp bind $filList <1> "
  312.         %W select from \[%W nearest %y\]
  313.         $thisInterpretor setFile \
  314.             \[%W get \[lindex \[%W curselection\] 0\]\]
  315.     "
  316.     MainInterp bind $filList <Double-Button-1> "
  317.         %W select from \[%W nearest %y\]
  318.         $thisInterpretor setFile \
  319.             \[%W get \[lindex \[%W curselection\] 0\]\]
  320.         $thisInterpretor okCommand
  321.     "
  322.  
  323.     return $topFrame
  324. }
  325.  
  326. # setDirectory - Set the given directory into $dirEntry.  If the last
  327. # component is ".." then strip it & its parent off.  If the length of
  328. # the whole thing is too short when stripping away the ".."  then assume
  329. # we've gone to/through the root and change to `/'.
  330. #
  331. # If the first component is "." then we expand that to be [pwd].
  332. #
  333. # BUG(let): If the string is something weird (like `a/..') then
  334. # the result is `/'.  
  335.  
  336.  
  337. proc setDirectory dir {
  338.     global dirEntry
  339.  catch {MainInterp $dirEntry delete 0 end}
  340.     set dl [split $dir "/"]
  341.     if {[lindex $dl 0] == "."} {
  342.         set s [split [pwd] "/"]
  343.         foreach d [lrange $dl 1 end] {lappend s $d}
  344.         set dl $s
  345.         set dlen [llength $dl]
  346.         set dir "/[join [lrange $dl 1 [expr $dlen-1]] /]"
  347.     } else {
  348.         set dlen [llength $dl]
  349.     }
  350.     if {[lindex $dl [expr $dlen-1]] == ".."} {
  351.         if {$dlen <= 3} {
  352.             set dir "/"
  353.         } else {
  354.             set dir "/[join [lrange $dl 1 [expr $dlen-3]] /]"
  355.         }
  356.     }
  357.     MainInterp $dirEntry insert end $dir
  358. }
  359.  
  360. proc changeDirectory {} {
  361.     global dirEntry patEntry dirList filList
  362.  
  363.     set newDir  [MainInterp $dirEntry get]
  364.     set pattern [MainInterp $patEntry get]
  365.  
  366.     if {[catch {set list [glob "${newDir}/*"]}] != 0} {
  367.             set list ""
  368.     }
  369.     if {$newDir == "/"} {
  370.         set dirs [list "/.."]
  371.     } else {
  372.         set dirs [list "$newDir/.."]
  373.     }
  374.     set files ""
  375.     foreach f $list {
  376.         if {[file isdirectory $f]}      {
  377.             lappend dirs  $f
  378.             continue
  379.         }
  380.         if {[string match $pattern $f]} {
  381.             set fl [split $f "/"]
  382.             # This should've been just [lindex $fl end]
  383.             set end [expr [llength $fl]-1]
  384.             lappend files [lindex $fl $end]
  385.         }
  386.     }
  387.  
  388.     catch         {MainInterp $dirList delete 0 end}
  389.     foreach d $dirs  {MainInterp $dirList insert end $d}
  390.     catch          {MainInterp $filList delete 0 end}
  391.     foreach f $files {MainInterp $filList insert end $f}
  392. }
  393.  
  394. proc setPattern newpat {
  395.     global patEntry
  396.  catch {MainInterp $patEntry delete 0 end}
  397.     MainInterp $patEntry insert end $newpat
  398.     changeDirectory
  399. }
  400.  
  401. proc setFile file {
  402.     global filEntry pathEntry dirEntry
  403.  catch {MainInterp $filEntry delete 0 end}
  404.     MainInterp $filEntry insert end $file
  405.  catch {MainInterp $pathEntry delete 0 end}
  406.     MainInterp $pathEntry insert end [MainInterp $dirEntry get]
  407. }
  408.  
  409. # proc getDirectory {} {
  410. # }
  411.  
  412. # proc getPattern {} {
  413. # }
  414.  
  415. # proc getFile {} {
  416. # }
  417.  
  418. proc okCommand {} {
  419. }
  420.  
  421. proc cancelCommand {} {
  422. }
  423.  
  424. proc helpCommand {} {
  425. }
  426.  
  427. }
  428. }
  429. # END: if ![interp exists FileBrowserClass]
  430.  
  431. ------------------------------> End `fileBrowserC.tcl'
  432.  
  433. <- David Herron <david@twg.com> (work) <david@davids.mmdf.com> (home)
  434. <-
  435. <- "That's our advantage at Microsoft; we set the standards and we can change them."
  436. <- Karen Hargrove of Microsoft quoted in the Feb 1993 Unix Review editorial.
  437.