Alexander Gavrilov | f10d5b0 | 2008-10-03 11:36:53 +0400 | [diff] [blame] | 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 {} |
Bert Wesarg | e9144d5 | 2011-10-14 10:14:51 +0200 | [diff] [blame] | 10 | field regexpsearch |
| 11 | field default_regexpsearch |
Bert Wesarg | 0a0243d | 2011-10-14 10:14:50 +0200 | [diff] [blame] | 12 | field casesensitive |
| 13 | field default_casesensitive |
Pat Thoyts | 8eaf24b | 2011-10-19 13:29:52 +0100 | [diff] [blame] | 14 | field smartcase |
Alexander Gavrilov | f10d5b0 | 2008-10-03 11:36:53 +0400 | [diff] [blame] | 15 | field searchdirn -forwards |
| 16 | |
Bert Wesarg | 1159971 | 2011-10-14 10:14:52 +0200 | [diff] [blame] | 17 | field history |
| 18 | field history_index |
| 19 | |
Alexander Gavrilov | f10d5b0 | 2008-10-03 11:36:53 +0400 | [diff] [blame] | 20 | field smarktop |
| 21 | field smarkbot |
| 22 | |
| 23 | constructor new {i_w i_text args} { |
Pat Thoyts | c80d7be | 2010-01-26 00:05:31 +0000 | [diff] [blame] | 24 | global use_ttk NS |
Alexander Gavrilov | f10d5b0 | 2008-10-03 11:36:53 +0400 | [diff] [blame] | 25 | set w $i_w |
| 26 | set ctext $i_text |
| 27 | |
Bert Wesarg | e9144d5 | 2011-10-14 10:14:51 +0200 | [diff] [blame] | 28 | set default_regexpsearch [is_config_true gui.search.regexp] |
Bert Wesarg | f9ace9e | 2011-10-20 21:27:27 +0200 | [diff] [blame] | 29 | switch -- [get_config gui.search.case] { |
| 30 | no { |
Bert Wesarg | 0a0243d | 2011-10-14 10:14:50 +0200 | [diff] [blame] | 31 | set default_casesensitive 0 |
Bert Wesarg | f9ace9e | 2011-10-20 21:27:27 +0200 | [diff] [blame] | 32 | set smartcase 0 |
| 33 | } |
| 34 | smart { |
| 35 | set default_casesensitive 0 |
| 36 | set smartcase 1 |
| 37 | } |
| 38 | yes - |
| 39 | default { |
Bert Wesarg | 0a0243d | 2011-10-14 10:14:50 +0200 | [diff] [blame] | 40 | set default_casesensitive 1 |
Bert Wesarg | f9ace9e | 2011-10-20 21:27:27 +0200 | [diff] [blame] | 41 | set smartcase 0 |
| 42 | } |
Bert Wesarg | 0a0243d | 2011-10-14 10:14:50 +0200 | [diff] [blame] | 43 | } |
| 44 | |
Bert Wesarg | 1159971 | 2011-10-14 10:14:52 +0200 | [diff] [blame] | 45 | set history [list] |
| 46 | |
Pat Thoyts | c80d7be | 2010-01-26 00:05:31 +0000 | [diff] [blame] | 47 | ${NS}::frame $w |
| 48 | ${NS}::label $w.l -text [mc Find:] |
Pat Thoyts | 3592767 | 2011-10-19 12:44:39 +0100 | [diff] [blame] | 49 | tentry $w.ent -textvariable ${__this}::searchstring -background lightgreen |
Pat Thoyts | c80d7be | 2010-01-26 00:05:31 +0000 | [diff] [blame] | 50 | ${NS}::button $w.bn -text [mc Next] -command [cb find_next] |
| 51 | ${NS}::button $w.bp -text [mc Prev] -command [cb find_prev] |
Bert Wesarg | e9144d5 | 2011-10-14 10:14:51 +0200 | [diff] [blame] | 52 | ${NS}::checkbutton $w.re -text [mc RegExp] \ |
| 53 | -variable ${__this}::regexpsearch -command [cb _incrsearch] |
| 54 | ${NS}::checkbutton $w.cs -text [mc Case] \ |
Alexander Gavrilov | f10d5b0 | 2008-10-03 11:36:53 +0400 | [diff] [blame] | 55 | -variable ${__this}::casesensitive -command [cb _incrsearch] |
Alexander Gavrilov | f10d5b0 | 2008-10-03 11:36:53 +0400 | [diff] [blame] | 56 | pack $w.l -side left |
| 57 | pack $w.cs -side right |
Bert Wesarg | e9144d5 | 2011-10-14 10:14:51 +0200 | [diff] [blame] | 58 | pack $w.re -side right |
Alexander Gavrilov | f10d5b0 | 2008-10-03 11:36:53 +0400 | [diff] [blame] | 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] |
David Fries | af9a462 | 2011-07-16 11:59:45 -0500 | [diff] [blame] | 67 | bind $w.ent <Return> [cb find_next] |
| 68 | bind $w.ent <Shift-Return> [cb find_prev] |
Bert Wesarg | 1159971 | 2011-10-14 10:14:52 +0200 | [diff] [blame] | 69 | bind $w.ent <Key-Up> [cb _prev_search] |
| 70 | bind $w.ent <Key-Down> [cb _next_search] |
Alexander Gavrilov | f10d5b0 | 2008-10-03 11:36:53 +0400 | [diff] [blame] | 71 | |
Alexander Gavrilov | 9419307 | 2008-11-13 21:52:52 +0300 | [diff] [blame] | 72 | bind $w <Destroy> [list delete_this $this] |
Alexander Gavrilov | f10d5b0 | 2008-10-03 11:36:53 +0400 | [diff] [blame] | 73 | return $this |
| 74 | } |
| 75 | |
| 76 | method show {} { |
Alexander Gavrilov | b28ebab | 2008-11-09 18:36:50 +0300 | [diff] [blame] | 77 | if {![visible $this]} { |
Alexander Gavrilov | f10d5b0 | 2008-10-03 11:36:53 +0400 | [diff] [blame] | 78 | grid $w |
Bert Wesarg | 1159971 | 2011-10-14 10:14:52 +0200 | [diff] [blame] | 79 | $w.ent delete 0 end |
Bert Wesarg | e9144d5 | 2011-10-14 10:14:51 +0200 | [diff] [blame] | 80 | set regexpsearch $default_regexpsearch |
Bert Wesarg | 0a0243d | 2011-10-14 10:14:50 +0200 | [diff] [blame] | 81 | set casesensitive $default_casesensitive |
Bert Wesarg | 1159971 | 2011-10-14 10:14:52 +0200 | [diff] [blame] | 82 | set history_index [llength $history] |
Alexander Gavrilov | f10d5b0 | 2008-10-03 11:36:53 +0400 | [diff] [blame] | 83 | } |
| 84 | focus -force $w.ent |
| 85 | } |
| 86 | |
| 87 | method hide {} { |
Alexander Gavrilov | b28ebab | 2008-11-09 18:36:50 +0300 | [diff] [blame] | 88 | if {[visible $this]} { |
Alexander Gavrilov | f10d5b0 | 2008-10-03 11:36:53 +0400 | [diff] [blame] | 89 | focus $ctext |
| 90 | grid remove $w |
Bert Wesarg | 1159971 | 2011-10-14 10:14:52 +0200 | [diff] [blame] | 91 | _save_search $this |
Alexander Gavrilov | f10d5b0 | 2008-10-03 11:36:53 +0400 | [diff] [blame] | 92 | } |
| 93 | } |
| 94 | |
Alexander Gavrilov | b28ebab | 2008-11-09 18:36:50 +0300 | [diff] [blame] | 95 | method visible {} { |
| 96 | return [winfo ismapped $w] |
| 97 | } |
| 98 | |
| 99 | method editor {} { |
| 100 | return $w.ent |
| 101 | } |
| 102 | |
Alexander Gavrilov | f10d5b0 | 2008-10-03 11:36:53 +0400 | [diff] [blame] | 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 | } |
Bert Wesarg | e9144d5 | 2011-10-14 10:14:51 +0200 | [diff] [blame] | 137 | if {$regexpsearch} { |
| 138 | lappend cmd -regexp |
| 139 | } |
Alexander Gavrilov | f10d5b0 | 2008-10-03 11:36:53 +0400 | [diff] [blame] | 140 | if {!$casesensitive} { |
| 141 | lappend cmd -nocase |
| 142 | } |
| 143 | if {$dir eq {}} { |
| 144 | set dir $searchdirn |
| 145 | } |
| 146 | lappend cmd $dir -- $searchstring |
Pat Thoyts | b66f4f7 | 2011-10-19 13:22:33 +0100 | [diff] [blame] | 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 | } |
Alexander Gavrilov | f10d5b0 | 2008-10-03 11:36:53 +0400 | [diff] [blame] | 155 | } |
Pat Thoyts | b66f4f7 | 2011-10-19 13:22:33 +0100 | [diff] [blame] | 156 | } err]} { set here {} } |
Alexander Gavrilov | f10d5b0 | 2008-10-03 11:36:53 +0400 | [diff] [blame] | 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 | } |
Bert Wesarg | f9ace9e | 2011-10-20 21:27:27 +0200 | [diff] [blame] | 169 | if {$searchstring ne {}} { |
| 170 | if {$smartcase && [regexp {[[:upper:]]} $searchstring]} { |
Pat Thoyts | 8eaf24b | 2011-10-19 13:29:52 +0100 | [diff] [blame] | 171 | set casesensitive 1 |
| 172 | } |
Alexander Gavrilov | f10d5b0 | 2008-10-03 11:36:53 +0400 | [diff] [blame] | 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" |
Pat Thoyts | 3592767 | 2011-10-19 12:44:39 +0100 | [diff] [blame] | 178 | #$w.ent configure -background lightgreen |
| 179 | $w.ent state !pressed |
Alexander Gavrilov | f10d5b0 | 2008-10-03 11:36:53 +0400 | [diff] [blame] | 180 | _set_marks $this 1 |
| 181 | } else { |
Pat Thoyts | 3592767 | 2011-10-19 12:44:39 +0100 | [diff] [blame] | 182 | #$w.ent configure -background lightpink |
| 183 | $w.ent state pressed |
Alexander Gavrilov | f10d5b0 | 2008-10-03 11:36:53 +0400 | [diff] [blame] | 184 | } |
Bert Wesarg | f9ace9e | 2011-10-20 21:27:27 +0200 | [diff] [blame] | 185 | } elseif {$smartcase} { |
| 186 | # clearing the field resets the smart case detection |
| 187 | set casesensitive 0 |
Alexander Gavrilov | f10d5b0 | 2008-10-03 11:36:53 +0400 | [diff] [blame] | 188 | } |
| 189 | } |
| 190 | |
Bert Wesarg | 1159971 | 2011-10-14 10:14:52 +0200 | [diff] [blame] | 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 | |
Alexander Gavrilov | f10d5b0 | 2008-10-03 11:36:53 +0400 | [diff] [blame] | 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 {}} { |
Bert Wesarg | 1159971 | 2011-10-14 10:14:52 +0200 | [diff] [blame] | 250 | _save_search $this |
Alexander Gavrilov | f10d5b0 | 2008-10-03 11:36:53 +0400 | [diff] [blame] | 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 | |
David Fries | af9a462 | 2011-07-16 11:59:45 -0500 | [diff] [blame] | 300 | } |