+ toplevel $w
+ wm title $w "About gitk"
+ message $w.m -text {
+Gitk - a commit viewer for git
+
+Copyright © 2005-2006 Paul Mackerras
+
+Use and redistribute under the terms of the GNU General Public License} \
+ -justify center -aspect 400
+ pack $w.m -side top -fill x -padx 20 -pady 20
+ button $w.ok -text Close -command "destroy $w"
+ pack $w.ok -side bottom
+}
+
+proc keys {} {
+ set w .keys
+ if {[winfo exists $w]} {
+ raise $w
+ return
+ }
+ toplevel $w
+ wm title $w "Gitk key bindings"
+ message $w.m -text {
+Gitk key bindings:
+
+<Ctrl-Q> Quit
+<Home> Move to first commit
+<End> Move to last commit
+<Up>, p, i Move up one commit
+<Down>, n, k Move down one commit
+<Left>, z, j Go back in history list
+<Right>, x, l Go forward in history list
+<PageUp> Move up one page in commit list
+<PageDown> Move down one page in commit list
+<Ctrl-Home> Scroll to top of commit list
+<Ctrl-End> Scroll to bottom of commit list
+<Ctrl-Up> Scroll commit list up one line
+<Ctrl-Down> Scroll commit list down one line
+<Ctrl-PageUp> Scroll commit list up one page
+<Ctrl-PageDown> Scroll commit list down one page
+<Shift-Up> Move to previous highlighted line
+<Shift-Down> Move to next highlighted line
+<Delete>, b Scroll diff view up one page
+<Backspace> Scroll diff view up one page
+<Space> Scroll diff view down one page
+u Scroll diff view up 18 lines
+d Scroll diff view down 18 lines
+<Ctrl-F> Find
+<Ctrl-G> Move to next find hit
+<Return> Move to next find hit
+/ Move to next find hit, or redo find
+? Move to previous find hit
+f Scroll diff view to next file
+<Ctrl-S> Search for next hit in diff view
+<Ctrl-R> Search for previous hit in diff view
+<Ctrl-KP+> Increase font size
+<Ctrl-plus> Increase font size
+<Ctrl-KP-> Decrease font size
+<Ctrl-minus> Decrease font size
+} \
+ -justify left -bg white -border 2 -relief sunken
+ pack $w.m -side top -fill both
+ button $w.ok -text Close -command "destroy $w"
+ pack $w.ok -side bottom
+}
+
+# Procedures for manipulating the file list window at the
+# bottom right of the overall window.
+
+proc treeview {w l openlevs} {
+ global treecontents treediropen treeheight treeparent treeindex
+
+ set ix 0
+ set treeindex() 0
+ set lev 0
+ set prefix {}
+ set prefixend -1
+ set prefendstack {}
+ set htstack {}
+ set ht 0
+ set treecontents() {}
+ $w conf -state normal
+ foreach f $l {
+ while {[string range $f 0 $prefixend] ne $prefix} {
+ if {$lev <= $openlevs} {
+ $w mark set e:$treeindex($prefix) "end -1c"
+ $w mark gravity e:$treeindex($prefix) left
+ }
+ set treeheight($prefix) $ht
+ incr ht [lindex $htstack end]
+ set htstack [lreplace $htstack end end]
+ set prefixend [lindex $prefendstack end]
+ set prefendstack [lreplace $prefendstack end end]
+ set prefix [string range $prefix 0 $prefixend]
+ incr lev -1
+ }
+ set tail [string range $f [expr {$prefixend+1}] end]
+ while {[set slash [string first "/" $tail]] >= 0} {
+ lappend htstack $ht
+ set ht 0
+ lappend prefendstack $prefixend
+ incr prefixend [expr {$slash + 1}]
+ set d [string range $tail 0 $slash]
+ lappend treecontents($prefix) $d
+ set oldprefix $prefix
+ append prefix $d
+ set treecontents($prefix) {}
+ set treeindex($prefix) [incr ix]
+ set treeparent($prefix) $oldprefix
+ set tail [string range $tail [expr {$slash+1}] end]
+ if {$lev <= $openlevs} {
+ set ht 1
+ set treediropen($prefix) [expr {$lev < $openlevs}]
+ set bm [expr {$lev == $openlevs? "tri-rt": "tri-dn"}]
+ $w mark set d:$ix "end -1c"
+ $w mark gravity d:$ix left
+ set str "\n"
+ for {set i 0} {$i < $lev} {incr i} {append str "\t"}
+ $w insert end $str
+ $w image create end -align center -image $bm -padx 1 \
+ -name a:$ix
+ $w insert end $d [highlight_tag $prefix]
+ $w mark set s:$ix "end -1c"
+ $w mark gravity s:$ix left
+ }
+ incr lev
+ }
+ if {$tail ne {}} {
+ if {$lev <= $openlevs} {
+ incr ht
+ set str "\n"
+ for {set i 0} {$i < $lev} {incr i} {append str "\t"}
+ $w insert end $str
+ $w insert end $tail [highlight_tag $f]
+ }
+ lappend treecontents($prefix) $tail
+ }
+ }
+ while {$htstack ne {}} {
+ set treeheight($prefix) $ht
+ incr ht [lindex $htstack end]
+ set htstack [lreplace $htstack end end]
+ }
+ $w conf -state disabled
+}
+
+proc linetoelt {l} {
+ global treeheight treecontents
+
+ set y 2
+ set prefix {}
+ while {1} {
+ foreach e $treecontents($prefix) {
+ if {$y == $l} {
+ return "$prefix$e"
+ }
+ set n 1
+ if {[string index $e end] eq "/"} {
+ set n $treeheight($prefix$e)
+ if {$y + $n > $l} {
+ append prefix $e
+ incr y
+ break
+ }
+ }
+ incr y $n
+ }
+ }
+}
+
+proc highlight_tree {y prefix} {
+ global treeheight treecontents cflist
+
+ foreach e $treecontents($prefix) {
+ set path $prefix$e
+ if {[highlight_tag $path] ne {}} {
+ $cflist tag add bold $y.0 "$y.0 lineend"
+ }
+ incr y
+ if {[string index $e end] eq "/" && $treeheight($path) > 1} {
+ set y [highlight_tree $y $path]
+ }
+ }
+ return $y
+}
+
+proc treeclosedir {w dir} {
+ global treediropen treeheight treeparent treeindex
+
+ set ix $treeindex($dir)
+ $w conf -state normal
+ $w delete s:$ix e:$ix
+ set treediropen($dir) 0
+ $w image configure a:$ix -image tri-rt
+ $w conf -state disabled
+ set n [expr {1 - $treeheight($dir)}]
+ while {$dir ne {}} {
+ incr treeheight($dir) $n
+ set dir $treeparent($dir)
+ }
+}
+
+proc treeopendir {w dir} {
+ global treediropen treeheight treeparent treecontents treeindex
+
+ set ix $treeindex($dir)
+ $w conf -state normal
+ $w image configure a:$ix -image tri-dn
+ $w mark set e:$ix s:$ix
+ $w mark gravity e:$ix right
+ set lev 0
+ set str "\n"
+ set n [llength $treecontents($dir)]
+ for {set x $dir} {$x ne {}} {set x $treeparent($x)} {
+ incr lev
+ append str "\t"
+ incr treeheight($x) $n
+ }
+ foreach e $treecontents($dir) {
+ set de $dir$e
+ if {[string index $e end] eq "/"} {
+ set iy $treeindex($de)
+ $w mark set d:$iy e:$ix
+ $w mark gravity d:$iy left
+ $w insert e:$ix $str
+ set treediropen($de) 0
+ $w image create e:$ix -align center -image tri-rt -padx 1 \
+ -name a:$iy
+ $w insert e:$ix $e [highlight_tag $de]
+ $w mark set s:$iy e:$ix
+ $w mark gravity s:$iy left
+ set treeheight($de) 1
+ } else {
+ $w insert e:$ix $str
+ $w insert e:$ix $e [highlight_tag $de]
+ }
+ }
+ $w mark gravity e:$ix left
+ $w conf -state disabled
+ set treediropen($dir) 1
+ set top [lindex [split [$w index @0,0] .] 0]
+ set ht [$w cget -height]
+ set l [lindex [split [$w index s:$ix] .] 0]
+ if {$l < $top} {
+ $w yview $l.0
+ } elseif {$l + $n + 1 > $top + $ht} {
+ set top [expr {$l + $n + 2 - $ht}]
+ if {$l < $top} {
+ set top $l
+ }
+ $w yview $top.0
+ }
+}
+
+proc treeclick {w x y} {
+ global treediropen cmitmode ctext cflist cflist_top
+
+ if {$cmitmode ne "tree"} return
+ if {![info exists cflist_top]} return
+ set l [lindex [split [$w index "@$x,$y"] "."] 0]
+ $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
+ $cflist tag add highlight $l.0 "$l.0 lineend"
+ set cflist_top $l
+ if {$l == 1} {
+ $ctext yview 1.0
+ return
+ }
+ set e [linetoelt $l]
+ if {[string index $e end] ne "/"} {
+ showfile $e
+ } elseif {$treediropen($e)} {
+ treeclosedir $w $e
+ } else {
+ treeopendir $w $e
+ }
+}
+
+proc setfilelist {id} {
+ global treefilelist cflist
+
+ treeview $cflist $treefilelist($id) 0
+}
+
+image create bitmap tri-rt -background black -foreground blue -data {
+ #define tri-rt_width 13
+ #define tri-rt_height 13
+ static unsigned char tri-rt_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x30, 0x00, 0x70, 0x00, 0xf0, 0x00,
+ 0xf0, 0x01, 0xf0, 0x00, 0x70, 0x00, 0x30, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00};
+} -maskdata {
+ #define tri-rt-mask_width 13
+ #define tri-rt-mask_height 13
+ static unsigned char tri-rt-mask_bits[] = {
+ 0x08, 0x00, 0x18, 0x00, 0x38, 0x00, 0x78, 0x00, 0xf8, 0x00, 0xf8, 0x01,
+ 0xf8, 0x03, 0xf8, 0x01, 0xf8, 0x00, 0x78, 0x00, 0x38, 0x00, 0x18, 0x00,
+ 0x08, 0x00};
+}
+image create bitmap tri-dn -background black -foreground blue -data {
+ #define tri-dn_width 13
+ #define tri-dn_height 13
+ static unsigned char tri-dn_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0xf8, 0x03,
+ 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00};
+} -maskdata {
+ #define tri-dn-mask_width 13
+ #define tri-dn-mask_height 13
+ static unsigned char tri-dn-mask_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x1f, 0xfe, 0x0f, 0xfc, 0x07,
+ 0xf8, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00};
+}
+
+proc init_flist {first} {
+ global cflist cflist_top selectedline difffilestart
+
+ $cflist conf -state normal
+ $cflist delete 0.0 end
+ if {$first ne {}} {
+ $cflist insert end $first
+ set cflist_top 1
+ $cflist tag add highlight 1.0 "1.0 lineend"
+ } else {
+ catch {unset cflist_top}
+ }
+ $cflist conf -state disabled
+ set difffilestart {}
+}
+
+proc highlight_tag {f} {
+ global highlight_paths
+
+ foreach p $highlight_paths {
+ if {[string match $p $f]} {
+ return "bold"
+ }
+ }
+ return {}
+}
+
+proc highlight_filelist {} {
+ global cmitmode cflist
+
+ $cflist conf -state normal
+ if {$cmitmode ne "tree"} {
+ set end [lindex [split [$cflist index end] .] 0]
+ for {set l 2} {$l < $end} {incr l} {
+ set line [$cflist get $l.0 "$l.0 lineend"]
+ if {[highlight_tag $line] ne {}} {
+ $cflist tag add bold $l.0 "$l.0 lineend"
+ }
+ }
+ } else {
+ highlight_tree 2 {}
+ }
+ $cflist conf -state disabled
+}
+
+proc unhighlight_filelist {} {
+ global cflist
+
+ $cflist conf -state normal
+ $cflist tag remove bold 1.0 end
+ $cflist conf -state disabled
+}
+
+proc add_flist {fl} {
+ global cflist
+
+ $cflist conf -state normal
+ foreach f $fl {
+ $cflist insert end "\n"
+ $cflist insert end $f [highlight_tag $f]
+ }
+ $cflist conf -state disabled
+}
+
+proc sel_flist {w x y} {
+ global ctext difffilestart cflist cflist_top cmitmode
+
+ if {$cmitmode eq "tree"} return
+ if {![info exists cflist_top]} return
+ set l [lindex [split [$w index "@$x,$y"] "."] 0]
+ $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
+ $cflist tag add highlight $l.0 "$l.0 lineend"
+ set cflist_top $l
+ if {$l == 1} {
+ $ctext yview 1.0
+ } else {
+ catch {$ctext yview [lindex $difffilestart [expr {$l - 2}]]}
+ }
+}
+
+# Functions for adding and removing shell-type quoting
+
+proc shellquote {str} {
+ if {![string match "*\['\"\\ \t]*" $str]} {
+ return $str
+ }
+ if {![string match "*\['\"\\]*" $str]} {
+ return "\"$str\""
+ }
+ if {![string match "*'*" $str]} {
+ return "'$str'"
+ }
+ return "\"[string map {\" \\\" \\ \\\\} $str]\""
+}
+
+proc shellarglist {l} {
+ set str {}
+ foreach a $l {
+ if {$str ne {}} {
+ append str " "
+ }
+ append str [shellquote $a]
+ }
+ return $str
+}
+
+proc shelldequote {str} {
+ set ret {}
+ set used -1
+ while {1} {
+ incr used
+ if {![regexp -start $used -indices "\['\"\\\\ \t]" $str first]} {
+ append ret [string range $str $used end]
+ set used [string length $str]
+ break
+ }
+ set first [lindex $first 0]
+ set ch [string index $str $first]
+ if {$first > $used} {
+ append ret [string range $str $used [expr {$first - 1}]]
+ set used $first
+ }
+ if {$ch eq " " || $ch eq "\t"} break
+ incr used
+ if {$ch eq "'"} {
+ set first [string first "'" $str $used]
+ if {$first < 0} {
+ error "unmatched single-quote"
+ }
+ append ret [string range $str $used [expr {$first - 1}]]
+ set used $first
+ continue
+ }
+ if {$ch eq "\\"} {
+ if {$used >= [string length $str]} {
+ error "trailing backslash"
+ }
+ append ret [string index $str $used]
+ continue
+ }
+ # here ch == "\""
+ while {1} {
+ if {![regexp -start $used -indices "\[\"\\\\]" $str first]} {
+ error "unmatched double-quote"
+ }
+ set first [lindex $first 0]
+ set ch [string index $str $first]
+ if {$first > $used} {
+ append ret [string range $str $used [expr {$first - 1}]]
+ set used $first
+ }
+ if {$ch eq "\""} break
+ incr used
+ append ret [string index $str $used]
+ incr used
+ }
+ }
+ return [list $used $ret]
+}
+
+proc shellsplit {str} {
+ set l {}
+ while {1} {
+ set str [string trimleft $str]
+ if {$str eq {}} break
+ set dq [shelldequote $str]
+ set n [lindex $dq 0]
+ set word [lindex $dq 1]
+ set str [string range $str $n end]
+ lappend l $word
+ }
+ return $l
+}
+
+# Code to implement multiple views
+
+proc newview {ishighlight} {
+ global nextviewnum newviewname newviewperm uifont newishighlight
+ global newviewargs revtreeargs
+
+ set newishighlight $ishighlight
+ set top .gitkview
+ if {[winfo exists $top]} {
+ raise $top
+ return
+ }
+ set newviewname($nextviewnum) "View $nextviewnum"
+ set newviewperm($nextviewnum) 0
+ set newviewargs($nextviewnum) [shellarglist $revtreeargs]
+ vieweditor $top $nextviewnum "Gitk view definition"
+}
+
+proc editview {} {
+ global curview
+ global viewname viewperm newviewname newviewperm
+ global viewargs newviewargs
+
+ set top .gitkvedit-$curview
+ if {[winfo exists $top]} {
+ raise $top
+ return
+ }
+ set newviewname($curview) $viewname($curview)
+ set newviewperm($curview) $viewperm($curview)
+ set newviewargs($curview) [shellarglist $viewargs($curview)]
+ vieweditor $top $curview "Gitk: edit view $viewname($curview)"
+}
+
+proc vieweditor {top n title} {
+ global newviewname newviewperm viewfiles
+ global uifont
+
+ toplevel $top
+ wm title $top $title
+ label $top.nl -text "Name" -font $uifont
+ entry $top.name -width 20 -textvariable newviewname($n)
+ grid $top.nl $top.name -sticky w -pady 5
+ checkbutton $top.perm -text "Remember this view" -variable newviewperm($n)
+ grid $top.perm - -pady 5 -sticky w
+ message $top.al -aspect 1000 -font $uifont \
+ -text "Commits to include (arguments to git rev-list):"
+ grid $top.al - -sticky w -pady 5
+ entry $top.args -width 50 -textvariable newviewargs($n) \
+ -background white
+ grid $top.args - -sticky ew -padx 5
+ message $top.l -aspect 1000 -font $uifont \
+ -text "Enter files and directories to include, one per line:"
+ grid $top.l - -sticky w
+ text $top.t -width 40 -height 10 -background white
+ if {[info exists viewfiles($n)]} {
+ foreach f $viewfiles($n) {
+ $top.t insert end $f
+ $top.t insert end "\n"
+ }
+ $top.t delete {end - 1c} end
+ $top.t mark set insert 0.0
+ }
+ grid $top.t - -sticky ew -padx 5
+ frame $top.buts
+ button $top.buts.ok -text "OK" -command [list newviewok $top $n]
+ button $top.buts.can -text "Cancel" -command [list destroy $top]
+ grid $top.buts.ok $top.buts.can
+ grid columnconfigure $top.buts 0 -weight 1 -uniform a
+ grid columnconfigure $top.buts 1 -weight 1 -uniform a
+ grid $top.buts - -pady 10 -sticky ew
+ focus $top.t
+}
+
+proc doviewmenu {m first cmd op argv} {
+ set nmenu [$m index end]
+ for {set i $first} {$i <= $nmenu} {incr i} {
+ if {[$m entrycget $i -command] eq $cmd} {
+ eval $m $op $i $argv
+ break
+ }
+ }
+}
+
+proc allviewmenus {n op args} {
+ global viewhlmenu
+
+ doviewmenu .bar.view 7 [list showview $n] $op $args
+ doviewmenu $viewhlmenu 1 [list addvhighlight $n] $op $args
+}
+
+proc newviewok {top n} {
+ global nextviewnum newviewperm newviewname newishighlight
+ global viewname viewfiles viewperm selectedview curview
+ global viewargs newviewargs viewhlmenu
+
+ if {[catch {
+ set newargs [shellsplit $newviewargs($n)]
+ } err]} {
+ error_popup "Error in commit selection arguments: $err"
+ wm raise $top
+ focus $top
+ return
+ }
+ set files {}
+ foreach f [split [$top.t get 0.0 end] "\n"] {
+ set ft [string trim $f]
+ if {$ft ne {}} {
+ lappend files $ft
+ }
+ }
+ if {![info exists viewfiles($n)]} {
+ # creating a new view
+ incr nextviewnum
+ set viewname($n) $newviewname($n)
+ set viewperm($n) $newviewperm($n)
+ set viewfiles($n) $files
+ set viewargs($n) $newargs
+ addviewmenu $n
+ if {!$newishighlight} {
+ after idle showview $n
+ } else {
+ after idle addvhighlight $n
+ }
+ } else {
+ # editing an existing view
+ set viewperm($n) $newviewperm($n)
+ if {$newviewname($n) ne $viewname($n)} {
+ set viewname($n) $newviewname($n)
+ doviewmenu .bar.view 7 [list showview $n] \
+ entryconf [list -label $viewname($n)]
+ doviewmenu $viewhlmenu 1 [list addvhighlight $n] \
+ entryconf [list -label $viewname($n) -value $viewname($n)]
+ }
+ if {$files ne $viewfiles($n) || $newargs ne $viewargs($n)} {
+ set viewfiles($n) $files
+ set viewargs($n) $newargs
+ if {$curview == $n} {
+ after idle updatecommits
+ }
+ }
+ }
+ catch {destroy $top}
+}
+
+proc delview {} {
+ global curview viewdata viewperm hlview selectedhlview
+
+ if {$curview == 0} return
+ if {[info exists hlview] && $hlview == $curview} {
+ set selectedhlview None
+ unset hlview
+ }
+ allviewmenus $curview delete
+ set viewdata($curview) {}
+ set viewperm($curview) 0
+ showview 0
+}
+
+proc addviewmenu {n} {
+ global viewname viewhlmenu
+
+ .bar.view add radiobutton -label $viewname($n) \
+ -command [list showview $n] -variable selectedview -value $n
+ $viewhlmenu add radiobutton -label $viewname($n) \
+ -command [list addvhighlight $n] -variable selectedhlview
+}
+
+proc flatten {var} {
+ global $var
+
+ set ret {}
+ foreach i [array names $var] {
+ lappend ret $i [set $var\($i\)]
+ }
+ return $ret
+}
+
+proc unflatten {var l} {
+ global $var
+
+ catch {unset $var}
+ foreach {i v} $l {
+ set $var\($i\) $v
+ }
+}
+
+proc showview {n} {
+ global curview viewdata viewfiles
+ global displayorder parentlist childlist rowidlist rowoffsets
+ global colormap rowtextx commitrow nextcolor canvxmax
+ global numcommits rowrangelist commitlisted idrowranges
+ global selectedline currentid canv canvy0
+ global matchinglines treediffs
+ global pending_select phase
+ global commitidx rowlaidout rowoptim linesegends
+ global commfd nextupdate
+ global selectedview
+ global vparentlist vchildlist vdisporder vcmitlisted
+ global hlview selectedhlview
+
+ if {$n == $curview} return
+ set selid {}
+ if {[info exists selectedline]} {
+ set selid $currentid
+ set y [yc $selectedline]
+ set ymax [lindex [$canv cget -scrollregion] 3]
+ set span [$canv yview]
+ set ytop [expr {[lindex $span 0] * $ymax}]
+ set ybot [expr {[lindex $span 1] * $ymax}]
+ if {$ytop < $y && $y < $ybot} {
+ set yscreen [expr {$y - $ytop}]
+ } else {
+ set yscreen [expr {($ybot - $ytop) / 2}]
+ }
+ }
+ unselectline
+ normalline
+ stopfindproc
+ if {$curview >= 0} {
+ set vparentlist($curview) $parentlist
+ set vchildlist($curview) $childlist
+ set vdisporder($curview) $displayorder
+ set vcmitlisted($curview) $commitlisted
+ if {$phase ne {}} {
+ set viewdata($curview) \
+ [list $phase $rowidlist $rowoffsets $rowrangelist \
+ [flatten idrowranges] [flatten idinlist] \
+ $rowlaidout $rowoptim $numcommits $linesegends]
+ } elseif {![info exists viewdata($curview)]
+ || [lindex $viewdata($curview) 0] ne {}} {
+ set viewdata($curview) \
+ [list {} $rowidlist $rowoffsets $rowrangelist]
+ }
+ }
+ catch {unset matchinglines}
+ catch {unset treediffs}
+ clear_display
+ if {[info exists hlview] && $hlview == $n} {
+ unset hlview
+ set selectedhlview None
+ }
+
+ set curview $n
+ set selectedview $n
+ .bar.view entryconf 2 -state [expr {$n == 0? "disabled": "normal"}]
+ .bar.view entryconf 3 -state [expr {$n == 0? "disabled": "normal"}]
+
+ if {![info exists viewdata($n)]} {
+ set pending_select $selid
+ getcommits
+ return
+ }
+
+ set v $viewdata($n)
+ set phase [lindex $v 0]
+ set displayorder $vdisporder($n)
+ set parentlist $vparentlist($n)
+ set childlist $vchildlist($n)
+ set commitlisted $vcmitlisted($n)
+ set rowidlist [lindex $v 1]
+ set rowoffsets [lindex $v 2]
+ set rowrangelist [lindex $v 3]
+ if {$phase eq {}} {
+ set numcommits [llength $displayorder]
+ catch {unset idrowranges}
+ } else {
+ unflatten idrowranges [lindex $v 4]
+ unflatten idinlist [lindex $v 5]
+ set rowlaidout [lindex $v 6]
+ set rowoptim [lindex $v 7]
+ set numcommits [lindex $v 8]
+ set linesegends [lindex $v 9]
+ }
+
+ catch {unset colormap}
+ catch {unset rowtextx}
+ set nextcolor 0
+ set canvxmax [$canv cget -width]
+ set curview $n
+ set row 0
+ setcanvscroll
+ set yf 0
+ set row 0
+ if {$selid ne {} && [info exists commitrow($n,$selid)]} {
+ set row $commitrow($n,$selid)
+ # try to get the selected row in the same position on the screen
+ set ymax [lindex [$canv cget -scrollregion] 3]
+ set ytop [expr {[yc $row] - $yscreen}]
+ if {$ytop < 0} {
+ set ytop 0
+ }
+ set yf [expr {$ytop * 1.0 / $ymax}]
+ }
+ allcanvs yview moveto $yf
+ drawvisible
+ selectline $row 0
+ if {$phase ne {}} {
+ if {$phase eq "getcommits"} {
+ show_status "Reading commits..."
+ }
+ if {[info exists commfd($n)]} {
+ layoutmore
+ } else {
+ finishcommits
+ }
+ } elseif {$numcommits == 0} {
+ show_status "No commits selected"
+ }
+}
+
+# Stuff relating to the highlighting facility
+
+proc ishighlighted {row} {
+ global vhighlights fhighlights nhighlights rhighlights
+
+ if {[info exists nhighlights($row)] && $nhighlights($row) > 0} {
+ return $nhighlights($row)
+ }
+ if {[info exists vhighlights($row)] && $vhighlights($row) > 0} {
+ return $vhighlights($row)
+ }
+ if {[info exists fhighlights($row)] && $fhighlights($row) > 0} {
+ return $fhighlights($row)
+ }
+ if {[info exists rhighlights($row)] && $rhighlights($row) > 0} {
+ return $rhighlights($row)
+ }
+ return 0
+}
+
+proc bolden {row font} {
+ global canv linehtag selectedline boldrows
+
+ lappend boldrows $row
+ $canv itemconf $linehtag($row) -font $font
+ if {[info exists selectedline] && $row == $selectedline} {
+ $canv delete secsel
+ set t [eval $canv create rect [$canv bbox $linehtag($row)] \
+ -outline {{}} -tags secsel \
+ -fill [$canv cget -selectbackground]]
+ $canv lower $t
+ }
+}
+
+proc bolden_name {row font} {
+ global canv2 linentag selectedline boldnamerows
+
+ lappend boldnamerows $row
+ $canv2 itemconf $linentag($row) -font $font
+ if {[info exists selectedline] && $row == $selectedline} {
+ $canv2 delete secsel
+ set t [eval $canv2 create rect [$canv2 bbox $linentag($row)] \
+ -outline {{}} -tags secsel \
+ -fill [$canv2 cget -selectbackground]]
+ $canv2 lower $t
+ }
+}
+
+proc unbolden {} {
+ global mainfont boldrows
+
+ set stillbold {}
+ foreach row $boldrows {
+ if {![ishighlighted $row]} {
+ bolden $row $mainfont
+ } else {
+ lappend stillbold $row
+ }
+ }
+ set boldrows $stillbold
+}
+
+proc addvhighlight {n} {
+ global hlview curview viewdata vhl_done vhighlights commitidx
+
+ if {[info exists hlview]} {
+ delvhighlight
+ }
+ set hlview $n
+ if {$n != $curview && ![info exists viewdata($n)]} {
+ set viewdata($n) [list getcommits {{}} {{}} {} {} {} 0 0 0 {}]
+ set vparentlist($n) {}
+ set vchildlist($n) {}
+ set vdisporder($n) {}
+ set vcmitlisted($n) {}
+ start_rev_list $n
+ }
+ set vhl_done $commitidx($hlview)
+ if {$vhl_done > 0} {
+ drawvisible
+ }
+}
+
+proc delvhighlight {} {
+ global hlview vhighlights
+
+ if {![info exists hlview]} return
+ unset hlview
+ catch {unset vhighlights}
+ unbolden
+}
+
+proc vhighlightmore {} {
+ global hlview vhl_done commitidx vhighlights
+ global displayorder vdisporder curview mainfont
+
+ set font [concat $mainfont bold]
+ set max $commitidx($hlview)
+ if {$hlview == $curview} {
+ set disp $displayorder
+ } else {
+ set disp $vdisporder($hlview)
+ }
+ set vr [visiblerows]
+ set r0 [lindex $vr 0]
+ set r1 [lindex $vr 1]
+ for {set i $vhl_done} {$i < $max} {incr i} {
+ set id [lindex $disp $i]
+ if {[info exists commitrow($curview,$id)]} {
+ set row $commitrow($curview,$id)
+ if {$r0 <= $row && $row <= $r1} {
+ if {![highlighted $row]} {
+ bolden $row $font
+ }
+ set vhighlights($row) 1
+ }
+ }
+ }
+ set vhl_done $max
+}
+
+proc askvhighlight {row id} {
+ global hlview vhighlights commitrow iddrawn mainfont
+
+ if {[info exists commitrow($hlview,$id)]} {
+ if {[info exists iddrawn($id)] && ![ishighlighted $row]} {
+ bolden $row [concat $mainfont bold]
+ }
+ set vhighlights($row) 1
+ } else {
+ set vhighlights($row) 0
+ }
+}
+
+proc hfiles_change {name ix op} {
+ global highlight_files filehighlight fhighlights fh_serial
+ global mainfont highlight_paths
+
+ if {[info exists filehighlight]} {
+ # delete previous highlights
+ catch {close $filehighlight}
+ unset filehighlight
+ catch {unset fhighlights}
+ unbolden
+ unhighlight_filelist
+ }
+ set highlight_paths {}
+ after cancel do_file_hl $fh_serial
+ incr fh_serial
+ if {$highlight_files ne {}} {
+ after 300 do_file_hl $fh_serial
+ }
+}
+
+proc makepatterns {l} {
+ set ret {}
+ foreach e $l {
+ set ee [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} $e]
+ if {[string index $ee end] eq "/"} {
+ lappend ret "$ee*"
+ } else {
+ lappend ret $ee
+ lappend ret "$ee/*"
+ }
+ }
+ return $ret
+}
+
+proc do_file_hl {serial} {
+ global highlight_files filehighlight highlight_paths gdttype fhl_list
+
+ if {$gdttype eq "touching paths:"} {
+ if {[catch {set paths [shellsplit $highlight_files]}]} return
+ set highlight_paths [makepatterns $paths]
+ highlight_filelist
+ set gdtargs [concat -- $paths]
+ } else {
+ set gdtargs [list "-S$highlight_files"]
+ }
+ set cmd [concat | git-diff-tree -r -s --stdin $gdtargs]
+ set filehighlight [open $cmd r+]
+ fconfigure $filehighlight -blocking 0
+ fileevent $filehighlight readable readfhighlight
+ set fhl_list {}
+ drawvisible
+ flushhighlights
+}
+
+proc flushhighlights {} {
+ global filehighlight fhl_list
+
+ if {[info exists filehighlight]} {
+ lappend fhl_list {}
+ puts $filehighlight ""
+ flush $filehighlight
+ }
+}
+
+proc askfilehighlight {row id} {
+ global filehighlight fhighlights fhl_list
+
+ lappend fhl_list $id
+ set fhighlights($row) -1
+ puts $filehighlight $id
+}
+
+proc readfhighlight {} {
+ global filehighlight fhighlights commitrow curview mainfont iddrawn
+ global fhl_list
+
+ while {[gets $filehighlight line] >= 0} {
+ set line [string trim $line]
+ set i [lsearch -exact $fhl_list $line]
+ if {$i < 0} continue
+ for {set j 0} {$j < $i} {incr j} {
+ set id [lindex $fhl_list $j]
+ if {[info exists commitrow($curview,$id)]} {
+ set fhighlights($commitrow($curview,$id)) 0
+ }
+ }
+ set fhl_list [lrange $fhl_list [expr {$i+1}] end]
+ if {$line eq {}} continue
+ if {![info exists commitrow($curview,$line)]} continue
+ set row $commitrow($curview,$line)
+ if {[info exists iddrawn($line)] && ![ishighlighted $row]} {
+ bolden $row [concat $mainfont bold]
+ }
+ set fhighlights($row) 1
+ }
+ if {[eof $filehighlight]} {
+ # strange...
+ puts "oops, git-diff-tree died"
+ catch {close $filehighlight}
+ unset filehighlight
+ }
+ next_hlcont
+}
+
+proc find_change {name ix op} {
+ global nhighlights mainfont boldnamerows
+ global findstring findpattern findtype
+
+ # delete previous highlights, if any
+ foreach row $boldnamerows {
+ bolden_name $row $mainfont
+ }
+ set boldnamerows {}
+ catch {unset nhighlights}
+ unbolden
+ if {$findtype ne "Regexp"} {
+ set e [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} \
+ $findstring]
+ set findpattern "*$e*"
+ }
+ drawvisible
+}
+
+proc askfindhighlight {row id} {
+ global nhighlights commitinfo iddrawn mainfont
+ global findstring findtype findloc findpattern
+
+ if {![info exists commitinfo($id)]} {
+ getcommit $id
+ }
+ set info $commitinfo($id)
+ set isbold 0
+ set fldtypes {Headline Author Date Committer CDate Comments}
+ foreach f $info ty $fldtypes {
+ if {$findloc ne "All fields" && $findloc ne $ty} {
+ continue
+ }
+ if {$findtype eq "Regexp"} {
+ set doesmatch [regexp $findstring $f]
+ } elseif {$findtype eq "IgnCase"} {
+ set doesmatch [string match -nocase $findpattern $f]
+ } else {
+ set doesmatch [string match $findpattern $f]
+ }
+ if {$doesmatch} {
+ if {$ty eq "Author"} {
+ set isbold 2
+ } else {
+ set isbold 1
+ }
+ }
+ }
+ if {[info exists iddrawn($id)]} {
+ if {$isbold && ![ishighlighted $row]} {
+ bolden $row [concat $mainfont bold]
+ }
+ if {$isbold >= 2} {
+ bolden_name $row [concat $mainfont bold]
+ }
+ }
+ set nhighlights($row) $isbold
+}
+
+proc vrel_change {name ix op} {
+ global highlight_related
+
+ rhighlight_none
+ if {$highlight_related ne "None"} {
+ after idle drawvisible
+ }
+}
+
+# prepare for testing whether commits are descendents or ancestors of a
+proc rhighlight_sel {a} {
+ global descendent desc_todo ancestor anc_todo
+ global highlight_related rhighlights
+
+ catch {unset descendent}
+ set desc_todo [list $a]
+ catch {unset ancestor}
+ set anc_todo [list $a]
+ if {$highlight_related ne "None"} {
+ rhighlight_none
+ after idle drawvisible
+ }
+}
+
+proc rhighlight_none {} {
+ global rhighlights
+
+ catch {unset rhighlights}
+ unbolden
+}
+
+proc is_descendent {a} {
+ global curview children commitrow descendent desc_todo
+
+ set v $curview
+ set la $commitrow($v,$a)
+ set todo $desc_todo
+ set leftover {}
+ set done 0
+ for {set i 0} {$i < [llength $todo]} {incr i} {
+ set do [lindex $todo $i]
+ if {$commitrow($v,$do) < $la} {
+ lappend leftover $do
+ continue
+ }
+ foreach nk $children($v,$do) {
+ if {![info exists descendent($nk)]} {
+ set descendent($nk) 1
+ lappend todo $nk
+ if {$nk eq $a} {
+ set done 1
+ }
+ }
+ }
+ if {$done} {
+ set desc_todo [concat $leftover [lrange $todo [expr {$i+1}] end]]
+ return
+ }
+ }
+ set descendent($a) 0
+ set desc_todo $leftover
+}
+
+proc is_ancestor {a} {
+ global curview parentlist commitrow ancestor anc_todo
+
+ set v $curview
+ set la $commitrow($v,$a)
+ set todo $anc_todo
+ set leftover {}
+ set done 0
+ for {set i 0} {$i < [llength $todo]} {incr i} {
+ set do [lindex $todo $i]
+ if {![info exists commitrow($v,$do)] || $commitrow($v,$do) > $la} {
+ lappend leftover $do
+ continue
+ }
+ foreach np [lindex $parentlist $commitrow($v,$do)] {
+ if {![info exists ancestor($np)]} {
+ set ancestor($np) 1
+ lappend todo $np
+ if {$np eq $a} {
+ set done 1
+ }
+ }
+ }
+ if {$done} {
+ set anc_todo [concat $leftover [lrange $todo [expr {$i+1}] end]]
+ return
+ }
+ }
+ set ancestor($a) 0
+ set anc_todo $leftover
+}
+
+proc askrelhighlight {row id} {
+ global descendent highlight_related iddrawn mainfont rhighlights
+ global selectedline ancestor
+
+ if {![info exists selectedline]} return
+ set isbold 0
+ if {$highlight_related eq "Descendent" ||
+ $highlight_related eq "Not descendent"} {
+ if {![info exists descendent($id)]} {
+ is_descendent $id
+ }
+ if {$descendent($id) == ($highlight_related eq "Descendent")} {
+ set isbold 1
+ }
+ } elseif {$highlight_related eq "Ancestor" ||
+ $highlight_related eq "Not ancestor"} {
+ if {![info exists ancestor($id)]} {
+ is_ancestor $id
+ }
+ if {$ancestor($id) == ($highlight_related eq "Ancestor")} {
+ set isbold 1
+ }
+ }
+ if {[info exists iddrawn($id)]} {
+ if {$isbold && ![ishighlighted $row]} {
+ bolden $row [concat $mainfont bold]
+ }
+ }
+ set rhighlights($row) $isbold
+}
+
+proc next_hlcont {} {
+ global fhl_row fhl_dirn displayorder numcommits
+ global vhighlights fhighlights nhighlights rhighlights
+ global hlview filehighlight findstring highlight_related
+
+ if {![info exists fhl_dirn] || $fhl_dirn == 0} return
+ set row $fhl_row
+ while {1} {
+ if {$row < 0 || $row >= $numcommits} {
+ bell
+ set fhl_dirn 0
+ return
+ }
+ set id [lindex $displayorder $row]
+ if {[info exists hlview]} {
+ if {![info exists vhighlights($row)]} {
+ askvhighlight $row $id
+ }
+ if {$vhighlights($row) > 0} break
+ }
+ if {$findstring ne {}} {
+ if {![info exists nhighlights($row)]} {
+ askfindhighlight $row $id
+ }
+ if {$nhighlights($row) > 0} break
+ }
+ if {$highlight_related ne "None"} {
+ if {![info exists rhighlights($row)]} {
+ askrelhighlight $row $id
+ }
+ if {$rhighlights($row) > 0} break
+ }
+ if {[info exists filehighlight]} {
+ if {![info exists fhighlights($row)]} {
+ # ask for a few more while we're at it...
+ set r $row
+ for {set n 0} {$n < 100} {incr n} {
+ if {![info exists fhighlights($r)]} {
+ askfilehighlight $r [lindex $displayorder $r]
+ }
+ incr r $fhl_dirn
+ if {$r < 0 || $r >= $numcommits} break
+ }
+ flushhighlights
+ }
+ if {$fhighlights($row) < 0} {
+ set fhl_row $row
+ return
+ }
+ if {$fhighlights($row) > 0} break
+ }
+ incr row $fhl_dirn
+ }
+ set fhl_dirn 0
+ selectline $row 1
+}