home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 3 / CD ACTUAL 3.iso / linux / incoming / jstools-.6v3 / jstools- / jstools-tk3.6v3.0 / lib / jfindpanel.tcl < prev    next >
Encoding:
Text File  |  1995-02-09  |  9.2 KB  |  310 lines

  1. # jfindpanel.tcl - `find' panel for text widgets
  2. # Copyright 1992-1994 by Jay Sekora.  All rights reserved, except 
  3. # that this file may be freely redistributed in whole or in part 
  4. # for non-profit, noncommercial use.
  5. # these procedures are required by (at least)
  6. #     browser.tk
  7. #     edit.tk
  8. #     help.tk
  9. #     more.tk
  10. #     people.tk
  11. ######################################################################
  12.  
  13. ### TO DO
  14. ###   have find wrap around (if last time didn't match)
  15. ###   regex search/replace
  16. ###   `find' tags instead of selection (set of buttons)
  17. ###   rewrite find routines to pass options instead of using globals
  18. ###     e.g. j:find:find_pattern -case 0 -regex 1 \
  19. ###            -backwards 0 -tag found {foo$} .main.t
  20.  
  21. ######################################################################
  22. # j:find ?options? t - search-and-replace panel for text widget t
  23. # option is:
  24. #   -replace (default 1)
  25. # if $replace, the replace box and buttons will be drawn, otherwise
  26. #   it'll only be a search panel
  27. ######################################################################
  28.  
  29. proc j:find {args} {
  30.   j:parse_args {{replace 1}}
  31.   
  32.   set t $args                ;# text widget to search in
  33.   global j_find
  34.   set j_find(widget) $t
  35.   append j_find(searchfor) {}        ;# make sure it's defined
  36.   append j_find(replacewith) {}        ;# make sure it's defined
  37.  
  38.   if {! [info exists j_find(backwards)]} {
  39.     set j_find(backwards) 0
  40.   }
  41.   if {! [info exists j_find(case)]} {
  42.     set j_find(case) 0
  43.   }
  44.   if {! [info exists j_find(regex)]} {
  45.     set j_find(regex) 0
  46.   }
  47.  
  48.   if [winfo exists .find] {
  49.     wm withdraw .find
  50.     wm deiconify .find            ;# just try to make it visible
  51.     focus .find.t.search.e        ;# and focus on the search field
  52.     return 0
  53.   }
  54.  
  55.   toplevel .find
  56.   wm title .find "Find Panel"
  57.   frame .find.t
  58.   j:variable_entry .find.t.search \
  59.     -label "Search for:" -variable j_find(searchfor)
  60.   j:variable_entry .find.t.replace \
  61.     -label "Replace with:" -variable j_find(replacewith)
  62.   frame .find.t.options
  63.   label .find.t.options.filler -text {} -width 16 -anchor e
  64.   checkbutton .find.t.options.backwards -relief flat -anchor w \
  65.     -text {Backwards} -variable j_find(backwards)
  66.   checkbutton .find.t.options.case -relief flat -anchor w \
  67.     -text "Case\255sensitive" -variable j_find(case)
  68.   checkbutton .find.t.options.regex -relief flat -anchor w \
  69.     -text "Regex" -variable j_find(regex)
  70.   set buttons(search) {
  71.     search Search
  72.       {
  73.         if {[j:find:find_pattern $j_find(searchfor) $j_find(widget)] == 0} {
  74.           j:alert -text "Not found."
  75.         }
  76.       }
  77.     }
  78.   set buttons(replace) {
  79.     replace Replace
  80.       {
  81.         j:find:replace $j_find(searchfor) $j_find(replacewith) $j_find(widget)
  82.         j:find:find_pattern $j_find(searchfor) $j_find(widget)
  83.       }
  84.     }
  85.   set buttons(replace_all) {
  86.     replace_all {Replace All}
  87.       {
  88.         j:find:replace_all $j_find(searchfor) $j_find(replacewith) \
  89.           $j_find(widget)
  90.       }
  91.     }
  92.   set buttons(cancel) {cancel Cancel {destroy .find}}
  93.  
  94.   if $replace {
  95.     j:buttonbar .find.b -default search -buttons [list \
  96.       $buttons(search) \
  97.       $buttons(replace) \
  98.       $buttons(replace_all) \
  99.       $buttons(cancel) \
  100.     ]
  101.   } else {
  102.     j:buttonbar .find.b -default search -buttons [list \
  103.       $buttons(search) \
  104.       $buttons(cancel) \
  105.     ]
  106.   }
  107.   
  108.   j:tab_ring .find.t.search.e .find.t.replace.e
  109.   
  110.   pack .find.t.options.filler -side left -fill both
  111.   pack \
  112.     .find.t.options.backwards \
  113.     [j:filler .find.t.options] \
  114.     .find.t.options.case \
  115.     [j:filler .find.t.options] \
  116.     .find.t.options.regex \
  117.     -side left -fill y
  118.   
  119.   pack [j:filler .find.t] -side top
  120.   pack .find.t.search -side top -expand yes -fill x
  121.   if $replace {
  122.     pack [j:filler .find.t] -side top
  123.     pack .find.t.replace -side top -expand yes -fill x
  124.   }
  125.   pack .find.t.options -side top -expand yes -fill both
  126.   pack .find.t -side top -fill both -padx 10 -pady 5
  127.   pack [j:rule .find] -side top -fill x
  128.   pack .find.b -side bottom -fill x
  129.  
  130.   # Meta-g (or Return, below) in either field searches:
  131.   bind .find.t.search.e <Meta-g> \
  132.     {.find.b.search invoke}
  133.   bind .find.t.replace.e <Meta-g> \
  134.     {.find.b.search invoke}
  135.  
  136.   j:default_button .find.b.search .find.t.search.e .find.t.replace.e
  137.   j:cancel_button .find.b.cancel .find.t.search.e .find.t.replace.e
  138.  
  139.   focus .find.t.search.e
  140. }
  141.  
  142. ######################################################################
  143. # j:find:again t - search again for same string
  144. ######################################################################
  145.  
  146. proc j:find:again {t} {
  147.   global j_find
  148.   append j_find(searchfor) {}            ;# make sure it's defined
  149.   set j_find(widget) $t
  150.  
  151.   if {$j_find(searchfor) == {}} {
  152.     j:find $t
  153.   } else {
  154.     if {[j:find:find_pattern $j_find(searchfor) $t] == 0} {
  155.       j:alert -text "Not found."
  156.     }
  157.   }
  158. }
  159.  
  160. ######################################################################
  161. # j:find:find_pattern string t - find and select string in text widget t
  162. ######################################################################
  163. # WARNING: since this takes a copy of the file, it could use a LOT
  164. # of memory!
  165. # should be rewritten to use a different mark than insert.
  166.  
  167. proc j:find:find_pattern { string t } {
  168.   global j_find                ;# text widget to search in
  169.   set j_find(widget) $t
  170.   append j_find(searchfor) {}        ;# make sure it's defined
  171.   append j_find(replacewith) {}        ;# make sure it's defined
  172.  
  173.   if {! [info exists j_find(backwards)]} {
  174.     set j_find(backwards) 0
  175.   }
  176.   if {! [info exists j_find(case)]} {
  177.     set j_find(case) 0
  178.   }
  179.   if {! [info exists j_find(regex)]} {
  180.     set j_find(regex) 0
  181.   }
  182.  
  183.   # don't bother looking for the null string:
  184.   if {$string == {}} {
  185.     return 0                ;# return 0 if null string
  186.   }
  187.   
  188.   if $j_find(regex) {
  189.     if $j_find(backwards) {
  190.       j:alert -text "Backwards regex searches not yet implemented."
  191.       return 0
  192.     } else {                ;# forwards:
  193.       set text [$t get insert end]
  194.       if $j_find(case) {        ;# case-sensitive:
  195.         set countfrom insert
  196.         if [regexp -indices -- $string $text range] {
  197.           set foundpos [lindex $range 0]
  198.           set lastpos [expr [lindex $range 1] + 1]
  199.         } else {
  200.           set foundpos -1
  201.         }
  202.       } else {                ;# not case-sensitive:
  203.         set countfrom insert
  204.         if [regexp -nocase -indices -- $string $text range] {
  205.           set foundpos [lindex $range 0]
  206.           set lastpos [expr [lindex $range 1] + 1]
  207.         } else {
  208.           set foundpos -1
  209.         }
  210.       }
  211.     }
  212.   } else {
  213.     if $j_find(backwards) {
  214.       set lastfirst last
  215.       set textpart [$t get 0.0 {insert -1char}]
  216.       set countfrom 0.0
  217.     } else {
  218.       set lastfirst first
  219.       set textpart [$t get insert end]
  220.       set countfrom insert
  221.     }
  222.   
  223.     if {!$j_find(case)} {
  224.       set string [string tolower $string]
  225.       set textpart [string tolower $textpart]
  226.     }
  227.     set foundpos [string $lastfirst $string $textpart]
  228.     # find length of selection:
  229.     set lastpos [expr {$foundpos + [string length $string]}]
  230.   }
  231.   if {$foundpos == -1} then {
  232.     return 0                ;# return 0 if not found
  233.   }
  234.   # deselect any already-selected text:
  235.   catch {$t tag remove sel sel.first sel.last}
  236.   $t tag add sel \
  237.     "$countfrom + $foundpos chars" "$countfrom + $lastpos chars"
  238.   # move insert just after the match (so we can continue from there)
  239.   j:text:move $t "$countfrom + $lastpos chars"
  240.   return 1                ;# return 1 if found
  241. }
  242.  
  243. ######################################################################
  244. # j:find:replace from to t - replace selection in t with string
  245. ######################################################################
  246. # SHOULD CONFIRM THAT THE SELECTION IS IN t!
  247. # SHOULD CHECK THAT THE SELECTION MATCHES searchfor!
  248.  
  249. proc j:find:replace { searchfor replacewith t } {
  250.   global j_find
  251.   if [j:no_selection] {
  252.     return 0
  253.   }
  254.   if {! [info exists j_find(regex)]} {
  255.     set j_find(regex) 0
  256.   }
  257.   if $j_find(regex) {
  258.     return [j:find:replace_regexp $searchfor $replacewith $t]
  259.   } else {
  260.     j:text:replace $t sel.first sel.last $replacewith
  261.     return 1
  262.   }
  263. }
  264.  
  265. ######################################################################
  266. # j:find:replace_regexp from to t - regexp (search-and-)replace 
  267. #   selection in t with $to
  268. ######################################################################
  269. # SHOULD CONFIRM THAT THE SELECTION IS IN T!
  270. # SHOULD CHECK THAT THE SELECTION MATCHES searchfor!
  271.  
  272. proc j:find:replace_regexp { searchfor replacewith t } {
  273.   if [j:no_selection] {
  274.     return 0
  275.   }
  276.   if {! [info exists j_find(case)]} {
  277.     set j_find(case) 0
  278.   }
  279.   set searchstring [$t get sel.first sel.last]
  280.   if $j_find(case) {
  281.     set regsub_result \
  282.       [regsub -- $searchfor $searchstring $replacewith newstring]
  283.   } else {
  284.     set regsub_result \
  285.       [regsub -nocase -- $searchfor $searchstring $replacewith newstring]
  286.   }
  287.   if $regsub_result {
  288.     j:text:replace $t sel.first sel.last $replacewith
  289.     return 1
  290.   } else {
  291.     return 0
  292.   }
  293. }
  294.  
  295. ######################################################################
  296. # j:find:replace_all from to t - change all from to to in widget t
  297. ### BUG: this and the other routines need to be rewritten not to use
  298. ### the "insert" mark
  299. ######################################################################
  300.  
  301. proc j:find:replace_all {from to t} {
  302.   j:text:move $t 0.0
  303.   while {[j:find:find_pattern $from $t]} {
  304.     j:find:replace $from $to $t
  305.   }
  306. }
  307.