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} {
17set w $i_w
18set ctext $i_text
1920
frame $w
21label $w.l -text [mc Find:]
22entry $w.ent -textvariable ${__this}::searchstring -background lightgreen
23button $w.bn -text [mc Next] -command [cb find_next]
24button $w.bp -text [mc Prev] -command [cb find_prev]
25checkbutton $w.cs -text [mc Case-Sensitive] \
26-variable ${__this}::casesensitive -command [cb _incrsearch]
27pack $w.l -side left
28pack $w.cs -side right
29pack $w.bp -side right
30pack $w.bn -side right
31pack $w.ent -side left -expand 1 -fill x
3233
eval grid conf $w -sticky we $args
34grid remove $w
3536
trace add variable searchstring write [cb _incrsearch_cb]
3738
bind $w <Destroy> [cb delete_this]
39return $this
40}
4142
method show {} {
43if {![visible $this]} {
44grid $w
45}
46focus -force $w.ent
47}
4849
method hide {} {
50if {[visible $this]} {
51focus $ctext
52grid remove $w
53}
54}
5556
method visible {} {
57return [winfo ismapped $w]
58}
5960
method editor {} {
61return $w.ent
62}
6364
method _get_new_anchor {} {
65# use start of selection if it is visible,
66# or the bounds of the visible area
67set top [$ctext index @0,0]
68set bottom [$ctext index @0,[winfo height $ctext]]
69set sel [$ctext tag ranges sel]
70if {$sel ne {}} {
71set spos [lindex $sel 0]
72if {[lindex $spos 0] >= [lindex $top 0] &&
73[lindex $spos 0] <= [lindex $bottom 0]} {
74return $spos
75}
76}
77if {$searchdirn eq "-forwards"} {
78return $top
79} else {
80return $bottom
81}
82}
8384
method _get_wrap_anchor {dir} {
85if {$dir eq "-forwards"} {
86return 1.0
87} else {
88return end
89}
90}
9192
method _do_search {start {mlenvar {}} {dir {}} {endbound {}}} {
93set cmd [list $ctext search]
94if {$mlenvar ne {}} {
95upvar $mlenvar mlen
96lappend cmd -count mlen
97}
98if {!$casesensitive} {
99lappend cmd -nocase
100}
101if {$dir eq {}} {
102set dir $searchdirn
103}
104lappend cmd $dir -- $searchstring
105if {$endbound ne {}} {
106set here [eval $cmd [list $start] [list $endbound]]
107} else {
108set here [eval $cmd [list $start]]
109if {$here eq {}} {
110set here [eval $cmd [_get_wrap_anchor $this $dir]]
111}
112}
113return $here
114}
115116
method _incrsearch_cb {name ix op} {
117after idle [cb _incrsearch]
118}
119120
method _incrsearch {} {
121$ctext tag remove found 1.0 end
122if {[catch {$ctext index anchor}]} {
123$ctext mark set anchor [_get_new_anchor $this]
124}
125if {$searchstring ne {}} {
126set here [_do_search $this anchor mlen]
127if {$here ne {}} {
128$ctext see $here
129$ctext tag remove sel 1.0 end
130$ctext tag add sel $here "$here + $mlen c"
131$w.ent configure -background lightgreen
132_set_marks $this 1
133} else {
134$w.ent configure -background lightpink
135}
136}
137}
138139
method find_prev {} {
140find_next $this -backwards
141}
142143
method find_next {{dir -forwards}} {
144focus $w.ent
145$w.ent icursor end
146set searchdirn $dir
147$ctext mark unset anchor
148if {$searchstring ne {}} {
149set start [_get_new_anchor $this]
150if {$dir eq "-forwards"} {
151set start "$start + 1c"
152}
153set match [_do_search $this $start mlen]
154$ctext tag remove sel 1.0 end
155if {$match ne {}} {
156$ctext see $match
157$ctext tag add sel $match "$match + $mlen c"
158}
159}
160}
161162
method _mark_range {first last} {
163set mend $first.0
164while {1} {
165set match [_do_search $this $mend mlen -forwards $last.end]
166if {$match eq {}} break
167set mend "$match + $mlen c"
168$ctext tag add found $match $mend
169}
170}
171172
method _set_marks {doall} {
173set topline [lindex [split [$ctext index @0,0] .] 0]
174set botline [lindex [split [$ctext index @0,[winfo height $ctext]] .] 0]
175if {$doall || $botline < $smarktop || $topline > $smarkbot} {
176# no overlap with previous
177_mark_range $this $topline $botline
178set smarktop $topline
179set smarkbot $botline
180} else {
181if {$topline < $smarktop} {
182_mark_range $this $topline [expr {$smarktop-1}]
183set smarktop $topline
184}
185if {$botline > $smarkbot} {
186_mark_range $this [expr {$smarkbot+1}] $botline
187set smarkbot $botline
188}
189}
190}
191192
method scrolled {} {
193if {$searchstring ne {}} {
194after idle [cb _set_marks 0]
195}
196}
197198
}