1#!/bin/sh
2# Tcl ignores the next line -*- tcl -*- \
3exec wish "$0" -- "${1+$@}"
4
5# Copyright (C) 2005 Paul Mackerras. All rights reserved.
6# This program is free software; it may be used, copied, modified
7# and distributed under the terms of the GNU General Public Licence,
8# either version 2, or (at your option) any later version.
9
10# CVS $Revision: 1.23 $
11
12proc getcommits {rargs} {
13 global commits commfd phase canv mainfont
14 global startmsecs nextupdate
15 global ctext maincursor textcursor
16
17 if {$rargs == {}} {
18 set rargs HEAD
19 }
20 set commits {}
21 set phase getcommits
22 set startmsecs [clock clicks -milliseconds]
23 set nextupdate [expr $startmsecs + 100]
24 if [catch {set commfd [open "|git-rev-list --merge-order $rargs" r]} err] {
25 puts stderr "Error executing git-rev-list: $err"
26 exit 1
27 }
28 fconfigure $commfd -blocking 0
29 fileevent $commfd readable "getcommitline $commfd"
30 $canv delete all
31 $canv create text 3 3 -anchor nw -text "Reading commits..." \
32 -font $mainfont -tags textitems
33 . config -cursor watch
34 $ctext config -cursor watch
35}
36
37proc getcommitline {commfd} {
38 global commits parents cdate children nchildren ncleft
39 global commitlisted phase commitinfo nextupdate
40 global stopped redisplaying
41
42 set n [gets $commfd line]
43 if {$n < 0} {
44 if {![eof $commfd]} return
45 # this works around what is apparently a bug in Tcl...
46 fconfigure $commfd -blocking 1
47 if {![catch {close $commfd} err]} {
48 after idle finishcommits
49 return
50 }
51 if {[string range $err 0 4] == "usage"} {
52 set err \
53{Gitk: error reading commits: bad arguments to git-rev-list.
54(Note: arguments to gitk are passed to git-rev-list
55to allow selection of commits to be displayed.)}
56 } else {
57 set err "Error reading commits: $err"
58 }
59 error_popup $err
60 exit 1
61 }
62 if {![regexp {^[0-9a-f]{40}$} $line id]} {
63 error_popup "Can't parse git-rev-list output: {$line}"
64 exit 1
65 }
66 lappend commits $id
67 set commitlisted($id) 1
68 if {![info exists commitinfo($id)]} {
69 readcommit $id
70 }
71 foreach p $parents($id) {
72 if {[info exists commitlisted($p)]} {
73 puts "oops, parent $p before child $id"
74 }
75 }
76 drawcommit $id
77 if {[clock clicks -milliseconds] >= $nextupdate} {
78 doupdate
79 }
80 while {$redisplaying} {
81 set redisplaying 0
82 if {$stopped == 1} {
83 set stopped 0
84 set phase "getcommits"
85 foreach id $commits {
86 drawcommit $id
87 if {$stopped} break
88 if {[clock clicks -milliseconds] >= $nextupdate} {
89 doupdate
90 }
91 }
92 }
93 }
94}
95
96proc doupdate {} {
97 global commfd nextupdate
98
99 incr nextupdate 100
100 fileevent $commfd readable {}
101 update
102 fileevent $commfd readable "getcommitline $commfd"
103}
104
105proc readcommit {id} {
106 global commitinfo children nchildren parents nparents cdate ncleft
107 global noreadobj
108
109 set inhdr 1
110 set comment {}
111 set headline {}
112 set auname {}
113 set audate {}
114 set comname {}
115 set comdate {}
116 if {![info exists nchildren($id)]} {
117 set children($id) {}
118 set nchildren($id) 0
119 set ncleft($id) 0
120 }
121 set parents($id) {}
122 set nparents($id) 0
123 if {$noreadobj} {
124 if [catch {set contents [exec git-cat-file commit $id]}] return
125 } else {
126 if [catch {set x [readobj $id]}] return
127 if {[lindex $x 0] != "commit"} return
128 set contents [lindex $x 1]
129 }
130 foreach line [split $contents "\n"] {
131 if {$inhdr} {
132 if {$line == {}} {
133 set inhdr 0
134 } else {
135 set tag [lindex $line 0]
136 if {$tag == "parent"} {
137 set p [lindex $line 1]
138 if {![info exists nchildren($p)]} {
139 set children($p) {}
140 set nchildren($p) 0
141 set ncleft($p) 0
142 }
143 lappend parents($id) $p
144 incr nparents($id)
145 if {[lsearch -exact $children($p) $id] < 0} {
146 lappend children($p) $id
147 incr nchildren($p)
148 incr ncleft($p)
149 } else {
150 puts "child $id already in $p's list??"
151 }
152 } elseif {$tag == "author"} {
153 set x [expr {[llength $line] - 2}]
154 set audate [lindex $line $x]
155 set auname [lrange $line 1 [expr {$x - 1}]]
156 } elseif {$tag == "committer"} {
157 set x [expr {[llength $line] - 2}]
158 set comdate [lindex $line $x]
159 set comname [lrange $line 1 [expr {$x - 1}]]
160 }
161 }
162 } else {
163 if {$comment == {}} {
164 set headline $line
165 } else {
166 append comment "\n"
167 }
168 append comment $line
169 }
170 }
171 if {$audate != {}} {
172 set audate [clock format $audate -format "%Y-%m-%d %H:%M:%S"]
173 }
174 if {$comdate != {}} {
175 set cdate($id) $comdate
176 set comdate [clock format $comdate -format "%Y-%m-%d %H:%M:%S"]
177 }
178 set commitinfo($id) [list $headline $auname $audate \
179 $comname $comdate $comment]
180}
181
182proc readrefs {} {
183 global tagids idtags headids idheads
184 set tags [glob -nocomplain -types f .git/refs/tags/*]
185 foreach f $tags {
186 catch {
187 set fd [open $f r]
188 set line [read $fd]
189 if {[regexp {^[0-9a-f]{40}} $line id]} {
190 set direct [file tail $f]
191 set tagids($direct) $id
192 lappend idtags($id) $direct
193 set contents [split [exec git-cat-file tag $id] "\n"]
194 set obj {}
195 set type {}
196 set tag {}
197 foreach l $contents {
198 if {$l == {}} break
199 switch -- [lindex $l 0] {
200 "object" {set obj [lindex $l 1]}
201 "type" {set type [lindex $l 1]}
202 "tag" {set tag [string range $l 4 end]}
203 }
204 }
205 if {$obj != {} && $type == "commit" && $tag != {}} {
206 set tagids($tag) $obj
207 lappend idtags($obj) $tag
208 }
209 }
210 close $fd
211 }
212 }
213 set heads [glob -nocomplain -types f .git/refs/heads/*]
214 foreach f $heads {
215 catch {
216 set fd [open $f r]
217 set line [read $fd 40]
218 if {[regexp {^[0-9a-f]{40}} $line id]} {
219 set head [file tail $f]
220 set headids($head) $line
221 lappend idheads($line) $head
222 }
223 close $fd
224 }
225 }
226}
227
228proc error_popup msg {
229 set w .error
230 toplevel $w
231 wm transient $w .
232 message $w.m -text $msg -justify center -aspect 400
233 pack $w.m -side top -fill x -padx 20 -pady 20
234 button $w.ok -text OK -command "destroy $w"
235 pack $w.ok -side bottom -fill x
236 bind $w <Visibility> "grab $w; focus $w"
237 tkwait window $w
238}
239
240proc makewindow {} {
241 global canv canv2 canv3 linespc charspc ctext cflist textfont
242 global findtype findloc findstring fstring geometry
243 global entries sha1entry sha1string sha1but
244 global maincursor textcursor
245
246 menu .bar
247 .bar add cascade -label "File" -menu .bar.file
248 menu .bar.file
249 .bar.file add command -label "Quit" -command doquit
250 menu .bar.help
251 .bar add cascade -label "Help" -menu .bar.help
252 .bar.help add command -label "About gitk" -command about
253 . configure -menu .bar
254
255 if {![info exists geometry(canv1)]} {
256 set geometry(canv1) [expr 45 * $charspc]
257 set geometry(canv2) [expr 30 * $charspc]
258 set geometry(canv3) [expr 15 * $charspc]
259 set geometry(canvh) [expr 25 * $linespc + 4]
260 set geometry(ctextw) 80
261 set geometry(ctexth) 30
262 set geometry(cflistw) 30
263 }
264 panedwindow .ctop -orient vertical
265 if {[info exists geometry(width)]} {
266 .ctop conf -width $geometry(width) -height $geometry(height)
267 set texth [expr {$geometry(height) - $geometry(canvh) - 56}]
268 set geometry(ctexth) [expr {($texth - 8) /
269 [font metrics $textfont -linespace]}]
270 }
271 frame .ctop.top
272 frame .ctop.top.bar
273 pack .ctop.top.bar -side bottom -fill x
274 set cscroll .ctop.top.csb
275 scrollbar $cscroll -command {allcanvs yview} -highlightthickness 0
276 pack $cscroll -side right -fill y
277 panedwindow .ctop.top.clist -orient horizontal -sashpad 0 -handlesize 4
278 pack .ctop.top.clist -side top -fill both -expand 1
279 .ctop add .ctop.top
280 set canv .ctop.top.clist.canv
281 canvas $canv -height $geometry(canvh) -width $geometry(canv1) \
282 -bg white -bd 0 \
283 -yscrollincr $linespc -yscrollcommand "$cscroll set"
284 .ctop.top.clist add $canv
285 set canv2 .ctop.top.clist.canv2
286 canvas $canv2 -height $geometry(canvh) -width $geometry(canv2) \
287 -bg white -bd 0 -yscrollincr $linespc
288 .ctop.top.clist add $canv2
289 set canv3 .ctop.top.clist.canv3
290 canvas $canv3 -height $geometry(canvh) -width $geometry(canv3) \
291 -bg white -bd 0 -yscrollincr $linespc
292 .ctop.top.clist add $canv3
293 bind .ctop.top.clist <Configure> {resizeclistpanes %W %w}
294
295 set sha1entry .ctop.top.bar.sha1
296 set entries $sha1entry
297 set sha1but .ctop.top.bar.sha1label
298 button $sha1but -text "SHA1 ID: " -state disabled -relief flat \
299 -command gotocommit -width 8
300 $sha1but conf -disabledforeground [$sha1but cget -foreground]
301 pack .ctop.top.bar.sha1label -side left
302 entry $sha1entry -width 40 -font $textfont -textvariable sha1string
303 trace add variable sha1string write sha1change
304 pack $sha1entry -side left -pady 2
305 button .ctop.top.bar.findbut -text "Find" -command dofind
306 pack .ctop.top.bar.findbut -side left
307 set findstring {}
308 set fstring .ctop.top.bar.findstring
309 lappend entries $fstring
310 entry $fstring -width 30 -font $textfont -textvariable findstring
311 pack $fstring -side left -expand 1 -fill x
312 set findtype Exact
313 tk_optionMenu .ctop.top.bar.findtype findtype Exact IgnCase Regexp
314 set findloc "All fields"
315 tk_optionMenu .ctop.top.bar.findloc findloc "All fields" Headline \
316 Comments Author Committer
317 pack .ctop.top.bar.findloc -side right
318 pack .ctop.top.bar.findtype -side right
319
320 panedwindow .ctop.cdet -orient horizontal
321 .ctop add .ctop.cdet
322 frame .ctop.cdet.left
323 set ctext .ctop.cdet.left.ctext
324 text $ctext -bg white -state disabled -font $textfont \
325 -width $geometry(ctextw) -height $geometry(ctexth) \
326 -yscrollcommand ".ctop.cdet.left.sb set"
327 scrollbar .ctop.cdet.left.sb -command "$ctext yview"
328 pack .ctop.cdet.left.sb -side right -fill y
329 pack $ctext -side left -fill both -expand 1
330 .ctop.cdet add .ctop.cdet.left
331
332 $ctext tag conf filesep -font [concat $textfont bold]
333 $ctext tag conf hunksep -back blue -fore white
334 $ctext tag conf d0 -back "#ff8080"
335 $ctext tag conf d1 -back green
336 $ctext tag conf found -back yellow
337
338 frame .ctop.cdet.right
339 set cflist .ctop.cdet.right.cfiles
340 listbox $cflist -bg white -selectmode extended -width $geometry(cflistw) \
341 -yscrollcommand ".ctop.cdet.right.sb set"
342 scrollbar .ctop.cdet.right.sb -command "$cflist yview"
343 pack .ctop.cdet.right.sb -side right -fill y
344 pack $cflist -side left -fill both -expand 1
345 .ctop.cdet add .ctop.cdet.right
346 bind .ctop.cdet <Configure> {resizecdetpanes %W %w}
347
348 pack .ctop -side top -fill both -expand 1
349
350 bindall <1> {selcanvline %x %y}
351 bindall <B1-Motion> {selcanvline %x %y}
352 bindall <ButtonRelease-4> "allcanvs yview scroll -5 units"
353 bindall <ButtonRelease-5> "allcanvs yview scroll 5 units"
354 bindall <2> "allcanvs scan mark 0 %y"
355 bindall <B2-Motion> "allcanvs scan dragto 0 %y"
356 bind . <Key-Up> "selnextline -1"
357 bind . <Key-Down> "selnextline 1"
358 bind . <Key-Prior> "allcanvs yview scroll -1 pages"
359 bind . <Key-Next> "allcanvs yview scroll 1 pages"
360 bindkey <Key-Delete> "$ctext yview scroll -1 pages"
361 bindkey <Key-BackSpace> "$ctext yview scroll -1 pages"
362 bindkey <Key-space> "$ctext yview scroll 1 pages"
363 bindkey p "selnextline -1"
364 bindkey n "selnextline 1"
365 bindkey b "$ctext yview scroll -1 pages"
366 bindkey d "$ctext yview scroll 18 units"
367 bindkey u "$ctext yview scroll -18 units"
368 bindkey / findnext
369 bindkey ? findprev
370 bindkey f nextfile
371 bind . <Control-q> doquit
372 bind . <Control-f> dofind
373 bind . <Control-g> findnext
374 bind . <Control-r> findprev
375 bind . <Control-equal> {incrfont 1}
376 bind . <Control-KP_Add> {incrfont 1}
377 bind . <Control-minus> {incrfont -1}
378 bind . <Control-KP_Subtract> {incrfont -1}
379 bind $cflist <<ListboxSelect>> listboxsel
380 bind . <Destroy> {savestuff %W}
381 bind . <Button-1> "click %W"
382 bind $fstring <Key-Return> dofind
383 bind $sha1entry <Key-Return> gotocommit
384
385 set maincursor [. cget -cursor]
386 set textcursor [$ctext cget -cursor]
387}
388
389# when we make a key binding for the toplevel, make sure
390# it doesn't get triggered when that key is pressed in the
391# find string entry widget.
392proc bindkey {ev script} {
393 global entries
394 bind . $ev $script
395 set escript [bind Entry $ev]
396 if {$escript == {}} {
397 set escript [bind Entry <Key>]
398 }
399 foreach e $entries {
400 bind $e $ev "$escript; break"
401 }
402}
403
404# set the focus back to the toplevel for any click outside
405# the entry widgets
406proc click {w} {
407 global entries
408 foreach e $entries {
409 if {$w == $e} return
410 }
411 focus .
412}
413
414proc savestuff {w} {
415 global canv canv2 canv3 ctext cflist mainfont textfont
416 global stuffsaved
417 if {$stuffsaved} return
418 if {![winfo viewable .]} return
419 catch {
420 set f [open "~/.gitk-new" w]
421 puts $f "set mainfont {$mainfont}"
422 puts $f "set textfont {$textfont}"
423 puts $f "set geometry(width) [winfo width .ctop]"
424 puts $f "set geometry(height) [winfo height .ctop]"
425 puts $f "set geometry(canv1) [expr [winfo width $canv]-2]"
426 puts $f "set geometry(canv2) [expr [winfo width $canv2]-2]"
427 puts $f "set geometry(canv3) [expr [winfo width $canv3]-2]"
428 puts $f "set geometry(canvh) [expr [winfo height $canv]-2]"
429 set wid [expr {([winfo width $ctext] - 8) \
430 / [font measure $textfont "0"]}]
431 puts $f "set geometry(ctextw) $wid"
432 set wid [expr {([winfo width $cflist] - 11) \
433 / [font measure [$cflist cget -font] "0"]}]
434 puts $f "set geometry(cflistw) $wid"
435 close $f
436 file rename -force "~/.gitk-new" "~/.gitk"
437 }
438 set stuffsaved 1
439}
440
441proc resizeclistpanes {win w} {
442 global oldwidth
443 if [info exists oldwidth($win)] {
444 set s0 [$win sash coord 0]
445 set s1 [$win sash coord 1]
446 if {$w < 60} {
447 set sash0 [expr {int($w/2 - 2)}]
448 set sash1 [expr {int($w*5/6 - 2)}]
449 } else {
450 set factor [expr {1.0 * $w / $oldwidth($win)}]
451 set sash0 [expr {int($factor * [lindex $s0 0])}]
452 set sash1 [expr {int($factor * [lindex $s1 0])}]
453 if {$sash0 < 30} {
454 set sash0 30
455 }
456 if {$sash1 < $sash0 + 20} {
457 set sash1 [expr $sash0 + 20]
458 }
459 if {$sash1 > $w - 10} {
460 set sash1 [expr $w - 10]
461 if {$sash0 > $sash1 - 20} {
462 set sash0 [expr $sash1 - 20]
463 }
464 }
465 }
466 $win sash place 0 $sash0 [lindex $s0 1]
467 $win sash place 1 $sash1 [lindex $s1 1]
468 }
469 set oldwidth($win) $w
470}
471
472proc resizecdetpanes {win w} {
473 global oldwidth
474 if [info exists oldwidth($win)] {
475 set s0 [$win sash coord 0]
476 if {$w < 60} {
477 set sash0 [expr {int($w*3/4 - 2)}]
478 } else {
479 set factor [expr {1.0 * $w / $oldwidth($win)}]
480 set sash0 [expr {int($factor * [lindex $s0 0])}]
481 if {$sash0 < 45} {
482 set sash0 45
483 }
484 if {$sash0 > $w - 15} {
485 set sash0 [expr $w - 15]
486 }
487 }
488 $win sash place 0 $sash0 [lindex $s0 1]
489 }
490 set oldwidth($win) $w
491}
492
493proc allcanvs args {
494 global canv canv2 canv3
495 eval $canv $args
496 eval $canv2 $args
497 eval $canv3 $args
498}
499
500proc bindall {event action} {
501 global canv canv2 canv3
502 bind $canv $event $action
503 bind $canv2 $event $action
504 bind $canv3 $event $action
505}
506
507proc about {} {
508 set w .about
509 if {[winfo exists $w]} {
510 raise $w
511 return
512 }
513 toplevel $w
514 wm title $w "About gitk"
515 message $w.m -text {
516Gitk version 1.1
517
518Copyright © 2005 Paul Mackerras
519
520Use and redistribute under the terms of the GNU General Public License
521
522(CVS $Revision: 1.23 $)} \
523 -justify center -aspect 400
524 pack $w.m -side top -fill x -padx 20 -pady 20
525 button $w.ok -text Close -command "destroy $w"
526 pack $w.ok -side bottom
527}
528
529proc assigncolor {id} {
530 global commitinfo colormap commcolors colors nextcolor
531 global parents nparents children nchildren
532 if [info exists colormap($id)] return
533 set ncolors [llength $colors]
534 if {$nparents($id) == 1 && $nchildren($id) == 1} {
535 set child [lindex $children($id) 0]
536 if {[info exists colormap($child)]
537 && $nparents($child) == 1} {
538 set colormap($id) $colormap($child)
539 return
540 }
541 }
542 set badcolors {}
543 foreach child $children($id) {
544 if {[info exists colormap($child)]
545 && [lsearch -exact $badcolors $colormap($child)] < 0} {
546 lappend badcolors $colormap($child)
547 }
548 if {[info exists parents($child)]} {
549 foreach p $parents($child) {
550 if {[info exists colormap($p)]
551 && [lsearch -exact $badcolors $colormap($p)] < 0} {
552 lappend badcolors $colormap($p)
553 }
554 }
555 }
556 }
557 if {[llength $badcolors] >= $ncolors} {
558 set badcolors {}
559 }
560 for {set i 0} {$i <= $ncolors} {incr i} {
561 set c [lindex $colors $nextcolor]
562 if {[incr nextcolor] >= $ncolors} {
563 set nextcolor 0
564 }
565 if {[lsearch -exact $badcolors $c]} break
566 }
567 set colormap($id) $c
568}
569
570proc initgraph {} {
571 global canvy canvy0 lineno numcommits lthickness nextcolor linespc
572 global linestarty
573 global nchildren ncleft
574
575 allcanvs delete all
576 set nextcolor 0
577 set canvy $canvy0
578 set lineno -1
579 set numcommits 0
580 set lthickness [expr {int($linespc / 9) + 1}]
581 catch {unset linestarty}
582 foreach id [array names nchildren] {
583 set ncleft($id) $nchildren($id)
584 }
585}
586
587proc drawcommitline {level} {
588 global parents children nparents nchildren ncleft todo
589 global canv canv2 canv3 mainfont namefont canvx0 canvy linespc
590 global datemode cdate
591 global lineid linehtag linentag linedtag commitinfo
592 global colormap numcommits currentparents
593 global oldlevel oldnlines oldtodo
594 global idtags idline idheads
595 global lineno lthickness linestarty
596 global commitlisted
597
598 incr numcommits
599 incr lineno
600 set id [lindex $todo $level]
601 set lineid($lineno) $id
602 set idline($id) $lineno
603 set ofill [expr {[info exists commitlisted($id)]? "blue": "white"}]
604 if {![info exists commitinfo($id)]} {
605 readcommit $id
606 if {![info exists commitinfo($id)]} {
607 set commitinfo($id) {"No commit information available"}
608 set nparents($id) 0
609 }
610 }
611 set currentparents {}
612 if {[info exists commitlisted($id)] && [info exists parents($id)]} {
613 set currentparents $parents($id)
614 }
615 set x [expr $canvx0 + $level * $linespc]
616 set y1 $canvy
617 set canvy [expr $canvy + $linespc]
618 allcanvs conf -scrollregion \
619 [list 0 0 0 [expr $y1 + 0.5 * $linespc + 2]]
620 if {[info exists linestarty($id)] && $linestarty($id) < $y1} {
621 set t [$canv create line $x $linestarty($id) $x $y1 \
622 -width $lthickness -fill $colormap($id)]
623 $canv lower $t
624 }
625 set orad [expr {$linespc / 3}]
626 set t [$canv create oval [expr $x - $orad] [expr $y1 - $orad] \
627 [expr $x + $orad - 1] [expr $y1 + $orad - 1] \
628 -fill $ofill -outline black -width 1]
629 $canv raise $t
630 set xt [expr $canvx0 + [llength $todo] * $linespc]
631 if {$nparents($id) > 2} {
632 set xt [expr {$xt + ($nparents($id) - 2) * $linespc}]
633 }
634 set marks {}
635 set ntags 0
636 if {[info exists idtags($id)]} {
637 set marks $idtags($id)
638 set ntags [llength $marks]
639 }
640 if {[info exists idheads($id)]} {
641 set marks [concat $marks $idheads($id)]
642 }
643 if {$marks != {}} {
644 set delta [expr {int(0.5 * ($linespc - $lthickness))}]
645 set yt [expr $y1 - 0.5 * $linespc]
646 set yb [expr $yt + $linespc - 1]
647 set xvals {}
648 set wvals {}
649 foreach tag $marks {
650 set wid [font measure $mainfont $tag]
651 lappend xvals $xt
652 lappend wvals $wid
653 set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}]
654 }
655 set t [$canv create line $x $y1 [lindex $xvals end] $y1 \
656 -width $lthickness -fill black]
657 $canv lower $t
658 foreach tag $marks x $xvals wid $wvals {
659 set xl [expr $x + $delta]
660 set xr [expr $x + $delta + $wid + $lthickness]
661 if {[incr ntags -1] >= 0} {
662 # draw a tag
663 $canv create polygon $x [expr $yt + $delta] $xl $yt\
664 $xr $yt $xr $yb $xl $yb $x [expr $yb - $delta] \
665 -width 1 -outline black -fill yellow
666 } else {
667 # draw a head
668 set xl [expr $xl - $delta/2]
669 $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
670 -width 1 -outline black -fill green
671 }
672 $canv create text $xl $y1 -anchor w -text $tag \
673 -font $mainfont
674 }
675 }
676 set headline [lindex $commitinfo($id) 0]
677 set name [lindex $commitinfo($id) 1]
678 set date [lindex $commitinfo($id) 2]
679 set linehtag($lineno) [$canv create text $xt $y1 -anchor w \
680 -text $headline -font $mainfont ]
681 set linentag($lineno) [$canv2 create text 3 $y1 -anchor w \
682 -text $name -font $namefont]
683 set linedtag($lineno) [$canv3 create text 3 $y1 -anchor w \
684 -text $date -font $mainfont]
685}
686
687proc updatetodo {level noshortcut} {
688 global datemode currentparents ncleft todo
689 global linestarty oldlevel oldtodo oldnlines
690 global canvy linespc
691 global commitinfo
692
693 foreach p $currentparents {
694 if {![info exists commitinfo($p)]} {
695 readcommit $p
696 }
697 }
698 if {!$noshortcut && [llength $currentparents] == 1} {
699 set p [lindex $currentparents 0]
700 if {$ncleft($p) == 0 && [lsearch -exact $todo $p] < 0} {
701 assigncolor $p
702 set linestarty($p) [expr $canvy - $linespc]
703 set todo [lreplace $todo $level $level $p]
704 return 0
705 }
706 }
707
708 set oldlevel $level
709 set oldtodo $todo
710 set oldnlines [llength $todo]
711 set todo [lreplace $todo $level $level]
712 set i $level
713 foreach p $currentparents {
714 incr ncleft($p) -1
715 set k [lsearch -exact $todo $p]
716 if {$k < 0} {
717 assigncolor $p
718 set todo [linsert $todo $i $p]
719 incr i
720 }
721 }
722 return 1
723}
724
725proc drawslants {} {
726 global canv linestarty canvx0 canvy linespc
727 global oldlevel oldtodo todo currentparents
728 global lthickness linespc canvy colormap
729
730 set y1 [expr $canvy - $linespc]
731 set y2 $canvy
732 set i -1
733 foreach id $oldtodo {
734 incr i
735 if {$id == {}} continue
736 set xi [expr {$canvx0 + $i * $linespc}]
737 if {$i == $oldlevel} {
738 foreach p $currentparents {
739 set j [lsearch -exact $todo $p]
740 if {$i == $j && ![info exists linestarty($p)]} {
741 set linestarty($p) $y1
742 } else {
743 set xj [expr {$canvx0 + $j * $linespc}]
744 set coords [list $xi $y1]
745 if {$j < $i - 1} {
746 lappend coords [expr $xj + $linespc] $y1
747 } elseif {$j > $i + 1} {
748 lappend coords [expr $xj - $linespc] $y1
749 }
750 lappend coords $xj $y2
751 set t [$canv create line $coords -width $lthickness \
752 -fill $colormap($p)]
753 $canv lower $t
754 if {![info exists linestarty($p)]} {
755 set linestarty($p) $y2
756 }
757 }
758 }
759 } elseif {[lindex $todo $i] != $id} {
760 set j [lsearch -exact $todo $id]
761 set xj [expr {$canvx0 + $j * $linespc}]
762 set coords {}
763 if {[info exists linestarty($id)] && $linestarty($id) < $y1} {
764 lappend coords $xi $linestarty($id)
765 }
766 lappend coords $xi $y1 $xj $y2
767 set t [$canv create line $coords -width $lthickness \
768 -fill $colormap($id)]
769 $canv lower $t
770 set linestarty($id) $y2
771 }
772 }
773}
774
775proc decidenext {} {
776 global parents children nchildren ncleft todo
777 global canv canv2 canv3 mainfont namefont canvx0 canvy linespc
778 global datemode cdate
779 global lineid linehtag linentag linedtag commitinfo
780 global currentparents oldlevel oldnlines oldtodo
781 global lineno lthickness
782
783 # remove the null entry if present
784 set nullentry [lsearch -exact $todo {}]
785 if {$nullentry >= 0} {
786 set todo [lreplace $todo $nullentry $nullentry]
787 }
788
789 # choose which one to do next time around
790 set todol [llength $todo]
791 set level -1
792 set latest {}
793 for {set k $todol} {[incr k -1] >= 0} {} {
794 set p [lindex $todo $k]
795 if {$ncleft($p) == 0} {
796 if {$datemode} {
797 if {$latest == {} || $cdate($p) > $latest} {
798 set level $k
799 set latest $cdate($p)
800 }
801 } else {
802 set level $k
803 break
804 }
805 }
806 }
807 if {$level < 0} {
808 if {$todo != {}} {
809 puts "ERROR: none of the pending commits can be done yet:"
810 foreach p $todo {
811 puts " $p"
812 }
813 }
814 return -1
815 }
816
817 # If we are reducing, put in a null entry
818 if {$todol < $oldnlines} {
819 if {$nullentry >= 0} {
820 set i $nullentry
821 while {$i < $todol
822 && [lindex $oldtodo $i] == [lindex $todo $i]} {
823 incr i
824 }
825 } else {
826 set i $oldlevel
827 if {$level >= $i} {
828 incr i
829 }
830 }
831 if {$i < $todol} {
832 set todo [linsert $todo $i {}]
833 if {$level >= $i} {
834 incr level
835 }
836 }
837 }
838 return $level
839}
840
841proc drawcommit {id} {
842 global phase todo nchildren datemode nextupdate
843 global startcommits
844
845 if {$phase != "incrdraw"} {
846 set phase incrdraw
847 set todo $id
848 set startcommits $id
849 initgraph
850 assigncolor $id
851 drawcommitline 0
852 updatetodo 0 $datemode
853 } else {
854 if {$nchildren($id) == 0} {
855 lappend todo $id
856 lappend startcommits $id
857 assigncolor $id
858 }
859 set level [decidenext]
860 if {$id != [lindex $todo $level]} {
861 return
862 }
863 while 1 {
864 drawslants
865 drawcommitline $level
866 if {[updatetodo $level $datemode]} {
867 set level [decidenext]
868 }
869 set id [lindex $todo $level]
870 if {![info exists commitlisted($id)]} {
871 break
872 }
873 if {[clock clicks -milliseconds] >= $nextupdate} {
874 doupdate
875 if {$stopped} break
876 }
877 }
878 }
879}
880
881proc finishcommits {} {
882 global phase
883 global startcommits
884 global ctext maincursor textcursor
885
886 if {$phase != "incrdraw"} {
887 $canv delete all
888 $canv create text 3 3 -anchor nw -text "No commits selected" \
889 -font $mainfont -tags textitems
890 set phase {}
891 return
892 }
893 drawslants
894 set level [decidenext]
895 drawrest $level [llength $startcommits]
896 . config -cursor $maincursor
897 $ctext config -cursor $textcursor
898}
899
900proc drawgraph {} {
901 global nextupdate startmsecs startcommits todo
902
903 if {$startcommits == {}} return
904 set startmsecs [clock clicks -milliseconds]
905 set nextupdate [expr $startmsecs + 100]
906 initgraph
907 set todo [lindex $startcommits 0]
908 drawrest 0 1
909}
910
911proc drawrest {level startix} {
912 global phase stopped redisplaying selectedline
913 global datemode currentparents todo
914 global numcommits
915 global nextupdate startmsecs startcommits idline
916
917 set phase drawgraph
918 set startid [lindex $startcommits $startix]
919 set startline -1
920 if {$startid != {}} {
921 set startline $idline($startid)
922 }
923 while 1 {
924 if {$stopped} break
925 drawcommitline $level
926 set hard [updatetodo $level $datemode]
927 if {$numcommits == $startline} {
928 lappend todo $startid
929 set hard 1
930 incr startix
931 set startid [lindex $startcommits $startix]
932 set startline -1
933 if {$startid != {}} {
934 set startline $idline($startid)
935 }
936 }
937 if {$hard} {
938 set level [decidenext]
939 if {$level < 0} break
940 drawslants
941 }
942 if {[clock clicks -milliseconds] >= $nextupdate} {
943 update
944 incr nextupdate 100
945 }
946 }
947 set phase {}
948 set drawmsecs [expr [clock clicks -milliseconds] - $startmsecs]
949 puts "overall $drawmsecs ms for $numcommits commits"
950 if {$redisplaying} {
951 if {$stopped == 0 && [info exists selectedline]} {
952 selectline $selectedline
953 }
954 if {$stopped == 1} {
955 set stopped 0
956 after idle drawgraph
957 } else {
958 set redisplaying 0
959 }
960 }
961}
962
963proc findmatches {f} {
964 global findtype foundstring foundstrlen
965 if {$findtype == "Regexp"} {
966 set matches [regexp -indices -all -inline $foundstring $f]
967 } else {
968 if {$findtype == "IgnCase"} {
969 set str [string tolower $f]
970 } else {
971 set str $f
972 }
973 set matches {}
974 set i 0
975 while {[set j [string first $foundstring $str $i]] >= 0} {
976 lappend matches [list $j [expr $j+$foundstrlen-1]]
977 set i [expr $j + $foundstrlen]
978 }
979 }
980 return $matches
981}
982
983proc dofind {} {
984 global findtype findloc findstring markedmatches commitinfo
985 global numcommits lineid linehtag linentag linedtag
986 global mainfont namefont canv canv2 canv3 selectedline
987 global matchinglines foundstring foundstrlen
988 unmarkmatches
989 focus .
990 set matchinglines {}
991 set fldtypes {Headline Author Date Committer CDate Comment}
992 if {$findtype == "IgnCase"} {
993 set foundstring [string tolower $findstring]
994 } else {
995 set foundstring $findstring
996 }
997 set foundstrlen [string length $findstring]
998 if {$foundstrlen == 0} return
999 if {![info exists selectedline]} {
1000 set oldsel -1
1001 } else {
1002 set oldsel $selectedline
1003 }
1004 set didsel 0
1005 for {set l 0} {$l < $numcommits} {incr l} {
1006 set id $lineid($l)
1007 set info $commitinfo($id)
1008 set doesmatch 0
1009 foreach f $info ty $fldtypes {
1010 if {$findloc != "All fields" && $findloc != $ty} {
1011 continue
1012 }
1013 set matches [findmatches $f]
1014 if {$matches == {}} continue
1015 set doesmatch 1
1016 if {$ty == "Headline"} {
1017 markmatches $canv $l $f $linehtag($l) $matches $mainfont
1018 } elseif {$ty == "Author"} {
1019 markmatches $canv2 $l $f $linentag($l) $matches $namefont
1020 } elseif {$ty == "Date"} {
1021 markmatches $canv3 $l $f $linedtag($l) $matches $mainfont
1022 }
1023 }
1024 if {$doesmatch} {
1025 lappend matchinglines $l
1026 if {!$didsel && $l > $oldsel} {
1027 findselectline $l
1028 set didsel 1
1029 }
1030 }
1031 }
1032 if {$matchinglines == {}} {
1033 bell
1034 } elseif {!$didsel} {
1035 findselectline [lindex $matchinglines 0]
1036 }
1037}
1038
1039proc findselectline {l} {
1040 global findloc commentend ctext
1041 selectline $l
1042 if {$findloc == "All fields" || $findloc == "Comments"} {
1043 # highlight the matches in the comments
1044 set f [$ctext get 1.0 $commentend]
1045 set matches [findmatches $f]
1046 foreach match $matches {
1047 set start [lindex $match 0]
1048 set end [expr [lindex $match 1] + 1]
1049 $ctext tag add found "1.0 + $start c" "1.0 + $end c"
1050 }
1051 }
1052}
1053
1054proc findnext {} {
1055 global matchinglines selectedline
1056 if {![info exists matchinglines]} {
1057 dofind
1058 return
1059 }
1060 if {![info exists selectedline]} return
1061 foreach l $matchinglines {
1062 if {$l > $selectedline} {
1063 findselectline $l
1064 return
1065 }
1066 }
1067 bell
1068}
1069
1070proc findprev {} {
1071 global matchinglines selectedline
1072 if {![info exists matchinglines]} {
1073 dofind
1074 return
1075 }
1076 if {![info exists selectedline]} return
1077 set prev {}
1078 foreach l $matchinglines {
1079 if {$l >= $selectedline} break
1080 set prev $l
1081 }
1082 if {$prev != {}} {
1083 findselectline $prev
1084 } else {
1085 bell
1086 }
1087}
1088
1089proc markmatches {canv l str tag matches font} {
1090 set bbox [$canv bbox $tag]
1091 set x0 [lindex $bbox 0]
1092 set y0 [lindex $bbox 1]
1093 set y1 [lindex $bbox 3]
1094 foreach match $matches {
1095 set start [lindex $match 0]
1096 set end [lindex $match 1]
1097 if {$start > $end} continue
1098 set xoff [font measure $font [string range $str 0 [expr $start-1]]]
1099 set xlen [font measure $font [string range $str 0 [expr $end]]]
1100 set t [$canv create rect [expr $x0+$xoff] $y0 [expr $x0+$xlen+2] $y1 \
1101 -outline {} -tags matches -fill yellow]
1102 $canv lower $t
1103 }
1104}
1105
1106proc unmarkmatches {} {
1107 global matchinglines
1108 allcanvs delete matches
1109 catch {unset matchinglines}
1110}
1111
1112proc selcanvline {x y} {
1113 global canv canvy0 ctext linespc selectedline
1114 global lineid linehtag linentag linedtag
1115 set ymax [lindex [$canv cget -scrollregion] 3]
1116 if {$ymax == {}} return
1117 set yfrac [lindex [$canv yview] 0]
1118 set y [expr {$y + $yfrac * $ymax}]
1119 set l [expr {int(($y - $canvy0) / $linespc + 0.5)}]
1120 if {$l < 0} {
1121 set l 0
1122 }
1123 if {[info exists selectedline] && $selectedline == $l} return
1124 unmarkmatches
1125 selectline $l
1126}
1127
1128proc selectline {l} {
1129 global canv canv2 canv3 ctext commitinfo selectedline
1130 global lineid linehtag linentag linedtag
1131 global canvy0 linespc nparents treepending
1132 global cflist treediffs currentid sha1entry
1133 global commentend seenfile idtags
1134 if {![info exists lineid($l)] || ![info exists linehtag($l)]} return
1135 $canv delete secsel
1136 set t [eval $canv create rect [$canv bbox $linehtag($l)] -outline {{}} \
1137 -tags secsel -fill [$canv cget -selectbackground]]
1138 $canv lower $t
1139 $canv2 delete secsel
1140 set t [eval $canv2 create rect [$canv2 bbox $linentag($l)] -outline {{}} \
1141 -tags secsel -fill [$canv2 cget -selectbackground]]
1142 $canv2 lower $t
1143 $canv3 delete secsel
1144 set t [eval $canv3 create rect [$canv3 bbox $linedtag($l)] -outline {{}} \
1145 -tags secsel -fill [$canv3 cget -selectbackground]]
1146 $canv3 lower $t
1147 set y [expr {$canvy0 + $l * $linespc}]
1148 set ymax [lindex [$canv cget -scrollregion] 3]
1149 set ytop [expr {$y - $linespc - 1}]
1150 set ybot [expr {$y + $linespc + 1}]
1151 set wnow [$canv yview]
1152 set wtop [expr [lindex $wnow 0] * $ymax]
1153 set wbot [expr [lindex $wnow 1] * $ymax]
1154 set wh [expr {$wbot - $wtop}]
1155 set newtop $wtop
1156 if {$ytop < $wtop} {
1157 if {$ybot < $wtop} {
1158 set newtop [expr {$y - $wh / 2.0}]
1159 } else {
1160 set newtop $ytop
1161 if {$newtop > $wtop - $linespc} {
1162 set newtop [expr {$wtop - $linespc}]
1163 }
1164 }
1165 } elseif {$ybot > $wbot} {
1166 if {$ytop > $wbot} {
1167 set newtop [expr {$y - $wh / 2.0}]
1168 } else {
1169 set newtop [expr {$ybot - $wh}]
1170 if {$newtop < $wtop + $linespc} {
1171 set newtop [expr {$wtop + $linespc}]
1172 }
1173 }
1174 }
1175 if {$newtop != $wtop} {
1176 if {$newtop < 0} {
1177 set newtop 0
1178 }
1179 allcanvs yview moveto [expr $newtop * 1.0 / $ymax]
1180 }
1181 set selectedline $l
1182
1183 set id $lineid($l)
1184 set currentid $id
1185 $sha1entry delete 0 end
1186 $sha1entry insert 0 $id
1187 $sha1entry selection from 0
1188 $sha1entry selection to end
1189
1190 $ctext conf -state normal
1191 $ctext delete 0.0 end
1192 set info $commitinfo($id)
1193 $ctext insert end "Author: [lindex $info 1] [lindex $info 2]\n"
1194 $ctext insert end "Committer: [lindex $info 3] [lindex $info 4]\n"
1195 if {[info exists idtags($id)]} {
1196 $ctext insert end "Tags:"
1197 foreach tag $idtags($id) {
1198 $ctext insert end " $tag"
1199 }
1200 $ctext insert end "\n"
1201 }
1202 $ctext insert end "\n"
1203 $ctext insert end [lindex $info 5]
1204 $ctext insert end "\n"
1205 $ctext tag delete Comments
1206 $ctext tag remove found 1.0 end
1207 $ctext conf -state disabled
1208 set commentend [$ctext index "end - 1c"]
1209
1210 $cflist delete 0 end
1211 if {$nparents($id) == 1} {
1212 if {![info exists treediffs($id)]} {
1213 if {![info exists treepending]} {
1214 gettreediffs $id
1215 }
1216 } else {
1217 addtocflist $id
1218 }
1219 }
1220 catch {unset seenfile}
1221}
1222
1223proc selnextline {dir} {
1224 global selectedline
1225 if {![info exists selectedline]} return
1226 set l [expr $selectedline + $dir]
1227 unmarkmatches
1228 selectline $l
1229}
1230
1231proc addtocflist {id} {
1232 global currentid treediffs cflist treepending
1233 if {$id != $currentid} {
1234 gettreediffs $currentid
1235 return
1236 }
1237 $cflist insert end "All files"
1238 foreach f $treediffs($currentid) {
1239 $cflist insert end $f
1240 }
1241 getblobdiffs $id
1242}
1243
1244proc gettreediffs {id} {
1245 global treediffs parents treepending
1246 set treepending $id
1247 set treediffs($id) {}
1248 set p [lindex $parents($id) 0]
1249 if [catch {set gdtf [open "|git-diff-tree -r $p $id" r]}] return
1250 fconfigure $gdtf -blocking 0
1251 fileevent $gdtf readable "gettreediffline $gdtf $id"
1252}
1253
1254proc gettreediffline {gdtf id} {
1255 global treediffs treepending
1256 set n [gets $gdtf line]
1257 if {$n < 0} {
1258 if {![eof $gdtf]} return
1259 close $gdtf
1260 unset treepending
1261 addtocflist $id
1262 return
1263 }
1264 set file [lindex $line 5]
1265 lappend treediffs($id) $file
1266}
1267
1268proc getblobdiffs {id} {
1269 global parents diffopts blobdifffd env curdifftag curtagstart
1270 global diffindex difffilestart
1271 set p [lindex $parents($id) 0]
1272 set env(GIT_DIFF_OPTS) $diffopts
1273 if [catch {set bdf [open "|git-diff-tree -r -p $p $id" r]} err] {
1274 puts "error getting diffs: $err"
1275 return
1276 }
1277 fconfigure $bdf -blocking 0
1278 set blobdifffd($id) $bdf
1279 set curdifftag Comments
1280 set curtagstart 0.0
1281 set diffindex 0
1282 catch {unset difffilestart}
1283 fileevent $bdf readable "getblobdiffline $bdf $id"
1284}
1285
1286proc getblobdiffline {bdf id} {
1287 global currentid blobdifffd ctext curdifftag curtagstart seenfile
1288 global diffnexthead diffnextnote diffindex difffilestart
1289 set n [gets $bdf line]
1290 if {$n < 0} {
1291 if {[eof $bdf]} {
1292 close $bdf
1293 if {$id == $currentid && $bdf == $blobdifffd($id)} {
1294 $ctext tag add $curdifftag $curtagstart end
1295 set seenfile($curdifftag) 1
1296 }
1297 }
1298 return
1299 }
1300 if {$id != $currentid || $bdf != $blobdifffd($id)} {
1301 return
1302 }
1303 $ctext conf -state normal
1304 if {[regexp {^---[ \t]+([^/])*/(.*)} $line match s1 fname]} {
1305 # start of a new file
1306 $ctext insert end "\n"
1307 $ctext tag add $curdifftag $curtagstart end
1308 set seenfile($curdifftag) 1
1309 set curtagstart [$ctext index "end - 1c"]
1310 set header $fname
1311 if {[info exists diffnexthead]} {
1312 set fname $diffnexthead
1313 set header "$diffnexthead ($diffnextnote)"
1314 unset diffnexthead
1315 }
1316 set difffilestart($diffindex) [$ctext index "end - 1c"]
1317 incr diffindex
1318 set curdifftag "f:$fname"
1319 $ctext tag delete $curdifftag
1320 set l [expr {(78 - [string length $header]) / 2}]
1321 set pad [string range "----------------------------------------" 1 $l]
1322 $ctext insert end "$pad $header $pad\n" filesep
1323 } elseif {[string range $line 0 2] == "+++"} {
1324 # no need to do anything with this
1325 } elseif {[regexp {^Created: (.*) \((mode: *[0-7]*)\)} $line match fn m]} {
1326 set diffnexthead $fn
1327 set diffnextnote "created, mode $m"
1328 } elseif {[string range $line 0 8] == "Deleted: "} {
1329 set diffnexthead [string range $line 9 end]
1330 set diffnextnote "deleted"
1331 } elseif {[regexp {^diff --git a/(.*) b/} $line match fn]} {
1332 # save the filename in case the next thing is "new file mode ..."
1333 set diffnexthead $fn
1334 set diffnextnote "modified"
1335 } elseif {[regexp {^new file mode ([0-7]+)} $line match m]} {
1336 set diffnextnote "new file, mode $m"
1337 } elseif {[string range $line 0 11] == "deleted file"} {
1338 set diffnextnote "deleted"
1339 } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
1340 $line match f1l f1c f2l f2c rest]} {
1341 $ctext insert end "\t" hunksep
1342 $ctext insert end " $f1l " d0 " $f2l " d1
1343 $ctext insert end " $rest \n" hunksep
1344 } else {
1345 set x [string range $line 0 0]
1346 if {$x == "-" || $x == "+"} {
1347 set tag [expr {$x == "+"}]
1348 set line [string range $line 1 end]
1349 $ctext insert end "$line\n" d$tag
1350 } elseif {$x == " "} {
1351 set line [string range $line 1 end]
1352 $ctext insert end "$line\n"
1353 } elseif {$x == "\\"} {
1354 # e.g. "\ No newline at end of file"
1355 $ctext insert end "$line\n" filesep
1356 } else {
1357 # Something else we don't recognize
1358 if {$curdifftag != "Comments"} {
1359 $ctext insert end "\n"
1360 $ctext tag add $curdifftag $curtagstart end
1361 set seenfile($curdifftag) 1
1362 set curtagstart [$ctext index "end - 1c"]
1363 set curdifftag Comments
1364 }
1365 $ctext insert end "$line\n" filesep
1366 }
1367 }
1368 $ctext conf -state disabled
1369}
1370
1371proc nextfile {} {
1372 global difffilestart ctext
1373 set here [$ctext index @0,0]
1374 for {set i 0} {[info exists difffilestart($i)]} {incr i} {
1375 if {[$ctext compare $difffilestart($i) > $here]} {
1376 $ctext yview $difffilestart($i)
1377 break
1378 }
1379 }
1380}
1381
1382proc listboxsel {} {
1383 global ctext cflist currentid treediffs seenfile
1384 if {![info exists currentid]} return
1385 set sel [$cflist curselection]
1386 if {$sel == {} || [lsearch -exact $sel 0] >= 0} {
1387 # show everything
1388 $ctext tag conf Comments -elide 0
1389 foreach f $treediffs($currentid) {
1390 if [info exists seenfile(f:$f)] {
1391 $ctext tag conf "f:$f" -elide 0
1392 }
1393 }
1394 } else {
1395 # just show selected files
1396 $ctext tag conf Comments -elide 1
1397 set i 1
1398 foreach f $treediffs($currentid) {
1399 set elide [expr {[lsearch -exact $sel $i] < 0}]
1400 if [info exists seenfile(f:$f)] {
1401 $ctext tag conf "f:$f" -elide $elide
1402 }
1403 incr i
1404 }
1405 }
1406}
1407
1408proc setcoords {} {
1409 global linespc charspc canvx0 canvy0 mainfont
1410 set linespc [font metrics $mainfont -linespace]
1411 set charspc [font measure $mainfont "m"]
1412 set canvy0 [expr 3 + 0.5 * $linespc]
1413 set canvx0 [expr 3 + 0.5 * $linespc]
1414}
1415
1416proc redisplay {} {
1417 global selectedline stopped redisplaying phase
1418 if {$stopped > 1} return
1419 if {$phase == "getcommits"} return
1420 set redisplaying 1
1421 if {$phase == "drawgraph" || $phase == "incrdraw"} {
1422 set stopped 1
1423 } else {
1424 drawgraph
1425 }
1426}
1427
1428proc incrfont {inc} {
1429 global mainfont namefont textfont selectedline ctext canv phase
1430 global stopped entries
1431 unmarkmatches
1432 set mainfont [lreplace $mainfont 1 1 [expr {[lindex $mainfont 1] + $inc}]]
1433 set namefont [lreplace $namefont 1 1 [expr {[lindex $namefont 1] + $inc}]]
1434 set textfont [lreplace $textfont 1 1 [expr {[lindex $textfont 1] + $inc}]]
1435 setcoords
1436 $ctext conf -font $textfont
1437 $ctext tag conf filesep -font [concat $textfont bold]
1438 foreach e $entries {
1439 $e conf -font $mainfont
1440 }
1441 if {$phase == "getcommits"} {
1442 $canv itemconf textitems -font $mainfont
1443 }
1444 redisplay
1445}
1446
1447proc sha1change {n1 n2 op} {
1448 global sha1string currentid sha1but
1449 if {$sha1string == {}
1450 || ([info exists currentid] && $sha1string == $currentid)} {
1451 set state disabled
1452 } else {
1453 set state normal
1454 }
1455 if {[$sha1but cget -state] == $state} return
1456 if {$state == "normal"} {
1457 $sha1but conf -state normal -relief raised -text "Goto: "
1458 } else {
1459 $sha1but conf -state disabled -relief flat -text "SHA1 ID: "
1460 }
1461}
1462
1463proc gotocommit {} {
1464 global sha1string currentid idline tagids
1465 if {$sha1string == {}
1466 || ([info exists currentid] && $sha1string == $currentid)} return
1467 if {[info exists tagids($sha1string)]} {
1468 set id $tagids($sha1string)
1469 } else {
1470 set id [string tolower $sha1string]
1471 }
1472 if {[info exists idline($id)]} {
1473 selectline $idline($id)
1474 return
1475 }
1476 if {[regexp {^[0-9a-fA-F]{40}$} $sha1string]} {
1477 set type "SHA1 id"
1478 } else {
1479 set type "Tag"
1480 }
1481 error_popup "$type $sha1string is not known"
1482}
1483
1484proc doquit {} {
1485 global stopped
1486 set stopped 100
1487 destroy .
1488}
1489
1490# defaults...
1491set datemode 0
1492set boldnames 0
1493set diffopts "-U 5 -p"
1494
1495set mainfont {Helvetica 9}
1496set textfont {Courier 9}
1497
1498set colors {green red blue magenta darkgrey brown orange}
1499
1500catch {source ~/.gitk}
1501
1502set namefont $mainfont
1503if {$boldnames} {
1504 lappend namefont bold
1505}
1506
1507set revtreeargs {}
1508foreach arg $argv {
1509 switch -regexp -- $arg {
1510 "^$" { }
1511 "^-b" { set boldnames 1 }
1512 "^-d" { set datemode 1 }
1513 default {
1514 lappend revtreeargs $arg
1515 }
1516 }
1517}
1518
1519set noreadobj [catch {load libreadobj.so.0.0}]
1520set stopped 0
1521set redisplaying 0
1522set stuffsaved 0
1523setcoords
1524makewindow
1525readrefs
1526getcommits $revtreeargs