home *** CD-ROM | disk | FTP | other *** search
- # jfindpanel.tcl - `find' panel for text widgets
- #
- # Copyright 1992-1994 by Jay Sekora. All rights reserved, except
- # that this file may be freely redistributed in whole or in part
- # for non-profit, noncommercial use.
- #
- #
- # these procedures are required by (at least)
- # browser.tk
- # edit.tk
- # help.tk
- # more.tk
- # people.tk
- ######################################################################
-
- ### TO DO
- ### have find wrap around (if last time didn't match)
- ### regex search/replace
- ### `find' tags instead of selection (set of buttons)
- ### rewrite find routines to pass options instead of using globals
- ### e.g. j:find:find_pattern -case 0 -regex 1 \
- ### -backwards 0 -tag found {foo$} .main.t
-
- ######################################################################
- # j:find ?options? t - search-and-replace panel for text widget t
- # option is:
- # -replace (default 1)
- # if $replace, the replace box and buttons will be drawn, otherwise
- # it'll only be a search panel
- ######################################################################
-
- proc j:find {args} {
- j:parse_args {{replace 1}}
-
- set t $args ;# text widget to search in
- global j_find
- set j_find(widget) $t
- append j_find(searchfor) {} ;# make sure it's defined
- append j_find(replacewith) {} ;# make sure it's defined
-
- if {! [info exists j_find(backwards)]} {
- set j_find(backwards) 0
- }
- if {! [info exists j_find(case)]} {
- set j_find(case) 0
- }
- if {! [info exists j_find(regex)]} {
- set j_find(regex) 0
- }
-
- if [winfo exists .find] {
- wm withdraw .find
- wm deiconify .find ;# just try to make it visible
- focus .find.t.search.e ;# and focus on the search field
- return 0
- }
-
- toplevel .find
- wm title .find "Find Panel"
- frame .find.t
- j:variable_entry .find.t.search \
- -label "Search for:" -variable j_find(searchfor)
- j:variable_entry .find.t.replace \
- -label "Replace with:" -variable j_find(replacewith)
- frame .find.t.options
- label .find.t.options.filler -text {} -width 16 -anchor e
- checkbutton .find.t.options.backwards -relief flat -anchor w \
- -text {Backwards} -variable j_find(backwards)
- checkbutton .find.t.options.case -relief flat -anchor w \
- -text "Case\255sensitive" -variable j_find(case)
- checkbutton .find.t.options.regex -relief flat -anchor w \
- -text "Regex" -variable j_find(regex)
- set buttons(search) {
- search Search
- {
- if {[j:find:find_pattern $j_find(searchfor) $j_find(widget)] == 0} {
- j:alert -text "Not found."
- }
- }
- }
- set buttons(replace) {
- replace Replace
- {
- j:find:replace $j_find(searchfor) $j_find(replacewith) $j_find(widget)
- j:find:find_pattern $j_find(searchfor) $j_find(widget)
- }
- }
- set buttons(replace_all) {
- replace_all {Replace All}
- {
- j:find:replace_all $j_find(searchfor) $j_find(replacewith) \
- $j_find(widget)
- }
- }
- set buttons(cancel) {cancel Cancel {destroy .find}}
-
- if $replace {
- j:buttonbar .find.b -default search -buttons [list \
- $buttons(search) \
- $buttons(replace) \
- $buttons(replace_all) \
- $buttons(cancel) \
- ]
- } else {
- j:buttonbar .find.b -default search -buttons [list \
- $buttons(search) \
- $buttons(cancel) \
- ]
- }
-
- j:tab_ring .find.t.search.e .find.t.replace.e
-
- pack .find.t.options.filler -side left -fill both
- pack \
- .find.t.options.backwards \
- [j:filler .find.t.options] \
- .find.t.options.case \
- [j:filler .find.t.options] \
- .find.t.options.regex \
- -side left -fill y
-
- pack [j:filler .find.t] -side top
- pack .find.t.search -side top -expand yes -fill x
- if $replace {
- pack [j:filler .find.t] -side top
- pack .find.t.replace -side top -expand yes -fill x
- }
- pack .find.t.options -side top -expand yes -fill both
- pack .find.t -side top -fill both -padx 10 -pady 5
- pack [j:rule .find] -side top -fill x
- pack .find.b -side bottom -fill x
-
- # Meta-g (or Return, below) in either field searches:
- bind .find.t.search.e <Meta-g> \
- {.find.b.search invoke}
- bind .find.t.replace.e <Meta-g> \
- {.find.b.search invoke}
-
- j:default_button .find.b.search .find.t.search.e .find.t.replace.e
- j:cancel_button .find.b.cancel .find.t.search.e .find.t.replace.e
-
- focus .find.t.search.e
- }
-
- ######################################################################
- # j:find:again t - search again for same string
- ######################################################################
-
- proc j:find:again {t} {
- global j_find
- append j_find(searchfor) {} ;# make sure it's defined
- set j_find(widget) $t
-
- if {$j_find(searchfor) == {}} {
- j:find $t
- } else {
- if {[j:find:find_pattern $j_find(searchfor) $t] == 0} {
- j:alert -text "Not found."
- }
- }
- }
-
- ######################################################################
- # j:find:find_pattern string t - find and select string in text widget t
- ######################################################################
- # WARNING: since this takes a copy of the file, it could use a LOT
- # of memory!
- # should be rewritten to use a different mark than insert.
-
- proc j:find:find_pattern { string t } {
- global j_find ;# text widget to search in
- set j_find(widget) $t
- append j_find(searchfor) {} ;# make sure it's defined
- append j_find(replacewith) {} ;# make sure it's defined
-
- if {! [info exists j_find(backwards)]} {
- set j_find(backwards) 0
- }
- if {! [info exists j_find(case)]} {
- set j_find(case) 0
- }
- if {! [info exists j_find(regex)]} {
- set j_find(regex) 0
- }
-
- # don't bother looking for the null string:
- if {$string == {}} {
- return 0 ;# return 0 if null string
- }
-
- if $j_find(regex) {
- if $j_find(backwards) {
- j:alert -text "Backwards regex searches not yet implemented."
- return 0
- } else { ;# forwards:
- set text [$t get insert end]
- if $j_find(case) { ;# case-sensitive:
- set countfrom insert
- if [regexp -indices -- $string $text range] {
- set foundpos [lindex $range 0]
- set lastpos [expr [lindex $range 1] + 1]
- } else {
- set foundpos -1
- }
- } else { ;# not case-sensitive:
- set countfrom insert
- if [regexp -nocase -indices -- $string $text range] {
- set foundpos [lindex $range 0]
- set lastpos [expr [lindex $range 1] + 1]
- } else {
- set foundpos -1
- }
- }
- }
- } else {
- if $j_find(backwards) {
- set lastfirst last
- set textpart [$t get 0.0 {insert -1char}]
- set countfrom 0.0
- } else {
- set lastfirst first
- set textpart [$t get insert end]
- set countfrom insert
- }
-
- if {!$j_find(case)} {
- set string [string tolower $string]
- set textpart [string tolower $textpart]
- }
- set foundpos [string $lastfirst $string $textpart]
- # find length of selection:
- set lastpos [expr {$foundpos + [string length $string]}]
- }
- if {$foundpos == -1} then {
- return 0 ;# return 0 if not found
- }
- # deselect any already-selected text:
- catch {$t tag remove sel sel.first sel.last}
- $t tag add sel \
- "$countfrom + $foundpos chars" "$countfrom + $lastpos chars"
- # move insert just after the match (so we can continue from there)
- j:text:move $t "$countfrom + $lastpos chars"
- return 1 ;# return 1 if found
- }
-
- ######################################################################
- # j:find:replace from to t - replace selection in t with string
- ######################################################################
- # SHOULD CONFIRM THAT THE SELECTION IS IN t!
- # SHOULD CHECK THAT THE SELECTION MATCHES searchfor!
-
- proc j:find:replace { searchfor replacewith t } {
- global j_find
- if [j:no_selection] {
- return 0
- }
- if {! [info exists j_find(regex)]} {
- set j_find(regex) 0
- }
- if $j_find(regex) {
- return [j:find:replace_regexp $searchfor $replacewith $t]
- } else {
- j:text:replace $t sel.first sel.last $replacewith
- return 1
- }
- }
-
- ######################################################################
- # j:find:replace_regexp from to t - regexp (search-and-)replace
- # selection in t with $to
- ######################################################################
- # SHOULD CONFIRM THAT THE SELECTION IS IN T!
- # SHOULD CHECK THAT THE SELECTION MATCHES searchfor!
-
- proc j:find:replace_regexp { searchfor replacewith t } {
- if [j:no_selection] {
- return 0
- }
- if {! [info exists j_find(case)]} {
- set j_find(case) 0
- }
- set searchstring [$t get sel.first sel.last]
- if $j_find(case) {
- set regsub_result \
- [regsub -- $searchfor $searchstring $replacewith newstring]
- } else {
- set regsub_result \
- [regsub -nocase -- $searchfor $searchstring $replacewith newstring]
- }
- if $regsub_result {
- j:text:replace $t sel.first sel.last $replacewith
- return 1
- } else {
- return 0
- }
- }
-
- ######################################################################
- # j:find:replace_all from to t - change all from to to in widget t
- ### BUG: this and the other routines need to be rewritten not to use
- ### the "insert" mark
- ######################################################################
-
- proc j:find:replace_all {from to t} {
- j:text:move $t 0.0
- while {[j:find:find_pattern $from $t]} {
- j:find:replace $from $to $t
- }
- }
-