Spaces:
Running
Running
# incremental search panel | |
# based on code from gitk, Copyright (C) Paul Mackerras | |
class searchbar { | |
field w | |
field ctext | |
field searchstring {} | |
field regexpsearch | |
field default_regexpsearch | |
field casesensitive | |
field default_casesensitive | |
field smartcase | |
field searchdirn -forwards | |
field history | |
field history_index | |
field smarktop | |
field smarkbot | |
constructor new {i_w i_text args} { | |
global use_ttk NS | |
set w $i_w | |
set ctext $i_text | |
set default_regexpsearch [is_config_true gui.search.regexp] | |
switch -- [get_config gui.search.case] { | |
no { | |
set default_casesensitive 0 | |
set smartcase 0 | |
} | |
smart { | |
set default_casesensitive 0 | |
set smartcase 1 | |
} | |
yes - | |
default { | |
set default_casesensitive 1 | |
set smartcase 0 | |
} | |
} | |
set history [list] | |
${NS}::frame $w | |
${NS}::label $w.l -text [mc Find:] | |
tentry $w.ent -textvariable ${__this}::searchstring -background lightgreen | |
${NS}::button $w.bn -text [mc Next] -command [cb find_next] | |
${NS}::button $w.bp -text [mc Prev] -command [cb find_prev] | |
${NS}::checkbutton $w.re -text [mc RegExp] \ | |
-variable ${__this}::regexpsearch -command [cb _incrsearch] | |
${NS}::checkbutton $w.cs -text [mc Case] \ | |
-variable ${__this}::casesensitive -command [cb _incrsearch] | |
pack $w.l -side left | |
pack $w.cs -side right | |
pack $w.re -side right | |
pack $w.bp -side right | |
pack $w.bn -side right | |
pack $w.ent -side left -expand 1 -fill x | |
eval grid conf $w -sticky we $args | |
grid remove $w | |
trace add variable searchstring write [cb _incrsearch_cb] | |
bind $w.ent <Return> [cb find_next] | |
bind $w.ent <Shift-Return> [cb find_prev] | |
bind $w.ent <Key-Up> [cb _prev_search] | |
bind $w.ent <Key-Down> [cb _next_search] | |
bind $w <Destroy> [list delete_this $this] | |
return $this | |
} | |
method show {} { | |
if {![visible $this]} { | |
grid $w | |
$w.ent delete 0 end | |
set regexpsearch $default_regexpsearch | |
set casesensitive $default_casesensitive | |
set history_index [llength $history] | |
} | |
focus -force $w.ent | |
} | |
method hide {} { | |
if {[visible $this]} { | |
focus $ctext | |
grid remove $w | |
_save_search $this | |
} | |
} | |
method visible {} { | |
return [winfo ismapped $w] | |
} | |
method editor {} { | |
return $w.ent | |
} | |
method _get_new_anchor {} { | |
# use start of selection if it is visible, | |
# or the bounds of the visible area | |
set top [$ctext index @0,0] | |
set bottom [$ctext index @0,[winfo height $ctext]] | |
set sel [$ctext tag ranges sel] | |
if {$sel ne {}} { | |
set spos [lindex $sel 0] | |
if {[lindex $spos 0] >= [lindex $top 0] && | |
[lindex $spos 0] <= [lindex $bottom 0]} { | |
return $spos | |
} | |
} | |
if {$searchdirn eq "-forwards"} { | |
return $top | |
} else { | |
return $bottom | |
} | |
} | |
method _get_wrap_anchor {dir} { | |
if {$dir eq "-forwards"} { | |
return 1.0 | |
} else { | |
return end | |
} | |
} | |
method _do_search {start {mlenvar {}} {dir {}} {endbound {}}} { | |
set cmd [list $ctext search] | |
if {$mlenvar ne {}} { | |
upvar $mlenvar mlen | |
lappend cmd -count mlen | |
} | |
if {$regexpsearch} { | |
lappend cmd -regexp | |
} | |
if {!$casesensitive} { | |
lappend cmd -nocase | |
} | |
if {$dir eq {}} { | |
set dir $searchdirn | |
} | |
lappend cmd $dir -- $searchstring | |
if {[catch { | |
if {$endbound ne {}} { | |
set here [eval $cmd [list $start] [list $endbound]] | |
} else { | |
set here [eval $cmd [list $start]] | |
if {$here eq {}} { | |
set here [eval $cmd [_get_wrap_anchor $this $dir]] | |
} | |
} | |
} err]} { set here {} } | |
return $here | |
} | |
method _incrsearch_cb {name ix op} { | |
after idle [cb _incrsearch] | |
} | |
method _incrsearch {} { | |
$ctext tag remove found 1.0 end | |
if {[catch {$ctext index anchor}]} { | |
$ctext mark set anchor [_get_new_anchor $this] | |
} | |
if {$searchstring ne {}} { | |
if {$smartcase && [regexp {[[:upper:]]} $searchstring]} { | |
set casesensitive 1 | |
} | |
set here [_do_search $this anchor mlen] | |
if {$here ne {}} { | |
$ctext see $here | |
$ctext tag remove sel 1.0 end | |
$ctext tag add sel $here "$here + $mlen c" | |
#$w.ent configure -background lightgreen | |
$w.ent state !pressed | |
_set_marks $this 1 | |
} else { | |
#$w.ent configure -background lightpink | |
$w.ent state pressed | |
} | |
} elseif {$smartcase} { | |
# clearing the field resets the smart case detection | |
set casesensitive 0 | |
} | |
} | |
method _save_search {} { | |
if {$searchstring eq {}} { | |
return | |
} | |
if {[llength $history] > 0} { | |
foreach {s_regexp s_case s_expr} [lindex $history end] break | |
} else { | |
set s_regexp $regexpsearch | |
set s_case $casesensitive | |
set s_expr "" | |
} | |
if {$searchstring eq $s_expr} { | |
# update modes | |
set history [lreplace $history end end \ | |
[list $regexpsearch $casesensitive $searchstring]] | |
} else { | |
lappend history [list $regexpsearch $casesensitive $searchstring] | |
} | |
set history_index [llength $history] | |
} | |
method _prev_search {} { | |
if {$history_index > 0} { | |
incr history_index -1 | |
foreach {s_regexp s_case s_expr} [lindex $history $history_index] break | |
$w.ent delete 0 end | |
$w.ent insert 0 $s_expr | |
set regexpsearch $s_regexp | |
set casesensitive $s_case | |
} | |
} | |
method _next_search {} { | |
if {$history_index < [llength $history]} { | |
incr history_index | |
} | |
if {$history_index < [llength $history]} { | |
foreach {s_regexp s_case s_expr} [lindex $history $history_index] break | |
} else { | |
set s_regexp $default_regexpsearch | |
set s_case $default_casesensitive | |
set s_expr "" | |
} | |
$w.ent delete 0 end | |
$w.ent insert 0 $s_expr | |
set regexpsearch $s_regexp | |
set casesensitive $s_case | |
} | |
method find_prev {} { | |
find_next $this -backwards | |
} | |
method find_next {{dir -forwards}} { | |
focus $w.ent | |
$w.ent icursor end | |
set searchdirn $dir | |
$ctext mark unset anchor | |
if {$searchstring ne {}} { | |
_save_search $this | |
set start [_get_new_anchor $this] | |
if {$dir eq "-forwards"} { | |
set start "$start + 1c" | |
} | |
set match [_do_search $this $start mlen] | |
$ctext tag remove sel 1.0 end | |
if {$match ne {}} { | |
$ctext see $match | |
$ctext tag add sel $match "$match + $mlen c" | |
} | |
} | |
} | |
method _mark_range {first last} { | |
set mend $first.0 | |
while {1} { | |
set match [_do_search $this $mend mlen -forwards $last.end] | |
if {$match eq {}} break | |
set mend "$match + $mlen c" | |
$ctext tag add found $match $mend | |
} | |
} | |
method _set_marks {doall} { | |
set topline [lindex [split [$ctext index @0,0] .] 0] | |
set botline [lindex [split [$ctext index @0,[winfo height $ctext]] .] 0] | |
if {$doall || $botline < $smarktop || $topline > $smarkbot} { | |
# no overlap with previous | |
_mark_range $this $topline $botline | |
set smarktop $topline | |
set smarkbot $botline | |
} else { | |
if {$topline < $smarktop} { | |
_mark_range $this $topline [expr {$smarktop-1}] | |
set smarktop $topline | |
} | |
if {$botline > $smarkbot} { | |
_mark_range $this [expr {$smarkbot+1}] $botline | |
set smarkbot $botline | |
} | |
} | |
} | |
method scrolled {} { | |
if {$searchstring ne {}} { | |
after idle [cb _set_marks 0] | |
} | |
} | |
} | |