]>
Commit | Line | Data |
---|---|---|
f10d5b06 AG |
1 | # incremental search panel |
2 | # based on code from gitk, Copyright (C) Paul Mackerras | |
3 | ||
4 | class searchbar { | |
5 | ||
6 | field w | |
7 | field ctext | |
8 | ||
9 | field searchstring {} | |
e9144d55 BW |
10 | field regexpsearch |
11 | field default_regexpsearch | |
0a0243d7 BW |
12 | field casesensitive |
13 | field default_casesensitive | |
8eaf24b9 | 14 | field smartcase |
f10d5b06 AG |
15 | field searchdirn -forwards |
16 | ||
1159971b BW |
17 | field history |
18 | field history_index | |
19 | ||
f10d5b06 AG |
20 | field smarktop |
21 | field smarkbot | |
22 | ||
23 | constructor new {i_w i_text args} { | |
c80d7be5 | 24 | global use_ttk NS |
f10d5b06 AG |
25 | set w $i_w |
26 | set ctext $i_text | |
27 | ||
e9144d55 | 28 | set default_regexpsearch [is_config_true gui.search.regexp] |
f9ace9e6 BW |
29 | switch -- [get_config gui.search.case] { |
30 | no { | |
0a0243d7 | 31 | set default_casesensitive 0 |
f9ace9e6 BW |
32 | set smartcase 0 |
33 | } | |
34 | smart { | |
35 | set default_casesensitive 0 | |
36 | set smartcase 1 | |
37 | } | |
38 | yes - | |
39 | default { | |
0a0243d7 | 40 | set default_casesensitive 1 |
f9ace9e6 BW |
41 | set smartcase 0 |
42 | } | |
0a0243d7 BW |
43 | } |
44 | ||
1159971b BW |
45 | set history [list] |
46 | ||
c80d7be5 PT |
47 | ${NS}::frame $w |
48 | ${NS}::label $w.l -text [mc Find:] | |
35927672 | 49 | tentry $w.ent -textvariable ${__this}::searchstring -background lightgreen |
c80d7be5 PT |
50 | ${NS}::button $w.bn -text [mc Next] -command [cb find_next] |
51 | ${NS}::button $w.bp -text [mc Prev] -command [cb find_prev] | |
e9144d55 BW |
52 | ${NS}::checkbutton $w.re -text [mc RegExp] \ |
53 | -variable ${__this}::regexpsearch -command [cb _incrsearch] | |
54 | ${NS}::checkbutton $w.cs -text [mc Case] \ | |
f10d5b06 | 55 | -variable ${__this}::casesensitive -command [cb _incrsearch] |
f10d5b06 AG |
56 | pack $w.l -side left |
57 | pack $w.cs -side right | |
e9144d55 | 58 | pack $w.re -side right |
f10d5b06 AG |
59 | pack $w.bp -side right |
60 | pack $w.bn -side right | |
61 | pack $w.ent -side left -expand 1 -fill x | |
62 | ||
63 | eval grid conf $w -sticky we $args | |
64 | grid remove $w | |
65 | ||
66 | trace add variable searchstring write [cb _incrsearch_cb] | |
af9a4625 DF |
67 | bind $w.ent <Return> [cb find_next] |
68 | bind $w.ent <Shift-Return> [cb find_prev] | |
1159971b BW |
69 | bind $w.ent <Key-Up> [cb _prev_search] |
70 | bind $w.ent <Key-Down> [cb _next_search] | |
f10d5b06 | 71 | |
94193073 | 72 | bind $w <Destroy> [list delete_this $this] |
f10d5b06 AG |
73 | return $this |
74 | } | |
75 | ||
76 | method show {} { | |
b28ebab2 | 77 | if {![visible $this]} { |
f10d5b06 | 78 | grid $w |
1159971b | 79 | $w.ent delete 0 end |
e9144d55 | 80 | set regexpsearch $default_regexpsearch |
0a0243d7 | 81 | set casesensitive $default_casesensitive |
1159971b | 82 | set history_index [llength $history] |
f10d5b06 AG |
83 | } |
84 | focus -force $w.ent | |
85 | } | |
86 | ||
87 | method hide {} { | |
b28ebab2 | 88 | if {[visible $this]} { |
f10d5b06 AG |
89 | focus $ctext |
90 | grid remove $w | |
1159971b | 91 | _save_search $this |
f10d5b06 AG |
92 | } |
93 | } | |
94 | ||
b28ebab2 AG |
95 | method visible {} { |
96 | return [winfo ismapped $w] | |
97 | } | |
98 | ||
99 | method editor {} { | |
100 | return $w.ent | |
101 | } | |
102 | ||
f10d5b06 AG |
103 | method _get_new_anchor {} { |
104 | # use start of selection if it is visible, | |
105 | # or the bounds of the visible area | |
106 | set top [$ctext index @0,0] | |
107 | set bottom [$ctext index @0,[winfo height $ctext]] | |
108 | set sel [$ctext tag ranges sel] | |
109 | if {$sel ne {}} { | |
110 | set spos [lindex $sel 0] | |
111 | if {[lindex $spos 0] >= [lindex $top 0] && | |
112 | [lindex $spos 0] <= [lindex $bottom 0]} { | |
113 | return $spos | |
114 | } | |
115 | } | |
116 | if {$searchdirn eq "-forwards"} { | |
117 | return $top | |
118 | } else { | |
119 | return $bottom | |
120 | } | |
121 | } | |
122 | ||
123 | method _get_wrap_anchor {dir} { | |
124 | if {$dir eq "-forwards"} { | |
125 | return 1.0 | |
126 | } else { | |
127 | return end | |
128 | } | |
129 | } | |
130 | ||
131 | method _do_search {start {mlenvar {}} {dir {}} {endbound {}}} { | |
132 | set cmd [list $ctext search] | |
133 | if {$mlenvar ne {}} { | |
134 | upvar $mlenvar mlen | |
135 | lappend cmd -count mlen | |
136 | } | |
e9144d55 BW |
137 | if {$regexpsearch} { |
138 | lappend cmd -regexp | |
139 | } | |
f10d5b06 AG |
140 | if {!$casesensitive} { |
141 | lappend cmd -nocase | |
142 | } | |
143 | if {$dir eq {}} { | |
144 | set dir $searchdirn | |
145 | } | |
146 | lappend cmd $dir -- $searchstring | |
b66f4f7a PT |
147 | if {[catch { |
148 | if {$endbound ne {}} { | |
149 | set here [eval $cmd [list $start] [list $endbound]] | |
150 | } else { | |
151 | set here [eval $cmd [list $start]] | |
152 | if {$here eq {}} { | |
153 | set here [eval $cmd [_get_wrap_anchor $this $dir]] | |
154 | } | |
f10d5b06 | 155 | } |
b66f4f7a | 156 | } err]} { set here {} } |
f10d5b06 AG |
157 | return $here |
158 | } | |
159 | ||
160 | method _incrsearch_cb {name ix op} { | |
161 | after idle [cb _incrsearch] | |
162 | } | |
163 | ||
164 | method _incrsearch {} { | |
165 | $ctext tag remove found 1.0 end | |
166 | if {[catch {$ctext index anchor}]} { | |
167 | $ctext mark set anchor [_get_new_anchor $this] | |
168 | } | |
f9ace9e6 BW |
169 | if {$searchstring ne {}} { |
170 | if {$smartcase && [regexp {[[:upper:]]} $searchstring]} { | |
8eaf24b9 PT |
171 | set casesensitive 1 |
172 | } | |
f10d5b06 AG |
173 | set here [_do_search $this anchor mlen] |
174 | if {$here ne {}} { | |
175 | $ctext see $here | |
176 | $ctext tag remove sel 1.0 end | |
177 | $ctext tag add sel $here "$here + $mlen c" | |
35927672 PT |
178 | #$w.ent configure -background lightgreen |
179 | $w.ent state !pressed | |
f10d5b06 AG |
180 | _set_marks $this 1 |
181 | } else { | |
35927672 PT |
182 | #$w.ent configure -background lightpink |
183 | $w.ent state pressed | |
f10d5b06 | 184 | } |
f9ace9e6 BW |
185 | } elseif {$smartcase} { |
186 | # clearing the field resets the smart case detection | |
187 | set casesensitive 0 | |
f10d5b06 AG |
188 | } |
189 | } | |
190 | ||
1159971b BW |
191 | method _save_search {} { |
192 | if {$searchstring eq {}} { | |
193 | return | |
194 | } | |
195 | if {[llength $history] > 0} { | |
196 | foreach {s_regexp s_case s_expr} [lindex $history end] break | |
197 | } else { | |
198 | set s_regexp $regexpsearch | |
199 | set s_case $casesensitive | |
200 | set s_expr "" | |
201 | } | |
202 | if {$searchstring eq $s_expr} { | |
203 | # update modes | |
204 | set history [lreplace $history end end \ | |
205 | [list $regexpsearch $casesensitive $searchstring]] | |
206 | } else { | |
207 | lappend history [list $regexpsearch $casesensitive $searchstring] | |
208 | } | |
209 | set history_index [llength $history] | |
210 | } | |
211 | ||
212 | method _prev_search {} { | |
213 | if {$history_index > 0} { | |
214 | incr history_index -1 | |
215 | foreach {s_regexp s_case s_expr} [lindex $history $history_index] break | |
216 | $w.ent delete 0 end | |
217 | $w.ent insert 0 $s_expr | |
218 | set regexpsearch $s_regexp | |
219 | set casesensitive $s_case | |
220 | } | |
221 | } | |
222 | ||
223 | method _next_search {} { | |
224 | if {$history_index < [llength $history]} { | |
225 | incr history_index | |
226 | } | |
227 | if {$history_index < [llength $history]} { | |
228 | foreach {s_regexp s_case s_expr} [lindex $history $history_index] break | |
229 | } else { | |
230 | set s_regexp $default_regexpsearch | |
231 | set s_case $default_casesensitive | |
232 | set s_expr "" | |
233 | } | |
234 | $w.ent delete 0 end | |
235 | $w.ent insert 0 $s_expr | |
236 | set regexpsearch $s_regexp | |
237 | set casesensitive $s_case | |
238 | } | |
239 | ||
f10d5b06 AG |
240 | method find_prev {} { |
241 | find_next $this -backwards | |
242 | } | |
243 | ||
244 | method find_next {{dir -forwards}} { | |
245 | focus $w.ent | |
246 | $w.ent icursor end | |
247 | set searchdirn $dir | |
248 | $ctext mark unset anchor | |
249 | if {$searchstring ne {}} { | |
1159971b | 250 | _save_search $this |
f10d5b06 AG |
251 | set start [_get_new_anchor $this] |
252 | if {$dir eq "-forwards"} { | |
253 | set start "$start + 1c" | |
254 | } | |
255 | set match [_do_search $this $start mlen] | |
256 | $ctext tag remove sel 1.0 end | |
257 | if {$match ne {}} { | |
258 | $ctext see $match | |
259 | $ctext tag add sel $match "$match + $mlen c" | |
260 | } | |
261 | } | |
262 | } | |
263 | ||
264 | method _mark_range {first last} { | |
265 | set mend $first.0 | |
266 | while {1} { | |
267 | set match [_do_search $this $mend mlen -forwards $last.end] | |
268 | if {$match eq {}} break | |
269 | set mend "$match + $mlen c" | |
270 | $ctext tag add found $match $mend | |
271 | } | |
272 | } | |
273 | ||
274 | method _set_marks {doall} { | |
275 | set topline [lindex [split [$ctext index @0,0] .] 0] | |
276 | set botline [lindex [split [$ctext index @0,[winfo height $ctext]] .] 0] | |
277 | if {$doall || $botline < $smarktop || $topline > $smarkbot} { | |
278 | # no overlap with previous | |
279 | _mark_range $this $topline $botline | |
280 | set smarktop $topline | |
281 | set smarkbot $botline | |
282 | } else { | |
283 | if {$topline < $smarktop} { | |
284 | _mark_range $this $topline [expr {$smarktop-1}] | |
285 | set smarktop $topline | |
286 | } | |
287 | if {$botline > $smarkbot} { | |
288 | _mark_range $this [expr {$smarkbot+1}] $botline | |
289 | set smarkbot $botline | |
290 | } | |
291 | } | |
292 | } | |
293 | ||
294 | method scrolled {} { | |
295 | if {$searchstring ne {}} { | |
296 | after idle [cb _set_marks 0] | |
297 | } | |
298 | } | |
299 | ||
af9a4625 | 300 | } |