1# incremental search panel
2# based on code from gitk, Copyright (C) Paul Mackerras
34
class searchbar {
56
field w
7field ctext
89
field searchstring {}
10field casesensitive 1
11field searchdirn -forwards
1213
field smarktop
14field smarkbot
1516
constructor new {i_w i_text args} {
17global use_ttk NS
18set w $i_w
19set ctext $i_text
2021
${NS}::frame $w
22${NS}::label $w.l -text [mc Find:]
23entry $w.ent -textvariable ${__this}::searchstring -background lightgreen
24${NS}::button $w.bn -text [mc Next] -command [cb find_next]
25${NS}::button $w.bp -text [mc Prev] -command [cb find_prev]
26${NS}::checkbutton $w.cs -text [mc Case-Sensitive] \
27-variable ${__this}::casesensitive -command [cb _incrsearch]
28pack $w.l -side left
29pack $w.cs -side right
30pack $w.bp -side right
31pack $w.bn -side right
32pack $w.ent -side left -expand 1 -fill x
3334
eval grid conf $w -sticky we $args
35grid remove $w
3637
trace add variable searchstring write [cb _incrsearch_cb]
38bind $w.ent <Return> [cb find_next]
39bind $w.ent <Shift-Return> [cb find_prev]
4041
bind $w <Destroy> [list delete_this $this]
42return $this
43}
4445
method show {} {
46if {![visible $this]} {
47grid $w
48}
49focus -force $w.ent
50}
5152
method hide {} {
53if {[visible $this]} {
54focus $ctext
55grid remove $w
56}
57}
5859
method visible {} {
60return [winfo ismapped $w]
61}
6263
method editor {} {
64return $w.ent
65}
6667
method _get_new_anchor {} {
68# use start of selection if it is visible,
69# or the bounds of the visible area
70set top [$ctext index @0,0]
71set bottom [$ctext index @0,[winfo height $ctext]]
72set sel [$ctext tag ranges sel]
73if {$sel ne {}} {
74set spos [lindex $sel 0]
75if {[lindex $spos 0] >= [lindex $top 0] &&
76[lindex $spos 0] <= [lindex $bottom 0]} {
77return $spos
78}
79}
80if {$searchdirn eq "-forwards"} {
81return $top
82} else {
83return $bottom
84}
85}
8687
method _get_wrap_anchor {dir} {
88if {$dir eq "-forwards"} {
89return 1.0
90} else {
91return end
92}
93}
9495
method _do_search {start {mlenvar {}} {dir {}} {endbound {}}} {
96set cmd [list $ctext search]
97if {$mlenvar ne {}} {
98upvar $mlenvar mlen
99lappend cmd -count mlen
100}
101if {!$casesensitive} {
102lappend cmd -nocase
103}
104if {$dir eq {}} {
105set dir $searchdirn
106}
107lappend cmd $dir -- $searchstring
108if {$endbound ne {}} {
109set here [eval $cmd [list $start] [list $endbound]]
110} else {
111set here [eval $cmd [list $start]]
112if {$here eq {}} {
113set here [eval $cmd [_get_wrap_anchor $this $dir]]
114}
115}
116return $here
117}
118119
method _incrsearch_cb {name ix op} {
120after idle [cb _incrsearch]
121}
122123
method _incrsearch {} {
124$ctext tag remove found 1.0 end
125if {[catch {$ctext index anchor}]} {
126$ctext mark set anchor [_get_new_anchor $this]
127}
128if {$searchstring ne {}} {
129set here [_do_search $this anchor mlen]
130if {$here ne {}} {
131$ctext see $here
132$ctext tag remove sel 1.0 end
133$ctext tag add sel $here "$here + $mlen c"
134$w.ent configure -background lightgreen
135_set_marks $this 1
136} else {
137$w.ent configure -background lightpink
138}
139}
140}
141142
method find_prev {} {
143find_next $this -backwards
144}
145146
method find_next {{dir -forwards}} {
147focus $w.ent
148$w.ent icursor end
149set searchdirn $dir
150$ctext mark unset anchor
151if {$searchstring ne {}} {
152set start [_get_new_anchor $this]
153if {$dir eq "-forwards"} {
154set start "$start + 1c"
155}
156set match [_do_search $this $start mlen]
157$ctext tag remove sel 1.0 end
158if {$match ne {}} {
159$ctext see $match
160$ctext tag add sel $match "$match + $mlen c"
161}
162}
163}
164165
method _mark_range {first last} {
166set mend $first.0
167while {1} {
168set match [_do_search $this $mend mlen -forwards $last.end]
169if {$match eq {}} break
170set mend "$match + $mlen c"
171$ctext tag add found $match $mend
172}
173}
174175
method _set_marks {doall} {
176set topline [lindex [split [$ctext index @0,0] .] 0]
177set botline [lindex [split [$ctext index @0,[winfo height $ctext]] .] 0]
178if {$doall || $botline < $smarktop || $topline > $smarkbot} {
179# no overlap with previous
180_mark_range $this $topline $botline
181set smarktop $topline
182set smarkbot $botline
183} else {
184if {$topline < $smarktop} {
185_mark_range $this $topline [expr {$smarktop-1}]
186set smarktop $topline
187}
188if {$botline > $smarkbot} {
189_mark_range $this [expr {$smarkbot+1}] $botline
190set smarkbot $botline
191}
192}
193}
194195
method scrolled {} {
196if {$searchstring ne {}} {
197after idle [cb _set_marks 0]
198}
199}
200201
}